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 #define DEBUG_PREFIX "ActionClasses"
20
21 #include "ActionClasses.h"
22
23 #include "App.h"
24 #include "EngineController.h"
25 #include "KNotificationBackend.h"
26 #include "MainWindow.h"
27 #include "aboutdialog/OcsData.h"
28 #include "amarokconfig.h"
29 #include <config.h>
30 #include "core/support/Amarok.h"
31 #include "core/support/Debug.h"
32 #include "playlist/PlaylistActions.h"
33 #include "playlist/PlaylistModelStack.h"
34 #include "widgets/Osd.h"
35
36 #include <QKeySequence>
37
38 #include <KAuthorized>
39 #include <KHelpMenu>
40 #include <KLocalizedString>
41 #include <KToolBar>
42 #include <KGlobalAccel>
43
44 extern OcsData ocsData;
45
46 namespace Amarok
47 {
favorNone()48 bool favorNone() { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::Off; }
favorScores()49 bool favorScores() { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::HigherScores; }
favorRatings()50 bool favorRatings() { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::HigherRatings; }
favorLastPlay()51 bool favorLastPlay() { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::LessRecentlyPlayed; }
52
53
entireAlbums()54 bool entireAlbums() { return AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RepeatAlbum ||
55 AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RandomAlbum; }
56
repeatEnabled()57 bool repeatEnabled() { return AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RepeatTrack ||
58 AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RepeatAlbum ||
59 AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RepeatPlaylist; }
60
randomEnabled()61 bool randomEnabled() { return AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RandomTrack ||
62 AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RandomAlbum; }
63 }
64
65 using namespace Amarok;
66
67 KHelpMenu *Menu::s_helpMenu = 0;
68
69 static void
safePlug(KActionCollection * ac,const char * name,QWidget * w)70 safePlug( KActionCollection *ac, const char *name, QWidget *w )
71 {
72 if( ac )
73 {
74 QAction *a = (QAction*) ac->action( name );
75 if( a && w ) w->addAction( a );
76 }
77 }
78
79
80 //////////////////////////////////////////////////////////////////////////////////////////
81 // MenuAction && Menu
82 // KActionMenu doesn't work very well, so we derived our own
83 //////////////////////////////////////////////////////////////////////////////////////////
84
MenuAction(KActionCollection * ac,QObject * parent)85 MenuAction::MenuAction( KActionCollection *ac, QObject *parent )
86 : QAction( parent )
87 {
88 setText(i18n( "Amarok Menu" ));
89 ac->addAction(QStringLiteral("amarok_menu"), this);
90 setShortcutConfigurable ( false ); //FIXME disabled as it doesn't work, should use QCursor::pos()
91 }
92
setShortcutConfigurable(bool b)93 void MenuAction::setShortcutConfigurable(bool b)
94 {
95 setProperty("isShortcutConfigurable", b);
96 }
97
98 Menu* Menu::s_instance = nullptr;
99
Menu(QWidget * parent)100 Menu::Menu( QWidget* parent )
101 : QMenu( parent )
102 {
103 s_instance = this;
104
105 KActionCollection *ac = Amarok::actionCollection();
106
107 safePlug( ac, "repeat", this );
108 safePlug( ac, "random_mode", this );
109
110 addSeparator();
111
112 safePlug( ac, "playlist_playmedia", this );
113
114 addSeparator();
115
116 safePlug( ac, "cover_manager", this );
117 safePlug( ac, "queue_manager", this );
118 safePlug( ac, "script_manager", this );
119
120 addSeparator();
121
122 safePlug( ac, "update_collection", this );
123 safePlug( ac, "rescan_collection", this );
124
125 #ifndef Q_WS_MAC
126 addSeparator();
127
128 safePlug( ac, KStandardAction::name(KStandardAction::ShowMenubar), this );
129 #endif
130
131 addSeparator();
132
133 safePlug( ac, KStandardAction::name(KStandardAction::ConfigureToolbars), this );
134 safePlug( ac, KStandardAction::name(KStandardAction::KeyBindings), this );
135 // safePlug( ac, "options_configure_globals", this ); //we created this one
136 safePlug( ac, KStandardAction::name(KStandardAction::Preferences), this );
137
138 addSeparator();
139
140 addMenu( helpMenu( this ) );
141
142 addSeparator();
143
144 safePlug( ac, KStandardAction::name(KStandardAction::Quit), this );
145 }
146
147 Menu*
instance()148 Menu::instance()
149 {
150 return s_instance ? s_instance : new Menu( The::mainWindow() );
151 }
152
153 QMenu*
helpMenu(QWidget * parent)154 Menu::helpMenu( QWidget *parent ) //STATIC
155 {
156 if ( s_helpMenu == 0 )
157 s_helpMenu = new KHelpMenu( parent, KAboutData::applicationData(), Amarok::actionCollection() );
158
159 QMenu* menu = s_helpMenu->menu();
160
161 // "What's This" isn't currently defined for anything in Amarok, so let's remove it
162 s_helpMenu->action( KHelpMenu::menuWhatsThis )->setVisible( false );
163
164 // Hide the default "About App" dialog, as we replace it with a custom one
165 s_helpMenu->action( KHelpMenu::menuAboutApp )->setVisible( false );
166
167 return menu;
168 }
169
170 //////////////////////////////////////////////////////////////////////////////////////////
171 // PlayPauseAction
172 //////////////////////////////////////////////////////////////////////////////////////////
173
PlayPauseAction(KActionCollection * ac,QObject * parent)174 PlayPauseAction::PlayPauseAction( KActionCollection *ac, QObject *parent )
175 : KToggleAction( parent )
176 {
177
178 ac->addAction( QStringLiteral("play_pause"), this );
179 setText( i18n( "Play/Pause" ) );
180 setShortcut( Qt::Key_Space );
181 KGlobalAccel::setGlobalShortcut( this, QKeySequence() );
182
183 EngineController *engine = The::engineController();
184
185 if( engine->isPaused() )
186 paused();
187 else if( engine->isPlaying() )
188 playing();
189 else
190 stopped();
191
192 connect( this, &PlayPauseAction::triggered,
193 engine, &EngineController::playPause );
194
195 connect( engine, &EngineController::stopped,
196 this, &PlayPauseAction::stopped );
197 connect( engine, &EngineController::paused,
198 this, &PlayPauseAction::paused );
199 connect( engine, &EngineController::trackPlaying,
200 this, &PlayPauseAction::playing );
201 }
202
203 void
stopped()204 PlayPauseAction::stopped()
205 {
206 setChecked( false );
207 setIcon( QIcon::fromTheme(QStringLiteral("media-playback-start-amarok")) );
208 }
209
210 void
paused()211 PlayPauseAction::paused()
212 {
213 setChecked( true );
214 setIcon( QIcon::fromTheme(QStringLiteral("media-playback-start-amarok")) );
215 }
216
217 void
playing()218 PlayPauseAction::playing()
219 {
220 setChecked( false );
221 setIcon( QIcon::fromTheme(QStringLiteral("media-playback-pause-amarok")) );
222 }
223
224
225 //////////////////////////////////////////////////////////////////////////////////////////
226 // ToggleAction
227 //////////////////////////////////////////////////////////////////////////////////////////
228
ToggleAction(const QString & text,void (* f)(bool),KActionCollection * const ac,const char * name,QObject * parent)229 ToggleAction::ToggleAction( const QString &text, void ( *f ) ( bool ), KActionCollection* const ac, const char *name, QObject *parent )
230 : KToggleAction( parent )
231 , m_function( f )
232 {
233 setText(text);
234 ac->addAction(name, this);
235 }
236
setChecked(bool b)237 void ToggleAction::setChecked( bool b )
238 {
239 const bool announce = b != isChecked();
240
241 m_function( b );
242 KToggleAction::setChecked( b );
243 AmarokConfig::self()->save(); //So we don't lose the setting when crashing
244 if( announce ) Q_EMIT toggled( b ); //KToggleAction doesn't do this for us. How gay!
245 }
246
setEnabled(bool b)247 void ToggleAction::setEnabled( bool b )
248 {
249 const bool announce = b != isEnabled();
250
251 KToggleAction::setEnabled( b );
252 AmarokConfig::self()->save(); //So we don't lose the setting when crashing
253 if( announce ) Q_EMIT QAction::triggered( b );
254 }
255
256 //////////////////////////////////////////////////////////////////////////////////////////
257 // SelectAction
258 //////////////////////////////////////////////////////////////////////////////////////////
259
SelectAction(const QString & text,void (* f)(int),KActionCollection * const ac,const char * name,QObject * parent)260 SelectAction::SelectAction( const QString &text, void ( *f ) ( int ), KActionCollection* const ac, const char *name, QObject *parent )
261 : KSelectAction( parent )
262 , m_function( f )
263 {
264 PERF_LOG( "In SelectAction" );
265 setText(text);
266 ac->addAction(name, this);
267 }
268
setCurrentItem(int n)269 void SelectAction::setCurrentItem( int n )
270 {
271 const bool announce = n != currentItem();
272
273 debug() << "setCurrentItem: " << n;
274
275 m_function( n );
276 KSelectAction::setCurrentItem( n );
277 AmarokConfig::self()->save(); //So we don't lose the setting when crashing
278 if( announce ) Q_EMIT triggered( n );
279 }
280
actionTriggered(QAction * a)281 void SelectAction::actionTriggered( QAction *a )
282 {
283 m_function( currentItem() );
284 AmarokConfig::self()->save();
285 KSelectAction::actionTriggered( a );
286 }
287
setEnabled(bool b)288 void SelectAction::setEnabled( bool b )
289 {
290 const bool announce = b != isEnabled();
291
292 KSelectAction::setEnabled( b );
293 AmarokConfig::self()->save(); //So we don't lose the setting when crashing
294 if( announce ) Q_EMIT QAction::triggered( b );
295 }
296
setIcons(QStringList icons)297 void SelectAction::setIcons( QStringList icons )
298 {
299 m_icons = icons;
300 foreach( QAction *a, selectableActionGroup()->actions() )
301 {
302 a->setIcon( QIcon::fromTheme(icons.takeFirst()) );
303 }
304 }
305
icons() const306 QStringList SelectAction::icons() const { return m_icons; }
307
currentIcon() const308 QString SelectAction::currentIcon() const
309 {
310 if( m_icons.count() )
311 return m_icons.at( currentItem() );
312 return QString();
313 }
314
currentText() const315 QString SelectAction::currentText() const {
316 return KSelectAction::currentText() + "<br /><br />" + i18n("Click to change");
317 }
318
319
320 void
setCurrentItem(int n)321 RandomAction::setCurrentItem( int n )
322 {
323 // Porting
324 //if( QAction *a = parentCollection()->action( "favor_tracks" ) )
325 // a->setEnabled( n );
326 SelectAction::setCurrentItem( n );
327 }
328
329 //////////////////////////////////////////////////////////////////////////////////////////
330 // ReplayGainModeAction
331 //////////////////////////////////////////////////////////////////////////////////////////
ReplayGainModeAction(KActionCollection * ac,QObject * parent)332 ReplayGainModeAction::ReplayGainModeAction( KActionCollection *ac, QObject *parent ) :
333 SelectAction( i18n( "&Replay Gain Mode" ), &AmarokConfig::setReplayGainMode, ac, "replay_gain_mode", parent )
334 {
335 setItems( QStringList() << i18nc( "Replay Gain state, as in, disabled", "&Off" )
336 << i18nc( "Item, as in, music", "&Track" )
337 << i18n( "&Album" ) );
338 EngineController *engine = EngineController::instance();
339 Q_ASSERT( engine );
340 if( engine->supportsGainAdjustments() )
341 setCurrentItem( AmarokConfig::replayGainMode() );
342 else
343 {
344 // Note: it would be nice to set a tooltip that would explain why this is disabled
345 // to users, but tooltips aren't shown in menu anyway :-(
346 actions().at( 1 )->setEnabled( false );
347 actions().at( 2 )->setEnabled( false );
348 }
349 }
350
351 //////////////////////////////////////////////////////////////////////////////////////////
352 // BurnMenuAction
353 //////////////////////////////////////////////////////////////////////////////////////////
BurnMenuAction(KActionCollection * ac,QObject * parent)354 BurnMenuAction::BurnMenuAction( KActionCollection *ac, QObject *parent )
355 : QAction( parent )
356 {
357 setText(i18n( "Burn" ));
358 ac->addAction(QStringLiteral("burn_menu"), this);
359 }
360
361 QWidget*
createWidget(QWidget * w)362 BurnMenuAction::createWidget( QWidget *w )
363 {
364 KToolBar *bar = dynamic_cast<KToolBar*>(w);
365
366 if( bar && KAuthorized::authorizeAction( objectName() ) )
367 {
368 //const int id = QAction::getToolButtonID();
369
370 //addContainer( bar, id );
371 w->addAction( this );
372 //connect( bar, &KToolBar::destroyed, this, &BurnMenuAction::slotDestroyed );
373
374 //bar->insertButton( QString::null, id, true, i18n( "Burn" ), index );
375
376 //KToolBarButton* button = bar->getButton( id );
377 //button->setPopup( Amarok::BurnMenu::instance() );
378 //button->setObjectName( "toolbutton_burn_menu" );
379 //button->setIcon( "k3b" );
380
381 //return associatedWidgets().count() - 1;
382 return 0;
383 }
384 //else return -1;
385 else return 0;
386 }
387
388
389 BurnMenu* BurnMenu::s_instance = nullptr;
390
BurnMenu(QWidget * parent)391 BurnMenu::BurnMenu( QWidget* parent )
392 : QMenu( parent )
393 {
394 s_instance = this;
395
396 addAction( i18n("Current Playlist"), this, &BurnMenu::slotBurnCurrentPlaylist );
397 addAction( i18n("Selected Tracks"), this, &BurnMenu::slotBurnSelectedTracks );
398 //TODO add "album" and "all tracks by artist"
399 }
400
401 QMenu*
instance()402 BurnMenu::instance()
403 {
404 return s_instance ? s_instance : new BurnMenu( The::mainWindow() );
405 }
406
407 void
slotBurnCurrentPlaylist()408 BurnMenu::slotBurnCurrentPlaylist() //SLOT
409 {
410 //K3bExporter::instance()->exportCurrentPlaylist();
411 }
412
413 void
slotBurnSelectedTracks()414 BurnMenu::slotBurnSelectedTracks() //SLOT
415 {
416 //K3bExporter::instance()->exportSelectedTracks();
417 }
418
419
420 //////////////////////////////////////////////////////////////////////////////////////////
421 // StopAction
422 //////////////////////////////////////////////////////////////////////////////////////////
423
StopAction(KActionCollection * ac,QObject * parent)424 StopAction::StopAction( KActionCollection *ac, QObject *parent )
425 : QAction( parent )
426 {
427 ac->addAction( QStringLiteral("stop"), this );
428 setText( i18n( "Stop" ) );
429 setIcon( QIcon::fromTheme(QStringLiteral("media-playback-stop-amarok")) );
430 KGlobalAccel::setGlobalShortcut(this, QKeySequence() );
431 connect( this, &StopAction::triggered, this, &StopAction::stop );
432
433 EngineController *engine = The::engineController();
434
435 if( engine->isStopped() )
436 stopped();
437 else
438 playing();
439
440 connect( engine, &EngineController::stopped,
441 this, &StopAction::stopped );
442 connect( engine, &EngineController::trackPlaying,
443 this, &StopAction::playing );
444 }
445
446 void
stopped()447 StopAction::stopped()
448 {
449 setEnabled( false );
450 }
451
452 void
playing()453 StopAction::playing()
454 {
455 setEnabled( true );
456 }
457
458 void
stop()459 StopAction::stop()
460 {
461 The::engineController()->stop();
462 }
463
464 //////////////////////////////////////////////////////////////////////////////////////////
465 // StopPlayingAfterCurrentTrackAction
466 //////////////////////////////////////////////////////////////////////////////////////////
467
StopPlayingAfterCurrentTrackAction(KActionCollection * ac,QObject * parent)468 StopPlayingAfterCurrentTrackAction::StopPlayingAfterCurrentTrackAction( KActionCollection *ac, QObject *parent )
469 : QAction( parent )
470 {
471 ac->addAction( QStringLiteral("stop_after_current"), this );
472 setText( i18n( "Stop after current Track" ) );
473 setIcon( QIcon::fromTheme(QStringLiteral("media-playback-stop-amarok")) );
474 KGlobalAccel::setGlobalShortcut(this, QKeySequence( Qt::META + Qt::SHIFT + Qt::Key_V ) );
475 connect( this, &StopPlayingAfterCurrentTrackAction::triggered, this, &StopPlayingAfterCurrentTrackAction::stopPlayingAfterCurrentTrack );
476 }
477
478 void
stopPlayingAfterCurrentTrack()479 StopPlayingAfterCurrentTrackAction::stopPlayingAfterCurrentTrack()
480 {
481 QString text;
482
483 quint64 activeTrack = Playlist::ModelStack::instance()->bottom()->activeId();
484 if( activeTrack )
485 {
486 if( The::playlistActions()->willStopAfterTrack( activeTrack ) )
487 {
488 The::playlistActions()->stopAfterPlayingTrack( 0 );
489 text = i18n( "Stop after current track: Off" );
490 }
491 else
492 {
493 The::playlistActions()->stopAfterPlayingTrack( activeTrack );
494 text = i18n( "Stop after current track: On" );
495 }
496 }
497 else
498 text = i18n( "No track playing" );
499
500 Amarok::OSD::instance()->OSDWidget::show( text );
501 if( Amarok::KNotificationBackend::instance()->isEnabled() )
502 Amarok::KNotificationBackend::instance()->show( i18n( "Amarok" ), text );
503 }
504