diff --git a/CMakeLists.txt b/CMakeLists.txt index 917a7fe051..91a9bae5cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,274 +1,274 @@ project(Amarok) cmake_minimum_required(VERSION 2.8.12) # Remove all warnings, ease the porting to cmake 3.x if (POLICY CMP0063) cmake_policy(SET CMP0063 NEW) endif() if (POLICY CMP0028) cmake_policy(SET CMP0028 NEW) endif() set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ) message(STATUS "${CMAKE_MODULE_PATH}") ############### find_package(PkgConfig REQUIRED) find_package(ECM 1.7.0 REQUIRED CONFIG) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) set(CMAKE_AUTOMOC ON) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) include(FeatureSummary) include(ECMInstallIcons) include(ECMSetupVersion) include(ECMAddTests) include(ECMAddAppIcon) include(FindPkgConfig) find_package( Qt5 REQUIRED COMPONENTS Core DBus Gui Quick QuickWidgets Qml Script ScriptTools Sql Svg Test Widgets Xml ) -find_package( KF5 REQUIRED COMPONENTS Archive Codecs CoreAddons DBusAddons DNSSD GlobalAccel GuiAddons I18n IconThemes KCMUtils KIO NewStuff Notifications NotifyConfig Plasma PlasmaQuick Solid TextEditor ThreadWeaver ) +find_package( KF5 REQUIRED COMPONENTS Archive Codecs CoreAddons DBusAddons Declarative DNSSD GlobalAccel GuiAddons I18n IconThemes KCMUtils KIO NewStuff Notifications NotifyConfig Package Solid TextEditor ThreadWeaver WindowSystem ) ############### option(WITH_UTILITIES "Enable building of utilities" ON) option(WITH_PLAYER "Enable building of main Amarok player" ON) option(WITH_MP3Tunes "Enable mp3tunes in the Amarok player, requires multiple extra dependencies" ON) option(WITH_IPOD "Enable iPod support in Amarok" ON) option(WITH_MYSQL_EMBEDDED "Build the embedded database library -- highly recommended" ON) option(WITH_PLAYGROUND "Enable building of playground scripts and applets (WARNING: some of them might have legal issues!)" OFF) ############### Taglib set(TAGLIB_MIN_VERSION "1.7") find_package(Taglib REQUIRED) # Check if TagLib is built with ASF and MP4 support include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES "${TAGLIB_INCLUDES}") set(CMAKE_REQUIRED_LIBRARIES "${TAGLIB_LIBRARIES}") check_cxx_source_compiles("#include int main() { TagLib::ASF::Tag tag; return 0;}" TAGLIB_ASF_FOUND) if( NOT TAGLIB_ASF_FOUND ) message(FATAL_ERROR "TagLib does not have ASF support compiled in.") endif() check_cxx_source_compiles("#include int main() { TagLib::MP4::Tag tag(0, 0); return 0;}" TAGLIB_MP4_FOUND) if( NOT TAGLIB_MP4_FOUND ) message(FATAL_ERROR "TagLib does not have MP4 support compiled in.") endif() check_cxx_source_compiles("#include #include #include #include #include using namespace TagLib; int main() { char *s; Mod::Tag tag; Mod::File modfile(s); S3M::File s3mfile(s); IT::File itfile(s); XM::File xmfile(s); return 0; }" TAGLIB_MOD_FOUND) check_cxx_source_compiles("#include int main() { char *s; TagLib::Ogg::Opus::File opusfile(s); return 0;}" TAGLIB_OPUS_FOUND) set(CMAKE_REQUIRED_INCLUDES) set(CMAKE_REQUIRED_LIBRARIES) set(TAGLIB-EXTRAS_MIN_VERSION "1.0") find_package(Taglib-Extras) set(TAGLIB_EXTRAS_FOUND ${TAGLIB-EXTRAS_FOUND}) # we need a c-compatible name for the include file include(CheckTagLibFileName) check_taglib_filename(COMPLEX_TAGLIB_FILENAME) ############### #Needed to conditionally build tests and gui if(BUILD_TESTING) add_definitions(-DDEBUG) endif() if(WITH_DESKTOP_UI) add_definitions(-DDESKTOP_UI) endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fmessage-length=0") if (CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmessage-length=0") if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--as-needed") endif() endif () include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/shared ${CMAKE_CURRENT_BINARY_DIR}/shared ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") # Require C++11 # WORKAROUND for Clang bug: http://llvm.org/bugs/show_bug.cgi?id=15651 if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND WIN32) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-delayed-template-parsing") endif () add_definitions(-DQT_NO_URL_CAST_FROM_STRING) find_package(Phonon4Qt5 4.6.60 REQUIRED NO_MODULE) include_directories(BEFORE ${PHONON_INCLUDES}) find_package( LibLastFm ) set( LIBLASTFM_MIN_VERSION "1.0.0" ) if( LIBLASTFM_FOUND ) if ( ${LIBLASTFM_MIN_VERSION} VERSION_LESS ${LIBLASTFM_VERSION} ) set( LIBLASTFM_FOUND TRUE ) endif() endif() find_package( LibOFA ) if( LIBOFA_FOUND ) PKG_SEARCH_MODULE(AVCODEC libavcodec) PKG_SEARCH_MODULE(AVFORMAT libavformat) PKG_SEARCH_MODULE(AVUTIL libavutil) endif() string( TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_TOLOWER ) if( CMAKE_BUILD_TYPE_TOLOWER MATCHES debug ) set( DEBUG_BUILD_TYPE ON ) add_definitions(-Wall -Wextra) endif() # this needs to be here because also code in shared/ needs config.h. This is also the # reason why various checks are above why they belong under if( WITH_PLAYER ) configure_file( shared/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/shared/config.h ) add_subdirectory( data ) add_subdirectory( images ) add_subdirectory( shared ) if( WITH_PLAYER ) set_package_properties( QTOPENGL PROPERTIES DESCRIPTION "Required for the spectrum analyzer" URL "http://qt-project.org" TYPE OPTIONAL ) find_package(MySQLAmarok REQUIRED) if( WITH_MYSQL_EMBEDDED ) set( BUILD_MYSQLE_COLLECTION TRUE ) set_package_properties( MYSQLD PROPERTIES DESCRIPTION "Embedded MySQL Libraries" URL "http://www.mysql.com" TYPE REQUIRED ) else() add_definitions( "-DNO_MYSQL_EMBEDDED" ) endif() set_package_properties( MYSQL PROPERTIES DESCRIPTION "MySQL Server Libraries" URL "http://www.mysql.com" TYPE REQUIRED ) # zlib is required for mysql embedded find_package(ZLIB REQUIRED) set_package_properties( ZLIB PROPERTIES DESCRIPTION "zlib" TYPE REQUIRED ) # QJson is required for the PlaydarCollection find_package(QJSON) set_package_properties( QJSON PROPERTIES DESCRIPTION "Qt JSON Parser used for the Playdar Collection" URL "http://qjson.sourceforge.net/" TYPE OPTIONAL ) # We tell users that we need 1.0.3, but we really check just >= 1.0.0. This is because # upstream forgot to update version in lastfm/global.h, so it looks like 1.0.2. :-( # will be fixed in liblastfm-1.0.4 set( LIBLASTFM_MIN_VERSION "1.0.3" ) set_package_properties( LIBLASTFM PROPERTIES DESCRIPTION "Enable Last.Fm service, including scrobbling, song submissions, and suggested song dynamic playlists" URL "http://cdn.last.fm/client/liblastfm-${LIBLASTFM_MIN_VERSION}.tar.gz" TYPE OPTIONAL ) if( LIBOFA_FOUND ) set_package_properties(LIBOFA PROPERTIES DESCRIPTION "Enable MusicDNS service" URL "http://code.google.com/p/musicip-libofa/" TYPE OPTIONAL) endif() ## gpodder Service ## Currently disabled as it can crash Amarok on startup, during dynamic linking phase, before main() is reached. # find_package( Mygpo-qt 1.0.7 QUIET ) # set_package_properties( LIBMYGPO_QT PROPERTIES DESCRIPTION "Enable gpodder.net service" URL "http://wiki.gpodder.org/wiki/Libmygpo-qt" TYPE OPTIONAL ) # macro_bool_to_01( LIBMYGPO_QT_FOUND HAVE_LIBMYGPOQT ) if( WITH_IPOD ) find_package(Ipod) set(IPOD_MIN_VERSION "0.8.2") if( IPOD_FOUND AND NOT WIN32 ) if ( ${IPOD_MIN_VERSION} VERSION_LESS ${IPOD_VERSION} ) set( IPOD_FOUND TRUE ) endif() endif() set_package_properties( IPOD PROPERTIES DESCRIPTION "Support Apple iPod/iPad/iPhone audio devices" URL "http://sourceforge.net/projects/gtkpod/" TYPE OPTIONAL ) find_package(GDKPixBuf) set_package_properties( GDKPIXBUF PROPERTIES DESCRIPTION "Support for artwork on iPod audio devices via GDK-PixBuf" URL "http://developer.gnome.org/arch/imaging/gdkpixbuf.html" TYPE OPTIONAL ) endif() find_package(Mtp) set_package_properties(MTP PROPERTIES DESCRIPTION "Enable Support for portable media devices that use the media transfer protocol" URL "http://libmtp.sourceforge.net/" TYPE OPTIONAL ) if( WITH_MP3Tunes ) find_package(CURL) set_package_properties( MTP PROPERTIES DESCRIPTION "Enable Support for portable media devices that use the media transfer protocol" URL "http://libmtp.sourceforge.net/" TYPE OPTIONAL ) find_package(LibXml2) set_package_properties( LIBXML2 PROPERTIES DESCRIPTION "LibXML2 is an XML parser required by mp3tunes." URL "http://www.xmlsoft.org" TYPE OPTIONAL ) find_package(OpenSSL) find_package(Libgcrypt) if ( OPENSSL_FOUND OR LIBGCRYPT_FOUND ) set (_mp3tunes_crypto TRUE ) else () message( SEND_ERROR "Building with mp3tunes support REQUIRES either OpenSSL or GNU Libgcrypt" ) endif () set_package_properties( OPENSSL_OR_LIBGCRYPT PROPERTIES DESCRIPTION "OpenSSL or GNU Libgcrypt provides cryptographic functions required by mp3tunes." URL "http://www.openssl.org/ or http://www.gnupg.org/download/#libgcrypt" TYPE OPTIONAL ) find_package(Loudmouth) set_package_properties( LOUDMOUTH PROPERTIES DESCRIPTION "Loudmouth is the communication backend needed by mp3tunes for syncing." URL "http://www.loudmouth-project.org" TYPE OPTIONAL ) include(CheckQtGlib) set_package_properties( QT4_GLIB PROPERTIES DESCRIPTION "Qt4 must be compiled with glib support for mp3tunes" URL "http://www.trolltech.com" TYPE OPTIONAL ) endif() if( WITH_IPOD OR WITH_MP3Tunes ) pkg_search_module(GOBJECT REQUIRED gobject-2.0) set_package_properties( GOBJECT PROPERTIES DESCRIPTION "Required by libgpod and mp3tunes." URL "http://www.gtk.org" TYPE OPTIONAL ) pkg_search_module(GLIB2 glib-2.0) set_package_properties( GLIB2 PROPERTIES DESCRIPTION "Required by libgpod and mp3tunes" URL "http://www.gtk.org" TYPE OPTIONAL ) endif() find_program( CLAMZ_FOUND clamz PATH ) set_package_properties( CLAMZ PROPERTIES DESCRIPTION "Optional requirement to download songs from the Amazon MP3 store. Highly recommended on Linux, as the official downloader from Amazon is quite broken on many systems." URL "https://code.google.com/p/clamz/" TYPE OPTIONAL ) find_package(PythonInterp) set_package_properties( PYTHON PROPERTIES DESCRIPTION "Required for generating the autocompletion file for the script console" URL "https://www.python.org" TYPE OPTIONAL ) if( BUILD_TESTING AND NOT WIN32 ) enable_testing() # add_subdirectory( tests ) endif() add_subdirectory( src ) # Also display taglib in the feature log set_package_properties( TAGLIB PROPERTIES DESCRIPTION "Support for Audio metadata." URL "http://developer.kde.org/~wheeler/taglib.html" TYPE REQUIRED PURPOSE "Required for tag reading" ) # following line is here (and not near TAGLIB_MOD_FOUND) because there may be no MacroLogFeature without kdelibs set_package_properties( TAGLIB_MOD PROPERTIES DESCRIPTION "Additional support for Audio metadata of mod, s3m, it and xm files." URL "http://developer.kde.org/~wheeler/taglib.html" TYPE OPTIONAL ) set_package_properties( TAGLIB_OPUS PROPERTIES DESCRIPTION "Additional support for Audio metadata of opus files." URL "http://developer.kde.org/~wheeler/taglib.html" TYPE OPTIONAL ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) #Do not remove or modify these. The release script substitutes in for these #comments with appropriate doc and translation directories. #PO_SUBDIR #DOC_SUBDIR endif() if( WITH_UTILITIES ) set(EXEC_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} CACHE PATH "Base directory for executables and libraries" FORCE) set(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" CACHE PATH "The subdirectory to the binaries prefix (default prefix/bin)" FORCE) add_subdirectory( utilities ) endif() if( WITH_PLAYGROUND ) add_subdirectory( playground ) message(STATUS "Included playground subdirectory in configuration") endif() include(CTest) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1d1e6eeef2..9caeaeae56 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,996 +1,980 @@ # Improves speed of string concatenation add_definitions(-DQT_USE_FAST_CONCATENATION) add_definitions(-DQT_USE_FAST_OPERATOR_PLUS) if(NOT MSVC) add_definitions(-DQT_STRICT_ITERATORS) endif() if(APPLE) set(mac_SRCS app_mac.cpp mac/GrowlInterface.cpp ) # Notification Center Appeared in 10.8, or Darwin 12 if( CMAKE_SYSTEM_VERSION VERSION_GREATER "11.9.9") list(APPEND mac_SRCS mac/MacSystemNotify.mm) add_definitions(-DHAVE_NOTIFICATION_CENTER) endif() include_directories ( services/lastfm/ ) set( MAC_FILES_DIR ${CMAKE_SOURCE_DIR}/src/mac ) endif() include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) add_subdirectory( core ) add_subdirectory( core-impl/collections ) add_subdirectory( core-impl/storage/sql ) add_subdirectory( context ) #add_subdirectory( services ) #FIXME: disabled temporarily for KF5 porting: Port this to KF5::Plasma after the rest of the code has been ported to the other KF5 components. add_subdirectory( scripting/scripts ) add_subdirectory( aboutdialog/libattica-ocsclient ) add_subdirectory( transcoding ) add_subdirectory( kconf_update ) #add_subdirectory( importers ) #FIXME: disabled temporarily for KF5 porting: Port this to KF5::Plasma after the rest of the code has been ported to the other KF5 components. ##################################################################### # PROXYCOLLECTION ##################################################################### set(aggregatecollection_SRCS core-impl/collections/aggregate/AggregateCollection.cpp core-impl/collections/aggregate/AggregateMeta.cpp core-impl/collections/aggregate/AggregateQueryMaker.cpp ) ##################################################################### # MEDIADEVICEFRAMEWORK ##################################################################### set(libmediadeviceframework_SRCS core-impl/collections/mediadevicecollection/MediaDeviceCollection.cpp core-impl/collections/mediadevicecollection/MediaDeviceCollectionLocation.cpp core-impl/collections/mediadevicecollection/MediaDeviceMeta.cpp core-impl/collections/mediadevicecollection/MediaDeviceTrackEditor.cpp core-impl/collections/mediadevicecollection/handler/MediaDeviceHandler.cpp core-impl/collections/mediadevicecollection/handler/MediaDeviceHandlerCapability.cpp core-impl/collections/mediadevicecollection/handler/capabilities/ArtworkCapability.cpp core-impl/collections/mediadevicecollection/handler/capabilities/PlaylistCapability.cpp core-impl/collections/mediadevicecollection/handler/capabilities/PodcastCapability.cpp core-impl/collections/mediadevicecollection/handler/capabilities/ReadCapability.cpp core-impl/collections/mediadevicecollection/handler/capabilities/WriteCapability.cpp core-impl/collections/mediadevicecollection/playlist/MediaDevicePlaylist.cpp core-impl/collections/mediadevicecollection/playlist/MediaDeviceUserPlaylistProvider.cpp core-impl/collections/mediadevicecollection/podcast/MediaDevicePodcastProvider.cpp core-impl/collections/mediadevicecollection/support/ConnectionAssistant.cpp core-impl/collections/mediadevicecollection/support/MediaDeviceInfo.cpp ) ##################################################################### # SERVICEFRAMEWORK ##################################################################### set(libserviceframework_SRCS services/DynamicServiceQueryMaker.cpp services/InfoParserBase.cpp services/ServiceAlbumCoverDownloader.cpp services/ServiceBase.cpp services/ServiceCapabilities.cpp services/ServiceCollection.cpp services/ServiceCollectionLocation.cpp services/ServiceCollectionTreeView.cpp services/ServiceMetaBase.cpp services/ServicePluginManager.cpp services/ServiceSqlCollection.cpp services/ServiceSqlQueryMaker.cpp services/ServiceSqlRegistry.cpp ) ##################################################################### # SERVICEBROWSER ##################################################################### set(libservicebrowser_SRCS browsers/servicebrowser/ServiceBrowser.cpp ) ##################################################################### # AMAROKURL ##################################################################### set(libamarokurl_SRCS amarokurls/AmarokUrl.cpp amarokurls/AmarokUrlAction.cpp amarokurls/AmarokUrlHandler.cpp amarokurls/BookmarkCurrentButton.cpp amarokurls/ContextUrlGenerator.cpp amarokurls/ContextUrlRunner.cpp amarokurls/NavigationUrlRunner.cpp amarokurls/NavigationUrlGenerator.cpp amarokurls/PlayUrlRunner.cpp amarokurls/PlayUrlGenerator.cpp amarokurls/BookmarkManager.cpp amarokurls/BookmarkManagerWidget.cpp amarokurls/BookmarkGroup.cpp amarokurls/BookmarkModel.cpp amarokurls/BookmarkTreeView.cpp amarokurls/BookmarkMetaActions.cpp ) ##################################################################### # SCRIPTABLESERVICE ##################################################################### set(libscriptableservice_SRCS services/scriptable/ScriptableService.cpp services/scriptable/ScriptableServiceCollection.cpp services/scriptable/ScriptableServiceCollectionTreeModel.cpp services/scriptable/ScriptableServiceInfoParser.cpp services/scriptable/ScriptableServiceManager.cpp services/scriptable/ScriptableServiceMeta.cpp services/scriptable/ScriptableServiceQueryMaker.cpp ) ##################################################################### # CONFIGDIALOG ##################################################################### set(libconfigdialog_SRCS configdialog/ConfigDialog.cpp configdialog/ConfigDialogBase.cpp configdialog/dialogs/CollectionConfig.cpp configdialog/dialogs/ExcludedLabelsDialog.cpp configdialog/dialogs/GeneralConfig.cpp configdialog/dialogs/MetadataConfig.cpp configdialog/dialogs/NotificationsConfig.cpp configdialog/dialogs/PlaybackConfig.cpp configdialog/dialogs/PluginsConfig.cpp configdialog/dialogs/ScriptsConfig.cpp configdialog/dialogs/ScriptSelector.cpp configdialog/dialogs/DatabaseConfig.cpp ) ki18n_wrap_ui(libconfigdialog_SRCS configdialog/dialogs/CollectionConfig.ui configdialog/dialogs/GeneralConfig.ui configdialog/dialogs/MetadataConfig.ui configdialog/dialogs/ExcludedLabelsDialog.ui configdialog/dialogs/NotificationsConfig.ui configdialog/dialogs/PlaybackConfig.ui configdialog/dialogs/DatabaseConfig.ui configdialog/dialogs/ScriptsConfig.ui ) set(libbrowserframework_SRCS browsers/BrowserBreadcrumbItem.cpp browsers/BrowserBreadcrumbWidget.cpp browsers/BrowserCategory.cpp browsers/BrowserCategoryList.cpp browsers/BrowserCategoryListModel.cpp browsers/BrowserCategoryListSortFilterProxyModel.cpp browsers/BrowserDock.cpp browsers/BrowserMessageArea.cpp browsers/CollectionSortFilterProxyModel.cpp browsers/CollectionTreeItem.cpp browsers/CollectionTreeItemModel.cpp browsers/CollectionTreeItemModelBase.cpp browsers/CollectionTreeView.cpp browsers/InfoProxy.cpp browsers/SingleCollectionTreeItemModel.cpp ) ##################################################################### # COLLECTIONBROWSER ##################################################################### set(libcollectionbrowser_SRCS browsers/collectionbrowser/CollectionBrowserTreeView.cpp browsers/collectionbrowser/CollectionWidget.cpp ) ##################################################################### # SYNCHRONIZATION ##################################################################### set(libsynchronization_SRCS synchronization/MasterSlaveSynchronizationJob.cpp synchronization/OneWaySynchronizationJob.cpp synchronization/SynchronizationBaseJob.cpp synchronization/UnionJob.cpp ) ##################################################################### # STATUSBAR ##################################################################### set(libstatusbar_SRCS statusbar/ProgressBar.cpp statusbar/KJobProgressBar.cpp statusbar/NetworkProgressBar.cpp statusbar/CompoundProgressBar.cpp statusbar/PopupWidget.cpp statusbar/LongMessageWidget.cpp ) ##################################################################### # META ##################################################################### set(libmetaimpl_SRCS core-impl/playlists/providers/user/UserPlaylistProvider.cpp core-impl/playlists/types/file/asx/ASXPlaylist.cpp core-impl/playlists/types/file/m3u/M3UPlaylist.cpp core-impl/playlists/types/file/pls/PLSPlaylist.cpp core-impl/playlists/types/file/PlaylistFileLoaderJob.cpp core-impl/playlists/types/file/PlaylistFileSupport.cpp core-impl/playlists/types/file/xspf/XSPFPlaylist.cpp core-impl/capabilities/AlbumActionsCapability.cpp core-impl/capabilities/timecode/TimecodeBoundedPlaybackCapability.cpp core-impl/capabilities/timecode/TimecodeLoadCapability.cpp core-impl/capabilities/timecode/TimecodeWriteCapability.cpp core-impl/capabilities/multisource/MultiSourceCapabilityImpl.cpp core-impl/meta/file/File.cpp core-impl/meta/file/FileTrackProvider.cpp core-impl/meta/multi/MultiTrack.cpp core-impl/meta/cue/CueFileSupport.cpp core-impl/meta/proxy/MetaProxy.cpp core-impl/meta/proxy/MetaProxyWorker.cpp core-impl/meta/stream/Stream.cpp core-impl/playlists/types/file/PlaylistFile.cpp core-impl/support/PersistentStatisticsStore.cpp core-impl/support/TagStatisticsStore.cpp core-impl/support/UrlStatisticsStore.cpp ) ##################################################################### # COLLECTION ##################################################################### set(collection_SRCS core-impl/collections/support/jobs/WriteTagsJob.cpp core-impl/collections/support/ArtistHelper.cpp core-impl/collections/support/CollectionManager.cpp core-impl/collections/support/CollectionLocationDelegateImpl.cpp core-impl/collections/support/MemoryCustomValue.cpp core-impl/collections/support/MemoryFilter.cpp core-impl/collections/support/MemoryMatcher.cpp core-impl/collections/support/MemoryMeta.cpp core-impl/collections/support/MemoryQueryMaker.cpp core-impl/collections/support/MemoryQueryMakerInternal.cpp core-impl/collections/support/MemoryQueryMakerHelper.cpp core-impl/collections/support/TrashCollectionLocation.cpp core-impl/collections/support/XmlQueryReader.cpp core-impl/collections/support/FileCollectionLocation.cpp core-impl/collections/support/Expression.cpp core-impl/collections/support/TextualQueryFilter.cpp ) ##################################################################### # STORAGE ##################################################################### set(storage_SRCS core-impl/storage/StorageManager.cpp ) ##################################################################### # SCANNER ##################################################################### set( scanner_SRCS scanner/GenericScanManager.cpp scanner/GenericScannerJob.cpp scanner/AbstractDirectoryWatcher.cpp scanner/AbstractScanResultProcessor.cpp ) ##################################################################### # CONTEXT ##################################################################### set( libcontextview_SRCS -# context/AmarokContextPackageStructure.cpp -# context/AppletLoader.cpp -# context/AppletModel.cpp -# context/ContextDock.cpp -# context/ContextView.cpp + context/AmarokContextPackageStructure.cpp + context/AppletLoader.cpp + context/AppletModel.cpp + context/ContextDock.cpp + context/ContextView.cpp context/LyricsManager.cpp -# context/ToolbarView.cpp -# context/toolbar/AppletItemOverlay.cpp -# context/toolbar/AppletToolbarAddItem.cpp -# context/toolbar/AppletToolbarAppletItem.cpp -# context/toolbar/AppletToolbarBase.cpp -# context/toolbar/AppletToolbarConfigItem.cpp -# context/widgets/AppletHeader.cpp -# context/widgets/RatingWidget.cpp -# context/widgets/TextScrollingWidget.cpp -# context/widgets/DropPixmapItem.cpp -# context/widgets/ToolBoxIcon.cpp -# context/widgets/ContainmentArrow.cpp -# context/widgets/ContainmentSelectionLayer.cpp -# context/widgets/appletexplorer/AppletExplorer.cpp -# context/widgets/appletexplorer/AppletIcon.cpp -# context/widgets/RecentlyPlayedListWidget.cpp ) ##################################################################### # PODCASTS ##################################################################### set(libpodcasts_SRCS core-impl/podcasts/sql/SqlPodcastMeta.cpp core-impl/podcasts/sql/SqlPodcastProvider.cpp core-impl/podcasts/sql/PodcastSettingsDialog.cpp core-impl/podcasts/sql/PodcastFilenameLayoutConfigDialog.cpp ) ##################################################################### # PLAYLISTBROWSER ##################################################################### set(libplaylistbrowser_SRCS browsers/playlistbrowser/APGCategory.cpp browsers/playlistbrowser/DynamicCategory.cpp browsers/playlistbrowser/DynamicBiasDelegate.cpp browsers/playlistbrowser/DynamicBiasDialog.cpp browsers/playlistbrowser/DynamicView.cpp browsers/playlistbrowser/PlaylistBrowserFilterProxy.cpp browsers/playlistbrowser/PlaylistBrowserModel.cpp browsers/playlistbrowser/PlaylistBrowserCategory.cpp browsers/playlistbrowser/QtGroupingProxy.cpp browsers/playlistbrowser/PlaylistBrowser.cpp browsers/playlistbrowser/PlaylistBrowserView.cpp browsers/playlistbrowser/UserPlaylistCategory.cpp browsers/playlistbrowser/PlaylistsInFoldersProxy.cpp browsers/playlistbrowser/PlaylistsByProviderProxy.cpp browsers/playlistbrowser/PodcastModel.cpp browsers/playlistbrowser/PodcastCategory.cpp browsers/playlistbrowser/UserPlaylistModel.cpp ) ##################################################################### # PLAYLISTMANAGER ##################################################################### set(libplaylistmanager_SRCS playlistmanager/PlaylistManager.cpp playlistmanager/file/PlaylistFileProvider.cpp playlistmanager/file/KConfigSyncRelStore.cpp playlistmanager/sql/SqlUserPlaylistProvider.cpp playlistmanager/sql/SqlPlaylist.cpp playlistmanager/sql/SqlPlaylistGroup.cpp playlistmanager/SyncedPlaylist.cpp playlistmanager/SyncedPodcast.cpp playlistmanager/SyncRelationStorage.cpp ) ##################################################################### # PLAYLIST ##################################################################### set(libplaylist_SRCS playlist/PlaylistActions.cpp playlist/PlaylistBreadcrumbItem.cpp playlist/PlaylistBreadcrumbItemSortButton.cpp playlist/PlaylistBreadcrumbLevel.cpp playlist/PlaylistDefines.cpp playlist/PlaylistController.cpp playlist/PlaylistInfoWidget.cpp playlist/PlaylistItem.cpp playlist/PlaylistModel.cpp playlist/PlaylistModelStack.cpp playlist/PlaylistRestorer.cpp playlist/PlaylistQueueEditor.cpp playlist/PlaylistSortWidget.cpp playlist/PlaylistViewUrlGenerator.cpp playlist/PlaylistViewUrlRunner.cpp playlist/PlaylistDock.cpp playlist/PlaylistToolBar.cpp playlist/ProgressiveSearchWidget.cpp playlist/UndoCommands.cpp playlist/layouts/LayoutEditDialog.cpp playlist/layouts/LayoutEditWidget.cpp playlist/layouts/LayoutConfigAction.cpp playlist/layouts/LayoutItemConfig.cpp playlist/layouts/LayoutManager.cpp playlist/layouts/PlaylistLayoutEditDialog.cpp playlist/navigators/AlbumNavigator.cpp playlist/navigators/DynamicTrackNavigator.cpp playlist/navigators/FavoredRandomTrackNavigator.cpp playlist/navigators/NavigatorConfigAction.cpp playlist/navigators/NonlinearTrackNavigator.cpp playlist/navigators/RandomAlbumNavigator.cpp playlist/navigators/RandomTrackNavigator.cpp playlist/navigators/RepeatAlbumNavigator.cpp playlist/navigators/RepeatTrackNavigator.cpp playlist/navigators/StandardTrackNavigator.cpp playlist/navigators/TrackNavigator.cpp playlist/view/PlaylistViewCommon.cpp playlist/view/listview/InlineEditorWidget.cpp playlist/view/listview/PrettyItemDelegate.cpp playlist/view/listview/PrettyListView.cpp playlist/view/listview/SourceSelectionPopup.cpp playlist/proxymodels/GroupingProxy.cpp playlist/proxymodels/ProxyBase.cpp playlist/proxymodels/SortAlgorithms.cpp playlist/proxymodels/SortFilterProxy.cpp playlist/proxymodels/SortScheme.cpp playlist/proxymodels/SearchProxy.cpp ) ki18n_wrap_ui(libplaylist_SRCS playlist/PlaylistQueueEditor.ui ) ##################################################################### # DYNAMIC ##################################################################### set(libdynamic_SRCS dynamic/TrackSet.cpp dynamic/BiasFactory.cpp dynamic/BiasedPlaylist.cpp dynamic/BiasSolver.cpp dynamic/DynamicPlaylist.cpp dynamic/DynamicModel.cpp # biases dynamic/Bias.cpp dynamic/biases/AlbumPlayBias.cpp dynamic/biases/EchoNestBias.cpp dynamic/biases/IfElseBias.cpp dynamic/biases/PartBias.cpp dynamic/biases/QuizPlayBias.cpp dynamic/biases/TagMatchBias.cpp dynamic/biases/SearchQueryBias.cpp ) ##################################################################### # DBUS ##################################################################### set(dbus_SRCS dbus/mpris1/RootHandler.cpp dbus/mpris1/PlayerHandler.cpp dbus/mpris1/TrackListHandler.cpp dbus/mpris2/DBusAbstractAdaptor.cpp dbus/mpris2/Mpris2.cpp dbus/mpris2/MediaPlayer2.cpp dbus/mpris2/MediaPlayer2Player.cpp dbus/mpris2/MediaPlayer2AmarokExtensions.cpp dbus/mpris2/DBusAmarokApp.cpp dbus/CollectionDBusHandler.cpp dbus/DBusQueryHelper.cpp ) ##################################################################### # SCRIPTING INTERFACE ##################################################################### set(scriptengine_SRCS scripting/scriptengine/AmarokBookmarkScript.cpp scripting/scriptengine/AmarokCollectionScript.cpp scripting/scriptengine/AmarokCollectionViewScript.cpp scripting/scriptengine/AmarokEngineScript.cpp scripting/scriptengine/AmarokEqualizerScript.cpp scripting/scriptengine/AmarokInfoScript.cpp scripting/scriptengine/AmarokKNotifyScript.cpp scripting/scriptengine/AmarokLyricsScript.cpp scripting/scriptengine/AmarokNetworkScript.cpp scripting/scriptengine/AmarokOSDScript.cpp scripting/scriptengine/AmarokPlaylistManagerScript.cpp scripting/scriptengine/AmarokPlaylistScript.cpp scripting/scriptengine/AmarokScript.cpp scripting/scriptengine/AmarokScriptConfig.cpp scripting/scriptengine/AmarokScriptableServiceScript.cpp scripting/scriptengine/AmarokServicePluginManagerScript.cpp scripting/scriptengine/AmarokStatusbarScript.cpp scripting/scriptengine/AmarokStreamItemScript.cpp scripting/scriptengine/AmarokWindowScript.cpp scripting/scriptengine/AmarokScriptXml.cpp scripting/scriptengine/ScriptImporter.cpp scripting/scriptengine/ScriptingDefines.cpp scripting/scriptengine/exporters/CollectionTypeExporter.cpp scripting/scriptengine/exporters/MetaTypeExporter.cpp scripting/scriptengine/exporters/PlaylistExporter.cpp scripting/scriptengine/exporters/PlaylistProviderExporter.cpp scripting/scriptengine/exporters/QueryMakerExporter.cpp scripting/scriptengine/exporters/ScriptableBiasExporter.cpp ) set(scriptconsole_SRCS scripting/scriptconsole/CompletionModel.cpp scripting/scriptconsole/ScriptConsole.cpp scripting/scriptconsole/ScriptEditorDocument.cpp scripting/scriptconsole/ScriptConsoleItem.cpp ) if (PYTHONINTERP_FOUND) execute_process(COMMAND "${PYTHON_EXECUTABLE}" ${CMAKE_SOURCE_DIR}/src/scripting/scriptengine/PHAACG2.py ${CMAKE_SOURCE_DIR}/src/scripting/scriptengine ${CMAKE_BINARY_DIR}/scriptconsole) install(FILES ${CMAKE_BINARY_DIR}/scriptconsole/AutoComplete.txt DESTINATION ${DATA_INSTALL_DIR}/amarok/scriptconsole) endif() ##################################################################### # PLAYLIST GENERATOR ##################################################################### set(apg_SRCS playlistgenerator/Constraint.cpp playlistgenerator/ConstraintGroup.cpp playlistgenerator/ConstraintFactory.cpp playlistgenerator/ConstraintNode.cpp playlistgenerator/ConstraintSolver.cpp playlistgenerator/Preset.cpp playlistgenerator/PresetEditDialog.cpp playlistgenerator/PresetModel.cpp playlistgenerator/TreeController.cpp playlistgenerator/TreeModel.cpp playlistgenerator/constraints/Checkpoint.cpp playlistgenerator/constraints/Matching.cpp playlistgenerator/constraints/PlaylistDuration.cpp playlistgenerator/constraints/PlaylistFileSize.cpp playlistgenerator/constraints/PlaylistLength.cpp playlistgenerator/constraints/PreventDuplicates.cpp playlistgenerator/constraints/TagMatch.cpp playlistgenerator/constraints/TagMatchSupport.cpp playlistgenerator/constraints/TrackSpreader.cpp ) ki18n_wrap_ui(apg_SRCS playlistgenerator/ConstraintGroupEditWidget.ui playlistgenerator/PresetEditDialog.ui playlistgenerator/constraints/CheckpointEditWidget.ui playlistgenerator/constraints/PlaylistDurationEditWidget.ui playlistgenerator/constraints/PlaylistFileSizeEditWidget.ui playlistgenerator/constraints/PlaylistLengthEditWidget.ui playlistgenerator/constraints/PreventDuplicatesEditWidget.ui playlistgenerator/constraints/TagMatchEditWidget.ui ) ##################################################################### # NETWORK ACCESS ##################################################################### set(network_access_SRCS network/NetworkAccessManagerProxy.cpp ) if( CMAKE_BUILD_TYPE_TOLOWER MATCHES debug ) set(network_access_SRCS ${network_access_SRCS} network/NetworkAccessViewer.cpp ) ki18n_wrap_ui(network_access_SRCS network/NetworkRequests.ui ) endif() ##################################################################### # STATISTICS SYNCHRONIZATION ##################################################################### set( statsyncing_SRCS statsyncing/Config.cpp statsyncing/Controller.cpp statsyncing/Options.cpp statsyncing/Process.cpp statsyncing/Provider.cpp statsyncing/ProviderFactory.cpp statsyncing/ScrobblingService.cpp statsyncing/SimpleTrack.cpp statsyncing/SimpleWritableTrack.cpp statsyncing/Track.cpp statsyncing/TrackTuple.cpp statsyncing/collection/CollectionProvider.cpp statsyncing/collection/CollectionTrack.cpp statsyncing/jobs/MatchTracksJob.cpp statsyncing/jobs/SynchronizeTracksJob.cpp statsyncing/models/CommonModel.cpp statsyncing/models/MatchedTracksModel.cpp statsyncing/models/ProvidersModel.cpp statsyncing/models/SingleTracksModel.cpp statsyncing/ui/ChooseProvidersPage.cpp statsyncing/ui/CreateProviderDialog.cpp statsyncing/ui/ConfigureProviderDialog.cpp statsyncing/ui/MatchedTracksPage.cpp statsyncing/ui/TrackDelegate.cpp ) ki18n_wrap_ui( statsyncing_SRCS statsyncing/ui/ChooseProvidersPage.ui statsyncing/ui/MatchedTracksPage.ui ) ##################################################################### # STATISTICS IMPORTERS ##################################################################### set( importers_SRCS importers/ImporterManager.cpp importers/ImporterProvider.cpp importers/ImporterSqlConnection.cpp importers/SimpleImporterConfigWidget.cpp ) ##################################################################### # LIBAMAROK ##################################################################### set(amaroklib_LIB_SRCS ${libscriptableservice_SRCS} ${libbrowserframework_SRCS} ${libcontextview_SRCS} ${libcollectionbrowser_SRCS} ${libconfigdialog_SRCS} ${libplaylist_SRCS} ${aggregatecollection_SRCS} ${libpodcasts_SRCS} ${libmediadeviceframework_SRCS} ${libserviceframework_SRCS} ${libservicebrowser_SRCS} ${libdynamic_SRCS} ${libmetaimpl_SRCS} ${apg_SRCS} ${collection_SRCS} ${storage_SRCS} ${scanner_SRCS} ${mac_SRCS} ${network_access_SRCS} ${libplaylistbrowser_SRCS} ${libplaylistmanager_SRCS} ${dbus_SRCS} ${scriptengine_SRCS} ${scriptconsole_SRCS} ${libstatusbar_SRCS} ${libamarokurl_SRCS} ${libsynchronization_SRCS} ${statsyncing_SRCS} # ${importers_SRCS} #FIXME: disabled temporarily for KF5 porting: Port this to KF5::Plasma after the rest of the code has been ported to the other KF5 components. core-impl/logger/ProxyLogger.cpp aboutdialog/AnimatedBarWidget.cpp aboutdialog/AnimatedWidget.cpp aboutdialog/ExtendedAboutDialog.cpp aboutdialog/FramedLabel.cpp aboutdialog/OcsData.cpp aboutdialog/OcsPersonItem.cpp aboutdialog/OcsPersonListWidget.cpp ActionClasses.cpp AmarokMimeData.cpp AmarokProcess.cpp App.cpp CaseConverter.cpp EngineController.cpp KNotificationBackend.cpp MainWindow.cpp MediaDeviceCache.cpp MediaDeviceMonitor.cpp PluginManager.cpp QStringx.cpp scripting/scriptmanager/ScriptManager.cpp scripting/scriptmanager/ScriptItem.cpp scripting/scriptmanager/ScriptUpdater.cpp SvgHandler.cpp SvgTinter.cpp TrayIcon.cpp core-impl/meta/timecode/TimecodeObserver.cpp core-impl/meta/timecode/TimecodeMeta.cpp core-impl/meta/timecode/TimecodeTrackProvider.cpp core-impl/support/TrackLoader.cpp covermanager/CoverCache.cpp covermanager/CoverFetcher.cpp covermanager/CoverFetchingActions.cpp covermanager/CoverFetchQueue.cpp covermanager/CoverFetchUnit.cpp covermanager/CoverFoundDialog.cpp covermanager/CoverManager.cpp covermanager/CoverViewDialog.cpp databaseimporter/SqlBatchImporter.cpp databaseimporter/SqlBatchImporterConfig.cpp dialogs/CollectionSetup.cpp dialogs/DatabaseImporterDialog.cpp dialogs/DiagnosticDialog.cpp dialogs/EditFilterDialog.cpp dialogs/EqualizerDialog.cpp dialogs/MusicBrainzTagger.cpp dialogs/OrganizeCollectionDialog.cpp dialogs/TrackOrganizer.cpp dialogs/TagDialog.cpp dialogs/TagGuesser.cpp dialogs/TagGuesserDialog.cpp dialogs/LabelListModel.cpp equalizer/EqualizerPresets.cpp browsers/filebrowser/DirPlaylistTrackFilterProxyModel.cpp browsers/filebrowser/FileBrowser.cpp browsers/filebrowser/FileView.cpp musicbrainz/MusicBrainzFinder.cpp musicbrainz/MusicBrainzTagsItem.cpp musicbrainz/MusicBrainzTagsModel.cpp musicbrainz/MusicBrainzTagsModelDelegate.cpp musicbrainz/MusicBrainzTagsView.cpp musicbrainz/MusicBrainzXmlParser.cpp OpmlOutline.cpp OpmlParser.cpp OpmlWriter.cpp PaletteHandler.cpp PopupDropperFactory.cpp playback/DelayedDoers.cpp playback/EqualizerController.cpp playback/Fadeouter.cpp playback/PowerManager.cpp statemanagement/ApplicationController.cpp statemanagement/DefaultApplicationController.cpp toolbar/CurrentTrackToolbar.cpp toolbar/SlimToolbar.cpp toolbar/VolumePopupButton.cpp toolbar/MainToolbar.cpp widgets/AlbumBreadcrumbWidget.cpp widgets/AmarokDockWidget.cpp widgets/AnimatedLabelStack.cpp widgets/BoxWidget.cpp widgets/BreadcrumbItemButton.cpp widgets/ClearSpinBox.cpp widgets/CoverLabel.cpp widgets/HintLineEdit.cpp widgets/kdatecombo.cpp widgets/TokenDropTarget.cpp widgets/EditDeleteComboBoxView.cpp widgets/EditDeleteDelegate.cpp widgets/ElidingButton.cpp widgets/FilenameLayoutWidget.cpp widgets/FlowLayout.cpp widgets/HorizontalDivider.cpp widgets/IconButton.cpp widgets/ComboBox.cpp widgets/LineEdit.cpp widgets/Osd.cpp widgets/TimeLabel.cpp widgets/PixmapViewer.cpp widgets/PlayPauseButton.cpp widgets/PrettyTreeView.cpp widgets/PrettyTreeDelegate.cpp widgets/ProgressWidget.cpp widgets/SearchWidget.cpp widgets/SliderWidget.cpp widgets/StarManager.cpp widgets/TokenPool.cpp widgets/Token.cpp widgets/TokenWithLayout.cpp widgets/VolumeDial.cpp widgets/TrackActionButton.cpp widgets/BookmarkTriangle.cpp widgets/BookmarkPopup.cpp widgets/TrackSelectWidget.cpp widgets/MetaQueryWidget.cpp GlobalCollectionActions.cpp GlobalCurrentTrackActions.cpp moodbar/MoodbarManager.cpp ) if( LIBMYGPO_QT_FOUND ) set( EXTRA_LIBS ${LIBMYGPO_QT_LIBRARIES} ) include_directories( ${LIBMYGPO_QT_INCLUDE_DIRS} ${LIBMYGPO_QT_INCLUDE_DIRS}/../ ) endif() if( LIBLASTFM_FOUND ) set(amaroklib_LIB_SRCS ${amaroklib_LIB_SRCS} LastfmReadLabelCapability.cpp ) include_directories( ${LIBLASTFM_INCLUDE_DIR}/.. ${LIBLASTFM_INCLUDE_DIR}) set( EXTRA_LIBS ${LIBLASTFM_LIBRARY} ) endif() if( LIBOFA_FOUND AND AVCODEC_FOUND AND AVFORMAT_FOUND AND AVUTIL_FOUND ) add_definitions( ${AVCODEC_DEFINITIONS} ${AVFORMAT_DEFINITIONS} ${AVUTIL_DEFINITIONS} ) include_directories( ${AVCODEC_INCLUDE_DIRS} ${AVFORMAT_INCLUDE_DIRS} ${AVUTIL_INCLUDE_DIRS} ) set( EXTRA_LIBS ${EXTRA_LIBS} ${LIBOFA_LIBRARY} ${AVFORMAT_LIBRARIES} ${AVCODEC_LIBRARIES} ${AVUTIL_LIBRARIES} ) set( amaroklib_LIB_SRCS ${amaroklib_LIB_SRCS} musicbrainz/MusicDNSAudioDecoder.cpp musicbrainz/MusicDNSFinder.cpp musicbrainz/MusicDNSXmlParser.cpp ) endif() qt5_add_dbus_adaptor( AMAROK_MPRIS1_ADAPTOR_SRCS dbus/mpris1/org.freedesktop.MediaPlayer.root.xml dbus/mpris1/RootHandler.h Mpris1::RootHandler Mpris1RootAdaptor Mpris1RootAdaptor ) qt5_add_dbus_adaptor( AMAROK_MPRIS1_ADAPTOR_SRCS dbus/mpris1/org.freedesktop.MediaPlayer.player.xml dbus/mpris1/PlayerHandler.h Mpris1::PlayerHandler Mpris1PlayerAdaptor Mpris1PlayerAdaptor ) qt5_add_dbus_adaptor( AMAROK_MPRIS1_ADAPTOR_SRCS dbus/mpris1/org.freedesktop.MediaPlayer.tracklist.xml dbus/mpris1/TrackListHandler.h Mpris1::TrackListHandler Mpris1TrackListAdaptor Mpris1TrackListAdaptor ) qt5_add_dbus_adaptor( AMAROK_MPRIS1_ADAPTOR_SRCS dbus/org.kde.amarok.App.xml dbus/mpris1/RootHandler.h Mpris1::RootHandler Mpris1AmarokAppAdaptor Mpris1AmarokAppAdaptor ) qt5_add_dbus_adaptor( AMAROK_MPRIS1_ADAPTOR_SRCS dbus/mpris1/org.kde.amarok.Mpris1Extensions.Player.xml dbus/mpris1/PlayerHandler.h Mpris1::PlayerHandler Mpris1AmarokPlayerAdaptor Mpris1AmarokPlayerAdaptor ) qt5_add_dbus_adaptor( AMAROK_MPRIS1_ADAPTOR_SRCS dbus/org.kde.amarok.Collection.xml dbus/CollectionDBusHandler.h CollectionDBusHandler CollectionAdaptor CollectionAdaptor ) # suppress deprecated methods warnings if(NOT WIN32) set_source_files_properties(${AMAROK_MPRIS1_ADAPTOR_SRCS} PROPERTIES COMPILE_FLAGS -Wno-deprecated-declarations) endif() set( amaroklib_DEPENDS "amarokpud" ) set( amaroklib_DEPENDS "amarokcore" ) set( amaroklib_DEPENDS "amarok-transcoding" ) # depends on generated ui_*.h file kconfig_add_kcfg_files(amaroklib_LIB_SRCS amarokconfig.kcfgc) add_custom_target(amarokconfig_h DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/amarokconfig.h) ki18n_wrap_ui(amaroklib_LIB_SRCS aboutdialog/OcsPersonItem.ui dialogs/EditFilterDialog.ui dialogs/EqualizerDialog.ui dialogs/MusicBrainzTagger.ui dialogs/TagDialogBase.ui dialogs/TagGuessOptions.ui dialogs/OrganizeCollectionOptions.ui dialogs/OrganizeCollectionDialogBase.ui playlist/layouts/PlaylistLayoutEditDialog.ui core-impl/podcasts/sql/PodcastSettingsBase.ui core-impl/podcasts/sql/SqlPodcastProviderSettingsWidget.ui core-impl/podcasts/sql/PodcastFilenameLayoutConfigWidget.ui browsers/playlistbrowser/PodcastCategoryBase.ui ) add_library(amaroklib SHARED ${amaroklib_LIB_SRCS} ${AMAROK_MPRIS1_ADAPTOR_SRCS}) target_link_libraries(amaroklib Phonon::phonon4qt5 KF5::Archive KF5::CoreAddons + KF5::Declarative KF5::GlobalAccel KF5::GuiAddons KF5::I18n KF5::IconThemes KF5::KCMUtils KF5::KIOCore KF5::KIOFileWidgets KF5::KIOWidgets KF5::KIONTLM KF5::NewStuff KF5::Notifications KF5::NotifyConfig KF5::Package - KF5::Plasma - KF5::PlasmaQuick KF5::TextEditor KF5::ThreadWeaver KF5::WindowSystem ${QT_QTSCRIPTTOOLS_LIBRARY} Qt5::Gui + Qt5::Quick Qt5::QuickWidgets Qt5::Script Qt5::ScriptTools Qt5::Sql Qt5::Svg ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBS} amarokpud amarokcore amarokocsclient amarok-transcoding amarokshared ) include_directories(${TAGLIB_INCLUDES}) add_definitions(${TAGLIB_CFLAGS}) target_link_libraries(amaroklib ${TAGLIB_LIBRARIES}) if( TAGLIB-EXTRAS_FOUND ) include_directories(${TAGLIB-EXTRAS_INCLUDES}) add_definitions(${TAGLIB-EXTRAS_CFLAGS}) target_link_libraries(amaroklib ${TAGLIB-EXTRAS_LIBRARIES}) endif() if(WIN32) target_link_libraries(amaroklib Qt5::WebKitWidgets) endif() if(APPLE) target_link_libraries(amaroklib "/System/Library/Frameworks/Foundation.framework") set_target_properties(amaroklib PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") endif() set_target_properties(amaroklib PROPERTIES VERSION 1.0.0 SOVERSION 1 ) install(TARGETS amaroklib ${INSTALL_TARGETS_DEFAULT_ARGS} ) ##################################################################### # AMAROK ##################################################################### set( amarok_SRCS main.cpp ) file(GLOB ICONS_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../images/*-apps-amarok.png) ecm_add_app_icon(amarok_SRCS ICONS ${ICONS_SRCS}) find_package(X11) add_executable(amarok ${amarok_SRCS}) if(APPLE) set_target_properties(amarok PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") set(MACOSX_BUNDLE_BUNDLE_NAME "Amarok 2") set(MACOSX_BUNDLE_BUNDLE_VERSION "2.8.0-git") set(MACOSX_BUNDLE_COPYRIGHT "Amarok Team") set_target_properties(amarok PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${MAC_FILES_DIR}/Info.plist.template) set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/amarok.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) endif() target_link_libraries(amarok KF5::CoreAddons KF5::DBusAddons KF5::I18n amarokcore amaroklib ${X11_LIBRARIES} ) install(TARGETS amarok ${INSTALL_TARGETS_DEFAULT_ARGS}) ########### install files ############### install(PROGRAMS org.kde.amarok.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) install(PROGRAMS org.kde.amarok_containers.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) install(FILES org.kde.amarok.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install(FILES amarok-plugin.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR}) -# install(FILES amarok-contextapplet.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR}) +install(FILES amarok-contextapplet.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR}) install(FILES amarok_codecinstall.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR}) install(FILES amarok_append.desktop DESTINATION ${SERVICES_INSTALL_DIR}/ServiceMenus) install(FILES amarok-play-audiocd.desktop DESTINATION ${DATA_INSTALL_DIR}/solid/actions) install(FILES amarok.knsrc DESTINATION ${CONFIG_INSTALL_DIR}) # protocol handlers install(FILES amarokurls/amarok.protocol DESTINATION ${SERVICES_INSTALL_DIR}) install(FILES amarokitpc.protocol DESTINATION ${SERVICES_INSTALL_DIR}) #install(FILES amarokpcast.protocol DESTINATION ${SERVICES_INSTALL_DIR}) install(FILES amarokconfig.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) install(FILES dbus/mpris1/org.freedesktop.MediaPlayer.root.xml dbus/mpris1/org.freedesktop.MediaPlayer.player.xml dbus/mpris1/org.freedesktop.MediaPlayer.tracklist.xml dbus/org.kde.amarok.App.xml dbus/org.kde.amarok.Collection.xml dbus/mpris1/org.kde.amarok.Mpris1Extensions.Player.xml dbus/mpris2/org.kde.amarok.Mpris2Extensions.Player.xml DESTINATION ${DBUS_INTERFACES_INSTALL_DIR}) install(FILES services/InfoParserLoading.html browsers/hover_info_template.html DESTINATION ${DATA_INSTALL_DIR}/amarok/data) ecm_install_icons(ICONS DESTINATION ${ICON_INSTALL_DIR} THEME hicolor ) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index c2a0cd8562..36b5e3c8e4 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -1,1391 +1,1389 @@ /**************************************************************************************** * Copyright (c) 2002-2013 Mark Kretschmann * * Copyright (c) 2002 Max Howell * * Copyright (c) 2002 Gabor Lehel * * Copyright (c) 2002 Nikolaj Hald Nielsen * * Copyright (c) 2009 Artur Szymiec * * Copyright (c) 2010 Téo Mrnjavac * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "MainWindow" #include "MainWindow.h" #include "ActionClasses.h" #include "EngineController.h" //for actions in ctor #include "KNotificationBackend.h" #include "PaletteHandler.h" #include "PluginManager.h" #include "SvgHandler.h" #include "amarokconfig.h" #include "aboutdialog/ExtendedAboutDialog.h" #include "aboutdialog/OcsData.h" #include "amarokurls/AmarokUrlHandler.h" #include "amarokurls/BookmarkManager.h" #include "browsers/BrowserDock.h" #include "browsers/collectionbrowser/CollectionWidget.h" #include "browsers/filebrowser/FileBrowser.h" #include "browsers/playlistbrowser/PlaylistBrowser.h" #include "browsers/playlistbrowser/PodcastCategory.h" #include "browsers/servicebrowser/ServiceBrowser.h" -// #include "context/ContextDock.h" +#include "context/ContextDock.h" #include "core/meta/Statistics.h" #include "core/support/Amarok.h" #include "core/support/Components.h" #include "core/support/Debug.h" #include "core-impl/collections/support/CollectionManager.h" #include "covermanager/CoverManager.h" // for actions #include "dialogs/DiagnosticDialog.h" #include "dialogs/EqualizerDialog.h" #include "moodbar/MoodbarManager.h" #include "network/NetworkAccessManagerProxy.h" #ifdef DEBUG_BUILD_TYPE #include "network/NetworkAccessViewer.h" #endif // DEBUG_BUILD_TYPE #include "playlist/PlaylistActions.h" #include "playlist/PlaylistController.h" #include "playlist/PlaylistDock.h" #include "playlist/PlaylistModelStack.h" #include "playlist/ProgressiveSearchWidget.h" #include "playlist/layouts/LayoutConfigAction.h" #include "playlistmanager/PlaylistManager.h" #include "playlistmanager/file/PlaylistFileProvider.h" #include "services/scriptable/ScriptableService.h" #include "statsyncing/Controller.h" #include "toolbar/MainToolbar.h" #include "toolbar/SlimToolbar.h" #include "widgets/Osd.h" #include //m_actionCollection #include //qApp #include #include #include #include #include #include //openPlaylist() #include //slotAddStream() #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_WS_X11 #include #include #endif #ifdef Q_WS_MAC #include "mac/GrowlInterface.h" #ifdef HAVE_NOTIFICATION_CENTER #include "mac/MacSystemNotify.h" #endif #endif #define AMAROK_CAPTION I18N_NOOP( "Amarok" ) extern OcsData ocsData; QPointer MainWindow::s_instance; namespace The { MainWindow* mainWindow() { return MainWindow::s_instance.data(); } } MainWindow::MainWindow() : KMainWindow( 0 ) , m_showMenuBar( 0 ) , m_lastBrowser( 0 ) , m_waitingForCd( false ) { DEBUG_BLOCK setObjectName( "MainWindow" ); s_instance = this; #ifdef Q_WS_MAC (void)new GrowlInterface( qApp->applicationName() ); #ifdef HAVE_NOTIFICATION_CENTER (void)new OSXNotify( qApp->applicationName() ); #endif #endif PERF_LOG( "Instantiate Collection Manager" ) CollectionManager::instance(); PERF_LOG( "Started Collection Manager instance" ) /* The PluginManager needs to be loaded before the playlist model * (which gets started by "statusBar::connectPlaylist" below so that it can handle any * tracks in the saved playlist that are associated with services. Eg, if * the playlist has a Magnatune track in it when Amarok is closed, then the * Magnatune service needs to be initialized before the playlist is loaded * here. */ PERF_LOG( "Instantiate Plugin Manager" ) The::pluginManager(); PERF_LOG( "Started Plugin Manager instance" ) createActions(); PERF_LOG( "Created actions" ) The::paletteHandler()->setPalette( palette() ); setPlainCaption( i18n( AMAROK_CAPTION ) ); init(); // We could as well move the code from init() here, but meh.. getting a tad long //restore active category ( as well as filters and levels and whatnot.. ) const QString path = Amarok::config().readEntry( "Browser Path", QString() ); if( !path.isEmpty() ) m_browserDock->list()->navigate( path ); setAutoSaveSettings(); m_showMenuBar->setChecked(!menuBar()->isHidden()); // workaround for bug #171080 EngineController *engine = The::engineController(); connect( engine, &EngineController::stopped, this, &MainWindow::slotStopped ); connect( engine, &EngineController::paused, this, &MainWindow::slotPaused ); connect( engine, &EngineController::trackPlaying, this, &MainWindow::slotNewTrackPlaying ); connect( engine, &EngineController::trackMetadataChanged, this, &MainWindow::slotMetadataChanged ); } MainWindow::~MainWindow() { DEBUG_BLOCK //save currently active category Amarok::config().writeEntry( "Browser Path", m_browserDock->list()->path() ); #ifdef DEBUG_BUILD_TYPE delete m_networkViewer.data(); #endif // DEBUG_BUILD_TYPE delete The::svgHandler(); delete The::paletteHandler(); } ///////// public interface /** * This function will initialize the main window. */ void MainWindow::init() { layout()->setContentsMargins( 0, 0, 0, 0 ); layout()->setSpacing( 0 ); //create main toolbar m_mainToolbar = new MainToolbar( this ); m_mainToolbar.data()->setAllowedAreas( Qt::TopToolBarArea | Qt::BottomToolBarArea ); m_mainToolbar.data()->setMovable ( true ); addToolBar( Qt::TopToolBarArea, m_mainToolbar.data() ); //create slim toolbar m_slimToolbar = new SlimToolbar( this ); m_slimToolbar.data()->setAllowedAreas( Qt::TopToolBarArea | Qt::BottomToolBarArea ); m_slimToolbar.data()->setMovable ( true ); addToolBar( Qt::TopToolBarArea, m_slimToolbar.data() ); m_slimToolbar->hide(); //BEGIN Creating Widgets PERF_LOG( "Create sidebar" ) m_browserDock = new BrowserDock( this ); m_browserDock->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Ignored ); m_browserDock->installEventFilter( this ); PERF_LOG( "Sidebar created" ) PERF_LOG( "Create Playlist" ) m_playlistDock = new Playlist::Dock( this ); m_playlistDock->installEventFilter( this ); //HACK, need to connect after because of order in MainWindow() connect( Amarok::actionCollection()->action( "playlist_edit_queue" ), &QAction::triggered, m_playlistDock.data(), &Playlist::Dock::slotEditQueue ); PERF_LOG( "Playlist created" ) -// PERF_LOG( "Creating ContextWidget" ) -// m_contextDock = new ContextDock( this ); -// m_contextDock->installEventFilter( this ); -// PERF_LOG( "ContextScene created" ) + PERF_LOG( "Creating ContextWidget" ) + m_contextDock = new ContextDock( this ); + m_contextDock->installEventFilter( this ); + PERF_LOG( "ContextScene created" ) //END Creating Widgets createMenus(); setDockOptions( QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks | QMainWindow::AnimatedDocks | QMainWindow::VerticalTabs ); addDockWidget( Qt::LeftDockWidgetArea, m_browserDock.data() ); -// addDockWidget( Qt::LeftDockWidgetArea, m_contextDock.data(), Qt::Horizontal ); + addDockWidget( Qt::LeftDockWidgetArea, m_contextDock.data(), Qt::Horizontal ); addDockWidget( Qt::LeftDockWidgetArea, m_playlistDock.data(), Qt::Horizontal ); setLayoutLocked( AmarokConfig::lockLayout() ); // { Debug::Block block( "Creating browsers. Please report long start times!" ); //TODO: parent these browsers? PERF_LOG( "Creating CollectionWidget" ) m_collectionBrowser = new CollectionWidget( "collections", 0 ); //TODO: rename "Music Collections m_collectionBrowser->setPrettyName( i18n( "Local Music" ) ); m_collectionBrowser->setIcon( QIcon::fromTheme( "drive-harddisk" ) ); m_collectionBrowser->setShortDescription( i18n( "Local sources of content" ) ); m_browserDock->list()->addCategory( m_collectionBrowser ); PERF_LOG( "Created CollectionWidget" ) PERF_LOG( "Creating ServiceBrowser" ) ServiceBrowser *serviceBrowser = ServiceBrowser::instance(); serviceBrowser->setParent( 0 ); serviceBrowser->setPrettyName( i18n( "Internet" ) ); serviceBrowser->setIcon( QIcon::fromTheme( "applications-internet" ) ); serviceBrowser->setShortDescription( i18n( "Online sources of content" ) ); m_browserDock->list()->addCategory( serviceBrowser ); PERF_LOG( "Created ServiceBrowser" ) PERF_LOG( "Creating PlaylistBrowser" ) m_playlistBrowser = new PlaylistBrowserNS::PlaylistBrowser( "playlists", 0 ); m_playlistBrowser->setPrettyName( i18n("Playlists") ); m_playlistBrowser->setIcon( QIcon::fromTheme( "view-media-playlist-amarok" ) ); m_playlistBrowser->setShortDescription( i18n( "Various types of playlists" ) ); m_browserDock->list()->addCategory( m_playlistBrowser ); PERF_LOG( "CreatedPlaylsitBrowser" ) PERF_LOG( "Creating FileBrowser" ) FileBrowser *fileBrowser = new FileBrowser( "files", 0 ); fileBrowser->setPrettyName( i18n("Files") ); fileBrowser->setIcon( QIcon::fromTheme( "folder-amarok" ) ); fileBrowser->setShortDescription( i18n( "Browse local hard drive for content" ) ); m_browserDock->list()->addCategory( fileBrowser ); PERF_LOG( "Created FileBrowser" ) serviceBrowser->setScriptableServiceManager( The::scriptableServiceManager() ); PERF_LOG( "ScriptableServiceManager done" ) PERF_LOG( "Creating Podcast Category" ) m_browserDock->list()->addCategory( The::podcastCategory() ); PERF_LOG( "Created Podcast Category" ) // If Amarok is started for the first time, set initial dock widget sizes if( !Amarok::config( "MainWindow" ).hasKey( "State" ) ) QTimer::singleShot( 0, this, &MainWindow::setDefaultDockSizes ); PERF_LOG( "finished MainWindow::init" ) } The::amarokUrlHandler(); //Instantiate The::coverFetcher(); //Instantiate // we must filter ourself to get mouseevents on the "splitter" - what is us, but filtered by the layouter installEventFilter( this ); } QMenu* MainWindow::createPopupMenu() { QMenu* menu = new QMenu( this ); // Show/hide menu bar if (!menuBar()->isVisible()) menu->addAction( m_showMenuBar ); menu->addSeparator(); addViewMenuItems(menu); return menu; } void MainWindow::addViewMenuItems(QMenu* menu) { menu->setTitle( i18nc("@item:inmenu", "&View" ) ); // Layout locking: QAction* lockAction = new QAction( i18n( "Lock Layout" ), this ); lockAction->setCheckable( true ); lockAction->setChecked( AmarokConfig::lockLayout() ); connect( lockAction, &QAction::toggled, this, &MainWindow::setLayoutLocked ); menu->addAction( lockAction ); menu->addSeparator(); // Dock widgets: QList dockwidgets = findChildren(); foreach( QDockWidget* dockWidget, dockwidgets ) { if( dockWidget->parentWidget() == this ) menu->addAction( dockWidget->toggleViewAction() ); } menu->addSeparator(); // Toolbars: QList toolbars = findChildren(); QActionGroup* toolBarGroup = new QActionGroup( this ); toolBarGroup->setExclusive( true ); foreach( QToolBar* toolBar, toolbars ) { if( toolBar->parentWidget() == this ) { QAction* action = toolBar->toggleViewAction(); connect( action, &QAction::toggled, toolBar, &QToolBar::setVisible ); toolBarGroup->addAction( action ); menu->addAction( action ); } } menu->addSeparator(); QAction *resetAction = new QAction( i18n( "Reset Layout" ), this ); connect( resetAction, &QAction::triggered, this, &MainWindow::resetLayout ); menu->addAction( resetAction ); } void MainWindow::showBrowser( const QString &name ) { Q_UNUSED( name ); // showBrowser( index ); // FIXME } void MainWindow::showDock( AmarokDockId dockId ) { QString name; switch( dockId ) { case AmarokDockNavigation: name = m_browserDock->windowTitle(); break; case AmarokDockContext: -// name = m_contextDock->windowTitle(); + name = m_contextDock->windowTitle(); break; case AmarokDockPlaylist: name = m_playlistDock->windowTitle(); break; } QList < QTabBar * > tabList = findChildren < QTabBar * > (); foreach( QTabBar *bar, tabList ) { for( int i = 0; i < bar->count(); i++ ) { if( bar->tabText( i ) == name ) { bar->setCurrentIndex( i ); break; } } } } void MainWindow::closeEvent( QCloseEvent *e ) { #ifdef Q_WS_MAC Q_UNUSED( e ); hide(); #else //KDE policy states we should hide to tray and not quit() when the //close window button is pushed for the main widget if( AmarokConfig::showTrayIcon() && e->spontaneous() && !qApp->isSavingSession() ) { KMessageBox::information( this, i18n( "Closing the main window will keep Amarok running in the System Tray. " "Use Quit from the menu, or the Amarok tray icon to exit the application." ), i18n( "Docking in System Tray" ), "hideOnCloseInfo" ); hide(); e->ignore(); return; } e->accept(); pApp->quit(); #endif } void MainWindow::exportPlaylist() //SLOT { DEBUG_BLOCK QFileDialog fileDialog; fileDialog.restoreState( Amarok::config( "playlist-export-dialog" ).readEntry( "state", QByteArray() ) ); // FIXME: Make checkbox visible in dialog QCheckBox *saveRelativeCheck = new QCheckBox( i18n("Use relative path for &saving"), &fileDialog ); saveRelativeCheck->setChecked( AmarokConfig::relativePlaylist() ); QStringList supportedMimeTypes; supportedMimeTypes << "video/x-ms-asf"; //ASX supportedMimeTypes << "audio/x-mpegurl"; //M3U supportedMimeTypes << "audio/x-scpls"; //PLS supportedMimeTypes << "application/xspf+xml"; //XSPF fileDialog.setMimeTypeFilters( supportedMimeTypes ); fileDialog.setAcceptMode( QFileDialog::AcceptSave ); fileDialog.setFileMode( QFileDialog::AnyFile ); fileDialog.setWindowTitle( i18n("Save As") ); fileDialog.setObjectName( "PlaylistExport" ); int result = fileDialog.exec(); QString playlistPath = fileDialog.selectedFiles().value( 0 ); if( result == QDialog::Accepted && !playlistPath.isEmpty() ) The::playlist()->exportPlaylist( playlistPath, saveRelativeCheck->isChecked() ); Amarok::config( "playlist-export-dialog" ).writeEntry( "state", fileDialog.saveState() ); } void MainWindow::slotShowActiveTrack() const { m_playlistDock->showActiveTrack(); } void MainWindow::slotEditTrackInfo() const { m_playlistDock->editTrackInfo(); } void MainWindow::slotShowCoverManager() //SLOT { CoverManager::showOnce( QString(), this ); } void MainWindow::slotShowDiagnosticsDialog() { DiagnosticDialog *dialog = new DiagnosticDialog( KAboutData::applicationData(), this ); dialog->show(); } void MainWindow::slotShowBookmarkManager() { BookmarkManager::showOnce( this ); } void MainWindow::slotShowEqualizer() { EqualizerDialog::showOnce( this ); } void MainWindow::slotPlayMedia() //SLOT { // Request location and immediately start playback slotAddLocation( true ); } void MainWindow::slotAddLocation( bool directPlay ) //SLOT { static QUrl lastDirectory; // open a file selector to add media to the playlist QList files; QFileDialog dlg; dlg.setDirectory( QStandardPaths::writableLocation(QStandardPaths::MusicLocation) ); if( !lastDirectory.isEmpty() ) dlg.setDirectoryUrl( lastDirectory ); dlg.setWindowTitle( directPlay ? i18n("Play Media (Files or URLs)") : i18n("Add Media (Files or URLs)") ); dlg.setFileMode( QFileDialog::ExistingFiles ); dlg.setObjectName( "PlayMedia" ); int accepted = dlg.exec(); files = dlg.selectedUrls(); lastDirectory = dlg.directoryUrl(); if( accepted != QDialog::Accepted || files.isEmpty() ) return; Playlist::AddOptions options = directPlay ? Playlist::OnPlayMediaAction : Playlist::OnAppendToPlaylistAction; The::playlistController()->insertOptioned( files, options ); } void MainWindow::slotAddStream() //SLOT { bool ok; QString url = QInputDialog::getText( this, i18n( "Add Stream" ), i18n( "Enter Stream URL:" ), QLineEdit::Normal, QString(), &ok ); if( !ok ) return; The::playlistController()->insertOptioned( QUrl( url ), Playlist::OnAppendToPlaylistAction | Playlist::RemotePlaylistsAreStreams ); } void MainWindow::slotFocusPlaylistSearch() { showDock( AmarokDockPlaylist ); // ensure that the dock is visible if tabbed m_playlistDock->searchWidget()->focusInputLine(); } void MainWindow::slotFocusCollectionSearch() { // ensure collection browser is activated within navigation dock: browserDock()->list()->navigate( QString("collections") ); showDock( AmarokDockNavigation ); // ensure that the dock is visible if tabbed m_collectionBrowser->focusInputLine(); } #ifdef DEBUG_BUILD_TYPE void MainWindow::showNetworkRequestViewer() //SLOT { if( !m_networkViewer ) { m_networkViewer = new NetworkAccessViewer( this ); The::networkAccessManager()->setNetworkAccessViewer( m_networkViewer.data() ); } The::networkAccessManager()->networkAccessViewer()->show(); } #endif // DEBUG_BUILD_TYPE /** * "Toggle Main Window" global shortcut connects to this slot */ void MainWindow::showHide() //SLOT { const KWindowInfo info( winId(), 0, 0 ); const int currentDesktop = KWindowSystem::currentDesktop(); if( !isVisible() ) { setVisible( true ); } else { if( !isMinimized() ) { if( !isActiveWindow() ) // not minimised and without focus { KWindowSystem::setOnDesktop( winId(), currentDesktop ); KWindowSystem::activateWindow( winId() ); } else // Amarok has focus { setVisible( false ); } } else // Amarok is minimised { setWindowState( windowState() & ~Qt::WindowMinimized ); KWindowSystem::setOnDesktop( winId(), currentDesktop ); KWindowSystem::activateWindow( winId() ); } } } void MainWindow::showNotificationPopup() // slot { if( Amarok::KNotificationBackend::instance()->isEnabled() && !Amarok::OSD::instance()->isEnabled() ) Amarok::KNotificationBackend::instance()->showCurrentTrack(); else Amarok::OSD::instance()->forceToggleOSD(); } void MainWindow::slotFullScreen() // slot { setWindowState( windowState() ^ Qt::WindowFullScreen ); } void MainWindow::slotLoveTrack() { emit loveTrack( The::engineController()->currentTrack() ); } void MainWindow::slotBanTrack() { emit banTrack( The::engineController()->currentTrack() ); } void MainWindow::slotShufflePlaylist() { m_playlistDock->sortWidget()->trimToLevel(); The::playlistActions()->shuffle(); } void MainWindow::slotSeekForwardShort() { EngineController* ec = The::engineController(); ec->seekBy( AmarokConfig::seekShort() * 1000 ); } void MainWindow::slotSeekForwardMedium() { EngineController* ec = The::engineController(); ec->seekBy( AmarokConfig::seekMedium() * 1000 ); } void MainWindow::slotSeekForwardLong() { EngineController* ec = The::engineController(); ec->seekBy( AmarokConfig::seekLong() * 1000 ); } void MainWindow::slotSeekBackwardShort() { EngineController* ec = The::engineController(); ec->seekBy( AmarokConfig::seekShort() * -1000 ); } void MainWindow::slotSeekBackwardMedium() { EngineController* ec = The::engineController(); ec->seekBy( AmarokConfig::seekMedium() * -1000 ); } void MainWindow::slotSeekBackwardLong() { EngineController* ec = The::engineController(); ec->seekBy( AmarokConfig::seekLong() * -1000 ); } void MainWindow::slotPutCurrentTrackToClipboard() { Meta::TrackPtr currentTrack = The::engineController()->currentTrack(); if ( currentTrack ) { QString text; Meta::ArtistPtr artist = currentTrack->artist(); if( artist ) text = artist->prettyName() + " - "; text += currentTrack->prettyName(); QClipboard *clipboard = QApplication::clipboard(); clipboard->setText( text ); } } void MainWindow::activate() { #ifdef Q_WS_X11 const KWindowInfo info = KWindowSystem::windowInfo( winId(), 0, 0 ); if( KWindowSystem::activeWindow() != winId() ) setVisible( true ); else if( !info.isMinimized() ) setVisible( true ); if( !isHidden() ) KWindowSystem::activateWindow( winId() ); #else setVisible( true ); #endif } void MainWindow::createActions() { KActionCollection* const ac = Amarok::actionCollection(); const EngineController* const ec = The::engineController(); const Playlist::Actions* const pa = The::playlistActions(); const Playlist::Controller* const pc = The::playlistController(); KStandardAction::keyBindings( pApp, &App::slotConfigShortcuts, ac ); KStandardAction::preferences( pApp, &App::slotConfigAmarokWithEmptyPage, ac ); m_showMenuBar = KStandardAction::showMenubar(this, &MainWindow::slotShowMenuBar, ac); ac->action( KStandardAction::name( KStandardAction::KeyBindings ) )->setIcon( QIcon::fromTheme( "configure-shortcuts-amarok" ) ); ac->action( KStandardAction::name( KStandardAction::Preferences ) )->setIcon( QIcon::fromTheme( "configure-amarok" ) ); ac->action( KStandardAction::name( KStandardAction::Preferences ) )->setMenuRole(QAction::PreferencesRole); // Define OS X Prefs menu here, removes need for ifdef later KStandardAction::quit( pApp, &App::quit, ac ); QAction *action = new QAction( QIcon::fromTheme( "document-open" ), i18n("&Add Media..."), this ); ac->addAction( "playlist_add", action ); connect( action, &QAction::triggered, this, &MainWindow::slotAddLocation ); KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_A ) ); action = new QAction( QIcon::fromTheme( "edit-clear-list" ), i18nc( "clear playlist", "&Clear Playlist" ), this ); connect( action, &QAction::triggered, pc, &Playlist::Controller::clear ); ac->addAction( "playlist_clear", action ); action = new QAction( QIcon::fromTheme( "format-list-ordered" ), i18nc( "edit play queue of playlist", "Edit &Queue" ), this ); //Qt::META+Qt::Key_Q is taken by Plasma as a global action->setShortcut( QKeySequence( Qt::META + Qt::Key_U ) ); ac->addAction( "playlist_edit_queue", action );; action = new QAction( i18nc( "Remove duplicate and dead (unplayable) tracks from the playlist", "Re&move Duplicates" ), this ); connect( action, &QAction::triggered, pc, &Playlist::Controller::removeDeadAndDuplicates ); ac->addAction( "playlist_remove_dead_and_duplicates", action ); action = new Playlist::LayoutConfigAction( this ); ac->addAction( "playlist_layout", action ); action = new QAction( QIcon::fromTheme( "document-open-remote" ), i18n("&Add Stream..."), this ); connect( action, &QAction::triggered, this, &MainWindow::slotAddStream ); ac->addAction( "stream_add", action ); action = new QAction( QIcon::fromTheme( "document-export-amarok" ), i18n("&Export Playlist As..."), this ); connect( action, &QAction::triggered, this, &MainWindow::exportPlaylist ); ac->addAction( "playlist_export", action ); action = new QAction( QIcon::fromTheme( "bookmark-new" ), i18n( "Bookmark Media Sources View" ), this ); ac->addAction( "bookmark_browser", action ); connect( action, &QAction::triggered, The::amarokUrlHandler(), &AmarokUrlHandler::bookmarkCurrentBrowserView ); action = new QAction( QIcon::fromTheme( "bookmarks-organize" ), i18n( "Bookmark Manager" ), this ); ac->addAction( "bookmark_manager", action ); connect( action, &QAction::triggered, this, &MainWindow::slotShowBookmarkManager ); action = new QAction( QIcon::fromTheme( "view-media-equalizer" ), i18n( "Equalizer" ), this ); ac->addAction( "equalizer_dialog", action ); connect( action, &QAction::triggered, this, &MainWindow::slotShowEqualizer ); action = new QAction( QIcon::fromTheme( "bookmark-new" ), i18n( "Bookmark Playlist Setup" ), this ); ac->addAction( "bookmark_playlistview", action ); connect( action, &QAction::triggered, The::amarokUrlHandler(), &AmarokUrlHandler::bookmarkCurrentPlaylistView ); action = new QAction( QIcon::fromTheme( "bookmark-new" ), i18n( "Bookmark Context Applets" ), this ); ac->addAction( "bookmark_contextview", action ); connect( action, &QAction::triggered, The::amarokUrlHandler(), &AmarokUrlHandler::bookmarkCurrentContextView ); action = new QAction( QIcon::fromTheme( "media-album-cover-manager-amarok" ), i18n( "Cover Manager" ), this ); connect( action, &QAction::triggered, this, &MainWindow::slotShowCoverManager ); ac->addAction( "cover_manager", action ); action = new QAction( QIcon::fromTheme("document-open"), i18n("Play Media..."), this ); ac->addAction( "playlist_playmedia", action ); action->setShortcut( Qt::CTRL + Qt::Key_O ); connect( action, &QAction::triggered, this, &MainWindow::slotPlayMedia ); action = new QAction( QIcon::fromTheme("media-track-edit-amarok"), i18n("Edit Details of Currently Selected Track"), this ); ac->addAction( "trackdetails_edit", action ); action->setShortcut( Qt::CTRL + Qt::Key_E ); connect( action, &QAction::triggered, this, &MainWindow::slotEditTrackInfo ); action = new QAction( QIcon::fromTheme( "media-seek-forward-amarok" ), i18n( "Seek Forward by %1 seconds", KFormat().formatDecimalDuration( AmarokConfig::seekShort() * 1000 ) ), this ); ac->addAction( "seek_forward_short", action ); action->setShortcut( Qt::CTRL + Qt::Key_Right ); connect( action, &QAction::triggered, this, &MainWindow::slotSeekForwardShort ); action = new QAction( QIcon::fromTheme( "media-seek-forward-amarok" ), i18n( "Seek Forward by %1 seconds", KFormat().formatDecimalDuration( AmarokConfig::seekMedium() * 1000 ) ), this ); ac->addAction( "seek_forward_medium", action ); action->setShortcut( Qt::Key_Right ); KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::SHIFT + Qt::Key_Plus ) ); connect( action, &QAction::triggered, this, &MainWindow::slotSeekForwardMedium ); action = new QAction( QIcon::fromTheme( "media-seek-forward-amarok" ), i18n( "Seek Forward by %1 seconds", KFormat().formatDecimalDuration( AmarokConfig::seekLong() * 1000 ) ), this ); ac->addAction( "seek_forward_long", action ); action->setShortcut( Qt::SHIFT + Qt::Key_Right ); connect( action, &QAction::triggered, this, &MainWindow::slotSeekForwardLong ); action = new QAction( QIcon::fromTheme( "media-seek-backward-amarok" ), i18n( "Seek Backward by %1 seconds", KFormat().formatDecimalDuration( AmarokConfig::seekShort() * 1000 ) ), this ); ac->addAction( "seek_backward_short", action ); action->setShortcut( Qt::CTRL + Qt::Key_Left ); connect( action, &QAction::triggered, this, &MainWindow::slotSeekBackwardShort ); action = new QAction( QIcon::fromTheme( "media-seek-backward-amarok" ), i18n( "Seek Backward by %1 seconds", KFormat().formatDecimalDuration( AmarokConfig::seekMedium() * 1000 ) ), this ); ac->addAction( "seek_backward_medium", action ); action->setShortcut( Qt::Key_Left ); KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::SHIFT + Qt::Key_Minus ) ); connect( action, &QAction::triggered, this, &MainWindow::slotSeekBackwardMedium ); action = new QAction( QIcon::fromTheme( "media-seek-backward-amarok" ), i18n( "Seek Backward by %1 seconds", KFormat().formatDecimalDuration( AmarokConfig::seekLong() * 1000 ) ), this ); ac->addAction( "seek_backward_long", action ); action->setShortcut( Qt::SHIFT + Qt::Key_Left ); connect( action, &QAction::triggered, this, &MainWindow::slotSeekBackwardLong ); PERF_LOG( "MainWindow::createActions 6" ) action = new QAction( QIcon::fromTheme("view-refresh"), i18n( "Update Collection" ), this ); connect ( action, &QAction::triggered, CollectionManager::instance(), &CollectionManager::checkCollectionChanges ); ac->addAction( "update_collection", action ); action = new QAction( QIcon::fromTheme( "amarok_playcount" ), i18n( "Synchronize Statistics..." ), this ); ac->addAction( "synchronize_statistics", action ); connect( action, &QAction::triggered, Amarok::Components::statSyncingController(), &StatSyncing::Controller::synchronize ); Amarok::Components::statSyncingController(); action = new QAction( this ); ac->addAction( "prev", action ); action->setIcon( QIcon::fromTheme("media-skip-backward-amarok") ); action->setText( i18n( "Previous Track" ) ); KGlobalAccel::setGlobalShortcut(action, QKeySequence() ); connect( action, &QAction::triggered, pa, &Playlist::Actions::back ); action = new QAction( this ); ac->addAction( "replay", action ); action->setIcon( QIcon::fromTheme("media-playback-start") ); action->setText( i18n( "Restart current track" ) ); KGlobalAccel::setGlobalShortcut(action, QKeySequence() ); connect( action, &QAction::triggered, ec, &EngineController::replay ); action = new QAction( this ); ac->addAction( "shuffle_playlist", action ); action->setIcon( QIcon::fromTheme("media-playlist-shuffle") ); action->setText( i18n( "Shuffle Playlist" ) ); action->setShortcut( Qt::CTRL + Qt::Key_H ); connect( action, &QAction::triggered, this, &MainWindow::slotShufflePlaylist ); action = new QAction( this ); ac->addAction( "repopulate", action ); action->setText( i18n( "Repopulate Playlist" ) ); action->setIcon( QIcon::fromTheme("view-refresh-amarok") ); connect( action, &QAction::triggered, pa, &Playlist::Actions::repopulateDynamicPlaylist ); action = new QAction( this ); ac->addAction( "disable_dynamic", action ); action->setText( i18n( "Disable Dynamic Playlist" ) ); action->setIcon( QIcon::fromTheme("edit-delete-amarok") ); //this is connected inside the dynamic playlist category action = new QAction( QIcon::fromTheme("media-skip-forward-amarok"), i18n( "Next Track" ), this ); ac->addAction( "next", action ); KGlobalAccel::setGlobalShortcut(action, QKeySequence() ); connect( action, &QAction::triggered, pa, &Playlist::Actions::next ); action = new QAction( i18n( "Increase Volume" ), this ); ac->addAction( "increaseVolume", action ); KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_Plus ) ); action->setShortcut( Qt::Key_Plus ); connect( action, &QAction::triggered, ec, &EngineController::increaseVolume ); action = new QAction( i18n( "Decrease Volume" ), this ); ac->addAction( "decreaseVolume", action ); KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_Minus ) ); action->setShortcut( Qt::Key_Minus ); connect( action, &QAction::triggered, ec, &EngineController::decreaseVolume ); action = new QAction( i18n( "Toggle Main Window" ), this ); ac->addAction( "toggleMainWindow", action ); KGlobalAccel::setGlobalShortcut(action, QKeySequence() ); connect( action, &QAction::triggered, this, &MainWindow::showHide ); action = new QAction( i18n( "Toggle Full Screen" ), this ); ac->addAction( "toggleFullScreen", action ); action->setShortcut( QKeySequence( Qt::CTRL + Qt::SHIFT + Qt::Key_F ) ); connect( action, &QAction::triggered, this, &MainWindow::slotFullScreen ); action = new QAction( i18n( "Search playlist" ), this ); ac->addAction( "searchPlaylist", action ); action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_J ) ); connect( action, &QAction::triggered, this, &MainWindow::slotFocusPlaylistSearch ); action = new QAction( i18n( "Search collection" ), this ); ac->addAction( "searchCollection", action ); action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_F ) ); connect( action, &QAction::triggered, this, &MainWindow::slotFocusCollectionSearch ); action = new QAction( QIcon::fromTheme( "music-amarok" ), i18n("Show active track"), this ); ac->addAction( "show_active_track", action ); connect( action, &QAction::triggered, this, &MainWindow::slotShowActiveTrack ); action = new QAction( i18n( "Show Notification Popup" ), this ); ac->addAction( "showNotificationPopup", action ); KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_O ) ); connect( action, &QAction::triggered, this, &MainWindow::showNotificationPopup ); action = new QAction( i18n( "Mute Volume" ), this ); ac->addAction( "mute", action ); KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_M ) ); connect( action, &QAction::triggered, ec, &EngineController::toggleMute ); action = new QAction( i18n( "Last.fm: Love Current Track" ), this ); ac->addAction( "loveTrack", action ); KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_L ) ); connect( action, &QAction::triggered, this, &MainWindow::slotLoveTrack ); action = new QAction( i18n( "Last.fm: Ban Current Track" ), this ); ac->addAction( "banTrack", action ); //KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_B ) ); connect( action, &QAction::triggered, this, &MainWindow::slotBanTrack ); action = new QAction( i18n( "Last.fm: Skip Current Track" ), this ); ac->addAction( "skipTrack", action ); KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_S ) ); connect( action, &QAction::triggered, this, &MainWindow::skipTrack ); action = new QAction( QIcon::fromTheme( "media-track-queue-amarok" ), i18n( "Queue Track" ), this ); ac->addAction( "queueTrack", action ); action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_D ) ); connect( action, &QAction::triggered, this, &MainWindow::switchQueueStateShortcut ); action = new QAction( i18n( "Put Artist - Title of the current track to the clipboard" ), this ); ac->addAction("artistTitleClipboard", action); action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_C ) ); connect( action, &QAction::triggered, this, &MainWindow::slotPutCurrentTrackToClipboard ); action = new QAction( i18n( "Rate Current Track: 1" ), this ); ac->addAction( "rate1", action ); KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_1 ) ); connect( action, &QAction::triggered, this, &MainWindow::setRating1 ); action = new QAction( i18n( "Rate Current Track: 2" ), this ); ac->addAction( "rate2", action ); KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_2 ) ); connect( action, &QAction::triggered, this, &MainWindow::setRating2 ); action = new QAction( i18n( "Rate Current Track: 3" ), this ); ac->addAction( "rate3", action ); KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_3 ) ); connect( action, &QAction::triggered, this, &MainWindow::setRating3 ); action = new QAction( i18n( "Rate Current Track: 4" ), this ); ac->addAction( "rate4", action ); KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_4 ) ); connect( action, &QAction::triggered, this, &MainWindow::setRating4 ); action = new QAction( i18n( "Rate Current Track: 5" ), this ); ac->addAction( "rate5", action ); KGlobalAccel::setGlobalShortcut(action, QKeySequence( Qt::META + Qt::Key_5 ) ); connect( action, &QAction::triggered, this, &MainWindow::setRating5 ); #ifdef DEBUG_BUILD_TYPE action = new QAction( i18n( "Network Request Viewer" ), this ); ac->addAction( "network_request_viewer", action ); action->setIcon( QIcon::fromTheme( "utilities-system-monitor" ) ); connect( action, &QAction::triggered, this, &MainWindow::showNetworkRequestViewer ); #endif // DEBUG_BUILD_TYPE action = KStandardAction::redo( pc, &Playlist::Controller::redo, this); ac->addAction( "playlist_redo", action ); action->setEnabled( false ); action->setIcon( QIcon::fromTheme( "edit-redo" ) ); connect( pc, &Playlist::Controller::canRedoChanged, action, &QAction::setEnabled ); action = KStandardAction::undo( pc, &Playlist::Controller::undo, this); ac->addAction( "playlist_undo", action ); action->setEnabled( false ); action->setIcon( QIcon::fromTheme( "edit-undo" ) ); connect( pc, &Playlist::Controller::canUndoChanged, action, &QAction::setEnabled ); action = new QAction( QIcon::fromTheme( "amarok" ), i18n( "&About Amarok" ), this ); ac->addAction( "extendedAbout", action ); connect( action, &QAction::triggered, this, &MainWindow::showAbout ); action = new QAction ( QIcon::fromTheme( "info-amarok" ), i18n( "&Diagnostics" ), this ); ac->addAction( "diagnosticDialog", action ); connect( action, &QAction::triggered, this, &MainWindow::slotShowDiagnosticsDialog ); action = new QAction( QIcon::fromTheme( "tools-report-bug" ), i18n("&Report Bug..."), this ); ac->addAction( "reportBug", action ); connect( action, &QAction::triggered, this, &MainWindow::showReportBug ); PERF_LOG( "MainWindow::createActions 8" ) new Amarok::MenuAction( ac, this ); new Amarok::StopAction( ac, this ); new Amarok::StopPlayingAfterCurrentTrackAction( ac, this ); new Amarok::PlayPauseAction( ac, this ); new Amarok::ReplayGainModeAction( ac, this ); ac->addAssociatedWidget( this ); foreach( QAction* action, ac->actions() ) action->setShortcutContext( Qt::WindowShortcut ); } void MainWindow::setRating( int n ) { n *= 2; Meta::TrackPtr track = The::engineController()->currentTrack(); if( track ) { Meta::StatisticsPtr statistics = track->statistics(); // if we're setting an identical rating then we really must // want to set the half-star below rating if( statistics->rating() == n ) n -= 1; statistics->setRating( n ); Amarok::OSD::instance()->OSDWidget::ratingChanged( statistics->rating() ); } } void MainWindow::createMenus() { m_menubar = menuBar(); //BEGIN Actions menu QMenu *actionsMenu = new QMenu( m_menubar ); #ifdef Q_WS_MAC // Add these functions to the dock icon menu in OS X //extern void qt_mac_set_dock_menu(QMenu *); //qt_mac_set_dock_menu(actionsMenu); // Change to avoid duplicate menu titles in OS X actionsMenu->setTitle( i18n("&Music") ); #else actionsMenu->setTitle( i18n("&Amarok") ); #endif actionsMenu->addAction( Amarok::actionCollection()->action("playlist_playmedia") ); actionsMenu->addSeparator(); actionsMenu->addAction( Amarok::actionCollection()->action("prev") ); actionsMenu->addAction( Amarok::actionCollection()->action("play_pause") ); actionsMenu->addAction( Amarok::actionCollection()->action("stop") ); actionsMenu->addAction( Amarok::actionCollection()->action("stop_after_current") ); actionsMenu->addAction( Amarok::actionCollection()->action("next") ); #ifndef Q_WS_MAC // Avoid duplicate "Quit" in OS X dock menu actionsMenu->addSeparator(); actionsMenu->addAction( Amarok::actionCollection()->action( KStandardAction::name( KStandardAction::Quit ) ) ); #endif //END Actions menu //BEGIN View menu QMenu* viewMenu = new QMenu(this); addViewMenuItems(viewMenu); //END View menu //BEGIN Playlist menu QMenu *playlistMenu = new QMenu( m_menubar.data() ); playlistMenu->setTitle( i18n("&Playlist") ); playlistMenu->addAction( Amarok::actionCollection()->action("playlist_add") ); playlistMenu->addAction( Amarok::actionCollection()->action("stream_add") ); //playlistMenu->addAction( Amarok::actionCollection()->action("playlist_save") ); //FIXME: See FIXME in PlaylistDock.cpp playlistMenu->addAction( Amarok::actionCollection()->action( "playlist_export" ) ); playlistMenu->addSeparator(); playlistMenu->addAction( Amarok::actionCollection()->action("playlist_undo") ); playlistMenu->addAction( Amarok::actionCollection()->action("playlist_redo") ); playlistMenu->addSeparator(); playlistMenu->addAction( Amarok::actionCollection()->action("playlist_clear") ); playlistMenu->addAction( Amarok::actionCollection()->action("playlist_remove_dead_and_duplicates") ); playlistMenu->addAction( Amarok::actionCollection()->action("playlist_layout") ); playlistMenu->addAction( Amarok::actionCollection()->action("playlist_edit_queue") ); //END Playlist menu //BEGIN Tools menu m_toolsMenu = new QMenu( m_menubar.data() ); m_toolsMenu->setTitle( i18n("&Tools") ); m_toolsMenu->addAction( Amarok::actionCollection()->action("bookmark_manager") ); m_toolsMenu->addAction( Amarok::actionCollection()->action("cover_manager") ); m_toolsMenu->addAction( Amarok::actionCollection()->action("equalizer_dialog") ); #ifdef DEBUG_BUILD_TYPE m_toolsMenu->addAction( Amarok::actionCollection()->action("network_request_viewer") ); #endif // DEBUG_BUILD_TYPE m_toolsMenu->addSeparator(); m_toolsMenu->addAction( Amarok::actionCollection()->action("update_collection") ); m_toolsMenu->addAction( Amarok::actionCollection()->action("synchronize_statistics") ); //END Tools menu //BEGIN Settings menu m_settingsMenu = new QMenu( m_menubar.data() ); m_settingsMenu->setTitle( i18n("&Settings") ); m_settingsMenu->addAction( Amarok::actionCollection()->action( KStandardAction::name( KStandardAction::ShowMenubar ) ) ); //TODO use KStandardAction or KXmlGuiWindow // the phonon-coreaudio backend has major issues with either the VolumeFaderEffect itself // or with it in the pipeline. track playback stops every ~3-4 tracks, and on tracks >5min it // stops at about 5:40. while we get this resolved upstream, don't make playing amarok such on osx. // so we disable replaygain on osx #ifndef Q_WS_MAC m_settingsMenu->addAction( Amarok::actionCollection()->action("replay_gain_mode") ); m_settingsMenu->addSeparator(); #endif m_settingsMenu->addAction( Amarok::actionCollection()->action( KStandardAction::name( KStandardAction::KeyBindings ) ) ); m_settingsMenu->addAction( Amarok::actionCollection()->action( KStandardAction::name( KStandardAction::Preferences ) ) ); //END Settings menu m_menubar->addMenu( actionsMenu ); m_menubar->addMenu( viewMenu ); m_menubar->addMenu( playlistMenu ); m_menubar->addMenu( m_toolsMenu.data() ); m_menubar->addMenu( m_settingsMenu.data() ); QMenu *helpMenu = Amarok::Menu::helpMenu(); helpMenu->insertAction( helpMenu->actions().last(), Amarok::actionCollection()->action( "extendedAbout" ) ); helpMenu->insertAction( helpMenu->actions().last(), Amarok::actionCollection()->action( "diagnosticDialog" ) ); m_menubar->addSeparator(); m_menubar->addMenu( helpMenu ); } void MainWindow::slotShowMenuBar() { if (!m_showMenuBar->isChecked()) { //User have chosen to hide a menu. Lets warn him if (KMessageBox::warningContinueCancel(this, i18n("You have chosen to hide the menu bar.\n\nPlease remember that you can always use the shortcut \"%1\" to bring it back.", m_showMenuBar->shortcut().toString() ), i18n("Hide Menu"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "showMenubar") != KMessageBox::Continue) { //Cancel menu hiding. Revert menu item to checked state. m_showMenuBar->setChecked(true); return; } } menuBar()->setVisible(m_showMenuBar->isChecked()); } void MainWindow::showAbout() { ExtendedAboutDialog dialog( KAboutData::applicationData(), &ocsData ); dialog.exec(); } void MainWindow::showReportBug() { KBugReport * rbDialog = new KBugReport( KAboutData::applicationData() ,this ); rbDialog->setObjectName( "KBugReport" ); rbDialog->exec(); } void MainWindow::changeEvent( QEvent *event ) { if( event->type() == QEvent::PaletteChange ) The::paletteHandler()->setPalette( palette() ); } void MainWindow::slotStopped() { setPlainCaption( i18n( AMAROK_CAPTION ) ); } void MainWindow::slotPaused() { setPlainCaption( i18n( "Paused :: %1", i18n( AMAROK_CAPTION ) ) ); } void MainWindow::slotNewTrackPlaying() { slotMetadataChanged( The::engineController()->currentTrack() ); } void MainWindow::slotMetadataChanged( Meta::TrackPtr track ) { if( track ) setPlainCaption( i18n( "%1 - %2 :: %3", track->artist() ? track->artist()->prettyName() : i18n( "Unknown" ), track->prettyName(), i18n( AMAROK_CAPTION ) ) ); } CollectionWidget * MainWindow::collectionBrowser() { return m_collectionBrowser; } QString MainWindow::activeBrowserName() { if(m_browserDock->list()->activeCategory() ) return m_browserDock->list()->activeCategory()->name(); else return QString(); } void MainWindow::setLayoutLocked( bool locked ) { DEBUG_BLOCK if( m_browserDock ) m_browserDock.data()->setMovable( !locked ); -// if( m_contextDock ) -// m_contextDock.data()->setMovable( !locked ); + if( m_contextDock ) + m_contextDock.data()->setMovable( !locked ); if( m_playlistDock ) m_playlistDock.data()->setMovable( !locked ); if( m_slimToolbar ) { m_slimToolbar.data()->setFloatable( !locked ); m_slimToolbar.data()->setMovable( !locked ); } if( m_mainToolbar ) { m_mainToolbar.data()->setFloatable( !locked ); m_mainToolbar.data()->setMovable( !locked ); } AmarokConfig::setLockLayout( locked ); AmarokConfig::self()->save(); } void MainWindow::resetLayout() { // Store current state, so that we can undo the operation const QByteArray state = saveState(); // Remove all dock widgets, then add them again. This resets their state completely. removeDockWidget( m_browserDock.data() ); -// removeDockWidget( m_contextDock.data() ); + removeDockWidget( m_contextDock.data() ); removeDockWidget( m_playlistDock.data() ); addDockWidget( Qt::LeftDockWidgetArea, m_browserDock.data() ); -// addDockWidget( Qt::LeftDockWidgetArea, m_contextDock.data(), Qt::Horizontal ); + addDockWidget( Qt::LeftDockWidgetArea, m_contextDock.data(), Qt::Horizontal ); addDockWidget( Qt::LeftDockWidgetArea, m_playlistDock.data(), Qt::Horizontal ); m_browserDock->setFloating( false ); -// m_contextDock->setFloating( false ); + m_contextDock->setFloating( false ); m_playlistDock->setFloating( false ); m_browserDock->show(); -// m_contextDock->show(); + m_contextDock->show(); m_playlistDock->show(); // Now set Amarok's default dockwidget sizes setDefaultDockSizes(); if( KMessageBox::warningContinueCancel( this, i18n( "Apply this layout change?" ), i18n( "Reset Layout" ) ) == KMessageBox::Cancel ) restoreState( state ); } void MainWindow::setDefaultDockSizes() // SLOT { int totalWidgetWidth = contentsRect().width(); //get the width of the splitter handles, we need to subtract these... const int splitterHandleWidth = style()->pixelMetric( QStyle::PM_DockWidgetSeparatorExtent, 0, 0 ); totalWidgetWidth -= ( splitterHandleWidth * 2 ); const int widgetWidth = totalWidgetWidth / 3; const int leftover = totalWidgetWidth - 3 * widgetWidth; - #pragma message("PORTME KF5")/* //We need to set fixed widths initially, just until the main window has been properly laid out. As soon as this has //happened, we will unlock these sizes again so that the elements can be resized by the user. const int mins[3] = { m_browserDock->minimumWidth(), m_contextDock->minimumWidth(), m_playlistDock->minimumWidth() }; const int maxs[3] = { m_browserDock->maximumWidth(), m_contextDock->maximumWidth(), m_playlistDock->maximumWidth() }; m_browserDock->setFixedWidth( widgetWidth * 0.65 ); m_contextDock->setFixedWidth( widgetWidth * 1.7 + leftover ); m_playlistDock->setFixedWidth( widgetWidth * 0.65 ); // Important: We need to activate the layout we have just set layout()->activate(); m_browserDock->setMinimumWidth( mins[0] ); m_browserDock->setMaximumWidth( maxs[0] ); m_contextDock->setMinimumWidth( mins[1] ); m_contextDock->setMaximumWidth( maxs[1] ); m_playlistDock->setMinimumWidth( mins[2] ); m_playlistDock->setMaximumWidth( maxs[2] ); - */ } bool MainWindow::playAudioCd() { DEBUG_BLOCK //drop whatever we are doing and play auidocd QList collections = CollectionManager::instance()->viewableCollections(); // Search a non-empty MemoryCollection with the id: AudioCd foreach( Collections::Collection *collection, collections ) { if( collection->collectionId() == "AudioCd" ) { debug() << "got audiocd collection"; Collections::MemoryCollection * cdColl = dynamic_cast( collection ); if( !cdColl || cdColl->trackMap().count() == 0 ) { debug() << "cd collection not ready yet (track count = 0 )"; m_waitingForCd = true; return false; } The::playlistController()->insertOptioned( cdColl->trackMap().values(), Playlist::OnPlayMediaAction ); m_waitingForCd = false; return true; } } debug() << "waiting for cd..."; m_waitingForCd = true; return false; } bool MainWindow::isWaitingForCd() const { DEBUG_BLOCK debug() << "waiting?: " << m_waitingForCd; return m_waitingForCd; } bool MainWindow::isOnCurrentDesktop() const { #ifdef Q_WS_X11 return KWindowSystem::windowInfo( winId(), NET::WMDesktop ).desktop() == KWindowSystem::currentDesktop(); #else return true; #endif } diff --git a/src/SvgHandler.cpp b/src/SvgHandler.cpp index ccd84c4c98..5c48ec39ac 100644 --- a/src/SvgHandler.cpp +++ b/src/SvgHandler.cpp @@ -1,476 +1,523 @@ /**************************************************************************************** * Copyright (c) 2008 Nikolaj Hald Nielsen * * Copyright (c) 2008 Jeff Mitchell * * Copyright (c) 2009-2013 Mark Kretschmann * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "SvgHandler" #include "SvgHandler.h" #include "App.h" #include "EngineController.h" #include "MainWindow.h" #include "PaletteHandler.h" #include "SvgTinter.h" #include "core/meta/Meta.h" #include "core/support/Debug.h" #include "covermanager/CoverCache.h" #include "moodbar/MoodbarManager.h" #include #include #include #include #include #include #include #include #include #include namespace The { static SvgHandler* s_SvgHandler_instance = 0; SvgHandler* svgHandler() { if( !s_SvgHandler_instance ) s_SvgHandler_instance = new SvgHandler(); return s_SvgHandler_instance; } } SvgHandler::SvgHandler( QObject* parent ) : QObject( parent ) , m_cache( new KImageCache( "Amarok-pixmaps", 20 * 1024 ) ) , m_themeFile( "amarok/images/default-theme-clean.svg" ) // //use default theme , m_customTheme( false ) { DEBUG_BLOCK connect( The::paletteHandler(), &PaletteHandler::newPalette, this, &SvgHandler::discardCache ); } SvgHandler::~SvgHandler() { DEBUG_BLOCK delete m_cache; qDeleteAll( m_renderers ); m_renderers.clear(); The::s_SvgHandler_instance = 0; } -bool SvgHandler::loadSvg( const QString& name ) +bool SvgHandler::loadSvg( const QString& name, bool forceCustomTheme ) { - const QString &svgFilename = !m_customTheme ? QStandardPaths::locate( QStandardPaths::GenericDataLocation, name ) : name; + const QString &svgFilename = m_customTheme || forceCustomTheme ? name : QStandardPaths::locate( QStandardPaths::GenericDataLocation, name ); QSvgRenderer *renderer = new QSvgRenderer( The::svgTinter()->tint( svgFilename ) ); if ( !renderer->isValid() ) { debug() << "Bluddy 'ell mateys, aye canna' load ya Ess Vee Gee at " << svgFilename; delete renderer; return false; } QWriteLocker writeLocker( &m_lock ); if( m_renderers[name] ) delete m_renderers[name]; m_renderers[name] = renderer; return true; } QSvgRenderer* SvgHandler::getRenderer( const QString& name ) { QReadLocker readLocker( &m_lock ); if( ! m_renderers[name] ) { readLocker.unlock(); if( !loadSvg( name ) ) { QWriteLocker writeLocker( &m_lock ); m_renderers[name] = new QSvgRenderer(); } readLocker.relock(); } return m_renderers[name]; } QSvgRenderer * SvgHandler::getRenderer() { return getRenderer( m_themeFile ); } QPixmap SvgHandler::renderSvg( const QString &name, const QString& keyname, int width, int height, const QString& element, bool skipCache, const qreal opacity ) { QString key; if( !skipCache ) { key = QString("%1:%2x%3") .arg( keyname ) .arg( width ) .arg( height ); } QPixmap pixmap; if( skipCache || !m_cache->findPixmap( key, &pixmap ) ) { pixmap = QPixmap( width, height ); pixmap.fill( Qt::transparent ); QReadLocker readLocker( &m_lock ); if( ! m_renderers[name] ) { readLocker.unlock(); if( !loadSvg( name ) ) { return pixmap; } readLocker.relock(); } QPainter pt( &pixmap ); pt.setOpacity( opacity ); if ( element.isEmpty() ) m_renderers[name]->render( &pt, QRectF( 0, 0, width, height ) ); else m_renderers[name]->render( &pt, element, QRectF( 0, 0, width, height ) ); if( !skipCache ) m_cache->insertPixmap( key, pixmap ); } return pixmap; } +QPixmap SvgHandler::renderSvg( const QUrl& url, const QString& keyname, int width, int height, const QString& element, bool skipCache, const qreal opacity ) +{ + if( !url.isLocalFile() ) + return QPixmap(); + + QString key; + if( !skipCache ) + { + key = QString("%1:%2x%3") + .arg( keyname ) + .arg( width ) + .arg( height ); + } + + QPixmap pixmap; + if( skipCache || !m_cache->findPixmap( key, &pixmap ) ) + { + pixmap = QPixmap( width, height ); + pixmap.fill( Qt::transparent ); + + QString name = url.toLocalFile(); + QReadLocker readLocker( &m_lock ); + if( ! m_renderers[name] ) + { + readLocker.unlock(); + if( !loadSvg( name, true ) ) + { + return pixmap; + } + readLocker.relock(); + } + + QPainter pt( &pixmap ); + pt.setOpacity( opacity ); + + if ( element.isEmpty() ) + m_renderers[name]->render( &pt, QRectF( 0, 0, width, height ) ); + else + m_renderers[name]->render( &pt, element, QRectF( 0, 0, width, height ) ); + + if( !skipCache ) + m_cache->insertPixmap( key, pixmap ); + } + + return pixmap; +} + QPixmap SvgHandler::renderSvg(const QString & keyname, int width, int height, const QString & element, bool skipCache, const qreal opacity ) { return renderSvg( m_themeFile, keyname, width, height, element, skipCache, opacity ); } QPixmap SvgHandler::renderSvgWithDividers(const QString & keyname, int width, int height, const QString & element) { const QString key = QString("%1:%2x%3-div") .arg( keyname ) .arg( width ) .arg( height ); QPixmap pixmap; if ( !m_cache->findPixmap( key, &pixmap ) ) { // debug() << QString("svg %1 not in cache...").arg( key ); pixmap = QPixmap( width, height ); pixmap.fill( Qt::transparent ); QString name = m_themeFile; QReadLocker readLocker( &m_lock ); if( ! m_renderers[name] ) { readLocker.unlock(); if( ! loadSvg( name ) ) { return pixmap; } readLocker.relock(); } QPainter pt( &pixmap ); if ( element.isEmpty() ) m_renderers[name]->render( &pt, QRectF( 0, 0, width, height ) ); else m_renderers[name]->render( &pt, element, QRectF( 0, 0, width, height ) ); //add dividers. 5% spacing on each side int margin = width / 20; m_renderers[name]->render( &pt, "divider_top", QRectF( margin, 0 , width - 1 * margin, 1 ) ); m_renderers[name]->render( &pt, "divider_bottom", QRectF( margin, height - 1 , width - 2 * margin, 1 ) ); m_cache->insertPixmap( key, pixmap ); } return pixmap; } void SvgHandler::reTint() { The::svgTinter()->init(); if ( !loadSvg( m_themeFile )) warning() << "Unable to load theme file: " << m_themeFile; emit retinted(); } QString SvgHandler::themeFile() { return m_themeFile; } void SvgHandler::setThemeFile( const QString & themeFile ) { DEBUG_BLOCK debug() << "got new theme file: " << themeFile; m_themeFile = themeFile; m_customTheme = true; discardCache(); } void SvgHandler::discardCache() { //redraw entire app.... reTint(); m_cache->clear(); if( auto window = pApp->mainWindow() ) window->update(); } QPixmap SvgHandler::imageWithBorder( Meta::AlbumPtr album, int size, int borderWidth ) { const int imageSize = size - ( borderWidth * 2 ); const QString &loc = album->imageLocation( imageSize ).url(); const QString &key = !loc.isEmpty() ? loc : album->name(); return addBordersToPixmap( The::coverCache()->getCover( album, imageSize ), borderWidth, key ); } QPixmap SvgHandler::addBordersToPixmap( const QPixmap &orgPixmap, int borderWidth, const QString &name, bool skipCache ) { int newWidth = orgPixmap.width() + borderWidth * 2; int newHeight = orgPixmap.height() + borderWidth *2; QString key; if( !skipCache ) { key = QString("%1:%2x%3b%4") .arg( name ) .arg( newWidth ) .arg( newHeight ) .arg( borderWidth ); } QPixmap pixmap; if( skipCache || !m_cache->findPixmap( key, &pixmap ) ) { // Cache miss! We need to create the pixmap // if skipCache is true, we might actually already have fetched the image, including borders from the cache.... // so we really need to create a blank pixmap here as well, to not pollute the cached pixmap pixmap = QPixmap( newWidth, newHeight ); pixmap.fill( Qt::transparent ); QReadLocker readLocker( &m_lock ); if( !m_renderers[m_themeFile] ) { readLocker.unlock(); if( !loadSvg( m_themeFile ) ) { return pixmap; } readLocker.relock(); } QPainter pt( &pixmap ); pt.drawPixmap( borderWidth, borderWidth, orgPixmap.width(), orgPixmap.height(), orgPixmap ); m_renderers[m_themeFile]->render( &pt, "cover_border_topleft", QRectF( 0, 0, borderWidth, borderWidth ) ); m_renderers[m_themeFile]->render( &pt, "cover_border_top", QRectF( borderWidth, 0, orgPixmap.width(), borderWidth ) ); m_renderers[m_themeFile]->render( &pt, "cover_border_topright", QRectF( newWidth - borderWidth , 0, borderWidth, borderWidth ) ); m_renderers[m_themeFile]->render( &pt, "cover_border_right", QRectF( newWidth - borderWidth, borderWidth, borderWidth, orgPixmap.height() ) ); m_renderers[m_themeFile]->render( &pt, "cover_border_bottomright", QRectF( newWidth - borderWidth, newHeight - borderWidth, borderWidth, borderWidth ) ); m_renderers[m_themeFile]->render( &pt, "cover_border_bottom", QRectF( borderWidth, newHeight - borderWidth, orgPixmap.width(), borderWidth ) ); m_renderers[m_themeFile]->render( &pt, "cover_border_bottomleft", QRectF( 0, newHeight - borderWidth, borderWidth, borderWidth ) ); m_renderers[m_themeFile]->render( &pt, "cover_border_left", QRectF( 0, borderWidth, borderWidth, orgPixmap.height() ) ); if( !skipCache ) m_cache->insertPixmap( key, pixmap ); } return pixmap; } #if 0 void SvgHandler::paintCustomSlider( QPainter *p, int x, int y, int width, int height, qreal percentage, bool active ) { int knobSize = height - 4; int sliderRange = width - ( knobSize + 4 ); int knobRelPos = x + sliderRange * percentage + 2; int knobY = y + ( height - knobSize ) / 2 + 1; int sliderY = y + ( height / 2 ) - 1; //first draw the played part p->drawPixmap( x, sliderY, renderSvg( "new_slider_top_played", width, 2, "new_slider_top_played" ), 0, 0, knobRelPos - x, 2 ); //and then the unplayed part p->drawPixmap( knobRelPos + 1, sliderY, renderSvg( "new_slider_top", width, 2, "new_slider_top" ), knobRelPos + 1 - x, 0, -1, 2 ); //and then the bottom p->drawPixmap( x, sliderY + 2, renderSvg( "new_slider_bottom", width, 2, "new_slider_bottom" ) ); //draw end markers p->drawPixmap( x, y, renderSvg( "new_slider_end", 2, height, "new_slider_end" ) ); p->drawPixmap( x + width - 2, y, renderSvg( "new_slider_end", 2, height, "new_slider_endr" ) ); if ( active ) p->drawPixmap( knobRelPos, knobY, renderSvg( "new_slider_knob_active", knobSize, knobSize, "new_slider_knob_active" ) ); else p->drawPixmap( knobRelPos, knobY, renderSvg( "new_slider_knob", knobSize, knobSize, "new_slider_knob" ) ); } #endif QRect SvgHandler::sliderKnobRect( const QRect &slider, qreal percent, bool inverse ) const { if ( inverse ) percent = 1.0 - percent; const int knobSize = slider.height() - 4; QRect ret( 0, 0, knobSize, knobSize ); ret.moveTo( slider.x() + qRound( ( slider.width() - knobSize ) * percent ), slider.y() + 1 ); return ret; } // Experimental, using a mockup from Nuno Pinheiro (new_slider_nuno) void SvgHandler::paintCustomSlider( QPainter *p, QStyleOptionSlider *slider, qreal percentage, bool paintMoodbar ) { int sliderHeight = slider->rect.height() - 6; const bool inverse = ( slider->orientation == Qt::Vertical ) ? slider->upsideDown : ( (slider->direction == Qt::RightToLeft) != slider->upsideDown ); QRect knob = sliderKnobRect( slider->rect, percentage, inverse ); QPoint pt = slider->rect.topLeft() + QPoint( 0, 2 ); //debug() << "rel: " << knobRelPos << ", width: " << width << ", height:" << height << ", %: " << percentage; //if we should paint moodbar, paint this as the bottom layer bool moodbarPainted = false; if ( paintMoodbar ) { Meta::TrackPtr currentTrack = The::engineController()->currentTrack(); if ( currentTrack ) { if( The::moodbarManager()->hasMoodbar( currentTrack ) ) { QPixmap moodbar = The::moodbarManager()->getMoodbar( currentTrack, slider->rect.width() - sliderHeight, sliderHeight, inverse ); p->drawPixmap( pt, renderSvg( "moodbar_end_left", sliderHeight / 2, sliderHeight, "moodbar_end_left" ) ); pt.rx() += sliderHeight / 2; p->drawPixmap( pt, moodbar ); pt.rx() += slider->rect.width() - sliderHeight; p->drawPixmap( pt, renderSvg( "moodbar_end_right", sliderHeight / 2, sliderHeight, "moodbar_end_right" ) ); moodbarPainted = true; } } } if( !moodbarPainted ) { // Draw the slider background in 3 parts p->drawPixmap( pt, renderSvg( "progress_slider_left", sliderHeight, sliderHeight, "progress_slider_left" ) ); pt.rx() += sliderHeight; QRect midRect(pt, QSize(slider->rect.width() - sliderHeight * 2, sliderHeight) ); p->drawTiledPixmap( midRect, renderSvg( "progress_slider_mid", 32, sliderHeight, "progress_slider_mid" ) ); pt = midRect.topRight() + QPoint( 1, 0 ); p->drawPixmap( pt, renderSvg( "progress_slider_right", sliderHeight, sliderHeight, "progress_slider_right" ) ); //draw the played background. int playedBarHeight = sliderHeight - 6; int sizeOfLeftPlayed = qBound( 0, inverse ? slider->rect.right() - knob.right() + 2 : knob.x() - 2, playedBarHeight ); if( sizeOfLeftPlayed > 0 ) { QPoint tl, br; if ( inverse ) { tl = knob.topRight() + QPoint( -5, 5 ); // 5px x padding to avoid a "gap" between it and the top and bottom of the round knob. br = slider->rect.topRight() + QPoint( -3, 5 + playedBarHeight - 1 ); QPixmap rightEnd = renderSvg( "progress_slider_played_right", playedBarHeight, playedBarHeight, "progress_slider_played_right" ); p->drawPixmap( br.x() - rightEnd.width() + 1, tl.y(), rightEnd, qMax(0, rightEnd.width() - (sizeOfLeftPlayed + 3)), 0, sizeOfLeftPlayed + 3, playedBarHeight ); br.rx() -= playedBarHeight; } else { tl = slider->rect.topLeft() + QPoint( 3, 5 ); br = QPoint( knob.x() + 5, tl.y() + playedBarHeight - 1 ); QPixmap leftEnd = renderSvg( "progress_slider_played_left", playedBarHeight, playedBarHeight, "progress_slider_played_left" ); p->drawPixmap( tl.x(), tl.y(), leftEnd, 0, 0, sizeOfLeftPlayed + 3, playedBarHeight ); tl.rx() += playedBarHeight; } if ( sizeOfLeftPlayed == playedBarHeight ) p->drawTiledPixmap( QRect(tl, br), renderSvg( "progress_slider_played_mid", 32, playedBarHeight, "progress_slider_played_mid" ) ); } } if ( slider->state & QStyle::State_Enabled ) { // Draw the knob (handle) const char *string = ( slider->activeSubControls & QStyle::SC_SliderHandle ) ? "slider_knob_200911_active" : "slider_knob_200911"; p->drawPixmap( knob.topLeft(), renderSvg( string, knob.width(), knob.height(), string ) ); } } diff --git a/src/SvgHandler.h b/src/SvgHandler.h index 52869afc3a..238db1ce60 100644 --- a/src/SvgHandler.h +++ b/src/SvgHandler.h @@ -1,162 +1,174 @@ /**************************************************************************************** * Copyright (c) 2008 Nikolaj Hald Nielsen * * Copyright (c) 2008 Jeff Mitchell * * Copyright (c) 2009-2013 Mark Kretschmann * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef SVGHANDLER_H #define SVGHANDLER_H #include "amarok_export.h" #include "core/meta/forward_declarations.h" #include #include #include #include #include class QStyleOptionSlider; class QSvgRenderer; class SvgHandler; namespace The { AMAROK_EXPORT SvgHandler* svgHandler(); } /** A class to abstract out some common operations of users of tinted svgs */ class AMAROK_EXPORT SvgHandler : public QObject { Q_OBJECT friend SvgHandler* The::svgHandler(); public: ~SvgHandler(); QSvgRenderer* getRenderer( const QString &name ); QSvgRenderer* getRenderer(); QPixmap renderSvg( const QString &name, const QString& keyname, int width, int height, const QString& element = QString(), bool skipCache = false, const qreal opacity = 1.0 ); /** * Overloaded function that uses the current theme * @param keyname the name of the key to save in the cache * @param width Width of the resulting pixmap * @param height Height of the resulting pixmap * @param element The theme element to render ( if none the entire svg is rendered ) * @param skipCache If true, the pixmap will always get rendered and never fetched from the cache. * @param opacity The opacity used for rendering. Range 0.0 to 1.0. * @return The svg element/file rendered into a pixmap */ QPixmap renderSvg( const QString& keyname, int width, int height, const QString& element = QString(), bool skipCache = false, const qreal opacity = 1.0 ); + + /** + * Another overloaded function that loads a svg file from an url. This function is usable from QML. + * @param keyname the name of the key to save in the cache + * @param width Width of the resulting pixmap + * @param height Height of the resulting pixmap + * @param element The theme element to render ( if none the entire svg is rendered ) + * @param skipCache If true, the pixmap will always get rendered and never fetched from the cache. + * @param opacity The opacity used for rendering. Range 0.0 to 1.0. + * @return The svg element/file rendered into a pixmap + */ + Q_INVOKABLE QPixmap renderSvg( const QUrl& url, const QString& keyname, int width, int height, const QString& element = QString(), bool skipCache = false, const qreal opacity = 1.0 ); /** * Yet another overloaded function. This one renders the svg element and adds half a divider element to the top and the bottom * so it looks sane when multiple elements with the same width are stacked. * * @param keyname the name of the key to save in the cache. * @param width Width of the resulting pixmap. * @param height Height of the resulting pixmap. * @param element The theme element to render ( if none the entire svg is rendered ) * @return The svg element/file rendered into a pixmap. */ QPixmap renderSvgWithDividers( const QString& keyname, int width, int height, const QString& element = QString() ); /** * Take an album and extract the pixmap for sending to addBordersToPixmap. * * @param album The AlbumPtr * @param size The size of the resulting image (border included) * @borderWidth The desired width of the border */ QPixmap imageWithBorder( Meta::AlbumPtr album, int size = 1, int borderWidth = 5 ); /** * Add nice borders to a pixmap. The function will create and return a new * Pixmal that is the size of the old one plus twice the border width in * each dimension. * * @param orgPixmap The original pixmap. * @param borderWidth The pixel width of the borders to add to the pixmap. * @param name A name for use as the basis of the cache key that for caching the completed image plus borders. * @param skipCache If true, the pixmap will always get rendered and never fetched from the cache. */ QPixmap addBordersToPixmap( const QPixmap &orgPixmap, int borderWidth, const QString &name, bool skipCache =false ); /** * Paint a custom slider using the specified painter. The slider consists * of a background part, a "knob" that moves along it to show the current * position, and 2 end markers to clearly mark the ends of the slider. * The background part before the knob, is painted in a different color than the * part after (and under) the knob. * @param p The painter to use. * @param percentage The percentange of the slider that the knob is positioned at. */ void paintCustomSlider( QPainter *p, QStyleOptionSlider *slider, qreal percentage, bool paintMoodbar = false ); /** * Calculate the visual slider knob rect from its value, use it instead the QStyle functions * QStyle::sliderPositionFromValue() and QStyle::subControlRect(); */ QRect sliderKnobRect( const QRect &slider, qreal percent, bool inverse ) const; /** * Get the path of the currently used svg theme file. * * @return the path of the currently used theme file. */ QString themeFile(); /** * Change the currently used svg theme file. This function also * clears the pixmap cache as all svg elements have potentially changed * and should be re-rendered. * * @param themeFile The path of the new theme file to use. */ void setThemeFile( const QString & themeFile ); public Q_SLOTS: void reTint(); Q_SIGNALS: void retinted(); private Q_SLOTS: void discardCache(); private: SvgHandler( QObject* parent = 0 ); - bool loadSvg( const QString& name ); + bool loadSvg( const QString& name, bool forceCustomTheme = false ); QPixmap sliderHandle( const QColor &color, bool pressed, int size ); QColor calcLightColor(const QColor &color) const; QColor calcDarkColor(const QColor &color) const; bool lowThreshold(const QColor &color) const; KImageCache * m_cache; QHash m_renderers; QReadWriteLock m_lock; QString m_themeFile; bool m_customTheme; }; #endif diff --git a/src/amarok-contextapplet.desktop b/src/amarok-contextapplet.desktop new file mode 100644 index 0000000000..b1833def81 --- /dev/null +++ b/src/amarok-contextapplet.desktop @@ -0,0 +1,93 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=Amarok/Plugin +Comment=Plugin for Amarok +Comment[be]=Утулка для Amarok +Comment[bg]=Приставка за Amarok +Comment[bs]=Priključak za Amarok +Comment[ca]=Connector per l'Amarok +Comment[ca@valencia]=Connector per l'Amarok +Comment[cs]=Modul pro AmaroK +Comment[csb]=Wtëkôcz dlô Amaroka +Comment[da]=Plugin til Amarok +Comment[de]=Erweiterungsmodul für Amarok +Comment[el]=Πρόσθετο για το AmaroK +Comment[en_GB]=Plugin for Amarok +Comment[eo]=Kromaĵo por Amarok +Comment[es]=Complemento para Amarok +Comment[et]=Amaroki plugin +Comment[eu]=Amarok-en plugina +Comment[fa]=وصله برای آماروک +Comment[fi]=Amarok-liitännäinen +Comment[fr]=Module externe pour Amarok +Comment[ga]=Breiseán Amarok +Comment[gl]=Extensión de Amarok +Comment[he]=תוסף ל־Amarok +Comment[hne]=अमाराक बर प्लगइन +Comment[hu]=Bővítőmodul az Amarokhoz +Comment[id]=Plugin untuk Amarok +Comment[is]=Íforrit fyrir Amarok +Comment[it]=Estensione per Amarok +Comment[ja]=Amarok のためのプラグイン +Comment[km]=កម្មវិធី​ជំនួយ​សម្រាប់ Amarok +Comment[ko]=Amarok 플러그인 +Comment[ku]=Pêvek ji bo Amarok +Comment[lt]=Amarok papildinys +Comment[lv]=Amarok spraudnis +Comment[nb]=Programtillegg for Amarok +Comment[nds]=Moduul för Amarok +Comment[ne]=अमारोकका लागि प्लगइन +Comment[nl]=Plugin voor Amarok +Comment[nn]=Programtillegg til Amarok +Comment[pa]=ਅਮਰੋਕ ਲਈ ਪਲੱਗਿਨ +Comment[pl]=Wtyczka dla Amaroka +Comment[pt]='Plugin' para o Amarok +Comment[pt_BR]=Plugin para o Amarok +Comment[ro]=Modul pentru Amarok +Comment[ru]=Модуль для Amarok +Comment[sk]=Modul pre Amarok +Comment[sl]=Vstavek za Amarok +Comment[sr]=Прикључак за Амарок +Comment[sr@ijekavian]=Прикључак за Амарок +Comment[sr@ijekavianlatin]=Priključak za Amarok +Comment[sr@latin]=Priključak za Amarok +Comment[sv]=Insticksprogram för Amarok +Comment[th]=ส่วนเสริมของแอมอะร็อก +Comment[tr]=Amarok için eklenti +Comment[uk]=Додаток для Amarok +Comment[wa]=Tchôke-divins pos Amarok +Comment[x-test]=xxPlugin for Amarokxx +Comment[zh_CN]=Amarok 插件 +Comment[zh_TW]=Amarok 的外掛程式 + + +# List of authors. +[PropertyDef::X-KDE-Amarok-authors] +Type=QStringList + +# List of author's email addresses. +[PropertyDef::X-KDE-Amarok-email] +Type=QStringList + +# Priority of the plugin. When KTrader returns multiple offers, the one with the highest rank is chosen. +# Range: 0 (disabled) - 255 (highest) +[PropertyDef::X-KDE-Amarok-rank] +Type=int + +# Version of the plugin. +[PropertyDef::X-KDE-Amarok-version] +Type=int + +# Version of the framework this plugin is compatible with. +# Must be bumped after making binary incompatible changes. +[PropertyDef::X-KDE-Amarok-framework-version] +Type=int + +# If true, Amarok will show a warning dialog about the experimental nature of this plugin. +[PropertyDef::X-KDE-Amarok-experimental] +Type=bool + +# If true, Amarok will always initialize this plugin indepenent of the configuration +[PropertyDef::X-KDE-Amarok-vital] +Type=bool + diff --git a/src/amarokurls/ContextUrlGenerator.cpp b/src/amarokurls/ContextUrlGenerator.cpp index 96d7a5071e..95c8f5557f 100644 --- a/src/amarokurls/ContextUrlGenerator.cpp +++ b/src/amarokurls/ContextUrlGenerator.cpp @@ -1,78 +1,75 @@ /**************************************************************************************** * Copyright (c) 2009 Nikolaj Hald Nielsen * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Pulic License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "ContextUrlGenerator.h" #include "AmarokUrl.h" #include "AmarokUrlHandler.h" +#include "context/ContextView.h" #include ContextUrlGenerator * ContextUrlGenerator::s_instance = 0; ContextUrlGenerator * ContextUrlGenerator::instance() { if( s_instance == 0 ) s_instance = new ContextUrlGenerator(); return s_instance; } ContextUrlGenerator::ContextUrlGenerator() { } ContextUrlGenerator::~ContextUrlGenerator() { The::amarokUrlHandler()->unRegisterGenerator( this ); } AmarokUrl ContextUrlGenerator::createContextBookmark() { -/* FIXME: disabled temporarily for KF5 porting QStringList pluginNames = Context::ContextView::self()->currentApplets(); QStringList appletNames = Context::ContextView::self()->currentAppletNames(); -*/ AmarokUrl url; url.setCommand( "context" ); -/* FIXME: disabled temporarily for KF5 porting url.setArg( "applets", pluginNames.join( "," ) ); url.setName( i18n( "Context: %1", appletNames.join( "," ) ) ); -*/ return url; } QString ContextUrlGenerator::description() { return i18n( "Bookmark Context View Applets" ); } QIcon ContextUrlGenerator::icon() { return QIcon::fromTheme( "x-media-podcast-amarok" ); } AmarokUrl ContextUrlGenerator::createUrl() { return createContextBookmark(); } diff --git a/src/amarokurls/ContextUrlRunner.cpp b/src/amarokurls/ContextUrlRunner.cpp index 081190e65a..93f573b688 100644 --- a/src/amarokurls/ContextUrlRunner.cpp +++ b/src/amarokurls/ContextUrlRunner.cpp @@ -1,77 +1,76 @@ /**************************************************************************************** * Copyright (c) 2009 Nikolaj Hald Nielsen * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Pulic License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "ContextUrlRunner.h" #include "MainWindow.h" #include "AmarokUrlHandler.h" +#include "context/ContextView.h" +#include "context/AppletModel.h" #include ContextUrlRunner::ContextUrlRunner() {} ContextUrlRunner::~ContextUrlRunner() { The::amarokUrlHandler()->unRegisterRunner ( this ); } QIcon ContextUrlRunner::icon() const { return QIcon::fromTheme( "x-media-podcast-amarok" ); } bool ContextUrlRunner::run( AmarokUrl url ) { DEBUG_BLOCK if( url.isNull() ) return false; if( url.command() != command() ) return false; QString appletsString = url.args().value( "applets" ); debug() << "applet string: " << appletsString; QStringList appletList = appletsString.split( ',' ); - -/* FIXME: disabled temporarily for KF5 porting - Context::ContextView::self()->clearNoSave(); - Context::Containment* cont = dynamic_cast< Context::Containment* >( Context::ContextView::self()->containment() ); - if( cont ) + auto model = Context::ContextView::self()->appletModel(); + if( model ) { + model->clear(); foreach( const QString &appletPluginName, appletList ) { - cont->addApplet( appletPluginName, -1 ); + model->setAppletEnabled( appletPluginName, true ); } } -*/ The::mainWindow()->showDock( MainWindow::AmarokDockContext ); return true; } QString ContextUrlRunner::command() const { return "context"; } QString ContextUrlRunner::prettyCommand() const { return i18nc( "A type of command that affects the context view", "Context" ); } diff --git a/src/browsers/CollectionTreeView.cpp b/src/browsers/CollectionTreeView.cpp index 4ab6521f49..accf9e6dbf 100644 --- a/src/browsers/CollectionTreeView.cpp +++ b/src/browsers/CollectionTreeView.cpp @@ -1,1456 +1,1454 @@ /**************************************************************************************** * Copyright (c) 2007 Alexandre Pereira de Oliveira * * Copyright (c) 2007 Ian Monroe * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) version 3 or * * any later version accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of * * version 3 of the license. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "CollectionTreeView" #include "CollectionTreeView.h" #include "AmarokMimeData.h" #include "GlobalCollectionActions.h" #include "PopupDropperFactory.h" #include "SvgHandler.h" #include "browsers/CollectionSortFilterProxyModel.h" #include "browsers/CollectionTreeItemModel.h" -// #include "context/ContextView.h" +#include "context/ContextView.h" #include "core/capabilities/ActionsCapability.h" #include "core/capabilities/BookmarkThisCapability.h" #include "core/collections/CollectionLocation.h" #include "core/collections/MetaQueryMaker.h" #include "core/collections/QueryMaker.h" #include "core/meta/Meta.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include "core-impl/collections/support/CollectionManager.h" #include "core-impl/collections/support/TextualQueryFilter.h" #include "core-impl/collections/support/TrashCollectionLocation.h" #include "dialogs/TagDialog.h" #include "playlist/PlaylistModelStack.h" #include "scripting/scriptengine/AmarokCollectionViewScript.h" #include #include #include #include #include #include #include #include #include #include using namespace Collections; /** * RAII class to perform restoring of the scroll position once all queries are * finished. DelayedScroller auto-deletes itself once its job is over (ot if it finds * it is useless). */ class DelayedScroller : public QObject { Q_OBJECT public: DelayedScroller( CollectionTreeView *treeView, CollectionTreeItemModelBase *treeModel, const QModelIndex &treeModelScrollToIndex, int topOffset ) : QObject( treeView ) , m_treeView( treeView ) , m_treeModel( treeModel ) , m_topOffset( topOffset ) { connect( treeModel, &CollectionTreeItemModelBase::destroyed, this, &DelayedScroller::deleteLater ); connect( treeModel, &CollectionTreeItemModelBase::allQueriesFinished, this, &DelayedScroller::slotScroll ); m_scrollToItem = m_treeModel->treeItem( treeModelScrollToIndex ); if( m_scrollToItem ) connect( m_scrollToItem, &CollectionTreeItem::destroyed, this, &DelayedScroller::deleteLater ); else deleteLater(); // nothing to do } private slots: void slotScroll() { deleteLater(); QModelIndex idx = m_treeModel->itemIndex( m_scrollToItem ); QSortFilterProxyModel *filterModel = m_treeView->filterModel(); idx = filterModel ? filterModel->mapFromSource( idx ) : QModelIndex(); QScrollBar *scrollBar = m_treeView->verticalScrollBar(); if( !idx.isValid() || !scrollBar ) return; int newTopOffset = m_treeView->visualRect( idx ).top(); scrollBar->setValue( scrollBar->value() + (newTopOffset - m_topOffset) ); } private: CollectionTreeView *m_treeView; CollectionTreeItemModelBase *m_treeModel; CollectionTreeItem *m_scrollToItem; int m_topOffset; }; /** * RAII class to auto-expand collection tree entries after filtering. * AutoExpander auto-deletes itself once its job is over (or if it finds * it is useless). */ class AutoExpander : public QObject { Q_OBJECT public: AutoExpander( CollectionTreeView *treeView, CollectionTreeItemModelBase *treeModel, QAbstractItemModel *filterModel) : QObject( treeView ) , m_treeView( treeView ) , m_filterModel( filterModel ) { connect( filterModel, &QObject::destroyed, this, &QObject::deleteLater ); connect( treeModel, &CollectionTreeItemModelBase::allQueriesFinished, this, &AutoExpander::slotExpandMore ); // start with the root index m_indicesToCheck.enqueue( QModelIndex() ); slotExpandMore(); } private slots: void slotExpandMore() { const int maxChildrenToExpand = 3; QQueue pendingIndices; while( !m_indicesToCheck.isEmpty() ) { if( !m_filterModel ) return; QModelIndex current = m_indicesToCheck.dequeue(); if( m_filterModel->canFetchMore( current ) ) { m_filterModel->fetchMore( current ); pendingIndices.enqueue( current ); continue; } if( m_filterModel->rowCount( current ) <= maxChildrenToExpand ) { m_treeView->expand( current ); for( int i = 0; i < m_filterModel->rowCount( current ); i++ ) m_indicesToCheck.enqueue( m_filterModel->index( i, 0, current ) ); } } if( pendingIndices.isEmpty() ) // nothing left to do deleteLater(); else // process pending indices when queries finish m_indicesToCheck.swap( pendingIndices ); } private: Q_DISABLE_COPY(AutoExpander) CollectionTreeView *m_treeView; QPointer m_filterModel; QQueue m_indicesToCheck; }; CollectionTreeView::CollectionTreeView( QWidget *parent) : Amarok::PrettyTreeView( parent ) , m_filterModel( 0 ) , m_treeModel( 0 ) , m_pd( 0 ) , m_appendAction( 0 ) , m_loadAction( 0 ) , m_editAction( 0 ) , m_organizeAction( 0 ) , m_ongoingDrag( false ) { setSortingEnabled( true ); setFocusPolicy( Qt::StrongFocus ); sortByColumn( 0, Qt::AscendingOrder ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); setEditTriggers( EditKeyPressed ); setDragDropMode( QAbstractItemView::DragDrop ); connect( this, &CollectionTreeView::collapsed, this, &CollectionTreeView::slotCollapsed ); connect( this, &CollectionTreeView::expanded, this, &CollectionTreeView::slotExpanded ); } void CollectionTreeView::setModel( QAbstractItemModel *model ) { if( m_treeModel ) disconnect( m_treeModel, 0, this, 0); m_treeModel = qobject_cast( model ); if( !m_treeModel ) return; connect( m_treeModel, &CollectionTreeItemModelBase::allQueriesFinished, this, &CollectionTreeView::slotCheckAutoExpand ); connect( m_treeModel, &CollectionTreeItemModelBase::expandIndex, this, &CollectionTreeView::slotExpandIndex ); if( m_filterModel ) m_filterModel->deleteLater(); m_filterModel = new CollectionSortFilterProxyModel( this ); m_filterModel->setSourceModel( model ); QTreeView::setModel( m_filterModel ); QTimer::singleShot( 0, this, &CollectionTreeView::slotCheckAutoExpandReally ); } CollectionTreeView::~CollectionTreeView() { // we don't own m_treeModel pointer // m_filterModel will get deleted by QObject parentship } void CollectionTreeView::setLevels( const QList &levels ) { if( m_treeModel ) m_treeModel->setLevels( levels ); } QList CollectionTreeView::levels() const { if( m_treeModel ) return m_treeModel->levels(); return QList(); } void CollectionTreeView::setLevel( int level, CategoryId::CatMenuId type ) { if( !m_treeModel ) return; QList levels = m_treeModel->levels(); if( type == CategoryId::None ) { while( levels.count() >= level ) levels.removeLast(); } else { levels.removeAll( type ); levels[level] = type; } setLevels( levels ); } QSortFilterProxyModel * CollectionTreeView::filterModel() const { return m_filterModel; } void CollectionTreeView::contextMenuEvent( QContextMenuEvent *event ) { if( !m_treeModel ) return; QModelIndex index = indexAt( event->pos() ); if( !index.isValid() ) { Amarok::PrettyTreeView::contextMenuEvent( event ); return; } QModelIndexList indices = selectedIndexes(); // if previously selected indices do not contain the index of the item // currently under the mouse when context menu is invoked. if( !indices.contains( index ) ) { indices.clear(); indices << index; setCurrentIndex( index ); } //TODO: get rid of this, it's a hack. // Put remove actions in model so we don't need access to the internal pointer in view if( m_filterModel ) { QModelIndexList tmp; foreach( const QModelIndex &idx, indices ) { tmp.append( m_filterModel->mapToSource( idx ) ); } indices = tmp; } // Abort if nothing is selected if( indices.isEmpty() ) return; m_currentItems.clear(); foreach( const QModelIndex &index, indices ) { if( index.isValid() && index.internalPointer() ) m_currentItems.insert( static_cast( index.internalPointer() ) ); } QMenu menu( this ); // Destroy the menu when the model is reset (collection update), so that we don't // operate on invalid data. see BUG 190056 connect( m_treeModel, &CollectionTreeItemModelBase::modelReset, &menu, &QMenu::deleteLater ); // create basic actions QActionList actions = createBasicActions( indices ); foreach( QAction *action, actions ) { menu.addAction( action ); } menu.addSeparator(); actions.clear(); QActionList albumActions = createCustomActions( indices ); QMenu menuAlbum( i18n( "Album" ) ); foreach( QAction *action, albumActions ) { if( !action->parent() ) action->setParent( &menuAlbum ); } if( albumActions.count() > 1 ) { menuAlbum.addActions( albumActions ); menuAlbum.setIcon( QIcon::fromTheme( "filename-album-amarok" ) ); menu.addMenu( &menuAlbum ); menu.addSeparator(); } else if( albumActions.count() == 1 ) { menu.addActions( albumActions ); } QActionList collectionActions = createCollectionActions( indices ); QMenu menuCollection( i18n( "Collection" ) ); foreach( QAction *action, collectionActions ) { if( !action->parent() ) action->setParent( &menuCollection ); } if( collectionActions.count() > 1 ) { menuCollection.setIcon( QIcon::fromTheme( "drive-harddisk" ) ); menuCollection.addActions( collectionActions ); menu.addMenu( &menuCollection ); menu.addSeparator(); } else if( collectionActions.count() == 1 ) { menu.addActions( collectionActions ); } m_currentCopyDestination = getCopyActions( indices ); m_currentMoveDestination = getMoveActions( indices ); if( !m_currentCopyDestination.empty() ) { QMenu *copyMenu = new QMenu( i18n( "Copy to Collection" ), &menu ); copyMenu->setIcon( QIcon::fromTheme( "edit-copy" ) ); copyMenu->addActions( m_currentCopyDestination.keys() ); menu.addMenu( copyMenu ); } //Move = copy + delete from source if( !m_currentMoveDestination.empty() ) { QMenu *moveMenu = new QMenu( i18n( "Move to Collection" ), &menu ); moveMenu->setIcon( QIcon::fromTheme( "go-jump" ) ); moveMenu->addActions( m_currentMoveDestination.keys() ); menu.addMenu( moveMenu ); } // create trash and delete actions if( onlyOneCollection( indices ) ) { Collection *collection = getCollection( indices.first() ); if( collection && collection->isWritable() ) { //TODO: don't recreate action QAction *trashAction = new QAction( QIcon::fromTheme( "user-trash" ), i18n( "Move Tracks to Trash" ), &menu ); trashAction->setProperty( "popupdropper_svg_id", "delete" ); // key shortcut is only for display purposes here, actual one is // determined by View in Model/View classes trashAction->setShortcut( Qt::Key_Delete ); connect( trashAction, &QAction::triggered, this, &CollectionTreeView::slotTrashTracks ); menu.addAction( trashAction ); QAction *deleteAction = new QAction( QIcon::fromTheme( "remove-amarok" ), i18n( "Delete Tracks" ), &menu ); deleteAction->setProperty( "popupdropper_svg_id", "delete" ); // key shortcut is only for display purposes here, actual one is // determined by View in Model/View classes deleteAction->setShortcut( Qt::SHIFT + Qt::Key_Delete ); connect( deleteAction, &QAction::triggered, this, &CollectionTreeView::slotDeleteTracks ); menu.addAction( deleteAction ); } } // add extended actions menu.addSeparator(); actions += createExtendedActions( indices ); foreach( QAction *action, actions ) { menu.addAction( action ); } AmarokScript::AmarokCollectionViewScript::createScriptedActions( menu, indices ); menu.exec( event->globalPos() ); } void CollectionTreeView::mouseDoubleClickEvent( QMouseEvent *event ) { if( event->button() == Qt::MidButton ) { event->accept(); return; } QModelIndex index = indexAt( event->pos() ); if( !index.isValid() ) { event->accept(); return; } // code copied in PlaylistBrowserView::mouseDoubleClickEvent(), keep in sync // mind bug 279513 bool isExpandable = model()->hasChildren( index ); bool wouldExpand = !visualRect( index ).contains( event->pos() ) || // clicked outside item, perhaps on expander icon ( isExpandable && !style()->styleHint( QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this ) ); // we're in doubleClick if( event->button() == Qt::LeftButton && event->modifiers() == Qt::NoModifier && !wouldExpand ) { CollectionTreeItem *item = getItemFromIndex( index ); playChildTracks( item, Playlist::OnDoubleClickOnSelectedItems ); event->accept(); return; } PrettyTreeView::mouseDoubleClickEvent( event ); } void CollectionTreeView::mouseReleaseEvent( QMouseEvent *event ) { if( m_pd ) { connect( m_pd, &PopupDropper::fadeHideFinished, m_pd, &PopupDropper::deleteLater ); m_pd->hide(); m_pd = 0; } QModelIndex index = indexAt( event->pos() ); if( !index.isValid() ) { PrettyTreeView::mouseReleaseEvent( event ); return; } if( event->button() == Qt::MidButton ) { CollectionTreeItem *item = getItemFromIndex( index ); playChildTracks( item, Playlist::OnMiddleClickOnSelectedItems ); event->accept(); return; } PrettyTreeView::mouseReleaseEvent( event ); } CollectionTreeItem * CollectionTreeView::getItemFromIndex( QModelIndex &index ) { QModelIndex filteredIndex; if( m_filterModel ) filteredIndex = m_filterModel->mapToSource( index ); else filteredIndex = index; if( !filteredIndex.isValid() ) { return 0; } return static_cast( filteredIndex.internalPointer() ); } void CollectionTreeView::keyPressEvent( QKeyEvent *event ) { QModelIndexList indices = selectedIndexes(); if( indices.isEmpty() ) { Amarok::PrettyTreeView::keyPressEvent( event ); return; } if( m_filterModel ) { QModelIndexList tmp; foreach( const QModelIndex &idx, indices ) tmp.append( m_filterModel->mapToSource( idx ) ); indices = tmp; } m_currentItems.clear(); foreach( const QModelIndex &index, indices ) { if( index.isValid() && index.internalPointer() ) { m_currentItems.insert( static_cast( index.internalPointer() ) ); } } QModelIndex current = currentIndex(); switch( event->key() ) { case Qt::Key_Enter: case Qt::Key_Return: playChildTracks( m_currentItems, Playlist::OnReturnPressedOnSelectedItems ); return; case Qt::Key_Delete: if( !onlyOneCollection( indices ) ) break; removeTracks( m_currentItems, !( event->modifiers() & Qt::ShiftModifier ) ); return; case Qt::Key_Up: if( current.parent() == QModelIndex() && current.row() == 0 ) { emit leavingTree(); return; } break; case Qt::Key_Down: break; // L and R should magically work when we get a patched version of qt case Qt::Key_Right: case Qt::Key_Direction_R: expand( current ); return; case Qt::Key_Left: case Qt::Key_Direction_L: collapse( current ); return; default: break; } Amarok::PrettyTreeView::keyPressEvent( event ); } void CollectionTreeView::dragEnterEvent( QDragEnterEvent *event ) { // We want to indicate to the user that dropping to the same collection is not possible. // CollectionTreeItemModel therefore needs to know what collection the drag originated // so that is can play with Qt::ItemIsDropEnabled in flags() const AmarokMimeData *mimeData = qobject_cast( event->mimeData() ); if( mimeData ) // drag from within Amarok { QSet srcCollections; foreach( Meta::TrackPtr track, mimeData->tracks() ) { srcCollections.insert( track->collection() ); } m_treeModel->setDragSourceCollections( srcCollections ); } QAbstractItemView::dragEnterEvent( event ); } void CollectionTreeView::dragMoveEvent( QDragMoveEvent *event ) { // this mangling is not needed for Copy/Move distinction to work, it is only needed // for mouse cursor changing to work if( (event->keyboardModifiers() & Qt::ShiftModifier) && (event->possibleActions() & Qt::MoveAction) ) { event->setDropAction( Qt::MoveAction ); } else if( event->possibleActions() & Qt::CopyAction ) { event->setDropAction( Qt::CopyAction ); } QTreeView::dragMoveEvent( event ); } void CollectionTreeView::startDrag(Qt::DropActions supportedActions) { DEBUG_BLOCK // Make sure that the left mouse button is actually pressed. Otherwise we're prone to // mis-detecting clicks as dragging if( !( QApplication::mouseButtons() & Qt::LeftButton ) ) return; QModelIndexList indices = selectedIndexes(); if( indices.isEmpty() ) return; // When a parent item is dragged, startDrag() is called a bunch of times. Here we // prevent that: if( m_ongoingDrag ) return; m_ongoingDrag = true; -/* FIXME: disabled temporarily for KF5 porting if( !m_pd ) m_pd = The::popupDropperFactory()->createPopupDropper( Context::ContextView::self() ); if( m_pd && m_pd->isHidden() ) { if( m_filterModel ) { QModelIndexList tmp; foreach( const QModelIndex &idx, indices ) { tmp.append( m_filterModel->mapToSource( idx ) ); } indices = tmp; } QActionList actions = createBasicActions( indices ); QFont font; font.setPointSize( 16 ); font.setBold( true ); foreach( QAction * action, actions ) m_pd->addItem( The::popupDropperFactory()->createItem( action ) ); m_currentCopyDestination = getCopyActions( indices ); m_currentMoveDestination = getMoveActions( indices ); m_currentItems.clear(); foreach( const QModelIndex &index, indices ) { if( index.isValid() && index.internalPointer() ) { m_currentItems.insert( static_cast( index.internalPointer() ) ); } } PopupDropperItem *subItem; actions = createExtendedActions( indices ); PopupDropper *morePud = 0; if( actions.count() > 1 ) { morePud = The::popupDropperFactory()->createPopupDropper( 0, true ); foreach( QAction *action, actions ) morePud->addItem( The::popupDropperFactory()->createItem( action ) ); } else m_pd->addItem( The::popupDropperFactory()->createItem( actions[0] ) ); //TODO: Keep bugging i18n team about problems with 3 dots if ( actions.count() > 1 ) { subItem = m_pd->addSubmenu( &morePud, i18n( "More..." ) ); The::popupDropperFactory()->adjustItem( subItem ); } m_pd->show(); } -*/ QTreeView::startDrag( supportedActions ); debug() << "After the drag!"; if( m_pd ) { debug() << "clearing PUD"; connect( m_pd, &PopupDropper::fadeHideFinished, m_pd, &PopupDropper::clear ); m_pd->hide(); } m_ongoingDrag = false; } void CollectionTreeView::selectionChanged( const QItemSelection &selected, const QItemSelection &deselected ) { QModelIndexList indexes = selected.indexes(); QModelIndexList changedIndexes = indexes; changedIndexes << deselected.indexes(); foreach( const QModelIndex &index, changedIndexes ) update( index ); if( indexes.count() < 1 ) return; QModelIndex index; if( m_filterModel ) index = m_filterModel->mapToSource( indexes[0] ); else index = indexes[0]; CollectionTreeItem *item = static_cast( index.internalPointer() ); emit( itemSelected ( item ) ); } void CollectionTreeView::slotCollapsed( const QModelIndex &index ) { if( !m_treeModel ) return; if( m_filterModel ) m_treeModel->slotCollapsed( m_filterModel->mapToSource( index ) ); else m_treeModel->slotCollapsed( index ); } void CollectionTreeView::slotExpanded( const QModelIndex &index ) { if( !m_treeModel ) return; if( m_filterModel ) m_treeModel->slotExpanded( m_filterModel->mapToSource( index )); else m_treeModel->slotExpanded( index ); } void CollectionTreeView::slotExpandIndex( const QModelIndex &index ) { if( !m_treeModel ) return; if( m_filterModel ) expand( m_filterModel->mapFromSource( index ) ); } void CollectionTreeView::slotCheckAutoExpand( bool reallyExpand ) { if( !m_filterModel || !reallyExpand ) return; // auto-deletes itself: new AutoExpander( this, m_treeModel, m_filterModel ); } void CollectionTreeView::playChildTracks( CollectionTreeItem *item, Playlist::AddOptions insertMode ) { QSet items; items.insert( item ); playChildTracks( items, insertMode ); } void CollectionTreeView::playChildTracks( const QSet &items, Playlist::AddOptions insertMode ) { if( !m_treeModel ) return; //Ensure that if a parent and child are both selected we ignore the child QSet parents( cleanItemSet( items ) ); //Store the type of playlist insert to be done and cause a slot to be invoked when the tracklist has been generated. AmarokMimeData *mime = dynamic_cast( m_treeModel->mimeData( QList::fromSet( parents ) ) ); m_playChildTracksMode.insert( mime, insertMode ); connect( mime, &AmarokMimeData::trackListSignal, this, &CollectionTreeView::playChildTracksSlot ); mime->getTrackListSignal(); } void CollectionTreeView::playChildTracksSlot( Meta::TrackList list ) //slot { AmarokMimeData *mime = dynamic_cast( sender() ); Playlist::AddOptions insertMode = m_playChildTracksMode.take( mime ); qStableSort( list.begin(), list.end(), Meta::Track::lessThan ); The::playlistController()->insertOptioned( list, insertMode ); mime->deleteLater(); } void CollectionTreeView::organizeTracks( const QSet &items ) const { DEBUG_BLOCK if( !items.count() ) return; //Create query based upon items, ensuring that if a parent and child are both //selected we ignore the child Collections::QueryMaker *qm = createMetaQueryFromItems( items, true ); if( !qm ) return; CollectionTreeItem *item = items.toList().first(); while( item->isDataItem() ) item = item->parent(); Collection *coll = item->parentCollection(); CollectionLocation *location = coll->location(); if( !location->isOrganizable() ) { debug() << "Collection not organizable"; //how did we get here?? delete location; delete qm; return; } location->prepareMove( qm, coll->location() ); } void CollectionTreeView::copySelectedToLocalCollection() { DEBUG_BLOCK // Get the local collection Collections::Collection *collection = 0; const QList collections = CollectionManager::instance()->collections().keys(); foreach( collection, collections ) { if ( collection->collectionId() == "localCollection" ) break; } if( !collection ) return; // Get selected items QModelIndexList indexes = selectedIndexes(); if( m_filterModel ) { QModelIndexList tmp; foreach( const QModelIndex &idx, indexes ) tmp.append( m_filterModel->mapToSource( idx ) ); indexes = tmp; } m_currentItems.clear(); foreach( const QModelIndex &index, indexes ) { if( index.isValid() && index.internalPointer() ) m_currentItems.insert( static_cast( index.internalPointer() ) ); } copyTracks( m_currentItems, collection, false ); } void CollectionTreeView::copyTracks( const QSet &items, Collection *destination, bool removeSources ) const { DEBUG_BLOCK if( !destination ) { warning() << "collection is not writable (0-pointer)! Aborting"; return; } if( !destination->isWritable() ) { warning() << "collection " << destination->prettyName() << " is not writable! Aborting"; return; } //copied from organizeTracks. create a method for this somewhere if( !items.count() ) { warning() << "No items to copy! Aborting"; return; } //Create query based upon items, ensuring that if a parent and child are both selected we ignore the child Collections::QueryMaker *qm = createMetaQueryFromItems( items, true ); if( !qm ) { warning() << "could not get qm!"; return; } CollectionTreeItem *item = items.toList().first(); while( item->isDataItem() ) { item = item->parent(); } Collection *coll = item->parentCollection(); CollectionLocation *source = coll->location(); CollectionLocation *dest = destination->location(); if( removeSources ) { if( !source->isWritable() ) //error { warning() << "We can not write to ze source!!! OMGooses!"; delete dest; delete source; delete qm; return; } debug() << "starting source->prepareMove"; source->prepareMove( qm, dest ); } else { debug() << "starting source->prepareCopy"; source->prepareCopy( qm, dest ); } } void CollectionTreeView::removeTracks( const QSet &items, bool useTrash ) const { DEBUG_BLOCK //copied from organizeTracks. create a method for this somewhere if( !items.count() ) return; //Create query based upon items, ensuring that if a parent and child are both selected we ignore the child Collections::QueryMaker *qm = createMetaQueryFromItems( items, true ); if( !qm ) return; CollectionTreeItem *item = items.toList().first(); while( item->isDataItem() ) item = item->parent(); Collection *coll = item->parentCollection(); CollectionLocation *source = coll->location(); if( !source->isWritable() ) //error { warning() << "We can not write to ze source!!! OMGooses!"; delete source; delete qm; return; } if( useTrash ) { TrashCollectionLocation *trash = new TrashCollectionLocation(); source->prepareMove( qm, trash ); } else source->prepareRemove( qm ); } void CollectionTreeView::editTracks( const QSet &items ) const { //Create query based upon items, ensuring that if a parent and child are both //selected we ignore the child Collections::QueryMaker *qm = createMetaQueryFromItems( items, true ); if( !qm ) return; (void)new TagDialog( qm ); //the dialog will show itself automatically as soon as it is ready } void CollectionTreeView::slotSetFilter( const QString &filter ) { QString currentFilter = m_treeModel ? m_treeModel->currentFilter() : QString(); if( !m_filterModel || !m_treeModel || filter == currentFilter ) return; // special case: transitioning from non-empty to empty buffer // -> trigger later restoring of the scroll position if( filter.isEmpty() ) // currentFilter must not be empty then (see earlier check) { // take first item, descending to leaf ones if expanded. There may be better // ways to determine what item should stay "fixed". QModelIndex scrollToIndex = m_filterModel->index( 0, 0 ); while( isExpanded( scrollToIndex ) && m_filterModel->rowCount( scrollToIndex ) > 0 ) scrollToIndex = scrollToIndex.child( 0, 0 ); int topOffset = visualRect( scrollToIndex ).top(); QModelIndex bottomIndex = m_filterModel->mapToSource( scrollToIndex ); // if we have somewhere to scroll to after filter is cleared... if( bottomIndex.isValid() ) // auto-destroys itself new DelayedScroller( this, m_treeModel, bottomIndex, topOffset ); } m_treeModel->setCurrentFilter( filter ); } void CollectionTreeView::slotAddFilteredTracksToPlaylist() { if( !m_treeModel ) return; // disconnect any possible earlier connection we've done disconnect( m_treeModel, &CollectionTreeItemModelBase::allQueriesFinished, this, &CollectionTreeView::slotAddFilteredTracksToPlaylist ); if( m_treeModel->hasRunningQueries() ) // wait for the queries to finish connect( m_treeModel, &CollectionTreeItemModelBase::allQueriesFinished, this, &CollectionTreeView::slotAddFilteredTracksToPlaylist ); else { // yay, we can add the tracks now QSet items; for( int row = 0; row < m_treeModel->rowCount(); row++ ) { QModelIndex idx = m_treeModel->index( row, 0 ); CollectionTreeItem *item = idx.isValid() ? static_cast( idx.internalPointer() ) : 0; if( item ) items.insert( item ); } if( !items.isEmpty() ) playChildTracks( items, Playlist::OnAppendToPlaylistAction ); emit addingFilteredTracksDone(); } } QActionList CollectionTreeView::createBasicActions( const QModelIndexList &indices ) { QActionList actions; if( !indices.isEmpty() ) { if( m_appendAction == 0 ) { m_appendAction = new QAction( QIcon::fromTheme( "media-track-add-amarok" ), i18n( "&Add to Playlist" ), this ); m_appendAction->setProperty( "popupdropper_svg_id", "append" ); connect( m_appendAction, &QAction::triggered, this, &CollectionTreeView::slotAppendChildTracks ); } actions.append( m_appendAction ); if( m_loadAction == 0 ) { m_loadAction = new QAction( i18nc( "Replace the currently loaded tracks with these", "&Replace Playlist" ), this ); m_loadAction->setProperty( "popupdropper_svg_id", "load" ); connect( m_loadAction, &QAction::triggered, this, &CollectionTreeView::slotReplacePlaylistWithChildTracks ); } actions.append( m_loadAction ); } return actions; } QActionList CollectionTreeView::createExtendedActions( const QModelIndexList &indices ) { QActionList actions; if( !indices.isEmpty() ) { { //keep the scope of item minimal CollectionTreeItem *item = static_cast( indices.first().internalPointer() ); while( item->isDataItem() ) item = item->parent(); Collection *collection = item->parentCollection(); CollectionLocation* location = collection->location(); if( location->isOrganizable() ) { bool onlyOneCollection = true; foreach( const QModelIndex &index, indices ) { Q_UNUSED( index ) CollectionTreeItem *item = static_cast( indices.first().internalPointer() ); while( item->isDataItem() ) item = item->parent(); onlyOneCollection = item->parentCollection() == collection; if( !onlyOneCollection ) break; } if( onlyOneCollection ) { if( m_organizeAction == 0 ) { m_organizeAction = new QAction( QIcon::fromTheme("folder-open" ), i18nc( "Organize Files", "Organize Files" ), this ); m_organizeAction->setProperty( "popupdropper_svg_id", "organize" ); connect( m_organizeAction, &QAction::triggered, this, &CollectionTreeView::slotOrganize ); } actions.append( m_organizeAction ); } } delete location; } //hmmm... figure out what kind of item we are dealing with.... if( indices.size() == 1 ) { debug() << "checking for global actions"; CollectionTreeItem *item = static_cast( indices.first().internalPointer() ); QActionList gActions = The::globalCollectionActions()->actionsFor( item->data() ); foreach( QAction *action, gActions ) { if( action ) // Can become 0-pointer, see http://bugs.kde.org/show_bug.cgi?id=183250 { actions.append( action ); debug() << "Got global action: " << action->text(); } } } if( m_editAction == 0 ) { m_editAction = new QAction( QIcon::fromTheme( "media-track-edit-amarok" ), i18n( "&Edit Track Details" ), this ); setProperty( "popupdropper_svg_id", "edit" ); connect( m_editAction, &QAction::triggered, this, &CollectionTreeView::slotEditTracks ); } actions.append( m_editAction ); } else debug() << "invalid index or null internalPointer"; return actions; } QActionList CollectionTreeView::createCustomActions( const QModelIndexList &indices ) { QActionList actions; if( indices.count() == 1 ) { if( indices.first().isValid() && indices.first().internalPointer() ) { Meta::DataPtr data = static_cast( indices.first().internalPointer() )->data(); if( data ) { QScopedPointer ac( data->create() ); if( ac ) { QActionList cActions = ac->actions(); foreach( QAction *action, cActions ) { Q_ASSERT( action ); actions.append( action ); debug() << "Got custom action: " << action->text(); } } //check if this item can be bookmarked... QScopedPointer btc( data->create() ); if( btc && btc->isBookmarkable() && btc->bookmarkAction() ) actions.append( btc->bookmarkAction() ); } } } return actions; } QActionList CollectionTreeView::createCollectionActions( const QModelIndexList &indices ) { QActionList actions; // Extract collection whose constituent was selected CollectionTreeItem *item = static_cast( indices.first().internalPointer() ); // Don't return any collection actions for non collection items if( item->isDataItem() ) return actions; Collection *collection = item->parentCollection(); // Generate CollectionCapability, test for existence QScopedPointer cc( collection->create() ); if( cc ) actions = cc->actions(); return actions; } QHash CollectionTreeView::getCopyActions( const QModelIndexList &indices ) { QHash currentCopyDestination; if( onlyOneCollection( indices ) ) { Collection *collection = getCollection( indices.first() ); QList writableCollections; QHash hash = CollectionManager::instance()->collections(); QHash::const_iterator it = hash.constBegin(); while( it != hash.constEnd() ) { Collection *coll = it.key(); if( coll && coll->isWritable() && coll != collection ) writableCollections.append( coll ); ++it; } if( !writableCollections.isEmpty() ) { foreach( Collection *coll, writableCollections ) { QAction *action = new QAction( coll->icon(), coll->prettyName(), 0 ); action->setProperty( "popupdropper_svg_id", "collection" ); connect( action, &QAction::triggered, this, &CollectionTreeView::slotCopyTracks ); currentCopyDestination.insert( action, coll ); } } } return currentCopyDestination; } QHash CollectionTreeView::getMoveActions( const QModelIndexList &indices ) { QHash currentMoveDestination; if( onlyOneCollection( indices ) ) { Collection *collection = getCollection( indices.first() ); QList writableCollections; QHash hash = CollectionManager::instance()->collections(); QHash::const_iterator it = hash.constBegin(); while( it != hash.constEnd() ) { Collection *coll = it.key(); if( coll && coll->isWritable() && coll != collection ) writableCollections.append( coll ); ++it; } if( !writableCollections.isEmpty() ) { if( collection->isWritable() ) { foreach( Collection *coll, writableCollections ) { QAction *action = new QAction( coll->icon(), coll->prettyName(), 0 ); action->setProperty( "popupdropper_svg_id", "collection" ); connect( action, &QAction::triggered, this, &CollectionTreeView::slotMoveTracks ); currentMoveDestination.insert( action, coll ); } } } } return currentMoveDestination; } bool CollectionTreeView::onlyOneCollection( const QModelIndexList &indices ) { if( !indices.isEmpty() ) { Collection *collection = getCollection( indices.first() ); foreach( const QModelIndex &index, indices ) { Collection *currentCollection = getCollection( index ); if( collection != currentCollection ) return false; } } return true; } Collection * CollectionTreeView::getCollection( const QModelIndex &index ) { Collection *collection = 0; if( index.isValid() ) { CollectionTreeItem *item = static_cast( index.internalPointer() ); while( item->isDataItem() ) item = item->parent(); collection = item->parentCollection(); } return collection; } void CollectionTreeView::slotReplacePlaylistWithChildTracks() { playChildTracks( m_currentItems, Playlist::OnReplacePlaylistAction ); } void CollectionTreeView::slotAppendChildTracks() { playChildTracks( m_currentItems, Playlist::OnAppendToPlaylistAction ); } void CollectionTreeView::slotQueueChildTracks() { playChildTracks( m_currentItems, Playlist::OnQueueToPlaylistAction ); } void CollectionTreeView::slotEditTracks() { editTracks( m_currentItems ); } void CollectionTreeView::slotCopyTracks() { if( !sender() ) return; if( QAction *action = dynamic_cast( sender() ) ) copyTracks( m_currentItems, m_currentCopyDestination[ action ], false ); } void CollectionTreeView::slotMoveTracks() { if( !sender() ) return; if ( QAction *action = dynamic_cast( sender() ) ) copyTracks( m_currentItems, m_currentMoveDestination[ action ], true ); } void CollectionTreeView::slotTrashTracks() { removeTracks( m_currentItems, true ); } void CollectionTreeView::slotDeleteTracks() { removeTracks( m_currentItems, false /* do not use trash */ ); } void CollectionTreeView::slotOrganize() { if( sender() ) { if( QAction *action = dynamic_cast( sender() ) ) { Q_UNUSED( action ) organizeTracks( m_currentItems ); } } } QSet CollectionTreeView::cleanItemSet( const QSet &items ) { QSet parents; foreach( CollectionTreeItem *item, items ) { CollectionTreeItem *tmpItem = item; while( tmpItem ) { if( items.contains( tmpItem->parent() ) ) tmpItem = tmpItem->parent(); else { parents.insert( tmpItem ); break; } } } return parents; } Collections::QueryMaker * CollectionTreeView::createMetaQueryFromItems( const QSet &items, bool cleanItems ) const { if( !m_treeModel ) return 0; QSet parents = cleanItems ? cleanItemSet( items ) : items; QList queryMakers; foreach( CollectionTreeItem *item, parents ) { Collections::QueryMaker *qm = item->queryMaker(); for( CollectionTreeItem *tmp = item; tmp; tmp = tmp->parent() ) tmp->addMatch( qm, m_treeModel->levelCategory( tmp->level() - 1 ) ); Collections::addTextualFilter( qm, m_treeModel->currentFilter() ); queryMakers.append( qm ); } return new Collections::MetaQueryMaker( queryMakers ); } #include "CollectionTreeView.moc" // Q_OBJECTs defined in CollectionTreeView.cpp #include "moc_CollectionTreeView.cpp" // Q_OBJECTs defined in CollectionTreeView.h diff --git a/src/browsers/filebrowser/FileView.cpp b/src/browsers/filebrowser/FileView.cpp index cc41dd7262..6b10e4f170 100644 --- a/src/browsers/filebrowser/FileView.cpp +++ b/src/browsers/filebrowser/FileView.cpp @@ -1,611 +1,609 @@ /**************************************************************************************** * Copyright (c) 2010 Nikolaj Hald Nielsen * * Copyright (c) 2010 Casey Link * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "FileView" #include "FileView.h" #include "EngineController.h" #include "PaletteHandler.h" #include "PopupDropperFactory.h" #include "SvgHandler.h" -// #include "context/ContextView.h" +#include "context/ContextView.h" #include "core/playlists/PlaylistFormat.h" #include "core/support/Debug.h" #include "core-impl/collections/support/CollectionManager.h" #include "core-impl/collections/support/FileCollectionLocation.h" #include "core-impl/meta/file/File.h" #include "core-impl/playlists/types/file/PlaylistFileSupport.h" #include "core-impl/support/TrackLoader.h" #include "dialogs/TagDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include FileView::FileView( QWidget *parent ) : Amarok::PrettyTreeView( parent ) , m_appendAction( 0 ) , m_loadAction( 0 ) , m_editAction( 0 ) , m_moveToTrashAction( 0 ) , m_deleteAction( 0 ) , m_pd( 0 ) , m_ongoingDrag( false ) { setFrameStyle( QFrame::NoFrame ); setItemsExpandable( false ); setRootIsDecorated( false ); setAlternatingRowColors( true ); setUniformRowHeights( true ); setEditTriggers( EditKeyPressed ); The::paletteHandler()->updateItemView( this ); connect( The::paletteHandler(), &PaletteHandler::newPalette, this, &FileView::newPalette ); } void FileView::contextMenuEvent( QContextMenuEvent *e ) { if( !model() ) return; //trying to do fancy stuff while showing places only leads to tears! if( model()->objectName() == "PLACESMODEL" ) { e->accept(); return; } QModelIndexList indices = selectedIndexes(); // Abort if nothing is selected if( indices.isEmpty() ) return; QMenu menu; foreach( QAction *action, actionsForIndices( indices, PlaylistAction ) ) menu.addAction( action ); menu.addSeparator(); // Create Copy/Move to menu items // ported from old filebrowser QList writableCollections; QHash hash = CollectionManager::instance()->collections(); QHash::const_iterator it = hash.constBegin(); while( it != hash.constEnd() ) { Collections::Collection *coll = it.key(); if( coll && coll->isWritable() ) writableCollections.append( coll ); ++it; } if( !writableCollections.isEmpty() ) { QMenu *copyMenu = new QMenu( i18n( "Copy to Collection" ), &menu ); copyMenu->setIcon( QIcon::fromTheme( "edit-copy" ) ); foreach( Collections::Collection *coll, writableCollections ) { CollectionAction *copyAction = new CollectionAction( coll, &menu ); connect( copyAction, &QAction::triggered, this, &FileView::slotPrepareCopyTracks ); copyMenu->addAction( copyAction ); } menu.addMenu( copyMenu ); QMenu *moveMenu = new QMenu( i18n( "Move to Collection" ), &menu ); moveMenu->setIcon( QIcon::fromTheme( "go-jump" ) ); foreach( Collections::Collection *coll, writableCollections ) { CollectionAction *moveAction = new CollectionAction( coll, &menu ); connect( moveAction, &QAction::triggered, this, &FileView::slotPrepareMoveTracks ); moveMenu->addAction( moveAction ); } menu.addMenu( moveMenu ); } foreach( QAction *action, actionsForIndices( indices, OrganizeAction ) ) menu.addAction( action ); menu.addSeparator(); foreach( QAction *action, actionsForIndices( indices, EditAction ) ) menu.addAction( action ); menu.exec( e->globalPos() ); } void FileView::mouseReleaseEvent( QMouseEvent *event ) { QModelIndex index = indexAt( event->pos() ); if( !index.isValid() ) { PrettyTreeView::mouseReleaseEvent( event ); return; } if( state() == QAbstractItemView::NoState && event->button() == Qt::MidButton ) { addIndexToPlaylist( index, Playlist::OnMiddleClickOnSelectedItems ); event->accept(); return; } KFileItem file = index.data( KDirModel::FileItemRole ).value(); if( state() == QAbstractItemView::NoState && event->button() == Qt::LeftButton && event->modifiers() == Qt::NoModifier && style()->styleHint( QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this ) && ( file.isDir() || file.isNull() ) ) { emit navigateToDirectory( index ); event->accept(); return; } PrettyTreeView::mouseReleaseEvent( event ); } void FileView::mouseDoubleClickEvent( QMouseEvent *event ) { QModelIndex index = indexAt( event->pos() ); if( !index.isValid() ) { event->accept(); return; } // swallow middle-button double-clicks if( event->button() == Qt::MidButton ) { event->accept(); return; } if( event->button() == Qt::LeftButton ) { KFileItem file = index.data( KDirModel::FileItemRole ).value(); QUrl url = file.url(); if( !file.isNull() && ( Playlists::isPlaylist( url ) || MetaFile::Track::isTrack( url ) ) ) addIndexToPlaylist( index, Playlist::OnDoubleClickOnSelectedItems ); else emit navigateToDirectory( index ); event->accept(); return; } PrettyTreeView::mouseDoubleClickEvent( event ); } void FileView::keyPressEvent( QKeyEvent *event ) { QModelIndex index = currentIndex(); if( !index.isValid() ) return; switch( event->key() ) { case Qt::Key_Enter: case Qt::Key_Return: { KFileItem file = index.data( KDirModel::FileItemRole ).value(); QUrl url = file.url(); if( !file.isNull() && ( Playlists::isPlaylist( url ) || MetaFile::Track::isTrack( url ) ) ) // right, we test the current item, but then add the selection to playlist addSelectionToPlaylist( Playlist::OnReturnPressedOnSelectedItems ); else emit navigateToDirectory( index ); return; } case Qt::Key_Delete: slotMoveToTrash( Qt::NoButton, event->modifiers() ); break; case Qt::Key_F5: emit refreshBrowser(); break; default: break; } QTreeView::keyPressEvent( event ); } void FileView::slotAppendToPlaylist() { addSelectionToPlaylist( Playlist::OnAppendToPlaylistAction ); } void FileView::slotReplacePlaylist() { addSelectionToPlaylist( Playlist::OnReplacePlaylistAction ); } void FileView::slotEditTracks() { Meta::TrackList tracks = tracksForEdit(); if( !tracks.isEmpty() ) { TagDialog *dialog = new TagDialog( tracks, this ); dialog->show(); } } void FileView::slotPrepareMoveTracks() { if( m_moveDestinationCollection ) return; CollectionAction *action = dynamic_cast( sender() ); if( !action ) return; m_moveDestinationCollection = action->collection(); const KFileItemList list = selectedItems(); if( list.isEmpty() ) return; // prevent bug 313003, require full metadata TrackLoader* dl = new TrackLoader( TrackLoader::FullMetadataRequired ); // auto-deletes itself connect( dl, &TrackLoader::finished, this, &FileView::slotMoveTracks ); dl->init( list.urlList() ); } void FileView::slotPrepareCopyTracks() { if( m_copyDestinationCollection ) return; CollectionAction *action = dynamic_cast( sender() ); if( !action ) return; m_copyDestinationCollection = action->collection(); const KFileItemList list = selectedItems(); if( list.isEmpty() ) return; // prevent bug 313003, require full metadata TrackLoader* dl = new TrackLoader( TrackLoader::FullMetadataRequired ); // auto-deletes itself connect( dl, &TrackLoader::finished, this, &FileView::slotMoveTracks ); dl->init( list.urlList() ); } void FileView::slotCopyTracks( const Meta::TrackList& tracks ) { if( !m_copyDestinationCollection ) return; QSet collections; foreach( const Meta::TrackPtr &track, tracks ) { collections.insert( track->collection() ); } if( collections.count() == 1 ) { Collections::Collection *sourceCollection = collections.values().first(); Collections::CollectionLocation *source; if( sourceCollection ) source = sourceCollection->location(); else source = new Collections::FileCollectionLocation(); Collections::CollectionLocation *destination = m_copyDestinationCollection->location(); source->prepareCopy( tracks, destination ); } else warning() << "Cannot handle copying tracks from multiple collections, doing nothing to be safe"; m_copyDestinationCollection.clear(); } void FileView::slotMoveTracks( const Meta::TrackList& tracks ) { if( !m_moveDestinationCollection ) return; QSet collections; foreach( const Meta::TrackPtr &track, tracks ) { collections.insert( track->collection() ); } if( collections.count() == 1 ) { Collections::Collection *sourceCollection = collections.values().first(); Collections::CollectionLocation *source; if( sourceCollection ) source = sourceCollection->location(); else source = new Collections::FileCollectionLocation(); Collections::CollectionLocation *destination = m_moveDestinationCollection->location(); source->prepareMove( tracks, destination ); } else warning() << "Cannot handle moving tracks from multiple collections, doing nothing to be safe"; m_moveDestinationCollection.clear(); } QList FileView::actionsForIndices( const QModelIndexList &indices, ActionType type ) { QList actions; if( indices.isEmpty() ) return actions; // get out of here! if( !m_appendAction ) { m_appendAction = new QAction( QIcon::fromTheme( "media-track-add-amarok" ), i18n( "&Add to Playlist" ), this ); m_appendAction->setProperty( "popupdropper_svg_id", "append" ); connect( m_appendAction, &QAction::triggered, this, &FileView::slotAppendToPlaylist ); } if( type & PlaylistAction ) actions.append( m_appendAction ); if( !m_loadAction ) { m_loadAction = new QAction( i18nc( "Replace the currently loaded tracks with these", "&Replace Playlist" ), this ); m_loadAction->setProperty( "popupdropper_svg_id", "load" ); connect( m_loadAction, &QAction::triggered, this, &FileView::slotReplacePlaylist ); } if( type & PlaylistAction ) actions.append( m_loadAction ); if( !m_moveToTrashAction ) { m_moveToTrashAction = new QAction( QIcon::fromTheme( "user-trash" ), i18n( "&Move to Trash" ), this ); m_moveToTrashAction->setProperty( "popupdropper_svg_id", "delete_file" ); // key shortcut is only for display purposes here, actual one is determined by View in Model/View classes m_moveToTrashAction->setShortcut( Qt::Key_Delete ); connect( m_moveToTrashAction, &QAction::triggered, this, &FileView::slotMoveToTrashWithoutModifiers ); } if( type & OrganizeAction ) actions.append( m_moveToTrashAction ); if( !m_deleteAction ) { m_deleteAction = new QAction( QIcon::fromTheme( "remove-amarok" ), i18n( "&Delete" ), this ); m_deleteAction->setProperty( "popupdropper_svg_id", "delete_file" ); // key shortcut is only for display purposes here, actual one is determined by View in Model/View classes m_deleteAction->setShortcut( Qt::SHIFT + Qt::Key_Delete ); connect( m_deleteAction, &QAction::triggered, this, &FileView::slotDelete ); } if( type & OrganizeAction ) actions.append( m_deleteAction ); if( !m_editAction ) { m_editAction = new QAction( QIcon::fromTheme( "media-track-edit-amarok" ), i18n( "&Edit Track Details" ), this ); m_editAction->setProperty( "popupdropper_svg_id", "edit" ); connect( m_editAction, &QAction::triggered, this, &FileView::slotEditTracks ); } if( type & EditAction ) { actions.append( m_editAction ); Meta::TrackList tracks = tracksForEdit(); m_editAction->setVisible( !tracks.isEmpty() ); } return actions; } void FileView::addSelectionToPlaylist( Playlist::AddOptions options ) { addIndicesToPlaylist( selectedIndexes(), options ); } void FileView::addIndexToPlaylist( const QModelIndex &idx, Playlist::AddOptions options ) { addIndicesToPlaylist( QModelIndexList() << idx, options ); } void FileView::addIndicesToPlaylist( QModelIndexList indices, Playlist::AddOptions options ) { if( indices.isEmpty() ) return; // let tracks & playlists appear in playlist as they are shown in the view: qSort( indices ); QList urls; foreach( const QModelIndex &index, indices ) { KFileItem file = index.data( KDirModel::FileItemRole ).value(); QUrl url = file.url(); if( file.isDir() || Playlists::isPlaylist( url ) || MetaFile::Track::isTrack( url ) ) { urls << file.url(); } } The::playlistController()->insertOptioned( urls, options ); } void FileView::startDrag( Qt::DropActions supportedActions ) { //setSelectionMode( QAbstractItemView::NoSelection ); // When a parent item is dragged, startDrag() is called a bunch of times. Here we prevent that: m_dragMutex.lock(); if( m_ongoingDrag ) { m_dragMutex.unlock(); return; } m_ongoingDrag = true; m_dragMutex.unlock(); -/* FIXME: disabled temporarily for KF5 porting if( !m_pd ) m_pd = The::popupDropperFactory()->createPopupDropper( Context::ContextView::self() ); if( m_pd && m_pd->isHidden() ) { QModelIndexList indices = selectedIndexes(); QList actions = actionsForIndices( indices ); QFont font; font.setPointSize( 16 ); font.setBold( true ); foreach( QAction *action, actions ) m_pd->addItem( The::popupDropperFactory()->createItem( action ) ); m_pd->show(); } -*/ QTreeView::startDrag( supportedActions ); if( m_pd ) { connect( m_pd, &PopupDropper::fadeHideFinished, m_pd, &PopupDropper::clear ); m_pd->hide(); } m_dragMutex.lock(); m_ongoingDrag = false; m_dragMutex.unlock(); } KFileItemList FileView::selectedItems() const { KFileItemList items; QModelIndexList indices = selectedIndexes(); if( indices.isEmpty() ) return items; foreach( const QModelIndex& index, indices ) { KFileItem item = index.data( KDirModel::FileItemRole ).value(); items << item; } return items; } Meta::TrackList FileView::tracksForEdit() const { Meta::TrackList tracks; QModelIndexList indices = selectedIndexes(); if( indices.isEmpty() ) return tracks; foreach( const QModelIndex &index, indices ) { KFileItem item = index.data( KDirModel::FileItemRole ).value(); Meta::TrackPtr track = CollectionManager::instance()->trackForUrl( item.url() ); if( track ) tracks << track; } return tracks; } void FileView::slotMoveToTrash( Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers ) { Q_UNUSED( buttons ) DEBUG_BLOCK QModelIndexList indices = selectedIndexes(); if( indices.isEmpty() ) return; const bool deleting = modifiers.testFlag( Qt::ShiftModifier ); QString caption; QString labelText; if( deleting ) { caption = i18nc( "@title:window", "Confirm Delete" ); labelText = i18np( "Are you sure you want to delete this item?", "Are you sure you want to delete these %1 items?", indices.count() ); } else { caption = i18nc( "@title:window", "Confirm Move to Trash" ); labelText = i18np( "Are you sure you want to move this item to trash?", "Are you sure you want to move these %1 items to trash?", indices.count() ); } QList urls; QStringList filepaths; foreach( const QModelIndex& index, indices ) { KFileItem file = index.data( KDirModel::FileItemRole ).value(); filepaths << file.localPath(); urls << file.url(); } KGuiItem confirmButton = deleting ? KStandardGuiItem::del() : KStandardGuiItem::remove(); if( KMessageBox::warningContinueCancelList( this, labelText, filepaths, caption, confirmButton ) != KMessageBox::Continue ) return; if( deleting ) { KIO::del( urls, KIO::HideProgressInfo ); return; } KIO::trash( urls, KIO::HideProgressInfo ); } void FileView::slotDelete() { slotMoveToTrash( Qt::NoButton, Qt::ShiftModifier ); } diff --git a/src/browsers/playlistbrowser/PlaylistBrowserView.cpp b/src/browsers/playlistbrowser/PlaylistBrowserView.cpp index 41438e151b..33575ab68e 100644 --- a/src/browsers/playlistbrowser/PlaylistBrowserView.cpp +++ b/src/browsers/playlistbrowser/PlaylistBrowserView.cpp @@ -1,585 +1,581 @@ /**************************************************************************************** * Copyright (c) 2008 Nikolaj Hald Nielsen * * Copyright (c) 2010 Bart Cerneels * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "PlaylistBrowserView" #include "PlaylistBrowserView.h" #include "MainWindow.h" #include "PaletteHandler.h" #include "PopupDropperFactory.h" #include "SvgHandler.h" #include "amarokconfig.h" #include "browsers/playlistbrowser/PlaylistBrowserModel.h" #include "browsers/playlistbrowser/PlaylistsByProviderProxy.h" #include "browsers/playlistbrowser/PlaylistsInFoldersProxy.h" -// #include "context/ContextView.h" +#include "context/ContextView.h" #include "core/support/Debug.h" #include "core-impl/playlists/types/file/PlaylistFileSupport.h" #include "playlist/PlaylistModel.h" #include "playlistmanager/PlaylistManager.h" #include "widgets/PrettyTreeRoles.h" #include #include #include #include #include #include #include #include using namespace PlaylistBrowserNS; PlaylistBrowserNS::PlaylistBrowserView::PlaylistBrowserView( QAbstractItemModel *model, QWidget *parent ) : Amarok::PrettyTreeView( parent ) , m_pd( 0 ) , m_ongoingDrag( false ) { DEBUG_BLOCK setModel( model ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectItems ); setDragDropMode( QAbstractItemView::DragDrop ); setAcceptDrops( true ); setEditTriggers( QAbstractItemView::EditKeyPressed ); setMouseTracking( true ); // needed for highlighting provider action icons m_createEmptyPlaylistAction = new QAction( QIcon::fromTheme( "media-track-add-amarok" ), i18n( "Create an Empty Playlist" ), this ); connect( m_createEmptyPlaylistAction, &QAction::triggered, this, &PlaylistBrowserView::slotCreateEmptyPlaylist ); m_appendAction = new QAction( QIcon::fromTheme( "media-track-add-amarok" ), i18n( "&Add to Playlist" ), this ); m_appendAction->setProperty( "popupdropper_svg_id", "append" ); connect( m_appendAction, &QAction::triggered, this, &PlaylistBrowserView::slotAppend ); m_loadAction = new QAction( QIcon::fromTheme( "folder-open" ), i18nc( "Replace the currently " "loaded tracks with these", "&Replace Playlist" ), this ); m_loadAction->setProperty( "popupdropper_svg_id", "load" ); connect( m_loadAction, &QAction::triggered, this, &PlaylistBrowserView::slotLoad ); m_setNewAction = new QAction( QIcon::fromTheme( "rating" ), i18nc( "toggle the \"new\" status " " of this podcast episode", "&New" ), this ); m_setNewAction->setProperty( "popupdropper_svg_id", "new" ); m_setNewAction->setCheckable( true ); connect( m_setNewAction, &QAction::triggered, this, &PlaylistBrowserView::slotSetNew ); m_renamePlaylistAction = new QAction( QIcon::fromTheme( "media-track-edit-amarok" ), i18n( "&Rename..." ), this ); m_renamePlaylistAction->setProperty( "popupdropper_svg_id", "edit" ); // key shortcut is only for display purposes here, actual one is determined by View in Model/View classes m_renamePlaylistAction->setShortcut( Qt::Key_F2 ); connect( m_renamePlaylistAction, &QAction::triggered, this, &PlaylistBrowserView::slotRename ); m_deletePlaylistAction = new QAction( QIcon::fromTheme( "media-track-remove-amarok" ), i18n( "&Delete..." ), this ); m_deletePlaylistAction->setProperty( "popupdropper_svg_id", "delete" ); // key shortcut is only for display purposes here, actual one is determined by View in Model/View classes m_deletePlaylistAction->setShortcut( Qt::Key_Delete ); connect( m_deletePlaylistAction, &QAction::triggered, this, &PlaylistBrowserView::slotDelete ); m_removeTracksAction = new QAction( QIcon::fromTheme( "media-track-remove-amarok" ), QString( "" ), this ); m_removeTracksAction->setProperty( "popupdropper_svg_id", "delete" ); // key shortcut is only for display purposes here, actual one is determined by View in Model/View classes m_removeTracksAction->setShortcut( Qt::Key_Delete ); connect( m_removeTracksAction, &QAction::triggered, this, &PlaylistBrowserView::slotRemoveTracks ); m_exportAction = new QAction( QIcon::fromTheme( "document-export-amarok" ), i18n( "&Export As..." ), this ); connect( m_exportAction, &QAction::triggered, this, &PlaylistBrowserView::slotExport ); m_separatorAction = new QAction( this ); m_separatorAction->setSeparator( true ); } void PlaylistBrowserNS::PlaylistBrowserView::setModel( QAbstractItemModel *model ) { if( this->model() ) disconnect( this->model(), 0, this, 0 ); Amarok::PrettyTreeView::setModel( model ); connect( this->model(), SIGNAL(renameIndex(QModelIndex)), SLOT(edit(QModelIndex)) ); } void PlaylistBrowserNS::PlaylistBrowserView::mouseReleaseEvent( QMouseEvent *event ) { if( m_pd ) { connect( m_pd, &PopupDropper::fadeHideFinished, m_pd, &QObject::deleteLater ); m_pd->hide(); m_pd = 0; } QModelIndex index = indexAt( event->pos() ); if( !index.isValid() ) { PrettyTreeView::mouseReleaseEvent( event ); return; } if( event->button() == Qt::MidButton ) { insertIntoPlaylist( index, Playlist::OnMiddleClickOnSelectedItems ); event->accept(); return; } PrettyTreeView::mouseReleaseEvent( event ); } void PlaylistBrowserNS::PlaylistBrowserView::startDrag( Qt::DropActions supportedActions ) { // Waah? when a parent item is dragged, startDrag is called a bunch of times if( m_ongoingDrag ) return; m_ongoingDrag = true; -/* FIXME: disabled temporarily for KF5 porting if( !m_pd ) m_pd = The::popupDropperFactory()->createPopupDropper( Context::ContextView::self() ); if( m_pd && m_pd->isHidden() ) { QActionList actions = actionsFor( selectedIndexes() ); foreach( QAction *action, actions ) m_pd->addItem( The::popupDropperFactory()->createItem( action ) ); m_pd->show(); } -*/ QTreeView::startDrag( supportedActions ); // We keep the items that the actions need to be applied to. // Clear the data from all actions now that the PUD has executed. resetActionTargets(); -/* FIXME: disabled temporarily for KF5 porting if( m_pd ) { connect( m_pd, &PopupDropper::fadeHideFinished, m_pd, &PopupDropper::clear ); m_pd->hide(); } -*/ m_ongoingDrag = false; } void PlaylistBrowserNS::PlaylistBrowserView::keyPressEvent( QKeyEvent *event ) { QModelIndexList indices = selectedIndexes(); // mind bug 305203 if( indices.isEmpty() || state() != QAbstractItemView::NoState ) { Amarok::PrettyTreeView::keyPressEvent( event ); return; } switch( event->key() ) { //activated() only works for current index, not all selected case Qt::Key_Enter: case Qt::Key_Return: insertIntoPlaylist( indices, Playlist::OnReturnPressedOnSelectedItems ); return; case Qt::Key_Delete: { QActionList actions = actionsFor( indices ); // sets action targets if( actions.contains( m_removeTracksAction ) ) m_removeTracksAction->trigger(); else if( actions.contains( m_deletePlaylistAction ) ) m_deletePlaylistAction->trigger(); resetActionTargets(); return; } default: break; } Amarok::PrettyTreeView::keyPressEvent( event ); } void PlaylistBrowserNS::PlaylistBrowserView::mouseDoubleClickEvent( QMouseEvent *event ) { if( event->button() == Qt::MidButton ) { event->accept(); return; } QModelIndex index = indexAt( event->pos() ); if( !index.isValid() ) { event->accept(); return; } // code copied in CollectionTreeView::mouseDoubleClickEvent(), keep in sync // mind bug 279513 bool isExpandable = model()->hasChildren( index ); bool wouldExpand = !visualRect( index ).contains( event->pos() ) || // clicked outside item, perhaps on expander icon ( isExpandable && !style()->styleHint( QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this ) ); // we're in doubleClick if( event->button() == Qt::LeftButton && event->modifiers() == Qt::NoModifier && !wouldExpand ) { insertIntoPlaylist( index, Playlist::OnDoubleClickOnSelectedItems ); event->accept(); return; } PrettyTreeView::mouseDoubleClickEvent( event ); } void PlaylistBrowserNS::PlaylistBrowserView::contextMenuEvent( QContextMenuEvent *event ) { QModelIndex clickedIdx = indexAt( event->pos() ); QModelIndexList indices; if( clickedIdx.isValid() && selectedIndexes().contains( clickedIdx ) ) indices << selectedIndexes(); else if( clickedIdx.isValid() ) indices << clickedIdx; QActionList actions = actionsFor( indices ); if( actions.isEmpty() ) { resetActionTargets(); return; } QMenu menu; foreach( QAction *action, actions ) menu.addAction( action ); menu.exec( mapToGlobal( event->pos() ) ); // We keep the items that the action need to be applied to. // Clear the data from all actions now that the context menu has executed. resetActionTargets(); } QList PlaylistBrowserNS::PlaylistBrowserView::actionsFor( const QModelIndexList &indexes ) { resetActionTargets(); if( indexes.isEmpty() ) return QActionList(); using namespace Playlists; QSet providers, writableProviders; QActionList actions; QModelIndexList newPodcastEpisodes, oldPodcastEpisodes; foreach( const QModelIndex &idx, indexes ) { // direct provider actions: actions << idx.data( PrettyTreeRoles::DecoratorRole ).value(); PlaylistProvider *provider = idx.data( PlaylistBrowserModel::ProviderRole ).value(); if( provider ) providers << provider; bool isWritable = provider ? provider->isWritable() : false; if( isWritable ) writableProviders |= provider; Meta::TrackPtr track = idx.data( PlaylistBrowserModel::TrackRole ).value(); PlaylistPtr playlist = idx.data( PlaylistBrowserModel::PlaylistRole ).value(); if( !track && playlist ) // a playlist (must check it is not a track) { m_actionPlaylists << playlist; if( isWritable ) m_writableActionPlaylists << playlist; } if( track ) { m_actionTracks.insert( playlist, idx.row() ); if( isWritable ) m_writableActionTracks.insert( playlist, idx.row() ); } QVariant episodeIsNew = idx.data( PlaylistBrowserModel::EpisodeIsNewRole ); if( episodeIsNew.type() == QVariant::Bool ) { if( episodeIsNew.toBool() ) newPodcastEpisodes << idx; else oldPodcastEpisodes << idx; } } // all actions taking provider have only sense with one provider if( writableProviders.count() == 1 ) m_writableActionProvider = writableProviders.toList().first(); // process per-provider actions foreach( PlaylistProvider *provider, providers ) { // prepare arguments and get relevant actions PlaylistList providerPlaylists; foreach( const PlaylistPtr &playlist, m_actionPlaylists ) { if( playlist->provider() == provider ) providerPlaylists << playlist; } actions << provider->playlistActions( providerPlaylists ); QMultiHash playlistTracks; QHashIterator it( m_actionTracks ); while( it.hasNext() ) { it.next(); if( it.key()->provider() == provider ) playlistTracks.insert( it.key(), it.value() ); } actions << provider->trackActions( playlistTracks ); } // separate model actions from standard actions we provide (at the top) QActionList standardActions; if( m_actionPlaylists.isEmpty() && m_actionTracks.isEmpty() && m_writableActionProvider ) standardActions << m_createEmptyPlaylistAction; if( !m_actionPlaylists.isEmpty() || !m_actionTracks.isEmpty() ) standardActions << m_appendAction << m_loadAction; if( !newPodcastEpisodes.isEmpty() || !oldPodcastEpisodes.isEmpty() ) { m_setNewAction->setChecked( oldPodcastEpisodes.isEmpty() ); m_setNewAction->setData( QVariant::fromValue( newPodcastEpisodes + oldPodcastEpisodes ) ); standardActions << m_setNewAction; } if( m_writableActionPlaylists.count() == 1 && m_actionTracks.isEmpty() ) standardActions << m_renamePlaylistAction; if( !m_writableActionPlaylists.isEmpty() && m_actionTracks.isEmpty() ) standardActions << m_deletePlaylistAction; if( m_actionPlaylists.isEmpty() && !m_writableActionTracks.isEmpty() ) { const int actionTrackCount = m_writableActionTracks.count(); const int playlistCount = m_writableActionTracks.uniqueKeys().count(); if( playlistCount > 1 ) m_removeTracksAction->setText( i18nc( "%1: number of tracks. %2: number of playlists", "Remove %1 From %2", i18ncp ("First part of 'Remove %1 From %2'", "a Track", "%1 Tracks", actionTrackCount), i18ncp ("Second part of 'Remove %1 From %2'", "1 Playlist", "%1 Playlists", playlistCount ) ) ); else m_removeTracksAction->setText( i18ncp( "%2 is saved playlist name", "Remove a Track From %2", "Remove %1 Tracks From %2", actionTrackCount, m_writableActionTracks.uniqueKeys().first()->prettyName() ) ); standardActions << m_removeTracksAction; } if( m_actionPlaylists.count() == 1 && m_actionTracks.isEmpty() ) standardActions << m_exportAction; standardActions << m_separatorAction; return standardActions + actions; } void PlaylistBrowserView::resetActionTargets() { m_writableActionProvider = 0; m_actionPlaylists.clear(); m_writableActionPlaylists.clear(); m_actionTracks.clear(); m_writableActionTracks.clear(); } void PlaylistBrowserNS::PlaylistBrowserView::currentChanged( const QModelIndex ¤t, const QModelIndex &previous ) { Q_UNUSED( previous ) emit currentItemChanged( current ); Amarok::PrettyTreeView::currentChanged( current, previous ); } void PlaylistBrowserView::slotCreateEmptyPlaylist() { // m_actionProvider may be null, which is fine The::playlistManager()->save( Meta::TrackList(), Amarok::generatePlaylistName( Meta::TrackList() ), m_writableActionProvider ); } void PlaylistBrowserView::slotAppend() { insertIntoPlaylist( Playlist::OnAppendToPlaylistAction ); } void PlaylistBrowserView::slotLoad() { insertIntoPlaylist( Playlist::OnReplacePlaylistAction ); } void PlaylistBrowserView::slotSetNew( bool newState ) { QModelIndexList indices = m_setNewAction->data().value(); foreach( const QModelIndex &idx, indices ) model()->setData( idx, newState, PlaylistBrowserModel::EpisodeIsNewRole ); } void PlaylistBrowserView::slotRename() { if( m_writableActionPlaylists.count() != 1 ) { warning() << __PRETTY_FUNCTION__ << "m_writableActionPlaylists.count() is not 1"; return; } Playlists::PlaylistPtr playlist = m_writableActionPlaylists.at( 0 ); // TODO: this makes a rather complicated round-trip and ends up in edit(QModelIndex) // here -- simplify that The::playlistManager()->rename( playlist ); } void PlaylistBrowserView::slotDelete() { if( m_writableActionPlaylists.isEmpty() ) return; using namespace Playlists; QHash providerPlaylists; foreach( const PlaylistPtr &playlist, m_writableActionPlaylists ) { if( playlist->provider() ) providerPlaylists[ playlist->provider() ] << playlist; } QStringList providerNames; foreach( const PlaylistProvider *provider, providerPlaylists.keys() ) providerNames << provider->prettyName(); auto button = QMessageBox::question( The::mainWindow(), i18n( "Confirm Playlist Deletion" ), i18nc( "%1 is playlist provider pretty name", "Delete playlist from %1.", providerNames.join( ", " ) ), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes ); if( button == QMessageBox::Yes ) { foreach( PlaylistProvider *provider, providerPlaylists.keys() ) provider->deletePlaylists( providerPlaylists.value( provider ) ); } } void PlaylistBrowserView::slotRemoveTracks() { foreach( Playlists::PlaylistPtr playlist, m_writableActionTracks.uniqueKeys() ) { QList trackIndices = m_writableActionTracks.values( playlist ); qSort( trackIndices ); int removed = 0; foreach( int trackIndex, trackIndices ) { playlist->removeTrack( trackIndex - removed /* account for already removed */ ); removed++; } } } void PlaylistBrowserView::slotExport() { if( m_actionPlaylists.count() != 1 ) { warning() << __PRETTY_FUNCTION__ << "m_actionPlaylists.count() is not 1"; return; } Playlists::PlaylistPtr playlist = m_actionPlaylists.at( 0 ); // --- display save location dialog // compare with MainWindow::exportPlaylist // TODO: have this code only once QFileDialog fileDialog; fileDialog.restoreState( Amarok::config( "playlist-export-dialog" ).readEntry( "state", QByteArray() ) ); // FIXME: Make checkbox visible in dialog QCheckBox *saveRelativeCheck = new QCheckBox( i18n("Use relative path for &saving"), &fileDialog ); saveRelativeCheck->setChecked( AmarokConfig::relativePlaylist() ); QStringList supportedMimeTypes; supportedMimeTypes << "video/x-ms-asf"; //ASX supportedMimeTypes << "audio/x-mpegurl"; //M3U supportedMimeTypes << "audio/x-scpls"; //PLS supportedMimeTypes << "application/xspf+xml"; //XSPF fileDialog.setMimeTypeFilters( supportedMimeTypes ); fileDialog.setAcceptMode( QFileDialog::AcceptSave ); fileDialog.setFileMode( QFileDialog::AnyFile ); fileDialog.setWindowTitle( i18n("Save As") ); fileDialog.setObjectName( "PlaylistExport" ); int result = fileDialog.exec(); QString playlistPath = fileDialog.selectedFiles().value( 0 ); if( result == QDialog::Accepted && !playlistPath.isEmpty() ) Playlists::exportPlaylistFile( playlist->tracks(), QUrl::fromLocalFile( playlistPath ) ); Amarok::config( "playlist-export-dialog" ).writeEntry( "state", fileDialog.saveState() ); } void PlaylistBrowserView::insertIntoPlaylist( const QModelIndex &index, Playlist::AddOptions options ) { insertIntoPlaylist( QModelIndexList() << index, options ); } void PlaylistBrowserView::insertIntoPlaylist( const QModelIndexList &list, Playlist::AddOptions options ) { actionsFor( list ); // sets action targets insertIntoPlaylist( options ); resetActionTargets(); } void PlaylistBrowserView::insertIntoPlaylist( Playlist::AddOptions options ) { Meta::TrackList tracks; // add tracks for fully-selected playlists: foreach( Playlists::PlaylistPtr playlist, m_actionPlaylists ) { tracks << playlist->tracks(); } // filter-out tracks from playlists that are selected, add lone tracks: foreach( Playlists::PlaylistPtr playlist, m_actionTracks.uniqueKeys() ) { if( m_actionPlaylists.contains( playlist ) ) continue; Meta::TrackList playlistTracks = playlist->tracks(); QList positions = m_actionTracks.values( playlist ); qSort( positions ); foreach( int position, positions ) { if( position >= 0 && position < playlistTracks.count() ) tracks << playlistTracks.at( position ); } } if( !tracks.isEmpty() ) The::playlistController()->insertOptioned( tracks, options ); } diff --git a/src/context/AmarokContextPackageStructure.cpp b/src/context/AmarokContextPackageStructure.cpp new file mode 100644 index 0000000000..be15e783ff --- /dev/null +++ b/src/context/AmarokContextPackageStructure.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 Malte Veerman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "AmarokContextPackageStructure.h" + +#include + +void AmarokContextPackageStructure::initPackage(KPackage::Package* package) +{ + package->addFileDefinition("mainscript", QStringLiteral("ui/main.qml"), i18n("Main Script File")); + package->setDefaultPackageRoot(QStringLiteral("kpackage/amarok")); + package->addDirectoryDefinition("images", QStringLiteral("images"), i18n("Images")); + auto mimetypes = QStringList() << QStringLiteral("image/svg+xml"); + package->setMimeTypes("images", mimetypes); +} diff --git a/src/context/AmarokContextPackageStructure.h b/src/context/AmarokContextPackageStructure.h new file mode 100644 index 0000000000..339e2081d4 --- /dev/null +++ b/src/context/AmarokContextPackageStructure.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 Malte Veerman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef AMAROKCONTEXTPACKAGESTRUCTURE_H +#define AMAROKCONTEXTPACKAGESTRUCTURE_H + +#include + +class AmarokContextPackageStructure : public KPackage::PackageStructure +{ + Q_OBJECT + +public: + virtual void initPackage(KPackage::Package *package) Q_DECL_OVERRIDE; +}; + +#endif // AMAROKCONTEXTPACKAGESTRUCTURE_H diff --git a/src/context/Applet.cpp b/src/context/Applet.cpp deleted file mode 100644 index da9bfe3c3a..0000000000 --- a/src/context/Applet.cpp +++ /dev/null @@ -1,373 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007-2009 Leo Franchi * - * Copyright (c) 2009 Riccardo Iaconelli * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "Context::Applet" - -#include "Applet.h" - -#include "amarokconfig.h" -#include "Containment.h" -#include "PaletteHandler.h" -#include "context/ContextView.h" -#include "core/support/Debug.h" -#include "context/widgets/AppletHeader.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace Context -{ - -} // Context namespace - -Context::Applet::Applet( QObject * parent, const QVariantList& args ) - : Plasma::Applet( parent, args ) - , m_canAnimate( !KServiceTypeTrader::self()->query("Plasma/Animator", QString()).isEmpty() ) - , m_heightCollapseOff( 0 ) - , m_header( 0 ) - , m_transient( 0 ) - , m_standardPadding( 6.0 ) -{ - setBackgroundHints( NoBackground ); -} - -Context::Applet::~Applet() -{ -} - -QFont -Context::Applet::shrinkTextSizeToFit( const QString& text, const QRectF& bounds ) -{ - Q_UNUSED( text ); - - int size = 13; // start here, shrink if needed - QFont font( QString(), size, QFont::Light ); - font.setStyleHint( QFont::SansSerif ); - font.setStyleStrategy( QFont::PreferAntialias ); - - QFontMetrics fm( font ); - while( fm.height() > bounds.height() + 4 ) - { - if( size < 0 ) - { - size = 5; - break; - } - size--; - fm = QFontMetrics( QFont( QString(), size ) ); - } - - // for aesthetics, we make it one smaller - size--; - - QFont returnFont( QString(), size, QFont::Light ); - font.setStyleHint( QFont::SansSerif ); - font.setStyleStrategy( QFont::PreferAntialias ); - - return QFont( returnFont ); -} - -QString -Context::Applet::truncateTextToFit( const QString &text, const QFont& font, const QRectF& bounds ) -{ - QFontMetrics fm( font ); - return fm.elidedText ( text, Qt::ElideRight, (int)bounds.width() ); -} - -void -Context::Applet::paintInterface( QPainter *p, - const QStyleOptionGraphicsItem *option, - const QRect &contentsRect ) -{ - Plasma::Applet::paintInterface( p, option, contentsRect ); - addGradientToAppletBackground( p ); -} - -void -Context::Applet::addGradientToAppletBackground( QPainter* p ) -{ - // tint the whole applet - // draw non-gradient backround. going for elegance and style - const QRectF roundRect = boundingRect().adjusted( 0, 1, -1, -1 ); - - p->save(); - p->setRenderHint( QPainter::Antialiasing ); - QPainterPath path; - path.addRoundedRect( roundRect, 4, 4 ); - QColor highlight = PaletteHandler::highlightColor( 0.4, 1.05 ); - highlight.setAlphaF( highlight.alphaF() * 0.5 ); - p->fillPath( path, highlight ); - p->restore(); - - p->save(); - p->setRenderHint( QPainter::Antialiasing ); - p->translate( 0.5, 0.5 ); - QColor col = PaletteHandler::highlightColor( 0.3, 0.5 ); - col.setAlphaF( col.alphaF() * 0.7 ); - p->setPen( col ); - p->drawRoundedRect( roundRect, 4, 4 ); - p->restore(); -} - -qreal -Context::Applet::standardPadding() -{ - return m_standardPadding; -} - -void -Context::Applet::destroy() -{ - if ( Plasma::Applet::immutability() != Plasma::Mutable || m_transient ) { - return; //don't double delete - } - m_transient = true; - cleanUpAndDelete(); -} - -void -Context::Applet::cleanUpAndDelete() -{ - QGraphicsWidget *parent = dynamic_cast( parentItem() ); - //it probably won't matter, but right now if there are applethandles, *they* are the parent. - //not the containment. - - //is the applet in a containment and is the containment have a layout? if yes, we remove the applet in the layout - if ( parent && parent->layout() ) - { - QGraphicsLayout *l = parent->layout(); - for ( int i = 0; i < l->count(); ++i ) - { - if ( this == l->itemAt( i ) ) - { - l->removeAt( i ); - break; - } - } - } - - if ( Plasma::Applet::configScheme() ) { - Plasma::Applet::configScheme()->setDefaults(); - } - Plasma::Applet::scene()->removeItem( this ); - Plasma::Applet::deleteLater(); -} - -Plasma::IconWidget* -Context::Applet::addAction( QGraphicsItem *parent, QAction *action, const int size ) -{ - if( !action ) - return 0; - - Plasma::IconWidget *tool = new Plasma::IconWidget( parent ); - tool->setAction( action ); - tool->setText( QString() ); - tool->setToolTip( action->text() ); - tool->setDrawBackground( false ); - tool->setOrientation( Qt::Horizontal ); - const QSizeF iconSize = tool->sizeFromIconSize( size ); - tool->setMinimumSize( iconSize ); - tool->setMaximumSize( iconSize ); - tool->resize( iconSize ); - tool->setZValue( zValue() + 1 ); - - return tool; -} - -void -Context::Applet::enableHeader( bool enable ) -{ - if( m_header ) - { - delete m_header; - m_header = 0; - } - - if( enable ) - { - m_header = new AppletHeader( this ); - m_header->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); - } -} - -Plasma::IconWidget * -Context::Applet::addLeftHeaderAction( QAction *action ) -{ - if( !m_header ) - return 0; - - Plasma::IconWidget *icon = addAction( 0, action ); - m_header->addIcon( icon, Qt::AlignLeft ); - return icon; -} - -Plasma::IconWidget * -Context::Applet::addRightHeaderAction( QAction *action ) -{ - if( !m_header ) - return 0; - - Plasma::IconWidget *icon = addAction( 0, action ); - m_header->addIcon( icon, Qt::AlignRight ); - return icon; -} - -QString -Context::Applet::headerText() const -{ - if( !m_header ) - return QString(); - return m_header->titleText(); -} - -void -Context::Applet::setHeaderText( const QString &text ) -{ - if( !m_header ) - return; - m_header->setTitleText( text ); -} - -bool -Context::Applet::isAnimating() const -{ - QSharedPointer anim = m_animation.toStrongRef(); - if( anim ) - return (anim->state() == QAbstractAnimation::Running); - return false; -} - -bool -Context::Applet::isCollapsed() const -{ - return m_heightCollapseOn == preferredHeight(); -} - -void -Context::Applet::setCollapseOn() -{ - collapse( true ); -} - -void -Context::Applet::setCollapseOff() -{ - collapse( false ); -} - -void -Context::Applet::collapse( bool on ) -{ - qreal finalHeight = ( on ) ? m_heightCollapseOn : m_heightCollapseOff; - const qreal maxHeight = containment()->size().height(); - if( (finalHeight > maxHeight) || (finalHeight < 0) ) - finalHeight = maxHeight; - - prepareGeometryChange(); - // warning: view() currently can return pointer to ToolbarView, not the ContextView - ContextView *v = ContextView::self(); // may return null - // Plasma::Applet::view() might return 0, if the widget is not yet constructed, etc. - // \sa https://bugs.kde.org/show_bug.cgi?id=258741. If view is not available - // yet, regardless of the animation setting the preferred size is set - // straight away. - if( !v || !AmarokConfig::animateAppletCollapse() ) - { - setPreferredHeight( finalHeight ); - emit sizeHintChanged( Qt::PreferredSize ); - updateGeometry(); - return; - } - - if( finalHeight == size().height() ) - return; - - // debug() << pluginName() << (on ? "collapsing to" : "uncollapsing to") << finalHeight; - QPropertyAnimation *pan = m_animation.data(); - if( !pan ) - pan = new QPropertyAnimation( this, "preferredSize" ); - if( pan->state() == QAbstractAnimation::Running ) - pan->stop(); - pan->setDuration( 600 ); - pan->setEasingCurve( QEasingCurve::InQuad ); - pan->setStartValue( size() ); - pan->setEndValue( QSizeF(size().width(), finalHeight) ); - connect( pan, SIGNAL(finished()), SLOT(collapseAnimationFinished()) ); - m_animation = pan; - pan->setDirection( QAbstractAnimation::Forward ); - - v->addCollapseAnimation( pan ); -} - -int -Context::Applet::collapseHeight() const -{ - return m_heightCollapseOn; -} - -int -Context::Applet::collapseOffHeight() const -{ - return m_heightCollapseOff; -} - -void -Context::Applet::collapseAnimationFinished() -{ - emit sizeHintChanged( Qt::PreferredSize ); - updateConstraints(); - update(); -} - -void -Context::Applet::setCollapseHeight( int h ) -{ - m_heightCollapseOn = h; -} - -void -Context::Applet::setCollapseOffHeight( int h ) -{ - m_heightCollapseOff = h; -} - -bool Context::Applet::canAnimate() -{ - return m_canAnimate; -} - -void -Context::Applet::showWarning( const QString &message, const char *slot ) -{ - int ret = KMessageBox::warningYesNo( 0, message ); - Plasma::MessageButton button = (ret == KMessageBox::Yes) ? Plasma::ButtonYes : Plasma::ButtonNo; - QByteArray sig = QMetaObject::normalizedSignature( slot ); - sig.remove( 0, 1 ); // remove method type - QMetaMethod me = metaObject()->method( metaObject()->indexOfSlot(sig) ); - QGenericArgument arg1 = Q_ARG( const Plasma::MessageButton, button ); - if( !me.invoke( this, arg1 ) ) - warning() << "invoking failed:" << sig; -} - diff --git a/src/context/Applet.h b/src/context/Applet.h deleted file mode 100644 index 93b1f1e248..0000000000 --- a/src/context/Applet.h +++ /dev/null @@ -1,210 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_APPLET_H -#define AMAROK_APPLET_H - -#include "amarok_export.h" - -#include -#include - -#include -#include -#include -#include - -class QPainter; -class QPropertyAnimation; - -namespace Plasma -{ - class IconWidget; -} - -namespace Context -{ - -class AppletHeader; - -class AMAROK_EXPORT Applet : public Plasma::Applet -{ - Q_OBJECT - Q_PROPERTY( int collapseHeight READ collapseHeight WRITE setCollapseHeight ) - Q_PROPERTY( int collapseOffHeight READ collapseOffHeight WRITE setCollapseOffHeight ) - Q_PROPERTY( QString headerText READ headerText WRITE setHeaderText ) - - public: - explicit Applet( QObject* parent, const QVariantList& args = QVariantList() ); - ~Applet(); - - /** - * Return a QFont that will allow the given text to fit within the rect. - */ - QFont shrinkTextSizeToFit( const QString& text, const QRectF& bounds ); - - /** - * Truncate the text by adding an ellipsis at the end in order to make the text with the given font - * rit in the bounding rect. - */ - QString truncateTextToFit( const QString &text, const QFont& font, const QRectF& bounds ); - - void paintInterface( QPainter *p, const QStyleOptionGraphicsItem *option, const QRect &contentsRect ); - - /** - * Returns a standard CV-wide padding that applets can use for consistency. - */ - qreal standardPadding(); - - /** - * Creates a header for showing title text and icon widgets, if enabled. - */ - void enableHeader( bool enable = true ); - - /** - * Adds an action on the left of the header text. A header needs to be - * created by enableHeader() first. - * @param action Action to add - * @return An icon widget created for the action - */ - Plasma::IconWidget *addLeftHeaderAction( QAction *action ); - - /** - * Adds an action on the right of the header text. A header needs to be - * created by enableHeader() first. - * @param action Action to add - * @return An icon widget created for the action - */ - Plasma::IconWidget *addRightHeaderAction( QAction *action ); - - /** - * Returns the current header text. If no header exists this will return - * an empty string. - */ - QString headerText() const; - - /** - * Sets the text shown in the header. If no header exists this method - * does nothing. - */ - void setHeaderText( const QString &text ); - - /** - * Set the preferred applet height when collapsed. - * The actual height when collapsed will be constrained by the applet's - * minimum and maximum heights, as well as the current available height - * from the containment. - */ - void setCollapseHeight( int ); - - /** - * Set the preferred applet height when uncollapsed. - * The actual height when collapsed will be constrained by the applet's - * minimum and maximum heights, as well as the current available height - * from the containment. Depending on the vertical size policy, the - * other applets currently showing, and the aforementioned constraints, - * the actual height when collapse is off may be different. This is so - * that, for example, the applet may take up the rest of the space when - * the size policy is set to Expanding. Setting this height to -1 will - * tell the layout to give the applet the rest of the available space. - */ - void setCollapseOffHeight( int ); - - /** - * Preferred applet height when collapsed. - */ - int collapseHeight() const; - - /** - * Preferred applet height when uncollapsed. - */ - int collapseOffHeight() const; - - /** - * Whether a collapse animation is currently running. - */ - bool isAnimating() const; - - /** - * Whether the applet is currently collapsed to collapseHeight(). - */ - bool isCollapsed() const; - - /** - * Shows a warning dialog which blocks access to the applet. - * This gives the user the message and a "Yes" and a "No" button. - * NOTE: Only one message/warning can be shown at a time. - * - * @param message The warning message. - * @param slot The slot which is called after either "Yes" or "No" has been clicked. - */ - void showWarning( const QString &message, const char *slot ); - - public Q_SLOTS: - virtual void destroy(); - - /** - * Collapse to collapseHeight(). - */ - void setCollapseOn(); - - /** - * Collapse to collapseOffHeight(). - */ - void setCollapseOff(); - - protected: - /** - * Paint the background of an applet, so it fits with all the other applets. - * Background is *no longer a gradient*. However, please use this to - * stay consistent with other applets. - */ - void addGradientToAppletBackground( QPainter* p ); - - Plasma::IconWidget* addAction( QGraphicsItem *parent, QAction *action, const int size = 16 ); - bool canAnimate(); - - bool m_canAnimate; - int m_heightCurrent; - int m_heightCollapseOn; - int m_heightCollapseOff; - - AppletHeader *m_header; - - - private Q_SLOTS: - void collapseAnimationFinished(); - void collapse( bool on ); - - private: - void cleanUpAndDelete(); - - bool m_transient; - qreal m_standardPadding; - QWeakPointer m_animation; -}; - -} // Context namespace - -/** - * Register an applet when it is contained in a loadable module - */ -#define AMAROK_EXPORT_APPLET(libname, classname) \ -K_PLUGIN_FACTORY(factory, registerPlugin();) \ -K_EXPORT_PLUGIN(factory("amarok_context_applet_" #libname))\ -K_EXPORT_PLUGIN_VERSION(PLASMA_VERSION) - -#endif // multiple inclusion guard diff --git a/src/context/AppletLoader.cpp b/src/context/AppletLoader.cpp new file mode 100644 index 0000000000..c930e8b7dc --- /dev/null +++ b/src/context/AppletLoader.cpp @@ -0,0 +1,77 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +#define DEBUG_PREFIX "AppletLoader" + +#include "AppletLoader.h" + +#include "AmarokContextPackageStructure.h" +#include "core/support/Amarok.h" +#include "core/support/Debug.h" + +#include + +using namespace Context; + +AppletLoader::AppletLoader(QObject *parent) : QObject(parent) +{ + findApplets(); +} + +AppletLoader::~AppletLoader() +{ +} + +QList AppletLoader::applets() const +{ + return m_applets; +} + +QList Context::AppletLoader::enabledApplets() const +{ + QList list; + QStringList enabledApplets = Amarok::config("Context").readEntry("enabledApplets", QStringList()); + + for (const auto &applet : m_applets) + { + if (enabledApplets.contains(applet.pluginId())) + list << applet; + } + return list; +} + +void AppletLoader::findApplets() +{ + DEBUG_BLOCK + + auto loader = KPackage::PackageLoader::self(); + auto structure = new AmarokContextPackageStructure; + loader->addKnownPackageStructure(QStringLiteral("Amarok/ContextApplet"), structure); + m_applets = loader->findPackages(QStringLiteral("Amarok/ContextApplet"), + QString(), + [] (const KPluginMetaData &data) + { return data.serviceTypes().contains(QStringLiteral("Amarok/ContextApplet")); }); + emit finished(m_applets); + + for (const auto &applet : m_applets) + { + debug() << "Applet found:" << applet.name(); + } + debug() << "Number of applets found:" << m_applets.size(); + + if( m_applets.isEmpty() ) + warning() << "No applets found"; +} diff --git a/src/context/Containment.cpp b/src/context/AppletLoader.h similarity index 69% rename from src/context/Containment.cpp rename to src/context/AppletLoader.h index 3ec058d10b..38bb9a3472 100644 --- a/src/context/Containment.cpp +++ b/src/context/AppletLoader.h @@ -1,42 +1,49 @@ /**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * + * Copyright (c) 2017 Malte Veerman * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ -#include "Containment.h" +#ifndef APPLETLOADER_H +#define APPLETLOADER_H -#include "core/support/Debug.h" - -#include +#include +#include +class KPluginMetaData; namespace Context { -Containment::Containment( QGraphicsItem* parent, const QString& serviceId, uint containmentId ) - : Plasma::Containment( parent, serviceId, containmentId ) -{} +class AppletLoader : public QObject +{ + Q_OBJECT -Containment::Containment( QObject* parent, const QVariantList& args ) - : Plasma::Containment( parent, args ) -{} +public: + AppletLoader(QObject *parent = Q_NULLPTR); + ~AppletLoader(); -Containment::~Containment() -{ - DEBUG_BLOCK -} + QList applets() const; + QList enabledApplets() const; + void findApplets(); + +signals: + void finished(QList); +private: + QList m_applets; +}; -} // namespace Context +} +#endif // APPLETLOADER_H diff --git a/src/context/AppletModel.cpp b/src/context/AppletModel.cpp new file mode 100644 index 0000000000..0eb8d9f14f --- /dev/null +++ b/src/context/AppletModel.cpp @@ -0,0 +1,358 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +#define DEBUG_PREFIX "AppletModel" + +#include "AppletModel.h" + +#include "AmarokContextPackageStructure.h" +#include "AppletLoader.h" +#include "core/support/Amarok.h" +#include "core/support/Debug.h" + +#include + +#include + +#include +#include +#include + + +using namespace Context; + + +class Context::AppletPackage : public KPackage::Package +{ +public: + AppletPackage(const KPackage::Package &package) : KPackage::Package(package) {} + + bool operator==(const AppletPackage &p) + { + return metadata() == p.metadata(); + } +}; + + +AppletModel::AppletModel(AppletLoader *loader, QObject* parent) + : QAbstractListModel(parent) + , m_loader(loader) +{ + newApplets(loader->applets()); + connect(loader, &AppletLoader::finished, this, &AppletModel::newApplets); +} + +AppletModel::~AppletModel() +{ +} + +int AppletModel::rowCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent) + + return m_packages.size(); +} + +void AppletModel::newApplets(const QList& applets) +{ + DEBUG_BLOCK + + beginResetModel(); + + m_packages.clear(); + + for (const auto applet : applets) + { + auto loader = KPackage::PackageLoader::self(); + auto structure = new AmarokContextPackageStructure; + loader->addKnownPackageStructure(QStringLiteral("Amarok/Context"), structure); + auto package = loader->loadPackage(QStringLiteral("Amarok/Context"), applet.pluginId()); + + if (package.isValid()) + { + m_packages << package; + } + else + error() << "Error loading package:" << applet.pluginId(); + } + + //Sort applets by name + std::sort(m_packages.begin(), m_packages.end(), [] (const AppletPackage &p1, const AppletPackage &p2) { + return p1.metadata().name() < p2.metadata().name(); + }); + + endResetModel(); +} + +QVariant AppletModel::data(const QModelIndex& index, int role) const +{ + int row = index.row(); + + if (row >= m_packages.size()) + return QVariant(); + + const auto &package = m_packages.at(row); + + switch (role) + { + case Name: + return package.metadata().name(); + + case Id: + return package.metadata().pluginId(); + + case Icon: + return package.metadata().iconName(); + + case Mainscript: + return QUrl::fromLocalFile(package.filePath("mainscript")); + + case Collapsed: + return Amarok::config("Context").readEntry(package.metadata().pluginId() + "_collapsed", false); + + case ContentHeight: + return Amarok::config("Context").readEntry(package.metadata().pluginId() + "_contentHeight", 300); + + case PackagePath: + return QVariant(package.path() + "contents/"); + } + + return QVariant(); +} + +bool Context::AppletModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + int row = index.row(); + + if (row >= m_packages.size()) + return false; + + const auto &package = m_packages.at(row); + + switch (role) + { + case Collapsed: + { + Amarok::config("Context").writeEntry(package.metadata().pluginId() + "_collapsed", value.toBool()); + emit dataChanged(index, index, QVector{role}); + return true; + } + case ContentHeight: + { + Amarok::config("Context").writeEntry(package.metadata().pluginId() + "_contentHeight", value.toReal()); + emit dataChanged(index, index, QVector{role}); + return true; + } + + default: + warning() << (Role) role << "is read-only."; + } + + return false; +} + +QHash< int, QByteArray > AppletModel::roleNames() const +{ + QHash roles; + roles.insert(Name, "name"); + roles.insert(Id, "appletId"); + roles.insert(Icon, "icon"); + roles.insert(Mainscript, "mainscript"); + roles.insert(Collapsed, "collapsed"); + roles.insert(PackagePath, "packagePath"); + roles.insert(ContentHeight, "contentHeight"); + + return roles; +} + +void AppletModel::setAppletCollapsed(const QString& id, bool collapsed) +{ + DEBUG_BLOCK + + debug() << "Set collapsed for applet:" << id << "to:" << collapsed; + + auto package = findPackage(id); + if (package.isValid()) + { + Amarok::config("Context").writeEntry(id + "_collapsed", collapsed); + int row = m_packages.indexOf(package); + auto index = createIndex(row, 0); + emit dataChanged(index, index, QVector{Collapsed}); + } +} + +void Context::AppletModel::setAppletContentHeight(const QString& id, qreal height) +{ + DEBUG_BLOCK + + debug() << "Set content height for applet:" << id << "to:" << height; + + auto package = findPackage(id); + if (package.isValid()) + { + Amarok::config("Context").writeEntry(id + "_contentHeight", height); + int row = m_packages.indexOf(package); + auto index = createIndex(row, 0); + emit dataChanged(index, index, QVector{ContentHeight}); + } +} + +AppletPackage AppletModel::findPackage(const QString& id) +{ + for (const auto &package : m_packages) + { + auto metadata = package.metadata(); + + if (metadata.pluginId() == id) + return package; + } + + warning() << "Applet with id:" << id << "not found."; + return AppletPackage(KPackage::Package()); +} + +AppletProxyModel::AppletProxyModel(AppletModel* appletModel, QObject *parent) + : QSortFilterProxyModel(parent) + , m_appletModel(appletModel) +{ + setSourceModel(appletModel); + setDynamicSortFilter(true); + sort(0); + + connect(m_appletModel->loader(), &AppletLoader::finished, this, &AppletProxyModel::enabledAppletsChanged); +} + +AppletProxyModel::~AppletProxyModel() +{ +} + +QStringList AppletProxyModel::enabledApplets() const +{ + QStringList list; + for (const auto &applet : m_appletModel->loader()->enabledApplets()) + { + list << applet.pluginId(); + } + + std::sort(list.begin(), list.end(), + [] (const QString &a, const QString &b) { + QStringList ae = Amarok::config("Context").readEntry("enabledApplets", QStringList()); + return ae.indexOf(a) < ae.indexOf(b); + } + ); + + return list; +} + +void AppletProxyModel::setAppletEnabled(const QString& id, bool enabled, int place) +{ + DEBUG_BLOCK + + debug() << "Set enabled for applet:" << id << "to:" << enabled; + + if (enabled == appletEnabled(id)) + return; + + QStringList ea = enabledApplets(); + + if (enabled) + { + if (place <= -1) + place = ea.size(); + + debug() << "Applet's new place is" << place; + ea.insert(place, id); + } + else + { + ea.removeAll(id); + } + Amarok::config("Context").writeEntry("enabledApplets", ea); + + debug() << "New enabled applets:" << ea; + + invalidateFilter(); + + emit enabledAppletsChanged(); +} + +void AppletProxyModel::setAppletPlace(const QString& id, int place) +{ + DEBUG_BLOCK + + debug() << "Set place for applet:" << id << "to:" << place; + + int currentPlace = appletPlace(id); + debug() << "Current place is" << currentPlace; + + if (currentPlace == place) + return; + + if (place <= -1) + { + setAppletEnabled(id, false); + return; + } + + if (currentPlace <= -1) + setAppletEnabled(id, true, place); + + QStringList ea = enabledApplets(); + + place = qMin(place, ea.size() - 1); + bool forward = place > currentPlace; + + beginMoveRows(QModelIndex(), currentPlace, currentPlace, QModelIndex(), forward ? place + 1 : place); + ea.move(currentPlace, place); + Amarok::config("Context").writeEntry("enabledApplets", ea); + endMoveRows(); + + debug() << "New enabled applets:" << ea; +} + +int AppletProxyModel::appletPlace(const QString& id) const +{ + return enabledApplets().indexOf(id); +} + +bool AppletProxyModel::appletEnabled(const QString& id) const +{ + return enabledApplets().contains(id); +} + +void Context::AppletProxyModel::clear() +{ + for( const auto &applet : enabledApplets() ) + { + setAppletEnabled( applet, false ); + } +} + +bool AppletProxyModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const +{ + int placeLeft = appletPlace(source_left.data(AppletModel::Id).toString()); + int placeRight = appletPlace(source_right.data(AppletModel::Id).toString()); + + return placeLeft < placeRight; +} + +bool AppletProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const +{ + QModelIndex index = sourceModel()->index(source_row, 0, source_parent); + return appletEnabled(index.data(AppletModel::Id).toString()); +} + + diff --git a/src/context/AppletModel.h b/src/context/AppletModel.h new file mode 100644 index 0000000000..74798bea08 --- /dev/null +++ b/src/context/AppletModel.h @@ -0,0 +1,145 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +#ifndef APPLETMODEL_H +#define APPLETMODEL_H + +#include +#include + + +class KPluginMetaData; + +namespace Context +{ + +class AppletLoader; +class AppletPackage; + +class AppletModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Role + { + Name, + Id, + Icon, + Mainscript, + Collapsed, + PackagePath, + ContentHeight + }; + Q_ENUM(Role) + + AppletModel(AppletLoader *loader, QObject *parent = Q_NULLPTR); + virtual ~AppletModel(); + + virtual int rowCount(const QModelIndex& parent) const Q_DECL_OVERRIDE; + virtual QVariant data(const QModelIndex& index, int role) const Q_DECL_OVERRIDE; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role) Q_DECL_OVERRIDE; + virtual QHash< int, QByteArray > roleNames() const Q_DECL_OVERRIDE; + + AppletLoader* loader() const { return m_loader; } + + Q_INVOKABLE void setAppletCollapsed(const QString &id, bool collapsed); + Q_INVOKABLE void setAppletContentHeight(const QString& id, qreal height); + +public Q_SLOTS: + void newApplets(const QList &applets); + +protected: + AppletPackage findPackage(const QString &id); + +private: + QList m_packages; + AppletLoader *const m_loader; +}; + +class AppletProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + + Q_PROPERTY(QStringList enabledApplets READ enabledApplets NOTIFY enabledAppletsChanged) + +public: + AppletProxyModel(AppletModel *appletModel, QObject *parent = Q_NULLPTR); + virtual ~AppletProxyModel(); + + /** + * @returns QStringList with ids of all enabled applets. + * Sorted in ascending order of place of applets. + */ + QStringList enabledApplets() const; + + /** + * Set an applet to be enabled or disabled. Does nothing if id is invalid. + * Disabling an applet sets its place to -1. + * + * @param id Id of the applet. + * @param enabled Set to true if applet should be enabled and false for disabled. + * @param place The place of the applet after enabling. -1 appends the applet to the end of the list. + * Irrelevant if applet is to be disabled. + */ + Q_INVOKABLE void setAppletEnabled(const QString &id, bool enabled, int place = -1); + + /** + * Set an applet's place. Does nothing if id is invalid. + * Enables the applet if it is disabled. + * + * @param id Id of the applet. + * @param place The new place of the applet. Negative values disable the applet. + */ + Q_INVOKABLE void setAppletPlace(const QString &id, int place); + + /** + * Find out an applet's place. + * + * @returns an integer with the applet's place. -1 if the applet is disabled. + * + * @param id Id of applet's place to be returned. + */ + Q_INVOKABLE int appletPlace(const QString &id) const; + + /** + * Find out if an applet is enabled or disabled. + * + * @returns true if applet is enabled. Returns false if not. + * + * @param id Id of the applet. + */ + Q_INVOKABLE bool appletEnabled(const QString &id) const; + + /** + * Clear the context area by disabling all applets + */ + Q_INVOKABLE void clear(); + +Q_SIGNALS: + void enabledAppletsChanged(); + +protected: + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const Q_DECL_OVERRIDE; + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const Q_DECL_OVERRIDE; + +private: + AppletModel *m_appletModel; +}; + +} + +#endif // APPLETMODEL_H diff --git a/src/context/CMakeLists.txt b/src/context/CMakeLists.txt index 37ef7bc953..d92d909cf6 100644 --- a/src/context/CMakeLists.txt +++ b/src/context/CMakeLists.txt @@ -1,53 +1,55 @@ -#add_subdirectory( applets ) -#add_subdirectory( tools ) +add_subdirectory( applets ) +add_subdirectory( tools ) ########### next target ############### set( amarokpud_LIB_SRCS popupdropper/libpud/PopupDropper.cpp popupdropper/libpud/PopupDropperItem.cpp popupdropper/libpud/PopupDropperView.cpp ) add_library( amarokpud SHARED ${amarokpud_LIB_SRCS} ) target_include_directories( amarokpud PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/popupdropper/libpud ${CMAKE_CURRENT_SOURCE_DIR}/popupdropper/libpud ) target_link_libraries( amarokpud Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Svg ) install( TARGETS amarokpud ${INSTALL_TARGETS_DEFAULT_ARGS} ) -set(applet_qml_plugin_qml_files - applet_qml_plugin/qmldir - applet_qml_plugin/Applet.qml - applet_qml_plugin/AppletHeader.qml +set(qml_plugin_qml_files + qml_plugin/qmldir + qml_plugin/Applet.qml + qml_plugin/AppletHeader.qml ) -set(applet_qml_plugin_SRCS - applet_qml_plugin/src/RatingItem.cpp - applet_qml_plugin/src/AppletPlugin.cpp +set(qml_plugin_SRCS + qml_plugin/src/RatingItem.cpp + qml_plugin/src/Plugin.cpp + qml_plugin/src/PixmapItem.cpp ) -#add_library(applet_qml_plugin SHARED ${applet_qml_plugin_SRCS}) +add_library(qml_plugin SHARED ${qml_plugin_SRCS}) -#target_link_libraries(applet_qml_plugin -# amarokcore -# Qt5::Quick -# KF5::WidgetsAddons -#) +target_link_libraries(qml_plugin + amarokcore + Qt5::Quick + KF5::QuickAddons + KF5::WidgetsAddons +) -#install( TARGETS applet_qml_plugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/applet ) +install( TARGETS qml_plugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/qml ) -#install( FILES ${applet_qml_plugin_qml_files} DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/applet ) +install( FILES ${qml_plugin_qml_files} DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/qml ) -#kpackage_install_package( context_qml_package org.kde.amarok.context genericqml ) +kpackage_install_package( context_qml_package org.kde.amarok.context genericqml ) diff --git a/src/context/Containment.h b/src/context/Containment.h deleted file mode 100644 index 2c87a3ece4..0000000000 --- a/src/context/Containment.h +++ /dev/null @@ -1,58 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_CONTAINMENT_H -#define AMAROK_CONTAINMENT_H - -#include "amarok_export.h" - -#include - -#include -#include - -namespace Context -{ - -class ContextView; - -class AMAROK_EXPORT Containment : public Plasma::Containment -{ - Q_OBJECT -public: - explicit Containment(QGraphicsItem* parent = 0, - const QString& serviceId = QString(), - uint containmentId = 0); - - Containment(QObject* parent, const QVariantList& args); - - ~Containment(); - - virtual void saveToConfig( KConfigGroup &conf ) = 0; - virtual void loadConfig( const KConfigGroup &conf ) = 0; - - virtual void setView( ContextView *newView ) = 0; - - virtual ContextView *view() = 0; - -public Q_SLOTS: - void showApplet( Plasma::Applet* ) {} - void moveApplet( Plasma::Applet*, int, int ) {} - virtual void addApplet( const QString& pluginName, const int ) = 0; -}; - -} // Context namespace -#endif diff --git a/src/context/Context.h b/src/context/Context.h deleted file mode 100644 index 9a6f065357..0000000000 --- a/src/context/Context.h +++ /dev/null @@ -1,35 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef CONTEXT_H -#define CONTEXT_H - -#include - -/** - * add the ContextState (which we need) - */ -namespace Context -{ - -enum ContextState { Home = 0 /**< Currently showing the home screen */, - Current, /**< Showing Current Track screen. NB: a Current message is sent every time the track changes */ - Service /**< User is browsing a service */ -}; - -} // Context namespace - -#endif // multiple inclusion guard diff --git a/src/context/ContextDock.cpp b/src/context/ContextDock.cpp index a2f3962547..5cb44521c6 100644 --- a/src/context/ContextDock.cpp +++ b/src/context/ContextDock.cpp @@ -1,76 +1,53 @@ /**************************************************************************************** * Copyright (c) 2010 Nikolaj Hald Nielsen * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "ContextDock" #include "ContextDock.h" -#include "amarokconfig.h" -#include "context/ContextScene.h" #include "context/ContextView.h" -#include "context/ToolbarView.h" #include "core/support/Debug.h" -#include +#include ContextDock::ContextDock( QWidget *parent ) : AmarokDockWidget( i18n( "&Context" ), parent ) { setObjectName( "Context dock" ); setAllowedAreas( Qt::AllDockWidgetAreas ); setMinimumWidth( 50 ); setContentsMargins( 0, 0, 0, 0 ); - m_mainWidget = new KVBox( this ); - m_mainWidget->setMinimumWidth( 400 ); - m_mainWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - m_mainWidget->setSpacing( 0 ); - m_mainWidget->setContentsMargins( 0, 0, 0, 0 ); - m_mainWidget->setFrameShape( QFrame::NoFrame ); - setWidget( m_mainWidget ); - - m_corona = new Context::ContextScene( this ); - connect( m_corona.data(), SIGNAL(containmentAdded(Plasma::Containment*)), - this, SLOT(createContextView(Plasma::Containment*)) ); - - m_corona.data()->loadDefaultSetup(); // this method adds our containment to the scene + createContextView(); } void ContextDock::polish() { } void -ContextDock::createContextView( Plasma::Containment *containment ) +ContextDock::createContextView() { - disconnect( m_corona.data(), SIGNAL(containmentAdded(Plasma::Containment*)), - this, SLOT(createContextView(Plasma::Containment*)) ); - - debug() << "Creating context view on containmend" << containment->name(); - PERF_LOG( "Creating ContexView" ) - m_contextView = new Context::ContextView( containment, m_corona.data(), m_mainWidget ); - m_contextView.data()->setFrameShape( QFrame::NoFrame ); - m_contextToolbarView = new Context::ToolbarView( containment, m_corona.data(), m_mainWidget ); - PERF_LOG( "Created ContexToolbarView" ) + auto mainWidget = new Context::ContextView(); + mainWidget->setMinimumWidth( 400 ); + mainWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + mainWidget->setContentsMargins( 0, 0, 0, 0 ); + setWidget( mainWidget ); - connect( m_corona.data(), SIGNAL(sceneRectChanged(QRectF)), m_contextView.data(), SLOT(updateSceneRect(QRectF)) ); - connect( m_contextToolbarView.data(), SIGNAL(hideAppletExplorer()), m_contextView.data(), SLOT(hideAppletExplorer()) ); - connect( m_contextToolbarView.data(), SIGNAL(showAppletExplorer()), m_contextView.data(), SLOT(showAppletExplorer()) ); - m_contextView.data()->showHome(); PERF_LOG( "ContexView created" ) } diff --git a/src/context/ContextDock.h b/src/context/ContextDock.h index 76dee40772..d532781ebe 100644 --- a/src/context/ContextDock.h +++ b/src/context/ContextDock.h @@ -1,55 +1,38 @@ /**************************************************************************************** * Copyright (c) 2010 Nikolaj Hald Nielsen * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef CONTEXTDOCK_H #define CONTEXTDOCK_H #include "widgets/AmarokDockWidget.h" -#include +#include -class KVBox; -class QResizeEvent; - -namespace Context { - class ContextScene; - class ContextView; - class ToolbarView; -} - -namespace Plasma { class Containment; } class ContextDock : public AmarokDockWidget { Q_OBJECT public: ContextDock( QWidget *parent ); void polish(); protected Q_SLOTS: - void createContextView( Plasma::Containment *containment ); - -private: - KVBox * m_mainWidget; - - QWeakPointer m_corona; - QWeakPointer m_contextView; - QWeakPointer m_contextToolbarView; + void createContextView(); }; #endif // CONTEXTDOCK_H diff --git a/src/context/ContextObserver.cpp b/src/context/ContextObserver.cpp deleted file mode 100644 index a2f717b627..0000000000 --- a/src/context/ContextObserver.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#include "ContextObserver.h" - -#include "core/support/Debug.h" - -////////////////////////////////////////////////////////////// -////// CLASS ContextObserver -////////////////////////////////////////////////////////////// - -ContextObserver::ContextObserver() - : m_subject( 0 ) -{} - -ContextObserver::ContextObserver( ContextSubject *s ) - : m_subject( s ) -{ - m_subject->attach( this ); -} - -ContextObserver::~ContextObserver() -{ - DEBUG_BLOCK - - if( m_subject ) - m_subject->detach( this ); -} - -//////////////////////////////////////////////////////////////// -//// CLASS ContextSubject -/////////////////////////////////////////////////////////////// - -ContextSubject::ContextSubject() -{ - DEBUG_BLOCK -} - -ContextSubject::~ContextSubject() -{ - DEBUG_BLOCK -} - -void ContextSubject::messageNotify( const Context::ContextState& message ) -{ - /*DEBUG_BLOCK - if( message == Context::Home ) - debug() << "notifying with Home"; - else if( message == Context::Current ) - debug() << "notifying with Current"; */ - foreach( ContextObserver* obs, m_observers ) - obs->message( message ); -} - -void ContextSubject::attach( ContextObserver *obs ) -{ - if( !obs ) - return; - - m_observers.insert( obs ); -} - -void ContextSubject::detach( ContextObserver *obs ) -{ - DEBUG_BLOCK - - if( obs ) - m_observers.remove( obs ); -} - diff --git a/src/context/ContextObserver.h b/src/context/ContextObserver.h deleted file mode 100644 index e5afcc71b2..0000000000 --- a/src/context/ContextObserver.h +++ /dev/null @@ -1,58 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef CONTEXT_OBSERVER_H -#define CONTEXT_OBSERVER_H - -#include "amarok_export.h" -#include "Context.h" - -#include - - -class ContextSubject; - -class AMAROK_EXPORT ContextObserver -{ -public: - virtual void message( const Context::ContextState& ) {} - -protected: - ContextObserver(); - ContextObserver( ContextSubject* ); - virtual ~ContextObserver(); - -private: - ContextSubject *m_subject; -}; - -class ContextSubject -{ -public: - void attach( ContextObserver *observer ); - void detach( ContextObserver *observer ); - -protected: - ContextSubject(); - virtual ~ContextSubject(); - - void messageNotify( const Context::ContextState& message ); - -private: - QSet m_observers; -}; - -#endif diff --git a/src/context/ContextScene.cpp b/src/context/ContextScene.cpp deleted file mode 100644 index 0fa83dc915..0000000000 --- a/src/context/ContextScene.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "ContextScene" - -#include "ContextScene.h" - -#include "core/support/Amarok.h" -#include "amarokconfig.h" -#include "core/support/Debug.h" - -#include -#include -#include - -#include - - -namespace Context -{ - -ContextScene::ContextScene( QObject * parent ) - : Plasma::Corona( parent ) -{ - DEBUG_BLOCK - setBackgroundBrush( Qt::NoBrush ); -} - -ContextScene::~ContextScene() -{ - DEBUG_BLOCK -} - -void ContextScene::loadDefaultSetup() -{ - Plasma::Containment* c = createContainment( "amarok_containment_vertical" ); -// c->setScreen( -1 ); This line may be removed as -1 is now the default value of lastScreen in ContainmentPrivate and there is no more a setScreen function in Plasma::Containment. - c->setFormFactor( Plasma::Planar ); -} - -} // Context namespace - - diff --git a/src/context/ContextScene.h b/src/context/ContextScene.h deleted file mode 100644 index 143d6bcabf..0000000000 --- a/src/context/ContextScene.h +++ /dev/null @@ -1,51 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_CONTEXT_SCENE_H -#define AMAROK_CONTEXT_SCENE_H - -#include "amarok_export.h" -#include "Applet.h" -#include "Context.h" - -#include - -#include - -namespace Context -{ - -/** - * The ContextScene is a very simple QGraphicsScene, it does the same thing as a Plasma::Corona. - * The only bit that is important is that it controls what the default containtainment to be loaded should be. - */ -class AMAROK_EXPORT ContextScene : public Plasma::Corona -{ - Q_OBJECT -public: - explicit ContextScene(QObject * parent = 0); - ~ContextScene(); - - void loadDefaultSetup(); - -Q_SIGNALS: - void appletRemoved( QObject *object ); - -}; - -} // Context namespace - -#endif diff --git a/src/context/ContextView.cpp b/src/context/ContextView.cpp index 096877031d..fb25760b17 100644 --- a/src/context/ContextView.cpp +++ b/src/context/ContextView.cpp @@ -1,543 +1,278 @@ /**************************************************************************************** * Copyright (c) 2007-2008 Leo Franchi * * Copyright (c) 2008 William Viana Soares * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "ContextView" -/* - Significant parts of this code is inspired and/or copied from KDE plasma sources, - available at kdebase/workspace/plasma -*/ - #include "ContextView.h" -#include - -#include "Context.h" -#include "ContextScene.h" -#include "Svg.h" -#include "Theme.h" -#include "amarokconfig.h" +#include "AppletLoader.h" +#include "AppletModel.h" +#include "PaletteHandler.h" +#include "SvgHandler.h" #include "amarokurls/AmarokUrlHandler.h" #include "amarokurls/ContextUrlRunner.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include "core/meta/Meta.h" -#include "EngineController.h" -#include +#include +#include +#include +#include +#include +#include +#include +#include -#include -#include -#include -#include +#include +#include +#include +#include -namespace Context -{ -class ViewPrivate +class FontFilter : public QObject { -public: - ViewPrivate(QGraphicsView *view, int uniqueId) - : q(view), - containment(0), - drawWallpaper(true), - trackChanges(true), - viewId(0), - lastScreen(-1), - lastDesktop(-2) - { - if (uniqueId > s_maxViewId) { - s_maxViewId = uniqueId; - viewId = uniqueId; - } + Q_OBJECT - if (viewId == 0) { - // we didn't get a sane value assigned to us, so lets - // grab the next available id - viewId = ++s_maxViewId; - } - } - - ~ViewPrivate() +public: + FontFilter( QObject *parent ) + : QObject( parent ) { + qApp->installEventFilter( this ); } - void updateSceneRect() + bool eventFilter( QObject *watched, QEvent *event ) { - if (!containment || !trackChanges) { - return; - } - - kDebug() << "!!!!!!!!!!!!!!!!! setting the scene rect to" - << containment->sceneBoundingRect() - << "associated screen is" << containment->screen(); - - emit q->sceneRectAboutToChange(); - if (q->transform().isIdentity()) { //we're not zoomed out - q->setSceneRect(containment->sceneBoundingRect()); - } else { - q->ensureVisible(containment->sceneBoundingRect()); + if( watched == qApp ) + { + if( event->type() == QEvent::ApplicationFontChange ) + emit fontChanged(); } - emit q->sceneRectChanged(); - } - - void containmentDestroyed() - { - containment = 0; + return QObject::eventFilter( watched, event ); } - void containmentScreenChanged(int wasScreen, int newScreen, Plasma::Containment *containment) - { - lastScreen = newScreen; - lastDesktop = this->containment->desktop(); - } +signals: + void fontChanged(); +}; - void initGraphicsView() - { - q->setFrameShape(QFrame::NoFrame); - q->setAutoFillBackground(true); - q->setDragMode(QGraphicsView::NoDrag); - q->setInteractive(true); - q->setAcceptDrops(true); - q->setAlignment(Qt::AlignLeft | Qt::AlignTop); - q->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - q->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - } - QGraphicsView *q; - Plasma::Containment *containment; - bool drawWallpaper; - bool trackChanges; - int viewId; - int lastScreen; - int lastDesktop; - static int s_maxViewId; -}; +namespace Context +{ -ContextView* ContextView::s_self = 0; +ContextView* ContextView::s_self = Q_NULLPTR; -ContextView::ContextView( Plasma::Containment *cont, Plasma::Corona *corona, QWidget* parent ) - : QGraphicsView(parent), - , m_curState( Home ) - , m_urlRunner(0) - , m_appletExplorer(0) - , m_collapseAnimations(0) - , m_queuedAnimations(0) - , m_collapseGroupTimer(0) - , d(new ViewPrivate(this, 0)) +ContextView::ContextView( QWidget *parent ) + : QQuickWidget( parent ) + , m_urlRunner( Q_NULLPTR ) + , m_loader( new AppletLoader( this ) ) + , m_appletModel( new AppletModel( m_loader, this ) ) + , m_proxyModel( new AppletProxyModel( m_appletModel, this ) ) + , m_fontFilter( new FontFilter( this ) ) + , m_smallSpacing( 2 ) + , m_largeSpacing( 8 ) + , m_iconSizes( new QQmlPropertyMap( this ) ) { - Q_UNUSED( corona ) DEBUG_BLOCK - // using QGraphicsScene::BspTreeIndex leads to crashes in some Qt versions - scene()->setItemIndexMethod( QGraphicsScene::NoIndex ); - //TODO: Figure out a way to use rubberband and ScrollHandDrag - //setDragMode( QGraphicsView::RubberBandDrag ); - setTransformationAnchor( QGraphicsView::NoAnchor ); - setCacheMode( QGraphicsView::CacheBackground ); - setInteractive( true ); - setAcceptDrops( true ); - setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - // setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - - //make background transparent - QPalette p = palette(); - QColor c = p.color( QPalette::Base ); - c.setAlpha( 0 ); - p.setColor( QPalette::Base, c ); - setPalette( p ); - - contextScene()->setAppletMimeType( "text/x-amarokappletservicename" ); - - if (cont) { - setScene(cont->scene()); - setContainment(cont); - } + KDeclarative::KDeclarative decl; + decl.setDeclarativeEngine( engine() ); + decl.setupBindings(); + + connect( this, &QQuickWidget::statusChanged, this, &ContextView::slotStatusChanged ); + connect( qApp, &QGuiApplication::primaryScreenChanged, this, &ContextView::updateDevicePixelRatio ); + connect( m_fontFilter, &FontFilter::fontChanged, this, &ContextView::updateSpacing ); + connect( KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, this, &ContextView::iconLoaderSettingsChanged ); + connect( The::paletteHandler(), &PaletteHandler::newPalette, this, &ContextView::updatePalette ); - cont->setPos( 0, 0 ); - cont->updateConstraints(); - Containment* amarokContainment = qobject_cast( cont ); - if( amarokContainment ) - amarokContainment->setView( this ); + updateSpacing(); + updateDevicePixelRatio( qApp->primaryScreen() ); m_urlRunner = new ContextUrlRunner(); The::amarokUrlHandler()->registerRunner( m_urlRunner, "context" ); - m_queuedAnimations = new QSequentialAnimationGroup( this ); - m_collapseAnimations = new QParallelAnimationGroup( this ); - connect( m_collapseAnimations, SIGNAL(finished()), - this, SLOT(slotCollapseAnimationsFinished()) ); + rootContext()->setContextProperty( QStringLiteral( "AppletModel" ), m_appletModel ); + rootContext()->setContextProperty( QStringLiteral( "AppletProxyModel" ), m_proxyModel ); + rootContext()->setContextProperty( QStringLiteral( "Context" ), this ); + rootContext()->setContextProperty( QStringLiteral( "Svg" ), The::svgHandler() ); + + quickWindow()->setColor( The::paletteHandler()->palette().color( QPalette::Window ) ); + + auto qmlPackage = KPackage::PackageLoader::self()->loadPackage( QStringLiteral( "KPackage/GenericQML" ), + QStringLiteral( "org.kde.amarok.context" ) ); + Q_ASSERT( qmlPackage.isValid() ); - m_collapseGroupTimer = new QTimer( this ); - m_collapseGroupTimer->setSingleShot( true ); - connect( m_collapseGroupTimer, SIGNAL(timeout()), SLOT(slotStartCollapseAnimations()) ); + const QString sourcePath = qmlPackage.filePath( "mainscript" ); + Q_ASSERT( QFile::exists( sourcePath ) ); - EngineController* const engine = The::engineController(); + ::debug() << "Loading context qml mainscript:" << sourcePath; - connect( engine, SIGNAL(trackChanged(Meta::TrackPtr)), - this, SLOT(slotTrackChanged(Meta::TrackPtr)) ); - connect( engine, SIGNAL(trackMetadataChanged(Meta::TrackPtr)), - this, SLOT(slotMetadataChanged(Meta::TrackPtr)) ); + setSource( QUrl::fromLocalFile( sourcePath ) ); + setResizeMode( SizeRootObjectToView ); // keep this assignment at bottom so that premature usage of ::self() asserts out s_self = this; } ContextView::~ContextView() { DEBUG_BLOCK - // Unload and destroy all Amarok plasma-engines - const QStringList engines = Plasma::DataEngineManager::self()->listAllEngines( "Amarok" ); - - // Assert added for tracing crash on exit, see BUG 187384 - Q_ASSERT_X( !engines.isEmpty(), "Listing loaded Plasma engines", "List is empty (no engines loaded!?)" ); - - foreach( const QString &engine, engines ) - { - debug() << "Unloading plasma engine: " << engine; - - // PlasmaDataEngineManager uses refcounting for the engines, so we need to unload until the refcount reaches 0 - while( Plasma::DataEngineManager::self()->engine( engine )->isValid() ) - Plasma::DataEngineManager::self()->unloadEngine( engine ); - } - - clear( m_curState ); - //this should be done to prevent a crash on exit - clearFocus(); - delete m_urlRunner; + s_self = Q_NULLPTR; } - -void -ContextView::clear( const ContextState& state ) +QStringList +ContextView::currentApplets() const { - Q_UNUSED( state ) - DEBUG_BLOCK - - const QString name = "amarok_homerc"; - // now we save the state, remembering the column info etc - KConfig appletConfig( name ); - // erase previous config - foreach( const QString& group, appletConfig.groupList() ) - appletConfig.deleteGroup( group ); - - const int numContainments = contextScene()->containments().size(); - for( int i = 0; i < numContainments; i++ ) + QStringList appletNames; + + auto applets = m_loader->enabledApplets(); + for( const auto &applet : applets ) { - DEBUG_LINE_INFO - Containment* containment = qobject_cast< Containment* >( contextScene()->containments()[i] ); - KConfigGroup cg( &appletConfig, QString( "Containment %1" ).arg( i ) ); - if( containment ) - containment->saveToConfig( cg ); + appletNames << applet.pluginId(); } - contextScene()->clearContainments(); -} - -void ContextView::clearNoSave() -{ - contextScene()->clearContainments(); -} - - -void ContextView::slotTrackChanged( Meta::TrackPtr track ) -{ - if( track ) - messageNotify( Current ); - else - messageNotify( Home ); -} + ::debug() << "Current applets: " << appletNames; -void -ContextView::slotMetadataChanged( Meta::TrackPtr track ) -{ - DEBUG_BLOCK - - // if we are listening to a stream, take the new metadata as a "new track" - if( track && The::engineController()->isStream() ) - messageNotify( Current ); -} - -void ContextView::showHome() -{ - DEBUG_BLOCK - - m_curState = Home; - loadConfig(); - messageNotify( m_curState ); + return appletNames; } - -// loads applets onto the ContextScene from saved data, using m_curState -void -ContextView::loadConfig() +QStringList +ContextView::currentAppletNames() const { - contextScene()->clearContainments(); + QStringList appletNames; - PERF_LOG( "Start to load config" ); - int numContainments = contextScene()->containments().size(); - KConfig conf( "amarok_homerc", KConfig::FullConfig ); - for( int i = 0; i < numContainments; i++ ) + auto applets = m_loader->enabledApplets(); + for( const auto &applet : applets ) { - Containment* containment = qobject_cast< Containment* >( contextScene()->containments()[i] ); - if( containment ) - { - KConfigGroup cg( &conf, QString( "Containment %1" ).arg( i ) ); -#ifdef QT_QTOPENGL_FOUND - // Special case: If this is the first time that the user runs an Amarok version - // containing the Analyzer applet, modify the user's config so that the applet - // will become active. We do this for discoverability and prettiness. - // Remove this code in a future Amarok release (possibly 3.0) - const bool firstTimeWithAnalyzer = Amarok::config( "Context View" ).readEntry( "firstTimeWithAnalyzer", true ); - if( firstTimeWithAnalyzer ) - { - QStringList plugins = cg.readEntry( "plugins", QStringList() ); - if( EngineController::instance()->supportsAudioDataOutput() && !plugins.contains( "analyzer" ) ) - { - Amarok::config( "Context View" ).writeEntry( "firstTimeWithAnalyzer", false ); - - // Put the Analyzer applet at position #2, which is most likely below the Currenttrack applet. - if( !plugins.empty() ) - plugins.insert( 1, "analyzer" ); - - cg.writeEntry( "plugins", plugins ); - } - } -#endif - containment->loadConfig( cg ); - } + appletNames << applet.name(); } - PERF_LOG( "Done loading config" ); -} -void -ContextView::addCollapseAnimation( QAbstractAnimation *anim ) -{ - if( !anim ) - { - debug() << "failed to add collapsing animation"; - return; - } + ::debug() << "Current applet names: " << appletNames; - if( m_collapseAnimations->state() == QAbstractAnimation::Running || - m_collapseGroupTimer->isActive() ) - { - m_queuedAnimations->addAnimation( anim ); - } - else - { - m_collapseAnimations->addAnimation( anim ); - m_collapseGroupTimer->start( 0 ); - } + return appletNames; } void -ContextView::slotCollapseAnimationsFinished() +ContextView::runLink( const QUrl& link ) const { - m_collapseGroupTimer->stop(); - m_collapseAnimations->clear(); - - while( m_queuedAnimations->animationCount() > 0 ) + if( link.scheme() == QStringLiteral( "amarok" ) ) { - if( QAbstractAnimation *anim = m_queuedAnimations->takeAnimation(0) ) - m_collapseAnimations->addAnimation( anim ); + AmarokUrl aUrl( link.toString() ); + aUrl.run(); } - - if( m_collapseAnimations->animationCount() > 0 ) - m_collapseGroupTimer->start( 0 ); + else + QDesktopServices::openUrl( link ); } void -ContextView::slotStartCollapseAnimations() +ContextView::slotStatusChanged( Status status ) { - if( m_collapseAnimations->animationCount() > 0 ) - m_collapseAnimations->start( QAbstractAnimation::KeepWhenStopped ); + if( status == Error ) + for( const auto &e : errors() ) + error( e.description() ); } void -ContextView::hideAppletExplorer() +ContextView::updateSpacing() { - if( m_appletExplorer ) - m_appletExplorer->hide(); -} + int gridUnit = QFontMetrics( QGuiApplication::font() ).boundingRect( QStringLiteral("M") ).height(); + if (gridUnit % 2 != 0) + gridUnit++; -void -ContextView::showAppletExplorer() -{ - if( !m_appletExplorer ) + if (gridUnit != m_largeSpacing) { - Context::Containment *cont = qobject_cast( containment() ); - m_appletExplorer = new AppletExplorer( cont ); - m_appletExplorer->setContainment( cont ); - m_appletExplorer->setZValue( m_appletExplorer->zValue() + 1000 ); - m_appletExplorer->setFlag( QGraphicsItem::ItemIsSelectable ); - - connect( m_appletExplorer, SIGNAL(addAppletToContainment(QString,int)), - cont, SLOT(addApplet(QString,int)) ); - connect( m_appletExplorer, SIGNAL(appletExplorerHid()), SIGNAL(appletExplorerHid()) ); - connect( m_appletExplorer, SIGNAL(geometryChanged()), SLOT(slotPositionAppletExplorer()) ); - - qreal height = m_appletExplorer->effectiveSizeHint( Qt::PreferredSize ).height(); - m_appletExplorer->resize( rect().width() - 2, height ); - m_appletExplorer->setPos( 0, rect().height() - height - 2 ); + m_smallSpacing = qMax( 2, (int)( gridUnit / 4 ) ); // 1/4 of gridUnit, at least 2 + m_largeSpacing = gridUnit; // msize.height + emit spacingChanged(); } - m_appletExplorer->show(); } void -ContextView::slotPositionAppletExplorer() +ContextView::updateDevicePixelRatio( QScreen *screen ) { - if( !m_appletExplorer ) + if (!screen) return; - qreal height = m_appletExplorer->effectiveSizeHint( Qt::PreferredSize ).height(); - m_appletExplorer->setPos( 0, rect().height() - height - 2 ); -} - -ContextScene* -ContextView::contextScene() -{ - return static_cast( scene() ); + const qreal dpi = screen->logicalDotsPerInchX(); + // Usual "default" is 96 dpi + m_devicePixelRatio = (qreal)dpi / (qreal)96; + iconLoaderSettingsChanged(); + emit devicePixelRatioChanged(); } void -ContextView::resizeEvent( QResizeEvent* event ) +ContextView::iconLoaderSettingsChanged() { - QGraphicsView::resizeEvent( event ); - if( testAttribute( Qt::WA_PendingResizeEvent ) ) - return; // lets not do this more than necessary, shall we? - - QRectF rect( pos(), maximumViewportSize() ); - containment()->setGeometry( rect ); - scene()->setSceneRect( rect ); - scene()->update( rect ); - - if( m_appletExplorer ) - { - qreal height = m_appletExplorer->effectiveSizeHint( Qt::PreferredSize ).height(); - m_appletExplorer->resize( rect.width() - 2, height ); - m_appletExplorer->setPos( 0, rect.height() - height - 2 ); - } + m_iconSizes->insert( QStringLiteral( "tiny" ), devicePixelIconSize( KIconLoader::SizeSmall ) / 2 ); + m_iconSizes->insert( QStringLiteral( "small" ), devicePixelIconSize( KIconLoader::SizeSmall ) ); + m_iconSizes->insert( QStringLiteral( "smallMedium" ), devicePixelIconSize( KIconLoader::SizeSmallMedium ) ); + m_iconSizes->insert( QStringLiteral( "medium" ), devicePixelIconSize( KIconLoader::SizeMedium ) ); + m_iconSizes->insert( QStringLiteral( "large" ), devicePixelIconSize( KIconLoader::SizeLarge ) ); + m_iconSizes->insert( QStringLiteral( "huge" ), devicePixelIconSize( KIconLoader::SizeHuge ) ); + m_iconSizes->insert( QStringLiteral( "enormous" ), devicePixelIconSize( KIconLoader::SizeEnormous ) ); + + emit iconSizesChanged(); } -void -ContextView::wheelEvent( QWheelEvent* event ) +int +ContextView::devicePixelIconSize( int size ) const { - if( event->orientation() != Qt::Horizontal ) - QGraphicsView::wheelEvent( event ); + const qreal ratio = devicePixelRatio(); + + if ( ratio < 1.5 ) + return size; + else if ( ratio < 2.0 ) + return size * 1.5; + else if ( ratio < 2.5 ) + return size * 2.0; + else if ( ratio < 3.0 ) + return size * 2.5; + else if ( ratio < 3.5 ) + return size * 3.0; + else + return size * ratio; } -QStringList -ContextView::currentApplets() +void +ContextView::updatePalette( const QPalette &palette ) { - DEBUG_BLOCK - QStringList appletNames; - - Applet::List applets = containment()->applets(); - foreach( Plasma::Applet * applet, applets ) - { - appletNames << applet->pluginName(); - } - - debug() << "current applets: " << appletNames; - - return appletNames; + quickWindow()->setColor( palette.color( QPalette::Window ) ); } -QStringList ContextView::currentAppletNames() +void +ContextView::debug( const QString &error ) const { - DEBUG_BLOCK - QStringList appletNames; - - Applet::List applets = containment()->applets(); - foreach( Plasma::Applet * applet, applets ) - { - appletNames << applet->name(); - } - - debug() << "current applets: " << appletNames; - - return appletNames; + ::debug() << error; } -Containment *ContextView::containment() const +void +ContextView::warning( const QString &error ) const { - return d->containment; + ::warning() << error; } -void ContextView::setContainment(Plasma::Containment *containment) +void +ContextView::error( const QString &error ) const { - if (containment == d->containment) { - return; - } - - if (d->containment) { - disconnect(d->containment, SIGNAL(destroyed(QObject*)), this, SLOT(containmentDestroyed())); - disconnect(d->containment, SIGNAL(geometryChanged()), this, SLOT(updateSceneRect())); - disconnect(d->containment, SIGNAL(screenChanged(int, int, Plasma::Containment *)), this, SLOT(containmentScreenChanged(int, int, Plasma::Containment *))); - d->containment->removeAssociatedWidget(this); - } - - if (!containment) { - d->containment = 0; - return; - } - - Containment *oldContainment = d->containment; - - int screen = -1; - int desktop = -1; - if (oldContainment) { - screen = d->containment->screen(); - desktop = d->containment->desktop(); - } else { - setScene(containment->scene()); - } - - d->containment = containment; - - //add keyboard-shortcut actions - d->containment->addAssociatedWidget(this); - - int otherScreen = containment->screen(); - int otherDesktop = containment->desktop(); - - if (screen > -1) { - d->lastScreen = screen; - d->lastDesktop = desktop; - containment->setScreen(screen, desktop); - } else { - d->lastScreen = otherScreen; - d->lastDesktop = otherDesktop; - } - - if (oldContainment && otherScreen > -1) { - // assign the old containment the old screen/desktop - oldContainment->setScreen(otherScreen, otherDesktop); - } - - d->updateSceneRect(); - connect(containment, SIGNAL(destroyed(QObject*)), this, SLOT(containmentDestroyed())); - connect(containment, SIGNAL(geometryChanged()), this, SLOT(updateSceneRect())); - connect(containment, SIGNAL(screenChanged(int, int, Plasma::Containment *)), this, SLOT(containmentScreenChanged(int, int, Plasma::Containment *))); + ::error() << error; } } // Context namespace +#include diff --git a/src/context/ContextView.h b/src/context/ContextView.h index 8ae8b4dab5..0262081178 100644 --- a/src/context/ContextView.h +++ b/src/context/ContextView.h @@ -1,178 +1,121 @@ /**************************************************************************************** * Copyright (c) 2007 Leo Franchi * * Copyright (c) 2008 William Viana Soares * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ /* Significant parts of this code is inspired and/or copied from KDE Plasma sources, available at kdebase/workspace/libs/plasma */ #ifndef AMAROK_CONTEXT_VIEW_H #define AMAROK_CONTEXT_VIEW_H -#include "Context.h" -#include "ContextObserver.h" -#include "ContextScene.h" -#include "EngineController.h" -#include "Svg.h" -#include "amarok_export.h" -#include "widgets/ContainmentArrow.h" -#include "widgets/appletexplorer/AppletExplorer.h" -#include +#include "amarok_export.h" -#include -#include -#include -#include +#include -class QPixmap; class ContextUrlRunner; -class QParallelAnimationGroup; +class FontFilter; +class QPalette; +class QQmlPropertyMap; +class QScreen; namespace Context { -class ContextScene; -class ControlBox; -class ViewPrivate; +class AppletLoader; +class AppletModel; +class AppletProxyModel; -/** - * Plasma::View is no longer present in the plasma API in KF5 and hence it has been replaced - * with QGraphicsView from which it was derived. - */ -class AMAROK_EXPORT ContextView : public QGraphicsView, public ContextSubject +class AMAROK_EXPORT ContextView : public QQuickWidget { Q_OBJECT + // Properties copied from KF5::Plasma::Units + Q_PROPERTY( int smallSpacing READ smallSpacing NOTIFY spacingChanged ) + Q_PROPERTY( int largeSpacing READ largeSpacing NOTIFY spacingChanged ) + Q_PROPERTY( QQmlPropertyMap *iconSizes READ iconSizes CONSTANT ) + Q_PROPERTY( qreal devicePixelRatio READ devicePixelRatio NOTIFY devicePixelRatioChanged ) + public: - ContextView( Plasma::Containment *containment, Plasma::Corona *corona, QWidget* parent = 0 ); + ContextView( QWidget *parent = Q_NULLPTR ); ~ContextView(); /** * Singleton pattern accessor. May return 0 if the view was not yet constructed. */ static ContextView *self() { return s_self; } /** - Returns the context scene that this view is attached to. - */ - ContextScene* contextScene(); - - /** - Clears the context scene of all items, but first saves the current state of the scene into the - config file using as a key the string parameter. - */ - void clear( const ContextState& name ); - - void clearNoSave(); - - /** - Shows the home state. Loads applets from config file. + * Get the plugin names, in order, of the applets currently in the contextView. */ - void showHome(); + QStringList currentApplets() const; /** - Get the plugin names, in order, of the applets currently in the contextView. + * Get the user visible applet names, in order, of the applets currently in the contextView. */ - QStringList currentApplets(); - - /** - Get the user visible applet names, in order, of the applets currently in the contextView. - */ - QStringList currentAppletNames(); - - /** - Adds a collapse animation - This object will take ownership of the animation. - */ - void addCollapseAnimation( QAbstractAnimation *anim ); - - /** - * @return the containment associated, or 0 if none is - */ - Containment *containment() const; - -public Q_SLOTS: - /** - * Convenience methods to show and hide the applet explorer. - */ - void hideAppletExplorer(); - void showAppletExplorer(); - - /** - * @param containment the containment to center the view on - */ - virtual void setContainment(Plasma::Containment *containment); - -Q_SIGNALS: - void appletExplorerHid(); - - /** - * This signal is emitted whenever the containment being viewed has - * changed its geometry, but before the View has shifted the viewd scene rect - * to the new geometry. This is useful for Views which want to keep - * their rect() in sync with the containment'sa - */ - void sceneRectAboutToChange(); - - /** - * This signal is emitted whenever the containment being viewed has - * changed its geometry, and after the View has shifted the viewd scene rect - * to the new geometry. This is useful for Views which want to keep - * their rect() in sync with the containment's. - */ - void sceneRectChanged(); + QStringList currentAppletNames() const; /** - * This is emitted after the containment is destroyed, for views that need to do something about - * it (like find a new one). + * Get the Context::AppletModel instance in use. + * It can be used to show, hide enable or disable applets among other things. */ - void lostContainment(); - -protected: - void resizeEvent(QResizeEvent *event); - void wheelEvent(QWheelEvent *event); - -private Q_SLOTS: - void slotTrackChanged( Meta::TrackPtr track ); - void slotMetadataChanged( Meta::TrackPtr track ); - void slotPositionAppletExplorer(); - void slotStartCollapseAnimations(); - void slotCollapseAnimationsFinished(); + AppletProxyModel *appletModel() const { return m_proxyModel; } + + Q_INVOKABLE void runLink( const QUrl &link ) const; + Q_INVOKABLE void debug( const QString &error ) const; + Q_INVOKABLE void warning( const QString &error ) const; + Q_INVOKABLE void error( const QString &error ) const; + + int smallSpacing() const { return m_smallSpacing; } + int largeSpacing() const { return m_largeSpacing; } + QQmlPropertyMap* iconSizes() const { return m_iconSizes; } + qreal devicePixelRatio() const { return m_devicePixelRatio; } + +private slots: + void slotStatusChanged( QQuickWidget::Status status ); + void updateSpacing(); + void updateDevicePixelRatio( QScreen *screen ); + void iconLoaderSettingsChanged(); + void updatePalette( const QPalette &palette ); + +signals: + void spacingChanged(); + void iconSizesChanged(); + void devicePixelRatioChanged(); private: - static ContextView* s_self; - - void loadConfig(); - - // holds what is currently being shown - ContextState m_curState; + // copied from KF5::Plasma::Units + int devicePixelIconSize( int size ) const; - ContextUrlRunner * m_urlRunner; + static ContextView *s_self; - AppletExplorer *m_appletExplorer; - QParallelAnimationGroup *m_collapseAnimations; - QAnimationGroup *m_queuedAnimations; - QTimer *m_collapseGroupTimer; - ViewPrivate * const d; + ContextUrlRunner *m_urlRunner; + AppletLoader *m_loader; + AppletModel *m_appletModel; + AppletProxyModel *m_proxyModel; + FontFilter *m_fontFilter; - friend class ViewPrivate; + int m_smallSpacing; + int m_largeSpacing; + QQmlPropertyMap *m_iconSizes; + qreal m_devicePixelRatio; }; } // Context namespace #endif diff --git a/src/context/DataSource.h b/src/context/DataSource.h deleted file mode 100644 index a6273ce307..0000000000 --- a/src/context/DataSource.h +++ /dev/null @@ -1,29 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_DATA_SOURCE_H -#define AMAROK_DATA_SOURCE_H - -#include - -namespace Context -{ - typedef Plasma::DataSource DataSource; -}; - -} // context namespace - -#endif diff --git a/src/context/Svg.h b/src/context/Svg.h deleted file mode 100644 index d70172389f..0000000000 --- a/src/context/Svg.h +++ /dev/null @@ -1,32 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_SVG_H -#define AMAROK_SVG_H - -#include "amarok_export.h" - -#include - -namespace Context -{ - - typedef Plasma::Svg Svg; - - -} // context namespace - -#endif diff --git a/src/context/Theme.h b/src/context/Theme.h deleted file mode 100644 index caa93d0191..0000000000 --- a/src/context/Theme.h +++ /dev/null @@ -1,28 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_THEME_H -#define AMAROK_THEME_H - -#include - -namespace Context -{ - typedef Plasma::Theme Theme; - -} // context namespace - -#endif diff --git a/src/context/ToolbarView.cpp b/src/context/ToolbarView.cpp deleted file mode 100644 index 6cd0beda65..0000000000 --- a/src/context/ToolbarView.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#include "ToolbarView.h" - -#include "Containment.h" -#include "core/support/Debug.h" -#include "PaletteHandler.h" -#include "toolbar/AppletItemOverlay.h" -#include "toolbar/AppletToolbarAppletItem.h" -#include "toolbar/AppletToolbar.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define TOOLBAR_X_OFFSET 2000 -#define TOOLBAR_SCENE_PADDING 2 - -Context::ToolbarView::ToolbarView( Plasma::Containment* containment, QGraphicsScene* scene, QWidget* parent ) - : QGraphicsView( scene, parent ) - , m_height( 36 ) - , m_cont( containment ) -{ - setObjectName( "ContextToolbarView" ); - - setFixedHeight( m_height ); - setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ); - setAutoFillBackground( true ); - setContentsMargins( 0, 0, 0, 0 ); - - setFrameStyle( QFrame::NoFrame ); - applyStyleSheet(); - - connect( The::paletteHandler(), &PaletteHandler::newPalette, this, &Context::ToolbarView::applyStyleSheet ); - - //Padding required to prevent view scrolling, probably caused by the 1px ridge - setSceneRect( TOOLBAR_X_OFFSET, 0, size().width()-TOOLBAR_SCENE_PADDING, - size().height()-TOOLBAR_SCENE_PADDING ); - - setInteractive( true ); - setAcceptDrops( true ); - setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - - // now we create the toolbar - m_toolbar = new AppletToolbar(0); - scene->addItem(m_toolbar.data()); - m_toolbar.data()->setContainment( qobject_cast(containment) ); - m_toolbar.data()->setZValue( m_toolbar.data()->zValue() + 1000 ); - m_toolbar.data()->setPos( TOOLBAR_X_OFFSET, 0 ); - - connect( m_toolbar.data(), &Context::AppletToolbar::configModeToggled, this, &Context::ToolbarView::toggleConfigMode ); - connect( m_toolbar.data(), &Context::AppletToolbar::hideAppletExplorer, this, &Context::ToolbarView::hideAppletExplorer ); - connect( m_toolbar.data(), &Context::AppletToolbar::showAppletExplorer, this, &Context::ToolbarView::showAppletExplorer ); - - Context::Containment* cont = dynamic_cast< Context::Containment* >( containment ); - if( cont ) - { - connect( cont, &Context::Containment::appletAdded, m_toolbar.data(), &Context::AppletToolbar::appletAdded ); - connect( m_toolbar.data(), &Context::AppletToolbar::appletAddedToToolbar, this, &Context::ToolbarView::appletAdded ); - connect( cont, &Context::Containment::appletRemoved, this, &Context::ToolbarView::appletRemoved ); - connect( m_toolbar.data(), &Context::AppletToolbar::showApplet, cont, &Context::Containment::showApplet ); - connect( m_toolbar.data(), &Context::AppletToolbar::moveApplet, cont, &Context::Containment::moveApplet ); - } - -} - -Context::ToolbarView::~ToolbarView() -{ - delete m_toolbar.data(); -} - -void -Context::ToolbarView::resizeEvent( QResizeEvent *event ) -{ - Q_UNUSED( event ) - - setSceneRect( TOOLBAR_X_OFFSET, 0, size().width()-TOOLBAR_SCENE_PADDING, - size().height()-TOOLBAR_SCENE_PADDING ); - - if( m_toolbar ) - m_toolbar.data()->setGeometry( sceneRect() ); -} - -void -Context::ToolbarView::dragEnterEvent( QDragEnterEvent *event ) -{ - Q_UNUSED( event ) -} - -void -Context::ToolbarView::dragMoveEvent( QDragMoveEvent *event ) -{ - Q_UNUSED( event ) -} - -void -Context::ToolbarView::dragLeaveEvent( QDragLeaveEvent *event ) -{ - Q_UNUSED( event ) -} - -void -Context::ToolbarView::applyStyleSheet() // SLOT -{ - DEBUG_BLOCK - - const QPalette palette = QApplication::palette(); - - setStyleSheet( QString( "QFrame#ContextToolbarView { border: 1px ridge %1; " \ - "background-color: %2; color: %3; border-radius: 3px; }" \ - "QLabel { color: %3; }" ) - .arg( palette.color( QPalette::Active, QPalette::Window ).name() ) - .arg( The::paletteHandler()->highlightColor().name() ) - .arg( palette.color( QPalette::Active, QPalette::HighlightedText ).name() ) - ); -} - -void -Context::ToolbarView::toggleConfigMode() -{ - DEBUG_BLOCK - if( m_toolbar.data()->configEnabled() ) // set up config stuff - { - debug() << "got config enabled, creating all the move overlays"; - // now add the overlays that handle the drag-n-dropping - QColor overlayColor( Plasma::Theme::defaultTheme()->color( Plasma::Theme::BackgroundColor ) ); - QBrush overlayBrush( overlayColor ); - QPalette p( palette() ); - p.setBrush( QPalette::Window, overlayBrush ); - /* for( int i = 0; i < m_toolbar->appletLayout()->count(); i++ ) - { - debug() << "item" << i << "has geometry:" << m_toolbar->appletLayout()->itemAt( i )->geometry(); - Context::AppletToolbarAddItem* item = dynamic_cast< Context::AppletToolbarAddItem* >( m_toolbar->appletLayout()->itemAt( i ) ); - if( item ) - debug() << "add item has boundingRect:" << item->boundingRect() << "and geom:" << item->geometry() << "and sizehint" << item->effectiveSizeHint( Qt::PreferredSize ); - } */ - - for( int i = 0; i < m_toolbar.data()->appletLayout()->count(); i++ ) - { - debug() << "creating a move overlay"; - Context::AppletToolbarAppletItem* item = dynamic_cast< Context::AppletToolbarAppletItem* >( m_toolbar.data()->appletLayout()->itemAt( i ) ); - if( item ) - { - Context::AppletItemOverlay *moveOverlay = new Context::AppletItemOverlay( item, m_toolbar.data()->appletLayout(), this ); - connect( moveOverlay, &Context::AppletItemOverlay::moveApplet, m_cont, &Context::Containment::moveApplet ); - connect( moveOverlay, &Context::AppletItemOverlay::moveApplet, this, &Context::ToolbarView::refreshOverlays ); - connect( moveOverlay, &Context::AppletItemOverlay::deleteApplet, this, &Context::ToolbarView::appletRemoved ); - moveOverlay->setPalette( p ); - moveOverlay->show(); - moveOverlay->raise(); - m_moveOverlays << moveOverlay; - debug() << moveOverlay << moveOverlay->geometry(); - } - - } - } else - { - debug() << "removing all the move overlays"; - foreach( Context::AppletItemOverlay *moveOverlay, m_moveOverlays ) - moveOverlay->deleteLater(); - m_moveOverlays.clear(); - } - -} - -void -Context::ToolbarView::appletRemoved( Plasma::Applet* applet ) -{ - DEBUG_BLOCK - foreach( Context::AppletItemOverlay* overlay, m_moveOverlays ) - { - if( overlay->applet()->applet() == applet ) - { - m_moveOverlays.removeAll( overlay ); - debug() << "got an overlay to remove"; - } - } - m_toolbar.data()->appletRemoved( applet ); - applet->deleteLater(); -} - -void -Context::ToolbarView::appletAdded( Plasma::Applet* applet, int loc ) -{ - DEBUG_BLOCK - Q_UNUSED( applet ) - Q_UNUSED( loc ) - - if( m_toolbar.data()->configEnabled() ) - recreateOverlays(); -} - - -void -Context::ToolbarView::refreshOverlays() -{ -} - -void -Context::ToolbarView::recreateOverlays() -{ - DEBUG_BLOCK - foreach( Context::AppletItemOverlay *moveOverlay, m_moveOverlays ) - moveOverlay->deleteLater(); - - m_moveOverlays.clear(); - - QColor overlayColor( Plasma::Theme::defaultTheme()->color( Plasma::Theme::BackgroundColor ) ); - QBrush overlayBrush( overlayColor ); - QPalette p( palette() ); - p.setBrush( QPalette::Window, overlayBrush ); - for( int i = 0; i < m_toolbar.data()->appletLayout()->count(); i++ ) - { - debug() << "creating a move overlay"; - Context::AppletToolbarAppletItem* item = dynamic_cast< Context::AppletToolbarAppletItem* >( m_toolbar.data()->appletLayout()->itemAt( i ) ); - if( item ) - { - Context::AppletItemOverlay *moveOverlay = new Context::AppletItemOverlay( item, m_toolbar.data()->appletLayout(), this ); - connect( moveOverlay, &Context::AppletItemOverlay::moveApplet, m_cont, &Context::Containment::moveApplet ); - connect( moveOverlay, &Context::AppletItemOverlay::moveApplet, this, &Context::ToolbarView::refreshOverlays ); - connect( moveOverlay, &Context::AppletItemOverlay::deleteApplet, this, &Context::ToolbarView::appletRemoved ); - moveOverlay->setPalette( p ); - moveOverlay->show(); - moveOverlay->raise(); - m_moveOverlays << moveOverlay; - debug() << moveOverlay << moveOverlay->geometry(); - } - - } -} - -void -Context::ToolbarView::refreshSycoca() -{ - QDBusInterface dbus("org.kde.kded", "/kbuildsycoca", "org.kde.kbuildsycoca"); - dbus.call(QDBus::Block, "recreate"); - - recreateOverlays(); -} - diff --git a/src/context/ToolbarView.h b/src/context/ToolbarView.h deleted file mode 100644 index b3925128f5..0000000000 --- a/src/context/ToolbarView.h +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_CONTEXT_TOOLBAR_VIEW -#define AMAROK_CONTEXT_TOOLBAR_VIEW - -#include -#include - -class QGraphicsScene; -class QWidget; - -namespace Plasma -{ - class Applet; - class Containment; -} - -namespace Context -{ - -class AppletToolbar; -class AppletToolbarAppletItem; -class AppletItemOverlay; - -/** - * The applet toolbar is a separate QGV on the same QGS that the ContextView uses. This is because we want to be able to show - * the add applet menu in the main CV, but we also want to manage the toolbar differently. So the toolbar is actually in the - * same scene as the applets but shifted a few thousand pixels so it is invisible in the main CV. The ToolbarView however is - * positioned directly above the toolbar in the scene. - */ -class ToolbarView : public QGraphicsView -{ - Q_OBJECT - - public: - explicit ToolbarView( Plasma::Containment* cont, QGraphicsScene* scene, QWidget* parent = 0 ); - ~ToolbarView(); - - Q_SIGNALS: - void hideAppletExplorer(); - void showAppletExplorer(); - - protected: - void resizeEvent( QResizeEvent * event ); - void dragEnterEvent(QDragEnterEvent *event); - void dragMoveEvent(QDragMoveEvent *event); - void dragLeaveEvent(QDragLeaveEvent *event); - - private Q_SLOTS: - void applyStyleSheet(); - void toggleConfigMode(); - void appletRemoved( Plasma::Applet* ); - void appletAdded( Plasma::Applet*, int); - void refreshOverlays(); - void recreateOverlays(); - void refreshSycoca(); - - private: - int m_height; - QWeakPointer m_toolbar; - QList< AppletItemOverlay* > m_moveOverlays; - Plasma::Containment* m_cont; -}; - -} - - -#endif diff --git a/src/context/amarokapplets.knsrc b/src/context/amarokapplets.knsrc deleted file mode 100644 index e91bbfb414..0000000000 --- a/src/context/amarokapplets.knsrc +++ /dev/null @@ -1,5 +0,0 @@ -[KNewStuff2] -ProvidersUrl=http://download.kde.org/khotnewstuff/amarokapplets-providers.xml -StandardResource=tmp -InstallationCommand=amarokpkg -i %f -UninstallCommand=amarokpkg -r %f diff --git a/src/context/applets/CMakeLists.txt b/src/context/applets/CMakeLists.txt index f4d5456d9f..389aac93bc 100644 --- a/src/context/applets/CMakeLists.txt +++ b/src/context/applets/CMakeLists.txt @@ -1,18 +1,15 @@ add_subdirectory( albums ) add_subdirectory( currenttrack ) -add_subdirectory( info ) -add_subdirectory( labels ) +#add_subdirectory( info ) +#add_subdirectory( labels ) add_subdirectory( lyrics ) add_subdirectory( photos ) -add_subdirectory( tabs ) -add_subdirectory( wikipedia ) - -if( QT_QTOPENGL_FOUND ) - add_subdirectory( analyzer ) -endif() +#add_subdirectory( tabs ) +#add_subdirectory( wikipedia ) +add_subdirectory( analyzer ) if( LIBLASTFM_FOUND ) - add_subdirectory( upcomingevents ) - add_subdirectory( similarartists ) +# add_subdirectory( upcomingevents ) +# add_subdirectory( similarartists ) endif() diff --git a/src/context/applets/albums/Albums.cpp b/src/context/applets/albums/Albums.cpp deleted file mode 100644 index 4106f1e73d..0000000000 --- a/src/context/applets/albums/Albums.cpp +++ /dev/null @@ -1,383 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Seb Ruiz * - * Copyright (c) 2008 William Viana Soares * - * Copyright (c) 2009 simon.esneault * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "Albums" - -#include "Albums.h" - -#include "AlbumItem.h" -#include "AlbumsView.h" -#include "core/collections/Collection.h" -#include "core/meta/Meta.h" -#include "core/support/Amarok.h" -#include "core/support/Debug.h" -#include "core-impl/collections/support/CollectionManager.h" -#include "context/widgets/AppletHeader.h" -#include "TrackItem.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -Albums::Albums( QObject* parent, const QVariantList& args ) - : Context::Applet( parent, args ) - , m_recentCount( Amarok::config("Albums Applet").readEntry("RecentlyAdded", 5) ) - , m_rightAlignLength( Amarok::config("Albums Applet").readEntry("RightAlignLength", false) ) - , m_albumsView( 0 ) -{ - setHasConfigurationInterface( true ); -} - -Albums::~Albums() -{ -} - -void Albums::init() -{ - DEBUG_BLOCK - - // Call the base implementation. - Context::Applet::init(); - - enableHeader( true ); - setHeaderText( i18n( "Recently Added Albums" ) ); - - setCollapseOffHeight( -1 ); - setCollapseHeight( m_header->height() ); - setMinimumHeight( collapseHeight() ); - - QAction *settingsAction = new QAction( this ); - settingsAction->setIcon( QIcon::fromTheme( "preferences-system" ) ); - settingsAction->setEnabled( true ); - settingsAction->setToolTip( i18n( "Settings" ) ); - addRightHeaderAction( settingsAction ); - connect( settingsAction, SIGNAL(triggered()), this, SLOT(showConfigurationInterface()) ); - - QAction *filterAction = new QAction( this ); - filterAction->setIcon( QIcon::fromTheme( "view-filter" ) ); - filterAction->setEnabled( true ); - filterAction->setToolTip( i18n( "Filter Albums" ) ); - m_filterIcon = addLeftHeaderAction( filterAction ); - connect( filterAction, SIGNAL(triggered()), this, SLOT(showFilterBar()) ); - - m_albumsView = new AlbumsView( this ); - m_albumsView->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - if( m_rightAlignLength ) - m_albumsView->setLengthAlignment( Qt::AlignRight ); - - QGraphicsLinearLayout *layout = new QGraphicsLinearLayout( Qt::Vertical ); - layout->addItem( m_header ); - layout->addItem( m_albumsView ); - setLayout( layout ); - - dataEngine( "amarok-current" )->connectSource( "albums", this ); - connect( CollectionManager::instance(), SIGNAL(collectionDataChanged(Collections::Collection*)), - this, SLOT(collectionDataChanged(Collections::Collection*)) ); - - updateConstraints(); -} - -void Albums::showFilterBar() -{ - m_filterIcon->setEnabled( false ); - AlbumsFilterBar *bar = new AlbumsFilterBar( this ); - bar->setContentsMargins( 0, 0, 0, 0 ); - QGraphicsLinearLayout *l = static_cast( layout() ); - l->setItemSpacing( 1, 0 ); - l->addItem( bar ); - connect( bar, SIGNAL(filterTextChanged(QString)), this, SLOT(filterTextChanged(QString)) ); - connect( bar, SIGNAL(closeRequested()), this, SLOT(closeFilterBar()) ); - bar->focusEditor(); -} - -void Albums::closeFilterBar() -{ - filterTextChanged( QString() ); - AlbumsFilterBar *bar = static_cast( sender() ); - QGraphicsLinearLayout *l = static_cast( layout() ); - l->removeItem( bar ); - bar->deleteLater(); - m_filterIcon->setEnabled( true ); -} - -void Albums::filterTextChanged( const QString &text ) -{ - m_albumsView->setFilterPattern( text ); -} - -void Albums::dataUpdated( const QString &name, const Plasma::DataEngine::Data &data ) -{ - if( name != QLatin1String("albums") ) - return; - - Meta::AlbumList albums = data[ "albums" ].value(); - Meta::TrackPtr track = data[ "currentTrack" ].value(); - QString headerText = data[ "headerText" ].toString(); - setHeaderText( headerText.isEmpty() ? i18n("Albums") : headerText ); - - //Don't keep showing the albums for the artist of the last track that had album in the collection - if( (m_currentTrack == track) && (m_albums == albums) ) - return; - - if( albums.isEmpty() ) - { - debug() << "received albums is empty"; - setCollapseOn(); - m_albums.clear(); - m_albumsView->clear(); - return; - } - - setCollapseOff(); - - m_albums = albums; - m_currentTrack = track; - m_albumsView->clear(); - m_albumsView->setMode( track ? AlbumsProxyModel::SortByYear : AlbumsProxyModel::SortByCreateDate ); - QStandardItem *currentItem( 0 ); - - foreach( Meta::AlbumPtr albumPtr, albums ) - { - // do not show all tracks without an album from the collection, this takes ages - // TODO: show all tracks from this artist that are not part of an album - if( albumPtr->name().isEmpty() ) - continue; - - Meta::TrackList tracks = albumPtr->tracks(); - if( tracks.isEmpty() ) - continue; - - AlbumItem *albumItem = new AlbumItem(); - albumItem->setIconSize( 50 ); - albumItem->setAlbum( albumPtr ); - albumItem->setShowArtist( !m_currentTrack ); - - int numberOfDiscs = 0; - int childRow = 0; - - qStableSort( tracks.begin(), tracks.end(), Meta::Track::lessThan ); - - QMultiHash< int, TrackItem* > trackItems; // hash of tracks items for each disc - foreach( Meta::TrackPtr trackPtr, tracks ) - { - if( numberOfDiscs < trackPtr->discNumber() ) - numberOfDiscs = trackPtr->discNumber(); - - TrackItem *trackItem = new TrackItem(); - trackItem->setTrack( trackPtr ); - - // bold the current track to make it more visible - if( m_currentTrack && *m_currentTrack == *trackPtr ) - { - currentItem = trackItem; - trackItem->bold(); - } - - // If compilation and same artist, then highlight, but only if there's a current track - if( m_currentTrack - && m_currentTrack->artist() && trackPtr->artist() - && (*m_currentTrack->artist() == *trackPtr->artist()) - && albumPtr->isCompilation() ) - { - trackItem->italicise(); - } - trackItems.insert( trackPtr->discNumber(), trackItem ); - } - - for( int i = 0; i <= numberOfDiscs; ++i ) - { - QList items = trackItems.values( i ); - if( !items.isEmpty() ) - { - const TrackItem *item = items.first(); - QStandardItem *discItem( 0 ); - if( numberOfDiscs > 1 ) - { - discItem = new QStandardItem( i18n("Disc %1", item->track()->discNumber()) ); - albumItem->setChild( childRow++, discItem ); - int discChildRow = 0; - foreach( TrackItem *trackItem, items ) - discItem->setChild( discChildRow++, trackItem ); - } - else - { - foreach( TrackItem *trackItem, items ) - albumItem->setChild( childRow++, trackItem ); - } - } - } - m_albumsView->appendAlbum( albumItem ); - } - - m_albumsView->sort(); - if( currentItem ) - { - m_albumsView->setRecursiveExpanded( currentItem, true ); - m_albumsView->scrollTo( currentItem ); - } - - updateConstraints(); -} - -void Albums::createConfigurationInterface( KConfigDialog *parent ) -{ - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); - QVBoxLayout *mainLayout = new QVBoxLayout; - parent->setLayout(mainLayout); - QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); - okButton->setDefault(true); - okButton->setShortcut(Qt::CTRL | Qt::Key_Return); - parent->connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); - parent->connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - mainLayout->addWidget(buttonBox); - - QSpinBox *spinBox = new QSpinBox; - spinBox->setRange( 1, 100 ); - spinBox->setValue( m_recentCount ); - connect( spinBox, SIGNAL(valueChanged(int)), SLOT(setRecentCount(int)) ); - - QCheckBox *checkBox = new QCheckBox( i18n( "Right align track lengths" ) ); - checkBox->setCheckState( m_rightAlignLength ? Qt::Checked : Qt::Unchecked ); - connect( checkBox, SIGNAL(stateChanged(int)), SLOT(setRightAlignLength(int)) ); - - QFormLayout *formLayout = new QFormLayout; - formLayout->addRow( i18n("Number of recently added albums:"), spinBox ); - formLayout->addRow( checkBox ); - - QWidget *config = new QWidget; - config->setLayout( formLayout ); - - parent->addPage( config, i18n( "Albums Applet Settings" ), "preferences-system"); - connect( parent, SIGNAL(accepted()), this, SLOT(saveConfiguration()) ); -} - -void Albums::keyPressEvent( QKeyEvent *event ) -{ - if( event->key() == Qt::Key_Slash || event->matches( QKeySequence::Find ) ) - { - if( m_filterIcon->isEnabled() ) - { - showFilterBar(); - event->accept(); - return; - } - } - Context::Applet::keyPressEvent( event ); -} - -void Albums::setRecentCount( int val ) -{ - m_recentCount = val; -} - -void Albums::setRightAlignLength( int state ) -{ - m_rightAlignLength = (state == Qt::Checked ); - m_albumsView->setLengthAlignment( m_rightAlignLength ? Qt::AlignRight : Qt::AlignLeft ); -} - -void Albums::saveConfiguration() -{ - Amarok::config("Albums Applet").writeEntry( "RecentlyAdded", QString::number( m_recentCount ) ); - Amarok::config("Albums Applet").writeEntry( "RightAlignLength", m_rightAlignLength ); - - // clear to force an update - m_albums.clear(); - - Plasma::DataEngine::Data data = dataEngine( "amarok-current" )->query( "albums" ); - dataUpdated( QLatin1String("albums"), data ); -} - -void Albums::collectionDataChanged( Collections::Collection *collection ) -{ - Q_UNUSED( collection ) - - DEBUG_BLOCK -} - -AlbumsFilterBar::AlbumsFilterBar( QGraphicsItem *parent, Qt::WindowFlags wFlags ) - : QGraphicsWidget( parent, wFlags ) - , m_editor( new KLineEdit ) - , m_closeIcon( new Plasma::IconWidget( QIcon::fromTheme("dialog-close"), QString(), this ) ) -{ - QGraphicsProxyWidget *editProxy = new QGraphicsProxyWidget( this ); - editProxy->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); - editProxy->setWidget( m_editor ); - - m_editor->installEventFilter( this ); - m_editor->setAttribute( Qt::WA_NoSystemBackground ); - m_editor->setAutoFillBackground( true ); - m_editor->setClearButtonShown( true ); - m_editor->setClickMessage( i18n( "Filter Albums" ) ); - m_editor->setContentsMargins( 0, 0, 0, 0 ); - - QSizeF iconSize = m_closeIcon->sizeFromIconSize( 16 ); - m_closeIcon->setMaximumSize( iconSize ); - m_closeIcon->setMinimumSize( iconSize ); - - QGraphicsLinearLayout *layout = new QGraphicsLinearLayout( Qt::Horizontal, this ); - layout->setSpacing( 1 ); - layout->addItem( editProxy ); - layout->addItem( m_closeIcon ); - layout->setStretchFactor( editProxy, 100 ); - layout->setAlignment( editProxy, Qt::AlignCenter ); - layout->setAlignment( m_closeIcon, Qt::AlignCenter ); - layout->setContentsMargins( 0, 2, 0, 0 ); - - m_closeIcon->setToolTip( i18n( "Close" ) ); - connect( m_closeIcon, SIGNAL(clicked()), SIGNAL(closeRequested()) ); - connect( m_editor, SIGNAL(textChanged(QString)), SIGNAL(filterTextChanged(QString)) ); -} - -bool -AlbumsFilterBar::eventFilter( QObject *obj, QEvent *e ) -{ - if( obj == m_editor ) - { - if( e->type() == QEvent::KeyPress ) - { - QKeyEvent *kev = static_cast( e ); - if( kev->key() == Qt::Key_Escape ) - { - kev->accept(); - emit closeRequested(); - return true; - } - } - } - return QGraphicsWidget::eventFilter( obj, e ); -} - -void -AlbumsFilterBar::focusEditor() -{ - m_editor->setFocus( Qt::PopupFocusReason ); -} - diff --git a/src/context/applets/albums/Albums.h b/src/context/applets/albums/Albums.h deleted file mode 100644 index 4a2180efed..0000000000 --- a/src/context/applets/albums/Albums.h +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Seb Ruiz * - * Copyright (c) 2008 William Viana Soares * - * Copyright (c) 2009 simon.esneault * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef ALBUMS_APPLET_H -#define ALBUMS_APPLET_H - -#include "context/Applet.h" -#include "context/DataEngine.h" -#include "core/meta/forward_declarations.h" - -#include - -class AlbumsView; -class KLineEdit; -namespace Collections { - class Collection; -} -namespace Plasma { - class IconWidget; -} - -class Albums : public Context::Applet -{ - Q_OBJECT -public: - Albums( QObject* parent, const QVariantList& args ); - ~Albums(); - -public Q_SLOTS: - virtual void init(); - void dataUpdated( const QString& name, const Plasma::DataEngine::Data &data ); - -protected: - void createConfigurationInterface( KConfigDialog *parent ); - void keyPressEvent( QKeyEvent *event ); - -private Q_SLOTS: - void collectionDataChanged( Collections::Collection *collection ); - void saveConfiguration(); - void setRecentCount( int val ); - void setRightAlignLength( int state ); - void showFilterBar(); - void closeFilterBar(); - void filterTextChanged( const QString &text ); - -private: - int m_recentCount; - bool m_rightAlignLength; - AlbumsView *m_albumsView; - Meta::AlbumList m_albums; - Meta::TrackPtr m_currentTrack; - Plasma::IconWidget *m_filterIcon; -}; - -class AlbumsFilterBar : public QGraphicsWidget -{ - Q_OBJECT - -public: - AlbumsFilterBar( QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0 ); - ~AlbumsFilterBar() {} - - bool eventFilter( QObject *obj, QEvent *e ); - void focusEditor(); - -Q_SIGNALS: - void closeRequested(); - void filterTextChanged( const QString &text ); - -private: - KLineEdit *m_editor; - Plasma::IconWidget *m_closeIcon; -}; - -AMAROK_EXPORT_APPLET( albums, Albums ) - -#endif diff --git a/src/context/applets/albums/AlbumsView.cpp b/src/context/applets/albums/AlbumsView.cpp deleted file mode 100644 index c9057949cf..0000000000 --- a/src/context/applets/albums/AlbumsView.cpp +++ /dev/null @@ -1,614 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 William Viana Soares * - * Copyright (c) 2008 Seb Ruiz * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "AlbumsView" - -#include "AlbumsView.h" - -#include "AlbumItem.h" -#include "AlbumsDefs.h" -#include "SvgHandler.h" -#include "TrackItem.h" -#include "core/capabilities/ActionsCapability.h" -#include "core/meta/Meta.h" -#include "core/meta/support/MetaUtility.h" -#include "core/support/Debug.h" -#include "dialogs/TagDialog.h" -#include "playlist/PlaylistController.h" -#include "widgets/PrettyTreeView.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -// Subclassed to override the access level of some methods. -// The AlbumsTreeView and the AlbumsView are so highly coupled that this is acceptable, imo. -class AlbumsTreeView : public Amarok::PrettyTreeView -{ - public: - AlbumsTreeView( QWidget *parent = 0 ) - : Amarok::PrettyTreeView( parent ) - { - setAttribute( Qt::WA_NoSystemBackground ); - viewport()->setAutoFillBackground( false ); - - setHeaderHidden( true ); - setIconSize( QSize(60,60) ); - setDragDropMode( QAbstractItemView::DragOnly ); - setSelectionMode( QAbstractItemView::ExtendedSelection ); - setSelectionBehavior( QAbstractItemView::SelectItems ); - if( KGlobalSettings::graphicEffectsLevel() != KGlobalSettings::NoEffects ) - setAnimated( true ); - setRootIsDecorated( false ); - setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); // see wheelEvent() - setItemDelegate( new AlbumsItemDelegate( this ) ); - setFrameStyle( QFrame::NoFrame ); - } - - // Override access level to make it public. Only visible to the AlbumsView. - // Used for context menu methods. - QModelIndexList selectedIndexes() const { return PrettyTreeView::selectedIndexes(); } - - protected: - void wheelEvent( QWheelEvent *e ) - { - // scroll per pixel doesn't work when using delegates with big height (QTBUG-7232). - // This is a work around for scrolling with smaller steps. - AlbumsProxyModel *proxyModel = static_cast( model() ); - AlbumsModel *albumsModel = static_cast( proxyModel->sourceModel() ); - verticalScrollBar()->setSingleStep( albumsModel->rowHeight() ); - Amarok::PrettyTreeView::wheelEvent( e ); - } -}; - -AlbumsView::AlbumsView( QGraphicsWidget *parent ) - : QGraphicsWidget( parent ) -{ - Plasma::Svg *borderSvg = new Plasma::Svg( this ); - borderSvg->setImagePath( "widgets/scrollwidget" ); - - m_topBorder = new Plasma::SvgWidget( this ); - m_topBorder->setSvg( borderSvg ); - m_topBorder->setElementID( "border-top" ); - m_topBorder->setZValue( 900 ); - m_topBorder->resize( -1, 10.0 ); - m_topBorder->show(); - - m_bottomBorder = new Plasma::SvgWidget( this ); - m_bottomBorder->setSvg( borderSvg ); - m_bottomBorder->setElementID( "border-bottom" ); - m_bottomBorder->setZValue( 900 ); - m_bottomBorder->resize( -1, 10.0 ); - m_bottomBorder->show(); - - m_treeProxy = new QGraphicsProxyWidget( this ); - m_treeView = new AlbumsTreeView( 0 ); - connect( m_treeView, SIGNAL(clicked(QModelIndex)), this, SLOT(itemClicked(QModelIndex)) ); - connect( m_treeView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotDoubleClicked()) ); - m_treeProxy->setWidget( m_treeView ); - - m_model = new AlbumsModel( this ); - m_model->setColumnCount( 1 ); - m_proxyModel = new AlbumsProxyModel( this ); - m_proxyModel->setFilterCaseSensitivity( Qt::CaseInsensitive ); - m_proxyModel->setSortLocaleAware( true ); - m_proxyModel->setDynamicSortFilter( true ); - m_proxyModel->setSourceModel( m_model ); - m_proxyModel->setFilterRole( NameRole ); - m_treeView->setModel( m_proxyModel ); - - QScrollBar *treeScrollBar = m_treeView->verticalScrollBar(); - m_scrollBar = new Plasma::ScrollBar( this ); - m_scrollBar->setFocusPolicy( Qt::NoFocus ); - - // synchronize scrollbars - connect( treeScrollBar, SIGNAL(rangeChanged(int,int)), SLOT(slotScrollBarRangeChanged(int,int)) ); - connect( treeScrollBar, SIGNAL(valueChanged(int)), m_scrollBar, SLOT(setValue(int)) ); - connect( m_scrollBar, SIGNAL(valueChanged(int)), treeScrollBar, SLOT(setValue(int)) ); - m_scrollBar->setRange( treeScrollBar->minimum(), treeScrollBar->maximum() ); - m_scrollBar->setPageStep( treeScrollBar->pageStep() ); - m_scrollBar->setSingleStep( treeScrollBar->singleStep() ); - - QGraphicsLinearLayout *layout = new QGraphicsLinearLayout( Qt::Horizontal ); - layout->addItem( m_treeProxy ); - layout->addItem( m_scrollBar ); - layout->setSpacing( 2 ); - layout->setContentsMargins( 0, 0, 0, 0 ); - setLayout( layout ); - setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - updateScrollBarVisibility(); -} - -AlbumsView::~AlbumsView() -{ -} - -void -AlbumsView::appendAlbum( QStandardItem *album ) -{ - m_model->appendRow( album ); -} - -void -AlbumsView::sort() -{ - m_proxyModel->sort( 0 ); -} - -void -AlbumsView::scrollTo( QStandardItem *album ) -{ - const QModelIndex &proxyIndex = m_proxyModel->mapFromSource( album->index() ); - m_treeView->scrollTo( proxyIndex, QAbstractItemView::EnsureVisible ); -} - -QString -AlbumsView::filterPattern() const -{ - return m_proxyModel->filterRegExp().pattern(); -} - -void -AlbumsView::setFilterPattern( const QString &pattern ) -{ - m_proxyModel->setFilterRegExp( QRegExp(pattern, Qt::CaseInsensitive) ); -} - -void -AlbumsView::clear() -{ - qDeleteAll( m_model->findItems( QLatin1String( "*" ), Qt::MatchWildcard ) ); - m_model->clear(); -} - -AlbumsProxyModel::Mode -AlbumsView::mode() const -{ - return m_proxyModel->mode(); -} - -void -AlbumsView::setMode( AlbumsProxyModel::Mode mode ) -{ - m_proxyModel->setMode( mode ); -} - -Qt::Alignment -AlbumsView::lengthAlignment() const -{ - return static_cast( m_treeView->itemDelegate() )->lengthAlignment(); -} - -void -AlbumsView::setLengthAlignment( Qt::Alignment alignment ) -{ - static_cast( m_treeView->itemDelegate() )->setLengthAlignment( alignment ); -} - -void -AlbumsView::itemClicked( const QModelIndex &index ) -{ - if( !m_treeView->model()->hasChildren( index ) ) - return; - - bool expanded = m_treeView->isExpanded( index ); - if( expanded ) - m_treeView->setExpanded( index, !expanded ); - else - setRecursiveExpanded( index, !expanded ); -} - -void -AlbumsView::contextMenuEvent( QGraphicsSceneContextMenuEvent *event ) -{ - const QModelIndex index = m_treeView->indexAt( event->pos().toPoint() ); - if( !index.isValid() ) - { - QGraphicsWidget::contextMenuEvent( event ); - return; - } - - QMenu menu; - QAction *appendAction = new QAction( QIcon::fromTheme( "media-track-add-amarok" ), i18n( "&Add to Playlist" ), &menu ); - QAction *loadAction = new QAction( QIcon::fromTheme( "folder-open" ), i18nc( "Replace the currently loaded tracks with these", "&Replace Playlist" ), &menu ); - QAction *queueAction = new QAction( QIcon::fromTheme( "media-track-queue-amarok" ), i18n( "&Queue" ), &menu ); - QAction *editAction = new QAction( QIcon::fromTheme( "media-track-edit-amarok" ), i18n( "Edit Track Details" ), &menu ); - - menu.addAction( appendAction ); - menu.addAction( loadAction ); - menu.addAction( queueAction ); - menu.addAction( editAction ); - - connect( appendAction, SIGNAL(triggered()), this, SLOT(slotAppendSelected()) ); - connect( loadAction , SIGNAL(triggered()), this, SLOT(slotReplaceWithSelected()) ); - connect( queueAction , SIGNAL(triggered()), this, SLOT(slotQueueSelected()) ); - connect( editAction , SIGNAL(triggered()), this, SLOT(slotEditSelected()) ); - - QMenu menuCover( i18n( "Album" ), &menu ); - const QStandardItem *item = m_model->itemFromIndex( m_proxyModel->mapToSource(index) ); - if( item->type() == AlbumType ) - { - Meta::AlbumPtr album = static_cast( item )->album(); - QScopedPointer ac( album->create() ); - if( ac ) - { - QList actions = ac->actions(); - if( !actions.isEmpty() ) - { - // ensure that the actions are cleaned up afterwards - foreach( QAction *action, actions ) - { - if( !action->parent() ) - action->setParent( &menuCover ); - } - - menuCover.addActions( actions ); - menuCover.setIcon( QIcon::fromTheme( "filename-album-amarok" ) ); - menu.addMenu( &menuCover ); - } - } - } - menu.exec( event->screenPos() ); -} - -void -AlbumsView::resizeEvent( QGraphicsSceneResizeEvent *event ) -{ - QGraphicsWidget::resizeEvent( event ); - if( m_topBorder ) - { - m_topBorder->resize( event->newSize().width(), m_topBorder->size().height() ); - m_bottomBorder->resize( event->newSize().width(), m_bottomBorder->size().height() ); - m_topBorder->setPos( m_treeProxy->pos() ); - QPointF bottomPoint = m_treeProxy->boundingRect().bottomLeft(); - bottomPoint.ry() -= m_bottomBorder->size().height(); - m_bottomBorder->setPos( bottomPoint ); - } -} - -void AlbumsView::slotDoubleClicked() -{ - Meta::TrackList selected = getSelectedTracks(); - The::playlistController()->insertOptioned( selected, Playlist::OnDoubleClickOnSelectedItems ); -} - -void -AlbumsView::slotAppendSelected() -{ - Meta::TrackList selected = getSelectedTracks(); - The::playlistController()->insertOptioned( selected, Playlist::OnAppendToPlaylistAction ); -} - -void -AlbumsView::slotReplaceWithSelected() -{ - Meta::TrackList selected = getSelectedTracks(); - The::playlistController()->insertOptioned( selected, Playlist::OnReplacePlaylistAction ); -} - -void -AlbumsView::slotQueueSelected() -{ - Meta::TrackList selected = getSelectedTracks(); - The::playlistController()->insertOptioned( selected, Playlist::OnQueueToPlaylistAction ); -} - -void -AlbumsView::slotEditSelected() -{ - Meta::TrackList selected = getSelectedTracks(); - if( !selected.isEmpty() ) - { - TagDialog *dialog = new TagDialog( selected ); - dialog->show(); - } -} - -void -AlbumsView::slotScrollBarRangeChanged( int min, int max ) -{ - m_scrollBar->setRange( min, max ); - m_scrollBar->setPageStep( m_treeView->verticalScrollBar()->pageStep() ); - m_scrollBar->setSingleStep( m_treeView->verticalScrollBar()->singleStep() ); - updateScrollBarVisibility(); -} - -void -AlbumsView::updateScrollBarVisibility() -{ - QGraphicsLinearLayout *lo = static_cast( layout() ); - if( m_scrollBar->maximum() == 0 ) - { - if( lo->count() > 1 && lo->itemAt(1) == m_scrollBar ) - { - lo->removeAt( 1 ); - m_scrollBar->hide(); - } - } - else if( lo->count() == 1 ) - { - lo->addItem( m_scrollBar ); - m_scrollBar->show(); - } -} - -Meta::TrackList -AlbumsView::getSelectedTracks() const -{ - Meta::TrackList selected; - - QModelIndexList indexes = static_cast( m_treeView )->selectedIndexes(); - foreach( const QModelIndex &index, indexes ) - { - if( index.isValid() ) - { - const QModelIndex &srcIndex = m_proxyModel->mapToSource( index ); - const QStandardItem *item = m_model->itemFromIndex( srcIndex ); - if( item->type() == AlbumType ) - { - selected << static_cast( item )->album()->tracks(); - } - else if( item->type() == TrackType ) - { - selected << static_cast( item )->track(); - } - else if( m_model->hasChildren( srcIndex ) ) // disc type - { - for( int i = m_model->rowCount( srcIndex ) - 1; i >= 0; --i ) - { - const QStandardItem *trackItem = m_model->itemFromIndex( srcIndex.child(i, 0) ); - selected << static_cast( trackItem )->track(); - } - } - } - } - - return selected; -} - -void -AlbumsView::setRecursiveExpanded( QStandardItem *item, bool expanded ) -{ - setRecursiveExpanded( m_proxyModel->mapFromSource( item->index() ), expanded ); -} - -void -AlbumsView::setRecursiveExpanded( const QModelIndex &index, bool expanded ) -{ - if( m_proxyModel->hasChildren( index ) ) - { - for( int i = 0, count = m_proxyModel->rowCount( index ); i < count; ++i ) - m_treeView->setExpanded( index.child( i, 0 ), expanded ); - } - m_treeView->setExpanded( index, expanded ); -} - -AlbumsItemDelegate::AlbumsItemDelegate( QObject *parent ) - : QStyledItemDelegate( parent ) - , m_lengthAlignment( Qt::AlignLeft ) -{} - -/* - * Customize the painting of items in the tree view. - * - * AlbumItems: The album items' displayed text has the format - * "artist - album (year)\ntracks, time", i.e. the album info is shown in two - * lines. When resizing the context view, the string is elided if the available - * width is less than the text width. That ellipsis is done on the whole string - * however, so the second line disappears. A solution is to do the eliding - * ourselves. - * - * TrackItems: The track number and length gets special treatment. They are - * painted at the beginning and end of the string, respectively. The track - * numbers are right aligned and a suitable width is used to make the numbers - * align for all the tracks in the album. The track name and artist (the latter - * is included if the album is a compilation), are placed in between the track - * number and length; it is elided if necessary. Thus the track number and - * length are always shown, even during eliding. - */ -void -AlbumsItemDelegate::paint( QPainter *p, - const QStyleOptionViewItem &option, - const QModelIndex &index ) const -{ - QStyleOptionViewItem sepOption = option; - QStyledItemDelegate::paint( p, sepOption, index ); - const QAbstractProxyModel *xyModel = qobject_cast( index.model() ); - const QStandardItemModel *stdModel = qobject_cast( xyModel->sourceModel() ); - const QStandardItem *item = stdModel->itemFromIndex( xyModel->mapToSource(index) ); - if( item->type() == AlbumType ) - { - // draw the text ourselves. The superclass will skip painting the - // text since the text in Qt::DisplayRole is not actually set. - QStyleOptionViewItemV4 vopt( option ); - initStyleOption( &vopt, index ); - const AlbumItem *albumItem = static_cast( item ); - int iconSize = albumItem->iconSize(); - QSize coverSize = albumItem->album()->image( iconSize ).size(); - coverSize.rwidth() += 6; // take into account of svg borders - coverSize.rheight() += 6; - qreal aspectRatio = static_cast( coverSize.width() ) / coverSize.height(); - const int margin = vopt.widget->style()->pixelMetric( QStyle::PM_FocusFrameHMargin ) + 1; - const int offset = qMin( int(iconSize * aspectRatio), iconSize ) + margin; - if( option.direction == Qt::RightToLeft ) - vopt.rect.adjust( 0, 0, -offset, 0 ); - else - vopt.rect.adjust( offset, 0, 0, 0 ); - drawAlbumText( p, vopt ); - } - else if( item->type() == TrackType ) - { - QStyleOptionViewItemV4 vopt( option ); - initStyleOption( &vopt, index ); - if( option.direction == Qt::RightToLeft ) - vopt.rect.adjust( 2, 0, 0, 0 ); - else - vopt.rect.adjust( 0, 0, -2, 0 ); - drawTrackText( p, vopt ); - } -} - -void -AlbumsItemDelegate::drawAlbumText( QPainter *p, const QStyleOptionViewItemV4 &vopt ) const -{ - const QModelIndex &index = vopt.index; - const QRect &textRect = vopt.rect.adjusted( 4, 0, -4, 0 ); - - p->save(); - p->setClipRect( textRect ); - applyCommonStyle( p, vopt ); - - QString name = index.data( NameRole ).toString(); - int year = index.data( AlbumYearRole ).toInt(); - - QStringList texts; - texts << ((year > 0) ? QString( "%1 (%2)" ).arg( name, QString::number(year) ) : name); - texts << index.data( AlbumLengthRole ).toString(); - - // elide each line according to available width - QFontMetrics fm = vopt.fontMetrics; - QMutableStringListIterator it( texts ); - while( it.hasNext() ) - { - const QString &text = it.next(); - if( fm.width( text ) > textRect.width() ) - it.setValue( fm.elidedText( text, Qt::ElideRight, textRect.width() ) ); - } - - p->drawText( textRect, Qt::AlignLeft | Qt::AlignVCenter, texts.join("\n") ); - p->restore(); -} - -void -AlbumsItemDelegate::drawTrackText( QPainter *p, const QStyleOptionViewItemV4 &vopt ) const -{ - const QModelIndex &index = vopt.index; - - int trackDigitCount = index.data( AlbumMaxTrackNumberRole ).toString().length(); - bool isCompilation = index.data( AlbumCompilationRole ).toBool(); - const QString &name = index.data( NameRole ).toString(); - const QString &artist = index.data( TrackArtistRole ).toString(); - QString length = " (" + Meta::msToPrettyTime( index.data( TrackLengthRole ).toInt() ) + ')'; - QString number = index.data( TrackNumberRole ).toString() + ". "; - QString middle = isCompilation ? QString( "%1 - %2" ).arg( artist, name ) : name; - - // use boldface font metrics for measuring track numbers - QFont boldFont = vopt.font; - boldFont.setBold( true ); - QFontMetrics boldFm( boldFont, p->device() ); - QFontMetrics fm( vopt.fontMetrics ); - - int numberFillWidth = boldFm.width( QChar('0') ) * ( trackDigitCount - number.length() + 2 ); - int numberRectWidth = 0; - if( number != QLatin1String("0. ") ) - numberRectWidth = numberFillWidth + boldFm.width( number ) + 2; - int lengthRectWidth = boldFm.width( length ); - int availableWidth = vopt.rect.width() - numberRectWidth - lengthRectWidth; - if( availableWidth < fm.width( middle ) ) - middle = fm.elidedText( middle, Qt::ElideRight, availableWidth ); - - p->save(); - p->setClipRect( vopt.rect ); - p->setBackground( vopt.backgroundBrush ); - p->setLayoutDirection( vopt.direction ); - p->setFont( vopt.font ); - applyCommonStyle( p, vopt ); - int textRectWidth = (m_lengthAlignment == Qt::AlignLeft) ? fm.width( middle ) : availableWidth; - - QRect numberRect; - QRect textRect; - QRect lengthRect; - if( vopt.direction == Qt::RightToLeft ) - { - QPoint corner = vopt.rect.topRight(); - corner.rx() -= numberRectWidth; - numberRect = QRect( corner, QSize( numberRectWidth, vopt.rect.height() ) ); - - corner = numberRect.topLeft(); - corner.rx() -= textRectWidth; - textRect = QRect( corner, QSize( textRectWidth, vopt.rect.height() ) ); - - corner = textRect.topLeft(); - corner.rx() -= lengthRectWidth; - lengthRect = QRect( corner, QSize( lengthRectWidth, vopt.rect.height() ) ); - } - else - { - numberRect = QRect( vopt.rect.topLeft(), QSize( numberRectWidth, vopt.rect.height() ) ); - textRect = QRect( numberRect.topRight(), QSize( textRectWidth, vopt.rect.height() ) ); - lengthRect = QRect( textRect.topRight(), QSize( lengthRectWidth, vopt.rect.height() ) ); - } - - if( number != QLatin1String("0. ") ) - p->drawText( numberRect, Qt::AlignRight | Qt::AlignVCenter, number ); - p->drawText( textRect, Qt::AlignVCenter, middle ); - p->drawText( lengthRect, m_lengthAlignment | Qt::AlignVCenter, length ); - p->restore(); -} - -void -AlbumsItemDelegate::applyCommonStyle( QPainter *p, const QStyleOptionViewItemV4 &vopt ) const -{ - // styling code from QCommonStyle. These aren't actually used right - // now, but will be needed if something like inline tag editing is - // implemented. - QPalette::ColorGroup cg = vopt.state & QStyle::State_Enabled - ? QPalette::Normal : QPalette::Disabled; - if (cg == QPalette::Normal && !(vopt.state & QStyle::State_Active)) - cg = QPalette::Inactive; - - if (vopt.state & QStyle::State_Selected) { - p->setPen(vopt.palette.color(cg, QPalette::HighlightedText)); - } else { - p->setPen(vopt.palette.color(cg, QPalette::Text)); - } - if (vopt.state & QStyle::State_Editing) { - p->setPen(vopt.palette.color(cg, QPalette::Text)); - p->drawRect(vopt.rect.adjusted(0, 0, -1, -1)); - } -} - -Qt::Alignment -AlbumsItemDelegate::lengthAlignment() const -{ - return m_lengthAlignment; -} - -void -AlbumsItemDelegate::setLengthAlignment( Qt::Alignment a ) -{ - if( a != Qt::AlignLeft && a != Qt::AlignRight ) - a = Qt::AlignLeft; - m_lengthAlignment = a; -} - -#include diff --git a/src/context/applets/albums/AlbumsView.h b/src/context/applets/albums/AlbumsView.h deleted file mode 100644 index ed70f66906..0000000000 --- a/src/context/applets/albums/AlbumsView.h +++ /dev/null @@ -1,115 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 William Viana Soares * - * Copyright (c) 2008 Seb Ruiz * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_ALBUMSVIEW_H -#define AMAROK_ALBUMSVIEW_H - -#include "core/meta/forward_declarations.h" -#include "AlbumsModel.h" - -#include -#include - -class QAbstractItemModel; -class QGraphicsSceneContextMenuEvent; -class QGraphicsProxyWidget; -class QStandardItem; -class QTreeView; -namespace Plasma -{ - class SvgWidget; - class ScrollBar; -} - -class AlbumsView : public QGraphicsWidget -{ - Q_OBJECT - Q_PROPERTY( AlbumsProxyModel::Mode mode READ mode WRITE setMode ) - Q_PROPERTY( Qt::Alignment lengthAlignment READ lengthAlignment WRITE setLengthAlignment ) - Q_PROPERTY( QString filterPattern READ filterPattern WRITE setFilterPattern ) - -public: - explicit AlbumsView( QGraphicsWidget *parent = 0 ); - ~AlbumsView(); - - void appendAlbum( QStandardItem *album ); - void scrollTo( QStandardItem *album ); - - AlbumsProxyModel::Mode mode() const; - void setMode( AlbumsProxyModel::Mode mode ); - - Qt::Alignment lengthAlignment() const; - void setLengthAlignment( Qt::Alignment alignment ); - - QString filterPattern() const; - void setFilterPattern( const QString &pattern ); - - void clear(); - -public Q_SLOTS: - void setRecursiveExpanded( QStandardItem *item, bool expanded ); - void sort(); - -protected: - void contextMenuEvent( QGraphicsSceneContextMenuEvent *event ); - void resizeEvent( QGraphicsSceneResizeEvent *event ); - -private Q_SLOTS: - void itemClicked( const QModelIndex &index ); - void slotDoubleClicked(); - void slotAppendSelected(); - void slotEditSelected(); - void slotReplaceWithSelected(); - void slotQueueSelected(); - void slotScrollBarRangeChanged( int min, int max ); - -private: - void updateScrollBarVisibility(); - void setRecursiveExpanded( const QModelIndex &index, bool expanded ); - - Meta::TrackList getSelectedTracks() const; - AlbumsModel *m_model; - AlbumsProxyModel *m_proxyModel; - QTreeView *m_treeView; - QGraphicsProxyWidget *m_treeProxy; - Plasma::SvgWidget *m_topBorder; - Plasma::SvgWidget *m_bottomBorder; - Plasma::ScrollBar *m_scrollBar; -}; - -class AlbumsItemDelegate : public QStyledItemDelegate -{ - Q_OBJECT - Q_PROPERTY( Qt::Alignment lengthAlignment READ lengthAlignment WRITE setLengthAlignment ) - -public: - AlbumsItemDelegate( QObject *parent = 0 ); - ~AlbumsItemDelegate() {} - - Qt::Alignment lengthAlignment() const; - void setLengthAlignment( Qt::Alignment alignment ); - - void paint( QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index ) const; - -private: - void drawAlbumText( QPainter *p, const QStyleOptionViewItemV4 &option ) const; - void drawTrackText( QPainter *p, const QStyleOptionViewItemV4 &option ) const; - void applyCommonStyle( QPainter *p, const QStyleOptionViewItemV4 &option ) const; - Qt::Alignment m_lengthAlignment; -}; - -#endif // multiple inclusion guard diff --git a/src/context/applets/albums/CMakeLists.txt b/src/context/applets/albums/CMakeLists.txt index 882a6e05ee..e14a85e574 100644 --- a/src/context/applets/albums/CMakeLists.txt +++ b/src/context/applets/albums/CMakeLists.txt @@ -1,22 +1,22 @@ -project(context-albums) +set(albums_SRCS + plugin/AlbumsPlugin.cpp + plugin/AlbumsEngine.cpp + plugin/AlbumItem.cpp + plugin/TrackItem.cpp + plugin/AlbumsModel.cpp +) -set(albums_SRCS Albums.cpp AlbumsView.cpp AlbumItem.cpp TrackItem.cpp AlbumsModel.cpp) +add_library(amarok_context_applet_albums SHARED ${albums_SRCS}) -include_directories( - ../.. - ../../.. - ) -add_library(amarok_context_applet_albums MODULE ${albums_SRCS}) -if(APPLE) - set_target_properties(amarok_context_applet_albums PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") -endif() target_link_libraries(amarok_context_applet_albums amarokcore amaroklib - KF5::Plasma + Qt5::Qml + Qt5::Quick ) -install(TARGETS amarok_context_applet_albums DESTINATION ${PLUGIN_INSTALL_DIR}) -install(FILES amarok-context-applet-albums.desktop DESTINATION ${SERVICES_INSTALL_DIR}) -install(FILES amarok-albums.svg DESTINATION ${DATA_INSTALL_DIR}/desktoptheme/default/widgets/ ) +install(TARGETS amarok_context_applet_albums DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/albums) +install(FILES plugin/qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/albums) + +kpackage_install_package(package org.kde.amarok.albums amarok) diff --git a/src/context/applets/albums/amarok-albums.svg b/src/context/applets/albums/package/contents/images/amarok-albums.svg similarity index 100% rename from src/context/applets/albums/amarok-albums.svg rename to src/context/applets/albums/package/contents/images/amarok-albums.svg diff --git a/src/context/applets/albums/package/contents/ui/main.qml b/src/context/applets/albums/package/contents/ui/main.qml new file mode 100644 index 0000000000..2ac5477a7d --- /dev/null +++ b/src/context/applets/albums/package/contents/ui/main.qml @@ -0,0 +1,92 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 1.4 +import QtQml.Models 2.2 +import org.kde.amarok.qml 1.0 as AmarokQml +import org.kde.amarok.albums 1.0 + +AmarokQml.Applet { + id: applet + + TreeView { + id: treeView + + anchors.fill: parent + sortIndicatorVisible: false + headerVisible: false + model: AlbumsEngine.model + selectionMode: SelectionMode.ExtendedSelection + selection: ItemSelectionModel { + id: selectionModel + + model: AlbumsEngine.model + } + + itemDelegate: Row { + Loader { + active: !!model && !!model["albumCover"] + height: parent.height + width: height + visible: active + + AmarokQml.PixmapItem { + source: !!model ? model["albumCover"] : undefined + } + } + Text { + anchors.verticalCenter: parent.verticalCenter + color: styleData.selected ? applet.palette.highlightedText : applet.palette.text + elide: styleData.elideMode + text: styleData.value + } + } + + rowDelegate: Rectangle { + property color backgroundColor: styleData.alternate ? applet.palette.alternateBase : applet.palette.base + + height: !!model && !!model["size"] ? model["size"].height : 0 + width: parent.width + color: styleData.selected ? applet.palette.highlight : backgroundColor + + MouseArea { + id: rowMouseArea + + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.NoButton + } + } + + TableViewColumn { + title: i18n("Display") + role: "display" + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton + onClicked: { + var index = treeView.indexAt(mouse.x, mouse.y); + if (!selectionModel.isSelected(index)) { + selectionModel.select(index, ItemSelectionModel.ClearAndSelect); + } + AlbumsEngine.showContextMenu(selectionModel.selectedIndexes, index); + } + } + } +} diff --git a/src/context/applets/albums/amarok-context-applet-albums.desktop b/src/context/applets/albums/package/metadata.desktop similarity index 91% rename from src/context/applets/albums/amarok-context-applet-albums.desktop rename to src/context/applets/albums/package/metadata.desktop index eb455abb68..4214daed24 100644 --- a/src/context/applets/albums/amarok-context-applet-albums.desktop +++ b/src/context/applets/albums/package/metadata.desktop @@ -1,79 +1,79 @@ [Desktop Entry] Name=Albums Name[be]=Альбомы Name[bg]=Албуми Name[bs]=Albumi Name[ca]=Àlbums Name[ca@valencia]=Àlbums Name[cs]=Alba Name[csb]=Albùmë Name[da]=Albummer Name[de]=Alben Name[el]=Άλμπουμ Name[en_GB]=Albums Name[eo]=Albumoj Name[es]=Álbumes Name[et]=Albumid Name[eu]=Albumak Name[fa]=آلبومها Name[fi]=Albumit Name[fr]=Albums Name[ga]=Albaim Name[gl]=Albuns Name[he]=אלבומים Name[hne]=एल्बम Name[hu]=Albumok Name[id]=Album Name[is]=Plötur Name[it]=Album Name[ja]=アルバム Name[km]=អាល់ប៊ុម Name[ko]=앨범 Name[ku]=Albûm Name[lt]=Albumai Name[lv]=Albūmi Name[mr]=अल्बम्स Name[ms]=Album Name[nb]=Album Name[nds]=Alben Name[nl]=Albums Name[nn]=Album Name[oc]=Albums Name[pa]=ਐਲਬਮ Name[pl]=Albumy Name[pt]=Álbuns Name[pt_BR]=Álbuns Name[ro]=Albume Name[ru]=Альбомы Name[sk]=Albumy Name[sl]=Albumi Name[sq]=Albumet Name[sr]=Албуми Name[sr@ijekavian]=Албуми Name[sr@ijekavianlatin]=Albumi Name[sr@latin]=Albumi Name[sv]=Album Name[th]=อัลบั้มต่าง ๆ Name[tr]=Albümler Name[ug]=ئالبوملار Name[uk]=Альбоми Name[wa]=Alboms Name[x-test]=xxAlbumsxx Name[zh_CN]=专辑 Name[zh_TW]=專輯 + Type=Service Icon=media-album-cover-manager-amarok -ServiceTypes=Plasma/Applet +ServiceTypes=Amarok/ContextApplet -X-KDE-Library=amarok_context_applet_albums X-KDE-PluginInfo-Author=Seb Ruiz X-KDE-PluginInfo-Email=ruiz@kde.org -X-KDE-PluginInfo-Name=albums +X-KDE-PluginInfo-Name=org.kde.amarok.albums X-KDE-PluginInfo-Version=pre0.1 X-KDE-PluginInfo-Website= X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL -X-KDE-PluginInfo-EnabledByDefault=true +X-KDE-PluginInfo-EnabledByDefault=false X-KDE-ParentApp=amarok X-KDE-PluginInfo-Category=Current diff --git a/src/context/applets/albums/AlbumItem.cpp b/src/context/applets/albums/plugin/AlbumItem.cpp similarity index 93% rename from src/context/applets/albums/AlbumItem.cpp rename to src/context/applets/albums/plugin/AlbumItem.cpp index 65c21a5c2d..b9162c941c 100644 --- a/src/context/applets/albums/AlbumItem.cpp +++ b/src/context/applets/albums/plugin/AlbumItem.cpp @@ -1,140 +1,144 @@ /**************************************************************************************** * Copyright (c) 2008 Seb Ruiz * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "AlbumItem.h" #include "SvgHandler.h" -#include "context/applets/albums/AlbumsDefs.h" +#include "AlbumsDefs.h" #include "core/meta/Meta.h" #include "core/meta/support/MetaUtility.h" -#include -#include //TODO KF5: Take care of this when moving to QCollator from KStringHandler::naturalCompare() +#include +#include #include #include AlbumItem::AlbumItem() : QStandardItem() , m_iconSize( 40 ) , m_showArtist( false ) { setEditable( false ); } AlbumItem::~AlbumItem() { if( m_album ) unsubscribeFrom( m_album ); } void AlbumItem::setAlbum( Meta::AlbumPtr albumPtr ) { if( m_album ) unsubscribeFrom( m_album ); m_album = albumPtr; subscribeTo( m_album ); update(); } void AlbumItem::setIconSize( const int iconSize ) { static const int padding = 5; m_iconSize = iconSize; QSize size = sizeHint(); size.setHeight( iconSize + padding*2 ); setSizeHint( size ); } void AlbumItem::setShowArtist( const bool showArtist ) { if( showArtist != m_showArtist ) { m_showArtist = showArtist; metadataChanged( m_album ); } } void AlbumItem::metadataChanged( Meta::AlbumPtr album ) { Q_UNUSED( album ); QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); } void AlbumItem::update() { if( !m_album ) return; Meta::TrackList tracks = m_album->tracks(); if( !tracks.isEmpty() ) { Meta::TrackPtr first = tracks.first(); Meta::YearPtr year = first->year(); if( year ) setData( year->year(), AlbumYearRole ); } QString albumName = m_album->name(); albumName = albumName.isEmpty() ? i18n("Unknown") : albumName; QString name = ( m_showArtist && m_album->hasAlbumArtist() ) ? QString( "%1 - %2" ).arg( m_album->albumArtist()->name(), albumName ) : albumName; setData( name, NameRole ); qint64 totalTime = 0; foreach( Meta::TrackPtr item, tracks ) totalTime += item->length(); QString trackCount = i18np( "%1 track", "%1 tracks", tracks.size() ); QString lengthText = QString( "%1, %2" ).arg( trackCount, Meta::msToPrettyTime( totalTime ) ); setData( lengthText, AlbumLengthRole ); QPixmap cover = The::svgHandler()->imageWithBorder( m_album, m_iconSize, 3 ); setIcon( QIcon( cover ) ); + setData( cover, AlbumCoverRole ); } int AlbumItem::type() const { return AlbumType; } bool AlbumItem::operator<( const QStandardItem &other ) const { // compare the opposite for descending year order int yearA = data( AlbumYearRole ).toInt(); int yearB = other.data( AlbumYearRole ).toInt(); if( yearA > yearB ) return true; else if( yearA == yearB ) { const QString nameA = data( NameRole ).toString(); const QString nameB = other.data( NameRole ).toString(); - return KStringHandler::naturalCompare( nameA, nameB, Qt::CaseInsensitive ) < 0; + QCollator collator; + collator.setNumericMode( true ); + collator.setCaseSensitivity( Qt::CaseInsensitive ); + return collator.compare( nameA, nameB ) < 0; } else return false; } diff --git a/src/context/applets/albums/AlbumItem.h b/src/context/applets/albums/plugin/AlbumItem.h similarity index 95% rename from src/context/applets/albums/AlbumItem.h rename to src/context/applets/albums/plugin/AlbumItem.h index 3d2e3e7821..84c3267667 100644 --- a/src/context/applets/albums/AlbumItem.h +++ b/src/context/applets/albums/plugin/AlbumItem.h @@ -1,82 +1,82 @@ /**************************************************************************************** * Copyright (c) 2008 Seb Ruiz * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef AMAROK_ALBUMITEM_H #define AMAROK_ALBUMITEM_H #include "core/meta/Observer.h" #include #include #include class AlbumItem : public QObject, public QStandardItem, public Meta::Observer { Q_OBJECT public: AlbumItem(); ~AlbumItem(); /** * Sets the AlbumPtr for this item to associate with * * @arg album pointer to associate with */ void setAlbum( Meta::AlbumPtr albumPtr ); /** * @return the album pointer associated with this item */ Meta::AlbumPtr album() const { return m_album; } /** * Sets the size of the album art to display */ void setIconSize( const int iconSize ); /** * @return the size of the album art */ int iconSize() const { return m_iconSize; } /** * Setter to determine whether the item should show the Artist as well as the * album name. Used for 'recent albums' listing. */ void setShowArtist( const bool showArtist ); // overloaded from Meta::Observer using Observer::metadataChanged; - virtual void metadataChanged( Meta::AlbumPtr album ); + virtual void metadataChanged( Meta::AlbumPtr album ) Q_DECL_OVERRIDE; - virtual int type() const; + virtual int type() const Q_DECL_OVERRIDE; - virtual bool operator<( const QStandardItem &other ) const; + virtual bool operator<( const QStandardItem &other ) const Q_DECL_OVERRIDE; private Q_SLOTS: /** Updates the item after metadataChanged was called. We need this indirection to get executed by the UI thread. */ void update(); private: Meta::AlbumPtr m_album; int m_iconSize; bool m_showArtist; }; #endif // multiple inclusion guard diff --git a/src/context/applets/albums/AlbumsDefs.h b/src/context/applets/albums/plugin/AlbumsDefs.h similarity index 98% rename from src/context/applets/albums/AlbumsDefs.h rename to src/context/applets/albums/plugin/AlbumsDefs.h index 119cafd628..793f0cb836 100644 --- a/src/context/applets/albums/AlbumsDefs.h +++ b/src/context/applets/albums/plugin/AlbumsDefs.h @@ -1,40 +1,41 @@ /**************************************************************************************** * Copyright (c) 2010 Rick W. Chen * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef AMAROK_ALBUMSDEFS_H #define AMAROK_ALBUMSDEFS_H #include enum AlbumsModelItemTypes { AlbumType = QStandardItem::UserType, TrackType }; enum AlbumsModelCustomRoles { NameRole = Qt::UserRole + 1, AlbumCompilationRole, AlbumMaxTrackNumberRole, AlbumLengthRole, AlbumYearRole, + AlbumCoverRole, TrackArtistRole, TrackNumberRole, TrackLengthRole }; #endif /* AMAROK_ALBUMSDEFS_H */ diff --git a/src/context/applets/albums/plugin/AlbumsEngine.cpp b/src/context/applets/albums/plugin/AlbumsEngine.cpp new file mode 100644 index 0000000000..48779614d7 --- /dev/null +++ b/src/context/applets/albums/plugin/AlbumsEngine.cpp @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2017 Malte Veerman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "AlbumsEngine.h" +#include "AlbumsDefs.h" +#include "AlbumItem.h" +#include "TrackItem.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +AlbumsEngine::AlbumsEngine( QObject *parent ) + : QObject( parent ) + , m_lastQueryMaker( Q_NULLPTR ) + , m_model( new AlbumsModel( this ) ) + , m_proxyModel( new AlbumsProxyModel( this ) ) +{ + EngineController* engine = The::engineController(); + + connect( engine, &EngineController::trackPlaying, + this, &AlbumsEngine::slotTrackChanged ); + connect( engine, &EngineController::stopped, + this, &AlbumsEngine::stopped ); + connect( engine, &EngineController::trackMetadataChanged, + this, &AlbumsEngine::slotTrackMetadataChanged ); + + m_model->setColumnCount( 1 ); + m_proxyModel->setFilterCaseSensitivity( Qt::CaseInsensitive ); + m_proxyModel->setSortLocaleAware( true ); + m_proxyModel->setDynamicSortFilter( true ); + m_proxyModel->setSourceModel( m_model ); + m_proxyModel->setFilterRole( NameRole ); +} + +void AlbumsEngine::slotTrackMetadataChanged( Meta::TrackPtr track ) +{ + if( !track || !track->album() || !track->album()->albumArtist() ) + return; + + if( track->album()->albumArtist() == m_artist ) + return; + + m_artist = track->album()->albumArtist(); + update(); +} + +void AlbumsEngine::slotTrackChanged( Meta::TrackPtr track ) +{ + if( !track || track == m_currentTrack ) + return; + + m_currentTrack = track; + slotTrackMetadataChanged( track ); +} + + +void AlbumsEngine::stopped() +{ + m_currentTrack.clear(); + m_artist.clear(); + + // Collect data for the recently added albums + Collections::QueryMaker *qm = CollectionManager::instance()->queryMaker(); + qm->setAutoDelete( true ); + qm->setQueryType( Collections::QueryMaker::Album ); + qm->excludeFilter( Meta::valAlbum, QString(), true, true ); + qm->orderBy( Meta::valCreateDate, true ); + qm->limitMaxResultSize( Amarok::config("Albums Applet").readEntry("RecentlyAdded", 5) ); + + connect( qm, &Collections::QueryMaker::newAlbumsReady, + this, &AlbumsEngine::resultReady, Qt::QueuedConnection ); + + m_lastQueryMaker = qm; + qm->run(); +} + +void AlbumsEngine::update() +{ + DEBUG_BLOCK + + // -- search the collection for albums with the same artist + Collections::QueryMaker *qm = CollectionManager::instance()->queryMaker(); + qm->setAutoDelete( true ); + qm->addFilter( Meta::valArtist, m_artist->name(), true, true ); + qm->setAlbumQueryMode( Collections::QueryMaker::AllAlbums ); + qm->setQueryType( Collections::QueryMaker::Album ); + + connect( qm, &Collections::QueryMaker::newAlbumsReady, + this, &AlbumsEngine::resultReady, Qt::QueuedConnection ); + + m_lastQueryMaker = qm; + qm->run(); +} + +void AlbumsEngine::resultReady( const Meta::AlbumList &albums ) +{ + if( sender() != m_lastQueryMaker ) + return; + + m_model->clear(); + m_proxyModel->setMode( m_currentTrack ? AlbumsProxyModel::SortByYear : AlbumsProxyModel::SortByCreateDate ); + + for( auto album : albums ) + { + // do not show all tracks without an album from the collection, this takes ages + // TODO: show all tracks from this artist that are not part of an album + if( album->name().isEmpty() ) + continue; + + Meta::TrackList tracks = album->tracks(); + if( tracks.isEmpty() ) + continue; + + AlbumItem *albumItem = new AlbumItem(); + albumItem->setIconSize( 50 ); + albumItem->setAlbum( album ); + albumItem->setShowArtist( !m_currentTrack ); + + int numberOfDiscs = 0; + int childRow = 0; + + std::stable_sort( tracks.begin(), tracks.end(), Meta::Track::lessThan ); + + QMultiHash< int, TrackItem* > trackItems; // hash of tracks items for each disc + for( const auto &track : tracks ) + { + if( numberOfDiscs < track->discNumber() ) + numberOfDiscs = track->discNumber(); + + TrackItem *trackItem = new TrackItem(); + trackItem->setTrack( track ); + + // bold the current track to make it more visible + if( m_currentTrack && m_currentTrack == track ) + { + trackItem->bold(); + } + + // If compilation and same artist, then highlight, but only if there's a current track + if( m_currentTrack + && m_currentTrack->artist() && track->artist() + && album->isCompilation() ) + { + trackItem->italicise(); + } + trackItems.insert( track->discNumber(), trackItem ); + } + + for( int i = 0; i <= numberOfDiscs; ++i ) + { + QList items = trackItems.values( i ); + if( !items.isEmpty() ) + { + const TrackItem *item = items.first(); + QStandardItem *discItem( 0 ); + if( numberOfDiscs > 1 ) + { + discItem = new QStandardItem( i18n("Disc %1", item->track()->discNumber()) ); + albumItem->setChild( childRow++, discItem ); + int discChildRow = 0; + foreach( TrackItem *trackItem, items ) + discItem->setChild( discChildRow++, trackItem ); + } + else + { + foreach( TrackItem *trackItem, items ) + albumItem->setChild( childRow++, trackItem ); + } + } + } + m_model->appendRow( albumItem ); + } + + m_proxyModel->sort( 0 ); +} + +QString AlbumsEngine::filterPattern() const +{ + return m_proxyModel->filterRegExp().pattern(); +} + +void AlbumsEngine::setFilterPattern( const QString &pattern ) +{ + if( m_proxyModel->filterRegExp().pattern() == pattern ) + return; + + m_proxyModel->setFilterRegExp( QRegExp(pattern, Qt::CaseInsensitive) ); + emit filterPatternChanged(); +} + +void AlbumsEngine::clear() +{ + qDeleteAll( m_model->findItems( QLatin1String( "*" ), Qt::MatchWildcard ) ); + m_model->clear(); +} + +void AlbumsEngine::appendSelected( const QModelIndexList& indexes ) const +{ + Meta::TrackList selected = getSelectedTracks( indexes ); + The::playlistController()->insertOptioned( selected, Playlist::OnAppendToPlaylistAction ); +} + +void AlbumsEngine::replaceWithSelected( const QModelIndexList& indexes ) const +{ + Meta::TrackList selected = getSelectedTracks( indexes ); + The::playlistController()->insertOptioned( selected, Playlist::OnReplacePlaylistAction ); +} + +void AlbumsEngine::queueSelected( const QModelIndexList& indexes ) const +{ + Meta::TrackList selected = getSelectedTracks( indexes ); + The::playlistController()->insertOptioned( selected, Playlist::OnQueueToPlaylistAction ); +} + +void AlbumsEngine::editSelected( const QModelIndexList& indexes ) const +{ + Meta::TrackList selected = getSelectedTracks( indexes ); + if( !selected.isEmpty() ) + { + TagDialog *dialog = new TagDialog( selected ); + dialog->show(); + } +} + +void AlbumsEngine::showContextMenu( const QModelIndexList &indexes, const QModelIndex &mouseOverIndex ) const +{ + if( indexes.isEmpty() || !mouseOverIndex.isValid() ) + return; + + QMenu menu; + QAction *appendAction = new QAction( QIcon::fromTheme( "media-track-add-amarok" ), i18n( "&Add to Playlist" ), &menu ); + QAction *loadAction = new QAction( QIcon::fromTheme( "folder-open" ), i18nc( "Replace the currently loaded tracks with these", "&Replace Playlist" ), &menu ); + QAction *queueAction = new QAction( QIcon::fromTheme( "media-track-queue-amarok" ), i18n( "&Queue" ), &menu ); + QAction *editAction = new QAction( QIcon::fromTheme( "media-track-edit-amarok" ), i18n( "Edit Track Details" ), &menu ); + + menu.addAction( appendAction ); + menu.addAction( loadAction ); + menu.addAction( queueAction ); + menu.addAction( editAction ); + + connect( appendAction, &QAction::triggered, this, [this, indexes] () { appendSelected( indexes ); } ); + connect( loadAction, &QAction::triggered, this, [this, indexes] () { replaceWithSelected( indexes ); } ); + connect( queueAction, &QAction::triggered, this, [this, indexes] () { queueSelected( indexes ); } ); + connect( editAction, &QAction::triggered, this, [this, indexes] () { editSelected( indexes ); } ); + + QMenu menuCover( i18n( "Album" ), &menu ); + const QStandardItem *item = m_model->itemFromIndex( m_proxyModel->mapToSource( mouseOverIndex ) ); + if( item->type() == AlbumType ) + { + Meta::AlbumPtr album = static_cast( item )->album(); + QScopedPointer ac( album->create() ); + if( ac ) + { + QList actions = ac->actions(); + if( !actions.isEmpty() ) + { + // ensure that the actions are cleaned up afterwards + foreach( QAction *action, actions ) + { + if( !action->parent() ) + action->setParent( &menuCover ); + } + + menuCover.addActions( actions ); + menuCover.setIcon( QIcon::fromTheme( "filename-album-amarok" ) ); + menu.addMenu( &menuCover ); + } + } + } + menu.exec( QCursor::pos() ); +} + +Meta::TrackList AlbumsEngine::getSelectedTracks( const QModelIndexList& indexes ) const +{ + Meta::TrackList selected; + + for( const QModelIndex &index : indexes ) + { + if( index.isValid() ) + { + const QModelIndex &srcIndex = m_proxyModel->mapToSource( index ); + const QStandardItem *item = m_model->itemFromIndex( srcIndex ); + if( item->type() == AlbumType ) + { + selected << static_cast( item )->album()->tracks(); + } + else if( item->type() == TrackType ) + { + selected << static_cast( item )->track(); + } + else if( m_model->hasChildren( srcIndex ) ) // disc type + { + for( int i = m_model->rowCount( srcIndex ) - 1; i >= 0; --i ) + { + const QStandardItem *trackItem = m_model->itemFromIndex( srcIndex.child(i, 0) ); + selected << static_cast( trackItem )->track(); + } + } + } + } + + return selected; +} diff --git a/src/context/applets/albums/plugin/AlbumsEngine.h b/src/context/applets/albums/plugin/AlbumsEngine.h new file mode 100644 index 0000000000..7df3ea3dad --- /dev/null +++ b/src/context/applets/albums/plugin/AlbumsEngine.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2017 Malte Veerman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef ALBUMSENGINE_H +#define ALBUMSENGINE_H + +#include "AlbumsModel.h" +#include "core/meta/Meta.h" + +#include + +class QMenu; +namespace Collections +{ + class QueryMaker; +} + +class AlbumsEngine : public QObject +{ + Q_OBJECT + Q_PROPERTY( AlbumsProxyModel* model READ model CONSTANT ) + Q_PROPERTY( QString filterPattern READ filterPattern WRITE setFilterPattern NOTIFY filterPatternChanged ) + +public: + AlbumsEngine( QObject *parent = Q_NULLPTR ); + + AlbumsProxyModel * model() const { return m_proxyModel; } + QString filterPattern() const; + void setFilterPattern( const QString &pattern ); + + Q_INVOKABLE void showContextMenu( const QModelIndexList &indexes, const QModelIndex &mouseOverIndex ) const; + +signals: + void lengthAlignmentChanged(); + void filterPatternChanged(); + +private Q_SLOTS: + void slotTrackChanged( Meta::TrackPtr track ); + void slotTrackMetadataChanged( Meta::TrackPtr track ); + void stopped(); + void resultReady( const Meta::AlbumList &albums ); + +private: + void update(); + void clear(); + void appendSelected( const QModelIndexList &indexes ) const; + void replaceWithSelected( const QModelIndexList &indexes ) const; + void queueSelected( const QModelIndexList &indexes ) const; + void editSelected( const QModelIndexList &indexes ) const; + Meta::TrackList getSelectedTracks( const QModelIndexList &indexes ) const; + + Collections::QueryMaker *m_lastQueryMaker; + Meta::TrackPtr m_currentTrack; + Meta::ArtistPtr m_artist; + + AlbumsModel *m_model; + AlbumsProxyModel *m_proxyModel; +}; + +#endif // ALBUMSENGINE_H diff --git a/src/context/applets/albums/AlbumsModel.cpp b/src/context/applets/albums/plugin/AlbumsModel.cpp similarity index 71% rename from src/context/applets/albums/AlbumsModel.cpp rename to src/context/applets/albums/plugin/AlbumsModel.cpp index 433bc2bca5..d48773fcca 100644 --- a/src/context/applets/albums/AlbumsModel.cpp +++ b/src/context/applets/albums/plugin/AlbumsModel.cpp @@ -1,186 +1,245 @@ /**************************************************************************************** * Copyright (c) 2008 Andreas Muetzel * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "AlbumsModel" #include "AlbumsModel.h" #include "AlbumsDefs.h" #include "AlbumItem.h" #include "AmarokMimeData.h" #include "core/support/Debug.h" +#include #include "TrackItem.h" -#include -#include //TODO KF5: Take care of this when moving to QCollator from KStringHandler::naturalCompare() - +#include #include +#include AlbumsModel::AlbumsModel( QObject *parent ) : QStandardItemModel( parent ) , m_rowHeight( 0 ) { - connect( KGlobalSettings::self(), SIGNAL(appearanceChanged()), SLOT(updateRowHeight()) ); + connect( qApp, &QGuiApplication::fontDatabaseChanged, this, &AlbumsModel::updateRowHeight ); updateRowHeight(); } int AlbumsModel::rowHeight() const { return m_rowHeight; } void AlbumsModel::updateRowHeight() { QFont font; m_rowHeight = QFontMetrics( font ).height(); } QVariant AlbumsModel::data( const QModelIndex &index, int role ) const { if( !index.isValid() ) return QVariant(); + if( role == Qt::DisplayRole ) + { + const QStandardItem *item = itemFromIndex( index ); + + if( const auto album = dynamic_cast( item ) ) + { + QString name = album->data( NameRole ).toString(); + int year = album->data( AlbumYearRole ).toInt(); + + QStringList texts; + texts << ((year > 0) ? QString( "%1 (%2)" ).arg( name, QString::number(year) ) : name); + texts << album->data( AlbumLengthRole ).toString(); + + return texts.join('\n'); + } + + if( const auto track = dynamic_cast( item ) ) + { + bool isCompilation = track->data( AlbumCompilationRole ).toBool(); + const QString &name = track->data( NameRole ).toString(); + const QString &artist = track->data( TrackArtistRole ).toString(); + QString length = " (" + Meta::msToPrettyTime( track->data( TrackLengthRole ).toInt() ) + ')'; + QString number = track->data( TrackNumberRole ).toString() + ". "; + QString middle = isCompilation ? QString( "%1 - %2" ).arg( artist, name ) : name; + + return QStringList( { number, middle, length } ).join( ' ' ); + } + } + if( role == Qt::SizeHintRole ) { const QStandardItem *item = itemFromIndex( index ); int h = 4; h += (item->type() != AlbumType) ? m_rowHeight : static_cast( item )->iconSize(); return QSize( -1, h ); } return itemFromIndex( index )->data( role ); } QMimeData* AlbumsModel::mimeData( const QModelIndexList &indices ) const { DEBUG_BLOCK if( indices.isEmpty() ) return 0; Meta::TrackList tracks; foreach( const QModelIndex &index, indices ) tracks << tracksForIndex( index ); tracks = tracks.toSet().toList(); // http://doc.trolltech.com/4.4/qabstractitemmodel.html#mimeData // If the list of indexes is empty, or there are no supported MIME types, // 0 is returned rather than a serialized empty list. if( tracks.isEmpty() ) return 0; AmarokMimeData *mimeData = new AmarokMimeData(); mimeData->setTracks( tracks ); return mimeData; } Meta::TrackList AlbumsModel::tracksForIndex( const QModelIndex &index ) const { Meta::TrackList tracks; if( !index.isValid() ) return tracks; if( hasChildren( index ) ) { for( int i = 0, rows = rowCount( index ); i < rows; ++i ) tracks << tracksForIndex( index.child( i, 0 ) ); } else if( QStandardItem *item = itemFromIndex( index ) ) { if( item->type() == TrackType ) { TrackItem* trackItem = static_cast( item ); if( trackItem ) tracks << trackItem->track(); } } return tracks; } QStringList AlbumsModel::mimeTypes() const { QStringList types; types << AmarokMimeData::TRACK_MIME; return types; } AlbumsProxyModel::AlbumsProxyModel( QObject *parent ) : QSortFilterProxyModel( parent ) , m_mode( SortByCreateDate ) -{} + , m_collator( new QCollator ) +{ + m_collator->setNumericMode( true ); +} + +AlbumsProxyModel::~AlbumsProxyModel() +{ + delete m_collator; +} bool AlbumsProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const { const QStandardItemModel *model = static_cast( sourceModel() ); const QStandardItem *leftItem = model->itemFromIndex( left ); int type = leftItem->type(); if( type == AlbumType && m_mode == SortByCreateDate ) { const AlbumItem *leftAlbum = static_cast( leftItem ); const AlbumItem *rightAlbum = static_cast( model->itemFromIndex( right ) ); QDateTime leftMaxCreateDate, rightMaxCreateDate; foreach( Meta::TrackPtr track, leftAlbum->album()->tracks() ) if( track->createDate() > leftMaxCreateDate ) leftMaxCreateDate = track->createDate(); foreach( Meta::TrackPtr track, rightAlbum->album()->tracks() ) if( track->createDate() > rightMaxCreateDate ) rightMaxCreateDate = track->createDate(); return leftMaxCreateDate > rightMaxCreateDate; } else if( type == AlbumType || type == TrackType ) return leftItem->operator<( *model->itemFromIndex( right ) ); else - return KStringHandler::naturalCompare( leftItem->text(), model->itemFromIndex(right)->text() ) < 0; + { + return m_collator->compare( leftItem->text(), model->itemFromIndex(right)->text() ) < 0; + } } bool AlbumsProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const { const QStandardItemModel *model = static_cast( sourceModel() ); const QModelIndex &srcIndex = sourceModel()->index( sourceRow, 0, sourceParent ); const QStandardItem *item = model->itemFromIndex( srcIndex ); if( item->data( NameRole ).toString().contains( filterRegExp() ) ) return true; if( item->type() == AlbumType ) { for( int i = 0, count = model->rowCount( srcIndex ); i < count; ++i ) { const QModelIndex &kid = srcIndex.child( i, 0 ); if( kid.data( NameRole ).toString().contains( filterRegExp() ) ) return true; } } return false; } AlbumsProxyModel::Mode AlbumsProxyModel::mode() const { return m_mode; } void AlbumsProxyModel::setMode( Mode mode ) { m_mode = mode; } +QHash +AlbumsProxyModel::roleNames() const +{ + QHash roleNames; + + roleNames.insert( Qt::DisplayRole, "display" ); + roleNames.insert( Qt::SizeHintRole, "size" ); + roleNames.insert( NameRole, "name" ); + roleNames.insert( AlbumCompilationRole, "albumIsCompilation" ); + roleNames.insert( AlbumMaxTrackNumberRole, "albumMaxTrackNumber" ); + roleNames.insert( AlbumLengthRole, "albumLength" ); + roleNames.insert( AlbumYearRole, "albumYear" ); + roleNames.insert( AlbumCoverRole, "albumCover" ); + roleNames.insert( TrackArtistRole, "trackArtist" ); + roleNames.insert( TrackNumberRole, "trackNumber" ); + roleNames.insert( TrackLengthRole, "trackLength" ); + + return roleNames; +} + diff --git a/src/context/applets/albums/AlbumsModel.h b/src/context/applets/albums/plugin/AlbumsModel.h similarity index 89% rename from src/context/applets/albums/AlbumsModel.h rename to src/context/applets/albums/plugin/AlbumsModel.h index a61caf4363..e15fdc8410 100644 --- a/src/context/applets/albums/AlbumsModel.h +++ b/src/context/applets/albums/plugin/AlbumsModel.h @@ -1,82 +1,91 @@ /**************************************************************************************** * Copyright (c) 2008 Andreas Muetzel * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef AMAROK_ALBUMSMODEL_H #define AMAROK_ALBUMSMODEL_H #include "core/meta/forward_declarations.h" #include #include /** * This Model is used to get the right mime type/data for entries in the albums treeview */ class AlbumsModel : public QStandardItemModel { Q_OBJECT public: AlbumsModel( QObject *parent = 0 ); virtual ~AlbumsModel() {} virtual QVariant data( const QModelIndex &index, int role ) const; virtual QMimeData* mimeData( const QModelIndexList &indices ) const; virtual QStringList mimeTypes() const; int rowHeight() const; private Q_SLOTS: void updateRowHeight(); private: Meta::TrackList tracksForIndex( const QModelIndex &index ) const; int m_rowHeight; }; +class QCollator; + class AlbumsProxyModel : public QSortFilterProxyModel { Q_OBJECT - Q_PROPERTY( Mode mode READ mode WRITE setMode ) - Q_ENUMS( Mode ) + Q_PROPERTY( Mode mode READ mode WRITE setMode NOTIFY modeChanged ) public: AlbumsProxyModel( QObject *parent ); - ~AlbumsProxyModel() {} + ~AlbumsProxyModel(); enum Mode { SortByCreateDate, SortByYear }; + Q_ENUM( Mode ) Mode mode() const; void setMode( Mode mode ); + QHash roleNames() const Q_DECL_OVERRIDE; + +signals: + void modeChanged(); + protected: /** * Determine if album @param left is less than album @param right. * * If @param left and @param right both reference albums and @c m_mode * is set to @c SortByCreateDate, @c lessThan will return @c true if * and only the album referenced by @param left has a track that was * added more recently than all of the tracks in the album * referenced by @param right. */ bool lessThan( const QModelIndex &left, const QModelIndex &right ) const; - bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const; + bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const Q_DECL_OVERRIDE; private: Mode m_mode; + QCollator *m_collator; }; +Q_DECLARE_METATYPE( AlbumsProxyModel* ) #endif diff --git a/src/context/applets/albums/plugin/AlbumsPlugin.cpp b/src/context/applets/albums/plugin/AlbumsPlugin.cpp new file mode 100644 index 0000000000..1e9611188e --- /dev/null +++ b/src/context/applets/albums/plugin/AlbumsPlugin.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2017 Malte Veerman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "AlbumsEngine.h" + +#include + +#include + + +class AlbumsPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char* uri) Q_DECL_OVERRIDE + { + Q_ASSERT(uri == QLatin1String("org.kde.amarok.albums")); + +// qmlRegisterUncreatableType(); + qmlRegisterSingletonType(uri, 1, 0, "AlbumsEngine", albums_engine_provider); + } + + static QObject *albums_engine_provider(QQmlEngine *engine, QJSEngine *scriptEngine) + { + Q_UNUSED(engine) + Q_UNUSED(scriptEngine) + + return new AlbumsEngine(); + } +}; + +#include diff --git a/src/context/applets/albums/TrackItem.cpp b/src/context/applets/albums/plugin/TrackItem.cpp similarity index 92% rename from src/context/applets/albums/TrackItem.cpp rename to src/context/applets/albums/plugin/TrackItem.cpp index 37865b9f7d..a07cf69a8c 100644 --- a/src/context/applets/albums/TrackItem.cpp +++ b/src/context/applets/albums/plugin/TrackItem.cpp @@ -1,120 +1,122 @@ /**************************************************************************************** * Copyright (c) 2008 Seb Ruiz * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "TrackItem.h" -#include "context/applets/albums/AlbumsDefs.h" +#include "AlbumsDefs.h" #include "core/meta/Meta.h" #include "core/meta/support/MetaUtility.h" -#include //TODO KF5: Take care of this when moving to QCollator from KStringHandler::naturalCompare() - +#include #include #include TrackItem::TrackItem() : QStandardItem() { setEditable( false ); } TrackItem::~TrackItem() { QMutexLocker locker( &m_mutex ); if( m_track ) unsubscribeFrom( m_track ); } void TrackItem::setTrack( Meta::TrackPtr trackPtr ) { if( m_track ) unsubscribeFrom( m_track ); m_track = trackPtr; subscribeTo( m_track ); metadataChanged( m_track ); } void TrackItem::metadataChanged( Meta::TrackPtr track ) { QMutexLocker locker( &m_mutex ); if( !track ) return; Meta::ArtistPtr artist = track->artist(); Meta::AlbumPtr album = track->album(); setData( track->prettyName(), NameRole ); setData( track->trackNumber(), TrackNumberRole ); setData( track->length(), TrackLengthRole ); if( artist ) setData( artist->prettyName(), TrackArtistRole ); if( album ) { setData( album->isCompilation(), AlbumCompilationRole ); int num = 0; foreach( const Meta::TrackPtr &track, album->tracks() ) { if( num < track->trackNumber() ) num = track->trackNumber(); } setData( num, AlbumMaxTrackNumberRole ); } setToolTip( QString( "%1 (%2)" ).arg( track->name(), Meta::msToPrettyTime(track->length()) ) ); } void TrackItem::italicise() { QFont f = font(); f.setItalic( true ); setFont( f ); } void TrackItem::bold() { QFont f = font(); f.setBold( true ); setFont( f ); } int TrackItem::type() const { return TrackType; } bool TrackItem::operator<( const QStandardItem &other ) const { int trackA = data( TrackNumberRole ).toInt(); int trackB = other.data( TrackNumberRole ).toInt(); if( trackA < trackB ) return true; else if( trackA == trackB ) { const QString nameA = data( NameRole ).toString(); const QString nameB = other.data( NameRole ).toString(); - return KStringHandler::naturalCompare( nameA, nameB, Qt::CaseInsensitive ) < 0; + QCollator collator; + collator.setNumericMode( true ); + collator.setCaseSensitivity( Qt::CaseInsensitive ); + return collator.compare( nameA, nameB ) < 0; } else return false; } diff --git a/src/context/applets/albums/TrackItem.h b/src/context/applets/albums/plugin/TrackItem.h similarity index 94% rename from src/context/applets/albums/TrackItem.h rename to src/context/applets/albums/plugin/TrackItem.h index d39e66f067..3f4e6c3f0d 100644 --- a/src/context/applets/albums/TrackItem.h +++ b/src/context/applets/albums/plugin/TrackItem.h @@ -1,68 +1,68 @@ /**************************************************************************************** * Copyright (c) 2008 Seb Ruiz * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef AMAROK_TRACKITEM_H #define AMAROK_TRACKITEM_H #include "core/meta/Observer.h" #include #include class TrackItem : public QStandardItem, public Meta::Observer { public: TrackItem(); ~TrackItem(); /** * Sets the TrackPtr for this item to associate with * * @arg track pointer to associate with */ void setTrack( Meta::TrackPtr trackPtr ); /** * @return the track pointer associated with this item */ Meta::TrackPtr track() const { return m_track; } /** * Applies an italic style if the track is the currently * playing track */ void italicise(); /** * Applies a bold style if the track is owned by the currently * playing artist */ void bold(); // overloaded from Meta::Observer using Observer::metadataChanged; - virtual void metadataChanged( Meta::TrackPtr track ); + virtual void metadataChanged( Meta::TrackPtr track ) Q_DECL_OVERRIDE; - virtual int type() const; + virtual int type() const Q_DECL_OVERRIDE; - virtual bool operator<( const QStandardItem &other ) const; + virtual bool operator<( const QStandardItem &other ) const Q_DECL_OVERRIDE; private: Meta::TrackPtr m_track; QMutex m_mutex; }; #endif // multiple inclusion guard diff --git a/src/context/applets/albums/plugin/qmldir b/src/context/applets/albums/plugin/qmldir new file mode 100644 index 0000000000..da1f804abf --- /dev/null +++ b/src/context/applets/albums/plugin/qmldir @@ -0,0 +1,2 @@ +module org.kde.amarok.albums +plugin amarok_context_applet_albums diff --git a/src/context/applets/analyzer/AnalyzerApplet.cpp b/src/context/applets/analyzer/AnalyzerApplet.cpp deleted file mode 100644 index 036db0870b..0000000000 --- a/src/context/applets/analyzer/AnalyzerApplet.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2013 Mark Kretschmann * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "AnalyzerApplet" - -#include "AnalyzerApplet.h" - -#include "core/support/Amarok.h" - -#include "BallsAnalyzer.h" -#include "BlockAnalyzer.h" -#include "DiscoAnalyzer.h" -#include "ASCIIAnalyzer.h" - -#include -#include -#include - - -AnalyzerApplet::AnalyzerApplet( QObject* parent, const QVariantList& args ) - : Context::Applet( parent, args ) - , m_analyzer( 0 ) -{ - setHasConfigurationInterface( false ); - - connect( this, SIGNAL(geometryChanged()), SLOT(newGeometry()) ); -} - -AnalyzerApplet::~AnalyzerApplet() -{ - KConfigGroup config = Amarok::config( "Analyzer Applet" ); - config.writeEntry( "Height", (int)m_currentHeight ); - config.writeEntry( "Current Analyzer", m_analyzerName ); -} - -void -AnalyzerApplet::init() -{ - // Call the base implementation. - Context::Applet::init(); - - m_analyzerNames["Balls"] = i18nc( "Analyzer name", "Balls" ); - m_analyzerNames["Blocky"] = i18nc( "Analyzer name", "Blocky" ); - m_analyzerNames["Disco"] = i18nc( "Analyzer name", "Disco" ); - m_analyzerNames["ASCII"] = i18nc( "Analyzer name", "ASCII" ); - - KConfigGroup config = Amarok::config( "Analyzer Applet" ); - setNewHeight( (WidgetHeight)config.readEntry( "Height", int() ) ); - - setCurrentAnalyzer( config.readEntry( "Current Analyzer", "Blocky" ) ); -} - -void -AnalyzerApplet::newGeometry() // SLOT -{ - if( !m_analyzer ) - return; - - // Use the applet's geometry for showing the analyzer widget at the same position - QRect analyzerGeometry = geometry().toRect(); - - // Adjust widget geometry to keep the applet border intact - analyzerGeometry.adjust( +3, +3, -3, -3 ); - - m_analyzer->setGeometry( analyzerGeometry ); -} - -void -AnalyzerApplet::hideEvent( QHideEvent* ) -{ - m_analyzer->hide(); -} - -void -AnalyzerApplet::showEvent( QShowEvent* ) -{ - m_analyzer->show(); -} - -QList -AnalyzerApplet::contextualActions () -{ - QList actions; - QAction *action; - - QMenu *heightMenu = new QMenu( i18n( "Height" ), view() ); - actions << heightMenu->menuAction(); - - QActionGroup *heightActions = new QActionGroup( this ); - - action = heightMenu->addAction( i18nc( "Height of the Analyzer applet", "Tiny" ) ); - action->setCheckable( true ); - action->setChecked( m_currentHeight == Tiny ); - action->setActionGroup( heightActions ); - action->setData( (int)Tiny ); - connect( action, SIGNAL(triggered()), SLOT(heightActionTriggered()) ); - - action = heightMenu->addAction( i18nc( "Height of the Analyzer applet", "Small" ) ); - action->setCheckable( true ); - action->setChecked( m_currentHeight == Small ); - action->setActionGroup( heightActions ); - action->setData( (int)Small ); - connect( action, SIGNAL(triggered()), SLOT(heightActionTriggered()) ); - - action = heightMenu->addAction( i18nc( "Height of the Analyzer applet", "Medium" ) ); - action->setCheckable( true ); - action->setChecked( m_currentHeight == Medium ); - action->setActionGroup( heightActions ); - action->setData( (int)Medium ); - connect( action, SIGNAL(triggered()), SLOT(heightActionTriggered()) ); - - action = heightMenu->addAction( i18nc( "Height of the Analyzer applet", "Tall" ) ); - action->setCheckable( true ); - action->setChecked( m_currentHeight == Tall ); - action->setActionGroup( heightActions ); - action->setData( (int)Tall ); - connect( action, SIGNAL(triggered()), SLOT(heightActionTriggered()) ); - - action = new QAction( this ); - action->setSeparator( true ); - actions << action; - - QActionGroup *analyzerActions = new QActionGroup( this ); - connect( analyzerActions, SIGNAL(triggered(QAction*)), SLOT( analyzerAction(QAction*)) ); - - QMap::const_iterator i = m_analyzerNames.constBegin(); - while ( i != m_analyzerNames.constEnd() ) { - action = new QAction( i.value(), this ); - action->setData( i.key() ); - action->setCheckable( true ); - action->setChecked( m_analyzerName == i.key() ); - action->setActionGroup( analyzerActions ); - actions << action; - i++; - } - - return actions; -} - -void -AnalyzerApplet::setNewHeight( WidgetHeight height ) -{ - if( !( height == Tiny || height == Small || height == Medium || height == Tall ) ) - height = Default; - - setMinimumHeight( (int)height ); - m_currentHeight = height; -} - -void -AnalyzerApplet::heightActionTriggered() // SLOT -{ - QAction *action = static_cast( sender() ); - setNewHeight( static_cast( action->data().toInt() ) ); -} - -void -AnalyzerApplet::analyzerAction( QAction *action ) // SLOT -{ - setCurrentAnalyzer( action->data().toString() ); -} - -void -AnalyzerApplet::setCurrentAnalyzer( const QString &name ) -{ - if( m_analyzerName == name ) - return; - - delete m_analyzer; - - if( name == "Balls" ) - m_analyzer = new BallsAnalyzer( view()->viewport() ); - else if( name == "Disco" ) - m_analyzer = new DiscoAnalyzer( view()->viewport() ); - else if( name == "ASCII" ) - m_analyzer = new ASCIIAnalyzer( view()->viewport() ); - else - m_analyzer = new BlockAnalyzer( view()->viewport() ); // The default - - m_analyzerName = m_analyzer->objectName(); - m_analyzer->setToolTip( i18n( "Right-click to configure" ) ); - - connect( this, SIGNAL(appletDestroyed(Plasma::Applet*)), m_analyzer, SLOT(deleteLater()) ); - - newGeometry(); - m_analyzer->show(); -} - diff --git a/src/context/applets/analyzer/AnalyzerApplet.h b/src/context/applets/analyzer/AnalyzerApplet.h deleted file mode 100644 index c3e5775d47..0000000000 --- a/src/context/applets/analyzer/AnalyzerApplet.h +++ /dev/null @@ -1,57 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2013 Mark Kretschmann * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef ANALYZER_APPLET_H -#define ANALYZER_APPLET_H - -#include "context/Applet.h" -#include "context/DataEngine.h" - - -class AnalyzerApplet : public Context::Applet -{ - Q_OBJECT - -public: - enum WidgetHeight { Tiny = 80, Small = 120, Medium = 170, Tall = 220, Default = Small }; - - AnalyzerApplet( QObject* parent, const QVariantList& args ); - virtual ~AnalyzerApplet(); - -public Q_SLOTS: - virtual void init(); - -private Q_SLOTS: - void newGeometry(); - void heightActionTriggered(); - void analyzerAction( QAction* ); - -private: - void hideEvent( QHideEvent* ); - void showEvent( QShowEvent* ); - void setNewHeight( WidgetHeight height ); - void setCurrentAnalyzer( const QString &name ); - QList contextualActions(); - - QWidget *m_analyzer; - QString m_analyzerName; - QMap m_analyzerNames; - WidgetHeight m_currentHeight; -}; - -AMAROK_EXPORT_APPLET( analyzer, AnalyzerApplet ) - -#endif diff --git a/src/context/applets/analyzer/AnalyzerBase.cpp b/src/context/applets/analyzer/AnalyzerBase.cpp deleted file mode 100644 index c38cb77884..0000000000 --- a/src/context/applets/analyzer/AnalyzerBase.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2003 Max Howell * - * Copyright (c) 2009 Martin Sandsmark * - * Copyright (c) 2013 Mark Kretschmann * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#include "AnalyzerBase.h" - -#include "core/support/Debug.h" -#include "EngineController.h" -#include "MainWindow.h" - -#include // interpolate() - -#include - -#include - - -// INSTRUCTIONS -// 1. reimplement analyze() -// 2. if you want to manipulate the scope, reimplement transform() - - -Analyzer::Base::Base( QWidget *parent ) - : QGLWidget( parent ) - , m_fht( new FHT( log2( EngineController::DATAOUTPUT_DATA_SIZE ) ) ) - , m_renderTimer( new QTimer( this ) ) - , m_demoTimer( new QTimer( this ) ) -{ - connect( EngineController::instance(), SIGNAL( playbackStateChanged() ), this, SLOT( playbackStateChanged() ) ); - - setFps( 60 ); // Default unless changed by subclass - m_demoTimer->setInterval( 33 ); // ~30 fps - - enableDemo( !EngineController::instance()->isPlaying() ); - -#ifdef Q_WS_X11 - connect( KWindowSystem::self(), SIGNAL( currentDesktopChanged( int ) ), this, SLOT( currentDesktopChanged() ) ); -#endif - - connect( m_renderTimer, SIGNAL( timeout() ), this, SLOT( updateGL() ) ); - - //initialize openGL context before managing GL calls - makeCurrent(); - - connectSignals(); -} - - -Analyzer::Base::~Base() -{ - delete m_fht; -} - -void -Analyzer::Base::connectSignals() -{ - DEBUG_BLOCK - - if( m_renderTimer->isActive() ) - return; - - connect( EngineController::instance(), SIGNAL( audioDataReady( const QMap > & ) ), - this, SLOT( processData( const QMap > & ) ) ); - connect( m_demoTimer, SIGNAL( timeout() ), this, SLOT( demo() ) ); - m_renderTimer->start(); -} - -void -Analyzer::Base::disconnectSignals() -{ - DEBUG_BLOCK - - disconnect( EngineController::instance(), SIGNAL( audioDataReady( const QMap > & ) ), - this, SLOT( processData( const QMap > & ) ) ); - m_demoTimer->disconnect( this ); - m_renderTimer->stop(); -} - -void -Analyzer::Base::currentDesktopChanged() -{ - // Optimization for X11/Linux desktops: - // Don't update the analyzer if Amarok is not on the active virtual desktop. - - if( The::mainWindow()->isOnCurrentDesktop() ) - connectSignals(); - else - disconnectSignals(); -} - -void -Analyzer::Base::playbackStateChanged() -{ - enableDemo( !EngineController::instance()->isPlaying() ); -} - -void -Analyzer::Base::enableDemo( bool enable ) -{ - enable ? m_demoTimer->start() : m_demoTimer->stop(); -} - -void -Analyzer::Base::hideEvent( QHideEvent * ) -{ - QTimer::singleShot( 0, this, SLOT( disconnectSignals() ) ); -} - -void -Analyzer::Base::showEvent( QShowEvent * ) -{ - QTimer::singleShot( 0, this, SLOT( connectSignals() ) ); -} - -void -Analyzer::Base::transform( QVector &scope ) //virtual -{ - //this is a standard transformation that should give - //an FFT scope that has bands for pretty analyzers - - float *front = static_cast( &scope.front() ); - - float* f = new float[ m_fht->size() ]; - m_fht->copy( &f[0], front ); - m_fht->logSpectrum( front, &f[0] ); - m_fht->scale( front, 1.0 / 20 ); - - scope.resize( m_fht->size() / 2 ); //second half of values are rubbish - delete [] f; -} - -void -Analyzer::Base::processData( const QMap > &thescope ) -{ - if( thescope.isEmpty() || thescope[Phonon::AudioDataOutput::LeftChannel].size() != m_fht->size() ) - return; - - QVector scope( m_fht->size() ); - - for( uint x = 0; ( int )x < m_fht->size(); ++x ) - { - if( thescope.size() == 1 ) // Mono - { - scope[x] = double( thescope[Phonon::AudioDataOutput::LeftChannel][x] ); - } - else // Anything > Mono is treated as Stereo - { - scope[x] = double( thescope[Phonon::AudioDataOutput::LeftChannel][x] - + thescope[Phonon::AudioDataOutput::RightChannel][x] ) - / ( 2 * ( 1 << 15 ) ); // Average between the channels - } - } - - transform( scope ); - analyze( scope ); -} - -void -Analyzer::Base::demo() //virtual -{ - static int t = 201; - - if( t > 300 ) - t = 1; //0 = wasted calculations - - if( t < 201 ) - { - QVector s( 512 ); - - const double dt = double( t ) / 200; - for( int i = 0; i < s.size(); ++i ) - s[i] = dt * ( sin( M_PI + ( i * M_PI ) / s.size() ) + 1.0 ); - - analyze( s ); - } - else - analyze( QVector( 1, 0 ) ); - - ++t; -} - -void -Analyzer::Base::interpolate( const QVector &inVec, QVector &outVec ) const -{ - double pos = 0.0; - const double step = ( double )inVec.size() / outVec.size(); - - for( int i = 0; i < outVec.size(); ++i, pos += step ) - { - const double error = pos - std::floor( pos ); - const unsigned long offset = ( unsigned long )pos; - - long indexLeft = offset + 0; - - if( indexLeft >= inVec.size() ) - indexLeft = inVec.size() - 1; - - long indexRight = offset + 1; - - if( indexRight >= inVec.size() ) - indexRight = inVec.size() - 1; - - outVec[i] = inVec[indexLeft ] * ( 1.0 - error ) + - inVec[indexRight] * error; - } -} - -void -Analyzer::Base::setFps( int fps ) -{ - m_renderTimer->setInterval( 1000 / fps ); -} - - diff --git a/src/context/applets/analyzer/AnalyzerBase.h b/src/context/applets/analyzer/AnalyzerBase.h deleted file mode 100644 index fb3993c0c2..0000000000 --- a/src/context/applets/analyzer/AnalyzerBase.h +++ /dev/null @@ -1,78 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2004 Max Howell * - * Copyright (c) 2009 Martin Sandsmark * - * Copyright (c) 2013 Mark Kretschmann * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef ANALYZERBASE_H -#define ANALYZERBASE_H - -#ifdef __FreeBSD__ -#include -#endif - -#include "fht.h" //stack allocated - -#include - -#include //included for convenience - -#include - - -namespace Analyzer -{ - -class Base : public QGLWidget -{ - Q_OBJECT - -protected: - Base( QWidget* ); - ~Base(); - - void interpolate( const QVector&, QVector& ) const; - - virtual void transform( QVector& ); - virtual void analyze( const QVector& ) = 0; - - void setFps( int fps ); - - FHT *m_fht; - QTimer *m_renderTimer; - -protected Q_SLOTS: - virtual void demo(); - -private Q_SLOTS: - void connectSignals(); - void disconnectSignals(); - void currentDesktopChanged(); - void processData( const QMap > &thescope ); - void playbackStateChanged(); - -private: - void enableDemo( bool enable ); - void hideEvent( QHideEvent* ); - void showEvent( QShowEvent* ); - - QTimer *m_demoTimer; -}; - - -} //END namespace Analyzer - - -#endif diff --git a/src/context/applets/analyzer/BlockAnalyzer.cpp b/src/context/applets/analyzer/BlockAnalyzer.cpp deleted file mode 100644 index 6325774695..0000000000 --- a/src/context/applets/analyzer/BlockAnalyzer.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2003-2005 Max Howell * - * Copyright (c) 2005-2013 Mark Kretschmann * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Pulic License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#include "BlockAnalyzer.h" - -#include "PaletteHandler.h" - -#include - -#include -#include - - -BlockAnalyzer* BlockAnalyzer::instance = 0; - -static inline uint myMax( uint v1, uint v2 ) -{ - return v1 > v2 ? v1 : v2; -} - -BlockAnalyzer::BlockAnalyzer( QWidget *parent ) - : Analyzer::Base( parent ) - , m_columns( 0 ) //int - , m_rows( 0 ) //int - , m_fade_bars( FADE_SIZE ) //vector - , m_fade_pos( MAX_COLUMNS, 50 ) //vector - , m_fade_intensity( MAX_COLUMNS, 32 ) //vector -{ - instance = this; - setObjectName( "Blocky" ); - - setMaximumWidth( MAX_COLUMNS * ( BLOCK_WIDTH + 1 ) - 1 ); - setFps( 50 ); -} - -void -BlockAnalyzer::initializeGL() -{ - // Disable depth test (all is drawn on a 2d plane) - glDisable( GL_DEPTH_TEST ); -} - -void -BlockAnalyzer::resizeGL( int w, int h ) -{ - glViewport( 0, 0, (GLint)w, (GLint)h ); - - // Set up a 2D projection matrix - glMatrixMode( GL_PROJECTION ); - glLoadIdentity(); - glOrtho( 0.0, (GLdouble)w, (GLdouble)h, 0.0, 0.0, 1.0 ); - - const int oldRows = m_rows; - - // Rounded up so that the last column/line is covered if partially visible - m_columns = std::min( std::ceil( (double)width() / ( BLOCK_WIDTH + 1 ) ), (double)MAX_COLUMNS ); - m_rows = std::ceil( (double)height() / ( BLOCK_HEIGHT + 1 ) ); - - m_scope.resize( m_columns ); - m_store.resize( m_columns ); - - if( m_rows != oldRows ) - { - m_barPixmap = QPixmap( BLOCK_WIDTH, m_rows * ( BLOCK_HEIGHT + 1 ) ); - - m_yscale.resize( m_rows + 1 ); - - const float PRE = 1, PRO = 1; //PRE and PRO allow us to restrict the range somewhat - - for( int z = 0; z < m_rows; ++z ) - m_yscale[z] = 1 - ( log10( PRE + z ) / log10( PRE + m_rows + PRO ) ); - - m_yscale[m_rows] = 0; - - determineStep(); - paletteChange( palette() ); - } - - drawBackground(); - analyze( m_scope ); -} - -void -BlockAnalyzer::determineStep() -{ - // falltime is dependent on rowcount due to our digital resolution (ie we have boxes/blocks of pixels) - // I calculated the value 30 based on some trial and error - - const double fallTime = 30 * m_rows; - m_step = double( m_rows * 80 ) / fallTime; //80 = ~milliseconds between signals with audio data -} - -void -BlockAnalyzer::transform( QVector &s ) //pure virtual -{ - for( int x = 0; x < s.size(); ++x ) - s[x] *= 2; - - float *front = static_cast( &s.front() ); - - m_fht->spectrum( front ); - m_fht->scale( front, 1.0 / 20 ); - - //the second half is pretty dull, so only show it if the user has a large analyzer - //by setting to m_scope.size() if large we prevent interpolation of large analyzers, this is good! - s.resize( m_scope.size() <= MAX_COLUMNS / 2 ? MAX_COLUMNS / 2 : m_scope.size() ); -} - -void -BlockAnalyzer::analyze( const QVector &s ) -{ - interpolate( s, m_scope ); -} - -void -BlockAnalyzer::paintGL() -{ - // y = 2 3 2 1 0 2 - // . . . . # . - // . . . # # . - // # . # # # # - // # # # # # # - // - // visual aid for how this analyzer works. - // y represents the number of blanks - // y starts from the top and increases in units of blocks - - // m_yscale looks similar to: { 0.7, 0.5, 0.25, 0.15, 0.1, 0 } - // if it contains 6 elements there are 5 rows in the analyzer - - glMatrixMode( GL_MODELVIEW ); - glLoadIdentity(); - - // Paint the background - drawTexture( m_background.data(), 0, 0, 0, 0 ); - - for( uint y, x = 0; x < (uint)m_scope.size(); ++x ) - { - // determine y - for( y = 0; m_scope[x] < m_yscale[y]; ++y ) - ; - - // this is opposite to what you'd think, higher than y - // means the bar is lower than y (physically) - if( ( float )y > m_store[x] ) - y = uint( m_store[x] += m_step ); - else - m_store[x] = y; - - // if y is lower than m_fade_pos, then the bar has exceeded the height of the fadeout - // if the fadeout is quite faded now, then display the new one - if( y <= m_fade_pos[x] /*|| m_fade_intensity[x] < FADE_SIZE / 3*/ ) - { - m_fade_pos[x] = y; - m_fade_intensity[x] = FADE_SIZE; - } - - if( m_fade_intensity[x] > 0 ) - { - const uint offset = --m_fade_intensity[x]; - const uint y = m_fade_pos[x] * ( BLOCK_HEIGHT + 1 ); - if( y < (uint)height() ) - drawTexture( m_fade_bars[offset].data(), x * ( BLOCK_WIDTH + 1 ), y, 0, 0 ); - } - - if( m_fade_intensity[x] == 0 ) - m_fade_pos[x] = m_rows; - - // REMEMBER: y is a number from 0 to m_rows, 0 means all blocks are glowing, m_rows means none are - drawTexture( m_barTexture.data(), x * ( BLOCK_WIDTH + 1 ), y * ( BLOCK_HEIGHT + 1 ), 0, y * ( BLOCK_HEIGHT + 1 ) ); - - // Draw top bar - drawTexture( m_topBarTexture.data(), x * ( BLOCK_WIDTH + 1 ), int( m_store[x] ) * ( BLOCK_HEIGHT + 1 ), 0, 0 ); - } -} - -void -BlockAnalyzer::drawTexture( Texture* texture, int x, int y, int sx, int sy ) -{ - const GLfloat xf = x; - const GLfloat yf = y; - const GLfloat wf = texture->size.width() - sx; - const GLfloat hf = texture->size.height() - sy; - const GLfloat sxf = (GLfloat)sx / texture->size.width(); - const GLfloat syf = (GLfloat)sy / texture->size.height(); - - glEnable( GL_TEXTURE_2D ); - glBindTexture( GL_TEXTURE_2D, texture->id ); - - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); - - // Draw a textured quad - glBegin(GL_QUADS); - glTexCoord2f( sxf, syf ); glVertex2f( xf, yf ); - glTexCoord2f( sxf, 1.0 ); glVertex2f( xf, yf + hf ); - glTexCoord2f( 1.0, 1.0 ); glVertex2f( xf + wf, yf + hf ); - glTexCoord2f( 1.0, syf ); glVertex2f( xf + wf, yf ); - glEnd(); - - glDisable( GL_TEXTURE_2D ); -} - -void -BlockAnalyzer::paletteChange( const QPalette& ) //virtual -{ - QPainter p( &m_barPixmap ); - - const QColor bg = The::paletteHandler()->backgroundColor(); - const QColor fg = palette().color( QPalette::Active, QPalette::Highlight ); - - QPixmap topBar( BLOCK_WIDTH, BLOCK_HEIGHT ); - topBar.fill( fg ); - m_topBarTexture = QSharedPointer( new Texture( topBar ) ); - - const double dr = 15 * double( bg.red() - fg.red() ) / ( m_rows * 16 ); - const double dg = 15 * double( bg.green() - fg.green() ) / ( m_rows * 16 ); - const double db = 15 * double( bg.blue() - fg.blue() ) / ( m_rows * 16 ); - const int r = fg.red(), g = fg.green(), b = fg.blue(); - - m_barPixmap.fill( bg ); - - for( int y = 0; y < m_rows; ++y ) - //graduate the fg color - p.fillRect( 0, y * ( BLOCK_HEIGHT + 1 ), BLOCK_WIDTH, BLOCK_HEIGHT, QColor( r + int( dr * y ), g + int( dg * y ), b + int( db * y ) ) ); - - { - const QColor bg = palette().color( QPalette::Active, QPalette::Window ).dark( 112 ); - - //make a complimentary fadebar colour - //TODO dark is not always correct, dumbo! - int h, s, v; palette().color( QPalette::Active, QPalette::Window ).dark( 150 ).getHsv( &h, &s, &v ); - const QColor fg = QColor::fromHsv( h + 60, s, v ); - - const double dr = fg.red() - bg.red(); - const double dg = fg.green() - bg.green(); - const double db = fg.blue() - bg.blue(); - const int r = bg.red(), g = bg.green(), b = bg.blue(); - - // Precalculate all fade-bar pixmaps - for( int y = 0; y < FADE_SIZE; ++y ) - { - QPixmap fadeBar( BLOCK_WIDTH, m_rows * ( BLOCK_HEIGHT + 1 ) ); - - fadeBar.fill( palette().color( QPalette::Active, QPalette::Window ) ); - const double Y = 1.0 - ( log10( ( FADE_SIZE ) - y ) / log10( ( FADE_SIZE ) ) ); - QPainter f( &fadeBar ); - for( int z = 0; z < m_rows; ++z ) - f.fillRect( 0, z * ( BLOCK_HEIGHT + 1 ), BLOCK_WIDTH, BLOCK_HEIGHT, QColor( r + int( dr * Y ), g + int( dg * Y ), b + int( db * Y ) ) ); - - m_fade_bars[y] = QSharedPointer( new Texture( fadeBar ) ); - } - } - - m_barTexture = QSharedPointer( new Texture( m_barPixmap ) ); - drawBackground(); -} - -void -BlockAnalyzer::drawBackground() -{ - const QColor bg = palette().color( QPalette::Active, QPalette::Window ); - const QColor bgdark = bg.dark( 112 ); - - QPixmap background( size() ); - background.fill( bg ); - - QPainter p( &background ); - for( int x = 0; x < m_columns; ++x ) - for( int y = 0; y < m_rows; ++y ) - p.fillRect( x * ( BLOCK_WIDTH + 1 ), y * ( BLOCK_HEIGHT + 1 ), BLOCK_WIDTH, BLOCK_HEIGHT, bgdark ); - - m_background = QSharedPointer( new Texture( background ) ); -} diff --git a/src/context/applets/analyzer/CMakeLists.txt b/src/context/applets/analyzer/CMakeLists.txt index 4e75f9de5b..eef918f828 100644 --- a/src/context/applets/analyzer/CMakeLists.txt +++ b/src/context/applets/analyzer/CMakeLists.txt @@ -1,33 +1,34 @@ -project(context-analyzer) - -find_package(OpenGL REQUIRED) - set(analyzer_SRCS - AnalyzerApplet.cpp - AnalyzerBase.cpp - BallsAnalyzer.cpp - BlockAnalyzer.cpp - DiscoAnalyzer.cpp - ASCIIAnalyzer.cpp - fht.cpp + plugin/AnalyzerPlugin.cpp + plugin/AnalyzerBase.cpp + plugin/AnalyzerWorker.cpp + #plugin/BallsAnalyzer.cpp + plugin/BlockAnalyzer.cpp + plugin/BlockWorker.cpp + #plugin/DiscoAnalyzer.cpp + #plugin/ASCIIAnalyzer.cpp ) -include_directories(${OPENGL_INCLUDE_DIR}) +find_library(FFTW_LIBRARIES NAMES fftw3) +if(NOT FFTW_LIBRARIES) + message(FATAL_ERROR "fftw not found") +endif(NOT FFTW_LIBRARIES) -add_library(amarok_context_applet_analyzer MODULE ${analyzer_SRCS}) +add_library(amarok_context_applet_analyzer SHARED ${analyzer_SRCS}) if(APPLE) set_target_properties(amarok_context_applet_analyzer PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") endif() - target_link_libraries(amarok_context_applet_analyzer amarokcore amaroklib - KF5::Plasma - Qt5::OpenGL - ${OPENGL_gl_LIBRARY} + Qt5::Qml + Qt5::Quick + fftw3 ) -install(TARGETS amarok_context_applet_analyzer DESTINATION ${PLUGIN_INSTALL_DIR}) -install(FILES amarok-context-applet-analyzer.desktop DESTINATION ${SERVICES_INSTALL_DIR}) +install(TARGETS amarok_context_applet_analyzer DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/analyzer) +install(FILES plugin/qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/analyzer) + +kpackage_install_package(package org.kde.amarok.analyzer amarok) diff --git a/src/context/applets/analyzer/fht.cpp b/src/context/applets/analyzer/fht.cpp deleted file mode 100644 index 7500261bd0..0000000000 --- a/src/context/applets/analyzer/fht.cpp +++ /dev/null @@ -1,253 +0,0 @@ -// FHT - Fast Hartley Transform Class -// -// Copyright (C) 2004 Melchior FRANZ - mfranz@kde.org -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -// -// $Id: fht.cpp 871912 2008-10-16 02:27:10Z mitchell $ - - -#include "fht.h" - -#include -#include - -FHT::FHT( int n ) : - m_buf( 0 ), - m_tab( 0 ), - m_log( 0 ) -{ - if( n < 3 ) - { - m_num = 0; - m_exp2 = -1; - return; - } - m_exp2 = n; - m_num = 1 << n; - if( n > 3 ) - { - m_buf = new float[m_num]; - m_tab = new float[m_num * 2]; - makeCasTable(); - } -} - - -FHT::~FHT() -{ - delete[] m_buf; - delete[] m_tab; - delete[] m_log; -} - - -void FHT::makeCasTable( void ) -{ - float d, *costab, *sintab; - int ul, ndiv2 = m_num / 2; - - for( costab = m_tab, sintab = m_tab + m_num / 2 + 1, ul = 0; ul < m_num; ul++ ) - { - d = M_PI * ul / ndiv2; - *costab = *sintab = cos( d ); - - costab += 2, sintab += 2; - if( sintab > m_tab + m_num * 2 ) - sintab = m_tab + 1; - } -} - - -float* FHT::copy( float *d, float *s ) -{ - return ( float * )memcpy( d, s, m_num * sizeof( float ) ); -} - - -float* FHT::clear( float *d ) -{ - return ( float * )memset( d, 0, m_num * sizeof( float ) ); -} - - -void FHT::scale( float *p, float d ) -{ - for( int i = 0; i < ( m_num / 2 ); i++ ) - *p++ *= d; -} - - -void FHT::ewma( float *d, float *s, float w ) -{ - for( int i = 0; i < ( m_num / 2 ); i++, d++, s++ ) - *d = *d * w + *s * ( 1 - w ); -} - - -void FHT::logSpectrum( float *out, float *p ) -{ - int n = m_num / 2, i, j, k, *r; - if( !m_log ) - { - m_log = new int[n]; - float f = n / log10( ( double )n ); - for( i = 0, r = m_log; i < n; i++, r++ ) - { - j = int( rint( log10( i + 1.0 ) * f ) ); - *r = j >= n ? n - 1 : j; - } - } - semiLogSpectrum( p ); - *out++ = *p = *p / 100; - for( k = i = 1, r = m_log; i < n; ++i ) - { - j = *r++; - if( i == j ) - *out++ = p[i]; - else - { - float base = p[k - 1]; - float step = ( p[j] - base ) / ( j - ( k - 1 ) ); - for( float corr = 0; k <= j; k++, corr += step ) - * out++ = base + corr; - } - } -} - - -void FHT::semiLogSpectrum( float *p ) -{ - float e; - power2( p ); - for( int i = 0; i < ( m_num / 2 ); i++, p++ ) - { - e = 10.0 * log10( sqrt( *p * .5 ) ); - *p = e < 0 ? 0 : e; - } -} - - -void FHT::spectrum( float *p ) -{ - power2( p ); - for( int i = 0; i < ( m_num / 2 ); i++, p++ ) - *p = ( float )sqrt( *p * .5 ); -} - - -void FHT::power( float *p ) -{ - power2( p ); - for( int i = 0; i < ( m_num / 2 ); i++ ) - *p++ *= .5; -} - - -void FHT::power2( float *p ) -{ - int i; - float *q; - _transform( p, m_num, 0 ); - - *p = ( *p * *p ), *p += *p, p++; - - for( i = 1, q = p + m_num - 2; i < ( m_num / 2 ); i++, --q ) - *p = ( *p * *p ) + ( *q * *q ), p++; -} - - -void FHT::transform( float *p ) -{ - if( m_num == 8 ) - transform8( p ); - else - _transform( p, m_num, 0 ); -} - - -void FHT::transform8( float *p ) -{ - float a, b, c, d, e, f, g, h, b_f2, d_h2; - float a_c_eg, a_ce_g, ac_e_g, aceg, b_df_h, bdfh; - - a = *p++, b = *p++, c = *p++, d = *p++; - e = *p++, f = *p++, g = *p++, h = *p; - b_f2 = ( b - f ) * M_SQRT2; - d_h2 = ( d - h ) * M_SQRT2; - - a_c_eg = a - c - e + g; - a_ce_g = a - c + e - g; - ac_e_g = a + c - e - g; - aceg = a + c + e + g; - - b_df_h = b - d + f - h; - bdfh = b + d + f + h; - - *p = a_c_eg - d_h2; - *--p = a_ce_g - b_df_h; - *--p = ac_e_g - b_f2; - *--p = aceg - bdfh; - *--p = a_c_eg + d_h2; - *--p = a_ce_g + b_df_h; - *--p = ac_e_g + b_f2; - *--p = aceg + bdfh; -} - - -void FHT::_transform( float *p, int n, int k ) -{ - if( n == 8 ) - { - transform8( p + k ); - return; - } - - int i, j, ndiv2 = n / 2; - float a, *t1, *t2, *t3, *t4, *ptab, *pp; - - for( i = 0, t1 = m_buf, t2 = m_buf + ndiv2, pp = &p[k]; i < ndiv2; i++ ) - *t1++ = *pp++, *t2++ = *pp++; - - memcpy( p + k, m_buf, sizeof( float ) * n ); - - _transform( p, ndiv2, k ); - _transform( p, ndiv2, k + ndiv2 ); - - j = m_num / ndiv2 - 1; - t1 = m_buf; - t2 = t1 + ndiv2; - t3 = p + k + ndiv2; - ptab = m_tab; - pp = p + k; - - a = *ptab++ * *t3++; - a += *ptab * *pp; - ptab += j; - - *t1++ = *pp + a; - *t2++ = *pp++ - a; - - for( i = 1, t4 = p + k + n; i < ndiv2; i++, ptab += j ) - { - a = *ptab++ * *t3++; - a += *ptab * *--t4; - - *t1++ = *pp + a; - *t2++ = *pp++ - a; - } - memcpy( p + k, m_buf, sizeof( float ) * n ); -} - diff --git a/src/context/applets/analyzer/fht.h b/src/context/applets/analyzer/fht.h deleted file mode 100644 index 9516f7ade7..0000000000 --- a/src/context/applets/analyzer/fht.h +++ /dev/null @@ -1,125 +0,0 @@ -// FHT - Fast Hartley Transform Class -// -// Copyright (C) 2004 Melchior FRANZ - mfranz@kde.org -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -// -// $Id: fht.h 668973 2007-05-28 08:55:15Z mkossick $ - -#ifndef FHT_H -#define FHT_H - -/** - * Implementation of the Hartley Transform after Bracewell's discrete - * algorithm. The algorithm is subject to US patent No. 4,646,256 (1987) - * but was put into public domain by the Board of Trustees of Stanford - * University in 1994 and is now freely available[1]. - * - * [1] Computer in Physics, Vol. 9, No. 4, Jul/Aug 1995 pp 373-379 - */ -class FHT -{ - int m_exp2; - int m_num; - float *m_buf; - float *m_tab; - int *m_log; - - /** - * Create a table of "cas" (cosine and sine) values. - * Has only to be done in the constructor and saves from - * calculating the same values over and over while transforming. - */ - void makeCasTable(); - - /** - * Recursive in-place Hartley transform. For internal use only! - */ - void _transform( float *, int, int ); - -public: - /** - * Prepare transform for data sets with @f$2^n@f$ numbers, whereby @f$n@f$ - * should be at least 3. Values of more than 3 need a trigonometry table. - * @see makeCasTable() - */ - FHT( int ); - - ~FHT(); - inline int sizeExp() const - { - return m_exp2; - } - inline int size() const - { - return m_num; - } - float *copy( float *, float * ); - float *clear( float * ); - void scale( float *, float ); - - /** - * Exponentially Weighted Moving Average (EWMA) filter. - * @param d is the filtered data. - * @param s is fresh input. - * @param w is the weighting factor. - */ - void ewma( float *d, float *s, float w ); - - /** - * Logarithmic audio spectrum. Maps semi-logarithmic spectrum - * to logarithmic frequency scale, interpolates missing values. - * A logarithmic index map is calculated at the first run only. - * @param p is the input array. - * @param out is the spectrum. - */ - void logSpectrum( float *out, float *p ); - - /** - * Semi-logarithmic audio spectrum. - */ - void semiLogSpectrum( float * ); - - /** - * Fourier spectrum. - */ - void spectrum( float * ); - - /** - * Calculates a mathematically correct FFT power spectrum. - * If further scaling is applied later, use power2 instead - * and factor the 0.5 in the final scaling factor. - * @see FHT::power2() - */ - void power( float * ); - - /** - * Calculates an FFT power spectrum with doubled values as a - * result. The values need to be multiplied by 0.5 to be exact. - * Note that you only get @f$2^{n-1}@f$ power values for a data set - * of @f$2^n@f$ input values. This is the fastest transform. - * @see FHT::power() - */ - void power2( float * ); - - /** - * Discrete Hartley transform of data sets with 8 values. - */ - void transform8( float * ); - - void transform( float * ); -}; - -#endif diff --git a/src/context/applets/analyzer/package/contents/ui/main.qml b/src/context/applets/analyzer/package/contents/ui/main.qml new file mode 100644 index 0000000000..6dbbf3e979 --- /dev/null +++ b/src/context/applets/analyzer/package/contents/ui/main.qml @@ -0,0 +1,208 @@ + /**************************************************************************************** + * Copyright (c) 2017 Malte Veerman * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 2.0 +import QtQuick.Dialogs 1.2 as Dialogs // QtQuick.Controls Dialogs only work properly with ApplicationWindow +import QtQuick.Layouts 1.0 +import QtQml.Models 2.1 +import org.kde.amarok.qml 1.0 as AmarokQml +import org.kde.amarok.analyzer 1.0 + +AmarokQml.Applet { + id: applet + + BlockAnalyzer { + id: blocky + + anchors.fill: parent + } + + configDialog: Dialogs.Dialog { + id: dialog + + title: i18nc("The title of the analyzer config dialog", "%1 config", applet.name) + width: Context.largeSpacing * 25 + standardButtons: Dialogs.StandardButton.Ok | Dialogs.StandardButton.Apply | Dialogs.StandardButton.Cancel + + function applyChanges() { + blocky.columnWidth = columnWidthSlider.trueValue; + blocky.showFadebars = showFadebarsBox.checked; + blocky.fallSpeed = fallSpeedSlider.value; +// blocky.windowFunction = windowFunctionCombo.currentIndex; + blocky.minimumFrequency = freqSlider.minValue; + blocky.maximumFrequency = freqSlider.maxValue; + blocky.sampleSize = sampleSizeSlider.sampleSize; + } + + onAccepted: applyChanges(); + onApply: applyChanges(); + + Column { + width: parent.width + spacing: Context.smallSpacing + + RowLayout { + width: parent.width + + Label { + Layout.alignment: Qt.AlignLeft + text: i18n("Bar width:") + } + Slider { + id: columnWidthSlider + + readonly property int trueValue: value + 1 // Slider buggy if you set "from"" to 1 + + Layout.alignment: Qt.AlignRight + Layout.fillWidth: true + from: 0 + to: 19 + stepSize: 1 + value: blocky.columnWidth - 1 + + ToolTip { + text: i18np("%1 pixel", "%1 pixels", columnWidthSlider.trueValue) + parent: columnWidthSlider.handle + x: columnWidthSlider.visualPosition * columnWidthSlider.width - width / 2 + visible: columnWidthSlider.pressed + } + } + } + RowLayout { + width: parent.width + + Label { + Layout.alignment: Qt.AlignLeft + text: i18n("Show fadebars:") + } + CheckBox { + id: showFadebarsBox + + Layout.alignment: Qt.AlignRight + Layout.fillWidth: true + checked: blocky.showFadebars + } + } + RowLayout { + width: parent.width + + Label { + Layout.alignment: Qt.AlignLeft + text: i18n("Bars fall speed:") + } + Slider { + id: fallSpeedSlider + + Layout.alignment: Qt.AlignRight + Layout.fillWidth: true + from: 0 + to: 4 + stepSize: 1 + value: blocky.fallSpeed + } + } + RowLayout { + width: parent.width + + Label { + Layout.alignment: Qt.AlignLeft + text: i18n("Frequency range:") + } + RangeSlider { + id: freqSlider + + readonly property real exp: Math.pow(1000, 1.0/100) + readonly property real minValue: 20 * Math.pow(exp, first.value) + readonly property real maxValue: 20 * Math.pow(exp, second.value) + + Layout.alignment: Qt.AlignRight + Layout.fillWidth: true + + first.value: Math.log(blocky.minimumFrequency / 20) / Math.log(exp) + second.value: Math.log(blocky.maximumFrequency / 20) / Math.log(exp) + from: 0 + to: 100 + + ToolTip { + text: i18n("%1 Hz", Math.round(freqSlider.minValue)) + parent: freqSlider.first.handle + visible: freqSlider.first.pressed + } + ToolTip { + text: i18n("%1 Hz", Math.round(freqSlider.maxValue)) + parent: freqSlider.second.handle + visible: freqSlider.second.pressed + } + } + } + RowLayout { + width: parent.width + + Label { + Layout.alignment: Qt.AlignLeft + text: i18n("Sample size:") + } + Slider { + id: sampleSizeSlider + + readonly property int sampleSize: 1024 * Math.pow(2, value) + + Layout.alignment: Qt.AlignRight + Layout.fillWidth: true + from: 0 + to: 4 + stepSize: 1 + value: { + if (blocky.sampleSize <= 1024) + return 0; + else if (blocky.sampleSize <= 2048) + return 1; + else if (blocky.sampleSize <= 4096) + return 2; + else if (blocky.sampleSize <= 8192) + return 3; + return 4; + } + snapMode: Slider.SnapAlways + + ToolTip { + text: Number(sampleSizeSlider.sampleSize).toLocaleString(Qt.locale(), 'f', 0) + parent: sampleSizeSlider.handle + x: sampleSizeSlider.visualPosition * sampleSizeSlider.width - width / 2 + visible: sampleSizeSlider.pressed + } + } + } +// RowLayout { +// width: parent.width +// +// Label { +// Layout.alignment: Qt.AlignLeft +// text: i18n("Window function:") +// } +// ComboBox { +// id: windowFunctionCombo +// +// Layout.alignment: Qt.AlignRight +// Layout.fillWidth: true +// currentIndex: blocky.windowFunction +// model: [i18n("Rectangular"), i18n("Hann"), i18n("Nuttall"), i18n("Lanczos"), i18n("Sine")] +// } +// } + } + } +} diff --git a/src/context/applets/analyzer/amarok-context-applet-analyzer.desktop b/src/context/applets/analyzer/package/metadata.desktop similarity index 87% rename from src/context/applets/analyzer/amarok-context-applet-analyzer.desktop rename to src/context/applets/analyzer/package/metadata.desktop index 550c7451c3..ce1924cec6 100644 --- a/src/context/applets/analyzer/amarok-context-applet-analyzer.desktop +++ b/src/context/applets/analyzer/package/metadata.desktop @@ -1,52 +1,50 @@ [Desktop Entry] Name=Analyzer Name[bs]=Analizator Name[ca]=Analitzador Name[ca@valencia]=Analitzador Name[cs]=Analyzátor Name[da]=Analysator Name[de]=Analysator Name[el]=Αναλυτής Name[en_GB]=Analyser Name[es]=Analizador Name[fi]=Analysaattori Name[fr]=Analyseur Name[gl]=Analizador Name[hu]=Elemző Name[id]=Analizer Name[it]=Analizzatore Name[lt]=Analizatorius Name[lv]=Analizators Name[nb]=Analysator Name[nl]=Analysator Name[pl]=Analizator Name[pt]=Analisador Name[pt_BR]=Analisador Name[ro]=Analizator Name[sk]=Analyzátor Name[sl]=Preučevalnik Name[sr]=Анализатор Name[sr@ijekavian]=Анализатор Name[sr@ijekavianlatin]=Analizator Name[sr@latin]=Analizator Name[sv]=Analysator Name[tr]=Çözümleyici Name[uk]=Аналізатор Name[x-test]=xxAnalyzerxx Name[zh_TW]=分析器 + Type=Service +ServiceTypes=Amarok/ContextApplet Icon=view-media-analyzer-amarok -ServiceTypes=Plasma/Applet -X-KDE-Library=amarok_context_applet_analyzer X-KDE-PluginInfo-Author=Mark Kretschmann X-KDE-PluginInfo-Email=kretschmann@kde.org -X-KDE-PluginInfo-Name=analyzer +X-KDE-PluginInfo-Name=org.kde.amarok.analyzer X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website= X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true -X-KDE-ParentApp=amarok -X-KDE-PluginInfo-Category=Current - +X-KDE-PluginInfo-Category=Multimedia diff --git a/src/context/applets/analyzer/ASCIIAnalyzer.cpp b/src/context/applets/analyzer/plugin/ASCIIAnalyzer.cpp similarity index 100% rename from src/context/applets/analyzer/ASCIIAnalyzer.cpp rename to src/context/applets/analyzer/plugin/ASCIIAnalyzer.cpp diff --git a/src/context/applets/analyzer/ASCIIAnalyzer.h b/src/context/applets/analyzer/plugin/ASCIIAnalyzer.h similarity index 100% rename from src/context/applets/analyzer/ASCIIAnalyzer.h rename to src/context/applets/analyzer/plugin/ASCIIAnalyzer.h diff --git a/src/context/applets/analyzer/plugin/AnalyzerBase.cpp b/src/context/applets/analyzer/plugin/AnalyzerBase.cpp new file mode 100644 index 0000000000..e1cc04a57d --- /dev/null +++ b/src/context/applets/analyzer/plugin/AnalyzerBase.cpp @@ -0,0 +1,239 @@ +/**************************************************************************************** + * Copyright (c) 2003 Max Howell * + * Copyright (c) 2009 Martin Sandsmark * + * Copyright (c) 2013 Mark Kretschmann * + * Copyright (c) 2017 Malte Veerman * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +#include "AnalyzerBase.h" +#include "AnalyzerWorker.h" + +#include "core/support/Amarok.h" +#include "core/support/Debug.h" +#include "EngineController.h" +#include "MainWindow.h" + +#include + +#ifdef Q_WS_X11 +#include +#endif + +#include +#include + + +// INSTRUCTIONS +// 1. Reimplement QQuickFramebufferObject::createRenderer(). +// 2. Reimplement Analyzer::Base::createWorker(). +// 3. Set your preferred scope width with setScopeSize(). + + +Analyzer::Base::Base( QQuickItem *parent ) + : QQuickFramebufferObject( parent ) + , m_sampleRate( 44100 ) + , m_scopeSize( 0 ) + , m_worker( Q_NULLPTR ) +{ + DEBUG_BLOCK + + qRegisterMetaType(); + + m_minFreq = config().readEntry( "minFreq", 50.0 ); + m_maxFreq = config().readEntry( "maxFreq", 15000.0 ); + + connect( The::engineController(), &EngineController::trackChanged, this, &Base::refreshSampleRate ); + connect( The::engineController(), &EngineController::trackMetadataChanged, this, &Base::refreshSampleRate ); + +#ifdef Q_WS_X11 + connect( KWindowSystem::self(), &KWindowSystem::currentDesktopChanged, this, &Base::currentDesktopChanged ); +#endif + + QTimer::singleShot( 0, this, &Base::connectSignals ); +} + +Analyzer::Base::~Base() +{ + DEBUG_BLOCK + + m_worker->deleteLater(); + m_worker = Q_NULLPTR; + m_workerThread.quit(); + m_workerThread.wait(); +} + +void +Analyzer::Base::connectSignals() +{ + DEBUG_BLOCK + + if( !m_worker ) + { + m_worker = createWorker(); + m_worker->setSampleSize( sampleSize() ); + m_worker->setScopeSize( m_scopeSize ); + m_worker->setWindowFunction( windowFunction() ); + m_worker->moveToThread( &m_workerThread ); + m_workerThread.start(); + + connect( this, &Base::calculateExpFactorNeeded, m_worker, &Worker::calculateExpFactor ); + connect( this, &Base::windowFunctionChanged, m_worker, &Worker::setWindowFunction ); + connect( this, &Base::sampleSizeChanged, m_worker, &Worker::setSampleSize ); + connect( this, &Base::scopeSizeChanged, m_worker, &Worker::setScopeSize ); + connect( The::engineController(), &EngineController::playbackStateChanged, m_worker, &Worker::playbackStateChanged ); + connect( The::engineController(), &EngineController::audioDataReady, m_worker, &Worker::receiveData, Qt::DirectConnection ); + + setSampleSize( config().readEntry( "sampleSize", 4096 ) ); + setWindowFunction( (WindowFunction) config().readEntry( "windowFunction", (int)Hann ) ); + emit calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate); + } +} + +void +Analyzer::Base::disconnectSignals() +{ + DEBUG_BLOCK + + if( m_worker ) + disconnect( The::engineController(), &EngineController::audioDataReady, m_worker, &Worker::receiveData ); +} + +void +Analyzer::Base::currentDesktopChanged() +{ + // Optimization for X11/Linux desktops: + // Don't update the analyzer if Amarok is not on the active virtual desktop. + + if( The::mainWindow()->isOnCurrentDesktop() ) + connectSignals(); + else + disconnectSignals(); +} + +void +Analyzer::Base::refreshSampleRate() +{ + const auto currentTrack = The::engineController()->currentTrack(); + int sampleRate = currentTrack ? currentTrack->sampleRate() : 44100; + + if( m_sampleRate == sampleRate ) + return; + + m_sampleRate = sampleRate; + + emit calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate ); +} + +KConfigGroup +Analyzer::Base::config() const +{ + return Amarok::config( QStringLiteral( "Context" ) ).group( "Analyzer" ); +} + +void +Analyzer::Base::setScopeSize( int scopeSize ) +{ + if( scopeSize <= 0 ) + { + debug() << "Scope size must be greater than zero"; + return; + } + + if( m_scopeSize == scopeSize ) + return; + + m_scopeSize = scopeSize; + emit scopeSizeChanged( scopeSize ); + emit calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate ); +} + +void +Analyzer::Base::setMaxFreq( qreal maxFreq ) +{ + DEBUG_BLOCK + + debug() << "Set maximum frequency to:" << maxFreq; + + if( m_maxFreq == maxFreq || maxFreq < 0.0 ) + return; + + config().writeEntry( "maxFreq", maxFreq ); + m_maxFreq = maxFreq; + emit maxFreqChanged(); + emit calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate ); +} + +void +Analyzer::Base::setMinFreq( qreal minFreq ) +{ + DEBUG_BLOCK + + debug() << "Set minimum frequency to:" << minFreq; + + if( m_minFreq == minFreq || minFreq <= 0.0 ) + return; + + config().writeEntry( "minFreq", minFreq ); + m_minFreq = minFreq; + emit minFreqChanged(); + emit calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate ); +} + +Analyzer::Base::WindowFunction +Analyzer::Base::windowFunction() const +{ + return (WindowFunction)config().readEntry( "windowFunction", (int)Hann ); +} + +void +Analyzer::Base::setWindowFunction( WindowFunction windowFunction ) +{ + DEBUG_BLOCK + + debug() << "Set window function to:" << windowFunction; + + config().writeEntry( "windowFunction", (int)windowFunction ); + emit windowFunctionChanged( windowFunction ); +} + +int Analyzer::Base::sampleSize() const +{ + return config().readEntry( "sampleSize", 2048 ); +} + +void +Analyzer::Base::setSampleSize( uint sampleSize ) +{ + DEBUG_BLOCK + + debug() << "Set sample size to:" << sampleSize; + + if( sampleSize < (int) EngineController::DATAOUTPUT_DATA_SIZE ) + { + warning() << "Sample size must be at least" << EngineController::DATAOUTPUT_DATA_SIZE; + sampleSize = EngineController::DATAOUTPUT_DATA_SIZE; + } + + config().writeEntry( "sampleSize", sampleSize ); + emit sampleSizeChanged( sampleSize ); + emit calculateExpFactorNeeded( m_minFreq, m_maxFreq, m_sampleRate ); +} + +const Analyzer::Worker * +Analyzer::Base::worker() const +{ + return const_cast( m_worker ); +} + diff --git a/src/context/applets/analyzer/plugin/AnalyzerBase.h b/src/context/applets/analyzer/plugin/AnalyzerBase.h new file mode 100644 index 0000000000..47eb1546ba --- /dev/null +++ b/src/context/applets/analyzer/plugin/AnalyzerBase.h @@ -0,0 +1,127 @@ +/**************************************************************************************** + * Copyright (c) 2004 Max Howell * + * Copyright (c) 2009 Martin Sandsmark * + * Copyright (c) 2013 Mark Kretschmann * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +#ifndef ANALYZERBASE_H +#define ANALYZERBASE_H + +#ifdef __FreeBSD__ +#include +#endif + +#include + +#include +#include +#include +#include + +#include + + +namespace Analyzer +{ + class Worker; + + class Base : public QQuickFramebufferObject +{ + Q_OBJECT + Q_PROPERTY(qreal minFrequency READ minFreq WRITE setMinFreq NOTIFY minFreqChanged) + Q_PROPERTY(qreal maxFrequency READ maxFreq WRITE setMaxFreq NOTIFY maxFreqChanged) + Q_PROPERTY(WindowFunction windowFunction READ windowFunction WRITE setWindowFunction NOTIFY windowFunctionChanged) + Q_PROPERTY(qreal minimumFrequency READ minFreq WRITE setMinFreq NOTIFY minFreqChanged) + Q_PROPERTY(qreal maximumFrequency READ maxFreq WRITE setMaxFreq NOTIFY maxFreqChanged) + Q_PROPERTY(int sampleSize READ sampleSize WRITE setSampleSize NOTIFY sampleSizeChanged) + +public: + enum WindowFunction + { + Rectangular = 0, + Hann = 1, + Nuttall = 2, + Lanczos = 3, + Sine = 4 + }; + Q_ENUM(WindowFunction) + + static const int DEMO_INTERVAL = 20; // ~50 fps + + virtual ~Base(); + + qreal maxFreq() const { return m_maxFreq; } + void setMaxFreq( qreal maxFreq ); + qreal minFreq() const { return m_minFreq; } + void setMinFreq( qreal minFreq ); + WindowFunction windowFunction() const; + void setWindowFunction( WindowFunction windowFunction ); + int sampleSize() const; + void setSampleSize( uint sampleSize ); + + /** + * Returns the worker object associated with this analyzer. + */ + const Worker* worker() const; + +signals: + void minFreqChanged(); + void maxFreqChanged(); + void scopeSizeChanged( uint ); + void windowFunctionChanged( WindowFunction ); + void sampleSizeChanged( uint ); + void calculateExpFactorNeeded( qreal, qreal, uint ); + +protected: + Base( QQuickItem* ); + + /** + * Creates a new worker instance. + * Subclasses must implement this function. + * All compute heavy tasks should be offloaded to the created worker. + * If you make any connections with your worker, remember to make them queued connections. + * Do not set a parent for the worker. Base will take ownership of it. + */ + virtual Worker* createWorker() const = 0; + + /** + * Returns the standard KConfigGroup for all analyzers. + * You can reimplement this function, if you want your subclass to have a different config. + */ + virtual KConfigGroup config() const; + + /** + * Use this function to set the size for the scope computed by the worker. + */ + void setScopeSize( int size ); + +private: + void connectSignals(); + void disconnectSignals(); + void currentDesktopChanged(); + void refreshSampleRate(); + + double m_minFreq, m_maxFreq; + int m_sampleRate; + int m_scopeSize; + + Worker *m_worker; + QThread m_workerThread; +}; + + +} //END namespace Analyzer + +#endif diff --git a/src/context/applets/analyzer/plugin/AnalyzerPlugin.cpp b/src/context/applets/analyzer/plugin/AnalyzerPlugin.cpp new file mode 100644 index 0000000000..c12051a4f7 --- /dev/null +++ b/src/context/applets/analyzer/plugin/AnalyzerPlugin.cpp @@ -0,0 +1,43 @@ +/* + * Copyright 2017 Malte Veerman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#include "BlockAnalyzer.h" + +#include + + +class AnalyzerPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char* uri) Q_DECL_OVERRIDE + { + Q_ASSERT(uri == QLatin1String("org.kde.amarok.analyzer")); + + qmlRegisterUncreatableType(uri, 1, 0, "Analyzer", QStringLiteral("Analyzer is an uncreatable type. Use its derived classes instead")); + qmlRegisterType(uri, 1, 0, "BlockAnalyzer"); + } +}; + +#include diff --git a/src/context/applets/analyzer/plugin/AnalyzerWorker.cpp b/src/context/applets/analyzer/plugin/AnalyzerWorker.cpp new file mode 100644 index 0000000000..e323dbc450 --- /dev/null +++ b/src/context/applets/analyzer/plugin/AnalyzerWorker.cpp @@ -0,0 +1,357 @@ +/* + * Copyright 2018 Malte Veerman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "AnalyzerWorker.h" + +#include "core/support/Debug.h" +#include "EngineController.h" + +#include +#include + + +Analyzer::Worker::Worker() + : m_currentScope( QVector( 1, 0.0 ) ) + , m_size( 0 ) + , m_windowFunction( Base::Hann ) + , m_expectedDataTime( 20 ) + , m_demoT( 201 ) + , m_lastUpdate( QTime::currentTime() ) + , m_demoTimer( new QTimer( this ) ) + , m_processTimer( new QTimer( this ) ) +{ + m_in = (double*) fftw_malloc( m_size * sizeof( double ) ); + m_out = (std::complex*) fftw_malloc( ( m_size / 2 + 1 ) * sizeof( std::complex ) ); + m_plan = fftw_plan_dft_r2c_1d( m_size, m_in, reinterpret_cast( m_out ), FFTW_ESTIMATE ); + + m_demoTimer->setInterval( Analyzer::Base::DEMO_INTERVAL ); + m_processTimer->setInterval( PROCESSING_INTERVAL ); + if( EngineController::instance()->isPlaying() ) + m_processTimer->start(); + else + m_demoTimer->start(); + + connect( m_demoTimer, &QTimer::timeout, this, &Worker::demo ); + connect( m_processTimer, &QTimer::timeout, this, &Worker::processData ); +} + +Analyzer::Worker::~Worker() +{ + fftw_destroy_plan( m_plan ); + fftw_free( m_in ); + fftw_free( m_out ); +} + +void Analyzer::Worker::receiveData( const QMap > &newData ) +{ + const int newDataSize = EngineController::DATAOUTPUT_DATA_SIZE; + + if( newData.isEmpty() || newData[Phonon::AudioDataOutput::LeftChannel].size() != newDataSize ) + return; + + if( m_size < newDataSize ) + { + debug() << "Data size mismatch in analyzer!"; + return; + } + + m_rawInMutex.lock(); + + for( int x = 0; x < newDataSize; x++ ) + { + if( newData.size() == 1 ) // Mono + { + m_rawIn << double( newData[Phonon::AudioDataOutput::LeftChannel][x] ); + } + else // Anything > Mono is treated as Stereo + { + m_rawIn << ( double( newData[Phonon::AudioDataOutput::LeftChannel][x] ) + + double( newData[Phonon::AudioDataOutput::RightChannel][x] ) ) + / 2; // Average between the channels + } + m_rawIn.last() /= ( 1 << 15 ); // Scale to [0, 1] + } + + while( m_rawIn.size() > (int)m_size + DATA_BUFFER_SIZE * newDataSize ) + m_rawIn.removeFirst(); + + m_rawInMutex.unlock(); +} + +void Analyzer::Worker::processData() +{ + int timeElapsed = m_lastUpdate.elapsed(); + + // Delay if processing is too fast + if( timeElapsed < m_expectedDataTime ) + QThread::currentThread()->msleep( m_expectedDataTime - timeElapsed ); + + applyWindowFunction(); +} + +void Analyzer::Worker::applyWindowFunction() +{ + m_rawInMutex.lock(); + + if( m_rawIn.size() < (int)m_size ) + { + m_rawInMutex.unlock(); + return; + } + + const int newDataSize = EngineController::DATAOUTPUT_DATA_SIZE; + + // Apply window function + for( uint i = 0; i < m_size; i++ ) + { + double windowFactor; + switch( m_windowFunction ) + { + case Base::Rectangular: + { + windowFactor = 1.0; + break; + } + case Base::Hann: + { + windowFactor = ( 1.0 - cos( 2.0 * M_PI * i / ( m_size - 1 ) ) ) / 2.0; + break; + } + case Base::Nuttall: + { + const double a = 0.355768; + const double b = 0.487396 * cos( 2 * M_PI * i / ( m_size - 1 ) ); + const double c = 0.144232 * cos( 4 * M_PI * i / ( m_size - 1 ) ); + const double d = 0.012604 * cos( 6 * M_PI * i / ( m_size - 1 ) ); + windowFactor = a - b + c - d; + break; + } + case Base::Lanczos: + { + const double x = 2.0 * i / ( m_size - 1 ) - 1; + windowFactor = sin( M_PI * x ) / M_PI / x; + break; + } + case Base::Sine: + { + windowFactor = ( M_PI * i ) / ( m_size - 1 ); + break; + } + }; + + if( i < newDataSize ) + m_in[i] = m_rawIn.takeFirst() * windowFactor; + else + m_in[i] = m_rawIn.at( i - newDataSize ) * windowFactor; + } + + m_rawInMutex.unlock(); + + fftw_execute( m_plan ); + makeScope(); +} + +void Analyzer::Worker::makeScope() +{ + for( const auto& band : m_notInterpolatedScopeBands ) + { + m_currentScope[band.scopeIndex] = 0.0; + uint numValues = 0; + for( long k = std::lround( std::ceil( band.lowerK ) ); k <= std::lround( std::floor( band.upperK ) ); k++ ) + { + m_currentScope[band.scopeIndex] += std::abs( m_out[k] ) * sqrt( k ); + numValues++; + } + m_currentScope[band.scopeIndex] /= numValues; + m_currentScope[band.scopeIndex] /= m_size / 2; + } + + // monotone cubic interpolation + QVector data; + for( uint k = 0; k < m_size / 2 + 1 && k <= m_interpolatedScopeBands.last().midK; k++ ) + { + data << QPointF( k, std::abs( m_out[k] ) * sqrt( k ) / m_size * 2 ); + } + // Get consecutive differences and slopes + QVector dys, dxs, ms; + for( int i = 0; i < data.size() - 1; i++ ) + { + double dx = data[i + 1].x() - data[i].x(); + double dy = data[i + 1].y() - data[i].y(); + dxs << dx; + dys << dy; + ms << dy / dx; + } + // Get degree-1 coefficients + QVector c1s = QVector() << ms[0]; + for( int i = 0; i < dxs.size() - 1; i++) + { + double m = ms[i], mNext = ms[i + 1]; + if( m * mNext <= 0 ) + c1s << 0.0; + else + { + double dx_ = dxs[i], dxNext = dxs[i + 1], common = dx_ + dxNext; + c1s << ( 3 * common / ( ( common + dxNext ) / m + ( common + dx_ ) / mNext ) ); + } + } + c1s << ms.last(); + // Get degree-2 and degree-3 coefficients + QVector c2s, c3s; + for( int i = 0; i < c1s.size() - 1; i++ ) + { + double c1 = c1s[i], m_ = ms[i], invDx = 1 / dxs[i], common_ = c1 + c1s[i + 1] - m_ - m_; + c2s << ( m_ - c1 - common_ ) * invDx; + c3s << common_ * invDx * invDx; + } + // write interpolated data to scope + for( auto &band : m_interpolatedScopeBands ) + { + const double x = band.midK; + auto &scope = m_currentScope[band.scopeIndex]; + + // Search for the interval x is in, returning the corresponding y if x is one of the original xs + int low = 0, mid, high = c3s.size() - 1; + while ( low <= high ) + { + mid = std::floor( 0.5 * ( low + high ) ); + double xHere = data[mid].x(); + if( xHere < x ) + low = mid + 1; + else if( xHere > x ) + high = mid - 1; + else + scope = data[mid].y(); + } + int i = qMax( 0, high ); + + // Interpolate + double diff = x - data[i].x(), diffSq = diff * diff; + scope = qMax( 0.0, data[i].y() + c1s[i] * diff + c2s[i] * diffSq + c3s[i] * diff * diffSq ); + } + + analyze(); +} + +void Analyzer::Worker::setSampleSize( uint size ) +{ + if( m_size == size ) + return; + + m_size = size; + + fftw_destroy_plan( m_plan ); + fftw_free( m_in ); + fftw_free( m_out ); + + m_in = (double*) fftw_malloc( m_size * sizeof( double ) ); + m_out = (std::complex*) fftw_malloc( ( m_size / 2 + 1 ) * sizeof( std::complex ) ); + m_plan = fftw_plan_dft_r2c_1d( m_size, m_in, reinterpret_cast( m_out ), FFTW_ESTIMATE ); +} + +void Analyzer::Worker::setWindowFunction( Base::WindowFunction windowFunction ) +{ + if( m_windowFunction == windowFunction ) + return; + + m_windowFunction = windowFunction; +} + +void Analyzer::Worker::setScopeSize( int size ) +{ + m_currentScope.resize( size ); +} + +void Analyzer::Worker::calculateExpFactor( qreal minFreq, qreal maxFreq, int sampleRate ) +{ + DEBUG_BLOCK + + if( minFreq >= maxFreq ) + { + warning() << "Minimum frequency must be smaller than maximum frequency!"; + return; + } + + m_expFactor = pow( maxFreq / minFreq, 1.0 / m_currentScope.size() ); + m_expectedDataTime = std::floor( (qreal)EngineController::DATAOUTPUT_DATA_SIZE * 1000.0 / sampleRate ); + + m_interpolatedScopeBands.clear(); + m_notInterpolatedScopeBands.clear(); + const uint outputSize = m_size / 2 + 1; + + for( int scopeIndex = 0; scopeIndex < m_currentScope.size(); scopeIndex++ ) + { + BandInfo newBandInfo; + newBandInfo.lowerFreq = minFreq * pow( m_expFactor, double( scopeIndex ) - 0.5 ); + newBandInfo.midFreq = minFreq * pow( m_expFactor, scopeIndex ); + newBandInfo.upperFreq = minFreq * pow( m_expFactor, double( scopeIndex ) + 0.5 ); + newBandInfo.lowerK = newBandInfo.lowerFreq / ( sampleRate / 2 ) * outputSize; + newBandInfo.midK = newBandInfo.midFreq / ( sampleRate / 2 ) * outputSize; + newBandInfo.upperK = newBandInfo.upperFreq / ( sampleRate / 2 ) * outputSize; + newBandInfo.scopeIndex = scopeIndex; + + if ( std::floor( newBandInfo.upperK ) >= std::ceil( newBandInfo.lowerK ) ) + m_notInterpolatedScopeBands << newBandInfo; + else + m_interpolatedScopeBands << newBandInfo; + } +} + +void Analyzer::Worker::demo() +{ + if( m_demoT > 300 ) + m_demoT = 1; //0 = wasted calculations + + if( m_demoT < 201 ) + { + const double dt = double( m_demoT ) / 200; + for( int i = 0; i < m_currentScope.size(); ++i ) + { + m_currentScope[i] = dt * ( sin( M_PI + ( i * M_PI ) / m_currentScope.size() ) + 1.0 ); + } + } + else + { + for( int i = 0; i < m_currentScope.size(); ++i ) + { + m_currentScope[i] = 0.0; + } + } + + ++m_demoT; + + int timeElapsed = m_lastUpdate.elapsed(); + + // Delay if interval is too low + if( timeElapsed < Analyzer::Base::DEMO_INTERVAL - 1 ) + QThread::currentThread()->msleep( Analyzer::Base::DEMO_INTERVAL - 1 - timeElapsed ); + + m_lastUpdate.restart(); + + analyze(); +} + +void Analyzer::Worker::playbackStateChanged() +{ + bool playing = EngineController::instance()->isPlaying(); + playing ? m_demoTimer->stop() : m_demoTimer->start(); + playing ? m_processTimer->start() : m_processTimer->stop(); + resetDemo(); +} diff --git a/src/context/applets/analyzer/plugin/AnalyzerWorker.h b/src/context/applets/analyzer/plugin/AnalyzerWorker.h new file mode 100644 index 0000000000..b96b9ead0e --- /dev/null +++ b/src/context/applets/analyzer/plugin/AnalyzerWorker.h @@ -0,0 +1,124 @@ +/* + * Copyright 2018 Malte Veerman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ANALYZERWORKER_H +#define ANALYZERWORKER_H + +#include "AnalyzerBase.h" + +#include + +#include +#include +#include + +#include +#include + + +class QTimer; + +namespace Analyzer +{ + +/** + * Base worker class for all analyzers + * All compute heavy tasks should be offloaded to this. + */ +class Worker : public QObject +{ + friend class Base; + + Q_OBJECT + +public: + const static int PROCESSING_INTERVAL = 5; // Interval between new data lookups + const static int DATA_BUFFER_SIZE = 7; // Higher values increase latency, lower values increase risk of missing frames + + Worker(); + ~Worker(); + +protected: + /** + * @return The current scope data. + */ + const QVector& scope() const { return m_currentScope; } + + /** + * This function is being called after new scope data is ready. + * Get the scope to be analyzed by calling scope(). + * Subclasses must implement this function. + */ + virtual void analyze() = 0; + +private: + struct BandInfo + { + double lowerFreq; + double midFreq; + double upperFreq; + double lowerK; + double midK; + double upperK; + int scopeIndex; + }; + + /** + * This function is thread-safe. + */ + void receiveData( const QMap > &newData ); + + // None of the following functions are thread-safe. Only connect with queued connections to them. + void processData(); + void applyWindowFunction(); + void makeScope(); + void setSampleSize( uint size ); + void setWindowFunction( Base::WindowFunction windowFunction ); + void setScopeSize( int size ); + void calculateExpFactor( qreal minFreq, qreal maxFreq, int sampleRate ); + void resetDemo() { m_demoT = 201; } + void playbackStateChanged(); + + /** + * Override this function for your custom idle animation. + */ + virtual void demo(); + + fftw_plan m_plan; + mutable QMutex m_rawInMutex; + QList m_rawIn; + double *m_in; + std::complex *m_out; + QVector m_currentScope; + QVector m_interpolatedScopeBands; + QVector m_notInterpolatedScopeBands; + uint m_size; + double m_expFactor; + Base::WindowFunction m_windowFunction; + int m_expectedDataTime; + int m_demoT; + QTime m_lastUpdate; + QTimer *m_demoTimer; + QTimer *m_processTimer; +}; + +} + +#endif // ANALYZERWORKER_H diff --git a/src/context/applets/analyzer/BallsAnalyzer.cpp b/src/context/applets/analyzer/plugin/BallsAnalyzer.cpp similarity index 97% rename from src/context/applets/analyzer/BallsAnalyzer.cpp rename to src/context/applets/analyzer/plugin/BallsAnalyzer.cpp index 6b4512f45e..b825104780 100644 --- a/src/context/applets/analyzer/BallsAnalyzer.cpp +++ b/src/context/applets/analyzer/plugin/BallsAnalyzer.cpp @@ -1,447 +1,447 @@ /**************************************************************************************** * Copyright (c) 2004 Enrico Ros * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "BallsAnalyzer.h" -#include +#include #include #include #include #include inline float myfabsf( float f ) { return f < 0.f ? -f : f; } class Ball { public: Ball() : x( drand48() - drand48() ), y( 1 - 2.0 * drand48() ), z( drand48() ), vx( 0.0 ), vy( 0.0 ), vz( 0.0 ), mass( 0.01 + drand48() / 10.0 ) //,color( (float[3]) { 0.0, drand48()*0.5, 0.7 + drand48() * 0.3 } ) { //this is because GCC < 3.3 can't compile the above line, we aren't sure why though color[0] = 0.0; color[1] = drand48() * 0.5; color[2] = 0.7 + drand48() * 0.3; }; float x, y, z, vx, vy, vz, mass; float color[3]; void updatePhysics( float dT ) { x += vx * dT; // position y += vy * dT; // position z += vz * dT; // position if( y < -0.8 ) vy = myfabsf( vy ); if( y > 0.8 ) vy = -myfabsf( vy ); if( z < 0.1 ) vz = myfabsf( vz ); if( z > 0.9 ) vz = -myfabsf( vz ); vx += ( ( x > 0 ) ? 4.94 : -4.94 ) * dT; // G-force vx *= ( 1 - 2.9 * dT ); // air friction vy *= ( 1 - 2.9 * dT ); // air friction vz *= ( 1 - 2.9 * dT ); // air friction } }; class Paddle { public: Paddle( float xPos ) : onLeft( xPos < 0 ), mass( 1.0 ), X( xPos ), x( xPos ), vx( 0.0 ) {}; void updatePhysics( float dT ) { x += vx * dT; // posision vx += ( 1300 * ( X - x ) / mass ) * dT; // elasticity vx *= ( 1 - 4.0 * dT ); // air friction } void renderGL() { glBegin( GL_TRIANGLE_STRIP ); glColor3f( 0.0f, 0.1f, 0.3f ); glVertex3f( x, -1.0f, 0.0 ); glVertex3f( x, 1.0f, 0.0 ); glColor3f( 0.1f, 0.2f, 0.6f ); glVertex3f( x, -1.0f, 1.0 ); glVertex3f( x, 1.0f, 1.0 ); glEnd(); } void bounce( Ball * ball ) { if( onLeft && ball->x < x ) { ball->vx = vx * mass / ( mass + ball->mass ) + myfabsf( ball->vx ); ball->vy = ( drand48() - drand48() ) * 1.8; ball->vz = ( drand48() - drand48() ) * 0.9; ball->x = x; } else if( !onLeft && ball->x > x ) { ball->vx = vx * mass / ( mass + ball->mass ) - myfabsf( ball->vx ); ball->vy = ( drand48() - drand48() ) * 1.8; ball->vz = ( drand48() - drand48() ) * 0.9; ball->x = x; } } void impulse( float strength ) { if( ( onLeft && strength > vx ) || ( !onLeft && strength < vx ) ) vx += strength; } private: bool onLeft; float mass, X, x, vx; }; BallsAnalyzer::BallsAnalyzer( QWidget *parent ): Analyzer::Base( parent ) { setObjectName( "Balls" ); - m_ballTexture = bindTexture( QImage( KStandardDirs::locate( "data", "amarok/images/ball.png" ) ) ); - m_gridTexture = bindTexture( QImage( KStandardDirs::locate( "data", "amarok/images/grid.png" ) ) ); + m_ballTexture = bindTexture( QImage( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/ball.png" ) ) ); + m_gridTexture = bindTexture( QImage( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/grid.png" ) ) ); m_leftPaddle = new Paddle( -1.0 ); m_rightPaddle = new Paddle( 1.0 ); for( int i = 0; i < NUMBER_OF_BALLS; i++ ) m_balls.append( new Ball() ); m_show.colorK = 0.0; m_show.gridScrollK = 0.0; m_show.gridEnergyK = 0.0; m_show.camRot = 0.0; m_show.camRoll = 0.0; m_show.peakEnergy = 1.0; m_frame.silence = true; m_frame.energy = 0.0; m_frame.dEnergy = 0.0; } BallsAnalyzer::~BallsAnalyzer() { deleteTexture( m_ballTexture ); deleteTexture( m_gridTexture ); delete m_leftPaddle; delete m_rightPaddle; qDeleteAll( m_balls ); } void BallsAnalyzer::initializeGL() { // Set a smooth shade model glShadeModel( GL_SMOOTH ); // Disable depth test (all is drawn 'z-sorted') glDisable( GL_DEPTH_TEST ); // Set blending function (Alpha addition) glBlendFunc( GL_SRC_ALPHA, GL_ONE ); // Clear frame with a black background glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); } void BallsAnalyzer::resizeGL( int w, int h ) { // Setup screen. We're going to manually do the perspective projection glViewport( 0, 0, ( GLint )w, ( GLint )h ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glFrustum( -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 4.5f ); // Get the aspect ratio of the screen to draw 'circular' particles float ratio = ( float )w / ( float )h; if( ratio >= 1.0 ) { m_unitX = 0.34 / ratio; m_unitY = 0.34; } else { m_unitX = 0.34; m_unitY = 0.34 * ratio; } // Get current timestamp. timeval tv; gettimeofday( &tv, NULL ); m_show.timeStamp = ( double )tv.tv_sec + ( double )tv.tv_usec / 1000000.0; } void BallsAnalyzer::analyze( const QVector &s ) { // compute the dTime since the last call timeval tv; gettimeofday( &tv, NULL ); double currentTime = ( double )tv.tv_sec + ( double )tv.tv_usec / 1000000.0; m_show.dT = currentTime - m_show.timeStamp; m_show.timeStamp = currentTime; // compute energy integrating frame's spectrum if( !s.empty() ) { int bands = s.size(); float currentEnergy = 0, maxValue = 0; // integrate spectrum -> energy for( int i = 0; i < bands; i++ ) { float value = s[i]; currentEnergy += value; if( value > maxValue ) maxValue = value; } currentEnergy *= 100.0 / ( float )bands; // emulate a peak detector: currentEnergy -> peakEnergy (3tau = 30 seconds) m_show.peakEnergy = 1.0 + ( m_show.peakEnergy - 1.0 ) * exp( - m_show.dT / 10.0 ); if( currentEnergy > m_show.peakEnergy ) m_show.peakEnergy = currentEnergy; // check for silence m_frame.silence = currentEnergy < 0.001; // normalize frame energy against peak energy and compute frame stats currentEnergy /= m_show.peakEnergy; m_frame.dEnergy = currentEnergy - m_frame.energy; m_frame.energy = currentEnergy; } else m_frame.silence = true; // limit max dT to 0.05 and update color and scroll constants if( m_show.dT > 0.05 ) m_show.dT = 0.05; m_show.colorK += m_show.dT * 0.4; if( m_show.colorK > 3.0 ) m_show.colorK -= 3.0; m_show.gridScrollK += 0.2 * m_show.peakEnergy * m_show.dT; // Roll camera up/down handling the beat m_show.camRot += m_show.camRoll * m_show.dT; // posision m_show.camRoll -= 400 * m_show.camRot * m_show.dT; // elasticity m_show.camRoll *= ( 1 - 2.0 * m_show.dT ); // friction if( !m_frame.silence && m_frame.dEnergy > 0.4 ) m_show.camRoll += m_show.peakEnergy * 2.0; if( ( m_show.gridEnergyK > 0.05 ) || ( !m_frame.silence && m_frame.dEnergy < -0.3 ) ) { m_show.gridEnergyK *= exp( -m_show.dT / 0.1 ); if( -m_frame.dEnergy > m_show.gridEnergyK ) m_show.gridEnergyK = -m_frame.dEnergy * 2.0; } foreach( Ball * ball, m_balls ) { ball->updatePhysics( m_show.dT ); if( ball->x < 0 ) m_leftPaddle->bounce( ball ); else m_rightPaddle->bounce( ball ); } // Update physics of paddles m_leftPaddle->updatePhysics( m_show.dT ); m_rightPaddle->updatePhysics( m_show.dT ); if( !m_frame.silence ) { m_leftPaddle->impulse( m_frame.energy * 3.0 + m_frame.dEnergy * 6.0 ); m_rightPaddle->impulse( -m_frame.energy * 3.0 - m_frame.dEnergy * 6.0 ); } } void BallsAnalyzer::paintGL() { // Switch to MODEL matrix and clear screen glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glClear( GL_COLOR_BUFFER_BIT ); // Draw scrolling grid float gridColor[4] = { 0.0, 1.0, 0.6, m_show.gridEnergyK }; drawScrollGrid( m_show.gridScrollK, gridColor ); glRotatef( m_show.camRoll / 2.0, 1, 0, 0 ); // Translate the drawing plane glTranslatef( 0.0f, 0.0f, -1.8f ); // Draw upper/lower planes and paddles drawHFace( -1.0 ); drawHFace( 1.0 ); m_leftPaddle->renderGL(); m_rightPaddle->renderGL(); // Draw Balls if( m_ballTexture ) { glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, m_ballTexture ); } else glDisable( GL_TEXTURE_2D ); glEnable( GL_BLEND ); foreach( Ball * ball, m_balls ) { float color[3], angle = m_show.colorK; // Rotate the color based on 'angle' value [0,3) if( angle < 1.0 ) { color[ 0 ] = ball->color[ 0 ] * ( 1 - angle ) + ball->color[ 1 ] * angle; color[ 1 ] = ball->color[ 1 ] * ( 1 - angle ) + ball->color[ 2 ] * angle; color[ 2 ] = ball->color[ 2 ] * ( 1 - angle ) + ball->color[ 0 ] * angle; } else if( angle < 2.0 ) { angle -= 1.0; color[ 0 ] = ball->color[ 1 ] * ( 1 - angle ) + ball->color[ 2 ] * angle; color[ 1 ] = ball->color[ 2 ] * ( 1 - angle ) + ball->color[ 0 ] * angle; color[ 2 ] = ball->color[ 0 ] * ( 1 - angle ) + ball->color[ 1 ] * angle; } else { angle -= 2.0; color[ 0 ] = ball->color[ 2 ] * ( 1 - angle ) + ball->color[ 0 ] * angle; color[ 1 ] = ball->color[ 0 ] * ( 1 - angle ) + ball->color[ 1 ] * angle; color[ 2 ] = ball->color[ 1 ] * ( 1 - angle ) + ball->color[ 2 ] * angle; } // Draw the dot and update its physics also checking at bounces glColor3fv( color ); drawDot3s( ball->x, ball->y, ball->z, 1.0 ); } glDisable( GL_BLEND ); glDisable( GL_TEXTURE_2D ); } void BallsAnalyzer::drawDot3s( float x, float y, float z, float size ) { // Circular XY dot drawing functions float sizeX = size * m_unitX, sizeY = size * m_unitY, pXm = x - sizeX, pXM = x + sizeX, pYm = y - sizeY, pYM = y + sizeY; // Draw the Dot glBegin( GL_QUADS ); glTexCoord2f( 0, 0 ); // Bottom Left glVertex3f( pXm, pYm, z ); glTexCoord2f( 0, 1 ); // Top Left glVertex3f( pXm, pYM, z ); glTexCoord2f( 1, 1 ); // Top Right glVertex3f( pXM, pYM, z ); glTexCoord2f( 1, 0 ); // Bottom Right glVertex3f( pXM, pYm, z ); glEnd(); // Shadow XZ drawing functions float sizeZ = size / 10.0, pZm = z - sizeZ, pZM = z + sizeZ, currentColor[4]; glGetFloatv( GL_CURRENT_COLOR, currentColor ); float alpha = currentColor[3], topSide = ( y + 1 ) / 4, bottomSide = ( 1 - y ) / 4; // Draw the top shadow currentColor[3] = topSide * topSide * alpha; glColor4fv( currentColor ); glBegin( GL_QUADS ); glTexCoord2f( 0, 0 ); // Bottom Left glVertex3f( pXm, 1, pZm ); glTexCoord2f( 0, 1 ); // Top Left glVertex3f( pXm, 1, pZM ); glTexCoord2f( 1, 1 ); // Top Right glVertex3f( pXM, 1, pZM ); glTexCoord2f( 1, 0 ); // Bottom Right glVertex3f( pXM, 1, pZm ); glEnd(); // Draw the bottom shadow currentColor[3] = bottomSide * bottomSide * alpha; glColor4fv( currentColor ); glBegin( GL_QUADS ); glTexCoord2f( 0, 0 ); // Bottom Left glVertex3f( pXm, -1, pZm ); glTexCoord2f( 0, 1 ); // Top Left glVertex3f( pXm, -1, pZM ); glTexCoord2f( 1, 1 ); // Top Right glVertex3f( pXM, -1, pZM ); glTexCoord2f( 1, 0 ); // Bottom Right glVertex3f( pXM, -1, pZm ); glEnd(); } void BallsAnalyzer::drawHFace( float y ) { glBegin( GL_TRIANGLE_STRIP ); glColor3f( 0.0f, 0.1f, 0.2f ); glVertex3f( -1.0f, y, 0.0 ); glVertex3f( 1.0f, y, 0.0 ); glColor3f( 0.1f, 0.6f, 0.5f ); glVertex3f( -1.0f, y, 2.0 ); glVertex3f( 1.0f, y, 2.0 ); glEnd(); } void BallsAnalyzer::drawScrollGrid( float scroll, float color[4] ) { if( !m_gridTexture ) return; glMatrixMode( GL_TEXTURE ); glLoadIdentity(); glTranslatef( 0.0, -scroll, 0.0 ); glMatrixMode( GL_MODELVIEW ); float backColor[4] = { 1.0, 1.0, 1.0, 0.0 }; for( int i = 0; i < 3; i++ ) backColor[ i ] = color[ i ]; glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, m_gridTexture ); glEnable( GL_BLEND ); glBegin( GL_TRIANGLE_STRIP ); glColor4fv( color ); // top face glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -1.0f, 1.0f, -1.0f ); glTexCoord2f( 1.0f, 1.0f ); glVertex3f( 1.0f, 1.0f, -1.0f ); glColor4fv( backColor ); // central points glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -1.0f, 0.0f, -3.0f ); glTexCoord2f( 1.0f, 0.0f ); glVertex3f( 1.0f, 0.0f, -3.0f ); glColor4fv( color ); // bottom face glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -1.0f, -1.0f, -1.0f ); glTexCoord2f( 1.0f, 1.0f ); glVertex3f( 1.0f, -1.0f, -1.0f ); glEnd(); glDisable( GL_BLEND ); glDisable( GL_TEXTURE_2D ); glMatrixMode( GL_TEXTURE ); glLoadIdentity(); glMatrixMode( GL_MODELVIEW ); } diff --git a/src/context/applets/analyzer/BallsAnalyzer.h b/src/context/applets/analyzer/plugin/BallsAnalyzer.h similarity index 100% rename from src/context/applets/analyzer/BallsAnalyzer.h rename to src/context/applets/analyzer/plugin/BallsAnalyzer.h diff --git a/src/context/applets/analyzer/plugin/BlockAnalyzer.cpp b/src/context/applets/analyzer/plugin/BlockAnalyzer.cpp new file mode 100644 index 0000000000..aaf3732f1c --- /dev/null +++ b/src/context/applets/analyzer/plugin/BlockAnalyzer.cpp @@ -0,0 +1,236 @@ +/**************************************************************************************** + * Copyright (c) 2003-2005 Max Howell * + * Copyright (c) 2005-2013 Mark Kretschmann * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Pulic License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +#include "BlockAnalyzer.h" + +#include "AnalyzerWorker.h" +#include "BlockRenderer.h" +#include "BlockWorker.h" + +#include "PaletteHandler.h" + +#include + +#include +#include +#include +#include + + +BlockAnalyzer::BlockAnalyzer( QQuickItem *parent ) + : Analyzer::Base( parent ) + , m_columns( 0 ) //int + , m_rows( 0 ) //int + , m_fadeBarsPixmaps( FADE_SIZE ) //vector +{ + setTextureFollowsItemSize( true ); + setObjectName( "Blocky" ); + + m_columnWidth = config().readEntry( "columnWidth", 4 ); + m_fallSpeed = (FallSpeed) config().readEntry( "fallSpeed", (int) Medium ); + m_showFadebars = config().readEntry( "showFadebars", true ); + + paletteChange( The::paletteHandler()->palette() ); + connect( The::paletteHandler(), &PaletteHandler::newPalette, this, &BlockAnalyzer::paletteChange ); + connect( this, &QQuickItem::windowChanged, this, &BlockAnalyzer::newWindow ); +} + +QQuickFramebufferObject::Renderer* +BlockAnalyzer::createRenderer() const +{ + return new BlockRenderer; +} + +Analyzer::Worker * BlockAnalyzer::createWorker() const +{ + auto worker = new BlockWorker( m_rows, m_columns, m_step, m_showFadebars ); + if( window() ) + worker->setRefreshRate( window()->screen()->refreshRate() ); + connect( worker, &BlockWorker::finished, this, &QQuickFramebufferObject::update, Qt::QueuedConnection ); + connect( this, &BlockAnalyzer::stepChanged, worker, &BlockWorker::setStep, Qt::QueuedConnection ); + connect( this, &BlockAnalyzer::rowsChanged, worker, &BlockWorker::setRows, Qt::QueuedConnection ); + connect( this, &BlockAnalyzer::columnsChanged, worker, &BlockWorker::setColumns, Qt::QueuedConnection ); + connect( this, &BlockAnalyzer::refreshRateChanged, worker, &BlockWorker::setRefreshRate, Qt::QueuedConnection ); + connect( this, &BlockAnalyzer::showFadebarsChanged, worker, &BlockWorker::setShowFadebars, Qt::QueuedConnection ); + return worker; +} + +void +BlockAnalyzer::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + QQuickFramebufferObject::geometryChanged( newGeometry, oldGeometry ); + + if( !newGeometry.isValid() ) + return; + + const int oldRows = m_rows; + + // Rounded up so that the last column/line is covered if partially visible + m_columns = std::lround( std::ceil( (double)newGeometry.width() / ( m_columnWidth + 1 ) ) ); + emit columnsChanged( m_columns ); + m_rows = std::ceil( (double)newGeometry.height() / ( BLOCK_HEIGHT + 1 ) ); + emit rowsChanged( m_rows ); + + setScopeSize( m_columns ); + + if( m_rows != oldRows ) + { + m_barPixmap = QPixmap( m_columnWidth, m_rows * ( BLOCK_HEIGHT + 1 ) ); + + determineStep(); + paletteChange( The::paletteHandler()->palette() ); + } + else + drawBackground( The::paletteHandler()->palette() ); +} + +void +BlockAnalyzer::determineStep() +{ + // falltime is dependent on rowcount due to our digital resolution (ie we have boxes/blocks of pixels) + + const qreal fallTime = 1.0 / pow( 1.5, m_fallSpeed ); // time to fall from top to bottom + m_step = qreal( m_rows ) / fallTime; // the amount of rows to fall per second + emit stepChanged( m_step ); +} + +void +BlockAnalyzer::paletteChange( const QPalette& palette ) //virtual +{ + const QColor bg = palette.color( QPalette::Active, QPalette::Base ); + const QColor abg = palette.color( QPalette::Active, QPalette::AlternateBase ); + const QColor highlight = palette.color( QPalette::Active, QPalette::Highlight ); + + m_topBarPixmap = QPixmap( m_columnWidth, BLOCK_HEIGHT ); + m_topBarPixmap.fill( highlight ); + + m_barPixmap.fill( QColor( ( highlight.red() + bg.red() ) / 2, ( highlight.green() + bg.green() ) / 2, ( highlight.blue() + bg.blue() ) / 2 ) ); + + QPainter p( &m_barPixmap ); + + int h, s, v; + palette.color( QPalette::Active, QPalette::Dark ).getHsv( &h, &s, &v ); + const QColor fade = QColor::fromHsv( h + 30, s, v ); + + const double dr = fade.red() - abg.red(); + const double dg = fade.green() - abg.green(); + const double db = fade.blue() - abg.blue(); + const int r = abg.red(), g = abg.green(), b = abg.blue(); + + // Precalculate all fade-bar pixmaps + for( int y = 0; y < FADE_SIZE; ++y ) + { + m_fadeBarsPixmaps[y] = QPixmap( m_columnWidth, m_rows * ( BLOCK_HEIGHT + 1 ) ); + + m_fadeBarsPixmaps[y].fill( palette.color( QPalette::Active, QPalette::Base ) ); + const double Y = 1.0 - ( log10( ( FADE_SIZE ) - y ) / log10( ( FADE_SIZE ) ) ); + QPainter f( &m_fadeBarsPixmaps[y] ); + for( int z = 0; z < m_rows; ++z ) + f.fillRect( 0, + z * ( BLOCK_HEIGHT + 1 ), + m_columnWidth, BLOCK_HEIGHT, + QColor( r + int( dr * Y ), g + int( dg * Y ), b + int( db * Y ) ) ); + } + + m_pixmapsChanged = true; + drawBackground( palette ); +} + +void +BlockAnalyzer::drawBackground( const QPalette &palette ) +{ + const QColor bg = palette.color( QPalette::Active, QPalette::Base ); + const QColor abg = palette.color( QPalette::Active, QPalette::AlternateBase ); + + // background gets stretched if it is too big + m_backgroundPixmap = QPixmap( width(), height() ); + m_backgroundPixmap.fill( bg ); + + QPainter p( &m_backgroundPixmap ); + for( int x = 0; x < m_columns; ++x ) + for( int y = 0; y < m_rows; ++y ) + p.fillRect( x * ( m_columnWidth + 1 ), y * ( BLOCK_HEIGHT + 1 ), m_columnWidth, BLOCK_HEIGHT, abg ); + + m_pixmapsChanged = true; + + update(); +} + +void +BlockAnalyzer::setFallSpeed( FallSpeed fallSpeed ) +{ + DEBUG_BLOCK + + debug() << "Fall speed set to:" << fallSpeed; + + if( m_fallSpeed == fallSpeed ) + return; + + m_fallSpeed = fallSpeed; + config().writeEntry( "fallSpeed", (int) m_fallSpeed ); + emit fallSpeedChanged(); + + determineStep(); +} + +void +BlockAnalyzer::setColumnWidth( int columnWidth ) +{ + DEBUG_BLOCK + + debug() << "Column width set to:" << columnWidth; + + if( columnWidth < 1 ) + { + warning() << "Column width can not be smaller than one!"; + columnWidth = 1; + } + + if( m_columnWidth == columnWidth ) + return; + + m_columnWidth = columnWidth; + config().writeEntry( "columnWidth", m_columnWidth ); + emit columnWidthChanged(); + + m_columns = std::lround( std::ceil( (double)width() / ( m_columnWidth + 1 ) ) ); + emit columnsChanged( m_columns ); + setScopeSize( m_columns ); + m_barPixmap = QPixmap( m_columnWidth, m_rows * ( BLOCK_HEIGHT + 1 ) ); + paletteChange( The::paletteHandler()->palette() ); +} + +void +BlockAnalyzer::setShowFadebars( bool showFadebars ) +{ + DEBUG_BLOCK + + debug() << "Show fadebars:" << showFadebars; + + if( m_showFadebars == showFadebars ) + return; + + m_showFadebars = showFadebars; + emit showFadebarsChanged( m_showFadebars ); +} + +void +BlockAnalyzer::newWindow( QQuickWindow* window ) +{ + if( window ) + emit refreshRateChanged( window->screen()->refreshRate() ); +} diff --git a/src/context/applets/analyzer/BlockAnalyzer.h b/src/context/applets/analyzer/plugin/BlockAnalyzer.h similarity index 54% rename from src/context/applets/analyzer/BlockAnalyzer.h rename to src/context/applets/analyzer/plugin/BlockAnalyzer.h index df0506bba2..cc543bc9e1 100644 --- a/src/context/applets/analyzer/BlockAnalyzer.h +++ b/src/context/applets/analyzer/plugin/BlockAnalyzer.h @@ -1,101 +1,100 @@ /**************************************************************************************** * Copyright (c) 2003-2005 Max Howell * * Copyright (c) 2005-2013 Mark Kretschmann * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Pulic License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef BLOCKANALYZER_H #define BLOCKANALYZER_H #include "AnalyzerBase.h" #include #include +#include #include #include -class QMouseEvent; class QPalette; -class QResizeEvent; +class QSGTexture; class BlockAnalyzer : public Analyzer::Base { + friend class BlockRenderer; + + Q_OBJECT + Q_PROPERTY(FallSpeed fallSpeed READ fallSpeed WRITE setFallSpeed NOTIFY fallSpeedChanged) + Q_PROPERTY(int columnWidth READ columnWidth WRITE setColumnWidth NOTIFY columnWidthChanged) + Q_PROPERTY(bool showFadebars READ showFadebars WRITE setShowFadebars NOTIFY showFadebarsChanged) + public: - BlockAnalyzer( QWidget* ); + enum FallSpeed + { + VerySlow = 0, + Slow = 1, + Medium = 2, + Fast = 3, + VeryFast = 4 + }; + Q_ENUM( FallSpeed ) - static GLuint createTexture( const QImage &image ) { return instance->bindTexture( image ); } - static void freeTexture( GLuint id ) { instance->deleteTexture( id ); } + BlockAnalyzer( QQuickItem *parent = Q_NULLPTR ); + + Renderer* createRenderer() const Q_DECL_OVERRIDE; + + FallSpeed fallSpeed() const { return m_fallSpeed; } + void setFallSpeed( FallSpeed fallSpeed ); + int columnWidth() const { return m_columnWidth; } + void setColumnWidth( int columnWidth ); + bool showFadebars() const { return m_showFadebars; } + void setShowFadebars( bool showFadebars ); // Signed ints because most of what we compare them against are ints static const int BLOCK_HEIGHT = 2; - static const int BLOCK_WIDTH = 4; - static const int MIN_ROWS = 30; //arbitrary - static const int MIN_COLUMNS = 32; //arbitrary - static const int MAX_COLUMNS = 256; //must be 2**n static const int FADE_SIZE = 90; +signals: + void fallSpeedChanged(); + void columnWidthChanged(); + void showFadebarsChanged( bool ); + void stepChanged( qreal ); + void rowsChanged( int ); + void columnsChanged( int ); + void refreshRateChanged( qreal ); + protected: - virtual void initializeGL(); - virtual void paintGL(); - virtual void resizeGL( int w, int h ); - virtual void transform( QVector& ); - virtual void analyze( const QVector& ); + virtual void geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) Q_DECL_OVERRIDE; + virtual Analyzer::Worker* createWorker() const Q_DECL_OVERRIDE; virtual void paletteChange( const QPalette& ); - void drawBackground(); + void drawBackground( const QPalette &palette ); void determineStep(); private: - struct Texture - { - Texture( const QPixmap &pixmap ) : - id( BlockAnalyzer::createTexture( pixmap.toImage().mirrored() ) ), // Flip texture vertically for OpenGL bottom-left coordinate system - size( pixmap.size() ) - {} - Texture( const Texture& texture ) - { - id = texture.id; - size = texture.size; - } - ~Texture() - { - BlockAnalyzer::freeTexture( id ); - } - - GLuint id; - QSize size; - }; - - void drawTexture( Texture* texture, int x, int y, int sx, int sy ); - - static BlockAnalyzer* instance; + void newWindow( QQuickWindow *window ); int m_columns, m_rows; //number of rows and columns of blocks + int m_columnWidth; + bool m_showFadebars; QPixmap m_barPixmap; - QVector m_scope; //so we don't create a vector every frame - QVector m_store; //current bar heights - QVector m_yscale; - - QSharedPointer m_barTexture; - QSharedPointer m_topBarTexture; - QSharedPointer m_background; - QVector> m_fade_bars; - - QVector m_fade_pos; - QVector m_fade_intensity; + QPixmap m_topBarPixmap; + QPixmap m_backgroundPixmap; + QVector m_fadeBarsPixmaps; + bool m_pixmapsChanged; - float m_step; //rows to fall per frame + qreal m_step; //rows to fall per frame + FallSpeed m_fallSpeed; }; #endif diff --git a/src/context/applets/analyzer/plugin/BlockRenderer.h b/src/context/applets/analyzer/plugin/BlockRenderer.h new file mode 100644 index 0000000000..5d38c9a057 --- /dev/null +++ b/src/context/applets/analyzer/plugin/BlockRenderer.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2003-2005 Max Howell + * Copyright (c) 2005-2013 Mark Kretschmann + * Copyright 2017 Malte Veerman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef BLOCKRENDERER_H +#define BLOCKRENDERER_H + +#include "BlockAnalyzer.h" +#include "BlockWorker.h" +#include "core/support/Debug.h" + +#include +#include +#include +#include +#include +#include + +class BlockRenderer : public QQuickFramebufferObject::Renderer +{ +public: + static const int BLOCK_HEIGHT = BlockAnalyzer::BLOCK_HEIGHT; + + BlockRenderer() {} + +protected: + QOpenGLFramebufferObject* createFramebufferObject(const QSize &size) Q_DECL_OVERRIDE + { + QOpenGLFramebufferObject* fo = new QOpenGLFramebufferObject(size); + fo->setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + return fo; + } + + void render() Q_DECL_OVERRIDE + { + QOpenGLPaintDevice d; + d.setSize(framebufferObject()->size()); + QPainter p(&d); + + // Draw the background + p.drawPixmap(QRect(QPoint(0, 0), framebufferObject()->size()), m_backgroundPixmap); + + + const int frameHeight = framebufferObject()->height(); + + for( uint x = 0; x < (uint)m_store.size(); ++x ) + { + // Draw fade bars + for( const auto &fadebar : m_fadebars.at(x) ) + { + if( fadebar.intensity > 0 ) + { + const uint offset = fadebar.intensity; + const int fadeHeight = frameHeight - fadebar.y * (BLOCK_HEIGHT + 1); + if( fadeHeight > 0 ) + p.drawPixmap(x * ( m_columnWidth + 1 ), 0, m_fadeBarsPixmaps.value(offset), 0, 0, m_columnWidth, fadeHeight); + } + } + + // Draw bars + const int height = frameHeight - m_store.at(x) * (BLOCK_HEIGHT + 1); + if (height > 0) + p.drawPixmap(x * (m_columnWidth + 1), 0, m_barPixmap, 0, 0, m_columnWidth, height); + + // Draw top bar + p.drawPixmap(x * (m_columnWidth + 1), height + BLOCK_HEIGHT - 1, m_topBarPixmap); + } + } + + void synchronize(QQuickFramebufferObject *item) Q_DECL_OVERRIDE + { + auto analyzer = qobject_cast(item); + if (!analyzer) + return; + + m_rows = analyzer->m_rows; + m_columnWidth = analyzer->m_columnWidth; + + auto worker = qobject_cast(analyzer->worker()); + if (worker) + { + worker->m_mutex.lock(); + m_store = worker->m_store; + m_fadebars = worker->m_fadebars; + worker->m_mutex.unlock(); + } + + if (analyzer->m_pixmapsChanged) + { + m_barPixmap = analyzer->m_barPixmap; + m_topBarPixmap = analyzer->m_topBarPixmap; + m_backgroundPixmap = analyzer->m_backgroundPixmap; + m_fadeBarsPixmaps = analyzer->m_fadeBarsPixmaps; + + analyzer->m_pixmapsChanged = false; + } + } + +private: + QVector m_store; + QVector > m_fadebars; + int m_rows; + int m_columnWidth; + + QPixmap m_barPixmap; + QPixmap m_topBarPixmap; + QPixmap m_backgroundPixmap; + QVector m_fadeBarsPixmaps; +}; + +#endif //BLOCKRENDERER_H diff --git a/src/context/applets/analyzer/plugin/BlockWorker.cpp b/src/context/applets/analyzer/plugin/BlockWorker.cpp new file mode 100644 index 0000000000..2c32d522fb --- /dev/null +++ b/src/context/applets/analyzer/plugin/BlockWorker.cpp @@ -0,0 +1,132 @@ +/* + * Copyright 2017 Malte Veerman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "BlockWorker.h" +#include "BlockAnalyzer.h" + +#include "core/support/Debug.h" + + +BlockWorker::BlockWorker( int rows, int columns, qreal step, bool showFadebars ) + : m_step( step ) + , m_rows( rows ) + , m_columns( columns ) + , m_refreshTime( 16 ) + , m_showFadebars( showFadebars ) +{ + m_yscale.resize( m_rows + 1 ); + const double PRE = 1, PRO = 1; //PRE and PRO allow us to restrict the range somewhat + + for( int z = 0; z < m_rows; ++z ) + m_yscale[z] = 1 - ( log10( PRE + z ) / log10( PRE + m_rows + PRO ) ); + + m_yscale[m_rows] = 0; + + m_store.resize( columns ); + m_fadebars.resize( columns ); + + m_lastUpdate.start(); +} + +void BlockWorker::setRows( int rows ) +{ + if( m_rows == rows ) + return; + + m_mutex.lock(); + m_rows = rows; + m_yscale.resize( m_rows + 1 ); + + const double PRE = 1, PRO = 1; //PRE and PRO allow us to restrict the range somewhat + + for( int z = 0; z < m_rows; ++z ) + m_yscale[z] = 1 - ( log10( PRE + z ) / log10( PRE + m_rows + PRO ) ); + + m_yscale[m_rows] = 0; + m_mutex.unlock(); +} + +void BlockWorker::setColumns(int columns) +{ + if( m_columns == columns ) + return; + + m_columns = columns; +} + +void BlockWorker::analyze() +{ + int timeElapsed = m_lastUpdate.elapsed(); + + // only analyze if screen is fast enough + if( timeElapsed < m_refreshTime ) + QThread::currentThread()->msleep( m_refreshTime - timeElapsed - 1 ); + + const auto scopeData = scope(); + const int scopeSize = scopeData.size(); + + timeElapsed = m_lastUpdate.restart(); + + const qreal step = m_step * timeElapsed / 1000.0; + const qreal fadeStep = (qreal)timeElapsed / 20.0; + + // block m_store and m_fadebars + QMutexLocker locker(&m_mutex); + + m_store.resize( scopeSize ); + m_fadebars.resize( scopeSize ); + + for( int x = 0; x < scopeSize; ++x ) + { + // determine y + int y = 0; + while( y < m_yscale.size() && scopeData.at(x) < m_yscale.at(y) ) + y++; + + auto &fadebars = m_fadebars[x]; + auto &store = m_store[x]; + + // remove obscured fadebars + while( !fadebars.isEmpty() && fadebars.last().y >= y ) + fadebars.removeLast(); + + // remove completely faded fadebars + while( !fadebars.isEmpty() && fadebars.first().intensity <= fadeStep ) + fadebars.removeFirst(); + + // fade the rest + for( auto &fadebar : fadebars ) + fadebar.intensity -= fadeStep; + + if( ( double )y > store ) + { + // add new fadebar at old column height + if( m_showFadebars ) + fadebars << Fadebar( store, BlockAnalyzer::FADE_SIZE ); + + store = qMin( store + step, double( y ) ); + } + else + store = y; + } + + emit finished(); +} diff --git a/src/context/applets/analyzer/plugin/BlockWorker.h b/src/context/applets/analyzer/plugin/BlockWorker.h new file mode 100644 index 0000000000..a24029f488 --- /dev/null +++ b/src/context/applets/analyzer/plugin/BlockWorker.h @@ -0,0 +1,76 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Pulic License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +#ifndef BLOCKWORKER_H +#define BLOCKWORKER_H + +#include "AnalyzerWorker.h" + +#include +#include +#include + + +class BlockWorker : public Analyzer::Worker +{ + friend class BlockRenderer; + + Q_OBJECT + +public: + BlockWorker( int rows, int columns, qreal step, bool showFadebars ); + + void setStep( qreal step ) { m_step = step; } + void setRows( int rows ); + void setColumns( int columns ); + void setRefreshRate( qreal refreshRate ) { m_refreshTime = std::floor( 1000.0 / refreshRate ); } + void setShowFadebars( bool showFadebars ) { m_showFadebars = showFadebars; } + +signals: + void finished(); + +protected: + void analyze() Q_DECL_OVERRIDE; + +private: + struct Fadebar + { + int y; + qreal intensity; + Fadebar() + { + y = 0; + intensity = 0.0; + } + Fadebar( int y, qreal intensity ) + { + this->y = y; + this->intensity = intensity; + } + }; + mutable QMutex m_mutex; //used to lock m_store and m_fadebars + QVector m_store; //current bar heights + QVector m_yscale; + QVector > m_fadebars; + qreal m_step; + int m_rows; + int m_columns; + int m_refreshTime; + bool m_showFadebars; + QTime m_lastUpdate; +}; + +#endif //BLOCKWORKER_H diff --git a/src/context/applets/analyzer/DiscoAnalyzer.cpp b/src/context/applets/analyzer/plugin/DiscoAnalyzer.cpp similarity index 63% rename from src/context/applets/analyzer/DiscoAnalyzer.cpp rename to src/context/applets/analyzer/plugin/DiscoAnalyzer.cpp index a81cde47b8..f6845242a7 100644 --- a/src/context/applets/analyzer/DiscoAnalyzer.cpp +++ b/src/context/applets/analyzer/plugin/DiscoAnalyzer.cpp @@ -1,314 +1,212 @@ /**************************************************************************************** * Copyright (c) 2004 Enrico Ros * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "DiscoAnalyzer.h" #include -#include +#include #include #include #include DiscoAnalyzer::DiscoAnalyzer( QWidget *parent ): Analyzer::Base( parent ) { setObjectName( "Disco" ); - m_dotTexture = bindTexture( QImage( KStandardDirs::locate( "data", "amarok/images/dot.png" ) ) ); - m_w1Texture = bindTexture( QImage( KStandardDirs::locate( "data", "amarok/images/wirl1.png" ) ) ); - m_w2Texture = bindTexture( QImage( KStandardDirs::locate( "data", "amarok/images/wirl2.png" ) ) ); + m_dotTexture = bindTexture( QImage( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/dot.png" ) ) ); + m_w1Texture = bindTexture( QImage( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/wirl1.png" ) ) ); + m_w2Texture = bindTexture( QImage( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/wirl2.png" ) ) ); m_show.paused = true; m_show.pauseTimer = 0.0; m_show.rotDegrees = 0.0; m_frame.rotDegrees = 0.0; } DiscoAnalyzer::~DiscoAnalyzer() { deleteTexture( m_dotTexture ); deleteTexture( m_w1Texture ); deleteTexture( m_w2Texture ); } void DiscoAnalyzer::demo() { static int t = 0; static bool forward = true; QVector s( 200 ); const double dt = double( t ) / 200; for( int i = 0; i < s.size(); ++i ) s[i] = dt * ( sin( M_PI + ( i * M_PI ) / s.size() ) + 1.0 ); analyze( s ); if( t == 0 ) forward = true; if( t == 200 ) forward = false; t = forward ? t + 2 : t - 2; } void DiscoAnalyzer::initializeGL() { // Set a smooth shade model glShadeModel( GL_SMOOTH ); // Disable depth test (all is drawn on a 2d plane) glDisable( GL_DEPTH_TEST ); // Set blend parameters for 'composting alpha' glBlendFunc( GL_SRC_ALPHA, GL_ONE ); //superpose //glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); //fade glEnable( GL_BLEND ); // Clear frame with a black background glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); glClear( GL_COLOR_BUFFER_BIT ); } void DiscoAnalyzer::resizeGL( int w, int h ) { // Setup screen. We're going to manually do the perspective projection glViewport( 0, 0, ( GLint )w, ( GLint )h ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho( -10.0f, 10.0f, -10.0f, 10.0f, -5.0f, 5.0f ); // Get the aspect ratio of the screen to draw 'cicular' particles const float ratio = ( float )w / ( float )h, eqPixH = 60, eqPixW = 80; if( ratio >= ( 4.0 / 3.0 ) ) { m_unitX = 10.0 / ( eqPixH * ratio ); m_unitY = 10.0 / eqPixH; } else { m_unitX = 10.0 / eqPixW; m_unitY = 10.0 / ( eqPixW / ratio ); } // Get current timestamp. timeval tv; gettimeofday( &tv, NULL ); m_show.timeStamp = ( double )tv.tv_sec + ( double )tv.tv_usec / 1000000.0; } void DiscoAnalyzer::analyze( const QVector &s ) { bool haveNoData = s.empty(); // if we're going into pause mode, clear timers. if( !m_show.paused && haveNoData ) m_show.pauseTimer = 0.0; // if we have got data, interpolate it (asking myself why I'm doing it here..) if( !( m_show.paused = haveNoData ) ) { int bands = s.size(), lowbands = bands / 4, hibands = bands / 3, midbands = bands - lowbands - hibands; Q_UNUSED( midbands ); float currentEnergy = 0, currentMeanBand = 0, maxValue = 0; for( int i = 0; i < bands; i++ ) { float value = s[i]; currentEnergy += value; currentMeanBand += ( float )i * value; if( value > maxValue ) maxValue = value; } m_frame.silence = currentEnergy < 0.001; if( !m_frame.silence ) { m_frame.meanBand = 100.0 * currentMeanBand / ( currentEnergy * bands ); currentEnergy = 100.0 * currentEnergy / ( float )bands; m_frame.dEnergy = currentEnergy - m_frame.energy; m_frame.energy = currentEnergy; // printf( "%d [%f :: %f ]\t%f \n", bands, frame.energy, frame.meanBand, maxValue ); } else m_frame.energy = 0.0; } } void DiscoAnalyzer::paintGL() { - // Compute the dT since the last call to paintGL and update timings - timeval tv; - gettimeofday( &tv, NULL ); - double currentTime = ( double )tv.tv_sec + ( double )tv.tv_usec / 1000000.0; - m_show.dT = currentTime - m_show.timeStamp; - m_show.timeStamp = currentTime; - - // Clear frame - glClear( GL_COLOR_BUFFER_BIT ); - - // Shitch to MODEL matrix and reset it to default - glMatrixMode( GL_MODELVIEW ); - glLoadIdentity(); - - // Fade the previous drawings. - /* glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - glBegin( GL_TRIANGLE_STRIP ); - glColor4f( 0.0f, 0.0f, 0.0f, 0.2f ); - glVertex2f( 10.0f, 10.0f ); - glVertex2f( -10.0f, 10.0f ); - glVertex2f( 10.0f, -10.0f ); - glVertex2f( -10.0f, -10.0f ); - glEnd();*/ - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - glEnable( GL_TEXTURE_2D ); - float alphaN = m_show.paused ? 0.2 : ( m_frame.energy / 10.0 ), - alphaP = m_show.paused ? 1.0 : ( 1 - m_frame.energy / 20.0 ); - if( alphaN > 1.0 ) - alphaN = 1.0; - if( alphaP < 0.1 ) - alphaP = 0.1; - glBindTexture( GL_TEXTURE_2D, m_w2Texture ); - setTextureMatrix( m_show.rotDegrees, 0.707 * alphaP ); - glColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); - glBegin( GL_TRIANGLE_STRIP ); - glTexCoord2f( 1.0, 1.0 ); - glVertex2f( 10.0f, 10.0f ); - glTexCoord2f( 0.0, 1.0 ); - glVertex2f( -10.0f, 10.0f ); - glTexCoord2f( 1.0, 0.0 ); - glVertex2f( 10.0f, -10.0f ); - glTexCoord2f( 0.0 , 0.0 ); - glVertex2f( -10.0f, -10.0f ); - glEnd(); - glBindTexture( GL_TEXTURE_2D, m_w1Texture ); - setTextureMatrix( -m_show.rotDegrees * 2, 0.707 ); - glColor4f( 1.0f, 1.0f, 1.0f, alphaN ); - glBegin( GL_TRIANGLE_STRIP ); - glTexCoord2f( 1.0, 1.0 ); - glVertex2f( 10.0f, 10.0f ); - glTexCoord2f( 0.0, 1.0 ); - glVertex2f( -10.0f, 10.0f ); - glTexCoord2f( 1.0, 0.0 ); - glVertex2f( 10.0f, -10.0f ); - glTexCoord2f( 0.0 , 0.0 ); - glVertex2f( -10.0f, -10.0f ); - glEnd(); - setTextureMatrix( 0.0, 0.0 ); - glDisable( GL_TEXTURE_2D ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE ); - - // Here begins the real draw loop - // some updates to the showStruct - m_show.rotDegrees += 40.0 * m_show.dT; - m_frame.rotDegrees += 80.0 * m_show.dT; - - // handle the 'pause' status - if( m_show.paused ) - { - if( m_show.pauseTimer > 0.5 ) - { - if( m_show.pauseTimer > 0.6 ) - m_show.pauseTimer -= 0.6; - drawFullDot( 0.0f, 0.4f, 0.8f, 1.0f ); - drawFullDot( 0.0f, 0.4f, 0.8f, 1.0f ); - } - m_show.pauseTimer += m_show.dT; - return; - } - - if( m_dotTexture ) - { - glEnable( GL_TEXTURE_2D ); - glBindTexture( GL_TEXTURE_2D, m_dotTexture ); - } - else - glDisable( GL_TEXTURE_2D ); - - glLoadIdentity(); -// glRotatef( -frame.rotDegrees, 0,0,1 ); - glBegin( GL_QUADS ); -// Particle * particle = particleList.first(); -// for (; particle; particle = particleList.next()) - { - glColor4f( 0.0f, 1.0f, 0.0f, 1.0f ); - drawDot( 0, 0, qMax( 10.0, ( 10.0 * m_frame.energy ) ) ); - glColor4f( 1.0f, 0.0f, 0.0f, 1.0f ); - drawDot( 6, 0, qMax( 10.0, ( 5.0 * m_frame.energy ) ) ); - glColor4f( 0.0f, 0.4f, 1.0f, 1.0f ); - drawDot( -6, 0, qMax( 10.0, ( 5.0 * m_frame.energy ) ) ); - } - glEnd(); } void DiscoAnalyzer::drawDot( float x, float y, float size ) { float sizeX = size * m_unitX, sizeY = size * m_unitY, pLeft = x - sizeX, pTop = y + sizeY, pRight = x + sizeX, pBottom = y - sizeY; glTexCoord2f( 0, 0 ); // Bottom Left glVertex2f( pLeft, pBottom ); glTexCoord2f( 0, 1 ); // Top Left glVertex2f( pLeft, pTop ); glTexCoord2f( 1, 1 ); // Top Right glVertex2f( pRight, pTop ); glTexCoord2f( 1, 0 ); // Bottom Right glVertex2f( pRight, pBottom ); } void DiscoAnalyzer::drawFullDot( float r, float g, float b, float a ) { glBindTexture( GL_TEXTURE_2D, m_dotTexture ); glEnable( GL_TEXTURE_2D ); glColor4f( r, g, b, a ); glBegin( GL_TRIANGLE_STRIP ); glTexCoord2f( 1.0, 1.0 ); glVertex2f( 10.0f, 10.0f ); glTexCoord2f( 0.0, 1.0 ); glVertex2f( -10.0f, 10.0f ); glTexCoord2f( 1.0, 0.0 ); glVertex2f( 10.0f, -10.0f ); glTexCoord2f( 0.0 , 0.0 ); glVertex2f( -10.0f, -10.0f ); glEnd(); glDisable( GL_TEXTURE_2D ); } void DiscoAnalyzer::setTextureMatrix( float rot, float scale ) { glMatrixMode( GL_TEXTURE ); glLoadIdentity(); if( rot != 0.0 || scale != 0.0 ) { glTranslatef( 0.5f, 0.5f, 0.0f ); glRotatef( rot, 0.0f, 0.0f, 1.0f ); glScalef( scale, scale, 1.0f ); glTranslatef( -0.5f, -0.5f, 0.0f ); } glMatrixMode( GL_MODELVIEW ); } diff --git a/src/context/applets/analyzer/DiscoAnalyzer.h b/src/context/applets/analyzer/plugin/DiscoAnalyzer.h similarity index 100% rename from src/context/applets/analyzer/DiscoAnalyzer.h rename to src/context/applets/analyzer/plugin/DiscoAnalyzer.h diff --git a/src/context/applets/analyzer/plugin/qmldir b/src/context/applets/analyzer/plugin/qmldir new file mode 100644 index 0000000000..bb095713e3 --- /dev/null +++ b/src/context/applets/analyzer/plugin/qmldir @@ -0,0 +1,2 @@ +module org.kde.amarok.analyzer +plugin amarok_context_applet_analyzer diff --git a/src/context/applets/currenttrack/CMakeLists.txt b/src/context/applets/currenttrack/CMakeLists.txt index 53807faab9..5c2b1af732 100644 --- a/src/context/applets/currenttrack/CMakeLists.txt +++ b/src/context/applets/currenttrack/CMakeLists.txt @@ -1,28 +1,18 @@ -project(context-currenttrack) - -include_directories( - ${Amarok_SOURCE_DIR}/src/context/widgets - ${Amarok_SOURCE_DIR}/src/core-impl/collections/support - ${Amarok_SOURCE_DIR}/src - ) +set(current_SRCS + plugin/CurrentPlugin.cpp + plugin/CurrentEngine.cpp +) -set( currenttrack_SRCS - CurrentTrack.cpp - ${Amarok_SOURCE_DIR}/src/context/widgets/RecentlyPlayedListWidget.cpp - ${Amarok_SOURCE_DIR}/src/widgets/PixmapViewer.cpp - ) +add_library(amarok_context_applet_currenttrack SHARED ${current_SRCS}) -ki18n_wrap_ui( currenttrack_SRCS currentTrackSettings.ui ) -add_library(amarok_context_applet_currenttrack MODULE ${currenttrack_SRCS}) -if(APPLE) - set_target_properties(amarok_context_applet_currenttrack PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") -endif() target_link_libraries(amarok_context_applet_currenttrack amarokcore amaroklib - KF5::Plasma + Qt5::Qml + KF5::CoreAddons ) -install(TARGETS amarok_context_applet_currenttrack DESTINATION ${PLUGIN_INSTALL_DIR}) -install(FILES amarok-context-applet-currenttrack.desktop DESTINATION ${SERVICES_INSTALL_DIR}) -install(FILES amarok-currenttrack.svg DESTINATION ${DATA_INSTALL_DIR}/desktoptheme/default/widgets/ ) +install(TARGETS amarok_context_applet_currenttrack DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/currenttrack) +install(FILES plugin/qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/currenttrack) + +kpackage_install_package(package org.kde.amarok.currenttrack amarok) diff --git a/src/context/applets/currenttrack/CurrentTrack.cpp b/src/context/applets/currenttrack/CurrentTrack.cpp deleted file mode 100644 index 04e6069653..0000000000 --- a/src/context/applets/currenttrack/CurrentTrack.cpp +++ /dev/null @@ -1,931 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007-2009 Leo Franchi * - * Copyright (c) 2008 William Viana Soares * - * Copyright (c) 2009 simon.esneault * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "CurrentTrack" - -#include "CurrentTrack.h" - -#include "App.h" -#include "EngineController.h" -#include "GlobalCurrentTrackActions.h" -#include "MainWindow.h" -#include "PaletteHandler.h" -#include "PluginManager.h" -#include "SvgHandler.h" -#include "amarokurls/AmarokUrl.h" -#include "context/widgets/RatingWidget.h" -#include "context/widgets/TextScrollingWidget.h" -#include "context/widgets/DropPixmapItem.h" -#include "context/widgets/RecentlyPlayedListWidget.h" -#include "core/capabilities/ActionsCapability.h" -#include "core/capabilities/BookmarkThisCapability.h" -#include "core/capabilities/FindInSourceCapability.h" -#include "core/support/Amarok.h" -#include "core/support/Debug.h" -#include "core/meta/Meta.h" -#include "core/meta/Statistics.h" -#include "core/meta/TrackEditor.h" -#include "core/meta/support/MetaUtility.h" -#include "core-impl/collections/support/CollectionManager.h" -#include "covermanager/CoverViewDialog.h" -#include "dialogs/TagDialog.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -CurrentTrack::CurrentTrack( QObject* parent, const QVariantList& args ) - : Context::Applet( parent, args ) - , m_actionsLayout( 0 ) - , m_findInSourceSignalMapper( 0 ) - , m_rating( -1 ) - , m_score( 0 ) - , m_trackLength( 0 ) - , m_playCount( 0 ) - , m_trackCount( 0 ) - , m_albumCount( 0 ) - , m_artistCount( 0 ) - , m_isStopped( true ) - , m_coverKey( 0 ) - , m_view( Stopped ) - , m_showEditTrackDetailsAction( true ) - , m_albumWidth( 135 ) -{ - setHasConfigurationInterface( true ); - setBackgroundHints( Plasma::Applet::NoBackground ); -} - -CurrentTrack::~CurrentTrack() -{ - clearTrackActions(); - delete m_albumCover; -} - -void -CurrentTrack::init() -{ - DEBUG_BLOCK - PERF_LOG( "Begin init" ); - - // Call the base implementation. - Context::Applet::init(); - - setToolTip( i18n( "Right-click to configure" ) ); - - m_ratingWidget = new RatingWidget( this ); - m_ratingWidget->setSpacing( 2 ); - m_ratingWidget->setMinimumSize( m_albumWidth + 10, 30 ); - m_ratingWidget->setMaximumSize( m_albumWidth + 10, 30 ); - connect( m_ratingWidget, SIGNAL(ratingChanged(int)), SLOT(trackRatingChanged(int)) ); - - QLabel *collectionLabel = new QLabel( i18n( "Local Collection" ) ); - collectionLabel->setAttribute( Qt::WA_NoSystemBackground ); - collectionLabel->setAlignment( Qt::AlignCenter ); - m_collectionLabel = new QGraphicsProxyWidget( this ); - m_collectionLabel->setWidget( collectionLabel ); - - m_title = new TextScrollingWidget( this ); - m_artist = new TextScrollingWidget( this ); - m_album = new TextScrollingWidget( this ); - m_byText = new QGraphicsSimpleTextItem( i18nc( "What artist is this track by", "By" ), this ); - m_onText = new QGraphicsSimpleTextItem( i18nc( "What album is this track on", "On" ), this ); - - m_recentWidget = new RecentlyPlayedListWidget( this ); - m_recentHeader = new TextScrollingWidget( this ); - m_recentHeader->setDrawBackground( true ); - m_recentHeader->setAlignment( Qt::AlignLeft ); - QFont recentHeaderFont; - recentHeaderFont.setPointSize( recentHeaderFont.pointSize() + 2 ); - m_recentHeader->setFont( recentHeaderFont ); - - m_title->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - m_artist->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - m_album->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - m_collectionLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - m_recentHeader->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); - m_recentWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - m_recentWidget->setMinimumHeight( 10 ); - - m_albumCover = new DropPixmapLayoutItem( this ); - m_albumCover->setPreferredSize( QSizeF( m_albumWidth, m_albumWidth ) ); - connect( m_albumCover, SIGNAL(imageDropped(QPixmap)), SLOT(coverDropped(QPixmap)) ); - - const QBrush brush = normalBrush(); - m_title->setBrush( brush ); - m_artist->setBrush( brush ); - m_album->setBrush( brush ); - m_byText->setBrush( brush ); - m_onText->setBrush( brush ); - - const QFont tinyFont = KGlobalSettings::smallestReadableFont(); - m_byText->setFont( tinyFont ); - m_onText->setFont( tinyFont ); - - m_actionsLayout = new QGraphicsLinearLayout; - m_actionsLayout->setMinimumWidth( 10 ); - m_actionsLayout->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - QGraphicsLinearLayout *titlesLayout = new QGraphicsLinearLayout( Qt::Vertical ); - titlesLayout->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - titlesLayout->setMinimumWidth( 10 ); - titlesLayout->setSpacing( 2 ); - titlesLayout->addItem( m_title ); - titlesLayout->addItem( m_artist ); - titlesLayout->addItem( m_album ); - titlesLayout->addItem( m_actionsLayout ); - titlesLayout->setItemSpacing( 2, 4 ); // a bit more spacing for the actions row - - const qreal pad = standardPadding(); - QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout( this ); - l->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - l->addCornerAnchors( m_ratingWidget, Qt::BottomLeftCorner, l, Qt::BottomLeftCorner ); - l->addCornerAnchors( m_ratingWidget, Qt::BottomLeftCorner, m_collectionLabel, Qt::BottomLeftCorner ); - l->addCornerAnchors( m_ratingWidget, Qt::TopRightCorner, m_collectionLabel, Qt::TopRightCorner ); - l->addCornerAnchors( m_recentHeader, Qt::TopRightCorner, l, Qt::TopRightCorner ); - l->addAnchor( m_albumCover, Qt::AnchorBottom, m_ratingWidget, Qt::AnchorTop )->setSpacing( 4 ); - l->addAnchor( m_albumCover, Qt::AnchorHorizontalCenter, m_ratingWidget, Qt::AnchorHorizontalCenter ); - l->addAnchor( titlesLayout, Qt::AnchorTop, l, Qt::AnchorTop )->setSpacing( 18 ); - l->addAnchor( titlesLayout, Qt::AnchorRight, l, Qt::AnchorRight )->setSpacing( pad ); - l->addAnchors( m_recentWidget, m_recentHeader, Qt::Horizontal ); - l->addAnchor( m_recentWidget, Qt::AnchorTop, m_recentHeader, Qt::AnchorBottom ); - l->addAnchor( m_recentWidget, Qt::AnchorRight, m_recentHeader, Qt::AnchorRight ); - l->addAnchor( m_recentWidget, Qt::AnchorLeft, m_ratingWidget, Qt::AnchorRight )->setSpacing( pad * 2 ); - l->addAnchor( m_recentWidget, Qt::AnchorBottom, m_albumCover, Qt::AnchorBottom )->setSpacing( 20 ); - qreal midSpacing = qMax( m_byText->boundingRect().width(), m_onText->boundingRect().width() ) + pad; - l->addAnchor( titlesLayout, Qt::AnchorLeft, m_ratingWidget, Qt::AnchorRight )->setSpacing( midSpacing ); - l->anchor( m_recentHeader, Qt::AnchorTop, l, Qt::AnchorTop )->setSpacing( 2 ); - - // Read config - KConfigGroup config = Amarok::config("Current Track Applet"); - const QString fontDesc = config.readEntry( "Font", QString() ); - QFont font; - if( !fontDesc.isEmpty() ) - font.fromString( fontDesc ); - else - font.setPointSize( font.pointSize() + 3 ); - - m_showEditTrackDetailsAction = config.readEntry( "ShowEditTrackAction", true ); - - m_title->setFont( font ); - m_artist->setFont( font ); - m_album->setFont( font ); - - m_title->setAlignment( Qt::AlignLeft ); - m_artist->setAlignment( Qt::AlignLeft ); - m_album->setAlignment( Qt::AlignLeft ); - - dataEngine( "amarok-current" )->setProperty( "coverWidth", m_albumWidth ); - dataEngine( "amarok-current" )->connectSource( "current", this ); - connect( The::paletteHandler(), SIGNAL(newPalette(QPalette)), SLOT(paletteChanged(QPalette)) ); - connect( CollectionManager::instance(), SIGNAL(collectionDataChanged(Collections::Collection*)), - this, SLOT(queryCollection()), Qt::QueuedConnection ); - queryCollection(); - setView( Stopped ); - - PERF_LOG( "Finished init" ); -} - -void -CurrentTrack::trackRatingChanged( int rating ) -{ - Meta::TrackPtr track = The::engineController()->currentTrack(); - if( !track ) - return; - - track->statistics()->setRating( rating ); -} - -QList -CurrentTrack::contextualActions() -{ - DEBUG_BLOCK - QList actions; - Meta::TrackPtr track = The::engineController()->currentTrack(); - if( !track ) - return actions; - - if( !m_contextActions.isEmpty() ) - return m_contextActions; - - Meta::AlbumPtr album = track->album(); - if( !album ) - return actions; - - QScopedPointer ac( album->create() ); - if( ac ) - { - m_contextActions << ac->actions(); - actions.append( m_contextActions ); - } - return actions; -} - -void -CurrentTrack::mousePressEvent( QGraphicsSceneMouseEvent *event ) -{ - if( !m_isStopped - && event->modifiers() == Qt::NoModifier - && event->button() == Qt::LeftButton ) - { - QGraphicsView *view = scene()->views().first(); - QGraphicsItem *item = view->itemAt( view->mapFromScene(event->scenePos()) ); - if( item == m_albumCover->graphicsItem() ) - { - Meta::AlbumPtr album = The::engineController()->currentTrack()->album(); - if( album ) - ( new CoverViewDialog( album, The::mainWindow() ) )->show(); - return; - } - } - Context::Applet::mousePressEvent( event ); -} - -void -CurrentTrack::constraintsEvent( Plasma::Constraints constraints ) -{ - Context::Applet::constraintsEvent( constraints ); - - prepareGeometryChange(); - m_byText->setPos( m_artist->pos() ); - m_onText->setPos( m_album->pos() ); - m_byText->setX( m_byText->x() - m_byText->boundingRect().width() - 4 ); - m_onText->setX( m_onText->x() - m_onText->boundingRect().width() - 4 ); - alignBaseLineToFirst( m_artist, m_byText ); - alignBaseLineToFirst( m_album, m_onText ); - - update(); // ensure the stats bg is repainted with correct geometry - if( m_isStopped ) - { - m_recentHeader->setScrollingText( i18n("Recently Played Tracks") ); - return; - } - - QString artist = handleUnknown( m_artist->text(), m_artist, UNKNOWN_ARTIST.toString() ); - QString album = handleUnknown( m_album->text(), m_album, UNKNOWN_ALBUM.toString() ); - m_title->setScrollingText( m_title->text() ); - m_artist->setScrollingText( artist ); - m_album->setScrollingText( album ); -} - -QSizeF -CurrentTrack::sizeHint( Qt::SizeHint which, const QSizeF &constraint ) const -{ - // figure out the size we want to be, in order to be able to squeeze in all - // that we want depends on the current font size, basically height should - // be increased for larger point sizes. here, the layout works correctly - // with size 8, which has the fontMetrics height of 13 a size too big, like - // font size 12, has a fontMetrics height of 19. So we add some height if - // it's too big - int height = ( QApplication::fontMetrics().height() - 13 ) * 2 + 180; - return QSizeF( Context::Applet::sizeHint(which, constraint).width(), height ); -} - -void -CurrentTrack::dataUpdated( const QString& name, const Plasma::DataEngine::Data& data ) -{ - if( data.isEmpty() || name != QLatin1String("current") ) - return; - - if( data.contains( QLatin1String("notrack" ) ) ) - { - if( m_view != Stopped ) - setView( Stopped ); - return; - } - - const QPixmap &cover = data[ "albumart" ].value(); - const QVariantMap ¤tInfo = data[ QLatin1String("current") ].toMap(); - bool updateCover = ( m_coverKey != cover.cacheKey() ); - if( (m_currentInfo == currentInfo) && !updateCover ) - return; - - if( m_view != Playing ) - setView( Playing ); - - QString title = currentInfo.value( Meta::Field::TITLE ).toString(); - QString artist = currentInfo.value( Meta::Field::ARTIST ).toString(); - QString album = currentInfo.value( Meta::Field::ALBUM ).toString(); - artist = handleUnknown( artist, m_artist, UNKNOWN_ARTIST.toString() ); - album = handleUnknown( album, m_album, UNKNOWN_ALBUM.toString() ); - - if( title != m_currentInfo.value(Meta::Field::TITLE) ) - m_title->setScrollingText( title ); - if( artist != m_currentInfo.value(Meta::Field::ARTIST) ) - m_artist->setScrollingText( artist ); - if( album != m_currentInfo.value(Meta::Field::ALBUM) ) - m_album->setScrollingText( album ); - - m_rating = currentInfo[ Meta::Field::RATING ].toInt(); - m_trackLength = currentInfo[ Meta::Field::LENGTH ].toInt(); - m_score = currentInfo[ Meta::Field::SCORE ].toInt(); - m_lastPlayed = currentInfo[ Meta::Field::LAST_PLAYED ].toDateTime(); - m_playCount = currentInfo[ Meta::Field::PLAYCOUNT ].toInt(); - - m_ratingWidget->setRating( m_rating ); - - if( updateCover ) - { - m_coverKey = cover.cacheKey(); - resizeCover( cover, m_albumWidth ); - } - m_currentInfo = currentInfo; - m_sourceEmblemPath = data[ "source_emblem" ].toString(); - clearTrackActions(); - setupLayoutActions( The::engineController()->currentTrack() ); - updateConstraints(); -} - -void -CurrentTrack::editTrack() -{ - Meta::TrackPtr track = The::engineController()->currentTrack(); - new TagDialog( track, scene()->views().first() ); -} - -void -CurrentTrack::paintInterface( QPainter *p, - const QStyleOptionGraphicsItem *option, - const QRect &contentsRect ) -{ - Context::Applet::paintInterface( p, option, contentsRect ); - drawSourceEmblem( p, contentsRect ); - drawStatsBackground( p, contentsRect ); - drawStatsTexts( p, contentsRect ); -} - -void -CurrentTrack::drawStatsBackground( QPainter *const p, const QRect &rect ) -{ - // draw the complete outline. lots of little steps :) at each corner, leave - // a 6x6 box. draw a quad bezier curve from the two ends of the lines, - // through the original corner - - const qreal leftEdge = m_ratingWidget->boundingRect().right() + standardPadding(); - const qreal rightEdge = rect.right() - standardPadding() / 2; - const qreal ratingWidgetX = m_ratingWidget->pos().x(); - const qreal ratingWidgetY = m_ratingWidget->pos().y(); - const qreal ratingWidgetH = m_ratingWidget->boundingRect().height(); - QColor topColor = The::paletteHandler()->palette().color( QPalette::Base ); - QColor bottomColor = topColor; - topColor.setAlpha( 200 ); - bottomColor.setAlpha( 100 ); - - QPainterPath statsPath; - statsPath.moveTo( leftEdge + 6, ratingWidgetY - ratingWidgetH + 8 ); // top left position of the rect, right below the album - statsPath.lineTo( rightEdge - 6, ratingWidgetY - ratingWidgetH + 8 ); // go right to margin - statsPath.quadTo( rightEdge, ratingWidgetY - ratingWidgetH + 8, - rightEdge, ratingWidgetY - ratingWidgetH + 8 + 6 ); - statsPath.lineTo( rightEdge, ratingWidgetY + ratingWidgetH - 6 ); // go down to bottom right corner - statsPath.quadTo( rightEdge, ratingWidgetY + ratingWidgetH, - rightEdge - 6, ratingWidgetY + ratingWidgetH ); - statsPath.lineTo( ratingWidgetX + 6, ratingWidgetY + ratingWidgetH ); // way bottom left corner - statsPath.quadTo( ratingWidgetX, ratingWidgetY + ratingWidgetH, - ratingWidgetX, ratingWidgetY + ratingWidgetH - 6 ); - statsPath.lineTo( ratingWidgetX, ratingWidgetY + 6 ); // top left of rating widget - statsPath.quadTo( ratingWidgetX, ratingWidgetY, - ratingWidgetX + 6, ratingWidgetY ); - statsPath.lineTo( leftEdge - 6, ratingWidgetY ); // joining of two rects - statsPath.quadTo( leftEdge, ratingWidgetY, - leftEdge, ratingWidgetY - 6 ); - statsPath.lineTo( leftEdge, ratingWidgetY - ratingWidgetH + 8 + 6 ); // back to start - statsPath.quadTo( leftEdge, ratingWidgetY - ratingWidgetH + 8, - leftEdge + 6, ratingWidgetY - ratingWidgetH + 8 ); - - // draw just the overlay which is the "header" row, to emphasize that we have 2 rows here - QPainterPath headerPath; - headerPath.moveTo( leftEdge + 6, ratingWidgetY - ratingWidgetH + 8 ); // top left position of the rect, right below the album - headerPath.lineTo( rightEdge - 6, ratingWidgetY - ratingWidgetH + 8 ); // go right to margin - headerPath.quadTo( rightEdge, ratingWidgetY - ratingWidgetH + 8, - rightEdge, ratingWidgetY - ratingWidgetH + 8 + 6 ); - headerPath.lineTo( rightEdge, ratingWidgetY ); // middle of the right side - headerPath.lineTo( leftEdge - 6, ratingWidgetY ); // join spot, before quad curve - headerPath.quadTo( leftEdge, ratingWidgetY, - leftEdge, ratingWidgetY - 6 ); - headerPath.lineTo( leftEdge, ratingWidgetY - ratingWidgetH + 8 + 6 ); // curve back through start - headerPath.quadTo( leftEdge, ratingWidgetY - ratingWidgetH + 8, - leftEdge + 6, ratingWidgetY - ratingWidgetH + 8 ); - - p->save(); - p->setRenderHint( QPainter::Antialiasing ); - p->fillPath( statsPath, bottomColor ); - p->fillPath( headerPath, topColor ); - p->restore(); - -} - -void -CurrentTrack::drawStatsTexts( QPainter *const p, const QRect &contentsRect ) -{ - const qreal leftEdge = m_ratingWidget->boundingRect().right() + standardPadding(); - const qreal maxTextWidth = contentsRect.right() - standardPadding() * 2 - leftEdge; - const QString column1Label = m_isStopped ? i18n( "Tracks" ) : i18n( "Play Count" ); - const QString column2Label = m_isStopped ? i18n( "Albums" ) : i18n( "Score" ); - const QString column3Label = m_isStopped ? i18n( "Artists" ) : i18n( "Last Played" ); - - //Align labels taking into account the string widths for each label - QFontMetricsF fm( font() ); - qreal totalWidth = fm.width( column1Label ) + fm.width( column2Label ) + fm.width( column3Label ); - qreal factor, prevFactor; - factor = fm.width( column1Label ) / totalWidth; - prevFactor = factor; - - QRectF rect( leftEdge, // align vertically with track info text - m_ratingWidget->pos().y() - m_ratingWidget->boundingRect().height() + 8, // align bottom horizontally with top of rating rounded rect - maxTextWidth * factor, - m_ratingWidget->boundingRect().height() - 4 ); // just the "first" row, so go halfway down - - p->save(); - p->setRenderHint( QPainter::Antialiasing ); - p->setPen( normalBrush().color() ); - - // labels - QString playCountLabel = fm.elidedText( column1Label, Qt::ElideRight, rect.width() ); - p->drawText( rect, Qt::AlignCenter | Qt::TextSingleLine, playCountLabel ); - - factor = fm.width( column2Label ) / totalWidth; - rect.setWidth( maxTextWidth * factor ); - rect.moveLeft( rect.topLeft().x() + maxTextWidth * prevFactor ); - prevFactor = factor; - - QString scoreLabel = fm.elidedText( column2Label, Qt::ElideRight, rect.width() ); - p->drawText( rect, Qt::AlignCenter | Qt::TextSingleLine, scoreLabel ); - - factor = fm.width( column3Label ) / totalWidth; - rect.setWidth( maxTextWidth * factor ); - rect.moveLeft( rect.topLeft().x() + maxTextWidth * prevFactor ); - - QString lastPlayedLabel = fm.elidedText( column3Label, Qt::ElideRight, rect.width() ); - p->drawText( rect, Qt::AlignCenter | Qt::TextSingleLine, lastPlayedLabel ); - - // stats - - const int column1Stat = m_isStopped ? m_trackCount : m_playCount; - const int column2Stat = m_isStopped ? m_albumCount : m_score; - - factor = fm.width( column1Label ) / totalWidth; - prevFactor = factor; - rect.setX( leftEdge ); - rect.setY( m_ratingWidget->pos().y() + 3 ); - rect.setWidth( maxTextWidth * factor); - rect.setHeight( m_ratingWidget->boundingRect().height() - 4 ); - p->drawText( rect, Qt::AlignCenter | Qt::TextSingleLine, QString::number(column1Stat) ); - - factor = fm.width( column2Label ) / totalWidth; - rect.setWidth( maxTextWidth * factor ); - rect.moveLeft( rect.topLeft().x() + maxTextWidth * prevFactor ); - prevFactor = factor; - - p->drawText( rect, Qt::AlignCenter | Qt::TextSingleLine, QString::number(column2Stat) ); - - factor = fm.width( column3Label ) / totalWidth; - rect.setWidth( maxTextWidth * factor ); - rect.moveLeft( rect.topLeft().x() + maxTextWidth * prevFactor ); - - const QString column3Stat = ( m_isStopped ) - ? QString::number( m_artistCount ) - : fm.elidedText( Amarok::verboseTimeSince(m_lastPlayed), Qt::ElideRight, rect.width() ); - p->drawText( rect, Qt::AlignCenter | Qt::TextSingleLine, column3Stat ); - - p->restore(); -} - -void -CurrentTrack::drawSourceEmblem( QPainter *const p, const QRect &contentsRect ) -{ - if( m_isStopped ) - return; - - p->save(); - p->setOpacity( 0.19 ); - - if( m_sourceEmblemPath.isEmpty() ) - { - QPixmap logo = Amarok::semiTransparentLogo( m_albumWidth ); - QRect rect = logo.rect(); - int y = standardPadding(); - int x = contentsRect.right() - rect.width() - y; - rect.moveTo( x, y ); - QRectF clipRect( rect ); - qreal clipY = m_ratingWidget->pos().y() - m_ratingWidget->boundingRect().height() + 8; - clipRect.setBottom( clipY ); - p->setClipRect( clipRect ); - p->drawPixmap( rect, logo ); - } - else - { - QSvgRenderer svg( m_sourceEmblemPath ); - // paint the emblem half as tall as the applet, anchored at the top-right - // assume it is a square emblem - qreal height = boundingRect().height() / 2; - int y = standardPadding(); - int x = contentsRect.right() - y - height; - QRectF rect( x, y, height, height ); - svg.render( p, rect ); - } - p->restore(); -} - -void -CurrentTrack::clearTrackActions() -{ - prepareGeometryChange(); - int actionCount = m_actionsLayout->count(); - while( --actionCount >= 0 ) - { - QGraphicsLayoutItem *child = m_actionsLayout->itemAt( 0 ); - m_actionsLayout->removeItem( child ); - delete child; - } - qDeleteAll( m_customActions ); - qDeleteAll( m_contextActions ); - m_customActions.clear(); - m_contextActions.clear(); -} - -void -CurrentTrack::resizeCover( const QPixmap &cover, qreal width ) -{ - QPixmap coverWithBorders; - if( !cover.isNull() ) - { - const int borderWidth = 5; - width -= borderWidth * 2; - qreal pixmapRatio = (qreal)cover.width() / width; - - //center the cover : if the cover is not squared, we get the missing pixels and center - coverWithBorders = ( cover.height() / pixmapRatio > width ) - ? cover.scaledToHeight( width, Qt::SmoothTransformation ) - : cover.scaledToWidth( width, Qt::SmoothTransformation ); - - coverWithBorders = The::svgHandler()->addBordersToPixmap( coverWithBorders, - borderWidth, - m_album->text(), - true ); - } - m_albumCover->setPixmap( coverWithBorders ); - m_albumCover->graphicsItem()->setAcceptDrops( true ); -} - -void -CurrentTrack::settingsAccepted() -{ - QFont font = ui_Settings.fontRequester->font(); - m_showEditTrackDetailsAction = (ui_Settings.editTrackDetailsCheckBox->checkState() == Qt::Checked); - - m_title->setFont( font ); - m_artist->setFont( font ); - m_album->setFont( font ); - - KConfigGroup config = Amarok::config("Current Track Applet"); - config.writeEntry( "Font", font.toString() ); - config.writeEntry( "ShowEditTrackAction", m_showEditTrackDetailsAction ); - - clearTrackActions(); - setupLayoutActions( The::engineController()->currentTrack() ); -} - -void -CurrentTrack::queryCollection() -{ - Collections::QueryMaker *qmTracks = CollectionManager::instance()->queryMaker(); - Collections::QueryMaker *qmAlbums = CollectionManager::instance()->queryMaker(); - Collections::QueryMaker *qmArtists = CollectionManager::instance()->queryMaker(); - connect( qmTracks, SIGNAL(newResultReady(QStringList)), - this, SLOT(tracksCounted(QStringList)) ); - connect( qmAlbums, SIGNAL(newResultReady(QStringList)), - this, SLOT(albumsCounted(QStringList)) ); - connect( qmArtists, SIGNAL(newResultReady(QStringList)), - this, SLOT(artistsCounted(QStringList)) ); - - qmTracks->setAutoDelete( true ) - ->setQueryType( Collections::QueryMaker::Custom ) - ->addReturnFunction( Collections::QueryMaker::Count, Meta::valUrl ) - ->run(); - qmAlbums->setAutoDelete( true ) - ->setQueryType( Collections::QueryMaker::Custom ) - ->addReturnFunction( Collections::QueryMaker::Count, Meta::valAlbum ) - ->run(); - qmArtists->setAutoDelete( true ) - ->setQueryType( Collections::QueryMaker::Custom ) - ->addReturnFunction( Collections::QueryMaker::Count, Meta::valArtist ) - ->run(); -} - -void -CurrentTrack::tracksCounted( QStringList results ) -{ - m_trackCount = !results.isEmpty() ? results.first().toInt() : 0; - update(); -} - -void -CurrentTrack::albumsCounted( QStringList results ) -{ - m_albumCount = !results.isEmpty() ? results.first().toInt() : 0; - update(); -} - -void -CurrentTrack::artistsCounted( QStringList results ) -{ - m_artistCount = !results.isEmpty() ? results.first().toInt() : 0; - update(); -} - -void -CurrentTrack::createConfigurationInterface( KConfigDialog *parent ) -{ - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); - QVBoxLayout *mainLayout = new QVBoxLayout; - parent->setLayout(mainLayout); - QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); - okButton->setDefault(true); - okButton->setShortcut(Qt::CTRL | Qt::Key_Return); - parent->connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); - parent->connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - mainLayout->addWidget(buttonBox); - - KConfigGroup configuration = config(); - QWidget *settings = new QWidget; - ui_Settings.setupUi( settings ); - ui_Settings.fontRequester->setFont( m_title->font() ); - ui_Settings.editTrackDetailsCheckBox->setCheckState( m_showEditTrackDetailsAction ? Qt::Checked : Qt::Unchecked ); - - parent->addPage( settings, i18n( "Current Track Settings" ), "preferences-system"); - - connect( parent, SIGNAL(accepted()), this, SLOT(settingsAccepted()) ); -} - -void -CurrentTrack::coverDropped( const QPixmap &cover ) -{ - DEBUG_BLOCK - Meta::TrackPtr track = The::engineController()->currentTrack(); - if( !track ) - return; - - Meta::AlbumPtr album = track->album(); - if( !album ) - return; - - if ( !cover.isNull() ) - album->setImage( cover.toImage() ); -} - -void -CurrentTrack::paletteChanged( const QPalette &palette ) -{ - m_title->setBrush( palette.text() ); - m_artist->setBrush( palette.text() ); - m_album->setBrush( palette.text() ); - m_byText->setBrush( palette.text() ); - m_onText->setBrush( palette.text() ); -} - -void -CurrentTrack::alignBaseLineToFirst( TextScrollingWidget *a, QGraphicsSimpleTextItem *b ) -{ - qreal guideY = a->pos().y() + QFontMetricsF( a->font() ).ascent(); - qreal newY = guideY - QFontMetricsF( b->font() ).ascent(); - b->setPos( b->x(), newY ); -} - -QBrush -CurrentTrack::normalBrush() const -{ - return The::paletteHandler()->palette().brush( QPalette::Active, QPalette::Text ); -} - -QBrush -CurrentTrack::unknownBrush() const -{ - return The::paletteHandler()->palette().brush( QPalette::Disabled, QPalette::Text ); -} - -QString -CurrentTrack::handleUnknown( const QString &original, - TextScrollingWidget *widget, - const QString &replacement ) -{ - if( original.isEmpty() ) - { - widget->setBrush( unknownBrush() ); - return replacement; - } - else - { - widget->setBrush( normalBrush() ); - return original; - } -} - -void -CurrentTrack::setupLayoutActions( Meta::TrackPtr track ) -{ - if( !track ) - return; - - PERF_LOG( "Begin actions layout setup" ); - //first, add any global CurrentTrackActions (iow, actions that are shown for all tracks) - QList actions = The::globalCurrentTrackActions()->actions(); - - using namespace Capabilities; - - QScopedPointer ac( track->create() ); - if( ac ) - { - QList trackActions = ac->actions(); - // ensure that the actions get deleted afterwards - foreach( QAction* action, trackActions ) - { - if( !action->parent() ) - action->setParent( this ); - actions << action; - } - } - - QScopedPointer btc( track->create() ); - if( btc && btc->bookmarkAction() ) - { - actions << btc->bookmarkAction(); - } - - if( m_showEditTrackDetailsAction && track->editor() ) - { - QAction *editAction = new QAction( QIcon::fromTheme("media-track-edit-amarok"), - i18n("Edit Track Details"), this ); - connect( editAction, SIGNAL(triggered()), SLOT(editTrack()) ); - m_customActions << editAction; - } - - if( track->has() ) - { - if( !m_findInSourceSignalMapper ) - { - m_findInSourceSignalMapper = new QSignalMapper( this ); - connect( m_findInSourceSignalMapper, SIGNAL(mapped(QString)), SLOT(findInSource(QString)) ); - } - - Meta::AlbumPtr album = track->album(); - Meta::ArtistPtr artist = track->artist(); - Meta::ComposerPtr composer = track->composer(); - Meta::GenrePtr genre = track->genre(); - Meta::YearPtr year = track->year(); - QAction *act( 0 ); - - if( album && !album->name().isEmpty() ) - { - act = new QAction( QIcon::fromTheme("current-track-amarok"), i18n("Show Album in Media Sources"), this ); - connect( act, SIGNAL(triggered()), m_findInSourceSignalMapper, SLOT(map()) ); - m_findInSourceSignalMapper->setMapping( act, QLatin1String("album") ); - m_customActions << act; - } - if( artist && !artist->name().isEmpty() ) - { - act = new QAction( QIcon::fromTheme("filename-artist-amarok"), i18n("Show Artist in Media Sources"), this ); - connect( act, SIGNAL(triggered()), m_findInSourceSignalMapper, SLOT(map()) ); - m_findInSourceSignalMapper->setMapping( act, QLatin1String("artist") ); - m_customActions << act; - - // show a special action if the amazon plugin is enabled - KPluginInfo::List services = The::pluginManager()->plugins( Plugins::PluginManager::Service ); - foreach( const KPluginInfo &service, services ) - { - if( service.pluginName() == QLatin1String("amarok_service_amazonstore") ) - { - if( service.isPluginEnabled() ) - { - act = new QAction( QIcon::fromTheme("view-services-amazon-amarok"), - i18n("Search for Artist in the MP3 Music Store"), this ); - connect( act, SIGNAL(triggered()), this, SLOT(findInStore()) ); - m_customActions << act; - } - break; - } - } - } - if( composer && !composer->name().isEmpty() && (composer->name() != i18n("Unknown Composer")) ) - { - act = new QAction( QIcon::fromTheme("filename-composer-amarok"), i18n("Show Composer in Media Sources"), this ); - connect( act, SIGNAL(triggered()), m_findInSourceSignalMapper, SLOT(map()) ); - m_findInSourceSignalMapper->setMapping( act, QLatin1String("composer") ); - m_customActions << act; - } - if( genre && !genre->name().isEmpty() ) - { - act = new QAction( QIcon::fromTheme("filename-genre-amarok"), i18n("Show Genre in Media Sources"), this ); - connect( act, SIGNAL(triggered()), m_findInSourceSignalMapper, SLOT(map()) ); - m_findInSourceSignalMapper->setMapping( act, QLatin1String("genre") ); - m_customActions << act; - } - if( year && !year->name().isEmpty() ) - { - act = new QAction( QIcon::fromTheme("filename-year-amarok"), i18n("Show Year in Media Sources"), this ); - connect( act, SIGNAL(triggered()), m_findInSourceSignalMapper, SLOT(map()) ); - m_findInSourceSignalMapper->setMapping( act, QLatin1String("year") ); - m_customActions << act; - } - } - - actions << m_customActions; - foreach( QAction* action, actions ) - { - Plasma::IconWidget *icon = addAction( this, action, 24 ); - icon->setText( QString() ); - m_actionsLayout->addItem( icon ); - } - PERF_LOG( "Finished actions layout setup" ); -} - -void -CurrentTrack::findInSource( const QString &name ) -{ - using namespace Capabilities; - Meta::TrackPtr track = The::engineController()->currentTrack(); - QScopedPointer fis( track->create() ); - if( !fis ) - return; - - if( name == QLatin1String("album") ) - fis->findInSource( FindInSourceCapability::Album ); - else if( name == QLatin1String("artist") ) - fis->findInSource( FindInSourceCapability::Artist ); - else if( name == QLatin1String("composer") ) - fis->findInSource( FindInSourceCapability::Composer ); - else if( name == QLatin1String("genre") ) - fis->findInSource( FindInSourceCapability::Genre ); - else if( name == QLatin1String("year") ) - fis->findInSource( FindInSourceCapability::Year ); -} - -void -CurrentTrack::findInStore() -{ - Meta::TrackPtr track = The::engineController()->currentTrack(); - AmarokUrl url( "amarok://navigate/internet/MP3%20Music%20Store/?filter=\"" + AmarokUrl::escape( track.data()->artist().data()->name() ) + '\"' ); - url.run(); -} - -void -CurrentTrack::setView( CurrentTrack::View mode ) -{ - m_view = mode; - m_isStopped = ( mode == CurrentTrack::Stopped ); - if( m_isStopped ) - { - m_coverKey = 0; - m_currentInfo.clear(); - m_sourceEmblemPath.clear(); - m_albumCover->setPixmap( Amarok::semiTransparentLogo(m_albumWidth) ); - m_albumCover->graphicsItem()->setAcceptDrops( false ); - m_albumCover->graphicsItem()->unsetCursor(); - clearTrackActions(); - updateConstraints(); - } - else - { - m_albumCover->graphicsItem()->setCursor( Qt::PointingHandCursor ); - } - - m_collectionLabel->setVisible( m_isStopped ); - m_recentWidget->setVisible( m_isStopped ); - m_recentHeader->setVisible( m_isStopped ); - - m_ratingWidget->setVisible( !m_isStopped ); - m_byText->setVisible( !m_isStopped ); - m_onText->setVisible( !m_isStopped ); - m_title->setVisible( !m_isStopped ); - m_artist->setVisible( !m_isStopped ); - m_album->setVisible( !m_isStopped ); -} - diff --git a/src/context/applets/currenttrack/CurrentTrack.h b/src/context/applets/currenttrack/CurrentTrack.h deleted file mode 100644 index a718a8f3cc..0000000000 --- a/src/context/applets/currenttrack/CurrentTrack.h +++ /dev/null @@ -1,142 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007-2009 Leo Franchi * - * Copyright (c) 2008 William Viana Soares * - * Copyright (c) 2009 simon.esneault * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef CURRENT_TRACK_APPLET_H -#define CURRENT_TRACK_APPLET_H - -#include "context/Applet.h" -#include "core/meta/forward_declarations.h" -#include "ui_currentTrackSettings.h" - -#include - -class TextScrollingWidget; -class DropPixmapLayoutItem; -class RatingWidget; -class RecentlyPlayedListWidget; -class QAction; -class QGraphicsLinearLayout; -class QGraphicsProxyWidget; -class QGraphicsSceneMouseEvent; -class QSignalMapper; - -static const KLocalizedString UNKNOWN_ARTIST = ki18n("Unknown Artist"); -static const KLocalizedString UNKNOWN_ALBUM = ki18n("Unknown Album"); - -class CurrentTrack : public Context::Applet -{ - Q_OBJECT - -public: - CurrentTrack( QObject* parent, const QVariantList& args ); - ~CurrentTrack(); - - virtual void paintInterface( QPainter *painter, - const QStyleOptionGraphicsItem *option, - const QRect &contentsRect ); - -public Q_SLOTS: - virtual void init(); - void dataUpdated( const QString& name, const Plasma::DataEngine::Data &data ); - -protected: - virtual void mousePressEvent( QGraphicsSceneMouseEvent *event ); - virtual void constraintsEvent( Plasma::Constraints constraints = Plasma::AllConstraints ); - virtual QSizeF sizeHint( Qt::SizeHint which, const QSizeF &constraint = QSizeF() ) const; - void createConfigurationInterface( KConfigDialog *parent ); - -private Q_SLOTS: - void trackRatingChanged( int rating ); - void paletteChanged( const QPalette &palette ); - void settingsAccepted(); - void coverDropped( const QPixmap &cover ); - void tracksCounted( QStringList results ); - void albumsCounted( QStringList results ); - void artistsCounted( QStringList results ); - void findInSource( const QString &name ); - void findInStore(); - void queryCollection(); - void editTrack(); - -private: - QList contextualActions(); - - void clearTrackActions(); - void drawStatsBackground( QPainter *const p, const QRect &rect ); - void drawStatsTexts( QPainter *const p, const QRect &rect ); - void drawSourceEmblem( QPainter *const p, const QRect &rect ); - void resizeCover( const QPixmap &cover, qreal width ); - void setupLayoutActions( Meta::TrackPtr track ); - - // aligns the second QGI to be at the same level as the first (the font baseline) - void alignBaseLineToFirst( TextScrollingWidget *a, QGraphicsSimpleTextItem *b ); - - QBrush normalBrush() const; - QBrush unknownBrush() const; - /** - * Bug 205038 - * We check if original is an 'invalid' value - * In that case we return replacement and - * set widget's brush to unknownBrush() - * - * If original is 'valid', widget brush is set - * to normalBrush() and original is returned - */ - QString handleUnknown( const QString &original, - TextScrollingWidget *widget, - const QString &replacement ); - - QGraphicsProxyWidget *m_collectionLabel; - RecentlyPlayedListWidget *m_recentWidget; - RatingWidget *m_ratingWidget; - DropPixmapLayoutItem *m_albumCover; - TextScrollingWidget *m_recentHeader; - TextScrollingWidget *m_title; - TextScrollingWidget *m_artist; - TextScrollingWidget *m_album; - QGraphicsSimpleTextItem *m_byText; - QGraphicsSimpleTextItem *m_onText; - QGraphicsLinearLayout *m_actionsLayout; - QSignalMapper *m_findInSourceSignalMapper; - QList m_customActions; // for storing non global actions - QList m_contextActions; - - int m_rating; - int m_score; - int m_trackLength; - int m_playCount; - int m_trackCount; - int m_albumCount; - int m_artistCount; - QDateTime m_lastPlayed; - QString m_sourceEmblemPath; - bool m_isStopped; - QVariantMap m_currentInfo; - qint64 m_coverKey; - - enum View { Stopped, Playing } m_view; - void setView( View mode ); - - Ui::currentTrackSettings ui_Settings; - bool m_showEditTrackDetailsAction; - const int m_albumWidth; -}; - -AMAROK_EXPORT_APPLET( currenttrack, CurrentTrack ) - -#endif diff --git a/src/context/applets/currenttrack/currentTrackSettings.ui b/src/context/applets/currenttrack/currentTrackSettings.ui deleted file mode 100644 index c006384021..0000000000 --- a/src/context/applets/currenttrack/currentTrackSettings.ui +++ /dev/null @@ -1,81 +0,0 @@ - - - currentTrackSettings - - - - 0 - 0 - 184 - 156 - - - - - 0 - 0 - - - - Current Track Settings - - - - - - - 0 - 0 - - - - Show Actions - - - - - - Edit Track Details - - - - - - - - - - - 0 - 0 - - - - Fonts - - - - - - - 0 - 0 - - - - - - - - - - - - KFontRequester - QWidget -
kfontrequester.h
-
-
- - -
diff --git a/src/context/applets/currenttrack/amarok-currenttrack.svg b/src/context/applets/currenttrack/package/contents/images/amarok-currenttrack.svg similarity index 100% rename from src/context/applets/currenttrack/amarok-currenttrack.svg rename to src/context/applets/currenttrack/package/contents/images/amarok-currenttrack.svg diff --git a/src/context/applets/currenttrack/package/contents/ui/InfoItem.qml b/src/context/applets/currenttrack/package/contents/ui/InfoItem.qml new file mode 100644 index 0000000000..19a4d85041 --- /dev/null +++ b/src/context/applets/currenttrack/package/contents/ui/InfoItem.qml @@ -0,0 +1,79 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 +import org.kde.amarok.currenttrack 1.0 + +ColumnLayout { + id: root + + property alias title: titleLabel.text + property alias album: albumLabel.text + property alias artist: artistLabel.text + + spacing: Context.smallSpacing + + Label { + id: titleLabel + + text: CurrentTrackEngine.track + + Layout.fillHeight: true + Layout.fillWidth: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + maximumLineCount: 1 + fontSizeMode: Text.Fit + font.pointSize: 32 + minimumPointSize: 12 + elide: Text.ElideRight + } + Label { + id: artistLabel + + text: CurrentTrackEngine.artist + + Layout.fillHeight: true + Layout.fillWidth: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + maximumLineCount: 1 + fontSizeMode: Text.Fit + font.pointSize: 32 + minimumPointSize: 12 + elide: Text.ElideRight + } + Label { + id: albumLabel + + text: CurrentTrackEngine.album + + Layout.fillHeight: true + Layout.fillWidth: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + maximumLineCount: 1 + fontSizeMode: Text.Fit + font.pointSize: 32 + minimumPointSize: 12 + elide: Text.ElideRight + } +} diff --git a/src/context/applets/currenttrack/package/contents/ui/StatsItem.qml b/src/context/applets/currenttrack/package/contents/ui/StatsItem.qml new file mode 100644 index 0000000000..adc9a7c22d --- /dev/null +++ b/src/context/applets/currenttrack/package/contents/ui/StatsItem.qml @@ -0,0 +1,99 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 +import org.kde.amarok.currenttrack 1.0 + +Column { + id: root + + property real textSize: Context.largeSpacing + property alias playCount: playCountLabel.text + property alias score: scoreLabel.text + property alias lastPlayed: lastPlayedLabel.text + + Row { + width: parent.width + + Label { + text: i18n("Play Count") + width: parent.width / 3 + horizontalAlignment: Text.AlignHCenter + maximumLineCount: 1 + font.pixelSize: root.textSize + } + Label { + text: i18n("Score") + width: parent.width / 3 + horizontalAlignment: Text.AlignHCenter + maximumLineCount: 1 + font.pixelSize: root.textSize + } + Label { + text: i18n("Last played") + width: parent.width / 3 + horizontalAlignment: Text.AlignHCenter + maximumLineCount: 1 + font.pixelSize: root.textSize + } + } + Rectangle { + width: parent.width + height: childrenRect.height + color: palette.base + + Row { + width: parent.width + + Label { + id: playCountLabel + + width: parent.width / 3 + horizontalAlignment: Text.AlignHCenter + maximumLineCount: 1 + font.pixelSize: root.textSize + text: CurrentTrackEngine.timesPlayed + elide: Text.ElideRight + } + Label { + id: scoreLabel + + width: parent.width / 3 + horizontalAlignment: Text.AlignHCenter + maximumLineCount: 1 + font.pixelSize: root.textSize + text: CurrentTrackEngine.score + elide: Text.ElideRight + } + Label { + id: lastPlayedLabel + + width: parent.width / 3 + horizontalAlignment: Text.AlignHCenter + maximumLineCount: 1 + font.pixelSize: root.textSize + text: CurrentTrackEngine.lastPlayed + elide: Text.ElideRight + } + } + } + + SystemPalette { + id: palette + } +} diff --git a/src/context/applets/currenttrack/package/contents/ui/main.qml b/src/context/applets/currenttrack/package/contents/ui/main.qml new file mode 100644 index 0000000000..dbb8557af1 --- /dev/null +++ b/src/context/applets/currenttrack/package/contents/ui/main.qml @@ -0,0 +1,97 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 +import org.kde.amarok.qml 1.0 as AmarokQml +import org.kde.amarok.currenttrack 1.0 + +AmarokQml.Applet { + id: applet + + //album art + Rectangle { + id: cover + + color: "white" + radius: Context.smallSpacing / 2 + border.width: 1 + border.color: applet.palette.light + height: parent.height + width: height + + AmarokQml.PixmapItem { + id: iconItem + + anchors.fill: parent + anchors.margins: parent.radius + source: CurrentTrackEngine.cover + + onWidthChanged: CurrentTrackEngine.coverWidth = width + + //show standard empty cover if no data is available + AmarokQml.PixmapItem { + anchors.fill: parent + source: Svg.renderSvg("file://" + applet.packagePath + "images/amarok-currenttrack.svg", + "CurrentTrack", + width, + height, + "album_old"); + visible: !iconItem.valid + } + } + } + ColumnLayout { + anchors { + left: cover.right + leftMargin: applet.spacing + right: parent.right + top: parent.top + bottom: parent.bottom + } + + RowLayout { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.alignment: Qt.AlignTop + + InfoItem { + id: infoItem + + Layout.fillHeight: true + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft + } + AmarokQml.RatingItem { + id: ratingItem + + height: Context.largeSpacing * 2 + width: height * 6 + Layout.alignment: Qt.AlignTop | Qt.AlignRight + rating: CurrentTrackEngine.rating + onClicked: CurrentTrackEngine.rating = newRating + } + } + StatsItem { + id: statsItem + + Layout.fillWidth: true + Layout.alignment: Qt.AlignBottom + height: Context.largeSpacing * 3 + } + } +} diff --git a/src/context/applets/currenttrack/amarok-context-applet-currenttrack.desktop b/src/context/applets/currenttrack/package/metadata.desktop similarity index 89% rename from src/context/applets/currenttrack/amarok-context-applet-currenttrack.desktop rename to src/context/applets/currenttrack/package/metadata.desktop index bdb3b9fa28..d5250c3ba1 100644 --- a/src/context/applets/currenttrack/amarok-context-applet-currenttrack.desktop +++ b/src/context/applets/currenttrack/package/metadata.desktop @@ -1,70 +1,69 @@ [Desktop Entry] Name=Current Track Name[bg]=Текущ запис Name[bs]=Trenutna numera Name[ca]=Peça actual Name[ca@valencia]=Peça actual Name[cs]=Aktuální stopa Name[csb]=Biéżny sztëczk Name[da]=Aktuelt spor Name[de]=Aktuelles Stück Name[el]=Τρέχον κομμάτι Name[en_GB]=Current Track Name[es]=Pista actual Name[et]=Aktiivne pala Name[eu]=Uneko pista Name[fa]=شیار جاری Name[fi]=Soiva kappale Name[fr]=Piste courante Name[ga]=An tAmhrán Reatha Name[gl]=Pista actual Name[hu]=Jelenlegi szám Name[id]=Track Saat Ini Name[is]=Núverandi lag Name[it]=Traccia attuale Name[ja]=現在のトラック Name[km]=​បទ​បច្ចុប្បន្ន Name[ko]=현재 곡 Name[lt]=Dabartinė daina Name[lv]=Aktīvais celiņš Name[nb]=Gjeldende spor Name[nds]=Aktuell Stück Name[nl]=Huidige track Name[nn]=Gjeldande spor Name[pa]=ਮੌਜੂਦਾ ਟਰੈਕ Name[pl]=Bieżący utwór Name[pt]=Faixa Actual Name[pt_BR]=Faixa atual Name[ro]=Piesa curentă Name[ru]=Текущая дорожка Name[sk]=Aktuálna skladba Name[sl]=Trenutna skladba Name[sr]=Текућа нумера Name[sr@ijekavian]=Текућа нумера Name[sr@ijekavianlatin]=Tekuća numera Name[sr@latin]=Tekuća numera Name[sv]=Aktuellt spår Name[th]=แทร็กปัจจุบัน Name[tr]=Mevcut Parça Name[ug]=نۆۋەتتىكى نەغمە Name[uk]=Поточна композиція Name[wa]=Boket do moumint Name[x-test]=xxCurrent Trackxx Name[zh_CN]=当前音轨 Name[zh_TW]=現在的曲目 + Type=Service Icon=current-track-amarok -ServiceTypes=Plasma/Applet +ServiceTypes=Amarok/ContextApplet -X-KDE-Library=amarok_context_applet_currenttrack +X-KDE-PluginInfo-Name=org.kde.amarok.currenttrack X-KDE-PluginInfo-Author=Leo Franchi X-KDE-PluginInfo-Email=lfranchi@gmail.com -X-KDE-PluginInfo-Name=currenttrack -X-KDE-PluginInfo-Version=pre0.1 +X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website= X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true -X-KDE-ParentApp=amarok -X-KDE-PluginInfo-Category=Current +X-KDE-PluginInfo-Category=Multimedia diff --git a/src/context/applets/currenttrack/plugin/CurrentEngine.cpp b/src/context/applets/currenttrack/plugin/CurrentEngine.cpp new file mode 100644 index 0000000000..2cbbe73412 --- /dev/null +++ b/src/context/applets/currenttrack/plugin/CurrentEngine.cpp @@ -0,0 +1,283 @@ +/**************************************************************************************** + * Copyright (c) 2007 Leo Franchi * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +#define DEBUG_PREFIX "CurrentEngine" + +#include "CurrentEngine.h" + +#include "EngineController.h" +#include "core/support/Debug.h" +#include "core/capabilities/SourceInfoCapability.h" +#include "core/collections/Collection.h" +#include "core/collections/QueryMaker.h" +#include "core/meta/support/MetaUtility.h" +#include "core/meta/Statistics.h" +#include "core/support/Amarok.h" +#include "core-impl/collections/support/CollectionManager.h" +#include "covermanager/CoverCache.h" + +#include + +#include +#include + + +CurrentEngine::CurrentEngine( QObject* parent ) + : QObject( parent ) + , m_coverWidth( 0 ) + , m_lastQueryMaker( Q_NULLPTR ) +{ + EngineController* engine = The::engineController(); + + connect( engine, &EngineController::trackPlaying, + this, &CurrentEngine::slotTrackChanged ); + connect( engine, &EngineController::stopped, + this, &CurrentEngine::stopped ); + connect( engine, &EngineController::trackMetadataChanged, + this, &CurrentEngine::slotTrackMetadataChanged ); + connect( engine, &EngineController::albumMetadataChanged, + this, &CurrentEngine::slotAlbumMetadataChanged ); +} + +CurrentEngine::~CurrentEngine() +{ +} + +void +CurrentEngine::slotAlbumMetadataChanged( Meta::AlbumPtr album ) +{ + DEBUG_BLOCK + + // disregard changes for other albums (BR: 306735) + if( !m_currentTrack || m_currentTrack->album() != album ) + return; + + QPixmap cover; + + if( album ) + cover = The::coverCache()->getCover( album, m_coverWidth ); + + if( m_cover.cacheKey() != cover.cacheKey() ) + { + m_cover = cover; + emit albumChanged(); + } +} + +void +CurrentEngine::slotTrackMetadataChanged( Meta::TrackPtr track ) +{ + if( !track ) + return; + + update( track->album() ); + emit trackChanged(); +} + +void +CurrentEngine::slotTrackChanged(Meta::TrackPtr track) +{ + DEBUG_BLOCK + + if( !track || track == m_currentTrack ) + return; + + m_currentTrack = track; + slotTrackMetadataChanged( track ); +} + + +void +CurrentEngine::stopped() +{ + m_currentTrack.clear(); + emit trackChanged(); + + m_cover = QPixmap(); + + // Collect data for the recently added albums + m_albums.clear(); + emit albumChanged(); + + Collections::QueryMaker *qm = CollectionManager::instance()->queryMaker(); + qm->setAutoDelete( true ); + qm->setQueryType( Collections::QueryMaker::Album ); + qm->excludeFilter( Meta::valAlbum, QString(), true, true ); + qm->orderBy( Meta::valCreateDate, true ); + qm->limitMaxResultSize( Amarok::config("Albums Applet").readEntry("RecentlyAdded", 5) ); + + connect( qm, &Collections::QueryMaker::newAlbumsReady, + this, &CurrentEngine::resultReady, Qt::QueuedConnection ); + + m_lastQueryMaker = qm; + qm->run(); +} + +void +CurrentEngine::update( Meta::AlbumPtr album ) +{ + m_lastQueryMaker = Q_NULLPTR; + + if( !album ) + return; + + slotAlbumMetadataChanged( album ); + + Meta::TrackPtr track = The::engineController()->currentTrack(); + + if( !track ) + return; + + Meta::ArtistPtr artist = track->artist(); + + // Prefer track artist to album artist BUG: 266682 + if( !artist ) + artist = album->albumArtist(); + + if( artist && !artist->name().isEmpty() ) + { + m_albums.clear(); + + // -- search the collection for albums with the same artist + Collections::QueryMaker *qm = CollectionManager::instance()->queryMaker(); + qm->setAutoDelete( true ); + qm->addFilter( Meta::valArtist, artist->name(), true, true ); + qm->setAlbumQueryMode( Collections::QueryMaker::AllAlbums ); + qm->setQueryType( Collections::QueryMaker::Album ); + + connect( qm, &Collections::QueryMaker::newAlbumsReady, + this, &CurrentEngine::resultReady, Qt::QueuedConnection ); + + m_lastQueryMaker = qm; + qm->run(); + } +} + +void +CurrentEngine::resultReady( const Meta::AlbumList &albums ) +{ + if( sender() == m_lastQueryMaker ) + m_albums << albums; +} + +QString +CurrentEngine::artist() const +{ + if( !m_currentTrack ) + return QString(); + + return m_currentTrack->artist()->prettyName(); +} + +QString +CurrentEngine::track() const +{ + if( !m_currentTrack ) + return QString(); + + return m_currentTrack->prettyName(); +} + +QString +CurrentEngine::album() const +{ + if( !m_currentTrack ) + return QString(); + + return m_currentTrack->album()->prettyName(); +} + +int +CurrentEngine::rating() const +{ + if( !m_currentTrack ) + return 0; + + return m_currentTrack->statistics()->rating(); +} + +void +CurrentEngine::setRating(int rating) +{ + DEBUG_BLOCK + + debug() << "New rating:" << rating; + + if( !m_currentTrack ) + return; + + if( rating == m_currentTrack->statistics()->rating() ) + return; + + m_currentTrack->statistics()->setRating( rating ); + emit trackChanged(); +} + +int +CurrentEngine::score() const +{ + if( !m_currentTrack ) + return 0; + + return m_currentTrack->statistics()->score(); +} + +int +CurrentEngine::length() const +{ + if( !m_currentTrack ) + return 0; + + return m_currentTrack->length(); +} + +QString +CurrentEngine::lastPlayed() const +{ + if( !m_currentTrack ) + return QString(); + + auto lastPlayed = m_currentTrack->statistics()->lastPlayed(); + QString lastPlayedString; + if( lastPlayed.isValid() ) + lastPlayedString = KFormat().formatRelativeDateTime( lastPlayed, QLocale::ShortFormat ); + else + lastPlayedString = i18n( "Never" ); + + return lastPlayedString; +} + +int +CurrentEngine::timesPlayed() const +{ + if( !m_currentTrack ) + return 0; + + return m_currentTrack->statistics()->playCount(); +} + +void +CurrentEngine::setCoverWidth(int width) +{ + if( m_coverWidth == width ) + return; + + m_coverWidth = width; + emit coverWidthChanged(); + + if( m_currentTrack ) + slotAlbumMetadataChanged( m_currentTrack->album() ); +} diff --git a/src/context/engines/current/CurrentEngine.h b/src/context/applets/currenttrack/plugin/CurrentEngine.h similarity index 58% rename from src/context/engines/current/CurrentEngine.h rename to src/context/applets/currenttrack/plugin/CurrentEngine.h index eec21ae05c..e06353b8d1 100644 --- a/src/context/engines/current/CurrentEngine.h +++ b/src/context/applets/currenttrack/plugin/CurrentEngine.h @@ -1,95 +1,92 @@ /**************************************************************************************** * Copyright (c) 2007 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef AMAROK_CURRENT_ENGINE #define AMAROK_CURRENT_ENGINE -#include "context/DataEngine.h" -#include "core/meta/forward_declarations.h" +#include "core/meta/Meta.h" + +#include +#include +#include + namespace Collections { class QueryMaker; } -/** - This class provides context information on the currently playing track. - This includes info such as the artist, trackname, album of the current song, etc. - - There is no data source: if you connect to the engine, you immediately - start getting updates when there is data. - - The key of the data is "current". - The data is structured as a QVariantList, with the order: - * Artist - * Track - * Album - * Rating (0-10) - * Score - * Track Length - * Last Played - * Number of times played - * Album cover (QImage) - -*/ -class CurrentEngine : public Context::DataEngine +class CurrentEngine : public QObject { Q_OBJECT - Q_PROPERTY( int coverWidth READ coverWidth WRITE setCoverWidth ) + Q_PROPERTY(QString artist READ artist NOTIFY trackChanged) + Q_PROPERTY(QString track READ track NOTIFY trackChanged) + Q_PROPERTY(QString album READ album NOTIFY trackChanged) + Q_PROPERTY(int rating READ rating WRITE setRating NOTIFY trackChanged) + Q_PROPERTY(int score READ score NOTIFY trackChanged) + Q_PROPERTY(int length READ length NOTIFY trackChanged) + Q_PROPERTY(QString lastPlayed READ lastPlayed NOTIFY trackChanged) + Q_PROPERTY(int timesPlayed READ timesPlayed NOTIFY trackChanged) + Q_PROPERTY(QVariant cover READ cover NOTIFY albumChanged) + Q_PROPERTY(int coverWidth READ coverWidth WRITE setCoverWidth NOTIFY coverWidthChanged) public: - CurrentEngine( QObject* parent, const QList& args ); + CurrentEngine( QObject* parent = Q_NULLPTR ); virtual ~CurrentEngine(); - QStringList sources() const; - + QString artist() const; + QString track() const; + QString album() const; + int rating() const; + void setRating( int rating ); + int score() const; + int length() const; + QString lastPlayed() const; + int timesPlayed() const; + QVariant cover() const { return QVariant(m_cover); } int coverWidth() { return m_coverWidth; } - void setCoverWidth( const int width ) { m_coverWidth = width; } + void setCoverWidth( int width ); + +signals: + void trackChanged(); + void albumChanged(); + void coverWidthChanged(); private Q_SLOTS: - void metadataChanged( Meta::AlbumPtr album ); - void metadataChanged( Meta::TrackPtr track ); - void trackPlaying( Meta::TrackPtr track ); + void slotAlbumMetadataChanged( Meta::AlbumPtr album ); + void slotTrackMetadataChanged( Meta::TrackPtr track ); + void slotTrackChanged( Meta::TrackPtr track ); void stopped(); -protected: - bool sourceRequestEvent( const QString& name ); - private: void update( Meta::TrackPtr track ); void update( Meta::AlbumPtr album ); int m_coverWidth; - QStringList m_sources; - QHash< QString, bool > m_requested; + QPixmap m_cover; Meta::AlbumList m_albums; - Plasma::DataEngine::Data m_albumData; Meta::TrackPtr m_currentTrack; - qint64 m_coverCacheKey; - QVariantMap m_trackInfo; /** The address of the query maker used for the albums query. This is only used to check if the query results are from the latest started query maker. */ Collections::QueryMaker *m_lastQueryMaker; private Q_SLOTS: void resultReady( const Meta::AlbumList &albums ); - void setupAlbumsData(); }; -AMAROK_EXPORT_DATAENGINE( current, CurrentEngine ) #endif diff --git a/src/context/applets/currenttrack/plugin/CurrentPlugin.cpp b/src/context/applets/currenttrack/plugin/CurrentPlugin.cpp new file mode 100644 index 0000000000..6334ba82dd --- /dev/null +++ b/src/context/applets/currenttrack/plugin/CurrentPlugin.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2017 Malte Veerman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "CurrentEngine.h" + +#include + +#include + + +class CurrentPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char* uri) Q_DECL_OVERRIDE + { + Q_ASSERT(uri == QLatin1String("org.kde.amarok.currenttrack")); + + qmlRegisterSingletonType(uri, 1, 0, "CurrentTrackEngine", current_engine_provider); + } + + static QObject *current_engine_provider(QQmlEngine *engine, QJSEngine *scriptEngine) + { + Q_UNUSED(engine) + Q_UNUSED(scriptEngine) + + return new CurrentEngine(); + } +}; + +#include diff --git a/src/context/applets/currenttrack/plugin/qmldir b/src/context/applets/currenttrack/plugin/qmldir new file mode 100644 index 0000000000..9259272d83 --- /dev/null +++ b/src/context/applets/currenttrack/plugin/qmldir @@ -0,0 +1,2 @@ +module org.kde.amarok.currenttrack +plugin amarok_context_applet_currenttrack diff --git a/src/context/applets/info/CMakeLists.txt b/src/context/applets/info/CMakeLists.txt index 8b7b3d7087..521e24a297 100644 --- a/src/context/applets/info/CMakeLists.txt +++ b/src/context/applets/info/CMakeLists.txt @@ -1,26 +1,17 @@ -project(context-info) - set(info_SRCS -InfoApplet.cpp ) + plugin/InfoPlugin.cpp + plugin/InfoEngine.cpp +) -include_directories( - ../.. - ../../.. - ) +add_library(amarok_context_applet_info SHARED ${info_SRCS}) -add_library(amarok_context_applet_info MODULE ${info_SRCS}) -if(APPLE) - set_target_properties(amarok_context_applet_info PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") -endif() target_link_libraries(amarok_context_applet_info amarokcore amaroklib - KF5::Plasma - ${KDE4_KDEWEBKIT_LIBS} - Qt5::WebKitWidgets - ) + Qt5::Qml +) + +install(TARGETS amarok_context_applet_info DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/info) +install(FILES plugin/qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/info) -install(TARGETS amarok_context_applet_info DESTINATION ${PLUGIN_INSTALL_DIR}) -install(FILES amarok-context-applet-info.desktop DESTINATION ${SERVICES_INSTALL_DIR}) -install(FILES amarok-info-applet.svg DESTINATION ${DATA_INSTALL_DIR}/desktoptheme/default/widgets/ ) -install(FILES InfoAppletCustomStyle.css DESTINATION ${DATA_INSTALL_DIR}/amarok/data ) +kpackage_install_package(package org.kde.amarok.info amarok) diff --git a/src/context/applets/info/InfoApplet.cpp b/src/context/applets/info/InfoApplet.cpp deleted file mode 100644 index 93b4d1b1d8..0000000000 --- a/src/context/applets/info/InfoApplet.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * Copyright (c) 2008 Nikolaj Hald Nielsen * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "InfoApplet" - -#include "InfoApplet.h" - -#include "App.h" -#include "amarokurls/AmarokUrl.h" -#include "core/support/Amarok.h" -#include "core/support/Debug.h" -#include "PaletteHandler.h" -#include "playlist/PlaylistController.h" - -#include -#include - -#include -#include -#include - -QString InfoApplet::s_defaultHtml = "" - " " - " " - " " - " " - " %%SUBJECT_NAME%%" - " " - ""; - -InfoApplet::InfoApplet( QObject* parent, const QVariantList& args ) - : Context::Applet( parent, args ) - , m_webView( 0 ) - , m_initialized( false ) -{ - setHasConfigurationInterface( false ); -} - -InfoApplet::~InfoApplet() -{ - delete m_webView; -} - - -void InfoApplet::init() -{ - // Call the base implementation. - Context::Applet::init(); - - dataEngine( "amarok-info" )->connectSource( "info", this ); - - m_webView = new KGraphicsWebView( this ); - - QPalette p = m_webView->palette(); - p.setColor( QPalette::Dark, QColor( 255, 255, 255, 0) ); - p.setColor( QPalette::Window, QColor( 255, 255, 255, 0) ); - m_webView->setPalette( p ); - - QGraphicsLinearLayout *layout = new QGraphicsLinearLayout( Qt::Vertical, this ); - layout->addItem( m_webView ); - - connect( m_webView->page(), SIGNAL(linkClicked(QUrl)), SLOT(linkClicked(QUrl)) ); - - updateConstraints(); -} - -void InfoApplet::constraintsEvent( Plasma::Constraints constraints ) -{ - Q_UNUSED( constraints ) - m_initialized = true; -} - -void InfoApplet::dataUpdated( const QString& name, const Plasma::DataEngine::Data& data ) -{ - Q_UNUSED( name ); - - if( data.isEmpty() ) - return; - - if( m_initialized ) - { - QString currentHtml = data[ "main_info" ].toString(); - if ( !currentHtml.isEmpty() ) - { - QColor highlight( App::instance()->palette().highlight().color() ); - highlight.setHsvF( highlight.hueF(), 0.3, .95, highlight.alphaF() ); - currentHtml = currentHtml.replace( "{text_color}", App::instance()->palette().brush( QPalette::Text ).color().name() ); - currentHtml = currentHtml.replace( "{content_background_color}", highlight.name() ); - currentHtml = currentHtml.replace( "{background_color}", PaletteHandler::highlightColor().lighter( 150 ).name()); - currentHtml = currentHtml.replace( "{border_color}", PaletteHandler::highlightColor().lighter( 150 ).name() ); - - m_webView->setHtml( currentHtml, QUrl( QString() ) ); - } - else - { - currentHtml = s_defaultHtml; - currentHtml = currentHtml.replace( "%%SUBJECT_NAME%%", data[ "subject_name" ].toString() ); - m_webView->setHtml( currentHtml ); - } - - m_webView->page()->setLinkDelegationPolicy( QWebPage::DelegateAllLinks ); - updateConstraints(); - } -} - -void InfoApplet::linkClicked( const QUrl & url ) -{ - DEBUG_BLOCK - debug() << "Link clicked: " << url.toString(); - - if ( url.toString().startsWith( "amarok://", Qt::CaseInsensitive ) ) - { - AmarokUrl aUrl( url.toString() ); - aUrl.run(); - } - else if ( url.toString().contains( ".xspf", Qt::CaseInsensitive ) ) - { - // FIXME: this doesn't work (triggerTrackLoad is not called) and leaks the playlist instance - new Playlists::XSPFPlaylist( url, 0, Playlists::XSPFPlaylist::AppendToPlaylist ); - } - else - QDesktopServices::openUrl( url.toString() ); -} - diff --git a/src/context/applets/info/InfoApplet.h b/src/context/applets/info/InfoApplet.h deleted file mode 100644 index 7d15ff7b45..0000000000 --- a/src/context/applets/info/InfoApplet.h +++ /dev/null @@ -1,58 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef SERVICE_INFO_APPLET_H -#define SERVICE_INFO_APPLET_H - -#include "context/Applet.h" -#include "context/DataEngine.h" -#include "context/Svg.h" -#include "core-impl/playlists/types/file/xspf/XSPFPlaylist.h" -#include "core/playlists/Playlist.h" - -#include - -#include - -class KGraphicsWebView; - -class InfoApplet : public Context::Applet -{ - Q_OBJECT - -public: - InfoApplet( QObject* parent, const QVariantList& args ); - virtual ~InfoApplet(); - - void constraintsEvent( Plasma::Constraints constraints = Plasma::AllConstraints ); - -public Q_SLOTS: - virtual void init(); - void dataUpdated( const QString& name, const Plasma::DataEngine::Data &data ); - -private Q_SLOTS: - void linkClicked( const QUrl & url ); - -private: - KGraphicsWebView *m_webView; - bool m_initialized; - - static QString s_defaultHtml; -}; - -AMAROK_EXPORT_APPLET( info, InfoApplet ) - -#endif diff --git a/src/context/applets/info/InfoAppletCustomStyle.css b/src/context/applets/info/package/contents/html/InfoAppletCustomStyle.css similarity index 100% rename from src/context/applets/info/InfoAppletCustomStyle.css rename to src/context/applets/info/package/contents/html/InfoAppletCustomStyle.css diff --git a/src/context/engines/info/info_frontpage.html b/src/context/applets/info/package/contents/html/info_frontpage.html similarity index 100% rename from src/context/engines/info/info_frontpage.html rename to src/context/applets/info/package/contents/html/info_frontpage.html diff --git a/src/context/engines/info/info_frontpage_bg.png b/src/context/applets/info/package/contents/html/info_frontpage_bg.png similarity index 100% rename from src/context/engines/info/info_frontpage_bg.png rename to src/context/applets/info/package/contents/html/info_frontpage_bg.png diff --git a/src/context/engines/info/info_frontpage_logo.png b/src/context/applets/info/package/contents/html/info_frontpage_logo.png similarity index 100% rename from src/context/engines/info/info_frontpage_logo.png rename to src/context/applets/info/package/contents/html/info_frontpage_logo.png diff --git a/src/context/engines/info/info_frontpage_shadow.png b/src/context/applets/info/package/contents/html/info_frontpage_shadow.png similarity index 100% rename from src/context/engines/info/info_frontpage_shadow.png rename to src/context/applets/info/package/contents/html/info_frontpage_shadow.png diff --git a/src/context/applets/info/amarok-info-applet.svg b/src/context/applets/info/package/contents/images/amarok-info-applet.svg similarity index 100% rename from src/context/applets/info/amarok-info-applet.svg rename to src/context/applets/info/package/contents/images/amarok-info-applet.svg diff --git a/src/context/DataEngine.h b/src/context/applets/info/package/contents/ui/main.qml similarity index 67% rename from src/context/DataEngine.h rename to src/context/applets/info/package/contents/ui/main.qml index 0cdd86f465..ba4c62b7f3 100644 --- a/src/context/DataEngine.h +++ b/src/context/applets/info/package/contents/ui/main.qml @@ -1,33 +1,38 @@ /**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * + * Copyright (c) 2017 Malte Veerman * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ -#ifndef AMAROK_DATA_ENGINE_H -#define AMAROK_DATA_ENGINE_H +import QtQuick 2.4 +// import QtWebView 1.1 +import org.kde.amarok.qml 1.0 as AmarokQml +import org.kde.amarok.info 1.0 -#include - -namespace Context -{ - typedef Plasma::DataEngine DataEngine; - -} // context namespace - -#define AMAROK_EXPORT_DATAENGINE(libname, classname) \ -K_PLUGIN_FACTORY(factory, registerPlugin();) \ -K_EXPORT_PLUGIN(factory("amarok_data_engine_" #libname))\ -K_EXPORT_PLUGIN_VERSION(PLASMA_VERSION) -#endif +AmarokQml.Applet { + id: root +// WebView { +// id: content +// +// anchors.top: parent.top +// width: parent.width +// height: Context.largeSpacing * 20 //TODO: Find a more elegant solution to set the height +// +// Connections { +// target: InfoEngine +// +// onMainInfoChanged: content.loadHtml(InfoEngine.mainInfo, root.packagePath + "html"); +// } +// } +} diff --git a/src/context/applets/info/amarok-context-applet-info.desktop b/src/context/applets/info/package/metadata.desktop similarity index 90% rename from src/context/applets/info/amarok-context-applet-info.desktop rename to src/context/applets/info/package/metadata.desktop index eb5d03933a..8d97b23dd3 100644 --- a/src/context/applets/info/amarok-context-applet-info.desktop +++ b/src/context/applets/info/package/metadata.desktop @@ -1,73 +1,71 @@ [Desktop Entry] Name=Info Name[bg]=Данни Name[bs]=Podaci Name[ca]=Informació Name[ca@valencia]=Informació Name[cs]=Informace Name[csb]=Wëdowiédzô Name[da]=Info Name[de]=Information Name[el]=Πληροφορίες Name[en_GB]=Info Name[es]=Información Name[et]=Teave Name[eu]=Informazioa Name[fa]=اطلاعات Name[fi]=Info Name[fr]=Informations Name[ga]=Eolas Name[gl]=Información Name[hu]=Információ Name[ia]=Info Name[id]=Info Name[is]=Upplýsingar Name[it]=Informazioni Name[ja]=情報 Name[km]=ព័ត៌មាន Name[ko]=정보 Name[lt]=Informacija Name[lv]=Informācija Name[mai]=सूचना Name[mr]=माहिती Name[nb]=Info Name[nds]=Info Name[nl]=Info Name[nn]=Info Name[pa]=ਜਾਣਕਾਰੀ Name[pl]=Informacja Name[pt]=Informação Name[pt_BR]=Informações Name[ro]=Informații Name[ru]=Сведения Name[sk]=Informácie Name[sl]=Podatki Name[sq]=Info Name[sr]=Подаци Name[sr@ijekavian]=Подаци Name[sr@ijekavianlatin]=Podaci Name[sr@latin]=Podaci Name[sv]=Information Name[th]=ข้อมูล Name[tr]=Bilgiler Name[ug]=ئۇچۇر Name[uk]=Інформація Name[x-test]=xxInfoxx Name[zh_CN]=信息 Name[zh_TW]=資訊 Type=Service Icon=info-amarok -ServiceTypes=Plasma/Applet +ServiceTypes=Amarok/ContextApplet -X-KDE-Library=amarok_context_applet_info X-KDE-PluginInfo-Author=Nikolaj Hald Nielsen X-KDE-PluginInfo-Email=nhnFreespirit@gmail.com -X-KDE-PluginInfo-Name=info X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website= X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true -X-KDE-ParentApp=amarok -X-KDE-PluginInfo-Category=Current +X-KDE-PluginInfo-Name=org.kde.amarok.info +X-KDE-PluginInfo-Category=Multimedia diff --git a/src/context/engines/info/InfoEngine.cpp b/src/context/applets/info/plugin/InfoEngine.cpp similarity index 55% rename from src/context/engines/info/InfoEngine.cpp rename to src/context/applets/info/plugin/InfoEngine.cpp index 45fff53c4e..87485c7b62 100644 --- a/src/context/engines/info/InfoEngine.cpp +++ b/src/context/applets/info/plugin/InfoEngine.cpp @@ -1,87 +1,74 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * Copyright (c) 2007 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "InfoEngine.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" -#include "ContextObserver.h" -#include "ContextView.h" #include "browsers/InfoProxy.h" - -#include - -using namespace Context; - -InfoEngine::InfoEngine( QObject* parent, const QList& args ) - : DataEngine( parent ) - , m_requested( true ) +#include "PaletteHandler.h" + +QString InfoEngine::s_standardContent = "" + " " + " " + " " + " " + " %%SUBJECT_NAME%%" + " " + ""; + +InfoEngine::InfoEngine( QObject* parent ) + : QObject( parent ) { - Q_UNUSED( args ) DEBUG_BLOCK - m_sources = QStringList(); - m_sources << "service"; The::infoProxy()->subscribe( this ); + + connect( The::paletteHandler(), &PaletteHandler::newPalette, this, &InfoEngine::serviceChanged ); } -InfoEngine::~ InfoEngine() +InfoEngine::~InfoEngine() { The::infoProxy()->unsubscribe( this ); } -QStringList InfoEngine::sources() const +QString InfoEngine::mainInfo() const { - return m_sources; // we don't have sources, if connected, it is enabled. -} + QString main_info; + + if( m_storedInfo.contains("main_info") ) + { + auto palette = The::paletteHandler()->palette(); + main_info = m_storedInfo.value("main_info").toString(); + main_info.replace("{text_color}", palette.windowText().color().name()); + main_info.replace("{content_background_color}", palette.window().color().name()); + main_info.replace("{background_color}", palette.window().color().name()); + main_info.replace("{border_color}", palette.text().color().name()); + } -bool InfoEngine::sourceRequestEvent( const QString& name ) -{ - Q_UNUSED( name ); -/* m_sources << name; // we are already enabled if we are alive*/ - setData( name, QVariant()); - update(); - m_requested = true; - return true; -} + if( main_info.isEmpty() ) + main_info = s_standardContent.replace("%%SUBJECT_NAME%%", serviceName()); -void InfoEngine::message( const ContextState& state ) -{ - if( state == Current && m_requested ) { - m_storedInfo = The::infoProxy()->info(); - update(); - } + return main_info; } - void InfoEngine::infoChanged( QVariantMap infoMap ) { m_storedInfo = infoMap; - update(); -} - -void InfoEngine::update() -{ - setData( "info", "subject_name", m_storedInfo["service_name"] ); - setData( "info", "main_info", m_storedInfo["main_info"] ); - + emit serviceChanged(); } - - - - diff --git a/src/context/engines/info/InfoEngine.h b/src/context/applets/info/plugin/InfoEngine.h similarity index 76% rename from src/context/engines/info/InfoEngine.h rename to src/context/applets/info/plugin/InfoEngine.h index 4c99189746..99812828ba 100644 --- a/src/context/engines/info/InfoEngine.h +++ b/src/context/applets/info/plugin/InfoEngine.h @@ -1,69 +1,64 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * Copyright (c) 2007 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef AMAROK_INFO_ENGINE #define AMAROK_INFO_ENGINE -#include "ContextObserver.h" #include "browsers/InfoObserver.h" -#include "context/DataEngine.h" + +#include +#include /** - This class provides context information realted to the currently active service + This class provides context information related to the currently active service There is no data source: if you connect to the engine, you immediately start getting updates when there is data. The key of the data is "service". The data is a QMap with the keys * service_name - the name of the currently running service */ -class InfoEngine : public Context::DataEngine, - public InfoObserver, - public ContextObserver +class InfoEngine : public QObject, + public InfoObserver { Q_OBJECT + Q_PROPERTY(QString serviceName READ serviceName NOTIFY serviceChanged) + Q_PROPERTY(QString mainInfo READ mainInfo NOTIFY serviceChanged) - public: - InfoEngine( QObject* parent, const QList& args ); + InfoEngine( QObject* parent = Q_NULLPTR ); ~InfoEngine(); - QStringList sources() const; - void message( const Context::ContextState& state ); + QString serviceName() const { return m_storedInfo.value("service_name").toString(); } + QString mainInfo() const; void infoChanged( QVariantMap infoMap ); -protected: - bool sourceRequestEvent( const QString& name ); +signals: + void serviceChanged(); private: - void update(); - - QStringList m_sources; - bool m_requested; QVariantMap m_storedInfo; - + static QString s_standardContent; }; -AMAROK_EXPORT_DATAENGINE( info, InfoEngine ) - #endif diff --git a/src/context/applets/info/plugin/InfoPlugin.cpp b/src/context/applets/info/plugin/InfoPlugin.cpp new file mode 100644 index 0000000000..c60d3bed36 --- /dev/null +++ b/src/context/applets/info/plugin/InfoPlugin.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2017 Malte Veerman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "InfoEngine.h" + +#include + +#include + + +class InfoPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char* uri) Q_DECL_OVERRIDE + { + Q_ASSERT(uri == QLatin1String("org.kde.amarok.info")); + + qmlRegisterSingletonType(uri, 1, 0, "InfoEngine", info_engine_provider); + } + + static QObject *info_engine_provider(QQmlEngine *engine, QJSEngine *scriptEngine) + { + Q_UNUSED(engine) + Q_UNUSED(scriptEngine) + + return new InfoEngine(); + } +}; + +#include diff --git a/src/context/applets/info/plugin/qmldir b/src/context/applets/info/plugin/qmldir new file mode 100644 index 0000000000..8e1fc7a178 --- /dev/null +++ b/src/context/applets/info/plugin/qmldir @@ -0,0 +1,2 @@ +module org.kde.amarok.info +plugin amarok_context_applet_info diff --git a/src/context/applets/lyrics/CMakeLists.txt b/src/context/applets/lyrics/CMakeLists.txt index eeb099dbe8..27d2e57b64 100644 --- a/src/context/applets/lyrics/CMakeLists.txt +++ b/src/context/applets/lyrics/CMakeLists.txt @@ -1,23 +1,17 @@ -project(context-currenttrack) - set(lyrics_SRCS - LyricsApplet.cpp - LyricsBrowser.cpp - LyricsSuggestionsListWidget.cpp) + plugin/LyricsPlugin.cpp + plugin/LyricsEngine.cpp +) -include_directories( ../.. - ../../.. ) +add_library(amarok_context_applet_lyrics SHARED ${lyrics_SRCS}) -ki18n_wrap_ui( lyrics_SRCS lyricsSettings.ui ) -add_library(amarok_context_applet_lyrics MODULE ${lyrics_SRCS}) -if(APPLE) - set_target_properties(amarok_context_applet_lyrics PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") -endif() target_link_libraries(amarok_context_applet_lyrics amarokcore amaroklib - KF5::Plasma + Qt5::Qml ) -install(TARGETS amarok_context_applet_lyrics DESTINATION ${PLUGIN_INSTALL_DIR}) -install(FILES amarok-context-applet-lyrics.desktop DESTINATION ${SERVICES_INSTALL_DIR}) +install(TARGETS amarok_context_applet_lyrics DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/lyrics) +install(FILES plugin/qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/lyrics) + +kpackage_install_package(package org.kde.amarok.lyrics amarok) diff --git a/src/context/applets/lyrics/LyricsApplet.cpp b/src/context/applets/lyrics/LyricsApplet.cpp deleted file mode 100644 index 6121b39bd5..0000000000 --- a/src/context/applets/lyrics/LyricsApplet.cpp +++ /dev/null @@ -1,757 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * Copyright (c) 2009 simon.esneault * - * Copyright (c) 2014 Yash Ladia * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "LyricsApplet" - -#include "LyricsApplet.h" - -#include "EngineController.h" -#include "context/widgets/AppletHeader.h" -#include "core/meta/Meta.h" -#include "core/support/Amarok.h" -#include "core/support/Debug.h" -#include "context/LyricsManager.h" -#include "LyricsBrowser.h" -#include "LyricsSuggestionsListWidget.h" -#include "scripting/scriptmanager/ScriptManager.h" - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -class LyricsAppletPrivate -{ -public: - LyricsAppletPrivate( LyricsApplet *parent ) - : saveIcon( 0 ) - , editIcon( 0 ) - , autoScrollIcon( 0 ) - , reloadIcon( 0 ) - , closeIcon( 0 ) - , settingsIcon( 0 ) - , browser( 0 ) - , suggestView( 0 ) - , currentTrack( 0 ) - , alignment( Qt::AlignLeft ) - , hasLyrics( false ) - , showBrowser( false ) - , showSuggestions( false ) - , isShowingUnsavedWarning( false ) - , userAutoScrollOffset( 0 ) - , oldSliderPosition( 0 ) - , q_ptr( parent ) {} - ~LyricsAppletPrivate() {} - - // member functions - void setEditing( bool isEditing ); - void determineActionIconsState(); - void refetchLyrics(); - void showLyrics( const QString &text ); - void showSuggested( const QVariantList &suggestions ); - void showUnsavedChangesWarning( Meta::TrackPtr ); - - // private slots - void _editLyrics(); - void _changeLyricsAlignment(); - void _changeLyricsFont(); - void _closeLyrics(); - void _saveLyrics(); - void _toggleAutoScroll(); - void _suggestionChosen( const LyricsSuggestion &suggestion ); - void _unsetCursor(); - void _trackChanged( Meta::TrackPtr ); - void _trackMetadataChanged( Meta::TrackPtr ); - void _trackPositionChanged( qint64 position, bool userSeek ); - - void _lyricsChangedMessageButtonPressed( const Plasma::MessageButton button ); - void _refetchMessageButtonPressed( const Plasma::MessageButton button ); - - Plasma::IconWidget *saveIcon; - Plasma::IconWidget *editIcon; - Plasma::IconWidget *autoScrollIcon; - Plasma::IconWidget *reloadIcon; - Plasma::IconWidget *closeIcon; - Plasma::IconWidget *settingsIcon; - - LyricsBrowser *browser; - LyricsSuggestionsListWidget *suggestView; - - Ui::lyricsSettings ui_settings; - - Meta::TrackPtr currentTrack; - Meta::TrackPtr modifiedTrack; - QString modifiedLyrics; - - Qt::Alignment alignment; - - bool hasLyrics; - bool showBrowser; - bool autoScroll; - bool showSuggestions; - bool isShowingUnsavedWarning; - int userAutoScrollOffset; - int oldSliderPosition; - -private: - LyricsApplet *const q_ptr; - Q_DECLARE_PUBLIC( LyricsApplet ) -}; - -void -LyricsAppletPrivate::setEditing( bool isEditing ) -{ - browser->setReadOnly( !isEditing ); -} - -void -LyricsAppletPrivate::determineActionIconsState() -{ - bool isEditing = !browser->isReadOnly(); - - editIcon->action()->setEnabled( !isEditing ); - closeIcon->action()->setEnabled( isEditing ); - saveIcon->action()->setEnabled( isEditing ); - autoScrollIcon->action()->setEnabled( !isEditing ); - reloadIcon->action()->setEnabled( !isEditing ); -} - -void -LyricsAppletPrivate::showLyrics( const QString &text ) -{ - browser->clear(); - browser->setLyrics( text ); - showSuggestions = false; - showBrowser = true; - determineActionIconsState(); -} - -void -LyricsAppletPrivate::showSuggested( const QVariantList &suggestions ) -{ - editIcon->action()->setEnabled( false ); - closeIcon->action()->setEnabled( false ); - saveIcon->action()->setEnabled( false ); - - suggestView->clear(); - foreach( const QVariant &suggestion, suggestions ) - { - QStringList s( suggestion.toStringList() ); - QString title( s.at(0) ); - QString artist( s.at(1) ); - QUrl url( s.at(2) ); - LyricsSuggestion lyricsSuggestion = { url, title, artist }; - suggestView->add( lyricsSuggestion ); - } - showSuggestions = true; -} - -void -LyricsAppletPrivate::refetchLyrics() -{ - DEBUG_BLOCK - ScriptManager::instance()->notifyFetchLyrics( currentTrack->artist()->name(), - currentTrack->name(), "", currentTrack ); -} - -void -LyricsAppletPrivate::showUnsavedChangesWarning( Meta::TrackPtr newTrack ) -{ - Q_Q( LyricsApplet ); - - // Set the track which was modified and store the current - // lyircs from the UI. - modifiedTrack = currentTrack; - modifiedLyrics = browser->lyrics(); - - QString artistName = modifiedTrack->artist() ? modifiedTrack->artist()->name() : i18nc( "Used if the current track has no artist.", "Unknown" ); - QString warningMessage; - - // Check if the track has changed. - if( newTrack != modifiedTrack ) - { - // Show a warning that the track has changed while the user was editing the lyrics for the current track. - warningMessage = i18n( "While you were editing the lyrics of %1 - %2 the track has changed. Do you want to save your changes?", - artistName, - modifiedTrack->prettyName() ); - } - else - { - // Show a warning that the lyrics for the track were modified (for example by a script). - warningMessage = i18n( "The lyrics of %1 - %2 changed while you were editing them. Do you want to save your changes?", - artistName, - modifiedTrack->prettyName() ); - } - - // Show the warning message. - q->showWarning( warningMessage, SLOT(_lyricsChangedMessageButtonPressed(Plasma::MessageButton)) ); - - // Make the contents readonly again. - // Since the applet is now blocked the user can not enable this again. - // Thus we can make sure that we won't overwrite modifiedTrack. - setEditing( false ); - - isShowingUnsavedWarning = false; -} - -void LyricsAppletPrivate::_refetchMessageButtonPressed( const Plasma::MessageButton button ) -{ - DEBUG_BLOCK - // Check if the user pressed "Yes". - if( button == Plasma::ButtonYes ) - // Refetch the lyrics. - refetchLyrics(); -} - -void LyricsAppletPrivate::_lyricsChangedMessageButtonPressed( const Plasma::MessageButton button ) -{ - DEBUG_BLOCK - // Check if the user pressed "Yes". - if( button == Plasma::ButtonYes ) - // Update the lyrics of the track. - modifiedTrack->setCachedLyrics( modifiedLyrics ); - - modifiedLyrics.clear(); -} - -void -LyricsAppletPrivate::_changeLyricsAlignment() -{ - if( ui_settings.alignLeft->isChecked() ) - alignment = Qt::AlignLeft; - else if( ui_settings.alignCenter->isChecked() ) - alignment = Qt::AlignCenter; - else if( ui_settings.alignRight->isChecked() ) - alignment = Qt::AlignRight; - Amarok::config("Lyrics Applet").writeEntry( "Alignment", int(alignment) ); - browser->setAlignment( alignment ); -} - -void -LyricsAppletPrivate::_changeLyricsFont() -{ - QFont font = ui_settings.fontChooser->font(); - browser->nativeWidget()->setFont( font ); - KConfigGroup config = Amarok::config("Lyrics Applet"); - config.writeEntry( "Font", font.toString() ); - debug() << "Setting Lyrics Applet font: " << font.family() << " " << font.pointSize(); -} - -void -LyricsAppletPrivate::_editLyrics() -{ - if( !hasLyrics ) - browser->clear(); - - Q_Q( LyricsApplet ); - if( q->isCollapsed() ) - q->setCollapseOff(); - - // disable autoscroll when starting editing - if (autoScroll) - _toggleAutoScroll(); - - if( !browser->isVisible() ) - { - browser->show(); - suggestView->hide(); - suggestView->clear(); - QGraphicsLinearLayout *lo = static_cast( q->layout() ); - lo->removeItem( suggestView ); - lo->addItem( browser ); - } - - browser->setAlignment( Qt::AlignLeft ); - setEditing( true ); - determineActionIconsState(); - browser->nativeWidget()->ensureCursorVisible(); -} - -void -LyricsAppletPrivate::_closeLyrics() -{ - if( hasLyrics ) - { - QScrollBar *vbar = browser->nativeWidget()->verticalScrollBar(); - int savedPosition = vbar->isVisible() ? vbar->value() : vbar->minimum(); - - showLyrics( currentTrack->cachedLyrics() ); - vbar->setSliderPosition( savedPosition ); - // emit sizeHintChanged(Qt::MaximumSize); - } - else - { - browser->clear(); - } - - setEditing( false ); - browser->setAlignment( alignment ); - determineActionIconsState(); -} - -void -LyricsAppletPrivate::_saveLyrics() -{ - if( currentTrack ) - { - if( !LyricsManager::self()->isEmpty( browser->nativeWidget()->toPlainText() ) ) - { - currentTrack->setCachedLyrics( browser->lyrics() ); - hasLyrics = true; - } - else - { - currentTrack->setCachedLyrics( QString() ); - hasLyrics = false; - } - // emit sizeHintChanged(Qt::MaximumSize); - } - - setEditing( false ); - browser->setAlignment( alignment ); - determineActionIconsState(); -} - -void -LyricsAppletPrivate::_toggleAutoScroll() -{ - Q_Q( LyricsApplet ); - Plasma::IconWidget *icon = qobject_cast(q->sender()); - DEBUG_ASSERT( icon, return ) // that should not happen - - autoScroll = !autoScroll; - icon->setPressed( autoScroll ); - Amarok::config( "Lyrics Applet" ).writeEntry( "AutoScroll", autoScroll ); -} - -void -LyricsAppletPrivate::_suggestionChosen( const LyricsSuggestion &suggestion ) -{ - DEBUG_BLOCK - QUrl url = suggestion.url; - if( !url.isValid() ) - return; - - QString title = suggestion.title; - QString artist = suggestion.artist; - - Q_Q( LyricsApplet ); - debug() << "clicked suggestion" << url; - ScriptManager::instance()->notifyFetchLyrics( artist, title, url.url(), Meta::TrackPtr() ); - suggestView->setCursor( Qt::BusyCursor ); - QTimer::singleShot( 10000, q, SLOT(_unsetCursor()) ); -} - -void -LyricsAppletPrivate::_unsetCursor() -{ - if( suggestView->hasCursor() ) - suggestView->unsetCursor(); -} - -void -LyricsAppletPrivate::_trackChanged( Meta::TrackPtr track ) -{ - userAutoScrollOffset = 0; - oldSliderPosition = 0; - // meta data also changed, so to avoid code duplication - _trackMetadataChanged( track ); -} - -void -LyricsAppletPrivate::_trackMetadataChanged( Meta::TrackPtr track ) -{ - // Check if we previously had a track. - // If the lyrics currently shown in the browser (which - // additionally is in edit mode) are different from the - // lyrics of the track we have to show a warning. - if( !isShowingUnsavedWarning && currentTrack && - !browser->isReadOnly() && - (currentTrack->cachedLyrics() != browser->lyrics()) ) - { - isShowingUnsavedWarning = true; - showUnsavedChangesWarning( track ); - } - - // Update the current track. - currentTrack = track; -} - -void -LyricsAppletPrivate::_trackPositionChanged( qint64 position, bool userSeek ) -{ - Q_UNUSED( userSeek ); - EngineController *engine = The::engineController(); - QScrollBar *vbar = browser->nativeWidget()->verticalScrollBar(); - if( engine->trackPositionMs() != 0 && !vbar->isSliderDown() && autoScroll ) - { - userAutoScrollOffset = userAutoScrollOffset + vbar->value() - oldSliderPosition; - - //prevent possible devision by 0 (example streams). - if( engine->trackLength() == 0 ) - return; - // Scroll to try and keep the current position in the lyrics centred. - int newSliderPosition = - position * (vbar->maximum() + vbar->pageStep()) / engine->trackLength() - - vbar->pageStep() / 2 + userAutoScrollOffset; - vbar->setSliderPosition( newSliderPosition ); - - oldSliderPosition = vbar->value(); - } -} - - -LyricsApplet::LyricsApplet( QObject* parent, const QVariantList& args ) - : Context::Applet( parent, args ) - , d_ptr( new LyricsAppletPrivate( this ) ) -{ - setHasConfigurationInterface( true ); - setBackgroundHints( Plasma::Applet::NoBackground ); -} - -LyricsApplet::~LyricsApplet() -{ - delete d_ptr; -} - -void -LyricsApplet::init() -{ - DEBUG_BLOCK - - Q_D( LyricsApplet ); - - // Call the base implementation. - Context::Applet::init(); - - enableHeader( true ); - setHeaderText( i18n( "Lyrics" ) ); - - setCollapseOffHeight( -1 ); - setCollapseHeight( m_header->height() ); - setMinimumHeight( collapseHeight() ); - setPreferredHeight( collapseHeight() ); - - QAction* editAction = new QAction( this ); - editAction->setIcon( QIcon::fromTheme( "document-edit" ) ); - editAction->setEnabled( false ); - editAction->setText( i18n( "Edit Lyrics" ) ); - d->editIcon = addLeftHeaderAction( editAction ); - connect( d->editIcon, SIGNAL(clicked()), this, SLOT(_editLyrics()) ); - - QAction* saveAction = new QAction( this ); - saveAction->setIcon( QIcon::fromTheme( "document-save" ) ); - saveAction->setEnabled( false ); - saveAction->setText( i18n( "Save Lyrics" ) ); - d->saveIcon = addLeftHeaderAction( saveAction ); - connect( d->saveIcon, SIGNAL(clicked()), this, SLOT(_saveLyrics()) ); - - QAction* closeAction = new QAction( this ); - closeAction->setIcon( QIcon::fromTheme( "document-close" ) ); - closeAction->setEnabled( false ); - closeAction->setText( i18n( "Close" ) ); - d->closeIcon = addLeftHeaderAction( closeAction ); - connect( d->closeIcon, SIGNAL(clicked()), this, SLOT(_closeLyrics()) ); - - QAction* autoScrollAction = new QAction( this ); - autoScrollAction->setIcon( QIcon( QPixmap( KStandardDirs::locate( "data", "amarok/images/playlist-sorting-16.png" ) ) ) ); - autoScrollAction->setEnabled( true ); - autoScrollAction->setText( i18n( "Scroll automatically" ) ); - d->autoScrollIcon = addRightHeaderAction( autoScrollAction ); - connect( d->autoScrollIcon, SIGNAL(clicked()), this, SLOT(_toggleAutoScroll()) ); - - QAction* reloadAction = new QAction( this ); - reloadAction->setIcon( QIcon::fromTheme( "view-refresh" ) ); - reloadAction->setEnabled( true ); - reloadAction->setText( i18n( "Reload Lyrics" ) ); - d->reloadIcon = addRightHeaderAction( reloadAction ); - connect( d->reloadIcon, SIGNAL(clicked()), this, SLOT(refreshLyrics()) ); - - QAction* settingsAction = new QAction( this ); - settingsAction->setIcon( QIcon::fromTheme( "preferences-system" ) ); - settingsAction->setEnabled( true ); - settingsAction->setText( i18n( "Settings" ) ); - d->settingsIcon = addRightHeaderAction( settingsAction ); - connect( d->settingsIcon, SIGNAL(clicked()), this, SLOT(showConfigurationInterface()) ); - - d->browser = new LyricsBrowser( this ); - d->browser->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - d->browser->hide(); - - d->suggestView = new LyricsSuggestionsListWidget( this ); - d->suggestView->hide(); - - QGraphicsLinearLayout *layout = new QGraphicsLinearLayout( Qt::Vertical ); - layout->addItem( m_header ); - layout->addItem( d->browser ); - setLayout( layout ); - - // Read config - const KConfigGroup &lyricsConfig = Amarok::config("Lyrics Applet"); - d->alignment = Qt::Alignment( lyricsConfig.readEntry("Alignment", int(Qt::AlignLeft)) ); - d->browser->setAlignment( d->alignment ); - d->autoScroll = lyricsConfig.readEntry( "AutoScroll", true ); - d->autoScrollIcon->setPressed( d->autoScroll ); - - QFont font; - if( font.fromString( lyricsConfig.readEntry("Font", QString()) ) ) - d->browser->setFont( font ); - - EngineController* engine = The::engineController(); - - connect( engine, SIGNAL(trackChanged(Meta::TrackPtr)), this, SLOT(_trackChanged(Meta::TrackPtr)) ); - connect( engine, SIGNAL(trackMetadataChanged(Meta::TrackPtr)), this, SLOT(_trackMetadataChanged(Meta::TrackPtr)) ); - connect( engine, SIGNAL(trackPositionChanged(qint64,bool)), this, SLOT(_trackPositionChanged(qint64,bool)) ); - connect( d->suggestView, SIGNAL(selected(LyricsSuggestion)), SLOT(_suggestionChosen(LyricsSuggestion)) ); - connect( dataEngine("amarok-lyrics"), SIGNAL(sourceAdded(QString)), this, SLOT(connectSource(QString)) ); - - // This is needed as a track might be playing when the lyrics applet - // is added to the ContextView. - d->_trackChanged( engine->currentTrack() ); - d->_trackPositionChanged( engine->trackPositionMs(), false ); - - d->determineActionIconsState(); - connectSource( "lyrics" ); -} - -void -LyricsApplet::connectSource( const QString& source ) -{ - if( source == "lyrics" ) - { - dataEngine( "amarok-lyrics" )->connectSource( source, this ); - refreshLyrics(); // get data initially - } - else if( source == "suggested" ) - { - dataEngine( "amarok-lyrics" )->connectSource( source, this ); - dataUpdated( source, dataEngine("amarok-lyrics" )->query( "suggested" ) ); - } -} - -void -LyricsApplet::dataUpdated( const QString& name, const Plasma::DataEngine::Data& data ) -{ - Q_D( LyricsApplet ); - - if( name != QLatin1String("lyrics") ) - return; - - unsetCursor(); - d->hasLyrics = false; - d->showSuggestions = false; - d->showBrowser = false; - setBusy( false ); - QString titleText; - - if( data.contains( "noscriptrunning" ) ) - { - titleText = i18n( "Lyrics: No script is running" ); - setCollapseOn(); - } - else if( data.contains( "stopped" ) ) - { - titleText = i18n( "Lyrics" ); - setCollapseOn(); - } - else if( data.contains( "fetching" ) ) - { - if( canAnimate() ) - setBusy( true ); - titleText = i18n( "Lyrics: Fetching ..." ); - } - else if( data.contains( "error" ) ) - { - titleText = i18n( "Lyrics: Fetch error" ); - setCollapseOn(); - } - else if( data.contains( "suggested" ) ) - { - QVariantList suggested = data[ "suggested" ].toList(); - titleText = i18n( "Lyrics: Suggested URLs" ); - d->showSuggested( suggested ); - setCollapseOff(); - } - else if( data.contains( "html" ) || data.contains( "lyrics" ) ) - { - const bool isHtml = data.contains( QLatin1String("html") ); - const QString key = isHtml ? QLatin1String("html") : QLatin1String("lyrics"); - const QVariant var = data.value( key ); - if( var.canConvert() ) - { - d->hasLyrics = true; - d->browser->setRichText( isHtml ); - LyricsData lyrics = var.value(); - QString trimmed = lyrics.text.trimmed(); - - if( trimmed != d->browser->lyrics() ) - { - d->showLyrics( trimmed ); - } - else // lyrics are the same, make sure browser is showing - { - d->showSuggestions = false; - d->showBrowser = true; - } - - titleText = i18nc( "Lyrics: - ", "Lyrics: %1 - %2", lyrics.artist, lyrics.title ); - setCollapseOff(); - } - } - else if( data.contains( "notfound" ) || data.contains( "notFound" ) ) - { - titleText = i18n( "Lyrics: Not found" ); - setCollapseOn(); - } - else - { - warning() << "should not be here:" << data; - titleText = headerText(); - } - - setHeaderText( titleText ); - - QGraphicsLinearLayout *lo = static_cast<QGraphicsLinearLayout*>( layout() ); - d->showSuggestions ? lo->insertItem( 1, d->suggestView ) : lo->removeItem( d->suggestView ); - d->showBrowser ? lo->addItem( d->browser ) : lo->removeItem( d->browser ); - - d->suggestView->setVisible( d->showSuggestions ); - d->browser->setVisible( d->showBrowser ); - - if( !d->showSuggestions ) - d->suggestView->clear(); - - d->determineActionIconsState(); -} - -bool -LyricsApplet::hasHeightForWidth() const -{ - return false; -} - -void -LyricsApplet::refreshLyrics() -{ - Q_D( LyricsApplet ); - if( !d->currentTrack || !d->currentTrack->artist() ) - return; - - if( d->hasLyrics ) - { - // Ask the user if he really wants to refetch the lyrics. - const QString text( i18nc( "@info", "Do you really want to refetch lyrics for this track? All changes you may have made will be lost.") ); - showWarning( text, SLOT(_refetchMessageButtonPressed(Plasma::MessageButton)) ); - } - else - { - // As we don't have lyrics yet we will - // refetch without asking the user. - d->refetchLyrics(); - } -} - -void -LyricsApplet::createConfigurationInterface( KConfigDialog *parent ) -{ - Q_D( LyricsApplet ); - - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); - QVBoxLayout *mainLayout = new QVBoxLayout; - parent->setLayout(mainLayout); - QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); - okButton->setDefault(true); - okButton->setShortcut(Qt::CTRL | Qt::Key_Return); - parent->connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); - parent->connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - mainLayout->addWidget(buttonBox); - - KConfigGroup configuration = config(); - QWidget *settings = new QWidget; - d->ui_settings.setupUi( settings ); - d->ui_settings.fontChooser->setFont( d->browser->nativeWidget()->currentFont() ); - - switch( d->alignment ) - { - default: - case Qt::AlignLeft: - d->ui_settings.alignLeft->setChecked( true ); - break; - - case Qt::AlignRight: - d->ui_settings.alignRight->setChecked( true ); - break; - - case Qt::AlignCenter: - d->ui_settings.alignCenter->setChecked( true ); - break; - } - - parent->addPage( settings, i18n( "Lyrics Settings" ), "preferences-system" ); - - connect( parent, SIGNAL(accepted()), this, SLOT(_changeLyricsFont()) ); - connect( parent, SIGNAL(accepted()), this, SLOT(_changeLyricsAlignment()) ); - connect( parent, SIGNAL(clicked()), this, SLOT(_changeLyricsFont()) ); - connect( parent, SIGNAL(clicked()), this, SLOT(_changeLyricsAlignment()) ); -} - -void -LyricsApplet::keyPressEvent( QKeyEvent *e ) -{ - Q_D( LyricsApplet ); - if( d->browser->nativeWidget()->isVisible() ) - { - bool propagate( true ); - switch( e->key() ) - { - case Qt::Key_Escape : - d->_closeLyrics(); - propagate = false; - break; - - case Qt::Key_F2 : - d->_editLyrics(); - propagate = false; - break; - } - - if( e->matches( QKeySequence::Save ) ) - { - d->_saveLyrics(); - propagate = false; - } - - if( !propagate ) - { - e->accept(); - return; - } - } - Context::Applet::keyPressEvent( e ); -} - diff --git a/src/context/applets/lyrics/LyricsApplet.h b/src/context/applets/lyrics/LyricsApplet.h deleted file mode 100644 index 9b0de2b022..0000000000 --- a/src/context/applets/lyrics/LyricsApplet.h +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi <lfranchi@gmail.com> * - * Copyright (c) 2009 simon.esneault <simon.esneault@gmail.com> * - * Copyright (c) 2014 Yash Ladia <yashladia1@gmail.com> * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see <http://www.gnu.org/licenses/>. * - ****************************************************************************************/ - -#ifndef LYRICS_APPLET_H -#define LYRICS_APPLET_H - -#include "context/Applet.h" -#include "context/DataEngine.h" - -#include <ui_lyricsSettings.h> - -class LyricsAppletPrivate; - -class LyricsApplet : public Context::Applet -{ - Q_OBJECT - -public: - LyricsApplet( QObject* parent, const QVariantList& args ); - ~LyricsApplet(); - - bool hasHeightForWidth() const; - -public Q_SLOTS: - virtual void init(); - void connectSource( const QString& source ); - void dataUpdated( const QString& name, const Plasma::DataEngine::Data& data ); - void refreshLyrics(); - -protected: - void createConfigurationInterface( KConfigDialog *parent ); - void keyPressEvent( QKeyEvent *e ); - -private: - LyricsAppletPrivate *const d_ptr; - Q_DECLARE_PRIVATE( LyricsApplet ) - - Q_PRIVATE_SLOT( d_ptr, void _editLyrics() ) - Q_PRIVATE_SLOT( d_ptr, void _changeLyricsFont() ) - Q_PRIVATE_SLOT( d_ptr, void _changeLyricsAlignment() ) - Q_PRIVATE_SLOT( d_ptr, void _closeLyrics() ) - Q_PRIVATE_SLOT( d_ptr, void _saveLyrics() ) - Q_PRIVATE_SLOT( d_ptr, void _toggleAutoScroll() ) - Q_PRIVATE_SLOT( d_ptr, void _suggestionChosen(LyricsSuggestion) ) - Q_PRIVATE_SLOT( d_ptr, void _unsetCursor() ) - Q_PRIVATE_SLOT( d_ptr, void _trackChanged( Meta::TrackPtr ) ) - Q_PRIVATE_SLOT( d_ptr, void _trackMetadataChanged( Meta::TrackPtr ) ) - Q_PRIVATE_SLOT( d_ptr, void _trackPositionChanged( qint64 position, bool userSeek ) ) - Q_PRIVATE_SLOT( d_ptr, void _lyricsChangedMessageButtonPressed(const Plasma::MessageButton) ) - Q_PRIVATE_SLOT( d_ptr, void _refetchMessageButtonPressed(const Plasma::MessageButton) ) -}; - -AMAROK_EXPORT_APPLET( lyrics, LyricsApplet ) - -#endif diff --git a/src/context/applets/lyrics/LyricsBrowser.cpp b/src/context/applets/lyrics/LyricsBrowser.cpp deleted file mode 100644 index 240e385307..0000000000 --- a/src/context/applets/lyrics/LyricsBrowser.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2011 Rick W. Chen <stuffcorpse@archlinux.us> * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see <http://www.gnu.org/licenses/>. * - ****************************************************************************************/ - -#include "LyricsBrowser.h" - -#include "PaletteHandler.h" - -#include <QApplication> -#include <QTextBlock> -#include <KTextBrowser> -#include <Plasma/Svg> -#include <Plasma/SvgWidget> - -#include <QGraphicsSceneResizeEvent> - -LyricsBrowser::LyricsBrowser( QGraphicsWidget *parent ) - : Plasma::TextBrowser( parent ) - , m_isRichText( true ) - , m_alignment( Qt::AlignLeft ) - , m_topBorder( new Plasma::SvgWidget( this ) ) - , m_bottomBorder( new Plasma::SvgWidget( this ) ) -{ - KTextBrowser *native = nativeWidget(); - native->setOpenExternalLinks( true ); - native->setUndoRedoEnabled( true ); - native->setAutoFillBackground( false ); - native->setReadOnly( false ); - native->setWordWrapMode( QTextOption::WordWrap ); - native->setCursorWidth( 0 ); - native->document()->setDocumentMargin( 10 ); - native->setTextInteractionFlags( Qt::TextBrowserInteraction | Qt::TextSelectableByKeyboard ); - - Plasma::Svg *borderSvg = new Plasma::Svg( this ); - borderSvg->setImagePath( QLatin1String("widgets/scrollwidget") ); - - m_topBorder->setSvg( borderSvg ); - m_topBorder->setElementID( QLatin1String("border-top") ); - m_topBorder->setZValue( 900 ); - - m_bottomBorder->setSvg( borderSvg ); - m_bottomBorder->setElementID( QLatin1String("border-bottom") ); - m_bottomBorder->setZValue( 900 ); - - connect( The::paletteHandler(), SIGNAL(newPalette(QPalette)), SLOT(paletteChanged(QPalette)) ); - paletteChanged( The::paletteHandler()->palette() ); -} - -LyricsBrowser::~LyricsBrowser() -{} - -Qt::Alignment LyricsBrowser::alignment() const -{ - return m_alignment; -} - -bool LyricsBrowser::isReadOnly() const -{ - return nativeWidget()->isReadOnly(); -} - -bool LyricsBrowser::isRichText() const -{ - return m_isRichText; -} - -QString LyricsBrowser::lyrics() const -{ - return m_isRichText ? nativeWidget()->toHtml() : nativeWidget()->toPlainText(); -} - -void LyricsBrowser::clear() -{ - nativeWidget()->clear(); -} - -void LyricsBrowser::setAlignment( Qt::Alignment alignment ) -{ - if( m_alignment == alignment ) - return; - - m_alignment = alignment; - updateAlignment(); -} - -void LyricsBrowser::setLyrics( const QString &lyrics ) -{ - KTextBrowser *w = nativeWidget(); - m_isRichText ? w->setHtml( lyrics ) : w->setPlainText( lyrics ); - updateAlignment(); -} - -void LyricsBrowser::setReadOnly( bool readOnly ) -{ - KTextBrowser *native = nativeWidget(); - - native->viewport()->setAutoFillBackground( !readOnly ); - native->setReadOnly( readOnly ); - native->setCursorWidth( !readOnly ? 1 : 0 ); -} - -void LyricsBrowser::setRichText( bool richText ) -{ - m_isRichText = richText; -} - -void LyricsBrowser::paletteChanged( const QPalette &palette ) -{ - QPalette p = palette; - // set text color using app theme instead of plasma theme - p.setColor( QPalette::Text, qApp->palette().text().color() ); - - nativeWidget()->setPalette( p ); -} - -void LyricsBrowser::updateAlignment() -{ - QTextCursor it( nativeWidget()->document()->firstBlock() ); - if( !it.block().isValid() ) - return; - - do - { - QTextBlockFormat fmt = it.blockFormat(); - fmt.setAlignment( m_alignment ); - it.setBlockFormat( fmt ); - } while ( it.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor) ); -} - -void LyricsBrowser::resizeEvent( QGraphicsSceneResizeEvent *event ) -{ - Plasma::TextBrowser::resizeEvent( event ); - if( event->newSize() == event->oldSize() ) - return; - - if( m_topBorder && m_topBorder->isVisible() ) - { - qreal newWidth = event->newSize().width(); - m_topBorder->resize( newWidth, m_topBorder->size().height() ); - m_bottomBorder->resize( newWidth, m_bottomBorder->size().height() ); - m_topBorder->setPos( boundingRect().topLeft() ); - QPointF bottomPoint = boundingRect().bottomLeft(); - bottomPoint.ry() -= m_bottomBorder->size().height(); - m_bottomBorder->setPos( bottomPoint ); - } -} - diff --git a/src/context/applets/lyrics/LyricsBrowser.h b/src/context/applets/lyrics/LyricsBrowser.h deleted file mode 100644 index 1e58055eb6..0000000000 --- a/src/context/applets/lyrics/LyricsBrowser.h +++ /dev/null @@ -1,64 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2011 Rick W. Chen <stuffcorpse@archlinux.us> * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see <http://www.gnu.org/licenses/>. * - ****************************************************************************************/ - -#ifndef LYRICS_BROWSER_H -#define LYRICS_BROWSER_H - -#include <Plasma/TextBrowser> - -namespace Plasma { - class SvgWidget; -} - -class LyricsBrowser : public Plasma::TextBrowser -{ - Q_OBJECT - Q_PROPERTY( Qt::Alignment alignment READ alignment WRITE setAlignment ) - Q_PROPERTY( bool isReadOnly READ isReadOnly WRITE setReadOnly ) - Q_PROPERTY( bool isRichText READ isRichText WRITE setRichText ) - Q_PROPERTY( QString lyrics READ lyrics WRITE setLyrics ) - -public: - explicit LyricsBrowser( QGraphicsWidget *parent = 0 ); - ~LyricsBrowser(); - - Qt::Alignment alignment() const; - bool isReadOnly() const; - bool isRichText() const; - QString lyrics() const; - - void clear(); - - void setAlignment( Qt::Alignment alignment ); - void setLyrics( const QString &lyrics ); - void setReadOnly( bool readOnly ); - void setRichText( bool richText ); - -protected: - void resizeEvent( QGraphicsSceneResizeEvent *event ); - -private Q_SLOTS: - void paletteChanged( const QPalette &palette ); - void updateAlignment(); - -private: - bool m_isRichText; - Qt::Alignment m_alignment; - Plasma::SvgWidget *m_topBorder; - Plasma::SvgWidget *m_bottomBorder; -}; - -#endif // LYRICS_BROWSER_H diff --git a/src/context/applets/lyrics/LyricsSuggestionsListWidget.cpp b/src/context/applets/lyrics/LyricsSuggestionsListWidget.cpp deleted file mode 100644 index 1182f03a55..0000000000 --- a/src/context/applets/lyrics/LyricsSuggestionsListWidget.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2011 Rick W. Chen <stuffcorpse@archlinux.us> * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see <http://www.gnu.org/licenses/>. * - ****************************************************************************************/ - -#define DEBUG_PREFIX "LyricsSuggestionsListWidget" - -#include "LyricsSuggestionsListWidget.h" - -#include "core/support/Amarok.h" -#include "core/support/Debug.h" - -#include <QIcon> -#include <KSqueezedTextLabel> -#include <Plasma/IconWidget> -#include <Plasma/Label> -#include <Plasma/Separator> - -#include <QGraphicsGridLayout> -#include <QGraphicsLinearLayout> -#include <QGraphicsProxyWidget> - -LyricsSuggestionsListWidget::LyricsSuggestionsListWidget( QGraphicsWidget *parent ) - : Plasma::ScrollWidget( parent ) -{ - QGraphicsWidget *viewport = new QGraphicsWidget( this ); - m_layout = new QGraphicsLinearLayout( Qt::Vertical, viewport ); - setWidget( viewport ); -} - -LyricsSuggestionsListWidget::~LyricsSuggestionsListWidget() -{} - -void -LyricsSuggestionsListWidget::add( const LyricsSuggestion &suggestion ) -{ - QGraphicsWidget *sep = new Plasma::Separator; - LyricsSuggestionItem *item = new LyricsSuggestionItem( suggestion ); - item->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Maximum ); - m_layout->addItem( item ); - m_layout->addItem( sep ); - m_items.append( item ); - m_separators.append( sep ); - connect( item, SIGNAL(selected(LyricsSuggestion)), SIGNAL(selected(LyricsSuggestion)) ); -} - -void -LyricsSuggestionsListWidget::clear() -{ - qDeleteAll( m_items ); - qDeleteAll( m_separators ); - m_items.clear(); - m_separators.clear(); -} - -LyricsSuggestionItem::LyricsSuggestionItem( const LyricsSuggestion &suggestion, QGraphicsItem *parent ) - : QGraphicsWidget( parent ) - , m_data( suggestion ) -{ - QGraphicsProxyWidget *titleProxy = new QGraphicsProxyWidget( this ); - KSqueezedTextLabel *titleLabel = new KSqueezedTextLabel( m_data.title ); - titleLabel->setTextElideMode( Qt::ElideRight ); - titleLabel->setAttribute( Qt::WA_NoSystemBackground ); - titleLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); - titleProxy->setWidget( titleLabel ); - QFont font = titleLabel->font(); - font.setBold( true ); - titleLabel->setFont( font ); - - const QUrl &url = m_data.url; - QString urlText = QString("<a href=\"%1\">%2</a>").arg(url.url(), url.host()); - Plasma::Label *urlLabel = new Plasma::Label( this ); - urlLabel->setText( urlText ); - urlLabel->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred ); - urlLabel->nativeWidget()->setOpenExternalLinks( true ); - urlLabel->nativeWidget()->setTextInteractionFlags( Qt::TextBrowserInteraction ); - urlLabel->nativeWidget()->setToolTip( url.url() ); - - QString artist = i18n( "artist: %1", m_data.artist ); - QGraphicsProxyWidget *artistProxy = new QGraphicsProxyWidget( this ); - KSqueezedTextLabel *artistLabel = new KSqueezedTextLabel( artist ); - artistLabel->setTextElideMode( Qt::ElideRight ); - artistLabel->setAttribute( Qt::WA_NoSystemBackground ); - artistLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); - artistProxy->setWidget( artistLabel ); - - Plasma::IconWidget *lyricsIcon( new Plasma::IconWidget(QIcon::fromTheme("amarok_lyrics"), QString(), this) ); - lyricsIcon->setDrawBackground( true ); - connect( lyricsIcon, SIGNAL(clicked()), SLOT(onClicked()) ); - - QGraphicsGridLayout *layout = new QGraphicsGridLayout( this ); - layout->setVerticalSpacing( 0 ); - layout->addItem( lyricsIcon, 0, 0, 3, 1, Qt::AlignCenter ); - layout->addItem( titleProxy, 0, 1, Qt::AlignLeft ); - layout->addItem( artistProxy, 1, 1, Qt::AlignLeft ); - layout->addItem( urlLabel, 2, 1, Qt::AlignLeft ); -} - -LyricsSuggestionItem::~LyricsSuggestionItem() -{} - -void -LyricsSuggestionItem::onClicked() -{ - emit selected( m_data ); -} - diff --git a/src/context/applets/lyrics/LyricsSuggestionsListWidget.h b/src/context/applets/lyrics/LyricsSuggestionsListWidget.h deleted file mode 100644 index f3b3db02f4..0000000000 --- a/src/context/applets/lyrics/LyricsSuggestionsListWidget.h +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2011 Rick W. Chen <stuffcorpse@archlinux.us> * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see <http://www.gnu.org/licenses/>. * - ****************************************************************************************/ - -#ifndef LYRICS_SUGGESTIONS_LIST_WIDGET_H -#define LYRICS_SUGGESTIONS_LIST_WIDGET_H - -#include <QUrl> -#include <Plasma/ScrollWidget> - -class LyricsSuggestionItem; -class QGraphicsLinearLayout; - -struct LyricsSuggestion -{ - QUrl url; - QString title; - QString artist; -}; - -class LyricsSuggestionsListWidget : public Plasma::ScrollWidget -{ - Q_OBJECT - -public: - explicit LyricsSuggestionsListWidget( QGraphicsWidget *parent = 0 ); - ~LyricsSuggestionsListWidget(); - - void add( const LyricsSuggestion &suggestion ); - - void clear(); - -Q_SIGNALS: - void selected( const LyricsSuggestion &suggestion ); - -private: - QList<LyricsSuggestionItem*> m_items; - QList<QGraphicsWidget*> m_separators; - QGraphicsLinearLayout *m_layout; - Q_DISABLE_COPY( LyricsSuggestionsListWidget ) -}; - -class LyricsSuggestionItem : public QGraphicsWidget -{ - Q_OBJECT - Q_PROPERTY( QUrl url READ url ) - Q_PROPERTY( QString title READ title ) - Q_PROPERTY( QString artist READ artist ) - -public: - LyricsSuggestionItem( const LyricsSuggestion &suggestion, QGraphicsItem *parent = 0 ); - ~LyricsSuggestionItem(); - - QString artist() const; - QString title() const; - QUrl url() const; - -Q_SIGNALS: - void selected( const LyricsSuggestion &suggestion ); - -private Q_SLOTS: - void onClicked(); - -private: - LyricsSuggestion m_data; - Q_DISABLE_COPY( LyricsSuggestionItem ) -}; - -inline QString LyricsSuggestionItem::title() const -{ return m_data.title; } - -inline QString LyricsSuggestionItem::artist() const -{ return m_data.artist; } - -inline QUrl LyricsSuggestionItem::url() const -{ return m_data.url; } - -Q_DECLARE_METATYPE( LyricsSuggestion ) - -#endif // LYRICS_SUGGESTIONS_LIST_WIDGET_H diff --git a/src/context/applets/lyrics/lyricsSettings.ui b/src/context/applets/lyrics/lyricsSettings.ui deleted file mode 100644 index 4b21a39aaa..0000000000 --- a/src/context/applets/lyrics/lyricsSettings.ui +++ /dev/null @@ -1,76 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>lyricsSettings</class> - <widget class="QWidget" name="lyricsSettings"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>195</width> - <height>131</height> - </rect> - </property> - <property name="windowTitle"> - <string>Lyrics Settings</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <widget class="QGroupBox" name="fontGroup"> - <property name="title"> - <string>Font</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="KFontRequester" name="fontChooser"/> - </item> - </layout> - </widget> - </item> - <item> - <widget class="KButtonGroup" name="alignmentGroup"> - <property name="title"> - <string>Alignment</string> - </property> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QRadioButton" name="alignLeft"> - <property name="text"> - <string comment="Left alignment">Left</string> - </property> - </widget> - </item> - <item> - <widget class="QRadioButton" name="alignCenter"> - <property name="text"> - <string comment="Center alignment">Center</string> - </property> - </widget> - </item> - <item> - <widget class="QRadioButton" name="alignRight"> - <property name="text"> - <string>Right</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - </layout> - </widget> - <customwidgets> - <customwidget> - <class>KFontRequester</class> - <extends>QWidget</extends> - <header>kfontrequester.h</header> - </customwidget> - <customwidget> - <class>KButtonGroup</class> - <extends>QGroupBox</extends> - <header>kbuttongroup.h</header> - <container>1</container> - </customwidget> - </customwidgets> - <resources/> - <connections/> -</ui> diff --git a/src/context/applets/lyrics/package/contents/ui/ConfigDialog.qml b/src/context/applets/lyrics/package/contents/ui/ConfigDialog.qml new file mode 100644 index 0000000000..cf57def7a3 --- /dev/null +++ b/src/context/applets/lyrics/package/contents/ui/ConfigDialog.qml @@ -0,0 +1,111 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman <malte.veerman@gmail.com> * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see <http://www.gnu.org/licenses/>. * + ****************************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 2.1 +import QtQuick.Dialogs 1.2 as Dialogs +import QtQuick.Layouts 1.3 +import org.kde.amarok.lyrics 1.0 + + +Dialogs.Dialog { + id: dialog + + function accept() { + LyricsEngine.fontSize = sizeBox.value; + LyricsEngine.font = fontCombo.currentText; + switch (alignmentCombo.currentIndex) { + case 0: + LyricsEngine.alignment = TextEdit.AlignLeft; + break; + case 1: + LyricsEngine.alignment = TextEdit.AlignRight; + break; + case 2: + LyricsEngine.alignment = TextEdit.AlignHCenter; + break; + } + } + + onAccepted: accept() + onApply: accept() + + title: i18n("Lyrics config") + standardButtons: Dialogs.StandardButton.Ok | Dialogs.StandardButton.Apply | Dialogs.StandardButton.Cancel + + Column { + width: 800 + + RowLayout { + width: parent.width + + Label { + Layout.alignment: Qt.AlignLeft + text: i18n("Font size:") + } + SpinBox { + id: sizeBox + + Layout.alignment: Qt.AlignRight + Layout.fillWidth: true + value: LyricsEngine.fontSize + editable: true + } + } + RowLayout { + width: parent.width + + Label { + Layout.alignment: Qt.AlignLeft + text: i18n("Text alignment:") + } + ComboBox { + id: alignmentCombo + + Layout.alignment: Qt.AlignRight + Layout.fillWidth: true + model: [i18n("Align left"), i18n("Align right"), i18n("Align center")] + currentIndex: { + switch (LyricsEngine.alignment) { + case TextEdit.AlignLeft: + return 0; + case TextEdit.AlignRight: + return 1; + case TextEdit.AlignHCenter: + return 2; + } + return 0; + } + } + } + RowLayout { + width: parent.width + + Label { + Layout.alignment: Qt.AlignLeft + text: i18n("Font:") + } + ComboBox { + id: fontCombo + + Layout.alignment: Qt.AlignRight + Layout.fillWidth: true + model: LyricsEngine.availableFonts() + currentIndex: LyricsEngine.availableFonts().indexOf(LyricsEngine.font) + } + } + } +} diff --git a/src/context/applets/lyrics/package/contents/ui/main.qml b/src/context/applets/lyrics/package/contents/ui/main.qml new file mode 100644 index 0000000000..937d282b64 --- /dev/null +++ b/src/context/applets/lyrics/package/contents/ui/main.qml @@ -0,0 +1,136 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman <malte.veerman@gmail.com> * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see <http://www.gnu.org/licenses/>. * + ****************************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 +import org.kde.amarok.qml 1.0 as AmarokQml +import org.kde.amarok.lyrics 1.0 + +AmarokQml.Applet { + id: applet + + property bool autoScroll: true + + configDialog: ConfigDialog {} + + Loader { + id: loader + + width: parent.width + height: parent.height + + sourceComponent: LyricsEngine.text === "" && LyricsEngine.suggestions.length > 0 ? suggestionsComponent : textComponent + + BusyIndicator { + anchors.centerIn: parent + running: LyricsEngine.fetching + } + } + + Component { + id: textComponent + + TextArea { + id: textArea + + text: LyricsEngine.text + font.pointSize: LyricsEngine.fontSize + font.family: LyricsEngine.font + wrapMode: TextEdit.Wrap + horizontalAlignment: LyricsEngine.alignment + verticalAlignment: TextEdit.AlignTop + textFormat: TextEdit.AutoText + readOnly: true + + Connections { + target: LyricsEngine + onPositionChanged: { + if (applet.autoScroll) { + var middle = textArea.contentHeight * LyricsEngine.position; + var top = middle - textArea.height / 2; + var maxTop = textArea.contentHeight - textArea.height; + var boundTop = Math.min(maxTop, Math.max(0, top)); + textArea.flickableItem.contentY = boundTop; + } + } + } + + RowLayout { + id: buttonRow + + y: applet.spacing + x: parent.effectiveHorizontalAlignment === TextEdit.AlignRight ? applet.spacing : parent.viewport.width - width - applet.spacing + + Button { + Layout.alignment: Qt.AlignRight + checkable: true + checked: applet.autoScroll + iconName: "arrow-down" + tooltip: i18n("Toggle auto scroll") + + onClicked: applet.autoScroll = !applet.autoScroll + } + Button { + Layout.alignment: Qt.AlignRight + iconName: "view-refresh" + tooltip: i18n("Reload lyrics") + + onClicked: LyricsEngine.refetchLyrics() + } + } + + Label { + anchors.centerIn: parent + text: i18n("No lyrics found") + visible: !textArea.text + font.pointSize: LyricsEngine.fontSize + font.family: LyricsEngine.font + } + } + } + + // untested + Component { + id: suggestionsComponent + + ListView { + model: LyricsEngine.suggestions + header: Label { + text: i18n("Suggestions: ") + LyricsEngine.suggestions.length + } + delegate: Item { + width: parent.width + + Column { + width: parent.width + + Label { + text: '<b>' + i18n("Title:") + '</b> ' + modelData[0] + } + Label { + text: '<b>' + i18n("Artist:") + '</b> ' + modelData[1] + } + } + MouseArea { + anchors.fill: parent + + onClicked: LyricsEngine.fetchLyrics(modelData[0], modelData[1], modelData[2]) + } + } + } + } +} diff --git a/src/context/applets/lyrics/amarok-context-applet-lyrics.desktop b/src/context/applets/lyrics/package/metadata.desktop similarity index 88% rename from src/context/applets/lyrics/amarok-context-applet-lyrics.desktop rename to src/context/applets/lyrics/package/metadata.desktop index a26d7af5d3..24c7fd6169 100644 --- a/src/context/applets/lyrics/amarok-context-applet-lyrics.desktop +++ b/src/context/applets/lyrics/package/metadata.desktop @@ -1,73 +1,71 @@ [Desktop Entry] Name=Lyrics Name[bg]=Текстове Name[bs]=Stihovi Name[ca]=Lletres Name[ca@valencia]=Lletres Name[cs]=Texty písní Name[csb]=Tekstë Name[da]=Sangtekster Name[de]=Liedtext Name[el]=Στίχοι Name[en_GB]=Lyrics Name[es]=Letras Name[et]=Sõnad Name[eu]=Hitzak Name[fa]=ترانه Name[fi]=Sanat Name[fr]=Paroles Name[ga]=Liricí Name[gl]=Letras de cancións Name[hu]=Dalszöveg Name[ia]=Parolas del Canto Name[id]=Lirik Name[is]=Lagatextar Name[it]=Testi Name[ja]=歌詞 Name[km]=ទំនុក​ Name[ko]=가사 Name[lt]=Tekstai Name[lv]=Dziesmu vārdi Name[mr]=गाण्याचे बोल Name[nb]=Sangtekst Name[nds]=Leedtexten Name[nl]=Liedteksten Name[nn]=Songtekst Name[pa]=ਬੋਲ Name[pl]=Słowa Name[pt]=Letras Musicais Name[pt_BR]=Letras Name[ro]=Versuri Name[ru]=Текст песни Name[sk]=Texty piesní Name[sl]=Besedilo Name[sq]=Lirikat Name[sr]=Стихови Name[sr@ijekavian]=Стихови Name[sr@ijekavianlatin]=Stihovi Name[sr@latin]=Stihovi Name[sv]=Sångtext Name[th]=เนื้อร้อง Name[tr]=Şarkı Sözleri Name[ug]=ناخشا سۆزى Name[uk]=Текст пісні Name[wa]=Paroles Name[x-test]=xxLyricsxx Name[zh_CN]=歌词 Name[zh_TW]=歌詞 + Type=Service Icon=amarok_lyrics -ServiceTypes=Plasma/Applet +ServiceTypes=Amarok/ContextApplet -X-KDE-Library=amarok_context_applet_lyrics +X-KDE-PluginInfo-Name=org.kde.amarok.lyrics X-KDE-PluginInfo-Author=Leo Franchi X-KDE-PluginInfo-Email=lfranchi@gmail.com -X-KDE-PluginInfo-Name=lyrics -X-KDE-PluginInfo-Version=pre0.1 +X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website= X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true -X-KDE-ParentApp=amarok -X-KDE-PluginInfo-Category=Current - +X-KDE-PluginInfo-Category=Multimedia diff --git a/src/context/engines/lyrics/LyricsEngine.cpp b/src/context/applets/lyrics/plugin/LyricsEngine.cpp similarity index 57% rename from src/context/engines/lyrics/LyricsEngine.cpp rename to src/context/applets/lyrics/plugin/LyricsEngine.cpp index 6f8efcec03..799a6021a7 100644 --- a/src/context/engines/lyrics/LyricsEngine.cpp +++ b/src/context/applets/lyrics/plugin/LyricsEngine.cpp @@ -1,194 +1,288 @@ /**************************************************************************************** * Copyright (c) 2007-2008 Leo Franchi <lfranchi@gmail.com> * * Copyright (c) 2008 Mark Kretschmann <kretschmann@kde.org> * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************************/ #define DEBUG_PREFIX "LyricsEngine" #include "LyricsEngine.h" #include "EngineController.h" #include "scripting/scriptmanager/ScriptManager.h" -#include "context/ContextView.h" -#include "core/meta/Meta.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" -#include <QTimer> -#include <QTextDocument> +#include <QFont> -using namespace Context; +#include <KFontChooser> -LyricsEngine::LyricsEngine( QObject* parent, const QList<QVariant>& /*args*/ ) - : DataEngine( parent ) + +LyricsEngine::LyricsEngine( QObject* parent ) + : QObject( parent ) , LyricsObserver( LyricsManager::self() ) + , m_fetching( false ) , m_isUpdateInProgress( false ) { EngineController* engine = The::engineController(); - connect( engine, SIGNAL(trackChanged(Meta::TrackPtr)), - this, SLOT(update()), Qt::QueuedConnection ); - connect( engine, SIGNAL(trackMetadataChanged(Meta::TrackPtr)), - this, SLOT(onTrackMetadataChanged(Meta::TrackPtr)), Qt::QueuedConnection ); -} - -QStringList LyricsEngine::sources() const -{ - QStringList sourcesList; - sourcesList << "lyrics" << "suggested"; - - return sourcesList; -} -bool LyricsEngine::sourceRequestEvent( const QString& name ) -{ - removeAllData( name ); - setData( name, QVariant()); - // in the case where we are resuming playback on startup. Need to be sure - // the script manager is running and a lyrics script is loaded first. - QTimer::singleShot( 0, this, SLOT(update()) ); - return true; + connect( engine, &EngineController::trackChanged, + this, &LyricsEngine::update ); + connect( engine, &EngineController::trackMetadataChanged, + this, &LyricsEngine::onTrackMetadataChanged ); + connect( engine, &EngineController::trackPositionChanged, + this, &LyricsEngine::positionChanged ); } void LyricsEngine::onTrackMetadataChanged( Meta::TrackPtr track ) { DEBUG_BLOCK // Only update if the lyrics have changed. QString artist = track->artist() ? track->artist()->name() : QString(); - if( m_prevLyrics.artist != artist || - m_prevLyrics.title != track->name() || - m_prevLyrics.text != track->cachedLyrics() ) + if( m_lyrics.artist != artist || + m_lyrics.title != track->name() || + m_lyrics.text != track->cachedLyrics() ) update(); } void LyricsEngine::update() { if( m_isUpdateInProgress ) return; m_isUpdateInProgress = true; // -- get current title and artist Meta::TrackPtr currentTrack = The::engineController()->currentTrack(); if( !currentTrack ) { debug() << "no current track"; - m_prevLyrics.clear(); - removeAllData( "lyrics" ); - setData( "lyrics", "stopped", "stopped" ); + m_lyrics.clear(); + emit lyricsChanged(); m_isUpdateInProgress = false; return; } QString title = currentTrack->name(); QString artist = currentTrack->artist() ? currentTrack->artist()->name() : QString(); // -- clean up title const QString magnatunePreviewString = QLatin1String( "PREVIEW: buy it at www.magnatune.com" ); if( title.contains(magnatunePreviewString, Qt::CaseSensitive) ) title = title.remove( " (" + magnatunePreviewString + ')' ); if( artist.contains(magnatunePreviewString, Qt::CaseSensitive) ) artist = artist.remove( " (" + magnatunePreviewString + ')' ); if( title.isEmpty() && currentTrack ) { /* If title is empty, try to use pretty title. The fact that it often (but not always) has "artist name" together, can be bad, but at least the user will hopefully get nice suggestions. */ QString prettyTitle = currentTrack->prettyName(); int h = prettyTitle.indexOf( QLatin1Char('-') ); if ( h != -1 ) { title = prettyTitle.mid( h + 1 ).trimmed(); if( title.contains(magnatunePreviewString, Qt::CaseSensitive) ) title = title.remove( " (" + magnatunePreviewString + ')' ); if( artist.isEmpty() ) { artist = prettyTitle.mid( 0, h ).trimmed(); if( artist.contains(magnatunePreviewString, Qt::CaseSensitive) ) artist = artist.remove( " (" + magnatunePreviewString + ')' ); } } } LyricsData lyrics = { currentTrack->cachedLyrics(), title, artist, QUrl() }; // Check if the title, the artist and the lyrics are still the same. - if( !lyrics.text.isEmpty() && (lyrics.text == m_prevLyrics.text) ) + if( !lyrics.text.isEmpty() && (lyrics.text == m_lyrics.text) ) { debug() << "nothing changed:" << lyrics.title; newLyrics( lyrics ); m_isUpdateInProgress = false; return; } // don't rely on caching for streams const bool cached = !LyricsManager::self()->isEmpty( lyrics.text ) && !The::engineController()->isStream(); if( cached ) { newLyrics( lyrics ); } else { // no lyrics, and no lyrics script! if( !ScriptManager::instance()->lyricsScriptRunning() ) { debug() << "no lyrics script running"; - removeAllData( "lyrics" ); - setData( "lyrics", "noscriptrunning", "noscriptrunning" ); - disconnect( ScriptManager::instance(), SIGNAL(lyricsScriptStarted()), this, 0 ); - connect( ScriptManager::instance(), SIGNAL(lyricsScriptStarted()), SLOT(update()) ); + clearLyrics(); + disconnect( ScriptManager::instance(), &ScriptManager::lyricsScriptStarted, this, 0 ); + connect( ScriptManager::instance(), &ScriptManager::lyricsScriptStarted, this, &LyricsEngine::update ); m_isUpdateInProgress = false; return; } // fetch by lyrics script - removeAllData( "lyrics" ); - setData( "lyrics", "fetching", "fetching" ); + clearLyrics(); + m_fetching = true; + emit fetchingChanged(); ScriptManager::instance()->notifyFetchLyrics( lyrics.artist, lyrics.title, "", currentTrack ); } m_isUpdateInProgress = false; } void LyricsEngine::newLyrics( const LyricsData &lyrics ) { - QString key = Qt::mightBeRichText( lyrics.text ) ? QLatin1String( "html" ) - : QLatin1String( "lyrics" ); - removeAllData( "lyrics" ); - setData( "lyrics", key, QVariant::fromValue(lyrics) ); - m_prevLyrics = lyrics; + DEBUG_BLOCK + + m_lyrics = lyrics; + emit lyricsChanged(); + + m_fetching = false; + emit fetchingChanged(); } void LyricsEngine::newSuggestions( const QVariantList &suggested ) { DEBUG_BLOCK + // each string is in "title - artist <url>" form - removeAllData( "lyrics" ); - setData( "lyrics", "suggested", suggested ); + m_suggestions = suggested; + clearLyrics(); } void LyricsEngine::lyricsMessage( const QString& key, const QString &val ) { DEBUG_BLOCK - removeAllData( "lyrics" ); - setData( "lyrics", key, val ); + clearLyrics(); + emit newLyricsMessage( key, val ); +} + +void LyricsEngine::clearLyrics() +{ + m_fetching = false; + emit fetchingChanged(); + + m_lyrics.clear(); + emit lyricsChanged(); +} + +qreal LyricsEngine::position() const +{ + return (qreal)The::engineController()->trackPosition() * 1000 / The::engineController()->trackLength(); +} + +void LyricsEngine::refetchLyrics() const +{ + Meta::TrackPtr currentTrack = The::engineController()->currentTrack(); + if( !currentTrack ) + return; + + ScriptManager::instance()->notifyFetchLyrics( m_lyrics.artist, m_lyrics.title, "", currentTrack ); +} + +void LyricsEngine::refetchLyrics() +{ + DEBUG_BLOCK + + auto currentTrack = The::engineController()->currentTrack(); + + if( currentTrack ) + ScriptManager::instance()->notifyFetchLyrics( currentTrack->artist()->name(), + currentTrack->name(), "", currentTrack ); + + m_fetching = true; + emit fetchingChanged(); } +void LyricsEngine::fetchLyrics(const QString& artist, const QString& title, const QString& url) +{ + DEBUG_BLOCK + + if( !QUrl( url ).isValid() ) + return; + + debug() << "clicked suggestion" << url; + + ScriptManager::instance()->notifyFetchLyrics( artist, title, url, Meta::TrackPtr() ); + + m_fetching = true; + emit fetchingChanged(); +} + +qreal LyricsEngine::fontSize() const +{ + return Amarok::config( "Context" ).group( "Lyrics" ).readEntry( "fontSize", 18 ); +} + +void LyricsEngine::setFontSize(qreal fontSize) +{ + DEBUG_BLOCK + + if( fontSize == this->fontSize() ) + return; + + Amarok::config( "Context" ).group( "Lyrics" ).writeEntry( "fontSize", fontSize ); + emit fontSizeChanged(); +} + +int LyricsEngine::alignment() const +{ + return Amarok::config( "Context" ).group( "Lyrics" ).readEntry( "alignment", 2 ); +} + +void LyricsEngine::setAlignment(int alignment) +{ + DEBUG_BLOCK + + if( alignment == this->alignment() ) + return; + + Amarok::config( "Context" ).group( "Lyrics" ).writeEntry( "alignment", alignment ); + emit alignmentChanged(); +} + +QString LyricsEngine::font() const +{ + return Amarok::config( "Context" ).group( "Lyrics" ).readEntry( "font", QFont().family() ); +} + +void LyricsEngine::setFont(const QString& font) +{ + DEBUG_BLOCK + + if( font == this->font() ) + return; + + Amarok::config( "Context" ).group( "Lyrics" ).writeEntry( "font", font ); + emit fontChanged(); +} + +QStringList LyricsEngine::availableFonts() const +{ + QStringList list; + + KFontChooser::getFontList( list, 0 ); + + return list; +} diff --git a/src/context/engines/lyrics/LyricsEngine.h b/src/context/applets/lyrics/plugin/LyricsEngine.h similarity index 50% rename from src/context/engines/lyrics/LyricsEngine.h rename to src/context/applets/lyrics/plugin/LyricsEngine.h index 20b361fdca..ad92668154 100644 --- a/src/context/engines/lyrics/LyricsEngine.h +++ b/src/context/applets/lyrics/plugin/LyricsEngine.h @@ -1,68 +1,91 @@ /**************************************************************************************** * Copyright (c) 2007 Leo Franchi <lfranchi@gmail.com> * * Copyright (c) 2008 Mark Kretschmann <kretschmann@kde.org> * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************************/ #ifndef AMAROK_LYRICS_ENGINE #define AMAROK_LYRICS_ENGINE -#include "context/DataEngine.h" #include "context/LyricsManager.h" -#include "core/meta/forward_declarations.h" +#include "core/meta/Meta.h" -/** - This class provides Lyrics data for use in Context applets. +#include <QObject> +#include <QString> +#include <QVariantList> -NOTE: The QVariant data is structured like this: - * the key name is lyrics - * the data is a QVariantList with title, artist, lyricsurl, lyrics -*/ - -using namespace Context; - -class LyricsEngine : public DataEngine, public LyricsObserver +class LyricsEngine : public QObject, public LyricsObserver { Q_OBJECT + Q_PROPERTY(QString text READ text NOTIFY lyricsChanged) + Q_PROPERTY(bool fetching READ fetching NOTIFY fetchingChanged) + Q_PROPERTY(QVariantList suggestions READ suggestions NOTIFY lyricsChanged) + Q_PROPERTY(qreal position READ position NOTIFY positionChanged) + Q_PROPERTY(qreal fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged) + Q_PROPERTY(int alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged) + Q_PROPERTY(QString font READ font WRITE setFont NOTIFY fontChanged) public: - LyricsEngine( QObject* parent, const QList<QVariant>& args ); - - QStringList sources() const; + LyricsEngine( QObject* parent = Q_NULLPTR ); // reimplemented from LyricsObserver - void newLyrics( const LyricsData &lyrics ); - void newSuggestions( const QVariantList &suggest ); - void lyricsMessage( const QString& key, const QString& val ); + void newLyrics( const LyricsData &lyrics ) Q_DECL_OVERRIDE; + void newSuggestions( const QVariantList &suggest ) Q_DECL_OVERRIDE; + void lyricsMessage( const QString& key, const QString& val ) Q_DECL_OVERRIDE; + + QString text() const { return m_lyrics.text; } + QVariantList suggestions() const { return m_suggestions; } + bool fetching() const { return m_fetching; } + qreal position() const; + qreal fontSize() const; + void setFontSize( qreal fontSize ); + int alignment() const; + void setAlignment( int alignment ); + QString font() const; + void setFont( const QString &font ); -protected: - bool sourceRequestEvent( const QString& name ); + Q_INVOKABLE void refetchLyrics() const; + Q_INVOKABLE void fetchLyrics( const QString &artist, const QString &title, const QString &url ); + Q_INVOKABLE QStringList availableFonts() const; + +Q_SIGNALS: + void lyricsChanged(); + void newLyricsMessage( const QString& key, const QString &val ); + void positionChanged(); + void fetchingChanged(); + void fontSizeChanged(); + void alignmentChanged(); + void fontChanged(); private Q_SLOTS: void update(); void onTrackMetadataChanged( Meta::TrackPtr track ); private: - LyricsData m_prevLyrics; + void setLyrics( const LyricsData &lyrics ); + void clearLyrics(); + void refetchLyrics(); + + LyricsData m_lyrics; + QVariantList m_suggestions; + bool m_fetching; bool m_isUpdateInProgress; struct trackMetadata { QString artist; QString title; } m_prevTrackMetadata; }; -AMAROK_EXPORT_DATAENGINE( lyrics, LyricsEngine ) - #endif diff --git a/src/context/applets/lyrics/plugin/LyricsPlugin.cpp b/src/context/applets/lyrics/plugin/LyricsPlugin.cpp new file mode 100644 index 0000000000..fc64e437fb --- /dev/null +++ b/src/context/applets/lyrics/plugin/LyricsPlugin.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2017 Malte Veerman <malte.veerman@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "LyricsEngine.h" + +#include <QQmlExtensionPlugin> + +#include <qqml.h> + + +class LyricsPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char* uri) Q_DECL_OVERRIDE + { + Q_ASSERT(uri == QLatin1String("org.kde.amarok.lyrics")); + + qmlRegisterSingletonType<LyricsEngine>(uri, 1, 0, "LyricsEngine", lyrics_engine_provider); + } + + static QObject *lyrics_engine_provider(QQmlEngine *engine, QJSEngine *scriptEngine) + { + Q_UNUSED(engine) + Q_UNUSED(scriptEngine) + + return new LyricsEngine(); + } +}; + +#include <LyricsPlugin.moc> diff --git a/src/context/applets/lyrics/plugin/qmldir b/src/context/applets/lyrics/plugin/qmldir new file mode 100644 index 0000000000..39eb8d3187 --- /dev/null +++ b/src/context/applets/lyrics/plugin/qmldir @@ -0,0 +1,2 @@ +module org.kde.amarok.lyrics +plugin amarok_context_applet_lyrics diff --git a/src/context/applets/photos/CMakeLists.txt b/src/context/applets/photos/CMakeLists.txt index 7b0f7dfcf4..3d863a8cc7 100644 --- a/src/context/applets/photos/CMakeLists.txt +++ b/src/context/applets/photos/CMakeLists.txt @@ -1,22 +1,17 @@ -include_directories( - ${Amarok_SOURCE_DIR}/src - ${Amarok_SOURCE_DIR}/src/network +set(photos_SRCS + plugin/PhotosPlugin.cpp + plugin/PhotosEngine.cpp ) -set( photos_applet_SRCS - DragPixmapItem.cpp - PhotosApplet.cpp - PhotosScrollWidget.cpp -) - -ki18n_wrap_ui( photos_applet_SRCS photosSettings.ui ) +add_library(amarok_context_applet_photos SHARED ${photos_SRCS}) -add_library(amarok_context_applet_photos MODULE ${photos_applet_SRCS}) -target_link_libraries( amarok_context_applet_photos +target_link_libraries(amarok_context_applet_photos amarokcore amaroklib - KF5::Plasma + Qt5::Qml ) -install( TARGETS amarok_context_applet_photos DESTINATION ${PLUGIN_INSTALL_DIR} ) -install( FILES amarok-context-applet-photos.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install(TARGETS amarok_context_applet_photos DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/photos) +install(FILES plugin/qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/photos) + +kpackage_install_package(package org.kde.amarok.photos amarok) diff --git a/src/context/applets/photos/DragPixmapItem.cpp b/src/context/applets/photos/DragPixmapItem.cpp deleted file mode 100644 index 7b9e5ccb8e..0000000000 --- a/src/context/applets/photos/DragPixmapItem.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see <http://www.gnu.org/licenses/>. * - ****************************************************************************************/ - -#define DEBUG_PREFIX "DragPixmapItem" - -#include "DragPixmapItem.h" - -#include "core/support/Debug.h" - -#include <QIcon> -#include <KLocale> - -#include <QApplication> -#include <QDesktopServices> -#include <QDrag> -#include <QGraphicsSceneMouseEvent> -#include <QMimeData> -#include <QPoint> - -DragPixmapItem::DragPixmapItem( QGraphicsItem* parent ) - : QGraphicsPixmapItem( parent ) - , m_dragPos( QPoint() ) -{ - setAcceptDrops( true ); - setCursor( Qt::PointingHandCursor ); -} - -void DragPixmapItem::SetClickableUrl( const QUrl &url ) -{ - m_url = url; -} - -void DragPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent* event) -{ -// DEBUG_BLOCK - - if (event->button() == Qt::LeftButton) - m_dragPos = event->pos().toPoint(); -} - -void DragPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) -{ - DEBUG_BLOCK - - if ( event->button() == Qt::LeftButton ) - { - if ( !m_url.isEmpty() ) - { - QDesktopServices::openUrl( m_url ); - debug() << "DragPixmapItem: clicked photos url "<<m_url; - } - } -} - -void DragPixmapItem::mouseMoveEvent( QGraphicsSceneMouseEvent* event ) -{ - if ( !( event->buttons() & Qt::LeftButton ) ) - return; - if ( ( event->pos().toPoint() - m_dragPos ).manhattanLength() < QApplication::startDragDistance() ) - return; - - QMimeData *data = new QMimeData; - data->setImageData( this->pixmap().toImage() ); - - QDrag *drag = new QDrag( event->widget() ); - drag->setMimeData( data ); - drag->setPixmap( pixmap().scaledToWidth( 140 ) ); - drag->setDragCursor( QIcon::fromTheme( "insert-image" ).pixmap( 24, 24 ), Qt::CopyAction ); - drag->exec( Qt::CopyAction ); -} - diff --git a/src/context/applets/photos/DragPixmapItem.h b/src/context/applets/photos/DragPixmapItem.h deleted file mode 100644 index bc2055abe2..0000000000 --- a/src/context/applets/photos/DragPixmapItem.h +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see <http://www.gnu.org/licenses/>. * - ****************************************************************************************/ - -#ifndef DRAGPIXMAPITEM_H -#define DRAGPIXMAPITEM_H - -#include "amarok_export.h" - -#include <QUrl> - -#include <QGraphicsPixmapItem> - -//forward -class QGraphicsSceneMouseEvent; - -/** -* \brief A drag-able QGraphicsPixmapItem -* -* Display a pixmap which is draggable and clickable. -* -* \sa QGraphicsPixmapItem -* -* \author Simon Esneault <simon.esneault@gmail.com> -*/ - -class DragPixmapItem : public QObject, public QGraphicsPixmapItem -{ - Q_OBJECT - public: - DragPixmapItem( QGraphicsItem* parent = 0 ); - - void SetClickableUrl( const QUrl &url ); - - protected Q_SLOTS: - /** - * Reimplement mouse event - */ - virtual void mousePressEvent( QGraphicsSceneMouseEvent * ); - virtual void mouseMoveEvent( QGraphicsSceneMouseEvent * ); - virtual void mouseReleaseEvent( QGraphicsSceneMouseEvent * ); - - private: - QPoint m_dragPos; - QUrl m_url; -}; - -#endif // DROPPIXMAPITEM_H diff --git a/src/context/applets/photos/PhotosApplet.cpp b/src/context/applets/photos/PhotosApplet.cpp deleted file mode 100644 index aa0e94568f..0000000000 --- a/src/context/applets/photos/PhotosApplet.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see <http://www.gnu.org/licenses/>. * - ****************************************************************************************/ - -#define DEBUG_PREFIX "PhotosApplet" - -#include "PhotosApplet.h" -#include "PhotosScrollWidget.h" - -// Amarok -#include "core/support/Amarok.h" -#include "core/support/Debug.h" -#include "context/ContextView.h" -#include "context/engines/photos/PhotosInfo.h" -#include "context/widgets/AppletHeader.h" - -// KDE -#include <QAction> -#include <KColorScheme> -#include <KConfigDialog> -#include <KGlobalSettings> -#include <Plasma/BusyWidget> -#include <Plasma/IconWidget> -#include <Plasma/Theme> - -// Qt -#include <QGraphicsLinearLayout> -#include <QGraphicsProxyWidget> -#include <QGraphicsTextItem> -#include <QGraphicsWidget> -#include <KConfigGroup> -#include <QDialogButtonBox> -#include <QPushButton> -#include <QVBoxLayout> - -PhotosApplet::PhotosApplet( QObject* parent, const QVariantList& args ) - : Context::Applet( parent, args ) - , m_settingsIcon( 0 ) -{ - DEBUG_BLOCK - setHasConfigurationInterface( true ); -} - -void -PhotosApplet::init() -{ - DEBUG_BLOCK - - // Call the base implementation. - Context::Applet::init(); - - // Create label - enableHeader( true ); - setHeaderText( i18n( "Photos" ) ); - - // Set the collapse size - setCollapseHeight( m_header->height() ); - setCollapseOffHeight( 220 ); - setMaximumHeight( 220 ); - setMinimumHeight( collapseHeight() ); - setPreferredHeight( collapseHeight() ); - - // Icon - QAction* settingsAction = new QAction( this ); - settingsAction->setIcon( QIcon::fromTheme( "preferences-system" ) ); - settingsAction->setVisible( true ); - settingsAction->setEnabled( true ); - settingsAction->setText( i18n( "Settings" ) ); - m_settingsIcon = addRightHeaderAction( settingsAction ); - connect( m_settingsIcon, SIGNAL(clicked()), this, SLOT(showConfigurationInterface()) ); - - m_widget = new PhotosScrollWidget( this ); - m_widget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - m_widget->setContentsMargins( 0, 0, 0, 0 ); - connect( m_widget, SIGNAL(photoAdded()), SLOT(photoAdded()) ); - - QGraphicsLinearLayout *layout = new QGraphicsLinearLayout( Qt::Vertical, this ); - layout->addItem( m_header ); - layout->addItem( m_widget ); - - // Read config and inform the engine. - KConfigGroup config = Amarok::config("Photos Applet"); - m_nbPhotos = config.readEntry( "NbPhotos", "10" ).toInt(); - m_Animation = config.readEntry( "Animation", "Fading" ); - m_KeyWords = config.readEntry( "KeyWords", QStringList() ); - - if( m_Animation == i18nc( "animation type", "Automatic" ) ) - m_widget->setMode( 0 ); - else if( m_Animation == i18n( "Interactive" ) ) - m_widget->setMode( 1 ); - else // fading - m_widget->setMode( 2 ); - - Plasma::DataEngine *engine = dataEngine( "amarok-photos" ); - engine->setProperty( "fetchSize", m_nbPhotos ); - engine->setProperty( "keywords", m_KeyWords ); - engine->connectSource( "photos", this ); -} - -PhotosApplet::~PhotosApplet() -{ - DEBUG_BLOCK -} - -void -PhotosApplet::stopped() -{ - DEBUG_BLOCK - setHeaderText( i18n( "Photos: No Track Playing" ) ); - m_widget->clear(); - m_widget->hide(); - setBusy( false ); - setMinimumHeight( m_header->height() ); - setCollapseHeight( m_header->height() ); - setCollapseOn(); - updateConstraints(); -} - -void -PhotosApplet::photoAdded() -{ - setBusy( false ); - setHeaderText( i18ncp( "@title:window Number of photos of artist", - "1 Photo: %2", - "%1 Photos: %2", - m_widget->count(), - m_currentArtist ) ); -} - -void -PhotosApplet::dataUpdated( const QString& name, const Plasma::DataEngine::Data& data ) // SLOT -{ - if( name != QLatin1String("photos") || data.isEmpty() ) - return; - - QString text; - - if( data.contains( "message" ) ) - { - text = data["message"].toString(); - if( text.contains( QLatin1String("Fetching") ) ) - { - debug() << "received message: Fetching"; - setHeaderText( i18n( "Photos: %1", text ) ); - setMinimumHeight( m_header->height() ); - setCollapseHeight( m_header->height() ); - setCollapseOn(); - m_widget->clear(); - m_widget->hide(); - if( canAnimate() ) - setBusy( true ); - } - else if( text.contains( QLatin1String("stopped") ) ) - { - debug() << "received message: stopped"; - stopped(); - } - else - { - debug() << "received message:" << text; - setHeaderText( i18n( "Photos: %1", text ) ); - m_widget->hide(); - setMinimumHeight( m_header->height() ); - setCollapseHeight( m_header->height() ); - setCollapseOn(); - setBusy( false ); - } - } - else if( data.contains( "data" ) ) - { - m_widget->clear(); - m_currentArtist = text = data["artist"].toString(); - PhotosInfo::List photos = data["data"].value< PhotosInfo::List >(); - debug() << "received data for:" << text << photos.count(); - setHeaderText( i18n( "Photos: %1", text ) ); - if( photos.isEmpty() ) - { - setBusy( false ); - setMinimumHeight( m_header->height() ); - setCollapseHeight( m_header->height() ); - setCollapseOn(); - return; - } - setBusy( true ); - m_widget->setPhotosInfoList( photos ); - setMinimumHeight( 220 ); - setCollapseOff(); - m_widget->show(); - layout()->invalidate(); - } - else - { - setMinimumHeight( m_header->height() ); - setCollapseHeight( m_header->height() ); - setCollapseOn(); - m_widget->clear(); - m_widget->hide(); - setBusy( false ); - } - updateConstraints(); -} - -void -PhotosApplet::createConfigurationInterface( KConfigDialog *parent ) -{ - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); - QVBoxLayout *mainLayout = new QVBoxLayout; - parent->setLayout(mainLayout); - QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); - okButton->setDefault(true); - okButton->setShortcut(Qt::CTRL | Qt::Key_Return); - parent->connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); - parent->connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - mainLayout->addWidget(buttonBox); - - KConfigGroup configuration = config(); - QWidget *settings = new QWidget; - ui_Settings.setupUi( settings ); - - parent->addPage( settings, i18n( "Photos Settings" ), "preferences-system"); - - ui_Settings.animationComboBox->setCurrentIndex( ui_Settings.animationComboBox->findText( m_Animation ) ); - ui_Settings.photosSpinBox->setValue( m_nbPhotos ); - ui_Settings.additionalkeywordsLineEdit->setText( m_KeyWords.join(", ") ); - connect( parent, SIGNAL(accepted()), this, SLOT(saveSettings()) ); -} - -void -PhotosApplet::saveSettings() -{ - DEBUG_BLOCK - KConfigGroup config = Amarok::config("Photos Applet"); - - m_nbPhotos = ui_Settings.photosSpinBox->value(); - m_Animation = ui_Settings.animationComboBox->currentText(); - m_KeyWords = ui_Settings.additionalkeywordsLineEdit->text().split(", "); - config.writeEntry( "NbPhotos", m_nbPhotos ); - config.writeEntry( "Animation", m_Animation ); - config.writeEntry( "KeyWords", m_KeyWords ); - - m_widget->setMode( ui_Settings.animationComboBox->currentIndex() ); - m_widget->clear(); - - Plasma::DataEngine *engine = dataEngine( "amarok-photos" ); - engine->setProperty( "fetchSize", m_nbPhotos ); - engine->setProperty( "keywords", m_KeyWords ); - engine->query( QLatin1String( "photos:forceUpdate" ) ); -} - diff --git a/src/context/applets/photos/PhotosApplet.h b/src/context/applets/photos/PhotosApplet.h deleted file mode 100644 index 84970ce07f..0000000000 --- a/src/context/applets/photos/PhotosApplet.h +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see <http://www.gnu.org/licenses/>. * - ****************************************************************************************/ - -//Plasma applet for showing photos from flickr - -#ifndef PHOTOS_APPLET_H -#define PHOTOS_APPLET_H - -#include "context/Applet.h" -#include "context/DataEngine.h" - -#include <ui_photosSettings.h> - -class KConfigDialog; -class PhotosScrollWidget; -class QGraphicsSimpleTextItem; - -namespace Plasma -{ - class IconWidget; -} - - /** PhotosApplet will display photos from the Internet, relative to the current playing song - */ -class PhotosApplet : public Context::Applet -{ - Q_OBJECT - - public: - PhotosApplet( QObject* parent, const QVariantList& args ); - ~PhotosApplet(); - - public Q_SLOTS: - virtual void init(); - void dataUpdated( const QString& name, const Plasma::DataEngine::Data& data ); - void saveSettings(); - - protected Q_SLOTS: - void stopped(); - - protected: - void createConfigurationInterface(KConfigDialog *parent); - - private Q_SLOTS: - void photoAdded(); - - private: - PhotosScrollWidget *m_widget; - - int m_nbPhotos; - - QString m_currentArtist; - QString m_Animation; - QStringList m_KeyWords; - - Ui::photosSettings ui_Settings; - Plasma::IconWidget *m_settingsIcon; -}; - -AMAROK_EXPORT_APPLET( photos, PhotosApplet ) - -#endif /* Photos_APPLET_H */ diff --git a/src/context/applets/photos/PhotosScrollWidget.cpp b/src/context/applets/photos/PhotosScrollWidget.cpp deleted file mode 100644 index 79f33b6b3b..0000000000 --- a/src/context/applets/photos/PhotosScrollWidget.cpp +++ /dev/null @@ -1,512 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * - * 2009 Nikolaj Hald Nielsen <nhn@kde.org> * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see <http://www.gnu.org/licenses/>. * - ****************************************************************************************/ - -#define DEBUG_PREFIX "PhotosScrollWidget" - -#include "PhotosScrollWidget.h" -#include "DragPixmapItem.h" - -// Amarok -#include "core/support/Amarok.h" -#include "core/support/Debug.h" -#include "SvgHandler.h" - -// QT -#include <QGraphicsItem> -#include <QGraphicsSceneHoverEvent> -#include <QList> -#include <QPixmap> -#include <QPixmapCache> -#include <QTimer> -#include <QPropertyAnimation> - -PhotosScrollWidget::PhotosScrollWidget( QGraphicsItem* parent ) - : QGraphicsWidget( parent ) - , m_speed( 1. ) - , m_margin( 5 ) - , m_scrollmax( 0 ) - , m_actualpos( 0 ) - , m_currentPix( 0 ) - , m_lastPix( 0 ) - , m_interval( 3500 ) - , m_mode( PHOTOS_MODE_INTERACTIVE ) - , m_delta( 0 ) - , m_animation( new QPropertyAnimation( this, "animValue" ) ) -{ - - setAcceptHoverEvents( true ); - setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); - - // prepare the timer for the fading effect - m_timer = new QTimer( this ); - m_timer->setSingleShot( true ); - connect(m_timer, SIGNAL(timeout()), this, SLOT(automaticAnimBegin()) ); - - m_animation->setEasingCurve( QEasingCurve::Linear ); - m_animation->setStartValue( 0.0 ); - m_animation->setEndValue( 1.0 ); - - // connect the end of the animation - connect( m_animation, SIGNAL(finished()), this, SLOT(automaticAnimEnd()) ); -} - -PhotosScrollWidget::~PhotosScrollWidget() -{ - clear(); -} - -void PhotosScrollWidget::clear() -{ - // DEBUG_BLOCK - if( m_animation->state() == QAbstractAnimation::Running ) - m_animation->stop(); - - // stop the timer for animation - if( m_timer->isActive() ) - m_timer->stop(); - - //delete!!! - // debug() << "Going to delete " << m_pixmaplist.count() << " items"; - - qDeleteAll( m_pixmaplist ); - - m_pixmaplist.clear(); - m_currentlist.clear(); - m_scrollmax = 0; - m_actualpos = 0; - m_currentPix = 0; - m_lastPix = 0; -} - -int PhotosScrollWidget::count() const -{ - return m_pixmaplist.count(); -} - -void PhotosScrollWidget::setMode( int mode ) -{ - DEBUG_BLOCK - m_mode = mode; - PhotosInfo::List tmp = m_currentlist; - clear(); - setPhotosInfoList( tmp ); - tmp.clear(); -} - -void PhotosScrollWidget::setPhotosInfoList( const PhotosInfo::List &list ) -{ - DEBUG_BLOCK - // if the list is the same, nothing happen. - if( list == m_currentlist ) - return; - - PhotosInfo::List toAddList; - foreach( const PhotosInfoPtr &item, list ) - { - if( m_currentlist.contains( item ) ) - continue; - - QUrl url = item->urlphoto; - if( url.isValid() ) - { - QPixmap pixmap; - if( QPixmapCache::find( url.url(), &pixmap ) ) - { - addPhoto( item, pixmap ); - } - else - { - m_infoHash[ url ] = item; - The::networkAccessManager()->getData( url, this, - SLOT(photoFetched(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); - } - toAddList << item; - } - } - debug() << "adding" << toAddList.count() << "new photos"; - m_currentlist = toAddList; -} - -void PhotosScrollWidget::photoFetched( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ) -{ - if( !m_infoHash.contains( url ) ) - return; - - PhotosInfoPtr info = m_infoHash.take( url ); - if( e.code != QNetworkReply::NoError ) - { - debug() << "Error fetching photo" << e.description; - return; - } - - QPixmap pixmap; - if( pixmap.loadFromData( data ) ) - { - QPixmapCache::insert( url.url(), pixmap ); - addPhoto( info, pixmap ); - } -} - -void PhotosScrollWidget::addPhoto( const PhotosInfoPtr &item, const QPixmap &photo ) -{ - if( photo.isNull() ) - return; - - qreal height = 180.0 - 2 * m_margin; - QPixmap pixmap = photo.scaledToHeight( height , Qt::SmoothTransformation ); - pixmap = The::svgHandler()->addBordersToPixmap( pixmap, 5, QString(), true ); - - switch( m_mode ) - { - case PHOTOS_MODE_INTERACTIVE: - { - if( m_animation->state() == QAbstractAnimation::Running ) // careful we're animating - m_animation->stop(); - - DragPixmapItem *dragpix = new DragPixmapItem( this ); - dragpix->setPixmap( pixmap ); - dragpix->setPos( m_actualpos, 0 ); - dragpix->SetClickableUrl( item->urlpage ); - dragpix->show(); - - m_pixmaplist << dragpix; - - int delta = dragpix->boundingRect().width() + m_margin; - m_scrollmax += delta; - m_actualpos += delta; - emit photoAdded(); - break; - } - - case PHOTOS_MODE_AUTOMATIC: - { - DragPixmapItem *dragpix = new DragPixmapItem( this ); - dragpix->setPixmap( pixmap ); - dragpix->SetClickableUrl( item->urlpage ); - - // only pos and show if no animation, otherwise it will be set at the end automatically - if( m_animation->state() != QAbstractAnimation::Running ) - { - if( !m_pixmaplist.isEmpty() ) - { - int x = m_pixmaplist.last()->boundingRect().width(); - x += m_pixmaplist.last()->pos().x() + m_margin; - dragpix->setPos( x , 0 ) ; - dragpix->show(); - } - else - { - m_actualpos = 0; - dragpix->setPos( m_actualpos, 0 ) ; - dragpix->show(); - } - } - - m_pixmaplist << dragpix; - - // set a timer after and launch - QTimer::singleShot( m_interval, this, SLOT(automaticAnimBegin()) ); - emit photoAdded(); - break; - } - - case PHOTOS_MODE_FADING: - { - DragPixmapItem *dragpix = new DragPixmapItem( this ); - dragpix->setPixmap( pixmap ); - dragpix->setPos( (size().width() - dragpix->boundingRect().width()) / 2, 0 ); - dragpix->SetClickableUrl( item->urlpage ); - dragpix->hide(); - m_pixmaplist << dragpix; - if( m_pixmaplist.size() == 1 ) - { - dragpix->show(); - m_timer->start( m_interval ); - } - emit photoAdded(); - break; - } - } -} - -void PhotosScrollWidget::hoverEnterEvent(QGraphicsSceneHoverEvent*) -{ -// DEBUG_BLOCK - switch ( m_mode ) - { - case PHOTOS_MODE_AUTOMATIC : - { - if( m_animation->state() == QAbstractAnimation::Running ) - { - m_animation->stop(); - if ( m_currentPix != 0 ) - m_currentPix--; - } - break; - } - } -} - -void PhotosScrollWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent*) -{ -// DEBUG_BLOCK - switch ( m_mode ) - { - case PHOTOS_MODE_INTERACTIVE : - { - if( m_animation->state() == QAbstractAnimation::Running ) - m_animation->stop(); - break; - } - - case PHOTOS_MODE_AUTOMATIC : - { - if( m_animation->state() == QAbstractAnimation::Running ) - QTimer::singleShot( 0, this, SLOT(automaticAnimBegin()) ); - break; - } - } -} - -void PhotosScrollWidget::hoverMoveEvent(QGraphicsSceneHoverEvent* event) -{ -// DEBUG_BLOCK - switch ( m_mode ) - { - case PHOTOS_MODE_INTERACTIVE : - { - m_speed = ( event->pos().x() - ( size().width() / 2 ) ) / size().width(); - m_speed *= 20; - - if( m_animation->state() == QAbstractAnimation::Running ) - { - m_animation->pause(); - m_animation->setDuration( m_scrollmax*10 ); - m_animation->resume(); - } else { - m_animation->setDuration( m_scrollmax*10 ); - m_animation->start(); - } - } - default: - break; - } -} - -void PhotosScrollWidget::resize(qreal wid, qreal hei) -{ - switch( m_mode ) - { - case PHOTOS_MODE_FADING: - { - foreach(DragPixmapItem *item, m_pixmaplist) - { - if( !item->pixmap().isNull() ) - { - if( size().height() != hei ) - item->setPixmap( item->pixmap().scaledToHeight( (int) hei - 2 * m_margin, Qt::SmoothTransformation ) ); - if( size().width() != wid ) - item->setPos( ( wid - item->boundingRect().width() ) / 2, 0 ); - } - } - break; - } - } - - QGraphicsWidget::resize( wid, hei ); -} - -void PhotosScrollWidget::automaticAnimBegin() -{ - if ( m_pixmaplist.size() > 1 && m_animation->state() != QAbstractAnimation::Running ) // only start if m_pixmaplist >= 2 - { - m_lastPix = m_currentPix; - m_currentPix = ( m_currentPix + 1 ) % ( m_pixmaplist.count() ); - - switch( m_mode ) - { - case PHOTOS_MODE_AUTOMATIC: - { - m_delta = m_pixmaplist.at( m_currentPix )->boundingRect().width() + m_margin; - if( m_animation->state() == QAbstractAnimation::Running ) - m_animation->stop(); - - m_animation->setDuration( m_delta*20 ); - m_animation->start(); - break; - } - - case PHOTOS_MODE_FADING: - { - if( m_animation->state() == QAbstractAnimation::Running ) - m_animation->stop(); - - m_animation->setDuration( 1200 ); - m_animation->start(); - break; - } - default: - break; - } - } -} - -void PhotosScrollWidget::automaticAnimEnd() -{ - switch( m_mode ) - { - case PHOTOS_MODE_AUTOMATIC: - { - // DEBUG_BLOCK - - /*if ( !m_pixmaplist.empty() && m_currentPix != 0 ) - { - - DragPixmapItem * orgCurrentPix = m_pixmaplist.at( m_currentPix ); - - m_pixmaplist << m_pixmaplist.takeAt( m_lastPix ); - - //update index of current pic - m_currentPix = m_pixmaplist.indexOf( orgCurrentPix ); - m_lastPix = m_pixmaplist.count() - 1; //update to point at same pic at new position at the end of the list - }*/ - - QTimer::singleShot( m_interval, this, SLOT(automaticAnimBegin()) ); - break; - } - case PHOTOS_MODE_FADING: - { - // DEBUG_BLOCK; - if ( !m_pixmaplist.empty() && m_currentPix != 0 ) - { - m_pixmaplist.at( m_lastPix )->hide(); - } - - m_timer->start( m_interval ); - break; - } - default : - break; - } -} - -qreal PhotosScrollWidget::animValue() const -{ - // Just a stub - return m_delta; -} - -void PhotosScrollWidget::animate( qreal anim ) -{ - // DEBUG_BLOCK - switch ( m_mode ) - { - case PHOTOS_MODE_INTERACTIVE : - { - // If we're are near the border and still asking to go higher ! - if ( !childItems().isEmpty() && ( ( childItems().first()->pos().x() + childItems().first()->boundingRect().width() + 10 ) > boundingRect().width() ) && ( m_speed < 0 ) ) - { - if( m_animation->state() == QAbstractAnimation::Running ) - m_animation->stop(); - return; - } - // If we're are near the border and still asking to go down - if ( !childItems().isEmpty() && ( ( childItems().last()->pos().x() - 10 ) < 0 ) && ( m_speed > 0 ) ) - { - if( m_animation->state() == QAbstractAnimation::Running ) - m_animation->stop(); - return; - } - - int right = 0; - foreach( QGraphicsItem *it, this->childItems() ) - { - qreal x = it->pos().x() - m_speed; - it->setPos( x, it->pos().y() ); - it->update(); - if ( x > right ) - right = x + it->boundingRect().width() + m_margin; - } - m_actualpos = right; - break; - } - case PHOTOS_MODE_AUTOMATIC : - { - if ( !m_pixmaplist.empty() ) // just for prevention, this should never appears - { - - if ( ( m_pixmaplist.at( m_currentPix )->pos().x() ) <= ( m_margin / 2 - 1) ) - { - m_actualpos = m_margin / 2 - 1; - automaticAnimEnd(); - return; - } - - m_actualpos--; - - //this is not totally obvious, but we already made the number two visual image the current one, - //so if we draw this as the first one, there will be no animation... - int a = m_lastPix; - - int last = a - 1; - if( last < 0 ) last = m_pixmaplist.count() - 1; - bool first = true; - int previousIndex = -1; - - while( true ) - { - int offset = m_margin; - if( first ) - { - //we just need to move the very first image and the rest will fall in line! - offset += m_actualpos; - first = false; - } - else - { - offset += m_pixmaplist.at( previousIndex )->pos().x() + m_pixmaplist.at( previousIndex )->boundingRect().width(); - } - - m_pixmaplist.at( a )->setPos( offset, m_pixmaplist.at( a )->pos().y() ); - m_pixmaplist.at( a )->show(); - - if( a == last ) - break; - - previousIndex = a; - a = ( a + 1 ) % ( m_pixmaplist.size() ); - - } - } - - break; - } - - case PHOTOS_MODE_FADING : - { - if ( !m_pixmaplist.empty() ) // just for prevention, this should never appears - { - m_pixmaplist.at( m_lastPix )->setOpacity( 1 - anim ); - m_pixmaplist.at( m_currentPix )->setOpacity( anim ); - m_pixmaplist.at( m_currentPix )->show(); - } - - break; - } - } -} - diff --git a/src/context/applets/photos/PhotosScrollWidget.h b/src/context/applets/photos/PhotosScrollWidget.h deleted file mode 100644 index 09231458b8..0000000000 --- a/src/context/applets/photos/PhotosScrollWidget.h +++ /dev/null @@ -1,114 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see <http://www.gnu.org/licenses/>. * - ****************************************************************************************/ - -#ifndef PHOTOSSCROLLWIDGET_H -#define PHOTOSSCROLLWIDGET_H - -#include "context/engines/photos/PhotosInfo.h" -#include "NetworkAccessManagerProxy.h" - -#include <QGraphicsWidget> - -#define PHOTOS_MODE_AUTOMATIC 0 -#define PHOTOS_MODE_INTERACTIVE 1 -#define PHOTOS_MODE_FADING 2 - -//forward -class QPixmap; -class QPropertyAnimation; -class QGraphicsSceneHoverEvent; -class DragPixmapItem; - -/** -* \brief A widget to present the photos -* 3 possible animation : -* - Interactive : the sliding is done on mouse hover -* - Automatic : the photos are presented in an infinite loop, always scrolling -* - Fading, the photos are presented one by one, fading ... -* \sa QGraphicsWidget -* -* \author Simon Esneault <simon.esneault@gmail.com> -*/ - -class PhotosScrollWidget : public QGraphicsWidget -{ - Q_OBJECT - Q_PROPERTY(qreal animValue READ animValue WRITE animate) - public: - - PhotosScrollWidget( QGraphicsItem* parent = 0 ); - ~PhotosScrollWidget(); - - void setPhotosInfoList( const PhotosInfo::List &list ); - - void setMode( int ); - - void clear(); - - int count() const; - - qreal animValue() const; - bool isAnimating() const; - - public Q_SLOTS: - void animate( qreal anim ); - void automaticAnimBegin(); - void automaticAnimEnd(); - - /** - * Reimplement resize in order to correctly repositioned the stack of pixmap - */ - virtual void resize( qreal, qreal ); - - Q_SIGNALS: - void photoAdded(); - - protected: - - /** - * Reimplement mouse interaction event - */ - virtual void hoverMoveEvent(QGraphicsSceneHoverEvent* event); - virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event); - virtual void hoverEnterEvent(QGraphicsSceneHoverEvent* event); - //virtual void keyPressEvent(QKeyEvent* event); - //virtual void wheelEvent(QGraphicsSceneWheelEvent* event); - - private Q_SLOTS: - void photoFetched( const QUrl&, QByteArray, NetworkAccessManagerProxy::Error ); - - private: - void addPhoto( const PhotosInfoPtr &item, const QPixmap &photo ); - - float m_speed; // if negative, go to left, if positive go to right, - int m_margin; // margin between the photos - int m_scrollmax; // length of the whole stack - int m_actualpos; // - int m_currentPix; // index of the current pix - int m_lastPix; // index of the lat pix - int m_interval; // time in ms between to change - int m_mode; // - int m_delta; - int m_deltastart; - QHash<QUrl, PhotosInfoPtr> m_infoHash; - QPropertyAnimation *m_animation; // animation - QList<int> m_timerlist; - PhotosInfo::List m_currentlist; // contain the list of the current PhotosItem in the widget - QList<DragPixmapItem *> m_pixmaplist; // contain the list of dragpixmap item - QTimer *m_timer; // our magnificent timer -}; - -#endif // PHOTOSSCROLLWIDGET_H diff --git a/src/context/applets/photos/package/contents/ui/main.qml b/src/context/applets/photos/package/contents/ui/main.qml new file mode 100644 index 0000000000..338f1c0a80 --- /dev/null +++ b/src/context/applets/photos/package/contents/ui/main.qml @@ -0,0 +1,76 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman <malte.veerman@gmail.com> * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see <http://www.gnu.org/licenses/>. * + ****************************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 2.0 +import org.kde.amarok.qml 1.0 as AmarokQml +import org.kde.amarok.photos 1.0 + +AmarokQml.Applet { + id: applet + + title: name + ": " + PhotosEngine.artist + + Flickable { + anchors.fill: parent + contentHeight: height + contentWidth: contentRow.width + + Row { + id: contentRow + + height: parent.height + spacing: Context.smallSpacing + + Repeater { + model: PhotosEngine.photoTitles.length + + Item { + height: parent.height + width: image.width + + Image { + id: image + + anchors.top: parent.top + height: parent.height + width: height * sourceSize.width / sourceSize.height + source: PhotosEngine.photoUrls[index] + asynchronous: true + fillMode: Image.PreserveAspectFit + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + + onClicked: Context.runLink(PhotosEngine.pageUrls[index]); + } + } + } + } + } + + Label { + anchors.centerIn: parent + text: PhotosEngine.error + visible: PhotosEngine.Status === PhotosEngine.Error + } + + BusyIndicator { + anchors.centerIn: parent + running: PhotosEngine.Status === PhotosEngine.Fetching + } +} diff --git a/src/context/applets/photos/amarok-context-applet-photos.desktop b/src/context/applets/photos/package/metadata.desktop similarity index 91% rename from src/context/applets/photos/amarok-context-applet-photos.desktop rename to src/context/applets/photos/package/metadata.desktop index 603b5767b8..62e8d1899e 100644 --- a/src/context/applets/photos/amarok-context-applet-photos.desktop +++ b/src/context/applets/photos/package/metadata.desktop @@ -1,71 +1,72 @@ [Desktop Entry] Name=Photos Name[bg]=Снимки Name[bs]=Fotografije Name[ca]=Fotos Name[ca@valencia]=Fotos Name[cs]=Fotky Name[csb]=Òdjimczi Name[da]=Fotos Name[de]=Fotos Name[el]=Φωτογραφίες Name[en_GB]=Photos Name[es]=Fotos Name[et]=Fotod Name[eu]=Argazkiak Name[fa]=عکسها Name[fi]=Valokuvat Name[fr]=Photos Name[ga]=Grianghraif Name[gl]=Fotos Name[hu]=Fotók Name[id]=Foto Name[is]=Ljósmyndir Name[it]=Fotografie Name[ja]=写真 Name[km]=រូប​ថត Name[ko]=사진 Name[lt]=Nuotraukos Name[lv]=Fotogrāfijas Name[mai]=फोटो Name[mr]=फोटो Name[nb]=Fotoer Name[nds]=Fotos Name[nl]=Foto's Name[nn]=Bilete Name[pa]=ਫੋਟੋ Name[pl]=Zdjęcia Name[pt]=Fotografias Name[pt_BR]=Fotos Name[ro]=Fotografii Name[ru]=Фотографии Name[sk]=Fotografie Name[sl]=Fotografije Name[sq]=Fotografitë Name[sr]=Фотографије Name[sr@ijekavian]=Фотографије Name[sr@ijekavianlatin]=Fotografije Name[sr@latin]=Fotografije Name[sv]=Foton Name[th]=ภาพถ่าย Name[tr]=Fotoğraflar Name[ug]=سۈرەتلەر Name[uk]=Фотографії Name[x-test]=xxPhotosxx Name[zh_CN]=照片 Name[zh_TW]=Photos + Type=Service Icon=photos-amarok -ServiceTypes=Plasma/Applet -X-KDE-Library=amarok_context_applet_photos +ServiceTypes=Amarok/ContextApplet + X-KDE-PluginInfo-Author=Simon Esneault X-KDE-PluginInfo-Email=simon.esneault@gmail.com -X-KDE-PluginInfo-Name=photos +X-KDE-PluginInfo-Name=org.kde.amarok.photos X-KDE-PluginInfo-Version=1 X-KDE-PluginInfo-Website= X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-KDE-ParentApp=amarok -X-KDE-PluginInfo-Category=Current +X-KDE-PluginInfo-Category=Multimedia diff --git a/src/context/applets/photos/photosSettings.ui b/src/context/applets/photos/photosSettings.ui deleted file mode 100644 index 814ec15f2c..0000000000 --- a/src/context/applets/photos/photosSettings.ui +++ /dev/null @@ -1,113 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>photosSettings</class> - <widget class="QWidget" name="photosSettings"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>388</width> - <height>165</height> - </rect> - </property> - <layout class="QFormLayout" name="formLayout"> - <property name="labelAlignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> - </property> - <item row="0" column="0"> - <widget class="QLabel" name="animationLabel"> - <property name="text"> - <string>Animation</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="KComboBox" name="animationComboBox"> - <property name="currentIndex"> - <number>0</number> - </property> - <item> - <property name="text"> - <string comment="animation type">Automatic</string> - </property> - </item> - <item> - <property name="text"> - <string>Interactive</string> - </property> - </item> - <item> - <property name="text"> - <string>Fading</string> - </property> - </item> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="numberofphotosLabel"> - <property name="text"> - <string>Number of photos</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="KIntSpinBox" name="photosSpinBox"> - <property name="minimum"> - <number>5</number> - </property> - <property name="maximum"> - <number>100</number> - </property> - <property name="singleStep"> - <number>5</number> - </property> - <property name="value"> - <number>10</number> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="additionalkeywordsLabel"> - <property name="text"> - <string>Additional key words:</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="KLineEdit" name="additionalkeywordsLineEdit"> - <property name="clickMessage"> - <string>Ex: band live 1977</string> - </property> - </widget> - </item> - <item row="3" column="0" colspan="2"> - <widget class="QLabel" name="addsomemorekeynwordstothequerywithaspaceseparatorLabel"> - <property name="text"> - <string>Add some more key words to the Flickr.com -query, with a space separator. -For example: band live 1977 </string> - </property> - </widget> - </item> - </layout> - </widget> - <customwidgets> - <customwidget> - <class>KIntSpinBox</class> - <extends>QSpinBox</extends> - <header>knuminput.h</header> - </customwidget> - <customwidget> - <class>KLineEdit</class> - <extends>QLineEdit</extends> - <header>klineedit.h</header> - </customwidget> - <customwidget> - <class>KComboBox</class> - <extends>QComboBox</extends> - <header>kcombobox.h</header> - </customwidget> - </customwidgets> - <resources/> - <connections/> -</ui> diff --git a/src/context/engines/photos/PhotosEngine.cpp b/src/context/applets/photos/plugin/PhotosEngine.cpp similarity index 66% rename from src/context/engines/photos/PhotosEngine.cpp rename to src/context/applets/photos/plugin/PhotosEngine.cpp index e974a709c5..cf45f7058c 100644 --- a/src/context/engines/photos/PhotosEngine.cpp +++ b/src/context/applets/photos/plugin/PhotosEngine.cpp @@ -1,262 +1,314 @@ /**************************************************************************************** * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************************/ -#define DEBUG_PREFIX "PhotosEngine" +#define DEBUG_PREFIX "Photos" #include "PhotosEngine.h" #include "EngineController.h" -#include "context/ContextView.h" -#include "core/meta/Meta.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include <QXmlStreamReader> -#include <QPixmap> +#include <QUrlQuery> -using namespace Context; -PhotosEngine::PhotosEngine( QObject* parent, const QList<QVariant>& /*args*/ ) - : DataEngine( parent ) - , m_nbPhotos( 10 ) +PhotosEngine::PhotosEngine( QObject* parent ) + : QObject( parent ) + , m_nbPhotos( 10 ) + , m_status( Stopped ) { - m_sources << "flickr" ; + DEBUG_BLOCK + + EngineController *controller = The::engineController(); + connect( controller, &EngineController::trackMetadataChanged, this, &PhotosEngine::trackChanged ); + connect( controller, &EngineController::trackChanged, this, &PhotosEngine::trackChanged ); + connect( controller, &EngineController::stopped, this, &PhotosEngine::stopped ); } PhotosEngine::~PhotosEngine() { } -void -PhotosEngine::init() -{ - DEBUG_BLOCK - EngineController *controller = The::engineController(); - connect( controller, SIGNAL(trackMetadataChanged(Meta::TrackPtr)), SLOT(trackChanged(Meta::TrackPtr)) ); - connect( controller, SIGNAL(trackChanged(Meta::TrackPtr)), SLOT(trackChanged(Meta::TrackPtr)) ); - connect( controller, SIGNAL(stopped(qint64,qint64)), SLOT(stopped()) ); -} - void PhotosEngine::stopped() { DEBUG_BLOCK - removeAllData( "photos" ); - setData( "photos", "message", "stopped" ); - m_artist.clear(); + + setPhotos( QList<PhotoInfo>() ); + setStatus( Stopped ); + setArtist( QString() ); m_currentTrack.clear(); } void PhotosEngine::trackChanged( Meta::TrackPtr track ) { if( !track ) return; update(); } -QStringList -PhotosEngine::sources() const -{ - return m_sources; -} - int PhotosEngine::fetchSize() const { return m_nbPhotos; } void PhotosEngine::setFetchSize( int size ) { m_nbPhotos = size; } QStringList PhotosEngine::keywords() const { return m_keywords; } void PhotosEngine::setKeywords( const QStringList &keywords ) { - m_keywords = keywords; -} + if( m_keywords == keywords ) + return; -bool -PhotosEngine::sourceRequestEvent( const QString& name ) -{ - DEBUG_BLOCK - bool force( false ); - QStringList tokens = name.split( QLatin1Char(':'), QString::SkipEmptyParts ); - if( tokens.contains( QLatin1String("forceUpdate") ) ) - force = true; - update( force ); - return true; + m_keywords = keywords; + emit keywordsChanged(); } void PhotosEngine::metadataChanged( Meta::TrackPtr track ) { const bool hasChanged = !track->artist() || track->artist()->name() != m_artist; if ( hasChanged ) update(); } void PhotosEngine::update( bool force ) { QString tmpYoutStr; // prevent Meta::TrackPtr currentTrack = The::engineController()->currentTrack(); if( !currentTrack || !currentTrack->artist() ) { debug() << "invalid current track"; - setData( "photos", Plasma::DataEngine::Data() ); + setPhotos( QList<PhotoInfo>() ); return; } else if( !force && currentTrack->artist()->name() == m_artist ) { debug() << "artist name unchanged"; - setData( "photos", Plasma::DataEngine::Data() ); return; } else { unsubscribeFrom( m_currentTrack ); m_currentTrack = currentTrack; subscribeTo( currentTrack ); if ( !currentTrack ) return; // Save artist - m_artist = currentTrack->artist()->name(); - - removeAllData( "photos" ); + setArtist( currentTrack->artist()->name() ); + setPhotos( QList<PhotoInfo>() ); // Show the information if( !m_artist.isEmpty() ) { - setData( "photos", "message", "Fetching"); - setData( "photos", "artist", m_artist ); + setStatus( Fetching ); } else { - removeAllData( "photos" ); + setPhotos( QList<PhotoInfo>() ); return; } QStringList tags = m_keywords; tags << m_artist; tags.removeDuplicates(); // Query flickr, order by relevance, 10 max // Flickr :http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=9c5a288116c34c17ecee37877397fe31&text=ARTIST&per_page=20 QUrl flickrUrl; - flickrUrl.setScheme( "http" ); + QUrlQuery query; + flickrUrl.setScheme( "https" ); flickrUrl.setHost( "api.flickr.com" ); flickrUrl.setPath( "/services/rest/" ); - flickrUrl.addQueryItem( "method", "flickr.photos.search" ); - flickrUrl.addQueryItem( "api_key", Amarok::flickrApiKey() ); - flickrUrl.addQueryItem( "per_page", QString::number( m_nbPhotos ) ); - flickrUrl.addQueryItem( "sort", "date-posted-desc" ); - flickrUrl.addQueryItem( "media", "photos" ); - flickrUrl.addQueryItem( "content_type", QString::number(1) ); - flickrUrl.addQueryItem( "text", tags.join(" ") ); + query.addQueryItem( "method", "flickr.photos.search" ); + query.addQueryItem( "api_key", Amarok::flickrApiKey() ); + query.addQueryItem( "per_page", QString::number( m_nbPhotos ) ); + query.addQueryItem( "sort", "date-posted-desc" ); + query.addQueryItem( "media", "photos" ); + query.addQueryItem( "content_type", QString::number(1) ); + query.addQueryItem( "text", tags.join(" ") ); + flickrUrl.setQuery( query ); debug() << "Flickr url:" << flickrUrl; m_flickrUrls << flickrUrl; The::networkAccessManager()->getData( flickrUrl, this, SLOT(resultFlickr(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); } } void PhotosEngine::resultFlickr( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ) { if( !m_flickrUrls.contains( url ) ) return; DEBUG_BLOCK + m_flickrUrls.remove( url ); if( e.code != QNetworkReply::NoError ) { - setData( "photos", "message", i18n( "Unable to retrieve from Flickr.com: %1", e.description ) ); + setError( e.description ); debug() << "Unable to retrieve Flickr information:" << e.description; return; } if( data.isNull() ) { debug() << "Got bad xml!"; return; } - removeAllData( "photos" ); + setPhotos( QList<PhotoInfo>() ); QXmlStreamReader xml( data ); - PhotosInfo::List photosInfo = photosListFromXml( xml ); + QList<PhotoInfo> photosInfo = photosListFromXml( xml ); debug() << "got" << photosInfo.size() << "photo info"; - setData( "photos", "artist", m_artist ); - setData( "photos", "data", qVariantFromValue( photosInfo ) ); + setPhotos( photosInfo ); + setStatus( Completed ); } -PhotosInfo::List +QList<PhotosEngine::PhotoInfo> PhotosEngine::photosListFromXml( QXmlStreamReader &xml ) { - PhotosInfo::List photoList; + QList<PhotoInfo> photoList; xml.readNextStartElement(); // rsp if( xml.attributes().value(QLatin1String("stat")) != QLatin1String("ok") ) return photoList; xml.readNextStartElement(); // photos while( xml.readNextStartElement() ) { if( xml.name() == QLatin1String("photo") ) { const QXmlStreamAttributes &attr = xml.attributes(); QStringRef id = attr.value( QLatin1String("id") ); QStringRef farm = attr.value( QLatin1String("farm") ); QStringRef owner = attr.value( QLatin1String("owner") ); QStringRef secret = attr.value( QLatin1String("secret") ); QStringRef server = attr.value( QLatin1String("server") ); QStringRef title = attr.value( QLatin1String("title") ); QUrl photoUrl; photoUrl.setScheme( "http" ); photoUrl.setHost( QString("farm%1.static.flickr.com").arg( farm.toString() ) ); photoUrl.setPath( QString("/%1/%2_%3.jpg").arg( server.toString(), id.toString(), secret.toString() ) ); QUrl pageUrl; pageUrl.setScheme( "http" ); pageUrl.setHost( QLatin1String("www.flickr.com") ); pageUrl.setPath( QString("/photos/%1/%2").arg( owner.toString(), id.toString() ) ); - PhotosInfoPtr info( new PhotosInfo ); - info->title = title.toString(); - info->urlpage = pageUrl; - info->urlphoto = photoUrl; + PhotoInfo info; + info.title = title.toString(); + info.urlpage = pageUrl; + info.urlphoto = photoUrl; photoList.append( info ); } xml.skipCurrentElement(); } return photoList; } +void +PhotosEngine::setPhotos( const QList<PhotoInfo> &photos ) +{ + if( m_photos == photos ) + return; + + m_photos = photos; + emit photosChanged(); +} + +void +PhotosEngine::setStatus( Status status ) +{ + if( m_status == status ) + return; + + m_status = status; + emit statusChanged(); +} + +void +PhotosEngine::setError( const QString &error ) +{ + if( m_error == error ) + return; + + m_error = error; + emit errorChanged(); +} + +void +PhotosEngine::setArtist( const QString &artist ) +{ + if( m_artist == artist ) + return; + + m_artist = artist; + emit artistChanged(); +} + +QList<QUrl> +PhotosEngine::photoUrls() const +{ + QList<QUrl> list; + for( const auto &photo : m_photos ) + { + list << photo.urlphoto; + } + return list; +} + +QList<QUrl> +PhotosEngine::pageUrls() const +{ + QList<QUrl> list; + for( const auto &photo : m_photos ) + { + list << photo.urlpage; + } + return list; +} + +QList<QString> +PhotosEngine::photoTitles() const +{ + QList<QString> list; + for( const auto &photo : m_photos ) + { + list << photo.title; + } + return list; +} diff --git a/src/context/engines/photos/PhotosEngine.h b/src/context/applets/photos/plugin/PhotosEngine.h similarity index 58% rename from src/context/engines/photos/PhotosEngine.h rename to src/context/applets/photos/plugin/PhotosEngine.h index 6906958153..b2bc5aa63d 100644 --- a/src/context/engines/photos/PhotosEngine.h +++ b/src/context/applets/photos/plugin/PhotosEngine.h @@ -1,102 +1,144 @@ -#/**************************************************************************************** +/**************************************************************************************** * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************************/ #ifndef AMAROK_PHOTOS_ENGINE #define AMAROK_PHOTOS_ENGINE -#include "context/DataEngine.h" +#include "core/meta/Meta.h" #include "core/meta/Observer.h" -#include "NetworkAccessManagerProxy.h" -#include "PhotosInfo.h" +#include "network/NetworkAccessManagerProxy.h" +#include <QObject> +#include <QUrl> #include <QXmlStreamReader> -using namespace Context; /** * This class provide photos from flickr * */ -class PhotosEngine : public DataEngine, public Meta::Observer + class PhotosEngine : public QObject, public Meta::Observer { Q_OBJECT - Q_PROPERTY( int fetchSize READ fetchSize WRITE setFetchSize ) - Q_PROPERTY( QStringList keywords READ keywords WRITE setKeywords ) + Q_PROPERTY( int fetchSize READ fetchSize WRITE setFetchSize NOTIFY fetchSizeChanged ) + Q_PROPERTY( QStringList keywords READ keywords WRITE setKeywords NOTIFY keywordsChanged ) + Q_PROPERTY( QList<QUrl> photoUrls READ photoUrls NOTIFY photosChanged ) + Q_PROPERTY( QList<QUrl> pageUrls READ pageUrls NOTIFY photosChanged ) + Q_PROPERTY( QList<QString> photoTitles READ photoTitles NOTIFY photosChanged ) + Q_PROPERTY( Status status READ status NOTIFY statusChanged ) + Q_PROPERTY( QString error READ error NOTIFY errorChanged ) + Q_PROPERTY( QString artist READ artist NOTIFY artistChanged ) public: - PhotosEngine( QObject* parent, const QList<QVariant>& args ); + enum Status + { + Stopped, + Fetching, + Completed, + Error + }; + Q_ENUM( Status ) + + PhotosEngine( QObject* parent = Q_NULLPTR ); virtual ~PhotosEngine(); - void init(); - int fetchSize() const; void setFetchSize( int size ); QStringList keywords() const; void setKeywords( const QStringList &keywords ); - QStringList sources() const; - // reimplemented from Meta::Observer using Observer::metadataChanged; void metadataChanged( Meta::TrackPtr track ); -protected: - //reimplement from Plasma::DataEngine - bool sourceRequestEvent( const QString& name ); + QList<QUrl> photoUrls() const; + QList<QUrl> pageUrls() const; + QList<QString> photoTitles() const; + + Status status() const { return m_status; } + QString error() const { return m_error; } + QString artist() const { return m_artist; } + +signals: + void fetchSizeChanged(); + void keywordsChanged(); + void photosChanged(); + void statusChanged(); + void errorChanged(); + void artistChanged(); private Q_SLOTS: /** * This slots will handle Flickr result for this query : * API key is : 9c5a288116c34c17ecee37877397fe31 * Secret is : cc25e5a9532ddc97 * http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=9c5a288116c34c17ecee37877397fe31&text=My+Bloody+Valentine * see here for details: http://www.flickr.com/services/api/ */ void resultFlickr( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ); void stopped(); void trackChanged( Meta::TrackPtr track ); private: + struct PhotoInfo + { + QString title; // Name of the phtos + QUrl urlphoto; // url of the photos, for the download + QUrl urlpage; // Url for the browser ( http://www.flickr.com/photos/wanderlustg/322285063/ ) + + bool operator==( const PhotoInfo &other ) + { + return title == other.title && + urlphoto == other.urlphoto && + urlpage == other.urlpage; + } + }; + /** * Engine was updated, so we check if the songs is different, and if it is, we delete every and start * all the query/ fetching stuff */ void update( bool force = false ); - PhotosInfo::List photosListFromXml( QXmlStreamReader &xml ); + void setPhotos( const QList<PhotoInfo> &photos ); + void setStatus( Status status ); + void setError( const QString &error ); + void setArtist( const QString &artist ); + + QList<PhotoInfo> photosListFromXml( QXmlStreamReader &xml ); // TODO implement a reload void reloadPhotos(); int m_nbPhotos; QSet<QUrl> m_flickrUrls; - QStringList m_sources; + QList<PhotoInfo> m_photos; Meta::TrackPtr m_currentTrack; // Cache the artist of the current track so we can check against metadata // updates. We only want to update the photos if the artist change QString m_artist; QStringList m_keywords; + Status m_status; + QString m_error; }; -AMAROK_EXPORT_DATAENGINE( photos, PhotosEngine ) - #endif diff --git a/src/context/applets/photos/plugin/PhotosPlugin.cpp b/src/context/applets/photos/plugin/PhotosPlugin.cpp new file mode 100644 index 0000000000..aed896e01b --- /dev/null +++ b/src/context/applets/photos/plugin/PhotosPlugin.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2017 Malte Veerman <malte.veerman@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "PhotosEngine.h" + +#include <QQmlExtensionPlugin> + +#include <qqml.h> + + +class PhotosPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char* uri) Q_DECL_OVERRIDE + { + Q_ASSERT(uri == QLatin1String("org.kde.amarok.photos")); + + qmlRegisterSingletonType<PhotosEngine>(uri, 1, 0, "PhotosEngine", photos_engine_provider); + } + + static QObject *photos_engine_provider(QQmlEngine *engine, QJSEngine *scriptEngine) + { + Q_UNUSED(engine) + Q_UNUSED(scriptEngine) + + return new PhotosEngine(); + } +}; + +#include <PhotosPlugin.moc> diff --git a/src/context/applets/photos/plugin/qmldir b/src/context/applets/photos/plugin/qmldir new file mode 100644 index 0000000000..15c381c16d --- /dev/null +++ b/src/context/applets/photos/plugin/qmldir @@ -0,0 +1,2 @@ +module org.kde.amarok.photos +plugin amarok_context_applet_photos diff --git a/src/context/applets/wikipedia/CMakeLists.txt b/src/context/applets/wikipedia/CMakeLists.txt index 8a2f1c940d..181e359d97 100644 --- a/src/context/applets/wikipedia/CMakeLists.txt +++ b/src/context/applets/wikipedia/CMakeLists.txt @@ -1,28 +1,19 @@ -project(context-currenttrack) - -set( wiki_SRCS WikipediaApplet.cpp ) - -include_directories(${Amarok_SOURCE_DIR}/src - ${Amarok_SOURCE_DIR}/src/context - ${Amarok_SOURCE_DIR}/src/network - ) +set(wikipedia_SRCS + plugin/WikipediaPlugin.cpp + plugin/WikipediaEngine.cpp +) -ki18n_wrap_ui( wiki_SRCS wikipediaGeneralSettings.ui wikipediaLanguageSettings.ui ) +add_library(amarok_context_applet_wikipedia SHARED ${wikipedia_SRCS}) -add_library(amarok_context_applet_wikipedia MODULE ${wiki_SRCS}) -if(APPLE) - set_target_properties(amarok_context_applet_wikipedia PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") -endif() target_link_libraries(amarok_context_applet_wikipedia amarokcore amaroklib - KF5::Plasma - KF5::KIOCore - ${KDE4_KDEWEBKIT_LIBS} - Qt5::WebKitWidgets + Qt5::Qml ) -install(TARGETS amarok_context_applet_wikipedia DESTINATION ${PLUGIN_INSTALL_DIR}) -install(FILES amarok-context-applet-wikipedia.desktop DESTINATION ${SERVICES_INSTALL_DIR}) -install(FILES amarok-wikipedia.svg amarok-wikipediaheader.svg DESTINATION ${DATA_INSTALL_DIR}/desktoptheme/default/widgets/ ) +install(TARGETS amarok_context_applet_wikipedia DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/wikipedia) +install(FILES plugin/qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/amarok/wikipedia) + install(FILES WikipediaCustomStyle.css bullet.gif DESTINATION ${DATA_INSTALL_DIR}/amarok/data ) + +kpackage_install_package(package org.kde.amarok.wikipedia amarok) diff --git a/src/context/applets/wikipedia/amarok-wikipedia.svg b/src/context/applets/wikipedia/package/contents/images/amarok-wikipedia.svg similarity index 100% rename from src/context/applets/wikipedia/amarok-wikipedia.svg rename to src/context/applets/wikipedia/package/contents/images/amarok-wikipedia.svg diff --git a/src/context/applets/wikipedia/amarok-wikipediaheader.svg b/src/context/applets/wikipedia/package/contents/images/amarok-wikipediaheader.svg similarity index 100% rename from src/context/applets/wikipedia/amarok-wikipediaheader.svg rename to src/context/applets/wikipedia/package/contents/images/amarok-wikipediaheader.svg diff --git a/src/context/applets/wikipedia/package/contents/ui/main.qml b/src/context/applets/wikipedia/package/contents/ui/main.qml new file mode 100644 index 0000000000..54e77ec3f3 --- /dev/null +++ b/src/context/applets/wikipedia/package/contents/ui/main.qml @@ -0,0 +1,120 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman <malte.veerman@gmail.com> * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see <http://www.gnu.org/licenses/>. * + ****************************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 +import QtWebEngine 1.1 +import org.kde.amarok.qml 1.0 as AmarokQml +import org.kde.amarok.wikipedia 1.0 + +AmarokQml.Applet { + id: applet + + RowLayout { + id: buttonRow + + anchors.top: parent.top + width: parent.width + + Button { + iconName: "go-previous" + enabled: content.canGoBack + Layout.alignment: Qt.AlignLeft + ToolTip.text: i18n("Previous") + + onClicked: content.goBack() + } + Button { + iconName: "go-next" + enabled: content.canGoForward + Layout.alignment: Qt.AlignLeft + ToolTip.text: i18n("Next") + + onClicked: content.goForward() + } + Button { + iconName: "view-refresh" + enabled: !content.loading + Layout.alignment: Qt.AlignLeft + ToolTip.text: i18n("Refresh") + + onClicked: content.reload() + } + Item { + Layout.fillWidth: true + } + Button { + iconName: "filename-artist-amarok" + Layout.alignment: Qt.AlignRight + ToolTip.text: i18n("Artist") + + onClicked: WikipediaEngine.selection = WikipediaEngine.Artist + } + Button { + iconName: "filename-composer-amarok" + Layout.alignment: Qt.AlignRight + ToolTip.text: i18n("Composer") + + onClicked: WikipediaEngine.selection = WikipediaEngine.Composer + } + Button { + iconName: "filename-album-amarok" + Layout.alignment: Qt.AlignRight + ToolTip.text: i18n("Album") + + onClicked: WikipediaEngine.selection = WikipediaEngine.Album + } + Button { + iconName: "filename-title-amarok" + Layout.alignment: Qt.AlignRight + ToolTip.text: i18n("Track") + + onClicked: WikipediaEngine.selection = WikipediaEngine.Track + } + } + + WebEngineView { + id: content + + anchors.top: buttonRow.bottom + anchors.topMargin: applet.spacing + width: parent.width + height: Context.largeSpacing * 25 //TODO: Find a more elegant solution to set the height + + onNavigationRequested: { + if (request.navigationType == WebEngineNavigationRequest.LinkClickedNavigation) { + request.action = WebEngineNavigationRequest.IgnoreRequest; + WikipediaEngine.url = request.url; + } + } + + Connections { + target: WikipediaEngine + + onPageChanged: content.loadHtml(WikipediaEngine.page, WikipediaEngine.url) + } + + BusyIndicator { + anchors.centerIn: parent + running: WikipediaEngine.busy + } + } + + SystemPalette { + id: palette + } +} diff --git a/src/context/applets/wikipedia/amarok-context-applet-wikipedia.desktop b/src/context/applets/wikipedia/package/metadata.desktop similarity index 88% rename from src/context/applets/wikipedia/amarok-context-applet-wikipedia.desktop rename to src/context/applets/wikipedia/package/metadata.desktop index c3310d3d66..09f2143e9b 100644 --- a/src/context/applets/wikipedia/amarok-context-applet-wikipedia.desktop +++ b/src/context/applets/wikipedia/package/metadata.desktop @@ -1,71 +1,71 @@ [Desktop Entry] Name=Wikipedia Name[bg]=Уикипедия Name[bs]=Wikipedia Name[ca]=Viquipèdia Name[ca@valencia]=Viquipèdia Name[cs]=Wikipedia Name[csb]=Wikipedijô Name[da]=Wikipedia Name[de]=Wikipedia Name[el]=Wikipedia Name[en_GB]=Wikipedia Name[es]=Wikipedia Name[et]=Wikipedia Name[eu]=Wikipedia Name[fa]=ویکی‌پدیا Name[fi]=Wikipedia Name[fr]=Wikipédia Name[ga]=Vicipéid Name[gl]=Wikipedia Name[hu]=Wikipedia Name[id]=Wikipedia Name[is]=Wikipedia Name[it]=Wikipedia Name[ja]=Wikipedia Name[km]=វីគីភីឌៀ Name[ko]=위키백과 Name[lt]=Vikipedija Name[lv]=Wikipēdija Name[mr]=विकिपेडिया Name[nb]=Wikipedia Name[nds]=Wikipedia Name[nl]=Wikipedia Name[nn]=Wikipedia Name[pa]=ਵਿਕਿਪੀਡਿਆ Name[pl]=Wikipedia Name[pt]=Wikipédia Name[pt_BR]=Wikipédia Name[ro]=Wikipedia Name[ru]=Википедия Name[sk]=Wikipédia Name[sl]=Wikipedija Name[sq]=Wikipedia Name[sr]=Википедија Name[sr@ijekavian]=Википедија Name[sr@ijekavianlatin]=Wikipedia Name[sr@latin]=Wikipedia Name[sv]=Wikipedia Name[th]=วิกิพีเดีย Name[tr]=Wikipedia Name[ug]=ۋىكىپېدىيە Name[uk]=Вікіпедія Name[wa]=Wikipedia Name[x-test]=xxWikipediaxx Name[zh_CN]=维基百科 Name[zh_TW]=維基百科 + Type=Service -ServiceTypes=Plasma/Applet +ServiceTypes=Amarok/ContextApplet +Icon=wikipedia-amarok -X-KDE-Library=amarok_context_applet_wikipedia X-KDE-PluginInfo-Author=Leo Franchi X-KDE-PluginInfo-Email=lfranchi@gmail.com -X-KDE-PluginInfo-Name=wikipedia -X-KDE-PluginInfo-Version=pre0.1 +X-KDE-PluginInfo-Name=org.kde.amarok.wikipedia +X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website= X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true -X-KDE-ParentApp=amarok -X-KDE-PluginInfo-Category=Current +X-KDE-PluginInfo-Category=Multimedia diff --git a/src/context/applets/wikipedia/WikipediaApplet.cpp b/src/context/applets/wikipedia/plugin/WikipediaApplet.cpp similarity index 98% rename from src/context/applets/wikipedia/WikipediaApplet.cpp rename to src/context/applets/wikipedia/plugin/WikipediaApplet.cpp index 59cc81a895..2af77cb629 100644 --- a/src/context/applets/wikipedia/WikipediaApplet.cpp +++ b/src/context/applets/wikipedia/plugin/WikipediaApplet.cpp @@ -1,810 +1,810 @@ /**************************************************************************************** * Copyright (c) 2007 Leo Franchi <lfranchi@gmail.com> * * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************************/ #define DEBUG_PREFIX "WikipediaApplet" #include "WikipediaApplet.h" #include "WikipediaApplet_p.h" #include "moc_WikipediaApplet_p.cpp" #include "App.h" #include "core/support/Amarok.h" #include "context/widgets/AppletHeader.h" #include "core/support/Debug.h" #include "PaletteHandler.h" #include <KConfigDialog> #include <KGlobalSettings> #include <KSaveFile> -#include <KStandardDirs> +#include <QStandardPaths> #include <KTemporaryFile> #include <Plasma/DataContainer> -#include <Plasma/IconWidget> +#include <Plasma/IconItem> #include <Plasma/Theme> #include <QAction> #include <QTimer> #include <QDesktopServices> #include <QGraphicsLinearLayout> #include <QGraphicsView> #include <QListWidget> #include <QPainter> #include <QProgressBar> #include <QTextStream> #include <QWebPage> #include <QXmlStreamReader> #include <KConfigGroup> #include <QDialogButtonBox> #include <QPushButton> #include <QVBoxLayout> void WikipediaAppletPrivate::parseWikiLangXml( const QByteArray &data ) { QXmlStreamReader xml( data ); while( !xml.atEnd() && !xml.hasError() ) { xml.readNext(); if( xml.isStartElement() && xml.name() == "iw" ) { const QXmlStreamAttributes &a = xml.attributes(); if( a.hasAttribute("prefix") && a.hasAttribute("language") && a.hasAttribute("url") ) { const QString &prefix = a.value("prefix").toString(); const QString &language = a.value("language").toString(); const QString &display = QString( "[%1] %2" ).arg( prefix, language ); QListWidgetItem *item = new QListWidgetItem( display, 0 ); // The urlPrefix is the lang code infront of the wikipedia host // url. It is mostly the same as the "prefix" attribute but in // some weird cases they differ, so we can't just use "prefix". QString urlPrefix = QUrl( a.value("url").toString() ).host().remove(".wikipedia.org"); item->setData( PrefixRole, prefix ); item->setData( UrlPrefixRole, urlPrefix ); item->setData( LanguageStringRole, language ); languageSettingsUi.langSelector->availableListWidget()->addItem( item ); } } } } qint64 WikipediaAppletPrivate::writeStyleSheet( const QByteArray &data ) { delete css; css = new KTemporaryFile(); css->setSuffix( ".css" ); qint64 written( -1 ); if( css->open() ) { written = css->write( data ); // NOTE shall we keep this commented out, and bring it back later or the base64 is just what we need ? // QString filename = css->fileName(); css->close(); // flush buffer to disk // debug() << "set user stylesheet to:" << "file://" + filename; // webView->page()->settings()->setUserStyleSheetUrl( "file://" + filename ); } return written; } void WikipediaAppletPrivate::scheduleEngineUpdate() { Q_Q( WikipediaApplet ); q->dataEngine( "amarok-wikipedia" )->query( "update" ); } void WikipediaAppletPrivate::setUrl( const QUrl &url ) { webView->settings()->resetFontSize( QWebSettings::MinimumFontSize ); webView->settings()->resetFontSize( QWebSettings::MinimumLogicalFontSize ); webView->settings()->resetFontSize( QWebSettings::DefaultFontSize ); webView->settings()->resetFontSize( QWebSettings::DefaultFixedFontSize ); webView->settings()->resetFontFamily( QWebSettings::StandardFont ); webView->setUrl( url ); currentUrl = url; dataContainer->removeAllData(); } void WikipediaAppletPrivate::pushUrlHistory( const QUrl &url ) { if( !isForwardHistory && !isBackwardHistory && !url.isEmpty() ) { if( historyBack.isEmpty() || (!historyBack.isEmpty() && (url != historyBack.top())) ) historyBack.push( url ); historyForward.clear(); } isBackwardHistory = false; isForwardHistory = false; updateNavigationIcons(); } void WikipediaAppletPrivate::updateNavigationIcons() { forwardIcon->action()->setEnabled( !historyForward.isEmpty() ); backwardIcon->action()->setEnabled( !historyBack.isEmpty() ); } void WikipediaAppletPrivate::_titleChanged( const QString &title ) { Q_Q( WikipediaApplet ); q->setHeaderText( title ); } void WikipediaAppletPrivate::_goBackward() { DEBUG_BLOCK if( !historyBack.empty() ) { historyForward.push( currentUrl ); currentUrl = historyBack.pop(); isBackwardHistory = true; dataContainer->removeAllData(); dataContainer->setData( "clickUrl", currentUrl ); scheduleEngineUpdate(); updateNavigationIcons(); } } void WikipediaAppletPrivate::_goForward() { DEBUG_BLOCK if( !historyForward.empty() ) { historyBack.push( currentUrl ); currentUrl = historyForward.pop(); isForwardHistory = true; dataContainer->removeAllData(); dataContainer->setData( "clickUrl", currentUrl ); scheduleEngineUpdate(); updateNavigationIcons(); } } void WikipediaAppletPrivate::_gotoAlbum() { dataContainer->setData( "goto", "album" ); scheduleEngineUpdate(); } void WikipediaAppletPrivate::_gotoArtist() { dataContainer->setData( "goto", "artist" ); scheduleEngineUpdate(); } void WikipediaAppletPrivate::_gotoComposer() { dataContainer->setData( "goto", "composer" ); scheduleEngineUpdate(); } void WikipediaAppletPrivate::_gotoTrack() { dataContainer->setData( "goto", "track" ); scheduleEngineUpdate(); } void WikipediaAppletPrivate::_jsWindowObjectCleared() { Q_Q( WikipediaApplet ); webView->page()->mainFrame()->addToJavaScriptWindowObject( "mWebPage", q ); } void WikipediaAppletPrivate::_linkClicked( const QUrl &url ) { DEBUG_BLOCK Q_Q( WikipediaApplet ); if( url.host().contains( "wikipedia.org" ) ) { isBackwardHistory = false; isForwardHistory = false; pushUrlHistory( currentUrl ); if( useMobileWikipedia ) { setUrl( url ); return; } q->setBusy( true ); dataContainer->setData( "clickUrl", url ); scheduleEngineUpdate(); } else QDesktopServices::openUrl( url.toString() ); } void WikipediaAppletPrivate::_loadSettings() { QStringList list; QListWidget *listWidget = languageSettingsUi.langSelector->selectedListWidget(); for( int i = 0, count = listWidget->count(); i < count; ++i ) { QListWidgetItem *item = listWidget->item( i ); const QString &prefix = item->data( PrefixRole ).toString(); const QString &urlPrefix = item->data( UrlPrefixRole ).toString(); QString concat = QString("%1:%2").arg( prefix, urlPrefix ); list << (prefix == urlPrefix ? prefix : concat); } langList = list; useMobileWikipedia = (generalSettingsUi.mobileCheckBox->checkState() == Qt::Checked); Amarok::config("Wikipedia Applet").writeEntry( "PreferredLang", list ); Amarok::config("Wikipedia Applet").writeEntry( "UseMobile", useMobileWikipedia ); - _paletteChanged( App::instance()->palette() ); + _paletteChanged( pApp->palette() ); dataContainer->setData( "lang", langList ); dataContainer->setData( "mobile", useMobileWikipedia ); scheduleEngineUpdate(); } void WikipediaAppletPrivate::_paletteChanged( const QPalette &palette ) { if( useMobileWikipedia ) { webView->settings()->setUserStyleSheetUrl( QUrl() ); return; } // read css, replace color placeholders, write to file, load into page - QFile file( KStandardDirs::locate("data", "amarok/data/WikipediaCustomStyle.css" ) ); + QFile file( QStandardPaths::locate("data", "amarok/data/WikipediaCustomStyle.css" ) ); if( file.open(QIODevice::ReadOnly | QIODevice::Text) ) { // transparent background QPalette newPalette( palette ); newPalette.setBrush( QPalette::Base, Qt::transparent ); webView->page()->setPalette( newPalette ); webView->setPalette( newPalette ); webView->setAttribute( Qt::WA_OpaquePaintEvent, false ); QString contents = QString( file.readAll() ); contents.replace( "/*{text_color}*/", palette.text().color().name() ); contents.replace( "/*{link_color}*/", palette.link().color().name() ); contents.replace( "/*{link_hover_color}*/", palette.linkVisited().color().name() ); const QString abg = The::paletteHandler()->alternateBackgroundColor().name(); contents.replace( "/*{shaded_text_background_color}*/", abg ); contents.replace( "/*{table_background_color}*/", abg ); contents.replace( "/*{headings_background_color}*/", abg ); const QString hiColor = The::paletteHandler()->highlightColor().name(); contents.replace( "/*{border_color}*/", hiColor ); const QString atbg = palette.highlight().color().name(); contents.replace( "/*{alternate_table_background_color}*/", atbg ); const QByteArray &css = contents.toLatin1(); qint64 written = writeStyleSheet( css ); if( written != -1 ) { QUrl cssUrl( QString("data:text/css;charset=utf-8;base64,") + css.toBase64() ); //NOTE We give it encoded on a base64 // as it is currently broken on QtWebkit (see https://bugs.webkit.org/show_bug.cgi?id=34884 ) webView->settings()->setUserStyleSheetUrl( cssUrl ); } } } void WikipediaAppletPrivate::_reloadWikipedia() { DEBUG_BLOCK if( useMobileWikipedia ) { webView->reload(); return; } dataContainer->setData( "reload", true ); scheduleEngineUpdate(); } void WikipediaAppletPrivate::_updateWebFonts() { Q_Q( WikipediaApplet ); if( !q->view() ) return; qreal ratio = q->view()->logicalDpiY() / 72.0; qreal fixedFontSize = KGlobalSettings::fixedFont().pointSize() * ratio; qreal generalFontSize = KGlobalSettings::generalFont().pointSize() * ratio; qreal minimumFontSize = KGlobalSettings::smallestReadableFont().pointSize() * ratio; QWebSettings *webSettings = webView->page()->settings(); webSettings->setFontSize( QWebSettings::DefaultFixedFontSize, qRound(fixedFontSize) ); webSettings->setFontSize( QWebSettings::DefaultFontSize, qRound(generalFontSize) ); webSettings->setFontSize( QWebSettings::MinimumFontSize, qRound(minimumFontSize) ); webSettings->setFontFamily( QWebSettings::StandardFont, KGlobalSettings::generalFont().family() ); } void WikipediaAppletPrivate::_getLangMapProgress( qint64 received, qint64 total ) { languageSettingsUi.progressBar->setValue( 100.0 * qreal(received) / total ); } void WikipediaAppletPrivate::_getLangMapFinished( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ) { Q_UNUSED( url ) languageSettingsUi.downloadButton->setEnabled( true ); languageSettingsUi.progressBar->setEnabled( false ); if( e.code != QNetworkReply::NoError ) { debug() << "Downloading Wikipedia supported languages failed:" << e.description; return; } QListWidget *availableListWidget = languageSettingsUi.langSelector->availableListWidget(); availableListWidget->clear(); parseWikiLangXml( data ); languageSettingsUi.langSelector->setButtonsEnabled(); QString buttonText = ( availableListWidget->count() > 0 ) ? i18n( "Update Supported Languages" ) : i18n( "Get Supported Languages" ); languageSettingsUi.downloadButton->setText( buttonText ); QListWidget *selectedListWidget = languageSettingsUi.langSelector->selectedListWidget(); QList<QListWidgetItem*> selectedListItems = selectedListWidget->findItems( QChar('*'), Qt::MatchWildcard ); for( int i = 0, count = selectedListItems.count(); i < count; ++i ) { QListWidgetItem *item = selectedListItems.at( i ); int rowAtSelectedList = selectedListWidget->row( item ); item = selectedListWidget->takeItem( rowAtSelectedList ); const QString &prefix = item->data( PrefixRole ).toString(); QList<QListWidgetItem*> foundItems = availableListWidget->findItems( QString("[%1]").arg( prefix ), Qt::MatchStartsWith ); // should only have found one item if any if( !foundItems.isEmpty() ) { item = foundItems.first(); int rowAtAvailableList = languageSettingsUi.langSelector->availableListWidget()->row( item ); item = availableListWidget->takeItem( rowAtAvailableList ); selectedListWidget->addItem( item ); } } KSaveFile saveFile; saveFile.setFileName( Amarok::saveLocation() + "wikipedia_languages.xml" ); if( saveFile.open() ) { debug() << "Saving" << saveFile.fileName(); QTextStream stream( &saveFile ); stream << data; stream.flush(); saveFile.finalize(); } else { debug() << "Failed to saving Wikipedia languages file"; } } void WikipediaAppletPrivate::_getLangMap() { Q_Q( WikipediaApplet ); languageSettingsUi.downloadButton->setEnabled( false ); languageSettingsUi.progressBar->setEnabled( true ); languageSettingsUi.progressBar->setMaximum( 100 ); languageSettingsUi.progressBar->setValue( 0 ); QUrl url; url.setScheme( "https" ); url.setHost( "en.wikipedia.org" ); url.setPath( "/w/api.php" ); url.addQueryItem( "action", "query" ); url.addQueryItem( "meta", "siteinfo" ); url.addQueryItem( "siprop", "interwikimap" ); url.addQueryItem( "sifilteriw", "local" ); url.addQueryItem( "format", "xml" ); QNetworkReply *reply = The::networkAccessManager()->getData( url, q, SLOT(_getLangMapFinished(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); q->connect( reply, SIGNAL(downloadProgress(qint64,qint64)), q, SLOT(_getLangMapProgress(qint64,qint64)) ); } void WikipediaAppletPrivate::_configureLangSelector() { DEBUG_BLOCK Q_Q( WikipediaApplet ); QFile savedFile( Amarok::saveLocation() + "wikipedia_languages.xml" ); if( savedFile.open(QIODevice::ReadOnly | QIODevice::Text) ) parseWikiLangXml( savedFile.readAll() ); savedFile.close(); QListWidget *availableListWidget = languageSettingsUi.langSelector->availableListWidget(); QString buttonText = ( availableListWidget->count() > 0 ) ? i18n( "Update Supported Languages" ) : i18n( "Get Supported Languages" ); languageSettingsUi.downloadButton->setText( buttonText ); for( int i = 0, count = langList.count(); i < count; ++i ) { const QStringList &split = langList.at( i ).split( QLatin1Char(':') ); const QString &prefix = split.first(); const QString &urlPrefix = (split.count() == 1) ? prefix : split.at( 1 ); QList<QListWidgetItem*> foundItems = availableListWidget->findItems( QString("[%1]").arg( prefix ), Qt::MatchStartsWith ); if( foundItems.isEmpty() ) { QListWidgetItem *item = new QListWidgetItem( prefix, 0 ); item->setData( WikipediaAppletPrivate::PrefixRole, prefix ); item->setData( WikipediaAppletPrivate::UrlPrefixRole, urlPrefix ); languageSettingsUi.langSelector->selectedListWidget()->addItem( item ); } else // should only have found one item if any { QListWidgetItem *item = foundItems.first(); int rowAtAvailableList = availableListWidget->row( item ); item = availableListWidget->takeItem( rowAtAvailableList ); languageSettingsUi.langSelector->selectedListWidget()->addItem( item ); } } q->connect( languageSettingsUi.langSelector, SIGNAL(added(QListWidgetItem*)), q, SLOT(_langSelectorItemChanged(QListWidgetItem*)) ); q->connect( languageSettingsUi.langSelector, SIGNAL(movedDown(QListWidgetItem*)), q, SLOT(_langSelectorItemChanged(QListWidgetItem*)) ); q->connect( languageSettingsUi.langSelector, SIGNAL(movedUp(QListWidgetItem*)), q, SLOT(_langSelectorItemChanged(QListWidgetItem*)) ); q->connect( languageSettingsUi.langSelector, SIGNAL(removed(QListWidgetItem*)), q, SLOT(_langSelectorItemChanged(QListWidgetItem*)) ); q->connect( languageSettingsUi.langSelector->availableListWidget(), SIGNAL(itemClicked(QListWidgetItem*)), q, SLOT(_langSelectorItemChanged(QListWidgetItem*)) ); q->connect( languageSettingsUi.langSelector->selectedListWidget(), SIGNAL(itemClicked(QListWidgetItem*)), q, SLOT(_langSelectorItemChanged(QListWidgetItem*)) ); } void WikipediaAppletPrivate::_pageLoadStarted() { Q_Q( WikipediaApplet ); // create a proxy widget for displaying the webview load status in form of a progress bar // if the proxyWidget still exists, re-use the existing object if ( proxyWidget ) return; proxyWidget = new QGraphicsProxyWidget; proxyWidget->setWidget( new QProgressBar ); // add proxy widget to layout QGraphicsLinearLayout *lo = static_cast<QGraphicsLinearLayout*>( q->layout() ); lo->addItem( proxyWidget ); lo->activate(); QObject::connect( webView, SIGNAL(loadProgress(int)), q, SLOT(_pageLoadProgress(int)) ); } void WikipediaAppletPrivate::_pageLoadProgress( int progress ) { DEBUG_ASSERT(proxyWidget, return) const QString kbytes = QString::number( webView->page()->totalBytes() / 1024 ); QProgressBar *pbar = qobject_cast<QProgressBar*>( proxyWidget->widget() ); pbar->setFormat( QString( "%1kB : %p%" ).arg( kbytes ) ); pbar->setValue( progress ); } void WikipediaAppletPrivate::_pageLoadFinished( bool ok ) { Q_UNUSED( ok ) Q_Q( WikipediaApplet ); // remove proxy widget from layout again, delete it QGraphicsLinearLayout *lo = static_cast<QGraphicsLinearLayout*>( q->layout() ); lo->removeItem( proxyWidget ); lo->activate(); // disconnect (so that we don't get any further progress signalling) and delete widget QObject::disconnect( webView, SIGNAL(loadProgress(int)), q, SLOT(_pageLoadProgress(int)) ); proxyWidget->deleteLater(); proxyWidget = 0; } void WikipediaAppletPrivate::_searchLineEditTextEdited( const QString &text ) { webView->page()->findText( QString(), QWebPage::HighlightAllOccurrences ); // clears preivous highlights webView->page()->findText( text, QWebPage::FindWrapsAroundDocument | QWebPage::HighlightAllOccurrences ); } void WikipediaAppletPrivate::_searchLineEditReturnPressed() { const QString &text = webView->lineEdit()->text(); webView->page()->findText( text, QWebPage::FindWrapsAroundDocument ); } void WikipediaAppletPrivate::_langSelectorItemChanged( QListWidgetItem *item ) { Q_UNUSED( item ) languageSettingsUi.langSelector->setButtonsEnabled(); } WikipediaApplet::WikipediaApplet( QObject* parent, const QVariantList& args ) : Context::Applet( parent, args ) , d_ptr( new WikipediaAppletPrivate( this ) ) { setHasConfigurationInterface( true ); } WikipediaApplet::~WikipediaApplet() { Q_D( WikipediaApplet ); delete d->webView; delete d->css; delete d_ptr; } void WikipediaApplet::init() { DEBUG_BLOCK Context::Applet::init(); Q_D( WikipediaApplet ); d->webView = new WikipediaWebView( this ); d->webView->page()->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff ); // d->webView->page()->mainFrame()->addToJavaScriptWindowObject( "mWebPage", this ); BUG:259075 d->webView->page()->setNetworkAccessManager( The::networkAccessManager() ); d->webView->page()->setLinkDelegationPolicy ( QWebPage::DelegateAllLinks ); d->webView->page()->settings()->setAttribute( QWebSettings::PrivateBrowsingEnabled, true ); QWebSettings::globalSettings()->setAttribute( QWebSettings::DnsPrefetchEnabled, true ); d->webView->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); d->_updateWebFonts(); connect( KGlobalSettings::self(), SIGNAL(appearanceChanged()), SLOT(_updateWebFonts()) ); connect( The::paletteHandler(), SIGNAL(newPalette(QPalette)), SLOT(_paletteChanged(QPalette)) ); connect( d->webView->page(), SIGNAL(linkClicked(QUrl)), SLOT(_linkClicked(QUrl)) ); connect( d->webView->page(), SIGNAL(loadStarted()), SLOT(_pageLoadStarted()) ); connect( d->webView->page(), SIGNAL(loadFinished(bool)), SLOT(_pageLoadFinished(bool)) ); // connect( d->webView->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), SLOT(_jsWindowObjectCleared()) ); BUG:259075 connect( d->webView->lineEdit(), SIGNAL(textChanged(QString)), SLOT(_searchLineEditTextEdited(QString)) ); connect( d->webView->lineEdit(), SIGNAL(returnPressed()), SLOT(_searchLineEditReturnPressed()) ); connect( d->webView, SIGNAL(titleChanged(QString)), this, SLOT(_titleChanged(QString)) ); enableHeader( true ); setHeaderText( i18n( "Wikipedia" ) ); setCollapseOffHeight( -1 ); setCollapseHeight( m_header->height() ); setMinimumHeight( collapseHeight() ); setPreferredHeight( collapseHeight() ); QAction* backwardAction = new QAction( this ); backwardAction->setIcon( QIcon::fromTheme( "go-previous" ) ); backwardAction->setEnabled( false ); backwardAction->setText( i18n( "Back" ) ); d->backwardIcon = addLeftHeaderAction( backwardAction ); connect( d->backwardIcon, SIGNAL(clicked()), this, SLOT(_goBackward()) ); QAction* forwardAction = new QAction( this ); forwardAction->setIcon( QIcon::fromTheme( "go-next" ) ); forwardAction->setEnabled( false ); forwardAction->setText( i18n( "Forward" ) ); d->forwardIcon = addLeftHeaderAction( forwardAction ); connect( d->forwardIcon, SIGNAL(clicked()), this, SLOT(_goForward()) ); QAction* reloadAction = new QAction( this ); reloadAction->setIcon( QIcon::fromTheme( "view-refresh" ) ); reloadAction->setText( i18n( "Reload" ) ); d->reloadIcon = addLeftHeaderAction( reloadAction ); connect( d->reloadIcon, SIGNAL(clicked()), this, SLOT(_reloadWikipedia()) ); QAction* artistAction = new QAction( this ); artistAction->setIcon( QIcon::fromTheme( "filename-artist-amarok" ) ); artistAction->setText( i18n( "Artist" ) ); d->artistIcon = addRightHeaderAction( artistAction ); connect( d->artistIcon, SIGNAL(clicked()), this, SLOT(_gotoArtist()) ); QAction* composerAction = new QAction( this ); composerAction->setIcon( QIcon::fromTheme( "filename-composer-amarok" ) ); composerAction->setText( i18n( "Composer" ) ); d->composerIcon = addRightHeaderAction( composerAction ); connect( d->composerIcon, SIGNAL(clicked()), this, SLOT(_gotoComposer()) ); QAction* albumAction = new QAction( this ); albumAction->setIcon( QIcon::fromTheme( "filename-album-amarok" ) ); albumAction->setText( i18n( "Album" ) ); d->albumIcon = addRightHeaderAction( albumAction ); connect( d->albumIcon, SIGNAL(clicked()), this, SLOT(_gotoAlbum()) ); QAction* trackAction = new QAction( this ); trackAction->setIcon( QIcon::fromTheme( "filename-title-amarok" ) ); trackAction->setText( i18n( "Track" ) ); d->trackIcon = addRightHeaderAction( trackAction ); connect( d->trackIcon, SIGNAL(clicked()), this, SLOT(_gotoTrack()) ); QAction* settingsAction = new QAction( this ); settingsAction->setIcon( QIcon::fromTheme( "preferences-system" ) ); settingsAction->setText( i18n( "Settings" ) ); d->settingsIcon = addRightHeaderAction( settingsAction ); connect( d->settingsIcon, SIGNAL(clicked()), this, SLOT(showConfigurationInterface()) ); QGraphicsLinearLayout *layout = new QGraphicsLinearLayout( Qt::Vertical ); layout->setSpacing( 2 ); layout->addItem( m_header ); layout->addItem( d->webView ); setLayout( layout ); dataEngine( "amarok-wikipedia" )->connectSource( "wikipedia", this ); d->dataContainer = dataEngine( "amarok-wikipedia" )->containerForSource( "wikipedia" ); // Read config and inform the engine. d->langList = Amarok::config("Wikipedia Applet").readEntry( "PreferredLang", QStringList() << "en" ); d->useMobileWikipedia = Amarok::config("Wikipedia Applet").readEntry( "UseMobile", false ); - d->_paletteChanged( App::instance()->palette() ); + d->_paletteChanged( pApp->palette() ); d->dataContainer->setData( "lang", d->langList ); d->dataContainer->setData( "mobile", d->useMobileWikipedia ); d->scheduleEngineUpdate(); updateConstraints(); } void WikipediaApplet::constraintsEvent( Plasma::Constraints constraints ) { Context::Applet::constraintsEvent( constraints ); update(); } bool WikipediaApplet::hasHeightForWidth() const { return true; } qreal WikipediaApplet::heightForWidth( qreal width ) const { Q_D( const WikipediaApplet ); return width * d->aspectRatio; } void WikipediaApplet::dataUpdated( const QString &source, const Plasma::DataEngine::Data &data ) { DEBUG_BLOCK Q_UNUSED( source ) Q_D( WikipediaApplet ); if( data.isEmpty() ) { debug() << "data Empty!"; d->webView->hide(); setCollapseOn(); return; } if( data.contains( "stopped" ) ) { debug() << "stopped"; d->dataContainer->removeAllData(); if( d->webView->title().isEmpty() ) { d->webView->hide(); setCollapseOn(); } return; } if( data.contains( "busy" ) ) { if( canAnimate() && data["busy"].toBool() ) setBusy( true ); return; } else { d->webView->show(); setBusy( false ); } if( data.contains( "message" ) ) { setCollapseOn(); // messages have higher priority than pages const QString &message = data.value( "message" ).toString(); if( !message.isEmpty() ) { setHeaderText( i18n( "Wikipedia: %1", message ) ); d->dataContainer->removeAllData(); } } else if( data.contains( "sourceUrl" ) ) { const QUrl &url = data.value( "sourceUrl" ).value<QUrl>(); d->setUrl( url ); debug() << "source URL" << url; setCollapseOff(); } else if( data.contains( "page" ) ) { if( data.contains( "url" ) && !data.value( "url" ).toUrl().isEmpty() ) { const QUrl &url = data.value( "url" ).toUrl(); d->_updateWebFonts(); d->currentUrl = url; d->webView->setHtml( data[ "page" ].toString(), url ); d->dataContainer->removeAllData(); } setCollapseOff(); } else { setHeaderText( i18n( "Wikipedia" ) ); } } void WikipediaApplet::loadWikipediaUrl( const QString &url ) { Q_D( WikipediaApplet ); d->_linkClicked( QUrl(url) ); } void WikipediaApplet::createConfigurationInterface( KConfigDialog *parent ) { Q_D( WikipediaApplet ); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QVBoxLayout *mainLayout = new QVBoxLayout; parent->setLayout(mainLayout); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); parent->connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); parent->connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(buttonBox); KConfigGroup configuration = config(); QWidget *langSettings = new QWidget; d->languageSettingsUi.setupUi( langSettings ); d->languageSettingsUi.downloadButton->setGuiItem( KStandardGuiItem::find() ); d->languageSettingsUi.langSelector->availableListWidget()->setAlternatingRowColors( true ); d->languageSettingsUi.langSelector->selectedListWidget()->setAlternatingRowColors( true ); d->languageSettingsUi.langSelector->availableListWidget()->setUniformItemSizes( true ); d->languageSettingsUi.langSelector->selectedListWidget()->setUniformItemSizes( true ); d->languageSettingsUi.progressBar->setEnabled( false ); QWidget *genSettings = new QWidget; d->generalSettingsUi.setupUi( genSettings ); d->generalSettingsUi.mobileCheckBox->setCheckState( d->useMobileWikipedia ? Qt::Checked : Qt::Unchecked ); - connect( d->languageSettingsUi.downloadButton, SIGNAL(clicked()), this, SLOT(_getLangMap()) ); + connect( d->languageSettingsUi.downloadButton, &QPushButton::clicked, this, SLOT(_getLangMap()) ); connect( parent, SIGNAL(clicked()), this, SLOT(_loadSettings()) ); parent->addPage( genSettings, i18n( "Wikipedia General Settings" ), "configure" ); parent->addPage( langSettings, i18n( "Wikipedia Language Settings" ), "applications-education-language" ); QTimer::singleShot( 0, this, SLOT(_configureLangSelector()) ); } diff --git a/src/context/applets/wikipedia/WikipediaApplet.h b/src/context/applets/wikipedia/plugin/WikipediaApplet.h similarity index 99% rename from src/context/applets/wikipedia/WikipediaApplet.h rename to src/context/applets/wikipedia/plugin/WikipediaApplet.h index 0cf7de4c35..69cc84cd5e 100644 --- a/src/context/applets/wikipedia/WikipediaApplet.h +++ b/src/context/applets/wikipedia/plugin/WikipediaApplet.h @@ -1,91 +1,91 @@ /**************************************************************************************** * Copyright (c) 2007 Leo Franchi <lfranchi@gmail.com> * * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************************/ #ifndef WIKIPEDIA_APPLET_H #define WIKIPEDIA_APPLET_H #include "context/Applet.h" #include "context/DataEngine.h" #include "NetworkAccessManagerProxy.h" class QAction; -class KDialog; +class QDialog; class KConfigDialog; class QListWidgetItem; class WikipediaAppletPrivate; namespace Plasma { class WebView; class IconWidget; } class WikipediaApplet : public Context::Applet { Q_OBJECT public: WikipediaApplet( QObject* parent, const QVariantList& args ); ~WikipediaApplet(); void constraintsEvent( Plasma::Constraints constraints = Plasma::AllConstraints ); bool hasHeightForWidth() const; qreal heightForWidth( qreal width ) const; public Q_SLOTS: virtual void init(); void dataUpdated( const QString& name, const Plasma::DataEngine::Data& data ); void loadWikipediaUrl( const QString &url ); protected: void createConfigurationInterface(KConfigDialog *parent); private: WikipediaAppletPrivate *const d_ptr; Q_DECLARE_PRIVATE( WikipediaApplet ) Q_PRIVATE_SLOT( d_ptr, void _goBackward() ) Q_PRIVATE_SLOT( d_ptr, void _goForward() ) Q_PRIVATE_SLOT( d_ptr, void _gotoAlbum() ) Q_PRIVATE_SLOT( d_ptr, void _gotoArtist() ) Q_PRIVATE_SLOT( d_ptr, void _gotoComposer() ) Q_PRIVATE_SLOT( d_ptr, void _gotoTrack() ) Q_PRIVATE_SLOT( d_ptr, void _linkClicked(const QUrl&) ) Q_PRIVATE_SLOT( d_ptr, void _loadSettings() ) Q_PRIVATE_SLOT( d_ptr, void _paletteChanged(const QPalette&) ) Q_PRIVATE_SLOT( d_ptr, void _reloadWikipedia() ) Q_PRIVATE_SLOT( d_ptr, void _updateWebFonts() ) Q_PRIVATE_SLOT( d_ptr, void _getLangMapProgress(qint64,qint64) ) Q_PRIVATE_SLOT( d_ptr, void _getLangMapFinished(const QUrl&,QByteArray,NetworkAccessManagerProxy::Error) ) Q_PRIVATE_SLOT( d_ptr, void _getLangMap() ) Q_PRIVATE_SLOT( d_ptr, void _configureLangSelector() ) Q_PRIVATE_SLOT( d_ptr, void _langSelectorItemChanged(QListWidgetItem*) ) Q_PRIVATE_SLOT( d_ptr, void _titleChanged(const QString&) ) Q_PRIVATE_SLOT( d_ptr, void _pageLoadStarted() ) Q_PRIVATE_SLOT( d_ptr, void _pageLoadProgress(int) ) Q_PRIVATE_SLOT( d_ptr, void _pageLoadFinished(bool) ) Q_PRIVATE_SLOT( d_ptr, void _searchLineEditTextEdited(const QString&) ) Q_PRIVATE_SLOT( d_ptr, void _searchLineEditReturnPressed() ) Q_PRIVATE_SLOT( d_ptr, void _jsWindowObjectCleared() ) }; AMAROK_EXPORT_APPLET( wikipedia, WikipediaApplet ) #endif diff --git a/src/context/applets/wikipedia/WikipediaApplet_p.h b/src/context/applets/wikipedia/plugin/WikipediaApplet_p.h similarity index 99% rename from src/context/applets/wikipedia/WikipediaApplet_p.h rename to src/context/applets/wikipedia/plugin/WikipediaApplet_p.h index a2eedb956b..24213f88bb 100644 --- a/src/context/applets/wikipedia/WikipediaApplet_p.h +++ b/src/context/applets/wikipedia/plugin/WikipediaApplet_p.h @@ -1,287 +1,287 @@ /**************************************************************************************** * Copyright (c) 2007 Leo Franchi <lfranchi@gmail.com> * * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * * Copyright (c) 2010 Rick W. Chen <stuffcorpse@archlinux.us> * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************************/ #ifndef AMAROK_WIKIPEDIAAPPLET_P_H #define AMAROK_WIKIPEDIAAPPLET_P_H #include "WikipediaApplet.h" #include "ui_wikipediaGeneralSettings.h" #include "ui_wikipediaLanguageSettings.h" #include <KGraphicsWebView> -#include <KLineEdit> +#include <QLineEdit> #include <QUrl> #include <Plasma/LineEdit> #include <Plasma/Svg> #include <Plasma/SvgWidget> #include <QGraphicsSceneResizeEvent> #include <QStack> #include <QWebFrame> #include <QWebPage> #include <QWebInspector> #include <QWebSettings> class KTemporaryFile; class WikipediaWebView; namespace Plasma { class DataContainer; class IconWidget; } class WikipediaAppletPrivate { private: WikipediaApplet *const q_ptr; Q_DECLARE_PUBLIC( WikipediaApplet ) public: WikipediaAppletPrivate( WikipediaApplet *parent ) : q_ptr( parent ) , css( 0 ) , dataContainer( 0 ) , albumIcon( 0 ) , artistIcon( 0 ) , composerIcon( 0 ) , backwardIcon( 0 ) , forwardIcon( 0 ) , reloadIcon( 0 ) , settingsIcon( 0 ) , trackIcon( 0 ) , webView( 0 ) , proxyWidget( 0 ) , aspectRatio( 0 ) , isForwardHistory( false ) , isBackwardHistory( false ) {} ~WikipediaAppletPrivate() {} // functions void pushUrlHistory( const QUrl &url ); void parseWikiLangXml( const QByteArray &xml ); qint64 writeStyleSheet( const QByteArray &css ); void scheduleEngineUpdate(); void setUrl( const QUrl &url ); void updateNavigationIcons(); // private slots void _linkClicked( const QUrl &url ); void _loadSettings(); void _goBackward(); void _goForward(); void _gotoArtist(); void _gotoComposer(); void _gotoAlbum(); void _gotoTrack(); void _switchToLang( const QString &lang ); void _reloadWikipedia(); void _updateWebFonts(); void _paletteChanged( const QPalette &palette ); void _getLangMapProgress( qint64 received, qint64 total ); void _getLangMapFinished( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ); void _getLangMap(); void _configureLangSelector(); void _langSelectorItemChanged( QListWidgetItem *item ); void _titleChanged( const QString &title ); void _pageLoadStarted(); void _pageLoadProgress( int progress ); void _pageLoadFinished( bool ok ); void _searchLineEditTextEdited( const QString &text ); void _searchLineEditReturnPressed(); void _jsWindowObjectCleared(); // data members enum WikiLangRoles { PrefixRole = Qt::UserRole + 1, UrlPrefixRole = Qt::UserRole + 2, LanguageStringRole = Qt::UserRole + 3 }; KTemporaryFile *css; Plasma::DataContainer *dataContainer; Plasma::IconWidget *albumIcon; Plasma::IconWidget *artistIcon; Plasma::IconWidget *composerIcon; Plasma::IconWidget *backwardIcon; Plasma::IconWidget *forwardIcon; Plasma::IconWidget *reloadIcon; Plasma::IconWidget *settingsIcon; Plasma::IconWidget *trackIcon; WikipediaWebView *webView; QGraphicsProxyWidget *proxyWidget; QStack<QUrl> historyBack; QStack<QUrl> historyForward; QUrl currentUrl; QStringList langList; Ui::wikipediaGeneralSettings generalSettingsUi; Ui::wikipediaLanguageSettings languageSettingsUi; qreal aspectRatio; bool isForwardHistory; bool isBackwardHistory; bool useMobileWikipedia; }; class WikipediaSearchLineEdit : public Plasma::LineEdit { Q_OBJECT public: WikipediaSearchLineEdit( QGraphicsWidget *parent = 0 ) : Plasma::LineEdit( parent ) {} ~WikipediaSearchLineEdit() {} void setFocus( Qt::FocusReason reason ) { nativeWidget()->setFocus( reason ); } protected: void focusOutEvent( QFocusEvent *event ) { hide(); nativeWidget()->clear(); parentWidget()->setFocus(); event->accept(); } void keyPressEvent( QKeyEvent *event ) { if( event->key() == Qt::Key_Escape ) { clearFocus(); event->accept(); } else Plasma::LineEdit::keyPressEvent( event ); } }; class WikipediaWebView : public KGraphicsWebView { Q_OBJECT public: WikipediaWebView( QGraphicsItem *parent = 0 ) : KGraphicsWebView( parent ) { m_lineEdit = new WikipediaSearchLineEdit( this ); m_lineEdit->setContentsMargins( 0, 0, 0, 0 ); m_lineEdit->setClearButtonShown( true ); m_lineEdit->setVisible( false ); Plasma::Svg *borderSvg = new Plasma::Svg( this ); borderSvg->setImagePath( "widgets/scrollwidget" ); m_topBorder = new Plasma::SvgWidget( this ); m_topBorder->setSvg( borderSvg ); m_topBorder->setElementID( "border-top" ); m_topBorder->setZValue( 900 ); m_topBorder->resize( -1, 10.0 ); m_topBorder->show(); m_bottomBorder = new Plasma::SvgWidget( this ); m_bottomBorder->setSvg( borderSvg ); m_bottomBorder->setElementID( "border-bottom" ); m_bottomBorder->setZValue( 900 ); m_bottomBorder->resize( -1, 10.0 ); m_bottomBorder->show(); page()->parent()->installEventFilter( this ); #if defined(DEBUG_BUILD_TYPE) page()->settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true ); m_inspector = new QWebInspector; m_inspector->setPage( page() ); #endif } ~WikipediaWebView() { #if defined(DEBUG_BUILD_TYPE) delete m_inspector; #endif } Plasma::LineEdit *lineEdit() { return m_lineEdit; } protected: bool eventFilter( QObject *obj , QEvent *event ) { if( obj == page()->parent() ) { if( event->type() == QEvent::KeyPress || event->type() == QEvent::ShortcutOverride ) { QKeyEvent *keyEvent = static_cast<QKeyEvent*>( event ); keyPressEvent( keyEvent ); return true; } return false; } return KGraphicsWebView::eventFilter( obj, event ); } void keyPressEvent( QKeyEvent *event ) { if( event->key() == Qt::Key_Slash || event->matches( QKeySequence::Find ) ) { qreal offsetX = m_lineEdit->rect().width(); qreal offsetY = m_lineEdit->rect().height(); offsetX += page()->currentFrame()->scrollBarGeometry( Qt::Vertical ).width(); m_lineEdit->setPos( rect().bottomRight() - QPointF( offsetX, offsetY ) ); m_lineEdit->setFocus( Qt::PopupFocusReason ); m_lineEdit->show(); event->accept(); } else KGraphicsWebView::keyPressEvent( event ); } void resizeEvent( QGraphicsSceneResizeEvent *event ) { KGraphicsWebView::resizeEvent( event ); if( m_topBorder ) { m_topBorder->resize( event->newSize().width(), m_topBorder->size().height() ); m_bottomBorder->resize( event->newSize().width(), m_bottomBorder->size().height() ); QPointF bottomPoint = boundingRect().bottomLeft(); bottomPoint.ry() -= m_bottomBorder->size().height(); m_bottomBorder->setPos( bottomPoint ); m_topBorder->setPos( mapFromParent( pos() ) ); } } private: #if defined(DEBUG_BUILD_TYPE) QWebInspector *m_inspector; #endif WikipediaSearchLineEdit *m_lineEdit; Plasma::SvgWidget *m_topBorder; Plasma::SvgWidget *m_bottomBorder; }; #endif /* AMAROK_WIKIPEDIAAPPLET_P_H */ diff --git a/src/context/engines/wikipedia/WikipediaEngine.cpp b/src/context/applets/wikipedia/plugin/WikipediaEngine.cpp similarity index 62% rename from src/context/engines/wikipedia/WikipediaEngine.cpp rename to src/context/applets/wikipedia/plugin/WikipediaEngine.cpp index b3bb6b39db..0283d11be7 100644 --- a/src/context/engines/wikipedia/WikipediaEngine.cpp +++ b/src/context/applets/wikipedia/plugin/WikipediaEngine.cpp @@ -1,951 +1,825 @@ /**************************************************************************************** * Copyright (c) 2012 Ryan McCoskrie <ryan.mccoskrie@gmail.com * * Copyright (c) 2007 Leo Franchi <lfranchi@gmail.com> * * Copyright (c) 2008 Mark Kretschmann <kretschmann@kde.org> * * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************************/ -#define DEBUG_PREFIX "WikipediaEngine" +#define DEBUG_PREFIX "Wikipedia" #include "WikipediaEngine.h" #include "EngineController.h" -#include "core/meta/Meta.h" #include "core/meta/support/MetaConstants.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" -#include <Plasma/DataContainer> - #include <QHashIterator> +#include <QUrlQuery> #include <QXmlStreamReader> -using namespace Context; - -class WikipediaEnginePrivate -{ -private: - WikipediaEngine *const q_ptr; - Q_DECLARE_PUBLIC( WikipediaEngine ) - -public: - WikipediaEnginePrivate( WikipediaEngine *parent ) - : q_ptr( parent ) - , currentSelection( Artist ) - , useMobileVersion( false ) - , dataContainer( 0 ) - {} - ~WikipediaEnginePrivate() {} - - enum SelectionType - { - Artist, - Composer, - Album, - Track - }; - - // functions - void fetchWikiUrl( const QString &title, const QString &urlPrefix ); - void fetchLangLinks( const QString &title, const QString &hostLang, const QString &llcontinue = QString() ); - void fetchListing( const QString &title, const QString &hostLang ); - void reloadWikipedia(); - bool setSelection( SelectionType type ); // returns true if selection is changed - bool setSelection( const QString &type ); - SelectionType selection() const; - void updateEngine(); - void wikiParse( QString &page ); - QString createLanguageComboBox( const QMap<QString, QString> &languageMap ); - - // data members - SelectionType currentSelection; - QUrl wikiCurrentUrl; - QStringList preferredLangs; - struct TrackMetadata - { - QString artist; - QString composer; - QString album; - QString track; - void clear() - { - artist.clear(); - composer.clear(); - album.clear(); - track.clear(); - } - } m_previousTrackMetadata; - bool useMobileVersion; +#include <KLocalizedString> - Plasma::DataContainer *dataContainer; - QSet< QUrl > urls; - - // private slots - void _checkRequireUpdate( Meta::TrackPtr track ); - void _dataContainerUpdated( const QString &source, const Plasma::DataEngine::Data &data ); - void _parseLangLinksResult( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ); - void _parseListingResult( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ); - void _wikiResult( const QUrl &url, QByteArray result, NetworkAccessManagerProxy::Error e ); - void _stopped(); -}; - -void -WikipediaEnginePrivate::_dataContainerUpdated( const QString &source, const Plasma::DataEngine::Data &data ) +WikipediaEngine::WikipediaEngine( QObject* parent ) + : QObject( parent ) + , currentSelection( Artist ) + , useMobileVersion( false ) { - DEBUG_BLOCK - Q_Q( WikipediaEngine ); - - if( source != QLatin1String("wikipedia") ) - return; + preferredLangs = Amarok::config("Wikipedia Applet").readEntry( "PreferredLang", QStringList() << "en" ); - if( data.isEmpty() ) - { - debug() << "data is empty"; - return; - } - - if( data.contains( QLatin1String("reload") ) ) - { - if( data.value( QLatin1String("reload") ).toBool() ) - { - debug() << QLatin1String("reloading"); - reloadWikipedia(); - } - q->removeData( source, QLatin1String("reload") ); - } + EngineController *engine = The::engineController(); - if( data.contains( QLatin1String("goto") ) ) - { - QString gotoType = data.value( QLatin1String("goto") ).toString(); - debug() << "goto:" << gotoType; - if( !gotoType.isEmpty() ) - { - setSelection( gotoType ); - q->setData( source, QLatin1String("busy"), true ); - updateEngine(); - } - q->removeData( source, QLatin1String("goto") ); - } + _checkRequireUpdate( engine->currentTrack() ); - if( data.contains( QLatin1String("clickUrl") ) ) - { - QUrl clickUrl = data.value( QLatin1String("clickUrl") ).toUrl(); - debug() << "clickUrl:" << clickUrl; - if( clickUrl.isValid() ) - { - wikiCurrentUrl = clickUrl; - if( !wikiCurrentUrl.hasQueryItem( QLatin1String("useskin") ) ) - wikiCurrentUrl.addQueryItem( QLatin1String("useskin"), QLatin1String("monobook") ); - QUrl encodedUrl( wikiCurrentUrl.toEncoded() ); - urls << encodedUrl; - q->setData( source, QLatin1String("busy"), true ); - The::networkAccessManager()->getData( encodedUrl, q, - SLOT(_wikiResult(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); - } - q->removeData( source, QLatin1String("clickUrl") ); - } - - if( data.contains( QLatin1String("mobile") ) ) - { - bool mobile = data.value( QLatin1String("mobile") ).toBool(); - if( mobile != useMobileVersion ) - { - debug() << (mobile ? "switching to mobile wikipedia" : "switching to normal wikipedia"); - useMobileVersion = mobile; - updateEngine(); - } - } + connect( engine, &EngineController::trackChanged, + this, &WikipediaEngine::_checkRequireUpdate ); + connect( engine, &EngineController::trackMetadataChanged, + this, &WikipediaEngine::_checkRequireUpdate ); + connect( engine, &EngineController::stopped, + this, &WikipediaEngine::_stopped ); +} - if( data.contains( QLatin1String("lang") ) ) - { - QStringList langList = data.value( QLatin1String("lang") ).toStringList(); - if( !langList.isEmpty() && (preferredLangs != langList) ) - { - preferredLangs = langList; - updateEngine(); - debug() << QLatin1String("updated preferred wikipedia languages:") << preferredLangs; - } - q->removeData( source, QLatin1String("lang") ); - } +WikipediaEngine::~WikipediaEngine() +{ } void -WikipediaEnginePrivate::_wikiResult( const QUrl &url, QByteArray result, NetworkAccessManagerProxy::Error e ) +WikipediaEngine::_wikiResult( const QUrl &url, QByteArray result, NetworkAccessManagerProxy::Error e ) { - Q_Q( WikipediaEngine ); if( !urls.contains( url ) ) return; urls.remove( url ); if( e.code != QNetworkReply::NoError ) { - q->removeAllData( QLatin1String("wikipedia") ); - q->setData( QLatin1String("wikipedia"), QLatin1String("message"), - i18n("Unable to retrieve Wikipedia information: %1", e.description) ); - q->scheduleSourcesUpdated(); + clear(); + setMessage( i18n("Unable to retrieve Wikipedia information: %1", e.description) ); return; } debug() << "Received page from wikipedia:" << url; QString wiki( result ); // FIXME: For now we test if we got an article or not with a test on this string "wgArticleId=0" // This is bad if( wiki.contains(QLatin1String("wgArticleId=0")) && (wiki.contains(QLatin1String("wgNamespaceNumber=0")) || wiki.contains(QLatin1String("wgPageName=\"Special:Badtitle\"")) ) ) // The article does not exist { debug() << "article does not exist"; - q->removeAllData( QLatin1String("wikipedia") ); - q->setData( QLatin1String("wikipedia"), QLatin1String("message"), i18n( "No information found..." ) ); - q->scheduleSourcesUpdated(); + clear(); + setMessage( i18n( "No information found..." ) ); return; } // We've found a page - DataEngine::Data data; wikiParse( wiki ); - data[QLatin1String("page")] = wiki; - data[QLatin1String("url")] = QUrl(url); - q->removeData( QLatin1String("wikipedia"), QLatin1String("busy") ); + setPage( wiki ); + setBusy( false ); Meta::TrackPtr currentTrack = The::engineController()->currentTrack(); if( !currentTrack ) return; if( currentSelection == Artist ) // default, or applet told us to fetch artist { if( currentTrack && currentTrack->artist() ) { - data[QLatin1String("label")] = QLatin1String("Artist"); - data[QLatin1String("title")] = currentTrack->artist()->prettyName(); + setTitle( currentTrack->artist()->prettyName() ); } } else if( currentSelection == Composer ) { - data[QLatin1String("label")] = QLatin1String("Title"); - data[QLatin1String("title")] = currentTrack->composer()->prettyName(); + setTitle( currentTrack->composer()->prettyName() ); } else if( currentSelection == Track ) { - data[QLatin1String("label")] = QLatin1String("Title"); - data[QLatin1String("title")] = currentTrack->prettyName(); + setTitle( currentTrack->prettyName() ); } else if( currentSelection == Album ) { if( currentTrack && currentTrack->album() ) { - data[QLatin1String("label")] = QLatin1String("Album"); - data[QLatin1String("title")] = currentTrack->album()->prettyName(); + setTitle( currentTrack->album()->prettyName() ); } } - q->setData( QLatin1String("wikipedia"), data ); - q->scheduleSourcesUpdated(); } void -WikipediaEnginePrivate::_parseLangLinksResult( const QUrl &url, QByteArray data, +WikipediaEngine::_parseLangLinksResult( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ) { - Q_Q( WikipediaEngine ); if( !urls.contains( url ) ) return; urls.remove( url ); if( e.code != QNetworkReply::NoError || data.isEmpty() ) { debug() << "Parsing langlinks result failed" << e.description; - q->removeAllData( QLatin1String("wikipedia") ); - q->setData( QLatin1String("wikipedia"), QLatin1String("message"), - i18n("Unable to retrieve Wikipedia information: %1", e.description) ); - q->scheduleSourcesUpdated(); + clear(); + setMessage( i18n("Unable to retrieve Wikipedia information: %1", e.description) ); return; } QString hostLang = url.host(); hostLang.remove( QLatin1String(".wikipedia.org") ); - const QString &title = url.queryItemValue( QLatin1String("titles") ); + const QString &title = QUrlQuery( url ).queryItemValue( QLatin1String("titles") ); QHash<QString, QString> langTitleMap; // a hash of langlinks and their titles QString llcontinue; QXmlStreamReader xml( data ); while( !xml.atEnd() && !xml.hasError() ) { xml.readNext(); if( xml.isStartElement() && xml.name() == QLatin1String("page") ) { if( xml.attributes().hasAttribute(QLatin1String("missing")) ) break; QXmlStreamAttributes a = xml.attributes(); if( a.hasAttribute(QLatin1String("pageid")) && a.hasAttribute(QLatin1String("title")) ) { const QString &pageTitle = a.value( QLatin1String("title") ).toString(); if( pageTitle.endsWith(QLatin1String("(disambiguation)")) ) { fetchListing( title, hostLang ); return; } langTitleMap[hostLang] = title; } while( !xml.atEnd() ) { xml.readNext(); if( xml.isEndElement() && xml.name() == QLatin1String("page") ) break; if( xml.isStartElement() ) { if( xml.name() == QLatin1String("ll") ) { QXmlStreamAttributes a = xml.attributes(); if( a.hasAttribute(QLatin1String("lang")) ) { QString lang = a.value( QLatin1String("lang") ).toString(); langTitleMap[lang] = xml.readElementText(); } } else if( xml.name() == QLatin1String("query-continue") ) { xml.readNext(); if( xml.isStartElement() && xml.name() == QLatin1String("langlinks") ) { QXmlStreamAttributes a = xml.attributes(); if( a.hasAttribute(QLatin1String("llcontinue")) ) llcontinue = a.value( QLatin1String("llcontinue") ).toString(); } } } } } } if( !langTitleMap.isEmpty() ) { /* When we query langlinks using a particular language, interwiki * results will not contain links for that language. However, it may * appear as part of the "page" element if there's a match or a redirect * has been set. So we need to manually add it here if it's still empty. */ if( preferredLangs.contains(hostLang) && !langTitleMap.contains(hostLang) ) langTitleMap[hostLang] = title; - q->removeData( QLatin1String("wikipedia"), QLatin1String("busy") ); + setBusy( false ); QStringListIterator langIter( preferredLangs ); while( langIter.hasNext() ) { QString prefix = langIter.next().split( QLatin1Char(':') ).back(); if( langTitleMap.contains(prefix) ) { QString pageTitle = langTitleMap.value( prefix ); fetchListing( pageTitle, prefix ); return; } } } if( !llcontinue.isEmpty() ) { fetchLangLinks( title, hostLang, llcontinue ); } else { QRegExp regex( QLatin1Char('^') + hostLang + QLatin1String(".*$") ); int index = preferredLangs.indexOf( regex ); if( (index != -1) && (index < preferredLangs.count() - 1) ) { // use next preferred language as base for fetching langlinks since // the current one did not get any results we want. QString prefix = preferredLangs.value( index + 1 ).split( QLatin1Char(':') ).back(); fetchLangLinks( title, prefix ); } else { QStringList refinePossibleLangs = preferredLangs.filter( QRegExp("^(en|fr|de|pl).*$") ); if( refinePossibleLangs.isEmpty() ) { - q->removeAllData( QLatin1String("wikipedia") ); - q->setData( QLatin1String("wikipedia"), QLatin1String("message"), - i18n( "No information found..." ) ); - q->scheduleSourcesUpdated(); + clear(); + setMessage( i18n( "No information found..." ) ); return; } fetchListing( title, refinePossibleLangs.first().split( QLatin1Char(':') ).back() ); } } } void -WikipediaEnginePrivate::_parseListingResult( const QUrl &url, +WikipediaEngine::_parseListingResult( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ) { - Q_Q( WikipediaEngine ); if( !urls.contains( url ) ) return; urls.remove( url ); if( e.code != QNetworkReply::NoError || data.isEmpty() ) { debug() << "Parsing listing result failed" << e.description; - q->removeAllData( QLatin1String("wikipedia") ); - q->setData( QLatin1String("wikipedia"), QLatin1String("message"), - i18n("Unable to retrieve Wikipedia information: %1", e.description) ); - q->scheduleSourcesUpdated(); + clear(); + setMessage( i18n("Unable to retrieve Wikipedia information: %1", e.description) ); return; } QString hostLang = url.host(); hostLang.remove( QLatin1String(".wikipedia.org") ); - const QString &title = url.queryItemValue( QLatin1String("srsearch") ); + const QString &title = QUrlQuery( url ).queryItemValue( QLatin1String("srsearch") ); QStringList titles; QXmlStreamReader xml( data ); while( !xml.atEnd() && !xml.hasError() ) { xml.readNext(); if( xml.isStartElement() && xml.name() == QLatin1String("search") ) { while( xml.readNextStartElement() ) { if( xml.name() == QLatin1String("p") ) { if( xml.attributes().hasAttribute( QLatin1String("title") ) ) titles << xml.attributes().value( QLatin1String("title") ).toString(); xml.skipCurrentElement(); } else xml.skipCurrentElement(); } } } if( titles.isEmpty() ) { QStringList refinePossibleLangs = preferredLangs.filter( QRegExp("^(en|fr|de|pl).*$") ); int index = refinePossibleLangs.indexOf( hostLang ); if( (index != -1) && (index < refinePossibleLangs.count() - 1) ) fetchListing( title, refinePossibleLangs.value( index + 1 ).split( QLatin1Char(':') ).back() ); else { - q->removeAllData( QLatin1String("wikipedia") ); - q->setData( QLatin1String("wikipedia"), QLatin1String("message"), i18n( "No information found..." ) ); + clear(); + setMessage( i18n( "No information found..." ) ); } return; } QString pattern; switch( currentSelection ) { default: case Artist: pattern = i18nc("Search pattern for an artist or band", ".*\\(.*(artist|band).*\\))").toLatin1(); break; case Composer: pattern = i18nc("Search pattern for a composer", ".*\\(.*(composer|musician).*\\))").toLatin1(); break; case Album: pattern = i18nc("Search pattern for an album", ".*\\(.*(album|score|soundtrack).*\\)").toLatin1(); break; case Track: pattern = i18nc("Search pattern for a song", ".*\\(.*(song|track).*\\)").toLatin1(); break; } pattern.prepend( title ); int patternIndex = titles.indexOf( QRegExp(pattern, Qt::CaseInsensitive) ); const QString result = ( patternIndex != -1 ) ? titles.at( patternIndex ) : titles.first(); fetchWikiUrl( result, hostLang ); // end of the line } void -WikipediaEnginePrivate::_checkRequireUpdate( Meta::TrackPtr track ) +WikipediaEngine::_checkRequireUpdate( Meta::TrackPtr track ) { if( !track ) return; bool updateNeeded(false); switch( currentSelection ) { - case WikipediaEnginePrivate::Artist: + case WikipediaEngine::Artist: if( track->artist() ) updateNeeded = track->artist()->name() != m_previousTrackMetadata.artist; break; - case WikipediaEnginePrivate::Composer: + case WikipediaEngine::Composer: if( track->composer() ) updateNeeded = track->composer()->name() != m_previousTrackMetadata.composer; break; - case WikipediaEnginePrivate::Album: + case WikipediaEngine::Album: if( track->album() ) updateNeeded = track->album()->name() != m_previousTrackMetadata.album; break; - case WikipediaEnginePrivate::Track: + case WikipediaEngine::Track: updateNeeded = track->name() != m_previousTrackMetadata.track; break; } if( updateNeeded ) { m_previousTrackMetadata.clear(); if( track->artist() ) m_previousTrackMetadata.artist = track->artist()->name(); if( track->composer() ) m_previousTrackMetadata.composer = track->composer()->name(); if( track->album() ) m_previousTrackMetadata.album = track->album()->name(); m_previousTrackMetadata.track = track->name(); urls.clear(); updateEngine(); } } void -WikipediaEnginePrivate::_stopped() +WikipediaEngine::_stopped() { DEBUG_BLOCK - Q_Q( WikipediaEngine ); - dataContainer->removeAllData(); - dataContainer->setData( "stopped", 1 ); - q->scheduleSourcesUpdated(); + + clear(); +// dataContainer->setData( "stopped", 1 ); m_previousTrackMetadata.clear(); } void -WikipediaEnginePrivate::fetchWikiUrl( const QString &title, const QString &urlPrefix ) +WikipediaEngine::fetchWikiUrl( const QString &title, const QString &urlPrefix ) { - Q_Q( WikipediaEngine ); QUrl pageUrl; QString host( ".wikipedia.org" ); pageUrl.setScheme( QLatin1String( "https" ) ); - if( useMobileVersion ) - { - host.prepend( ".m" ); - host.prepend( urlPrefix ); - pageUrl.setHost( host ); - pageUrl.setPath( QString("/wiki/%1").arg(title) ); - DataEngine::Data data; - data[QLatin1String("sourceUrl")] = pageUrl; - q->removeAllData( QLatin1String("wikipedia") ); - q->setData( QLatin1String("wikipedia"), data ); - q->scheduleSourcesUpdated(); - return; - } +// if( useMobileVersion ) +// { +// host.prepend( ".m" ); +// host.prepend( urlPrefix ); +// pageUrl.setHost( host ); +// pageUrl.setPath( QString("/wiki/%1").arg(title) ); +// DataEngine::Data data; +// data[QLatin1String("sourceUrl")] = pageUrl; +// removeAllData( QLatin1String("wikipedia") ); +// setData( QLatin1String("wikipedia"), data ); +// return; +// } // We now use: http://en.wikipedia.org/w/index.php?title=The_Beatles&useskin=monobook // instead of: http://en.wikipedia.org/wiki/The_Beatles // So that wikipedia skin is forced to default "monoskin", and the page can be parsed correctly (see BUG 205901 ) host.prepend( urlPrefix ); pageUrl.setHost( host ); pageUrl.setPath( QLatin1String("/w/index.php") ); - pageUrl.addQueryItem( QLatin1String("title"), title ); - pageUrl.addQueryItem( QLatin1String("redirects"), QString::number(1) ); - pageUrl.addQueryItem( QLatin1String("useskin"), QLatin1String("monobook") ); + QUrlQuery query; + query.addQueryItem( QLatin1String("title"), title ); + query.addQueryItem( QLatin1String("redirects"), QString::number(1) ); + query.addQueryItem( QLatin1String("useskin"), QLatin1String("monobook") ); + pageUrl.setQuery( query ); wikiCurrentUrl = pageUrl; urls << pageUrl; - The::networkAccessManager()->getData( pageUrl, q, + emit urlChanged(); + The::networkAccessManager()->getData( pageUrl, this, SLOT(_wikiResult(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); } void -WikipediaEnginePrivate::fetchLangLinks( const QString &title, - const QString &hostLang, - const QString &llcontinue ) +WikipediaEngine::fetchLangLinks( const QString &title, + const QString &hostLang, + const QString &llcontinue ) { - Q_Q( WikipediaEngine ); QUrl url; url.setScheme( QLatin1String( "https" ) ); url.setHost( hostLang + QLatin1String(".wikipedia.org") ); url.setPath( QLatin1String("/w/api.php") ); - url.addQueryItem( QLatin1String("action"), QLatin1String("query") ); - url.addQueryItem( QLatin1String("prop"), QLatin1String("langlinks") ); - url.addQueryItem( QLatin1String("titles"), title ); - url.addQueryItem( QLatin1String("format"), QLatin1String("xml") ); - url.addQueryItem( QLatin1String("lllimit"), QString::number(100) ); - url.addQueryItem( QLatin1String("redirects"), QString::number(1) ); + QUrlQuery query; + query.addQueryItem( QLatin1String("action"), QLatin1String("query") ); + query.addQueryItem( QLatin1String("prop"), QLatin1String("langlinks") ); + query.addQueryItem( QLatin1String("titles"), title ); + query.addQueryItem( QLatin1String("format"), QLatin1String("xml") ); + query.addQueryItem( QLatin1String("lllimit"), QString::number(100) ); + query.addQueryItem( QLatin1String("redirects"), QString::number(1) ); if( !llcontinue.isEmpty() ) - url.addQueryItem( QLatin1String("llcontinue"), llcontinue ); + query.addQueryItem( QLatin1String("llcontinue"), llcontinue ); + url.setQuery( query ); urls << url; debug() << "Fetching langlinks:" << url; - The::networkAccessManager()->getData( url, q, + The::networkAccessManager()->getData( url, this, SLOT(_parseLangLinksResult(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); } void -WikipediaEnginePrivate::fetchListing( const QString &title, const QString &hostLang ) +WikipediaEngine::fetchListing( const QString &title, const QString &hostLang ) { - Q_Q( WikipediaEngine ); QUrl url; url.setScheme( QLatin1String( "https" ) ); url.setHost( hostLang + QLatin1String(".wikipedia.org") ); url.setPath( QLatin1String("/w/api.php") ); - url.addQueryItem( QLatin1String("action"), QLatin1String("query") ); - url.addQueryItem( QLatin1String("list"), QLatin1String("search") ); - url.addQueryItem( QLatin1String("srsearch"), title ); - url.addQueryItem( QLatin1String("srprop"), QLatin1String("size") ); - url.addQueryItem( QLatin1String("srredirects"), QString::number(1) ); - url.addQueryItem( QLatin1String("srlimit"), QString::number(20) ); - url.addQueryItem( QLatin1String("format"), QLatin1String("xml") ); + QUrlQuery query; + query.addQueryItem( QLatin1String("action"), QLatin1String("query") ); + query.addQueryItem( QLatin1String("list"), QLatin1String("search") ); + query.addQueryItem( QLatin1String("srsearch"), title ); + query.addQueryItem( QLatin1String("srprop"), QLatin1String("size") ); + query.addQueryItem( QLatin1String("srredirects"), QString::number(1) ); + query.addQueryItem( QLatin1String("srlimit"), QString::number(20) ); + query.addQueryItem( QLatin1String("format"), QLatin1String("xml") ); + url.setQuery( query ); urls << url; debug() << "Fetching listing:" << url; - The::networkAccessManager()->getData( url, q, + The::networkAccessManager()->getData( url, this, SLOT(_parseListingResult(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); } void -WikipediaEnginePrivate::updateEngine() +WikipediaEngine::updateEngine() { static QMap<SelectionType, qint64> typeToFieldMap; if( typeToFieldMap.isEmpty() ) { typeToFieldMap.insert( Artist, Meta::valArtist ); typeToFieldMap.insert( Composer, Meta::valComposer ); typeToFieldMap.insert( Album, Meta::valAlbum ); typeToFieldMap.insert( Track, Meta::valTitle ); } - Q_Q( WikipediaEngine ); - Meta::TrackPtr currentTrack = The::engineController()->currentTrack(); if( !currentTrack ) return; //TODO: Look into writing one function that can be used with different arguments for each case in this switch. QString tmpWikiStr; QString notice = i18nc( "%1 is field name such as 'Artist Name'", "%1 is needed for searching Wikipedia.", Meta::i18nForField( typeToFieldMap.value( currentSelection ) ) ); switch( currentSelection ) { case Artist: if( currentTrack->artist() ) { if( currentTrack->artist()->name().isEmpty() ) { - q->removeAllData( QLatin1String("wikipedia") ); - q->scheduleSourcesUpdated(); - q->setData( QLatin1String("wikipedia"), QLatin1String("message"), - notice ); + clear(); + setMessage( notice ); return; } - if( ( currentTrack->playableUrl().protocol() == QLatin1String("lastfm") ) || - ( currentTrack->playableUrl().protocol() == QLatin1String("daap") ) || + if( ( currentTrack->playableUrl().scheme() == QLatin1String("lastfm") ) || + ( currentTrack->playableUrl().scheme() == QLatin1String("daap") ) || !The::engineController()->isStream() ) tmpWikiStr = currentTrack->artist()->name(); else tmpWikiStr = currentTrack->artist()->prettyName(); } break; case Composer: if( currentTrack->composer() ) { if( currentTrack->composer()->name().isEmpty() ) { - q->removeAllData( QLatin1String("wikipedia") ); - q->scheduleSourcesUpdated(); - q->setData( QLatin1String("wikipedia"), QLatin1String("message"), - notice ); + clear(); + setMessage( notice ); return; } - if( ( currentTrack->playableUrl().protocol() == QLatin1String("lastfm") ) || - ( currentTrack->playableUrl().protocol() == QLatin1String("daap") ) || + if( ( currentTrack->playableUrl().scheme() == QLatin1String("lastfm") ) || + ( currentTrack->playableUrl().scheme() == QLatin1String("daap") ) || !The::engineController()->isStream() ) tmpWikiStr = currentTrack->composer()->name(); } break; case Album: if( currentTrack->album() ) { if( currentTrack->album()->name().isEmpty() ) { - q->removeAllData( QLatin1String("wikipedia") ); - q->scheduleSourcesUpdated(); - q->setData( QLatin1String("wikipedia"), QLatin1String("message"), - notice ); + clear(); + setMessage( notice ); return; } - if( ( currentTrack->playableUrl().protocol() == QLatin1String("lastfm") ) || - ( currentTrack->playableUrl().protocol() == QLatin1String("daap") ) || + if( ( currentTrack->playableUrl().scheme() == QLatin1String("lastfm") ) || + ( currentTrack->playableUrl().scheme() == QLatin1String("daap") ) || !The::engineController()->isStream() ) tmpWikiStr = currentTrack->album()->name(); } break; case Track: if( currentTrack->name().isEmpty() ) { - q->removeAllData( QLatin1String("wikipedia") ); - q->scheduleSourcesUpdated(); - q->setData( QLatin1String("wikipedia"), QLatin1String("message"), - notice ); + clear(); + setMessage( notice ); return; } tmpWikiStr = currentTrack->prettyName(); break; } //Hack to make wiki searches work with magnatune preview tracks if( tmpWikiStr.contains( QLatin1String("PREVIEW: buy it at www.magnatune.com") ) ) { tmpWikiStr = tmpWikiStr.remove(QLatin1String(" (PREVIEW: buy it at www.magnatune.com)") ); int index = tmpWikiStr.indexOf( QLatin1Char('-') ); if( index != -1 ) tmpWikiStr = tmpWikiStr.left (index - 1); } if( preferredLangs.isEmpty() ) preferredLangs = QStringList() << QLatin1String("en:en"); fetchLangLinks( tmpWikiStr, preferredLangs.first().split( QLatin1Char(':') ).back() ); } void -WikipediaEnginePrivate::wikiParse( QString &wiki ) +WikipediaEngine::wikiParse( QString &wiki ) { //remove the new-lines and tabs(replace with spaces IS needed). wiki.replace( '\n', QLatin1Char(' ') ); wiki.replace( '\t', QLatin1Char(' ') ); // Get the available language list QString wikiLanguagesSection; QMap<QString, QString> langMap; int langSectionIndex = 0; if( (langSectionIndex = wiki.indexOf( QLatin1String("<div id=\"p-lang\" class=\"portlet\">") )) != -1 ) { QStringRef sref = wiki.midRef( langSectionIndex ); sref = wiki.midRef( sref.position(), wiki.indexOf( QLatin1String("<ul>"), sref.position() ) - sref.position() ); sref = wiki.midRef( sref.position(), wiki.indexOf( QLatin1String("</dev>"), sref.position() ) - sref.position() ); wikiLanguagesSection = sref.toString(); QXmlStreamReader xml( wikiLanguagesSection ); while( !xml.atEnd() && !xml.hasError() ) { xml.readNext(); if( xml.isStartElement() && xml.name() == QLatin1String("li") ) { while( xml.readNextStartElement() ) { if( xml.name() == QLatin1String("a") ) { QString url = xml.attributes().value( QLatin1String("href") ).toString(); langMap[ xml.readElementText() ] = url; } else xml.skipCurrentElement(); } } } } QString copyright; const QString copyrightMark = QLatin1String("<li id=\"f-copyright\">"); int copyrightIndex = wiki.indexOf( copyrightMark ); if( copyrightIndex != -1 ) { QStringRef sref = wiki.midRef( copyrightIndex + copyrightMark.length() ); sref = wiki.midRef( sref.position(), wiki.indexOf( QLatin1String("</li>"), sref.position() ) - sref.position() ); copyright = sref.toString(); copyright.remove( QLatin1String("<br />") ); //only one br at the beginning copyright.prepend( QLatin1String("<br />") ); } const int titleIndex = wiki.indexOf( QRegExp( QLatin1String("<title>[^<]*") ) ) + 7; const int bsTitleIndex = wiki.indexOf( QLatin1String(""), titleIndex ) - titleIndex; const QString title = wiki.mid( titleIndex, bsTitleIndex ); // Ok lets remove the top and bottom parts of the page QStringRef wikiRef; wikiRef = wiki.midRef( wiki.indexOf( QLatin1String("") ) ); wikiRef = wiki.midRef( wikiRef.position(), wiki.indexOf( QLatin1String("
"), wikiRef.position() ) - wikiRef.position() ); wiki = wikiRef.toString(); auto removeTag = [&wiki] ( const QString& tagStart, const QString& tagEnd ) { const int tagEndSize = tagEnd.size(); int matchIndex = 0; const QStringMatcher tagMatcher( tagStart ); while( ( matchIndex = tagMatcher.indexIn( wiki, matchIndex ) ) != -1 ) { const int nToTagEnd = wiki.indexOf( tagEnd, matchIndex ) - matchIndex; const QStringRef tagRef = wiki.midRef( matchIndex, nToTagEnd + tagEndSize ); wiki.remove( tagRef.toString() ); } }; // lets remove the warning box removeTag( "" ); // remove protection policy (we don't do edits) removeTag( "
" ); // lets also remove the "lock" image removeTag( "" ); // remove
") ); wiki.remove( QRegExp( QLatin1String("

[^<]*

") ) ); wiki.remove( QRegExp( QLatin1String("]*>[^<]*<[^>]*>[^<]*<[^>]*>[^<]*") ) ); wiki.remove( QRegExp( QLatin1String("

]*><[^\"]*\"#_skip_noteTA\">[^<]*<[^<]*

") ) ); wiki.replace( QRegExp( QLatin1String("
]*>([^<]*)") ), QLatin1String("\\1") ); // Remove anything inside of a class called urlexpansion, as it's pointless for us wiki.remove( QRegExp( QLatin1String("[^(]*[(][^)]*[)]") ) ); // Remove hidden table rows as well QRegExp hidden( QLatin1String("
.*"), Qt::CaseInsensitive ); hidden.setMinimal( true ); //greedy behaviour wouldn't be any good! wiki.remove( hidden ); // we want to keep our own style (we need to modify the stylesheet a bit to handle things nicely) wiki.remove( QRegExp( QLatin1String("style= *\"[^\"]*\"") ) ); // We need to leave the classes behind, otherwise styling it ourselves gets really nasty and tedious and roughly impossible to do in a sane maner //wiki.replace( QRegExp( "class= *\"[^\"]*\"" ), QString() ); // let's remove the form elements, we don't want them. wiki.remove( QRegExp( QLatin1String("]*>") ) ); wiki.remove( QRegExp( QLatin1String("]*>") ) ); wiki.remove( QLatin1String("\n") ); wiki.remove( QRegExp( QLatin1String("]*>") ) ); wiki.remove( QLatin1String("\n") ); wiki.remove( QRegExp( QLatin1String("]*>") ) ); wiki.remove( QLatin1String("") ); wiki.prepend( QLatin1String("\n") ); wiki.append( QString(QLatin1String("%1\n")).arg(title) ); wiki.append( QLatin1String("\n") ); // wiki.append( createLanguageComboBox(langMap) ); // BUG:259075 wiki.append( QLatin1String("\n") ); } QString -WikipediaEnginePrivate::createLanguageComboBox( const QMap &languageMap ) +WikipediaEngine::createLanguageComboBox( const QMap &languageMap ) { if( languageMap.isEmpty() ) return QString(); QString html; QMapIterator i(languageMap); while( i.hasNext() ) { i.next(); html += QString( "" ).arg( i.value(), i.key() ); } html.prepend( QString("
") ); return html; } void -WikipediaEnginePrivate::reloadWikipedia() +WikipediaEngine::reloadWikipedia() { - Q_Q( WikipediaEngine ); if( !wikiCurrentUrl.isValid() ) return; urls << wikiCurrentUrl; - q->setData( QLatin1String("wikipedia"), QLatin1String("busy"), true ); - q->scheduleSourcesUpdated(); - The::networkAccessManager()->getData( wikiCurrentUrl, q, + setBusy( true ); + The::networkAccessManager()->getData( wikiCurrentUrl, this, SLOT(_wikiResult(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); } -WikipediaEnginePrivate::SelectionType -WikipediaEnginePrivate::selection() const +WikipediaEngine::SelectionType +WikipediaEngine::selection() const { return currentSelection; } bool -WikipediaEnginePrivate::setSelection( SelectionType type ) +WikipediaEngine::setSelection( SelectionType type ) { - if( currentSelection != type ) - { - currentSelection = type; - return true; - } - return false; + if( currentSelection == type ) + return false; + + currentSelection = type; + emit selectionChanged(); + + updateEngine(); + + return true; } bool -WikipediaEnginePrivate::setSelection( const QString &type ) +WikipediaEngine::setSelection( const QString &type ) { bool changed( false ); if( type == QLatin1String("artist") ) changed = setSelection( Artist ); else if( type == QLatin1String("composer") ) changed = setSelection( Composer ); else if( type == QLatin1String("album") ) changed = setSelection( Album ); else if( type == QLatin1String("track") ) changed = setSelection( Track ); return changed; } -WikipediaEngine::WikipediaEngine( QObject* parent, const QList& /*args*/ ) - : DataEngine( parent ) - , d_ptr( new WikipediaEnginePrivate( this ) ) +void +WikipediaEngine::setPage(const QString& page) { + if( m_page == page ) + return; + m_page = page; + emit pageChanged(); } -WikipediaEngine::~WikipediaEngine() +void +WikipediaEngine::setMessage(const QString& message) { - delete d_ptr; + if( m_message == message ) + return; + + m_message = message; + emit messageChanged(); } + void -WikipediaEngine::init() +WikipediaEngine::setBusy(bool busy) { - Q_D( WikipediaEngine ); - d->dataContainer = new Plasma::DataContainer( this ); - d->dataContainer->setObjectName( QLatin1String("wikipedia") ); - addSource( d->dataContainer ); - connect( d->dataContainer, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), - this, SLOT(_dataContainerUpdated(QString,Plasma::DataEngine::Data)) ); + if( m_busy == busy ) + return; - EngineController *engine = The::engineController(); + m_busy = busy; + emit busyChanged(); +} + +void +WikipediaEngine::setTitle(const QString& title) +{ + if( m_title == title ) + return; - connect( engine, SIGNAL(trackChanged(Meta::TrackPtr)), - this, SLOT(_checkRequireUpdate(Meta::TrackPtr)) ); - connect( engine, SIGNAL(trackMetadataChanged(Meta::TrackPtr)), - this, SLOT(_checkRequireUpdate(Meta::TrackPtr)) ); - connect( engine, SIGNAL(stopped(qint64,qint64)), - this, SLOT(_stopped()) ); + m_title = title; + emit titleChanged(); } -bool -WikipediaEngine::sourceRequestEvent( const QString &source ) +void +WikipediaEngine::clear() { - if( source == QLatin1String("update") ) - { - scheduleSourcesUpdated(); - } - else if( source == QLatin1String("wikipedia") ) - { - Q_D( WikipediaEngine ); - d->updateEngine(); - return true; - } - return false; + setPage( QString() ); + setBusy( false ); + setTitle( QString() ); +} + +void +WikipediaEngine::setLanguage(const QString& language) +{ + if( preferredLangs.first() == language ) + return; + + preferredLangs.removeAll( language ); + preferredLangs.prepend( language ); + emit languageChanged(); + + updateEngine(); +} + +void +WikipediaEngine::setUrl(const QUrl& url) +{ + if( wikiCurrentUrl == url ) + return; + + wikiCurrentUrl = url; + urls << url; + emit urlChanged(); + + The::networkAccessManager()->getData( url, this, SLOT(_wikiResult(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); } diff --git a/src/context/applets/wikipedia/plugin/WikipediaEngine.h b/src/context/applets/wikipedia/plugin/WikipediaEngine.h new file mode 100644 index 0000000000..f995d7729b --- /dev/null +++ b/src/context/applets/wikipedia/plugin/WikipediaEngine.h @@ -0,0 +1,122 @@ +/**************************************************************************************** + * Copyright (c) 2007 Leo Franchi * + * Copyright (c) 2008 Mark Kretschmann * + * Copyright (c) 2009 Simon Esneault * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +#ifndef AMAROK_WIKIPEDIA_ENGINE +#define AMAROK_WIKIPEDIA_ENGINE + +#include "core/meta/Meta.h" +#include "network/NetworkAccessManagerProxy.h" + +#include + + +class WikipediaEngine : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString page READ page NOTIFY pageChanged) + Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged) + Q_PROPERTY(QString message READ message NOTIFY messageChanged) + Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) + Q_PROPERTY(SelectionType selection READ selection WRITE setSelection NOTIFY selectionChanged) + Q_PROPERTY(QString title READ title NOTIFY titleChanged) + Q_PROPERTY(QString language READ language WRITE setLanguage NOTIFY languageChanged) + +public: + enum SelectionType + { + Artist, + Composer, + Album, + Track + }; + Q_ENUM(SelectionType) + + WikipediaEngine( QObject* parent = Q_NULLPTR ); + virtual ~WikipediaEngine(); + + QString page() const { return m_page; } + QUrl url() const { return wikiCurrentUrl; } + void setUrl( const QUrl &url ); + QString message() const { return m_message; } + bool busy() const { return m_busy; } + SelectionType selection() const; + bool setSelection( SelectionType type ); // returns true if selection is changed + QString title() const { return m_title; } + QString language() const { return preferredLangs.first(); } + void setLanguage( const QString &language ); + +signals: + void pageChanged(); + void messageChanged(); + void busyChanged(); + void selectionChanged(); + void languageChanged(); + void titleChanged(); + void urlChanged(); + +private: + void fetchWikiUrl( const QString &title, const QString &urlPrefix ); + void fetchLangLinks( const QString &title, const QString &hostLang, const QString &llcontinue = QString() ); + void fetchListing( const QString &title, const QString &hostLang ); + void reloadWikipedia(); + bool setSelection( const QString &type ); + void updateEngine(); + void wikiParse( QString &page ); + QString createLanguageComboBox( const QMap &languageMap ); + void setPage( const QString &page ); + void setMessage( const QString &message ); + void setBusy( bool busy ); + void setTitle( const QString &title ); + void clear(); + + SelectionType currentSelection; + QUrl wikiCurrentUrl; + QStringList preferredLangs; + struct TrackMetadata + { + QString artist; + QString composer; + QString album; + QString track; + void clear() + { + artist.clear(); + composer.clear(); + album.clear(); + track.clear(); + } + } m_previousTrackMetadata; + bool useMobileVersion; + + QSet< QUrl > urls; + QString m_page; + QString m_message; + bool m_busy; + QString m_title; + +private slots: + void _checkRequireUpdate( Meta::TrackPtr track ); + void _parseLangLinksResult( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ); + void _parseListingResult( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ); + void _wikiResult( const QUrl &url, QByteArray result, NetworkAccessManagerProxy::Error e ); + void _stopped(); +}; + + +#endif + diff --git a/src/context/applets/wikipedia/plugin/WikipediaPlugin.cpp b/src/context/applets/wikipedia/plugin/WikipediaPlugin.cpp new file mode 100644 index 0000000000..56ef7f4e84 --- /dev/null +++ b/src/context/applets/wikipedia/plugin/WikipediaPlugin.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2017 Malte Veerman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "WikipediaEngine.h" + +#include + +#include + + +class WikipediaPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char* uri) Q_DECL_OVERRIDE + { + Q_ASSERT(uri == QLatin1String("org.kde.amarok.wikipedia")); + + qmlRegisterSingletonType(uri, 1, 0, "WikipediaEngine", wikipedia_engine_provider); + } + + static QObject *wikipedia_engine_provider(QQmlEngine *engine, QJSEngine *scriptEngine) + { + Q_UNUSED(engine) + Q_UNUSED(scriptEngine) + + return new WikipediaEngine(); + } +}; + +#include diff --git a/src/context/applets/wikipedia/plugin/qmldir b/src/context/applets/wikipedia/plugin/qmldir new file mode 100644 index 0000000000..8b151fbde5 --- /dev/null +++ b/src/context/applets/wikipedia/plugin/qmldir @@ -0,0 +1,2 @@ +module org.kde.amarok.wikipedia +plugin amarok_context_applet_wikipedia diff --git a/src/context/applets/wikipedia/wikipediaGeneralSettings.ui b/src/context/applets/wikipedia/wikipediaGeneralSettings.ui deleted file mode 100644 index b476ff0847..0000000000 --- a/src/context/applets/wikipedia/wikipediaGeneralSettings.ui +++ /dev/null @@ -1,41 +0,0 @@ - - - wikipediaGeneralSettings - - - - 0 - 0 - 253 - 62 - - - - - 0 - 0 - - - - - 0 - - - 0 - - - - - - - Use Wikipedia &mobile version - - - - - - - - - - diff --git a/src/context/applets/wikipedia/wikipediaLanguageSettings.ui b/src/context/applets/wikipedia/wikipediaLanguageSettings.ui deleted file mode 100644 index 80b1b6a1fc..0000000000 --- a/src/context/applets/wikipedia/wikipediaLanguageSettings.ui +++ /dev/null @@ -1,81 +0,0 @@ - - - wikipediaLanguageSettings - - - - 0 - 0 - 469 - 300 - - - - - 0 - 0 - - - - - 400 - 300 - - - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - - - - 4 - - - 4 - - - - - - - - - 0 - 0 - - - - - - - - - - - KPushButton - QPushButton -
kpushbutton.h
-
- - KActionSelector - QWidget -
kactionselector.h
-
-
- - -
diff --git a/src/context/containments/CMakeLists.txt b/src/context/containments/CMakeLists.txt deleted file mode 100644 index 1ebca0b2c1..0000000000 --- a/src/context/containments/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory( verticallayout ) diff --git a/src/context/containments/verticallayout/CMakeLists.txt b/src/context/containments/verticallayout/CMakeLists.txt deleted file mode 100644 index 7eaa9c3a50..0000000000 --- a/src/context/containments/verticallayout/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -project(context-vertical-containment) - -include_directories( ../../.. - ../.. - .. ) - -set(context_SRCS - VerticalToolbarContainment.cpp - VerticalAppletLayout.cpp - ) - -add_library(amarok_containment_vertical MODULE ${context_SRCS}) -if(APPLE) - set_target_properties(amarok_containment_vertical PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") -endif() -target_link_libraries(amarok_containment_vertical amarokcore amaroklib KF5::Plasma KF5::KIOCore) - -install(TARGETS amarok_containment_vertical DESTINATION ${PLUGIN_INSTALL_DIR}) -install(FILES amarok-containment-vertical.desktop DESTINATION ${SERVICES_INSTALL_DIR}) diff --git a/src/context/containments/verticallayout/VerticalAppletLayout.cpp b/src/context/containments/verticallayout/VerticalAppletLayout.cpp deleted file mode 100644 index 240c52bdf1..0000000000 --- a/src/context/containments/verticallayout/VerticalAppletLayout.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "VerticalAppletLayout" - -#include "VerticalAppletLayout.h" - -#include "Applet.h" -#include "Containment.h" -#include "core/support/Debug.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -Context::VerticalAppletLayout::VerticalAppletLayout( QGraphicsItem* parent ) - : QGraphicsWidget( parent ) - , m_showingIndex( -1 ) - , m_layout( new QGraphicsLinearLayout(Qt::Vertical, this) ) - , m_dummyWidget( new QGraphicsWidget( this ) ) -{ - m_layout->setContentsMargins( 0, 2, 0, 2 ); - m_layout->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - m_layout->setSpacing( 0 ); - - // This dummy widget is added at the end of the layout to eat up the - // remaining space and keep the graphicslayout at the right size. Otherwise, - // if the last applet has a sizehint that is smaller than ours, the layout - // will assume that size, causing applets to be constrained when switching - // to another applet. - m_dummyWidget->setMinimumHeight( 0.0 ); - m_dummyWidget->setPreferredHeight( 0.0 ); - m_dummyWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::MinimumExpanding ); - setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); -} - -Context::VerticalAppletLayout::~VerticalAppletLayout() -{ - DEBUG_BLOCK - qDeleteAll( m_appletList ); -} - -void -Context::VerticalAppletLayout::resizeEvent( QGraphicsSceneResizeEvent * event ) -{ - if( testAttribute( Qt::WA_PendingResizeEvent ) ) - return; // lets not do this more than necessary, shall we? - QGraphicsWidget::resizeEvent( event ); -} - -void -Context::VerticalAppletLayout::addApplet( Plasma::Applet* applet, int location ) -{ - DEBUG_BLOCK - debug() << "layout told to add applet" << applet->pluginName() << "at" << location; - if( m_appletList.isEmpty() ) - emit noApplets( false ); - - applet->show(); - - if( location < 0 ) // being told to add at end - { - m_appletList << applet; - m_layout->addItem( applet ); - location = m_appletList.size() - 1; // so the signal has the correct location - } - else - { - m_appletList.insert( location, applet ); - m_layout->insertItem( location, applet ); - } - - debug() << "emitting addApplet with location" << location; - emit appletAdded( applet, location ); - - // every time the geometry change, we will call showapplet ;) - connect( applet, SIGNAL(sizeHintChanged(Qt::SizeHint)), SLOT(refresh()) ); -} - -void -Context::VerticalAppletLayout::saveToConfig( KConfigGroup &conf ) -{ - DEBUG_BLOCK - QStringList plugins; - - for( int i = 0; i < m_appletList.size(); i++ ) - { - Plasma::Applet *applet = m_appletList.at(i); - if( applet != 0 ) - { - debug() << "saving applet" << applet->pluginName(); - plugins << applet->pluginName(); - } - conf.writeEntry( "plugins", plugins ); - } - conf.writeEntry( "firstShowingApplet", m_showingIndex ); -} - -void -Context::VerticalAppletLayout::refresh() -{ - showAtIndex( m_showingIndex ); -} - -void -Context::VerticalAppletLayout::showApplet( Plasma::Applet* applet ) // SLOT -{ - debug() << "showing applet" << applet->pluginName(); - showAtIndex( m_appletList.indexOf( applet ) ); -} - -void -Context::VerticalAppletLayout::moveApplet( Plasma::Applet* applet, int oldLoc, int newLoc) -{ - DEBUG_BLOCK - // if oldLoc is -1 we search for the applet to get the real location - if( oldLoc == -1 ) - oldLoc = m_appletList.indexOf( applet ); - if( oldLoc == -1 ) - debug() << "COULDN'T FIND APPLET IN LIST!"; - - // debug() << "moving applet in layout from" << oldLoc << "to" << newLoc; - - if( oldLoc < 0 || oldLoc > m_appletList.size() - 1 || newLoc < 0 || newLoc > m_appletList.size() || oldLoc == newLoc ) - return; - m_appletList.insert( newLoc, m_appletList.takeAt( oldLoc ) ); - QGraphicsLayoutItem *item = m_layout->itemAt( oldLoc ); - m_layout->removeAt( oldLoc ); - m_layout->insertItem( newLoc, item ); - showApplet( applet ); -} - -void -Context::VerticalAppletLayout::appletRemoved( Plasma::Applet* app ) -{ - DEBUG_BLOCK - int removedIndex = m_appletList.indexOf( app ); - debug() << "removing applet at index:" << removedIndex; - m_appletList.removeAll( app ); - if( m_showingIndex > removedIndex ) - m_showingIndex--; - m_layout->removeItem( app ); - - debug() << "got " << m_appletList.size() << " applets left"; - if( m_appletList.size() == 0 ) - emit noApplets( true ); - refresh(); -} - -void -Context::VerticalAppletLayout::showAtIndex( int index ) -{ - if( (index < 0) || (index > m_appletList.size() - 1) ) - return; - if( m_appletList.isEmpty() || !m_appletList.value( index ) ) - return; - - setGeometry( scene()->sceneRect() ); - m_layout->removeItem( m_dummyWidget ); - - // remove and hide all applets prior to index - QList toRemove; - for( int i = 0, count = m_layout->count(); i < count; ++i ) - { - if( QGraphicsLayoutItem *item = m_layout->itemAt( i ) ) - { - Plasma::Applet *applet = static_cast( item ); - if( m_appletList.indexOf( applet ) < index ) - toRemove << applet; - } - } - - foreach( Plasma::Applet *applet, toRemove ) - { - m_layout->removeItem( applet ); - applet->hide(); - } - - // iterate through the applets and add ones that we can fit using the size - // hints provided by the applets - qreal height = 0.0; - int currentIndex = m_appletList.size(); - for( int count = currentIndex, i = index; i < count; ++i ) - { - Plasma::Applet *item = m_appletList.at( i ); - const qreal remainingH = size().height() - height; - const qreal preferredH = item->effectiveSizeHint( Qt::PreferredSize ).height(); - const qreal minimumH = item->effectiveSizeHint( Qt::MinimumSize ).height(); - const qreal maximumH = item->effectiveSizeHint( Qt::MaximumSize ).height(); - - Context::Applet *applet = qobject_cast( item ); - if( applet ) { - const bool wantSpace = (applet->collapseOffHeight() < 0) && (maximumH > remainingH); - - if( (preferredH > remainingH) || (wantSpace && !applet->isCollapsed() ) ) - { - bool show = ( minimumH <= remainingH ); - currentIndex = i; - applet->setVisible( show ); - if( show ) - { - m_layout->addItem( applet ); - if( wantSpace ) - { - applet->resize( size().width(), remainingH ); - m_layout->setStretchFactor( applet, 10000 ); - } - applet->update(); - ++currentIndex; - } - break; - } - } - - height += preferredH; - m_layout->addItem( item ); - item->show(); - item->update(); - } - - // remove and hide all other applets - for( int i = currentIndex; i < m_appletList.count(); ++i ) - { - Plasma::Applet *item = m_appletList.at( i ); - m_layout->removeItem( item ); - item->hide(); - } - - m_layout->addItem( m_dummyWidget ); - m_showingIndex = index; -} - diff --git a/src/context/containments/verticallayout/VerticalAppletLayout.h b/src/context/containments/verticallayout/VerticalAppletLayout.h deleted file mode 100644 index 1a9608a760..0000000000 --- a/src/context/containments/verticallayout/VerticalAppletLayout.h +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_VERTICAL_APPLET_LAYOUT_H -#define AMAROK_VERTICAL_APPLET_LAYOUT_H - - -#include "amarok_export.h" - -#include - -class KConfigGroup; - -class QGraphicsItem; -class QGraphicsSceneResizeEvent; -class QPainter; -class QStyleOptionGraphicsItem; -class QGraphicsLinearLayout; - -namespace Plasma -{ - class Applet; -} - -namespace Context -{ - -class Containment; - -class VerticalAppletLayout : public QGraphicsWidget -{ - Q_OBJECT - public: - VerticalAppletLayout( QGraphicsItem* parent = 0 ); - ~VerticalAppletLayout(); - - void addApplet( Plasma::Applet*, int location = -1 ); - - virtual void saveToConfig( KConfigGroup &conf ); - - void showAtIndex( int index ); - - Q_SIGNALS: - void appletAdded( Plasma::Applet* applet, int location ); - void noApplets( bool ); - - public Q_SLOTS: - void showApplet( Plasma::Applet* ); - void moveApplet( Plasma::Applet*, int, int); - void appletRemoved( Plasma::Applet* app ); - void refresh(); - - protected: - // reimplemented from QGraphicsWidget - virtual void resizeEvent( QGraphicsSceneResizeEvent * event ); - - private: - QList< Plasma::Applet* > m_appletList; - int m_showingIndex; - - QGraphicsLinearLayout *m_layout; - QGraphicsWidget *m_dummyWidget; -}; - -} // namespace Context - -#endif diff --git a/src/context/containments/verticallayout/VerticalToolbarContainment.cpp b/src/context/containments/verticallayout/VerticalToolbarContainment.cpp deleted file mode 100644 index f47acf9566..0000000000 --- a/src/context/containments/verticallayout/VerticalToolbarContainment.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "VerticalToolbarContainment" - -#include "VerticalToolbarContainment.h" - -#include "ContextView.h" -#include "core/interfaces/Logger.h" -#include "core/support/Components.h" -#include "core/support/Debug.h" -#include "EngineController.h" -#include "PaletteHandler.h" -#include "VerticalAppletLayout.h" - -#include - -#include - -Context::VerticalToolbarContainment::VerticalToolbarContainment( QObject *parent, const QVariantList &args ) - : Containment( parent, args ) - , m_view( 0 ) - , m_applets( 0 ) - , m_noAppletText( 0 ) -{ - DEBUG_BLOCK - setContainmentType( CustomContainment ); - setDrawWallpaper( false ); - // setScreen( -1 ); - setImmutability( Plasma::Mutable ); - - debug() << "applet containment has corona:" << corona(); - m_applets = new VerticalAppletLayout( this ); - - connect( this, SIGNAL(appletRemoved(Plasma::Applet*)), SLOT(appletRemoved(Plasma::Applet*)) ); - connect( this, SIGNAL(appletRemoved(Plasma::Applet*)), SIGNAL(geometryChanged()) ); - - connect( m_applets, SIGNAL(appletAdded(Plasma::Applet*,int)), SIGNAL(appletAdded(Plasma::Applet*,int)) ); - connect( m_applets, SIGNAL(appletAdded(Plasma::Applet*,int)), SIGNAL(geometryChanged()) ); - connect( m_applets, SIGNAL(noApplets(bool)), SLOT(showEmptyText(bool)) ); - -} - -Context::VerticalToolbarContainment::~VerticalToolbarContainment() -{} - -void -Context::VerticalToolbarContainment::constraintsEvent( Plasma::Constraints constraints ) -{ - Q_UNUSED( constraints ) - if( m_noAppletText ) - { - QRectF masterRect = contentsRect(); - m_noAppletText->setTextWidth( masterRect.width() * .4 ); - QPointF topLeft( ( masterRect.width() / 2 ) - ( m_noAppletText->boundingRect().width() / 2 ), ( masterRect.height() / 2 ) - ( m_noAppletText->boundingRect().height() / 2 ) ); - m_noAppletText->setPos( topLeft ); - } -} - -QList -Context::VerticalToolbarContainment::contextualActions() -{ - return QList< QAction* >(); -} - -void -Context::VerticalToolbarContainment::saveToConfig( KConfigGroup &conf ) -{ - m_applets->saveToConfig( conf ); -} - -void -Context::VerticalToolbarContainment::loadConfig( const KConfigGroup &conf ) -{ - DEBUG_BLOCK - - QStringList plugins = conf.readEntry( "plugins", QStringList() ); - debug() << "plugins.size(): " << plugins.size(); - - foreach( const QString& plugin, plugins ) - { - PERF_LOG( qPrintable( QString("Adding applet: %1").arg( plugin ) ) ) - debug() << "Adding applet: " << plugin; - addApplet( plugin, -1 ); - } - - int showing = conf.readEntry( "firstShowingApplet", 0 ); - m_applets->showAtIndex( showing ); -} - -void -Context::VerticalToolbarContainment::setView( ContextView* view ) -{ - DEBUG_BLOCK - - m_view = view; - // kick the toolbar with a real corona no w - emit updatedContainment( this ); -} - -Context::ContextView* -Context::VerticalToolbarContainment::view() -{ - return m_view; -} - -void -Context::VerticalToolbarContainment::updateGeometry() -{ - Context::Containment::updateGeometry(); - - /* We used to use _scene_ sceneRect here to update applets and geomtery, but that - * leaded to infinite loop (across mainloop) - see bug 278897. - * (m_applets->setGeometry(), refresh() would enlarge _scene_ sceneRect by a few - * pixels which would trigger updateGeometry() and so on...) - * - * We now use _view_ sceneRect to update geometry and do nothing without a view - */ - if(!view()) - return; - - // mimic ContextView::resizeEvent(), nothing else seems to work, bug 292895 - QRectF rect( view()->pos(), view()->maximumViewportSize() ); - setGeometry( rect ); - m_applets->setGeometry( rect ); - m_applets->refresh(); -} - -void -Context::VerticalToolbarContainment::addApplet( const QString& pluginName, const int loc ) // SLOT -{ - DEBUG_BLOCK - - if( pluginName == "analyzer" && !EngineController::instance()->supportsAudioDataOutput() ) - { - Amarok::Components::logger()->longMessage( i18n( "Error: Visualizations are not supported by your current Phonon backend." ), - Amarok::Logger::Error ) ; - - return; - } - - Plasma::Applet* applet = Plasma::Containment::addApplet( pluginName ); - - Q_ASSERT_X( applet, "addApplet", "FAILED ADDING APPLET TO CONTAINMENT!! NOT FOUND!!" ); - - m_applets->addApplet( applet, loc ); - applet->setFlag( QGraphicsItem::ItemIsMovable, false ); -} - -void -Context::VerticalToolbarContainment::appletRemoved( Plasma::Applet* applet ) -{ - m_applets->appletRemoved( applet ); -} - -void -Context::VerticalToolbarContainment::showApplet( Plasma::Applet* applet ) -{ - m_applets->showApplet( applet ); -} - -void -Context::VerticalToolbarContainment::moveApplet( Plasma::Applet* applet, int a, int b) -{ - m_applets->moveApplet( applet, a, b); -} - -void -Context::VerticalToolbarContainment::wheelEvent( QGraphicsSceneWheelEvent* event ) -{ - Q_UNUSED( event ) - //eat wheel events, we don't want scrolling -} - -void -Context::VerticalToolbarContainment::showEmptyText( bool toShow ) // SLOT -{ - if( toShow ) - { - if( !m_noAppletText ) - { - m_noAppletText = new QGraphicsTextItem( this ); - m_noAppletText->setHtml( QString( " \ -

%2 " ) - .arg( The::paletteHandler()->highlightColor().name() ) - .arg( i18n( "Please add some applets from the toolbar at the bottom of the context view." ) ) ); - } - m_noAppletText->show(); - } - else if( m_noAppletText ) - { - m_noAppletText->hide(); - } - updateConstraints(); - update(); -} - diff --git a/src/context/containments/verticallayout/VerticalToolbarContainment.h b/src/context/containments/verticallayout/VerticalToolbarContainment.h deleted file mode 100644 index bd5564958e..0000000000 --- a/src/context/containments/verticallayout/VerticalToolbarContainment.h +++ /dev/null @@ -1,79 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_VERTICAL_TOOLBAR_CONTAINMENT_H -#define AMAROK_VERTICAL_TOOLBAR_CONTAINMENT_H - -#include "Applet.h" -#include "Containment.h" -#include "ContextView.h" - -class KConfigGroup; - -class QGraphicsLinearLayout; - -namespace Context -{ - -class VerticalAppletLayout; - -class VerticalToolbarContainment : public Containment -{ - Q_OBJECT - public: - VerticalToolbarContainment( QObject *parent, const QVariantList &args ); - ~VerticalToolbarContainment(); - - void constraintsEvent( Plasma::Constraints constraints ); - - QList contextualActions(); - - virtual void saveToConfig( KConfigGroup &conf ); - virtual void loadConfig( const KConfigGroup &conf ); - - virtual void setView( ContextView* view); - virtual ContextView *view(); - - public Q_SLOTS: - void addApplet( const QString& pluginName, const int ); - void appletRemoved( Plasma::Applet* ); - // these slots below are forwarded to the layout - void showApplet( Plasma::Applet* ); - void moveApplet( Plasma::Applet*, int, int ); - - protected: - virtual void wheelEvent( QGraphicsSceneWheelEvent* event ); - virtual void updateGeometry(); - - Q_SIGNALS: - void updatedContainment( Containment* ); - void appletAdded( Plasma::Applet*, int ); - - private Q_SLOTS: - void showEmptyText( bool ); - - private: - ContextView* m_view; - VerticalAppletLayout* m_applets; - QGraphicsTextItem* m_noAppletText; -}; - -K_EXPORT_PLASMA_APPLET( amarok_containment_vertical, VerticalToolbarContainment ) - -} - -#endif - diff --git a/src/context/containments/verticallayout/amarok-containment-vertical.desktop b/src/context/containments/verticallayout/amarok-containment-vertical.desktop deleted file mode 100644 index 1bc7f4b642..0000000000 --- a/src/context/containments/verticallayout/amarok-containment-vertical.desktop +++ /dev/null @@ -1,116 +0,0 @@ -[Desktop Entry] -Name=Vertical Context Containment -Name[bg]=Вертикален контекст -Name[bs]=Sadržalac za vertikalni kontekst -Name[ca]=Contenidor vertical de context -Name[ca@valencia]=Contenidor vertical de context -Name[cs]=Svislý kontejner kontextu -Name[da]=Lodret kontekstbeholder -Name[de]=Vertikaler Kontext-Container -Name[el]=Υποδοχέας κατακόρυφου περιεχομένου -Name[en_GB]=Vertical Context Containment -Name[es]=Contenedor de contexto vertical -Name[et]=Püstine kontekstikonteiner -Name[eu]=Testuinguru bertikaleko edukia -Name[fi]=Pystysuuntainen kontekstin sovelmasäiliö -Name[fr]=Conteneur vertical du navigateur de contexte -Name[ga]=Coimeádán Comhthéacs Ingearaigh -Name[gl]=Contedor vertical de contextos -Name[hu]=Függőleges környezettároló -Name[id]=Ruang Kendali Konteks Tegak -Name[is]=Lóðréttur geymslugrunnur samhengisupplýsinga -Name[it]=Contenitore verticale di contesto -Name[ja]=垂直コンテキストコンテンツ -Name[km]=ការ​ផ្ទុក​បរិបទ​បញ្ឈរ -Name[ko]=수직 컨텍스트 컨테이너 -Name[lt]=Vertikali konteksto vieta -Name[lv]=Vertikāls konteksta konteiners -Name[nb]=Loddrett kontekstbeholder -Name[nds]=Pielrecht Kontextgelaats -Name[nl]=Verticale contextcontainer -Name[nn]=Loddrett behaldar -Name[pa]=ਵਰਟੀਕਲ ਪਰਸੰਗ ਕੰਨਟੇਨਮੈਂਟ -Name[pl]=Zasobnik kontekstu pionowej -Name[pt]=Contentor de Contexto Vertical -Name[pt_BR]=Contendor vertical de contexto -Name[ro]=Container vertical pentru context -Name[ru]=Содержимое контекста по вертикали -Name[sk]=Vertikálne obmedzenie kontextu -Name[sl]=Vsebnik za navpično vsebino -Name[sr]=Садржалац за вертикални контекст -Name[sr@ijekavian]=Садржалац за вертикални контекст -Name[sr@ijekavianlatin]=Sadržalac za vertikalni kontekst -Name[sr@latin]=Sadržalac za vertikalni kontekst -Name[sv]=Vertikal sammanhangsomgivning -Name[th]=ส่วนบรรจุสำหรับคอนเท็กซ์ของแอมอะร็อก -Name[tr]=Dikey İçerik Taşıyıcı -Name[uk]=Вертикальний контейнер вмісту -Name[wa]=Contneu d' astampé do contecse -Name[x-test]=xxVertical Context Containmentxx -Name[zh_CN]=垂直环境容器 -Name[zh_TW]=垂直的內容容器 -Comment=A vertical containment for the Amarok Context -Comment[bg]=Вертикален контекст на Amarok -Comment[bs]=Vertikalni sadržalac za Amarokov kontekst -Comment[ca]=Un contenidor vertical per a l'Amarok Context -Comment[ca@valencia]=Un contenidor vertical per a l'Amarok Context -Comment[cs]=Svislý kontejner pro kontext Amaroku -Comment[da]=En lodret beholder til Amarok-kontekst -Comment[de]=Ein senkrechtes Behältnis für Amarok-Kontext -Comment[el]=Ένας κατακόρυφος υποδοχέας για το περιεχόμενο Amarok -Comment[en_GB]=A vertical containment for the Amarok Context -Comment[es]=Un contenedor vertical para el contexto de Amarok -Comment[et]=Amaroki konteksti püstine konteiner -Comment[eu]=Amarok-en testuingururako eduki bertikala -Comment[fi]=Pystysuuntainen sovelmasäiliö Amarokin kontekstille -Comment[fr]=Un conteneur vertical pour le navigateur de contexte de Amarok -Comment[ga]=Coimeádán ingearach le haghaidh Comhthéacs Amarok -Comment[gl]=Un contedor vertical para o contexto de Amarok -Comment[hu]=Függőleges tároló az Amarok-környezethez -Comment[id]=Sebuah ruang kendali tegak untuk Konteks Amarok -Comment[is]=Lóðréttur grunnur fyrir Amarok samhengisupplýsingar -Comment[it]=Un contenitore verticale per i contesti di Amarok -Comment[ja]=Amarok コンテキストの垂直コンテンツ -Comment[km]=ការ​ផ្ទុក​បញ្ឈរ​សម្រាប់​​បរិបទ Amarok -Comment[ko]=Amarok 컨텍스트를 포함하는 수직 컨테이너 -Comment[lt]=Vertikali Amaroko kontekstinės informacijos vieta -Comment[lv]=Vertikāls konteiners Amarok kontekstam -Comment[nb]=En loddrett beholder for Amarok Kontekst -Comment[nds]=En pielrecht Schell för Amarok-Kontext -Comment[nl]=Een verticale container voor de Amarok-context -Comment[nn]=Ein loddrett behaldar for Amarok-samanheng -Comment[pl]=Zasobnik dla pionowego kontekstu Amaroka -Comment[pt]=Um contentor vertical para o Contexto do Amarok -Comment[pt_BR]=Um contendor vertical para o contexto do Amarok -Comment[ro]=Un container vertical pentru contextul Amarok -Comment[ru]=Содержимое контекста Amarok по вертикали -Comment[sk]=Vertikálne obmedzenie pre kontext Amaroku -Comment[sl]=Vsebnik za navpično vsebino v Amaroku -Comment[sr]=Вертикални садржалац за Амароков контекст -Comment[sr@ijekavian]=Вертикални садржалац за Амароков контекст -Comment[sr@ijekavianlatin]=Vertikalni sadržalac za Amarokov kontekst -Comment[sr@latin]=Vertikalni sadržalac za Amarokov kontekst -Comment[sv]=En vertikal omgivning för Amaroks Sammanhang -Comment[th]=ส่วนบรรจุทางแนวตั้งสำหรับคอนเท็กซ์ของแอมอะร็อก -Comment[tr]=Amarok İçeriği için bir dikey taşıyıcı -Comment[uk]=Вертикальний контейнер для контексту Amarok -Comment[wa]=On contneu d' astampé po l' contecse d' Amarok -Comment[x-test]=xxA vertical containment for the Amarok Contextxx -Comment[zh_CN]=用于 Amarok 环境的一个垂直容器 -Comment[zh_TW]=提供給 Amarok 內容的垂直容器 - -Icon= -Type=Service -ServiceTypes=Plasma/Containment - -X-KDE-Library=amarok_containment_vertical -X-KDE-PluginInfo-Author=Leo Franchi -X-KDE-PluginInfo-Email=amarok@kde.org -X-KDE-PluginInfo-Name=amarok_containment_vertical -X-KDE-PluginInfo-Version=.1 -X-KDE-PluginInfo-Website=http://amarok.kde.org/ -X-KDE-PluginInfo-Category=Miscellaneous -X-KDE-PluginInfo-Depends= -X-KDE-PluginInfo-License=GPL -X-KDE-PluginInfo-EnabledByDefault=true -X-KDE-ParentApp=AmarokInvisible diff --git a/src/context/context_qml_package/contents/ui/main.qml b/src/context/context_qml_package/contents/ui/main.qml new file mode 100644 index 0000000000..33914988d4 --- /dev/null +++ b/src/context/context_qml_package/contents/ui/main.qml @@ -0,0 +1,116 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.2 +import QtQml.Models 2.2 +import "toolbar" + +Item { + id: root + + Component.onCompleted: Context.debug("Context created") + + ColumnLayout { + anchors.fill: parent + + ListView { + id: appletListView + + signal scrollToApplet(string id) + + Layout.alignment: Qt.AlignTop + Layout.fillHeight: true + Layout.fillWidth: true + spacing: Context.smallSpacing + displayMarginEnd: 100000 + displayMarginBeginning: 100000 + clip: true + + model: AppletProxyModel + + ScrollBar.vertical: ScrollBar { id: scrollBar } + + delegate: Loader { + width: scrollBar.visible ? parent.width - scrollBar.width : parent.width + active: true + asynchronous: true + + function initialize() { + setSource(mainscript, { + "name": name, + "appletId": appletId, + "iconSource": "image://icon/" + icon, + "collapsed": collapsed, + "contentHeight": contentHeight, + "packagePath": packagePath, + "configEnabled": Qt.binding(function() { return appletToolbar.configEnabled; } ) + }); + } + + Component.onCompleted: initialize() + + onStatusChanged: { + if (status == Loader.Error) { + Context.error("Error loading applet: " + appletId); + Context.error(sourceComponent.errorString()); + } + if (status == Loader.Ready) { + Context.debug("Applet loaded: " + appletId); + } + } + + Connections { + target: AppletProxyModel + + onDataChanged: { + if (!!mainscript && mainscript != source) { + Context.debug("Data changed for applet " + appletId); + initialize(); + } + } + } + Connections { + target: appletListView + + onScrollToApplet: { + if (id == appletId) { + appletListView.positionViewAtIndex(index, ListView.Beginning); + Context.debug("Scroll to applet: " + appletId); + } + } + } + } + } + AppletToolbarAddItem { + id: appletToolbarAddItem + + Layout.fillWidth: true + height: Context.iconSizes.enormous + visible: appletToolbar.configEnabled + } + AppletToolbar { + id: appletToolbar + + contextRoot: root + addItem: appletToolbarAddItem + listView: appletListView + Layout.alignment: Qt.AlignBottom + Layout.fillWidth: true + } + } +} diff --git a/src/context/context_qml_package/contents/ui/toolbar/AppletToolbar.qml b/src/context/context_qml_package/contents/ui/toolbar/AppletToolbar.qml new file mode 100644 index 0000000000..23f240b8b0 --- /dev/null +++ b/src/context/context_qml_package/contents/ui/toolbar/AppletToolbar.qml @@ -0,0 +1,132 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 1.4 +import QtQml.Models 2.1 +import QtQuick.Layouts 1.3 + +Rectangle { + id: root + + readonly property alias configEnabled: configureButton.checked + property var addItem + property var listView + property var contextRoot + + function resizeApplets() { + var items = []; + var children = toolbarAppletRow.contentItem.visibleChildren; + for (var i=0; i 0) { + var space = toolbarAppletRow.width - (items.length - 1) * toolbarAppletRow.spacing; + var threshhold = space / items.length; + var smallApplets = []; + var largeApplets = []; + for (var i=0; i 0 && space > 0) { + var countSmallApplets = smallApplets.length; + smallApplets.forEach(function (applet, index) { + if (applet.implicitWidth >= threshhold) { + largeApplets.push(applet); + applet.width = applet.implicitWidth; + space -= applet.width; + } + }); + smallApplets = smallApplets.filter(function (applet) { return largeApplets.indexOf(applet) == -1 }); + if (countSmallApplets == smallApplets.length) { + smallApplets.forEach(function (applet, index) { + applet.width = space / countSmallApplets; + }); + return; + } + threshhold = space / smallApplets.length; + } + if (smallApplets.length == 0) return; + for (var i=0; i * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 + + +ScrollView { + id: root + + verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff + frameVisible: true + + ListView { + id: listView + + anchors.margins: Context.smallSpacing + orientation: ListView.Horizontal + spacing: Context.smallSpacing + + model: AppletModel + + delegate: Rectangle { + readonly property bool appletEnabled: AppletProxyModel.enabledApplets.indexOf(appletId) != -1 + + height: root.height - 3 * Context.smallSpacing + width: height + radius: Context.smallSpacing + color: delegateMouseArea.pressed ? palette.highlight : appletEnabled ? palette.highlight : "transparent" + border.color: delegateMouseArea.containsMouse ? palette.highlight : "transparent" + border.width: 2 + + ColumnLayout { + anchors.fill: parent + + Image { + source: "image://icon/" + icon + Layout.fillHeight: true + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + Layout.margins: Context.smallSpacing + sourceSize.width: width + sourceSize.height: height + } + Label { + Layout.alignment: Qt.AlignBottom + Layout.margins: Context.smallSpacing + Layout.fillWidth: true + text: name + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + } + } + + MouseArea { + id: delegateMouseArea + + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton + + onClicked: AppletProxyModel.setAppletEnabled(appletId, !appletEnabled); + } + } + + SystemPalette { + id: palette + } + } +} diff --git a/src/context/context_qml_package/contents/ui/toolbar/AppletToolbarAppletItem.qml b/src/context/context_qml_package/contents/ui/toolbar/AppletToolbarAppletItem.qml new file mode 100644 index 0000000000..7edad13ba6 --- /dev/null +++ b/src/context/context_qml_package/contents/ui/toolbar/AppletToolbarAppletItem.qml @@ -0,0 +1,114 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 + + +MouseArea { + id: root + + property alias name: label.text + property string appletId + property var toolbar + property var listView + property bool configEnabled: !!toolbar ? toolbar.configEnabled : false + property bool held: false + + height: held ? toolbar.height : toolbar.height - Context.smallSpacing + anchors.verticalCenter: parent.verticalCenter + implicitWidth: label.implicitWidth + Context.smallSpacing * 2 + + acceptedButtons: Qt.LeftButton + hoverEnabled: true + drag.target: held ? content : undefined + drag.axis: Drag.XAxis + cursorShape: configEnabled ? held ? Qt.ClosedHandCursor : Qt.PointingHandCursor : Qt.ArrowCursor + + onPressAndHold: if (configEnabled) held = true + onReleased: held = false + onPressed: if (!configEnabled) listView.scrollToApplet(appletId) + onImplicitWidthChanged: toolbar.resizeApplets() + + DropArea { + anchors { + fill: parent + leftMargin: Context.smallSpacing + rightMargin: Context.smallSpacing + } + + onEntered: { + AppletProxyModel.setAppletPlace(drag.source.appletId, AppletProxyModel.appletPlace(root.appletId)); + } + } + + Rectangle { + id: content + + readonly property string appletId: root.appletId + + border.width: 1 + color: root.pressed ? palette.highlight : palette.button + border.color: root.containsMouse ? palette.highlight : palette.buttonText + radius: Context.smallSpacing / 2 + anchors { + horizontalCenter: root.horizontalCenter + verticalCenter: root.verticalCenter + } + width: root.width + height: root.height + + Drag.active: root.held + Drag.source: root + Drag.hotSpot.x: width / 2 + Drag.hotSpot.y: height / 2 + + states: State { + when: root.held + + ParentChange { + target: content + parent: contextRoot + } + AnchorChanges { + target: content + anchors { + horizontalCenter: undefined + verticalCenter: undefined + } + } + } + + Label { + id: label + + anchors { + fill: parent + leftMargin: Context.smallSpacing + rightMargin: Context.smallSpacing + } + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + clip: true + } + } + + SystemPalette { + id: palette + } +} diff --git a/src/context/context_qml_package/metadata.desktop b/src/context/context_qml_package/metadata.desktop new file mode 100644 index 0000000000..c66d006e7a --- /dev/null +++ b/src/context/context_qml_package/metadata.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Name=Amarok-Context-QML-Package +Comment=QML base package for amarok context area +Encoding=UTF-8 +Type=Service +Icon=amarok +X-KDE-PluginInfo-Author=Malte Veerman +X-KDE-PluginInfo-Email=malte.veerman@gmail.com +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-Name=org.kde.amarok.context +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Website= +X-KDE-ServiceTypes=KPackage/Generic diff --git a/src/context/engines/CMakeLists.txt b/src/context/engines/CMakeLists.txt index dc3b33f9b2..d8367905e3 100644 --- a/src/context/engines/CMakeLists.txt +++ b/src/context/engines/CMakeLists.txt @@ -1,12 +1,7 @@ -add_subdirectory( current ) -add_subdirectory( info ) -add_subdirectory( labels ) -add_subdirectory( lyrics ) -add_subdirectory( photos ) -add_subdirectory( tabs ) -add_subdirectory( wikipedia ) +#add_subdirectory( labels ) +#add_subdirectory( tabs ) if(LIBLASTFM_FOUND) - add_subdirectory( similarartists ) - add_subdirectory( upcomingevents ) +# add_subdirectory( similarartists ) +# add_subdirectory( upcomingevents ) endif() diff --git a/src/context/engines/current/CMakeLists.txt b/src/context/engines/current/CMakeLists.txt deleted file mode 100644 index c9fde512b3..0000000000 --- a/src/context/engines/current/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -include_directories( ../../.. - ../../../context - ${CMAKE_CURRENT_BINARY_DIR}/../../.. # for amarok_config.h -) - -set( current_engine_SRCS - CurrentEngine.cpp -) - -add_library(amarok_data_engine_current MODULE ${current_engine_SRCS}) -target_link_libraries( amarok_data_engine_current amarokcore amaroklib KF5::Plasma KF5::KDELibs4Support Qt5::Gui ) - -install( TARGETS amarok_data_engine_current DESTINATION ${PLUGIN_INSTALL_DIR} ) -install( FILES amarok-data-engine-current.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) diff --git a/src/context/engines/current/CurrentEngine.cpp b/src/context/engines/current/CurrentEngine.cpp deleted file mode 100644 index 1e3cf9d0ec..0000000000 --- a/src/context/engines/current/CurrentEngine.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "CurrentEngine" - -#include "CurrentEngine.h" - -#include "EngineController.h" -#include "context/ContextView.h" -#include "core/support/Debug.h" -#include "core/capabilities/SourceInfoCapability.h" -#include "core/collections/Collection.h" -#include "core/collections/QueryMaker.h" -#include "core/meta/Meta.h" -#include "core/meta/support/MetaUtility.h" -#include "core/support/Amarok.h" -#include "core-impl/collections/support/CollectionManager.h" -#include "covermanager/CoverCache.h" - -#include - -#include -#include -#include -#include -#include //Needed for the slot - -using namespace Context; - -CurrentEngine::CurrentEngine( QObject* parent, const QList& args ) - : DataEngine( parent ) - , m_coverWidth( 0 ) - , m_coverCacheKey( 0 ) - , m_lastQueryMaker( 0 ) -{ - Q_UNUSED( args ) - - m_sources << QLatin1String("current") << QLatin1String("albums"); - m_requested[ QLatin1String("current") ] = false; - m_requested[ QLatin1String("albums") ] = false; - EngineController* engine = The::engineController(); - - connect( engine, SIGNAL(trackPlaying(Meta::TrackPtr)), - this, SLOT(trackPlaying(Meta::TrackPtr)) ); - connect( engine, SIGNAL(stopped(qint64,qint64)), - this, SLOT(stopped()) ); - - connect( engine, SIGNAL(trackMetadataChanged(Meta::TrackPtr)), - this, SLOT(metadataChanged(Meta::TrackPtr)) ); - connect( engine, SIGNAL(albumMetadataChanged(Meta::AlbumPtr)), - this, SLOT(metadataChanged(Meta::AlbumPtr)) ); -} - -CurrentEngine::~CurrentEngine() -{ -} - -QStringList -CurrentEngine::sources() const -{ - return m_sources; // we don't have sources, if connected, it is enabled. -} - -bool -CurrentEngine::sourceRequestEvent( const QString& name ) -{ - Meta::TrackPtr track = The::engineController()->currentTrack(); - m_requested[ name ] = true; - if( !track ) - stopped(); - - if( name == QLatin1String("current") ) - update( track ); - else if( name == QLatin1String("albums") ) - track ? update(track->album()) : setData(name, Plasma::DataEngine::Data()); - else - return false; - - return true; -} - -void -CurrentEngine::metadataChanged( Meta::AlbumPtr album ) -{ - // disregard changes for other albums (BR: 306735) - if( !m_currentTrack || m_currentTrack->album() != album ) - return; - - QImage cover = album->image( m_coverWidth ); - qint64 coverCacheKey = cover.cacheKey(); - if( m_coverCacheKey != coverCacheKey ) - { - m_coverCacheKey = coverCacheKey; - setData( "current", "albumart", cover ); - } -} - -void -CurrentEngine::metadataChanged( Meta::TrackPtr track ) -{ - DEBUG_BLOCK - - QVariantMap trackInfo = Meta::Field::mapFromTrack( track ); - if( m_trackInfo != trackInfo ) - { - m_trackInfo = trackInfo; - setData( "current", "current", trackInfo ); - if( track && m_requested.value( QLatin1String("albums") ) ) - update( track->album() ); - } -} - -void -CurrentEngine::trackPlaying( Meta::TrackPtr track ) -{ - DEBUG_BLOCK - m_lastQueryMaker = 0; - if( m_requested.value( QLatin1String("current") ) ) - update( track ); - if( track && m_requested.value( QLatin1String("albums") ) ) - update( track->album() ); -} - -void -CurrentEngine::stopped() -{ - if( m_requested.value( QLatin1String("current") ) ) - { - removeAllData( "current" ); - setData( "current", "notrack", i18n( "No track playing") ); - m_currentTrack.clear(); - } - - if( m_requested.value( QLatin1String("albums") ) ) - { - removeAllData( "albums" ); - m_albumData.clear(); - - // Collect data for the recently added albums - setData( "albums", "headerText", QVariant( i18n( "Recently Added Albums" ) ) ); - m_albums.clear(); - - Collections::QueryMaker *qm = CollectionManager::instance()->queryMaker(); - qm->setAutoDelete( true ); - qm->setQueryType( Collections::QueryMaker::Album ); - qm->excludeFilter( Meta::valAlbum, QString(), true, true ); - qm->orderBy( Meta::valCreateDate, true ); - qm->limitMaxResultSize( Amarok::config("Albums Applet").readEntry("RecentlyAdded", 5) ); - - connect( qm, SIGNAL(newResultReady(Meta::AlbumList)), - SLOT(resultReady(Meta::AlbumList)), Qt::QueuedConnection ); - connect( qm, SIGNAL(queryDone()), SLOT(setupAlbumsData()) ); - - m_lastQueryMaker = qm; - qm->run(); - } -} - -void -CurrentEngine::update( Meta::TrackPtr track ) -{ - if( !m_requested.value( QLatin1String("current") ) || - track == m_currentTrack ) - return; - - m_currentTrack = track; - removeAllData( QLatin1String("current") ); - - if( !track ) - return; - - Plasma::DataEngine::Data data; - QVariantMap trackInfo = Meta::Field::mapFromTrack( track ); - data["current"] = trackInfo; - Meta::AlbumPtr album = track->album(); - data["albumart"] = QVariant( album ? The::coverCache()->getCover( album, m_coverWidth) : QPixmap() ); - - Capabilities::SourceInfoCapability *sic = track->create(); - if( sic ) - { - //is the source defined - const QString source = sic->sourceName(); - debug() <<" We have source " <scalableEmblem(); - - delete sic; - } - else - data["source_emblem"] = QVariant( QPixmap() ); - - debug() << "updating track" << track->name(); - setData( "current", data ); -} - -void -CurrentEngine::update( Meta::AlbumPtr album ) -{ - if( !m_requested.value( QLatin1String("albums") ) ) - return; - - m_lastQueryMaker = 0; - Meta::TrackPtr track = The::engineController()->currentTrack(); - - if( !album ) - return; - - Meta::ArtistPtr artist = track->artist(); - - // Prefer track artist to album artist BUG: 266682 - if( !artist ) - artist = album->albumArtist(); - - if( artist && !artist->name().isEmpty() ) - { - m_albums.clear(); - m_albumData.clear(); - m_albumData[ QLatin1String("currentTrack") ] = qVariantFromValue( track ); - m_albumData[ QLatin1String("headerText") ] = QVariant( i18n( "Albums by %1", artist->name() ) ); - - // -- search the collection for albums with the same artist - Collections::QueryMaker *qm = CollectionManager::instance()->queryMaker(); - qm->setAutoDelete( true ); - qm->addFilter( Meta::valArtist, artist->name(), true, true ); - qm->setAlbumQueryMode( Collections::QueryMaker::AllAlbums ); - qm->setQueryType( Collections::QueryMaker::Album ); - - connect( qm, SIGNAL(newResultReady(Meta::AlbumList)), - SLOT(resultReady(Meta::AlbumList)), Qt::QueuedConnection ); - connect( qm, SIGNAL(queryDone()), SLOT(setupAlbumsData()) ); - - m_lastQueryMaker = qm; - qm->run(); - } - else - { - removeAllData( QLatin1String("albums") ); - setData( QLatin1String("albums"), QLatin1String("headerText"), - i18nc( "Header text for current album applet", "Albums" ) ); - } -} - -void -CurrentEngine::setupAlbumsData() -{ - if( sender() == m_lastQueryMaker ) - { - m_albumData[ QLatin1String("albums") ] = QVariant::fromValue( m_albums ); - setData( QLatin1String("albums"), m_albumData ); - } -} - -void -CurrentEngine::resultReady( const Meta::AlbumList &albums ) -{ - if( sender() == m_lastQueryMaker ) - m_albums << albums; -} - diff --git a/src/context/engines/current/amarok-data-engine-current.desktop b/src/context/engines/current/amarok-data-engine-current.desktop deleted file mode 100644 index db58906c54..0000000000 --- a/src/context/engines/current/amarok-data-engine-current.desktop +++ /dev/null @@ -1,62 +0,0 @@ -[Desktop Entry] -Name=Current Info Data Engine -Name[bg]=Ядро за дата и време -Name[bs]=Pogon tekućih podataka -Name[ca]=Motor de dades d'informació actual -Name[ca@valencia]=Motor de dades d'informació actual -Name[cs]=Datový nástroj Aktuální informace -Name[csb]=Mòtór aktualny wëdowiedzë ò pòdôwkach -Name[da]=Datamotor til Aktuel info -Name[de]=Datenmodul für Informationen über das aktuelle Stück -Name[el]=Μηχανή δεδομένων τρεχουσών πληροφοριών -Name[en_GB]=Current Info Data Engine -Name[eo]=Datuma motoro de la aktuala informo -Name[es]=Información del motor de datos actual -Name[et]=Aktiivse info andmemootor -Name[eu]=Uneko informazioaren datuen motorra -Name[fi]=Nykyisen infon tietomoottori -Name[fr]=Moteur de données pour les informations courantes -Name[ga]=Inneall Sonraí - Eolas Reatha -Name[gl]=Motor actual de datos de información -Name[hne]=अभी हाल के डाटा इंजिन जानकारी -Name[hu]=Aktuális információk adatmotorja -Name[id]=Mesin Info Data Saat Ini -Name[is]=Núverandi gagnavél fyrir spilun -Name[it]=Motore dati informazione attuale -Name[ja]=現在の情報データエンジン -Name[km]=ម៉ាស៊ីន​ទិន្នន័យ​ព័ត៌មាន​បច្ចុប្បន្ន -Name[ko]=현재 정보 데이터 엔진 -Name[ku]=Motora Dane ya Agahiyên Hene -Name[lt]=Dabartinės informacijos duomenų sistema -Name[lv]=Pašreizējās informācijas datu dzinējs -Name[nb]=Gjeldende Informasjonsdatamotor -Name[nds]=Datenkarn för aktuell Daten -Name[nl]=Informatie-gegevensengine -Name[nn]=Datamotor for gjeldande spor -Name[pa]=ਮੌਜੂਦਾ ਜਾਣਕਾਰੀ ਡਾਟਾ ਇੰਜਣ -Name[pl]=Moduł danych bieżących informacji -Name[pt]=Motor de Dados da Informação Actual -Name[pt_BR]=Mecanismo de dados das informações atuais -Name[ro]=Motor de date Informație curentă -Name[ru]=Источник данных для текущих сведений -Name[sk]=Dátový nástroj Aktuálne informácie -Name[sl]=Podatkovni pogon za podatke trenutni skladbi -Name[sr]=Датомотор текућих података -Name[sr@ijekavian]=Датомотор текућих података -Name[sr@ijekavianlatin]=Datomotor tekućih podataka -Name[sr@latin]=Datomotor tekućih podataka -Name[sv]=Datagränssnitt för aktuell information -Name[th]=กลไกข้อมูลของข้อมูลรายละเอียดปัจจุบัน -Name[tr]=Kullanılan Bilgi Veri Motoru -Name[uk]=Поточний рушій інформаційних даних -Name[wa]=Éndjin d' dinêyes des informåcions do moumint -Name[x-test]=xxCurrent Info Data Enginexx -Name[zh_CN]=当前信息数据引擎 -Name[zh_TW]=目前訊息的資料引擎 -X-KDE-ServiceTypes=Plasma/DataEngine -Type=Service -Icon=amarok -X-KDE-Library=amarok_data_engine_current -X-KDE-PluginInfo-Name=amarok-current -X-KDE-ParentApp=Amarok - diff --git a/src/context/engines/info/CMakeLists.txt b/src/context/engines/info/CMakeLists.txt deleted file mode 100644 index 1fb60af3ab..0000000000 --- a/src/context/engines/info/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -include_directories( ../../.. - ../../../context - ${CMAKE_CURRENT_BINARY_DIR}/../../.. # for amarok_config.h -) - -set( info_engine_SRCS - InfoEngine.cpp -) - -add_library(amarok_data_engine_info MODULE ${info_engine_SRCS}) -target_link_libraries( amarok_data_engine_info amarokcore amaroklib KF5::Plasma KF5::KDELibs4Support Qt5::Gui) - -install( TARGETS amarok_data_engine_info DESTINATION ${PLUGIN_INSTALL_DIR} ) -install( FILES amarok-data-engine-info.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) -install( FILES info_frontpage.html DESTINATION ${DATA_INSTALL_DIR}/amarok/data ) -install( FILES - info_frontpage_bg.png - info_frontpage_logo.png - info_frontpage_shadow.png - DESTINATION ${DATA_INSTALL_DIR}/amarok/images - ) diff --git a/src/context/engines/info/amarok-data-engine-info.desktop b/src/context/engines/info/amarok-data-engine-info.desktop deleted file mode 100644 index 641e0a1e29..0000000000 --- a/src/context/engines/info/amarok-data-engine-info.desktop +++ /dev/null @@ -1,59 +0,0 @@ -[Desktop Entry] -Name=Info Data Engine -Name[bg]=Ядро за данни -Name[bs]=Pogon podataka -Name[ca]=Motor de dades d'informació -Name[ca@valencia]=Motor de dades d'informació -Name[cs]=Datový nástroj informací -Name[csb]=Mòtór wëdowiedzë ò pòdôwkach -Name[da]=Datamotor til info -Name[de]=Datenmodul für Informationen -Name[el]=Μηχανή δεδομένων πληροφοριών -Name[en_GB]=Info Data Engine -Name[es]=Información del motor de datos -Name[et]=Teabe andmemootor -Name[eu]=informazioaren datuen motorra -Name[fi]=Infon tietomoottori -Name[fr]=Moteur de données pour les informations -Name[ga]=Inneall Sonraí Eolais -Name[gl]=Motor de datos de información -Name[hu]=Információs adatmotor -Name[id]=Mesin Info Data -Name[is]=Gagnavél fyrir spilun -Name[it]=Motore dati informazione -Name[ja]=情報データエンジン -Name[km]=ម៉ាស៊ីន​ទិន្នន័យ​ព័ត៌មាន​ -Name[ko]=정보 데이터 엔진 -Name[lt]=Informacijos duomenų sistema -Name[lv]=Informācijas datu dzinējs -Name[nb]=Informasjonsdatamotor -Name[nds]=Info-Datenkarn -Name[nl]=Informatie-gegevensengine -Name[nn]=Datamotor for informasjon -Name[pa]=ਜਾਣਕਾਰੀ ਡਾਟਾ ਇੰਜਣ -Name[pl]=Moduł danych informacji -Name[pt]=Motor de Dados da Informação -Name[pt_BR]=Mecanismo de dados das informações -Name[ro]=Motor de date Informații -Name[ru]=Источник данных для сведений -Name[sk]=Dátový nástroj Informácie -Name[sl]=Podatkovni pogon za podatke -Name[sr]=Датомотор података -Name[sr@ijekavian]=Датомотор података -Name[sr@ijekavianlatin]=Datomotor podataka -Name[sr@latin]=Datomotor podataka -Name[sv]=Datagränssnitt för information -Name[th]=กลไกข้อมูลของข้อมูล -Name[tr]=Bilgi Veri Motoru -Name[uk]=Рушій інформаційних даних -Name[x-test]=xxInfo Data Enginexx -Name[zh_CN]=信息数据引擎 -Name[zh_TW]=資訊資料引擎 - -X-KDE-ServiceTypes=Plasma/DataEngine -Type=Service -Icon=amarok -X-KDE-Library=amarok_data_engine_info -X-KDE-PluginInfo-Name=amarok-info -X-KDE-ParentApp=Amarok - diff --git a/src/context/engines/labels/LabelsEngine.cpp b/src/context/engines/labels/LabelsEngine.cpp index 447051c540..3764c33cf5 100644 --- a/src/context/engines/labels/LabelsEngine.cpp +++ b/src/context/engines/labels/LabelsEngine.cpp @@ -1,373 +1,373 @@ /**************************************************************************************** * Copyright (c) 2009 Simon Esneault * * Copyright (c) 2010 Daniel Faust * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "LabelsEngine" #include "LabelsEngine.h" #include "EngineController.h" #include "context/ContextObserver.h" #include "context/ContextView.h" #include "core/collections/Collection.h" #include "core/collections/QueryMaker.h" #include "core/meta/Meta.h" #include "core/support/Debug.h" #include "core-impl/collections/support/CollectionManager.h" #include -#include +#include #include using namespace Context; LabelsEngine::LabelsEngine( QObject *parent, const QList &args ) : DataEngine( parent ) , ContextObserver( ContextView::self() ) { Q_UNUSED( args ) m_sources << "lastfm" ; m_timeoutTimer.setInterval( 10000 ); m_timeoutTimer.setSingleShot( true ); connect( &m_timeoutTimer, SIGNAL(timeout()), this, SLOT(timeout()) ); EngineController *engine = The::engineController(); connect( engine, SIGNAL(trackChanged(Meta::TrackPtr)), this, SLOT(update()) ); connect( engine, SIGNAL(trackMetadataChanged(Meta::TrackPtr)), this, SLOT(update()) ); } LabelsEngine::~LabelsEngine() { DEBUG_BLOCK } QStringList LabelsEngine::sources() const { return m_sources; } bool LabelsEngine::sourceRequestEvent( const QString &name ) { DEBUG_BLOCK Collections::Collection *coll = CollectionManager::instance()->primaryCollection(); if( coll ) { Collections::QueryMaker *qm = coll->queryMaker(); qm->setAutoDelete( true ); qm->setQueryType( Collections::QueryMaker::Label ); m_allLabels.clear(); connect( qm, SIGNAL(newResultReady(Meta::LabelList)), SLOT(resultReady(Meta::LabelList)), Qt::QueuedConnection ); connect( qm, SIGNAL(queryDone()), SLOT(dataQueryDone()) ); qm->run(); } update( name == "reload" ); return true; } void LabelsEngine::resultReady( const Meta::LabelList &labels ) { foreach( const Meta::LabelPtr &label, labels ) { if( !label->name().isEmpty() ) m_allLabels << label->name(); } } void LabelsEngine::dataQueryDone() { DEBUG_BLOCK QVariant varAll; varAll.setValue< QStringList >( m_allLabels ); setData( "labels", "all", varAll ); } void LabelsEngine::update( bool reload ) { Meta::TrackPtr track = The::engineController()->currentTrack(); if( !track ) { removeAllData( "labels" ); m_artist.clear(); m_title.clear(); m_album.clear(); m_userLabels.clear(); m_webLabels.clear(); setData( "labels", "state", "stopped" ); return; } const QString title = track->name(); Meta::ArtistPtr artist = track->artist(); if( !artist ) { setData( "labels", "message", i18n( "No labels found on Last.fm" ) ); debug() << "track has no artist, returning"; return; } QStringList userLabels; foreach( const Meta::LabelPtr &label, track->labels() ) userLabels += label->name(); userLabels.sort(); m_userLabels.sort(); // check what changed if( !reload && artist->name() == m_artist && title == m_title && userLabels == m_userLabels ) { // nothing important changed return; } else if( !reload && artist->name() == m_artist && title == m_title ) { // only the labels changed - no download necessary debug() << "only the labels changed - no download necessary"; QVariant varUser; varUser.setValue< QStringList >( userLabels ); setData( "labels", "user", varUser ); m_userLabels = userLabels; return; } removeAllData( "labels" ); setData( "labels", "state", "started" ); m_artist = artist->name(); m_title = title; if( track->album() ) m_album = track->album()->name(); else m_album.clear(); m_userLabels = userLabels; m_webLabels.clear(); QVariant varUser; varUser.setValue< QStringList >( m_userLabels ); setData( "labels", "user", varUser ); m_try = 0; fetchLastFm(); } void LabelsEngine::fetchLastFm() { QStringList separators; QString currentArtist; QString currentTitle; if( m_title.isEmpty() || m_artist.isEmpty() ) { // stop timeout timer m_timeoutTimer.stop(); setData( "labels", "message", i18n( "No labels found on Last.fm" ) ); debug() << "current track is invalid, returning"; return; } if( m_try == 0 ) { currentArtist = m_artist; currentTitle = m_title; m_timeoutTimer.start(); } else if( m_try == 1 ) { currentArtist = m_artist; currentTitle = m_title; separators.clear(); separators << " (" << " [" << " - " << " featuring " << " feat. " << " feat " << " ft. " << " ft " << "/"; foreach( const QString &separator, separators ) { if( m_title.contains(separator,Qt::CaseInsensitive) ) { currentTitle = m_title.left( m_title.indexOf(separator,0,Qt::CaseInsensitive) ); break; } } if ( currentTitle == m_title ) { debug() << "try 2: title is the same, retrying"; m_try++; fetchLastFm(); return; } } else if( m_try == 2 ) { currentArtist = m_artist; currentTitle = m_title; separators.clear(); separators << " vs. " << " vs " << " featuring " << " feat. " << " feat " << " ft. " << " ft " << ", " << " and " << " & " << "/"; foreach( const QString &separator, separators ) { if( m_artist.contains(separator,Qt::CaseInsensitive) ) { currentArtist = m_artist.left( m_artist.indexOf(separator,0,Qt::CaseInsensitive) ); break; } } separators.clear(); separators << " (" << " [" << " - " << " featuring " << " feat. " << " feat " << " ft. " << " ft " << "/"; foreach( const QString &separator, separators ) { if( m_title.contains(separator,Qt::CaseInsensitive) ) { currentTitle = m_title.left( m_title.indexOf(separator,0,Qt::CaseInsensitive) ); break; } } if( currentArtist == m_artist ) // the title got modified the same way as on the last try { // stop timeout timer m_timeoutTimer.stop(); setData( "labels", "message", i18n( "No labels found on Last.fm" ) ); debug() << "try 3: artist and title are the same, returning"; return; } } else { // shouldn't happen // stop timeout timer m_timeoutTimer.stop(); setData( "labels", "message", i18n( "No labels found on Last.fm" ) ); debug() << "try > 2, returning"; return; } if( !currentArtist.isEmpty() && !currentTitle.isEmpty() ) { setData( "labels", "message", "fetching"); // send the atist and title actually used for searching labels setData( "labels", "artist", currentArtist ); setData( "labels", "title", currentTitle ); setData( "labels", "album", m_album ); // Query lastfm QUrl lastFmUrl; lastFmUrl.setScheme( "http" ); lastFmUrl.setHost( "ws.audioscrobbler.com" ); lastFmUrl.setPath( "/2.0/" ); lastFmUrl.addQueryItem( "method", "track.gettoptags" ); lastFmUrl.addQueryItem( "api_key", "402d3ca8e9bc9d3cf9b85e1202944ca5" ); lastFmUrl.addQueryItem( "artist", currentArtist.toLocal8Bit() ); lastFmUrl.addQueryItem( "track", currentTitle.toLocal8Bit() ); m_lastFmUrl = lastFmUrl; QNetworkRequest req( lastFmUrl ); // req.setAttribute( QNetworkRequest::ConnectionEncryptedAttribute, QNetworkRequest::AlwaysNetwork ); The::networkAccessManager()->get( req ); The::networkAccessManager()->getData( lastFmUrl, this, SLOT(resultLastFm(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); } else { // stop timeout timer m_timeoutTimer.stop(); setData( "labels", "message", i18n( "No labels found on Last.fm" ) ); debug() << "artist or track empty"; } } void LabelsEngine::resultLastFm( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ) { DEBUG_BLOCK; if( m_lastFmUrl != url ) { debug() << "urls not matching, returning"; return; } if( e.code != QNetworkReply::NoError ) { // stop timeout timer m_timeoutTimer.stop(); setData( "labels", "message", i18n( "Unable to retrieve from Last.fm" ) ); debug() << "Unable to retrieve last.fm information: " << e.description; return; } QDomDocument xmlDoc; xmlDoc.setContent( data ); const QDomElement topElement = xmlDoc.elementsByTagName("toptags").at(0).toElement(); const QDomNodeList xmlNodeList = topElement.elementsByTagName( "tag" ); for( uint i = 0; i < xmlNodeList.length(); i++ ) { // Get all the information const QDomElement nd = xmlNodeList.at( i ).toElement(); const QDomElement nameElement = nd.elementsByTagName("name").at(0).toElement(); const QString name = nameElement.text().toLower(); const QDomElement countElement = nd.elementsByTagName("count").at(0).toElement(); const int count = countElement.text().toInt(); m_webLabels.insert( name, count ); } if( m_webLabels.isEmpty() ) { if( m_try < 2 ) { m_try++; fetchLastFm(); } else { // stop timeout timer m_timeoutTimer.stop(); setData( "labels", "message", i18n( "No labels found on Last.fm" ) ); } } else { // remove previous message removeData( "labels", "message" ); // stop timeout timer m_timeoutTimer.stop(); QVariant varWeb; varWeb.setValue< QMap< QString, QVariant > > ( m_webLabels ); setData( "labels", "web", varWeb ); } } void LabelsEngine::timeout() { setData( "labels", "message", i18n( "No connection to Last.fm" ) ); } diff --git a/src/context/engines/labels/LabelsEngine.h b/src/context/engines/labels/LabelsEngine.h index e7cdb71577..783d856efa 100644 --- a/src/context/engines/labels/LabelsEngine.h +++ b/src/context/engines/labels/LabelsEngine.h @@ -1,98 +1,98 @@ /**************************************************************************************** * Copyright (c) 2009 Simon Esneault * * Copyright (c) 2010 Daniel Faust * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef AMAROK_LABELS_ENGINE #define AMAROK_LABELS_ENGINE #include "ContextObserver.h" #include "context/DataEngine.h" #include "core/meta/forward_declarations.h" #include "network/NetworkAccessManagerProxy.h" #include #include -#include +#include using namespace Context; /** * This class provide labels from last.fm * */ class LabelsEngine : public DataEngine, public ContextObserver { Q_OBJECT public: LabelsEngine( QObject *parent, const QList &args ); virtual ~LabelsEngine(); QStringList sources() const; protected: // reimplemented from Plasma::DataEngine bool sourceRequestEvent( const QString &name ); private Q_SLOTS: void update( bool reload = false ); /** * This slots will handle last.fm result for this query: * API key is : 402d3ca8e9bc9d3cf9b85e1202944ca5 * http://ws.audioscrobbler.com/2.0/?method=track.gettoptags&artist=radiohead&track=paranoid+android&api_key=b25b959554ed76058ac220b7b2e0a026 * see here for details: http://www.lastfm.com/api/ */ void resultLastFm( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ); void resultReady( const Meta::LabelList &labels ); void dataQueryDone(); void timeout(); private: /** * Engine was updated, so we check if the songs is different, and if it is, we delete every and start * all the query/ fetching stuff */ void fetchLastFm(); void updateLocal(); QTimer m_timeoutTimer; /// The URL for the network request QUrl m_lastFmUrl; QStringList m_sources; // Cache the artist and title of the current track so we can check against metadata // updates. We only want to update the labels if the artist change QString m_artist; QString m_title; // Send the album name to the applet, used to filter labels that match the album QString m_album; int m_try; QStringList m_allLabels; // all labels known to amarok QStringList m_userLabels; // user labels QMap < QString, QVariant > m_webLabels; // downloaded labels }; AMAROK_EXPORT_DATAENGINE( labels, LabelsEngine ) #endif diff --git a/src/context/engines/lyrics/CMakeLists.txt b/src/context/engines/lyrics/CMakeLists.txt deleted file mode 100644 index 0cd0764aef..0000000000 --- a/src/context/engines/lyrics/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -include_directories( ../../.. - ../../../context - ../../../dialogs - ${CMAKE_CURRENT_BINARY_DIR}/../../.. # for amarok_config.h -) - -set( lyrics_engine_SRCS - LyricsEngine.cpp -) - -add_library(amarok_data_engine_lyrics MODULE ${lyrics_engine_SRCS}) -target_link_libraries( amarok_data_engine_lyrics amarokcore amaroklib KF5::Plasma KF5::KDELibs4Support Qt5::Gui) - -install( TARGETS amarok_data_engine_lyrics DESTINATION ${PLUGIN_INSTALL_DIR} ) -install( FILES amarok-data-engine-lyrics.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) diff --git a/src/context/engines/lyrics/amarok-data-engine-lyrics.desktop b/src/context/engines/lyrics/amarok-data-engine-lyrics.desktop deleted file mode 100644 index 2063e7e219..0000000000 --- a/src/context/engines/lyrics/amarok-data-engine-lyrics.desktop +++ /dev/null @@ -1,62 +0,0 @@ -[Desktop Entry] -Name=Lyrics Data Engine -Name[bg]=Ядро за текстове -Name[bs]=Pogon podatka stihova -Name[ca]=Motor de dades de lletres -Name[ca@valencia]=Motor de dades de lletres -Name[cs]=Datový nástroj textů písní -Name[csb]=Mòtór tekstów -Name[da]=Datamotor til sangtekst -Name[de]=Datenmodul für Liedtexte -Name[el]=Μηχανή δεδομένων στίχων -Name[en_GB]=Lyrics Data Engine -Name[eo]=Datuma motoro de la parolaro -Name[es]=Motor de datos para letras -Name[et]=Sõnade andmemootor -Name[eu]=Hitzen datuen motorra -Name[fi]=Sanojen tietomoottori -Name[fr]=Moteur de données pour les paroles -Name[ga]=Inneall Sonraí Liricí -Name[gl]=Motor de datos de letras de cancións -Name[hne]=गीत डाटा इंजिन -Name[hu]=Dalszöveg-adatmotor -Name[id]=Mesin Data Lirik -Name[is]=Lagatextagagnavél -Name[it]=Motore dati dei testi -Name[ja]=歌詞データエンジン -Name[km]=ម៉ាស៊ីន​ទិន្នន័យ​ទំនុក -Name[ko]=가사 데이터 엔진 -Name[ku]=Motora Dane ya Lîrîk -Name[lt]=Dainos tekstų duomenų sistema -Name[lv]=Dziesmu vārdu datu dzinējs -Name[nb]=Datamotor for sangtekster -Name[nds]=Text-Datenkarn -Name[nl]=Liedteksten-gegevensengine -Name[nn]=Datamotor for songtekstar -Name[pa]=ਬੋਲ ਡਾਟਾ ਇੰਜਣ -Name[pl]=Moduł danych tekstów -Name[pt]=Motor de Dados de Letras Musicais -Name[pt_BR]=Mecanismo de dados das letras -Name[ro]=Motor de date Versuri -Name[ru]=Источник данных для текстов песен -Name[sk]=Dátový nástroj Texty piesní -Name[sl]=Podatkovni pogon za besedila -Name[sr]=Датомотор стихова -Name[sr@ijekavian]=Датомотор стихова -Name[sr@ijekavianlatin]=Datomotor stihova -Name[sr@latin]=Datomotor stihova -Name[sv]=Datagränssnitt för sångtexter -Name[th]=กลไกข้อมูลของเนื้อร้อง -Name[tr]=Şarkı Sözü Veri Motoru -Name[uk]=Рушій даних слів пісень -Name[wa]=Éndjin d' dinêyes des paroles -Name[x-test]=xxLyrics Data Enginexx -Name[zh_CN]=歌词数据引擎 -Name[zh_TW]=歌詞資料引擎 -X-KDE-ServiceTypes=Plasma/DataEngine -Type=Service -Icon=lyrics -X-KDE-Library=amarok_data_engine_lyrics -X-KDE-PluginInfo-Name=amarok-lyrics -X-KDE-ParentApp=Amarok - diff --git a/src/context/engines/photos/CMakeLists.txt b/src/context/engines/photos/CMakeLists.txt deleted file mode 100644 index 7676446f87..0000000000 --- a/src/context/engines/photos/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -include_directories( - ${Amarok_SOURCE_DIR}/src - ${Amarok_SOURCE_DIR}/src/context - ${Amarok_SOURCE_DIR}/src/network - ${CMAKE_CURRENT_BINARY_DIR} # for amarok_config.h -) - -set( photos_engine_SRCS - PhotosEngine.cpp -) - -add_library(amarok_data_engine_photos MODULE ${photos_engine_SRCS}) -target_link_libraries( amarok_data_engine_photos amarokcore amaroklib KF5::Plasma KF5::KIOCore) - -install( TARGETS amarok_data_engine_photos DESTINATION ${PLUGIN_INSTALL_DIR} ) -install( FILES amarok-data-engine-photos.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) diff --git a/src/context/engines/photos/PhotosInfo.h b/src/context/engines/photos/PhotosInfo.h deleted file mode 100644 index d1fa2e4613..0000000000 --- a/src/context/engines/photos/PhotosInfo.h +++ /dev/null @@ -1,65 +0,0 @@ -/**************************************************************************************** - * - * Copyright (c) 2009 Simon Esneault * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_PHOTOS_INFO -#define AMAROK_PHOTOS_INFO - -#include -#include - -#include -#include - -class PhotosInfo; -typedef KSharedPtr PhotosInfoPtr; - -//! Struct PhotosInfo, contain all the info vor a photos -class PhotosInfo : public QSharedData -{ -public: - typedef QList List; - - PhotosInfo() - { - static bool metaTypeRegistered = false; - if( !metaTypeRegistered ) - { - qRegisterMetaType( "PhotosInfo" ); - qRegisterMetaType( "PhotosInfoPtr" ); - qRegisterMetaType( "PhotosInfo::List" ); - metaTypeRegistered = true; - } - } - - PhotosInfo( const PhotosInfo &other ) - : QSharedData( other ) - , title( other.title ) - , urlphoto( other.urlphoto ) - , urlpage( other.urlpage ) - {} - ~PhotosInfo() {} - - QString title; // Name of the phtos - QUrl urlphoto; // url of the photos, for the download - QUrl urlpage; // Url for the browser ( http://www.flickr.com/photos/wanderlustg/322285063/ ) -}; - -Q_DECLARE_METATYPE( PhotosInfo ) -Q_DECLARE_METATYPE( PhotosInfoPtr ) -Q_DECLARE_METATYPE( PhotosInfo::List ) - -#endif diff --git a/src/context/engines/photos/amarok-data-engine-photos.desktop b/src/context/engines/photos/amarok-data-engine-photos.desktop deleted file mode 100644 index 51e431d70a..0000000000 --- a/src/context/engines/photos/amarok-data-engine-photos.desktop +++ /dev/null @@ -1,57 +0,0 @@ -[Desktop Entry] -Name=Photos Data Engine -Name[bg]=Ядро за снимки -Name[bs]=Pogon podatka fotografija -Name[ca]=Motor de dades de fotos -Name[ca@valencia]=Motor de dades de fotos -Name[cs]=Datový nástroj fotek -Name[csb]=Mòtór pòdôwków òdjimków -Name[da]=Datamotor til fotos -Name[de]=Datenmodul für Fotos -Name[el]=Μηχανή δεδομένων φωτογραφιών -Name[en_GB]=Photos Data Engine -Name[es]=Motor de datos de fotos -Name[et]=Fotode andmemootor -Name[eu]=Argazkien datuen motorra -Name[fi]=Valokuvien tietomoottori -Name[fr]=Moteur de données pour les photos -Name[ga]=Inneall Sonraí Grianghraf -Name[gl]=Motor de datos de fotos -Name[hu]=Fotó-adatmotor -Name[id]=Mesin Data Foto -Name[is]=Ljósmyndagagnavél -Name[it]=Motore dati delle fotografie -Name[ja]=写真 データエンジン -Name[km]=ម៉ាស៊ីន​ទិន្នន័យ​រូប​ថត -Name[ko]=사진 데이터 엔진 -Name[lt]=Nuotraukų duomenų sistema -Name[lv]=Fotogrāfiju datu dzinējs -Name[nb]=Foto datamotor -Name[nds]=Foto-Datenkarn -Name[nl]=Foto-gegevensengine -Name[nn]=Datamotor for bilete -Name[pa]=ਫੋਟੋ ਡਾਟਾ ਇੰਜਣ -Name[pl]=Moduł danych zdjęć -Name[pt]=Motor de Dados de Fotografias -Name[pt_BR]=Mecanismo de dados de fotos -Name[ro]=Motor de date Fotografii -Name[ru]=Источник данных фотографий -Name[sk]=Dátový nástroj Fotografie -Name[sl]=Podatkovni pogon za fotografije -Name[sr]=Датомотор фотографија -Name[sr@ijekavian]=Датомотор фотографија -Name[sr@ijekavianlatin]=Datomotor fotografija -Name[sr@latin]=Datomotor fotografija -Name[sv]=Datagränssnitt för foton -Name[th]=กลไกข้อมูลของภาพถ่าย -Name[tr]=Fotoğraf Veri Motoru -Name[uk]=Рушій даних фотографій -Name[x-test]=xxPhotos Data Enginexx -Name[zh_CN]=照片数据引擎 -Name[zh_TW]=Photos 資料引擎 -X-KDE-ServiceTypes=Plasma/DataEngine -X-KDE-ParentApp=Amarok -Type=Service -Icon=Photos -X-KDE-Library=amarok_data_engine_photos -X-KDE-PluginInfo-Name=amarok-photos diff --git a/src/context/engines/songkick/SongkickEngine.cpp b/src/context/engines/songkick/SongkickEngine.cpp index 8876379cd1..a6fedb87a8 100644 --- a/src/context/engines/songkick/SongkickEngine.cpp +++ b/src/context/engines/songkick/SongkickEngine.cpp @@ -1,166 +1,166 @@ /**************************************************************************************** * Copyright (c) 2008 Jeff Mitchell * * Copyright (c) 2007-2008 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "SongkickEngine.h" #include "JsonQt/lib/JsonToVariant.h" #include "JsonQt/lib/ParseException.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include "ContextObserver.h" #include "ContextView.h" #include "EngineController.h" #include #include #include #include using namespace Context; SongkickEngine::SongkickEngine( QObject* parent, const QList& args ) : DataEngine( parent ) , ContextObserver( ContextView::self() ) , m_datesJob( ) , m_currentTrack( 0 ) , m_ontour( true ) , m_dates( true ) { Q_UNUSED( args ) DEBUG_BLOCK m_sources << I18N_NOOP( "ontour" ) << I18N_NOOP( "dates" ); } QStringList SongkickEngine::sources() const { DEBUG_BLOCK return m_sources; } bool SongkickEngine::sourceRequestEvent( const QString& name ) { DEBUG_BLOCK debug() << "sourceRequested with name " << name; removeAllData( name ); setData( name, "fetching" ); update(); return true; } void SongkickEngine::message( const ContextState& state ) { DEBUG_BLOCK if( state == Current ) update(); } void SongkickEngine::metadataChanged( Meta::TrackPtr track ) { Q_UNUSED( track ) DEBUG_BLOCK update(); } void SongkickEngine::update() { DEBUG_BLOCK unsubscribeFrom( m_currentTrack ); Meta::TrackPtr currentTrack = The::engineController()->currentTrack(); m_currentTrack = currentTrack; subscribeTo( currentTrack ); if ( !currentTrack ) { debug() << "No current track!"; return; } else if ( !currentTrack->artist() ) { debug() << "No artist found!"; return; } QString country = QLocale::system().name().right( 2 ).toLower(); QUrl ontourUrl( QString( "http://api.songkick.com/api/V2/get_tour_status?key=kJcAUmzi8AoAngzh&id=0&country=%2&range=all&name=%1" ).arg( QUrl::toPercentEncoding( currentTrack->artist()->prettyName() ), country ) ); debug() << "getting ontour status: " << ontourUrl; m_ontourJob = KIO::storedGet( ontourUrl, KIO::NoReload, KIO::HideProgressInfo ); - connect( m_ontourJob, SIGNAL(result(KJob*)), this, SLOT(ontourResult(KJob*)) ); + connect( m_ontourJob, &KJob::result, this, SLOT(ontourResult(KJob*)) ); QUrl datesUrl( QString( "http://api.songkick.com/api/V2/get_dates_extended?key=kJcAUmzi8AoAngzh&id=0&country=%2&range=all&name=%1" ).arg( QUrl::toPercentEncoding( currentTrack->artist()->prettyName() ), country ) ); debug() << "getting concert dates: " << datesUrl; m_datesJob = KIO::storedGet( datesUrl, KIO::NoReload, KIO::HideProgressInfo ); - connect( m_datesJob, SIGNAL(result(KJob*)), this, SLOT(datesResult(KJob*)) ); + connect( m_datesJob, &KJob::result, this, SLOT(datesResult(KJob*)) ); } void SongkickEngine::datesResult( KJob* job ) { DEBUG_BLOCK if( job != m_datesJob ) return; if( !m_datesJob ) return; if( !job->error() == 0 && m_datesJob == job) { setData( "dates", "error" ); return; } KIO::StoredTransferJob* const storedJob = static_cast( job ); QString data = QString( storedJob->data() ); QVariantMap dates; /*QVariant datesResult = JsonQt::JsonToVariant::parse( data ); debug() << "got dates: " << dates; QMapIterator< QString, QVariant > iter( dates ); while( iter.hasNext() ) { iter.next(); setData( "dates", iter.key(), iter.value() ); } */ setData( "dates", data ); } void SongkickEngine::ontourResult( KJob* job ) { DEBUG_BLOCK if( job != m_ontourJob ) return; m_ontour = false; if( !m_ontourJob ) return; if( !job->error() == 0 && m_ontourJob == job ) { setData( "ontour", "error" ); return; } KIO::StoredTransferJob* const storedJob = static_cast( job ); QString data = QString( storedJob->data() ); QVariantMap status; setData( "ontour", data ); } diff --git a/src/context/engines/wikipedia/CMakeLists.txt b/src/context/engines/wikipedia/CMakeLists.txt deleted file mode 100644 index b41ad24690..0000000000 --- a/src/context/engines/wikipedia/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -include_directories( ../../.. - ${CMAKE_SOURCE_DIR}/src/context - ${CMAKE_SOURCE_DIR}/src/network - ${CMAKE_CURRENT_BINARY_DIR}/../../.. # for amarok_config.h -) - -set( wikipedia_engine_SRCS - WikipediaEngine.cpp -) - -add_library(amarok_data_engine_wikipedia MODULE ${wikipedia_engine_SRCS}) -target_link_libraries( amarok_data_engine_wikipedia amarokcore amaroklib KF5::Plasma KF5::KIOCore ) - -install( TARGETS amarok_data_engine_wikipedia DESTINATION ${PLUGIN_INSTALL_DIR} ) -install( FILES amarok-data-engine-wikipedia.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) diff --git a/src/context/engines/wikipedia/WikipediaEngine.h b/src/context/engines/wikipedia/WikipediaEngine.h deleted file mode 100644 index 1d3f63160a..0000000000 --- a/src/context/engines/wikipedia/WikipediaEngine.h +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * Copyright (c) 2008 Mark Kretschmann * - * Copyright (c) 2009 Simon Esneault * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_WIKIPEDIA_ENGINE -#define AMAROK_WIKIPEDIA_ENGINE - -#include "core/meta/forward_declarations.h" -#include "context/DataEngine.h" -#include "NetworkAccessManagerProxy.h" - -/** - This class provide Wikipedia data for use in Context applets. - -NOTE: The QVariant data is structured like this: - * the key name is the artist - * the data is a QString containing the html of the wikipedia page -*/ - -using namespace Context; -namespace Plasma -{ - class DataContainer; -} -class WikipediaEnginePrivate; - -class WikipediaEngine : public DataEngine -{ - Q_OBJECT - -public: - WikipediaEngine( QObject* parent, const QList& args ); - virtual ~WikipediaEngine(); - - virtual void init(); - -protected: - bool sourceRequestEvent( const QString &source ); - -private: - WikipediaEnginePrivate *const d_ptr; - Q_DECLARE_PRIVATE( WikipediaEngine ) - - Q_PRIVATE_SLOT( d_ptr, void _checkRequireUpdate(Meta::TrackPtr) ) - Q_PRIVATE_SLOT( d_ptr, void _dataContainerUpdated(const QString&,const Plasma::DataEngine::Data&) ) - Q_PRIVATE_SLOT( d_ptr, void _wikiResult(const QUrl&,QByteArray,NetworkAccessManagerProxy::Error) ) - Q_PRIVATE_SLOT( d_ptr, void _parseLangLinksResult(const QUrl&,QByteArray,NetworkAccessManagerProxy::Error) ) - Q_PRIVATE_SLOT( d_ptr, void _parseListingResult(const QUrl&,QByteArray,NetworkAccessManagerProxy::Error) ) - Q_PRIVATE_SLOT( d_ptr, void _stopped() ) -}; - -AMAROK_EXPORT_DATAENGINE( wikipedia, WikipediaEngine ) - -#endif - diff --git a/src/context/engines/wikipedia/amarok-data-engine-wikipedia.desktop b/src/context/engines/wikipedia/amarok-data-engine-wikipedia.desktop deleted file mode 100644 index f29ded60c5..0000000000 --- a/src/context/engines/wikipedia/amarok-data-engine-wikipedia.desktop +++ /dev/null @@ -1,62 +0,0 @@ -[Desktop Entry] -Name=Wikipedia Data Engine -Name[bg]=Ядро за Уикипедия -Name[bs]=Pogon podataka Wikipediјe -Name[ca]=Motor de dades de la Viquipèdia -Name[ca@valencia]=Motor de dades de la Viquipèdia -Name[cs]=Datový nástroj Wikipedia -Name[csb]=Mòtór pòdôwków z Wikipedijë -Name[da]=Datamotor til Wikipedia -Name[de]=Datenmodul für die Wikipedia-Erweiterung -Name[el]=Μηχανή δεδομένων Wikipedia -Name[en_GB]=Wikipedia Data Engine -Name[eo]=Datuma motoro de Vikipedio -Name[es]=Motor de datos de Wikipedia -Name[et]=Wikipedia andmemootor -Name[eu]=Wikipediako datuen motorra -Name[fi]=Wikipedian tietomoottori -Name[fr]=Moteur de données pour Wikipédia -Name[ga]=Inneall Sonraí: An Vicipéid -Name[gl]=Motor de datos de Wikipedia -Name[hne]=विकिपीडिया डाटा इंजिन -Name[hu]=Wikipedia-adatmotor -Name[id]=Mesin Data Wikipedia -Name[is]=Wikipedia gagnavél -Name[it]=Motore dati di Wikipedia -Name[ja]=Wikipedia データエンジン -Name[km]=ម៉ាស៊ីន​ទិន្នន័យ​វីគីភីឌៀ -Name[ko]=위키백과 데이터 엔진 -Name[ku]=Motora Dane ya Wikipedia -Name[lt]=Vikipedijos duomenų sistema -Name[lv]=Wikipēdijas datu dzinējs -Name[nb]=Datamotor for Wikipedia -Name[nds]=Wikipedia-Datenkarn -Name[nl]=Wikipedia-gegevensengine -Name[nn]=Datamotor for Wikipedia -Name[pa]=ਵਿਕਿਪੀਡਿਆ ਡਾਟਾ ਇੰਜਣ -Name[pl]=Moduł danych Wikipedii -Name[pt]=Motor de Dados do Wikipédia -Name[pt_BR]=Mecanismo de dados da Wikipédia -Name[ro]=Motor de date Wikipedia -Name[ru]=Источник данных Википедии -Name[sk]=Dátový nástroj Wikipedia -Name[sl]=Podatkovni pogon za Wikipedijo -Name[sr]=Датомотор Википедије -Name[sr@ijekavian]=Датомотор Википедије -Name[sr@ijekavianlatin]=Datomotor Wikipedije -Name[sr@latin]=Datomotor Wikipedije -Name[sv]=Datagränssnitt för Wikipedia -Name[th]=กลไกข้อมูลของวิกิพีเดีย -Name[tr]=Vikipedi Veri Motoru -Name[uk]=Рушій даних Вікіпедії -Name[wa]=Éndjin d' dinêyes Wikipedia -Name[x-test]=xxWikipedia Data Enginexx -Name[zh_CN]=维基百科数据引擎 -Name[zh_TW]=維基百科資料引擎 -X-KDE-ServiceTypes=Plasma/DataEngine -Type=Service -Icon=wikipedia -X-KDE-Library=amarok_data_engine_wikipedia -X-KDE-PluginInfo-Name=amarok-wikipedia -X-KDE-ParentApp=Amarok - diff --git a/src/context/qml_plugin/Applet.qml b/src/context/qml_plugin/Applet.qml new file mode 100644 index 0000000000..e3c5808923 --- /dev/null +++ b/src/context/qml_plugin/Applet.qml @@ -0,0 +1,99 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Dialogs 1.2 + + +Rectangle { + id: root + + default property alias contents: content.data + property alias title: header.title + property string name: "Nameless Applet" + property string appletId + property string packagePath + property url iconSource + property bool collapsed: false + property bool configEnabled: false + property real spacing: Context.smallSpacing + property real padding: spacing + property real contentHeight: content.childrenRect.height + property Dialog configDialog: null + readonly property SystemPalette palette: palette + + radius: Context.smallSpacing + border.width: 2 + border.color: palette.mid + color: "transparent" + clip: true + height: content.height + header.height + 2 * padding + !collapsed * spacing + + onCollapsedChanged: AppletModel.setAppletCollapsed(appletId, collapsed) + onContentHeightChanged: AppletModel.setAppletContentHeight(appletId, contentHeight) + + AppletHeader { + id: header + + title: root.name + iconSource: root.iconSource + } + + Item { + id: content + + anchors { + top: header.bottom + left: root.left + right: root.right + topMargin: root.spacing + leftMargin: root.padding + rightMargin: root.padding + } + + height: root.collapsed ? 0 : root.contentHeight + clip: true + + Behavior on height { + enabled: !resizeMouseArea.pressed + NumberAnimation { duration: 350 } + } + } + + MouseArea { + id: resizeMouseArea + + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + height: root.padding + enabled: root.configEnabled + cursorShape: enabled ? Qt.SizeVerCursor : Qt.ArrowCursor + acceptedButtons: Qt.LeftButton + preventStealing: true + onMouseYChanged: { + if(pressed) { + root.contentHeight = Math.max(Context.largeSpacing, root.contentHeight + mouseY); + } + } + } + + SystemPalette { + id: palette + } +} diff --git a/src/context/qml_plugin/AppletHeader.qml b/src/context/qml_plugin/AppletHeader.qml new file mode 100644 index 0000000000..25ffd41c54 --- /dev/null +++ b/src/context/qml_plugin/AppletHeader.qml @@ -0,0 +1,75 @@ +/**************************************************************************************** + * Copyright (c) 2017 Malte Veerman * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 + + +RowLayout { + id: root + + property var applet: parent + property alias title: label.text + property alias iconSource: icon.source + + anchors { + left: parent.left + right: parent.right + top: parent.top + margins: parent.padding + } + height: collapseButton.height + + Image { + id: icon + + height: collapseButton.height + width: height + sourceSize.width: width + sourceSize.height: height + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + } + + Label { + id: label + + text: root.title + fontSizeMode: Text.VerticalFit + horizontalAlignment: Text.AlignHCenter + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + } + + ToolButton { + id: configButton + + visible: applet.configEnabled && !!applet.configDialog + iconName: "preferences-other" + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + + onClicked: applet.configDialog.open() + } + ToolButton { + id: collapseButton + + //TODO: Icons are not part of official standard. Maybe provide our own icons? + iconName: !applet.collapsed ? "window-minimize" : "window-restore" + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + + onClicked: applet.collapsed = applet.collapsed ? false : true + } +} diff --git a/src/context/qml_plugin/qmldir b/src/context/qml_plugin/qmldir new file mode 100644 index 0000000000..3e6d586d85 --- /dev/null +++ b/src/context/qml_plugin/qmldir @@ -0,0 +1,6 @@ +module org.kde.amarok.qml +plugin qml_plugin +Applet 1.0 Applet.qml +depends QtQuick 2.6 +depends QtQuick.Controls 1.4 +depends QtQuick.Layouts 1.3 diff --git a/src/context/qml_plugin/src/PixmapItem.cpp b/src/context/qml_plugin/src/PixmapItem.cpp new file mode 100644 index 0000000000..f86e5071ba --- /dev/null +++ b/src/context/qml_plugin/src/PixmapItem.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2018 Malte Veerman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PixmapItem.h" + +#include +#include +#include +#include + +#include + + +PixmapItem::PixmapItem() + : m_sizeChanged( true ) +{ + setFlag( ItemHasContents ); +} + +void PixmapItem::setSource( const QPixmap& source ) +{ + if( m_source.toImage() == source.toImage() ) + return; + + m_source = source; + m_pixmapChanged = true; + emit sourceChanged(); + + setImplicitSize( source.width(), source.height() ); + + update(); +} + +QSGNode* PixmapItem::updatePaintNode( QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData ) +{ + Q_UNUSED( updatePaintNodeData ) + + if( m_source.isNull() || width() == 0 || height() == 0 ) + { + delete oldNode; + + return nullptr; + } + + ManagedTextureNode *textureNode = dynamic_cast( oldNode ); + + if( !textureNode || m_pixmapChanged ) + { + delete oldNode; + textureNode = new ManagedTextureNode; + textureNode->setFiltering( QSGTexture::Linear ); + textureNode->setTexture( QSharedPointer( window()->createTextureFromImage( m_source.toImage(), QQuickWindow::TextureCanUseAtlas ) ) ); + m_sizeChanged = true; + m_pixmapChanged = false; + } + + if( m_sizeChanged ) + { + textureNode->setRect( boundingRect() ); + m_sizeChanged = false; + } + + return textureNode; +} + +void PixmapItem::geometryChanged( const QRectF &newGeometry, const QRectF &oldGeometry ) +{ + if( newGeometry.size() != oldGeometry.size() ) + m_sizeChanged = true; + + update(); + + QQuickItem::geometryChanged( newGeometry, oldGeometry ); +} diff --git a/src/context/qml_plugin/src/PixmapItem.h b/src/context/qml_plugin/src/PixmapItem.h new file mode 100644 index 0000000000..4f1a60fe79 --- /dev/null +++ b/src/context/qml_plugin/src/PixmapItem.h @@ -0,0 +1,55 @@ +/* + * Copyright 2018 Malte Veerman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PIXMAPITEM_H +#define PIXMAPITEM_H + +#include + +#include + + +class PixmapItem : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY( QPixmap source READ source WRITE setSource NOTIFY sourceChanged RESET resetSource ) + Q_PROPERTY( bool valid READ valid NOTIFY sourceChanged ) + +public: + PixmapItem(); + + void setSource( const QPixmap &source ); + void resetSource() { setSource( QPixmap() ); } + QPixmap source() const { return m_source; } + bool valid() const { return !m_source.isNull(); } + + QSGNode* updatePaintNode( QSGNode * oldNode, UpdatePaintNodeData * updatePaintNodeData ) Q_DECL_OVERRIDE; + void geometryChanged( const QRectF &newGeometry, const QRectF &oldGeometry ) Q_DECL_OVERRIDE; + +signals: + void sourceChanged(); + +private: + QPixmap m_source; + bool m_pixmapChanged; + bool m_sizeChanged; +}; + +#endif // PIXMAPITEM_H diff --git a/src/context/qml_plugin/src/Plugin.cpp b/src/context/qml_plugin/src/Plugin.cpp new file mode 100644 index 0000000000..ea8da17cfc --- /dev/null +++ b/src/context/qml_plugin/src/Plugin.cpp @@ -0,0 +1,45 @@ +/* + * Copyright 2017 Malte Veerman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "RatingItem.h" +#include "PixmapItem.h" + +#include + +#include + + +class Plugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char* uri) Q_DECL_OVERRIDE + { + Q_ASSERT(uri == QLatin1String("org.kde.amarok.qml")); + + qmlRegisterType(uri, 1, 0, "RatingItem"); + qmlRegisterType(uri, 1, 0, "PixmapItem"); + } +}; + +#include diff --git a/src/context/qml_plugin/src/RatingItem.cpp b/src/context/qml_plugin/src/RatingItem.cpp new file mode 100644 index 0000000000..68073b541d --- /dev/null +++ b/src/context/qml_plugin/src/RatingItem.cpp @@ -0,0 +1,275 @@ +/**************************************************************************************** + * Copyright (c) 2008 William Viana Soarjs * + * Copyright (c) 2010 Mark Kretschmann * + * * + * This program is free software; you can redistribute it and/or modify it under * + * the terms of the GNU General Public License as published by the Free Software * + * Foundation; either version 2 of the License, or (at your option) any later * + * version. * + * * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY * + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along with * + * this program. If not, see . * + ****************************************************************************************/ + +/* + Significant parts of this code is inspired and/or copied from + KDE Nepomuk sources, available at kdelibs/nepomuk +*/ + +#include "RatingItem.h" + +#include "core/support/Debug.h" + +#include +#include +#include +#include + +#include + +class RatingItem::Private +{ +public: + Private() + : rating(0) + , hoverRating(-1) + , pixSize( 16 ) + { + } + + int rating; + int hoverRating; + int pixSize; + + KRatingPainter ratingPainter; +}; + + +RatingItem::RatingItem( QQuickItem* parent ) + : QQuickPaintedItem( parent ) + , d( new Private() ) +{ + setAcceptedMouseButtons( Qt::LeftButton ); + setAcceptHoverEvents( true ); + + connect( qApp, &QGuiApplication::paletteChanged, this, &QQuickItem::update ); +} + + +RatingItem::~RatingItem() +{ + delete d; +} + + +void +RatingItem::setIcon( const QString& iconName ) +{ + if( iconName == icon() ) + return; + + d->ratingPainter.setIcon( QIcon::fromTheme( iconName ) ); + emit iconChanged(); + + update(); +} + + +int +RatingItem::spacing() const +{ + return d->ratingPainter.spacing(); +} + + +QString +RatingItem::icon() const +{ + return d->ratingPainter.icon().name(); +} + + +void +RatingItem::setSpacing( int s ) +{ + if( s == d->ratingPainter.spacing() ) + return; + + d->ratingPainter.setSpacing( s ); + emit spacingChanged(); + + update(); +} + + +Qt::Alignment +RatingItem::alignment() const +{ + return d->ratingPainter.alignment(); +} + + +void +RatingItem::setAlignment( Qt::Alignment align ) +{ + if( align == d->ratingPainter.alignment() ) + return; + + d->ratingPainter.setAlignment( align ); + emit alignmentChanged(); + + update(); +} + + +Qt::LayoutDirection +RatingItem::layoutDirection() const +{ + return d->ratingPainter.layoutDirection(); +} + + +void +RatingItem::setLayoutDirection( Qt::LayoutDirection direction ) +{ + if( direction == d->ratingPainter.layoutDirection() ) + return; + + d->ratingPainter.setLayoutDirection( direction ); + emit layoutDirectionChanged(); + + update(); +} + + +unsigned int +RatingItem::rating() const +{ + return d->rating; +} + + +int +RatingItem::maxRating() const +{ + return d->ratingPainter.maxRating(); +} + + +int RatingItem::hoverRating() const +{ + return d->hoverRating; +} + + +bool +RatingItem::halfStepsEnabled() const +{ + return d->ratingPainter.halfStepsEnabled(); +} + +void +RatingItem::setRating( int rating ) +{ + if( rating == d->rating ) + return; + + d->rating = rating; + d->hoverRating = rating; + emit ratingChanged(); + emit hoverRatingChanged(); + + update(); +} + +void +RatingItem::setMaxRating( int max ) +{ + if( max == d->ratingPainter.maxRating() ) + return; + + bool halfSteps = d->ratingPainter.halfStepsEnabled(); + + d->ratingPainter.setMaxRating( max ); + emit maxRatingChanged(); + + if( halfSteps != d->ratingPainter.halfStepsEnabled() ) + emit halfStepsEnabledChanged(); + + update(); +} + + +void +RatingItem::setHalfStepsEnabled( bool enabled ) +{ + if( enabled == d->ratingPainter.halfStepsEnabled() ) + return; + + d->ratingPainter.setHalfStepsEnabled( enabled ); + emit halfStepsEnabledChanged(); + + update(); +} + +void +RatingItem::mousePressEvent( QMouseEvent* e ) +{ + DEBUG_BLOCK + + if ( e->button() == Qt::LeftButton ) + { + QRect rect( 0, 0, width(), height() ); + int ratingFromPos = d->ratingPainter.ratingFromPosition( rect, e->pos() ); + debug() << "Rating item clicked. New rating:" << ratingFromPos; + + if ( ratingFromPos >= 0 ) + { + // setToolTip( i18n( "Track rating: %1", (float)d->rating / 2 ) ); + emit clicked( ratingFromPos ); + } + } +} + + +void +RatingItem::hoverMoveEvent( QHoverEvent* e ) +{ + QRect rect( 0, 0, width(), height() ); + d->hoverRating = d->ratingPainter.ratingFromPosition( rect, e->pos() ); + + update(); +} + + +void +RatingItem::hoverEnterEvent( QHoverEvent* e ) +{ + QRect rect( 0, 0, width(), height() ); + d->hoverRating = d->ratingPainter.ratingFromPosition( rect, e->pos() ); + + // setToolTip( i18n( "Track rating: %1", (float)d->rating / 2 ) ); + + update(); +} + +void +RatingItem::hoverLeaveEvent( QHoverEvent* ) +{ + d->hoverRating = -1; + update(); +} + + +void +RatingItem::paint( QPainter* painter ) +{ + + d->ratingPainter.setEnabled( isEnabled() ); + QRect rect( 0, 0, width(), height() ); + d->ratingPainter.paint( painter, rect, d->rating, d->hoverRating ); +} diff --git a/src/context/widgets/RatingWidget.h b/src/context/qml_plugin/src/RatingItem.h similarity index 61% rename from src/context/widgets/RatingWidget.h rename to src/context/qml_plugin/src/RatingItem.h index 241130c4c9..fe7148ca8a 100644 --- a/src/context/widgets/RatingWidget.h +++ b/src/context/qml_plugin/src/RatingItem.h @@ -1,169 +1,158 @@ /**************************************************************************************** * Copyright (c) 2008 William Viana Soares * * Copyright (c) 2010 Mark Kretschmann * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ -/* - Significant parts of this code is inspired and/or copied from - KDE Nepomuk sources, available at kdelibs/nepomuk -*/ -#ifndef AMAROK_RATING_WIDGET_H -#define AMAROK_RATING_WIDGET_H -#include "amarok_export.h" +#ifndef AMAROK_RATING_ITEM_H +#define AMAROK_RATING_ITEM_H -#include -class AMAROK_EXPORT RatingWidget : public QGraphicsWidget +#include + +class RatingItem : public QQuickPaintedItem { Q_OBJECT + Q_PROPERTY( int rating READ rating WRITE setRating NOTIFY ratingChanged ) + Q_PROPERTY( int hoverRating READ hoverRating NOTIFY hoverRatingChanged ) + Q_PROPERTY( int maxRating READ maxRating WRITE setMaxRating NOTIFY maxRatingChanged ) + Q_PROPERTY( int spacing READ spacing WRITE setSpacing NOTIFY spacingChanged ) + Q_PROPERTY( Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged ) + Q_PROPERTY( Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged ) + Q_PROPERTY( bool halfStepsEnabled READ halfStepsEnabled WRITE setHalfStepsEnabled NOTIFY halfStepsEnabledChanged ) + Q_PROPERTY( QString icon READ icon WRITE setIcon NOTIFY iconChanged ) - public: +public: /** * Creates a new rating widget. */ - RatingWidget( QGraphicsItem* parent = 0 ); + RatingItem( QQuickItem* parent = Q_NULLPTR ); /** * Destructor */ - ~RatingWidget(); + ~RatingItem(); /** * @return The current rating. */ unsigned int rating() const; /** * @return the maximum possible rating. */ int maxRating() const; + /** + * @return the rating that corresponds to the cursor position while hovering. + * Returns -1 if the cursor is outside of the item. + */ + int hoverRating() const; + /** * The alignment of the stars. - * */ Qt::Alignment alignment() const; /** * The layout direction. If RTL the stars - * representing the rating value will be drawn from the + * representing the rating value will be drawn from the * right. - * */ Qt::LayoutDirection layoutDirection() const; /** * The spacing between the rating stars. - * */ int spacing() const; - QSizeF sizeHint( Qt::SizeHint hint, const QSizeF& size ) const; - /** * If half steps are enabled one star equals to 2 rating * points and uneven rating values result in half-stars being * drawn. - * */ bool halfStepsEnabled() const; /** - * The icon used to draw a star. In case a custom pixmap has been set - * this value is ignored. - * + * The icon name used to draw a star. */ - QIcon icon() const; + QString icon() const; - void show(); - - void hide(); +Q_SIGNALS: + void ratingChanged(); + void hoverRatingChanged(); + void maxRatingChanged(); + void spacingChanged(); + void layoutDirectionChanged(); + void alignmentChanged(); + void halfStepsEnabledChanged(); + void iconChanged(); + void clicked( int newRating ); - Q_SIGNALS: +public Q_SLOTS: /** - * Emitted if the rating is changed by user interaction (ie. mouse click). - * A call to setRating does not trigger this signal. - */ - void ratingChanged( int rating ); - - public Q_SLOTS: - /** - * Set the current rating. Calling this method will NOT trigger the - * ratingChanged signal. + * Set the current rating. */ void setRating( int rating ); /** * Set the maximum allowed rating value. The default is 10 which means * that a rating from 1 to 10 is selectable. If \a max is uneven steps * are automatically only allowed full. */ void setMaxRating( int max ); /** * If half steps are enabled (the default) then * one rating step corresponds to half a star. */ void setHalfStepsEnabled( bool enabled ); /** * Set the spacing between the pixmaps. The default is 0. */ void setSpacing( int ); /** * The alignment of the stars in the drawing rect. * All alignment flags are supported. */ void setAlignment( Qt::Alignment align ); /** * LTR or RTL */ void setLayoutDirection( Qt::LayoutDirection direction ); /** * Set a custom icon. Defaults to "rating". */ - void setIcon( const QIcon& icon ); - - /** - * Set a custom pixmap. - */ - void setCustomPixmap( const QPixmap& pixmap ); + void setIcon( const QString& iconName ); - /** - * Set the recommended size of the pixmaps. This is - * only used for the sizeHint. The actual size is always - * dependant on the size of the widget itself. - */ - void setPixmapSize( int size ); - protected: - virtual void mousePressEvent( QGraphicsSceneMouseEvent* e ); - virtual void hoverMoveEvent( QGraphicsSceneHoverEvent* e ); - virtual void hoverEnterEvent( QGraphicsSceneHoverEvent* e ); - virtual void hoverLeaveEvent( QGraphicsSceneHoverEvent* e ); - virtual void paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0 ); +protected: + virtual void mousePressEvent( QMouseEvent* e ) Q_DECL_OVERRIDE; + virtual void hoverMoveEvent( QHoverEvent* e ) Q_DECL_OVERRIDE; + virtual void hoverEnterEvent( QHoverEvent* e ) Q_DECL_OVERRIDE; + virtual void hoverLeaveEvent( QHoverEvent* e ) Q_DECL_OVERRIDE; + virtual void paint( QPainter* painter ) Q_DECL_OVERRIDE; - private: +private: class Private; Private* const d; - int m_startupUpdates; }; #endif diff --git a/src/context/servicetypes/amarok_animator.desktop b/src/context/servicetypes/amarok_animator.desktop deleted file mode 100644 index 6e21108542..0000000000 --- a/src/context/servicetypes/amarok_animator.desktop +++ /dev/null @@ -1,63 +0,0 @@ -[Desktop Entry] -Type=ServiceType -X-KDE-ServiceType=Plasma/Animator - -Comment=Plasma Animation Engine -Comment[bg]=Ядро за Plasma -Comment[bs]=Plazmin pogon podataka animacija -Comment[ca]=Motor d'animació del Plasma -Comment[ca@valencia]=Motor d'animació del Plasma -Comment[cs]=Animační nástroj Plasma -Comment[csb]=Mòtór animacëjów Plasmë -Comment[da]=Motor til Plasma-animation -Comment[de]=Modul für Plasma-Animationen -Comment[el]=Μηχανή κίνησης Plasma -Comment[en_GB]=Plasma Animation Engine -Comment[eo]=Viviga motoro de Plasma -Comment[es]=Motor de animación Plasma -Comment[et]=Plasma animatsiooni mootor -Comment[eu]=Plasma animazioen motorra -Comment[fi]=Plasma-animointimoottori -Comment[fr]=Moteur d'animations de Plasma -Comment[ga]=Inneall Beochana Plasma -Comment[gl]=Motor de animación de Plasma -Comment[hi]=प्लाज्मा एनिमेशन इंजिन -Comment[hne]=प्लाज्मा एनिमेसन इंजिन -Comment[hu]=Motor Plasma-animációkhoz -Comment[id]=Mesin Animasi Plasma -Comment[is]=Plasma hreyfingastjóri -Comment[it]=Motore animazione di Plasma -Comment[ja]=Plasma アニメーションエンジン -Comment[km]=ម៉ាស៊ីន​ចលនា​ប្លាស្មា -Comment[ko]=Plasma 애니메이션 엔진 -Comment[ku]=Motora Zindîkirina Plasma -Comment[lt]=Plasma animacijos sistema -Comment[lv]=Plasma animācijas dzinējs -Comment[mai]=प्लाजमा भावचिन्ह इंजन -Comment[mr]=प्लाज्मा एनीमेशन इंजिन -Comment[nb]=Animasjonsmotor for Plasma -Comment[nds]=Animeerkarn Plasma -Comment[nl]=Plasma animatie-engine -Comment[nn]=Plasma-animasjonsmotor -Comment[pa]=ਪਲਾਜ਼ਮਾ ਐਨੀਮੇਸ਼ਨ ਇੰਜਣ -Comment[pl]=Moduł animacji Plazmy -Comment[pt]=Motor de Animação do Plasma -Comment[pt_BR]=Mecanismo de animação do Plasma -Comment[ro]=Motor de animație Plasma -Comment[ru]=Движок анимации Plasma -Comment[sk]=Animačný nástroj Plasma -Comment[sl]=Animacijski pogon za Plasmo -Comment[sr]=Плазмин мотор анимација -Comment[sr@ijekavian]=Плазмин мотор анимација -Comment[sr@ijekavianlatin]=Plasmin motor animacija -Comment[sr@latin]=Plasmin motor animacija -Comment[sv]=Animeringsgränssnitt för Plasma -Comment[th]=กลไกของพลาสมา สำหรับทำภาพเคลื่อนไหว -Comment[tr]=Plasma Canlandırma Motoru -Comment[ug]=Plasma جانلاندۇرۇم ماتورى -Comment[uk]=Рушій анімації Плазми -Comment[wa]=Éndjin d' animåcion Plasma -Comment[x-test]=xxPlasma Animation Enginexx -Comment[zh_CN]=Plasma 动画引擎 -Comment[zh_TW]=Plasma 動畫引擎 - diff --git a/src/context/servicetypes/amarok_data_engine.desktop b/src/context/servicetypes/amarok_data_engine.desktop deleted file mode 100644 index 82fd1c9ee5..0000000000 --- a/src/context/servicetypes/amarok_data_engine.desktop +++ /dev/null @@ -1,118 +0,0 @@ -[Desktop Entry] -Name=Amarok Data Engine -Name[bg]=Ядро Amarok -Name[bs]=Pogon podataka Amaroka -Name[ca]=Motor de dades de l'Amarok -Name[ca@valencia]=Motor de dades de l'Amarok -Name[cs]=Datový nástroj Amarok -Name[csb]=Mòtór pòdôwków Amaroka -Name[da]=Datamotor til Amarok -Name[de]=Amarok-Datenmodul -Name[el]=Μηχανή δεδομένων Amarok -Name[en_GB]=Amarok Data Engine -Name[eo]=Datuma motoro de Amarok -Name[es]=Motor de datos de Amarok -Name[et]=Amaroki andmemootor -Name[eu]=Amarok-eko datuen motorra -Name[fi]=Amarok-tietomoottori -Name[fr]=Moteur de données pour Amarok -Name[ga]=Inneall Sonraí Amarok -Name[gl]=Motor de datos de Amarok -Name[he]=מנוע המידע של Amarok -Name[hne]=अमाराक डाटा इंजिन -Name[hu]=Amarok-adatmotor -Name[id]=Mesin Data Amarok -Name[is]=Amarok gagnavél -Name[it]=Motore dati di Amarok -Name[ja]=Amarok データエンジン -Name[km]=ម៉ាស៊ីន​ទិន្នន័យ Amarok -Name[ko]=Amarok 데이터 엔진 -Name[ku]=Motora Dane ya Amarok -Name[lt]=Amarok duomenų sistema -Name[lv]=Amarok datu dzinējs -Name[nb]=Datamotor for Amarok -Name[nds]=Amarok-Datenkarn -Name[nl]=Amarok-gegevensengine -Name[nn]=Amarok-datamotor -Name[pa]=ਅਮਰੋਕ ਡਾਟਾ ਇੰਜਣ -Name[pl]=Moduł danych Amaroka -Name[pt]=Motor de Dados do Amarok -Name[pt_BR]=Mecanismo de dados do Amarok -Name[ro]=Motor de date Amarok -Name[ru]=Источник данных Amarok -Name[sk]=Dátový nástroj Amarok -Name[sl]=Podatkovni pogon za Amarok -Name[sr]=Датомотор Амарока -Name[sr@ijekavian]=Датомотор Амарока -Name[sr@ijekavianlatin]=Datomotor Amaroka -Name[sr@latin]=Datomotor Amaroka -Name[sv]=Datagränssnitt för Amarok -Name[th]=กลไกข้อมูลของแอมอะร็อก -Name[tr]=Amarok Veri Motoru -Name[uk]=Рушій даних Amarok -Name[wa]=Éndjin d' dinêyes Amarok -Name[x-test]=xxAmarok Data Enginexx -Name[zh_CN]=Amarok 数据引擎 -Name[zh_TW]=Amarok 資料引擎 -Type=ServiceType -X-KDE-ServiceType=Plasma/DataEngine - -Comment=Amarok Data Engine -Comment[bg]=Ядро Amarok -Comment[bs]=Pogon podataka Amaroka -Comment[ca]=Motor de dades de l'Amarok -Comment[ca@valencia]=Motor de dades de l'Amarok -Comment[cs]=Datový nástroj Amarok -Comment[csb]=Mòtór pòdôwków Amaroka -Comment[da]=Datamotor til Amarok -Comment[de]=Amarok-Datenmodul -Comment[el]=Μηχανή δεδομένων Amarok -Comment[en_GB]=Amarok Data Engine -Comment[eo]=Datuma motoro de Amarok -Comment[es]=Motor de datos de Amarok -Comment[et]=Amaroki andmemootor -Comment[eu]=Amarok-eko datuen motorra -Comment[fi]=Amarok-tietomoottori -Comment[fr]=Moteur de données pour Amarok -Comment[ga]=Inneall Sonraí Amarok -Comment[gl]=Motor de datos de Amarok -Comment[he]=מנוע המידע של Amarok -Comment[hne]=अमाराक डाटा इंजिन -Comment[hu]=Amarok-adatmotor -Comment[id]=Mesin Data Amarok -Comment[is]=Amarok gagnavél -Comment[it]=Motore dati di Amarok -Comment[ja]=Amarok データエンジン -Comment[km]=ម៉ាស៊ីន​ទិន្នន័យ Amarok -Comment[ko]=Amarok 데이터 엔진 -Comment[ku]=Motora Dane ya Amarok -Comment[lt]=Amarok duomenų sistema -Comment[lv]=Amarok datu dzinējs -Comment[nb]=Datamotor for Amarok -Comment[nds]=Amarok-Datenkarn -Comment[nl]=Amarok-gegevensengine -Comment[nn]=Amarok-datamotor -Comment[pa]=ਅਮਰੋਕ ਡਾਟਾ ਇੰਜਣ -Comment[pl]=Moduł danych Amaroka -Comment[pt]=Motor de Dados do Amarok -Comment[pt_BR]=Mecanismo de dados do Amarok -Comment[ro]=Motor de date Amarok -Comment[ru]=Источник данных Amarok -Comment[sk]=Dátový nástroj Amarok -Comment[sl]=Podatkovni pogon za Amarok -Comment[sr]=Датомотор Амарока -Comment[sr@ijekavian]=Датомотор Амарока -Comment[sr@ijekavianlatin]=Datomotor Amaroka -Comment[sr@latin]=Datomotor Amaroka -Comment[sv]=Datagränssnitt för Amarok -Comment[th]=กลไกข้อมูลของแอมอะร็อก -Comment[tr]=Amarok Veri Motoru -Comment[uk]=Рушій даних Amarok -Comment[wa]=Éndjin d' dinêyes Amarok -Comment[x-test]=xxAmarok Data Enginexx -Comment[zh_CN]=Amarok 数据引擎 -Comment[zh_TW]=Amarok 資料引擎 - -[PropertyDef::X-KDE-PluginInfo-Name] -Type=QString - diff --git a/src/context/toolbar/AppletItemOverlay.cpp b/src/context/toolbar/AppletItemOverlay.cpp deleted file mode 100644 index e3170267d3..0000000000 --- a/src/context/toolbar/AppletItemOverlay.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "AppletItemOverlay" - -#include "AppletItemOverlay.h" - -#include "context/toolbar/AppletToolbarAppletItem.h" -#include "core/support/Debug.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -// stolen verbatim and shamelessly from workspace/plasma/shells/desktop/panelappletoverlay -class AppletMoveSpacer : public QGraphicsWidget -{ -public: - AppletMoveSpacer( QGraphicsWidget *applet ) - : QGraphicsWidget( applet ) - { - } - -protected: - void paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * widget = 0 ) - { - Q_UNUSED( option ) - Q_UNUSED( widget ) - - //TODO: make this a pretty gradient? - painter->setRenderHint( QPainter::Antialiasing ); - QPainterPath p = Plasma::PaintUtils::roundedRectangle( contentsRect().adjusted( 1, 1, -2, -2 ), 4 ); - QColor c = Plasma::Theme::defaultTheme()->color( Plasma::Theme::TextColor ); - c.setAlphaF( 0.3 ); - - painter->fillPath( p, c ); - } -}; - - -Context::AppletItemOverlay::AppletItemOverlay( Context::AppletToolbarAppletItem *applet, QGraphicsLinearLayout* layout, QWidget *parent ) - : QWidget( parent ), - m_applet( applet ), - m_spacer(0), - m_layout( layout ), - m_deleteIcon( 0 ), - m_index( 0 ), - m_itemHasSwapped( false ) -{ - DEBUG_BLOCK - - if( layout ) - { - m_layout = layout; - int i = 0; - for(; i < m_layout->count(); ++i) - { - QGraphicsWidget *w = dynamic_cast< QGraphicsWidget* >( m_layout->itemAt( i ) ); - if( w == m_applet ) - { - m_index = i; - break; - } - } - } else - debug() << "GOT APPLET WITH NO LAYOUT! BAD!"; - - m_deleteIcon = new QToolButton( this ); - QAction* delApplet = new QAction( i18n( "Remove Applet" ), this ); - delApplet->setIcon( QIcon::fromTheme( "edit-delete" ) ); - delApplet->setVisible( true ); - delApplet->setEnabled( true ); - m_deleteIcon->addAction( delApplet ); - m_deleteIcon->setIcon( QIcon::fromTheme( "edit-delete" ) ); - m_deleteIcon->setMaximumSize( 24, 24 ); - QColor trans; - trans.setAlpha( 0 ); - QBrush brush( Qt::transparent ); - QPalette pal = m_deleteIcon->palette(); - pal.setBrush( QPalette::Window, brush ); - // m_deleteIcon->setBackgroundRole( QPalette::Base ); - m_deleteIcon->setPalette( pal ); - m_deleteIcon->setAutoFillBackground( false ); - m_deleteIcon->setAttribute( Qt::WA_NoSystemBackground ); - //m_deleteIcon->setAttribute( Qt::WA_TranslucentBackground ); //NB: Introduced in Qt 4.5 - - connect( delApplet, SIGNAL(triggered()), this, SLOT(deleteApplet()) ); - connect( m_deleteIcon, SIGNAL(released()), this, SLOT(deleteApplet()) ); - - syncGeometry(); - - connect( m_applet, SIGNAL(destroyed(QObject*)), this, SLOT(deleteLater()) ); - connect( m_applet, SIGNAL(geometryChanged()), this, SLOT(delaySyncGeometry()) ); -} - -Context::AppletItemOverlay::~AppletItemOverlay() -{ - QApplication::restoreOverrideCursor(); - - if( m_spacer ) - { - m_layout->removeItem( m_spacer ); - m_spacer->deleteLater(); - m_spacer = 0; - } - m_applet = 0; - m_layout = 0; -} - -void -Context::AppletItemOverlay::paintEvent( QPaintEvent *event ) -{ - Q_UNUSED( event ) - QStyleOption op; - op.initFrom( this ); - - bool hovered = op.state & QStyle::State_MouseOver; - bool mover = mouseGrabber() == this; - if( !hovered || mover ) - { - return; - } - - QPainter p( this ); - p.save(); - QIcon icon( "transform-move" ); - int iconSize; - QRect iconRect; - - if( m_applet ) - { // it's possible m_applet is null if we just opened amarok and it failed to load the applet plugin - // so the user is seeing a big red X and trying to get rid of item - iconSize = qMin( qMin( height(), int( m_applet->size().width() ) ), 64 ); - iconRect = QRect( rect().center() - QPoint( iconSize / 2, iconSize / 2 ), QSize( iconSize, iconSize ) ); - p.drawPixmap( iconRect, icon.pixmap( iconSize, iconSize ) ); - } - - p.restore(); -} - -void -Context::AppletItemOverlay::mousePressEvent( QMouseEvent *event ) -{ - Q_UNUSED( event ) - DEBUG_BLOCK - - m_itemHasSwapped = false; - - if( !m_spacer ) - { - m_spacer = new AppletMoveSpacer( m_applet ); - } else - { - m_layout->removeItem( m_spacer ); - } - - m_origin = mapToParent( event->pos() ); - m_spacer->setMinimumSize( m_applet->geometry().size() ); - m_spacer->setMaximumSize( m_applet->geometry().size() ); - m_layout->removeItem( m_applet ); - m_layout->insertItem( m_index, m_spacer ); - m_applet->setZValue( m_applet->zValue() + 1 ); - - m_offset = geometry().x() - m_origin.x(); - - QApplication::setOverrideCursor( Qt::ClosedHandCursor ); - grabMouse(); -} - -void -Context::AppletItemOverlay::mouseMoveEvent( QMouseEvent *event ) -{ - if( !m_spacer ) - { - m_spacer = new AppletMoveSpacer( m_applet ); - m_spacer->setMinimumSize( m_applet->geometry().size() ); - m_spacer->setMaximumSize( m_applet->geometry().size() ); - m_layout->removeItem( m_applet ); - m_layout->insertItem( m_index, m_spacer ); - } - - QPoint p = mapToParent( event->pos() ); - QRectF g = m_applet->geometry(); - - g.moveLeft( p.x() + m_offset ); - - m_applet->setGeometry( g ); - - // find position of the config item (always last item) - QGraphicsLayoutItem *lastItem = m_layout->itemAt( m_layout->count() - 1 ); - - // swap items if we move further than two thirds across the next/previous item - if( !m_itemHasSwapped ) - { - if( m_prevGeom.isValid() && g.left() <= m_prevGeom.left() + m_prevGeom.width() / 3 ) - { - swapWithPrevious(); - m_itemHasSwapped = true; - } - else if( m_nextGeom.isValid() && ( g.right() >= m_nextGeom.right() - m_nextGeom.width() / 3 ) && ( g.right() < lastItem->geometry().left() ) ) - { - swapWithNext(); - m_itemHasSwapped = true; - } - } - - if( ( m_prevGeom.isValid() && g.left() <= m_prevGeom.left() ) || ( m_nextGeom.isValid() && g.right() >= m_nextGeom.right() ) ) - m_itemHasSwapped = false; -} - -void -Context::AppletItemOverlay::mouseReleaseEvent( QMouseEvent *event ) -{ - Q_UNUSED( event ) - DEBUG_BLOCK - - QApplication::restoreOverrideCursor(); - releaseMouse(); - - if( !m_spacer ) - return; - - m_layout->removeItem( m_spacer ); - m_spacer->deleteLater(); - m_spacer = 0; - - m_layout->insertItem( m_index, m_applet ); - m_applet->setZValue( m_applet->zValue() - 1 ); // -1 means not specifying where it is from - - emit moveApplet( m_applet->applet(), -1, m_index ); -} - -void -Context::AppletItemOverlay::enterEvent( QEvent *event ) -{ - Q_UNUSED( event ) - update(); -} - -void -Context::AppletItemOverlay::leaveEvent( QEvent *event ) -{ - Q_UNUSED( event ) - update(); -} - - -Context::AppletToolbarAppletItem* -Context::AppletItemOverlay::applet() -{ - return m_applet; -} - - -void -Context::AppletItemOverlay::resizeEvent( QResizeEvent* ) -{ - m_deleteIcon->setGeometry( QRect( QPoint( ( size().width() - (m_deleteIcon->size().width() ) ) , 0 ), m_deleteIcon->geometry().size() ) ); -} - -void -Context::AppletItemOverlay::deleteApplet() -{ - emit deleteApplet( dynamic_cast< Plasma::Applet* >( m_applet->applet() ) ); - m_applet = 0; - deleteLater(); -} - -void -Context::AppletItemOverlay::swapWithPrevious() -{ - DEBUG_BLOCK - - m_index -= 1; - - if( m_index > 1 ) - { - QGraphicsLayoutItem* layout = m_layout->itemAt( m_index - 1 ); - m_prevGeom = layout ? layout->geometry() : QRectF(); - } - else - { - m_prevGeom = QRectF(); - } - - QGraphicsLayoutItem* layout = m_layout->itemAt( m_index + 1 ); - m_nextGeom = layout ? layout->geometry() : QRectF(); - - m_layout->removeItem( m_spacer ); - m_layout->insertItem( m_index, m_spacer ); -} - -void -Context::AppletItemOverlay::swapWithNext() -{ - DEBUG_BLOCK - m_index += 1; - - if ( m_index < m_layout->count() - 1 ) { - m_nextGeom = m_layout->itemAt( m_index + 1)->geometry(); - } else - { - m_nextGeom = QRectF(); - } - - m_prevGeom = m_layout->itemAt( m_index - 1 )->geometry(); - m_layout->removeItem( m_spacer ); - m_layout->insertItem( m_index, m_spacer ); -} - -void -Context::AppletItemOverlay::delaySyncGeometry() -{ - // we need to do this because it gets called in a round-about-way - // from our own mouseMoveEvent. if we call syncGeometry directly, - // we end up with a maze of duplicated and confused mouseMoveEvents - // of which only half are real (the other half being caused by the - // immediate call to syncGeometry!) - QTimer::singleShot( 0, this, SLOT(syncGeometry()) ); -} - -void -Context::AppletItemOverlay::syncGeometry() -{ - // DEBUG_BLOCK - setGeometry( m_applet->geometry().toRect() ); - // debug() << "setting overlay geometry to" << m_applet->geometry().toRect(); - - if( m_index > 0 ) - { - if( m_layout->itemAt( m_index - 1 ) ) - m_prevGeom = m_layout->itemAt( m_index - 1 )->geometry(); - } else - { - m_prevGeom = QRectF(); - } - - if( m_index < m_layout->count() - 1 ) - { - if( m_layout->itemAt( m_index + 1 ) ) - m_nextGeom = m_layout->itemAt( m_index + 1 )->geometry(); - } else - { - m_nextGeom = QRectF(); - } - //debug() << m_index << m_layout->count() << m_prevGeom << m_nextGeom; -} - diff --git a/src/context/toolbar/AppletItemOverlay.h b/src/context/toolbar/AppletItemOverlay.h deleted file mode 100644 index 29d5f89ae9..0000000000 --- a/src/context/toolbar/AppletItemOverlay.h +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_APPLET_ITEM_OVERLAY_H -#define AMAROK_APPLET_ITEM_OVERLAY_H - -#include - -class QGraphicsLinearLayout; -class QGraphicsWidget; -class QToolButton; - -// NOTE inspiration and code taken from kdebase/workspace/plasma/shells/desktop/panelappletoverlay.{h,cpp} - -namespace Plasma -{ - class Applet; -} - -namespace Context -{ - -class Applet; -class AppletToolbarAppletItem; - -class AppletItemOverlay : public QWidget -{ - Q_OBJECT - -public: - AppletItemOverlay(AppletToolbarAppletItem *applet, QGraphicsLinearLayout* layout, QWidget *parent); - ~AppletItemOverlay(); - - AppletToolbarAppletItem* applet(); -protected: - virtual void resizeEvent( QResizeEvent* ); - virtual void paintEvent(QPaintEvent *event); - virtual void mousePressEvent(QMouseEvent *event); - virtual void mouseMoveEvent(QMouseEvent *event); - virtual void mouseReleaseEvent(QMouseEvent *event); - virtual void enterEvent(QEvent *event); - virtual void leaveEvent(QEvent *event); - -Q_SIGNALS: - void moveApplet( Plasma::Applet*, int, int ); - void deleteApplet( Plasma::Applet* ); - -private Q_SLOTS: - void deleteApplet(); - void delaySyncGeometry(); - void syncGeometry(); - -private: - void swapWithPrevious(); - void swapWithNext(); - - AppletToolbarAppletItem *m_applet; - QGraphicsWidget *m_spacer; - QGraphicsLinearLayout *m_layout; - QRectF m_prevGeom; - QRectF m_nextGeom; - QPoint m_origin; - QToolButton* m_deleteIcon; - int m_offset; - int m_index; - bool m_itemHasSwapped; -}; - -} - -#endif diff --git a/src/context/toolbar/AppletToolbar.cpp b/src/context/toolbar/AppletToolbar.cpp deleted file mode 100644 index f0ace2e2ca..0000000000 --- a/src/context/toolbar/AppletToolbar.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * Copyright (c) 2009 Mark Kretschmann * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#include "AppletToolbar.h" - -#include "App.h" -#include "PaletteHandler.h" -#include "context/toolbar/AppletToolbarAddItem.h" -#include "context/toolbar/AppletToolbarAppletItem.h" -#include "context/toolbar/AppletToolbarConfigItem.h" -#include "context/Containment.h" -#include "core/support/Amarok.h" -#include "core/support/Debug.h" - -#include -#include -#include -#include -#include -#include -#include - -Context::AppletToolbar::AppletToolbar( QGraphicsItem* parent ) - : QGraphicsWidget( parent ) - , m_configMode( false ) - , m_appletLayout( 0 ) - , m_cont( 0 ) - , m_configItem( 0 ) -{ - Context::Containment* cont = dynamic_cast( parent ); - if( cont ) - { - m_cont = cont; - debug() << "applettoolbar created with a real containment"; - } - - setAcceptDrops( true ); - - m_appletLayout = new QGraphicsLinearLayout( Qt::Horizontal, this ); - - m_appletLayout->setContentsMargins( 3, 3, 3, 3 ); - m_appletLayout->setSpacing( 4 ); - - m_configItem = new AppletToolbarConfigItem( this ); - connect( m_configItem, SIGNAL(triggered()), this, SLOT(toggleConfigMode()) ); - m_appletLayout->addItem( m_configItem ); - m_appletLayout->setAlignment( m_configItem, Qt::AlignRight ); - m_appletLayout->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ); -} - -Context::AppletToolbar::~AppletToolbar() -{ -} - -void - -Context::AppletToolbar::setContainment( Containment * containment ) -{ - m_cont = containment; -} - -Context::Containment * -Context::AppletToolbar::containment() const -{ - return m_cont; -} - -void -Context::AppletToolbar::resizeEvent( QGraphicsSceneResizeEvent *event ) -{ - m_appletLayout->setGeometry( QRectF( QPointF( 0, 0 ), event->newSize() ) ); -} - -QSizePolicy -Context::AppletToolbar::sizePolicy () const -{ - return QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); -} - -bool -Context::AppletToolbar::configEnabled() const -{ - return m_configMode; -} - -QGraphicsLinearLayout* -Context::AppletToolbar::appletLayout() const -{ - return m_appletLayout; -} - - -// this takes care of the cleanup after the applet has been removed from the containment itself -void -Context::AppletToolbar::appletRemoved( Plasma::Applet* applet ) -{ - DEBUG_BLOCK - for( int i = 0; i < m_appletLayout->count(); i++ ) - { - AppletToolbarAppletItem* app = static_cast( m_appletLayout->itemAt(i) ); - if( app && app->applet() == applet ) - { - m_appletLayout->removeItem( app ); - app->deleteLater(); - } - } -} - -QSizeF -Context::AppletToolbar::sizeHint( Qt::SizeHint which, const QSizeF &constraint ) const -{ - Q_UNUSED( which ) - return QSizeF( constraint.width(), constraint.height() ); -} - - -void -Context::AppletToolbar::mousePressEvent( QGraphicsSceneMouseEvent *event ) -{ - Q_UNUSED( event ) -} - -// called when the containment is done successfully adding the applet, updates the toolbar -void -Context::AppletToolbar::appletAdded( Plasma::Applet* applet, int loc ) // SLOT -{ - DEBUG_BLOCK - - debug() << "inserting applet icon in position" << loc; - Context::AppletToolbarAppletItem* item = new Context::AppletToolbarAppletItem( this, applet ); - item->setConfigEnabled( m_configMode ); - connect( item, SIGNAL(appletChosen(Plasma::Applet*)), - this, SIGNAL(showApplet(Plasma::Applet*)) ); - - // add the item - m_appletLayout->insertItem( loc, item ); - - // notifications for others who need to know when the layout is done adding the applet - emit appletAddedToToolbar( applet, loc ); -} - -void -Context::AppletToolbar::toggleConfigMode() // SLOT -{ - DEBUG_BLOCK - if( !m_configMode ) - { - m_configMode = true; - emit showAppletExplorer(); - } - else - { - for( int i = 0; i < m_appletLayout->count(); i++ ) // tell each applet we are done configuring - { - Context::AppletToolbarAppletItem* appletItem = dynamic_cast< Context::AppletToolbarAppletItem* >( m_appletLayout->itemAt( i ) ); - if( appletItem ) - appletItem->setConfigEnabled( false ); - } - - m_configMode = false; - - emit hideAppletExplorer(); - } - emit configModeToggled(); -} - diff --git a/src/context/toolbar/AppletToolbar.h b/src/context/toolbar/AppletToolbar.h deleted file mode 100644 index a265071e83..0000000000 --- a/src/context/toolbar/AppletToolbar.h +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * Copyright (c) 2009 Mark Kretschmann * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_APPLET_TOOLBAR_H -#define AMAROK_APPLET_TOOLBAR_H - -#include "amarok_export.h" - -#include - -class QGraphicsItem; -class QGraphicsSceneResizeEvent; -class QPainter; -class QStyleOptionGraphicsItem; -class QSizePolicy; -class QGraphicsLinearLayout; - -// this provides a simple toolbar to switch between applets in the CV -namespace Plasma -{ - class Applet; -} - -namespace Context -{ - -class AppletToolbarAddItem; -class AppletToolbarConfigItem; -class Containment; - -class AppletToolbar : public QGraphicsWidget -{ - Q_OBJECT - public: - AppletToolbar( QGraphicsItem* parent = 0 ); - ~AppletToolbar(); - - QSizePolicy sizePolicy () const; - QGraphicsLinearLayout* appletLayout() const; - bool configEnabled() const; - - void appletRemoved( Plasma::Applet* applet ); - - void setContainment( Containment * containment ); - Containment* containment() const; - - Q_SIGNALS: - void showApplet( Plasma::Applet* ); - void appletAddedToToolbar( Plasma::Applet* applet, int loc ); - void moveApplet( Plasma::Applet*, int, int ); - void configModeToggled(); - void hideAppletExplorer(); - void showAppletExplorer(); - - protected: - virtual QSizeF sizeHint( Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const; - virtual void resizeEvent( QGraphicsSceneResizeEvent * event ); - virtual void mousePressEvent( QGraphicsSceneMouseEvent *event ); - - private Q_SLOTS: - void appletAdded( Plasma::Applet*, int ); - void toggleConfigMode(); - - private: - void newAddItem( int loc ); - - qreal m_width; - - bool m_configMode; - - QGraphicsLinearLayout* m_appletLayout; - - Containment* m_cont; - AppletToolbarConfigItem* m_configItem; -}; - -} - -#endif diff --git a/src/context/toolbar/AppletToolbarAddItem.cpp b/src/context/toolbar/AppletToolbarAddItem.cpp deleted file mode 100644 index aac65bd77b..0000000000 --- a/src/context/toolbar/AppletToolbarAddItem.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#include "AppletToolbarAddItem.h" - -#include "App.h" -#include "PaletteHandler.h" -#include "context/Containment.h" -#include "context/ContextView.h" -#include "core/support/Amarok.h" -#include "core/support/Debug.h" - -#include - -#include -#include -#include -#include -#include -#include - -#define MARGIN 10 -#define TOOLBAR_X_OFFSET 5 -#define TOOLBAR_Y_OFFSET 5 - -Context::AppletToolbarAddItem::AppletToolbarAddItem( QGraphicsItem* parent, Context::Containment* cont, bool fixedAdd ) - : AppletToolbarBase( parent ) - , m_iconPadding( 0 ) - , m_fixedAdd( fixedAdd ) - , m_showingAppletExplorer( false ) - , m_cont( cont ) - , m_icon( 0 ) - , m_label( 0 ) -{ - QAction* listAdd = new QAction( i18n( "Add Applets..." ), this ); - listAdd->setIcon( QIcon::fromTheme( "list-add" ) ); - listAdd->setVisible( true ); - listAdd->setEnabled( true ); - - connect( listAdd, SIGNAL(triggered()), this, SLOT(iconClicked()) ); - - m_icon = new Plasma::IconWidget( this ); - - m_icon->setAction( listAdd ); - m_icon->setText( QString() ); - m_icon->setToolTip( listAdd->text() ); - m_icon->setDrawBackground( false ); - m_icon->setOrientation( Qt::Horizontal ); - QSizeF iconSize; - if( m_fixedAdd ) - iconSize = m_icon->sizeFromIconSize( 22 ); - else - iconSize = m_icon->sizeFromIconSize( 11 ); - m_icon->setMinimumSize( iconSize ); - m_icon->setMaximumSize( iconSize ); - m_icon->resize( iconSize ); - m_icon->setZValue( zValue() + 1 ); - - m_label = new QGraphicsSimpleTextItem( i18n( "Add Applet..." ), this ); - m_label->hide(); - - if( m_cont ) - connect( m_cont->view(), SIGNAL(appletExplorerHid()), this, SLOT(appletExplorerHid()) ); - - if( m_fixedAdd ) - setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - else - setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred ); - // resize( QSizeF( 18, 24 ) ); -} - -Context::AppletToolbarAddItem::~AppletToolbarAddItem() -{} - -void -Context::AppletToolbarAddItem::appletExplorerHid() // SLOT -{ - m_showingAppletExplorer = false; -} -void -Context::AppletToolbarAddItem::iconClicked() // SLOT -{ - if( m_showingAppletExplorer ) - { - m_showingAppletExplorer = false; - emit hideAppletExplorer(); - } - else - { - m_showingAppletExplorer = true; - emit showAppletExplorer(); - } -} - -void -Context::AppletToolbarAddItem::resizeEvent( QGraphicsSceneResizeEvent * event ) -{ - Q_UNUSED( event ) - if( m_label->boundingRect().width() < ( boundingRect().width() - 2*m_icon->boundingRect().width() ) ) - // do we have size to show it? - { - m_icon->setPos( boundingRect().width() - m_icon->boundingRect().width(), ( boundingRect().height() / 2 ) - ( -m_icon->size().height() / 2 ) ); - m_label->setPos( ( boundingRect().width() / 2 ) - ( m_label->boundingRect().width() / 2 ), ( -boundingRect().height() / 2 ) - ( m_label->boundingRect().height() / 2 ) ); - m_label->show(); - } else - { - m_icon->setPos( ( boundingRect().width() / 2 ) - ( m_icon->boundingRect().width() / 2 ) , ( -boundingRect().height() / 2 ) - ( m_icon->size().height() / 2 ) ); - m_label->hide(); - } -} - - -QSizeF -Context::AppletToolbarAddItem::sizeHint( Qt::SizeHint which, const QSizeF & constraint ) const -{ - if( m_fixedAdd ) - // return QSizeF( m_icon->size().width() + 2 * m_iconPadding, QGraphicsWidget::sizeHint( which,constraint -//).height() ); - return QGraphicsWidget::sizeHint(which, constraint); - else - if( which == Qt::MinimumSize ) - return QSizeF(); - else - return QSizeF( m_icon->size().width() + 2 * m_iconPadding, QGraphicsWidget::sizeHint( which, constraint -).height() ); - -} - -void -Context::AppletToolbarAddItem::mousePressEvent( QGraphicsSceneMouseEvent * event ) -{ - DEBUG_BLOCK - emit showAppletExplorer(); - event->accept(); -} - diff --git a/src/context/toolbar/AppletToolbarAddItem.h b/src/context/toolbar/AppletToolbarAddItem.h deleted file mode 100644 index cacbe6d747..0000000000 --- a/src/context/toolbar/AppletToolbarAddItem.h +++ /dev/null @@ -1,73 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_APPLET_TOOLBAR_ADD_ITEM_H -#define AMAROK_APPLET_TOOLBAR_ADD_ITEM_H - - -#include "amarok_export.h" - -#include - -#include -#include "AppletToolbarBase.h" - -class QGraphicsItem; -class QGraphicsSceneResizeEvent; -class QGraphicsSimpleTextItem; -class QPainter; -class QStyleOptionGraphicsItem; - -namespace Context -{ - -class Containment; -class WidgetExplorer; - -class AppletToolbarAddItem : public AppletToolbarBase -{ - Q_OBJECT - public: - explicit AppletToolbarAddItem( QGraphicsItem* parent = 0, Containment* cont = 0, bool fixedAdd = false ); - ~AppletToolbarAddItem(); - - Q_SIGNALS: - void addApplet( const QString&, AppletToolbarAddItem* ); - void hideAppletExplorer(); - void showAppletExplorer(); - - public Q_SLOTS: - void appletExplorerHid(); - void iconClicked(); - - protected: - virtual void resizeEvent( QGraphicsSceneResizeEvent * event ); - virtual QSizeF sizeHint( Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const; - - void mousePressEvent( QGraphicsSceneMouseEvent * event ); - private: - int m_iconPadding; - bool m_fixedAdd; - bool m_showingAppletExplorer; - - Containment* m_cont; - Plasma::IconWidget* m_icon; - QGraphicsSimpleTextItem* m_label; -}; - -} - -#endif diff --git a/src/context/toolbar/AppletToolbarAppletItem.cpp b/src/context/toolbar/AppletToolbarAppletItem.cpp deleted file mode 100644 index 7687db9231..0000000000 --- a/src/context/toolbar/AppletToolbarAppletItem.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * Copyright (c) 2009 Simon Esneault * - * Copyright (c) 2009 Mark Kretschmann * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#include "AppletToolbarAppletItem.h" - -#include "core/support/Amarok.h" -#include "core/support/Debug.h" -#include "PaletteHandler.h" - -#include -#include - -#include - -#include -#include -#include -#include -#include - - -Context::AppletToolbarAppletItem::AppletToolbarAppletItem( QGraphicsItem* parent, Plasma::Applet* applet ) - : AppletToolbarBase( parent ) - , m_applet( applet ) - , m_label( 0 ) - , m_deleteIcon( 0 ) - , m_configEnabled( false ) -{ - m_label = new QGraphicsTextItem( this ); - - // Don't propagate opacity changes to the text label, as this reduces readability - m_label->setFlags( QGraphicsItem::ItemIgnoresParentOpacity ); - - if( m_applet ) - { - m_label->setPlainText( m_applet->name() ); - } - else - { - m_label->setPlainText( i18n("no applet name") ); - } - - setAcceptHoverEvents( true ); - m_label->setAcceptHoverEvents( true ); - QAction* delApplet = new QAction( i18n( "Remove Applet" ), this ); - delApplet->setIcon( QIcon::fromTheme( "edit-delete" ) ); - delApplet->setVisible( true ); - delApplet->setEnabled( true ); - - connect( delApplet, SIGNAL(triggered()), this, SLOT(deleteApplet()) ); - m_deleteIcon = addAction( delApplet, 18 ); - m_deleteIcon->hide(); - - setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - - paletteChanged( palette() ); - connect( The::paletteHandler(), SIGNAL(newPalette(QPalette)), SLOT(paletteChanged(QPalette)) ); -} - -Context::AppletToolbarAppletItem::~AppletToolbarAppletItem() -{ -} - -void -Context::AppletToolbarAppletItem::setConfigEnabled( bool config ) -{ - if( config && !m_configEnabled ) // switching to config mode - { - // center over top-right corner - m_deleteIcon->setPos( ( boundingRect().width() - (m_deleteIcon->boundingRect().width() ) ) - 1, -1 ); - } - else - m_deleteIcon->hide(); - - m_configEnabled = config; -} - -bool -Context::AppletToolbarAppletItem::configEnabled() -{ - return m_configEnabled; -} - -QRectF -Context::AppletToolbarAppletItem::delIconSceneRect() -{ - return mapToScene( m_deleteIcon->boundingRect() ).boundingRect(); -} - -void -Context::AppletToolbarAppletItem::resizeEvent( QGraphicsSceneResizeEvent *event ) -{ - Q_UNUSED( event ) - QFontMetrics fm( m_label->font() ); - if( m_configEnabled ) - { - m_deleteIcon->setPos( ( boundingRect().width() - (m_deleteIcon->boundingRect().width() ) ) - 1, -1 ); - - if( fm.width( m_applet->name() ) + m_deleteIcon->boundingRect().width() > boundingRect().width() ) - m_label->setPlainText( fm.elidedText( m_applet->name(), Qt::ElideRight, boundingRect().width() - m_deleteIcon->boundingRect().width() ) ); - else - m_label->setPlainText( m_applet->name() ); - - m_label->setPos( ( ( boundingRect().width() - m_deleteIcon->boundingRect().width() ) - m_label->boundingRect().width() ) / 2, - ( boundingRect().height() - m_label->boundingRect().height() ) / 2 ); - } - else - { - if( fm.width( m_applet->name() ) > boundingRect().width() ) - m_label->setPlainText( fm.elidedText( m_applet->name(), Qt::ElideRight, boundingRect().width() ) ); - else - m_label->setPlainText( m_applet->name() ); - - m_label->setPos( ( boundingRect().width() - m_label->boundingRect().width() ) / 2, - ( boundingRect().height() - m_label->boundingRect().height() ) / 2 ); - - } - - emit geometryChanged(); -} - -void -Context::AppletToolbarAppletItem::paletteChanged( const QPalette &palette ) -{ - m_label->setDefaultTextColor( palette.text().color() ); -} - -QVariant -Context::AppletToolbarAppletItem::itemChange( GraphicsItemChange change, const QVariant &value ) -{ - QVariant ret = QGraphicsWidget::itemChange( change, value ); - - if( change == ItemPositionHasChanged ) - emit geometryChanged(); - return ret; -} - -QSizeF -Context::AppletToolbarAppletItem::sizeHint( Qt::SizeHint which, const QSizeF & constraint ) const -{ - Q_UNUSED( constraint ) - if( which == Qt::MinimumSize ) - return QSizeF(); - else - return QSizeF( 10000, 10000 ); -} - -void -Context::AppletToolbarAppletItem::mousePressEvent( QGraphicsSceneMouseEvent * event ) -{ - emit appletChosen( m_applet ); - event->accept(); -} - -void -Context::AppletToolbarAppletItem::deleteApplet() -{ - DEBUG_BLOCK - m_applet->deleteLater(); -} - -Plasma::IconWidget* -Context::AppletToolbarAppletItem::addAction( QAction *action, int size ) -{ - if ( !action ) { - debug() << "ERROR!!! PASSED INVALID ACTION"; - return 0; - } - - Plasma::IconWidget *tool = new Plasma::IconWidget( this ); - - tool->setAction( action ); - tool->setText( QString() ); - tool->setToolTip( action->text() ); - tool->setDrawBackground( false ); - tool->setOrientation( Qt::Horizontal ); - QSizeF iconSize = tool->sizeFromIconSize( size ); - tool->setMinimumSize( iconSize ); - tool->setMaximumSize( iconSize ); - tool->resize( iconSize ); - - tool->hide(); - tool->setZValue( zValue() + 1000 ); - - return tool; -} - -void -Context::AppletToolbarAppletItem::hoverEnterEvent( QGraphicsSceneHoverEvent * ) -{ - QPropertyAnimation *animation = m_opacityAnimation.data(); - if( !animation ) - { - animation = new QPropertyAnimation( this, "opacity" ); - animation->setDuration( 250 ); - animation->setStartValue( 0.3 ); - animation->setEndValue( 1.0 ); - m_opacityAnimation = animation; - } - else if( animation->state() == QAbstractAnimation::Running ) - animation->stop(); - - animation->setEasingCurve( QEasingCurve::OutCubic ); - animation->setDirection( QAbstractAnimation::Backward ); - animation->start( QAbstractAnimation::KeepWhenStopped ); -} - -void -Context::AppletToolbarAppletItem::hoverLeaveEvent( QGraphicsSceneHoverEvent * ) -{ - QPropertyAnimation *animation = m_opacityAnimation.data(); - if( !animation ) - { - animation = new QPropertyAnimation( this, "opacity" ); - animation->setDuration( 250 ); - animation->setStartValue( 0.3 ); - animation->setEndValue( 1.0 ); - m_opacityAnimation = animation; - } - else if( animation->state() == QAbstractAnimation::Running ) - animation->pause(); - - animation->setEasingCurve( QEasingCurve::OutCubic ); - animation->setDirection( QAbstractAnimation::Forward ); - animation->start( QAbstractAnimation::DeleteWhenStopped ); -} - - diff --git a/src/context/toolbar/AppletToolbarAppletItem.h b/src/context/toolbar/AppletToolbarAppletItem.h deleted file mode 100644 index 0231839b6c..0000000000 --- a/src/context/toolbar/AppletToolbarAppletItem.h +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * Copyright (c) 2009 Simon Esneault * - * Copyright (c) 2009 Mark Kretschmann * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_APPLET_TOOLBAR_APPLET_ITEM_H -#define AMAROK_APPLET_TOOLBAR_APPLET_ITEM_H - -#include "AppletToolbarBase.h" - -#include - -class QPropertyAnimation; -class QPalette; - -namespace Plasma -{ - class Animation; - class Applet; - class IconWidget; -} - -namespace Context -{ - -class AppletToolbarAppletItem : public AppletToolbarBase -{ - Q_OBJECT - - public: - explicit AppletToolbarAppletItem( QGraphicsItem* parent = 0, Plasma::Applet* applet = 0 ); - ~AppletToolbarAppletItem(); - - void setConfigEnabled( bool config ); - bool configEnabled(); - - Plasma::Applet* applet() { return m_applet; } - // needed for the overlay to check if the click is over the del icon - QRectF delIconSceneRect(); - - Q_SIGNALS: - void appletChosen( Plasma::Applet* ); - void geometryChanged(); - - protected: - virtual void resizeEvent( QGraphicsSceneResizeEvent * event ); - virtual QSizeF sizeHint( Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const; - - /** - * Reimplemented from QGraphicsItem - */ - virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value); - - virtual void hoverEnterEvent( QGraphicsSceneHoverEvent * event ); - virtual void hoverLeaveEvent( QGraphicsSceneHoverEvent * event ); - - void mousePressEvent( QGraphicsSceneMouseEvent * event ); - - private Q_SLOTS: - void deleteApplet(); - void paletteChanged( const QPalette &palette ); - - private: - Plasma::IconWidget* addAction( QAction *action, int size ); - - Plasma::Applet* m_applet; - QGraphicsTextItem* m_label; - - QWeakPointer m_opacityAnimation; - - Plasma::IconWidget* m_deleteIcon; - - bool m_configEnabled; -}; - -} // namespace - -#endif diff --git a/src/context/toolbar/AppletToolbarBase.cpp b/src/context/toolbar/AppletToolbarBase.cpp deleted file mode 100644 index 6a0af33da4..0000000000 --- a/src/context/toolbar/AppletToolbarBase.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#include "AppletToolbarBase.h" - -#include - -Context::AppletToolbarBase::AppletToolbarBase( QGraphicsItem* parent, Qt::WindowFlags wFlags ) - : QGraphicsWidget( parent, wFlags ) -{ - -} - -Context::AppletToolbarBase::~AppletToolbarBase() -{} - -void -Context::AppletToolbarBase::paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget ) -{ - Q_UNUSED( option ) - Q_UNUSED( widget ) - - painter->save(); - painter->setRenderHint( QPainter::Antialiasing ); - - QColor topColor = palette().color( QPalette::Active, QPalette::Button ); - QColor bottomColor = topColor; - topColor.setAlpha( 200 ); - bottomColor.setAlpha( 100 ); - qreal radius = 3; - qreal boundWidth = boundingRect().width(); - qreal boundHeight = boundingRect().height(); - - // draw top half of rounded applet - QPainterPath path; - path.moveTo( 0, boundHeight / 2 ); - path.lineTo( 0, radius ); - path.quadTo( 0, 0, radius, 0 ); - path.lineTo( boundWidth - radius, 0 ); - path.quadTo( boundWidth, 0, boundWidth, radius ); - path.lineTo( boundWidth, boundHeight / 2 ); - path.lineTo( 0, boundHeight / 2 ); - - painter->fillPath( path, topColor ); - QPainterPath bottom; - bottom.moveTo( 0, boundHeight / 2 ); - bottom.lineTo( 0, boundHeight - radius ); - bottom.quadTo( 0, boundHeight, radius, boundHeight ); - bottom.lineTo( boundWidth - radius, boundHeight ); - bottom.quadTo( boundWidth, boundHeight, boundWidth, boundHeight - radius ); - bottom.lineTo( boundWidth, boundHeight / 2 ); - bottom.lineTo( 0, boundHeight / 2 ); - - painter->fillPath( bottom, bottomColor ); - painter->restore(); -} diff --git a/src/context/toolbar/AppletToolbarConfigItem.cpp b/src/context/toolbar/AppletToolbarConfigItem.cpp deleted file mode 100644 index e5d693fd46..0000000000 --- a/src/context/toolbar/AppletToolbarConfigItem.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#include "AppletToolbarConfigItem.h" - -#include "App.h" -#include "PaletteHandler.h" - -#include - -#include - -#include -#include -#include -#include - -Context::AppletToolbarConfigItem::AppletToolbarConfigItem( QGraphicsItem* parent ) - : AppletToolbarBase( parent ) - , m_iconPadding( 2 ) - , m_icon( 0 ) -{ - QAction* listAdd = new QAction( i18n( "Configure Applets..." ), this ); - listAdd->setIcon( QIcon::fromTheme( "configure" ) ); - listAdd->setVisible( true ); - listAdd->setEnabled( true ); - - connect( listAdd, SIGNAL(triggered()), this, SIGNAL(triggered()) ); - - m_icon = new Plasma::IconWidget( this ); - - m_icon->setAction( listAdd ); - m_icon->setText( QString() ); - m_icon->setToolTip( listAdd->text() ); - m_icon->setDrawBackground( false ); - m_icon->setOrientation( Qt::Horizontal ); - QSizeF iconSize = m_icon->sizeFromIconSize( 22 ); - m_icon->setMinimumSize( iconSize ); - m_icon->setMaximumSize( iconSize ); - m_icon->resize( iconSize ); - m_icon->setZValue( zValue() + 1 ); - - setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred ); - -} - -Context::AppletToolbarConfigItem::~AppletToolbarConfigItem() -{} - -void -Context::AppletToolbarConfigItem::resizeEvent( QGraphicsSceneResizeEvent * event ) -{ - Q_UNUSED( event ) - // center horizontally and vertically - m_icon->setPos( ( boundingRect().width() / 2 ) - ( m_icon->boundingRect().width() / 2 ) , ( boundingRect().height() / 2 ) - ( m_icon->size().height() / 2 ) ); -} - -QSizeF -Context::AppletToolbarConfigItem::sizeHint( Qt::SizeHint which, const QSizeF & constraint ) const -{ - return QSizeF( m_icon->size().width() + 2 * m_iconPadding , QGraphicsWidget::sizeHint( which, constraint ).height() ); -} - -void -Context::AppletToolbarConfigItem::mousePressEvent( QGraphicsSceneMouseEvent *event ) -{ - Q_UNUSED( event ) - emit triggered(); -} - diff --git a/src/context/toolbar/AppletToolbarConfigItem.h b/src/context/toolbar/AppletToolbarConfigItem.h deleted file mode 100644 index 998287748f..0000000000 --- a/src/context/toolbar/AppletToolbarConfigItem.h +++ /dev/null @@ -1,59 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_APPLET_TOOLBAR_CONFIG_ITEM_H -#define AMAROK_APPLET_TOOLBAR_CONFIG_ITEM_H - -#include -#include "AppletToolbarBase.h" - - -class QPainter; -class QSizePolicy; -class QStyleOptionGraphicsItem; - -namespace Plasma -{ - class IconWidget; -} - -namespace Context -{ - -class AppletToolbarConfigItem : public AppletToolbarBase -{ - Q_OBJECT - public: - AppletToolbarConfigItem( QGraphicsItem* parent = 0 ); - ~AppletToolbarConfigItem(); - - Q_SIGNALS: - void triggered(); - - protected: - virtual void resizeEvent( QGraphicsSceneResizeEvent * event ); - virtual QSizeF sizeHint( Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const; - - void mousePressEvent( QGraphicsSceneMouseEvent * event ); - private: - int m_iconPadding; - - Plasma::IconWidget* m_icon; -}; - -} - -#endif diff --git a/src/context/tools/CMakeLists.txt b/src/context/tools/CMakeLists.txt index d032876147..5cb77d869b 100644 --- a/src/context/tools/CMakeLists.txt +++ b/src/context/tools/CMakeLists.txt @@ -1,17 +1,17 @@ set(amarokpkg_SRCS amarokpkg.cpp + ../AmarokContextPackageStructure.cpp ) add_executable(amarokpkg ${amarokpkg_SRCS}) target_link_libraries(amarokpkg KF5::I18n - KF5::Plasma KF5::ConfigWidgets - KF5::Service + KF5::Package Qt5::DBus Qt5::Core Qt5::Widgets ) install(TARGETS amarokpkg ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/context/tools/amarokpkg.cpp b/src/context/tools/amarokpkg.cpp index 792ee82d08..6efe8d97ca 100644 --- a/src/context/tools/amarokpkg.cpp +++ b/src/context/tools/amarokpkg.cpp @@ -1,206 +1,209 @@ /**************************************************************************************** * Copyright (c) 2008 Aaron Seigo * * Copyright (c) 2009 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ +#include "../AmarokContextPackageStructure.h" + #include #include #include #include #include #include #include +#include #include #include #include -#include -#include #include -#include #include -#include #include +#include #include -#include static const char description[] = "Install, list, remove Amarok applets"; -static const char version[] = "0.1"; +static const char version[] = "0.2"; void output(const QString &msg) { std::cout << msg.toLocal8Bit().constData() << std::endl; } void runKbuildsycoca() { QDBusInterface dbus("org.kde.kded", "/kbuildsycoca", "org.kde.kbuildsycoca"); dbus.call(QDBus::NoBlock, "recreate"); } -QStringList packages(const QString& type) +QStringList packages() { + auto loader = KPackage::PackageLoader::self(); + auto structure = new AmarokContextPackageStructure; + loader->addKnownPackageStructure(QStringLiteral("Amarok/ContextApplet"), structure); + auto applets = loader->findPackages(QStringLiteral("Amarok/ContextApplet"), + QString(), + [] (const KPluginMetaData &data) + { return data.serviceTypes().contains(QStringLiteral("Amarok/ContextApplet")); }); + QStringList result; - KService::List services = KServiceTypeTrader::self()->query("Plasma/" + type, "'amarok' ~ [X-KDE-ParentApp]"); - foreach(const KService::Ptr &service, services) { - result << service->property("X-KDE-PluginInfo-Name", QVariant::String).toString(); - } + + for (const auto &applet : applets) + result << applet.pluginId(); + return result; } -void listPackages(const QString& type) +void listPackages() { - QStringList list = packages(type); + QStringList list = packages(); list.sort(); foreach(const QString& package, list) { output(package); } } int main(int argc, char **argv) { KAboutData aboutData("amarokpkg", i18n("Amarok Applet Manager"), version, i18n(description), KAboutLicense::GPL, i18n("(C) 2008, Aaron Seigo, (C) 2009, Leo Franchi")); aboutData.addAuthor( i18n("Aaron Seigo"), i18n("Original author"), "aseigo@kde.org" ); aboutData.addAuthor( i18n( "Leo Franchi" ), i18n( "Developer" ) , "lfranchi@kde.org" ); QApplication app(argc, argv); app.setApplicationName("amarokpkg"); app.setOrganizationDomain("kde.org"); app.setApplicationDisplayName(i18n("Amarok Applet Manager")); app.setApplicationVersion(version); /** * @TODO: DO WE NEED THIS ? */ KAboutData::setApplicationData(aboutData); QCommandLineParser parser; - parser.addVersionOption(); - parser.addHelpOption(); - aboutData.setupCommandLine(&parser); - parser.process(app); - aboutData.processCommandLine(&parser); - parser.addOption(QCommandLineOption(QStringList() << "g" << "global", i18n("For install or remove, operates on applets installed for all users."))); parser.addOption(QCommandLineOption(QStringList() << "s" << "i" << "install ", i18nc("Do not translate ", "Install the applet at "))); parser.addOption(QCommandLineOption(QStringList() << "u" << "upgrade ", i18nc("Do not translate ", "Upgrade the applet at "))); parser.addOption(QCommandLineOption(QStringList() << "l" << "list", i18n("Most installed applets"))); parser.addOption(QCommandLineOption(QStringList() << "r" << "remove ", i18nc("Do not translate ", "Remove the applet named "))); parser.addOption(QCommandLineOption(QStringList() << "p" << "packageroot ", i18n("Absolute path to the package root. If not supplied, then the standard data directories for this KDE session will be searched instead."))); - QString packageRoot = "plasma/plasmoids/"; - QString servicePrefix = "amarok-applet-"; - QString pluginType = "Applet"; + parser.addVersionOption(); + parser.addHelpOption(); + aboutData.setupCommandLine(&parser); + parser.process(app); + aboutData.processCommandLine(&parser); + + QString packageRoot = "kpackage/amarok"; KPackage::Package *installer = 0; if (parser.isSet("list")) { - listPackages(pluginType); + listPackages(); } else { // install, remove or upgrade - if (!installer) { - installer = new KPackage::Package(); - installer->setContentsPrefixPaths(QStringList() << servicePrefix); - } + if (!installer) + installer = new KPackage::Package(new AmarokContextPackageStructure); if (parser.isSet("packageroot")) { packageRoot = parser.value("packageroot"); } else if (parser.isSet("global")) { packageRoot = QStandardPaths::locate(QStandardPaths::GenericDataLocation, packageRoot); } else { //@FIXME Maybe we need to check whether the folders exist or not. packageRoot = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + packageRoot; } QString package; QString packageFile; if (parser.isSet("remove")) { package = parser.value("remove"); } else if (parser.isSet("upgrade")) { package = parser.value("upgrade"); } else if (parser.isSet("install")) { package = parser.value("install"); } if (!QDir::isAbsolutePath(package)) { packageFile = QDir(QDir::currentPath() + '/' + package).absolutePath(); } else { packageFile = package; } if (parser.isSet("remove") || parser.isSet("upgrade")) { installer->setPath(packageFile); KPluginMetaData metadata = installer->metadata(); - QString pluginName; + QString pluginId; if (metadata.name().isEmpty()) { // plugin name given in command line - pluginName = package; + pluginId = package; } else { // Parameter was a plasma package, get plugin name from the package - pluginName = metadata.name(); + pluginId = metadata.pluginId(); } - QStringList installed = packages(pluginType); - if (installed.contains(pluginName)) { - if (installer->uninstall(pluginName, packageRoot)) { - output(i18n("Successfully removed %1", pluginName)); + QStringList installed = packages(); + if (installed.contains(pluginId)) { + if (installer->uninstall(pluginId, packageRoot)) { + output(i18n("Successfully removed %1", pluginId)); } else if (!parser.isSet("upgrade")) { - output(i18n("Removal of %1 failed.", pluginName)); + output(i18n("Removal of %1 failed.", pluginId)); delete installer; return 1; } } else { - output(i18n("Plugin %1 is not installed.", pluginName)); + output(i18n("Plugin %1 is not installed.", pluginId)); } } if (parser.isSet("install") || parser.isSet("upgrade")) { if (installer->install(packageFile, packageRoot)) { output(i18n("Successfully installed %1", packageFile)); runKbuildsycoca(); } else { output(i18n("Installation of %1 failed.", packageFile)); delete installer; return 1; } } if (package.isEmpty()) { QTextStream out(stdout); out << i18nc("No option was given, this is the error message telling the user he needs at least one, do not translate install, remove, upgrade nor list", "One of install, remove, upgrade or list is required."); } else { runKbuildsycoca(); } } delete installer; return 0; } diff --git a/src/context/widgets/AppletHeader.cpp b/src/context/widgets/AppletHeader.cpp deleted file mode 100644 index 634cd2fa88..0000000000 --- a/src/context/widgets/AppletHeader.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2010 Rick W. Chen * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "AppletHeader" - -#include "AppletHeader.h" - -#include "TextScrollingWidget.h" -#include "core/support/Debug.h" - -#include - -#include -#include -#include -#include -#include - -Context::AppletHeader::AppletHeader( QGraphicsItem *parent, Qt::WindowFlags wFlags ) - : QGraphicsWidget( parent, wFlags ) - , m_mainLayout( new QGraphicsLinearLayout( Qt::Horizontal, this ) ) - , m_leftLayout( new QGraphicsLinearLayout( Qt::Horizontal ) ) - , m_rightLayout( new QGraphicsLinearLayout( Qt::Horizontal ) ) - , m_titleWidget( new TextScrollingWidget( this ) ) -{ - QFont labelFont; - labelFont.setPointSize( labelFont.pointSize() + 2 ); - m_titleWidget->setFont( labelFont ); - m_titleWidget->setDrawBackground( true ); - m_titleWidget->setText( i18n( "Context Applet" ) ); - m_titleWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); - - m_mainLayout->setSpacing( 4 ); - m_mainLayout->addItem( m_leftLayout ); - m_mainLayout->addItem( m_titleWidget ); - m_mainLayout->addItem( m_rightLayout ); - m_mainLayout->setContentsMargins( 2, 4, 2, 2 ); - m_mainLayout->setStretchFactor( m_titleWidget, 10000 ); - m_mainLayout->setAlignment( m_leftLayout, Qt::AlignLeft ); - m_mainLayout->setAlignment( m_titleWidget, Qt::AlignHCenter ); - m_mainLayout->setAlignment( m_rightLayout, Qt::AlignRight ); - m_mainLayout->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); - m_leftLayout->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Fixed ); - m_rightLayout->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Fixed ); - - m_height = 4 + 2 + m_titleWidget->size().height() - + QApplication::style()->pixelMetric( QStyle::PM_LayoutTopMargin ) - + QApplication::style()->pixelMetric( QStyle::PM_LayoutBottomMargin ); -} - -Context::AppletHeader::~AppletHeader() -{ -} - -qreal -Context::AppletHeader::height() const -{ - return m_height; -} - -void -Context::AppletHeader::addIcon( Plasma::IconWidget *icon, Qt::Alignment align ) -{ - if( !icon ) - return; - - clearDummyItems(); - if( align == Qt::AlignLeft ) - m_leftLayout->addItem( icon ); - else if( align == Qt::AlignRight ) - m_rightLayout->addItem( icon ); - else - return; - - const int diff = m_leftLayout->count() - m_rightLayout->count(); - QGraphicsLinearLayout *layout = ( diff > 0 ) ? m_rightLayout : m_leftLayout; - int index = ( diff > 0 ) ? 0 : -1; - for( int i = 0, count = qAbs( diff ); i < count; ++i ) - { - QGraphicsWidget *dummy = new QGraphicsWidget( this ); - dummy->setMinimumSize( icon->size() ); - dummy->setMaximumSize( icon->size() ); - dummy->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); - m_dummyItems << dummy; - layout->insertItem( index, dummy ); - } -} - -QString -Context::AppletHeader::titleText() const -{ - return m_titleWidget->text(); -} - -void -Context::AppletHeader::setTitleText( const QString &text ) -{ - m_titleWidget->setScrollingText( text ); -} - -TextScrollingWidget * -Context::AppletHeader::textScrollingWidget() -{ - return m_titleWidget; -} - -void -Context::AppletHeader::clearDummyItems() -{ - if( m_dummyItems.isEmpty() ) - return; - - QList toRemove; - for( int i = 0, count = m_leftLayout->count(); i < count; ++i ) - { - QGraphicsLayoutItem *item = m_leftLayout->itemAt( i ); - if( m_dummyItems.contains( item ) ) - { - m_dummyItems.removeAll( item ); - toRemove << i; - } - } - while( !toRemove.isEmpty() ) - { - int index = toRemove.takeLast(); - QGraphicsLayoutItem *item = m_leftLayout->itemAt( index ); - m_leftLayout->removeAt( index ); - delete item; - } - toRemove.clear(); - - for( int i = 0, count = m_rightLayout->count(); i < count; ++i ) - { - QGraphicsLayoutItem *item = m_rightLayout->itemAt( i ); - if( m_dummyItems.contains( item ) ) - { - m_dummyItems.removeAll( item ); - toRemove << i; - } - } - while( !toRemove.isEmpty() ) - { - int index = toRemove.takeLast(); - QGraphicsLayoutItem *item = m_rightLayout->itemAt( index ); - m_rightLayout->removeAt( index ); - delete item; - } - m_dummyItems.clear(); -} - diff --git a/src/context/widgets/AppletHeader.h b/src/context/widgets/AppletHeader.h deleted file mode 100644 index cc10260e7f..0000000000 --- a/src/context/widgets/AppletHeader.h +++ /dev/null @@ -1,67 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2010 Rick W. Chen * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef CONTEXTAPPLETHEADER_H -#define CONTEXTAPPLETHEADER_H - -#include "amarok_export.h" - -#include - -class TextScrollingWidget; -class QGraphicsLinearLayout; - -namespace Plasma { - class IconWidget; -} - -namespace Context -{ - -class AMAROK_EXPORT AppletHeader : public QGraphicsWidget -{ - Q_OBJECT - Q_PROPERTY( QString titleText READ titleText WRITE setTitleText ) - Q_PROPERTY( qreal height READ height ) - -public: - AppletHeader( QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0 ); - ~AppletHeader(); - - qreal height() const; - - QString titleText() const; - void setTitleText( const QString &text ); - - void addIcon( Plasma::IconWidget *icon, Qt::Alignment align ); - - TextScrollingWidget *textScrollingWidget(); - -private: - void clearDummyItems(); - - qreal m_height; - QGraphicsLinearLayout *m_mainLayout; - QGraphicsLinearLayout *m_leftLayout; - QGraphicsLinearLayout *m_rightLayout; - QList m_dummyItems; - TextScrollingWidget *m_titleWidget; - Q_DISABLE_COPY( AppletHeader ) -}; - -} // namespace Context - -#endif // CONTEXTAPPLETHEADER_H diff --git a/src/context/widgets/ContainmentArrow.cpp b/src/context/widgets/ContainmentArrow.cpp deleted file mode 100644 index f2b38746ed..0000000000 --- a/src/context/widgets/ContainmentArrow.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#include "ContainmentArrow.h" - -#include "core/support/Debug.h" - -#include -#include -#include - -#include -#include - -// by default this item just grabs the coords of its parents, and positions itself to maximise in the -// direction that it is pointing to. - -namespace Context -{ - -ContainmentArrow::ContainmentArrow( QGraphicsItem *parent, int direction ) : - QGraphicsWidget( parent ), - m_showing( false ), - m_disabled( false ), - m_timer( 0 ) , - m_arrowSvg( 0 ), - m_containment( 0 ) -{ - DEBUG_BLOCK - - setZValue( 10000000 ); - setFlag( ItemClipsToShape, false ); - setFlag( ItemClipsChildrenToShape, false ); - setFlag( ItemIgnoresTransformations, true ); - setAcceptsHoverEvents( true ); - - m_timer = new QTimer( this ); - connect( m_timer, SIGNAL(timeout()), this, SLOT(timeToHide()) ); - - m_arrowSvg = new Context::Svg( this ); - m_arrowSvg->setImagePath( KStandardDirs::locate( "data", "amarok/images/navigation_arrows.svg" ) ); - m_arrowSvg->setContainsMultipleImages( true ); - - debug() << "got svg path: " << m_arrowSvg->imagePath(); - - m_containment = dynamic_cast( parent ); - if( !m_containment ) - { - debug() << "ERROR! ContainmentArrow needs to be passed a Containment parent!"; - } - else - { - qreal height = 0, width = 0; - switch( direction ) - { - case DOWN: - case UP: - { - width = m_containment->size().width(); - debug() << " up/down arrow original width: " << width; - QRectF arrow; - if( direction == UP ) - arrow = m_arrowSvg->elementRect( "up_arrow" ); - else - arrow = m_arrowSvg->elementRect( "down_arrow" ); - m_aspectRatio = arrow.width() / arrow.height(); - - height = width / m_aspectRatio; - debug() << "up/down arrow m_aspectRatio and height is: " << m_aspectRatio << height; - debug() << "got UP/DOWN arrow with sizes: " << width << height; - break; - } - case LEFT: - case RIGHT: - { - height = m_containment->size().height(); - QRectF arrow; - debug() << " left/right arrow original height: " << height; - - if( direction == LEFT ) - arrow = m_arrowSvg->elementRect( "left_arrow" ); - else - arrow = m_arrowSvg->elementRect( "right_arrow" ); - m_aspectRatio = arrow.width() / arrow.height(); - - width = height * m_aspectRatio; - debug() << "left/right arrow m_aspectRatio and width is: " << m_aspectRatio << width; - - debug() << "got RIGHT/LEFT arrow with sizes: " << width << height; - break; - } - default: - error() << "Unspecified state, setting 0 size for arrows"; - } - m_size = QSize( width, height ); - } - - debug() << "ContainmentArrow: SETTING DIRECTION TO: " << direction; - m_arrowDirection = direction; -} - -ContainmentArrow::~ContainmentArrow() -{ - // delete m_timer; - // delete m_arrowSvg; - // delete m_containment; -} - - -QRectF -ContainmentArrow::boundingRect() const -{ - return QRectF( QPointF( 0, 0 ), m_size ); - -} - -QSize -ContainmentArrow::size() const -{ - return m_size; -} - -void -ContainmentArrow::resize( const QSizeF newSize ) -{ - DEBUG_BLOCK - prepareGeometryChange(); - switch( m_arrowDirection ) - { - case DOWN: // anchor to new width - case UP: - { - qreal newheight = newSize.width() / m_aspectRatio; - m_size = QSize( newSize.width(), newheight ); - break; - } - case LEFT: - case RIGHT: // anchor on height - { - qreal newWidth = newSize.height() * m_aspectRatio; - m_size = QSize( newWidth, newSize.height() ); - break; - } - } - m_arrowSvg->resize( m_size ); - debug() << "updating new size to: " << m_size; - update(); -} - -void -ContainmentArrow::enable() -{ - m_disabled = false; -} - -void ContainmentArrow::disable() -{ - m_disabled = true; -} - - -void -ContainmentArrow::show() -{ - // DEBUG_BLOCK - m_showing = true; - Plasma::Animation *fadeAnimation = m_fadeAnimation.data(); - if( !fadeAnimation ) - { - fadeAnimation = Plasma::Animator::create( Plasma::Animator::FadeAnimation ); - fadeAnimation->setTargetWidget( this ); - m_fadeAnimation = fadeAnimation; - } - else if( fadeAnimation->state() == QAbstractAnimation::Running ) - { - fadeAnimation->pause(); - } - - fadeAnimation->setProperty( "direction", QAbstractAnimation::Forward ); - fadeAnimation->start( QAbstractAnimation::DeleteWhenStopped ); -} - -void -ContainmentArrow::hide() -{ - - m_showing = false; - update(); - - Plasma::Animation *fadeAnimation = m_fadeAnimation.data(); - if( !fadeAnimation ) - { - fadeAnimation = Plasma::Animator::create( Plasma::Animator::FadeAnimation ); - fadeAnimation->setTargetWidget( this ); - m_fadeAnimation = fadeAnimation; - } - else if( fadeAnimation->state() == QAbstractAnimation::Running ) - { - fadeAnimation->pause(); - } - - fadeAnimation->setProperty( "direction", QAbstractAnimation::Backward ); - fadeAnimation->start( QAbstractAnimation::DeleteWhenStopped ); -} - -void -ContainmentArrow::paint( QPainter *p, const QStyleOptionGraphicsItem *option, QWidget *widget ) -{ - // DEBUG_BLOCK - - Q_UNUSED( option ) - Q_UNUSED( widget ) - - if( !m_showing ) - return; - - p->save(); - - if( m_arrowDirection == UP ) - m_arrowSvg->paint( p, boundingRect(), "up_arrow" ); - else if( m_arrowDirection == DOWN ) - m_arrowSvg->paint( p, boundingRect(), "down_arrow" ); - else if( m_arrowDirection == LEFT ) - m_arrowSvg->paint( p, boundingRect(), "left_arrow" ); - else if( m_arrowDirection == RIGHT ) - m_arrowSvg->paint( p, boundingRect(), "right_arrow" ); - p->restore(); - -} - - -void -ContainmentArrow::hoverEnterEvent( QGraphicsSceneHoverEvent *event ) -{ - // DEBUG_BLOCK - if( m_hovering || m_disabled ) - return; - if( m_timer->isActive() ) - m_timer->stop(); - m_hovering = true; - m_showing = true; - - Plasma::Animation *fadeAnimation = m_fadeAnimation.data(); - if( !fadeAnimation ) - { - fadeAnimation = Plasma::Animator::create( Plasma::Animator::FadeAnimation ); - fadeAnimation->setTargetWidget( this ); - m_fadeAnimation = fadeAnimation; - } - else if( fadeAnimation->state() == QAbstractAnimation::Running ) - { - fadeAnimation->pause(); - } - - fadeAnimation->setProperty( "direction", QAbstractAnimation::Forward ); - fadeAnimation->start( QAbstractAnimation::KeepWhenStopped ); - - QGraphicsItem::hoverEnterEvent( event ); -} - -void -ContainmentArrow::hoverLeaveEvent( QGraphicsSceneHoverEvent *event ) -{ - // DEBUG_BLOCK - if( m_disabled ) - return; - m_hovering = false; - // m_showing = false; - - // m_timer->start( 100 ); - timeToHide(); - - QGraphicsItem::hoverLeaveEvent( event ); -} - - -void -ContainmentArrow::timeToHide() -{ - m_timer->stop(); - - Plasma::Animation *fadeAnimation = m_fadeAnimation.data(); - if( !fadeAnimation ) - { - fadeAnimation = Plasma::Animator::create( Plasma::Animator::FadeAnimation ); - fadeAnimation->setTargetWidget( this ); - m_fadeAnimation = fadeAnimation; - } - else if( fadeAnimation->state() == QAbstractAnimation::Running ) - { - fadeAnimation->pause(); - } - - fadeAnimation->setProperty( "direction", QAbstractAnimation::Backward ); - fadeAnimation->start( QAbstractAnimation::DeleteWhenStopped ); -} - -void -ContainmentArrow::mousePressEvent( QGraphicsSceneMouseEvent *event ) -{ - // DEBUG_BLOCK - event->accept(); -} - -void -ContainmentArrow::mouseReleaseEvent( QGraphicsSceneMouseEvent *event ) -{ - // DEBUG_BLOCK - if ( boundingRect().contains( event->pos() ) && !m_disabled ) - { - debug() << "EMITTING changeContainment!"; - emit changeContainment( m_arrowDirection ); - // TODO add up/down - if( m_timer->isActive() ) - m_timer->stop(); - - } -} - -} - diff --git a/src/context/widgets/ContainmentArrow.h b/src/context/widgets/ContainmentArrow.h deleted file mode 100644 index 29acb604d4..0000000000 --- a/src/context/widgets/ContainmentArrow.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -// -// This provides a simple on-hover SVG arrow to switch between containments. -// - -#ifndef CONTAINMENT_ARROW_H -#define CONTAINMENT_ARROW_H - -#include "amarok_export.h" -#include "context/Svg.h" - -#include -#include -#include -#include - -enum ArrowDirection { - LEFT, - RIGHT, - DOWN, - UP, - DOWN_RIGHT, - DOWN_LEFT, - UP_RIGHT, - UP_LEFT -}; - -namespace Plasma { - class Animation; -} - -#include "context/Containment.h" // needs ArrowDirection to be defined first - -namespace Context { - -class SvgRenderJob; - -class ContainmentArrow : public QGraphicsWidget -{ - Q_OBJECT - Q_INTERFACES( QGraphicsItem ) -public: - explicit ContainmentArrow( QGraphicsItem *parent = 0, int direction = RIGHT ); - ~ContainmentArrow(); - - virtual QRectF boundingRect() const; - - QSize size() const; - void resize( const QSizeF newSize ); - - void enable(); - void disable(); - -public Q_SLOTS: - void show(); - void hide(); - -Q_SIGNALS: - void changeContainment( int to ); - -protected: - virtual void paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0 ); - void hoverEnterEvent( QGraphicsSceneHoverEvent *event ); - void hoverLeaveEvent( QGraphicsSceneHoverEvent *event ); - void mousePressEvent( QGraphicsSceneMouseEvent *event ); - void mouseReleaseEvent( QGraphicsSceneMouseEvent *event ); - - -private Q_SLOTS: - void timeToHide(); - -private: - QSize m_size; - qreal m_animHighlightFrame; - int m_animHighlightId; - bool m_hovering; - bool m_showing; - bool m_disabled; - qreal m_aspectRatio; - - QTimer *m_timer; - - Svg* m_arrowSvg; - int m_arrowDirection; - Plasma::Containment *m_containment; - QWeakPointer m_fadeAnimation; -}; - - // namespace - -} -#endif diff --git a/src/context/widgets/ContainmentSelectionLayer.cpp b/src/context/widgets/ContainmentSelectionLayer.cpp deleted file mode 100644 index f9a4881c83..0000000000 --- a/src/context/widgets/ContainmentSelectionLayer.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 William Viana Soares * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#include "ContainmentSelectionLayer.h" - -#include - -#include - -#include -#include -#include -#include -#include - -#define ICON_SIZE 60 - -ContainmentSelectionLayer::ContainmentSelectionLayer( QGraphicsItem *parent ) - : QGraphicsItem( parent ) - , m_mouseHover( 0 ) -{ - m_containment = static_cast( parent ); - setAcceptsHoverEvents( true ); - m_zoomInText = new QGraphicsSimpleTextItem( i18n( "Zoom In" ), this ); - - QFont font; - - font.setBold( true ); - font.setStyleHint( QFont::Times ); - font.setPointSize( font.pointSize() + 10 ); - font.setStyleStrategy( QFont::PreferAntialias ); - - m_zoomInText->setFont( font ); - m_zoomInText->setBrush( Qt::white ); - - m_zoomInText->hide(); - m_zoomInIcon = new QIcon::fromTheme( "zoom-in" ); -} - -QRectF -ContainmentSelectionLayer::boundingRect() const -{ - qreal left, top, right, bottom; - m_containment->getContentsMargins( &left, &top, &right, &bottom ); - return QRectF( m_containment->boundingRect().adjusted( left, top, right, bottom ) ); -} - -void -ContainmentSelectionLayer::hoverEnterEvent( QGraphicsSceneHoverEvent *event ) -{ - Q_UNUSED( event ); - - m_mouseHover = true; - m_zoomInText->show(); - update(); -} - -void -ContainmentSelectionLayer::hoverLeaveEvent( QGraphicsSceneHoverEvent *event ) -{ - Q_UNUSED( event ); - - m_mouseHover = false; - m_zoomInText->hide(); - update(); -} - -void -ContainmentSelectionLayer::mousePressEvent( QGraphicsSceneMouseEvent *event ) -{ - Q_UNUSED( event ); - - m_mouseHover = false; - m_zoomInText->hide(); - emit zoomRequested( m_containment, Plasma::ZoomIn ); -} - -void -ContainmentSelectionLayer::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget ) -{ - Q_UNUSED( option ); - Q_UNUSED( widget ); - - QFontMetricsF fm( m_zoomInText->font() ); - m_zoomInText->setPos( boundingRect().width() / 2 - fm.boundingRect( m_zoomInText->text() ).width() / 2, - boundingRect().height() / 2 ); - painter->save(); - QColor color = KColorScheme( QPalette::Active, KColorScheme::Window, - Plasma::Theme::defaultTheme()->colorScheme() ).background().color(); - - - painter->setRenderHint( QPainter::Antialiasing ); - - if( m_mouseHover ) - { - m_zoomInIcon->paint( painter, boundingRect().width() / 2 - ICON_SIZE / 2 , - boundingRect().height() / 2 - ICON_SIZE, ICON_SIZE, ICON_SIZE, - Qt::AlignCenter, QIcon::Disabled ); - painter->setOpacity( 0.3 ); - painter->setPen( QPen( Qt::gray, 1) ); - } - else - { - painter->setOpacity( 0 ); - } - - color.setAlpha( 200 ); - painter->setBrush( color ); - - painter->drawRect( boundingRect() ); - - painter->restore(); -} diff --git a/src/context/widgets/ContainmentSelectionLayer.h b/src/context/widgets/ContainmentSelectionLayer.h deleted file mode 100644 index 0d00953dcf..0000000000 --- a/src/context/widgets/ContainmentSelectionLayer.h +++ /dev/null @@ -1,59 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 William Viana Soares * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef CONTAINMENT_SELECTION_LAYER_H -#define CONTAINMENT_SELECTION_LAYER_H - -#include "amarok_export.h" - -#include - -#include - -#include -#include -#include -#include - -/** - * @class ContainmentSelectionLayer - * @short A layer to add hover effects in the containments when in zoomed out mode. - */ -class AMAROK_EXPORT ContainmentSelectionLayer: public QObject, public QGraphicsItem -{ - Q_OBJECT - Q_INTERFACES( QGraphicsItem ) - public: - ContainmentSelectionLayer( QGraphicsItem *parent = 0 ); - QRectF boundingRect() const; - - protected: - virtual void hoverEnterEvent( QGraphicsSceneHoverEvent *event ); - virtual void hoverLeaveEvent( QGraphicsSceneHoverEvent *event ); - virtual void mousePressEvent( QGraphicsSceneMouseEvent *event ); - virtual void paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0 ); - - private: - Plasma::Containment *m_containment; - bool m_mouseHover; - QGraphicsSimpleTextItem *m_zoomInText; - QIcon *m_zoomInIcon; - Q_SIGNALS: - void zoomRequested( Plasma::Containment *containment, Plasma::ZoomDirection direction ); - -}; - -#endif diff --git a/src/context/widgets/DropPixmapItem.cpp b/src/context/widgets/DropPixmapItem.cpp deleted file mode 100644 index 9a6b82b1bf..0000000000 --- a/src/context/widgets/DropPixmapItem.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 Simon Esneault * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "DropPixmapItem" - -#include "DropPixmapItem.h" - -#include "core/support/Debug.h" - -#include -#include - -DropPixmapItem::DropPixmapItem( QGraphicsItem* parent ) - : QGraphicsPixmapItem( parent ) -{ - setAcceptDrops( true ); -} - -void DropPixmapItem::dropEvent(QGraphicsSceneDragDropEvent* event) -{ - DEBUG_BLOCK - if( event->mimeData()->hasText() ) - { - QString file( event->mimeData()->text() ); - debug() << "dropped:" << file; - - if ( file.contains( "http://" ) || file.contains( "https://" ) ) - { - m_url = QUrl( file ); - The::networkAccessManager()->getData( m_url, this, - SLOT(imageDownloadResult(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); - } - - else if ( file.contains( "file://" ) ) - { - file.remove( "file://" ); - QPixmap cover; - cover.load( file ); - if ( !cover.isNull() ) - { - emit imageDropped( cover ); - } - else - { - debug() << "not an image"; - } - } - } - - if( event->mimeData()->hasImage() ) - { - debug() << "mimeData has image"; - emit imageDropped( qVariantValue< QPixmap >( event->mimeData()->imageData() ) ); - } -} - -void DropPixmapItem::imageDownloadResult( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ) -{ - if( !url.isValid() || m_url != url ) - return; - - m_url.clear(); - if( e.code != QNetworkReply::NoError ) - { - debug() << "unable to download the image:" << e.description; - return; - } - - QPixmap cover; - if( cover.loadFromData( data ) ) - emit imageDropped( cover ); - else - debug() << "not an image"; -} - -DropPixmapLayoutItem::DropPixmapLayoutItem( QGraphicsLayoutItem *parent, bool isLayout ) - : QGraphicsLayoutItem( parent, isLayout ) -{ - m_pixmap = new DropPixmapItem; - m_pixmap->setZValue( 100 ); - setGraphicsItem( m_pixmap ); - connect( m_pixmap, SIGNAL(imageDropped(QPixmap)), SIGNAL(imageDropped(QPixmap)) ); -} - -DropPixmapLayoutItem::~DropPixmapLayoutItem() -{ - delete m_pixmap; -} - -void -DropPixmapLayoutItem::setGeometry( const QRectF &rect ) -{ - QGraphicsLayoutItem::setGeometry( rect ); - int width = m_pixmap->pixmap().width(); - int height = m_pixmap->pixmap().height(); - QPointF pos = rect.topLeft(); - pos.rx() += (rect.width() - width) / 2; - pos.ry() += (rect.height() - height) / 2; - m_pixmap->setPos( pos ); -} - -QSizeF -DropPixmapLayoutItem::sizeHint( Qt::SizeHint which, const QSizeF &constraint ) const -{ - Q_UNUSED( which ) - Q_UNUSED( constraint ) - return m_pixmap->boundingRect().size(); -} - -qreal -DropPixmapLayoutItem::opacity() const -{ - return m_pixmap->opacity(); -} - -void -DropPixmapLayoutItem::setOpacity( qreal value ) -{ - m_pixmap->setOpacity( value ); -} - -QPixmap -DropPixmapLayoutItem::pixmap() const -{ - return m_pixmap->pixmap(); -} - -void -DropPixmapLayoutItem::setPixmap( const QPixmap &pixmap ) -{ - m_pixmap->setPixmap( pixmap ); -} - -void -DropPixmapLayoutItem::show() -{ - m_pixmap->show(); -} - -void -DropPixmapLayoutItem::hide() -{ - m_pixmap->hide(); -} - diff --git a/src/context/widgets/DropPixmapItem.h b/src/context/widgets/DropPixmapItem.h deleted file mode 100644 index ec085a28cf..0000000000 --- a/src/context/widgets/DropPixmapItem.h +++ /dev/null @@ -1,100 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 Simon Esneault * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef DROPPIXMAPITEM_H -#define DROPPIXMAPITEM_H - -#include "amarok_export.h" -#include "network/NetworkAccessManagerProxy.h" - -#include - -#include -#include - -//forward -class QGraphicsSceneDragDropEvent; - -/** -* \brief A QGraphicsPixmapItem on which you can drop an image -* -* Used for drag'n drop support for the cover. Will download the file if it's a link (from webrowser) -* -* \sa QGraphicsPixmapItem -* -* \author Simon Esneault -*/ - -class AMAROK_EXPORT DropPixmapItem : public QObject, public QGraphicsPixmapItem -{ - Q_OBJECT - public: - - DropPixmapItem( QGraphicsItem* parent = 0 ); - - Q_SIGNALS: - void imageDropped( const QPixmap &pixmap ); - - public Q_SLOTS: - /** - * Result of the image fetching stuff - */ - void imageDownloadResult( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ); - - protected Q_SLOTS: - /** - * Reimplement dropEvent - */ - virtual void dropEvent( QGraphicsSceneDragDropEvent* ); - - private: - QUrl m_url; - -}; - -class AMAROK_EXPORT DropPixmapLayoutItem : public QObject, public QGraphicsLayoutItem -{ - Q_OBJECT - Q_INTERFACES( QGraphicsLayoutItem ) - Q_PROPERTY( QPixmap pixmap READ pixmap WRITE setPixmap ) - Q_PROPERTY( qreal opacity READ opacity WRITE setOpacity ) - -public: - explicit DropPixmapLayoutItem( QGraphicsLayoutItem *parent = 0, bool isLayout = false ); - virtual ~DropPixmapLayoutItem(); - - virtual void setGeometry( const QRectF &rect ); - - qreal opacity() const; - void setOpacity( qreal value ); - - QPixmap pixmap() const; - void setPixmap( const QPixmap &pixmap ); - - void show(); - void hide(); - -Q_SIGNALS: - void imageDropped( const QPixmap &pixmap ); - -protected: - virtual QSizeF sizeHint( Qt::SizeHint which, const QSizeF &constraint = QSizeF() ) const; - -private: - DropPixmapItem *m_pixmap; -}; - -#endif // DROPPIXMAPITEM_H diff --git a/src/context/widgets/RatingWidget.cpp b/src/context/widgets/RatingWidget.cpp deleted file mode 100644 index d77f3084ff..0000000000 --- a/src/context/widgets/RatingWidget.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 William Viana Soarjs * - * Copyright (c) 2010 Mark Kretschmann * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -/* - Significant parts of this code is inspired and/or copied from - KDE Nepomuk sources, available at kdelibs/nepomuk -*/ - -#include "RatingWidget.h" - -#include "core/support/Debug.h" - -#include "kratingpainter.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -class RatingWidget::Private -{ -public: - Private() - : rating(0), - hoverRating(-1), - pixSize( 16 ), - showing( true ){ - } - - int rating; - int hoverRating; - int pixSize; - - bool showing; - - KRatingPainter ratingPainter; -}; - - -RatingWidget::RatingWidget( QGraphicsItem* parent ) - : QGraphicsWidget( parent ) - , d( new Private() ) - , m_startupUpdates( 2 ) -{ - setAcceptHoverEvents( true ); - setToolTip( i18n( "Track rating: %1", (float)d->rating / 2 ) ); -} - - -RatingWidget::~RatingWidget() -{ - delete d; -} - -void -RatingWidget::show() -{ - d->showing = true; -} - -void -RatingWidget::hide() -{ - d->showing = false; -} - -void -RatingWidget::setCustomPixmap( const QPixmap& pix ) -{ - d->ratingPainter.setCustomPixmap( pix ); - update(); -} - - -void -RatingWidget::setIcon( const QIcon& icon ) -{ - d->ratingPainter.setIcon( icon ); - update(); -} - - -void -RatingWidget::setPixmapSize( int size ) -{ - d->pixSize = size; - updateGeometry(); -} - - -int -RatingWidget::spacing() const -{ - return d->ratingPainter.spacing(); -} - - -QIcon -RatingWidget::icon() const -{ - return d->ratingPainter.icon(); -} - - -void -RatingWidget::setSpacing( int s ) -{ - d->ratingPainter.setSpacing( s ); - update(); -} - - -Qt::Alignment -RatingWidget::alignment() const -{ - return d->ratingPainter.alignment(); -} - - -void -RatingWidget::setAlignment( Qt::Alignment align ) -{ - d->ratingPainter.setAlignment( align ); - update(); -} - - -Qt::LayoutDirection -RatingWidget::layoutDirection() const -{ - return d->ratingPainter.layoutDirection(); -} - - -void -RatingWidget::setLayoutDirection( Qt::LayoutDirection direction ) -{ - d->ratingPainter.setLayoutDirection( direction ); - update(); -} - - -unsigned int -RatingWidget::rating() const -{ - return d->rating; -} - - -int -RatingWidget::maxRating() const -{ - return d->ratingPainter.maxRating(); -} - - -bool -RatingWidget::halfStepsEnabled() const -{ - return d->ratingPainter.halfStepsEnabled(); -} - -void -RatingWidget::setRating( int rating ) -{ - d->rating = rating; - d->hoverRating = rating; - update(); -} - -void -RatingWidget::setMaxRating( int max ) -{ - d->ratingPainter.setMaxRating( max ); - update(); -} - - -void -RatingWidget::setHalfStepsEnabled( bool enabled ) -{ - d->ratingPainter.setHalfStepsEnabled( enabled ); - update(); -} - -void -RatingWidget::mousePressEvent( QGraphicsSceneMouseEvent* e ) -{ - if ( e->button() == Qt::LeftButton ) - { - QRect rect( contentsRect().topLeft().x(), contentsRect().topLeft().y(), - contentsRect().width(), contentsRect().height() ); - int ratingFromPos = d->ratingPainter.ratingFromPosition( rect, QPoint( e->pos().x(), e->pos().y() ) ); - if ( ratingFromPos >= 0 ) - { - d->hoverRating = d->rating = ratingFromPos; - setToolTip( i18n( "Track rating: %1", (float)d->rating / 2 ) ); - update(); - emit ratingChanged( d->rating ); - } - } -} - - -void -RatingWidget::hoverMoveEvent( QGraphicsSceneHoverEvent* e ) -{ - QRect rect( contentsRect().topLeft().x(), contentsRect().topLeft().y(), - contentsRect().width(), contentsRect().height() ); - d->hoverRating = d->ratingPainter.ratingFromPosition( rect, QPoint( e->pos().x(), e->pos().y() ) ); - - update(); -} - - -void -RatingWidget::hoverEnterEvent( QGraphicsSceneHoverEvent* e ) -{ - QRect rect( contentsRect().topLeft().x(), contentsRect().topLeft().y(), - contentsRect().width(), contentsRect().height() ); - d->hoverRating = d->ratingPainter.ratingFromPosition( rect, QPoint( e->pos().x(), e->pos().y() ) ); - - setToolTip( i18n( "Track rating: %1", (float)d->rating / 2 ) ); - - update(); -} - -void -RatingWidget::hoverLeaveEvent( QGraphicsSceneHoverEvent* ) -{ - d->hoverRating = -1; - update(); -} - - -void -RatingWidget::paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) -{ - Q_UNUSED( option ) - Q_UNUSED( widget ) - if( d->showing ) - { - d->ratingPainter.setEnabled( isEnabled() ); - QRect rect( contentsRect().topLeft().x(), contentsRect().topLeft().y(), - contentsRect().width(), contentsRect().height() ); - d->ratingPainter.paint( painter, rect, d->rating, d->hoverRating ); - } - - // HACK: (this works fine, but if a better fix is found, we should replace it) - // Make sure that the parent item updates itself correctly on startup. - // We use a counter variable to prevent infinite recursion. - if( m_startupUpdates ) - { - parentItem()->update(); - m_startupUpdates--; - } -} - -QSizeF -RatingWidget::sizeHint( Qt::SizeHint hint, const QSizeF& size ) const -{ - Q_UNUSED( hint ) - Q_UNUSED( size ) - int numPix = d->ratingPainter.maxRating(); - if( d->ratingPainter.halfStepsEnabled() ) - numPix /= 2; - - QSizeF pixSize( d->pixSize, d->pixSize ); - if ( !d->ratingPainter.customPixmap().isNull() ) { - pixSize = d->ratingPainter.customPixmap().size(); - } - - return QSizeF( pixSize.width()*numPix + spacing()*(numPix-1) + contentsRect().width(), - pixSize.height() + contentsRect().width() ); -} - - diff --git a/src/context/widgets/RecentlyPlayedListWidget.cpp b/src/context/widgets/RecentlyPlayedListWidget.cpp deleted file mode 100644 index 4d1eeee683..0000000000 --- a/src/context/widgets/RecentlyPlayedListWidget.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2010 Rick W. Chen * - * Copyright (c) 2013 Konrad Zemek * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "RecentlyPlayedListWidget" - -#include "RecentlyPlayedListWidget.h" - -#include "EngineController.h" -#include "core/meta/Meta.h" -#include "core/support/Amarok.h" -#include "core/support/Debug.h" -#include "core-impl/collections/support/CollectionManager.h" -#include "playlist/PlaylistController.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ClickableGraphicsWidget::ClickableGraphicsWidget( const QString &url, - QGraphicsItem *parent, - Qt::WindowFlags wFlags ) - : QGraphicsWidget( parent, wFlags ) - , m_url( url ) -{ - setAcceptHoverEvents( true ); - setCursor( Qt::PointingHandCursor ); -} - -ClickableGraphicsWidget::~ClickableGraphicsWidget() -{ -} - -void -ClickableGraphicsWidget::hoverEnterEvent( QGraphicsSceneHoverEvent *event ) -{ - Q_UNUSED( event ) - setOpacity( 0.5 ); - update(); -} - -void -ClickableGraphicsWidget::hoverLeaveEvent( QGraphicsSceneHoverEvent *event ) -{ - Q_UNUSED( event ) - setOpacity( 1 ); - update(); -} - -void -ClickableGraphicsWidget::mousePressEvent( QGraphicsSceneMouseEvent *event ) -{ - Q_UNUSED( event ) -} - -void -ClickableGraphicsWidget::mouseReleaseEvent( QGraphicsSceneMouseEvent *event ) -{ - if( !m_url.isEmpty() ) - { - if( event->button() == Qt::LeftButton ) - emit leftClicked( m_url ); - else if( event->button() == Qt::MidButton ) - emit middleClicked( m_url ); - } -} - -TimeDifferenceLabel::TimeDifferenceLabel( const QDateTime &eventTime , QWidget *parent, - Qt::WindowFlags wFlags ) - : QLabel( parent, wFlags ) - , m_eventTime( eventTime ) -{ - update(); -} - -TimeDifferenceLabel::~TimeDifferenceLabel() -{ -} - -void -TimeDifferenceLabel::update() -{ - setText( Amarok::verboseTimeSince( m_eventTime ) ); -} - -RecentlyPlayedListWidget::RecentlyPlayedListWidget( QGraphicsWidget *parent ) - : Plasma::ScrollWidget( parent ) - , m_layout( new QGraphicsLinearLayout( Qt::Vertical ) ) - , m_trackIcon( QIcon::fromTheme( "media-album-track") ) -{ - QGraphicsWidget *content = new QGraphicsWidget; - content->setLayout( m_layout ); - setWidget( content ); - - connect( EngineController::instance(), SIGNAL(trackChanged(Meta::TrackPtr)), - SLOT(trackChanged(Meta::TrackPtr)) ); - - m_updateTimer = new QTimer( this ); - m_updateTimer->start( 20 * 1000 ); - - // Load saved data - const KConfigGroup group = Amarok::config( "Recently Played" ); - const QVariantList recentlyPlayed = group.readEntry( "Last Played Dates", QVariantList() ); - const QStringList displayNames = group.readEntry( "Display Names", QStringList() ); - const QStringList trackUrls = group.readEntry( "Urls", QStringList() ); - for( int i = 0; i < trackUrls.size(); ++i ) - addTrack( recentlyPlayed[i].toDateTime(), displayNames[i], trackUrls[i] ); -} - -RecentlyPlayedListWidget::~RecentlyPlayedListWidget() -{ - QVariantList recentlyPlayed; - QStringList displayNames; - QStringList trackUrls; - foreach( const RecentlyPlayedTrackData &data, m_recentTracks ) - { - recentlyPlayed.append( data.recentlyPlayed ); - displayNames.append( data.displayName ); - trackUrls.append( data.trackUrl ); - } - - KConfigGroup group = Amarok::config( "Recently Played" ); - group.writeEntry( "Last Played Dates", recentlyPlayed ); - group.writeEntry( "Display Names", displayNames ); - group.writeEntry( "Urls", trackUrls ); - group.sync(); -} - -QGraphicsWidget* -RecentlyPlayedListWidget::addWidgetItem( const RecentlyPlayedTrackData &data ) -{ - KSqueezedTextLabel *squeezer = new KSqueezedTextLabel( data.displayName ); - squeezer->setTextElideMode( Qt::ElideRight ); - squeezer->setAttribute( Qt::WA_NoSystemBackground ); - squeezer->setCursor( Qt::PointingHandCursor ); - - QGraphicsProxyWidget *labelWidget = new QGraphicsProxyWidget; - labelWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); - labelWidget->setWidget( squeezer ); - - TimeDifferenceLabel *lastPlayed = new TimeDifferenceLabel( data.recentlyPlayed ); - lastPlayed->setAttribute( Qt::WA_NoSystemBackground ); - lastPlayed->setAlignment( Qt::AlignRight ); - lastPlayed->setWordWrap( false ); - lastPlayed->setCursor( Qt::PointingHandCursor ); - connect( m_updateTimer, SIGNAL(timeout()), lastPlayed, SLOT(update()) ); - - QGraphicsProxyWidget *lastPlayedWidget = new QGraphicsProxyWidget; - lastPlayedWidget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ); - lastPlayedWidget->setWidget( lastPlayed ); - - Plasma::IconWidget *icon = new Plasma::IconWidget; - QSizeF iconSize = icon->sizeFromIconSize( QFontMetricsF(QFont()).height() ); - icon->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); - icon->setMinimumSize( iconSize ); - icon->setMaximumSize( iconSize ); - icon->setIcon( m_trackIcon ); - - QGraphicsLinearLayout *itemLayout = new QGraphicsLinearLayout( Qt::Horizontal ); - itemLayout->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - itemLayout->setContentsMargins( 0, 0, 0, 0 ); - itemLayout->addItem( icon ); - itemLayout->addItem( labelWidget ); - itemLayout->addItem( lastPlayedWidget ); - - ClickableGraphicsWidget *itemWidget = new ClickableGraphicsWidget( data.trackUrl ); - itemWidget->setLayout( itemLayout ); - connect( itemWidget, SIGNAL(leftClicked(QString)), SLOT(itemLeftClicked(QString)) ); - connect( itemWidget, SIGNAL(middleClicked(QString)), SLOT(itemMiddleClicked(QString)) ); - - m_layout->insertItem( 0, itemWidget ); - - return itemWidget; -} - -void -RecentlyPlayedListWidget::itemLeftClicked( const QString &url ) -{ - Playlist::Controller::instance()->insertOptioned( QUrl( url ), - Playlist::OnDoubleClickOnSelectedItems ); -} - -void -RecentlyPlayedListWidget::itemMiddleClicked( const QString &url ) -{ - Playlist::Controller::instance()->insertOptioned( QUrl( url ), - Playlist::OnMiddleClickOnSelectedItems ); -} - -void -RecentlyPlayedListWidget::addTrack( const Meta::TrackPtr &track ) -{ - const Meta::ArtistPtr artist = track->artist(); - const QString displayName = !artist || artist->prettyName().isEmpty() - ? track->prettyName() - : i18nc( "%1 is artist, %2 is title", "%1 - %2", - artist->prettyName(), track->prettyName() ); - - addTrack( QDateTime::currentDateTime(), displayName, track->uidUrl() ); -} - -void RecentlyPlayedListWidget::addTrack( const QDateTime &recentlyPlayed, - const QString &displayName, - const QString &trackUrl ) -{ - while( m_recentTracks.size() >= 10 ) - { - // Get rid of the least recent entry - RecentlyPlayedTrackData data = m_recentTracks.dequeue(); - delete data.widget; - } - - RecentlyPlayedTrackData data; - data.recentlyPlayed = recentlyPlayed; - data.displayName = displayName; - data.trackUrl = trackUrl; - data.widget = addWidgetItem( data ); - m_recentTracks.enqueue( data ); -} - -void -RecentlyPlayedListWidget::trackChanged( const Meta::TrackPtr &track ) -{ - // Nothing has changed - if( m_currentTrack == track ) - return; - - // lastTrack will be null if we're resuming from a stopped state - Meta::TrackPtr lastTrack = m_currentTrack; - m_currentTrack = track; - - if( lastTrack ) - addTrack( lastTrack ); -} diff --git a/src/context/widgets/RecentlyPlayedListWidget.h b/src/context/widgets/RecentlyPlayedListWidget.h deleted file mode 100644 index 94ce7331bb..0000000000 --- a/src/context/widgets/RecentlyPlayedListWidget.h +++ /dev/null @@ -1,109 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2010 Rick W. Chen * - * Copyright (c) 2013 Konrad Zemek * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef RECENTLY_PLAYED_LIST_WIDGET_H -#define RECENTLY_PLAYED_LIST_WIDGET_H - -#include "core/meta/forward_declarations.h" - -#include -#include -#include - -#include -#include -#include -#include - -class QGraphicsLinearLayout; - -class ClickableGraphicsWidget : public QGraphicsWidget -{ - Q_OBJECT - -public: - explicit ClickableGraphicsWidget( const QString &url, QGraphicsItem *parent = 0, - Qt::WindowFlags wFlags = 0 ); - ~ClickableGraphicsWidget(); - -Q_SIGNALS: - void leftClicked( const QString &url ); - void middleClicked( const QString &url ); - -protected: - void hoverEnterEvent( QGraphicsSceneHoverEvent *event ); - void hoverLeaveEvent( QGraphicsSceneHoverEvent *event ); - void mousePressEvent( QGraphicsSceneMouseEvent *event ); - void mouseReleaseEvent( QGraphicsSceneMouseEvent *event ); - -private: - const QString m_url; -}; - -class TimeDifferenceLabel : public QLabel -{ - Q_OBJECT - -public: - explicit TimeDifferenceLabel( const QDateTime &eventTime, QWidget *parent = 0, - Qt::WindowFlags wFlags = 0 ); - ~TimeDifferenceLabel(); - -public Q_SLOTS: - void update(); - -private: - const QDateTime m_eventTime; -}; - -class RecentlyPlayedListWidget : public Plasma::ScrollWidget -{ - Q_OBJECT - - struct RecentlyPlayedTrackData - { - QDateTime recentlyPlayed; - QString displayName; - QString trackUrl; - QGraphicsWidget *widget; - }; - -public: - explicit RecentlyPlayedListWidget( QGraphicsWidget *parent = 0 ); - ~RecentlyPlayedListWidget(); - -private Q_SLOTS: - void itemLeftClicked( const QString &url ); - void itemMiddleClicked( const QString &url ); - void trackChanged( const Meta::TrackPtr &track ); - -private: - Q_DISABLE_COPY( RecentlyPlayedListWidget ) - - void addTrack( const Meta::TrackPtr &track ); - void addTrack( const QDateTime &recentlyPlayed, const QString &displayName, - const QString &trackUrl ); - QGraphicsWidget *addWidgetItem( const RecentlyPlayedTrackData &data ); - - Meta::TrackPtr m_currentTrack; - QGraphicsLinearLayout *m_layout; - QQueue m_recentTracks; - QIcon m_trackIcon; - QTimer *m_updateTimer; -}; - -#endif // RECENTLY_PLAYED_LIST_WIDGET_H diff --git a/src/context/widgets/TextScrollingWidget.cpp b/src/context/widgets/TextScrollingWidget.cpp deleted file mode 100644 index 58d5b51b52..0000000000 --- a/src/context/widgets/TextScrollingWidget.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 Simon Esneault * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "TextScrollingWidget" - -#include "TextScrollingWidget.h" - -#include "core/support/Debug.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - -class TextScrollingWidgetPrivate -{ -public: - TextScrollingWidgetPrivate( TextScrollingWidget *parent ) - : width( 0.0 ) - , delta( 0.0 ) - , currentDelta( 0.0 ) - , drawBackground( false ) - , alignment( Qt::AlignHCenter ) - , textBackground( 0 ) - , textItem( new QGraphicsSimpleTextItem( parent ) ) - , q_ptr( parent ) - {} - - ~TextScrollingWidgetPrivate() - {} - - void _delayedForwardAnimation() - { - Q_Q( TextScrollingWidget ); - if( q->isUnderMouse() ) - { - q->setText( text ); - q->startAnimation( QAbstractAnimation::Forward ); - } - } - - void drawRoundedRectAroundText( QPainter *p ) - { - Q_Q( TextScrollingWidget ); - p->save(); - p->setRenderHint( QPainter::Antialiasing ); - - if( !textBackground ) - { - textBackground = new Plasma::FrameSvg( q ); - textBackground->setImagePath( QLatin1String("widgets/text-background") ); - textBackground->setEnabledBorders( Plasma::FrameSvg::AllBorders ); - } - - QRectF rect = textItem->boundingRect(); - rect = q->mapRectFromItem( textItem, rect ); - rect.adjust( -5, -5, 5, 5 ); - - textBackground->resizeFrame( rect.size() ); - textBackground->paintFrame( p, rect.topLeft() ); - p->restore(); - } - - void setScrollingText( const QString &str ) - { - text = str; - } - - void setText( const QString &str ) - { - text = str; - textItem->setText( text ); - - if( animation ) - animation.data()->stop(); - - } - - qreal width; // box width - qreal delta; // complete delta - qreal currentDelta; // current delta - bool drawBackground; // whether to draw background svg - QString text; // full sentence - Qt::Alignment alignment; // horizontal text item alignment - Plasma::FrameSvg *textBackground; // background svg for text - QWeakPointer animation; // scroll animation - QGraphicsSimpleTextItem *textItem; - -private: - TextScrollingWidget *const q_ptr; - Q_DECLARE_PUBLIC( TextScrollingWidget ) -}; - -TextScrollingWidget::TextScrollingWidget( QGraphicsWidget *parent, Qt::WindowFlags wFlags ) - : QGraphicsWidget( parent, wFlags ) - , d_ptr( new TextScrollingWidgetPrivate(this) ) -{ - setAcceptHoverEvents( true ); - setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ); - setFlags( flags() | QGraphicsItem::ItemClipsChildrenToShape ); -} - -TextScrollingWidget::~TextScrollingWidget() -{ - delete d_ptr; -} - -void -TextScrollingWidget::setBrush( const QBrush &brush ) -{ - Q_D( TextScrollingWidget ); - d->textItem->setBrush( brush ); -} - -void -TextScrollingWidget::setScrollingText( const QString &text ) -{ - Q_D( TextScrollingWidget ); - d->setScrollingText( text ); - updateGeometry(); -} - -void -TextScrollingWidget::setText( const QString &text ) -{ - Q_D( TextScrollingWidget ); - d->setText( text ); -} - -void -TextScrollingWidget::setAlignment( Qt::Alignment alignment ) -{ - Q_D( TextScrollingWidget ); - d->alignment = alignment; - updateGeometry(); -} - -void -TextScrollingWidget::setFont( const QFont &font ) -{ - Q_D( TextScrollingWidget ); - d->textItem->setFont( font ); - QFontMetricsF fm( font ); - const int textWidth = fm.width( d->text ); - d->delta = textWidth > d->width ? textWidth - d->width + 5 : 0; -} - -QBrush -TextScrollingWidget::brush() const -{ - Q_D( const TextScrollingWidget ); - return d->textItem->brush(); -} - -QFont -TextScrollingWidget::font() const -{ - Q_D( const TextScrollingWidget ); - return d->textItem->font(); -} - -QString -TextScrollingWidget::text() const -{ - Q_D( const TextScrollingWidget ); - return d->text; -} - -void -TextScrollingWidget::setGeometry( const QRectF &rect ) -{ - Q_D( TextScrollingWidget ); - QGraphicsWidget::setGeometry( rect ); - - // reset the animation and stuff - QPropertyAnimation *animation = d->animation.data(); - if( animation ) - { - animation->stop(); - animation->deleteLater(); - d->animation.clear(); - } - - QRectF textRect = mapFromParent( rect ).boundingRect(); - QFontMetricsF fm( font() ); - int textWidth = fm.width( d->text ); - d->width = textRect.width(); - d->delta = (textWidth > d->width) ? (textWidth - d->width) : 0; - d->textItem->setText( fm.elidedText( d->text, Qt::ElideRight, d->width ) ); - - qreal textX( 0.0 ); - switch( d->alignment ) - { - case Qt::AlignHCenter: - textX = ( textRect.width() - d->textItem->boundingRect().width() ) / 2; - break; - default: - case Qt::AlignLeft: - textX = textRect.left(); - break; - case Qt::AlignRight: - textX = textRect.right() - d->textItem->boundingRect().width(); - break; - } - d->textItem->setPos( textX, textRect.top() ); -} - -void -TextScrollingWidget::hoverEnterEvent( QGraphicsSceneHoverEvent* e ) -{ - Q_D( TextScrollingWidget ); - if( !isAnimating() && d->delta ) - QTimer::singleShot( 150, this, SLOT(_delayedForwardAnimation()) ); - e->accept(); -} - -bool -TextScrollingWidget::isEmpty() const -{ - Q_D( const TextScrollingWidget ); - return d->text.isEmpty(); -} - -bool -TextScrollingWidget::isAnimating() const -{ - Q_D( const TextScrollingWidget ); - return ( d->animation && d->animation.data()->state() == QAbstractAnimation::Running ); -} - -qreal -TextScrollingWidget::animationValue() const -{ - Q_D( const TextScrollingWidget ); - return d->currentDelta; -} - -void -TextScrollingWidget::animationFinished() -{ - Q_D( TextScrollingWidget ); - if( !d->animation ) - return; - - QPropertyAnimation *animation = d->animation.data(); - if( animation->direction() == QAbstractAnimation::Forward ) - { - startAnimation( QAbstractAnimation::Backward ); - } - else - { - // Scroll again if the mouse is still over. - if( isUnderMouse() ) - startAnimation( QAbstractAnimation::Forward ); - else - { - setScrollingText( d->text ); - d->animation.data()->deleteLater(); - update(); - } - } -} - -void -TextScrollingWidget::startAnimation( QAbstractAnimation::Direction direction ) -{ - Q_D( TextScrollingWidget ); - QPropertyAnimation *animation = d->animation.data(); - if( !animation ) - { - animation = new QPropertyAnimation( this, "animationValue" ); - animation->setDuration( d->delta*15 ); - animation->setStartValue( 0.0 ); - animation->setEndValue( 1.0 ); - animation->setEasingCurve( QEasingCurve::InOutQuad ); - d->animation = animation; - connect( animation, SIGNAL(finished()), this, SLOT(animationFinished()) ); - } - else - { - animation->stop(); - } - - animation->setDirection( direction ); - animation->start( QAbstractAnimation::KeepWhenStopped ); -} - -void -TextScrollingWidget::animate( qreal value ) -{ - Q_D( TextScrollingWidget ); - if( d->animation.isNull() ) - return; - - d->currentDelta = -value * d->delta; - d->textItem->setPos( d->currentDelta, 0 ); -} - -QSizeF -TextScrollingWidget::sizeHint( Qt::SizeHint which, const QSizeF &constraint ) const -{ - Q_D( const TextScrollingWidget ); - QFontMetricsF fm( font() ); - switch( which ) - { - case Qt::MinimumSize: - return QSizeF( fm.width( d->text ) / 4.0, fm.height() ); - case Qt::MaximumSize: - return QSizeF( -1, fm.height() + 8 ); - case Qt::MinimumDescent: - return QSizeF( QGraphicsWidget::sizeHint(which, constraint).width(), fm.descent() ); - default: - break; - } - return QSizeF( constraint.width(), fm.height() ); -} - -Qt::Alignment -TextScrollingWidget::alignment() const -{ - Q_D( const TextScrollingWidget ); - return d->alignment; -} - -QRectF -TextScrollingWidget::boundingRect() const -{ - Q_D( const TextScrollingWidget ); - return mapRectFromItem( d->textItem, d->textItem->boundingRect() ); -} - -void -TextScrollingWidget::paint( QPainter *p, const QStyleOptionGraphicsItem *option, QWidget *widget ) -{ - QGraphicsWidget::paint( p, option, widget ); - Q_D( TextScrollingWidget ); - if( d->drawBackground ) - d->drawRoundedRectAroundText( p ); -} - -bool -TextScrollingWidget::isDrawingBackground() const -{ - Q_D( const TextScrollingWidget ); - return d->drawBackground; -} - -void -TextScrollingWidget::setDrawBackground( bool enable ) -{ - Q_D( TextScrollingWidget ); - d->drawBackground = enable; -} - diff --git a/src/context/widgets/TextScrollingWidget.h b/src/context/widgets/TextScrollingWidget.h deleted file mode 100644 index 83493963d5..0000000000 --- a/src/context/widgets/TextScrollingWidget.h +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 Simon Esneault * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_TEXT_SCROLLING_WIDGET_H -#define AMAROK_TEXT_SCROLLING_WIDGET_H - -#include "amarok_export.h" - -#include -#include - -//forward -class TextScrollingWidgetPrivate; - -/** -* \brief An animated QGrahicsTextItem on hovering -* -* The text will be automatically truncate to a specified width and will -* be animated when mouse is hovering above the text (if truncated). -* -* \author Simon Esneault -*/ - -class AMAROK_EXPORT TextScrollingWidget : public QGraphicsWidget -{ - Q_OBJECT - Q_PROPERTY( qreal animationValue READ animationValue WRITE animate ) - Q_PROPERTY( Qt::Alignment alignment READ alignment WRITE setAlignment ) - Q_PROPERTY( QBrush brush READ brush WRITE setBrush ) - Q_PROPERTY( QString text READ text WRITE setText ) - Q_PROPERTY( bool drawBackground READ isDrawingBackground WRITE setDrawBackground ) - Q_PROPERTY( QFont font READ font WRITE setFont ) - Q_PROPERTY( bool empty READ isEmpty ) - - public: - TextScrollingWidget( QGraphicsWidget* parent = 0, Qt::WindowFlags wFlags = 0 ); - virtual ~TextScrollingWidget(); - - /** - * Set the scrolling text. The text will be elided and scrolled - * automatically if text is wider than the avaialble geometry. - */ - void setScrollingText( const QString &text ); - - void setAlignment( Qt::Alignment alignment ); - - void setBrush( const QBrush &brush ); - - void setDrawBackground( bool enable ); - - void setText( const QString &text ); - - void setFont( const QFont &font ); - - Qt::Alignment alignment() const; - - QBrush brush() const; - - QFont font() const; - - QString text() const; - - bool isAnimating() const; - - bool isEmpty() const; - - bool isDrawingBackground() const; - - virtual QRectF boundingRect() const; - - void paint( QPainter *p, const QStyleOptionGraphicsItem *option, QWidget *widget = 0 ); - - protected Q_SLOTS: - void startAnimation( QAbstractAnimation::Direction direction ); - void animationFinished(); - - protected: - /** - * Reimplement mouse hover enter event - */ - virtual void hoverEnterEvent( QGraphicsSceneHoverEvent* ); - - virtual QSizeF sizeHint( Qt::SizeHint which, const QSizeF &constraint = QSizeF() ) const; - - virtual void setGeometry( const QRectF &rect ); - - qreal animationValue() const; - void animate( qreal anim ); - - private: - TextScrollingWidgetPrivate *const d_ptr; - Q_DECLARE_PRIVATE( TextScrollingWidget ) - - Q_PRIVATE_SLOT( d_ptr, void _delayedForwardAnimation() ) -}; - -#endif diff --git a/src/context/widgets/TextWidget.cpp b/src/context/widgets/TextWidget.cpp deleted file mode 100644 index ab2617bf84..0000000000 --- a/src/context/widgets/TextWidget.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "TextWidget" - -#include "TextWidget.h" - -#include "core/support/Debug.h" - -#include - -namespace Context -{ - -TextWidget::TextWidget( QGraphicsItem* parent, QGraphicsScene* scene ) - : QGraphicsTextItem( parent, scene ) -{} - -void TextWidget::setText( const QString text ) -{ - setHtml( text ); -} - -Qt::Orientations TextWidget::expandingDirections() const -{ - return Qt::Vertical; -} - -QSizeF TextWidget::minimumSize() const -{ - return QSizeF( textWidth(), boundingRect().height() ); -} - -QSizeF TextWidget::maximumSize() const -{ - return minimumSize(); -} - -bool TextWidget::hasHeightForWidth() const -{ - return true; -} - -qreal TextWidget::heightForWidth( qreal w ) const -{ - document()->setTextWidth( w ); - qreal height = document()->size().height(); -// debug() << "heightForWidth( " << w << " ) is " << height; - return height; -} - -bool TextWidget::hasWidthForHeight() const -{ - return false; -} - -qreal TextWidget::widthForHeight( qreal h ) const -{ - Q_UNUSED( h ) - return 0; -} - -QRectF TextWidget::geometry() const -{ -// debug() << "returning geometry: " << boundingRect().toRect(); - return boundingRect().toRect(); -} - -void TextWidget::setGeometry( const QRectF& geom ) -{ -// debug() << "getting told to change geometry from: " << geometry() << " to : " << geom; - prepareGeometryChange(); - setTextWidth( geom.width() ); - setPos( geom.topLeft() ); - if( document()->size().height() > geom.height() ) - setDocument( shortenHeight( geom.height() ) ); - - update(); -} - -QSizeF TextWidget::sizeHint() const -{ - return document()->size(); -} - -QTextDocument* TextWidget::shortenHeight( qreal height ) -{ - QStringList lines = document()->toHtml().split( "
" ); -// debug() << "trying to shorten: " << document()->toHtml() << " split into " << lines.size() << " lines"; - for( int i = lines.size(); i > 1; i-- ) - { - QStringList tmp = lines; - for( int k = lines.size(); k >= i; k-- ) - tmp.removeAt( k - 1 ); // remove lines from the cut to the end - QString newtext = tmp.join( "
" ); - QTextDocument* newdoc = new QTextDocument(); - newdoc->setHtml( newtext ); -// debug() << "trying to remove bottom line: " << i - 1 << " new size is: " << newdoc->size().height() << " max is: " << height; - if( newdoc->size().height() <= height ) - return newdoc; - } -// debug() << "couldn't shorten height, failing!"; - return document(); -} - -} // Context namespace - diff --git a/src/context/widgets/TextWidget.h b/src/context/widgets/TextWidget.h deleted file mode 100644 index c1987edd8b..0000000000 --- a/src/context/widgets/TextWidget.h +++ /dev/null @@ -1,62 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef AMAROK_TEXT_WIDGET_H -#define AMAROK_TEXT_WIDGET_H - -#include "amarok_export.h" - -#include - -#include - -namespace Context -{ - -class AMAROK_EXPORT TextWidget : public QGraphicsTextItem, - public Plasma::LayoutItem -{ - -public: - explicit TextWidget( QGraphicsItem* parent = 0, QGraphicsScene* scene = 0 ); - - void setText( const QString text ); - - // layout stuff - Qt::Orientations expandingDirections() const; - - QSizeF minimumSize() const; - QSizeF maximumSize() const; - - bool hasHeightForWidth() const; - qreal heightForWidth( qreal w ) const; - - bool hasWidthForHeight() const; - qreal widthForHeight(qreal h) const; - - QRectF geometry() const; - void setGeometry( const QRectF& geometry ); - - QSizeF sizeHint() const; - -private: - - QTextDocument* shortenHeight( qreal height ); -}; - -} // Context namespace - -#endif diff --git a/src/context/widgets/ToolBoxIcon.cpp b/src/context/widgets/ToolBoxIcon.cpp deleted file mode 100644 index 58580f7dd0..0000000000 --- a/src/context/widgets/ToolBoxIcon.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 William Viana Soares * - * Copyright (c) 2009 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#include "ToolBoxIcon.h" - -#include "core/support/Debug.h" -#include "PaletteHandler.h" - -#include -#include -#include - -#include - -#include - -#define PADDING 15 - - -ToolBoxIcon::ToolBoxIcon( QGraphicsItem *parent, const float opacity ) - : Plasma::IconWidget( parent ) - , m_hovering( 0 ) - , m_baseOpacity( opacity ) -{ - m_text = new QGraphicsSimpleTextItem( this ); - m_text->setCursor( Qt::ArrowCursor ); // Don't show the carot, the text isn't editable. - - QFont font; - font.setBold( false ); - font.setPointSize( font.pointSize() - 1 ); - font.setStyleStrategy( QFont::PreferAntialias ); - - m_text->setFont( font ); - m_text->show(); - - setOpacity( 1.0 - m_baseOpacity ); -} - -ToolBoxIcon::~ToolBoxIcon() -{} - -void -ToolBoxIcon::mousePressEvent( QGraphicsSceneMouseEvent *event ) -{ - if( event->button() != Qt::LeftButton ) - { - Plasma::IconWidget::mousePressEvent( event ); - return; - } - - if( data( 0 ) != QVariant() ) - { - DEBUG_LINE_INFO - debug() << data( 0 ).toString(); - emit appletChosen( data( 0 ).toString() ); - } - else - { - Plasma::IconWidget::mousePressEvent( event ); - } -} - -void -ToolBoxIcon::mousePressed( bool pressed ) -{ - DEBUG_BLOCK - - if( pressed && data( 0 ) != QVariant() ) - { - debug() << data( 0 ).toString(); - emit appletChosen( data( 0 ).toString() ); - } -} - -void -ToolBoxIcon::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget ) -{ - painter->setRenderHint( QPainter::Antialiasing ); - if( Plasma::IconWidget::drawBackground() ) - { - if( m_text->text().isEmpty() ) - m_text->setText( text() ); - - const QFontMetricsF fm( m_text->font() ); - m_text->setPos( PADDING, size().height() / 2 - fm.boundingRect( m_text->text() ).height() / 2 ); - painter->save(); - - // QColor color = KColorScheme( QPalette::Active, KColorScheme::Window, - // Plasma::Theme::defaultTheme()->colorScheme() ).background().color(); - - QLinearGradient gradient( boundingRect().topLeft(), boundingRect().bottomLeft() ); - QColor highlight = PaletteHandler::highlightColor(); - highlight.setAlpha( 160 ); - gradient.setColorAt( 0, highlight.darker( 140 ) ); - highlight.setAlpha( 220 ); - gradient.setColorAt( 1, highlight.darker( 180 ) ); - QPainterPath path; - path.addRoundedRect( boundingRect(), 3, 3 ); - painter->fillPath( path, gradient ); - painter->restore(); - - // draw border - painter->save(); - painter->translate(0.5, 0.5); - painter->setPen( PaletteHandler::highlightColor().darker( 150 ) ); - painter->drawRoundedRect( boundingRect(), 3, 3 ); - painter->restore(); - } - else - Plasma::IconWidget::paint( painter, option, widget ); -} - -QRectF -ToolBoxIcon::boundingRect() const -{ - return QRectF( QPointF( 0, 0 ), size() ); -} - -void -ToolBoxIcon::hoverEnterEvent( QGraphicsSceneHoverEvent *event ) -{ - Plasma::Animation *animation = m_animHighLight.data(); - if( !animation ) - { - animation = Plasma::Animator::create( Plasma::Animator::FadeAnimation ); - animation->setTargetWidget( this ); - animation->setProperty( "startOpacity", 1.0 - m_baseOpacity ); - animation->setProperty( "targetOpacity", 1.0 ); - animation->setProperty( "duration", 240 ); - m_animHighLight = animation; - } - else - animation->stop(); - - m_hovering = true; - m_defaultTextBrush = m_text->brush(); - m_text->setBrush( The::paletteHandler()->palette().highlightedText() ); - animation->setDirection( QAbstractAnimation::Forward ); - animation->setEasingCurve( QEasingCurve::InQuad ); - animation->start( QAbstractAnimation::KeepWhenStopped ); - Plasma::IconWidget::hoverEnterEvent( event ); -} - -void -ToolBoxIcon::hoverLeaveEvent( QGraphicsSceneHoverEvent *event ) -{ - Plasma::Animation *animation = m_animHighLight.data(); - if( !animation ) - { - animation = Plasma::Animator::create( Plasma::Animator::FadeAnimation ); - animation->setTargetWidget( this ); - animation->setProperty( "startOpacity", 1.0 - m_baseOpacity ); - animation->setProperty( "targetOpacity", 1.0 ); - animation->setProperty( "duration", 240 ); - m_animHighLight = animation; - } - else - animation->stop(); - - m_hovering = false; - m_text->setBrush( m_defaultTextBrush ); - animation->setDirection( QAbstractAnimation::Backward ); - animation->setEasingCurve( QEasingCurve::OutQuad ); - animation->start( QAbstractAnimation::DeleteWhenStopped ); - Plasma::IconWidget::hoverLeaveEvent( event ); -} - -QPainterPath -ToolBoxIcon::shape() const -{ - if( Plasma::IconWidget::drawBackground() ) - { - QSize shapeSize( size().width() - 2, size().height() - 2 ); - return Plasma::PaintUtils::roundedRectangle( QRectF( QPointF( 0.0, 0.0 ), shapeSize ), 10.0 ); - } - - return Plasma::IconWidget::shape(); -} - -void -ToolBoxIcon::setText( const QString &text ) -{ - m_text->setText( text ); - update(); -} - -QString -ToolBoxIcon::text() const -{ - return m_text->text(); -} - -void -ToolBoxIcon::setBrush( const QBrush& b ) -{ - m_text->setBrush( b ); -} - - diff --git a/src/context/widgets/ToolBoxIcon.h b/src/context/widgets/ToolBoxIcon.h deleted file mode 100644 index 1b0d047286..0000000000 --- a/src/context/widgets/ToolBoxIcon.h +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 William Viana Soares * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef TOOLBOX_ICON_H -#define TOOLBOX_ICON_H - -#include "amarok_export.h" - -#include - -#include -#include -#include -#include - -class QPainterPath; - -namespace Plasma -{ - class Animation; -} - -class AMAROK_EXPORT ToolBoxIcon: public Plasma::IconWidget -{ - Q_OBJECT - -public: - explicit ToolBoxIcon( QGraphicsItem *parent = 0, const float opacity = 0.8 ); - ~ToolBoxIcon(); - - /** - * reimplemented from Plasma::Icon - */ - QPainterPath shape() const; - - QRectF boundingRect() const; - - /** - * reimplemented from Plasma::Icon - */ - void setText( const QString &text ); - - QString text() const; - - void setBrush( const QBrush& ); - -protected: - void paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0 ); - void hoverEnterEvent( QGraphicsSceneHoverEvent *event ); - void hoverLeaveEvent( QGraphicsSceneHoverEvent *event ); - void mousePressEvent( QGraphicsSceneMouseEvent *event ); - -Q_SIGNALS: - void appletChosen( const QString &pluginName ); - -private Q_SLOTS: - void mousePressed( bool pressed ); - -private: - bool m_hovering; - - const qreal m_baseOpacity; - QWeakPointer m_animHighLight; - - QGraphicsSimpleTextItem *m_text; - QBrush m_defaultTextBrush; -}; - -#endif - diff --git a/src/context/widgets/appletexplorer/AppletExplorer.cpp b/src/context/widgets/appletexplorer/AppletExplorer.cpp deleted file mode 100644 index c517896aae..0000000000 --- a/src/context/widgets/appletexplorer/AppletExplorer.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 William Viana Soares * - * Significant parts of this code is inspired * - * and/or copied from KDE Plasma sources, available * - * at kdebase/workspace/libs/plasmagenericshell * - * * - ****************************************************************************************/ - -/**************************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#define DEBUG_PREFIX "AppletExplorer" - -#include "AppletExplorer.h" - -#include "AppletIcon.h" -#include "core/support/Debug.h" -#include "PaletteHandler.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace Context -{ - -AppletExplorer::AppletExplorer( QGraphicsItem *parent ) - : QGraphicsWidget( parent ) - , m_containment( 0 ) - , m_scrollWidget( 0 ) -{ - init(); -} - -AppletExplorer::~AppletExplorer() -{} - -void -AppletExplorer::addApplet( const QString &name ) -{ - DEBUG_BLOCK - if( !name.isEmpty() && containment() ) - emit addAppletToContainment( name, -1 ); //always add the applet at the end -} - -void -AppletExplorer::hideMenu() -{ - hide(); - emit appletExplorerHid(); -} - -void -AppletExplorer::init() -{ - QGraphicsLinearLayout *layout = new QGraphicsLinearLayout( Qt::Vertical, this ); - QSignalMapper *iconTriggerMapper = new QSignalMapper( this ); - QGraphicsWidget *scrollView = new QGraphicsWidget( this ); - m_scrollWidget = new Plasma::ScrollWidget( this ); - m_scrollWidget->setWidget( scrollView ); - // m_scrollWidget->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - m_scrollWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum ); - QGraphicsLinearLayout *scrollLayout = new QGraphicsLinearLayout( scrollView ); - m_scrollWidget->setMinimumHeight( 0 ); - - foreach( AppletIconWidget *widget, listAppletWidgets() ) - { - scrollLayout->addItem( widget ); - scrollLayout->setAlignment( widget, Qt::AlignCenter ); - widget->setMinimumSize( widget->sizeFromIconSize( 48 ) ); - widget->setMaximumSize( widget->sizeFromIconSize( 48 ) ); - connect( widget, SIGNAL(clicked()), iconTriggerMapper, SLOT(map()) ); - iconTriggerMapper->setMapping( widget, widget->pluginName() ); - } - connect( iconTriggerMapper, SIGNAL(mapped(QString)), SLOT(addApplet(QString)) ); - - Plasma::IconWidget *appletIcon = new Plasma::IconWidget( this ); - appletIcon->setIcon( QIcon::fromTheme( "preferences-plugin" ) ); - const QSizeF iconSize = appletIcon->sizeFromIconSize( 22 ); - appletIcon->setMinimumSize( iconSize ); - appletIcon->setMaximumSize( iconSize ); - - Plasma::IconWidget *hideIcon = new Plasma::IconWidget( this ); - hideIcon->setIcon( QIcon::fromTheme( "window-close" ) ); - hideIcon->setToolTip( i18n( "Hide menu" ) ); - hideIcon->setMinimumSize( iconSize ); - hideIcon->setMaximumSize( iconSize ); - connect( hideIcon, SIGNAL(clicked()), this, SLOT(hideMenu()) ); - - Plasma::IconWidget *forwardIcon = new Plasma::IconWidget( this ); - forwardIcon->setIcon( QIcon::fromTheme( "go-next" ) ); - forwardIcon->setMinimumSize( iconSize ); - forwardIcon->setMaximumSize( iconSize ); - connect( forwardIcon, SIGNAL(clicked()), this, SLOT(scrollRight()) ); - - Plasma::IconWidget *backIcon = new Plasma::IconWidget( this ); - backIcon->setIcon( QIcon::fromTheme( "go-previous" ) ); - backIcon->setMinimumSize( iconSize ); - backIcon->setMaximumSize( iconSize ); - connect( backIcon, SIGNAL(clicked()), this, SLOT(scrollLeft()) ); - - QLabel *titleLabel = new QLabel( i18n("Applet Explorer") ); - titleLabel->setAttribute( Qt::WA_NoSystemBackground ); - titleLabel->setWordWrap( false ); - QGraphicsProxyWidget *titleWidget = new QGraphicsProxyWidget( this ); - titleWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); - titleWidget->setWidget( titleLabel ); - - QGraphicsLinearLayout *headerLayout = new QGraphicsLinearLayout; - headerLayout->addItem( appletIcon ); - headerLayout->addItem( titleWidget ); - headerLayout->addItem( backIcon ); - headerLayout->addItem( forwardIcon ); - headerLayout->addItem( hideIcon ); - headerLayout->setAlignment( appletIcon, Qt::AlignLeft | Qt::AlignTop ); - headerLayout->setAlignment( titleWidget, Qt::AlignLeft | Qt::AlignTop ); - headerLayout->setAlignment( backIcon, Qt::AlignRight | Qt::AlignTop ); - headerLayout->setAlignment( forwardIcon, Qt::AlignRight | Qt::AlignTop ); - headerLayout->setAlignment( hideIcon, Qt::AlignRight | Qt::AlignTop ); - headerLayout->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Maximum ); - - layout->addItem( headerLayout ); - layout->addItem( m_scrollWidget ); - layout->setAlignment( headerLayout, Qt::AlignTop ); - layout->setAlignment( m_scrollWidget, Qt::AlignCenter ); -} - -QSizeF -AppletExplorer::sizeHint( Qt::SizeHint which, const QSizeF &constraint ) const -{ - QSizeF sz = QGraphicsWidget::sizeHint( which, constraint ); - return QSizeF( sz.width(), sz.height() + 2 ); -} - -void -AppletExplorer::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget ) -{ - Q_UNUSED( option ) - Q_UNUSED( widget ) - - painter->save(); - painter->setRenderHint( QPainter::Antialiasing ); - painter->setOpacity( 0.9 ); - - QLinearGradient gradient( boundingRect().topLeft().x(), boundingRect().topLeft().y(), - boundingRect().bottomLeft().x(), boundingRect().bottomLeft().y() / 1.8 + 3 ); - - QColor highlight = PaletteHandler::highlightColor(); - gradient.setSpread( QGradient::RepeatSpread ); - gradient.setColorAt( 0, highlight.lighter( 100 ) ); - gradient.setColorAt( 1, highlight.lighter( 140 ) ); - QPainterPath path; - path.addRoundedRect( boundingRect(), 6, 6 ); - painter->fillPath( path, gradient ); - painter->restore(); - - // draw border - painter->save(); - painter->setRenderHint( QPainter::Antialiasing ); - painter->translate( 0.5, 0.5 ); - QPen pen( PaletteHandler::highlightColor().lighter( 140 ) ); - pen.setWidth( 3 ); - painter->setPen( pen ); - painter->drawRoundedRect( boundingRect(), 6, 6 ); - painter->restore(); -} - -void -AppletExplorer::setContainment( Containment *containment ) -{ - m_containment = containment; -} - -Containment * -AppletExplorer::containment() const -{ - return m_containment; -} - -void -AppletExplorer::scrollLeft() -{ - QGraphicsSceneWheelEvent event( QEvent::GraphicsSceneWheel ); - event.setDelta( 480 ); - scene()->sendEvent( m_scrollWidget, &event ); -} - -void -AppletExplorer::scrollRight() -{ - QGraphicsSceneWheelEvent event( QEvent::GraphicsSceneWheel ); - event.setDelta( -480 ); - scene()->sendEvent( m_scrollWidget, &event ); -} - -QList -AppletExplorer::listAppletWidgets() -{ - QList widgets; - foreach( const KPluginInfo &info, Plasma::Applet::listAppletInfo( QString(), "amarok" ) ) - { - if( info.property( "NoDisplay" ).toBool() || info.category() == i18n( "Containments" ) ) - continue; - - widgets << new AppletIconWidget( info, this ); - } - return widgets; -} - -} //namespace Context - diff --git a/src/context/widgets/appletexplorer/AppletExplorer.h b/src/context/widgets/appletexplorer/AppletExplorer.h deleted file mode 100644 index 4548b585be..0000000000 --- a/src/context/widgets/appletexplorer/AppletExplorer.h +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 William Viana Soares * - * Significant parts of this code is inspired * - * and/or copied from KDE Plasma sources, available * - * at kdebase/workspace/libs/plasmagenericshell * - * * - ****************************************************************************************/ - -/**************************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef APPLET_EXPLORER_H -#define APPLET_EXPLORER_H - -#include "amarok_export.h" - -#include -#include - -class QAction; -class QStyleOptionGraphicsItem; - -namespace Plasma { - class IconWidget; - class ScrollWidget; -} - -namespace Context -{ -class AppletIconWidget; -class Containment; - -class AMAROK_EXPORT AppletExplorer: public QGraphicsWidget -{ - Q_OBJECT - -public: - AppletExplorer( QGraphicsItem *parent = 0 ); - virtual ~AppletExplorer(); - - void paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0 ); - void setContainment( Containment *containment ); - Containment *containment() const; - -Q_SIGNALS: - void addAppletToContainment( const QString &pluginName, const int loc ); - void appletExplorerHid(); - -protected: - QSizeF sizeHint( Qt::SizeHint which, const QSizeF &constraint = QSizeF() ) const; - -private Q_SLOTS: - void addApplet( const QString &name ); - void hideMenu(); - void scrollLeft(); - void scrollRight(); - -private: - void init(); - QList listAppletWidgets(); - Containment *m_containment; - Plasma::ScrollWidget *m_scrollWidget; - Q_DISABLE_COPY( AppletExplorer ) -}; - -} // namespace Context - -#endif diff --git a/src/context/widgets/appletexplorer/AppletIcon.cpp b/src/context/widgets/appletexplorer/AppletIcon.cpp deleted file mode 100644 index b254145f1d..0000000000 --- a/src/context/widgets/appletexplorer/AppletIcon.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 William Viana Soares * - * Copyright (c) 2009 Mark Kretschmann * - * * - * Significant parts of this code is inspired * - * and/or copied from KDE Plasma sources, available * - * at kdebase/workspace/libs/plasmagenericshell * - * * - ****************************************************************************************/ - -/**************************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#include "AppletIcon.h" - -#include "PaletteHandler.h" - -#include -#include - -#include -#include - -namespace Context -{ - -AppletIconWidget::AppletIconWidget( const KPluginInfo &info, QGraphicsItem *parent ) - : Plasma::IconWidget( parent ) - , m_pluginName( info.pluginName() ) -{ - setText( info.name() ); - setIcon( QIcon::fromTheme( info.icon().isEmpty() ? "application-x-plasma" : info.icon() ) ); - setToolTip( info.name() ); - setTextBackgroundColor( Qt::transparent ); -} - -AppletIconWidget::~AppletIconWidget() -{} - -void -AppletIconWidget::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget ) -{ - painter->save(); - painter->setRenderHint( QPainter::Antialiasing ); - - QColor topColor = The::paletteHandler()->palette().color( QPalette::Base ); - QColor bottomColor = topColor; - topColor.setAlpha( 200 ); - bottomColor.setAlpha( 100 ); - qreal radius = 6; - qreal boundWidth = boundingRect().width(); - qreal boundHeight = boundingRect().height(); - - // draw top half of rounded applet - QPainterPath path; - path.moveTo( 0, boundHeight / 2 ); - path.lineTo( 0, radius ); - path.quadTo( 0, 0, radius, 0 ); - path.lineTo( boundWidth - radius, 0 ); - path.quadTo( boundWidth, 0, boundWidth, radius ); - path.lineTo( boundWidth, boundHeight / 2 ); - path.lineTo( 0, boundHeight / 2 ); - - painter->fillPath( path, topColor ); - QPainterPath bottom; - bottom.moveTo( 0, boundHeight / 2 ); - bottom.lineTo( 0, boundHeight - radius ); - bottom.quadTo( 0, boundHeight, radius, boundHeight ); - bottom.lineTo( boundWidth - radius, boundHeight ); - bottom.quadTo( boundWidth, boundHeight, boundWidth, boundHeight - radius ); - bottom.lineTo( boundWidth, boundHeight / 2 ); - bottom.lineTo( 0, boundHeight / 2 ); - - painter->fillPath( bottom, bottomColor ); - painter->restore(); - Plasma::IconWidget::paint( painter, option, widget ); -} - -QString -AppletIconWidget::pluginName() const -{ - return m_pluginName; -} - -} // namespace Context - diff --git a/src/context/widgets/appletexplorer/AppletIcon.h b/src/context/widgets/appletexplorer/AppletIcon.h deleted file mode 100644 index b9eda8b755..0000000000 --- a/src/context/widgets/appletexplorer/AppletIcon.h +++ /dev/null @@ -1,59 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 William Viana Soares * - * Copyright (c) 2009 Mark Kretschmann * - * * - * Significant parts of this code is inspired * - * and/or copied from KDE Plasma sources, available * - * at kdebase/workspace/libs/plasmagenericshell * - * * - ****************************************************************************************/ - -/**************************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -#ifndef APPLET_ICON_H -#define APPLET_ICON_H - -#include "amarok_export.h" - -#include - -class KPluginInfo; -class QPainter; - -namespace Context -{ - -class AMAROK_EXPORT AppletIconWidget: public Plasma::IconWidget -{ - Q_OBJECT - -public: - explicit AppletIconWidget( const KPluginInfo &info, QGraphicsItem *parent = 0 ); - virtual ~AppletIconWidget(); - - QString pluginName() const; - -protected: - void paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0 ); - -private: - QString m_pluginName; - Q_DISABLE_COPY( AppletIconWidget ) -}; - -} // namespace Context - -#endif // APPLET_ICON_H diff --git a/src/dialogs/DiagnosticDialog.cpp b/src/dialogs/DiagnosticDialog.cpp index aace1254ad..a5d6e5e8c9 100644 --- a/src/dialogs/DiagnosticDialog.cpp +++ b/src/dialogs/DiagnosticDialog.cpp @@ -1,144 +1,142 @@ /**************************************************************************************** * Copyright (c) 2012 Andrzej J. R. Hunt * * Copyright (c) Mark Kretschmann * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "DiagnosticDialog.h" -// #include "context/ContextView.h" +#include "context/ContextView.h" #include "PluginManager.h" #include "scripting/scriptmanager/ScriptManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include DiagnosticDialog::DiagnosticDialog( const KAboutData about, QWidget *parent ) : QDialog( parent ) { setLayout( new QVBoxLayout ); m_textBox = new QPlainTextEdit( generateReport( &about ), this ); m_textBox->setReadOnly( true ); layout()->addWidget( m_textBox ); auto buttonBox = new QDialogButtonBox( this ); auto closeButton = buttonBox->addButton( QDialogButtonBox::Close ); auto copyButton = new QPushButton( i18n( "Copy to Clipboard" ) ); buttonBox->addButton( copyButton, QDialogButtonBox::ActionRole ); layout()->addWidget( buttonBox ); setWindowTitle( i18nc( "%1 is the program name", "%1 Diagnostics", KAboutData::applicationData().displayName() ) ); resize( QSize( 480, 460 ) ); connect( closeButton, &QPushButton::clicked, this, &DiagnosticDialog::close ); connect( copyButton, &QPushButton::clicked, this, &DiagnosticDialog::slotCopyToClipboard ); setAttribute( Qt::WA_DeleteOnClose ); } const QString DiagnosticDialog::generateReport( const KAboutData *aboutData ) { // Get scripts -- we have to assemble 3 lists into one KPluginInfo::List aScripts; const auto aScriptManager = ScriptManager::instance(); aScripts.append( aScriptManager->scripts( QLatin1String( "Generic" ) ) ); aScripts.append( aScriptManager->scripts( QLatin1String( "Lyrics" ) ) ); aScripts.append( aScriptManager->scripts( QLatin1String( "Scriptable Service" ) ) ); // Format the data to be readable QString aScriptString; foreach( KPluginInfo aInfo, aScripts ) { if( aInfo.isPluginEnabled() ) aScriptString += " " + aInfo.name() + " (" + aInfo.version() + ")\n"; } // Get plugins -- we have to assemble a list again. QList aPlugins; const auto aPluginManager = Plugins::PluginManager::instance(); aPlugins.append( aPluginManager->enabledPlugins( Plugins::PluginManager::Collection ) ); aPlugins.append( aPluginManager->enabledPlugins( Plugins::PluginManager::Service ) ); aPlugins.append( aPluginManager->enabledPlugins( Plugins::PluginManager::Importer ) ); QString aPluginString; for( const auto &aInfo : aPlugins ) { aPluginString += " " + aInfo.name() + " (" + aInfo.version() + ")\n"; } -/* FIXME: disabled temporarily for KF5 porting // Get applets QString appletString; const QStringList appletList = Context::ContextView::self()->currentAppletNames(); foreach( const QString &applet, appletList ) { // Currently we cannot extract the applet version number this way appletString += " " + applet + '\n'; } -*/ const KService::Ptr aPhononBackend = KServiceTypeTrader::self()->preferredService( "PhononBackend" ); const bool hasPulse = Phonon::PulseSupport::getInstance()->isActive(); const QString pulse = hasPulse ? i18nc( "Usage", "Yes" ) : i18nc( "Usage", "No" ); return i18n( "%1 Diagnostics\n\nGeneral Information:\n" " %1 Version: %2\n" " KDE Frameworks Version: %3\n" " Qt Version: %4\n" " Phonon Version: %5\n" " Phonon Backend: %6 (%7)\n" " PulseAudio: %8\n\n", KAboutData::applicationData().displayName(), aboutData->version(), // Amarok KCoreAddons::versionString(), // KDE Frameworks qVersion(), // Qt Phonon::phononVersion(), // Phonon aPhononBackend->name(), aPhononBackend->property( "X-KDE-PhononBackendInfo-Version", QVariant::String ).toString(), // & Backend pulse // PulseAudio ) + i18n( "Enabled Scripts:\n%1\n" "Enabled Plugins:\n%2\n" "Enabled Applets:\n%3\n", - aScriptString, aPluginString //FIXME: disabled temporarily for KF5 porting: //, appletString + aScriptString, aPluginString, appletString ); } void DiagnosticDialog::slotCopyToClipboard() const { QClipboard *clipboard = QApplication::clipboard(); clipboard->setText( m_textBox->toPlainText() ); } diff --git a/src/playlist/view/listview/PrettyListView.cpp b/src/playlist/view/listview/PrettyListView.cpp index bfd3e68933..e5ce0dc928 100644 --- a/src/playlist/view/listview/PrettyListView.cpp +++ b/src/playlist/view/listview/PrettyListView.cpp @@ -1,1055 +1,1053 @@ /**************************************************************************************** * Copyright (c) 2008 Soren Harward * * Copyright (c) 2009 Téo Mrnjavac * * Copyright (c) 2009 Nikolaj Hald Nielsen * * Copyright (c) 2009 John Atkinson * * Copyright (c) 2009-2010 Oleksandr Khayrullin * * Copyright (c) 2010 Nanno Langstraat * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) version 3 or * * any later version accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of * * version 3 of the license. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "Playlist::PrettyListView" #include "PrettyListView.h" #include "amarokconfig.h" #include "AmarokMimeData.h" -// #include "context/ContextView.h" -// #include "context/popupdropper/libpud/PopupDropper.h" +#include "context/ContextView.h" +#include "context/popupdropper/libpud/PopupDropper.h" #include "core/support/Debug.h" #include "EngineController.h" #include "dialogs/TagDialog.h" #include "GlobalCurrentTrackActions.h" #include "core/capabilities/ActionsCapability.h" #include "core/capabilities/FindInSourceCapability.h" #include "core/capabilities/MultiSourceCapability.h" #include "core/meta/Meta.h" #include "PaletteHandler.h" #include "playlist/layouts/LayoutManager.h" #include "playlist/proxymodels/GroupingProxy.h" #include "playlist/PlaylistActions.h" #include "playlist/PlaylistModelStack.h" #include "playlist/PlaylistController.h" #include "playlist/view/PlaylistViewCommon.h" #include "playlist/PlaylistDefines.h" #include "PopupDropperFactory.h" #include "SvgHandler.h" #include "SourceSelectionPopup.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Playlist::PrettyListView::PrettyListView( QWidget* parent ) : QListView( parent ) , ViewCommon() , m_headerPressIndex( QModelIndex() ) , m_mousePressInHeader( false ) , m_skipAutoScroll( false ) , m_firstScrollToActiveTrack( true ) , m_rowsInsertedScrollItem( 0 ) , m_showOnlyMatches( false ) , m_pd( 0 ) { // QAbstractItemView basics setModel( The::playlist()->qaim() ); m_prettyDelegate = new PrettyItemDelegate( this ); connect( m_prettyDelegate, &PrettyItemDelegate::redrawRequested, this, &Playlist::PrettyListView::redrawActive ); setItemDelegate( m_prettyDelegate ); setSelectionMode( ExtendedSelection ); setDragDropMode( DragDrop ); setDropIndicatorShown( false ); // we draw our own drop indicator setEditTriggers ( SelectedClicked | EditKeyPressed ); setAutoScroll( true ); setVerticalScrollMode( ScrollPerPixel ); setMouseTracking( true ); // Rendering adjustments setFrameShape( QFrame::NoFrame ); setAlternatingRowColors( true) ; The::paletteHandler()->updateItemView( this ); connect( The::paletteHandler(), &PaletteHandler::newPalette, this, &PrettyListView::newPalette ); setAutoFillBackground( false ); // Signal connections connect( this, &Playlist::PrettyListView::doubleClicked, this, &Playlist::PrettyListView::trackActivated ); connect( selectionModel(), &QItemSelectionModel::selectionChanged, this, &Playlist::PrettyListView::slotSelectionChanged ); connect( LayoutManager::instance(), &LayoutManager::activeLayoutChanged, this, &PrettyListView::playlistLayoutChanged ); if (auto m = static_cast(model())) { connect( m, &Playlist::Model::activeTrackChanged, this, &Playlist::PrettyListView::slotPlaylistActiveTrackChanged ); connect( m, &Playlist::Model::queueChanged, viewport(), QOverload<>::of(&QWidget::update) ); } else warning() << "Model is not a Playlist::Model"; // Warning, this one doesn't connect to the normal 'model()' (i.e. '->top()'), but to '->bottom()'. connect( Playlist::ModelStack::instance()->bottom(), &Playlist::Model::rowsInserted, this, &Playlist::PrettyListView::bottomModelRowsInserted ); // Timers m_proxyUpdateTimer = new QTimer( this ); m_proxyUpdateTimer->setSingleShot( true ); connect( m_proxyUpdateTimer, &QTimer::timeout, this, &Playlist::PrettyListView::updateProxyTimeout ); m_animationTimer = new QTimer(this); connect( m_animationTimer, &QTimer::timeout, this, &Playlist::PrettyListView::redrawActive ); m_animationTimer->setInterval( 250 ); playlistLayoutChanged(); // We do the following call here to be formally correct, but note: // - It happens to be redundant, because 'playlistLayoutChanged()' already schedules // another one, via a QTimer( 0 ). // - Both that one and this one don't work right (they scroll like 'PositionAtTop', // not 'PositionAtCenter'). This is probably because MainWindow changes its // geometry in a QTimer( 0 )? As a fix, MainWindow does a 'slotShowActiveTrack()' // at the end of its QTimer slot, which will finally scroll to the right spot. slotPlaylistActiveTrackChanged(); } Playlist::PrettyListView::~PrettyListView() {} int Playlist::PrettyListView::verticalOffset() const { int ret = QListView::verticalOffset(); if ( verticalScrollBar() && verticalScrollBar()->maximum() ) ret += verticalScrollBar()->value() * 10 / verticalScrollBar()->maximum(); return ret; } void Playlist::PrettyListView::editTrackInformation() { Meta::TrackList tl; foreach( const QModelIndex &index, selectedIndexes() ) { tl.append( index.data( TrackRole ).value() ); } if( !tl.isEmpty() ) { TagDialog *dialog = new TagDialog( tl, this ); dialog->show(); } } void Playlist::PrettyListView::playFirstSelected() { QModelIndexList selected = selectedIndexes(); if( !selected.isEmpty() ) trackActivated( selected.first() ); } void Playlist::PrettyListView::removeSelection() { QList sr = selectedRows(); if( !sr.isEmpty() ) { // Now that we have the list of selected rows in the topmost proxy, we can perform the // removal. The::playlistController()->removeRows( sr ); // Next, we look for the first row. int firstRow = sr.first(); foreach( int i, sr ) { if( i < firstRow ) firstRow = i; } //Select the track occupied by the first deleted track. Also move the current item to here as //button presses up or down wil otherwise not behave as expected. firstRow = qBound( 0, firstRow, model()->rowCount() - 1 ); QModelIndex newSelectionIndex = model()->index( firstRow, 0 ); setCurrentIndex( newSelectionIndex ); selectionModel()->select( newSelectionIndex, QItemSelectionModel::Select ); } } void Playlist::PrettyListView::queueSelection() { Actions::instance()->queue( selectedRows() ); } void Playlist::PrettyListView::dequeueSelection() { Actions::instance()->dequeue( selectedRows() ); } void Playlist::PrettyListView::switchQueueState() // slot { DEBUG_BLOCK const bool isQueued = currentIndex().data( Playlist::QueuePositionRole ).toInt() != 0; isQueued ? dequeueSelection() : queueSelection(); } void Playlist::PrettyListView::selectSource() { DEBUG_BLOCK QList rows = selectedRows(); //for now, bail out of more than 1 row... if ( rows.count() != 1 ) return; //get the track... QModelIndex index = model()->index( rows.at( 0 ), 0 ); Meta::TrackPtr track = index.data( Playlist::TrackRole ).value< Meta::TrackPtr >(); //get multiSource capability: Capabilities::MultiSourceCapability *msc = track->create(); if ( msc ) { debug() << "sources: " << msc->sources(); SourceSelectionPopup * sourceSelector = new SourceSelectionPopup( this, msc ); sourceSelector->show(); //dialog deletes msc when done with it. } } void Playlist::PrettyListView::scrollToActiveTrack() { DEBUG_BLOCK if( m_skipAutoScroll ) { m_skipAutoScroll = false; return; } QModelIndex activeIndex = model()->index( The::playlist()->activeRow(), 0, QModelIndex() ); if ( activeIndex.isValid() ) { scrollTo( activeIndex, QAbstractItemView::PositionAtCenter ); m_firstScrollToActiveTrack = false; m_rowsInsertedScrollItem = 0; // This "new active track" scroll supersedes a pending "rows inserted" scroll. } } void Playlist::PrettyListView::downOneTrack() { DEBUG_BLOCK moveTrackSelection( 1 ); } void Playlist::PrettyListView::upOneTrack() { DEBUG_BLOCK moveTrackSelection( -1 ); } void Playlist::PrettyListView::moveTrackSelection( int offset ) { if ( offset == 0 ) return; int finalRow = model()->rowCount() - 1; int target = 0; if ( offset < 0 ) target = finalRow; QList rows = selectedRows(); if ( rows.count() > 0 ) target = rows.at( 0 ) + offset; target = qBound(0, target, finalRow); QModelIndex index = model()->index( target, 0 ); setCurrentIndex( index ); } void Playlist::PrettyListView::slotPlaylistActiveTrackChanged() { DEBUG_BLOCK // A playlist 'activeTrackChanged' signal happens: // - During startup, on "saved playlist" load. (Might happen before this view exists) // - When Amarok starts playing a new item in the playlist. // In that case, don't auto-scroll if the user doesn't like us to. if( AmarokConfig::autoScrollPlaylist() || m_firstScrollToActiveTrack ) scrollToActiveTrack(); } void Playlist::PrettyListView::slotSelectionChanged() { m_lastTimeSelectionChanged = QDateTime::currentDateTime(); } void Playlist::PrettyListView::trackActivated( const QModelIndex& idx ) { DEBUG_BLOCK m_skipAutoScroll = true; // we don't want to do crazy view changes when selecting an item in the view Actions::instance()->play( idx ); //make sure that the track we just activated is also set as the current index or //the selected index will get moved to the first row, making keyboard navigation difficult (BUG 225791) selectionModel_setCurrentIndex( idx, QItemSelectionModel::ClearAndSelect ); setFocus(); } // The following 2 functions are a workaround for crash BUG 222961 and BUG 229240: // There appears to be a bad interaction between Qt 'setCurrentIndex()' and // Qt 'selectedIndexes()' / 'selectionModel()->select()' / 'scrollTo()'. // // 'setCurrentIndex()' appears to do something bad with its QModelIndex parameter, // leading to a crash deep within Qt. // // It might be our fault, but we suspect a bug in Qt. (Qt 4.6 at least) // // The problem goes away if we use a fresh QModelIndex, which we also don't re-use // afterwards. void Playlist::PrettyListView::setCurrentIndex( const QModelIndex &index ) { QModelIndex indexCopy = model()->index( index.row(), index.column() ); QListView::setCurrentIndex( indexCopy ); } void Playlist::PrettyListView::selectionModel_setCurrentIndex( const QModelIndex &index, QItemSelectionModel::SelectionFlags command ) { QModelIndex indexCopy = model()->index( index.row(), index.column() ); selectionModel()->setCurrentIndex( indexCopy, command ); } void Playlist::PrettyListView::showEvent( QShowEvent* event ) { QTimer::singleShot( 0, this, &Playlist::PrettyListView::fixInvisible ); QListView::showEvent( event ); } // This method is a workaround for BUG 184714. // // It prevents the playlist from becoming invisible (clear) after changing the model, while Amarok is hidden in the tray. // Without this workaround the playlist stays invisible when the application is restored from the tray. // This is especially a problem with the Dynamic Playlist mode, which modifies the model without user interaction. // // The bug only seems to happen with Qt 4.5.x, so it might actually be a bug in Qt. void Playlist::PrettyListView::fixInvisible() //SLOT { // DEBUG_BLOCK // Part 1: Palette change newPalette( palette() ); // Part 2: Change item selection const QItemSelection oldSelection( selectionModel()->selection() ); selectionModel()->clear(); selectionModel()->select( oldSelection, QItemSelectionModel::SelectCurrent ); // NOTE: A simple update() call is not sufficient, but in fact the above two steps are required. } void Playlist::PrettyListView::contextMenuEvent( QContextMenuEvent* event ) { DEBUG_BLOCK QModelIndex index = indexAt( event->pos() ); if ( !index.isValid() ) return; //Ctrl + Right Click is used for queuing if( event->modifiers() & Qt::ControlModifier ) return; trackMenu( this, &index, event->globalPos() ); event->accept(); } void Playlist::PrettyListView::dragLeaveEvent( QDragLeaveEvent* event ) { m_mousePressInHeader = false; m_dropIndicator = QRect( 0, 0, 0, 0 ); QListView::dragLeaveEvent( event ); } void Playlist::PrettyListView::stopAfterTrack() { const quint64 id = currentIndex().data( UniqueIdRole ).value(); if( Actions::instance()->willStopAfterTrack( id ) ) Actions::instance()->stopAfterPlayingTrack( 0 ); // disable stopping else Actions::instance()->stopAfterPlayingTrack( id ); } void Playlist::PrettyListView::findInSource() { DEBUG_BLOCK Meta::TrackPtr track = currentIndex().data( TrackRole ).value(); if ( track ) { if( track->has() ) { Capabilities::FindInSourceCapability *fis = track->create(); if ( fis ) { fis->findInSource(); } delete fis; } } } void Playlist::PrettyListView::dragEnterEvent( QDragEnterEvent *event ) { const QMimeData *mime = event->mimeData(); if( mime->hasUrls() || mime->hasFormat( AmarokMimeData::TRACK_MIME ) || mime->hasFormat( AmarokMimeData::PLAYLIST_MIME ) || mime->hasFormat( AmarokMimeData::PODCASTEPISODE_MIME ) || mime->hasFormat( AmarokMimeData::PODCASTCHANNEL_MIME ) ) { event->acceptProposedAction(); } } void Playlist::PrettyListView::dragMoveEvent( QDragMoveEvent* event ) { QModelIndex index = indexAt( event->pos() ); if ( index.isValid() ) { m_dropIndicator = visualRect( index ); } else { // draw it on the bottom of the last item index = model()->index( model()->rowCount() - 1, 0, QModelIndex() ); m_dropIndicator = visualRect( index ); m_dropIndicator = m_dropIndicator.translated( 0, m_dropIndicator.height() ); } QListView::dragMoveEvent( event ); } void Playlist::PrettyListView::dropEvent( QDropEvent* event ) { DEBUG_BLOCK QRect oldDrop = m_dropIndicator; m_dropIndicator = QRect( 0, 0, 0, 0 ); if ( qobject_cast( event->source() ) == this ) { QAbstractItemModel* plModel = model(); int targetRow = indexAt( event->pos() ).row(); targetRow = ( targetRow < 0 ) ? plModel->rowCount() : targetRow; // target of < 0 means we dropped on the end of the playlist QList sr = selectedRows(); int realtarget = The::playlistController()->moveRows( sr, targetRow ); QItemSelection selItems; foreach( int row, sr ) { Q_UNUSED( row ) selItems.select( plModel->index( realtarget, 0 ), plModel->index( realtarget, 0 ) ); realtarget++; } selectionModel()->select( selItems, QItemSelectionModel::ClearAndSelect ); event->accept(); } else { QListView::dropEvent( event ); } // add some padding around the old drop area which to repaint, as we add offsets when painting. See paintEvent(). oldDrop.adjust( -6, -6, 6, 6 ); repaint( oldDrop ); } void Playlist::PrettyListView::keyPressEvent( QKeyEvent *event ) { if( event->matches( QKeySequence::Delete ) ) { removeSelection(); event->accept(); } else if( event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return ) { trackActivated( currentIndex() ); event->accept(); } else if( event->matches( QKeySequence::SelectAll ) ) { QModelIndex topIndex = model()->index( 0, 0 ); QModelIndex bottomIndex = model()->index( model()->rowCount() - 1, 0 ); QItemSelection selItems( topIndex, bottomIndex ); selectionModel()->select( selItems, QItemSelectionModel::ClearAndSelect ); event->accept(); } else QListView::keyPressEvent( event ); } void Playlist::PrettyListView::mousePressEvent( QMouseEvent* event ) { //get the item that was clicked QModelIndex index = indexAt( event->pos() ); //first of all, if a left click, check if the delegate wants to do something about this click if( event->button() == Qt::LeftButton ) { //we need to translate the position of the click into something relative to the item that was clicked. QRect itemRect = visualRect( index ); QPoint relPos = event->pos() - itemRect.topLeft(); if( m_prettyDelegate->clicked( relPos, itemRect, index ) ) { event->accept(); return; //click already handled... } } if ( mouseEventInHeader( event ) && ( event->button() == Qt::LeftButton ) ) { m_mousePressInHeader = true; m_headerPressIndex = QPersistentModelIndex( index ); int rows = index.data( GroupedTracksRole ).toInt(); QModelIndex bottomIndex = model()->index( index.row() + rows - 1, 0 ); //offset by 1 as the actual header item is selected in QListView::mousePressEvent( event ); and is otherwise deselected again QItemSelection selItems( model()->index( index.row() + 1, 0 ), bottomIndex ); QItemSelectionModel::SelectionFlags command = headerPressSelectionCommand( index, event ); selectionModel()->select( selItems, command ); // TODO: if you're doing shift-select on rows above the header, then the rows following the header will be lost from the selection selectionModel_setCurrentIndex( index, QItemSelectionModel::NoUpdate ); } else { m_mousePressInHeader = false; } if ( event->button() == Qt::MidButton ) { QUrl url( QApplication::clipboard()->text() ); if ( url.isValid() ) { QList urls = QList() << url; if( index.isValid() ) The::playlistController()->insertUrls( index.row() + 1, urls ); else The::playlistController()->insertOptioned( urls, Playlist::OnAppendToPlaylistAction ); } } // This should always be forwarded, as it is used to determine the offset // relative to the mouse of the selection we are dragging! QListView::mousePressEvent( event ); // This must go after the call to the super class as the current index is not yet selected otherwise // Queueing support for Ctrl Right click if( event->button() == Qt::RightButton && event->modifiers() & Qt::ControlModifier ) { QList list; if (selectedRows().contains( index.row()) ) { // select all selected rows if mouse is over selection area list = selectedRows(); } else { // select only current mouse-over-index if mouse is out of selection area list.append( index.row() ); } if( index.data( Playlist::QueuePositionRole ).toInt() != 0 ) Actions::instance()->dequeue( list ); else Actions::instance()->queue( list ); } } void Playlist::PrettyListView::mouseReleaseEvent( QMouseEvent* event ) { if ( mouseEventInHeader( event ) && ( event->button() == Qt::LeftButton ) && m_mousePressInHeader && m_headerPressIndex.isValid() ) { QModelIndex index = indexAt( event->pos() ); if ( index == m_headerPressIndex ) { int rows = index.data( GroupedTracksRole ).toInt(); QModelIndex bottomIndex = model()->index( index.row() + rows - 1, 0 ); QItemSelection selItems( index, bottomIndex ); QItemSelectionModel::SelectionFlags command = headerReleaseSelectionCommand( index, event ); selectionModel()->select( selItems, command ); } event->accept(); } else { QListView::mouseReleaseEvent( event ); } m_mousePressInHeader = false; } bool Playlist::PrettyListView::mouseEventInHeader( const QMouseEvent* event ) const { QModelIndex index = indexAt( event->pos() ); if ( index.data( GroupRole ).toInt() == Grouping::Head ) { QPoint mousePressPos = event->pos(); mousePressPos.rx() += horizontalOffset(); mousePressPos.ry() += verticalOffset(); return m_prettyDelegate->insideItemHeader( mousePressPos, rectForIndex( index ) ); } return false; } void Playlist::PrettyListView::paintEvent( QPaintEvent *event ) { if( m_dropIndicator.isValid() || model()->rowCount( rootIndex() ) == 0 ) { QPainter painter( viewport() ); if( m_dropIndicator.isValid() ) { const QPoint offset( 6, 0 ); QColor c = QApplication::palette().color( QPalette::Highlight ); painter.setPen( QPen( c, 6, Qt::SolidLine, Qt::RoundCap ) ); painter.drawLine( m_dropIndicator.topLeft() + offset, m_dropIndicator.topRight() - offset ); } if( model()->rowCount( rootIndex() ) == 0 ) { // here we assume that an empty list is caused by the filter if it's active QString emptyText; if( m_showOnlyMatches && Playlist::ModelStack::instance()->bottom()->rowCount() > 0 ) emptyText = i18n( "Tracks have been hidden due to the active search." ); else emptyText = i18n( "Add some songs here by dragging them from all around." ); QColor c = QApplication::palette().color( foregroundRole() ); c.setAlpha( c.alpha() / 2 ); painter.setPen( c ); painter.drawText( rect(), Qt::AlignCenter | Qt::TextWordWrap, emptyText ); } } QListView::paintEvent( event ); } void Playlist::PrettyListView::startDrag( Qt::DropActions supportedActions ) { DEBUG_BLOCK QModelIndexList indices = selectedIndexes(); if( indices.isEmpty() ) return; // no items selected in the view, abort. See bug 226167 //Waah? when a parent item is dragged, startDrag is called a bunch of times static bool ongoingDrags = false; if( ongoingDrags ) return; ongoingDrags = true; -/* FIXME: disabled temporarily for KF5 porting if( !m_pd ) m_pd = The::popupDropperFactory()->createPopupDropper( Context::ContextView::self() ); if( m_pd && m_pd->isHidden() ) { m_pd->setSvgRenderer( The::svgHandler()->getRenderer( "amarok/images/pud_items.svg" ) ); qDebug() << "svgHandler SVG renderer is " << (QObject*)(The::svgHandler()->getRenderer( "amarok/images/pud_items.svg" )); qDebug() << "m_pd SVG renderer is " << (QObject*)(m_pd->svgRenderer()); qDebug() << "does play exist in renderer? " << ( The::svgHandler()->getRenderer( "amarok/images/pud_items.svg" )->elementExists( "load" ) ); QList actions = actionsFor( this, &indices.first() ); foreach( QAction * action, actions ) m_pd->addItem( The::popupDropperFactory()->createItem( action ), true ); m_pd->show(); } -*/ QListView::startDrag( supportedActions ); debug() << "After the drag!"; if( m_pd ) { debug() << "clearing PUD"; connect( m_pd, &PopupDropper::fadeHideFinished, m_pd, &PopupDropper::clear ); m_pd->hide(); } ongoingDrags = false; } bool Playlist::PrettyListView::edit( const QModelIndex &index, EditTrigger trigger, QEvent *event ) { // we want to prevent a click to change the selection and open the editor (BR 220818) if( m_lastTimeSelectionChanged.msecsTo( QDateTime::currentDateTime() ) < qApp->doubleClickInterval() + 50 ) return false; return QListView::edit( index, trigger, event ); } QItemSelectionModel::SelectionFlags Playlist::PrettyListView::headerPressSelectionCommand( const QModelIndex& index, const QMouseEvent* event ) const { if ( !index.isValid() ) return QItemSelectionModel::NoUpdate; const bool shiftKeyPressed = event->modifiers() & Qt::ShiftModifier; //const bool controlKeyPressed = event->modifiers() & Qt::ControlModifier; const bool indexIsSelected = selectionModel()->isSelected( index ); const bool controlKeyPressed = event->modifiers() & Qt::ControlModifier; if ( shiftKeyPressed ) return QItemSelectionModel::SelectCurrent; if ( indexIsSelected && controlKeyPressed ) //make this consistent with how single items work. This also makes it possible to drag the header return QItemSelectionModel::Deselect; return QItemSelectionModel::Select; } QItemSelectionModel::SelectionFlags Playlist::PrettyListView::headerReleaseSelectionCommand( const QModelIndex& index, const QMouseEvent* event ) const { if ( !index.isValid() ) return QItemSelectionModel::NoUpdate; const bool shiftKeyPressed = event->modifiers() & Qt::ShiftModifier; const bool controlKeyPressed = event->modifiers() & Qt::ControlModifier; if ( !controlKeyPressed && !shiftKeyPressed ) return QItemSelectionModel::ClearAndSelect; return QItemSelectionModel::NoUpdate; } QList Playlist::PrettyListView::selectedRows() const { QList rows; foreach( const QModelIndex &idx, selectedIndexes() ) rows.append( idx.row() ); return rows; } void Playlist::PrettyListView::newPalette( const QPalette & palette ) { Q_UNUSED( palette ) The::paletteHandler()->updateItemView( this ); reset(); } void Playlist::PrettyListView::find( const QString &searchTerm, int fields, bool filter ) { bool updateProxy = false; if ( ( The::playlist()->currentSearchFields() != fields ) || ( The::playlist()->currentSearchTerm() != searchTerm ) ) updateProxy = true; m_searchTerm = searchTerm; m_fields = fields; m_filter = filter; int row = The::playlist()->find( m_searchTerm, m_fields ); if( row != -1 ) { //select this track QModelIndex index = model()->index( row, 0 ); QItemSelection selItems( index, index ); selectionModel()->select( selItems, QItemSelectionModel::SelectCurrent ); } //instead of kicking the proxy right away, start a small timeout. //this stops us from updating it for each letter of a long search term, //and since it does not affect any views, this is fine. Worst case is that //a navigator skips to a track form the old search if the track change happens //before this timeout. Only start count if values have actually changed! if ( updateProxy ) startProxyUpdateTimeout(); } void Playlist::PrettyListView::findNext( const QString & searchTerm, int fields ) { DEBUG_BLOCK QList selected = selectedRows(); bool updateProxy = false; if ( ( The::playlist()->currentSearchFields() != fields ) || ( The::playlist()->currentSearchTerm() != searchTerm ) ) updateProxy = true; int currentRow = -1; if( selected.size() > 0 ) currentRow = selected.last(); int row = The::playlist()->findNext( searchTerm, currentRow, fields ); if( row != -1 ) { //select this track QModelIndex index = model()->index( row, 0 ); QItemSelection selItems( index, index ); selectionModel()->select( selItems, QItemSelectionModel::SelectCurrent ); QModelIndex foundIndex = model()->index( row, 0, QModelIndex() ); setCurrentIndex( foundIndex ); if ( foundIndex.isValid() ) scrollTo( foundIndex, QAbstractItemView::PositionAtCenter ); emit( found() ); } else emit( notFound() ); if ( updateProxy ) The::playlist()->filterUpdated(); } void Playlist::PrettyListView::findPrevious( const QString & searchTerm, int fields ) { DEBUG_BLOCK QList selected = selectedRows(); bool updateProxy = false; if ( ( The::playlist()->currentSearchFields() != fields ) || ( The::playlist()->currentSearchTerm() != searchTerm ) ) updateProxy = true; int currentRow = model()->rowCount(); if( selected.size() > 0 ) currentRow = selected.first(); int row = The::playlist()->findPrevious( searchTerm, currentRow, fields ); if( row != -1 ) { //select this track QModelIndex index = model()->index( row, 0 ); QItemSelection selItems( index, index ); selectionModel()->select( selItems, QItemSelectionModel::SelectCurrent ); QModelIndex foundIndex = model()->index( row, 0, QModelIndex() ); setCurrentIndex( foundIndex ); if ( foundIndex.isValid() ) scrollTo( foundIndex, QAbstractItemView::PositionAtCenter ); emit( found() ); } else emit( notFound() ); if ( updateProxy ) The::playlist()->filterUpdated(); } void Playlist::PrettyListView::clearSearchTerm() { DEBUG_BLOCK // Choose a focus item, to scroll to later. QModelIndex focusIndex; QModelIndexList selected = selectedIndexes(); if( !selected.isEmpty() ) focusIndex = selected.first(); else focusIndex = indexAt( QPoint( 0, 0 ) ); // Remember the focus item id, because the row numbers change when we reset the filter. quint64 focusItemId = The::playlist()->idAt( focusIndex.row() ); The::playlist()->clearSearchTerm(); The::playlist()->filterUpdated(); // Now scroll to the focus item. QModelIndex newIndex = model()->index( The::playlist()->rowForId( focusItemId ), 0, QModelIndex() ); if ( newIndex.isValid() ) scrollTo( newIndex, QAbstractItemView::PositionAtCenter ); } void Playlist::PrettyListView::startProxyUpdateTimeout() { DEBUG_BLOCK if ( m_proxyUpdateTimer->isActive() ) m_proxyUpdateTimer->stop(); m_proxyUpdateTimer->setInterval( 200 ); m_proxyUpdateTimer->start(); } void Playlist::PrettyListView::updateProxyTimeout() { DEBUG_BLOCK The::playlist()->filterUpdated(); int row = The::playlist()->find( m_searchTerm, m_fields ); if( row != -1 ) { QModelIndex foundIndex = model()->index( row, 0, QModelIndex() ); setCurrentIndex( foundIndex ); if ( !m_filter ) { if ( foundIndex.isValid() ) scrollTo( foundIndex, QAbstractItemView::PositionAtCenter ); } emit( found() ); } else emit( notFound() ); } void Playlist::PrettyListView::showOnlyMatches( bool onlyMatches ) { m_showOnlyMatches = onlyMatches; The::playlist()->showOnlyMatches( onlyMatches ); } // Handle scrolling to newly inserted playlist items. // Warning, this slot is connected to the 'rowsInserted' signal of the *bottom* model, // not the normal top model. // The reason: FilterProxy can emit *A LOT* (thousands) of 'rowsInserted' signals when its // search string changes. For that case we don't want to do any scrollTo() at all. void Playlist::PrettyListView::bottomModelRowsInserted( const QModelIndex& parent, int start, int end ) { Q_UNUSED( parent ) Q_UNUSED( end ) // skip scrolling if tracks were added while playlist is in dynamicMode if( m_rowsInsertedScrollItem == 0 && !AmarokConfig::dynamicMode() ) { m_rowsInsertedScrollItem = Playlist::ModelStack::instance()->bottom()->idAt( start ); QTimer::singleShot( 0, this, &Playlist::PrettyListView::bottomModelRowsInsertedScroll ); } } void Playlist::PrettyListView::bottomModelRowsInsertedScroll() { DEBUG_BLOCK if( m_rowsInsertedScrollItem ) { // Note: we don't bother handling the case "first inserted item in bottom model // does not have a row in the top 'model()' due to FilterProxy" nicely. int firstRowInserted = The::playlist()->rowForId( m_rowsInsertedScrollItem ); // In the *top* model. QModelIndex index = model()->index( firstRowInserted, 0 ); if( index.isValid() ) scrollTo( index, QAbstractItemView::PositionAtCenter ); m_rowsInsertedScrollItem = 0; } } void Playlist::PrettyListView::redrawActive() { int activeRow = The::playlist()->activeRow(); QModelIndex index = model()->index( activeRow, 0, QModelIndex() ); update( index ); } void Playlist::PrettyListView::playlistLayoutChanged() { if ( LayoutManager::instance()->activeLayout().inlineControls() ) m_animationTimer->start(); else m_animationTimer->stop(); // -- update the tooltip columns in the playlist model bool tooltipColumns[Playlist::NUM_COLUMNS]; for( int i=0; iactiveLayout(); Playlist::LayoutItemConfig item = layout.layoutForPart( (PlaylistLayout::Part)part ); for (int activeRow = 0; activeRow < item.rows(); activeRow++) { for (int activeElement = 0; activeElement < item.row(activeRow).count();activeElement++) { Playlist::Column column = (Playlist::Column)item.row(activeRow).element(activeElement).value(); tooltipColumns[column] = false; } } // excludeCover |= item.showCover(); } Playlist::Model::setTooltipColumns( tooltipColumns ); Playlist::Model::enableToolTip( Playlist::LayoutManager::instance()->activeLayout().tooltips() ); update(); // Schedule a re-scroll to the active playlist row. Assumption: Qt will run this *after* the repaint. QTimer::singleShot( 0, this, &Playlist::PrettyListView::slotPlaylistActiveTrackChanged ); }