File indexing completed on 2024-05-05 16:54:09

0001 /****************************************************************************************
0002  * Copyright (c) 2002 Mark Kretschmann <kretschmann@kde.org>                            *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #include "App.h"
0018 
0019 #include <config.h>
0020 #include "EngineController.h"
0021 #include "KNotificationBackend.h"
0022 #include "MainWindow.h"
0023 #include "PluginManager.h"
0024 #include "scripting/scriptmanager/ScriptManager.h"
0025 #include "TrayIcon.h"
0026 #include "amarokconfig.h"
0027 #include "aboutdialog/OcsData.h"
0028 #include "amarokurls/AmarokUrl.h"
0029 #include "configdialog/ConfigDialog.h"
0030 #include "configdialog/dialogs/PlaybackConfig.h"
0031 #include "core/capabilities/SourceInfoCapability.h"
0032 #include "core/logger/Logger.h"
0033 #include "core/meta/Meta.h"
0034 #include "core/meta/support/MetaConstants.h"
0035 #include "core/meta/support/MetaUtility.h"
0036 #include "core/playlists/Playlist.h"
0037 #include "core/playlists/PlaylistFormat.h"
0038 #include "core/podcasts/PodcastProvider.h"
0039 #include "core/support/Amarok.h"
0040 #include "core/support/Components.h"
0041 #include "core/support/Debug.h"
0042 #include "core/transcoding/TranscodingController.h"
0043 #include "core-impl/collections/support/CollectionManager.h"
0044 #ifdef DEBUG_BUILD_TYPE
0045 #include "core-impl/logger/DebugLogger.h"
0046 #endif // DEBUG_BUILD_TYPE
0047 #include "core-impl/playlists/types/file/PlaylistFileSupport.h"
0048 #include "core-impl/storage/StorageManager.h"
0049 #include "covermanager/CoverCache.h"
0050 #include "covermanager/CoverFetcher.h"
0051 #include "dbus/CollectionDBusHandler.h"
0052 #include "dbus/mpris2/Mpris2.h"
0053 #include "network/NetworkAccessManagerProxy.h"
0054 #include "playlist/PlaylistActions.h"
0055 #include "playlist/PlaylistController.h"
0056 #include "playlist/PlaylistModelStack.h"
0057 #include "playlistmanager/PlaylistManager.h"
0058 #include "services/ServicePluginManager.h"
0059 #include "scripting/scriptconsole/ScriptConsole.h"
0060 #include "statemanagement/ApplicationController.h"
0061 #include "statemanagement/DefaultApplicationController.h"
0062 #include "statsyncing/Controller.h"
0063 #include "widgets/Osd.h"
0064 
0065 #include <iostream>
0066 
0067 #include <KDirLister>
0068 #include <KEditToolBar>                  //slotConfigToolbars()
0069 #include <KIO/CopyJob>
0070 #include <KJobUiDelegate>
0071 #include <KLocalizedString>
0072 #include <KMessageBox>
0073 #include <KShortcutsDialog>              //slotConfigShortcuts()
0074 #include <ThreadWeaver/Queue>
0075 
0076 #include <QAction>
0077 #include <QByteArray>
0078 #include <QCommandLineParser>
0079 #include <QDesktopServices>
0080 #include <QFile>
0081 #include <QFileOpenEvent>
0082 #include <QStringList>
0083 #include <QTimer>                       //showHyperThreadingWarning()
0084 
0085 #ifdef Q_WS_MAC
0086 #include <CoreFoundation/CoreFoundation.h>
0087 extern void setupEventHandler_mac(SRefCon);
0088 #endif
0089 
0090 AMAROK_EXPORT OcsData ocsData( "api.kde-look.org" );
0091 
0092 App::App(int &argc, char **argv)
0093     : QApplication(argc, argv)
0094     , m_tray(nullptr)
0095     , m_args(nullptr)
0096 {
0097     DEBUG_BLOCK
0098     PERF_LOG( "Begin Application ctor" )
0099 
0100     KLocalizedString::setApplicationDomain("amarok");
0101 
0102     // required for last.fm plugin to grab app version
0103     setApplicationVersion( AMAROK_VERSION );
0104 
0105     qRegisterMetaType<Meta::DataPtr>();
0106     qRegisterMetaType<Meta::DataList>();
0107     qRegisterMetaType<Meta::TrackPtr>();
0108     qRegisterMetaType<Meta::TrackList>();
0109     qRegisterMetaType<Meta::AlbumPtr>();
0110     qRegisterMetaType<Meta::AlbumList>();
0111     qRegisterMetaType<Meta::ArtistPtr>();
0112     qRegisterMetaType<Meta::ArtistList>();
0113     qRegisterMetaType<Meta::GenrePtr>();
0114     qRegisterMetaType<Meta::GenreList>();
0115     qRegisterMetaType<Meta::ComposerPtr>();
0116     qRegisterMetaType<Meta::ComposerList>();
0117     qRegisterMetaType<Meta::YearPtr>();
0118     qRegisterMetaType<Meta::YearList>();
0119     qRegisterMetaType<Meta::LabelPtr>();
0120     qRegisterMetaType<Meta::LabelList>();
0121     qRegisterMetaType<Playlists::PlaylistPtr>();
0122     qRegisterMetaType<Playlists::PlaylistList>();
0123 
0124 #ifdef Q_WS_MAC
0125     // this is inspired by OpenSceneGraph: osgDB/FilePath.cpp
0126 
0127     // Start with the Bundle PlugIns directory.
0128 
0129     // Get the main bundle first. No need to retain or release it since
0130     //  we are not keeping a reference
0131     CFBundleRef myBundle = CFBundleGetMainBundle();
0132     if( myBundle )
0133     {
0134         // CFBundleGetMainBundle will return a bundle ref even if
0135         //  the application isn't part of a bundle, so we need to
0136         //  check
0137         //  if the path to the bundle ends in ".app" to see if it is
0138         //  a
0139         //  proper application bundle. If it is, the plugins path is
0140         //  added
0141         CFURLRef urlRef = CFBundleCopyBundleURL(myBundle);
0142         if(urlRef)
0143         {
0144             char bundlePath[1024];
0145             if( CFURLGetFileSystemRepresentation( urlRef, true, (UInt8 *)bundlePath, sizeof(bundlePath) ) )
0146             {
0147                 QByteArray bp( bundlePath );
0148                 size_t len = bp.length();
0149                 if( len > 4 && bp.right( 4 ) == ".app" )
0150                 {
0151                     bp.append( "/Contents/MacOS" );
0152                     QByteArray path = qgetenv( "PATH" );
0153                     if( path.length() > 0 )
0154                     {
0155                         path.prepend( ":" );
0156                     }
0157                     path.prepend( bp );
0158                     debug() << "setting PATH=" << path;
0159                     setenv("PATH", path, 1);
0160                 }
0161             }
0162             // docs say we are responsible for releasing CFURLRef
0163             CFRelease(urlRef);
0164         }
0165     }
0166 
0167     setupEventHandler_mac(this);
0168 #endif
0169 
0170     PERF_LOG( "Done App ctor" )
0171 }
0172 
0173 App::~App()
0174 {
0175     DEBUG_BLOCK
0176 
0177     //delete m_args;
0178     CollectionManager::instance()->stopScan();
0179 
0180     // Hiding the OSD before exit prevents crash
0181     Amarok::OSD::instance()->hide();
0182 
0183     // This following can't go in the PlaylistModel destructor, because by the time that
0184     // happens, the Config has already been written.
0185 
0186     // Use the bottom model because that provides the most dependable/invariable row
0187     // number to save in an external file.
0188     AmarokConfig::setLastPlaying( Playlist::ModelStack::instance()->bottom()->activeRow() );
0189 
0190     if ( AmarokConfig::resumePlayback() )
0191     {
0192         Meta::TrackPtr engineTrack = The::engineController()->currentTrack();
0193         if( engineTrack )
0194         {
0195             AmarokConfig::setResumeTrack( engineTrack->playableUrl().toDisplayString() );
0196             AmarokConfig::setResumeTime( The::engineController()->trackPositionMs() );
0197             AmarokConfig::setResumePaused( The::engineController()->isPaused() );
0198         }
0199         else
0200             AmarokConfig::setResumeTrack( QString() ); //otherwise it'll play previous resume next time!
0201     }
0202 
0203     The::engineController()->endSession(); //records final statistics
0204 
0205 #ifndef Q_WS_MAC
0206     // do even if trayicon is not shown, it is safe
0207     Amarok::config().writeEntry( "HiddenOnExit", mainWindow()->isHidden() );
0208     AmarokConfig::self()->save();
0209 #else
0210     // for some reason on OS X the main window always reports being hidden
0211     // this means if you have the tray icon enabled, amarok will always open minimized
0212     Amarok::config().writeEntry( "HiddenOnExit", false );
0213     AmarokConfig::self()->save();
0214 #endif
0215 
0216     // wait for threads to finish
0217     ThreadWeaver::Queue::instance()->requestAbort();
0218     ThreadWeaver::Queue::instance()->finish();
0219     ThreadWeaver::Queue::instance()->shutDown();
0220 
0221     ScriptManager::destroy();
0222 
0223     // this must be deleted before the connection to the Xserver is
0224     // severed, or we risk a crash when the QApplication is exited,
0225     // I asked Trolltech! *smug*
0226     Amarok::OSD::destroy();
0227     Amarok::KNotificationBackend::destroy();
0228 
0229     AmarokConfig::self()->save();
0230 
0231     delete m_mainWindow.data();
0232 
0233     Playlist::Controller::destroy();
0234     Playlist::ModelStack::destroy();
0235     Playlist::Actions::destroy();
0236     PlaylistManager::destroy();
0237     CoverFetcher::destroy();
0238     CoverCache::destroy();
0239     ServicePluginManager::destroy();
0240     CollectionManager::destroy();
0241     StorageManager::destroy();
0242     NetworkAccessManagerProxy::destroy();
0243     Plugins::PluginManager::destroy();
0244 
0245     //this should be moved to App::quit() I guess
0246     Amarok::Components::applicationController()->shutdown();
0247 
0248 #ifdef Q_WS_WIN
0249     // work around for KUniqueApplication being not completely implemented on windows
0250     QDBusConnectionInterface* dbusService;
0251     if (QDBusConnection::sessionBus().isConnected() && (dbusService = QDBusConnection::sessionBus().interface()))
0252     {
0253         dbusService->unregisterService("org.mpris.amarok");
0254         dbusService->unregisterService("org.mpris.MediaPlayer2.amarok");
0255     }
0256 #endif
0257 }
0258 
0259 void
0260 App::handleCliArgs(const QString &cwd)
0261 {
0262     DEBUG_BLOCK
0263 
0264     //TODO Resolve positional arguments using cwd
0265     if( m_args->isSet( "cwd" ) ) {
0266         m_cwd = m_args->value( "cwd" );
0267     } else {
0268         m_cwd = cwd;
0269     }
0270 
0271     bool haveArgs = true; // assume having args in first place
0272     if( !m_args->positionalArguments().isEmpty() )
0273     {
0274         QList<QUrl> list;
0275         for( int i = 0; i < m_args->positionalArguments().count() ; i++ )
0276         {
0277             QUrl url( QUrl::fromUserInput( m_args->positionalArguments().at( i ), QString(), QUrl::AssumeLocalFile ) );
0278             //TODO:PORTME
0279             if( Podcasts::PodcastProvider::couldBeFeed( url.url() ) )
0280             {
0281                 QUrl feedUrl = Podcasts::PodcastProvider::toFeedUrl( url.url() );
0282                 The::playlistManager()->defaultPodcasts()->addPodcast( feedUrl );
0283             }
0284             else if( url.scheme() == "amarok" )
0285                 s_delayedAmarokUrls.append( url.url() );
0286             else
0287                 list << url;
0288         }
0289 
0290         Playlist::AddOptions options;
0291         if( m_args->isSet( "queue" ) )
0292            options = Playlist::OnQueueToPlaylistAction;
0293         else if( m_args->isSet( "append" ) )
0294            options = Playlist::OnAppendToPlaylistAction;
0295         else if( m_args->isSet( "load" ) )
0296             options = Playlist::OnReplacePlaylistAction;
0297         else
0298             options = Playlist::OnPlayMediaAction;
0299 
0300         The::playlistController()->insertOptioned( list, options );
0301     }
0302     else if ( m_args->isSet( "cdplay" ) )
0303         The::mainWindow()->playAudioCd();
0304 
0305     //we shouldn't let the user specify two of these since it is pointless!
0306     //so we prioritise, pause > stop > play > next > prev
0307     //thus pause is the least destructive, followed by stop as brakes are the most important bit of a car(!)
0308     //then the others seemed sensible. Feel free to modify this order, but please leave justification in the cvs log
0309     //I considered doing some sanity checks (eg only stop if paused or playing), but decided it wasn't worth it
0310     else if ( m_args->isSet( "pause" ) )
0311         The::engineController()->pause();
0312     else if ( m_args->isSet( "stop" ) )
0313         The::engineController()->stop();
0314     else if ( m_args->isSet( "play-pause" ) )
0315         The::engineController()->playPause();
0316     else if ( m_args->isSet( "play" ) ) //will restart if we are playing
0317         The::engineController()->play();
0318     else if ( m_args->isSet( "next" ) )
0319         The::playlistActions()->next();
0320     else if ( m_args->isSet( "previous" ) )
0321         The::playlistActions()->back();
0322     else // no args given
0323         haveArgs = false;
0324 
0325     static bool firstTime = true;
0326 
0327     //allows debugging on OS X. Bundles have to be started with "open". Therefore it is not possible to pass an argument
0328     const bool forceDebug = Amarok::config().readEntry( "Force Debug", false );
0329 
0330     if( firstTime && !Debug::debugEnabled() && !forceDebug )
0331     {
0332         qDebug() << "**********************************************************************************************";
0333         qDebug() << "** AMAROK WAS STARTED IN NORMAL MODE. IF YOU WANT TO SEE DEBUGGING INFORMATION, PLEASE USE: **";
0334         qDebug() << "** amarok --debug                                                                           **";
0335         qDebug() << "**********************************************************************************************";
0336     }
0337 
0338     if( !firstTime && !haveArgs )
0339     {
0340         // mainWindow() can be 0 if another instance is loading, see https://bugs.kde.org/show_bug.cgi?id=202713
0341         if( pApp->mainWindow() )
0342             pApp->mainWindow()->activate();
0343     }
0344 
0345     firstTime = false;
0346     m_args->clearPositionalArguments();    //free up memory
0347 }
0348 
0349 
0350 /////////////////////////////////////////////////////////////////////////////////////
0351 // INIT
0352 /////////////////////////////////////////////////////////////////////////////////////
0353 
0354 void
0355 App::initCliArgs(QCommandLineParser *parser)
0356 {
0357     m_args = parser;
0358     // Update main.cpp (below KUniqueApplication::start() wrt instanceOptions) aswell if needed!
0359     QList<QCommandLineOption> options;
0360 
0361     options.append(QCommandLineOption("+[URL(s)]", i18n( "Files/URLs to open" )));
0362     options.append(QCommandLineOption("cdplay", i18n("Immediately start playing an audio cd")));
0363     options.append(QCommandLineOption(QStringList() << "r" << "previous", i18n( "Skip backwards in playlist" )));
0364     options.append(QCommandLineOption(QStringList() << "p" << "play", i18n( "Start playing current playlist" )));
0365     options.append(QCommandLineOption(QStringList() << "t" << "play-pause", i18n( "Play if stopped, pause if playing" )));
0366     options.append(QCommandLineOption("pause", i18n( "Pause playback" )));
0367     options.append(QCommandLineOption(QStringList() << "s" << "stop", i18n( "Stop playback" )));
0368     options.append(QCommandLineOption(QStringList() << "f" << "next", i18n( "Skip forwards in playlist" )
0369     + "\n\n\n"
0370     + i18n("Additional options:")));
0371     options.append(QCommandLineOption(QStringList() << "a" << "append", i18n( "Append files/URLs to playlist" )));
0372     options.append(QCommandLineOption("queue", i18n("Queue URLs after the currently playing track")));
0373     options.append(QCommandLineOption(QStringList() << "l" << "load", i18n("Load URLs, replacing current playlist")));
0374     options.append(QCommandLineOption(QStringList() << "d" << "debug", i18n("Print verbose debugging information")));
0375     options.append(QCommandLineOption("debug-audio", i18n("Print verbose debugging information from the audio system")));
0376     options.append(QCommandLineOption(QStringList() << "c" << "coloroff", i18n("Disable colorization for debug output.")));
0377     options.append(QCommandLineOption(QStringList() << "m" << "multipleinstances", i18n("Allow running multiple Amarok instances")));
0378     options.append(QCommandLineOption("cwd", i18n( "Base for relative filenames/URLs" )));
0379 
0380     parser->addOptions(options);      //add our own options
0381 }
0382 
0383 
0384 /////////////////////////////////////////////////////////////////////////////////////
0385 // METHODS
0386 /////////////////////////////////////////////////////////////////////////////////////
0387 
0388 
0389 //SLOT
0390 void App::applySettings()
0391 {
0392     DEBUG_BLOCK
0393 
0394     if( AmarokConfig::showTrayIcon() && ! m_tray )
0395     {
0396         m_tray = new Amarok::TrayIcon( m_mainWindow.data() );
0397     }
0398     else if( !AmarokConfig::showTrayIcon() && m_tray )
0399     {
0400         delete m_tray;
0401         m_tray = nullptr;
0402     }
0403 
0404     Amarok::OSD::instance()->applySettings();
0405 
0406     Q_EMIT settingsChanged();
0407 
0408     if( AmarokConfig::enableScriptConsole() && !m_scriptConsole )
0409         m_scriptConsole = ScriptConsoleNS::ScriptConsole::instance();
0410     else if( !AmarokConfig::enableScriptConsole() && m_scriptConsole )
0411         m_scriptConsole.data()->deleteLater();
0412 }
0413 
0414 //SLOT
0415 void App::applySettingsFirstTime()
0416 {
0417     DEBUG_BLOCK
0418 
0419     if( AmarokConfig::showTrayIcon() && ! m_tray )
0420     {
0421         m_tray = new Amarok::TrayIcon( m_mainWindow.data() );
0422     }
0423     else if( !AmarokConfig::showTrayIcon() && m_tray )
0424     {
0425         delete m_tray;
0426         m_tray = nullptr;
0427     }
0428 
0429     if( AmarokConfig::enableScriptConsole() && !m_scriptConsole )
0430         m_scriptConsole = ScriptConsoleNS::ScriptConsole::instance();
0431     else if( !AmarokConfig::enableScriptConsole() && m_scriptConsole )
0432         m_scriptConsole.data()->deleteLater();
0433 }
0434 
0435 //SLOT
0436 void
0437 App::continueInit()
0438 {
0439     DEBUG_BLOCK
0440 
0441     PERF_LOG( "Begin App::continueInit" )
0442 
0443     AmarokConfig::instance( "amarokrc" );
0444     newInstance();
0445 
0446     const bool restoreSession = m_args->positionalArguments().isEmpty() || m_args->isSet( "append" )
0447                                 || m_args->isSet( "queue" )
0448                                 || Amarok::config().readEntry( "AppendAsDefault", false );
0449 
0450 #ifdef DEBUG_BUILD_TYPE
0451     new DebugLogger( this );
0452 #endif // DEBUG_BUILD_TYPE
0453 
0454     new Amarok::DefaultApplicationController( this );
0455     Amarok::Components::applicationController()->start();
0456 
0457     // Instantiate statistics synchronization controller. Needs to be before creating
0458     // MainWindow as MainWindow connects a signal to StatSyncing::Controller.
0459     Amarok::Components::setStatSyncingController( new StatSyncing::Controller( this ) );
0460 
0461     PERF_LOG( "Creating MainWindow" )
0462     m_mainWindow = new MainWindow();
0463     PERF_LOG( "Done creating MainWindow" )
0464 
0465     if( AmarokConfig::showTrayIcon() )
0466         m_tray = new Amarok::TrayIcon( m_mainWindow );
0467 
0468     PERF_LOG( "Creating DBus handlers" )
0469     QDBusConnection::sessionBus().registerService("org.mpris.amarok");
0470     new CollectionDBusHandler( this );
0471     new Amarok::Mpris2( this );
0472     PERF_LOG( "Done creating DBus handlers" )
0473 
0474     //DON'T DELETE THIS NEXT LINE or the app crashes when you click the X (unless we reimplement closeEvent)
0475     //Reason: in ~App we have to call the deleteBrowsers method or else we run afoul of refcount foobar in KHTMLPart
0476     //But if you click the X (not Action->Quit) it automatically kills MainWindow because KMainWindow sets this
0477     //for us as default (bad KMainWindow)
0478     m_mainWindow->setAttribute( Qt::WA_DeleteOnClose, false );
0479     //init playlist window as soon as the database is guaranteed to be usable
0480 
0481     // Create engine, show TrayIcon etc.
0482     applySettingsFirstTime();
0483     // Must be created _after_ MainWindow.
0484     PERF_LOG( "Starting ScriptManager" )
0485     ScriptManager::instance();
0486     PERF_LOG( "ScriptManager started" )
0487 
0488     The::engineController()->setVolume( AmarokConfig::masterVolume() );
0489     The::engineController()->setMuted( AmarokConfig::muteState() );
0490 
0491     Amarok::KNotificationBackend::instance()->setEnabled( AmarokConfig::kNotifyEnabled() );
0492     Amarok::OSD::instance()->applySettings(); // Create after setting volume (don't show OSD for that)
0493 
0494     // Restore keyboard shortcuts etc from config
0495     Amarok::actionCollection()->readSettings();
0496     //on startup we need to show the window, but only if it wasn't hidden on exit
0497     //and always if the trayicon isn't showing
0498     if( !Amarok::config().readEntry( "HiddenOnExit", false ) || !AmarokConfig::showTrayIcon() )
0499     {
0500         PERF_LOG( "showing main window again" )
0501         m_mainWindow->show();
0502         PERF_LOG( "after showing mainWindow" )
0503     }
0504 
0505     //Instantiate the Transcoding::Controller, this fires up an asynchronous KProcess with
0506     //FFmpeg which should not take more than ~200msec.
0507     Amarok::Components::setTranscodingController( new Transcoding::Controller( this ) );
0508 
0509     PERF_LOG( "App init done" )
0510 
0511     // check that the amarok sql configuration is valid.
0512     if( !StorageManager::instance()->getLastErrors().isEmpty() )
0513     {
0514         QMessageBox::critical( The::mainWindow(), i18n( "Database Error" ),
0515                  i18n( "The Amarok database reported the following errors:"
0516                  "\n%1\nIn most cases you will need to resolve these errors "
0517                  "before Amarok will run properly.",
0518                  StorageManager::instance()->getLastErrors().join( "\n" ) ) );
0519         StorageManager::instance()->clearLastErrors();
0520         slotConfigAmarok( "DatabaseConfig" );
0521     }
0522     else
0523     {
0524         handleFirstRun();
0525     }
0526 
0527     if( AmarokConfig::resumePlayback() && restoreSession && !m_args->isSet( "stop" ) ) {
0528         //restore session as long as the user didn't specify media to play etc.
0529         //do this after applySettings() so OSD displays correctly
0530         The::engineController()->restoreSession();
0531     }
0532     //and now we can run any amarokurls provided on startup, as all components should be initialized by now!
0533     foreach( const QString& urlString, s_delayedAmarokUrls )
0534     {
0535         AmarokUrl aUrl( urlString );
0536         aUrl.run();
0537     }
0538     
0539     s_delayedAmarokUrls.clear();
0540 }
0541 
0542 //SLOT
0543 void App::activateRequested(const QStringList &arguments, const QString & cwd)
0544 {
0545         qDebug() << "activateRequested";
0546     if (!arguments.isEmpty()) {
0547         m_args->parse(arguments);
0548         handleCliArgs(cwd);
0549     } else {
0550         newInstance( );
0551     }
0552 }
0553 
0554 void App::slotConfigAmarok( const QString& page )
0555 {
0556     KConfigDialog *dialog = KConfigDialog::exists( "settings" );
0557     if( !dialog )
0558     {
0559         //KConfigDialog didn't find an instance of this dialog, so lets create it :
0560         dialog = new Amarok2ConfigDialog( mainWindow(), "settings", AmarokConfig::self() );
0561 
0562         connect( dialog, &KConfigDialog::settingsChanged,
0563                  this, &App::applySettings );
0564     }
0565     static_cast<Amarok2ConfigDialog*>( dialog )->show( page );
0566 }
0567 
0568 void App::slotConfigAmarokWithEmptyPage()
0569 {
0570     slotConfigAmarok( QString() );
0571 }
0572 
0573 void App::slotConfigShortcuts()
0574 {
0575     KShortcutsDialog::configure( Amarok::actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, mainWindow() );
0576     AmarokConfig::self()->save();
0577 }
0578 
0579 KIO::Job *App::trashFiles( const QList<QUrl> &files )
0580 {
0581     KIO::Job *job = KIO::trash( files );
0582     Amarok::Logger::newProgressOperation( job, i18n("Moving files to trash") );
0583     connect( job, &KIO::Job::result, this, &App::slotTrashResult );
0584     return job;
0585 }
0586 
0587 void App::slotTrashResult( KJob *job )
0588 {
0589     if( job->error() )
0590         job->uiDelegate()->showErrorMessage();
0591 }
0592 
0593 void App::quit()
0594 {
0595     DEBUG_BLOCK
0596     The::playlistManager()->completePodcastDownloads();
0597 
0598     // Following signal is relayed to scripts, which may block quitting for a while
0599     Q_EMIT prepareToQuit();
0600     QApplication::quit();
0601 }
0602 
0603 bool App::event( QEvent *event )
0604 {
0605     switch( event->type() )
0606     {
0607         //allows Amarok to open files from the finder on OS X
0608         case QEvent::FileOpen:
0609         {
0610             QString file = static_cast<QFileOpenEvent*>( event )->file();
0611             The::playlistController()->insertOptioned( QUrl( file ), Playlist::OnPlayMediaAction );
0612             return true;
0613         }
0614         default:
0615             return QApplication::event( event );
0616     }
0617 }
0618 
0619 int App::newInstance()
0620 {
0621     DEBUG_BLOCK
0622 
0623     static bool first = true;
0624     if ( isSessionRestored() && first )
0625     {
0626         first = false;
0627         return 0;
0628     }
0629 
0630     first = false;
0631 
0632     handleCliArgs(QDir::currentPath());
0633     return 0;
0634 }
0635 
0636 void App::handleFirstRun()
0637 {
0638     KConfigGroup config = Amarok::config( "General" );
0639     if( !config.readEntry( "First Run", true ) )
0640         return;
0641 
0642     const QString musicDir = QStandardPaths::writableLocation( QStandardPaths::MusicLocation );
0643     const QDir dir( musicDir );
0644 
0645     int result = KMessageBox::No;
0646     if( dir.exists() && dir.isReadable() )
0647     {
0648         result = KMessageBox::questionYesNoCancel( m_mainWindow, i18n( "A music path, "
0649                 "%1, is set in System Settings.\nWould you like to use that as a "
0650                 "collection folder?", musicDir ) );
0651     }
0652 
0653     switch( result )
0654     {
0655         case KMessageBox::Yes:
0656         {
0657             Collections::Collection *coll = CollectionManager::instance()->primaryCollection();
0658             if( coll )
0659             {
0660                 coll->setProperty( "collectionFolders", QStringList() << musicDir );
0661                 CollectionManager::instance()->startFullScan();
0662             }
0663             break;
0664         }
0665         case KMessageBox::No:
0666             slotConfigAmarok( "CollectionConfig" );
0667             break;
0668         default:
0669             break;
0670     }
0671 
0672     config.writeEntry( "First Run", false );
0673 }