PageRenderTime 187ms CodeModel.GetById 40ms app.highlight 104ms RepoModel.GetById 37ms app.codeStats 0ms

/src/libtomahawk/ContextMenu.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 531 lines | 402 code | 102 blank | 27 comment | 82 complexity | e63a62e9e2fe72712b77a380b6d3fa4f MD5 | raw file
  1/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  2 *
  3 *   Copyright 2010-2015, Christian Muehlhaeuser <muesli@tomahawk-player.org>
  4 *   Copyright 2013,      Teo Mrnjavac <teo@kde.org>
  5 *
  6 *   Tomahawk is free software: you can redistribute it and/or modify
  7 *   it under the terms of the GNU General Public License as published by
  8 *   the Free Software Foundation, either version 3 of the License, or
  9 *   (at your option) any later version.
 10 *
 11 *   Tomahawk is distributed in the hope that it will be useful,
 12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 14 *   GNU General Public License for more details.
 15 *
 16 *   You should have received a copy of the GNU General Public License
 17 *   along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
 18 */
 19
 20#include "ContextMenu.h"
 21
 22#include "audio/AudioEngine.h"
 23#include "playlist/ContextView.h"
 24#include "playlist/TrackView.h"
 25#include "playlist/PlayableModel.h"
 26#include "filemetadata/MetadataEditor.h"
 27#include "utils/LinkGenerator.h"
 28#include "ViewManager.h"
 29#include "Query.h"
 30#include "Result.h"
 31#include "collection/Collection.h"
 32#include "Source.h"
 33#include "SourceList.h"
 34#include "Artist.h"
 35#include "Album.h"
 36
 37#include "utils/ImageRegistry.h"
 38#include "utils/Logger.h"
 39
 40#include <QDesktopServices>
 41#include <QFileInfo>
 42
 43using namespace Tomahawk;
 44
 45
 46ContextMenu::ContextMenu( QWidget* parent )
 47    : QMenu( parent )
 48    , m_playlists_sigmap( 0 )
 49    , m_sources_sigmap( 0 )
 50    , m_loveAction( 0 )
 51{
 52    setFont( TomahawkUtils::systemFont() );
 53
 54    m_sigmap = new QSignalMapper( this );
 55    connect( m_sigmap, SIGNAL( mapped( int ) ), SLOT( onTriggered( int ) ) );
 56
 57    clear();
 58}
 59
 60
 61ContextMenu::~ContextMenu()
 62{
 63}
 64
 65
 66void
 67ContextMenu::clear()
 68{
 69    QMenu::clear();
 70
 71    m_queries.clear();
 72    m_albums.clear();
 73    m_artists.clear();
 74
 75    m_supportedActions = ActionPlay | ActionQueue | ActionPlaylist | ActionCopyLink | ActionLove | ActionStopAfter | ActionPage | ActionEditMetadata | ActionSend | ActionOpenFileManager;
 76}
 77
 78
 79unsigned int
 80ContextMenu::itemCount() const
 81{
 82   return m_queries.count() + m_artists.count() + m_albums.count();
 83}
 84
 85
 86void
 87ContextMenu::addToPlaylist( int playlistIdx )
 88{
 89    Tomahawk::playlist_ptr playlist = m_playlists.at( playlistIdx );
 90    playlist->addEntries( m_queries );
 91}
 92
 93
 94void
 95ContextMenu::sendToSource( int sourceIdx )
 96{
 97    const Tomahawk::source_ptr &src = m_sources.at( sourceIdx );
 98    foreach ( Tomahawk::query_ptr query, m_queries )
 99    {
100        query->queryTrack()->share( src );
101    }
102}
103
104
105bool
106playlistsLessThan( const Tomahawk::playlist_ptr& s1, const Tomahawk::playlist_ptr& s2 )
107{
108    return s1->title().toLower() < s2->title().toLower();
109}
110
111
112bool
113sourcesLessThan( const Tomahawk::source_ptr& s1, const Tomahawk::source_ptr& s2 )
114{
115    return s1->friendlyName().toLower() < s2->friendlyName().toLower();
116}
117
118
119void
120ContextMenu::setQueries( const QList<Tomahawk::query_ptr>& queries )
121{
122    if ( queries.isEmpty() )
123        return;
124
125    QMenu::clear();
126    m_queries.clear();
127    m_queries << queries;
128
129    if ( m_supportedActions & ActionPlay && itemCount() == 1 )
130        m_sigmap->setMapping( addAction( ImageRegistry::instance()->icon( RESPATH "images/play.svg" ), tr( "&Play" ) ), ActionPlay );
131
132    if ( m_supportedActions & ActionDownload )
133        m_sigmap->setMapping( addAction( ImageRegistry::instance()->icon( RESPATH "images/downloads.svg" ), tr( "Download" ) ), ActionDownload );
134
135    if ( m_supportedActions & ActionQueue )
136        m_sigmap->setMapping( addAction( ImageRegistry::instance()->icon( RESPATH "images/queue.svg" ), tr( "Add to &Queue" ) ), ActionQueue );
137
138    if ( m_supportedActions & ActionPlaylist )
139    {
140        // Get the current list of all playlists.
141        m_playlists = QList< Tomahawk::playlist_ptr >( SourceList::instance()->getLocal()->dbCollection()->playlists() );
142        // Sort the playlist
143        qSort( m_playlists.begin(), m_playlists.end(), playlistsLessThan );
144        if ( m_playlists_sigmap != 0 )
145            m_playlists_sigmap->deleteLater();
146        m_playlists_sigmap = new QSignalMapper( this );
147
148        // Build the menu listing all available playlists
149        QMenu* playlistMenu = addMenu( ImageRegistry::instance()->icon( RESPATH "images/playlist-icon.svg" ), tr( "Add to &Playlist" ) );
150        for ( int i = 0; i < m_playlists.length(); ++i )
151        {
152            QAction* action = new QAction( m_playlists.at( i )->title() , this );
153            playlistMenu->addAction( action );
154            m_playlists_sigmap->setMapping( action, i );
155            connect( action, SIGNAL( triggered() ), m_playlists_sigmap, SLOT( map() ) );
156        }
157        connect( m_playlists_sigmap, SIGNAL( mapped( int ) ), this, SLOT( addToPlaylist( int ) ) );
158    }
159
160    if ( m_supportedActions & ActionSend ) //Send to someone's Inbox!
161    {
162        // Get the buddies list
163        m_sources = SourceList::instance()->sources( true );
164        qSort( m_sources.begin(), m_sources.end(), sourcesLessThan );
165
166        if ( m_sources_sigmap != 0 )
167            m_sources_sigmap->deleteLater();
168        m_sources_sigmap = new QSignalMapper( this );
169
170        QMenu* sourcesMenu = addMenu( ImageRegistry::instance()->icon( RESPATH "images/share.svg" ), tr( "Send to &Friend" ) );
171        for ( int i = 0; i < m_sources.length(); ++i )
172        {
173            QAction* action = new QAction( m_sources.at( i )->friendlyName(), this );
174            sourcesMenu->addAction( action );
175            m_sources_sigmap->setMapping( action, i );
176            connect( action, SIGNAL( triggered() ), m_sources_sigmap, SLOT( map() ) );
177        }
178        connect( m_sources_sigmap, SIGNAL( mapped( int ) ), this, SLOT( sendToSource( int ) ) );
179    }
180
181    if ( m_supportedActions & ActionStopAfter && itemCount() == 1 )
182    {
183        if ( AudioEngine::instance()->stopAfterTrack() == queries.first() )
184            m_sigmap->setMapping( addAction( tr( "Continue Playback after this &Track" ) ), ActionStopAfter );
185        else
186            m_sigmap->setMapping( addAction( tr( "Stop Playback after this &Track" ) ), ActionStopAfter );
187    }
188
189    addSeparator();
190
191    if ( m_supportedActions & ActionLove && itemCount() == 1 )
192    {
193        m_loveAction = addAction( tr( "&Love" ) );
194        m_sigmap->setMapping( m_loveAction, ActionLove );
195
196        connect( queries.first()->track().data(), SIGNAL( socialActionsLoaded() ), SLOT( onSocialActionsLoaded() ) );
197        onSocialActionsLoaded();
198    }
199
200    addSeparator();
201
202    if ( m_supportedActions & ActionPage && itemCount() == 1 )
203    {
204        // Ampersands need to be escaped as they indicate a keyboard shortcut
205        const QString track = m_queries.first()->track()->track().replace( QString( "&" ), QString( "&&" ) );
206        m_sigmap->setMapping( addAction( ImageRegistry::instance()->icon( RESPATH "images/track-icon.svg" ),
207                                         tr( "View Similar Tracks to \"%1\"" ).arg( track ) ), ActionTrackPage );
208        if ( !m_queries.first()->track()->album().isEmpty() )
209        {
210            const QString album = m_queries.first()->track()->album().replace( QString( "&" ), QString( "&&" ) );
211            m_sigmap->setMapping( addAction( ImageRegistry::instance()->icon( RESPATH "images/album-icon.svg" ),
212                                             tr( "Go to \"%1\"" ).arg( album ) ), ActionAlbumPage );
213        }
214        const QString artist = m_queries.first()->track()->artist().replace( QString( "&" ), QString( "&&" ) );
215        m_sigmap->setMapping( addAction( ImageRegistry::instance()->icon( RESPATH "images/artist-icon.svg" ),
216                                         tr( "Go to \"%1\"" ).arg( artist ) ), ActionArtistPage );
217    }
218
219    addSeparator();
220
221    if ( m_supportedActions & ActionCopyLink && itemCount() == 1 )
222    {
223        m_sigmap->setMapping( addAction( tr( "&Copy Track Link" ) ), ActionCopyLink );
224    }
225
226    if ( m_supportedActions & ActionOpenFileManager &&
227         queries.length() == 1 &&
228         queries.first()->numResults() &&
229         queries.first()->results().first()->isLocal() )
230    {
231        m_sigmap->setMapping( addAction( ImageRegistry::instance()->icon( RESPATH "images/folder.svg" ),
232                                         tr( "Open Folder in File Manager..." ) ), ActionOpenFileManager );
233    }
234
235    if ( m_supportedActions & ActionEditMetadata && itemCount() == 1 )
236    {
237        m_sigmap->setMapping( addAction( tr( "Properties..." ) ), ActionEditMetadata );
238    }
239
240    addSeparator();
241
242    if ( m_supportedActions & ActionMarkListened )
243    {
244        bool thereAreUnlistenedTracks = false;
245        foreach ( const Tomahawk::query_ptr& query, m_queries )
246        {
247            if ( !query->queryTrack()->isListened() )
248            {
249                thereAreUnlistenedTracks = true;
250                break;
251            }
252        }
253
254        if ( thereAreUnlistenedTracks )
255            m_sigmap->setMapping( addAction( tr( "Mark as &Listened" ) ), ActionMarkListened );
256    }
257
258    addSeparator();
259
260    if ( m_supportedActions & ActionDelete )
261        m_sigmap->setMapping( addAction( queries.count() > 1 ? tr( "&Remove Items" ) : tr( "&Remove Item" ) ), ActionDelete );
262
263    foreach ( QAction* action, actions() )
264    {
265        connect( action, SIGNAL( triggered() ), m_sigmap, SLOT( map() ) );
266    }
267}
268
269
270void
271ContextMenu::setQuery( const Tomahawk::query_ptr& query )
272{
273    if ( query.isNull() )
274        return;
275
276    QList<query_ptr> queries;
277    queries << query;
278    setQueries( queries );
279}
280
281
282void
283ContextMenu::setAlbums( const QList<Tomahawk::album_ptr>& albums )
284{
285    if ( albums.isEmpty() )
286        return;
287
288    QMenu::clear();
289    m_albums.clear();
290    m_albums << albums;
291
292    if ( m_supportedActions & ActionQueue )
293        m_sigmap->setMapping( addAction( tr( "Add to &Queue" ) ), ActionQueue );
294
295    addSeparator();
296
297    if ( m_supportedActions & ActionPage && itemCount() == 1 )
298    {
299        const QString album = m_albums.first()->name().replace( QString( "&" ), QString( "&&" ) );
300        m_sigmap->setMapping( addAction( ImageRegistry::instance()->icon( RESPATH "images/album-icon.svg" ),
301                                         tr( "&Go to \"%1\"" ).arg( album ) ), ActionAlbumPage );
302        const QString artist = m_albums.first()->artist()->name().replace( QString( "&" ), QString( "&&" ) );
303        m_sigmap->setMapping( addAction( ImageRegistry::instance()->icon( RESPATH "images/artist-icon.svg" ),
304                                         tr( "Go to \"%1\"" ).arg( artist ) ), ActionArtistPage );
305    }
306
307    //m_sigmap->setMapping( addAction( tr( "&Add to Playlist" ) ), ActionAddToPlaylist );
308
309    addSeparator();
310
311    if ( m_supportedActions & ActionCopyLink && itemCount() == 1 )
312        m_sigmap->setMapping( addAction( tr( "Copy Album &Link" ) ), ActionCopyLink );
313
314    foreach ( QAction* action, actions() )
315    {
316        connect( action, SIGNAL( triggered() ), m_sigmap, SLOT( map() ) );
317    }
318}
319
320
321void
322ContextMenu::setAlbum( const Tomahawk::album_ptr& album )
323{
324    QList<album_ptr> albums;
325    albums << album;
326    setAlbums( albums );
327}
328
329
330void
331ContextMenu::setArtists( const QList<Tomahawk::artist_ptr>& artists )
332{
333    if ( artists.isEmpty() )
334        return;
335
336    QMenu::clear();
337    m_artists.clear();
338    m_artists << artists;
339
340/*    if ( m_supportedActions & ActionPlay && itemCount() == 1 )
341        m_sigmap->setMapping( addAction( tr( "Show &Artist Page" ) ), ActionPlay );*/
342
343    if ( m_supportedActions & ActionQueue )
344        m_sigmap->setMapping( addAction( tr( "Add to &Queue" ) ), ActionQueue );
345
346    addSeparator();
347
348    if ( m_supportedActions & ActionPage && itemCount() == 1 )
349    {
350        const QString artist = m_artists.first()->name().replace( QString( "&" ), QString( "&&" ) );
351        m_sigmap->setMapping( addAction( ImageRegistry::instance()->icon( RESPATH "images/artist-icon.svg" ),
352                                         tr( "&Go to \"%1\"" ).arg( artist ) ), ActionArtistPage );
353    }
354
355    //m_sigmap->setMapping( addAction( tr( "&Add to Playlist" ) ), ActionAddToPlaylist );
356
357    addSeparator();
358
359    if ( m_supportedActions & ActionCopyLink && itemCount() == 1 )
360        m_sigmap->setMapping( addAction( tr( "Copy Artist &Link" ) ), ActionCopyLink );
361
362    foreach ( QAction* action, actions() )
363    {
364        connect( action, SIGNAL( triggered() ), m_sigmap, SLOT( map() ) );
365    }
366}
367
368
369void
370ContextMenu::setArtist( const Tomahawk::artist_ptr& artist )
371{
372    QList<artist_ptr> artists;
373    artists << artist;
374    setArtists( artists );
375}
376
377
378void
379ContextMenu::onTriggered( int action )
380{
381    switch ( action )
382    {
383        case ActionQueue:
384            addToQueue();
385            break;
386
387        case ActionCopyLink:
388            copyLink();
389            break;
390
391        case ActionTrackPage:
392        case ActionArtistPage:
393        case ActionAlbumPage:
394            openPage( (MenuActions)action );
395            break;
396
397        case ActionLove:
398            m_queries.first()->track()->setLoved( !m_queries.first()->track()->loved() );
399            break;
400
401        case ActionStopAfter:
402            if ( m_queries.first()->equals( AudioEngine::instance()->stopAfterTrack() ) )
403                AudioEngine::instance()->setStopAfterTrack( query_ptr() );
404            else
405                AudioEngine::instance()->setStopAfterTrack( m_queries.first() );
406            break;
407
408        case ActionEditMetadata:
409            {
410                MetadataEditor* d = new MetadataEditor( m_queries.first(), m_interface, this );
411                d->show();
412            }
413            break;
414
415        case ActionOpenFileManager:
416            {
417                result_ptr result = m_queries.first()->results().first();
418                QString path = QFileInfo( result->url() ).path();
419                tLog() << Q_FUNC_INFO << "open directory" << path;
420                QDesktopServices::openUrl( path );
421            }
422            break;
423
424        default:
425            emit triggered( action );
426    }
427
428    clear();
429}
430
431
432
433void
434ContextMenu::addToQueue()
435{
436    foreach ( const query_ptr& query, m_queries )
437    {
438        ViewManager::instance()->queue()->view()->trackView()->model()->appendQuery( query );
439    }
440    foreach ( const artist_ptr& artist, m_artists )
441    {
442        ViewManager::instance()->queue()->view()->trackView()->model()->appendArtist( artist );
443    }
444    foreach ( const album_ptr& album, m_albums )
445    {
446        ViewManager::instance()->queue()->view()->trackView()->model()->appendAlbum( album );
447    }
448}
449
450
451void
452ContextMenu::copyLink()
453{
454    if ( !m_queries.isEmpty() )
455    {
456        Utils::LinkGenerator::instance()->copyOpenLink( m_queries.first() );
457    }
458    else if ( !m_albums.isEmpty() )
459    {
460        Utils::LinkGenerator::instance()->copyOpenLink( m_albums.first() );
461    }
462    else if ( !m_artists.isEmpty() )
463    {
464        Utils::LinkGenerator::instance()->copyOpenLink( m_artists.first() );
465    }
466}
467
468
469void
470ContextMenu::openPage( MenuActions action )
471{
472    if ( !m_queries.isEmpty() )
473    {
474        if ( action == ActionTrackPage )
475        {
476            ViewManager::instance()->show( m_queries.first() );
477        }
478        else
479        {
480            if ( action == ActionArtistPage )
481            {
482                ViewManager::instance()->show( m_queries.first()->track()->artistPtr() );
483            }
484            else if ( action == ActionAlbumPage )
485            {
486                ViewManager::instance()->show( m_queries.first()->track()->albumPtr() );
487            }
488        }
489    }
490    else if ( !m_albums.isEmpty() )
491    {
492        if ( action == ActionArtistPage )
493        {
494            ViewManager::instance()->show( m_albums.first()->artist() );
495        }
496        else
497        {
498            ViewManager::instance()->show( m_albums.first() );
499        }
500    }
501    else if ( !m_artists.isEmpty() )
502    {
503        ViewManager::instance()->show( m_artists.first() );
504    }
505}
506
507
508void
509ContextMenu::onSocialActionsLoaded()
510{
511    if ( m_queries.isEmpty() || m_queries.first().isNull() )
512        return;
513
514    if ( m_loveAction && m_queries.first()->track()->loved() )
515    {
516        m_loveAction->setText( tr( "Un-&Love" ) );
517        m_loveAction->setIcon( ImageRegistry::instance()->icon( RESPATH "images/not-loved.svg" ) );
518    }
519    else if ( m_loveAction )
520    {
521        m_loveAction->setText( tr( "&Love" ) );
522        m_loveAction->setIcon( ImageRegistry::instance()->icon( RESPATH "images/loved.svg" ) );
523    }
524}
525
526
527void
528ContextMenu::setPlaylistInterface( const Tomahawk::playlistinterface_ptr& plInterface )
529{
530    m_interface = plInterface;
531}