From 89ab3344d4523bb030acd6461aba3d2d2ad159ef Mon Sep 17 00:00:00 2001 From: Leo Franchi <lfranchi@kde.org> Date: Sun, 17 Jun 2012 00:09:03 +0200 Subject: [PATCH] Create copyable artist and album page links --- src/libtomahawk/GlobalActionManager.cpp | 42 ++++++++++ src/libtomahawk/GlobalActionManager.h | 5 ++ .../widgets/infowidgets/AlbumInfoWidget.h | 2 + .../widgets/infowidgets/ArtistInfoWidget.h | 2 + .../widgets/infowidgets/TrackInfoWidget.h | 2 + src/sourcetree/SourceTreeView.cpp | 8 ++ src/sourcetree/SourcesModel.cpp | 62 ++++++++------- src/sourcetree/SourcesModel.h | 8 +- src/sourcetree/items/SourceTreeItem.h | 1 + src/sourcetree/items/TemporaryPageItem.cpp | 76 +++++++++++++++++++ src/sourcetree/items/TemporaryPageItem.h | 9 +++ 11 files changed, 187 insertions(+), 30 deletions(-) diff --git a/src/libtomahawk/GlobalActionManager.cpp b/src/libtomahawk/GlobalActionManager.cpp index afbddc214..b51bab079 100644 --- a/src/libtomahawk/GlobalActionManager.cpp +++ b/src/libtomahawk/GlobalActionManager.cpp @@ -97,6 +97,48 @@ GlobalActionManager::openLinkFromQuery( const query_ptr& query ) const } +QUrl +GlobalActionManager::copyOpenLink( const query_ptr& query ) const +{ + const QUrl link = openLinkFromQuery( query ); + + QClipboard* cb = QApplication::clipboard(); + QByteArray data = link.toEncoded(); + data.replace( "'", "%27" ); // QUrl doesn't encode ', which it doesn't have to. Some apps don't like ' though, and want %27. Both are valid. + cb->setText( data ); + + return link; +} + + +QUrl +GlobalActionManager::copyOpenLink( const artist_ptr& artist ) const +{ + const QUrl link( QString( "%1/artist/%2" ).arg( hostname() ).arg( artist->name() ) ); + + QClipboard* cb = QApplication::clipboard(); + QByteArray data = link.toEncoded(); + data.replace( "'", "%27" ); // QUrl doesn't encode ', which it doesn't have to. Some apps don't like ' though, and want %27. Both are valid. + cb->setText( data ); + + return link; +} + + +QUrl +GlobalActionManager::copyOpenLink( const album_ptr& album ) const +{ + const QUrl link( QString( "%1/album/%2/%3" ).arg( hostname() ).arg( album->artist().isNull() ? QString() : album->artist()->name() ).arg( album->name()) ); + + QClipboard* cb = QApplication::clipboard(); + QByteArray data = link.toEncoded(); + data.replace( "'", "%27" ); // QUrl doesn't encode ', which it doesn't have to. Some apps don't like ' though, and want %27. Both are valid. + cb->setText( data ); + + return link; +} + + QUrl GlobalActionManager::openLink( const QString& title, const QString& artist, const QString& album ) const { diff --git a/src/libtomahawk/GlobalActionManager.h b/src/libtomahawk/GlobalActionManager.h index a7d519ff3..00c639224 100644 --- a/src/libtomahawk/GlobalActionManager.h +++ b/src/libtomahawk/GlobalActionManager.h @@ -43,6 +43,11 @@ public: virtual ~GlobalActionManager(); QUrl openLinkFromQuery( const Tomahawk::query_ptr& query ) const; + + QUrl copyOpenLink( const Tomahawk::artist_ptr& artist ) const; + QUrl copyOpenLink( const Tomahawk::album_ptr& album ) const; + QUrl copyOpenLink( const Tomahawk::query_ptr& query ) const; + QUrl openLink( const QString& title, const QString& artist, const QString& album ) const; public slots: diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h index 9d00c2847..4bfe66fb0 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h @@ -55,6 +55,8 @@ public: AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* parent = 0 ); ~AlbumInfoWidget(); + Tomahawk::album_ptr album() const { return m_album; } + virtual QWidget* widget() { return this; } virtual Tomahawk::playlistinterface_ptr playlistInterface() const; diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h index 9e1ac0279..91686e335 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h @@ -66,6 +66,8 @@ public: */ void load( const Tomahawk::artist_ptr& artist ); + Tomahawk::artist_ptr artist() const { return m_artist; } + virtual QWidget* widget() { return this; } virtual Tomahawk::playlistinterface_ptr playlistInterface() const; diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h index dc9c3de64..78ea6a174 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h @@ -54,6 +54,8 @@ public: TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* parent = 0 ); ~TrackInfoWidget(); + Tomahawk::query_ptr query() const { return m_query; } + virtual QWidget* widget() { return this; } virtual Tomahawk::playlistinterface_ptr playlistInterface() const; diff --git a/src/sourcetree/SourceTreeView.cpp b/src/sourcetree/SourceTreeView.cpp index 23b1a0b81..b840bae33 100644 --- a/src/sourcetree/SourceTreeView.cpp +++ b/src/sourcetree/SourceTreeView.cpp @@ -568,6 +568,8 @@ SourceTreeView::onCustomContextMenu( const QPoint& pos ) setupMenus(); + const QList< QAction* > customActions = model()->data( m_contextMenuIndex, SourcesModel::CustomActionRole ).value< QList< QAction* > >(); + if ( model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::StaticPlaylist || model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::AutomaticPlaylist || model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::Station ) @@ -586,6 +588,12 @@ SourceTreeView::onCustomContextMenu( const QPoint& pos ) else if ( !item->source().isNull() ) m_privacyMenu.exec( mapToGlobal( pos ) ); } + else if ( !customActions.isEmpty() ) + { + QMenu customMenu; + customMenu.addActions( customActions ); + customMenu.exec( mapToGlobal( pos ) ); + } } diff --git a/src/sourcetree/SourcesModel.cpp b/src/sourcetree/SourcesModel.cpp index 1b532e3b8..a2fe261ae 100644 --- a/src/sourcetree/SourcesModel.cpp +++ b/src/sourcetree/SourcesModel.cpp @@ -107,39 +107,43 @@ SourcesModel::data( const QModelIndex& index, int role ) const switch ( role ) { - case Qt::SizeHintRole: - return QSize( 0, 18 ); - case SourceTreeItemRole: - return QVariant::fromValue< SourceTreeItem* >( item ); - case SourceTreeItemTypeRole: - return item->type(); - case Qt::DisplayRole: - case Qt::EditRole: - return item->text(); - case Qt::DecorationRole: - return item->icon(); - case SourcesModel::SortRole: - return item->peerSortValue(); - case SourcesModel::IDRole: - return item->IDValue(); - case SourcesModel::LatchedOnRole: + case Qt::SizeHintRole: + return QSize( 0, 18 ); + case SourceTreeItemRole: + return QVariant::fromValue< SourceTreeItem* >( item ); + case SourceTreeItemTypeRole: + return item->type(); + case Qt::DisplayRole: + case Qt::EditRole: + return item->text(); + case Qt::DecorationRole: + return item->icon(); + case SourcesModel::SortRole: + return item->peerSortValue(); + case SourcesModel::IDRole: + return item->IDValue(); + case SourcesModel::LatchedOnRole: + { + if ( item->type() == Collection ) { - if ( item->type() == Collection ) - { - SourceItem* cItem = qobject_cast< SourceItem* >( item ); - return cItem->localLatchedOn(); - } - return false; + SourceItem* cItem = qobject_cast< SourceItem* >( item ); + return cItem->localLatchedOn(); } - case SourcesModel::LatchedRealtimeRole: + return false; + } + case SourcesModel::LatchedRealtimeRole: + { + if ( item->type() == Collection ) { - if ( item->type() == Collection ) - { - SourceItem* cItem = qobject_cast< SourceItem* >( item ); - return cItem->localLatchMode() == Tomahawk::PlaylistModes::RealTime; - } - return false; + SourceItem* cItem = qobject_cast< SourceItem* >( item ); + return cItem->localLatchMode() == Tomahawk::PlaylistModes::RealTime; } + return false; + } + case SourcesModel::CustomActionRole: + { + return QVariant::fromValue< QList< QAction* > >( item->customActions() ); + } case Qt::ToolTipRole: if ( !item->tooltip().isEmpty() ) return item->tooltip(); diff --git a/src/sourcetree/SourcesModel.h b/src/sourcetree/SourcesModel.h index dade7577a..b570cf8a0 100644 --- a/src/sourcetree/SourcesModel.h +++ b/src/sourcetree/SourcesModel.h @@ -28,6 +28,9 @@ #include "Typedefs.h" #include "Source.h" +#include <QList> +#include <QAction> + class QMimeData; class SourceTreeItem; @@ -72,7 +75,8 @@ public: SortRole = Qt::UserRole + 12, IDRole = Qt::UserRole + 13, LatchedOnRole = Qt::UserRole + 14, - LatchedRealtimeRole = Qt::UserRole + 15 + LatchedRealtimeRole = Qt::UserRole + 15, + CustomActionRole = Qt::UserRole + 16 // QList< QAction* > }; SourcesModel( QObject* parent = 0 ); @@ -149,4 +153,6 @@ private: Tomahawk::ViewPage* m_viewPageDelayedCacheItem; }; +Q_DECLARE_METATYPE( QList< QAction* > ) + #endif // SOURCESMODEL_H diff --git a/src/sourcetree/items/SourceTreeItem.h b/src/sourcetree/items/SourceTreeItem.h index 410542e9e..628322458 100644 --- a/src/sourcetree/items/SourceTreeItem.h +++ b/src/sourcetree/items/SourceTreeItem.h @@ -70,6 +70,7 @@ public: virtual void setDropType( DropType type ) { m_dropType = type; } virtual DropType dropType() const { return m_dropType; } virtual bool isBeingPlayed() const { return false; } + virtual QList< QAction* > customActions() const { return QList< QAction* >(); } /// don't call me unless you are a sourcetreeitem. i prefer this to making everyone a friend void beginRowsAdded( int from, int to ) { emit beginChildRowsAdded( from, to ); } diff --git a/src/sourcetree/items/TemporaryPageItem.cpp b/src/sourcetree/items/TemporaryPageItem.cpp index dc9f4a169..8b9ea3a04 100644 --- a/src/sourcetree/items/TemporaryPageItem.cpp +++ b/src/sourcetree/items/TemporaryPageItem.cpp @@ -17,11 +17,24 @@ */ #include "TemporaryPageItem.h" + +#include "GlobalActionManager.h" #include "ViewManager.h" #include "widgets/infowidgets/AlbumInfoWidget.h" #include "widgets/infowidgets/ArtistInfoWidget.h" #include "widgets/infowidgets/TrackInfoWidget.h" #include "widgets/SearchWidget.h" +#include "utils/Closure.h" + +#include <QAction> + +namespace { + enum LinkType { + ArtistLink, + AlbumLink, + TrackLink + }; +} using namespace Tomahawk; @@ -31,14 +44,39 @@ TemporaryPageItem::TemporaryPageItem ( SourcesModel* mdl, SourceTreeItem* parent , m_icon( QIcon( RESPATH "images/playlist-icon.png" ) ) , m_sortValue( sortValue ) { + QAction* action = 0; + if ( dynamic_cast< ArtistInfoWidget* >( page ) ) + { + action = new QAction( tr( "Copy Artist Link" ), this ); + action->setProperty( "linkType", (int)ArtistLink ); + m_icon = QIcon( RESPATH "images/artist-icon.png" ); + } else if ( dynamic_cast< AlbumInfoWidget* >( page ) ) + { + action = new QAction( tr( "Copy Album Link" ), this ); + action->setProperty( "linkType", (int)AlbumLink ); + m_icon = QIcon( RESPATH "images/album-icon.png" ); + } else if ( dynamic_cast< TrackInfoWidget* >( page ) ) + { + action = new QAction( tr( "Copy Track Link" ), this ); + action->setProperty( "linkType", (int)TrackLink ); + m_icon = QIcon( RESPATH "images/track-icon-sidebar.png" ); + } else if ( dynamic_cast< SearchWidget* >( page ) ) + { m_icon = QIcon( RESPATH "images/search-icon.png" ); + } + + if ( action ) + { + m_customActions << action; + NewClosure( action, SIGNAL( triggered() ), this, SLOT( linkActionTriggered( QAction* ) ), action ); + } model()->linkSourceItemToPage( this, page ); } @@ -95,3 +133,41 @@ TemporaryPageItem::removeFromList() deleteLater(); } + + +void +TemporaryPageItem::linkActionTriggered( QAction* action ) +{ + Q_ASSERT( action ); + if ( !action ) + return; + + const LinkType type = (LinkType)action->property( "linkType" ).toInt(); + switch( type ) + { + case ArtistLink: + { + ArtistInfoWidget* aPage = dynamic_cast< ArtistInfoWidget* >( m_page ); + Q_ASSERT( aPage ); + GlobalActionManager::instance()->copyOpenLink( aPage->artist() ); + + break; + } + case AlbumLink: + { + AlbumInfoWidget* aPage = dynamic_cast< AlbumInfoWidget* >( m_page ); + Q_ASSERT( aPage ); + GlobalActionManager::instance()->copyOpenLink( aPage->album() ); + + break; + } + case TrackLink: + { + TrackInfoWidget* tPage = dynamic_cast< TrackInfoWidget* >( m_page ); + Q_ASSERT( tPage ); + GlobalActionManager::instance()->copyOpenLink( tPage->query() ); + + break; + } + } +} diff --git a/src/sourcetree/items/TemporaryPageItem.h b/src/sourcetree/items/TemporaryPageItem.h index f0cfb8b27..72350d87f 100644 --- a/src/sourcetree/items/TemporaryPageItem.h +++ b/src/sourcetree/items/TemporaryPageItem.h @@ -22,6 +22,8 @@ #include "items/SourceTreeItem.h" #include "ViewPage.h" +class QAction; + class TemporaryPageItem : public SourceTreeItem { Q_OBJECT @@ -34,6 +36,7 @@ public: virtual QIcon icon() const; virtual int peerSortValue() const; virtual int IDValue() const; + virtual QList< QAction* > customActions() const { return m_customActions; } Tomahawk::ViewPage* page() const { return m_page; } virtual bool isBeingPlayed() const { return m_page->isBeingPlayed(); } @@ -44,10 +47,16 @@ public slots: signals: bool removed(); +private slots: + void linkActionTriggered( QAction* ); + private: Tomahawk::ViewPage* m_page; QIcon m_icon; int m_sortValue; + QList< QAction* > m_customActions; }; +Q_DECLARE_METATYPE( QAction* ) + #endif // TEMPORARYPAGEITEM_H