1 /****************************************************************************************
2  * Copyright (c) 2008 Bonne Eggleston <b.eggleston@gmail.com>                           *
3  * Copyright (c) 2009 Seb Ruiz <ruiz@kde.org>                                           *
4  * Copyright (c) 2009 Louis Bayle <louis.bayle@gmail.com>                               *
5  * Copyright (c) 2010 Nikolaj Hald Nielsen <nhn@kde.org>                                *
6  *                                                                                      *
7  * This program is free software; you can redistribute it and/or modify it under        *
8  * the terms of the GNU General Public License as published by the Free Software        *
9  * Foundation; either version 2 of the License, or (at your option) version 3 or        *
10  * any later version accepted by the membership of KDE e.V. (or its successor approved  *
11  * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of  *
12  * version 3 of the license.                                                            *
13  *                                                                                      *
14  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
15  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
16  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
17  *                                                                                      *
18  * You should have received a copy of the GNU General Public License along with         *
19  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
20  ****************************************************************************************/
21 
22 #include "PlaylistViewCommon.h"
23 
24 #include "EngineController.h"
25 #include "GlobalCurrentTrackActions.h"
26 #include "core/capabilities/ActionsCapability.h"
27 #include "core/capabilities/FindInSourceCapability.h"
28 #include "core/meta/Meta.h"
29 #include "core/support/Debug.h"
30 #include "covermanager/CoverFetchingActions.h"
31 #include "dialogs/TagDialog.h"
32 #include "playlist/proxymodels/GroupingProxy.h"
33 #include "playlist/view/listview/PrettyListView.h"
34 
35 #include <QMenu>
36 
37 #include <QObject>
38 #include <QModelIndex>
39 
ViewCommon()40 Playlist::ViewCommon::ViewCommon()
41     : m_stopAfterTrackAction( 0 )
42     , m_cueTrackAction( 0 )
43     , m_removeTracTrackAction( 0 )
44     , m_findInSourceAction( 0 )
45 {}
46 
~ViewCommon()47 Playlist::ViewCommon::~ViewCommon()
48 {}
49 
50 
51 void
trackMenu(QWidget * parent,const QModelIndex * index,const QPoint & pos)52 Playlist::ViewCommon::trackMenu( QWidget *parent, const QModelIndex *index, const QPoint &pos )
53 {
54     DEBUG_BLOCK
55 
56     QMenu *menu = new QMenu( parent );
57 
58     menu->addActions( parentCheckActions( parent, trackActionsFor( parent, index ) ) );
59     menu->addSeparator();
60 
61     QList<QAction *> albumActionsList = parentCheckActions( parent, albumActionsFor( index ) );
62     if( !albumActionsList.isEmpty() )
63     {
64         // there are no cover actions if the song/album is not in the collection
65         QMenu *menuCover = new QMenu( i18n( "Album" ), menu );
66         menuCover->addActions( albumActionsList );
67         menuCover->setIcon( QIcon::fromTheme( QStringLiteral("filename-album-amarok") ) );
68         menu->addMenu( menuCover );
69         menu->addSeparator();
70     }
71 
72     menu->addActions( parentCheckActions( parent, multiSourceActionsFor( parent, index ) ) );
73     menu->addSeparator();
74     menu->addActions( parentCheckActions( parent, editActionsFor( parent, index ) ) );
75 
76     menu->exec( pos );
77     delete menu;
78 }
79 
80 
81 QList<QAction *>
actionsFor(QWidget * parent,const QModelIndex * index)82 Playlist::ViewCommon::actionsFor( QWidget *parent, const QModelIndex *index )
83 {
84     QList<QAction *> actions;
85 
86     QAction *separator = new QAction( parent );
87     separator->setSeparator( true );
88 
89     actions << parentCheckActions( parent, trackActionsFor( parent, index ) );
90     actions << separator;
91 
92     QList<QAction *> albumActionsList = parentCheckActions( parent, albumActionsFor( index ) );
93     if( !albumActionsList.isEmpty() )
94     {
95         actions << albumActionsList;
96         actions << separator;
97     }
98 
99     actions << parentCheckActions( parent, multiSourceActionsFor( parent, index ) );
100     actions << separator;
101     actions << parentCheckActions( parent, editActionsFor( parent, index ) );
102 
103     return actions;
104 }
105 
106 
107 QList<QAction *>
trackActionsFor(QWidget * parent,const QModelIndex * index)108 Playlist::ViewCommon::trackActionsFor( QWidget *parent, const QModelIndex *index )
109 {
110     QList<QAction *> actions;
111 
112     Meta::TrackPtr track = index->data( Playlist::TrackRole ).value< Meta::TrackPtr >();
113 
114     QAction *separator = new QAction( parent );
115     separator->setSeparator( true );
116     const bool isQueued = index->data( Playlist::QueuePositionRole ).toInt() != 0;
117     const QString queueText = !isQueued ? i18n( "Queue Track" ) : i18n( "Dequeue Track" );
118 
119     //display "Queue track" option only if the track is playable
120     if( track->isPlayable() )
121     {
122 
123         if( m_cueTrackAction == 0 )
124         {
125             m_cueTrackAction = new QAction( QIcon::fromTheme( QStringLiteral("media-track-queue-amarok") ), queueText, parent );
126         }
127         else
128         {
129             m_cueTrackAction->disconnect();
130             m_cueTrackAction->setText( queueText );
131         }
132 
133         if( auto p = static_cast<Playlist::PrettyListView*>(parent))
134         {
135             if( isQueued )
136                 QObject::connect( m_cueTrackAction, &QAction::triggered,
137                                   p, &Playlist::PrettyListView::dequeueSelection );
138             else
139                 QObject::connect( m_cueTrackAction, &QAction::triggered,
140                                   p, &Playlist::PrettyListView::queueSelection );
141         }
142 
143         actions << m_cueTrackAction;
144 
145     }
146 
147     //actions << separator;
148 
149     const bool isCurrentTrack = index->data( Playlist::ActiveTrackRole ).toBool();
150 
151     //display "Stop after this track" option only if track is playable. not sure if this check is really needed
152     if( track->isPlayable() )
153     {
154         if( m_stopAfterTrackAction == 0 )
155         {
156             m_stopAfterTrackAction = new QAction( QIcon::fromTheme( QStringLiteral("media-playback-stop-amarok") ),
157                                                   i18n( "Stop Playing After This Track" ), parent );
158 
159             if ( auto p = static_cast<Playlist::PrettyListView*>(parent) )
160                 QObject::connect( m_stopAfterTrackAction, &QAction::triggered,
161                                   p, &Playlist::PrettyListView::stopAfterTrack );
162         }
163         actions << m_stopAfterTrackAction;
164     }
165 
166     //actions << separator;
167 
168     if( m_removeTracTrackAction == 0 )
169     {
170         m_removeTracTrackAction = new QAction( QIcon::fromTheme( QStringLiteral("media-track-remove-amarok") ),
171                                                i18n( "Remove From Playlist" ), parent );
172 
173         if ( auto p = static_cast<Playlist::PrettyListView*>(parent) )
174             QObject::connect( m_removeTracTrackAction, &QAction::triggered,
175                               p, &Playlist::PrettyListView::removeSelection );
176     }
177     actions << m_removeTracTrackAction;
178 
179     //lets see if parent is the currently playing tracks, and if it has CurrentTrackActionsCapability
180     if( isCurrentTrack )
181     {
182         //actions << separator;
183 
184         QList<QAction *> globalCurrentTrackActions = The::globalCurrentTrackActions()->actions();
185         foreach( QAction *action, globalCurrentTrackActions )
186             actions << action;
187 
188         if( track->has<Capabilities::ActionsCapability>() )
189         {
190             QScopedPointer<Capabilities::ActionsCapability>
191                     ac( track->create<Capabilities::ActionsCapability>() );
192             if ( ac )
193                 actions.append( ac->actions() );
194         }
195     }
196 
197     if( track->has<Capabilities::FindInSourceCapability>() )
198     {
199         if( m_findInSourceAction == 0 )
200         {
201             m_findInSourceAction = new QAction( QIcon::fromTheme( QStringLiteral("edit-find") ),
202                                                 i18n( "Show in Media Sources" ), parent );
203 
204             if( auto p = static_cast<Playlist::PrettyListView*>(parent) )
205                 QObject::connect( m_findInSourceAction, &QAction::triggered,
206                                   p, &Playlist::PrettyListView::findInSource );
207         }
208         actions << m_findInSourceAction;
209     }
210 
211     return actions;
212 }
213 
214 QList<QAction *>
albumActionsFor(const QModelIndex * index)215 Playlist::ViewCommon::albumActionsFor( const QModelIndex *index )
216 {
217     QList<QAction *> actions;
218 
219     Meta::TrackPtr track = index->data( Playlist::TrackRole ).value< Meta::TrackPtr >();
220 
221     Meta::AlbumPtr album = track->album();
222     if( album )
223     {
224         QScopedPointer<Capabilities::ActionsCapability>
225                 ac( album->create<Capabilities::ActionsCapability>() );
226         if( ac )
227             actions.append( ac->actions() );
228     }
229 
230     return actions;
231 }
232 
233 
234 QList<QAction *>
multiSourceActionsFor(QWidget * parent,const QModelIndex * index)235 Playlist::ViewCommon::multiSourceActionsFor( QWidget *parent, const QModelIndex *index )
236 {
237     QList<QAction *> actions;
238     Meta::TrackPtr track = index->data( Playlist::TrackRole ).value< Meta::TrackPtr >();
239 
240     const bool isMultiSource = index->data( Playlist::MultiSourceRole ).toBool();
241 
242     if( isMultiSource )
243     {
244         QAction *selectSourceAction = new QAction( QIcon::fromTheme( QStringLiteral("media-playlist-repeat") ),
245                                                    i18n( "Select Source" ), parent );
246 
247         if( auto p = static_cast<Playlist::PrettyListView*>(parent) )
248             QObject::connect( selectSourceAction, &QAction::triggered, p, &Playlist::PrettyListView::selectSource );
249 
250         actions << selectSourceAction;
251     }
252 
253     return actions;
254 }
255 
256 
257 QList<QAction *>
editActionsFor(QWidget * parent,const QModelIndex * index)258 Playlist::ViewCommon::editActionsFor( QWidget *parent, const QModelIndex *index )
259 {
260     QList<QAction *> actions;
261 
262     Meta::TrackPtr track = index->data( Playlist::TrackRole ).value< Meta::TrackPtr >();
263 
264     QAction *editAction = new QAction( QIcon::fromTheme( QStringLiteral("media-track-edit-amarok") ),
265                                        i18n( "Edit Track Details" ), parent );
266     editAction->setProperty( "popupdropper_svg_id", "edit" );
267 
268     if( auto p = static_cast<Playlist::PrettyListView*>(parent) )
269         QObject::connect( editAction, &QAction::triggered, p, &Playlist::PrettyListView::editTrackInformation );
270 
271     actions << editAction;
272 
273     return actions;
274 }
275 
276 QList<QAction *>
parentCheckActions(QObject * parent,QList<QAction * > actions)277 Playlist::ViewCommon::parentCheckActions( QObject *parent, QList<QAction *> actions )
278 {
279     foreach( QAction *action, actions )
280     {
281         if( !action->parent() )
282             action->setParent( parent );
283     }
284 
285     return actions;
286 }
287