/** * This file is part of the KAudioCreator package * Copyright (C) 2003 Benjamin C Meyer (ben+kaudiocreator at meyerhome dot net) * * 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. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. 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 "job.h" #include "kcompactdisc.h" #include "libkcddb/cdinfodialogbase.h" #include "prefs.h" #include "tracksimp.h" /** * Constructor, connect up slots and signals. */ TracksImp::TracksImp( TQWidget* parent, const char* name) : Tracks(parent,name), cddbInfo() { cd = new TDECompactDisc; connect(cd,TQT_SIGNAL(discChanged(unsigned)),this,TQT_SLOT(newDisc(unsigned))); connect(trackListing, TQT_SIGNAL(clicked( TQListViewItem * )), this, TQT_SLOT(selectTrack(TQListViewItem*))); connect(trackListing, TQT_SIGNAL(doubleClicked(TQListViewItem *)), this, TQT_SLOT(editInformation())); connect(trackListing, TQT_SIGNAL(returnPressed(TQListViewItem *)), this, TQT_SLOT(editInformation())); connect(selectAllTracksButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(selectAllTracks())); connect(deselectAllTracksButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(deselectAllTracks())); connect(deviceCombo, TQT_SIGNAL(textChanged(const TQString &)), this, TQT_SLOT(changeDevice(const TQString &))); selectAllTracksButton->setEnabled( false ); deselectAllTracksButton->setEnabled( false ); cddb = new KCDDB::Client(); cddb->setBlockingMode(false); connect(cddb, TQT_SIGNAL(finished(CDDB::Result)), this, TQT_SLOT(lookupCDDBDone(CDDB::Result))); trackListing->setSorting(-1, false); loadSettings(); } /** * store the current device from the combo. */ TracksImp::~TracksImp() { TQStringList list; if( deviceCombo->count() != 0) list.append(deviceCombo->currentText()); for ( int i=0; icount();i++ ) { TQString text = deviceCombo->text(i); if( list.find(text) == list.end()) list.append(text); if( list.count() == 5) break; } Prefs::setDevice(list); Prefs::writeConfig(); } /** * Load the class settings. */ void TracksImp::loadSettings() { TQStringList list; // Add the saved list, no dups TQStringList prefsList = Prefs::device(); TQStringList::Iterator it; for ( it = prefsList.begin(); it != prefsList.end(); ++it ) { if( list.find( *it ) == list.end()) list.append(*it); } // Get current list, no dups for ( int i=0; icount();i++ ) { TQString text = deviceCombo->text(i); if( list.find(text) == list.end()) list.append(text); } // Set list, get top one deviceCombo->clear(); deviceCombo->insertStringList(list); changeDevice(deviceCombo->currentText()); } void TracksImp::newDisc(unsigned discId) { if (discId == TDECompactDisc::missingDisc) { kdDebug() << "newDisc - No disc" << endl; cddbInfo.clear(); cddbInfo.title = i18n("No disc"); newAlbum(); emit(hasCD(false)); selectAllTracksButton->setEnabled( false ); deselectAllTracksButton->setEnabled( false ); return; } kdDebug() << "newDisc - " << discId << endl; emit(hasCD(true)); selectAllTracksButton->setEnabled( true ); deselectAllTracksButton->setEnabled( true ); cddbInfo.clear(); cddbInfo.id = TQString::number(discId, 16).rightJustify(8,'0'); cddbInfo.length = cd->discLength(); cddbInfo.artist = cd->discArtist(); cddbInfo.title = 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 <= cd->tracks(); i++) { if (isSampler) track.title = cd->trackArtist(i) + " / " + cd->trackTitle(i); else track.title = cd->trackTitle(i); cddbInfo.trackInfoList.append(track); } newAlbum(); if (Prefs::performCDDBauto()) lookupCDDB(); } /** * @return if there is a cd inserted or not. */ bool TracksImp::hasCD(){ return cd->discId() != TDECompactDisc::missingDisc; } /** * The device text has changed. * @param file - the new text to check. */ void TracksImp::changeDevice(const TQString &file ) { TQString newDevice = TDECompactDisc::urlToDevice(file); if( newDevice == cd->device() ) { //tqDebug("Device names match, returning"); return; } TQFileInfo fileInfo(newDevice); if( !fileInfo.exists() || fileInfo.isDir()) { //tqDebug("Device file !exist or isDir or !file"); return; } if (!cd->setDevice(newDevice, 50, false)) { TQString errstring = i18n("CDROM read or access error (or no audio disk in drive).\n"\ "Please make sure you have access permissions to:\n%1") .arg(file); KMessageBox::error(this, errstring, i18n("Error")); } } /** * Helper function (toolbar button) for users. **/ void TracksImp::performCDDB() { if (!hasCD()) { KMessageBox::sorry(this, i18n("Please insert a disk."), i18n("CDDB Failed")); return; } lookupCDDB(); } /** * See if we can't get the cddb value for this cd. */ void TracksImp::lookupCDDB() { cddb->config().reparse(); cddb->lookup(cd->discSignature()); } /** * The non blocking CDDB function calling has finished. Report an error or * continue. * @param result the success or failure of the cddb retrieval. */ void TracksImp::lookupCDDBDone(CDDB::Result result ) { if ((result != KCDDB::CDDB::Success) && (result != KCDDB::CDDB::MultipleRecordFound)) { KMessageBox::sorry(this, i18n("Unable to retrieve CDDB information."), i18n("CDDB Failed")); return; } // Choose the cddb entry KCDDB::CDInfo info = cddb->bestLookupResponse(); // TODO Why doesn't libcddb not return MultipleRecordFound? //if( result == KCDDB::CDDB::MultipleRecordFound ) { if( Prefs::promptIfIncompleteInfo() && cddb->lookupResponse().count() > 1 ) { TQString searchedCDId = cddbInfo.id; 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 } // Check that the CD we looked up is the one now loaded. // The user might have ejected the CD while we were in the // KInputDialog event loop, and replaced it with another one. if ( searchedCDId != cddbInfo.id ) return; } // 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; newAlbum(); // See if the user wishes to automaticly rip after successfully retrieving if( Prefs::autoRip()) ripWholeAlbum(); } /** * Bring up the dialog to edit the information about this album. * If there is not currently selected track return. * If ok is pressed then store the information and update track name. */ void TracksImp::editInformation( ) { if( !hasCD() ) return; // Create dialog. KDialogBase *dialog = new KDialogBase( this, "name", false, i18n( "CD Editor" ), KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true ); CDInfoDialogBase *base = new CDInfoDialogBase(dialog, "Album info editor dialog"); // Workaround the fact that CDInfoDialogBase doesn't take // a const TrackOffsetList TQValueList discSig = cd->discSignature(); base->setInfo(cddbInfo, discSig); dialog->setMainWidget(base); // Show dialog->and save results. bool okClicked = dialog->exec(); if( okClicked ) { cddbInfo = base->info(); newAlbum(); KCDDB::Cache::store(cddbInfo); } delete dialog; } TQString TracksImp::formatTime(unsigned ms) { TQTime time; time = time.addMSecs((int)ms); // Use ".zzz" for milliseconds... TQString temp2; if (time.hour() > 0) temp2 = time.toString("hh:mm:ss"); else temp2 = time.toString("mm:ss"); return temp2; } /** * Helper function. * Selects all tracks and then calls startSession to rip them all. */ void TracksImp::ripWholeAlbum() { selectAllTracks(); startSession(); } /** * Start of the "ripping session" by emiting signals to rip the selected tracks. * If any album information is not set, notify the user first. */ void TracksImp::startSession( int encoder ) { TQPtrList selected = selectedTracks(); if( selected.isEmpty() ) { int i = KMessageBox::questionYesNo( this, i18n("No tracks have been selected. Would you like to rip the entire CD?"), i18n("No Tracks Selected"), i18n("Rip CD"), KStdGuiItem::cancel() ); if( i == KMessageBox::No ) return; selectAllTracks(); selected = selectedTracks(); } TQStringList list; if( cddbInfo.genre == "Unknown" ) list += "Genre"; if( cddbInfo.year == 0 ) list += "Year"; if( cddbInfo.artist == "Unknown Artist") list += "Artist"; if( cddbInfo.title == "Unknown Album") list += "Album"; if( Prefs::promptIfIncompleteInfo() && list.count() > 0 ) { int r = KMessageBox::questionYesNo( this, i18n( "Part of the album is not set: %1.\n (To change album information click the \"Edit Information\" button.)\n Would you like to rip the selected tracks anyway?").arg(list.join(", ")), i18n("Album Information Incomplete"), i18n("Rip"), KStdGuiItem::cancel() ); if( r == KMessageBox::No ) return; } if ( encoder == -1 ) encoder = Prefs::currentEncoder(); Job *lastJob = 0; TracksItem *item = selected.first(); for( ; item ; item = selected.next() ) { Job *newJob = new Job(); newJob->encoder = encoder; newJob->device = cd->device(); newJob->album = cddbInfo.title; newJob->genre = cddbInfo.genre; if( newJob->genre.isEmpty() ) newJob->genre = "Pop"; newJob->group = cddbInfo.artist; newJob->comment = cddbInfo.extd; newJob->year = cddbInfo.year; newJob->track = item->track(); // newJob->track_title = item->title(); newJob->track_title = item->text( HEADER_TRACK_NAME ); newJob->track_artist = item->artist(); newJob->track_comment = item->comment(); lastJob = newJob; emit( ripTrack(newJob) ); } if( lastJob) lastJob->lastSongInAlbum = true; KMessageBox::information(this, i18n("%1 Job(s) have been started. You can watch their progress in the " "jobs section.").arg( selected.count() ), i18n("Jobs have started"), i18n("Jobs have started")); } /** * Selects and unselects the tracks. * @param currentItem the track to swich the selection choice. */ void TracksImp::selectTrack( TQListViewItem *item ) { if( !item ) return; #define item static_cast(item) item->setChecked( !item->checked() ); #undef item } TQPtrList TracksImp::selectedTracks() { TQPtrList selected; TracksItem *item = static_cast(trackListing->firstChild()); while( item ) { if( item->checked() ) selected.append( item ); item = static_cast(item->nextSibling()); } return selected; } /** * Turn on all of the tracks. */ void TracksImp::selectAllTracks() { TracksItem *currentItem = static_cast(trackListing->firstChild()); while( currentItem ) { currentItem->setChecked( true ); currentItem = static_cast(currentItem->nextSibling()); } } /** * Turn off all of the tracks. */ void TracksImp::deselectAllTracks() { TracksItem *currentItem = static_cast(trackListing->firstChild()); while( currentItem ) { currentItem->setChecked( false ); currentItem = static_cast(currentItem->nextSibling()); } } /** * Set the current stats for the new album being displayed. */ void TracksImp::newAlbum() { TQString albumText = cddbInfo.title; if( !cddbInfo.artist.isEmpty() ) albumText = cddbInfo.artist + i18n( " - " ) + albumText; albumName->setText( albumText ); trackListing->clear(); selectAllTracksButton->setEnabled(false); deselectAllTracksButton->setEnabled(false); emit( hasTracks(false) ); KCDDB::TrackInfoList t = cddbInfo.trackInfoList; bool isSampler = true; for( unsigned i = 0; i < t.count(); i++ ) { if (t[i].title.find(" / ") == -1) { isSampler = false; break; } } TracksItem *last = 0; for( unsigned i = 0; i < t.count(); i++ ) { TQString trackArtist; TQString title; if( isSampler ) { // Support for multiple artists stripping. int delimiter = t[i].title.find(" / "); Q_ASSERT( delimiter != -1 ); trackArtist = t[i].title.left(delimiter); title = t[i].title.mid(delimiter + 3); } else { trackArtist = cddbInfo.artist; title = t[i].title; } // There is a new track for this title. Add it to the list of tracks. TQString trackLength = formatTime(cd->trackLength(i+1)); last = new TracksItem( trackListing, last, title, trackArtist, i+1, trackLength, t[i].extt ); } if( t.count() ) { // Set the current selected track to the first one. trackListing->setCurrentItem(trackListing->firstChild()); selectAllTracksButton->setEnabled(true); deselectAllTracksButton->setEnabled(true); emit(hasTracks(true)); } } /** * If the user presses the F2 key, trigger renaming of the title. * @param event the TQKeyEvent passed to this event handler. */ void TracksImp::keyPressEvent(TQKeyEvent *event) { TQListViewItem *item = trackListing->selectedItem(); if( !item ) return; if( event->key() == TQt::Key_F2 ) { item->setRenameEnabled( HEADER_TRACK_NAME, true ); item->startRename( HEADER_TRACK_NAME ); event->accept(); } else Tracks::keyPressEvent(event); } /** * Eject the current cd device */ void TracksImp::eject() { ejectDevice(cd->device()); } /** * Eject a device * @param deviceToEject the device to eject. */ void TracksImp::ejectDevice(const TQString &deviceToEject) { changeDevice(deviceToEject); cd->eject(); } #include "tracksimp.moc"