1
/****************************************************************************************
2
 * Copyright (c) 2004 Max Howell <max.howell@methylblue.com>                            *
3
 * Copyright (c) 2008 Mark Kretschmann <kretschmann@kde.org>                            *
4
 * Copyright (c) 2009 Artur Szymiec <artur.szymiec@gmail.com>                           *
5
 *                                                                                      *
6
 * This program is free software; you can redistribute it and/or modify it under        *
7
 * the terms of the GNU General Public License as published by the Free Software        *
8
 * Foundation; either version 2 of the License, or (at your option) any later           *
9
 * version.                                                                             *
10
 *                                                                                      *
11
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
12
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
13
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
14
 *                                                                                      *
15
 * You should have received a copy of the GNU General Public License along with         *
16
 * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
17
 ****************************************************************************************/
18
19
#include "ActionClasses.h"
20
21
#include <config-amarok.h>
22
23
#include "core/support/Amarok.h"
24
#include "App.h"
25
#include "core/support/Debug.h"
26
#include "EngineController.h"
27
#include "MainWindow.h"
28
#include "amarokconfig.h"
29
#include "covermanager/CoverManager.h"
30
#include "playlist/PlaylistActions.h"
31
#include "playlist/PlaylistModelStack.h"
32
33
#include <KAuthorized>
34
#include <KHelpMenu>
35
#include <KLocale>
36
#include <KToolBar>
37
#include <Osd.h>
38
39
40
extern KAboutData aboutData;
41
extern OcsData ocsData;
42
43
namespace Amarok
44
{
45
    bool favorNone()      { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::Off; }
46
    bool favorScores()    { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::HigherScores; }
47
    bool favorRatings()   { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::HigherRatings; }
48
    bool favorLastPlay()  { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::LessRecentlyPlayed; }
49
50
51
    bool entireAlbums()   { return AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RepeatAlbum ||
52
                                   AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RandomAlbum; }
53
54
    bool repeatEnabled()  { return AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RepeatTrack ||
55
                                   AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RepeatAlbum  ||
56
                                   AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RepeatPlaylist; }
57
58
    bool randomEnabled()  { return AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RandomTrack ||
59
                                   AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RandomAlbum; }
60
}
61
62
using namespace Amarok;
63
64
KHelpMenu *Menu::s_helpMenu = 0;
65
66
static void
67
safePlug( KActionCollection *ac, const char *name, QWidget *w )
68
{
69
    if( ac )
70
    {
71
        KAction *a = (KAction*) ac->action( name );
72
        if( a && w ) w->addAction( a );
73
    }
74
}
75
76
77
//////////////////////////////////////////////////////////////////////////////////////////
78
// MenuAction && Menu
79
// KActionMenu doesn't work very well, so we derived our own
80
//////////////////////////////////////////////////////////////////////////////////////////
81
82
MenuAction::MenuAction( KActionCollection *ac, QObject *parent )
83
  : KAction( parent )
84
{
85
    setText(i18n( "Amarok Menu" ));
86
    ac->addAction("amarok_menu", this);
87
    setShortcutConfigurable ( false ); //FIXME disabled as it doesn't work, should use QCursor::pos()
88
}
89
90
91
Menu* Menu::s_instance = 0;
92
93
Menu::Menu( QWidget* parent )
94
    : KMenu( parent )
95
{
96
    s_instance = this;
97
98
    KActionCollection *ac = Amarok::actionCollection();
99
100
    safePlug( ac, "repeat", this );
101
    safePlug( ac, "random_mode", this );
102
103
    addSeparator();
104
105
    safePlug( ac, "playlist_playmedia", this );
106
107
    addSeparator();
108
109
    safePlug( ac, "cover_manager", this );
110
    safePlug( ac, "queue_manager", this );
111
    safePlug( ac, "script_manager", this );
112
113
    addSeparator();
114
115
    safePlug( ac, "update_collection", this );
116
    safePlug( ac, "rescan_collection", this );
117
118
#ifndef Q_WS_MAC
119
    addSeparator();
120
121
    safePlug( ac, KStandardAction::name(KStandardAction::ShowMenubar), this );
122
#endif
123
124
    addSeparator();
125
126
    safePlug( ac, KStandardAction::name(KStandardAction::ConfigureToolbars), this );
127
    safePlug( ac, KStandardAction::name(KStandardAction::KeyBindings), this );
128
//    safePlug( ac, "options_configure_globals", this ); //we created this one
129
    safePlug( ac, KStandardAction::name(KStandardAction::Preferences), this );
130
131
    addSeparator();
132
133
    addMenu( helpMenu( this ) );
134
135
    addSeparator();
136
137
    safePlug( ac, KStandardAction::name(KStandardAction::Quit), this );
138
}
139
140
Menu*
141
Menu::instance()
142
{
143
    return s_instance ? s_instance : new Menu( The::mainWindow() );
144
}
145
146
KMenu*
147
Menu::helpMenu( QWidget *parent ) //STATIC
148
{
149
    if ( s_helpMenu == 0 )
150
        s_helpMenu = new KHelpMenu( parent, &aboutData, Amarok::actionCollection() );
151
152
    KMenu* menu = s_helpMenu->menu();
153
154
    // NOTE: We hide "Report Bug..." because we need to add it on our own to name the dialog
155
    // so it can be blacklisted from LikeBack.
156
    s_helpMenu->action( KHelpMenu::menuReportBug )->setVisible( false );
157
158
    // NOTE: "What's This" isn't currently defined for anything in Amarok, so let's remove that too
159
    s_helpMenu->action( KHelpMenu::menuWhatsThis )->setVisible( false );
160
161
    s_helpMenu->action( KHelpMenu::menuAboutApp )->setVisible( false );
162
163
    return menu;
164
}
165
166
//////////////////////////////////////////////////////////////////////////////////////////
167
// PlayPauseAction
168
//////////////////////////////////////////////////////////////////////////////////////////
169
170
PlayPauseAction::PlayPauseAction( KActionCollection *ac, QObject *parent )
171
        : KToggleAction( parent )
172
        , Engine::EngineObserver( The::engineController() )
173
{
174
    ac->addAction( "play_pause", this );
175
    setText( i18n( "Play/Pause" ) );
176
    setShortcut( Qt::Key_Space );
177
    setGlobalShortcut( KShortcut( Qt::Key_MediaPlay ) );
178
    PERF_LOG( "PlayPauseAction: before engineStateChanged" )
179
    engineStateChanged( The::engineController()->state() );
180
    PERF_LOG( "PlayPauseAction: after engineStateChanged" )
181
182
    connect( this, SIGNAL(triggered()), The::engineController(), SLOT(playPause()) );
183
}
184
185
void
186
PlayPauseAction::engineStateChanged( Phonon::State state,  Phonon::State oldState )
187
{
188
    Q_UNUSED( oldState)
189
190
    switch( state )
191
    {
192
    case Phonon::PlayingState:
193
        setChecked( false );
194
        setIcon( KIcon("media-playback-pause-amarok") );
195
        break;
196
    case Phonon::PausedState:
197
        setChecked( true );
198
        setIcon( KIcon("media-playback-start-amarok") );
199
        break;
200
    case Phonon::StoppedState:
201
    case Phonon::LoadingState:
202
        setChecked( false );
203
        setIcon( KIcon("media-playback-start-amarok") );
204
        break;
205
    case Phonon::ErrorState:
206
    case Phonon::BufferingState:
207
        break;
208
    }
209
}
210
211
//////////////////////////////////////////////////////////////////////////////////////////
212
// ToggleAction
213
//////////////////////////////////////////////////////////////////////////////////////////
214
215
ToggleAction::ToggleAction( const QString &text, void ( *f ) ( bool ), KActionCollection* const ac, const char *name, QObject *parent )
216
        : KToggleAction( parent )
217
        , m_function( f )
218
{
219
    setText(text);
220
    ac->addAction(name, this);
221
}
222
223
void ToggleAction::setChecked( bool b )
224
{
225
    const bool announce = b != isChecked();
226
227
    m_function( b );
228
    KToggleAction::setChecked( b );
229
    AmarokConfig::self()->writeConfig(); //So we don't lose the setting when crashing
230
    if( announce ) emit toggled( b ); //KToggleAction doesn't do this for us. How gay!
231
}
232
233
void ToggleAction::setEnabled( bool b )
234
{
235
    const bool announce = b != isEnabled();
236
237
    KToggleAction::setEnabled( b );
238
    AmarokConfig::self()->writeConfig(); //So we don't lose the setting when crashing
239
    if( announce ) emit QAction::triggered( b );
240
}
241
242
//////////////////////////////////////////////////////////////////////////////////////////
243
// SelectAction
244
//////////////////////////////////////////////////////////////////////////////////////////
245
246
SelectAction::SelectAction( const QString &text, void ( *f ) ( int ), KActionCollection* const ac, const char *name, QObject *parent )
247
        : KSelectAction( parent )
248
        , m_function( f )
249
{
250
    PERF_LOG( "In SelectAction" );
251
    setText(text);
252
    ac->addAction(name, this);
253
}
254
255
void SelectAction::setCurrentItem( int n )
256
{
257
    const bool announce = n != currentItem();
258
259
    debug() << "setCurrentItem: " << n;
260
261
    m_function( n );
262
    KSelectAction::setCurrentItem( n );
263
    AmarokConfig::self()->writeConfig(); //So we don't lose the setting when crashing
264
    if( announce ) emit triggered( n );
265
}
266
267
void SelectAction::actionTriggered( QAction *a )
268
{
269
    m_function( currentItem() );
270
    AmarokConfig::self()->writeConfig();
271
    KSelectAction::actionTriggered( a );
272
}
273
274
void SelectAction::setEnabled( bool b )
275
{
276
    const bool announce = b != isEnabled();
277
278
    KSelectAction::setEnabled( b );
279
    AmarokConfig::self()->writeConfig(); //So we don't lose the setting when crashing
280
    if( announce ) emit QAction::triggered( b );
281
}
282
283
void SelectAction::setIcons( QStringList icons )
284
{
285
    m_icons = icons;
286
    foreach( QAction *a, selectableActionGroup()->actions() )
287
    {
288
        a->setIcon( KIcon(icons.takeFirst()) );
289
    }
290
}
291
292
QStringList SelectAction::icons() const { return m_icons; }
293
294
QString SelectAction::currentIcon() const
295
{
296
    if( m_icons.count() )
297
        return m_icons.at( currentItem() );
298
    return QString();
299
}
300
301
QString SelectAction::currentText() const {
302
    return KSelectAction::currentText() + "<br /><br />" + i18n("Click to change");
303
}
304
305
306
void
307
RandomAction::setCurrentItem( int n )
308
{
309
    // Porting
310
    //if( KAction *a = parentCollection()->action( "favor_tracks" ) )
311
    //    a->setEnabled( n );
312
    SelectAction::setCurrentItem( n );
313
}
314
315
//////////////////////////////////////////////////////////////////////////////////////////
316
// ReplayGainModeAction
317
//////////////////////////////////////////////////////////////////////////////////////////
318
ReplayGainModeAction::ReplayGainModeAction( KActionCollection *ac, QObject *parent ) :
319
    SelectAction( i18n( "&Replay Gain Mode" ), &AmarokConfig::setReplayGainMode, ac, "replay_gain_mode", parent )
320
{
321
    setItems( QStringList() << i18nc( "Replay Gain state, as in, disabled", "&Off" ) << i18nc( "Item, as in, music", "&Track" )
322
                            << i18n( "&Album" ) );
323
    //setIcons( QStringList() << "media-playlist-replaygain-off-amarok" << "media-track-replaygain-amarok" << "media-album-replaygain-amarok" );
324
    setCurrentItem( AmarokConfig::replayGainMode() );
325
}
326
327
//////////////////////////////////////////////////////////////////////////////////////////
328
// EqualizerAction
329
//////////////////////////////////////////////////////////////////////////////////////////
330
EqualizerAction::EqualizerAction( KActionCollection *ac, QObject *parent ) :
331
    SelectAction( i18n( "&Equalizer" ), &AmarokConfig::setEqualizerMode, ac, "equalizer_mode", parent )
332
{
333
    // build a new preset list in menu
334
    newList();
335
    // set selected preset from config
336
    updateContent();
337
    connect( this, SIGNAL( triggered( int ) ), this, SLOT( actTrigg( int ) ) );
338
}
339
340
void
341
EqualizerAction::updateContent() //SLOT
342
{
343
    // this slot update the content of equalizer main window menu
344
    // according to config blocking is neccessary to prevent
345
    // circluar loop between menu and config dialog
346
    blockSignals( true );
347
    setCurrentItem( AmarokConfig::equalizerMode() );
348
    blockSignals( false );
349
}
350
351
void
352
EqualizerAction::newList() //SLOT
353
{
354
    // this slot build a new list of presets in equalizer menu
355
    // or disable this menu if equalizer is not supported
356
    if( !The::engineController()->isEqSupported() )
357
    {
358
        setEnabled( false );
359
        setToolTip( i18n("Your current setup does not support the equalizer feature") );
360
        return;
361
    }
362
    setEnabled( true );
363
    setToolTip( QString() );
364
    setItems( QStringList() << i18nc( "Equalizer state, as in, disabled", "&Off" ) << eqGlobalList() );
365
}
366
367
void
368
EqualizerAction::actTrigg( int index ) //SLOT
369
{
370
    if( The::engineController()->isEqSupported() )
371
    {
372
        AmarokConfig::setEqualizerGains( eqCfgGetPresetVal( index - 1 ) );
373
        The::engineController()->eqUpdate();
374
    }
375
}
376
377
QStringList
378
EqualizerAction::eqGlobalList()
379
{
380
    // Prepare a global list with duplicates removed
381
    QStringList mGlobalList;
382
    mGlobalList += AmarokConfig::defEqualizerPresetsNames();
383
    foreach( const QString &mUsrName, AmarokConfig::equalizerPresetsNames() )
384
    {
385
        if( mGlobalList.indexOf( mUsrName ) < 0 )
386
            mGlobalList.append( mUsrName );
387
    }
388
    return mGlobalList;
389
}
390
391
QList<int>
392
EqualizerAction::eqCfgGetPresetVal( int mPresetNo )
393
{
394
    QList<int> mPresetVal;
395
    if( mPresetNo > eqGlobalList().count() ||  mPresetNo < 0 )
396
        return mPresetVal;
397
    QString mPresetName = eqGlobalList().at(mPresetNo);
398
    int idUsr = AmarokConfig::equalizerPresetsNames().indexOf( mPresetName );
399
    int idDef = AmarokConfig::defEqualizerPresetsNames().indexOf( mPresetName );
400
401
    if( idUsr >= 0 )
402
        mPresetVal = AmarokConfig::equalizerPresestValues().mid( idUsr*11,11 );
403
    else if( idDef >= 0)
404
        mPresetVal = AmarokConfig::defEqualizerPresestValues().mid( idDef*11,11 );
405
406
    return mPresetVal;
407
}
408
409
//////////////////////////////////////////////////////////////////////////////////////////
410
// BurnMenuAction
411
//////////////////////////////////////////////////////////////////////////////////////////
412
BurnMenuAction::BurnMenuAction( KActionCollection *ac, QObject *parent )
413
  : KAction( parent )
414
{
415
    setText(i18n( "Burn" ));
416
    ac->addAction("burn_menu", this);
417
}
418
419
QWidget*
420
BurnMenuAction::createWidget( QWidget *w )
421
{
422
    KToolBar *bar = dynamic_cast<KToolBar*>(w);
423
424
    if( bar && KAuthorized::authorizeKAction( objectName() ) )
425
    {
426
        //const int id = KAction::getToolButtonID();
427
428
        //addContainer( bar, id );
429
        w->addAction( this );
430
        connect( bar, SIGNAL( destroyed() ), SLOT( slotDestroyed() ) );
431
432
        //bar->insertButton( QString::null, id, true, i18n( "Burn" ), index );
433
434
        //KToolBarButton* button = bar->getButton( id );
435
        //button->setPopup( Amarok::BurnMenu::instance() );
436
        //button->setObjectName( "toolbutton_burn_menu" );
437
        //button->setIcon( "k3b" );
438
439
        //return associatedWidgets().count() - 1;
440
        return 0;
441
    }
442
    //else return -1;
443
    else return 0;
444
}
445
446
447
BurnMenu* BurnMenu::s_instance = 0;
448
449
BurnMenu::BurnMenu( QWidget* parent )
450
    : KMenu( parent )
451
{
452
    s_instance = this;
453
454
    addAction( i18n("Current Playlist"), this, SLOT( slotBurnCurrentPlaylist() ) );
455
    addAction( i18n("Selected Tracks"), this, SLOT( slotBurnSelectedTracks() ) );
456
    //TODO add "album" and "all tracks by artist"
457
}
458
459
KMenu*
460
BurnMenu::instance()
461
{
462
    return s_instance ? s_instance : new BurnMenu( The::mainWindow() );
463
}
464
465
void
466
BurnMenu::slotBurnCurrentPlaylist() //SLOT
467
{
468
    //K3bExporter::instance()->exportCurrentPlaylist();
469
}
470
471
void
472
BurnMenu::slotBurnSelectedTracks() //SLOT
473
{
474
    //K3bExporter::instance()->exportSelectedTracks();
475
}
476
477
478
//////////////////////////////////////////////////////////////////////////////////////////
479
// StopAction
480
//////////////////////////////////////////////////////////////////////////////////////////
481
482
StopAction::StopAction( KActionCollection *ac, QObject *parent )
483
  : KAction( parent )
484
  , Engine::EngineObserver( The::engineController() )
485
{
486
    ac->addAction( "stop", this );
487
    setText( i18n( "Stop" ) );
488
    setIcon( KIcon("media-playback-stop-amarok") );
489
    setGlobalShortcut( KShortcut( Qt::Key_MediaStop ) );
490
    connect( this, SIGNAL( triggered() ), this, SLOT( stop() ) );
491
    setEnabled( false );  // Disable action at startup
492
}
493
494
void
495
StopAction::engineStateChanged( Phonon::State state,  Phonon::State /*oldState*/ )
496
{
497
    switch( state ) {
498
    case Phonon::PlayingState:
499
    case Phonon::PausedState:
500
        setEnabled( true );
501
        break;
502
    case Phonon::StoppedState:
503
    case Phonon::LoadingState:
504
        setDisabled( true );
505
        break;
506
    case Phonon::ErrorState:
507
    case Phonon::BufferingState:
508
        break;
509
    }
510
}
511
512
void
513
StopAction::stop()
514
{
515
    if( The::playlistActions()->stopAfterMode() )
516
    {
517
        The::playlistActions()->setStopAfterMode( Playlist::StopNever );
518
        The::playlistActions()->setTrackToBeLast( 0 );
519
        The::playlistActions()->repaintPlaylist();
520
    }
521
    The::engineController()->stop();
522
}
523
524
525
//////////////////////////////////////////////////////////////////////////////////////////
526
// StopPlayingAfterCurrentTrackAction
527
//////////////////////////////////////////////////////////////////////////////////////////
528
529
StopPlayingAfterCurrentTrackAction::StopPlayingAfterCurrentTrackAction( KActionCollection *ac, QObject *parent )
530
: KAction( parent )
531
, Engine::EngineObserver( The::engineController() )
532
{
533
    ac->addAction( "stop_after_current", this );
534
    setText( i18n( "Stop after current Track" ) );
535
    setIcon( KIcon("media-playback-stop-amarok") );
536
    setGlobalShortcut( KShortcut( Qt::META + Qt::SHIFT + Qt::Key_V ) );
537
    connect( this, SIGNAL( triggered() ), SLOT( stopPlayingAfterCurrentTrack() ) );
538
    setEnabled( false );  // Disable action at startup
539
}
540
541
void
542
StopPlayingAfterCurrentTrackAction::stopPlayingAfterCurrentTrack()
543
{
544
    if ( !The::playlistActions()->willStopAfterTrack( Playlist::ModelStack::instance()->bottom()->activeId() ) )
545
    {
546
        The::playlistActions()->setStopAfterMode( Playlist::StopAfterCurrent );
547
        The::playlistActions()->setTrackToBeLast( Playlist::ModelStack::instance()->bottom()->activeId() );
548
        The::playlistActions()->repaintPlaylist();
549
        Amarok::OSD::instance()->setImage( QImage( KIconLoader::global()->iconPath( "amarok", -KIconLoader::SizeHuge ) ) );
550
        Amarok::OSD::instance()->OSDWidget::show( i18n( "Stop after current track: On" ) );
551
    } else {
552
        The::playlistActions()->setStopAfterMode( Playlist::StopNever );
553
        The::playlistActions()->setTrackToBeLast( 0 );
554
        The::playlistActions()->repaintPlaylist();
555
        Amarok::OSD::instance()->setImage( QImage( KIconLoader::global()->iconPath( "amarok", -KIconLoader::SizeHuge ) ) );
556
        Amarok::OSD::instance()->OSDWidget::show( i18n( "Stop after current track: Off" ) );
557
    }
558
}
559
560
void
561
StopPlayingAfterCurrentTrackAction::engineStateChanged( Phonon::State state,  Phonon::State /*oldState*/ )
562
{
563
    switch( state ) {
564
        case Phonon::PlayingState:
565
        case Phonon::PausedState:
566
        case Phonon::StoppedState:
567
            setEnabled( true );
568
            break;
569
        case Phonon::LoadingState:
570
            setDisabled( true );
571
            break;
572
        case Phonon::ErrorState:
573
        case Phonon::BufferingState:
574
            break;
575
    }
576
}
577
578
#include "ActionClasses.moc"