#include "ConsistencyCheckDialog.h" #include #include #include #include #include #include #include #include #include #include #include "ConsistencyCheckParameter.h" #include "applytoalldialog.h" #include "mylist.h" #include "yammimodel.h" #include "songentrystring.h" #include "util.h" #include "yammigui.h" extern YammiGui* gYammiGui; ConsistencyCheckDialog::ConsistencyCheckDialog(QWidget* parent, ConsistencyCheckParameter* para, MyList* selectedSongs, YammiModel* model) : CheckConsistencyDialogBase(parent, i18n("Check consistency - settings"), false) { p=para; this->model = model; this->selectedSongs = selectedSongs; setParameter(); TextEditOutput->setReadOnly(true); TextLabelTitle->setText(QString(i18n("Checking %1 songs")).arg(selectedSongs->count())); this->setModal(false); connect( CheckBoxCheckForExistence, SIGNAL( clicked() ), this, SLOT( changeSetting() ) ); connect( CheckBoxCheckTags, SIGNAL( clicked() ), this, SLOT( changeSetting() ) ); connect( CheckBoxCheckFilenames, SIGNAL( clicked() ), this, SLOT( changeSetting() ) ); connect( CheckBoxCheckDirectories, SIGNAL( clicked() ), this, SLOT( changeSetting() ) ); connect( CheckBoxCorrectDirectories, SIGNAL( clicked() ), this, SLOT( changeSetting() ) ); connect( PushButtonOk, SIGNAL( clicked() ), this, SLOT( myAccept() ) ); connect( PushButtonPatternReplacements, SIGNAL( clicked() ), this, SLOT( showReplacements() ) ); changeSetting(); } ConsistencyCheckDialog::~ConsistencyCheckDialog() {} void ConsistencyCheckDialog::setParameter() { CheckBoxCheckForExistence->setChecked(p->checkForExistence); CheckBoxUpdateNonExisting->setChecked(p->updateNonExisting); CheckBoxCheckTags->setChecked(p->checkTags); CheckBoxIgnoreCase->setChecked(p->ignoreCaseInFilenames); CheckBoxCorrectTags->setChecked(p->correctTags); CheckBoxCheckFilenames->setChecked(p->checkFilenames); CheckBoxCorrectFilenames->setChecked(p->correctFilenames); CheckBoxCheckDirectories->setChecked(p->checkDirectories); CheckBoxCorrectDirectories->setChecked(p->correctDirectories); CheckBoxDeleteEmptyDirectories->setChecked(p->deleteEmptyDirectories); CheckBoxCheckDoubles->setChecked(p->checkDoubles); LineEditDirectoryPattern->setText(p->directoryPattern); LineEditFilenamePattern->setText(p->filenamePattern); } void ConsistencyCheckDialog::myAccept() { p->checkForExistence=CheckBoxCheckForExistence->isChecked(); p->updateNonExisting=CheckBoxUpdateNonExisting->isChecked(); p->checkTags=CheckBoxCheckTags->isChecked(); p->ignoreCaseInFilenames=CheckBoxIgnoreCase->isChecked(); p->correctTags=CheckBoxCorrectTags->isChecked(); p->correctTagsDirection=ComboBoxCorrectTagsDirection->currentItem(); p->checkFilenames=CheckBoxCheckFilenames->isChecked(); p->correctFilenames=CheckBoxCorrectFilenames->isChecked(); p->checkDirectories=CheckBoxCheckDirectories->isChecked(); p->correctDirectories=CheckBoxCorrectDirectories->isChecked(); p->deleteEmptyDirectories=CheckBoxDeleteEmptyDirectories->isChecked(); p->checkDoubles=CheckBoxCheckDoubles->isChecked(); QString directoryPattern = p->directoryPattern; QString filenamePattern = p->filenamePattern; p->directoryPattern=LineEditDirectoryPattern->text(); p->filenamePattern=LineEditFilenamePattern->text(); startCheck(); // restore values that have impact on normal yammi operation // (too dangerous to leave them changed!) p->directoryPattern=directoryPattern; p->filenamePattern=filenamePattern; } void ConsistencyCheckDialog::startCheck() { p->resetStatistics(); TextEditOutput->clear(); KProgressDialog progress( this, 0, i18n("Yammi"), i18n("Checking consistency..."), true); progress.setLabel(i18n("Step 1: checking all songs in database...")); progress.progressBar()->setTotalSteps(selectedSongs->count()); progress.progressBar()->setProgress(0); progress.setMinimumDuration(0); progress.setAutoReset(false); progress.setAutoClose(false); progress.setAllowCancel(true); progress.showCancelButton(true); kapp->processEvents(); KTextEdit* output = TextEditOutput; output->append(i18n("Checking consistency of %1 songs...").arg(selectedSongs->count())); model->problematicSongs.clear(); // 1. iterate through all songs in database if(p->checkForExistence || p->checkTags || p->checkFilenames || p->checkDirectories) { int i=0; for(Song* s=selectedSongs->firstSong(); s; s=selectedSongs->nextSong(), i++) { kapp->processEvents(); if(progress.wasCancelled()) { break; } output->append(QString(" - %1...\n").arg(s->displayName())); if(i % 10 == 0) { progress.progressBar()->setProgress(i); } QString diagnosis=s->checkConsistency(true, true, p->ignoreCaseInFilenames, true); if(diagnosis=="") { continue; } // okay, some kind of problem... if(diagnosis=="file not readable" && p->checkForExistence) { p->nonExisting++; output->append("! " + i18n("file not existing or readable: %1\n").arg(s->displayName())); bool onMedia=s->mediaName.count()>0; if(p->updateNonExisting) { // if we update, there are two cases: if(onMedia) { // 1. update entry: set filename+path to "" s->filename=""; s->path=""; p->nonExistingUpdated++; model->problematicSongs.append(new SongEntryString(s, i18n("filename cleared in song entry"))); output->append("=> " + i18n("filename cleared in song entry")); } else { // 2. delete entry in database output->append(i18n("=> deleting song entry %1\n").arg(s->displayName())); gYammiGui->deleteEntry(s); p->nonExistingDeleted++; } } else { model->problematicSongs.append(new SongEntryString(s, i18n("file not existing or readable"))); } } if(diagnosis.contains("tags not correct") && p->checkTags) { p->dirtyTags++; if(p->correctTags) { bool reallyCorrect; if(p->correctTagsConfirmed==-1) { // warning dialog! ApplyToAllDialog confirm(0); QString msg=QString("Correct tags in file\n\n\t%1?\n\n").arg(s->filename); if(p->correctTagsDirection==p->YAMMI2TAGS) { msg+=QString("(Write yammi info to file tags:\n"); msg+=QString("artist: %1, title: %2\n").arg(s->artist).arg(s->title); msg+=QString("album: %1, comment: %2\n").arg(s->album).arg(s->comment); msg+=QString("year: %1, trackNr: %2, genre: %3)").arg(s->year).arg(s->trackNr).arg(s->genre); } if(p->correctTagsDirection==p->TAGS2YAMMI) { msg+=QString("(Reread tags from filename and update Yammi info)"); } confirm.TextLabel->setText(msg); // show dialog int result=confirm.exec(); if(result==42) { progress.cancel(); break; } if(result==QDialog::Accepted) { reallyCorrect=true; if(confirm.CheckBoxApply->isChecked()) { p->correctTagsConfirmed=1; } } else { reallyCorrect=false; if(confirm.CheckBoxApply->isChecked()) { p->correctTagsConfirmed=0; } } } else { reallyCorrect=(p->correctTagsConfirmed==1); } if(reallyCorrect) { if(p->correctTagsDirection==p->YAMMI2TAGS) { if(s->saveTags()) { p->tagsCorrected++; model->problematicSongs.append(new SongEntryString(s, i18n("Yammi info written to file tags"))); output->append("=> " + i18n("Yammi info written to file tags")); } } if(p->correctTagsDirection==p->TAGS2YAMMI) { if(s->rereadTags()) { p->tagsCorrected++; model->problematicSongs.append(new SongEntryString(s, i18n("Tags reread from file and Yammi info updated"))); output->append("=> " + i18n("Tags reread from file and Yammi info updated")); } } } else { model->problematicSongs.append(new SongEntryString(s, "Yammi info and file tags not consistent")); output->append("! " + i18n("Yammi info and file tags not consistent")); } } else { model->problematicSongs.append(new SongEntryString(s, i18n("Yammi info and file tags not consistent"))); output->append("! " + i18n("Yammi info and file tags not consistent")); } } if(diagnosis.contains("filename not consistent") && p->checkFilenames) { p->dirtyFilenames++; output->append("! " + i18n("Filename not consistent with Yammi info")); output->append(" " + i18n("expected: %1").arg(s->constructFilename())); output->append(" " + i18n("found: %1").arg(s->filename)); if(p->correctFilenames) { bool reallyCorrect; if(p->correctFilenamesConfirmed==-1) { // warning dialog! ApplyToAllDialog confirm(0); QString msg=QString("Correct filename from\n\t%1\n").arg(s->filename); msg+=QString("to\n\t%1?").arg(s->constructFilename()); confirm.TextLabel->setText(msg); // show dialog int result=confirm.exec(); if(result==42) { progress.cancel(); break; } if(result==QDialog::Accepted) { reallyCorrect=true; if(confirm.CheckBoxApply->isChecked()) { p->correctFilenamesConfirmed=1; } } else { reallyCorrect=false; if(confirm.CheckBoxApply->isChecked()) { p->correctFilenamesConfirmed=0; } } } else { reallyCorrect=(p->correctFilenamesConfirmed==1); } if(reallyCorrect) { if(s->correctFilename()) { p->filenamesCorrected++; model->problematicSongs.append(new SongEntryString(s, i18n("Filename corrected"))); output->append("=> " + i18n("Filename corrected")); } } else { model->problematicSongs.append(new SongEntryString(s, "Filename not consistent with Yammi info")); } } else { model->problematicSongs.append(new SongEntryString(s, i18n("Filename not consistent with Yammi info"))); } } if(diagnosis.contains("directory not consistent") && p->checkDirectories) { p->dirtyDirectories++; output->append("! " + i18n("Directory not consistent with Yammi info")); output->append(" " + i18n("expected: %1").arg(s->constructPath())); output->append(" " + i18n("found: %1").arg(s->path)); if(p->correctDirectories) { bool reallyCorrect; if(p->correctDirectoriesConfirmed==-1) { // warning dialog! ApplyToAllDialog confirm(0); QString msg=QString("Correct path for file \n\t%1\n").arg(s->filename); msg+=QString("from\n\t%1\n").arg(s->path); msg+=QString("to\n\t%1?").arg(s->constructPath()); confirm.TextLabel->setText(msg); // show dialog int result=confirm.exec(); if(result==42) { progress.cancel(); break; } if(result==QDialog::Accepted) { reallyCorrect=true; if(confirm.CheckBoxApply->isChecked()) { p->correctDirectoriesConfirmed=1; } } else { reallyCorrect=false; if(confirm.CheckBoxApply->isChecked()) { p->correctDirectoriesConfirmed=0; } } } else { reallyCorrect=(p->correctDirectoriesConfirmed==1); } if(reallyCorrect) { QString pathBefore=s->path; if(s->correctPath()) { p->directoriesCorrected++; model->problematicSongs.append(new SongEntryString(s, i18n("Directory corrected"))); output->append("=> " + i18n("Directory corrected")); if(p->deleteEmptyDirectories) { Util::deleteDirectoryIfEmpty(pathBefore, model->config()->scanDir); } } } else { model->problematicSongs.append(new SongEntryString(s, i18n("Directory not consistent with Yammi info"))); } } else { model->problematicSongs.append(new SongEntryString(s, i18n("Directory not consistent with Yammi info"))); } } } } // 2. check for songs contained twice if(!progress.wasCancelled() && p->checkDoubles) { progress.setLabel(i18n("Step 2: check for song entries pointing to same file")); progress.progressBar()->setTotalSteps(model->allSongs.count()); progress.progressBar()->setProgress(0); kapp->processEvents(); model->allSongs.setSortOrderAndSort(MyList::ByFilename + 16*(MyList::ByPath)); Song* last=model->allSongs.firstSong(); int i=0; for(Song* s=model->allSongs.nextSong(); s; s=model->allSongs.nextSong(), i++) { if(progress.wasCancelled()) { break; } if(i % 20 == 0) { progress.progressBar()->setProgress(i); } if(s->artist=="{wish}") { // ignore wishes continue; } if(s->path=="" && s->filename=="") { // ignore songs not on local harddisk continue; } // check for songs contained twice in database (but pointing to same file+path) if(last->location()==s->location()) { output->append("! " + i18n("two database entries pointing to same file: %1, deleting one").arg(s->filename)); output->append(QString(" 1.: %1").arg(last->displayName())); output->append(QString(" 2.: %1").arg(s->displayName())); model->allSongs.remove(); // TODO: check: could this cause a problem, because we are iterating through this list??? model->allSongsChanged(true); p->doublesFound++; continue; } last=s; } // 3. check for two songs with identical primary key progress.setLabel(i18n("Step 3: check for songs with identical primary keys")); progress.progressBar()->setTotalSteps(model->allSongs.count()); progress.progressBar()->setProgress(0); kapp->processEvents(); model->allSongs.setSortOrderAndSort(MyList::ByKey); last=model->allSongs.firstSong(); i=0; for(Song* s=model->allSongs.nextSong(); s; s=model->allSongs.nextSong(), i++) { if(progress.wasCancelled()) { break; } if(i % 20 == 0) { progress.progressBar()->setProgress(i); } if(s->artist=="{wish}") { continue; } if(s->sameAs(last)) { output->append("! " + i18n("2 songs with identical primary key\n")); output->append(QString(" 1.: %1").arg(last->location())); output->append(QString(" 2.: %1").arg(s->location())); model->problematicSongs.append(new SongEntryString(last, i18n("contained twice(1)"))); model->problematicSongs.append(new SongEntryString(s, i18n("contained twice(2)"))); p->doublesFound++; } last=s; } } // reset sortOrder model->allSongs.setSortOrderAndSort(MyList::ByKey); progress.close(); if(!progress.wasCancelled()) { output->append(i18n("Consistency check finished\n")); } else { output->append(i18n("Consistency check was cancelled\n")); } if(model->problematicSongs.count()==0) { output->append("Your Yammi database is nice and clean!\n"); return; } else { model->allSongsChanged(true); output->append("\n\n"); output->append(i18n("Result of consistency check: (%1 songs)\n").arg(selectedSongs->count())); output->append("---------------------"); output->append(i18n("%1 issues found, check folder \"Problematic Songs\" (won't be saved)\n\n").arg(model->problematicSongs.count())); output->append(i18n("- %1 songs not existing\n").arg(p->nonExisting)); if(p->nonExisting > 0) { output->append(i18n(" %1 entries updated (filename cleared)\n").arg(p->nonExistingUpdated)); output->append(i18n(" %1 entries deleted (because not existing on any media)\n").arg(p->nonExistingDeleted)); } output->append(i18n("- %1 songs with inconsistent tags\n").arg(p->dirtyTags)); if(p->dirtyTags> 0) { output->append(i18n(" %1 tags corrected\n").arg(p->tagsCorrected)); } output->append(i18n("- %1 songs with inconsistent filename\n").arg(p->dirtyFilenames)); if(p->dirtyFilenames> 0) { output->append(i18n(" %1 filenames corrected\n").arg(p->filenamesCorrected)); } output->append(i18n("- %1 songs with inconsistent path\n").arg(p->dirtyDirectories)); if(p->dirtyDirectories> 0) { output->append(i18n(" %1 paths corrected\n").arg(p->directoriesCorrected)); } output->append(i18n("- %1 double entries found\n").arg(p->doublesFound)); return; } } void ConsistencyCheckDialog::changeSetting() { if(CheckBoxCheckForExistence->isChecked()) { CheckBoxUpdateNonExisting->setEnabled(true); } else { CheckBoxUpdateNonExisting->setEnabled(false); } if(CheckBoxCheckTags->isChecked()) { CheckBoxCorrectTags->setEnabled(true); } else { CheckBoxCorrectTags->setEnabled(false); } if(CheckBoxCheckFilenames->isChecked()) { CheckBoxCorrectFilenames->setEnabled(true); } else { CheckBoxCorrectFilenames->setEnabled(false); } if(CheckBoxCheckDirectories->isChecked()) { CheckBoxCorrectDirectories->setEnabled(true); if(CheckBoxCorrectDirectories->isChecked()) { CheckBoxDeleteEmptyDirectories->setEnabled(true); } else { CheckBoxDeleteEmptyDirectories->setEnabled(false); } } else { CheckBoxCorrectDirectories->setEnabled(false); CheckBoxDeleteEmptyDirectories->setEnabled(false); } if(!CheckBoxCheckForExistence->isChecked() && !CheckBoxCheckTags->isChecked() && !CheckBoxCheckFilenames->isChecked() && !CheckBoxCheckDoubles->isChecked() && !CheckBoxCheckDirectories->isChecked()) { PushButtonOk->setEnabled(false); } else { PushButtonOk->setEnabled(true); } } void ConsistencyCheckDialog::showReplacements() { QString msg; msg+=i18n("Replacements for filename/directory pattern:\n\n"); msg+=Song::getReplacementsDescription(); QMessageBox::information( this, "Yammi",msg); }