diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ 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) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -278,28 +278,12 @@ # 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 ) ##################################################################### @@ -868,6 +852,7 @@ Phonon::phonon4qt5 KF5::Archive KF5::CoreAddons + KF5::Declarative KF5::GlobalAccel KF5::GuiAddons KF5::I18n @@ -881,13 +866,12 @@ 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 @@ -964,7 +948,7 @@ 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) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -40,7 +40,7 @@ #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" @@ -229,19 +229,19 @@ &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() ); @@ -387,7 +387,7 @@ name = m_browserDock->windowTitle(); break; case AmarokDockContext: -// name = m_contextDock->windowTitle(); + name = m_contextDock->windowTitle(); break; case AmarokDockPlaylist: name = m_playlistDock->windowTitle(); @@ -1250,8 +1250,8 @@ 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 ); @@ -1280,19 +1280,19 @@ // 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 @@ -1315,7 +1315,6 @@ 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() }; @@ -1331,7 +1330,6 @@ 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 diff --git a/src/SvgHandler.h b/src/SvgHandler.h --- a/src/SvgHandler.h +++ b/src/SvgHandler.h @@ -64,6 +64,18 @@ * @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 @@ -143,7 +155,7 @@ 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; diff --git a/src/SvgHandler.cpp b/src/SvgHandler.cpp --- a/src/SvgHandler.cpp +++ b/src/SvgHandler.cpp @@ -76,9 +76,9 @@ } -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() ) @@ -166,6 +166,53 @@ 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 ); diff --git a/src/amarok-contextapplet.desktop b/src/amarok-contextapplet.desktop new file mode 100644 --- /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 --- a/src/amarokurls/ContextUrlGenerator.cpp +++ b/src/amarokurls/ContextUrlGenerator.cpp @@ -18,6 +18,7 @@ #include "AmarokUrl.h" #include "AmarokUrlHandler.h" +#include "context/ContextView.h" #include @@ -44,18 +45,14 @@ 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; } diff --git a/src/amarokurls/ContextUrlRunner.cpp b/src/amarokurls/ContextUrlRunner.cpp --- a/src/amarokurls/ContextUrlRunner.cpp +++ b/src/amarokurls/ContextUrlRunner.cpp @@ -18,6 +18,8 @@ #include "MainWindow.h" #include "AmarokUrlHandler.h" +#include "context/ContextView.h" +#include "context/AppletModel.h" #include @@ -48,18 +50,15 @@ 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; diff --git a/src/browsers/CollectionTreeView.cpp b/src/browsers/CollectionTreeView.cpp --- a/src/browsers/CollectionTreeView.cpp +++ b/src/browsers/CollectionTreeView.cpp @@ -27,7 +27,7 @@ #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" @@ -640,7 +640,6 @@ return; m_ongoingDrag = true; -/* FIXME: disabled temporarily for KF5 porting if( !m_pd ) m_pd = The::popupDropperFactory()->createPopupDropper( Context::ContextView::self() ); @@ -702,7 +701,6 @@ m_pd->show(); } -*/ QTreeView::startDrag( supportedActions ); debug() << "After the drag!"; diff --git a/src/browsers/filebrowser/FileView.cpp b/src/browsers/filebrowser/FileView.cpp --- a/src/browsers/filebrowser/FileView.cpp +++ b/src/browsers/filebrowser/FileView.cpp @@ -23,7 +23,7 @@ #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" @@ -484,7 +484,6 @@ m_ongoingDrag = true; m_dragMutex.unlock(); -/* FIXME: disabled temporarily for KF5 porting if( !m_pd ) m_pd = The::popupDropperFactory()->createPopupDropper( Context::ContextView::self() ); @@ -503,7 +502,6 @@ m_pd->show(); } -*/ QTreeView::startDrag( supportedActions ); diff --git a/src/browsers/playlistbrowser/PlaylistBrowserView.cpp b/src/browsers/playlistbrowser/PlaylistBrowserView.cpp --- a/src/browsers/playlistbrowser/PlaylistBrowserView.cpp +++ b/src/browsers/playlistbrowser/PlaylistBrowserView.cpp @@ -27,7 +27,7 @@ #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" @@ -154,7 +154,6 @@ return; m_ongoingDrag = true; -/* FIXME: disabled temporarily for KF5 porting if( !m_pd ) m_pd = The::popupDropperFactory()->createPopupDropper( Context::ContextView::self() ); @@ -166,21 +165,18 @@ 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; } diff --git a/src/context/AmarokContextPackageStructure.h b/src/context/AmarokContextPackageStructure.h new file mode 100644 --- /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/AmarokContextPackageStructure.cpp b/src/context/AmarokContextPackageStructure.cpp new file mode 100644 --- /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/Applet.h b/src/context/Applet.h deleted file mode 100644 --- 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/Applet.cpp b/src/context/Applet.cpp deleted file mode 100644 --- 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/Containment.cpp b/src/context/AppletLoader.h rename from src/context/Containment.cpp rename to src/context/AppletLoader.h --- a/src/context/Containment.cpp +++ b/src/context/AppletLoader.h @@ -1,5 +1,5 @@ /**************************************************************************************** - * 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 * @@ -14,29 +14,36 @@ * 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/AppletLoader.cpp b/src/context/AppletLoader.cpp new file mode 100644 --- /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/AppletModel.h b/src/context/AppletModel.h new file mode 100644 --- /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/AppletModel.cpp b/src/context/AppletModel.cpp new file mode 100644 --- /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/CMakeLists.txt b/src/context/CMakeLists.txt --- a/src/context/CMakeLists.txt +++ b/src/context/CMakeLists.txt @@ -1,5 +1,5 @@ -#add_subdirectory( applets ) -#add_subdirectory( tools ) +add_subdirectory( applets ) +add_subdirectory( tools ) ########### next target ############### @@ -27,27 +27,29 @@ 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 --- 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 --- 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.h b/src/context/ContextDock.h --- a/src/context/ContextDock.h +++ b/src/context/ContextDock.h @@ -19,18 +19,8 @@ #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 { @@ -42,14 +32,7 @@ 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/ContextDock.cpp b/src/context/ContextDock.cpp --- a/src/context/ContextDock.cpp +++ b/src/context/ContextDock.cpp @@ -18,13 +18,10 @@ #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 ) @@ -34,42 +31,22 @@ 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/ContextObserver.h b/src/context/ContextObserver.h deleted file mode 100644 --- 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/ContextObserver.cpp b/src/context/ContextObserver.cpp deleted file mode 100644 --- 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/ContextScene.h b/src/context/ContextScene.h deleted file mode 100644 --- 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/ContextScene.cpp b/src/context/ContextScene.cpp deleted file mode 100644 --- 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/ContextView.h b/src/context/ContextView.h --- a/src/context/ContextView.h +++ b/src/context/ContextView.h @@ -23,154 +23,97 @@ #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 diff --git a/src/context/ContextView.cpp b/src/context/ContextView.cpp --- a/src/context/ContextView.cpp +++ b/src/context/ContextView.cpp @@ -17,182 +17,112 @@ #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; @@ -202,342 +132,147 @@ { 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/DataSource.h b/src/context/DataSource.h deleted file mode 100644 --- 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 --- 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 --- 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.h b/src/context/ToolbarView.h deleted file mode 100644 --- 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/ToolbarView.cpp b/src/context/ToolbarView.cpp deleted file mode 100644 --- 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/amarokapplets.knsrc b/src/context/amarokapplets.knsrc deleted file mode 100644 --- 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 --- 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.h b/src/context/applets/albums/Albums.h deleted file mode 100644 --- 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/Albums.cpp b/src/context/applets/albums/Albums.cpp deleted file mode 100644 --- 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/AlbumsView.h b/src/context/applets/albums/AlbumsView.h deleted file mode 100644 --- 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/AlbumsView.cpp b/src/context/applets/albums/AlbumsView.cpp deleted file mode 100644 --- 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/CMakeLists.txt b/src/context/applets/albums/CMakeLists.txt --- 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 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 --- /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 rename from src/context/applets/albums/amarok-context-applet-albums.desktop rename to src/context/applets/albums/package/metadata.desktop --- a/src/context/applets/albums/amarok-context-applet-albums.desktop +++ b/src/context/applets/albums/package/metadata.desktop @@ -61,19 +61,19 @@ 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.h b/src/context/applets/albums/plugin/AlbumItem.h rename from src/context/applets/albums/AlbumItem.h rename to src/context/applets/albums/plugin/AlbumItem.h --- a/src/context/applets/albums/AlbumItem.h +++ b/src/context/applets/albums/plugin/AlbumItem.h @@ -61,11 +61,11 @@ // 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. diff --git a/src/context/applets/albums/AlbumItem.cpp b/src/context/applets/albums/plugin/AlbumItem.cpp rename from src/context/applets/albums/AlbumItem.cpp rename to src/context/applets/albums/plugin/AlbumItem.cpp --- a/src/context/applets/albums/AlbumItem.cpp +++ b/src/context/applets/albums/plugin/AlbumItem.cpp @@ -17,13 +17,13 @@ #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 @@ -112,6 +112,7 @@ QPixmap cover = The::svgHandler()->imageWithBorder( m_album, m_iconSize, 3 ); setIcon( QIcon( cover ) ); + setData( cover, AlbumCoverRole ); } int @@ -132,7 +133,10 @@ { 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/AlbumsDefs.h b/src/context/applets/albums/plugin/AlbumsDefs.h rename from src/context/applets/albums/AlbumsDefs.h rename to src/context/applets/albums/plugin/AlbumsDefs.h --- a/src/context/applets/albums/AlbumsDefs.h +++ b/src/context/applets/albums/plugin/AlbumsDefs.h @@ -32,6 +32,7 @@ AlbumMaxTrackNumberRole, AlbumLengthRole, AlbumYearRole, + AlbumCoverRole, TrackArtistRole, TrackNumberRole, TrackLengthRole diff --git a/src/context/applets/albums/plugin/AlbumsEngine.h b/src/context/applets/albums/plugin/AlbumsEngine.h new file mode 100644 --- /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/plugin/AlbumsEngine.cpp b/src/context/applets/albums/plugin/AlbumsEngine.cpp new file mode 100644 --- /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/AlbumsModel.h b/src/context/applets/albums/plugin/AlbumsModel.h rename from src/context/applets/albums/AlbumsModel.h rename to src/context/applets/albums/plugin/AlbumsModel.h --- a/src/context/applets/albums/AlbumsModel.h +++ b/src/context/applets/albums/plugin/AlbumsModel.h @@ -45,21 +45,28 @@ 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. @@ -72,11 +79,13 @@ */ 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/AlbumsModel.cpp b/src/context/applets/albums/plugin/AlbumsModel.cpp rename from src/context/applets/albums/AlbumsModel.cpp rename to src/context/applets/albums/plugin/AlbumsModel.cpp --- a/src/context/applets/albums/AlbumsModel.cpp +++ b/src/context/applets/albums/plugin/AlbumsModel.cpp @@ -21,18 +21,18 @@ #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(); } @@ -55,6 +55,35 @@ 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 ); @@ -123,7 +152,15 @@ 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 @@ -147,7 +184,9 @@ 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 @@ -184,3 +223,23 @@ 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/plugin/AlbumsPlugin.cpp b/src/context/applets/albums/plugin/AlbumsPlugin.cpp new file mode 100644 --- /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.h b/src/context/applets/albums/plugin/TrackItem.h rename from src/context/applets/albums/TrackItem.h rename to src/context/applets/albums/plugin/TrackItem.h --- a/src/context/applets/albums/TrackItem.h +++ b/src/context/applets/albums/plugin/TrackItem.h @@ -54,11 +54,11 @@ // 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; diff --git a/src/context/applets/albums/TrackItem.cpp b/src/context/applets/albums/plugin/TrackItem.cpp rename from src/context/applets/albums/TrackItem.cpp rename to src/context/applets/albums/plugin/TrackItem.cpp --- a/src/context/applets/albums/TrackItem.cpp +++ b/src/context/applets/albums/plugin/TrackItem.cpp @@ -16,12 +16,11 @@ #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 @@ -113,7 +112,10 @@ { 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/plugin/qmldir b/src/context/applets/albums/plugin/qmldir new file mode 100644 --- /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.h b/src/context/applets/analyzer/AnalyzerApplet.h deleted file mode 100644 --- 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/AnalyzerApplet.cpp b/src/context/applets/analyzer/AnalyzerApplet.cpp deleted file mode 100644 --- 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/AnalyzerBase.h b/src/context/applets/analyzer/AnalyzerBase.h deleted file mode 100644 --- 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/AnalyzerBase.cpp b/src/context/applets/analyzer/AnalyzerBase.cpp deleted file mode 100644 --- 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/BlockAnalyzer.cpp b/src/context/applets/analyzer/BlockAnalyzer.cpp deleted file mode 100644 --- 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 --- 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.h b/src/context/applets/analyzer/fht.h deleted file mode 100644 --- 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/fht.cpp b/src/context/applets/analyzer/fht.cpp deleted file mode 100644 --- 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/package/contents/ui/main.qml b/src/context/applets/analyzer/package/contents/ui/main.qml new file mode 100644 --- /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 rename from src/context/applets/analyzer/amarok-context-applet-analyzer.desktop rename to src/context/applets/analyzer/package/metadata.desktop --- a/src/context/applets/analyzer/amarok-context-applet-analyzer.desktop +++ b/src/context/applets/analyzer/package/metadata.desktop @@ -34,19 +34,17 @@ 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.h b/src/context/applets/analyzer/plugin/ASCIIAnalyzer.h rename from src/context/applets/analyzer/ASCIIAnalyzer.h rename to src/context/applets/analyzer/plugin/ASCIIAnalyzer.h diff --git a/src/context/applets/analyzer/ASCIIAnalyzer.cpp b/src/context/applets/analyzer/plugin/ASCIIAnalyzer.cpp rename from src/context/applets/analyzer/ASCIIAnalyzer.cpp rename to src/context/applets/analyzer/plugin/ASCIIAnalyzer.cpp diff --git a/src/context/applets/analyzer/plugin/AnalyzerBase.h b/src/context/applets/analyzer/plugin/AnalyzerBase.h new file mode 100644 --- /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/AnalyzerBase.cpp b/src/context/applets/analyzer/plugin/AnalyzerBase.cpp new file mode 100644 --- /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/AnalyzerPlugin.cpp b/src/context/applets/analyzer/plugin/AnalyzerPlugin.cpp new file mode 100644 --- /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.h b/src/context/applets/analyzer/plugin/AnalyzerWorker.h new file mode 100644 --- /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/plugin/AnalyzerWorker.cpp b/src/context/applets/analyzer/plugin/AnalyzerWorker.cpp new file mode 100644 --- /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/BallsAnalyzer.h b/src/context/applets/analyzer/plugin/BallsAnalyzer.h rename from src/context/applets/analyzer/BallsAnalyzer.h rename to src/context/applets/analyzer/plugin/BallsAnalyzer.h diff --git a/src/context/applets/analyzer/BallsAnalyzer.cpp b/src/context/applets/analyzer/plugin/BallsAnalyzer.cpp rename from src/context/applets/analyzer/BallsAnalyzer.cpp rename to src/context/applets/analyzer/plugin/BallsAnalyzer.cpp --- a/src/context/applets/analyzer/BallsAnalyzer.cpp +++ b/src/context/applets/analyzer/plugin/BallsAnalyzer.cpp @@ -16,7 +16,7 @@ #include "BallsAnalyzer.h" -#include +#include #include @@ -122,8 +122,8 @@ { 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 ); diff --git a/src/context/applets/analyzer/BlockAnalyzer.h b/src/context/applets/analyzer/plugin/BlockAnalyzer.h rename from src/context/applets/analyzer/BlockAnalyzer.h rename to src/context/applets/analyzer/plugin/BlockAnalyzer.h --- a/src/context/applets/analyzer/BlockAnalyzer.h +++ b/src/context/applets/analyzer/plugin/BlockAnalyzer.h @@ -22,80 +22,79 @@ #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/BlockAnalyzer.cpp b/src/context/applets/analyzer/plugin/BlockAnalyzer.cpp new file mode 100644 --- /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/plugin/BlockRenderer.h b/src/context/applets/analyzer/plugin/BlockRenderer.h new file mode 100644 --- /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.h b/src/context/applets/analyzer/plugin/BlockWorker.h new file mode 100644 --- /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/plugin/BlockWorker.cpp b/src/context/applets/analyzer/plugin/BlockWorker.cpp new file mode 100644 --- /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/DiscoAnalyzer.h b/src/context/applets/analyzer/plugin/DiscoAnalyzer.h rename from src/context/applets/analyzer/DiscoAnalyzer.h rename to src/context/applets/analyzer/plugin/DiscoAnalyzer.h diff --git a/src/context/applets/analyzer/DiscoAnalyzer.cpp b/src/context/applets/analyzer/plugin/DiscoAnalyzer.cpp rename from src/context/applets/analyzer/DiscoAnalyzer.cpp rename to src/context/applets/analyzer/plugin/DiscoAnalyzer.cpp --- a/src/context/applets/analyzer/DiscoAnalyzer.cpp +++ b/src/context/applets/analyzer/plugin/DiscoAnalyzer.cpp @@ -17,7 +17,7 @@ #include "DiscoAnalyzer.h" #include -#include +#include #include #include @@ -29,9 +29,9 @@ { 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; @@ -156,109 +156,7 @@ 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 ) diff --git a/src/context/applets/analyzer/plugin/qmldir b/src/context/applets/analyzer/plugin/qmldir new file mode 100644 --- /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 --- 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.h b/src/context/applets/currenttrack/CurrentTrack.h deleted file mode 100644 --- 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/CurrentTrack.cpp b/src/context/applets/currenttrack/CurrentTrack.cpp deleted file mode 100644 --- 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/currentTrackSettings.ui b/src/context/applets/currenttrack/currentTrackSettings.ui deleted file mode 100644 --- 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 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 --- /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 --- /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 --- /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 rename from src/context/applets/currenttrack/amarok-context-applet-currenttrack.desktop rename to src/context/applets/currenttrack/package/metadata.desktop --- a/src/context/applets/currenttrack/amarok-context-applet-currenttrack.desktop +++ b/src/context/applets/currenttrack/package/metadata.desktop @@ -52,19 +52,18 @@ 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/engines/current/CurrentEngine.h b/src/context/applets/currenttrack/plugin/CurrentEngine.h rename from src/context/engines/current/CurrentEngine.h rename to src/context/applets/currenttrack/plugin/CurrentEngine.h --- a/src/context/engines/current/CurrentEngine.h +++ b/src/context/applets/currenttrack/plugin/CurrentEngine.h @@ -17,79 +17,76 @@ #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/CurrentEngine.cpp b/src/context/applets/currenttrack/plugin/CurrentEngine.cpp new file mode 100644 --- /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/applets/currenttrack/plugin/CurrentPlugin.cpp b/src/context/applets/currenttrack/plugin/CurrentPlugin.cpp new file mode 100644 --- /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 --- /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 --- 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.h b/src/context/applets/info/InfoApplet.h deleted file mode 100644 --- 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/InfoApplet.cpp b/src/context/applets/info/InfoApplet.cpp deleted file mode 100644 --- 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/InfoAppletCustomStyle.css b/src/context/applets/info/package/contents/html/InfoAppletCustomStyle.css 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 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 rename from src/context/engines/info/info_frontpage_bg.png rename to src/context/applets/info/package/contents/html/info_frontpage_bg.png index e9c4ad63c53068448a03556e4fe9e7036c04c5a3..e9c4ad63c53068448a03556e4fe9e7036c04c5a3 GIT binary patch literal 180 zc%17D@N?(olHy`uVBq!ia0vp^j6ht*!2~4R0`isvDVB6cUq=Rp^(V|(yIunMk|nMY zCBgY=CFO}lsSJ)O`AMk?p1FzXsX?iUDV2pMQ*D5X+&o}1B?(+jNlHjyNODtS zb7Nzf-N@)WXQG9jva*!4TKgKl&fYa_`qF1>L^gMrHVa74j`rka)=5-P?mV(wnvKJN Z;q+?8)8?yW&44B{c)I$ztaD0e0sv`VGT{IK literal 180 zc%17D@N?(olHy`uVBq!ia0vp^j6ht*!2~4R0`isvDVB6cUq=Rp^(V|(yIunMk|nMY zCBgY=CFO}lsSJ)O`AMk?p1FzXsX?iUDV2pMQ*D5X+&o}1B?(+jNlHjyNODtS zb7Nzf-N@)WXQG9jva*!4TKgKl&fYa_`qF1>L^gMrHVa74j`rka)=5-P?mV(wnvKJN Z;q+?8)8?yW&44B{c)I$ztaD0e0sv`VGT{IK diff --git a/src/context/engines/info/info_frontpage_logo.png b/src/context/applets/info/package/contents/html/info_frontpage_logo.png rename from src/context/engines/info/info_frontpage_logo.png rename to src/context/applets/info/package/contents/html/info_frontpage_logo.png index 466390194fa015edb599d01c6d62ff742beb36ab..466390194fa015edb599d01c6d62ff742beb36ab GIT binary patch literal 14517 zc$_6$19T-#6OA_5BpcgyvazwTZQJ&XZQC|CHa5=2wryMg+wXtp%+t)7?yjo7b#G5s zxV)?w{CDi{ARr*{65_&&ARwT!!0(38kid6-Nv=5H2ZWQLgfcYng;CVXaeHq=0mnPFrmd3x#edcIw76*1o+v9Q4xLF!4Hf`x+e$+$|4Z$!Syo zrETG4>-cf&82LA7G=X2*y?ELJ#t8Kg^>*2^9_0_4cW-;^njMRmr$Iq)-Z^jU?MFGD zlP9MYc3oEh_8CnSLIJ-n1c{$Z>|mMBOx;#;HDBcrFk#HV ztDq!`V!p{jO@hRn-d;a}R(|aL$c}>2d!#2KoCE=VBqWWm_fORiyJgW|!O!pjx5o<+ z_AO4ldB@Li>k$^n0tFjqB#r+LUOOCjK-JsX)j{kE%5Err?~xs4_IT^@$O$okMc9k` z3z-UZ5|kb+B>*?{)@Dc6*7YHDitAXW0#jLDZS)jOq6D*EC%RM~` zK7RXmS^$G6e4Y`r&+A}QcMR@EYMUoJYOAdU zy~-RI3@4egMEjbHmW~5HY~8)8XtvI9uSK@KvZFdSFztPR{WoC51n~z@ehAs@6HLi% zEZ88FAE-G!%IfoLcm^(GL5GhI8T>VK>pe1i6PH##%yHAB&+2OEEd+%B3~CA@QS<|} z1}bOeq=5)FM#FmK763*4+JE3FNo81v*=%F9#+G7|JegvWpHv`EnH1y27kk!Td}htY zZPUG>@q7mvyS8NU*)EZonSS01$st3rhcIz`@&O0x00})!L>kY9^^Ej$ebfoufa7f? zLJhwHdhNh3uK)pc&M-JuLZ!{@Jh*lwbL_ z$N#DEEehHFAV0z>MsYkcgKex{z15!R?q~|iT->i|sex#^fRu}mPnIjYMsu$Qj`Zs0 z`J^A;*6i|X3v%;A8GZxqpI_1Ag#Y#hQ8e2?cx?LjWyUJtF55-%68F8o+-U%BmtxW+ zlLow*&D7DIw6t{G*g?f&2SCv2L)< z)-8*QB@QO0{%8VOkYGMFF?bo8_UT3~#rk;TMzakhB;<%`gS4#dZu^j8W0~6XG%Q_= zR>4c|n$GQoL~d=LwN__C_2wHASU9kcYdsSH53 zAe7ansCC=A*pXX$RX&!Nms^~^Wn5%Ty$_j~xi~R*YEkUz*>ybm`2BN*IWU5BLDmIv zVlKuYgJQF*Bz@9$?9+r0c$4Huc(`!-s=6-O{7oA)N=iy9NEa@Y>}P7V@i$h``BDAU zPK@6dUAe9h{+kb6al)2Z*Jk=fDqBZ%NPN#=mBx%$7Z(THfe1tOw0WEss@*x|0~)n@ z9aHGBg89As`^IPhQ`qZ+HEo|SVJ%$7@3^&zH)3liowX$YV%w51Tp8|}*WS6Z+s?gY zS7u5fjNqD?g=L`LNII4_j?^ePyw< znx`kezrRl+HTk(BsmiP6@Yt|c5Mc(E`gZg7#`^fi|5L#4$Bn`^d^HzdkMMJ7;h0|T z&W-^(y*9LbDsALZx@5M9bEW{nD$Ac#pLfbL*QEZif*DFl6{Y@)LR1QR8=9EUhqQr_ zH&!qeUI7pSAl5*B{Rqk-ukf&R^-q6a~ zPS~mv1HXF{^t#K9F)myBWu+6rRk0r`4a)bk-5)j+HOx=b{Rm^dm7bqDoFwH?) zKgT;Id)efqW4*?dvZL4SiQwyQe$?1=B^P^6{BTrVY*mcftb<3=kT={33cSslnryi~a&osVoxc8oN9nxKa=Uwe zWPxHhJ=zxepe)~Pd4H@vTVYg)$!oOnT=hAMhYfP$ttE4zl!l4_KxSUuOA@xk+oiDny* zFw_>?<9kPUlc@}J(FcId#${JOSUZ&H|$k%S&5m4kEv`EH+;x9|8Nar2Zz<-@BPvEi8c~@{ax*>axx>a9>Vdm32LW8Y5J8F zngQH{8|Rl?!1|9@%>mu&S`M4NsHFt)?1+@~BT@N!2|b5bg0mR!$p_e*btivz@Tr_l z|Hucl8+LY$wJSdimQIH21tr=a*tAk+p?I#CGv;M8is^~NE!iiilVkBQf#cnrdy#D?DrSg zYhb!xx*!Xm_wO}$xODM*b7AUkQlS7^34u1_KpUh`hJU1+f03%U2dwtk-X1jNd3en4 zD?#{ltXIUwL<#8=aK{(e?E1xbLZ5B(MIoJ#qRnR6rKI+EkZI z+QTn3uoL&qjwvg|GWzAT#DUvhY;qiRAUYod1$)we7(|m-?gIOqG5Jj1@3P zXOkDfT+ap;N3cLaLWT(wyDaRkE;-S;s7|VRe%y7m+yvv`oATHg`^W#mB=U8dbzUG8 z^qnV~BQO}Y-Ai!Z&YPH@M@=zd{Q1*w5qqq@0&lPBFz?rn2^5Xbi}_p>;V`Hf_$cJl zmY>8DJ5H4)6CV%-@chcRu`+??`kIf^2lfF@$?64?i($V#ybK$)QKm$%Y=MsA&AlT-lhWEj`qsH$Imu8 z+!AF=qm8KpOf-s@B&mdh*zv2OReE?It}P^rR%PFL#9I9>;|?!+pU!;h-uEF}7y~`1 zZMN07fx+BVDB2QK)qY8;-G(yZ)WMkJ&j(q371vM;cxZf4)x`rk-V=H&_>|#}_Qo*D zJGDx>%bpxztl~^JsQ&BwYZ84;IllEa9_8Co{q5qIwFk!RVMM;^ju|lJ)wD!SA+x$H(RpBDJKG!~ptZh0f0ty7W6l%f?_{7$9NK z3mz8EwD)&AG1itNk?rBTkM)0n$6xHV^Z&ZWaH2j0f9Khj-u_5?5B9aF_e0tymnbr= zX$N(DeegEFa@5yn+hgfPEU*P~5)GB=zpq5sDV%8t`g{>M#s@B9uD5ZdHN5B-A8-wH z^`-W-f4gN=b+**xrk`;*~vrUFQSpd`OVEu zLXbu*&y-KIjGoA!Hb{;xli0TY(i7Rsov*pkXT5SfY28CJRw9~zI^fMx{wu~XnJqSd58SrdrhYm;=c77uBM{mZGQ>7$1j>o zXutXBeY1r?ScZ6YK!o=o8i6*=EYN`~xR-I9jLog|bw7Pn%{Dn$5Tbm;j+(p48FEbW zEbhElqiAHQ07#Ep3Kc%{Yqj24Z(B5Ahbv0IT1>oV0g4K0`=C03EN-otTeX;**Nxhx z#ECxlbq3o9P#e!=Bc*VCrVsZdW^Wz5I8@&CT!ndt2vWGG(HLSP#5T+ji^?t_^}(Dj zeWFRF^6&(DA424iG|}?4eotawL8>T8v6qc@oJIzmv7jW=6X;t0v1kmI8qYTKYQ951 zUmicTk938Ojo(Xq<;db+F&{`^HmSnrQ-Lb4JJDfc+mT#R?RG8dkSPdgH6ObR8P0=} zpWr8Ic?Zz~1<~ocy(>$fiB+sUp^`zz7v zH#av!AT&HTED7o#*XiRIyLDSgCIG3MV})ALVjBejMp`oKWkw0T`Fx69JGxg=b*Y@> z*+ZO|6Xpae7(*(XUkHPaKWHSu6*nup68Kr7XWP#a^gEaLDwgm0t%JzRcLL@xg-_%LU2! zDhFx!LM>PhGtfBZ6(t0m#459JgZ$scE3kk1!^-0pW@o1=wVrO@2y za#~El#DAVfYOFPWMx*)pVJ2xzS$rc(5!DD-1`91jfJ;@r-rb@l9x$x4xDZ+Bau*hg zFiLG)KU*(6Z6{T%{U&?aC*K1_Uv6FYF2Ni9syIvmbpNom>BHb1yFo(sVgU~Nn?l?Y zP!9yiYI``}Mr8`*hDb(6t~vVLH49=(88e zU?^c1oxW*GWYt#O#X;7b^z8$E4Ng8~Ko=7)T(OyB*^ve&%Rzc=7L2dgoZr*EL`F-Pmwl(r+&V zeqN-|(8Vib9-H;T&{tx0$&xnw2Jg@F~wgYhDp>GL} ziQT97o|+G{?A>fM;a2>C<=GqbKjKxEa7$}B8oJqo-BFHLG(y|MN@fp>*V6VBwg$}#P{(7c zRjI3s|J0LFsW2(TP7CAGsV1qbkNaeolruGwHJH1;Xbg-hkQ5Vvt*Y2O<&B<)$UWk(1IY7*{qr zQFq-zu3<`dZI=^N{C&pbws3NEHNGUBaO;x^W$ukVO>s*&+?%{;-U9ueni!@S`U`l^ zo_bSui$_7lbKwo~q#Cu=O67NNEj0&c{N6$Fe<)*$B}vQY@1Mr*Sk~OkDvG2*q+sqm z`dN*sT#AL3=8BKx0IGHiABMU9{-C(SRMmBlb~R0t7}NES?^YO#M%!q%f=xHB=IPB_j7G)>9(`O> z&Q{)7iamb|x=$Nf&+)bm#PMQ%}59uL-8(}k=sFY7pN;&NuYjU8-a_I`43s4I?%Kuq0LJPKGXyb}Mw zuGe77TmD^1#Gsib%#n%hl19cSytke0ssOUNQPsamIC=6?o`EDLbbOgYP} zIL0DG`uJokQ?6tdC9%8uFijhoxQX*sm|jpfz{$SsmlFFEzfE)NdDXS}s>Dirmj7c* zZ|cOeS<#IA=yqo8YR3)XQs4MzgbmgDcR|`#e3Wz^r4_IjL8qN62JF7NBs-9b6tKws z69B#>*6{O9j`t<<2Dk}5`~ z$q2|`=~?YhstYa}w!oMM_T=kS1XB(#?gSt>^Oj%B(_0F@|UQK3;-KFvve zY9Acaa-$d)k^b<+*R!&FJ0Sf$k#$2BGeS0%0|$wsQ7&ij&Pa;_u=VsgbeL@s@+R|B z`Om{OOH_=RJ#Xw&jsq=5*MZ3WV8zy?7@MlEvbEF<2K)lqZx6GnIcej+L2UCzYHgUn z9s@alipfm}REyJS?UKWirV)-tH{sC16>l>sfGyL?$4=@fspd!-NWSg2FLW?Z125)- za~U08>$>|fg{FwVT%ZeGF%UWqSM#|?u>`p8nXM7H&2yApTy5nZ@~|>#GQ&F$ppa(4 zYen6`Gt}~|jgl(KK;g-)v~L~~HNku!TCS&niC7z;n-4t?6dRxL!0Jix|1#{t6(6Z-#14QX26Cw z;Dqz|6er%$Xu6&06ZdEjRr)W1Fbuulcrpy<9CK2R5z$GC9AlXY*zp|D3L%}rx1F}5 zOmDs!Y%AwIjZOsO?FGJ8G@M=}wnWB|O_~nCTu5XueL9fj>UBX@vwrXnt*qa@ncUHT zTiR0@raMCUQs*zd?eRJ=<7qFab$ArbO7JyP5Mf9tPH*tiIcH8TkGXf@o%p~x)9O_j ze2w@QYDBP}niZ!uh-NKhlY2j5;>`ki=->pC&67blAveP%WF2_qN_K4NKd!kfrxYyJ z(r72KfX=RfdDkJP`s};RMU={z-ex^j>)mZ+{&j$ziIW+jN|vA8WJ^{$*=<5g9LGP?gh-9whKNTnv`gxPXPLbfGKnbqUmwn- zjKm6O6cvvomx|&)QzFcP)?R5pumPbz-}krJ-{X$cm5kI5_QbQKC!O?0Nf7r&e;(P? z9nKP(x2fiC5*AX?DeD@_>S{)Q=icO%oga89!uRzOv!;d(j|7UN{xb*aPLf-?npz#{t(tfgY{5o7z(zTQ0m`Oa`q zE-9%sY$+aZ>=KWa-(cXwmu)p`&6#Q&GuqXHEj!O>9ZqZaT&Uy#)xULmm?C ze?rgN;TP`U3oZDZPrP~BO#=E;;bsY7>zsJ{+ig%#c@Uy&Dv2>*Cg(ELc-=VlAGzc{ zmRVJbuIGjp!-KF3Sj~W~;eU9xR4%_1|h(IJZVW+r~WIhowZZE7p?F#&PHL^uN70;B zQ~G_=xc$a{T|zzxba|ZkRNtNu&u<2qxdn2t3-0y#6k1G(%QX$mhj9?BagBEL+_B%{ zXF2chCU{N<_(OX$hI%f=?6#xTNT3!sIYt`M=z82M)&o+Xzg50WY>*iV^6yVFBc5F zghM{n&W*a02xe1(?Pie)BTinjNDBJ=qI4B~BZ}Belwr2l@D_#TUu&LLmcf;glT(wY zM5SJj+&*%%JNWYX0k&p%Y()x7TT^#@7srt^e|E| zG1YnGi?|V@geCE$EVi{rB`oQq%sG>DD9z8)R8kzafIWM#1P~BR@_%~)+Gs@&un3XE zf9wANBaR1w+Vz4?bbiw_<(y0Q=cXnmX<#+-7e#bS$R_TBnz zr71hE`8;&itWU*wK#$#yMitRfluW9z+E8UvySLZo$t1?5reC&ncxOg;nCqJyqs%lXgCRdM;K)u~j#X~FU2JpU{qQjQK zZf%sag**67I#`NziMZ*u)%&C$e}KvqXXAb1o7LpC?bKv&0jpU4`|+gH>^qC0)HNiOBJU!V<@=_G2* zwt>yW?e;zYqQ#;%6RjYf+T7Ir!S}5H`t~OE6tQ@>t6*K})qVTf%-b^@ZWs8Xh!OJY zn;_maUTUC}%aJ=h`W!<~TYx@1M;dPJw-}eK7Ao&*Mr2iBnt*sfC6RUJ)~4+_j9Jbt zw!(UVTu+y1yj+JRceKo9>I_&@TuveEw$zSlrAleH=apgG& zV>&e)9(s7+c3WG{c0CDShI_zrVbil;saxAmhT%r%MancEA;*4;IXl}~URkb?Lrp#Y z#Z18c8^!wM27OQ=_i4{znVSDN8_{_pvQKrUs0cWbb@NCBwcDQsV7gXr7O2^NeL^)D zepGQp3Dm_(W@d{Qf=GdTvVR5zpQpmxmr55ARL-+*R5vg8vRP=c2IA&#)=m=#+G0so)F!yNZL(dr6a+x%dPYf;_LpVGn)A( zB2k3xvxHg#VM@pKfMES^B|S6JA=eSwlli5IPfAsH<8!lqi3|LZFJ=M$N*D?zSaAQA z{v|+c(`6Jx+T37-%`;{1ghy+*WSKF?z?o<4aRaOQLbAcaIilJ~>0T7?hwi)_?e6vU zkdCwoO%xe%SiR(c*+`vqntF0j`807!tz2HyzQ#+ZsDCXVhIXuTO{dEBE(zp2n7v6t zgH2|!R4Ve1&`in1XwqqqVFvu=jnl3kTlBA%lxuM3&n{RYItY48=LRGP`Z!@r00w#O zfM>?Kr{YT&PkFpc3t0B2Awqstvmmp{LQ`&@2-?U8NOZUoYTG9GaipfS39RVuy>yZc zt@Rv+OIWatU~}J{v|-e4J_I_#Am(Lk+$6fY^_?wnehvMtwuGJ*t(3f_K`y=>RjLrL zrwx})<*6K*qu3j*imWL!^1Wq-EREl(HyH~PErc%~HigXhcoL(~xzt=@D)5jEM-iad z<1dhvxWL-h@v)y~DI-U5w&7PqE>G3h{)TPPqYQfl=3}voRYvR>LaO>Dtg5TC^rZxJ z0F7BGUv$cc&Z;oQ9s+o@69aW-Dqa_al=QoMw|>!N00I2|azsv8zkpoW%ZI|@lNcrz zOAt&6#L&=;L_>=@@%U*WA3)RmW^|^$TzL&Xomzgf(KU&hKQ@lak3ytewa%Pyd=BBb zygu*g=_ysb!F8%Zhq#(-S8MUSM5K2fJ3Gp)CqmFWYblr!^%}lsRRqmix-SM{5pc}h z&3s(t#Yq;AJy29`MXoYA^mJMtcE)HT&mF|~&18!re1RX-8{-=Bm@Myy*m(m^GV!Kd zT?V;{HvS6Up*KVBl<$WZ&Pr#bkw#c)bbIA5EPfYZZQ9~%?bOkzVP$eVIIeR+%7oQ}F^7SzR< zxVTxOJ`Y}8k>Aj$mIlc@w&J&S4hwTlO@`AZrgt*HFQzh`N#>xE$*Qd0WzjoS zM7WMo9M*r=TP09IPX`+^FRGg5%JJ5hL$e4GB=Yd^z^>SvSUr=-AQmjid%4)k7W~X>`9oqSq+i;`+#WSw7Mz2iCSFg3c><3K<@gTFkxzss|TUA6;=q|;1)VnK8c(mzwYX=2P9U4 z_~FWDUrtDz_&}W4rG!+G9ki!}YwnI*`d6_>$FTpv3RPo{f5qksocWbZ*wSbyFOON} z36nEZjYWL#W5ydVIc!8?Zd06F7G|;fp_rADRN46p$pR@As~DLDI!r@>PWaHRqq=U* z(5!A-i$!X*U=o?adzT`D$YddlE#$Jm@<)CmjV%-(d#I9^s2<=j+MFw#P>Zs3 zy>Tjzf^Pm{6j$53R*VDpP(+M=yPqM_LBMxnQe{S`Off^5^|W0sMe)SSmb@rcOPIBn9qzCw0($NKo&dR>Dg}NItrP{-F6cJfjLB>9?NR6BKj| zDO#RVD79cIV|5#gr|P5bzcI>*rKQ0EYMa!@u_7a_haM;Lobga;QDV_y^>{8_k1Nqe zoZWYsDKHxZgGu`-v5UQH-!{twB5^WJgW$JZ;kEC*J5_ zMz`U#$|IM9muQT;Fmez97hLq^~~#EG`&8hVWZPu z6SQW<*W5fVS+X_HrVr6S8fsw2%vip317wG4!F{%>rmABt@Clgh&ZegkpR9`mSZL;* z%6Vv$iZM@Je&Y?c;t&_&HOf^PXhCZU{`sDm8b+jHlAjbD7HpD!Wb>`rwGNH4QhY>Y zFD{w_9shvN55Q|s;;0**l{)DJq`#885Ub=fntc%N`g^EqA9W^#YkGb(X3tkASKKenq!Qi>Cr==?sZO<=xN@JM z6Ac16KX4`Y*1?~MU%#VDc)vu5ojtk_@wz!IIUP1NK!VioyyDz+6mAh_ef(P6o9`QS zBMEmP$r3}koyVqc8{6BX&{}WsiFj*iH7UpvO*^!wjm+l@y#c15UkVZU$bVF)_sp4e z!#d60EDn0*FPfFI)y;?0MTu>;+HafaPJ^!`~vkJ1)Kh2(iq(ebCGv+xejXWE|?@aL! zDa-hUTn@lQ=0zll=PO9Osn$`K_T%*HyjNp`b;A!hE(J%=kWsfbG;?m~zG>Q!zj}fd zCr$>NGsIsEz|{_ zk|_!R2-7;34P(YMhEhl2;PsvS(rVUgkSXLvkQ3Tma9{0zVCh1N{F63_vQ`2Dzq*&8 z5=e{XELr3}fF!ofA*^P>BjU?{Oqg3R9cU`l?7`Tn^8UDbYq694W-)0OFWv zyvsp{K8|LQk^0{bb#%dKr-OA-uXZ$FIVJ`)2eAe-1TZrg4kPHjszVP{=gZ3IHH{Z1 zJuuNMldJm!gUv47Oj&OaJ;|rP+5eaT6Jv);sW)0f`F9M$-p>#@kR!z@bu7G~adn-A z&J%xqDiOO8^(C>nfa(G949T#tNIG?9xhp;n|a>GUp(h@~bP!nm3Fn?F11|IU-&l1==sK8Clz9sZR?Nu)Om zr-5#;Msm&(MBEi*O=1v|lt-yg6K#}QJRv?}`kO+SEjC`5F4%}F#Ard!Zmj-PGA&tH zvm;kSd4X5%uGwyDg7<7_4M4+Rns`IO^$B$@1Wh^#0$OpS5yfzaQ?e008F)X5-DfSG zBbuxlbEWS?D{(}X_4_m-O84qZw8 z*F5!h$K*T7&n~7iG%yNPvGy!_i`BolLS00zrMyJ};&{c!uv)@G5uL)ny+>zmiYIiT zh+~8}a37_dFi+;|P*W%cWudjwKMmFl%NT+bBLXOj0X9;M^K+J2UZ-h0mY#=H`=c=(Zx%?{zP<^8qrjPa?X@WY}td zMG*}vn%rh#1m9>wOT9CpfA1OpU9vQ5+=aMnCc`wi#*!nz#t^pmtm<+cP1%<+p-vxq zAr(33J@zMTrL3dsa!}O|$chH{QQ(^72qz zSiXYDJjx3JdvmF%gxt3iLJ}ce--nJQSUsf6ApN-1FIp%C?@g6 zjKTwtR)VO{*)(ClRrexg#fgCanfci>^$UWDv!TZ#$yq2ZaOQNszu9Jq^;5axc<>RJ zx{<$}Fnm1==)lCORkI~r@U z7Omhk|EA0Atdw3ZrVGL&+NC7^0XKwdxFU>~?iz##RPxTh)yB{2j$vqU$xwMD?@+@% z2x(PFHK-iWizhs&Uvu%X_Vyz`m+39Q-YaE?>@H^4y5;_>KF9#dvaLc}O{VhS^{^r> zBx7!@ii+!YNeRD36e9kl^()K18B-NI4In`p4SgqYMU9Umnk(xW#)5%SfFdR;z1P+o zAhyPODvl!ys=D2*s7CePu>_Zo=fFWKIRcQ{tqqjxz0zp8wm5TA?`CpgeR-sAUV20R zH(o0*M3g%{r5;fMPM@q+{cwGW;DgTH-^qr$dST&Fp++!7TJ}}DpfP0QkyPaMhz^Z* z1`Jb0P5OZEuux-+$fhi$95MPGqK*%dMroe{c4eJC8dh|*DpO0gQ`hM@_+}5;Yg_<5 zAIQ#@LN`bL|Mn+0`pFLINEGe%`YP3qHz^N!OO0|U^Q=iCInb_yE%RA~STiaX*n~Au zSPwySw+lKJ8{^*cAgb-C6E+uYTjGuxtN(r5OJ0nmxw!!JBaghqF+? z;NK+CV){hWcmb#awv$dNx#hk(Y3Fz{A2KQUZ*&}#+AuqoL~0-hmB-#lBEI8>Pf2H~ z47&+S598X~;6OuLVr(vYqyJX}0_x24DI-y| zsIN%*Lv3@cgC1ptHn7e-c)+4amaM%h<1Gm5ajNdOAk}fCx(anClU8ZopNHN>atyOc zG^W3((G7GYDfH*C@uQ~ih|&%CVgPfS^~i{~%)}KiwJ(#T+D}ImZgS0(jEM`Q0>CPhJubOq^jR^4)gx9(keJ82F4CA^FLaU1-#s&`G zKr4O{&3+(?n+cKF820+VGte?q7px1LD7iSQKH{F;X&ZLf#7b~E|>2*U zoAEMNxtaPds$U0V@PZH59Z?R~e`PJ40i=on3L6Xou4Cj@1a zu1CvkIc9%V+-F(k`dreiFH-r?X8*Xr{+l{KhvS5);X3h^MD9@YJ{-rK0(E+$do8y; z{w)ffKlsG_E5BI|j1&F~sT)8vGv{aGX?_ad>w;PTzM#aaT(9+C^IgyqMKOb|EZLyA z3kN$_{fGya+dud_y#46^gWw#}IN=Hc4iNSAZEYS9Hc*@oBc%xd$!`gYy^hEI*Idcx ZFT((Ikf~s@e5+bs~HG=v9{{!&`b0Po$ literal 14517 zc$_6$19T-#6OA_5BpcgyvazwTZQJ&XZQC|CHa5=2wryMg+wXtp%+t)7?yjo7b#G5s zxV)?w{CDi{ARr*{65_&&ARwT!!0(38kid6-Nv=5H2ZWQLgfcYng;CVXaeHq=0mnPFrmd3x#edcIw76*1o+v9Q4xLF!4Hf`x+e$+$|4Z$!Syo zrETG4>-cf&82LA7G=X2*y?ELJ#t8Kg^>*2^9_0_4cW-;^njMRmr$Iq)-Z^jU?MFGD zlP9MYc3oEh_8CnSLIJ-n1c{$Z>|mMBOx;#;HDBcrFk#HV ztDq!`V!p{jO@hRn-d;a}R(|aL$c}>2d!#2KoCE=VBqWWm_fORiyJgW|!O!pjx5o<+ z_AO4ldB@Li>k$^n0tFjqB#r+LUOOCjK-JsX)j{kE%5Err?~xs4_IT^@$O$okMc9k` z3z-UZ5|kb+B>*?{)@Dc6*7YHDitAXW0#jLDZS)jOq6D*EC%RM~` zK7RXmS^$G6e4Y`r&+A}QcMR@EYMUoJYOAdU zy~-RI3@4egMEjbHmW~5HY~8)8XtvI9uSK@KvZFdSFztPR{WoC51n~z@ehAs@6HLi% zEZ88FAE-G!%IfoLcm^(GL5GhI8T>VK>pe1i6PH##%yHAB&+2OEEd+%B3~CA@QS<|} z1}bOeq=5)FM#FmK763*4+JE3FNo81v*=%F9#+G7|JegvWpHv`EnH1y27kk!Td}htY zZPUG>@q7mvyS8NU*)EZonSS01$st3rhcIz`@&O0x00})!L>kY9^^Ej$ebfoufa7f? zLJhwHdhNh3uK)pc&M-JuLZ!{@Jh*lwbL_ z$N#DEEehHFAV0z>MsYkcgKex{z15!R?q~|iT->i|sex#^fRu}mPnIjYMsu$Qj`Zs0 z`J^A;*6i|X3v%;A8GZxqpI_1Ag#Y#hQ8e2?cx?LjWyUJtF55-%68F8o+-U%BmtxW+ zlLow*&D7DIw6t{G*g?f&2SCv2L)< z)-8*QB@QO0{%8VOkYGMFF?bo8_UT3~#rk;TMzakhB;<%`gS4#dZu^j8W0~6XG%Q_= zR>4c|n$GQoL~d=LwN__C_2wHASU9kcYdsSH53 zAe7ansCC=A*pXX$RX&!Nms^~^Wn5%Ty$_j~xi~R*YEkUz*>ybm`2BN*IWU5BLDmIv zVlKuYgJQF*Bz@9$?9+r0c$4Huc(`!-s=6-O{7oA)N=iy9NEa@Y>}P7V@i$h``BDAU zPK@6dUAe9h{+kb6al)2Z*Jk=fDqBZ%NPN#=mBx%$7Z(THfe1tOw0WEss@*x|0~)n@ z9aHGBg89As`^IPhQ`qZ+HEo|SVJ%$7@3^&zH)3liowX$YV%w51Tp8|}*WS6Z+s?gY zS7u5fjNqD?g=L`LNII4_j?^ePyw< znx`kezrRl+HTk(BsmiP6@Yt|c5Mc(E`gZg7#`^fi|5L#4$Bn`^d^HzdkMMJ7;h0|T z&W-^(y*9LbDsALZx@5M9bEW{nD$Ac#pLfbL*QEZif*DFl6{Y@)LR1QR8=9EUhqQr_ zH&!qeUI7pSAl5*B{Rqk-ukf&R^-q6a~ zPS~mv1HXF{^t#K9F)myBWu+6rRk0r`4a)bk-5)j+HOx=b{Rm^dm7bqDoFwH?) zKgT;Id)efqW4*?dvZL4SiQwyQe$?1=B^P^6{BTrVY*mcftb<3=kT={33cSslnryi~a&osVoxc8oN9nxKa=Uwe zWPxHhJ=zxepe)~Pd4H@vTVYg)$!oOnT=hAMhYfP$ttE4zl!l4_KxSUuOA@xk+oiDny* zFw_>?<9kPUlc@}J(FcId#${JOSUZ&H|$k%S&5m4kEv`EH+;x9|8Nar2Zz<-@BPvEi8c~@{ax*>axx>a9>Vdm32LW8Y5J8F zngQH{8|Rl?!1|9@%>mu&S`M4NsHFt)?1+@~BT@N!2|b5bg0mR!$p_e*btivz@Tr_l z|Hucl8+LY$wJSdimQIH21tr=a*tAk+p?I#CGv;M8is^~NE!iiilVkBQf#cnrdy#D?DrSg zYhb!xx*!Xm_wO}$xODM*b7AUkQlS7^34u1_KpUh`hJU1+f03%U2dwtk-X1jNd3en4 zD?#{ltXIUwL<#8=aK{(e?E1xbLZ5B(MIoJ#qRnR6rKI+EkZI z+QTn3uoL&qjwvg|GWzAT#DUvhY;qiRAUYod1$)we7(|m-?gIOqG5Jj1@3P zXOkDfT+ap;N3cLaLWT(wyDaRkE;-S;s7|VRe%y7m+yvv`oATHg`^W#mB=U8dbzUG8 z^qnV~BQO}Y-Ai!Z&YPH@M@=zd{Q1*w5qqq@0&lPBFz?rn2^5Xbi}_p>;V`Hf_$cJl zmY>8DJ5H4)6CV%-@chcRu`+??`kIf^2lfF@$?64?i($V#ybK$)QKm$%Y=MsA&AlT-lhWEj`qsH$Imu8 z+!AF=qm8KpOf-s@B&mdh*zv2OReE?It}P^rR%PFL#9I9>;|?!+pU!;h-uEF}7y~`1 zZMN07fx+BVDB2QK)qY8;-G(yZ)WMkJ&j(q371vM;cxZf4)x`rk-V=H&_>|#}_Qo*D zJGDx>%bpxztl~^JsQ&BwYZ84;IllEa9_8Co{q5qIwFk!RVMM;^ju|lJ)wD!SA+x$H(RpBDJKG!~ptZh0f0ty7W6l%f?_{7$9NK z3mz8EwD)&AG1itNk?rBTkM)0n$6xHV^Z&ZWaH2j0f9Khj-u_5?5B9aF_e0tymnbr= zX$N(DeegEFa@5yn+hgfPEU*P~5)GB=zpq5sDV%8t`g{>M#s@B9uD5ZdHN5B-A8-wH z^`-W-f4gN=b+**xrk`;*~vrUFQSpd`OVEu zLXbu*&y-KIjGoA!Hb{;xli0TY(i7Rsov*pkXT5SfY28CJRw9~zI^fMx{wu~XnJqSd58SrdrhYm;=c77uBM{mZGQ>7$1j>o zXutXBeY1r?ScZ6YK!o=o8i6*=EYN`~xR-I9jLog|bw7Pn%{Dn$5Tbm;j+(p48FEbW zEbhElqiAHQ07#Ep3Kc%{Yqj24Z(B5Ahbv0IT1>oV0g4K0`=C03EN-otTeX;**Nxhx z#ECxlbq3o9P#e!=Bc*VCrVsZdW^Wz5I8@&CT!ndt2vWGG(HLSP#5T+ji^?t_^}(Dj zeWFRF^6&(DA424iG|}?4eotawL8>T8v6qc@oJIzmv7jW=6X;t0v1kmI8qYTKYQ951 zUmicTk938Ojo(Xq<;db+F&{`^HmSnrQ-Lb4JJDfc+mT#R?RG8dkSPdgH6ObR8P0=} zpWr8Ic?Zz~1<~ocy(>$fiB+sUp^`zz7v zH#av!AT&HTED7o#*XiRIyLDSgCIG3MV})ALVjBejMp`oKWkw0T`Fx69JGxg=b*Y@> z*+ZO|6Xpae7(*(XUkHPaKWHSu6*nup68Kr7XWP#a^gEaLDwgm0t%JzRcLL@xg-_%LU2! zDhFx!LM>PhGtfBZ6(t0m#459JgZ$scE3kk1!^-0pW@o1=wVrO@2y za#~El#DAVfYOFPWMx*)pVJ2xzS$rc(5!DD-1`91jfJ;@r-rb@l9x$x4xDZ+Bau*hg zFiLG)KU*(6Z6{T%{U&?aC*K1_Uv6FYF2Ni9syIvmbpNom>BHb1yFo(sVgU~Nn?l?Y zP!9yiYI``}Mr8`*hDb(6t~vVLH49=(88e zU?^c1oxW*GWYt#O#X;7b^z8$E4Ng8~Ko=7)T(OyB*^ve&%Rzc=7L2dgoZr*EL`F-Pmwl(r+&V zeqN-|(8Vib9-H;T&{tx0$&xnw2Jg@F~wgYhDp>GL} ziQT97o|+G{?A>fM;a2>C<=GqbKjKxEa7$}B8oJqo-BFHLG(y|MN@fp>*V6VBwg$}#P{(7c zRjI3s|J0LFsW2(TP7CAGsV1qbkNaeolruGwHJH1;Xbg-hkQ5Vvt*Y2O<&B<)$UWk(1IY7*{qr zQFq-zu3<`dZI=^N{C&pbws3NEHNGUBaO;x^W$ukVO>s*&+?%{;-U9ueni!@S`U`l^ zo_bSui$_7lbKwo~q#Cu=O67NNEj0&c{N6$Fe<)*$B}vQY@1Mr*Sk~OkDvG2*q+sqm z`dN*sT#AL3=8BKx0IGHiABMU9{-C(SRMmBlb~R0t7}NES?^YO#M%!q%f=xHB=IPB_j7G)>9(`O> z&Q{)7iamb|x=$Nf&+)bm#PMQ%}59uL-8(}k=sFY7pN;&NuYjU8-a_I`43s4I?%Kuq0LJPKGXyb}Mw zuGe77TmD^1#Gsib%#n%hl19cSytke0ssOUNQPsamIC=6?o`EDLbbOgYP} zIL0DG`uJokQ?6tdC9%8uFijhoxQX*sm|jpfz{$SsmlFFEzfE)NdDXS}s>Dirmj7c* zZ|cOeS<#IA=yqo8YR3)XQs4MzgbmgDcR|`#e3Wz^r4_IjL8qN62JF7NBs-9b6tKws z69B#>*6{O9j`t<<2Dk}5`~ z$q2|`=~?YhstYa}w!oMM_T=kS1XB(#?gSt>^Oj%B(_0F@|UQK3;-KFvve zY9Acaa-$d)k^b<+*R!&FJ0Sf$k#$2BGeS0%0|$wsQ7&ij&Pa;_u=VsgbeL@s@+R|B z`Om{OOH_=RJ#Xw&jsq=5*MZ3WV8zy?7@MlEvbEF<2K)lqZx6GnIcej+L2UCzYHgUn z9s@alipfm}REyJS?UKWirV)-tH{sC16>l>sfGyL?$4=@fspd!-NWSg2FLW?Z125)- za~U08>$>|fg{FwVT%ZeGF%UWqSM#|?u>`p8nXM7H&2yApTy5nZ@~|>#GQ&F$ppa(4 zYen6`Gt}~|jgl(KK;g-)v~L~~HNku!TCS&niC7z;n-4t?6dRxL!0Jix|1#{t6(6Z-#14QX26Cw z;Dqz|6er%$Xu6&06ZdEjRr)W1Fbuulcrpy<9CK2R5z$GC9AlXY*zp|D3L%}rx1F}5 zOmDs!Y%AwIjZOsO?FGJ8G@M=}wnWB|O_~nCTu5XueL9fj>UBX@vwrXnt*qa@ncUHT zTiR0@raMCUQs*zd?eRJ=<7qFab$ArbO7JyP5Mf9tPH*tiIcH8TkGXf@o%p~x)9O_j ze2w@QYDBP}niZ!uh-NKhlY2j5;>`ki=->pC&67blAveP%WF2_qN_K4NKd!kfrxYyJ z(r72KfX=RfdDkJP`s};RMU={z-ex^j>)mZ+{&j$ziIW+jN|vA8WJ^{$*=<5g9LGP?gh-9whKNTnv`gxPXPLbfGKnbqUmwn- zjKm6O6cvvomx|&)QzFcP)?R5pumPbz-}krJ-{X$cm5kI5_QbQKC!O?0Nf7r&e;(P? z9nKP(x2fiC5*AX?DeD@_>S{)Q=icO%oga89!uRzOv!;d(j|7UN{xb*aPLf-?npz#{t(tfgY{5o7z(zTQ0m`Oa`q zE-9%sY$+aZ>=KWa-(cXwmu)p`&6#Q&GuqXHEj!O>9ZqZaT&Uy#)xULmm?C ze?rgN;TP`U3oZDZPrP~BO#=E;;bsY7>zsJ{+ig%#c@Uy&Dv2>*Cg(ELc-=VlAGzc{ zmRVJbuIGjp!-KF3Sj~W~;eU9xR4%_1|h(IJZVW+r~WIhowZZE7p?F#&PHL^uN70;B zQ~G_=xc$a{T|zzxba|ZkRNtNu&u<2qxdn2t3-0y#6k1G(%QX$mhj9?BagBEL+_B%{ zXF2chCU{N<_(OX$hI%f=?6#xTNT3!sIYt`M=z82M)&o+Xzg50WY>*iV^6yVFBc5F zghM{n&W*a02xe1(?Pie)BTinjNDBJ=qI4B~BZ}Belwr2l@D_#TUu&LLmcf;glT(wY zM5SJj+&*%%JNWYX0k&p%Y()x7TT^#@7srt^e|E| zG1YnGi?|V@geCE$EVi{rB`oQq%sG>DD9z8)R8kzafIWM#1P~BR@_%~)+Gs@&un3XE zf9wANBaR1w+Vz4?bbiw_<(y0Q=cXnmX<#+-7e#bS$R_TBnz zr71hE`8;&itWU*wK#$#yMitRfluW9z+E8UvySLZo$t1?5reC&ncxOg;nCqJyqs%lXgCRdM;K)u~j#X~FU2JpU{qQjQK zZf%sag**67I#`NziMZ*u)%&C$e}KvqXXAb1o7LpC?bKv&0jpU4`|+gH>^qC0)HNiOBJU!V<@=_G2* zwt>yW?e;zYqQ#;%6RjYf+T7Ir!S}5H`t~OE6tQ@>t6*K})qVTf%-b^@ZWs8Xh!OJY zn;_maUTUC}%aJ=h`W!<~TYx@1M;dPJw-}eK7Ao&*Mr2iBnt*sfC6RUJ)~4+_j9Jbt zw!(UVTu+y1yj+JRceKo9>I_&@TuveEw$zSlrAleH=apgG& zV>&e)9(s7+c3WG{c0CDShI_zrVbil;saxAmhT%r%MancEA;*4;IXl}~URkb?Lrp#Y z#Z18c8^!wM27OQ=_i4{znVSDN8_{_pvQKrUs0cWbb@NCBwcDQsV7gXr7O2^NeL^)D zepGQp3Dm_(W@d{Qf=GdTvVR5zpQpmxmr55ARL-+*R5vg8vRP=c2IA&#)=m=#+G0so)F!yNZL(dr6a+x%dPYf;_LpVGn)A( zB2k3xvxHg#VM@pKfMES^B|S6JA=eSwlli5IPfAsH<8!lqi3|LZFJ=M$N*D?zSaAQA z{v|+c(`6Jx+T37-%`;{1ghy+*WSKF?z?o<4aRaOQLbAcaIilJ~>0T7?hwi)_?e6vU zkdCwoO%xe%SiR(c*+`vqntF0j`807!tz2HyzQ#+ZsDCXVhIXuTO{dEBE(zp2n7v6t zgH2|!R4Ve1&`in1XwqqqVFvu=jnl3kTlBA%lxuM3&n{RYItY48=LRGP`Z!@r00w#O zfM>?Kr{YT&PkFpc3t0B2Awqstvmmp{LQ`&@2-?U8NOZUoYTG9GaipfS39RVuy>yZc zt@Rv+OIWatU~}J{v|-e4J_I_#Am(Lk+$6fY^_?wnehvMtwuGJ*t(3f_K`y=>RjLrL zrwx})<*6K*qu3j*imWL!^1Wq-EREl(HyH~PErc%~HigXhcoL(~xzt=@D)5jEM-iad z<1dhvxWL-h@v)y~DI-U5w&7PqE>G3h{)TPPqYQfl=3}voRYvR>LaO>Dtg5TC^rZxJ z0F7BGUv$cc&Z;oQ9s+o@69aW-Dqa_al=QoMw|>!N00I2|azsv8zkpoW%ZI|@lNcrz zOAt&6#L&=;L_>=@@%U*WA3)RmW^|^$TzL&Xomzgf(KU&hKQ@lak3ytewa%Pyd=BBb zygu*g=_ysb!F8%Zhq#(-S8MUSM5K2fJ3Gp)CqmFWYblr!^%}lsRRqmix-SM{5pc}h z&3s(t#Yq;AJy29`MXoYA^mJMtcE)HT&mF|~&18!re1RX-8{-=Bm@Myy*m(m^GV!Kd zT?V;{HvS6Up*KVBl<$WZ&Pr#bkw#c)bbIA5EPfYZZQ9~%?bOkzVP$eVIIeR+%7oQ}F^7SzR< zxVTxOJ`Y}8k>Aj$mIlc@w&J&S4hwTlO@`AZrgt*HFQzh`N#>xE$*Qd0WzjoS zM7WMo9M*r=TP09IPX`+^FRGg5%JJ5hL$e4GB=Yd^z^>SvSUr=-AQmjid%4)k7W~X>`9oqSq+i;`+#WSw7Mz2iCSFg3c><3K<@gTFkxzss|TUA6;=q|;1)VnK8c(mzwYX=2P9U4 z_~FWDUrtDz_&}W4rG!+G9ki!}YwnI*`d6_>$FTpv3RPo{f5qksocWbZ*wSbyFOON} z36nEZjYWL#W5ydVIc!8?Zd06F7G|;fp_rADRN46p$pR@As~DLDI!r@>PWaHRqq=U* z(5!A-i$!X*U=o?adzT`D$YddlE#$Jm@<)CmjV%-(d#I9^s2<=j+MFw#P>Zs3 zy>Tjzf^Pm{6j$53R*VDpP(+M=yPqM_LBMxnQe{S`Off^5^|W0sMe)SSmb@rcOPIBn9qzCw0($NKo&dR>Dg}NItrP{-F6cJfjLB>9?NR6BKj| zDO#RVD79cIV|5#gr|P5bzcI>*rKQ0EYMa!@u_7a_haM;Lobga;QDV_y^>{8_k1Nqe zoZWYsDKHxZgGu`-v5UQH-!{twB5^WJgW$JZ;kEC*J5_ zMz`U#$|IM9muQT;Fmez97hLq^~~#EG`&8hVWZPu z6SQW<*W5fVS+X_HrVr6S8fsw2%vip317wG4!F{%>rmABt@Clgh&ZegkpR9`mSZL;* z%6Vv$iZM@Je&Y?c;t&_&HOf^PXhCZU{`sDm8b+jHlAjbD7HpD!Wb>`rwGNH4QhY>Y zFD{w_9shvN55Q|s;;0**l{)DJq`#885Ub=fntc%N`g^EqA9W^#YkGb(X3tkASKKenq!Qi>Cr==?sZO<=xN@JM z6Ac16KX4`Y*1?~MU%#VDc)vu5ojtk_@wz!IIUP1NK!VioyyDz+6mAh_ef(P6o9`QS zBMEmP$r3}koyVqc8{6BX&{}WsiFj*iH7UpvO*^!wjm+l@y#c15UkVZU$bVF)_sp4e z!#d60EDn0*FPfFI)y;?0MTu>;+HafaPJ^!`~vkJ1)Kh2(iq(ebCGv+xejXWE|?@aL! zDa-hUTn@lQ=0zll=PO9Osn$`K_T%*HyjNp`b;A!hE(J%=kWsfbG;?m~zG>Q!zj}fd zCr$>NGsIsEz|{_ zk|_!R2-7;34P(YMhEhl2;PsvS(rVUgkSXLvkQ3Tma9{0zVCh1N{F63_vQ`2Dzq*&8 z5=e{XELr3}fF!ofA*^P>BjU?{Oqg3R9cU`l?7`Tn^8UDbYq694W-)0OFWv zyvsp{K8|LQk^0{bb#%dKr-OA-uXZ$FIVJ`)2eAe-1TZrg4kPHjszVP{=gZ3IHH{Z1 zJuuNMldJm!gUv47Oj&OaJ;|rP+5eaT6Jv);sW)0f`F9M$-p>#@kR!z@bu7G~adn-A z&J%xqDiOO8^(C>nfa(G949T#tNIG?9xhp;n|a>GUp(h@~bP!nm3Fn?F11|IU-&l1==sK8Clz9sZR?Nu)Om zr-5#;Msm&(MBEi*O=1v|lt-yg6K#}QJRv?}`kO+SEjC`5F4%}F#Ard!Zmj-PGA&tH zvm;kSd4X5%uGwyDg7<7_4M4+Rns`IO^$B$@1Wh^#0$OpS5yfzaQ?e008F)X5-DfSG zBbuxlbEWS?D{(}X_4_m-O84qZw8 z*F5!h$K*T7&n~7iG%yNPvGy!_i`BolLS00zrMyJ};&{c!uv)@G5uL)ny+>zmiYIiT zh+~8}a37_dFi+;|P*W%cWudjwKMmFl%NT+bBLXOj0X9;M^K+J2UZ-h0mY#=H`=c=(Zx%?{zP<^8qrjPa?X@WY}td zMG*}vn%rh#1m9>wOT9CpfA1OpU9vQ5+=aMnCc`wi#*!nz#t^pmtm<+cP1%<+p-vxq zAr(33J@zMTrL3dsa!}O|$chH{QQ(^72qz zSiXYDJjx3JdvmF%gxt3iLJ}ce--nJQSUsf6ApN-1FIp%C?@g6 zjKTwtR)VO{*)(ClRrexg#fgCanfci>^$UWDv!TZ#$yq2ZaOQNszu9Jq^;5axc<>RJ zx{<$}Fnm1==)lCORkI~r@U z7Omhk|EA0Atdw3ZrVGL&+NC7^0XKwdxFU>~?iz##RPxTh)yB{2j$vqU$xwMD?@+@% z2x(PFHK-iWizhs&Uvu%X_Vyz`m+39Q-YaE?>@H^4y5;_>KF9#dvaLc}O{VhS^{^r> zBx7!@ii+!YNeRD36e9kl^()K18B-NI4In`p4SgqYMU9Umnk(xW#)5%SfFdR;z1P+o zAhyPODvl!ys=D2*s7CePu>_Zo=fFWKIRcQ{tqqjxz0zp8wm5TA?`CpgeR-sAUV20R zH(o0*M3g%{r5;fMPM@q+{cwGW;DgTH-^qr$dST&Fp++!7TJ}}DpfP0QkyPaMhz^Z* z1`Jb0P5OZEuux-+$fhi$95MPGqK*%dMroe{c4eJC8dh|*DpO0gQ`hM@_+}5;Yg_<5 zAIQ#@LN`bL|Mn+0`pFLINEGe%`YP3qHz^N!OO0|U^Q=iCInb_yE%RA~STiaX*n~Au zSPwySw+lKJ8{^*cAgb-C6E+uYTjGuxtN(r5OJ0nmxw!!JBaghqF+? z;NK+CV){hWcmb#awv$dNx#hk(Y3Fz{A2KQUZ*&}#+AuqoL~0-hmB-#lBEI8>Pf2H~ z47&+S598X~;6OuLVr(vYqyJX}0_x24DI-y| zsIN%*Lv3@cgC1ptHn7e-c)+4amaM%h<1Gm5ajNdOAk}fCx(anClU8ZopNHN>atyOc zG^W3((G7GYDfH*C@uQ~ih|&%CVgPfS^~i{~%)}KiwJ(#T+D}ImZgS0(jEM`Q0>CPhJubOq^jR^4)gx9(keJ82F4CA^FLaU1-#s&`G zKr4O{&3+(?n+cKF820+VGte?q7px1LD7iSQKH{F;X&ZLf#7b~E|>2*U zoAEMNxtaPds$U0V@PZH59Z?R~e`PJ40i=on3L6Xou4Cj@1a zu1CvkIc9%V+-F(k`dreiFH-r?X8*Xr{+l{KhvS5);X3h^MD9@YJ{-rK0(E+$do8y; z{w)ffKlsG_E5BI|j1&F~sT)8vGv{aGX?_ad>w;PTzM#aaT(9+C^IgyqMKOb|EZLyA z3kN$_{fGya+dud_y#46^gWw#}IN=Hc4iNSAZEYS9Hc*@oBc%xd$!`gYy^hEI*Idcx ZFT((Ikf~s@e5+bs~HG=v9{{!&`b0Po$ diff --git a/src/context/engines/info/info_frontpage_shadow.png b/src/context/applets/info/package/contents/html/info_frontpage_shadow.png rename from src/context/engines/info/info_frontpage_shadow.png rename to src/context/applets/info/package/contents/html/info_frontpage_shadow.png index 271d9d74c966ac0e3f467b85fb5e68a12a5f4eec..271d9d74c966ac0e3f467b85fb5e68a12a5f4eec GIT binary patch literal 285 zc$@(p0pk9NP)8F2Md#<| z^z`)V>gw0m*Y@`I;Nal%^Yhcw)7jbC?d|RQ`ugeV>HYov@bK{W_xI-J=Frg4cv_$M!APW=4(~z%-$lC jm&)KDd|bh8F2Md#<| z^z`)V>gw0m*Y@`I;Nal%^Yhcw)7jbC?d|RQ`ugeV>HYov@bK{W_xI-J=Frg4cv_$M!APW=4(~z%-$lC jm&)KDd|bh * + * 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 * @@ -14,20 +14,25 @@ * 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 rename from src/context/applets/info/amarok-context-applet-info.desktop rename to src/context/applets/info/package/metadata.desktop --- a/src/context/applets/info/amarok-context-applet-info.desktop +++ b/src/context/applets/info/package/metadata.desktop @@ -57,17 +57,15 @@ 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.h b/src/context/applets/info/plugin/InfoEngine.h rename from src/context/engines/info/InfoEngine.h rename to src/context/applets/info/plugin/InfoEngine.h --- a/src/context/engines/info/InfoEngine.h +++ b/src/context/applets/info/plugin/InfoEngine.h @@ -18,12 +18,13 @@ #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. @@ -35,35 +36,29 @@ */ -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/engines/info/InfoEngine.cpp b/src/context/applets/info/plugin/InfoEngine.cpp rename from src/context/engines/info/InfoEngine.cpp rename to src/context/applets/info/plugin/InfoEngine.cpp --- a/src/context/engines/info/InfoEngine.cpp +++ b/src/context/applets/info/plugin/InfoEngine.cpp @@ -19,69 +19,56 @@ #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/applets/info/plugin/InfoPlugin.cpp b/src/context/applets/info/plugin/InfoPlugin.cpp new file mode 100644 --- /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 --- /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 --- 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.h b/src/context/applets/lyrics/LyricsApplet.h deleted file mode 100644 --- a/src/context/applets/lyrics/LyricsApplet.h +++ /dev/null @@ -1,70 +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 . * - ****************************************************************************************/ - -#ifndef LYRICS_APPLET_H -#define LYRICS_APPLET_H - -#include "context/Applet.h" -#include "context/DataEngine.h" - -#include - -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/LyricsApplet.cpp b/src/context/applets/lyrics/LyricsApplet.cpp deleted file mode 100644 --- 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/LyricsBrowser.h b/src/context/applets/lyrics/LyricsBrowser.h deleted file mode 100644 --- 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/LyricsBrowser.cpp b/src/context/applets/lyrics/LyricsBrowser.cpp deleted file mode 100644 --- 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/LyricsSuggestionsListWidget.h b/src/context/applets/lyrics/LyricsSuggestionsListWidget.h deleted file mode 100644 --- 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/LyricsSuggestionsListWidget.cpp b/src/context/applets/lyrics/LyricsSuggestionsListWidget.cpp deleted file mode 100644 --- 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/lyricsSettings.ui b/src/context/applets/lyrics/lyricsSettings.ui deleted file mode 100644 --- 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 --- /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 --- /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 rename from src/context/applets/lyrics/amarok-context-applet-lyrics.desktop rename to src/context/applets/lyrics/package/metadata.desktop --- a/src/context/applets/lyrics/amarok-context-applet-lyrics.desktop +++ b/src/context/applets/lyrics/package/metadata.desktop @@ -55,19 +55,17 @@ 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.h b/src/context/applets/lyrics/plugin/LyricsEngine.h rename from src/context/engines/lyrics/LyricsEngine.h rename to src/context/applets/lyrics/plugin/LyricsEngine.h --- a/src/context/engines/lyrics/LyricsEngine.h +++ b/src/context/applets/lyrics/plugin/LyricsEngine.h @@ -18,51 +18,74 @@ #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/engines/lyrics/LyricsEngine.cpp b/src/context/applets/lyrics/plugin/LyricsEngine.cpp rename from src/context/engines/lyrics/LyricsEngine.cpp rename to src/context/applets/lyrics/plugin/LyricsEngine.cpp --- a/src/context/engines/lyrics/LyricsEngine.cpp +++ b/src/context/applets/lyrics/plugin/LyricsEngine.cpp @@ -21,56 +21,40 @@ #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(); } @@ -86,9 +70,8 @@ if( !currentTrack ) { debug() << "no current track"; - m_prevLyrics.clear(); - removeAllData( "lyrics" ); - setData( "lyrics", "stopped", "stopped" ); + m_lyrics.clear(); + emit lyricsChanged(); m_isUpdateInProgress = false; return; } @@ -128,7 +111,7 @@ 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 ); @@ -150,45 +133,156 @@ 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/applets/lyrics/plugin/LyricsPlugin.cpp b/src/context/applets/lyrics/plugin/LyricsPlugin.cpp new file mode 100644 --- /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 --- /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 --- 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.h b/src/context/applets/photos/DragPixmapItem.h deleted file mode 100644 --- 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/DragPixmapItem.cpp b/src/context/applets/photos/DragPixmapItem.cpp deleted file mode 100644 --- 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/PhotosApplet.h b/src/context/applets/photos/PhotosApplet.h deleted file mode 100644 --- 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/PhotosApplet.cpp b/src/context/applets/photos/PhotosApplet.cpp deleted file mode 100644 --- 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/PhotosScrollWidget.h b/src/context/applets/photos/PhotosScrollWidget.h deleted file mode 100644 --- 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/PhotosScrollWidget.cpp b/src/context/applets/photos/PhotosScrollWidget.cpp deleted file mode 100644 --- 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/package/contents/ui/main.qml b/src/context/applets/photos/package/contents/ui/main.qml new file mode 100644 --- /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 rename from src/context/applets/photos/amarok-context-applet-photos.desktop rename to src/context/applets/photos/package/metadata.desktop --- a/src/context/applets/photos/amarok-context-applet-photos.desktop +++ b/src/context/applets/photos/package/metadata.desktop @@ -54,18 +54,19 @@ 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 --- 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.h b/src/context/applets/photos/plugin/PhotosEngine.h rename from src/context/engines/photos/PhotosEngine.h rename to src/context/applets/photos/plugin/PhotosEngine.h --- a/src/context/engines/photos/PhotosEngine.h +++ b/src/context/applets/photos/plugin/PhotosEngine.h @@ -1,4 +1,4 @@ -#/**************************************************************************************** +/**************************************************************************************** * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * * * * This program is free software; you can redistribute it and/or modify it under * @@ -17,46 +17,69 @@ #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: @@ -73,30 +96,49 @@ 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/engines/photos/PhotosEngine.cpp b/src/context/applets/photos/plugin/PhotosEngine.cpp rename from src/context/engines/photos/PhotosEngine.cpp rename to src/context/applets/photos/plugin/PhotosEngine.cpp --- a/src/context/engines/photos/PhotosEngine.cpp +++ b/src/context/applets/photos/plugin/PhotosEngine.cpp @@ -14,49 +14,43 @@ * 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(); } @@ -69,12 +63,6 @@ update(); } -QStringList -PhotosEngine::sources() const -{ - return m_sources; -} - int PhotosEngine::fetchSize() const { @@ -96,19 +84,11 @@ 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 @@ -128,13 +108,12 @@ 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 @@ -147,19 +126,17 @@ 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; } @@ -170,16 +147,18 @@ // 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; @@ -196,10 +175,11 @@ 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; } @@ -210,18 +190,18 @@ 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; @@ -249,14 +229,86 @@ 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/applets/photos/plugin/PhotosPlugin.cpp b/src/context/applets/photos/plugin/PhotosPlugin.cpp new file mode 100644 --- /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 --- /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 --- 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 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 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 --- /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 rename from src/context/applets/wikipedia/amarok-context-applet-wikipedia.desktop rename to src/context/applets/wikipedia/package/metadata.desktop --- a/src/context/applets/wikipedia/amarok-context-applet-wikipedia.desktop +++ b/src/context/applets/wikipedia/package/metadata.desktop @@ -54,18 +54,18 @@ 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.h b/src/context/applets/wikipedia/plugin/WikipediaApplet.h rename from src/context/applets/wikipedia/WikipediaApplet.h rename to src/context/applets/wikipedia/plugin/WikipediaApplet.h --- a/src/context/applets/wikipedia/WikipediaApplet.h +++ b/src/context/applets/wikipedia/plugin/WikipediaApplet.h @@ -23,7 +23,7 @@ #include "NetworkAccessManagerProxy.h" class QAction; -class KDialog; +class QDialog; class KConfigDialog; class QListWidgetItem; class WikipediaAppletPrivate; diff --git a/src/context/applets/wikipedia/WikipediaApplet.cpp b/src/context/applets/wikipedia/plugin/WikipediaApplet.cpp rename from src/context/applets/wikipedia/WikipediaApplet.cpp rename to src/context/applets/wikipedia/plugin/WikipediaApplet.cpp --- a/src/context/applets/wikipedia/WikipediaApplet.cpp +++ b/src/context/applets/wikipedia/plugin/WikipediaApplet.cpp @@ -30,11 +30,11 @@ #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> @@ -256,7 +256,7 @@ 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(); @@ -272,7 +272,7 @@ } // 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 @@ -660,7 +660,7 @@ // 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(); @@ -800,7 +800,7 @@ 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" ); diff --git a/src/context/applets/wikipedia/WikipediaApplet_p.h b/src/context/applets/wikipedia/plugin/WikipediaApplet_p.h rename from src/context/applets/wikipedia/WikipediaApplet_p.h rename to src/context/applets/wikipedia/plugin/WikipediaApplet_p.h --- a/src/context/applets/wikipedia/WikipediaApplet_p.h +++ b/src/context/applets/wikipedia/plugin/WikipediaApplet_p.h @@ -24,7 +24,7 @@ #include "ui_wikipediaLanguageSettings.h" #include <KGraphicsWebView> -#include <KLineEdit> +#include <QLineEdit> #include <QUrl> #include <Plasma/LineEdit> diff --git a/src/context/applets/wikipedia/plugin/WikipediaEngine.h b/src/context/applets/wikipedia/plugin/WikipediaEngine.h new file mode 100644 --- /dev/null +++ b/src/context/applets/wikipedia/plugin/WikipediaEngine.h @@ -0,0 +1,122 @@ +/**************************************************************************************** + * 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/>. * + ****************************************************************************************/ + +#ifndef AMAROK_WIKIPEDIA_ENGINE +#define AMAROK_WIKIPEDIA_ENGINE + +#include "core/meta/Meta.h" +#include "network/NetworkAccessManagerProxy.h" + +#include <QObject> + + +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<QString, QString> &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/engines/wikipedia/WikipediaEngine.cpp b/src/context/applets/wikipedia/plugin/WikipediaEngine.cpp rename from src/context/engines/wikipedia/WikipediaEngine.cpp rename to src/context/applets/wikipedia/plugin/WikipediaEngine.cpp --- a/src/context/engines/wikipedia/WikipediaEngine.cpp +++ b/src/context/applets/wikipedia/plugin/WikipediaEngine.cpp @@ -17,185 +17,56 @@ * 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; } @@ -209,18 +80,15 @@ 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 ) @@ -230,54 +98,45 @@ { 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; @@ -343,7 +202,7 @@ if( preferredLangs.contains(hostLang) && !langTitleMap.contains(hostLang) ) langTitleMap[hostLang] = title; - q->removeData( QLatin1String("wikipedia"), QLatin1String("busy") ); + setBusy( false ); QStringListIterator langIter( preferredLangs ); while( langIter.hasNext() ) { @@ -377,40 +236,35 @@ 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 ); @@ -440,8 +294,8 @@ 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; } @@ -472,29 +326,29 @@ } 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; } @@ -516,100 +370,102 @@ } 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() ) @@ -620,8 +476,6 @@ typeToFieldMap.insert( Track, Meta::valTitle ); } - Q_Q( WikipediaEngine ); - Meta::TrackPtr currentTrack = The::engineController()->currentTrack(); if( !currentTrack ) return; @@ -638,14 +492,12 @@ { 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 @@ -658,14 +510,12 @@ { 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(); } @@ -675,14 +525,12 @@ { 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(); @@ -692,10 +540,8 @@ 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(); @@ -718,7 +564,7 @@ } void -WikipediaEnginePrivate::wikiParse( QString &wiki ) +WikipediaEngine::wikiParse( QString &wiki ) { //remove the new-lines and tabs(replace with spaces IS needed). wiki.replace( '\n', QLatin1Char(' ') ); @@ -838,7 +684,7 @@ } QString -WikipediaEnginePrivate::createLanguageComboBox( const QMap<QString, QString> &languageMap ) +WikipediaEngine::createLanguageComboBox( const QMap<QString, QString> &languageMap ) { if( languageMap.isEmpty() ) return QString(); @@ -857,37 +703,38 @@ } 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") ) @@ -901,51 +748,78 @@ return changed; } -WikipediaEngine::WikipediaEngine( QObject* parent, const QList<QVariant>& /*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/WikipediaPlugin.cpp b/src/context/applets/wikipedia/plugin/WikipediaPlugin.cpp new file mode 100644 --- /dev/null +++ b/src/context/applets/wikipedia/plugin/WikipediaPlugin.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 "WikipediaEngine.h" + +#include <QQmlExtensionPlugin> + +#include <qqml.h> + + +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<WikipediaEngine>(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 <WikipediaPlugin.moc> diff --git a/src/context/applets/wikipedia/plugin/qmldir b/src/context/applets/wikipedia/plugin/qmldir new file mode 100644 --- /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 --- a/src/context/applets/wikipedia/wikipediaGeneralSettings.ui +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>wikipediaGeneralSettings</class> - <widget class="QWidget" name="wikipediaGeneralSettings"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>253</width> - <height>62</height> - </rect> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <layout class="QGridLayout" name="gridLayout"> - <property name="topMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item row="0" column="0"> - <layout class="QFormLayout" name="layout"> - <item row="0" column="1"> - <widget class="QCheckBox" name="mobileCheckBox"> - <property name="text"> - <string>Use Wikipedia &mobile version</string> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/context/applets/wikipedia/wikipediaLanguageSettings.ui b/src/context/applets/wikipedia/wikipediaLanguageSettings.ui deleted file mode 100644 --- a/src/context/applets/wikipedia/wikipediaLanguageSettings.ui +++ /dev/null @@ -1,81 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>wikipediaLanguageSettings</class> - <widget class="QWidget" name="wikipediaLanguageSettings"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>469</width> - <height>300</height> - </rect> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>400</width> - <height>300</height> - </size> - </property> - <layout class="QGridLayout" name="gridLayout"> - <property name="topMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item row="1" column="0"> - <widget class="KActionSelector" name="langSelector"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - </widget> - </item> - <item row="2" column="0" colspan="2"> - <layout class="QHBoxLayout" name="progressLayout"> - <property name="leftMargin"> - <number>4</number> - </property> - <property name="rightMargin"> - <number>4</number> - </property> - <item> - <widget class="KPushButton" name="downloadButton"/> - </item> - <item> - <widget class="QProgressBar" name="progressBar"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - <customwidgets> - <customwidget> - <class>KPushButton</class> - <extends>QPushButton</extends> - <header>kpushbutton.h</header> - </customwidget> - <customwidget> - <class>KActionSelector</class> - <extends>QWidget</extends> - <header>kactionselector.h</header> - </customwidget> - </customwidgets> - <resources/> - <connections/> -</ui> diff --git a/src/context/containments/CMakeLists.txt b/src/context/containments/CMakeLists.txt deleted file mode 100644 --- 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 --- 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.h b/src/context/containments/verticallayout/VerticalAppletLayout.h deleted file mode 100644 --- a/src/context/containments/verticallayout/VerticalAppletLayout.h +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi <lfranchi@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_VERTICAL_APPLET_LAYOUT_H -#define AMAROK_VERTICAL_APPLET_LAYOUT_H - - -#include "amarok_export.h" - -#include <QGraphicsWidget> - -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/VerticalAppletLayout.cpp b/src/context/containments/verticallayout/VerticalAppletLayout.cpp deleted file mode 100644 --- a/src/context/containments/verticallayout/VerticalAppletLayout.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi <lfranchi@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 "VerticalAppletLayout" - -#include "VerticalAppletLayout.h" - -#include "Applet.h" -#include "Containment.h" -#include "core/support/Debug.h" - -#include <Plasma/Applet> -#include <KConfig> - -#include <QPainter> -#include <QStyleOptionGraphicsItem> -#include <QGraphicsSceneResizeEvent> -#include <QGraphicsLinearLayout> -#include <QGraphicsScene> -#include <QGraphicsView> - -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<Plasma::Applet*> toRemove; - for( int i = 0, count = m_layout->count(); i < count; ++i ) - { - if( QGraphicsLayoutItem *item = m_layout->itemAt( i ) ) - { - Plasma::Applet *applet = static_cast<Plasma::Applet*>( 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<Context::Applet*>( 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/VerticalToolbarContainment.h b/src/context/containments/verticallayout/VerticalToolbarContainment.h deleted file mode 100644 --- a/src/context/containments/verticallayout/VerticalToolbarContainment.h +++ /dev/null @@ -1,79 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi <lfranchi@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_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<QAction*> 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/VerticalToolbarContainment.cpp b/src/context/containments/verticallayout/VerticalToolbarContainment.cpp deleted file mode 100644 --- a/src/context/containments/verticallayout/VerticalToolbarContainment.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi <lfranchi@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 "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 <KConfig> - -#include <QGraphicsLinearLayout> - -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<QAction*> -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( "<html> <style type=\"text/css\"> body { background-color: %1; } </style> \ - <body> <p align=\"center\"> %2 </</p></body></html>" ) - .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/amarok-containment-vertical.desktop b/src/context/containments/verticallayout/amarok-containment-vertical.desktop deleted file mode 100644 --- 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 Vertikal -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 vertikal 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 --- /dev/null +++ b/src/context/context_qml_package/contents/ui/main.qml @@ -0,0 +1,116 @@ +/**************************************************************************************** + * 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 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 --- /dev/null +++ b/src/context/context_qml_package/contents/ui/toolbar/AppletToolbar.qml @@ -0,0 +1,132 @@ +/**************************************************************************************** + * 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 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<children.length; i++) { + if (!!children[i].status && children[i].status != Loader.Error) + items.push(children[i]); + } + if (items.length > 0) { + var space = toolbarAppletRow.width - (items.length - 1) * toolbarAppletRow.spacing; + var threshhold = space / items.length; + var smallApplets = []; + var largeApplets = []; + for (var i=0; i<items.length; i++) { + if (!!items[i] && !!items[i].status && items[i].status != Loader.Error) + smallApplets.push(items[i]); + } + while (smallApplets.length > 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<smallApplets.length; i++) { + var applet = smallApplets[i]; + applet.width = applet.implicitWidth; + } + } + } + + height: configureButton.height + Context.smallSpacing + radius: 4 + color: palette.mid + + ListView { + id: toolbarAppletRow + + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + right: configureButton.left + leftMargin: spacing + rightMargin: spacing + } + orientation: ListView.Horizontal + spacing: Context.smallSpacing + interactive: false + clip: true + + model: AppletProxyModel + + delegate: Loader { + active: true + anchors.verticalCenter: parent.verticalCenter + + Component.onCompleted: { + setSource("AppletToolbarAppletItem.qml", { + "name": name, + "appletId": appletId, + "toolbar": root, + "listView": root.listView, + "contextRoot": root.contextRoot + }); + } + onStatusChanged: { + if (status == Loader.Error) { + Context.error("Error loading toolbar item for applet: " + name); + Context.error(sourceComponent.errorString()); + } + } + } + onWidthChanged: root.resizeApplets() + onCountChanged: root.resizeApplets() + } + + ToolButton { + id: configureButton + + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.margins: (parent.height - height) / 2 + iconName: "configure" + checkable: true + tooltip: i18n( "Configure Applets..." ) + } + + SystemPalette { + id: palette + } +} diff --git a/src/context/context_qml_package/contents/ui/toolbar/AppletToolbarAddItem.qml b/src/context/context_qml_package/contents/ui/toolbar/AppletToolbarAddItem.qml new file mode 100644 --- /dev/null +++ b/src/context/context_qml_package/contents/ui/toolbar/AppletToolbarAddItem.qml @@ -0,0 +1,84 @@ +/**************************************************************************************** + * 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 + + +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 --- /dev/null +++ b/src/context/context_qml_package/contents/ui/toolbar/AppletToolbarAppletItem.qml @@ -0,0 +1,114 @@ +/**************************************************************************************** + * 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.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 --- /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 --- 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 --- 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 --- a/src/context/engines/current/CurrentEngine.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi <lfranchi@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 "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 <KConfigDialog> - -#include <QVariant> -#include <Phonon/MediaObject> -#include <Phonon/Path> -#include <Phonon/MediaController> -#include <Phonon/MediaSource> //Needed for the slot - -using namespace Context; - -CurrentEngine::CurrentEngine( QObject* parent, const QList<QVariant>& 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<Capabilities::SourceInfoCapability>(); - if( sic ) - { - //is the source defined - const QString source = sic->sourceName(); - debug() <<" We have source " <<source; - if( !source.isEmpty() ) - data["source_emblem"] = sic->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 --- 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 dados 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 --- 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 --- 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.h b/src/context/engines/labels/LabelsEngine.h --- a/src/context/engines/labels/LabelsEngine.h +++ b/src/context/engines/labels/LabelsEngine.h @@ -25,7 +25,7 @@ #include <QMap> #include <QTimer> -#include <QWeakPointer> +#include <QPointer> using namespace Context; diff --git a/src/context/engines/labels/LabelsEngine.cpp b/src/context/engines/labels/LabelsEngine.cpp --- a/src/context/engines/labels/LabelsEngine.cpp +++ b/src/context/engines/labels/LabelsEngine.cpp @@ -29,7 +29,7 @@ #include "core-impl/collections/support/CollectionManager.h" #include <KIO/Job> -#include <KLocale> +#include <KLocalizedString> #include <QDomDocument> diff --git a/src/context/engines/lyrics/CMakeLists.txt b/src/context/engines/lyrics/CMakeLists.txt deleted file mode 100644 --- 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 --- 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 dados 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 --- 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 --- a/src/context/engines/photos/PhotosInfo.h +++ /dev/null @@ -1,65 +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 AMAROK_PHOTOS_INFO -#define AMAROK_PHOTOS_INFO - -#include <QUrl> -#include <KSharedPtr> - -#include <QSharedData> -#include <QPixmap> - -class PhotosInfo; -typedef KSharedPtr<PhotosInfo> PhotosInfoPtr; - -//! Struct PhotosInfo, contain all the info vor a photos -class PhotosInfo : public QSharedData -{ -public: - typedef QList<PhotosInfoPtr> List; - - PhotosInfo() - { - static bool metaTypeRegistered = false; - if( !metaTypeRegistered ) - { - qRegisterMetaType<PhotosInfo>( "PhotosInfo" ); - qRegisterMetaType<PhotosInfoPtr>( "PhotosInfoPtr" ); - qRegisterMetaType<PhotosInfo::List>( "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 --- 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 --- a/src/context/engines/songkick/SongkickEngine.cpp +++ b/src/context/engines/songkick/SongkickEngine.cpp @@ -104,12 +104,12 @@ 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 ) diff --git a/src/context/engines/wikipedia/CMakeLists.txt b/src/context/engines/wikipedia/CMakeLists.txt deleted file mode 100644 --- 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 --- a/src/context/engines/wikipedia/WikipediaEngine.h +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************************** - * 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/>. * - ****************************************************************************************/ - -#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<QVariant>& 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 --- 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 dados da 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 --- /dev/null +++ b/src/context/qml_plugin/Applet.qml @@ -0,0 +1,99 @@ +/**************************************************************************************** + * 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.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 --- /dev/null +++ b/src/context/qml_plugin/AppletHeader.qml @@ -0,0 +1,75 @@ +/**************************************************************************************** + * 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 + + +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 --- /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.h b/src/context/qml_plugin/src/PixmapItem.h new file mode 100644 --- /dev/null +++ b/src/context/qml_plugin/src/PixmapItem.h @@ -0,0 +1,55 @@ +/* + * Copyright 2018 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/>. + */ + +#ifndef PIXMAPITEM_H +#define PIXMAPITEM_H + +#include <QQuickItem> + +#include <QPixmap> + + +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/PixmapItem.cpp b/src/context/qml_plugin/src/PixmapItem.cpp new file mode 100644 --- /dev/null +++ b/src/context/qml_plugin/src/PixmapItem.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2018 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 "PixmapItem.h" + +#include <QPaintEngine> +#include <QPainter> +#include <QSGSimpleTextureNode> +#include <QQuickWindow> + +#include <KDeclarative/KQuickAddons/ManagedTextureNode> + + +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<ManagedTextureNode*>( oldNode ); + + if( !textureNode || m_pixmapChanged ) + { + delete oldNode; + textureNode = new ManagedTextureNode; + textureNode->setFiltering( QSGTexture::Linear ); + textureNode->setTexture( QSharedPointer<QSGTexture>( 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/Plugin.cpp b/src/context/qml_plugin/src/Plugin.cpp new file mode 100644 --- /dev/null +++ b/src/context/qml_plugin/src/Plugin.cpp @@ -0,0 +1,45 @@ +/* + * 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 "RatingItem.h" +#include "PixmapItem.h" + +#include <QQmlExtensionPlugin> + +#include <qqml.h> + + +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<RatingItem>(uri, 1, 0, "RatingItem"); + qmlRegisterType<PixmapItem>(uri, 1, 0, "PixmapItem"); + } +}; + +#include <Plugin.moc> diff --git a/src/context/widgets/RatingWidget.h b/src/context/qml_plugin/src/RatingItem.h rename from src/context/widgets/RatingWidget.h rename to src/context/qml_plugin/src/RatingItem.h --- a/src/context/widgets/RatingWidget.h +++ b/src/context/qml_plugin/src/RatingItem.h @@ -15,32 +15,36 @@ * this program. If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************************/ -/* - 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 <QGraphicsWidget> -class AMAROK_EXPORT RatingWidget : public QGraphicsWidget +#include <QQuickPaintedItem> + +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. @@ -52,58 +56,55 @@ */ 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 ); @@ -139,31 +140,19 @@ /** * 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/qml_plugin/src/RatingItem.cpp b/src/context/qml_plugin/src/RatingItem.cpp new file mode 100644 --- /dev/null +++ b/src/context/qml_plugin/src/RatingItem.cpp @@ -0,0 +1,275 @@ +/**************************************************************************************** + * Copyright (c) 2008 William Viana Soarjs <vianasw@gmail.com> * + * Copyright (c) 2010 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/>. * + ****************************************************************************************/ + +/* + 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 <QGuiApplication> +#include <QHoverEvent> +#include <QIcon> +#include <QMouseEvent> + +#include <KRatingPainter> + +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/servicetypes/amarok_animator.desktop b/src/context/servicetypes/amarok_animator.desktop deleted file mode 100644 --- 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 --- 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 dados 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 dados 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.h b/src/context/toolbar/AppletItemOverlay.h deleted file mode 100644 --- a/src/context/toolbar/AppletItemOverlay.h +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi <lfranchi@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_APPLET_ITEM_OVERLAY_H -#define AMAROK_APPLET_ITEM_OVERLAY_H - -#include <QWidget> - -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/AppletItemOverlay.cpp b/src/context/toolbar/AppletItemOverlay.cpp deleted file mode 100644 --- a/src/context/toolbar/AppletItemOverlay.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi <lfranchi@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 "AppletItemOverlay" - -#include "AppletItemOverlay.h" - -#include "context/toolbar/AppletToolbarAppletItem.h" -#include "core/support/Debug.h" - -#include <QIcon> -#include <KGlobalSettings> -#include <Plasma/Applet> -#include <Plasma/PaintUtils> -#include <Plasma/Theme> - -#include <QAction> -#include <QApplication> -#include <QGraphicsLinearLayout> -#include <QGraphicsWidget> -#include <QPainter> -#include <QStyleOptionGraphicsItem> -#include <QTimer> -#include <QToolButton> - -// 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/AppletToolbar.h b/src/context/toolbar/AppletToolbar.h deleted file mode 100644 --- a/src/context/toolbar/AppletToolbar.h +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi <lfranchi@kde.org> * - * Copyright (c) 2009 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_APPLET_TOOLBAR_H -#define AMAROK_APPLET_TOOLBAR_H - -#include "amarok_export.h" - -#include <QGraphicsWidget> - -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/AppletToolbar.cpp b/src/context/toolbar/AppletToolbar.cpp deleted file mode 100644 --- a/src/context/toolbar/AppletToolbar.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi <lfranchi@kde.org> * - * Copyright (c) 2009 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/>. * - ****************************************************************************************/ - -#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 <QGraphicsScene> -#include <QPainter> -#include <QPalette> -#include <QStyleOptionGraphicsItem> -#include <QGraphicsSceneResizeEvent> -#include <QGraphicsLinearLayout> -#include <QSizePolicy> - -Context::AppletToolbar::AppletToolbar( QGraphicsItem* parent ) - : QGraphicsWidget( parent ) - , m_configMode( false ) - , m_appletLayout( 0 ) - , m_cont( 0 ) - , m_configItem( 0 ) -{ - Context::Containment* cont = dynamic_cast<Context::Containment*>( 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<AppletToolbarAppletItem*>( 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/AppletToolbarAddItem.h b/src/context/toolbar/AppletToolbarAddItem.h deleted file mode 100644 --- a/src/context/toolbar/AppletToolbarAddItem.h +++ /dev/null @@ -1,73 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi <lfranchi@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_APPLET_TOOLBAR_ADD_ITEM_H -#define AMAROK_APPLET_TOOLBAR_ADD_ITEM_H - - -#include "amarok_export.h" - -#include <plasma/widgets/iconwidget.h> - -#include <QGraphicsWidget> -#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/AppletToolbarAddItem.cpp b/src/context/toolbar/AppletToolbarAddItem.cpp deleted file mode 100644 --- a/src/context/toolbar/AppletToolbarAddItem.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi <lfranchi@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/>. * - ****************************************************************************************/ - -#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 <QIcon> - -#include <QAction> -#include <QGraphicsSceneResizeEvent> -#include <QGraphicsSimpleTextItem> -#include <QPainter> -#include <QSizeF> -#include <QStyleOptionGraphicsItem> - -#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/AppletToolbarAppletItem.h b/src/context/toolbar/AppletToolbarAppletItem.h deleted file mode 100644 --- a/src/context/toolbar/AppletToolbarAppletItem.h +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi <lfranchi@kde.org> * - * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * - * Copyright (c) 2009 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_APPLET_TOOLBAR_APPLET_ITEM_H -#define AMAROK_APPLET_TOOLBAR_APPLET_ITEM_H - -#include "AppletToolbarBase.h" - -#include <QWeakPointer> - -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<QPropertyAnimation> m_opacityAnimation; - - Plasma::IconWidget* m_deleteIcon; - - bool m_configEnabled; -}; - -} // namespace - -#endif diff --git a/src/context/toolbar/AppletToolbarAppletItem.cpp b/src/context/toolbar/AppletToolbarAppletItem.cpp deleted file mode 100644 --- a/src/context/toolbar/AppletToolbarAppletItem.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi <lfranchi@kde.org> * - * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * - * Copyright (c) 2009 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/>. * - ****************************************************************************************/ - -#include "AppletToolbarAppletItem.h" - -#include "core/support/Amarok.h" -#include "core/support/Debug.h" -#include "PaletteHandler.h" - -#include <Plasma/Applet> -#include <Plasma/IconWidget> - -#include <QIcon> - -#include <QAction> -#include <QPainter> -#include <QGraphicsSceneMouseEvent> -#include <QGraphicsTextItem> -#include <QPropertyAnimation> - - -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/AppletToolbarBase.cpp b/src/context/toolbar/AppletToolbarBase.cpp deleted file mode 100644 --- a/src/context/toolbar/AppletToolbarBase.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 Leo Franchi <lfranchi@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/>. * - ****************************************************************************************/ - -#include "AppletToolbarBase.h" - -#include <QPainter> - -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.h b/src/context/toolbar/AppletToolbarConfigItem.h deleted file mode 100644 --- a/src/context/toolbar/AppletToolbarConfigItem.h +++ /dev/null @@ -1,59 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi <lfranchi@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_APPLET_TOOLBAR_CONFIG_ITEM_H -#define AMAROK_APPLET_TOOLBAR_CONFIG_ITEM_H - -#include <QGraphicsWidget> -#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/toolbar/AppletToolbarConfigItem.cpp b/src/context/toolbar/AppletToolbarConfigItem.cpp deleted file mode 100644 --- a/src/context/toolbar/AppletToolbarConfigItem.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi <lfranchi@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/>. * - ****************************************************************************************/ - -#include "AppletToolbarConfigItem.h" - -#include "App.h" -#include "PaletteHandler.h" - -#include <plasma/widgets/iconwidget.h> - -#include <QIcon> - -#include <QAction> -#include <QPainter> -#include <QSizePolicy> -#include <QStyleOptionGraphicsItem> - -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/tools/CMakeLists.txt b/src/context/tools/CMakeLists.txt --- a/src/context/tools/CMakeLists.txt +++ b/src/context/tools/CMakeLists.txt @@ -1,13 +1,13 @@ 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 diff --git a/src/context/tools/amarokpkg.cpp b/src/context/tools/amarokpkg.cpp --- a/src/context/tools/amarokpkg.cpp +++ b/src/context/tools/amarokpkg.cpp @@ -15,31 +15,30 @@ * this program. If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************************/ +#include "../AmarokContextPackageStructure.h" + #include <iostream> #include <QApplication> #include <QAction> #include <QCommandLineParser> #include <QDBusInterface> #include <QDir> #include <QLocale> +#include <QStandardPaths> #include <QTextStream> #include <KAboutData> #include <KLocalizedString> -#include <KService> -#include <KServiceTypeTrader> #include <KShell> -#include <QStandardPaths> #include <KProcess> -#include <KSycoca> #include <KConfigGroup> +#include <KPackage/PackageLoader> #include <KPackage/Package> -#include <KPluginInfo> 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) @@ -53,19 +52,27 @@ 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); @@ -97,12 +104,6 @@ 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 <path>", @@ -116,19 +117,21 @@ parser.addOption(QCommandLineOption(QStringList() << "p" << "packageroot <path>", 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"); @@ -158,26 +161,26 @@ 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")) { diff --git a/src/context/widgets/AppletHeader.h b/src/context/widgets/AppletHeader.h deleted file mode 100644 --- a/src/context/widgets/AppletHeader.h +++ /dev/null @@ -1,67 +0,0 @@ -/**************************************************************************************** - * 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 CONTEXTAPPLETHEADER_H -#define CONTEXTAPPLETHEADER_H - -#include "amarok_export.h" - -#include <QGraphicsWidget> - -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<QGraphicsLayoutItem*> m_dummyItems; - TextScrollingWidget *m_titleWidget; - Q_DISABLE_COPY( AppletHeader ) -}; - -} // namespace Context - -#endif // CONTEXTAPPLETHEADER_H diff --git a/src/context/widgets/AppletHeader.cpp b/src/context/widgets/AppletHeader.cpp deleted file mode 100644 --- a/src/context/widgets/AppletHeader.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/**************************************************************************************** - * 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/>. * - ****************************************************************************************/ - -#define DEBUG_PREFIX "AppletHeader" - -#include "AppletHeader.h" - -#include "TextScrollingWidget.h" -#include "core/support/Debug.h" - -#include <Plasma/IconWidget> - -#include <QApplication> -#include <QFont> -#include <QGraphicsLinearLayout> -#include <QPainter> -#include <QStyle> - -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<int> 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/ContainmentArrow.h b/src/context/widgets/ContainmentArrow.h deleted file mode 100644 --- a/src/context/widgets/ContainmentArrow.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi <lfranchi@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/>. * - ****************************************************************************************/ - -// -// 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 <QGraphicsWidget> -#include <QObject> -#include <QTimer> -#include <QWeakPointer> - -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<Plasma::Animation> m_fadeAnimation; -}; - - // namespace - -} -#endif diff --git a/src/context/widgets/ContainmentArrow.cpp b/src/context/widgets/ContainmentArrow.cpp deleted file mode 100644 --- a/src/context/widgets/ContainmentArrow.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 Leo Franchi <lfranchi@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/>. * - ****************************************************************************************/ - -#include "ContainmentArrow.h" - -#include "core/support/Debug.h" - -#include <KStandardDirs> -#include <QPainter> -#include <QGraphicsSceneMouseEvent> - -#include <Plasma/Animation> -#include <Plasma/Animator> - -// 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<Containment *>( 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/ContainmentSelectionLayer.h b/src/context/widgets/ContainmentSelectionLayer.h deleted file mode 100644 --- a/src/context/widgets/ContainmentSelectionLayer.h +++ /dev/null @@ -1,59 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 William Viana Soares <vianasw@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 CONTAINMENT_SELECTION_LAYER_H -#define CONTAINMENT_SELECTION_LAYER_H - -#include "amarok_export.h" - -#include <plasma/containment.h> - -#include <QIcon> - -#include <QGraphicsItem> -#include <QGraphicsSceneHoverEvent> -#include <QGraphicsSceneMouseEvent> -#include <QGraphicsSimpleTextItem> - -/** - * @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/ContainmentSelectionLayer.cpp b/src/context/widgets/ContainmentSelectionLayer.cpp deleted file mode 100644 --- a/src/context/widgets/ContainmentSelectionLayer.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 William Viana Soares <vianasw@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/>. * - ****************************************************************************************/ - -#include "ContainmentSelectionLayer.h" - -#include <plasma/paintutils.h> - -#include <KColorScheme> - -#include <QColor> -#include <QIcon> -#include <QFont> -#include <QPainter> -#include <QPen> - -#define ICON_SIZE 60 - -ContainmentSelectionLayer::ContainmentSelectionLayer( QGraphicsItem *parent ) - : QGraphicsItem( parent ) - , m_mouseHover( 0 ) -{ - m_containment = static_cast<Plasma::Containment *>( 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/DropPixmapItem.h b/src/context/widgets/DropPixmapItem.h deleted file mode 100644 --- a/src/context/widgets/DropPixmapItem.h +++ /dev/null @@ -1,100 +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 DROPPIXMAPITEM_H -#define DROPPIXMAPITEM_H - -#include "amarok_export.h" -#include "network/NetworkAccessManagerProxy.h" - -#include <QUrl> - -#include <QGraphicsLayoutItem> -#include <QGraphicsPixmapItem> - -//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 <simon.esneault@gmail.com> -*/ - -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/DropPixmapItem.cpp b/src/context/widgets/DropPixmapItem.cpp deleted file mode 100644 --- a/src/context/widgets/DropPixmapItem.cpp +++ /dev/null @@ -1,158 +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 "DropPixmapItem" - -#include "DropPixmapItem.h" - -#include "core/support/Debug.h" - -#include <QGraphicsSceneDragDropEvent> -#include <QMimeData> - -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/RatingWidget.cpp b/src/context/widgets/RatingWidget.cpp deleted file mode 100644 --- a/src/context/widgets/RatingWidget.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 William Viana Soarjs <vianasw@gmail.com> * - * Copyright (c) 2010 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/>. * - ****************************************************************************************/ - -/* - 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 <QPainter> -#include <QPixmap> -#include <QKeyEvent> -#include <QImage> -#include <QIcon> -#include <QGraphicsSceneMouseEvent> -#include <QGraphicsSceneHoverEvent> - -#include <kiconeffect.h> -#include <kiconloader.h> -#include <klocale.h> -#include <kstandarddirs.h> - -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.h b/src/context/widgets/RecentlyPlayedListWidget.h deleted file mode 100644 --- a/src/context/widgets/RecentlyPlayedListWidget.h +++ /dev/null @@ -1,109 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2010 Rick W. Chen <stuffcorpse@archlinux.us> * - * Copyright (c) 2013 Konrad Zemek <konrad.zemek@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 RECENTLY_PLAYED_LIST_WIDGET_H -#define RECENTLY_PLAYED_LIST_WIDGET_H - -#include "core/meta/forward_declarations.h" - -#include <QIcon> -#include <QUrl> -#include <Plasma/ScrollWidget> - -#include <QDateTime> -#include <QGraphicsWidget> -#include <QLabel> -#include <QQueue> - -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<RecentlyPlayedTrackData> m_recentTracks; - QIcon m_trackIcon; - QTimer *m_updateTimer; -}; - -#endif // RECENTLY_PLAYED_LIST_WIDGET_H diff --git a/src/context/widgets/RecentlyPlayedListWidget.cpp b/src/context/widgets/RecentlyPlayedListWidget.cpp deleted file mode 100644 --- a/src/context/widgets/RecentlyPlayedListWidget.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2010 Rick W. Chen <stuffcorpse@archlinux.us> * - * Copyright (c) 2013 Konrad Zemek <konrad.zemek@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 "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 <KSqueezedTextLabel> -#include <Plasma/IconWidget> - -#include <QApplication> -#include <QFontMetricsF> -#include <QGraphicsLinearLayout> -#include <QGraphicsProxyWidget> -#include <QGraphicsSceneMouseEvent> -#include <QLabel> -#include <QStringList> -#include <QVariant> -#include <QTimer> - -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/TextScrollingWidget.h b/src/context/widgets/TextScrollingWidget.h deleted file mode 100644 --- a/src/context/widgets/TextScrollingWidget.h +++ /dev/null @@ -1,110 +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 AMAROK_TEXT_SCROLLING_WIDGET_H -#define AMAROK_TEXT_SCROLLING_WIDGET_H - -#include "amarok_export.h" - -#include <QGraphicsWidget> -#include <QAbstractAnimation> - -//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 <simon.esneault@gmail.com> -*/ - -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/TextScrollingWidget.cpp b/src/context/widgets/TextScrollingWidget.cpp deleted file mode 100644 --- a/src/context/widgets/TextScrollingWidget.cpp +++ /dev/null @@ -1,372 +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 "TextScrollingWidget" - -#include "TextScrollingWidget.h" - -#include "core/support/Debug.h" - -#include <Plasma/FrameSvg> - -#include <QFont> -#include <QFontMetricsF> -#include <QGraphicsSimpleTextItem> -#include <QGraphicsSceneHoverEvent> -#include <QPainter> -#include <QTimer> -#include <QPropertyAnimation> - -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<QPropertyAnimation> 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/TextWidget.h b/src/context/widgets/TextWidget.h deleted file mode 100644 --- a/src/context/widgets/TextWidget.h +++ /dev/null @@ -1,62 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi <lfranchi@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_TEXT_WIDGET_H -#define AMAROK_TEXT_WIDGET_H - -#include "amarok_export.h" - -#include <plasma/layouts/layoutitem.h> - -#include <QGraphicsRectItem> - -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/TextWidget.cpp b/src/context/widgets/TextWidget.cpp deleted file mode 100644 --- a/src/context/widgets/TextWidget.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2007 Leo Franchi <lfranchi@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 "TextWidget" - -#include "TextWidget.h" - -#include "core/support/Debug.h" - -#include <QTextDocument> - -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( "<br />" ); -// 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( "<br />" ); - 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/ToolBoxIcon.h b/src/context/widgets/ToolBoxIcon.h deleted file mode 100644 --- a/src/context/widgets/ToolBoxIcon.h +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 William Viana Soares <vianasw@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 TOOLBOX_ICON_H -#define TOOLBOX_ICON_H - -#include "amarok_export.h" - -#include <plasma/widgets/iconwidget.h> - -#include <QGraphicsSceneMouseEvent> -#include <QPainter> -#include <QTextLine> -#include <QWeakPointer> - -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<Plasma::Animation> m_animHighLight; - - QGraphicsSimpleTextItem *m_text; - QBrush m_defaultTextBrush; -}; - -#endif - diff --git a/src/context/widgets/ToolBoxIcon.cpp b/src/context/widgets/ToolBoxIcon.cpp deleted file mode 100644 --- a/src/context/widgets/ToolBoxIcon.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2008 William Viana Soares <vianasw@gmail.com> * - * Copyright (c) 2009 Leo Franchi <lfranchi@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/>. * - ****************************************************************************************/ - -#include "ToolBoxIcon.h" - -#include "core/support/Debug.h" -#include "PaletteHandler.h" - -#include <Plasma/Animator> -#include <Plasma/Animation> -#include <plasma/paintutils.h> - -#include <KColorScheme> - -#include <QFont> - -#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/appletexplorer/AppletExplorer.h b/src/context/widgets/appletexplorer/AppletExplorer.h deleted file mode 100644 --- a/src/context/widgets/appletexplorer/AppletExplorer.h +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 William Viana Soares <vianasw@gmail.com> * - * 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 <http://www.gnu.org/licenses/>. * - ****************************************************************************************/ - -#ifndef APPLET_EXPLORER_H -#define APPLET_EXPLORER_H - -#include "amarok_export.h" - -#include <QGraphicsWidget> -#include <QPainter> - -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<AppletIconWidget*> listAppletWidgets(); - Containment *m_containment; - Plasma::ScrollWidget *m_scrollWidget; - Q_DISABLE_COPY( AppletExplorer ) -}; - -} // namespace Context - -#endif diff --git a/src/context/widgets/appletexplorer/AppletExplorer.cpp b/src/context/widgets/appletexplorer/AppletExplorer.cpp deleted file mode 100644 --- a/src/context/widgets/appletexplorer/AppletExplorer.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 William Viana Soares <vianasw@gmail.com> * - * 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 <http://www.gnu.org/licenses/>. * - ****************************************************************************************/ - -#define DEBUG_PREFIX "AppletExplorer" - -#include "AppletExplorer.h" - -#include "AppletIcon.h" -#include "core/support/Debug.h" -#include "PaletteHandler.h" - -#include <QIcon> -#include <Plasma/Applet> -#include <Plasma/ScrollWidget> - -#include <QGraphicsLinearLayout> -#include <QGraphicsScene> -#include <QGraphicsSceneWheelEvent> -#include <QGraphicsProxyWidget> -#include <QLabel> -#include <QStyleOptionGraphicsItem> -#include <QSignalMapper> - -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("<strong>Applet Explorer</strong>") ); - 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<AppletIconWidget*> -AppletExplorer::listAppletWidgets() -{ - QList<AppletIconWidget*> 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/AppletIcon.h b/src/context/widgets/appletexplorer/AppletIcon.h deleted file mode 100644 --- a/src/context/widgets/appletexplorer/AppletIcon.h +++ /dev/null @@ -1,59 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 William Viana Soares <vianasw@gmail.com> * - * Copyright (c) 2009 Mark Kretschmann <kretschmann@kde.org> * - * * - * 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 <http://www.gnu.org/licenses/>. * - ****************************************************************************************/ - -#ifndef APPLET_ICON_H -#define APPLET_ICON_H - -#include "amarok_export.h" - -#include <Plasma/IconWidget> - -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/context/widgets/appletexplorer/AppletIcon.cpp b/src/context/widgets/appletexplorer/AppletIcon.cpp deleted file mode 100644 --- a/src/context/widgets/appletexplorer/AppletIcon.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2009 William Viana Soares <vianasw@gmail.com> * - * Copyright (c) 2009 Mark Kretschmann <kretschmann@kde.org> * - * * - * 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 <http://www.gnu.org/licenses/>. * - ****************************************************************************************/ - -#include "AppletIcon.h" - -#include "PaletteHandler.h" - -#include <QIcon> -#include <KPluginInfo> - -#include <QColor> -#include <QPainter> - -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/dialogs/DiagnosticDialog.cpp b/src/dialogs/DiagnosticDialog.cpp --- a/src/dialogs/DiagnosticDialog.cpp +++ b/src/dialogs/DiagnosticDialog.cpp @@ -17,7 +17,7 @@ #include "DiagnosticDialog.h" -// #include "context/ContextView.h" +#include "context/ContextView.h" #include "PluginManager.h" #include "scripting/scriptmanager/ScriptManager.h" @@ -95,7 +95,6 @@ aPluginString += " " + aInfo.name() + " (" + aInfo.version() + ")\n"; } -/* FIXME: disabled temporarily for KF5 porting // Get applets QString appletString; const QStringList appletList = Context::ContextView::self()->currentAppletNames(); @@ -105,7 +104,6 @@ // Currently we cannot extract the applet version number this way appletString += " " + applet + '\n'; } -*/ const KService::Ptr aPhononBackend = KServiceTypeTrader::self()->preferredService( "PhononBackend" ); @@ -132,7 +130,7 @@ "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 ); } diff --git a/src/playlist/view/listview/PrettyListView.cpp b/src/playlist/view/listview/PrettyListView.cpp --- a/src/playlist/view/listview/PrettyListView.cpp +++ b/src/playlist/view/listview/PrettyListView.cpp @@ -27,8 +27,8 @@ #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" @@ -717,7 +717,6 @@ return; ongoingDrags = true; -/* FIXME: disabled temporarily for KF5 porting if( !m_pd ) m_pd = The::popupDropperFactory()->createPopupDropper( Context::ContextView::self() ); @@ -734,7 +733,6 @@ m_pd->show(); } -*/ QListView::startDrag( supportedActions ); debug() << "After the drag!";