/* * 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 * Copyright (c) 2004 Alexander Kern * Copyright (c) 2003-2006 Richard Lärkäng * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "docking.h" #include "kscd.h" #include "version.h" #include "prefs.h" #include #include #include #include #include "cddbdlg.h" #include "configWidget.h" #include #include #include 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 TDECompactDisc(); cddbInfo.clear(); // The first freedb revision is "0" //!!!! random_current = random_list.begin(); cddb = new KCDDB::Client(); connect(cddb, TQ_SIGNAL(finished(CDDB::Result)), this, TQ_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, TQ_SIGNAL(sliderPressed()), TQ_SLOT(timeSliderPressed())); connect(timeSlider, TQ_SIGNAL(sliderReleased()), TQ_SLOT(timeSliderReleased())); connect(timeSlider, TQ_SIGNAL(sliderMoved(int)), TQ_SLOT(timeSliderMoved(int))); connect(timeSlider, TQ_SIGNAL(valueChanged(int)), TQ_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, TQ_SIGNAL(valueChanged(int)), TQ_SLOT(volChanged(int))); /* FIXME check for return value */ setDevicePaths(/*Prefs::cdDevice(), Prefs::audioSystem(), Prefs::audioDevice()*/); connect(m_cd, TQ_SIGNAL(trackPlaying(unsigned, unsigned)), this, TQ_SLOT(trackUpdate(unsigned, unsigned))); connect(m_cd, TQ_SIGNAL(trackPaused(unsigned, unsigned)), this, TQ_SLOT(trackUpdate(unsigned, unsigned))); connect(m_cd, TQ_SIGNAL(trackChanged(unsigned, unsigned)), this, TQ_SLOT(trackChanged(unsigned, unsigned))); connect(m_cd, TQ_SIGNAL(discStopped()), this, TQ_SLOT(discStopped())); connect(m_cd, TQ_SIGNAL(discChanged(unsigned)), this, TQ_SLOT(discChanged(unsigned))); connect( &queryledtimer, TQ_SIGNAL(timeout()), TQ_SLOT(togglequeryled()) ); connect( &titlelabeltimer, TQ_SIGNAL(timeout()), TQ_SLOT(titlelabeltimeout()) ); connect( &cycletimer, TQ_SIGNAL(timeout()), TQ_SLOT(cycletimeout()) ); connect( &jumpTrackTimer, TQ_SIGNAL(timeout()), TQ_SLOT(jumpTracks()) ); /* these are always connected in base class connect( playPB, TQ_SIGNAL(clicked()), TQ_SLOT(playClicked()) ); connect( nextPB, TQ_SIGNAL(clicked()), TQ_SLOT(nextClicked()) ); connect( prevPB, TQ_SIGNAL(clicked()), TQ_SLOT(prevClicked()) ); connect( stopPB, TQ_SIGNAL(clicked()), TQ_SLOT(stopClicked()) ); connect( ejectPB, TQ_SIGNAL(clicked()), TQ_SLOT(ejectClicked()) ); */ connect( repeatPB, TQ_SIGNAL(clicked()), TQ_SLOT(loopClicked()) ); connect( songListCB, TQ_SIGNAL(activated(int)), TQ_SLOT(trackSelected(int))); connect( shufflePB, TQ_SIGNAL(clicked()), TQ_SLOT(randomSelected())); connect( cddbPB, TQ_SIGNAL(clicked()), TQ_SLOT(CDDialogSelected())); connect(kapp, TQ_SIGNAL(tdedisplayPaletteChanged()), this, TQ_SLOT(setColors())); connect(kapp, TQ_SIGNAL(iconChanged(int)), this, TQ_SLOT(setIcons())); TQToolTip::remove(songListCB); TQToolTip::add(songListCB, i18n("Track list")); // set up the actions and keyboard accels m_actions = new TDEActionCollection(this); TDEAction* action; action = new TDEAction(i18n("Play/Pause"), Key_P, this, TQ_SLOT(playClicked()), m_actions, "Play/Pause"); action = new TDEAction(i18n("Stop"), Key_S, this, TQ_SLOT(stopClicked()), m_actions, "Stop"); action = new TDEAction(i18n("Previous"), Key_B, this, TQ_SLOT(prevClicked()), m_actions, "Previous"); action = new TDEAction(i18n("Next"), Key_N, this, TQ_SLOT(nextClicked()), m_actions, "Next"); action = KStdAction::quit(this, TQ_SLOT(quitClicked()), m_actions); action = KStdAction::keyBindings(this, TQ_SLOT(configureKeys()), m_actions, "options_configure_shortcuts"); action = KStdAction::keyBindings(this, TQ_SLOT(configureGlobalKeys()), m_actions, "options_configure_globals"); action = KStdAction::preferences(this, TQ_SLOT(showConfig()), m_actions); action = new TDEAction(i18n("Loop"), Key_L, this, TQ_SLOT(loopClicked()), m_actions, "Loop"); action = new TDEAction(i18n("Eject"), CTRL + Key_E, this, TQ_SLOT(ejectClicked()), m_actions, "Eject"); action = new TDEAction(i18n("Increase Volume"), Key_Plus, this, TQ_SLOT(incVolume()), m_actions, "IncVolume"); TDEShortcut increaseVolume = action->shortcut(); increaseVolume.append( KKey( Key_Equal ) ); action->setShortcut( increaseVolume ); action = new TDEAction(i18n("Decrease Volume"), Key_Minus, this, TQ_SLOT(decVolume()), m_actions, "DecVolume"); action = new TDEAction(i18n("Options"), CTRL + Key_T, this, TQ_SLOT(showConfig()), m_actions, "Options"); action = new TDEAction(i18n("Shuffle"), Key_R, this, TQ_SLOT(randomSelected()), m_actions, "Shuffle"); action = new TDEAction(i18n("CDDB"), CTRL + Key_D, this, TQ_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(TQWidget::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( 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"), this, TQ_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"), this, TQ_SLOT(prevClicked())); m_globalAccel->insert("Play/Pause", i18n("Play/Pause"), 0, KKey("WIN+P"), 0, this, TQ_SLOT(playClicked())); m_globalAccel->insert("Stop", i18n("Stop"), 0, KKey("WIN+S"), 0, this, TQ_SLOT(stopClicked())); m_globalAccel->insert("IncVolume", i18n("Increase Volume"), 0, KKey("WIN+Plus"), KKey("WIN+Up"), this, TQ_SLOT(incVolume())); m_globalAccel->insert("DecVolume", i18n("Decrease Volume"), 0, KKey("WIN+Minus"), KKey("WIN+Down"), this, TQ_SLOT(decVolume())); m_globalAccel->insert("Shuffle", i18n("Shuffle"), 0, KKey("WIN+R"), 0, this, TQ_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], TQ_SIGNAL(clicked()), this, TQ_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("media-playback-start")); stopPB->setIconSet(SmallIconSet("media-playback-stop")); ejectPB->setIconSet(SmallIconSet("player_eject")); prevPB->setIconSet(SmallIconSet("media-skip-backward")); nextPB->setIconSet(SmallIconSet("media-skip-forward")); cddbPB->setIconSet(SmallIconSet("view_text")); infoPB->setIconSet(SmallIconSet("system-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, TQ_SIGNAL(activated(int)), TQ_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() == TDECompactDisc::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("media-playback-pause")); playPB->setText(i18n("Pause")); } else { m_cd->pause(); // Update UI to allow a subsequent play. statuslabel->setText(i18n("Pause")); playPB->setIconSet(SmallIconSet("media-playback-start")); 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"), "application-x-cda", i18n("Configure Fetching Items")); connect(configDialog, TQ_SIGNAL(okClicked()), m, TQ_SLOT(save())); connect(configDialog, TQ_SIGNAL(applyClicked()), m, TQ_SLOT(save())); connect(configDialog, TQ_SIGNAL(defaultClicked()), m, TQ_SLOT(defaults())); } } } updateConfigDialog(confWidget); connect(configDialog, TQ_SIGNAL(settingsChanged()), this, TQ_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( TDECompactDisc::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, TQ_SIGNAL(quitSelected()), this, TQ_SLOT(quitClicked())); } m_dockWidget->show(); connect(this, TQ_SIGNAL(trackChanged(const TQString&)), m_dockWidget, TQ_SLOT(setToolTip(const TQString&))); connect(this, TQ_SIGNAL(trackChanged(const TQString&)), m_dockWidget, TQ_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 == TDECompactDisc::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() != TDECompactDisc::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("media-playback-start")); /* 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,TQ_SIGNAL(cddbQuery()),TQ_SLOT(lookupCDDB())); connect(cddialog,TQ_SIGNAL(newCDInfoStored(KCDDB::CDInfo)), TQ_SLOT(setCDInfo(KCDDB::CDInfo))); connect(cddialog,TQ_SIGNAL(finished()),TQ_SLOT(CDDialogDone())); connect(cddialog,TQ_SIGNAL(play(int)),TQ_SLOT(trackSelected(int))); } cddialog->show(); cddialog->raise(); } void KSCD::CDDialogDone() { cddialog->delayedDestruct(); cddialog = 0L; } void KSCD::lookupCDDB() { if (m_cd->discId() == TDECompactDisc::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() != TDECompactDisc::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 TDECmdLineOptions options[] = { {"s",0,0}, {"start",I18N_NOOP("Start playing"),0}, {"+[device]",I18N_NOOP("CD device, can be a path or a media:/ URL"),0}, TDECmdLineLastOption }; /** * 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"