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 "tractdetooltip.h" //engineNewMetaData()
#include <iostream>
#include <tdeconfigdialogmanager.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 <tdeio/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 TDEAboutData aboutData( "amarok",
I18N_NOOP( "Amarok" ), APP_VERSION,
I18N_NOOP( "The audio player for TDE" ), TDEAboutData::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()
: TDEApplication(TDEApplication::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() )
tqWarning("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];
TDECmdLineArgs* const args = TDECmdLineArgs::parsedArgs();
if ( args->isSet( "cwd" ) )
{
strncpy(cwd, args->getOption( "cwd" ), sizeof(cwd) );
cwd[sizeof(cwd)-1] = '\0';
TDECmdLineArgs::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 }
};
TDECmdLineArgs::reset();
TDECmdLineArgs::init( argc, argv, &::aboutData ); //calls TDEApplication::addCmdLineOptions()
TDECmdLineArgs::addCmdLineOptions( options ); //add our own options
}
#include <tdeaction.h>
#include <tdeshortcutlist.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 tdeactions find appropriate global shortcuts
// and there is only one configure shortcuts dialog
TDEActionCollection* const ac = Amarok::actionCollection();
TDEAccelShortcutList list( m_pGlobalAccel );
for( uint i = 0; i < list.count(); ++i )
{
TDEAction *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-TDE-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 TDECmdLineArgs* const args = TDECmdLineArgs::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 TDEGlobalAccel( 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 = TDEGlobalSettings::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;
Text = TQt::white;
Background = 0x002090;
Foreground = 0x80A0FF;
//all children() derive their palette from this
playlistWindow()->setPalette( TQPalette( group, group, group ) );
browserBar->unsetPalette();
contextBrowser->setPalette( TQPalette( group, group, group ) );
}
else if( AmarokConfig::schemeCustom() )
{
// we try to be smart: this code figures out contrasting colors for
// selection and alternate background rows
group = TQApplication::palette().active();
const TQColor fg( AmarokConfig::playlistWindowFgColor() );
const TQColor bg( AmarokConfig::playlistWindowBgColor() );
//TODO use the ensureContrast function you devised in BlockAnalyzer
bg.hsv( &h, &s, &v );
v += (v < 128) ? +50 : -50;
v &= 255; //ensures 0 <= v < 256
AltBase.setHsv( h, s, v );
fg.hsv( &h, &s, &v );
v += (v < 128) ? +150 : -150;
v &= 255; //ensures 0 <= v < 256
TQColor highlight( h, s, v, TQColor::Hsv );
group.setColor( TQColorGroup::Base, bg );
group.setColor( TQColorGroup::Background, bg.dark( 115 ) );
group.setColor( TQColorGroup::Text, fg );
group.setColor( TQColorGroup::Link, fg.light( 120 ) );
group.setColor( TQColorGroup::Highlight, highlight );
group.setColor( TQColorGroup::HighlightedText, TQt::white );
group.setColor( TQColorGroup::Dark, TQt::darkGray );
PlayerWidget::determineAmarokColors();
// we only colour the middle section since we only
// allow the user to choose two colours
browserBar->setPalette( TQPalette( group, group, group ) );
contextBrowser->setPalette( TQPalette( group, group, group ) );
playlistWindow()->unsetPalette();
}
// set the TDEListView alternate colours
TQObjectList* const list = playlistWindow()->queryList( "TDEListView" );
for( TQObject *o = list->first(); o; o = list->next() )
static_cast<TDEListView*>(TQT_TQWIDGET(o))->setAlternateBackground( AltBase );
delete list; //heap allocated!
}
bool Amarok::genericEventHandler( TQWidget *recipient, TQEvent *e )
{
//this is used as a generic event handler for widgets that want to handle
//typical events in an Amarok fashion
//to use it just pass the event eg:
//
// void Foo::barEvent( TQBarEvent *e )
// {
// Amarok::genericEventHandler( this, e );
// }
switch( e->type() )
{
case TQEvent::DragEnter:
#define e static_cast<TQDropEvent*>(e)
e->accept( KURLDrag::canDecode( e ) );
break;
case TQEvent::Drop:
if( KURLDrag::canDecode( e ) )
{
TQPopupMenu popup;
//FIXME this isn't a good way to determine if there is a currentTrack, need playlist() function
const bool b = EngineController::engine()->loaded();
popup.insertItem( SmallIconSet( Amarok::icon( "add_playlist" ) ), i18n( "&Append to Playlist" ),
Playlist::Append );
popup.insertItem( SmallIconSet( Amarok::icon( "add_playlist" ) ), i18n( "Append && &Play" ),
Playlist::DirectPlay | Playlist::Append );
if( b )
popup.insertItem( SmallIconSet( Amarok::icon( "fast_forward" ) ), i18n( "&Queue Track" ),
Playlist::Queue );
popup.insertSeparator();
popup.insertItem( i18n( "&Cancel" ), 0 );
const int id = popup.exec( recipient->mapToGlobal( e->pos() ) );
KURL::List list;
KURLDrag::decode( e, list );
if ( id > 0 )
Playlist::instance()->insertMedia( list, id );
}
else return false;
#undef e
break;
//this like every entry in the generic event handler is used by more than one widget
//please don't remove!
case TQEvent::Wheel:
{
#define e TQT_TQWHEELEVENT(e)
//this behaviour happens for the systray and the player window
//to override one, override it in that class
switch( e->state() )
{
case TQt::ControlButton:
{
const bool up = e->delta() > 0;
//if this seems strange to you, please bring it up on #amarok
//for discussion as we can't decide which way is best!
if( up ) EngineController::instance()->previous();
else EngineController::instance()->next();
break;
}
case TQt::ShiftButton:
{
EngineController::instance()->seekRelative( ( e->delta() / 120 ) * 10000 ); // 10 seconds
break;
}
default:
EngineController::instance()->increaseVolume( e->delta() / Amarok::VOLUME_SENSITIVITY );
}
e->accept();
#undef e
break;
}
case TQEvent::Close:
//KDE policy states we should hide to tray and not quit() when the
//close window button is pushed for the main widget
TQT_TQCLOSEEVENT(e)->accept(); //if we don't do this the info box appears on quit()!
if( AmarokConfig::showTrayIcon() && !e->spontaneous() && !kapp->sessionSaving() )
{
KMessageBox::information( recipient,
i18n( "<qt>Closing the main-window will keep Amarok running in the System Tray. "
"Use <B>Quit</B> from the menu, or the Amarok tray-icon to exit the application.</qt>" ),
i18n( "Docking in System Tray" ), "hideOnCloseInfo" );
}
else pApp->quit();
break;
default:
return false;
}
return true;
}
void App::engineStateChanged( Engine::State state, Engine::State oldState )
{
const MetaBundle &bundle = EngineController::instance()->bundle();
switch( state )
{
case Engine::Empty:
if ( AmarokConfig::showPlayerWindow() )
m_pPlaylistWindow->setCaption( kapp->makeStdCaption( i18n("Playlist") ) );
else m_pPlaylistWindow->setCaption( "Amarok" );
TrackToolTip::instance()->clear();
Amarok::OSD::instance()->setImage( KIconLoader().iconPath( "amarok", -KIcon::SizeHuge ) );
break;
case Engine::Playing:
if ( oldState == Engine::Paused )
Amarok::OSD::instance()->OSDWidget::show( i18n( "state, as in playing", "Play" ) );
if ( !bundle.prettyTitle().isEmpty() )
m_pPlaylistWindow->setCaption( i18n("Amarok - %1").arg( bundle.veryNiceTitle() ) );
break;
case Engine::Paused:
Amarok::OSD::instance()->OSDWidget::show( i18n("Paused") );
break;
case Engine::Idle:
if ( AmarokConfig::showPlayerWindow() )
m_pPlaylistWindow->setCaption( kapp->makeStdCaption( i18n("Playlist") ) );
else m_pPlaylistWindow->setCaption( "Amarok" );
break;
default:
;
}
}
void App::engineNewMetaData( const MetaBundle &bundle, bool /*trackChanged*/ )
{
Amarok::OSD::instance()->show( bundle );
if ( !bundle.prettyTitle().isEmpty() )
m_pPlaylistWindow->setCaption( i18n("Amarok - %1").arg( bundle.veryNiceTitle() ) );
TrackToolTip::instance()->setTrack( bundle );
}
void App::engineTrackPositionChanged( long position, bool /*userSeek*/ )
{
TrackToolTip::instance()->setPos( position );
}
void App::engineVolumeChanged( int newVolume )
{
Amarok::OSD::instance()->OSDWidget::volChanged( newVolume );
}
void App::slotConfigEqualizer() //SLOT
{
EqualizerSetup::instance()->show();
EqualizerSetup::instance()->raise();
}
void App::slotConfigAmarok( const TQCString& page )
{
DEBUG_THREAD_FUNC_INFO
AmarokConfigDialog* dialog = static_cast<AmarokConfigDialog*>( TDEConfigDialog::exists( "settings" ) );
if( !dialog )
{
//TDEConfigDialog didn't find an instance of this dialog, so lets create it :
dialog = new AmarokConfigDialog( m_pPlaylistWindow, "settings", AmarokConfig::self() );
connect( dialog, TQT_SIGNAL(settingsChanged()), TQT_SLOT(applySettings()) );
}
//FIXME it seems that if the dialog is on a different desktop it gets lost
// what do to? detect and move it?
if ( page.isNull() )
dialog->showPage( AmarokConfigDialog::s_currentPage );
else
dialog->showPageByName( page );
dialog->show();
dialog->raise();
dialog->setActiveWindow();
}
void App::slotConfigShortcuts()
{
KKeyDialog::configure( Amarok::actionCollection(), m_pPlaylistWindow );
}
void App::slotConfigGlobalShortcuts()
{
KKeyDialog::configure( m_pGlobalAccel, true, m_pPlaylistWindow, true );
}
void App::slotConfigToolBars()
{
PlaylistWindow* const pw = playlistWindow();
KEditToolbar dialog( pw->actionCollection(), pw->xmlFile(), true, pw );
dialog.showButtonApply( false );
if( dialog.exec() )
{
playlistWindow()->reloadXML();
playlistWindow()->createGUI();
}
}
void App::firstRunWizard()
{
///show firstRunWizard
DEBUG_BLOCK
FirstRunWizard wizard;
setTopWidget( &wizard );
TDEConfigDialogManager* config = new TDEConfigDialogManager(&wizard, AmarokConfig::self(), "wizardconfig");
config->updateWidgets();
// connect(config, TQT_SIGNAL(settingsChanged()), TQT_SLOT(updateSettings()));
wizard.setCaption( makeStdCaption( i18n( "First-Run Wizard" ) ) );
if( wizard.exec() != TQDialog::Rejected )
{
//make sure that the DB config is stored in amarokrc before calling CollectionDB's ctor
AmarokConfig::setDatabaseEngine(
TQString::number( Amarok::databaseTypeCode( wizard.dbSetup7->databaseEngine->currentText() ) ) );
config->updateSettings();
const TQStringList oldCollectionFolders = MountPointManager::instance()->collectionFolders();
wizard.writeCollectionConfig();
// If wizard is invoked at runtime, rescan collection if folder setup has changed
if ( !Amarok::config()->readBoolEntry( "First Run", true ) &&
oldCollectionFolders != MountPointManager::instance()->collectionFolders() )
CollectionDB::instance()->startScan();
}
}
void App::setUseScores( bool use )
{
AmarokConfig::setUseScores( use );
emit useScores( use );
}
void App::setUseRatings( bool use )
{
AmarokConfig::setUseRatings( use );
emit useRatings( use );
}
void App::setMoodbarPrefs( bool show, bool moodier, int alter, bool withMusic )
{
AmarokConfig::setShowMoodbar( show );
AmarokConfig::setMakeMoodier( moodier );
AmarokConfig::setAlterMood( alter );
AmarokConfig::setMoodsWithMusic( withMusic );
emit moodbarPrefs( show, moodier, alter, withMusic );
}
TDEIO::Job *App::trashFiles( const KURL::List &files )
{
#if KDE_IS_VERSION( 3, 3, 91 )
TDEIO::Job *job = TDEIO::trash( files, true /*show progress*/ );
Amarok::StatusBar::instance()->newProgressOperation( job ).setDescription( i18n("Moving files to trash") );
connect( job, TQT_SIGNAL( result( TDEIO::Job* ) ), this, TQT_SLOT( slotTrashResult( TDEIO::Job* ) ) );
return job;
#else
TDEIO::Job* job = TDEIO::move( files, TDEGlobalSettings::trashPath() );
return job;
#endif
}
void App::setRating( int n )
{
if( !AmarokConfig::useRatings() ) return;
n *= 2;
const Engine::State s = EngineController::instance()->engine()->state();
if( s == Engine::Playing || s == Engine::Paused || s == Engine::Idle )
{
const TQString path = EngineController::instance()->playingURL().path();
CollectionDB::instance()->setSongRating( path, n, true );
const int rating = CollectionDB::instance()->getSongRating( path );
EngineController::instance()->updateBundleRating( rating );
Amarok::OSD::instance()->OSDWidget::ratingChanged( rating );
if( !Amarok::OSD::instance()->isShown() && !PlaylistWindow::self()->isReallyShown() )
Amarok::OSD::instance()->forceToggleOSD();
}
else if( PlaylistWindow::self()->isReallyShown() && Playlist::instance()->qscrollview()->hasFocus() )
Playlist::instance()->setSelectedRatings( n );
}
void App::slotTrashResult( TDEIO::Job *job )
{
if( job->error() )
job->showErrorDialog( PlaylistWindow::self() );
}
TQWidget *App::mainWindow() const
{
return AmarokConfig::showPlayerWindow() ? TQT_TQWIDGET( m_pPlayerWindow )
: TQT_TQWIDGET( m_pPlaylistWindow );
}
void App::quit()
{
emit prepareToQuit();
if( MediaBrowser::instance()->blockQuit() )
{
// don't quit yet, as some media devices still have to finish transferring data
TQTimer::singleShot( 100, this, TQT_SLOT( quit() ) );
return;
}
TDEApplication::quit();
}
namespace Amarok
{
/// @see amarok.h
TQWidget *mainWindow()
{
return pApp->playlistWindow();
}
TDEActionCollection *actionCollection()
{
return pApp->playlistWindow()->actionCollection();
}
TDEConfig *config( const TQString &group )
{
//Slightly more useful config() that allows setting the group simultaneously
kapp->config()->setGroup( group );
return kapp->config();
}
bool invokeBrowser( const TQString& url )
{
//URL can be in whatever forms KURL::fromPathOrURL understands - ie most.
const TQString cmd = "%1 \"%2\"";
return KRun::runCommand( cmd.arg( AmarokConfig::externalBrowser(), KURL::fromPathOrURL( url ).url() ) ) > 0;
}
namespace ColorScheme
{
TQColor Base;
TQColor Text;
TQColor Background;
TQColor Foreground;
TQColor AltBase;
}
OverrideCursor::OverrideCursor( TQt::CursorShape cursor )
{
TQApplication::setOverrideCursor( cursor == TQt::WaitCursor ? KCursor::waitCursor() : KCursor::workingCursor() );
}
OverrideCursor::~OverrideCursor()
{
TQApplication::restoreOverrideCursor();
}
TQString saveLocation( const TQString &directory )
{
globalDirsMutex.lock();
TQString result = TDEGlobal::dirs()->saveLocation( "data", TQString("amarok/") + directory, true );
globalDirsMutex.unlock();
return result;
}
TQString cleanPath( const TQString &path )
{
TQString result = path;
// german umlauts
result.replace( TQChar(0x00e4), "ae" ).replace( TQChar(0x00c4), "Ae" );
result.replace( TQChar(0x00f6), "oe" ).replace( TQChar(0x00d6), "Oe" );
result.replace( TQChar(0x00fc), "ue" ).replace( TQChar(0x00dc), "Ue" );
result.replace( TQChar(0x00df), "ss" );
// some strange accents
result.replace( TQChar(0x00e7), "c" ).replace( TQChar(0x00c7), "C" );
result.replace( TQChar(0x00fd), "y" ).replace( TQChar(0x00dd), "Y" );
result.replace( TQChar(0x00f1), "n" ).replace( TQChar(0x00d1), "N" );
// czech letters with carons
result.replace( TQChar(0x0161), "s" ).replace( TQChar(0x0160), "S" );
result.replace( TQChar(0x010d), "c" ).replace( TQChar(0x010c), "C" );
result.replace( TQChar(0x0159), "r" ).replace( TQChar(0x0158), "R" );
result.replace( TQChar(0x017e), "z" ).replace( TQChar(0x017d), "Z" );
result.replace( TQChar(0x0165), "t" ).replace( TQChar(0x0164), "T" );
result.replace( TQChar(0x0148), "n" ).replace( TQChar(0x0147), "N" );
result.replace( TQChar(0x010f), "d" ).replace( TQChar(0x010e), "D" );
// accented vowels
TQChar a[] = { 'a', 0xe0,0xe1,0xe2,0xe3,0xe5, 0 };
TQChar A[] = { 'A', 0xc0,0xc1,0xc2,0xc3,0xc5, 0 };
TQChar e[] = { 'e', 0xe8,0xe9,0xea,0xeb,0x11b, 0 };
TQChar E[] = { 'E', 0xc8,0xc9,0xca,0xcb,0x11a, 0 };
TQChar i[] = { 'i', 0xec,0xed,0xee,0xef, 0 };
TQChar I[] = { 'I', 0xcc,0xcd,0xce,0xcf, 0 };
TQChar o[] = { 'o', 0xf2,0xf3,0xf4,0xf5,0xf8, 0 };
TQChar O[] = { 'O', 0xd2,0xd3,0xd4,0xd5,0xd8, 0 };
TQChar u[] = { 'u', 0xf9,0xfa,0xfb,0x16f, 0 };
TQChar U[] = { 'U', 0xd9,0xda,0xdb,0x16e, 0 };
TQChar nul[] = { 0 };
TQChar *replacements[] = { a, A, e, E, i, I, o, O, u, U, nul };
for( uint i = 0; i < result.length(); i++ )
{
TQChar c = result.ref( i );
for( uint n = 0; replacements[n][0] != TQChar(0); n++ )
{
for( uint k=0; replacements[n][k] != TQChar(0); k++ )
{
if( replacements[n][k] == c )
{
c = replacements[n][0];
}
}
}
result.ref( i ) = c;
}
return result;
}
TQString asciiPath( const TQString &path )
{
TQString result = path;
for( uint i = 0; i < result.length(); i++ )
{
TQChar c = result.ref( i );
if( c > TQChar(0x7f) || c == TQChar(0) )
{
c = '_';
}
result.ref( i ) = c;
}
return result;
}
TQString vfatPath( const TQString &path )
{
TQString s = path;
for( uint i = 0; i < s.length(); i++ )
{
TQChar c = s.ref( i );
if( c < TQChar(0x20)
|| c=='*' || c=='?' || c=='<' || c=='>'
|| c=='|' || c=='"' || c==':' || c=='/'
|| c=='\\' )
c = '_';
s.ref( i ) = c;
}
uint len = s.length();
if( len == 3 || (len > 3 && s[3] == '.') )
{
TQString l = s.left(3).lower();
if( l=="aux" || l=="con" || l=="nul" || l=="prn" )
s = '_' + s;
}
else if( len == 4 || (len > 4 && s[4] == '.') )
{
TQString l = s.left(3).lower();
TQString d = s.mid(3,1);
if( (l=="com" || l=="lpt") &&
(d=="0" || d=="1" || d=="2" || d=="3" || d=="4" ||
d=="5" || d=="6" || d=="7" || d=="8" || d=="9") )
s = '_' + s;
}
while( s.startsWith( "." ) )
s = s.mid(1);
while( s.endsWith( "." ) )
s = s.left( s.length()-1 );
s = s.left(255);
len = s.length();
if( s[len-1] == ' ' )
s[len-1] = '_';
return s;
}
TQString decapitateString( const TQString &input, const TQString &ref )
{
TQString t = ref.upper();
int length = t.length();
int commonLength = 0;
while( length > 0 )
{
if ( input.upper().startsWith( t ) )
{
commonLength = t.length();
t = ref.upper().left( t.length() + length/2 );
length = length/2;
}
else
{
t = ref.upper().left( t.length() - length/2 );
length = length/2;
}
}
TQString clean = input;
if( t.endsWith( " " ) || !ref.at( t.length() ).isLetterOrNumber() ) // common part ends with a space or complete word
clean = input.right( input.length() - commonLength ).stripWhiteSpace();
return clean;
}
void setUseScores( bool use ) { App::instance()->setUseScores( use ); }
void setUseRatings( bool use ) { App::instance()->setUseRatings( use ); }
void setMoodbarPrefs( bool show, bool moodier, int alter, bool withMusic )
{ App::instance()->setMoodbarPrefs( show, moodier, alter, withMusic ); }
TDEIO::Job *trashFiles( const KURL::List &files ) { return App::instance()->trashFiles( files ); }
}
#include "app.moc"