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 }