Contents of /trunk/extragear/multimedia/amarok/src/app.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 634709 - (show annotations) (download) (as text)
Sun Feb 18 01:47:17 2007 UTC (10 years, 10 months ago) by dmeltzer
File MIME type: text/x-c++src
File size: 51072 byte(s)
s/smalliconset/kicon as smalliconset is deprecated
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 ***************************************************************************/
8
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
18 #include "amarok.h"
19 #include "amarokconfig.h"
20 #include "amarokdbushandler.h"
21 #include "app.h"
22 #include "atomicstring.h"
23 #include "config.h"
24 #include "configdialog.h"
25 #include "contextbrowser.h"
26 #include "collectionbrowser.h"
27 //#include "dbsetup.h" //firstRunWizard()
28 #include "debug.h"
29 #include "devicemanager.h"
30 #include "mediadevicemanager.h"
31 #include "enginebase.h"
32 #include "enginecontroller.h"
33 #include "equalizersetup.h"
34 //#include "firstrunwizard.h"
35 #include "mediabrowser.h"
36 #include "metabundle.h"
37 #include "mountpointmanager.h"
38 #include "osd.h"
39 #include "playlist.h"
40 #include "playlistbrowser.h"
41 #include "playlistwindow.h"
42 #include "pluginmanager.h"
43 #include "refreshimages.h"
44 #include "scriptmanager.h"
45 #include "scrobbler.h"
46 #include "sidebar.h"
47 #include "statusbar.h"
48 #include "systray.h"
49 #include "threadmanager.h"
50 #include "tracktooltip.h" //engineNewMetaData()
51
52 #include <iostream>
53
54 #include <kaction.h>
55 #include <kconfigdialogmanager.h>
56 #include <kcombobox.h> //firstRunWizard()
57 #include <kcmdlineargs.h> //initCliArgs()
58 #include <kcursor.h> //Amarok::OverrideCursor
59 #include <kedittoolbar.h> //slotConfigToolbars()
60 #include <kglobalaccel.h> //initGlobalShortcuts()
61 #include <kglobalsettings.h> //applyColorScheme()
62 #include <kiconloader.h> //amarok Icon
63 #include <kjob.h>
64 #include <kjobuidelegate.h>
65 #include <kkeydialog.h> //slotConfigShortcuts()
66 #include <klocale.h>
67 #include <kmessagebox.h> //applySettings(), genericEventHandler()
68 #include <krun.h> //Amarok::invokeBrowser()
69 #include <kstandarddirs.h>
70 #include <kaboutdata.h>
71 #include <kio/copyjob.h>
72
73 #include <QEvent> //genericEventHandler()
74 #include <QEventLoop> //applySettings()
75 #include <QFile>
76 #include <QObject> //applyColorScheme()
77 #include <QPalette> //applyColorScheme()
78 #include <q3popupmenu.h> //genericEventHandler
79 #include <QTimer> //showHyperThreadingWarning()
80 #include <QToolTip> //default tooltip for trayicon
81 //Added by qt3to4:
82 #include <QCloseEvent>
83 #include <Q3CString>
84 #include <QDBusInterface>
85 #include <QDBusReply>
86
87 QMutex Debug::mutex;
88 QMutex Amarok::globalDirsMutex;
89
90 int App::mainThreadId = 0;
91
92 #ifdef Q_WS_MAC
93 #include <Carbon/Carbon.h>
94
95 static AEEventHandlerUPP appleEventProcessorUPP = 0;
96
97 OSStatus
98 appleEventProcessor(const AppleEvent *ae, AppleEvent *, long /*handlerRefCon*/)
99 {
100 OSType aeID = typeWildCard;
101 OSType aeClass = typeWildCard;
102 AEGetAttributePtr(ae, keyEventClassAttr, typeType, 0, &aeClass, sizeof(aeClass), 0);
103 AEGetAttributePtr(ae, keyEventIDAttr, typeType, 0, &aeID, sizeof(aeID), 0);
104
105 if(aeClass == kCoreEventClass)
106 {
107 if(aeID == kAEReopenApplication)
108 {
109 if( PlaylistWindow::self() )
110 PlaylistWindow::self()->show();
111 }
112 return noErr;
113 }
114 return eventNotHandledErr;
115 }
116 #endif
117
118 AMAROK_EXPORT KAboutData aboutData( "amarok",
119 I18N_NOOP( "Amarok" ), APP_VERSION,
120 I18N_NOOP( "The audio player for KDE" ), KAboutData::License_GPL,
121 I18N_NOOP( "(C) 2002-2003, Mark Kretschmann\n(C) 2003-2007, The Amarok Development Squad" ),
122 I18N_NOOP( "IRC:\nirc.freenode.net - #amarok, #amarok.de, #amarok.es\n\nFeedback:\namarok@kde.org\n\n(Build Date: " __DATE__ ")" ),
123 ( "http://amarok.kde.org" ) );
124
125 App::App()
126 : KApplication()
127 {
128 DEBUG_BLOCK
129
130 qRegisterMetaType<MetaBundle>();
131
132 #ifdef Q_WS_MAC
133 // this is inspired by OpenSceneGraph: osgDB/FilePath.cpp
134
135 // Start with the the Bundle PlugIns directory.
136
137 // Get the main bundle first. No need to retain or release it since
138 // we are not keeping a reference
139 CFBundleRef myBundle = CFBundleGetMainBundle();
140 if( myBundle )
141 {
142 // CFBundleGetMainBundle will return a bundle ref even if
143 // the application isn't part of a bundle, so we need to
144 // check
145 // if the path to the bundle ends in ".app" to see if it is
146 // a
147 // proper application bundle. If it is, the plugins path is
148 // added
149 CFURLRef urlRef = CFBundleCopyBundleURL(myBundle);
150 if(urlRef)
151 {
152 char bundlePath[1024];
153 if( CFURLGetFileSystemRepresentation( urlRef, true, (UInt8 *)bundlePath, sizeof(bundlePath) ) )
154 {
155 Q3CString bp( bundlePath );
156 size_t len = bp.length();
157 if( len > 4 && bp.right( 4 ) == ".app" )
158 {
159 bp.append( "/Contents/MacOS" );
160 Q3CString path = getenv( "PATH" );
161 if( path.length() > 0 )
162 {
163 path.prepend( ":" );
164 }
165 path.prepend( bp );
166 debug() << "setting PATH=" << path << endl;
167 setenv("PATH", path, 1);
168 }
169 }
170 // docs say we are responsible for releasing CFURLRef
171 CFRelease(urlRef);
172 }
173 }
174 #endif
175
176 //needs to be created before the wizard
177 new Amarok::DbusPlayerHandler(); // Must be created first
178 new Amarok::DbusPlaylistHandler();
179 new Amarok::DbusPlaylistBrowserHandler();
180 new Amarok::DbusContextBrowserHandler();
181 new Amarok::DbusCollectionHandler();
182 new Amarok::DbusMediaBrowserHandler();
183 new Amarok::DbusScriptHandler();
184 new Amarok::DbusDevicesHandler();
185
186 // tell AtomicString that this is the GUI thread
187 if ( !AtomicString::isMainThread() )
188 qWarning("AtomicString was initialized from a thread other than the GUI "
189 "thread. This could lead to memory leaks.");
190
191 #ifdef Q_WS_MAC
192 appleEventProcessorUPP = AEEventHandlerUPP(appleEventProcessor);
193 AEInstallEventHandler(kCoreEventClass, kAEReopenApplication, appleEventProcessorUPP, (long)this, true);
194 #endif
195 QDBusConnection::sessionBus().registerService("org.kde.amarok");
196 QTimer::singleShot( 0, this, SLOT( continueInit() ) );
197 }
198
199 App::~App()
200 {
201 DEBUG_BLOCK
202
203 // Hiding the OSD before exit prevents crash
204 Amarok::OSD::instance()->hide();
205
206 EngineBase* const engine = EngineController::engine();
207
208 if ( AmarokConfig::resumePlayback() ) {
209 if ( engine->state() != Engine::Empty ) {
210 AmarokConfig::setResumeTrack( EngineController::instance()->playingURL().prettyUrl() );
211 AmarokConfig::setResumeTime( engine->position() );
212 }
213 else AmarokConfig::setResumeTrack( QString::null ); //otherwise it'll play previous resume next time!
214 }
215
216 EngineController::instance()->endSession(); //records final statistics
217 EngineController::instance()->detach( this );
218
219 // do even if trayicon is not shown, it is safe
220 Amarok::config()->writeEntry( "HiddenOnExit", mainWindow()->isHidden() );
221
222 CollectionDB::instance()->stopScan();
223
224 delete m_pPlaylistWindow; //sets some XT keys
225
226 ThreadManager::deleteInstance(); //waits for jobs to finish
227
228 // this must be deleted before the connection to the Xserver is
229 // severed, or we risk a crash when the QApplication is exited,
230 // I asked Trolltech! *smug*
231 delete Amarok::OSD::instance();
232
233 AmarokConfig::setVersion( APP_VERSION );
234 AmarokConfig::writeConfig();
235
236 //need to unload the engine before the kapplication is destroyed
237 PluginManager::unload( engine );
238 }
239
240
241 #include <QStringList>
242
243 namespace
244 {
245 // grabbed from KsCD source, kompatctdisk.cpp
246 QString urlToDevice(const QString& device)
247 {
248 KUrl deviceUrl(device);
249 if (deviceUrl.protocol() == "media" || deviceUrl.protocol() == "system")
250 {
251 debug() << "WARNING: urlToDevice needs to be reimplemented with KDE4 technology, it's just a stub at the moment" << endl;
252 QDBusInterface mediamanager( "org.kde.kded", "/modules/mediamanager", "org.kde.MediaManager" );
253 QDBusReply<QStringList> reply = mediamanager.call( "properties",deviceUrl.fileName() );
254 if (!reply.isValid()) {
255 debug() << "Invalid reply from mediamanager" << endl;
256 return QString();
257 }
258 QStringList properties = reply;
259 if( properties.count()< 6 )
260 return QString();
261 debug() << "Reply from mediamanager " << properties[5] << endl;
262 return properties[5];
263 }
264
265 return device;
266 }
267
268 }
269
270
271 void App::handleCliArgs() //static
272 {
273 static char cwd[PATH_MAX];
274 KCmdLineArgs* const args = KCmdLineArgs::parsedArgs();
275
276 if ( args->isSet( "cwd" ) )
277 {
278 strncpy(cwd, args->getOption( "cwd" ), sizeof(cwd) );
279 cwd[sizeof(cwd)-1] = '\0';
280 KCmdLineArgs::setCwd( cwd );
281 }
282
283 bool haveArgs = false;
284 if ( args->count() > 0 )
285 {
286 haveArgs = true;
287
288 KUrl::List list;
289 for( int i = 0; i < args->count(); i++ )
290 {
291 KUrl url = args->url( i );
292 if( url.protocol() == "itpc" || url.protocol() == "pcast" )
293 PlaylistBrowser::instance()->addPodcast( url );
294 else
295 list << url;
296 }
297
298 int options = Playlist::DefaultOptions;
299 if( args->isSet( "queue" ) )
300 options = Playlist::Queue;
301 else if( args->isSet( "append" ) || args->isSet( "enqueue" ) )
302 options = Playlist::Append;
303 else if( args->isSet( "load" ) )
304 options = Playlist::Replace;
305
306 if( args->isSet( "play" ) )
307 options |= Playlist::DirectPlay;
308
309 Playlist::instance()->insertMedia( list, options );
310 }
311
312 //we shouldn't let the user specify two of these since it is pointless!
313 //so we prioritise, pause > stop > play > next > prev
314 //thus pause is the least destructive, followed by stop as brakes are the most important bit of a car(!)
315 //then the others seemed sensible. Feel free to modify this order, but please leave justification in the cvs log
316 //I considered doing some sanity checks (eg only stop if paused or playing), but decided it wasn't worth it
317 else if ( args->isSet( "pause" ) )
318 {
319 haveArgs = true;
320 EngineController::instance()->pause();
321 }
322 else if ( args->isSet( "stop" ) )
323 {
324 haveArgs = true;
325 EngineController::instance()->stop();
326 }
327 else if ( args->isSet( "play-pause" ) )
328 {
329 haveArgs = true;
330 EngineController::instance()->playPause();
331 }
332 else if ( args->isSet( "play" ) ) //will restart if we are playing
333 {
334 haveArgs = true;
335 EngineController::instance()->play();
336 }
337 else if ( args->isSet( "next" ) )
338 {
339 haveArgs = true;
340 EngineController::instance()->next();
341 }
342 else if ( args->isSet( "previous" ) )
343 {
344 haveArgs = true;
345 EngineController::instance()->previous();
346 }
347 else if (args->isSet("cdplay"))
348 {
349 haveArgs = true;
350 QString device = args->getOption("cdplay");
351 device = DeviceManager::instance()->convertMediaUrlToDevice( device );
352 KUrl::List urls;
353 if (EngineController::engine()->getAudioCDContents(device, urls)) {
354 Playlist::instance()->insertMedia(
355 urls, Playlist::Replace|Playlist::DirectPlay);
356 } else { // Default behaviour
357 debug() <<
358 "Sorry, the engine doesn't support direct play from AudioCD..."
359 << endl;
360 }
361 }
362
363 if ( args->isSet( "toggle-playlist-window" ) )
364 {
365 haveArgs = true;
366 pApp->m_pPlaylistWindow->showHide();
367 }
368
369 static bool firstTime = true;
370 if( !firstTime && !haveArgs )
371 pApp->m_pPlaylistWindow->activate();
372 firstTime = false;
373
374 args->clear(); //free up memory
375 }
376
377
378 /////////////////////////////////////////////////////////////////////////////////////
379 // INIT
380 /////////////////////////////////////////////////////////////////////////////////////
381
382 void App::initCliArgs( int argc, char *argv[] ) //static
383 {
384 static KCmdLineOptions options[] =
385 {
386 { "+[URL(s)]", I18N_NOOP( "Files/URLs to open" ), 0 },
387 { "r", 0, 0 },
388 { "previous", I18N_NOOP( "Skip backwards in playlist" ), 0 },
389 { "p", 0, 0 },
390 { "play", I18N_NOOP( "Start playing current playlist" ), 0 },
391 { "t", 0, 0 },
392 { "play-pause", I18N_NOOP( "Play if stopped, pause if playing" ), 0 },
393 { "pause", I18N_NOOP( "Pause playback" ), 0 },
394 { "s", 0, 0 },
395 { "stop", I18N_NOOP( "Stop playback" ), 0 },
396 { "f", 0, 0 },
397 { "next", I18N_NOOP( "Skip forwards in playlist" ), 0 },
398 { ":", I18N_NOOP("Additional options:"), 0 },
399 { "a", 0, 0 },
400 { "append", I18N_NOOP( "Append files/URLs to playlist" ), 0 },
401 { "e", 0, 0 },
402 { "enqueue", I18N_NOOP("See append, available for backwards compatability"), 0 },
403 { "queue", I18N_NOOP("Queue URLs after the currently playing track"), 0 },
404 { "l", 0, 0 },
405 { "load", I18N_NOOP("Load URLs, replacing current playlist"), 0 },
406 { "m", 0, 0 },
407 { "toggle-playlist-window", I18N_NOOP("Toggle the Playlist-window"), 0 },
408 { "wizard", I18N_NOOP( "Run first-run wizard" ), 0 },
409 { "engine <name>", I18N_NOOP( "Use the <name> engine" ), 0 },
410 { "cwd <directory>", I18N_NOOP( "Base for relative filenames/URLs" ), 0 },
411 { "cdplay <device>", I18N_NOOP("Play an AudioCD from <device>"), 0 },
412 //FIXME: after string freeze { "cdplay <device>", I18N_NOOP("Play an AudioCD from <device> or system:/media/<device>"), 0 },
413 { 0, 0, 0 }
414 };
415
416 KCmdLineArgs::reset();
417 KCmdLineArgs::init( argc, argv, &::aboutData ); //calls KCmdLineArgs::addStdCmdLineOptions()
418 KCmdLineArgs::addCmdLineOptions( options ); //add our own options
419 }
420
421
422 void App::initGlobalShortcuts()
423 {
424 EngineController* const ec = EngineController::instance();
425 KAction* action;
426
427 // m_pGlobalAccel->insert( "play", i18n( "Play" ), 0, KKey("WIN+x"), 0, ec, SLOT( play() ), true, true );
428 action = new KAction( i18n( "Play" ), m_pPlaylistWindow );
429 action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_X ) );
430 connect( action, SIGNAL( triggered() ), ec, SLOT( play() ) );
431
432 // m_pGlobalAccel->insert( "pause", i18n( "Pause" ), 0, 0, 0, ec, SLOT( pause() ), true, true );
433 action = new KAction( i18n( "Pause" ), m_pPlaylistWindow );
434 connect( action, SIGNAL( triggered() ), ec, SLOT( pause() ) );
435
436 // m_pGlobalAccel->insert( "play_pause", i18n( "Play/Pause" ), 0, KKey("WIN+c"), 0, ec, SLOT( playPause() ), true, true );
437 action = new KAction( i18n( "Play/Pause" ), m_pPlaylistWindow );
438 action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_C ) );
439 connect( action, SIGNAL( triggered() ), ec, SLOT( playPause() ) );
440
441 // m_pGlobalAccel->insert( "stop", i18n( "Stop" ), 0, KKey("WIN+v"), 0, ec, SLOT( stop() ), true, true );
442 action = new KAction( i18n( "Stop" ), m_pPlaylistWindow );
443 action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_V ) );
444 connect( action, SIGNAL( triggered() ), ec, SLOT( stop() ) );
445
446 // 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 );
447 action = new KAction( i18n( "Stop Playing After Current Track" ), m_pPlaylistWindow );
448 action->setGlobalShortcut( KShortcut( Qt::META + Qt::CTRL + Qt::Key_V ) );
449 connect( action, SIGNAL( triggered() ), Playlist::instance()->qscrollview(), SLOT( toggleStopAfterCurrentTrack() ) );
450
451 // m_pGlobalAccel->insert( "next", i18n( "Next Track" ), 0, KKey("WIN+b"), 0, ec, SLOT( next() ), true, true );
452 action = new KAction( i18n( "Next Track" ), m_pPlaylistWindow );
453 action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_B ) );
454 connect( action, SIGNAL( triggered() ), ec, SLOT( next() ) );
455
456 // m_pGlobalAccel->insert( "prev", i18n( "Previous Track" ), 0, KKey("WIN+z"), 0, ec, SLOT( previous() ), true, true );
457 action = new KAction( i18n( "Previous Track" ), m_pPlaylistWindow );
458 action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_Z ) );
459 connect( action, SIGNAL( triggered() ), ec, SLOT( previous() ) );
460
461 // m_pGlobalAccel->insert( "volup", i18n( "Increase Volume" ), 0, KKey("WIN+KP_Add"), 0, ec, SLOT( increaseVolume() ), true, true );
462 action = new KAction( i18n( "Increase Volume" ), m_pPlaylistWindow );
463 action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_Plus ) );
464 connect( action, SIGNAL( triggered() ), ec, SLOT( increaseVolume() ) );
465
466 // m_pGlobalAccel->insert( "voldn", i18n( "Decrease Volume" ), 0, KKey("WIN+KP_Subtract"), 0, ec, SLOT( decreaseVolume() ), true, true );
467 action = new KAction( i18n( "Decrease Volume" ), m_pPlaylistWindow );
468 action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_Minus ) );
469 connect( action, SIGNAL( triggered() ), ec, SLOT( decreaseVolume() ) );
470
471
472 // m_pGlobalAccel->insert( "seekforward", i18n( "Seek Forward" ), 0, KKey("WIN+Shift+KP_Add"), 0, ec, SLOT( seekForward() ), true, true );
473 action = new KAction( i18n( "Seek Forward" ), m_pPlaylistWindow );
474 action->setGlobalShortcut( KShortcut( Qt::META + Qt::SHIFT + Qt::Key_Plus ) );
475 connect( action, SIGNAL( triggered() ), ec, SLOT( seekForward() ) );
476
477
478 // m_pGlobalAccel->insert( "seekbackward", i18n( "Seek Backward" ), 0, KKey("WIN+Shift+KP_Subtract"), 0, ec, SLOT( seekBackward() ), true, true );
479 action = new KAction( i18n( "Seek Backward" ), m_pPlaylistWindow );
480 action->setGlobalShortcut( KShortcut( Qt::META + Qt::SHIFT + Qt::Key_Minus ) );
481 connect( action, SIGNAL( triggered() ), ec, SLOT( seekBackward() ) );
482
483 // m_pGlobalAccel->insert( "playlist_add", i18n( "Add Media..." ), 0, KKey("WIN+a"), 0, m_pPlaylistWindow, SLOT( slotAddLocation() ), true, true );
484 action = new KAction( i18n( "Add Media..." ), m_pPlaylistWindow );
485 action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_A ) );
486 connect( action, SIGNAL( triggered() ), m_pPlaylistWindow, SLOT( slotAddLocation() ) );
487
488 // m_pGlobalAccel->insert( "show", i18n( "Toggle Playlist Window" ), 0, KKey("WIN+p"), 0, m_pPlaylistWindow, SLOT( showHide() ), true, true );
489 action = new KAction( i18n( "Toggle Playlist Window" ), m_pPlaylistWindow );
490 action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_P ) );
491 connect( action, SIGNAL( triggered() ), m_pPlaylistWindow, SLOT( showHide() ) );
492
493 #ifdef Q_WS_X11
494 // m_pGlobalAccel->insert( "osd", i18n( "Show OSD" ), 0, KKey("WIN+o"), 0, Amarok::OSD::instance(), SLOT( forceToggleOSD() ), true, true );
495 action = new KAction( i18n( "Show OSD" ), m_pPlaylistWindow );
496 action->setGlobalShortcut( KShortcut( Qt::META + Qt::Key_O ) );
497 connect( action, SIGNAL( triggered() ), Amarok::OSD::instance(), SLOT( forceToggleOSD() ) );
498 #endif
499 #if 0
500 m_pGlobalAccel->insert( "mute", i18n( "Mute Volume" ), 0, KKey("WIN+m"), 0,
501 ec, SLOT( mute() ), true, true );
502 m_pGlobalAccel->insert( "rating1", i18n( "Rate Current Track: 1" ), 0, KKey("WIN+1"), 0,
503 this, SLOT( setRating1() ), true, true );
504 m_pGlobalAccel->insert( "rating2", i18n( "Rate Current Track: 2" ), 0, KKey("WIN+2"), 0,
505 this, SLOT( setRating2() ), true, true );
506 m_pGlobalAccel->insert( "rating3", i18n( "Rate Current Track: 3" ), 0, KKey("WIN+3"), 0,
507 this, SLOT( setRating3() ), true, true );
508 m_pGlobalAccel->insert( "rating4", i18n( "Rate Current Track: 4" ), 0, KKey("WIN+4"), 0,
509 this, SLOT( setRating4() ), true, true );
510 m_pGlobalAccel->insert( "rating5", i18n( "Rate Current Track: 5" ), 0, KKey("WIN+5"), 0,
511 this, SLOT( setRating5() ), true, true );
512 #endif
513
514 // KGlobalAccel::self()->setConfigGroup( "Shortcuts" );
515 // KGlobalAccel::self()->readSettings( KGlobal::config().data() );
516
517
518 // FIXME Is this still needed with KDE4?
519 #if 0
520 //TODO fix kde accel system so that kactions find appropriate global shortcuts
521 // and there is only one configure shortcuts dialog
522
523 KActionCollection* const ac = Amarok::actionCollection();
524 KAccelShortcutList list( m_pGlobalAccel );
525
526 for( uint i = 0; i < list.count(); ++i )
527 {
528 KAction *action = ac->action( list.name( i ).toLatin1() );
529
530 if( action )
531 {
532 //this is a hack really, also it means there may be two calls to the slot for the shortcut
533 action->setShortcutConfigurable( false );
534 action->setShortcut( list.shortcut( i ) );
535 }
536 }
537 #endif
538 }
539
540
541 /////////////////////////////////////////////////////////////////////////////////////
542 // METHODS
543 /////////////////////////////////////////////////////////////////////////////////////
544
545 #include <id3v1tag.h>
546 #include <tbytevector.h>
547 #include <QTextCodec>
548 #include <kglobal.h>
549
550 //this class is only used in this module, so I figured I may as well define it
551 //here and save creating another header/source file combination
552
553 class ID3v1StringHandler : public TagLib::ID3v1::StringHandler
554 {
555 QTextCodec *m_codec;
556
557 virtual TagLib::String parse( const TagLib::ByteVector &data ) const
558 {
559 return QStringToTString( m_codec->toUnicode( data.data(), data.size() ) );
560 }
561
562 virtual TagLib::ByteVector render( const TagLib::String &ts ) const
563 {
564 const Q3CString qcs = m_codec->fromUnicode( TStringToQString(ts) );
565 return TagLib::ByteVector( qcs, (uint) qcs.length() );
566 }
567
568 public:
569 ID3v1StringHandler( int codecIndex )
570 : m_codec( QTextCodec::codecForIndex( codecIndex ) )
571 {
572 debug() << "codec: " << m_codec << endl;
573 debug() << "codec-name: " << m_codec->name() << endl;
574 }
575
576 ID3v1StringHandler( QTextCodec *codec )
577 : m_codec( codec )
578 {
579 debug() << "codec: " << m_codec << endl;
580 debug() << "codec-name: " << m_codec->name() << endl;
581 }
582 };
583
584 //SLOT
585 void App::applySettings( bool firstTime )
586 {
587 ///Called when the configDialog is closed with OK or Apply
588
589 DEBUG_BLOCK
590
591 //determine and apply colors first
592 applyColorScheme();
593
594 #ifdef Q_WS_X11
595 //probably needs to be done in TrayIcon when it receives a QEvent::ToolTip (see QSystemtrayIcon documentation)
596 //TrackToolTip::instance()->removeFromWidget( m_pTray );
597 #endif
598 playlistWindow()->applySettings();
599 Scrobbler::instance()->applySettings();
600 Amarok::OSD::instance()->applySettings();
601 CollectionDB::instance()->applySettings();
602 #ifdef Q_WS_X11
603 m_pTray->setVisible( AmarokConfig::showTrayIcon() );
604 //TrackToolTip::instance()->addToWidget( m_pTray );
605 #endif
606
607
608 //on startup we need to show the window, but only if it wasn't hidden on exit
609 //and always if the trayicon isn't showing
610 QWidget* main_window = mainWindow();
611 #ifdef Q_WS_X11
612 if( ( main_window && firstTime && !Amarok::config()->readBoolEntry( "HiddenOnExit", false ) ) || ( main_window && !AmarokConfig::showTrayIcon() ) )
613 #endif
614 {
615 main_window->show();
616
617 //takes longer but feels shorter. Crazy eh? :)
618 kapp->processEvents( QEventLoop::ExcludeUserInputEvents );
619 }
620
621
622 { //<Engine>
623 EngineBase *engine = EngineController::engine();
624
625 if( firstTime || AmarokConfig::soundSystem() !=
626 PluginManager::getService( engine )->property( "X-KDE-Amarok-name" ).toString() )
627 {
628 //will unload engine for us first if necessary
629 engine = EngineController::instance()->loadEngine();
630 }
631
632 engine->setXfadeLength( AmarokConfig::crossfade() ? AmarokConfig::crossfadeLength() : 0 );
633 engine->setVolume( AmarokConfig::masterVolume() );
634
635 engine->setEqualizerEnabled( AmarokConfig::equalizerEnabled() );
636 if ( AmarokConfig::equalizerEnabled() )
637 engine->setEqualizerParameters( AmarokConfig::equalizerPreamp(), AmarokConfig::equalizerGains() );
638
639 Amarok::actionCollection()->action("play_audiocd")->setEnabled( EngineController::hasEngineProperty( "HasKIO" ) || EngineController::hasEngineProperty("HasCDDA"));
640 } //</Engine>
641
642 { //<Collection>
643 CollectionView::instance()->renderView(true);
644 } //</Collection>
645 { //<Context>
646 ContextBrowser::instance()->renderView();
647 } //</Context>
648
649 { // delete unneeded cover images from cache
650 const QString size = QString::number( AmarokConfig::coverPreviewSize() ) + '@';
651 const QDir cacheDir = Amarok::saveLocation( "albumcovers/cache/" );
652 const QStringList obsoleteCovers = cacheDir.entryList( "*" );
653 oldForeach( obsoleteCovers )
654 if ( !(*it).startsWith( size ) && !(*it).startsWith( "50@" ) )
655 QFile( cacheDir.filePath( *it ) ).remove();
656 }
657
658 //if ( !firstTime )
659 // Bizarrely and ironically calling this causes crashes for
660 // some people! FIXME
661 //AmarokConfig::writeConfig();
662
663 }
664
665 //SLOT
666 void
667 App::continueInit()
668 {
669 DEBUG_BLOCK
670 const KCmdLineArgs* const args = KCmdLineArgs::parsedArgs();
671 bool restoreSession = args->count() == 0 || args->isSet( "append" ) || args->isSet( "enqueue" )
672 || Amarok::config()->readBoolEntry( "AppendAsDefault", false );
673
674 // Make this instance so it can start receiving signals
675 MoodServer::instance();
676
677 // Remember old folder setup, so we can detect changes after the wizard was used
678 //const QStringList oldCollectionFolders = MountPointManager::instance()->collectionFolders();
679
680
681 if ( Amarok::config()->readBoolEntry( "First Run", true ) || args->isSet( "wizard" ) ) {
682 std::cout << "STARTUP\n" << std::flush; //hide the splashscreen
683 firstRunWizard();
684 Amarok::config()->writeEntry( "First Run", false );
685 Amarok::config()->sync();
686 }
687
688 CollectionDB::instance()->checkDatabase();
689
690 m_pMediaDeviceManager = MediaDeviceManager::instance();
691 m_pPlaylistWindow = new PlaylistWindow();
692 #ifdef Q_WS_X11
693 m_pTray = new Amarok::TrayIcon( m_pPlaylistWindow );
694 #endif
695 m_pPlaylistWindow->init(); //creates the playlist, browsers, etc.
696 //init playlist window as soon as the database is guaranteed to be usable
697 //connect( CollectionDB::instance(), SIGNAL( databaseUpdateDone() ), m_pPlaylistWindow, SLOT( init() ) );
698 initGlobalShortcuts();
699 //load previous playlist in separate thread
700 if ( restoreSession && AmarokConfig::savePlaylist() )
701 {
702 Playlist::instance()->restoreSession();
703 //Debug::stamp();
704 //p->restoreSession();
705 }
706 if( args->isSet( "engine" ) ) {
707 // we correct some common errors (case issues, missing -engine off the end)
708 QString engine = args->getOption( "engine" ).toLower();
709 if( engine.startsWith( "gstreamer" ) ) engine = "gst-engine";
710 if( !engine.endsWith( "engine" ) ) engine += "-engine";
711
712 AmarokConfig::setSoundSystem( engine );
713 }
714 Debug::stamp();
715 //create engine, show TrayIcon etc.
716 applySettings( true );
717 Debug::stamp();
718 // Start ScriptManager. Must be created _after_ PlaylistWindow.
719 ScriptManager::instance();
720 Debug::stamp();
721 //notify loader application that we have started
722 std::cout << "STARTUP\n" << std::flush;
723
724 //do after applySettings(), or the OSD will flicker and other wierdness!
725 //do before restoreSession()!
726 EngineController::instance()->attach( this );
727
728 //set a default interface
729 engineStateChanged( Engine::Empty );
730
731 if ( AmarokConfig::resumePlayback() && restoreSession && !args->isSet( "stop" ) ) {
732 //restore session as long as the user didn't specify media to play etc.
733 //do this after applySettings() so OSD displays correctly
734 EngineController::instance()->restoreSession();
735 }
736
737 // Refetch covers every 80 days to comply with Amazon license
738 #ifdef AMAZON_SUPPORT
739 new RefreshImages();
740 #endif
741
742 CollectionDB *collDB = CollectionDB::instance();
743 //Collection scan is triggered in firstRunWizard if the colelction folder setup was changed in the wizard
744
745 // If database version is updated, the collection needs to be rescanned.
746 // Works also if the collection is empty for some other reason
747 // (e.g. deleted collection.db)
748 if ( CollectionDB::instance()->isEmpty() )
749 {
750 //connect( collDB, SIGNAL( databaseUpdateDone() ), collDB, SLOT( startScan() ) );
751 collDB->startScan();
752 }
753 else if ( AmarokConfig::monitorChanges() )
754 //connect( collDB, SIGNAL( databaseUpdateDone() ), collDB, SLOT( scanModifiedDirs() ) );
755 collDB->scanModifiedDirs();
756
757
758 handleCliArgs();
759 }
760
761 void
762 App::applyColorScheme()
763 {
764 QColorGroup group;
765 using Amarok::ColorScheme::AltBase;
766 int h, s, v;
767 QWidget* const browserBar = static_cast<QWidget*>( playlistWindow()->sideBar() );
768 QWidget* const contextBrowser = static_cast<QWidget*>( ContextBrowser::instance() );
769
770 if( AmarokConfig::schemeKDE() )
771 {
772 AltBase = KGlobalSettings::alternateBackgroundColor();
773
774 playlistWindow()->unsetPalette();
775 //browserBar->unsetPalette();
776 contextBrowser->unsetPalette();
777
778 // PlayerWidget::determineAmarokColors();
779 }
780
781 else if( AmarokConfig::schemeAmarok() )
782 {
783 group = QApplication::palette().active();
784 const QColor bg( Amarok::blue );
785 AltBase.setRgb( 57, 64, 98 );
786
787 group.setColor( QColorGroup::Text, Qt::white );
788 group.setColor( QColorGroup::Link, 0xCCCCCC );
789 group.setColor( QColorGroup::Base, bg );
790 group.setColor( QColorGroup::Foreground, 0xd7d7ef );
791 group.setColor( QColorGroup::Background, AltBase );
792
793 group.setColor( QColorGroup::Button, AltBase );
794 group.setColor( QColorGroup::ButtonText, 0xd7d7ef );
795
796 // group.setColor( QColorGroup::Light, Qt::cyan /*lighter than Button color*/ );
797 // group.setColor( QColorGroup::Midlight, Qt::blue /*between Button and Light*/ );
798 // group.setColor( QColorGroup::Dark, Qt::green /*darker than Button*/ );
799 // group.setColor( QColorGroup::Mid, Qt::red /*between Button and Dark*/ );
800 // group.setColor( QColorGroup::Shadow, Qt::yellow /*a very dark color. By default, the shadow color is Qt::black*/ );
801
802 group.setColor( QColorGroup::Highlight, Qt::white );
803 group.setColor( QColorGroup::HighlightedText, bg );
804 //group.setColor( QColorGroup::BrightText, QColor( 0xff, 0x40, 0x40 ) ); //GlowColor
805
806 AltBase.getHsv( &h, &s, &v );
807 group.setColor( QColorGroup::Midlight, QColor( h, s/3, (int)(v * 1.2), QColor::Hsv ) ); //column separator in playlist
808
809 //TODO set all colours, even button colours, that way we can change the dark,
810 //light, etc. colours and Amarok scheme will look much better
811
812 using namespace Amarok::ColorScheme;
813 Base = Amarok::blue;
814 Text = Qt::white;
815 Background = 0x002090;
816 Foreground = 0x80A0FF;
817
818 //all children() derive their palette from this
819 playlistWindow()->setPalette( QPalette( group, group, group ) );
820 browserBar->unsetPalette();
821 contextBrowser->setPalette( QPalette( group, group, group ) );
822 }
823
824 else if( AmarokConfig::schemeCustom() )
825 {
826 // we try to be smart: this code figures out contrasting colors for
827 // selection and alternate background rows
828 group = QApplication::palette().active();
829 const QColor fg( AmarokConfig::playlistWindowFgColor() );
830 const QColor bg( AmarokConfig::playlistWindowBgColor() );
831
832 //TODO use the ensureContrast function you devised in BlockAnalyzer
833
834 bg.hsv( &h, &s, &v );
835 v += (v < 128) ? +50 : -50;
836 v &= 255; //ensures 0 <= v < 256
837 AltBase.setHsv( h, s, v );
838
839 fg.hsv( &h, &s, &v );
840 v += (v < 128) ? +150 : -150;
841 v &= 255; //ensures 0 <= v < 256
842 QColor highlight( h, s, v, QColor::Hsv );
843
844 group.setColor( QColorGroup::Base, bg );
845 group.setColor( QColorGroup::Background, bg.dark( 115 ) );
846 group.setColor( QColorGroup::Text, fg );
847 group.setColor( QColorGroup::Link, fg.light( 120 ) );
848 group.setColor( QColorGroup::Highlight, highlight );
849 group.setColor( QColorGroup::HighlightedText, Qt::white );
850 group.setColor( QColorGroup::Dark, Qt::darkGray );
851
852 // PlayerWidget::determineAmarokColors();
853
854 // we only colour the middle section since we only
855 // allow the user to choose two colours
856 browserBar->setPalette( QPalette( group, group, group ) );
857 contextBrowser->setPalette( QPalette( group, group, group ) );
858 playlistWindow()->unsetPalette();
859 }
860
861 #if 0
862 // set the K3ListView alternate colours
863 QObjectList* const list = playlistWindow()->queryList( "K3ListView" );
864 for( QObject *o = list->first(); o; o = list->next() )
865 static_cast<K3ListView*>(o)->setAlternateBackground( AltBase );
866 delete list; //heap allocated!
867 #endif
868 }
869
870
871 bool Amarok::genericEventHandler( QWidget *recipient, QEvent *e )
872 {
873 //this is used as a generic event handler for widgets that want to handle
874 //typical events in an Amarok fashion
875
876 //to use it just pass the event eg:
877 //
878 // void Foo::barEvent( QBarEvent *e )
879 // {
880 // Amarok::genericEventHandler( this, e );
881 // }
882
883 switch( e->type() )
884 {
885 case QEvent::DragEnter:
886 #define e static_cast<QDropEvent*>(e)
887 e->accept( KUrl::List::canDecode( e->mimeData() ) );
888 break;
889
890 case QEvent::Drop:
891 {
892 KUrl::List list = KUrl::List::fromMimeData( e->mimeData() );
893 if( !list.isEmpty() )
894 {
895 Q3PopupMenu popup;
896 //FIXME this isn't a good way to determine if there is a currentTrack, need playlist() function
897 const bool b = EngineController::engine()->loaded();
898
899 popup.insertItem( KIcon( Amarok::icon( "add_playlist" ) ), i18n( "&Append to Playlist" ),
900 Playlist::Append );
901 popup.insertItem( KIcon( Amarok::icon( "add_playlist" ) ), i18n( "Append && &Play" ),
902 Playlist::DirectPlay | Playlist::Append );
903 if( b )
904 popup.insertItem( KIcon( Amarok::icon( "fast_forward" ) ), i18n( "&Queue Track" ),
905 Playlist::Queue );
906 popup.insertSeparator();
907 popup.insertItem( i18n( "&Cancel" ), 0 );
908
909 const int id = popup.exec( recipient->mapToGlobal( e->pos() ) );
910
911 if ( id > 0 )
912 Playlist::instance()->insertMedia( list, id );
913 }
914 else return false;
915 #undef e
916
917 break;
918 }
919
920 //this like every entry in the generic event handler is used by more than one widget
921 //please don't remove!
922 case QEvent::Wheel:
923 {
924 #define e static_cast<QWheelEvent*>(e)
925
926 //this behaviour happens for the systray
927 //to override one, override it in that class
928
929 switch( e->state() )
930 {
931 case Qt::ControlModifier:
932 {
933 const bool up = e->delta() > 0;
934
935 //if this seems strange to you, please bring it up on #amarok
936 //for discussion as we can't decide which way is best!
937 if( up ) EngineController::instance()->previous();
938 else EngineController::instance()->next();
939 break;
940 }
941 case Qt::ShiftModifier:
942 {
943 EngineController::instance()->seekRelative( ( e->delta() / 120 ) * 10000 ); // 10 seconds
944 break;
945 }
946 default:
947 EngineController::instance()->increaseVolume( e->delta() / Amarok::VOLUME_SENSITIVITY );
948 }
949
950 e->accept();
951 #undef e
952
953 break;
954 }
955
956 case QEvent::Close:
957
958 //KDE policy states we should hide to tray and not quit() when the
959 //close window button is pushed for the main widget
960
961 static_cast<QCloseEvent*>(e)->accept(); //if we don't do this the info box appears on quit()!
962
963 if( AmarokConfig::showTrayIcon() && !e->spontaneous() && !kapp->sessionSaving() )
964 {
965 KMessageBox::information( recipient,
966 i18n( "<qt>Closing the main-window will keep Amarok running in the System Tray. "
967 "Use <B>Quit</B> from the menu, or the Amarok tray-icon to exit the application.</qt>" ),
968 i18n( "Docking in System Tray" ), "hideOnCloseInfo" );
969 }
970 else pApp->quit();
971
972 break;
973
974 default:
975 return false;
976 }
977
978 return true;
979 }
980
981
982 void App::engineStateChanged( Engine::State state, Engine::State oldState )
983 {
984 const MetaBundle &bundle = EngineController::instance()->bundle();
985 switch( state )
986 {
987 case Engine::Empty:
988 m_pPlaylistWindow->setCaption( "Amarok" );
989 TrackToolTip::instance()->clear();
990 Amarok::OSD::instance()->setImage( QImage( KIconLoader().iconPath( "amarok", -K3Icon::SizeHuge ) ) );
991 break;
992
993 case Engine::Playing:
994 if ( oldState == Engine::Paused )
995 Amarok::OSD::instance()->OSDWidget::show( i18nc( "state, as in playing", "Play" ) );
996 if ( !bundle.prettyTitle().isEmpty() )
997 m_pPlaylistWindow->setCaption( i18n("Amarok - %1", bundle.veryNiceTitle() ) );
998 break;
999
1000 case Engine::Paused:
1001 Amarok::OSD::instance()->OSDWidget::show( i18n("Paused") );
1002 break;
1003
1004 case Engine::Idle:
1005 m_pPlaylistWindow->setCaption( "Amarok" );
1006 break;
1007
1008 default:
1009 ;
1010 }
1011 }
1012
1013 void App::engineNewMetaData( const MetaBundle &bundle, bool /*trackChanged*/ )
1014 {
1015 Amarok::OSD::instance()->show( bundle );
1016 if ( !bundle.prettyTitle().isEmpty() )
1017 m_pPlaylistWindow->setCaption( i18n("Amarok - %1", bundle.veryNiceTitle() ) );
1018
1019 TrackToolTip::instance()->setTrack( bundle );
1020 }
1021
1022 void App::engineTrackPositionChanged( long position, bool /*userSeek*/ )
1023 {
1024 TrackToolTip::instance()->setPos( position );
1025 }
1026
1027 void App::engineVolumeChanged( int newVolume )
1028 {
1029 Amarok::OSD::instance()->OSDWidget::volChanged( newVolume );
1030 }
1031
1032 void App::slotConfigEqualizer() //SLOT
1033 {
1034 EqualizerSetup::instance()->show();
1035 EqualizerSetup::instance()->raise();
1036 }
1037
1038
1039 void App::slotConfigAmarok( const Q3CString& page )
1040 {
1041 DEBUG_THREAD_FUNC_INFO
1042
1043 AmarokConfigDialog* dialog = static_cast<AmarokConfigDialog*>( KConfigDialog::exists( "settings" ) );
1044
1045 if( !dialog )
1046 {
1047 //KConfigDialog didn't find an instance of this dialog, so lets create it :
1048 dialog = new AmarokConfigDialog( m_pPlaylistWindow, "settings", AmarokConfig::self() );
1049
1050 connect( dialog, SIGNAL(settingsChanged()), SLOT(applySettings()) );
1051 }
1052
1053 //FIXME it seems that if the dialog is on a different desktop it gets lost
1054 // what do to? detect and move it?
1055
1056 // if ( page.isNull() )
1057 // FIXME
1058 // dialog->showPage( AmarokConfigDialog::s_currentPage );
1059 // else
1060 dialog->showPageByName( page );
1061
1062 dialog->show();
1063 dialog->raise();
1064 dialog->setActiveWindow();
1065 }
1066
1067 void App::slotConfigShortcuts()
1068 {
1069 KKeyDialog::configure( Amarok::actionCollection(), KKeyChooser::LetterShortcutsAllowed, m_pPlaylistWindow );
1070 }
1071
1072 void App::slotConfigToolBars()
1073 {
1074 PlaylistWindow* const pw = playlistWindow();
1075 KEditToolbar dialog( pw->actionCollection(), pw->xmlFile(), true, pw );
1076
1077 dialog.showButton( KEditToolbar::Apply, false );
1078
1079 if( dialog.exec() )
1080 {
1081 playlistWindow()->reloadXML();
1082 playlistWindow()->createGUI();
1083 }
1084 }
1085
1086 void App::firstRunWizard()
1087 {
1088 #if 0
1089 ///show firstRunWizard
1090 DEBUG_BLOCK
1091
1092 FirstRunWizard wizard;
1093 setTopWidget( &wizard );
1094 KConfigDialogManager* config = new KConfigDialogManager(&wizard, AmarokConfig::self(), "wizardconfig");
1095 config->updateWidgets();
1096 // connect(config, SIGNAL(settingsChanged()), SLOT(updateSettings()));
1097 wizard.setCaption( makeStdCaption( i18n( "First-Run Wizard" ) ) );
1098
1099 if( wizard.exec() != QDialog::Rejected )
1100 {
1101 //make sure that the DB config is stored in amarokrc before calling CollectionDB's ctor
1102 AmarokConfig::setDatabaseEngine(
1103 QString::number( Amarok::databaseTypeCode( wizard.dbSetup7->databaseEngine->currentText() ) ) );
1104 config->updateSettings();
1105
1106 const QStringList oldCollectionFolders = MountPointManager::instance()->collectionFolders();
1107 wizard.writeCollectionConfig();
1108
1109 // If wizard is invoked at runtime, rescan collection if folder setup has changed
1110 if ( !Amarok::config()->readBoolEntry( "First Run", true ) &&
1111 oldCollectionFolders != MountPointManager::instance()->collectionFolders() )
1112 CollectionDB::instance()->startScan();
1113
1114 }
1115 #endif
1116 }
1117
1118 void App::setUseScores( bool use )
1119 {
1120 AmarokConfig::setUseScores( use );
1121 emit useScores( use );
1122 }
1123
1124 void App::setUseRatings( bool use )
1125 {
1126 AmarokConfig::setUseRatings( use );
1127 emit useRatings( use );
1128 }
1129
1130 void App::setMoodbarPrefs( bool show, bool moodier, int alter, bool withMusic )
1131 {
1132 AmarokConfig::setShowMoodbar( show );
1133 AmarokConfig::setMakeMoodier( moodier );
1134 AmarokConfig::setAlterMood( alter );
1135 AmarokConfig::setMoodsWithMusic( withMusic );
1136 emit moodbarPrefs( show, moodier, alter, withMusic );
1137 }
1138
1139 KIO::Job *App::trashFiles( const KUrl::List &files )
1140 {
1141 KIO::Job *job = KIO::trash( files, true /*show progress*/ );
1142 Amarok::StatusBar::instance()->newProgressOperation( job ).setDescription( i18n("Moving files to trash") );
1143 connect( job, SIGNAL( result( KJob* ) ), this, SLOT( slotTrashResult( KJob* ) ) );
1144 return job;
1145 }
1146
1147 void App::setRating( int n )
1148 {
1149 if( !AmarokConfig::useRatings() ) return;
1150
1151 n *= 2;
1152
1153 const Engine::State s = EngineController::instance()->engine()->state();
1154 if( s == Engine::Playing || s == Engine::Paused || s == Engine::Idle )
1155 {
1156 const QString path = EngineController::instance()->playingURL().path();
1157 CollectionDB::instance()->setSongRating( path, n, true );
1158 const int rating = CollectionDB::instance()->getSongRating( path );
1159 EngineController::instance()->updateBundleRating( rating );
1160 Amarok::OSD::instance()->OSDWidget::ratingChanged( rating );
1161 }
1162 else if( PlaylistWindow::self()->isReallyShown() && Playlist::instance()->qscrollview()->hasFocus() )
1163 Playlist::instance()->setSelectedRatings( n );
1164 }
1165
1166 void App::slotTrashResult( KJob *job )
1167 {
1168 if( job->error() )
1169 job->uiDelegate()->showErrorMessage();
1170 }
1171
1172 QWidget *App::mainWindow() const
1173 {
1174 return static_cast<QWidget*>( m_pPlaylistWindow );
1175 }
1176
1177 void App::quit()
1178 {
1179 emit prepareToQuit();
1180 if( MediaBrowser::instance() && MediaBrowser::instance()->blockQuit() )
1181 {
1182 // don't quit yet, as some media devices still have to finish transferring data
1183 QTimer::singleShot( 100, this, SLOT( quit() ) );
1184 return;
1185 }
1186 KApplication::quit();
1187 }
1188
1189 namespace Amarok
1190 {
1191 /// @see amarok.h
1192
1193 //TODO remove these, they suck, do a generic getImage
1194
1195 QPixmap getPNG( const QString &filename )
1196 {
1197 QString file = !filename.endsWith( ".png", false ) ? "amarok/images/%1.png" : "amarok/images/%1";
1198
1199 return QPixmap( KStandardDirs::locate( "data", file.arg( filename ) ), "PNG" );
1200 }
1201
1202 QPixmap getJPG( const QString &filename )
1203 {
1204 QString file = !filename.endsWith( ".jpg", false ) ? "amarok/images/%1.jpg" : "amarok/images/%1";
1205
1206 return QPixmap( KStandardDirs::locate( "data", QString( "amarok/images/%1.jpg" ).arg( filename ) ), "JPEG" );
1207 }
1208
1209 QWidget *mainWindow()
1210 {
1211 return pApp->playlistWindow();
1212 }
1213
1214 KActionCollection *actionCollection()
1215 {
1216 return pApp->playlistWindow()->actionCollection();
1217 }
1218
1219 KSharedConfig::Ptr config( const QString &group )
1220 {
1221 //Slightly more useful config() that allows setting the group simultaneously
1222 KGlobal::config()->setGroup( group );
1223 return KGlobal::config();
1224 }
1225
1226 bool invokeBrowser( const QString& url )
1227 {
1228 //URL can be in whatever forms KUrl::fromPathOrUrl understands - ie most.
1229 const QString cmd = "%1 \"%2\"";
1230 return KRun::runCommand( cmd.arg( AmarokConfig::externalBrowser(), KUrl::fromPathOrUrl( url ).url() ) ) > 0;
1231 }
1232
1233 namespace ColorScheme
1234 {
1235 QColor Base;
1236 QColor Text;
1237 QColor Background;
1238 QColor Foreground;
1239 QColor AltBase;
1240 }
1241
1242 OverrideCursor::OverrideCursor( Qt::CursorShape cursor )
1243 {
1244 QApplication::setOverrideCursor( cursor == Qt::WaitCursor ? KCursor::waitCursor() : KCursor::workingCursor() );
1245 }
1246
1247 OverrideCursor::~OverrideCursor()
1248 {
1249 QApplication::restoreOverrideCursor();
1250 }
1251
1252 QString saveLocation( const QString &directory )
1253 {
1254 globalDirsMutex.lock();
1255 QString result = KGlobal::dirs()->saveLocation( "data", QString("amarok/") + directory, true );
1256 globalDirsMutex.unlock();
1257 return result;
1258 }
1259
1260 QString cleanPath( const QString &path )
1261 {
1262 QString result = path;
1263 // german umlauts
1264 result.replace( QChar(0x00e4), "ae" ).replace( QChar(0x00c4), "Ae" );
1265 result.replace( QChar(0x00f6), "oe" ).replace( QChar(0x00d6), "Oe" );
1266 result.replace( QChar(0x00fc), "ue" ).replace( QChar(0x00dc), "Ue" );
1267 result.replace( QChar(0x00df), "ss" );
1268
1269 // some strange accents
1270 result.replace( QChar(0x00e7), "c" ).replace( QChar(0x00c7), "C" );
1271 result.replace( QChar(0x00fd), "y" ).replace( QChar(0x00dd), "Y" );
1272 result.replace( QChar(0x00f1), "n" ).replace( QChar(0x00d1), "N" );
1273
1274 // accented vowels
1275 QChar a[] = { 'a', 0xe0,0xe1,0xe2,0xe3,0xe5, 0 };
1276 QChar A[] = { 'A', 0xc0,0xc1,0xc2,0xc3,0xc5, 0 };
1277 QChar E[] = { 'e', 0xe8,0xe9,0xea,0xeb, 0 };
1278 QChar e[] = { 'E', 0xc8,0xc9,0xca,0xcb, 0 };
1279 QChar i[] = { 'i', 0xec,0xed,0xee,0xef, 0 };
1280 QChar I[] = { 'I', 0xcc,0xcd,0xce,0xcf, 0 };
1281 QChar o[] = { 'o', 0xf2,0xf3,0xf4,0xf5,0xf8, 0 };
1282 QChar O[] = { 'O', 0xd2,0xd3,0xd4,0xd5,0xd8, 0 };
1283 QChar u[] = { 'u', 0xf9,0xfa,0xfb, 0 };
1284 QChar U[] = { 'U', 0xd9,0xda,0xdb, 0 };
1285 QChar nul[] = { 0 };
1286 QChar *replacements[] = { a, A, e, E, i, I, o, O, u, U, nul };
1287
1288 for( uint i = 0; i < result.length(); i++ )
1289 {
1290 QChar c = result.ref( i );
1291 for( uint n = 0; replacements[n][0] != QChar(0); n++ )
1292 {
1293 for( uint k=0; replacements[n][k] != QChar(0); k++ )
1294 {
1295 if( replacements[n][k] == c )
1296 {
1297 c = replacements[n][0];
1298 }
1299 }
1300 }
1301 result.ref( i ) = c;
1302 }
1303 return result;
1304 }
1305
1306 QString asciiPath( const QString &path )
1307 {
1308 QString result = path;
1309 for( uint i = 0; i < result.length(); i++ )
1310 {
1311 QChar c = result.ref( i );
1312 if( c > QChar(0x7f) || c == QChar(0) )
1313 {
1314 c = '_';
1315 }
1316 result.ref( i ) = c;
1317 }
1318 return result;
1319 }
1320
1321 QString vfatPath( const QString &path )
1322 {
1323 QString s = path;
1324
1325 for( uint i = 0; i < s.length(); i++ )
1326 {
1327 QChar c = s.ref( i );
1328 if( c < QChar(0x20)
1329 || c=='*' || c=='?' || c=='<' || c=='>'
1330 || c=='|' || c=='"' || c==':' || c=='/'
1331 || c=='\\' )
1332 c = '_';
1333 s.ref( i ) = c;
1334 }
1335
1336 uint len = s.length();
1337 if( len == 3 || (len > 3 && s[3] == '.') )
1338 {
1339 QString l = s.left(3).toLower();
1340 if( l=="aux" || l=="con" || l=="nul" || l=="prn" )
1341 s = '_' + s;
1342 }
1343 else if( len == 4 || (len > 4 && s[4] == '.') )
1344 {
1345 QString l = s.left(3).toLower();
1346 QString d = s.mid(3,1);
1347 if( (l=="com" || l=="lpt") &&
1348 (d=="0" || d=="1" || d=="2" || d=="3" || d=="4" ||
1349 d=="5" || d=="6" || d=="7" || d=="8" || d=="9") )
1350 s = '_' + s;
1351 }
1352
1353 while( s.startsWith( "." ) )
1354 s = s.mid(1);
1355
1356 while( s.endsWith( "." ) )
1357 s = s.left( s.length()-1 );
1358
1359 s = s.left(255);
1360 len = s.length();
1361 if( s[len-1] == ' ' )
1362 s[len-1] = '_';
1363
1364 return s;
1365 }
1366
1367 QString decapitateString( const QString &input, const QString &ref )
1368 {
1369 QString t = ref.toUpper();
1370 int length = t.length();
1371 int commonLength = 0;
1372 while( length > 0 )
1373 {
1374 if ( input.toUpper().startsWith( t ) )
1375 {
1376 commonLength = t.length();
1377 t = ref.toUpper().left( t.length() + length/2 );
1378 length = length/2;
1379 }
1380 else
1381 {
1382 t = ref.toUpper().left( t.length() - length/2 );
1383 length = length/2;
1384 }
1385 }
1386 QString clean = input;
1387 if( t.endsWith( " " ) || !ref.at( t.length() ).isLetterOrNumber() ) // common part ends with a space or complete word
1388 clean = input.right( input.length() - commonLength ).trimmed();
1389 return clean;
1390 }
1391
1392 void setUseScores( bool use ) { App::instance()->setUseScores( use ); }
1393 void setUseRatings( bool use ) { App::instance()->setUseRatings( use ); }
1394 void setMoodbarPrefs( bool show, bool moodier, int alter, bool withMusic )
1395 { App::instance()->setMoodbarPrefs( show, moodier, alter, withMusic ); }
1396 KIO::Job *trashFiles( const KUrl::List &files ) { return App::instance()->trashFiles( files ); }
1397 }
1398
1399 #include "app.moc"

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision
svn:mime-type text/x-c++src