You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
amarok/amarok/src/app.cpp

1495 lines
54 KiB

/***************************************************************************
app.cpp - description
-------------------
begin : Mit Okt 23 14:35:18 CEST 2002
copyright : (C) 2002 by Mark Kretschmann
email : markey@web.de
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "amarok.h"
#include "amarokconfig.h"
#include "amarokdcophandler.h"
#include "app.h"
#include "atomicstring.h"
#include "config.h"
#include "configdialog.h"
#include "contextbrowser.h"
#include "collectionbrowser.h"
#include "dbsetup.h" //firstRunWizard()
#include "debug.h"
#include "devicemanager.h"
#include "mediadevicemanager.h"
#include "enginebase.h"
#include "enginecontroller.h"
#include "equalizersetup.h"
#include "firstrunwizard.h"
#include "mediabrowser.h"
#include "metabundle.h"
#include "mountpointmanager.h"
#include "osd.h"
#include "playerwindow.h"
#include "playlist.h"
#include "playlistbrowser.h"
#include "playlistwindow.h"
#include "pluginmanager.h"
#include "refreshimages.h"
#include "scriptmanager.h"
#include "scrobbler.h"
#include "statusbar.h"
#include "systray.h"
#include "threadmanager.h"
#include "tracktooltip.h" //engineNewMetaData()
#include <iostream>
#include <kconfigdialogmanager.h>
#include <kcombobox.h> //firstRunWizard()
#include <kcmdlineargs.h> //initCliArgs()
#include <kcursor.h> //Amarok::OverrideCursor
#include <kedittoolbar.h> //slotConfigToolbars()
#include <kglobalaccel.h> //initGlobalShortcuts()
#include <kglobalsettings.h> //applyColorScheme()
#include <kiconloader.h> //amarok Icon
#include <kkeydialog.h> //slotConfigShortcuts()
#include <klocale.h>
#include <kmessagebox.h> //applySettings(), genericEventHandler()
#include <krun.h> //Amarok::invokeBrowser()
#include <kstandarddirs.h>
#include <kurldrag.h> //genericEventHandler()
#include <kaboutdata.h>
#include <kio/job.h>
#include <tqevent.h> //genericEventHandler()
#include <tqeventloop.h> //applySettings()
#include <tqfile.h>
#include <tqobjectlist.h> //applyColorScheme()
#include <tqpalette.h> //applyColorScheme()
#include <tqpixmap.h> //TQPixmap::setDefaultOptimization()
#include <tqpopupmenu.h> //genericEventHandler
#include <tqtimer.h> //showHyperThreadingWarning()
#include <tqtooltip.h> //default tooltip for trayicon
// For the HyperThreading fix
#ifdef __linux__
#ifdef SCHEDAFFINITY_SUPPORT
#include <errno.h>
#include <sched.h>
#endif //SCHEDAFFINITY_SUPPORT
#endif //__linux__
TQMutex Debug::mutex;
TQMutex Amarok::globalDirsMutex;
int App::mainThreadId = 0;
#ifdef TQ_WS_MAC
#include <qt_mac.h>
static AEEventHandlerUPP appleEventProcessorUPP = 0;
OSStatus
appleEventProcessor(const AppleEvent *ae, AppleEvent *, long /*handlerRefCon*/)
{
OSType aeID = typeWildCard;
OSType aeClass = typeWildCard;
AEGetAttributePtr(ae, keyEventClassAttr, typeType, 0, &aeClass, sizeof(aeClass), 0);
AEGetAttributePtr(ae, keyEventIDAttr, typeType, 0, &aeID, sizeof(aeID), 0);
if(aeClass == kCoreEventClass)
{
if(aeID == kAEReopenApplication)
{
if( PlaylistWindow::self() )
PlaylistWindow::self()->show();
}
return noErr;
}
return eventNotHandledErr;
}
#endif
LIBAMAROK_EXPORT KAboutData aboutData( "amarok",
I18N_NOOP( "Amarok" ), APP_VERSION,
I18N_NOOP( "The audio player for KDE" ), KAboutData::License_GPL,
I18N_NOOP( "(C) 2002-2003, Mark Kretschmann\n(C) 2003-2007, The Amarok Development Squad\n(C) 2007-2011, The Trinity Desktop Project" ),
I18N_NOOP( "IRC:\nirc.freenode.net - #amarok, #amarok.de, #amarok.es\n\nFeedback:\namarok@kde.org\n\n(Build Date: " __DATE__ ")" ),
( "http://amarok.kde.org" ) );
App::App()
: KApplication(KApplication::openX11RGBADisplay())
, m_pPlayerWindow( 0 ) //will be created in applySettings()
{
DEBUG_BLOCK
#ifdef TQ_WS_MAC
// this is inspired by OpenSceneGraph: osgDB/FilePath.cpp
// Start with the the Bundle PlugIns directory.
// Get the main bundle first. No need to retain or release it since
// we are not keeping a reference
CFBundleRef myBundle = CFBundleGetMainBundle();
if( myBundle )
{
// CFBundleGetMainBundle will return a bundle ref even if
// the application isn't part of a bundle, so we need to
// check
// if the path to the bundle ends in ".app" to see if it is
// a
// proper application bundle. If it is, the plugins path is
// added
CFURLRef urlRef = CFBundleCopyBundleURL(myBundle);
if(urlRef)
{
char bundlePath[1024];
if( CFURLGetFileSystemRepresentation( urlRef, true, (UInt8 *)bundlePath, sizeof(bundlePath) ) )
{
TQCString bp( bundlePath );
size_t len = bp.length();
if( len > 4 && bp.right( 4 ) == ".app" )
{
bp.append( "/Contents/MacOS" );
TQCString path = getenv( "PATH" );
if( path.length() > 0 )
{
path.prepend( ":" );
}
path.prepend( bp );
debug() << "setting PATH=" << path << endl;
setenv("PATH", path, 1);
}
}
// docs say we are responsible for releasing CFURLRef
CFRelease(urlRef);
}
}
#endif
TQPixmap::setDefaultOptimization( TQPixmap::MemoryOptim );
//needs to be created before the wizard
new Amarok::DcopPlayerHandler(); // Must be created first
new Amarok::DcopPlaylistHandler();
new Amarok::DcopPlaylistBrowserHandler();
new Amarok::DcopContextBrowserHandler();
new Amarok::DcopCollectionHandler();
new Amarok::DcopMediaBrowserHandler();
new Amarok::DcopScriptHandler();
new Amarok::DcopDevicesHandler();
fixHyperThreading();
// tell AtomicString that this is the GUI thread
if ( !AtomicString::isMainThread() )
qWarning("AtomicString was initialized from a thread other than the GUI "
"thread. This could lead to memory leaks.");
#ifdef TQ_WS_MAC
appleEventProcessorUPP = AEEventHandlerUPP(appleEventProcessor);
AEInstallEventHandler(kCoreEventClass, kAEReopenApplication, appleEventProcessorUPP, (long)this, true);
#endif
TQTimer::singleShot( 0, this, TQT_SLOT( continueInit() ) );
}
App::~App()
{
DEBUG_BLOCK
// Hiding the OSD before exit prevents crash
Amarok::OSD::instance()->hide();
EngineBase* const engine = EngineController::engine();
if ( AmarokConfig::resumePlayback() ) {
if ( engine->state() != Engine::Empty ) {
AmarokConfig::setResumeTrack( EngineController::instance()->playingURL().prettyURL() );
AmarokConfig::setResumeTime( engine->position() );
}
else AmarokConfig::setResumeTrack( TQString() ); //otherwise it'll play previous resume next time!
}
EngineController::instance()->endSession(); //records final statistics
EngineController::instance()->detach( this );
// do even if trayicon is not shown, it is safe
Amarok::config()->writeEntry( "HiddenOnExit", mainWindow()->isHidden() );
CollectionDB::instance()->stopScan();
delete m_pPlayerWindow; //sets some XT keys
delete m_pPlaylistWindow; //sets some XT keys
ThreadManager::deleteInstance(); //waits for jobs to finish
// this must be deleted before the connection to the Xserver is
// severed, or we risk a crash when the TQApplication is exited,
// I asked Trolltech! *smug*
delete Amarok::OSD::instance();
AmarokConfig::setVersion( APP_VERSION );
AmarokConfig::writeConfig();
//need to unload the engine before the kapplication is destroyed
PluginManager::unload( engine );
}
#include <dcopref.h>
#include <tqstringlist.h>
namespace
{
// grabbed from KsCD source, kompatctdisk.cpp
TQString urlToDevice(const TQString& device)
{
KURL deviceUrl(device);
if (deviceUrl.protocol() == "media" || deviceUrl.protocol() == "system")
{
DCOPRef mediamanager( "kded", "mediamanager" );
DCOPReply reply = mediamanager.call( "properties(TQString)", deviceUrl.fileName() );
TQStringList properties = reply;
if (!reply.isValid() || properties.count() < 6)
{
debug() << "Invalid reply from mediamanager" << endl;
return TQString();
}
else
{
debug() << "Reply from mediamanager " << properties[5] << endl;
return properties[5];
}
}
return device;
}
}
void App::handleCliArgs() //static
{
static char cwd[PATH_MAX];
KCmdLineArgs* const args = KCmdLineArgs::parsedArgs();
if ( args->isSet( "cwd" ) )
{
strncpy(cwd, args->getOption( "cwd" ), sizeof(cwd) );
cwd[sizeof(cwd)-1] = '\0';
KCmdLineArgs::setCwd( cwd );
}
bool haveArgs = false;
if ( args->count() > 0 )
{
haveArgs = true;
KURL::List list;
for( int i = 0; i < args->count(); i++ )
{
KURL url = args->url( i );
if( url.protocol() == "itpc" || url.protocol() == "pcast" )
PlaylistBrowser::instance()->addPodcast( url );
else
list << url;
}
int options = Playlist::DefaultOptions;
if( args->isSet( "queue" ) )
options = Playlist::Queue;
else if( args->isSet( "append" ) || args->isSet( "enqueue" ) )
options = Playlist::Append;
else if( args->isSet( "load" ) )
options = Playlist::Replace;
if( args->isSet( "play" ) )
options |= Playlist::DirectPlay;
Playlist::instance()->insertMedia( list, options );
}
//we shouldn't let the user specify two of these since it is pointless!
//so we prioritise, pause > stop > play > next > prev
//thus pause is the least destructive, followed by stop as brakes are the most important bit of a car(!)
//then the others seemed sensible. Feel free to modify this order, but please leave justification in the cvs log
//I considered doing some sanity checks (eg only stop if paused or playing), but decided it wasn't worth it
else if ( args->isSet( "pause" ) )
{
haveArgs = true;
EngineController::instance()->pause();
}
else if ( args->isSet( "stop" ) )
{
haveArgs = true;
EngineController::instance()->stop();
}
else if ( args->isSet( "play-pause" ) )
{
haveArgs = true;
EngineController::instance()->playPause();
}
else if ( args->isSet( "play" ) ) //will restart if we are playing
{
haveArgs = true;
EngineController::instance()->play();
}
else if ( args->isSet( "next" ) )
{
haveArgs = true;
EngineController::instance()->next();
}
else if ( args->isSet( "previous" ) )
{
haveArgs = true;
EngineController::instance()->previous();
}
else if (args->isSet("cdplay"))
{
haveArgs = true;
TQString device = args->getOption("cdplay");
device = DeviceManager::instance()->convertMediaUrlToDevice( device );
KURL::List urls;
if (EngineController::engine()->getAudioCDContents(device, urls)) {
Playlist::instance()->insertMedia(
urls, Playlist::Replace|Playlist::DirectPlay);
} else { // Default behaviour
debug() <<
"Sorry, the engine doesn't support direct play from AudioCD..."
<< endl;
}
}
if ( args->isSet( "toggle-playlist-window" ) )
{
haveArgs = true;
pApp->m_pPlaylistWindow->showHide();
}
static bool firstTime = true;
if( !firstTime && !haveArgs )
pApp->m_pPlaylistWindow->activate();
firstTime = false;
args->clear(); //free up memory
}
/////////////////////////////////////////////////////////////////////////////////////
// INIT
/////////////////////////////////////////////////////////////////////////////////////
void App::initCliArgs( int argc, char *argv[] ) //static
{
static KCmdLineOptions options[] =
{
{ "+[URL(s)]", I18N_NOOP( "Files/URLs to open" ), 0 },
{ "r", 0, 0 },
{ "previous", I18N_NOOP( "Skip backwards in playlist" ), 0 },
{ "p", 0, 0 },
{ "play", I18N_NOOP( "Start playing current playlist" ), 0 },
{ "t", 0, 0 },
{ "play-pause", I18N_NOOP( "Play if stopped, pause if playing" ), 0 },
{ "pause", I18N_NOOP( "Pause playback" ), 0 },
{ "s", 0, 0 },
{ "stop", I18N_NOOP( "Stop playback" ), 0 },
{ "f", 0, 0 },
{ "next", I18N_NOOP( "Skip forwards in playlist" ), 0 },
{ ":", I18N_NOOP("Additional options:"), 0 },
{ "a", 0, 0 },
{ "append", I18N_NOOP( "Append files/URLs to playlist" ), 0 },
{ "e", 0, 0 },
{ "enqueue", I18N_NOOP("See append, available for backwards compatability"), 0 },
{ "queue", I18N_NOOP("Queue URLs after the currently playing track"), 0 },
{ "l", 0, 0 },
{ "load", I18N_NOOP("Load URLs, replacing current playlist"), 0 },
{ "m", 0, 0 },
{ "toggle-playlist-window", I18N_NOOP("Toggle the Playlist-window"), 0 },
{ "wizard", I18N_NOOP( "Run first-run wizard" ), 0 },
{ "engine <name>", I18N_NOOP( "Use the <name> engine" ), 0 },
{ "cwd <directory>", I18N_NOOP( "Base for relative filenames/URLs" ), 0 },
{ "cdplay <device>", I18N_NOOP("Play an AudioCD from <device>"), 0 },
//FIXME: after string freeze { "cdplay <device>", I18N_NOOP("Play an AudioCD from <device> or system:/media/<device>"), 0 },
{ 0, 0, 0 }
};
KCmdLineArgs::reset();
KCmdLineArgs::init( argc, argv, &::aboutData ); //calls KApplication::addCmdLineOptions()
KCmdLineArgs::addCmdLineOptions( options ); //add our own options
}
#include <kaction.h>
#include <kshortcutlist.h>
void App::initGlobalShortcuts()
{
EngineController* const ec = EngineController::instance();
m_pGlobalAccel->insert( "play", i18n( "Play" ), 0, KKey("WIN+x"), 0,
ec, TQT_SLOT( play() ), true, true );
m_pGlobalAccel->insert( "pause", i18n( "Pause" ), 0, 0, 0,
ec, TQT_SLOT( pause() ), true, true );
m_pGlobalAccel->insert( "play_pause", i18n( "Play/Pause" ), 0, KKey("WIN+c"), 0,
ec, TQT_SLOT( playPause() ), true, true );
m_pGlobalAccel->insert( "stop", i18n( "Stop" ), 0, KKey("WIN+v"), 0,
ec, TQT_SLOT( stop() ), true, true );
m_pGlobalAccel->insert( "stop_after_global", i18n( "Stop Playing After Current Track" ), 0, KKey("WIN+CTRL+v"), 0,
TQT_TQOBJECT(Playlist::instance()->qscrollview()), TQT_SLOT( toggleStopAfterCurrentTrack() ), true, true );
m_pGlobalAccel->insert( "next", i18n( "Next Track" ), 0, KKey("WIN+b"), 0,
ec, TQT_SLOT( next() ), true, true );
m_pGlobalAccel->insert( "prev", i18n( "Previous Track" ), 0, KKey("WIN+z"), 0,
ec, TQT_SLOT( previous() ), true, true );
m_pGlobalAccel->insert( "volup", i18n( "Increase Volume" ), 0, KKey("WIN+KP_Add"), 0,
ec, TQT_SLOT( increaseVolume() ), true, true );
m_pGlobalAccel->insert( "voldn", i18n( "Decrease Volume" ), 0, KKey("WIN+KP_Subtract"), 0,
ec, TQT_SLOT( decreaseVolume() ), true, true );
m_pGlobalAccel->insert( "seekforward", i18n( "Seek Forward" ), 0, KKey("WIN+Shift+KP_Add"), 0,
ec, TQT_SLOT( seekForward() ), true, true );
m_pGlobalAccel->insert( "seekbackward", i18n( "Seek Backward" ), 0, KKey("WIN+Shift+KP_Subtract"), 0,
ec, TQT_SLOT( seekBackward() ), true, true );
m_pGlobalAccel->insert( "playlist_add", i18n( "Add Media..." ), 0, KKey("WIN+a"), 0,
TQT_TQOBJECT(m_pPlaylistWindow), TQT_SLOT( slotAddLocation() ), true, true );
m_pGlobalAccel->insert( "show", i18n( "Toggle Playlist Window" ), 0, KKey("WIN+p"), 0,
TQT_TQOBJECT(m_pPlaylistWindow), TQT_SLOT( showHide() ), true, true );
#ifdef TQ_WS_X11
m_pGlobalAccel->insert( "osd", i18n( "Show OSD" ), 0, KKey("WIN+o"), 0,
TQT_TQOBJECT(Amarok::OSD::instance()), TQT_SLOT( forceToggleOSD() ), true, true );
#endif
m_pGlobalAccel->insert( "mute", i18n( "Mute Volume" ), 0, KKey("WIN+m"), 0,
ec, TQT_SLOT( mute() ), true, true );
m_pGlobalAccel->insert( "rating1", i18n( "Rate Current Track: 1" ), 0, KKey("WIN+1"), 0,
TQT_TQOBJECT(this), TQT_SLOT( setRating1() ), true, true );
m_pGlobalAccel->insert( "rating2", i18n( "Rate Current Track: 2" ), 0, KKey("WIN+2"), 0,
TQT_TQOBJECT(this), TQT_SLOT( setRating2() ), true, true );
m_pGlobalAccel->insert( "rating3", i18n( "Rate Current Track: 3" ), 0, KKey("WIN+3"), 0,
TQT_TQOBJECT(this), TQT_SLOT( setRating3() ), true, true );
m_pGlobalAccel->insert( "rating4", i18n( "Rate Current Track: 4" ), 0, KKey("WIN+4"), 0,
TQT_TQOBJECT(this), TQT_SLOT( setRating4() ), true, true );
m_pGlobalAccel->insert( "rating5", i18n( "Rate Current Track: 5" ), 0, KKey("WIN+5"), 0,
TQT_TQOBJECT(this), TQT_SLOT( setRating5() ), true, true );
m_pGlobalAccel->setConfigGroup( "Shortcuts" );
m_pGlobalAccel->readSettings( kapp->config() );
m_pGlobalAccel->updateConnections();
//TODO fix kde accel system so that kactions find appropriate global shortcuts
// and there is only one configure shortcuts dialog
KActionCollection* const ac = Amarok::actionCollection();
KAccelShortcutList list( m_pGlobalAccel );
for( uint i = 0; i < list.count(); ++i )
{
KAction *action = ac->action( list.name( i ).latin1() );
if( action )
{
//this is a hack really, also it means there may be two calls to the slot for the shortcut
action->setShortcutConfigurable( false );
action->setShortcut( list.shortcut( i ) );
}
}
}
void App::fixHyperThreading()
{
/** Workaround for stability issues with HyperThreading CPU's, @see BUG 99199.
* First we detect the presence of HyperThreading. If active, we bind amarokapp
* to the first CPU only (hard affinity).
*
* @see http://www-128.ibm.com/developerworks/linux/library/l-affinity.html
* @see http://www.linuxjournal.com/article/6799
* (articles on processor affinity with the linux kernel)
*/
DEBUG_BLOCK
#ifdef __linux__
TQString line;
uint cpuCount = 0;
TQFile cpuinfo( "/proc/cpuinfo" );
if ( cpuinfo.open( IO_ReadOnly ) ) {
while ( cpuinfo.readLine( line, 20000 ) != -1 ) {
if ( line.startsWith( "flags" ) )
cpuCount++;
}
}
// If multiple CPUs are listed with the HT flag, we got HyperThreading enabled
if ( cpuCount > 1 ) {
debug() << "SMP system detected. Enabling WORKAROUND.\n";
// If the library is new enough try and call sched_setaffinity.
#ifdef SCHEDAFFINITY_SUPPORT
cpu_set_t mask;
CPU_ZERO( &mask ); // Initializes all the bits in the mask to zero
CPU_SET( 0, &mask ); // Sets only the bit corresponding to cpu
#ifdef SCHEDAFFINITY_3PARAMS
if ( sched_setaffinity( 0, sizeof(mask), &mask ) == -1 )
#else //SCHEDAFFINITY_3PARAMS
if ( sched_setaffinity( 0, &mask ) == -1 )
#endif //SCHEDAFFINITY_3PARAMS
{
warning() << "sched_setaffinity() call failed with error code: " << errno << endl;
TQTimer::singleShot( 0, this, TQT_SLOT( showHyperThreadingWarning() ) );
return;
}
#else //SCHEDAFFINITY_SUPPORT
warning()<<"glibc failed checks for sched_setaffinity" << endl;
TQTimer::singleShot( 0, this, TQT_SLOT( showHyperThreadingWarning() ) );
#endif //SCHEDAFFINITY_SUPPORT
}
else { debug() << "Workaround not enabled" << endl; }
#else //__linux__
debug() << "SCHEDAFFINITY_SUPPORT disabled since this isn't Linux" << endl;
#endif //__linux__
}
void App::showHyperThreadingWarning() // SLOT
{
const TQString text =
i18n( "<p>You are using a system with multiple CPUs. "
"Please note that Amarok may be unstable with this "
"configuration.</p>"
"<p>If your system has hyperthreading, you can improve Amarok's stability by using the Linux kernel option 'NOHT', "
"or by disabling <i>HyperThreading</i> in your BIOS setup.</p>"
"<p>More information can be found in the README file. For further assistance "
"join us at #amarok on irc.freenode.net.</p>" );
KMessageBox::information( 0, text, i18n( "Warning" ), "showHyperThreadingWarning" );
}
/////////////////////////////////////////////////////////////////////////////////////
// METHODS
/////////////////////////////////////////////////////////////////////////////////////
#include <taglib/id3v1tag.h>
#include <taglib/tbytevector.h>
#include <tqtextcodec.h>
//this class is only used in this module, so I figured I may as well define it
//here and save creating another header/source file combination
class ID3v1StringHandler : public TagLib::ID3v1::StringHandler
{
TQTextCodec *m_codec;
virtual TagLib::String parse( const TagLib::ByteVector &data ) const
{
return QStringToTString( m_codec->toUnicode( data.data(), data.size() ) );
}
virtual TagLib::ByteVector render( const TagLib::String &ts ) const
{
const TQCString qcs = m_codec->fromUnicode( TStringToQString(ts) );
return TagLib::ByteVector( qcs, qcs.length() );
}
public:
ID3v1StringHandler( int codecIndex )
: m_codec( TQTextCodec::codecForIndex( codecIndex ) )
{
debug() << "codec: " << m_codec << endl;
debug() << "codec-name: " << m_codec->name() << endl;
}
ID3v1StringHandler( TQTextCodec *codec )
: m_codec( codec )
{
debug() << "codec: " << m_codec << endl;
debug() << "codec-name: " << m_codec->name() << endl;
}
};
//SLOT
void App::applySettings( bool firstTime )
{
///Called when the configDialog is closed with OK or Apply
DEBUG_BLOCK
//determine and apply colors first
applyColorScheme();
#ifdef TQ_WS_X11
TrackToolTip::instance()->removeFromWidget( m_pTray );
#endif
if( AmarokConfig::showPlayerWindow() )
{
if( !m_pPlayerWindow )
{
//the player Window becomes the main Window
//it is the focus for hideWithMainWindow behaviour etc.
//it gets the majestic "Amarok" caption
m_pPlaylistWindow->setCaption( kapp->makeStdCaption( i18n("Playlist") ) );
m_pPlayerWindow = new PlayerWidget( m_pPlaylistWindow, "PlayerWindow", firstTime && AmarokConfig::playlistWindowEnabled() );
//don't show PlayerWindow on firstTime, that is done below
//we need to explicately set the PL button if it's the first time
if( !firstTime ) m_pPlayerWindow->show();
connect( m_pPlayerWindow, TQT_SIGNAL(playlistToggled( bool )), m_pPlaylistWindow, TQT_SLOT(showHide()) );
#ifdef TQ_WS_X11
//TODO get this to work!
//may work if you set no parent for the systray?
//KWin::setSystemTrayWindowFor( m_pTray->winId(), m_pPlayerWindow->winId() );
delete m_pTray; m_pTray = new Amarok::TrayIcon( m_pPlayerWindow );
//make tray icon behave properly after selecting to show or hide player window
m_pTray->engineStateChanged(EngineController::instance()->engine()->state(), EngineController::instance()->engine()->state());
m_pTray->engineNewMetaData(EngineController::instance()->bundle(), false);
#endif
//make player window minimal if it was last time
if( AmarokConfig::playerWindowMinimalView() ){
m_pPlayerWindow->setMinimalView( true );
}
}
else
//this is called in the PlayerWindow ctor, hence the else
m_pPlayerWindow->applySettings();
} else if( m_pPlayerWindow ) {
#ifdef TQ_WS_X11
delete m_pTray; m_pTray = new Amarok::TrayIcon( m_pPlaylistWindow );
m_pTray->engineStateChanged(EngineController::instance()->engine()->state(), EngineController::instance()->engine()->state());
m_pTray->engineNewMetaData(EngineController::instance()->bundle(), false);
#endif
delete m_pPlayerWindow; m_pPlayerWindow = 0;
//Set the caption correctly.
if ( !EngineController::instance()->bundle().prettyTitle().isEmpty() )
m_pPlaylistWindow->setCaption( i18n("Amarok - %1").arg( EngineController::instance()->bundle().veryNiceTitle() ) );
else
m_pPlaylistWindow->setCaption( "Amarok" );
//m_pPlaylistWindow->show(); //must be shown //we do below now
//ensure that at least one Menu is plugged into an accessible UI element
if( !AmarokConfig::showMenuBar() && !Amarok::actionCollection()->action( "amarok_menu" )->isPlugged() )
playlistWindow()->createGUI();
}
playlistWindow()->applySettings();
Scrobbler::instance()->applySettings();
Amarok::OSD::instance()->applySettings();
CollectionDB::instance()->applySettings();
#ifdef TQ_WS_X11
m_pTray->setShown( AmarokConfig::showTrayIcon() );
TrackToolTip::instance()->addToWidget( m_pTray );
#endif
//on startup we need to show the window, but only if it wasn't hidden on exit
//and always if the trayicon isn't showing
TQWidget* main_window = mainWindow();
#ifdef TQ_WS_X11
if( ( main_window && firstTime && !Amarok::config()->readBoolEntry( "HiddenOnExit", false ) ) || ( main_window && !AmarokConfig::showTrayIcon() ) )
#endif
{
main_window->show();
//takes longer but feels shorter. Crazy eh? :)
kapp->eventLoop()->processEvents( TQEventLoop::ExcludeUserInput );
}
{ //<Engine>
EngineBase *engine = EngineController::engine();
if( firstTime || AmarokConfig::soundSystem() !=
PluginManager::getService( engine )->property( "X-KDE-Amarok-name" ).toString() )
{
//will unload engine for us first if necessary
engine = EngineController::instance()->loadEngine();
}
engine->setXfadeLength( AmarokConfig::crossfade() ? AmarokConfig::crossfadeLength() : 0 );
engine->setVolume( AmarokConfig::masterVolume() );
engine->setEqualizerEnabled( AmarokConfig::equalizerEnabled() );
if ( AmarokConfig::equalizerEnabled() )
engine->setEqualizerParameters( AmarokConfig::equalizerPreamp(), AmarokConfig::equalizerGains() );
Amarok::actionCollection()->action("play_audiocd")->setEnabled( EngineController::hasEngineProperty( "HasKIO" ) || EngineController::hasEngineProperty("HasCDDA"));
} //</Engine>
{ //<Collection>
CollectionView::instance()->renderView(true);
} //</Collection>
{ //<Context>
ContextBrowser::instance()->renderView();
} //</Context>
{ // delete unneeded cover images from cache
const TQString size = TQString::number( AmarokConfig::coverPreviewSize() ) + '@';
const TQDir cacheDir = Amarok::saveLocation( "albumcovers/cache/" );
const TQStringList obsoleteCovers = cacheDir.entryList( "*" );
foreach( obsoleteCovers )
if ( !(*it).startsWith( size ) && !(*it).startsWith( "50@" ) )
TQFile( cacheDir.filePath( *it ) ).remove();
}
//if ( !firstTime )
// Bizarrely and ironically calling this causes crashes for
// some people! FIXME
//AmarokConfig::writeConfig();
}
//SLOT
void
App::continueInit()
{
DEBUG_BLOCK
const KCmdLineArgs* const args = KCmdLineArgs::parsedArgs();
bool restoreSession = args->count() == 0 || args->isSet( "append" ) || args->isSet( "enqueue" )
|| Amarok::config()->readBoolEntry( "AppendAsDefault", false );
// Make this instance so it can start receiving signals
MoodServer::instance();
// Remember old folder setup, so we can detect changes after the wizard was used
//const TQStringList oldCollectionFolders = MountPointManager::instance()->collectionFolders();
if ( Amarok::config()->readBoolEntry( "First Run", true ) || args->isSet( "wizard" ) ) {
std::cout << "STARTUP\n" << std::flush; //hide the splashscreen
firstRunWizard();
Amarok::config()->writeEntry( "First Run", false );
Amarok::config()->sync();
}
CollectionDB::instance()->checkDatabase();
m_pMediaDeviceManager = MediaDeviceManager::instance();
m_pGlobalAccel = new KGlobalAccel( TQT_TQOBJECT(this) );
m_pPlaylistWindow = new PlaylistWindow();
#ifdef TQ_WS_X11
m_pTray = new Amarok::TrayIcon( m_pPlaylistWindow );
#endif
m_pPlaylistWindow->init(); //creates the playlist, browsers, etc.
//init playlist window as soon as the database is guaranteed to be usable
//connect( CollectionDB::instance(), TQT_SIGNAL( databaseUpdateDone() ), m_pPlaylistWindow, TQT_SLOT( init() ) );
initGlobalShortcuts();
//load previous playlist in separate thread
if ( restoreSession && AmarokConfig::savePlaylist() )
{
Playlist::instance()->restoreSession();
//Debug::stamp();
//p->restoreSession();
}
if( args->isSet( "engine" ) ) {
// we correct some common errors (case issues, missing -engine off the end)
TQString engine = args->getOption( "engine" ).lower();
if( engine.startsWith( "gstreamer" ) ) engine = "gst-engine";
if( !engine.endsWith( "engine" ) ) engine += "-engine";
AmarokConfig::setSoundSystem( engine );
}
Debug::stamp();
//create engine, show PlayerWindow, show TrayIcon etc.
applySettings( true );
Debug::stamp();
// Start ScriptManager. Must be created _after_ PlaylistWindow.
ScriptManager::instance();
Debug::stamp();
//notify loader application that we have started
std::cout << "STARTUP\n" << std::flush;
//after this point only analyzer and temporary pixmaps will be created
TQPixmap::setDefaultOptimization( TQPixmap::BestOptim );
//do after applySettings(), or the OSD will flicker and other wierdness!
//do before restoreSession()!
EngineController::instance()->attach( this );
//set a default interface
engineStateChanged( Engine::Empty );
if ( AmarokConfig::resumePlayback() && restoreSession && !args->isSet( "stop" ) ) {
//restore session as long as the user didn't specify media to play etc.
//do this after applySettings() so OSD displays correctly
EngineController::instance()->restoreSession();
}
CollectionDB *collDB = CollectionDB::instance();
//Collection scan is triggered in firstRunWizard if the colelction folder setup was changed in the wizard
// If database version is updated, the collection needs to be rescanned.
// Works also if the collection is empty for some other reason
// (e.g. deleted collection.db)
if ( CollectionDB::instance()->isEmpty() )
{
//connect( collDB, TQT_SIGNAL( databaseUpdateDone() ), collDB, TQT_SLOT( startScan() ) );
collDB->startScan();
}
else if ( AmarokConfig::monitorChanges() )
//connect( collDB, TQT_SIGNAL( databaseUpdateDone() ), collDB, TQT_SLOT( scanModifiedDirs() ) );
collDB->scanModifiedDirs();
handleCliArgs();
}
void
App::applyColorScheme()
{
TQColorGroup group;
using Amarok::ColorScheme::AltBase;
int h, s, v;
TQWidget* const browserBar = TQT_TQWIDGET( playlistWindow()->child( "BrowserBar" ) );
TQWidget* const contextBrowser = TQT_TQWIDGET( ContextBrowser::instance() );
if( AmarokConfig::schemeKDE() )
{
AltBase = KGlobalSettings::alternateBackgroundColor();
playlistWindow()->unsetPalette();
browserBar->unsetPalette();
contextBrowser->unsetPalette();
PlayerWidget::determineAmarokColors();
}
else if( AmarokConfig::schemeAmarok() )
{
group = TQApplication::palette().active();
const TQColor bg( Amarok::blue );
AltBase.setRgb( 57, 64, 98 );
group.setColor( TQColorGroup::Text, TQt::white );
group.setColor( TQColorGroup::Link, 0xCCCCCC );
group.setColor( TQColorGroup::Base, bg );
group.setColor( TQColorGroup::Foreground, 0xd7d7ef );
group.setColor( TQColorGroup::Background, AltBase );
group.setColor( TQColorGroup::Button, AltBase );
group.setColor( TQColorGroup::ButtonText, 0xd7d7ef );
// group.setColor( TQColorGroup::Light, TQt::cyan /*lighter than Button color*/ );
// group.setColor( TQColorGroup::Midlight, TQt::blue /*between Button and Light*/ );
// group.setColor( TQColorGroup::Dark, TQt::green /*darker than Button*/ );
// group.setColor( TQColorGroup::Mid, TQt::red /*between Button and Dark*/ );
// group.setColor( TQColorGroup::Shadow, TQt::yellow /*a very dark color. By default, the shadow color is TQt::black*/ );
group.setColor( TQColorGroup::Highlight, TQt::white );
group.setColor( TQColorGroup::HighlightedText, bg );
//group.setColor( TQColorGroup::BrightText, TQColor( 0xff, 0x40, 0x40 ) ); //GlowColor
AltBase.getHsv( &h, &s, &v );
group.setColor( TQColorGroup::Midlight, TQColor( h, s/3, (int)(v * 1.2), TQColor::Hsv ) ); //column separator in playlist
//TODO set all colours, even button colours, that way we can change the dark,
//light, etc. colours and Amarok scheme will look much better
using namespace Amarok::ColorScheme;
Base = Amarok::blue;