File indexing completed on 2024-04-21 04:47:48

0001 /****************************************************************************************
0002  * Copyright (c) 2004 Max Howell <max.howell@methylblue.com>                            *
0003  * Copyright (c) 2008 Mark Kretschmann <kretschmann@kde.org>                            *
0004  * Copyright (c) 2009 Artur Szymiec <artur.szymiec@gmail.com>                           *
0005  *                                                                                      *
0006  * This program is free software; you can redistribute it and/or modify it under        *
0007  * the terms of the GNU General Public License as published by the Free Software        *
0008  * Foundation; either version 2 of the License, or (at your option) any later           *
0009  * version.                                                                             *
0010  *                                                                                      *
0011  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0012  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0013  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0014  *                                                                                      *
0015  * You should have received a copy of the GNU General Public License along with         *
0016  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0017  ****************************************************************************************/
0018 
0019 #define DEBUG_PREFIX "ActionClasses"
0020 
0021 #include "ActionClasses.h"
0022 
0023 #include "App.h"
0024 #include "EngineController.h"
0025 #include "KNotificationBackend.h"
0026 #include "MainWindow.h"
0027 #include "aboutdialog/OcsData.h"
0028 #include "amarokconfig.h"
0029 #include <config.h>
0030 #include "core/support/Amarok.h"
0031 #include "core/support/Debug.h"
0032 #include "playlist/PlaylistActions.h"
0033 #include "playlist/PlaylistModelStack.h"
0034 #include "widgets/Osd.h"
0035 
0036 #include <QKeySequence>
0037 
0038 #include <KAuthorized>
0039 #include <KHelpMenu>
0040 #include <KLocalizedString>
0041 #include <KToolBar>
0042 #include <KGlobalAccel>
0043 
0044 extern OcsData ocsData;
0045 
0046 namespace Amarok
0047 {
0048     bool favorNone()      { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::Off; }
0049     bool favorScores()    { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::HigherScores; }
0050     bool favorRatings()   { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::HigherRatings; }
0051     bool favorLastPlay()  { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::LessRecentlyPlayed; }
0052 
0053 
0054     bool entireAlbums()   { return AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RepeatAlbum ||
0055                                    AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RandomAlbum; }
0056 
0057     bool repeatEnabled()  { return AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RepeatTrack ||
0058                                    AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RepeatAlbum  ||
0059                                    AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RepeatPlaylist; }
0060 
0061     bool randomEnabled()  { return AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RandomTrack ||
0062                                    AmarokConfig::trackProgression() == AmarokConfig::EnumTrackProgression::RandomAlbum; }
0063 }
0064 
0065 using namespace Amarok;
0066 
0067 KHelpMenu *Menu::s_helpMenu = nullptr;
0068 
0069 static void
0070 safePlug( KActionCollection *ac, const char *name, QWidget *w )
0071 {
0072     if( ac )
0073     {
0074         QAction *a = (QAction*) ac->action( name );
0075         if( a && w ) w->addAction( a );
0076     }
0077 }
0078 
0079 
0080 //////////////////////////////////////////////////////////////////////////////////////////
0081 // MenuAction && Menu
0082 // KActionMenu doesn't work very well, so we derived our own
0083 //////////////////////////////////////////////////////////////////////////////////////////
0084 
0085 MenuAction::MenuAction( KActionCollection *ac, QObject *parent )
0086   : QAction( parent )
0087 {
0088     setText(i18n( "Amarok Menu" ));
0089     ac->addAction(QStringLiteral("amarok_menu"), this);
0090     setShortcutConfigurable ( false ); //FIXME disabled as it doesn't work, should use QCursor::pos()
0091 }
0092 
0093 void MenuAction::setShortcutConfigurable(bool b)
0094 {
0095     setProperty("isShortcutConfigurable", b);
0096 }
0097 
0098 Menu* Menu::s_instance = nullptr;
0099 
0100 Menu::Menu( QWidget* parent )
0101     : QMenu( parent )
0102 {
0103     s_instance = this;
0104 
0105     KActionCollection *ac = Amarok::actionCollection();
0106 
0107     safePlug( ac, "repeat", this );
0108     safePlug( ac, "random_mode", this );
0109 
0110     addSeparator();
0111 
0112     safePlug( ac, "playlist_playmedia", this );
0113 
0114     addSeparator();
0115 
0116     safePlug( ac, "cover_manager", this );
0117     safePlug( ac, "queue_manager", this );
0118     safePlug( ac, "script_manager", this );
0119 
0120     addSeparator();
0121 
0122     safePlug( ac, "update_collection", this );
0123     safePlug( ac, "rescan_collection", this );
0124 
0125 #ifndef Q_WS_MAC
0126     addSeparator();
0127 
0128     safePlug( ac, KStandardAction::name(KStandardAction::ShowMenubar), this );
0129 #endif
0130 
0131     addSeparator();
0132 
0133     safePlug( ac, KStandardAction::name(KStandardAction::ConfigureToolbars), this );
0134     safePlug( ac, KStandardAction::name(KStandardAction::KeyBindings), this );
0135 //    safePlug( ac, "options_configure_globals", this ); //we created this one
0136     safePlug( ac, KStandardAction::name(KStandardAction::Preferences), this );
0137 
0138     addSeparator();
0139 
0140     addMenu( helpMenu( this ) );
0141 
0142     addSeparator();
0143 
0144     safePlug( ac, KStandardAction::name(KStandardAction::Quit), this );
0145 }
0146 
0147 Menu*
0148 Menu::instance()
0149 {
0150     return s_instance ? s_instance : new Menu( The::mainWindow() );
0151 }
0152 
0153 QMenu*
0154 Menu::helpMenu( QWidget *parent ) //STATIC
0155 {
0156     if ( s_helpMenu == nullptr )
0157         s_helpMenu = new KHelpMenu( parent, KAboutData::applicationData(), Amarok::actionCollection() );
0158 
0159     QMenu* menu = s_helpMenu->menu();
0160 
0161     // "What's This" isn't currently defined for anything in Amarok, so let's remove it
0162     s_helpMenu->action( KHelpMenu::menuWhatsThis )->setVisible( false );
0163 
0164     // Hide the default "About App" dialog, as we replace it with a custom one
0165     s_helpMenu->action( KHelpMenu::menuAboutApp )->setVisible( false );
0166 
0167     return menu;
0168 }
0169 
0170 //////////////////////////////////////////////////////////////////////////////////////////
0171 // PlayPauseAction
0172 //////////////////////////////////////////////////////////////////////////////////////////
0173 
0174 PlayPauseAction::PlayPauseAction( KActionCollection *ac, QObject *parent )
0175         : KToggleAction( parent )
0176 {
0177 
0178     ac->addAction( QStringLiteral("play_pause"), this );
0179     setText( i18n( "Play/Pause" ) );
0180     setShortcut( Qt::Key_Space );
0181     KGlobalAccel::setGlobalShortcut( this, QKeySequence() );
0182 
0183     EngineController *engine = The::engineController();
0184 
0185     if( engine->isPaused() )
0186         paused();
0187     else if( engine->isPlaying() )
0188         playing();
0189     else
0190         stopped();
0191 
0192     connect( this, &PlayPauseAction::triggered,
0193              engine, &EngineController::playPause );
0194 
0195     connect( engine, &EngineController::stopped,
0196              this, &PlayPauseAction::stopped );
0197     connect( engine, &EngineController::paused,
0198              this, &PlayPauseAction::paused );
0199     connect( engine, &EngineController::trackPlaying,
0200              this, &PlayPauseAction::playing );
0201 }
0202 
0203 void
0204 PlayPauseAction::stopped()
0205 {
0206     setChecked( false );
0207     setIcon( QIcon::fromTheme(QStringLiteral("media-playback-start-amarok")) );
0208 }
0209 
0210 void
0211 PlayPauseAction::paused()
0212 {
0213     setChecked( true );
0214     setIcon( QIcon::fromTheme(QStringLiteral("media-playback-start-amarok")) );
0215 }
0216 
0217 void
0218 PlayPauseAction::playing()
0219 {
0220     setChecked( false );
0221     setIcon( QIcon::fromTheme(QStringLiteral("media-playback-pause-amarok")) );
0222 }
0223 
0224 
0225 //////////////////////////////////////////////////////////////////////////////////////////
0226 // ToggleAction
0227 //////////////////////////////////////////////////////////////////////////////////////////
0228 
0229 ToggleAction::ToggleAction( const QString &text, void ( *f ) ( bool ), KActionCollection* const ac, const char *name, QObject *parent )
0230         : KToggleAction( parent )
0231         , m_function( f )
0232 {
0233     setText(text);
0234     ac->addAction(name, this);
0235 }
0236 
0237 void ToggleAction::setChecked( bool b )
0238 {
0239     const bool announce = b != isChecked();
0240 
0241     m_function( b );
0242     KToggleAction::setChecked( b );
0243     AmarokConfig::self()->save(); //So we don't lose the setting when crashing
0244     if( announce ) Q_EMIT toggled( b ); //KToggleAction doesn't do this for us. How gay!
0245 }
0246 
0247 void ToggleAction::setEnabled( bool b )
0248 {
0249     const bool announce = b != isEnabled();
0250 
0251     KToggleAction::setEnabled( b );
0252     AmarokConfig::self()->save(); //So we don't lose the setting when crashing
0253     if( announce ) Q_EMIT QAction::triggered( b );
0254 }
0255 
0256 //////////////////////////////////////////////////////////////////////////////////////////
0257 // SelectAction
0258 //////////////////////////////////////////////////////////////////////////////////////////
0259 
0260 SelectAction::SelectAction( const QString &text, void ( *f ) ( int ), KActionCollection* const ac, const char *name, QObject *parent )
0261         : KSelectAction( parent )
0262         , m_function( f )
0263 {
0264     PERF_LOG( "In SelectAction" );
0265     setText(text);
0266     ac->addAction(name, this);
0267 }
0268 
0269 void SelectAction::setCurrentItem( int n )
0270 {
0271     const bool announce = n != currentItem();
0272 
0273     debug() << "setCurrentItem: " << n;
0274 
0275     m_function( n );
0276     KSelectAction::setCurrentItem( n );
0277     AmarokConfig::self()->save(); //So we don't lose the setting when crashing
0278     if( announce ) Q_EMIT indexTriggered( n );
0279 }
0280 
0281 void SelectAction::actionTriggered( QAction *a )
0282 {
0283     m_function( currentItem() );
0284     AmarokConfig::self()->save();
0285     KSelectAction::actionTriggered( a );
0286 }
0287 
0288 void SelectAction::setEnabled( bool b )
0289 {
0290     const bool announce = b != isEnabled();
0291 
0292     KSelectAction::setEnabled( b );
0293     AmarokConfig::self()->save(); //So we don't lose the setting when crashing
0294     if( announce ) Q_EMIT QAction::triggered( b );
0295 }
0296 
0297 void SelectAction::setIcons( QStringList icons )
0298 {
0299     m_icons = icons;
0300     foreach( QAction *a, selectableActionGroup()->actions() )
0301     {
0302         a->setIcon( QIcon::fromTheme(icons.takeFirst()) );
0303     }
0304 }
0305 
0306 QStringList SelectAction::icons() const { return m_icons; }
0307 
0308 QString SelectAction::currentIcon() const
0309 {
0310     if( m_icons.count() )
0311         return m_icons.at( currentItem() );
0312     return QString();
0313 }
0314 
0315 QString SelectAction::currentText() const {
0316     return KSelectAction::currentText() + "<br /><br />" + i18n("Click to change");
0317 }
0318 
0319 
0320 void
0321 RandomAction::setCurrentItem( int n )
0322 {
0323     // Porting
0324     //if( QAction *a = parentCollection()->action( "favor_tracks" ) )
0325     //    a->setEnabled( n );
0326     SelectAction::setCurrentItem( n );
0327 }
0328 
0329 //////////////////////////////////////////////////////////////////////////////////////////
0330 // ReplayGainModeAction
0331 //////////////////////////////////////////////////////////////////////////////////////////
0332 ReplayGainModeAction::ReplayGainModeAction( KActionCollection *ac, QObject *parent ) :
0333     SelectAction( i18n( "&Replay Gain Mode" ), &AmarokConfig::setReplayGainMode, ac, "replay_gain_mode", parent )
0334 {
0335     setItems( QStringList() << i18nc( "Replay Gain state, as in, disabled", "&Off" )
0336                             << i18nc( "Item, as in, music", "&Track" )
0337                             << i18n( "&Album" ) );
0338     EngineController *engine = EngineController::instance();
0339     Q_ASSERT( engine );
0340     if( engine->supportsGainAdjustments() )
0341         setCurrentItem( AmarokConfig::replayGainMode() );
0342     else
0343     {
0344         // Note: it would be nice to set a tooltip that would explain why this is disabled
0345         // to users, but tooltips aren't shown in menu anyway :-(
0346         actions().at( 1 )->setEnabled( false );
0347         actions().at( 2 )->setEnabled( false );
0348     }
0349 }
0350 
0351 //////////////////////////////////////////////////////////////////////////////////////////
0352 // BurnMenuAction
0353 //////////////////////////////////////////////////////////////////////////////////////////
0354 BurnMenuAction::BurnMenuAction( KActionCollection *ac, QObject *parent )
0355   : QAction( parent )
0356 {
0357     setText(i18n( "Burn" ));
0358     ac->addAction(QStringLiteral("burn_menu"), this);
0359 }
0360 
0361 QWidget*
0362 BurnMenuAction::createWidget( QWidget *w )
0363 {
0364     KToolBar *bar = dynamic_cast<KToolBar*>(w);
0365 
0366     if( bar && KAuthorized::authorizeAction( objectName() ) )
0367     {
0368         //const int id = QAction::getToolButtonID();
0369 
0370         //addContainer( bar, id );
0371         w->addAction( this );
0372         //connect( bar, &KToolBar::destroyed, this, &BurnMenuAction::slotDestroyed );
0373 
0374         //bar->insertButton( QString::null, id, true, i18n( "Burn" ), index );
0375 
0376         //KToolBarButton* button = bar->getButton( id );
0377         //button->setPopup( Amarok::BurnMenu::instance() );
0378         //button->setObjectName( "toolbutton_burn_menu" );
0379         //button->setIcon( "k3b" );
0380 
0381         //return associatedWidgets().count() - 1;
0382         return nullptr;
0383     }
0384     //else return -1;
0385     else return nullptr;
0386 }
0387 
0388 
0389 BurnMenu* BurnMenu::s_instance = nullptr;
0390 
0391 BurnMenu::BurnMenu( QWidget* parent )
0392     : QMenu( parent )
0393 {
0394     s_instance = this;
0395 
0396     addAction( i18n("Current Playlist"), this, &BurnMenu::slotBurnCurrentPlaylist );
0397     addAction( i18n("Selected Tracks"), this, &BurnMenu::slotBurnSelectedTracks );
0398     //TODO add "album" and "all tracks by artist"
0399 }
0400 
0401 QMenu*
0402 BurnMenu::instance()
0403 {
0404     return s_instance ? s_instance : new BurnMenu( The::mainWindow() );
0405 }
0406 
0407 void
0408 BurnMenu::slotBurnCurrentPlaylist() //SLOT
0409 {
0410     //K3bExporter::instance()->exportCurrentPlaylist();
0411 }
0412 
0413 void
0414 BurnMenu::slotBurnSelectedTracks() //SLOT
0415 {
0416     //K3bExporter::instance()->exportSelectedTracks();
0417 }
0418 
0419 
0420 //////////////////////////////////////////////////////////////////////////////////////////
0421 // StopAction
0422 //////////////////////////////////////////////////////////////////////////////////////////
0423 
0424 StopAction::StopAction( KActionCollection *ac, QObject *parent )
0425   : QAction( parent )
0426 {
0427     ac->addAction( QStringLiteral("stop"), this );
0428     setText( i18n( "Stop" ) );
0429     setIcon( QIcon::fromTheme(QStringLiteral("media-playback-stop-amarok")) );
0430     KGlobalAccel::setGlobalShortcut(this, QKeySequence() );
0431     connect( this, &StopAction::triggered, this, &StopAction::stop );
0432 
0433     EngineController *engine = The::engineController();
0434 
0435     if( engine->isStopped() )
0436         stopped();
0437     else
0438         playing();
0439 
0440     connect( engine, &EngineController::stopped,
0441              this, &StopAction::stopped );
0442     connect( engine, &EngineController::trackPlaying,
0443              this, &StopAction::playing );
0444 }
0445 
0446 void
0447 StopAction::stopped()
0448 {
0449     setEnabled( false );
0450 }
0451 
0452 void
0453 StopAction::playing()
0454 {
0455     setEnabled( true );
0456 }
0457 
0458 void
0459 StopAction::stop()
0460 {
0461     The::engineController()->stop();
0462 }
0463 
0464 //////////////////////////////////////////////////////////////////////////////////////////
0465 // StopPlayingAfterCurrentTrackAction
0466 //////////////////////////////////////////////////////////////////////////////////////////
0467 
0468 StopPlayingAfterCurrentTrackAction::StopPlayingAfterCurrentTrackAction( KActionCollection *ac, QObject *parent )
0469 : QAction( parent )
0470 {
0471     ac->addAction( QStringLiteral("stop_after_current"), this );
0472     setText( i18n( "Stop after current Track" ) );
0473     setIcon( QIcon::fromTheme(QStringLiteral("media-playback-stop-amarok")) );
0474     KGlobalAccel::setGlobalShortcut(this, QKeySequence( Qt::META + Qt::SHIFT + Qt::Key_V ) );
0475     connect( this, &StopPlayingAfterCurrentTrackAction::triggered, this, &StopPlayingAfterCurrentTrackAction::stopPlayingAfterCurrentTrack );
0476 }
0477 
0478 void
0479 StopPlayingAfterCurrentTrackAction::stopPlayingAfterCurrentTrack()
0480 {
0481     QString text;
0482 
0483     quint64 activeTrack = Playlist::ModelStack::instance()->bottom()->activeId();
0484     if( activeTrack )
0485     {
0486         if( The::playlistActions()->willStopAfterTrack( activeTrack ) )
0487         {
0488             The::playlistActions()->stopAfterPlayingTrack( 0 );
0489             text = i18n( "Stop after current track: Off" );
0490         }
0491         else
0492         {
0493             The::playlistActions()->stopAfterPlayingTrack( activeTrack );
0494             text = i18n( "Stop after current track: On" );
0495         }
0496     }
0497     else
0498         text = i18n( "No track playing" );
0499 
0500     Amarok::OSD::instance()->OSDWidget::show( text );
0501     if( Amarok::KNotificationBackend::instance()->isEnabled() )
0502         Amarok::KNotificationBackend::instance()->show( i18n( "Amarok" ), text );
0503 }