Temporarily disable showing the mediabrowser as jefferais commit caused Amarok to...
[amarok.git] / src / app.cpp
blob29ed254b35cce9c98343881727cf7b8fee22ab6c
1 /***************************************************************************
2 app.cpp - description
3 -------------------
4 begin : Mit Okt 23 14:35:18 CEST 2002
5 copyright : (C) 2002 by Mark Kretschmann
6 email : markey@web.de
7 ***************************************************************************/
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17 #include "app.h"
19 #include "amarok.h"
20 #include "amarokconfig.h"
21 #include "amarokdbushandler.h"
22 #include "atomicstring.h"
23 #include "collectiondb.h"
24 #include "collectionmanager.h"
25 #include "ConfigDialog.h"
26 #include "context/ContextView.h"
27 //#include "dbsetup.h" //firstRunWizard()
28 #include "debug.h"
29 #include "enginebase.h"
30 #include "enginecontroller.h"
31 #include "equalizersetup.h"
32 //#include "firstrunwizard.h"
33 #include "MainWindow.h"
34 #include "mediabrowser.h"
35 #include "meta.h"
36 #include "metabundle.h"
37 #include "mountpointmanager.h"
38 #include "osd.h"
39 #include "playlist/PlaylistModel.h"
40 #include "playlist.h"
41 #include "playlistbrowser.h"
42 #include "pluginmanager.h"
43 #include "portabledevices/SolidHandler.h"
44 #include "refreshimages.h"
45 #include "scriptmanager.h"
46 #include "scrobbler.h"
47 #include "sidebar.h"
48 #include "statusbar.h"
49 #include "systray.h"
50 #include "threadmanager.h"
51 #include "tracktooltip.h" //engineNewMetaData()
52 #include "TheInstances.h"
53 #include "metadata/tplugins.h"
55 #include <iostream>
57 #include <KAboutData>
58 #include <KAction>
59 #include <KCmdLineArgs> //initCliArgs()
60 #include <KComboBox> //firstRunWizard()
61 #include <KConfigDialogManager>
62 #include <KCursor> //Amarok::OverrideCursor
63 #include <KEditToolBar> //slotConfigToolbars()
64 #include <KGlobalAccel> //initGlobalShortcuts()
65 #include <KGlobalSettings> //applyColorScheme()
66 #include <KIO/CopyJob>
67 #include <KIconLoader> //amarok Icon
68 #include <KJob>
69 #include <KJobUiDelegate>
70 #include <KLocale>
71 #include <KMessageBox> //applySettings(), genericEventHandler()
72 #include <KRun> //Amarok::invokeBrowser()
73 #include <kshell.h>
74 #include <KShortcutsDialog> //slotConfigShortcuts()
75 #include <KSplashScreen>
76 #include <KStandardDirs>
78 #include <QCloseEvent>
79 #include <QDBusInterface>
80 #include <QDBusReply>
81 #include <QEvent> //genericEventHandler()
82 #include <QEventLoop> //applySettings()
83 #include <QFile>
84 #include <Q3PopupMenu> //genericEventHandler
85 #include <QTimer> //showHyperThreadingWarning()
86 #include <QToolTip> //default tooltip for trayicon
88 QMutex Debug::mutex;
89 QMutex Amarok::globalDirsMutex;
91 int App::mainThreadId = 0;
93 #ifdef Q_WS_MAC
94 #include <CoreFoundation/CoreFoundation.h>
95 extern void setupEventHandler_mac(long);
96 #endif
98 AMAROK_EXPORT KAboutData aboutData( "amarok", 0,
99 ki18n( "Amarok" ), APP_VERSION,
100 ki18n( "The audio player for KDE" ), KAboutData::License_GPL,
101 ki18n( "(C) 2002-2003, Mark Kretschmann\n(C) 2003-2007, The Amarok Development Squad" ),
102 ki18n( "IRC:\nirc.freenode.net - #amarok, #amarok.de, #amarok.es\n\nFeedback:\namarok@kde.org\n\n(Build Date: " __DATE__ ")" ),
103 ( "http://amarok.kde.org" ) );
106 App::App()
107 : KUniqueApplication()
108 , m_splash( 0 )
110 DEBUG_BLOCK
112 if( AmarokConfig::showSplashscreen() )
114 QPixmap splashpix( KStandardDirs().findResource("data", "amarok/images/splash_screen.jpg") );
115 m_splash = new KSplashScreen( splashpix, Qt::WindowStaysOnTopHint );
116 m_splash->show();
119 registerTaglibPlugins();
121 qRegisterMetaType<MetaBundle>();
123 qRegisterMetaType<Meta::DataPtr>();
124 qRegisterMetaType<Meta::DataList>();
125 qRegisterMetaType<Meta::TrackPtr>();
126 qRegisterMetaType<Meta::TrackList>();
127 qRegisterMetaType<Meta::AlbumPtr>();
128 qRegisterMetaType<Meta::AlbumList>();
129 qRegisterMetaType<Meta::ArtistPtr>();
130 qRegisterMetaType<Meta::ArtistList>();
131 qRegisterMetaType<Meta::GenrePtr>();
132 qRegisterMetaType<Meta::GenreList>();
133 qRegisterMetaType<Meta::ComposerPtr>();
134 qRegisterMetaType<Meta::ComposerList>();
135 qRegisterMetaType<Meta::YearPtr>();
136 qRegisterMetaType<Meta::YearList>();
138 #ifdef Q_WS_MAC
139 // this is inspired by OpenSceneGraph: osgDB/FilePath.cpp
141 // Start with the the Bundle PlugIns directory.
143 // Get the main bundle first. No need to retain or release it since
144 // we are not keeping a reference
145 CFBundleRef myBundle = CFBundleGetMainBundle();
146 if( myBundle )
148 // CFBundleGetMainBundle will return a bundle ref even if
149 // the application isn't part of a bundle, so we need to
150 // check
151 // if the path to the bundle ends in ".app" to see if it is
152 // a
153 // proper application bundle. If it is, the plugins path is
154 // added
155 CFURLRef urlRef = CFBundleCopyBundleURL(myBundle);
156 if(urlRef)
158 char bundlePath[1024];
159 if( CFURLGetFileSystemRepresentation( urlRef, true, (UInt8 *)bundlePath, sizeof(bundlePath) ) )
161 QByteArray bp( bundlePath );
162 size_t len = bp.length();
163 if( len > 4 && bp.right( 4 ) == ".app" )
165 bp.append( "/Contents/MacOS" );
166 QByteArray path = getenv( "PATH" );
167 if( path.length() > 0 )
169 path.prepend( ":" );
171 path.prepend( bp );
172 debug() << "setting PATH=" << path;
173 setenv("PATH", path, 1);
176 // docs say we are responsible for releasing CFURLRef
177 CFRelease(urlRef);
180 #endif
182 //needs to be created before the wizard
183 new Amarok::DbusPlayerHandler(); // Must be created first
184 new Amarok::DbusPlaylistHandler();
185 new Amarok::DbusPlaylistBrowserHandler();
186 new Amarok::DbusContextHandler();
187 new Amarok::DbusCollectionHandler();
188 new Amarok::DbusMediaBrowserHandler();
189 new Amarok::DbusScriptHandler();
191 // tell AtomicString that this is the GUI thread
192 if ( !AtomicString::isMainThread() )
193 qWarning("AtomicString was initialized from a thread other than the GUI "
194 "thread. This could lead to memory leaks.");
196 #ifdef Q_WS_MAC
197 setupEventHandler_mac((long)this);
198 #endif
199 QDBusConnection::sessionBus().registerService("org.kde.amarok");
200 QTimer::singleShot( 0, this, SLOT( continueInit() ) );
203 App::~App()
205 DEBUG_BLOCK
207 delete m_splash;
208 m_splash = 0;
210 // Hiding the OSD before exit prevents crash
211 Amarok::OSD::instance()->hide();
213 EngineBase* const engine = EngineController::engine();
215 if ( AmarokConfig::resumePlayback() ) {
216 if ( engine->state() != Engine::Empty ) {
217 AmarokConfig::setResumeTrack( EngineController::instance()->playingURL().prettyUrl() );
218 AmarokConfig::setResumeTime( engine->position() );
220 else AmarokConfig::setResumeTrack( QString() ); //otherwise it'll play previous resume next time!
223 EngineController::instance()->endSession(); //records final statistics
224 EngineController::instance()->detach( this );
226 // do even if trayicon is not shown, it is safe
227 Amarok::config().writeEntry( "HiddenOnExit", mainWindow()->isHidden() );
229 CollectionDB::instance()->stopScan();
231 ThreadManager::deleteInstance(); //waits for jobs to finish
233 delete mainWindow();
235 // this must be deleted before the connection to the Xserver is
236 // severed, or we risk a crash when the QApplication is exited,
237 // I asked Trolltech! *smug*
238 delete Amarok::OSD::instance();
240 AmarokConfig::setVersion( APP_VERSION );
241 AmarokConfig::self()->writeConfig();
243 //need to unload the engine before the kapplication is destroyed
244 PluginManager::unload( engine );
248 #include <QStringList>
250 namespace
252 // grabbed from KsCD source, kompatctdisk.cpp
253 QString urlToDevice(const QString& device)
255 KUrl deviceUrl(device);
256 if (deviceUrl.protocol() == "media" || deviceUrl.protocol() == "system")
258 debug() << "WARNING: urlToDevice needs to be reimplemented with KDE4 technology, it's just a stub at the moment";
259 QDBusInterface mediamanager( "org.kde.kded", "/modules/mediamanager", "org.kde.MediaManager" );
260 QDBusReply<QStringList> reply = mediamanager.call( "properties",deviceUrl.fileName() );
261 if (!reply.isValid()) {
262 debug() << "Invalid reply from mediamanager";
263 return QString();
265 QStringList properties = reply;
266 if( properties.count()< 6 )
267 return QString();
268 debug() << "Reply from mediamanager " << properties[5];
269 return properties[5];
272 return device;
278 void App::handleCliArgs() //static
280 KCmdLineArgs* const args = KCmdLineArgs::parsedArgs();
282 if ( args->isSet( "cwd" ) )
284 KCmdLineArgs::setCwd( args->getOption( "cwd" ).toLocal8Bit() );
287 bool haveArgs = false;
288 if ( args->count() > 0 )
290 haveArgs = true;
292 KUrl::List list;
293 for( int i = 0; i < args->count(); i++ )
295 KUrl url = args->url( i );
296 if( url.protocol() == "itpc" || url.protocol() == "pcast" )
297 PlaylistBrowser::instance()->addPodcast( url );
298 else
299 list << url;
302 int options = PlaylistNS::AppendAndPlay;
303 if( args->isSet( "queue" ) )
304 options = Playlist::Queue;
305 else if( args->isSet( "append" ) || args->isSet( "enqueue" ) )
306 options = Playlist::Append;
307 else if( args->isSet( "load" ) )
308 options = Playlist::Replace;
310 if( args->isSet( "play" ) )
311 options |= Playlist::DirectPlay;
313 The::playlistModel()->insertMedia( list, options );
316 //we shouldn't let the user specify two of these since it is pointless!
317 //so we prioritise, pause > stop > play > next > prev
318 //thus pause is the least destructive, followed by stop as brakes are the most important bit of a car(!)
319 //then the others seemed sensible. Feel free to modify this order, but please leave justification in the cvs log
320 //I considered doing some sanity checks (eg only stop if paused or playing), but decided it wasn't worth it
321 else if ( args->isSet( "pause" ) )
323 haveArgs = true;
324 EngineController::instance()->pause();
326 else if ( args->isSet( "stop" ) )
328 haveArgs = true;
329 EngineController::instance()->stop();
331 else if ( args->isSet( "play-pause" ) )
333 haveArgs = true;
334 EngineController::instance()->playPause();
336 else if ( args->isSet( "play" ) ) //will restart if we are playing
338 haveArgs = true;
339 EngineController::instance()->play();
341 else if ( args->isSet( "next" ) )
343 haveArgs = true;
344 EngineController::instance()->next();
346 else if ( args->isSet( "previous" ) )
348 haveArgs = true;
349 EngineController::instance()->previous();
351 else if (args->isSet("cdplay"))
353 haveArgs = true;
354 QString device = args->getOption("cdplay");
355 KUrl::List urls;
356 if (EngineController::engine()->getAudioCDContents(device, urls)) {
357 Meta::TrackList tracks = CollectionManager::instance()->tracksForUrls( urls );
358 The::playlistModel()->insertOptioned(
359 tracks, Playlist::Replace|Playlist::DirectPlay);
360 } else { // Default behaviour
361 debug() <<
362 "Sorry, the engine doesn't support direct play from AudioCD..."
363 << endl;
367 if ( args->isSet( "toggle-playlist-window" ) )
369 haveArgs = true;
370 pApp->mainWindow()->showHide();
373 Amarok::config().writeEntry( "Debug Enabled", args->isSet( "debug" ) );
375 static bool firstTime = true;
376 if( !firstTime && !haveArgs )
377 pApp->mainWindow()->activate();
378 firstTime = false;
380 args->clear(); //free up memory
384 /////////////////////////////////////////////////////////////////////////////////////
385 // INIT
386 /////////////////////////////////////////////////////////////////////////////////////
388 void App::initCliArgs( int argc, char *argv[] ) //static
390 KCmdLineArgs::reset();
391 KCmdLineArgs::init( argc, argv, &::aboutData ); //calls KCmdLineArgs::addStdCmdLineOptions()
393 KCmdLineOptions options;
394 options.add("+[URL(s)]", ki18n( "Files/URLs to open" ));
395 options.add("r");
396 options.add("previous", ki18n( "Skip backwards in playlist" ));
397 options.add("p");
398 options.add("play", ki18n( "Start playing current playlist" ));
399 options.add("t");
400 options.add("play-pause", ki18n( "Play if stopped, pause if playing" ));
401 options.add("pause", ki18n( "Pause playback" ));
402 options.add("s");
403 options.add("stop", ki18n( "Stop playback" ));
404 options.add("f");
405 options.add("next", ki18n( "Skip forwards in playlist" ));
406 options.add(":", ki18n("Additional options:"));
407 options.add("a");
408 options.add("append", ki18n( "Append files/URLs to playlist" ));
409 options.add("e");
410 options.add("enqueue", ki18n("See append, available for backwards compatability"));
411 options.add("queue", ki18n("Queue URLs after the currently playing track"));
412 options.add("l");
413 options.add("load", ki18n("Load URLs, replacing current playlist"));
414 options.add("d");
415 options.add("debug", ki18n("Print verbose debugging information"));
416 options.add("m");
417 options.add("toggle-playlist-window", ki18n("Toggle the Playlist-window"));
418 options.add("wizard", ki18n( "Run first-run wizard" ));
419 options.add("engine <name>", ki18n( "Use the <name> engine" ));
420 options.add("cwd <directory>", ki18n( "Base for relative filenames/URLs" ));
421 options.add("cdplay <device>", ki18n("Play an AudioCD from <device> or system:/media/<device>"));
422 KCmdLineArgs::addCmdLineOptions( options ); //add our own options
426 void App::initGlobalShortcuts()
428 EngineController* const ec = EngineController::instance();
429 KAction* action;
431 // m_pGlobalAccel->insert( "play", i18n( "Play" ), 0, KKey("WIN+x"), 0, ec, SLOT( play() ), true, true );
432 action = new KAction( i18n( "Play" ), mainWindow() );
433 // action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_X ) );
434 connect( action, SIGNAL( triggered() ), ec, SLOT( play() ) );
436 // m_pGlobalAccel->insert( "pause", i18n( "Pause" ), 0, 0, 0, ec, SLOT( pause() ), true, true );
437 action = new KAction( i18n( "Pause" ), mainWindow() );
438 connect( action, SIGNAL( triggered() ), ec, SLOT( pause() ) );
440 // m_pGlobalAccel->insert( "play_pause", i18n( "Play/Pause" ), 0, KKey("WIN+c"), 0, ec, SLOT( playPause() ), true, true );
441 action = new KAction( i18n( "Play/Pause" ), mainWindow() );
442 // action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_C ) );
443 connect( action, SIGNAL( triggered() ), ec, SLOT( playPause() ) );
445 // m_pGlobalAccel->insert( "stop", i18n( "Stop" ), 0, KKey("WIN+v"), 0, ec, SLOT( stop() ), true, true );
446 action = new KAction( i18n( "Stop" ), mainWindow() );
447 // action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_V ) );
448 connect( action, SIGNAL( triggered() ), ec, SLOT( stop() ) );
450 // m_pGlobalAccel->insert( "stop_after_global", i18n( "Stop Playing After Current Track" ), 0, KKey("WIN+CTRL+v"), 0, Playlist::instance()->qscrollview(), SLOT( toggleStopAfterCurrentTrack() ), true, true );
451 action = new KAction( i18n( "Stop Playing After Current Track" ), mainWindow() );
452 // action->setGlobalShortcut( KShortcut( Qt::META + Qt::CTRL + Qt::Key_V ) );
453 connect( action, SIGNAL( triggered() ), Playlist::instance()->qscrollview(), SLOT( toggleStopAfterCurrentTrack() ) );
455 // m_pGlobalAccel->insert( "next", i18n( "Next Track" ), 0, KKey("WIN+b"), 0, ec, SLOT( next() ), true, true );
456 action = new KAction( i18n( "Next Track" ), mainWindow() );
457 // action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_B ) );
458 connect( action, SIGNAL( triggered() ), ec, SLOT( next() ) );
460 // m_pGlobalAccel->insert( "prev", i18n( "Previous Track" ), 0, KKey("WIN+z"), 0, ec, SLOT( previous() ), true, true );
461 action = new KAction( i18n( "Previous Track" ), mainWindow() );
462 // action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_Z ) );
463 connect( action, SIGNAL( triggered() ), ec, SLOT( previous() ) );
465 // m_pGlobalAccel->insert( "volup", i18n( "Increase Volume" ), 0, KKey("WIN+KP_Add"), 0, ec, SLOT( increaseVolume() ), true, true );
466 action = new KAction( i18n( "Increase Volume" ), mainWindow() );
467 // action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_Plus ) );
468 connect( action, SIGNAL( triggered() ), ec, SLOT( increaseVolume() ) );
470 // m_pGlobalAccel->insert( "voldn", i18n( "Decrease Volume" ), 0, KKey("WIN+KP_Subtract"), 0, ec, SLOT( decreaseVolume() ), true, true );
471 action = new KAction( i18n( "Decrease Volume" ), mainWindow() );
472 // action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_Minus ) );
473 connect( action, SIGNAL( triggered() ), ec, SLOT( decreaseVolume() ) );
476 // m_pGlobalAccel->insert( "seekforward", i18n( "Seek Forward" ), 0, KKey("WIN+Shift+KP_Add"), 0, ec, SLOT( seekForward() ), true, true );
477 action = new KAction( i18n( "Seek Forward" ), mainWindow() );
478 // action->setGlobalShortcut( KShortcut( Qt::META + Qt::SHIFT + Qt::Key_Plus ) );
479 connect( action, SIGNAL( triggered() ), ec, SLOT( seekForward() ) );
482 // m_pGlobalAccel->insert( "seekbackward", i18n( "Seek Backward" ), 0, KKey("WIN+Shift+KP_Subtract"), 0, ec, SLOT( seekBackward() ), true, true );
483 action = new KAction( i18n( "Seek Backward" ), mainWindow() );
484 // action->setGlobalShortcut( KShortcut( Qt::META + Qt::SHIFT + Qt::Key_Minus ) );
485 connect( action, SIGNAL( triggered() ), ec, SLOT( seekBackward() ) );
487 // m_pGlobalAccel->insert( "playlist_add", i18n( "Add Media..." ), 0, KKey("WIN+a"), 0, mainWindow(), SLOT( slotAddLocation() ), true, true );
488 action = new KAction( i18n( "Add Media..." ), mainWindow() );
489 // action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_A ) );
490 connect( action, SIGNAL( triggered() ), mainWindow(), SLOT( slotAddLocation() ) );
492 // m_pGlobalAccel->insert( "show", i18n( "Toggle Playlist Window" ), 0, KKey("WIN+p"), 0, mainWindow(), SLOT( showHide() ), true, true );
493 action = new KAction( i18n( "Toggle Playlist Window" ), mainWindow() );
494 // action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_P ) );
495 connect( action, SIGNAL( triggered() ), mainWindow(), SLOT( showHide() ) );
498 // m_pGlobalAccel->insert( "osd", i18n( "Show OSD" ), 0, KKey("WIN+o"), 0, Amarok::OSD::instance(), SLOT( forceToggleOSD() ), true, true );
499 action = new KAction( i18n( "Show OSD" ), mainWindow() );
500 // action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_O ) );
501 connect( action, SIGNAL( triggered() ), Amarok::OSD::instance(), SLOT( forceToggleOSD() ) );
502 #if 0
503 m_pGlobalAccel->insert( "mute", i18n( "Mute Volume" ), 0, KKey("WIN+m"), 0,
504 ec, SLOT( mute() ), true, true );
505 m_pGlobalAccel->insert( "rating1", i18n( "Rate Current Track: 1" ), 0, KKey("WIN+1"), 0,
506 this, SLOT( setRating1() ), true, true );
507 m_pGlobalAccel->insert( "rating2", i18n( "Rate Current Track: 2" ), 0, KKey("WIN+2"), 0,
508 this, SLOT( setRating2() ), true, true );
509 m_pGlobalAccel->insert( "rating3", i18n( "Rate Current Track: 3" ), 0, KKey("WIN+3"), 0,
510 this, SLOT( setRating3() ), true, true );
511 m_pGlobalAccel->insert( "rating4", i18n( "Rate Current Track: 4" ), 0, KKey("WIN+4"), 0,
512 this, SLOT( setRating4() ), true, true );
513 m_pGlobalAccel->insert( "rating5", i18n( "Rate Current Track: 5" ), 0, KKey("WIN+5"), 0,
514 this, SLOT( setRating5() ), true, true );
515 #endif
517 // KGlobalAccel::self()->setConfigGroup( "Shortcuts" );
518 // KGlobalAccel::self()->readSettings( KGlobal::config().data() );
521 // FIXME Is this still needed with KDE4?
522 #if 0
523 //TODO fix kde accel system so that kactions find appropriate global shortcuts
524 // and there is only one configure shortcuts dialog
526 KActionCollection* const ac = Amarok::actionCollection();
527 KAccelShortcutList list( m_pGlobalAccel );
529 for( uint i = 0; i < list.count(); ++i )
531 KAction *action = ac->action( list.name( i ).toLatin1() );
533 if( action )
535 //this is a hack really, also it means there may be two calls to the slot for the shortcut
536 action->setShortcutConfigurable( false );
537 action->setShortcut( list.shortcut( i ) );
540 #endif
544 /////////////////////////////////////////////////////////////////////////////////////
545 // METHODS
546 /////////////////////////////////////////////////////////////////////////////////////
548 #include <id3v1tag.h>
549 #include <tbytevector.h>
550 #include <QTextCodec>
551 #include <KGlobal>
553 //this class is only used in this module, so I figured I may as well define it
554 //here and save creating another header/source file combination
556 class ID3v1StringHandler : public TagLib::ID3v1::StringHandler
558 QTextCodec *m_codec;
560 virtual TagLib::String parse( const TagLib::ByteVector &data ) const
562 return QStringToTString( m_codec->toUnicode( data.data(), data.size() ) );
565 virtual TagLib::ByteVector render( const TagLib::String &ts ) const
567 const QByteArray qcs = m_codec->fromUnicode( TStringToQString(ts) );
568 return TagLib::ByteVector( qcs, (uint) qcs.length() );
571 public:
572 ID3v1StringHandler( int codecIndex )
573 : m_codec( QTextCodec::codecForName( QTextCodec::availableCodecs().at( codecIndex ) ) )
575 debug() << "codec: " << m_codec;
576 debug() << "codec-name: " << m_codec->name();
579 ID3v1StringHandler( QTextCodec *codec )
580 : m_codec( codec )
582 debug() << "codec: " << m_codec;
583 debug() << "codec-name: " << m_codec->name();
586 virtual ~ID3v1StringHandler()
590 //SLOT
591 void App::applySettings( bool firstTime )
593 ///Called when the configDialog is closed with OK or Apply
595 DEBUG_BLOCK
597 #ifndef Q_WS_MAC
598 //probably needs to be done in TrayIcon when it receives a QEvent::ToolTip (see QSystemtrayIcon documentation)
599 //TrackToolTip::instance()->removeFromWidget( m_tray );
600 #endif
601 mainWindow()->applySettings();
602 Scrobbler::instance()->applySettings();
603 Amarok::OSD::instance()->applySettings();
604 CollectionDB::instance()->applySettings();
605 #ifndef Q_WS_MAC
606 m_tray->setVisible( AmarokConfig::showTrayIcon() );
607 //TrackToolTip::instance()->addToWidget( m_tray );
608 #endif
611 //on startup we need to show the window, but only if it wasn't hidden on exit
612 //and always if the trayicon isn't showing
613 QWidget* main_window = mainWindow();
614 #ifndef Q_WS_MAC
615 if( ( main_window && firstTime && !Amarok::config().readEntry( "HiddenOnExit", false ) ) || ( main_window && !AmarokConfig::showTrayIcon() ) )
616 #endif
618 main_window->show();
620 //takes longer but feels shorter. Crazy eh? :)
621 kapp->processEvents( QEventLoop::ExcludeUserInputEvents );
625 { //<Engine>
626 EngineBase *engine = EngineController::engine();
628 if( firstTime || AmarokConfig::soundSystem() !=
629 PluginManager::getService( engine )->property( "X-KDE-Amarok-name" ).toString() )
631 //will unload engine for us first if necessary
632 engine = EngineController::instance()->loadEngine();
635 engine->setXfadeLength( AmarokConfig::crossfade() ? AmarokConfig::crossfadeLength() : 0 );
636 engine->setVolume( AmarokConfig::masterVolume() );
638 engine->setEqualizerEnabled( AmarokConfig::equalizerEnabled() );
639 if ( AmarokConfig::equalizerEnabled() )
640 engine->setEqualizerParameters( AmarokConfig::equalizerPreamp(), AmarokConfig::equalizerGains() );
642 Amarok::actionCollection()->action("play_audiocd")->setEnabled( EngineController::hasEngineProperty( "HasKIO" ) || EngineController::hasEngineProperty("HasCDDA"));
643 } //</Engine>
645 { //<Collection>
646 //PORT 2.0 CollectionView::instance()->renderView(true);
647 } //</Collection>
648 { //<Context>
649 //PORT 2.0 ContextBrowser::instance()->renderView();
650 } //</Context>
652 { // delete unneeded cover images from cache
653 const QString size = QString::number( AmarokConfig::coverPreviewSize() ) + '@';
654 const QDir cacheDir = Amarok::saveLocation( "albumcovers/cache/" );
655 const QStringList obsoleteCovers = cacheDir.entryList( QStringList("*") );
656 foreach( QString it, obsoleteCovers )
657 if ( !it.startsWith( size ) && !it.startsWith( "50@" ) )
658 QFile( cacheDir.filePath( it ) ).remove();
661 //if ( !firstTime )
662 // Bizarrely and ironically calling this causes crashes for
663 // some people! FIXME
664 //AmarokConfig::self()->writeConfig();
668 //SLOT
669 void
670 App::continueInit()
672 DEBUG_BLOCK
673 const KCmdLineArgs* const args = KCmdLineArgs::parsedArgs();
674 bool restoreSession = args->count() == 0 || args->isSet( "append" ) || args->isSet( "enqueue" )
675 || Amarok::config().readEntry( "AppendAsDefault", false );
677 // Make this instance so it can start receiving signals
678 MoodServer::instance();
680 // Remember old folder setup, so we can detect changes after the wizard was used
681 //const QStringList oldCollectionFolders = MountPointManager::instance()->collectionFolders();
684 if ( Amarok::config().readEntry( "First Run", true ) || args->isSet( "wizard" ) ) {
685 std::cout << "STARTUP\n" << std::flush; //hide the splashscreen
686 firstRunWizard();
687 Amarok::config().writeEntry( "First Run", false );
688 Amarok::config().sync();
691 CollectionDB::instance()->checkDatabase();
693 The::SolidHandler()->Initialize();
694 m_mainWindow = new MainWindow();
695 #ifndef Q_WS_MAC
696 m_tray = new Amarok::TrayIcon( mainWindow() );
697 #endif
698 mainWindow()->init(); //creates the playlist, browsers, etc.
699 //init playlist window as soon as the database is guaranteed to be usable
700 //connect( CollectionDB::instance(), SIGNAL( databaseUpdateDone() ), mainWindow(), SLOT( init() ) );
701 //initGlobalShortcuts();
702 //load previous playlist in separate thread
703 if ( restoreSession && AmarokConfig::savePlaylist() )
705 The::playlistModel()->restoreSession();
707 if( args->isSet( "engine" ) ) {
708 // we correct some common errors (case issues, missing -engine off the end)
709 QString engine = args->getOption( "engine" ).toLower();
710 if( engine.startsWith( "gstreamer" ) ) engine = "gst-engine";
711 if( !engine.endsWith( "engine" ) ) engine += "-engine";
713 AmarokConfig::setSoundSystem( engine );
715 Debug::stamp();
716 //create engine, show TrayIcon etc.
717 applySettings( true );
718 Debug::stamp();
719 // Start ScriptManager. Must be created _after_ MainWindow.
720 ScriptManager::instance();
721 Debug::stamp();
722 //notify loader application that we have started
723 std::cout << "STARTUP\n" << std::flush;
725 //do after applySettings(), or the OSD will flicker and other wierdness!
726 //do before restoreSession()!
727 EngineController::instance()->attach( this );
729 //set a default interface
730 engineStateChanged( Engine::Empty );
732 if ( AmarokConfig::resumePlayback() && restoreSession && !args->isSet( "stop" ) ) {
733 //restore session as long as the user didn't specify media to play etc.
734 //do this after applySettings() so OSD displays correctly
735 EngineController::instance()->restoreSession();
738 // Refetch covers every 80 days to comply with Amazon license
739 new RefreshImages();
741 CollectionDB *collDB = CollectionDB::instance();
742 //Collection scan is triggered in firstRunWizard if the colelction folder setup was changed in the wizard
744 // If database version is updated, the collection needs to be rescanned.
745 // Works also if the collection is empty for some other reason
746 // (e.g. deleted collection.db)
747 if ( CollectionDB::instance()->isEmpty() )
749 //connect( collDB, SIGNAL( databaseUpdateDone() ), collDB, SLOT( startScan() ) );
750 collDB->startScan();
752 else if ( AmarokConfig::monitorChanges() )
753 //connect( collDB, SIGNAL( databaseUpdateDone() ), collDB, SLOT( scanModifiedDirs() ) );
754 collDB->scanModifiedDirs();
757 handleCliArgs();
759 delete m_splash;
760 m_splash = 0;
763 bool Amarok::genericEventHandler( QWidget *recipient, QEvent *e )
765 //this is used as a generic event handler for widgets that want to handle
766 //typical events in an Amarok fashion
768 //to use it just pass the event eg:
770 // void Foo::barEvent( QBarEvent *e )
771 // {
772 // Amarok::genericEventHandler( this, e );
773 // }
775 switch( e->type() )
777 case QEvent::DragEnter:
778 #define e static_cast<QDropEvent*>(e)
779 e->setAccepted( KUrl::List::canDecode( e->mimeData() ) );
780 break;
782 case QEvent::Drop:
784 KUrl::List list = KUrl::List::fromMimeData( e->mimeData() );
785 if( !list.isEmpty() )
787 Q3PopupMenu popup;
788 //FIXME this isn't a good way to determine if there is a currentTrack, need playlist() function
789 const bool b = EngineController::engine()->loaded();
791 popup.insertItem( KIcon( Amarok::icon( "add_playlist" ) ), i18n( "&Append to Playlist" ),
792 Playlist::Append );
793 popup.insertItem( KIcon( Amarok::icon( "add_playlist" ) ), i18n( "Append && &Play" ),
794 Playlist::DirectPlay | Playlist::Append );
795 if( b )
796 popup.insertItem( KIcon( Amarok::icon( "fast_forward" ) ), i18n( "&Queue Track" ),
797 Playlist::Queue );
798 popup.addSeparator();
799 popup.insertItem( i18n( "&Cancel" ), 0 );
801 const int id = popup.exec( recipient->mapToGlobal( e->pos() ) );
803 if ( id > 0 )
804 The::playlistModel()->insertMedia( list, id );
806 else return false;
807 #undef e
809 break;
812 //this like every entry in the generic event handler is used by more than one widget
813 //please don't remove!
814 case QEvent::Wheel:
816 #define e static_cast<QWheelEvent*>(e)
818 //this behaviour happens for the systray
819 //to override one, override it in that class
821 switch( e->state() )
823 case Qt::ControlModifier:
825 const bool up = e->delta() > 0;
827 //if this seems strange to you, please bring it up on #amarok
828 //for discussion as we can't decide which way is best!
829 if( up ) EngineController::instance()->previous();
830 else EngineController::instance()->next();
831 break;
833 case Qt::ShiftModifier:
835 EngineController::instance()->seekRelative( ( e->delta() / 120 ) * 5000 ); //5 seconds for keyboard seeking.
836 break;
838 default:
839 EngineController::instance()->increaseVolume( e->delta() / Amarok::VOLUME_SENSITIVITY );
842 e->accept();
843 #undef e
845 break;
848 case QEvent::Close:
850 //KDE policy states we should hide to tray and not quit() when the
851 //close window button is pushed for the main widget
853 static_cast<QCloseEvent*>(e)->accept(); //if we don't do this the info box appears on quit()!
855 if( AmarokConfig::showTrayIcon() && !e->spontaneous() && !kapp->sessionSaving() )
857 KMessageBox::information( recipient,
858 i18n( "<qt>Closing the main-window will keep Amarok running in the System Tray. "
859 "Use <B>Quit</B> from the menu, or the Amarok tray-icon to exit the application.</qt>" ),
860 i18n( "Docking in System Tray" ), "hideOnCloseInfo" );
862 else pApp->quit();
864 break;
866 default:
867 return false;
870 return true;
874 void App::engineStateChanged( Engine::State state, Engine::State oldState )
876 Meta::TrackPtr track = EngineController::instance()->currentTrack();
877 //track is 0 if the engien state is Empty. we check that in the switch
878 switch( state )
880 case Engine::Empty:
881 mainWindow()->setCaption( "Amarok" );
882 TrackToolTip::instance()->clear();
883 Amarok::OSD::instance()->setImage( QImage( KIconLoader().iconPath( "amarok", -K3Icon::SizeHuge ) ) );
884 break;
886 case Engine::Playing:
887 if ( oldState == Engine::Paused )
888 Amarok::OSD::instance()->OSDWidget::show( i18nc( "state, as in playing", "Play" ) );
889 if ( !track->prettyName().isEmpty() )
890 //TODO: write a utility function somewhere
891 mainWindow()->setCaption( i18n("Amarok - %1", /*bundle.veryNiceTitle()*/ track->prettyName() ) );
892 break;
894 case Engine::Paused:
895 Amarok::OSD::instance()->OSDWidget::show( i18n("Paused") );
896 break;
898 case Engine::Idle:
899 mainWindow()->setCaption( "Amarok" );
900 break;
902 default:
907 void App::engineNewMetaData( const MetaBundle &bundle, bool /*trackChanged*/ )
909 Amarok::OSD::instance()->show( bundle );
910 if ( !bundle.prettyTitle().isEmpty() )
911 mainWindow()->setCaption( i18n("Amarok - %1", bundle.veryNiceTitle() ) );
913 TrackToolTip::instance()->setTrack( bundle );
916 void App::engineTrackPositionChanged( long position, bool /*userSeek*/ )
918 TrackToolTip::instance()->setPos( position );
921 void App::engineVolumeChanged( int newVolume )
923 Amarok::OSD::instance()->OSDWidget::volChanged( newVolume );
926 void App::slotConfigEqualizer() //SLOT
928 EqualizerSetup::instance()->show();
929 EqualizerSetup::instance()->raise();
933 void App::slotConfigAmarok( const QByteArray& page )
935 DEBUG_THREAD_FUNC_INFO
937 Amarok2ConfigDialog* dialog = static_cast<Amarok2ConfigDialog*>( KConfigDialog::exists( "settings" ) );
939 if( !dialog )
941 //KConfigDialog didn't find an instance of this dialog, so lets create it :
942 dialog = new Amarok2ConfigDialog( mainWindow(), "settings", AmarokConfig::self() );
944 connect( dialog, SIGNAL(settingsChanged(const QString&)), SLOT(applySettings()) );
947 //FIXME it seems that if the dialog is on a different desktop it gets lost
948 // what do to? detect and move it?
950 // if ( page.isNull() )
951 // FIXME
952 // dialog->showPage( AmarokConfigDialog::s_currentPage );
953 // else
954 dialog->showPageByName( page );
956 dialog->show();
957 dialog->raise();
958 dialog->activateWindow();
961 void App::slotConfigShortcuts()
963 KShortcutsDialog::configure( Amarok::actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, mainWindow() );
966 void App::slotConfigToolBars()
968 KEditToolBar dialog( mainWindow()->actionCollection(), mainWindow() );
969 dialog.setResourceFile( mainWindow()->xmlFile() );
971 dialog.showButton( KEditToolBar::Apply, false );
973 // if( dialog.exec() )
974 // {
975 // mainWindow()->reloadXML();
976 // mainWindow()->createGUI();
977 // }
980 void App::firstRunWizard()
982 #if 0
983 ///show firstRunWizard
984 DEBUG_BLOCK
986 FirstRunWizard wizard;
987 setTopWidget( &wizard );
988 KConfigDialogManager* config = new KConfigDialogManager(&wizard, AmarokConfig::self(), "wizardconfig");
989 config->updateWidgets();
990 // connect(config, SIGNAL(settingsChanged()), SLOT(updateSettings()));
991 wizard.setCaption( makeStdCaption( i18n( "First-Run Wizard" ) ) );
993 if( wizard.exec() != QDialog::Rejected )
995 //make sure that the DB config is stored in amarokrc before calling CollectionDB's ctor
996 AmarokConfig::setDatabaseEngine(
997 QString::number( Amarok::databaseTypeCode( wizard.dbSetup7->databaseEngine->currentText() ) ) );
998 config->updateSettings();
1000 const QStringList oldCollectionFolders = MountPointManager::instance()->collectionFolders();
1001 wizard.writeCollectionConfig();
1003 // If wizard is invoked at runtime, rescan collection if folder setup has changed
1004 if ( !Amarok::config().readEntry( "First Run", true ) &&
1005 oldCollectionFolders != MountPointManager::instance()->collectionFolders() )
1006 CollectionDB::instance()->startScan();
1009 #endif
1012 void App::setUseScores( bool use )
1014 AmarokConfig::setUseScores( use );
1015 emit useScores( use );
1018 void App::setUseRatings( bool use )
1020 AmarokConfig::setUseRatings( use );
1021 emit useRatings( use );
1024 void App::setMoodbarPrefs( bool show, bool moodier, int alter, bool withMusic )
1026 AmarokConfig::setShowMoodbar( show );
1027 AmarokConfig::setMakeMoodier( moodier );
1028 AmarokConfig::setAlterMood( alter );
1029 AmarokConfig::setMoodsWithMusic( withMusic );
1030 emit moodbarPrefs( show, moodier, alter, withMusic );
1033 KIO::Job *App::trashFiles( const KUrl::List &files )
1035 KIO::Job *job = KIO::trash( files, true /*show progress*/ );
1036 Amarok::StatusBar::instance()->newProgressOperation( job ).setDescription( i18n("Moving files to trash") );
1037 connect( job, SIGNAL( result( KJob* ) ), this, SLOT( slotTrashResult( KJob* ) ) );
1038 return job;
1041 void App::setRating( int n )
1043 if( !AmarokConfig::useRatings() ) return;
1045 n *= 2;
1047 const Engine::State s = EngineController::instance()->engine()->state();
1048 if( s == Engine::Playing || s == Engine::Paused || s == Engine::Idle )
1050 Meta::TrackPtr track = EngineController::instance()->currentTrack();
1051 track->setRating( n );
1052 Amarok::OSD::instance()->OSDWidget::ratingChanged( track->rating() );
1054 else if( MainWindow::self()->isReallyShown() && Playlist::instance()->qscrollview()->hasFocus() )
1055 Playlist::instance()->setSelectedRatings( n );
1058 void App::slotTrashResult( KJob *job )
1060 if( job->error() )
1061 job->uiDelegate()->showErrorMessage();
1064 void App::quit()
1066 emit prepareToQuit();
1067 if( MediaBrowser::instance() && MediaBrowser::instance()->blockQuit() )
1069 // don't quit yet, as some media devices still have to finish transferring data
1070 QTimer::singleShot( 100, this, SLOT( quit() ) );
1071 return;
1073 KApplication::quit();
1076 namespace Amarok
1078 /// @see amarok.h
1080 //TODO remove these, they suck, do a generic getImage
1082 QPixmap getPNG( const QString &filename )
1084 QString file = !filename.endsWith( ".png", Qt::CaseInsensitive ) ? "amarok/images/%1.png" : "amarok/images/%1";
1086 return QPixmap( KStandardDirs::locate( "data", file.arg( filename ) ), "PNG" );
1089 QPixmap getJPG( const QString &filename )
1091 QString file = !filename.endsWith( ".jpg", Qt::CaseInsensitive ) ? "amarok/images/%1.jpg" : "amarok/images/%1";
1093 return QPixmap( KStandardDirs::locate( "data", QString( "amarok/images/%1.jpg" ).arg( filename ) ), "JPEG" );
1096 QWidget *mainWindow()
1098 return pApp->mainWindow();
1101 KActionCollection *actionCollection()
1103 return pApp->mainWindow()->actionCollection();
1106 KConfigGroup config( const QString &group )
1108 //Slightly more useful config() that allows setting the group simultaneously
1109 // KGlobal::config()->setGroup( group );
1110 KConfigGroup configGroup = KGlobal::config()->group( group );
1111 return configGroup;
1114 bool invokeBrowser( const QString& url )
1116 //URL can be in whatever forms KUrl understands - ie most.
1117 const QString cmd = KShell::quoteArg(AmarokConfig::externalBrowser())
1118 + ' ' + KShell::quoteArg(KUrl( url ).url());
1119 return KRun::runCommand( cmd, 0L ) > 0;
1122 namespace ColorScheme
1124 QColor Base;
1125 QColor Text;
1126 QColor Background;
1127 QColor Foreground;
1128 QColor AltBase;
1131 OverrideCursor::OverrideCursor( Qt::CursorShape cursor )
1133 QApplication::setOverrideCursor( cursor == Qt::WaitCursor ?
1134 Qt::WaitCursor :
1135 Qt::BusyCursor );
1138 OverrideCursor::~OverrideCursor()
1140 QApplication::restoreOverrideCursor();
1143 QString saveLocation( const QString &directory )
1145 globalDirsMutex.lock();
1146 QString result = KGlobal::dirs()->saveLocation( "data", QString("amarok/") + directory, true );
1147 globalDirsMutex.unlock();
1148 return result;
1151 QString cleanPath( const QString &path )
1153 QString result = path;
1154 // german umlauts
1155 result.replace( QChar(0x00e4), "ae" ).replace( QChar(0x00c4), "Ae" );
1156 result.replace( QChar(0x00f6), "oe" ).replace( QChar(0x00d6), "Oe" );
1157 result.replace( QChar(0x00fc), "ue" ).replace( QChar(0x00dc), "Ue" );
1158 result.replace( QChar(0x00df), "ss" );
1160 // some strange accents
1161 result.replace( QChar(0x00e7), "c" ).replace( QChar(0x00c7), "C" );
1162 result.replace( QChar(0x00fd), "y" ).replace( QChar(0x00dd), "Y" );
1163 result.replace( QChar(0x00f1), "n" ).replace( QChar(0x00d1), "N" );
1165 // czech letters with carons
1166 result.replace( QChar(0x0161), "s" ).replace( QChar(0x0160), "S" );
1167 result.replace( QChar(0x010d), "c" ).replace( QChar(0x010c), "C" );
1168 result.replace( QChar(0x0159), "r" ).replace( QChar(0x0158), "R" );
1169 result.replace( QChar(0x017e), "z" ).replace( QChar(0x017d), "Z" );
1170 result.replace( QChar(0x0165), "t" ).replace( QChar(0x0164), "T" );
1171 result.replace( QChar(0x0148), "n" ).replace( QChar(0x0147), "N" );
1172 result.replace( QChar(0x010f), "d" ).replace( QChar(0x010e), "D" );
1174 // accented vowels
1175 QChar a[] = { 'a', 0xe0,0xe1,0xe2,0xe3,0xe5, 0 };
1176 QChar A[] = { 'A', 0xc0,0xc1,0xc2,0xc3,0xc5, 0 };
1177 QChar E[] = { 'e', 0xe8,0xe9,0xea,0xeb,0x11a, 0 };
1178 QChar e[] = { 'E', 0xc8,0xc9,0xca,0xcb,0x11b, 0 };
1179 QChar i[] = { 'i', 0xec,0xed,0xee,0xef, 0 };
1180 QChar I[] = { 'I', 0xcc,0xcd,0xce,0xcf, 0 };
1181 QChar o[] = { 'o', 0xf2,0xf3,0xf4,0xf5,0xf8, 0 };
1182 QChar O[] = { 'O', 0xd2,0xd3,0xd4,0xd5,0xd8, 0 };
1183 QChar u[] = { 'u', 0xf9,0xfa,0xfb,0x16e, 0 };
1184 QChar U[] = { 'U', 0xd9,0xda,0xdb,0x16f, 0 };
1185 QChar nul[] = { 0 };
1186 QChar *replacements[] = { a, A, e, E, i, I, o, O, u, U, nul };
1188 for( int i = 0; i < result.length(); i++ )
1190 QChar c = result[ i ];
1191 for( uint n = 0; replacements[n][0] != QChar(0); n++ )
1193 for( uint k=0; replacements[n][k] != QChar(0); k++ )
1195 if( replacements[n][k] == c )
1197 c = replacements[n][0];
1201 result[ i ] = c;
1203 return result;
1206 QString asciiPath( const QString &path )
1208 QString result = path;
1209 for( int i = 0; i < result.length(); i++ )
1211 QChar c = result[ i ];
1212 if( c > QChar(0x7f) || c == QChar(0) )
1214 c = '_';
1216 result[ i ] = c;
1218 return result;
1221 QString vfatPath( const QString &path )
1223 QString s = path;
1225 for( int i = 0; i < s.length(); i++ )
1227 QChar c = s[ i ];
1228 if( c < QChar(0x20)
1229 || c=='*' || c=='?' || c=='<' || c=='>'
1230 || c=='|' || c=='"' || c==':' || c=='/'
1231 || c=='\\' )
1232 c = '_';
1233 s[ i ] = c;
1236 uint len = s.length();
1237 if( len == 3 || (len > 3 && s[3] == '.') )
1239 QString l = s.left(3).toLower();
1240 if( l=="aux" || l=="con" || l=="nul" || l=="prn" )
1241 s = '_' + s;
1243 else if( len == 4 || (len > 4 && s[4] == '.') )
1245 QString l = s.left(3).toLower();
1246 QString d = s.mid(3,1);
1247 if( (l=="com" || l=="lpt") &&
1248 (d=="0" || d=="1" || d=="2" || d=="3" || d=="4" ||
1249 d=="5" || d=="6" || d=="7" || d=="8" || d=="9") )
1250 s = '_' + s;
1253 while( s.startsWith( '.' ) )
1254 s = s.mid(1);
1256 while( s.endsWith( '.' ) )
1257 s = s.left( s.length()-1 );
1259 s = s.left(255);
1260 len = s.length();
1261 if( s[len-1] == ' ' )
1262 s[len-1] = '_';
1264 return s;
1267 QString decapitateString( const QString &input, const QString &ref )
1269 QString t = ref.toUpper();
1270 int length = t.length();
1271 int commonLength = 0;
1272 while( length > 0 )
1274 if ( input.toUpper().startsWith( t ) )
1276 commonLength = t.length();
1277 t = ref.toUpper().left( t.length() + length/2 );
1278 length = length/2;
1280 else
1282 t = ref.toUpper().left( t.length() - length/2 );
1283 length = length/2;
1286 QString clean = input;
1287 if( t.endsWith( ' ' ) || !ref.at( t.length() ).isLetterOrNumber() ) // common part ends with a space or complete word
1288 clean = input.right( input.length() - commonLength ).trimmed();
1289 return clean;
1292 void setUseScores( bool use ) { App::instance()->setUseScores( use ); }
1293 void setUseRatings( bool use ) { App::instance()->setUseRatings( use ); }
1294 void setMoodbarPrefs( bool show, bool moodier, int alter, bool withMusic )
1295 { App::instance()->setMoodbarPrefs( show, moodier, alter, withMusic ); }
1296 KIO::Job *trashFiles( const KUrl::List &files ) { return App::instance()->trashFiles( files ); }
1299 #include "app.moc"