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.
tdemultimedia/kscd/kscd.cpp

1678 lines
46 KiB

/*
* Kscd - A simple cd player for the KDE Project
*
* Copyright (c) 1997 Bernd Johannes wuebben@math.cornell.edu
* Copyright (c) 2002-2003 Aaron J. Seigo <aseigo@kde.org>
* Copyright (c) 2004 Alexander Kern <alex.kern@gmx.de>
* Copyright (c) 2003-2006 Richard Lärkäng <nouseforaname@home.se>
*
* 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, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include <tqdir.h>
#include <tqregexp.h>
#include <tqtextstream.h>
#include <tqlayout.h>
#include <tqhbox.h>
#include <tqvbox.h>
#include <tqapplication.h>
#include <tqgroupbox.h>
#include <tqsqlpropertymap.h>
#include <dcopclient.h>
#include <kaboutdata.h>
#include <kaccel.h>
#include <kaction.h>
#include <dcopref.h>
#include <kcharsets.h>
#include <kcmdlineargs.h>
#include <tdeconfig.h>
#include <kdebug.h>
#include <kdialogbase.h>
#include <kemailsettings.h>
#include <kglobal.h>
#include <khelpmenu.h>
#include <kkeydialog.h>
#include <kiconloader.h>
#include <kinputdialog.h>
#include <klocale.h>
#include <kmainwindow.h>
#include <kmessagebox.h>
#include <kpopupmenu.h>
#include <kprotocolmanager.h>
#include <krun.h>
#include <kstandarddirs.h>
#include <kstdaction.h>
#include <kstringhandler.h>
#include <kurl.h>
#include <kuniqueapplication.h>
#include <kglobalsettings.h>
#include <tdecmoduleloader.h>
#include <tdeconfigdialog.h>
#include "docking.h"
#include "kscd.h"
#include "version.h"
#include "prefs.h"
#include <twin.h>
#include <netwm.h>
#include <stdlib.h>
#include <config.h>
#include "cddbdlg.h"
#include "configWidget.h"
#include <tqtextcodec.h>
#include <kcompactdisc.h>
#include <fixx11h.h>
static const char description[] = I18N_NOOP("TDE CD player");
bool stoppedByUser = false;
/****************************************************************************
The GUI part
*****************************************************************************/
KSCD::KSCD( TQWidget *parent, const char *name )
: DCOPObject("CDPlayer"),
kscdPanelDlg( parent, name, TQt::WDestructiveClose ),
configDialog(0L),
cddialog(0L), //!!!!
jumpToTrack(0L),
updateTime(true),
m_dockWidget(0)
{
m_cd = new KCompactDisc();
cddbInfo.clear(); // The first freedb revision is "0" //!!!!
random_current = random_list.begin();
cddb = new KCDDB::Client();
connect(cddb, TQT_SIGNAL(finished(CDDB::Result)), TQT_TQOBJECT(this), TQT_SLOT(lookupCDDBDone(CDDB::Result)));
#if defined(BUILD_CDDA)
audio_systems_list
<< "arts"
#if defined(HAVE_ARTS_LIBASOUND2)
<< "alsa"
#endif
#ifdef USE_SUN_AUDIO
<< "sun"
#endif
;
#endif
readSettings();
initFont();
drawPanel();
setColors();
// the time slider
timeIcon->setPixmap(SmallIcon("player_time"));
connect(timeSlider, TQT_SIGNAL(sliderPressed()), TQT_SLOT(timeSliderPressed()));
connect(timeSlider, TQT_SIGNAL(sliderReleased()), TQT_SLOT(timeSliderReleased()));
connect(timeSlider, TQT_SIGNAL(sliderMoved(int)), TQT_SLOT(timeSliderMoved(int)));
connect(timeSlider, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(jumpToTime(int)));
// the volume slider
volumeIcon->setPixmap(SmallIcon("player_volume"));
volumeSlider->setValue(Prefs::volume());
TQString str;
str = TQString::fromUtf8( TQCString().sprintf(i18n("Vol: %02d%%").utf8(), Prefs::volume()) );
volumelabel->setText(str);
connect(volumeSlider, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(volChanged(int)));
/* FIXME check for return value */
setDevicePaths(/*Prefs::cdDevice(), Prefs::audioSystem(), Prefs::audioDevice()*/);
connect(m_cd, TQT_SIGNAL(trackPlaying(unsigned, unsigned)), TQT_TQOBJECT(this), TQT_SLOT(trackUpdate(unsigned, unsigned)));
connect(m_cd, TQT_SIGNAL(trackPaused(unsigned, unsigned)), TQT_TQOBJECT(this), TQT_SLOT(trackUpdate(unsigned, unsigned)));
connect(m_cd, TQT_SIGNAL(trackChanged(unsigned, unsigned)), TQT_TQOBJECT(this), TQT_SLOT(trackChanged(unsigned, unsigned)));
connect(m_cd, TQT_SIGNAL(discStopped()), TQT_TQOBJECT(this), TQT_SLOT(discStopped()));
connect(m_cd, TQT_SIGNAL(discChanged(unsigned)), TQT_TQOBJECT(this), TQT_SLOT(discChanged(unsigned)));
connect( &queryledtimer, TQT_SIGNAL(timeout()), TQT_SLOT(togglequeryled()) );
connect( &titlelabeltimer, TQT_SIGNAL(timeout()), TQT_SLOT(titlelabeltimeout()) );
connect( &cycletimer, TQT_SIGNAL(timeout()), TQT_SLOT(cycletimeout()) );
connect( &jumpTrackTimer, TQT_SIGNAL(timeout()), TQT_SLOT(jumpTracks()) );
/*
these are always connected in base class
connect( playPB, TQT_SIGNAL(clicked()), TQT_SLOT(playClicked()) );
connect( nextPB, TQT_SIGNAL(clicked()), TQT_SLOT(nextClicked()) );
connect( prevPB, TQT_SIGNAL(clicked()), TQT_SLOT(prevClicked()) );
connect( stopPB, TQT_SIGNAL(clicked()), TQT_SLOT(stopClicked()) );
connect( ejectPB, TQT_SIGNAL(clicked()), TQT_SLOT(ejectClicked()) );
*/
connect( repeatPB, TQT_SIGNAL(clicked()), TQT_SLOT(loopClicked()) );
connect( songListCB, TQT_SIGNAL(activated(int)), TQT_SLOT(trackSelected(int)));
connect( shufflePB, TQT_SIGNAL(clicked()), TQT_SLOT(randomSelected()));
connect( cddbPB, TQT_SIGNAL(clicked()), TQT_SLOT(CDDialogSelected()));
connect(kapp, TQT_SIGNAL(kdisplayPaletteChanged()), TQT_TQOBJECT(this), TQT_SLOT(setColors()));
connect(kapp, TQT_SIGNAL(iconChanged(int)), TQT_TQOBJECT(this), TQT_SLOT(setIcons()));
TQToolTip::remove(songListCB);
TQToolTip::add(songListCB, i18n("Track list"));
// set up the actions and keyboard accels
m_actions = new KActionCollection(this);
KAction* action;
action = new KAction(i18n("Play/Pause"), Key_P, TQT_TQOBJECT(this), TQT_SLOT(playClicked()), m_actions, "Play/Pause");
action = new KAction(i18n("Stop"), Key_S, TQT_TQOBJECT(this), TQT_SLOT(stopClicked()), m_actions, "Stop");
action = new KAction(i18n("Previous"), Key_B, TQT_TQOBJECT(this), TQT_SLOT(prevClicked()), m_actions, "Previous");
action = new KAction(i18n("Next"), Key_N, TQT_TQOBJECT(this), TQT_SLOT(nextClicked()), m_actions, "Next");
action = KStdAction::quit(TQT_TQOBJECT(this), TQT_SLOT(quitClicked()), m_actions);
action = KStdAction::keyBindings(TQT_TQOBJECT(this), TQT_SLOT(configureKeys()), m_actions, "options_configure_shortcuts");
action = KStdAction::keyBindings(TQT_TQOBJECT(this), TQT_SLOT(configureGlobalKeys()), m_actions, "options_configure_globals");
action = KStdAction::preferences(TQT_TQOBJECT(this), TQT_SLOT(showConfig()), m_actions);
action = new KAction(i18n("Loop"), Key_L, TQT_TQOBJECT(this), TQT_SLOT(loopClicked()), m_actions, "Loop");
action = new KAction(i18n("Eject"), CTRL + Key_E, TQT_TQOBJECT(this), TQT_SLOT(ejectClicked()), m_actions, "Eject");
action = new KAction(i18n("Increase Volume"), Key_Plus, TQT_TQOBJECT(this), TQT_SLOT(incVolume()), m_actions, "IncVolume");
KShortcut increaseVolume = action->shortcut();
increaseVolume.append( KKey( Key_Equal ) );
action->setShortcut( increaseVolume );
action = new KAction(i18n("Decrease Volume"), Key_Minus, TQT_TQOBJECT(this), TQT_SLOT(decVolume()), m_actions, "DecVolume");
action = new KAction(i18n("Options"), CTRL + Key_T, TQT_TQOBJECT(this), TQT_SLOT(showConfig()), m_actions, "Options");
action = new KAction(i18n("Shuffle"), Key_R, TQT_TQOBJECT(this), TQT_SLOT(randomSelected()), m_actions, "Shuffle");
action = new KAction(i18n("CDDB"), CTRL + Key_D, TQT_TQOBJECT(this), TQT_SLOT(CDDialogSelected()), m_actions, "CDDB");
m_actions->readShortcutSettings("Shortcuts");
m_actions->action( "options_configure_globals" )->setText( i18n( "Configure &Global Shortcuts..." ) );
kapp->installKDEPropertyMap();
TQSqlPropertyMap *map = TQSqlPropertyMap::defaultMap();
map->insert("KComboBox", "currentText");
initGlobalShortcuts();
setupPopups();
if (Prefs::looping())
{
loopled->on();
loopled->show();
repeatPB->setOn(true);
}
setDocking(Prefs::docking());
setFocusPolicy(TQ_NoFocus);
songListCB->setSizePolicy(TQSizePolicy::Ignored, TQSizePolicy::Fixed);
adjustSize();
setFixedHeight(this->height());
} // KSCD
KSCD::~KSCD()
{
delete cddb;
delete m_cd;
} // ~KSCD
void KSCD::initGlobalShortcuts() {
m_globalAccel = new TDEGlobalAccel( TQT_TQOBJECT(this) );
//Definition of global shortcuts is based on 'local' shortcuts which follow
//the WIN key.
m_globalAccel->insert("Next", i18n("Next"), 0, KKey("WIN+N"), KKey("WIN+Right"),
TQT_TQOBJECT(this), TQT_SLOT(nextClicked()));
//NOTE: WIN+B collidates with amarok's default global shortcut.
m_globalAccel->insert("Previous", i18n("Previous"), 0, KKey("WIN+B"), KKey("WIN+Left"),
TQT_TQOBJECT(this), TQT_SLOT(prevClicked()));
m_globalAccel->insert("Play/Pause", i18n("Play/Pause"), 0, KKey("WIN+P"), 0,
TQT_TQOBJECT(this), TQT_SLOT(playClicked()));
m_globalAccel->insert("Stop", i18n("Stop"), 0, KKey("WIN+S"), 0,
TQT_TQOBJECT(this), TQT_SLOT(stopClicked()));
m_globalAccel->insert("IncVolume", i18n("Increase Volume"), 0, KKey("WIN+Plus"), KKey("WIN+Up"),
TQT_TQOBJECT(this), TQT_SLOT(incVolume()));
m_globalAccel->insert("DecVolume", i18n("Decrease Volume"), 0, KKey("WIN+Minus"), KKey("WIN+Down"),
TQT_TQOBJECT(this), TQT_SLOT(decVolume()));
m_globalAccel->insert("Shuffle", i18n("Shuffle"), 0, KKey("WIN+R"), 0,
TQT_TQOBJECT(this), TQT_SLOT(incVolume()));
m_globalAccel->setConfigGroup( "GlobalShortcuts" );
m_globalAccel->readSettings( kapp->config() );
m_globalAccel->updateConnections();
}
bool KSCD::digitalPlayback() {
#if defined(BUILD_CDDA)
return !(Prefs::audioSystem().isEmpty());
#else
return false;
#endif
}
void KSCD::setVolume(int v)
{
volChanged(v);
volumeSlider->setValue(v);
}
void KSCD::setDevice(const TQString& dev)
{
Prefs::self()->setCdDevice(dev);
setDevicePaths();
}
/**
* Initialize smallfont which fits into the 13 and 14 pixel widgets.
*/
void KSCD::initFont()
{
/* int theSmallPtSize = 10;
// Find a font that fits the 13 and 14 pixel widgets
TQFont fn( TDEGlobalSettings::generalFont().family(), theSmallPtSize, TQFont::Bold );
bool fits = false;
while (!fits && theSmallPtSize > 1)
{
TQFontMetrics metrics(fn);
if(metrics.height() > 13)
{
--theSmallPtSize;
fn.setPointSize(theSmallPtSize);
} else {
fits = true;
}
}
smallfont = TQFont(TDEGlobalSettings::generalFont().family(), theSmallPtSize, TQFont::Bold);
*/
} // initFont()
/**
* drawPanel() constructs KSCD's little black LED area
* all settings are made via panel.ui
*/
void KSCD::drawPanel()
{
setIcons();
adjustSize();
const int D = 6;
for (int u = 0; u < 5; u++) {
trackTimeLED[u] = new BW_LED_Number(frameleds);
trackTimeLED[u]->setLEDoffColor(Prefs::backColor());
trackTimeLED[u]->setLEDColor(Prefs::ledColor(), Prefs::backColor());
trackTimeLED[u]->setGeometry(2 + u * 18, D, 23, 30);
connect(trackTimeLED[u], TQT_SIGNAL(clicked()), TQT_TQOBJECT(this), TQT_SLOT(cycleplaytimemode()));
}
setLEDs(-1);
queryled = new LedLamp(symbols);
queryled->move(+10, D + 1);
queryled->off();
queryled->hide();
loopled = new LedLamp(symbols, LedLamp::Loop);
loopled->move(+10, D + 18);
loopled->off();
totaltimelabel->hide();
} // drawPanel
void KSCD::setIcons()
{
playPB->setIconSet(SmallIconSet("player_play"));
stopPB->setIconSet(SmallIconSet("player_stop"));
ejectPB->setIconSet(SmallIconSet("player_eject"));
prevPB->setIconSet(SmallIconSet("player_start"));
nextPB->setIconSet(SmallIconSet("player_end"));
cddbPB->setIconSet(SmallIconSet("view_text"));
infoPB->setIconSet(SmallIconSet("run"));
}
void KSCD::setupPopups()
{
TQPopupMenu* mainPopup = new TQPopupMenu(this);
infoPB->setPopup(mainPopup);
infoPopup = new TQPopupMenu (this);
infoPopup->insertItem("MusicMoz", 0);
infoPopup->insertItem("Ultimate Bandlist", 1);
infoPopup->insertItem("CD Universe", 2);
infoPopup->insertSeparator();
infoPopup->insertItem("AlltheWeb", 3);
infoPopup->insertItem("Altavista", 4);
infoPopup->insertItem("Excite", 5);
infoPopup->insertItem("Google", 6);
infoPopup->insertItem("Google Groups", 7);
infoPopup->insertItem("HotBot", 8);
infoPopup->insertItem("Lycos", 9);
infoPopup->insertItem("Open Directory", 10);
infoPopup->insertItem("Yahoo!", 11);
m_actions->action(KStdAction::name(KStdAction::Preferences))->plug(mainPopup);
//NEW add the shortcut dialogs
m_actions->action("options_configure_globals")->plug(mainPopup);
m_actions->action("options_configure_shortcuts")->plug(mainPopup);
mainPopup->insertSeparator();
mainPopup->insertItem(i18n("Artist Information"), infoPopup);
connect( infoPopup, TQT_SIGNAL(activated(int)), TQT_SLOT(information(int)) );
KHelpMenu* helpMenu = new KHelpMenu(this, TDEGlobal::instance()->aboutData(), false);
mainPopup->insertItem(SmallIcon("help"),i18n("&Help"), helpMenu->menu());
mainPopup->insertSeparator();
m_actions->action(KStdAction::name(KStdAction::Quit))->plug(mainPopup);
} // setupPopups
void KSCD::playClicked()
{
if (m_cd->discId() == KCompactDisc::missingDisc)
return;
kapp->processEvents();
kapp->flushX();
if (!m_cd->isPlaying())
{
kapp->processEvents();
kapp->flushX();
if (m_cd->isPaused())
{
// Unpause (!!).
m_cd->pause();
}
else
{
setLEDs(0);
resetTimeSlider(true);
if(Prefs::randomPlay())
{
make_random_list();
// next clicked handles updating the play button, etc.
nextClicked();
}
else
{
m_cd->play(0, 0, playlist.isEmpty() ? 0 : 1);
}
}
// Update UI to allow a subsequent pause.
statuslabel->setText(i18n("Play"));
playPB->setIconSet(SmallIconSet("player_pause"));
playPB->setText(i18n("Pause"));
}
else
{
m_cd->pause();
// Update UI to allow a subsequent play.
statuslabel->setText(i18n("Pause"));
playPB->setIconSet(SmallIconSet("player_play"));
playPB->setText(i18n("Play"));
}
kapp->processEvents();
kapp->flushX();
} // playClicked()
void KSCD::setShuffle(int shuffle)
{
if (shuffle == 2) {
if(Prefs::randomPlay() && m_cd->tracks() > 0) {
shufflePB->blockSignals(true);
shufflePB->setOn(true);
shufflePB->blockSignals(false);
make_random_list(); /* koz: Build a unique, once, random list */
if(m_cd->isPlaying())
nextClicked();
}
return;
}
Prefs::setRandomPlay(shuffle);
shufflePB->blockSignals(true);
shufflePB->setOn(shuffle);
shufflePB->blockSignals(false);
if (Prefs::randomPlay() && m_cd->tracks() > 0) {
make_random_list(); /* koz: Build a unique, once, random list */
if(m_cd->isPlaying())
nextClicked();
}
}
void KSCD::stopClicked()
{
stoppedByUser = true;
kapp->processEvents();
kapp->flushX();
m_cd->stop();
} // stopClicked()
void KSCD::prevClicked()
{
int track = m_cd->track();
if (Prefs::randomPlay()) {
track = prev_randomtrack();
if (track == -1) {
return;
}
} else {
if (track <= 1) {
if (Prefs::looping()) {
track = m_cd->tracks();
} else {
return;
}
} else {
track--;
}
}
kapp->processEvents();
kapp->flushX();
m_cd->play(track, 0, playlist.isEmpty() ? 0 : track);
} // prevClicked()
bool KSCD::nextClicked()
{
unsigned track = m_cd->track();
if (Prefs::randomPlay()) {
track = next_randomtrack();
if(track == 0) {
return false;
}
} else {
if(track < 1) {
track = 1;
} else if (track >= m_cd->tracks()) {
if (Prefs::looping()) {
track = 1;
} else {
return true;
}
} else {
track++;
}
}
kapp->processEvents();
kapp->flushX();
m_cd->play(track, 0, Prefs::randomPlay() || !playlist.isEmpty() ? track + 1 : 0);
return true;
} // nextClicked()
void KSCD::trackChanged(unsigned track, unsigned trackLength)
{
TQString tooltip = artistlabel->text();
if (track < 1)
{
setLEDs(-1);
resetTimeSlider(true);
tracklabel->setText("--/--");
titlelabel->clear();
}
else
{
// if (!nextClicked())
// {
// statuslabel->setText(i18n("Disc Finished"));
// m_cd->stop();
// }
// break;
if (songListCB->count())
{
songListCB->setCurrentItem(track - 1);
// drop the number.
// for Mahlah, a picky though otherwise wonderful person - AJS
TQString justTheName = songListCB->currentText();
justTheName = justTheName.right(justTheName.length() - 4);
TQToolTip::remove(songListCB);
TQToolTip::add(songListCB, i18n("Current track: %1").arg(justTheName));
}
timeSlider->blockSignals(true);
timeSlider->setRange(0, trackLength ? trackLength - 1 : 0);
timeSlider->blockSignals(false);
TQString str;
str.sprintf("%02d/%02d", track, m_cd->tracks());
tracklabel->setText(str);
TQString title = cddbInfo.trackInfoList[track-1].title;
titlelabel->setText(title);
tooltip += "/";
tooltip += KStringHandler::rsqueeze(title, 30);
}
emit trackChanged(tooltip);
} //trackChanged(int track)
void KSCD::jumpToTime(int ms, bool forcePlay)
{
kapp->processEvents();
kapp->flushX();
int track = m_cd->track();
if ((m_cd->isPlaying() || forcePlay) &&
ms < (int)m_cd->trackLength())
{
if(Prefs::randomPlay() || !playlist.isEmpty())
{
m_cd->play(track, ms, track + 1);
}
else
{
m_cd->play(track, ms);
}
}
} // jumpToTime(int ms)
void KSCD::timeSliderPressed()
{
updateTime = false;
} // timeSliderPressed()
void KSCD::timeSliderMoved(int milliseconds)
{
setLEDs(milliseconds);
} // timeSliderMoved(int seconds)
void KSCD::timeSliderReleased()
{
updateTime = true;
} // timeSliderReleased()
void KSCD::quitClicked()
{
// ensure nothing else starts happening
queryledtimer.stop();
titlelabeltimer.stop();
cycletimer.stop();
jumpTrackTimer.stop();
writeSettings();
//setShuffle(0);
statuslabel->clear();
setLEDs(-1);
// Good GOD this is evil
kapp->processEvents();
kapp->flushX();
if(Prefs::stopExit())
m_cd->stop();
delete m_cd;
kapp->quit();
} // quitClicked()
bool KSCD::event( TQEvent *e )
{
return TQWidget::event(e);
} // event
void KSCD::loopOn()
{
Prefs::setLooping(true);
loopled->on();
loopled->show();
kapp->processEvents();
kapp->flushX();
} // loopOn;
void KSCD::loopOff()
{
Prefs::setLooping(false);
loopled->off();
loopled->show();
kapp->processEvents();
kapp->flushX();
} // loopOff;
void KSCD::loopClicked()
{
if(Prefs::looping())
{
loopOff();
}
else
{
loopOn();
}
} // loopClicked
/**
* Do everything needed if the user requested to eject the disc.
*
*/
void KSCD::ejectClicked()
{
m_cd->eject();
} // ejectClicked
void KSCD::closeEvent(TQCloseEvent *e)
{
if (Prefs::docking() && !kapp->sessionSaving())
{
hide();
e->ignore();
return;
}
e->accept();
}
void KSCD::randomSelected()
{
setShuffle(Prefs::randomPlay()?0:1);
/* FIXME this helps us to display "Random" in Status line
should it maybe to be replaced with symbol "RAND" or something others */
statuslabel->setText(Prefs::randomPlay()?i18n("Random"):i18n("Play"));
} // randomSelected
/**
* A Track was selected for playback from the drop down box.
*
*/
void KSCD::trackSelected( int cb_index )
{
if (cb_index < 0)
return;
unsigned int track = cb_index + 1;
setShuffle(0);
m_cd->play(track, 0);
} // trackSelected
void KSCD::updateConfigDialog(configWidget* widget)
{
if(!widget)
return;
static TQString originalTitleOfGroupBox = widget->groupBox3->title();
if(m_cd->isPlaying()) {
widget->groupBox3->setEnabled(false);
widget->groupBox3->setTitle( i18n( "CD Drive (you must stop playing to change this)" ) );
} else {
widget->groupBox3->setEnabled(true);
widget->groupBox3->setTitle(originalTitleOfGroupBox);
}
}
void KSCD::showConfig()
{
static configWidget* confWidget = 0;
if (TDEConfigDialog::showDialog("settings")) {
updateConfigDialog(confWidget);
return;
}
configDialog = new TDEConfigDialog(this, "settings", Prefs::self());
configDialog->setHelp(TQString());
confWidget = new configWidget(this, 0, "Kscd");
// kscd config page
configDialog->addPage(confWidget, i18n("CD Player"), "kscd", i18n("Settings & Behavior"));
// libkcddb page
KService::Ptr libkcddb = KService::serviceByDesktopName("libkcddb");
if (libkcddb && libkcddb->isValid())
{
TDECModuleInfo info(libkcddb->desktopEntryPath());
if (info.service()->isValid())
{
TDECModule *m = TDECModuleLoader::loadModule(info, TDECModuleLoader::Inline);
if (m)
{
m->load();
KCDDB::Config* cfg = new KCDDB::Config();
cfg->readConfig();
configDialog -> addPage(m, cfg, TQString("CDDB"), "cdtrack", i18n("Configure Fetching Items"));
connect(configDialog, TQT_SIGNAL(okClicked()), m, TQT_SLOT(save()));
connect(configDialog, TQT_SIGNAL(applyClicked()), m, TQT_SLOT(save()));
connect(configDialog, TQT_SIGNAL(defaultClicked()), m, TQT_SLOT(defaults()));
}
}
}
updateConfigDialog(confWidget);
connect(configDialog, TQT_SIGNAL(settingsChanged()), TQT_TQOBJECT(this), TQT_SLOT(configDone()));
configDialog -> show();
} // showConfig()
void KSCD::configDone()
{
setColors();
setDocking(Prefs::docking());
setDevicePaths();
volumeIcon->setEnabled(!Prefs::digitalPlayback());
volumeSlider->setEnabled(!Prefs::digitalPlayback());
// dialog deletes itself
configDialog = 0L;
}
void KSCD::configureKeys()
{
KKeyDialog::configure(m_actions, this);
}
void KSCD::configureGlobalKeys()
{
KKeyDialog::configure(m_globalAccel, true, this, true);
}
void KSCD::setDevicePaths()
{
if (!m_cd->setDevice(Prefs::cdDevice(), Prefs::volume(), Prefs::digitalPlayback(),
Prefs::audioSystem(), Prefs::audioDevice()))
{
// This device did not seem usable.
TQString str = i18n("CD-ROM read or access error (or no audio disc in drive).\n"\
"Please make sure you have access permissions to:\n%1").arg(
KCompactDisc::urlToDevice(Prefs::cdDevice()));
KMessageBox::error(this, str, i18n("Error"));
}
} // setDevicePath()
void KSCD::setDocking(bool dock)
{
Prefs::setDocking(dock);
if (Prefs::docking())
{
if (!m_dockWidget)
{
m_dockWidget = new DockWidget(this, "dockw");
connect(m_dockWidget, TQT_SIGNAL(quitSelected()), TQT_TQOBJECT(this), TQT_SLOT(quitClicked()));
}
m_dockWidget->show();
connect(this, TQT_SIGNAL(trackChanged(const TQString&)),
m_dockWidget, TQT_SLOT(setToolTip(const TQString&)));
connect(this, TQT_SIGNAL(trackChanged(const TQString&)),
m_dockWidget, TQT_SLOT(createPopup(const TQString&)));
}
else
{
show();
delete m_dockWidget;
m_dockWidget = 0;
}
}
void KSCD::incVolume()
{
int v = Prefs::volume() + 5;
if (v > 100)
{
v = 100;
}
volChanged(v);
volumeSlider->setValue(v);
} // incVolume
void KSCD::decVolume()
{
int v = Prefs::volume() - 5;
if (v < 0)
{
v = 0;
}
volChanged(v);
volumeSlider->setValue(v);
} // decVolume
void KSCD::volChanged( int vol )
{
TQString str;
str = TQString::fromUtf8( TQCString().sprintf(i18n("Vol: %02d%%").utf8(), vol) );
volumelabel->setText(str);
m_cd->setVolume(vol);
Prefs::setVolume(vol);
} // volChanged
void KSCD::make_random_list()
{
/* koz: 15/01/00. I want a random list that does not repeat tracks. Ie, */
/* a list is created in which each track is listed only once. The tracks */
/* are picked off one by one until the end of the list */
int selected = 0;
bool rejected = false;
//kdDebug(67000) << "Playlist has " << size << " entries\n" << endl;
random_list.clear();
for(unsigned i = 0; i < m_cd->tracks(); i++)
{
do {
selected = 1 + (int) randSequence.getLong(m_cd->tracks());
rejected = (random_list.find(selected) != random_list.end());
} while(rejected == true);
random_list.append(selected);
}
/*
printf("debug: dump random list\n");
RandomList::iterator it;
for(it = random_list.begin(); it != random_list.end(); it++) {
printf("%i ", *it);
}
printf("\n");
*/
random_current = random_list.end();
} // make_random_list()
int KSCD::next_randomtrack()
{
/* Check to see if we are at invalid state */
if(random_current == random_list.end())
{
random_current = random_list.begin();
}
else if(random_current == random_list.fromLast())
{
if(!Prefs::looping())
{
m_cd->stop();
return 0;
}
else
{
// playing the same random list isn't very random, is it?
make_random_list();
return next_randomtrack();
}
}
else
{
++random_current;
}
return *random_current;
} // next_randomtrack
int KSCD::prev_randomtrack()
{
/* Check to see if we are at invalid state */
if(random_current == random_list.end())
{
random_current = random_list.fromLast();
}
else if(random_current == random_list.begin())
{
if(!Prefs::looping())
{
return -1;
}
else
{
// playing the same random list isn't very random, is it?
make_random_list();
return prev_randomtrack();
}
}
else
{
--random_current;
}
return *random_current;
} // prev_randomtrack
void KSCD::discChanged(unsigned discId)
{
cddbInfo.clear();
if (discId == KCompactDisc::missingDisc)
{
statuslabel->setText(i18n("No disc"));
}
else
{
cddbInfo.id = TQString::number(discId, 16).rightJustify(8,'0');
cddbInfo.length = m_cd->discLength() / 1000;
cddbInfo.artist = m_cd->discArtist();
cddbInfo.title = m_cd->discTitle();
// If it's a sampler, we'll do artist/title.
bool isSampler = (cddbInfo.title.compare("Various") == 0);
KCDDB::TrackInfo track;
for (unsigned i = 1; i <= m_cd->tracks(); i++)
{
if (isSampler)
{
track.title = m_cd->trackArtist(i);
track.title.append("/");
track.title.append(m_cd->trackTitle(i));
}
else
{
track.title = m_cd->trackTitle(i);
}
// FIXME: KDE4
// track.length = cd->trk[i - 1].length;
cddbInfo.trackInfoList.append(track);
}
}
// Set the total time.
TQTime dml;
dml = dml.addSecs(m_cd->discLength() / 1000);
TQString fmt;
if(dml.hour() > 0)
fmt.sprintf("%02d:%02d:%02d",dml.hour(),dml.minute(),dml.second());
else
fmt.sprintf("%02d:%02d",dml.minute(),dml.second());
totaltimelabel->setText(fmt);
trackChanged(0, 0);
populateSongList("");
//totaltimelabel->clear();
totaltimelabel->lower();
if ((Prefs::autoplay() || TDECmdLineArgs::parsedArgs()->isSet("start"))
&& !m_cd->isPlaying())
{
playClicked();
}
// We just populated the GUI with what we got from the CD. Now look for
// more from the Internet...
lookupCDDB();
}
void KSCD::discStopped()
{
if (Prefs::ejectOnFinish() && !stoppedByUser && (m_cd->discId() != KCompactDisc::missingDisc))
{
ejectClicked();
}
if (!stoppedByUser)
{
if (Prefs::randomPlay())
{
// If nextClicked returns false, it was the last
// random track, and the player should be stopped
if (nextClicked())
return;
}
else if (Prefs::looping())
{
playClicked();
return;
}
}
statuslabel->setText(i18n("Stopped"));
playPB->setText(i18n("Play"));
playPB->setIconSet(SmallIconSet("player_play"));
/* reset to initial value, only stopclicked() sets this to true */
stoppedByUser = false;
trackChanged(0, 0);
populateSongList("");
totaltimelabel->clear();
totaltimelabel->lower();
}
void KSCD::setLEDs(int milliseconds)
{
TQString symbols;
if (milliseconds < 0)
{
symbols = "--:--";
}
else
{
unsigned mymin;
unsigned mysec;
mymin = milliseconds / 60000;
mysec = (milliseconds % 60000) / 1000;
symbols.sprintf("%02d:%02d", mymin, mysec);
}
for (int i = 0; i < 5; i++)
{
trackTimeLED[i]->display((char)symbols.local8Bit().at(i));
}
}
void KSCD::resetTimeSlider(bool enabled)
{
timeSlider->setEnabled(enabled);
timeSlider->blockSignals(true);
timeSlider->setValue(0);
timeSlider->blockSignals(false);
} // resetTimeSlider(bool enabled);
void KSCD::setColors()
{
TQColor led_color = Prefs::ledColor();
TQColor background_color = Prefs::backColor();
backdrop->setBackgroundColor(background_color);
TQColorGroup colgrp( led_color, background_color, led_color,led_color , led_color,
led_color, white );
titlelabel ->setPalette( TQPalette(colgrp,colgrp,colgrp) );
artistlabel->setPalette( TQPalette(colgrp,colgrp,colgrp) );
volumelabel->setPalette( TQPalette(colgrp,colgrp,colgrp) );
statuslabel->setPalette( TQPalette(colgrp,colgrp,colgrp) );
tracklabel ->setPalette( TQPalette(colgrp,colgrp,colgrp) );
totaltimelabel->setPalette( TQPalette(colgrp,colgrp,colgrp) );
queryled->setPalette( TQPalette(colgrp,colgrp,colgrp) );
loopled->setPalette( TQPalette(colgrp,colgrp,colgrp) );
for (int u = 0; u< 5;u++){
trackTimeLED[u]->setLEDoffColor(background_color);
trackTimeLED[u]->setLEDColor(led_color,background_color);
}
titlelabel ->setFont( Prefs::ledFont() );
artistlabel->setFont( Prefs::ledFont() );
volumelabel->setFont( Prefs::ledFont() );
statuslabel->setFont( Prefs::ledFont() );
tracklabel ->setFont( Prefs::ledFont() );
totaltimelabel->setFont( Prefs::ledFont() );
}
void KSCD::readSettings()
{
/*
time_display_mode = config->readNumEntry("TimeDisplay", TRACK_SEC);
*/
#ifndef DEFAULT_CD_DEVICE
#define DEFAULT_CD_DEVICE "/dev/cdrom"
// sun ultrix etc have a canonical cd rom device specified in the
// respective plat_xxx.c file. On those platforms you need nnot
// specify the cd rom device and DEFAULT_CD_DEVICE is not defined
// in config.h
#endif
if (Prefs::cdDevice().isEmpty())
{
Prefs::setCdDevice(DEFAULT_CD_DEVICE);
}
volumeIcon->setEnabled(!Prefs::digitalPlayback());
volumeSlider->setEnabled(!Prefs::digitalPlayback());
}
/**
* Write KSCD's Configuration into the kderc file.
*
*/
void KSCD::writeSettings()
{
Prefs::writeConfig();
} // writeSettings()
void KSCD::CDDialogSelected()
{
if (!cddialog)
{
cddialog = new CDDBDlg(this);
cddialog->setData(cddbInfo, m_cd->discSignature(), playlist);
connect(cddialog,TQT_SIGNAL(cddbQuery()),TQT_SLOT(lookupCDDB()));
connect(cddialog,TQT_SIGNAL(newCDInfoStored(KCDDB::CDInfo)),
TQT_SLOT(setCDInfo(KCDDB::CDInfo)));
connect(cddialog,TQT_SIGNAL(finished()),TQT_SLOT(CDDialogDone()));
connect(cddialog,TQT_SIGNAL(play(int)),TQT_SLOT(trackSelected(int)));
}
cddialog->show();
cddialog->raise();
}
void KSCD::CDDialogDone()
{
cddialog->delayedDestruct();
cddialog = 0L;
}
void KSCD::lookupCDDB()
{
if (m_cd->discId() == KCompactDisc::missingDisc)
return;
kdDebug(67000) << "lookupCDDB() called" << endl;
populateSongList(i18n("Start freedb lookup."));
setShuffle(2);
led_on();
cddb->config().reparse();
cddb->setBlockingMode(false);
cddb->lookup(m_cd->discSignature());
} // lookupCDDB
void KSCD::lookupCDDBDone(CDDB::Result result)
{
led_off();
if ((result != KCDDB::CDDB::Success) &&
(result != KCDDB::CDDB::MultipleRecordFound))
{
populateSongList(result == CDDB::NoRecordFound ? i18n("No matching freedb entry found.") : i18n("Error getting freedb entry."));
return;
}
// The intent of the original code here seems to have been to perform the
// lookup, and then to convert all the string data within the CDDB response
// using the use Prefs::selectedEncoding() and a TQTextCodec. However, that
// seems to be irrelevant these days.
KCDDB::CDInfo info = cddb->bestLookupResponse();
// TODO Why doesn't libcddb not return MultipleRecordFound?
//if( result == KCDDB::CDDB::MultipleRecordFound ) {
if( cddb->lookupResponse().count() > 1 ) {
CDInfoList cddb_info = cddb->lookupResponse();
CDInfoList::iterator it;
TQStringList list;
for ( it = cddb_info.begin(); it != cddb_info.end(); ++it ) {
list.append( TQString("%1, %2, %3").arg((*it).artist).arg((*it).title)
.arg((*it).genre));
}
bool ok(false);
TQString res = KInputDialog::getItem(
i18n("Select CDDB Entry"),
i18n("Select a CDDB entry:"), list, 0, false, &ok,
this );
if ( ok ) {
// The user selected and item and pressed OK
uint c = 0;
for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
if( *it == res) break;
c++;
}
if( c < cddb_info.size() )
info = cddb_info[c];
} else {
return;
// user pressed Cancel
}
}
setCDInfo(info);
// In case the cddb dialog is open, update it
if (cddialog)
cddialog->setData(cddbInfo, m_cd->discSignature(), playlist);
} // lookupCDDBDone
void KSCD::setCDInfo(KCDDB::CDInfo info)
{
// Some sanity provisions to ensure that the number of records matches what
// the CD actually contains.
while (info.trackInfoList.count() < cddbInfo.trackInfoList.count())
{
info.trackInfoList.append(KCDDB::TrackInfo());
}
while (info.trackInfoList.count() > cddbInfo.trackInfoList.count())
{
info.trackInfoList.pop_back();
}
cddbInfo = info;
populateSongList("");
}
void KSCD::led_off()
{
queryledtimer.stop();
queryled->off();
queryled->hide();
totaltimelabel->raise();
totaltimelabel->show();
} // led_off
void KSCD::led_on()
{
totaltimelabel->hide();
totaltimelabel->lower();
queryledtimer.start(800);
queryled->off();
queryled->show();
kapp->processEvents();
kapp->flushX();
} // led_on
void KSCD::togglequeryled()
{
queryled->show();
queryled->toggle();
} // togglequeryled
void KSCD::titlelabeltimeout()
{
// clear the cddb error message on the title label.
titlelabeltimer.stop();
titlelabel->clear();
} // titlelabeltimeout
void KSCD::trayOpening()
{
statuslabel->setText(i18n("Ejected"));
trackChanged(0, 0);
}
int KSCD::currentTrack()
{
return m_cd->track();
}
int KSCD::currentTrackLength()
{
return m_cd->trackLength();
}
int KSCD::currentPosition()
{
return m_cd->trackPosition();
}
int KSCD::getStatus()
{
if (m_cd->isPlaying())
return 2;
else if (m_cd->isPaused())
return 4;
else if (m_cd->discId() != KCompactDisc::missingDisc)
return 5;
else
return 6;
}
bool KSCD::playing()
{
return m_cd->isPlaying();
}
void KSCD::trackUpdate(unsigned /*track*/, unsigned trackPosition)
{
unsigned tmp;
switch (Prefs::timeDisplayMode())
{
case TRACK_REM:
tmp = m_cd->trackLength() - trackPosition;
break;
case TOTAL_SEC:
tmp = m_cd->discPosition();
break;
case TOTAL_REM:
tmp = m_cd->discLength() - m_cd->discPosition();
break;
case TRACK_SEC:
default:
tmp = trackPosition;
break;
}
if (updateTime)
{
setLEDs(tmp);
timeSlider->blockSignals(true);
timeSlider->setValue(trackPosition);
timeSlider->blockSignals(false);
}
}
void KSCD::cycleplaytimemode()
{
cycletimer.stop();
if (Prefs::timeDisplayMode() > 2) {
Prefs::setTimeDisplayMode(TRACK_SEC);
} else {
Prefs::setTimeDisplayMode(Prefs::timeDisplayMode() + 1);
}
switch(Prefs::timeDisplayMode()) {
case TRACK_REM:
volumelabel->setText(i18n("Tra Rem"));
break;
case TOTAL_SEC:
volumelabel->setText(i18n("Tot Sec"));
break;
case TOTAL_REM:
volumelabel->setText(i18n("Tot Rem"));
break;
case TRACK_SEC:
default:
volumelabel->setText(i18n("Tra Sec"));
break;
}
cycletimer.start(3000, true);
} // cycleplaymode
void KSCD::cycletimeout()
{
cycletimer.stop();
TQString str;
str = TQString::fromUtf8( TQCString().sprintf(i18n("Vol: %02d%%").utf8(), Prefs::volume()) );
volumelabel->setText(str);
} // cycletimeout
void KSCD::information(int i)
{
//kdDebug(67000) << "Information " << i << "\n" << endl;
if(cddbInfo.artist.isEmpty())
return;
TQString encodedArtist = KURL::encode_string_no_slash(cddbInfo.artist);
TQString str;
switch(i)
{
case 0:
str = TQString("http://musicmoz.org/cgi-bin/ext.cgi?artist=%1")
.arg(encodedArtist);
break;
case 1:
str = TQString("http://ubl.artistdirect.com/cgi-bin/gx.cgi/AppLogic+Search?select=MusicArtist&searchstr=%1&searchtype=NormalSearch")
.arg(encodedArtist);
break;
case 2:
str = TQString("http://www.cduniverse.com/cgi-bin/cdubin.exe/rlinka/ean=%1")
.arg(encodedArtist);
break;
case 3:
str = TQString("http://www.alltheweb.com/search?cat=web&q=%1")
.arg(encodedArtist);
break;
case 4:
str = TQString("http://altavista.com/web/results?q=%1&kgs=0&kls=1&avkw=xytx")
.arg(encodedArtist);
break;
case 5:
str = TQString("http://msxml.excite.com/_1_2UDOUB70SVHVHR__info.xcite/dog/results?otmpl=dog/webresults.htm&qkw=%1&qcat=web&qk=20&top=1&start=&ver=14060")
.arg(encodedArtist);
break;
case 6:
str = TQString("http://www.google.com/search?q=%1")
.arg(encodedArtist);
break;
case 7:
str = TQString("http://groups.google.com/groups?oi=djq&as_q=%1&num=20")
.arg(encodedArtist);
break;
case 8:
str = TQString("http://www.hotbot.com/default.asp?prov=Inktomi&query=%1&ps=&loc=searchbox&tab=web")
.arg(encodedArtist);
break;
case 9:
str = TQString("http://search.lycos.com/default.asp?lpv=1&loc=searchhp&tab=web&query=%1")
.arg(encodedArtist);
break;
case 10:
str = TQString("http://search.dmoz.org/cgi-bin/search?search=%1")
.arg(encodedArtist);
break;
case 11:
str = TQString("http://search.yahoo.com/bin/search?p=%1")
.arg(encodedArtist);
break;
default:
return;
break;
} // switch()
KRun::runURL(KURL( str ), "text/html");
} // information
/**
* Save state on session termination
*/
bool KSCD::saveState(TQSessionManager& /*sm*/)
{
writeSettings();
TDEConfig* config = TDEApplication::kApplication()->sessionConfig();
config->setGroup("General");
config->writeEntry("Show", isVisible());
return true;
} // saveState
/**
* Allow the user to type in the number of the track
*/
void KSCD::keyPressEvent(TQKeyEvent* e)
{
bool isNum;
int value = e->text().toInt(&isNum);
if (e->key() == TQt::Key_F1)
{
kapp->invokeHelp();
}
else if (isNum)
{
value = (jumpToTrack * 10) + value;
if (value <= (int)cddbInfo.trackInfoList.count())
{
jumpToTrack = value;
jumpTrackTimer.stop();
jumpTrackTimer.start(333);
}
}
else
{
TQWidget::keyPressEvent(e);
}
} //keyPressEvent
void KSCD::jumpTracks()
{
if (jumpToTrack > 0 && jumpToTrack <= (int)cddbInfo.trackInfoList.count())
{
m_cd->play(jumpToTrack, 0, jumpToTrack + 1);
}
jumpToTrack = 0;
} // jumpTracks
TQString KSCD::currentTrackTitle()
{
int track = m_cd->track();
return (track > -1) ? cddbInfo.trackInfoList[track-1].title : TQString();
}
TQString KSCD::currentAlbum()
{
return cddbInfo.title;
}
TQString KSCD::currentArtist()
{
return cddbInfo.artist;
}
TQStringList KSCD::trackList()
{
TQStringList tracks;
for (TrackInfoList::const_iterator it = cddbInfo.trackInfoList.begin();
it != cddbInfo.trackInfoList.end(); ++it)
tracks << (*it).title;
return tracks;
}
void KSCD::populateSongList(TQString infoStatus)
{
// set the artist and title labels as well as the dock tooltip.
if (!infoStatus.isEmpty())
artistlabel->setText(infoStatus);
else
artistlabel->setText(TQString("%1 - %2").arg(cddbInfo.artist, cddbInfo.title));
songListCB->clear();
for (unsigned i = 0; i < cddbInfo.trackInfoList.count(); i++)
{
unsigned tmp = m_cd->trackLength(i + 1);
unsigned mymin;
unsigned mysec;
mymin = tmp / 60000;
mysec = (tmp % 60000) / 1000;
TQString str1;
str1.sprintf("%02d: ", i + 1);
TQString str2;
str2.sprintf(" (%02d:%02d) ", mymin, mysec);
str1.append(cddbInfo.trackInfoList[i].title);
str1.append(str2);
songListCB->insertItem(str1);
}
emit trackChanged(m_cd->track(), m_cd->trackLength());
}
static const KCmdLineOptions options[] =
{
{"s",0,0},
{"start",I18N_NOOP("Start playing"),0},
{"+[device]",I18N_NOOP("CD device, can be a path or a media:/ URL"),0},
KCmdLineLastOption
};
/**
* main()
*/
int main( int argc, char *argv[] )
{
TDEAboutData aboutData("kscd", I18N_NOOP("KsCD"),
KSCDVERSION, description,
TDEAboutData::License_GPL,
"(c) 2001, Dirk Försterling\n(c) 2003, Aaron J. Seigo");
aboutData.addAuthor("Aaron J. Seigo", I18N_NOOP("Current maintainer"), "aseigo@kde.org");
aboutData.addAuthor("Alexander Kern",I18N_NOOP("Workman library update, CDTEXT, CDDA"), "kernalex@kde.org");
aboutData.addAuthor("Bernd Johannes Wuebben",0, "wuebben@kde.org");
aboutData.addAuthor("Dirk Försterling", I18N_NOOP("Workman library, previous maintainer"), "milliByte@gmx.net");
aboutData.addCredit("Wilfried Huss", I18N_NOOP("Patches galore"));
aboutData.addCredit("Steven Grimm", I18N_NOOP("Workman library"));
aboutData.addCredit("Sven Lueppken", I18N_NOOP("UI Work"));
aboutData.addCredit("freedb.org", I18N_NOOP("Special thanks to freedb.org for providing a free CDDB-like CD database"), 0, "http://freedb.org");
TDECmdLineArgs::init( argc, argv, &aboutData );
TDECmdLineArgs::addCmdLineOptions(options);
KUniqueApplication::addCmdLineOptions();
TDECmdLineArgs* args = TDECmdLineArgs::parsedArgs();
if (!KUniqueApplication::start())
{
fprintf(stderr, "kscd is already running\n");
if (args->count()>0 || args->isSet("start"))
{
DCOPClient client;
if (client.attach())
{
// Forward the command line args to the running instance.
DCOPRef ref("kscd", "CDPlayer");
if (args->count() > 0)
{
ref.send("setDevice(TQString)", TQString(args->arg(0)));
}
if (args->isSet("start"))
{
ref.send("play()");
}
}
}
exit(0);
}
KUniqueApplication a;
kapp->dcopClient()->setDefaultObject("CDPlayer");
KSCD *k = new KSCD();
a.setTopWidget( k );
a.setMainWidget( k );
k->setCaption(a.caption());
if (kapp->isRestored())
{
TDEConfig* config = TDEApplication::kApplication()->sessionConfig();
config->setGroup("General");
if (config->readBoolEntry("Show"))
k->show();
}
else
{
k->show();
}
if (args->count()>0) Prefs::self()->setCdDevice(args->arg(0));
return a.exec();
}
#include "kscd.moc"