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/kaudiocreator/tracksimp.cpp

579 lines
16 KiB

/**
* 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 <dcopref.h>
#include <fixx11h.h>
#include <tdeapplication.h>
#include <tdeconfig.h>
#include <kcombobox.h>
#include <kdebug.h>
#include <kinputdialog.h>
#include <tdelistview.h>
#include <kprocess.h>
#include <tdemessagebox.h>
#include <kurl.h>
#include <tqfileinfo.h>
#include <tqlabel.h>
#include <tqptrlist.h>
#include <tqpushbutton.h>
#include <tqregexp.h>
#include <tqspinbox.h>
#include <tqtimer.h>
#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,TQ_SIGNAL(discChanged(unsigned)),this,TQ_SLOT(newDisc(unsigned)));
connect(trackListing, TQ_SIGNAL(clicked( TQListViewItem * )), this, TQ_SLOT(selectTrack(TQListViewItem*)));
connect(trackListing, TQ_SIGNAL(doubleClicked(TQListViewItem *)), this, TQ_SLOT(editInformation()));
connect(trackListing, TQ_SIGNAL(returnPressed(TQListViewItem *)), this, TQ_SLOT(editInformation()));
connect(selectAllTracksButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(selectAllTracks()));
connect(deselectAllTracksButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(deselectAllTracks()));
connect(deviceCombo, TQ_SIGNAL(textChanged(const TQString &)), this, TQ_SLOT(changeDevice(const TQString &)));
selectAllTracksButton->setEnabled( false );
deselectAllTracksButton->setEnabled( false );
cddb = new KCDDB::Client();
cddb->setBlockingMode(false);
connect(cddb, TQ_SIGNAL(finished(CDDB::Result)), this, TQ_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; i<deviceCombo->count();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; i<deviceCombo->count();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<unsigned> 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<TracksItem> 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<TracksItem*>(item)
item->setChecked( !item->checked() );
#undef item
}
TQPtrList<TracksItem> TracksImp::selectedTracks()
{
TQPtrList<TracksItem> selected;
TracksItem *item = static_cast<TracksItem*>(trackListing->firstChild());
while( item )
{
if( item->checked() )
selected.append( item );
item = static_cast<TracksItem*>(item->nextSibling());
}
return selected;
}
/**
* Turn on all of the tracks.
*/
void TracksImp::selectAllTracks()
{
TracksItem *currentItem = static_cast<TracksItem*>(trackListing->firstChild());
while( currentItem )
{
currentItem->setChecked( true );
currentItem = static_cast<TracksItem*>(currentItem->nextSibling());
}
}
/**
* Turn off all of the tracks.
*/
void TracksImp::deselectAllTracks()
{
TracksItem *currentItem = static_cast<TracksItem*>(trackListing->firstChild());
while( currentItem )
{
currentItem->setChecked( false );
currentItem = static_cast<TracksItem*>(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"