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.
579 lines
16 KiB
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 <kapplication.h>
|
|
#include <kconfig.h>
|
|
#include <kcombobox.h>
|
|
#include <kdebug.h>
|
|
#include <kinputdialog.h>
|
|
#include <klistview.h>
|
|
#include <kprocess.h>
|
|
#include <kmessagebox.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 KCompactDisc;
|
|
|
|
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; 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 == KCompactDisc::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() != KCompactDisc::missingDisc;
|
|
}
|
|
|
|
/**
|
|
* The device text has changed.
|
|
* @param file - the new text to check.
|
|
*/
|
|
void TracksImp::changeDevice(const TQString &file ) {
|
|
TQString newDevice = KCompactDisc::urlToDevice(file);
|
|
|
|
if( newDevice == cd->device() ) {
|
|
//qDebug("Device names match, returning");
|
|
return;
|
|
}
|
|
|
|
TQFileInfo fileInfo(newDevice);
|
|
if( !fileInfo.exists() || fileInfo.isDir()) {
|
|
//qDebug("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"
|
|
|