|
|
|
/***************************************************************************
|
|
|
|
copyright : (C) 2005-2006 by Robby Stephenson
|
|
|
|
email : robby@periapsis.org
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of version 2 of the GNU General Public License as *
|
|
|
|
* published by the Free Software Foundation; *
|
|
|
|
* *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#include "entryupdater.h"
|
|
|
|
#include "entry.h"
|
|
|
|
#include "collection.h"
|
|
|
|
#include "tellico_kernel.h"
|
|
|
|
#include "tellico_debug.h"
|
|
|
|
#include "progressmanager.h"
|
|
|
|
#include "statusbar.h"
|
|
|
|
#include "gui/richtextlabel.h"
|
|
|
|
#include "document.h"
|
|
|
|
|
|
|
|
#include <kdialogbase.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <klistview.h>
|
|
|
|
#include <kiconloader.h>
|
|
|
|
|
|
|
|
#include <tqvbox.h>
|
|
|
|
#include <tqtimer.h>
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
static const int CHECK_COLLECTION_IMAGES_STEP_SIZE = 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
using Tellico::EntryUpdater;
|
|
|
|
|
|
|
|
// for each entry, we loop over all available fetchers
|
|
|
|
// then we loop over all entries
|
|
|
|
EntryUpdater::EntryUpdater(Data::CollPtr coll_, Data::EntryVec entries_, TQObject* parent_)
|
|
|
|
: TQObject(parent_), m_coll(coll_), m_entriesToUpdate(entries_), m_cancelled(false) {
|
|
|
|
// for now, we're assuming all entries are same collection type
|
|
|
|
m_fetchers = Fetch::Manager::self()->createUpdateFetchers(m_coll->type());
|
|
|
|
for(Fetch::FetcherVec::Iterator it = m_fetchers.begin(); it != m_fetchers.end(); ++it) {
|
|
|
|
connect(it.data(), TQT_SIGNAL(signalResultFound(Tellico::Fetch::SearchResult*)),
|
|
|
|
TQT_SLOT(slotResult(Tellico::Fetch::SearchResult*)));
|
|
|
|
connect(it.data(), TQT_SIGNAL(signalDone(Tellico::Fetch::Fetcher::Ptr)),
|
|
|
|
TQT_SLOT(slotDone()));
|
|
|
|
}
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
EntryUpdater::EntryUpdater(const TQString& source_, Data::CollPtr coll_, Data::EntryVec entries_, TQObject* parent_)
|
|
|
|
: TQObject(parent_)
|
|
|
|
, m_coll(coll_)
|
|
|
|
, m_entriesToUpdate(entries_)
|
|
|
|
, m_cancelled(false) {
|
|
|
|
// for now, we're assuming all entries are same collection type
|
|
|
|
Fetch::Fetcher::Ptr f = Fetch::Manager::self()->createUpdateFetcher(m_coll->type(), source_);
|
|
|
|
if(f) {
|
|
|
|
m_fetchers.append(f);
|
|
|
|
connect(f, TQT_SIGNAL(signalResultFound(Tellico::Fetch::SearchResult*)),
|
|
|
|
TQT_SLOT(slotResult(Tellico::Fetch::SearchResult*)));
|
|
|
|
connect(f, TQT_SIGNAL(signalDone(Tellico::Fetch::Fetcher::Ptr)),
|
|
|
|
TQT_SLOT(slotDone()));
|
|
|
|
}
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
EntryUpdater::~EntryUpdater() {
|
|
|
|
for(ResultList::Iterator res = m_results.begin(); res != m_results.end(); ++res) {
|
|
|
|
delete (*res).first;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EntryUpdater::init() {
|
|
|
|
m_fetchIndex = 0;
|
|
|
|
m_origEntryCount = m_entriesToUpdate.count();
|
|
|
|
TQString label;
|
|
|
|
if(m_entriesToUpdate.count() == 1) {
|
|
|
|
label = i18n("Updating %1...").tqarg(m_entriesToUpdate.front()->title());
|
|
|
|
} else {
|
|
|
|
label = i18n("Updating entries...");
|
|
|
|
}
|
|
|
|
Kernel::self()->beginCommandGroup(i18n("Update Entries"));
|
|
|
|
ProgressItem& item = ProgressManager::self()->newProgressItem(this, label, true /*canCancel*/);
|
|
|
|
item.setTotalSteps(m_fetchers.count() * m_origEntryCount);
|
|
|
|
connect(&item, TQT_SIGNAL(signalCancelled(ProgressItem*)), TQT_SLOT(slotCancel()));
|
|
|
|
|
|
|
|
// done if no fetchers available
|
|
|
|
if(m_fetchers.isEmpty()) {
|
|
|
|
TQTimer::singleShot(500, this, TQT_SLOT(slotCleanup()));
|
|
|
|
} else {
|
|
|
|
slotStartNext(); // starts fetching
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EntryUpdater::slotStartNext() {
|
|
|
|
StatusBar::self()->settqStatus(i18n("Updating <b>%1</b>...").tqarg(m_entriesToUpdate.front()->title()));
|
|
|
|
ProgressManager::self()->setProgress(this, m_fetchers.count() * (m_origEntryCount - m_entriesToUpdate.count()) + m_fetchIndex);
|
|
|
|
|
|
|
|
Fetch::Fetcher::Ptr f = m_fetchers[m_fetchIndex];
|
|
|
|
// myDebug() << "EntryUpdater::slotDone() - starting " << f->source() << endl;
|
|
|
|
f->updateEntry(m_entriesToUpdate.front());
|
|
|
|
}
|
|
|
|
|
|
|
|
void EntryUpdater::slotDone() {
|
|
|
|
if(m_cancelled) {
|
|
|
|
myLog() << "EntryUpdater::slotDone() - cancelled" << endl;
|
|
|
|
TQTimer::singleShot(500, this, TQT_SLOT(slotCleanup()));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!m_results.isEmpty()) {
|
|
|
|
handleResults();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_results.clear();
|
|
|
|
++m_fetchIndex;
|
|
|
|
// myDebug() << "EntryUpdater::slotDone() " << m_fetchIndex << endl;
|
|
|
|
if(m_fetchIndex == static_cast<int>(m_fetchers.count())) {
|
|
|
|
m_fetchIndex = 0;
|
|
|
|
// we've gone through the loop for the first entry in the vector
|
|
|
|
// pop it and move on
|
|
|
|
m_entriesToUpdate.remove(m_entriesToUpdate.begin());
|
|
|
|
// if there are no more entries, and this is the last fetcher, time to delete
|
|
|
|
if(m_entriesToUpdate.isEmpty()) {
|
|
|
|
TQTimer::singleShot(500, this, TQT_SLOT(slotCleanup()));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
kapp->processEvents();
|
|
|
|
// so the entry updater can clean up a bit
|
|
|
|
TQTimer::singleShot(500, this, TQT_SLOT(slotStartNext()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void EntryUpdater::slotResult(Fetch::SearchResult* result_) {
|
|
|
|
if(!result_ || m_cancelled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// myDebug() << "EntryUpdater::slotResult() - " << result_->title << " [" << result_->fetcher->source() << "]" << endl;
|
|
|
|
m_results.append(UpdateResult(result_, m_fetchers[m_fetchIndex]->updateOverwrite()));
|
|
|
|
Data::EntryPtr e = result_->fetchEntry();
|
|
|
|
if(e) {
|
|
|
|
m_fetchedEntries.append(e);
|
|
|
|
int match = m_coll->sameEntry(m_entriesToUpdate.front(), e);
|
|
|
|
if(match > Data::Collection::ENTRY_PERFECT_MATCH) {
|
|
|
|
result_->fetcher->stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
kapp->processEvents();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EntryUpdater::slotCancel() {
|
|
|
|
// myDebug() << "EntryUpdater::slotCancel()" << endl;
|
|
|
|
m_cancelled = true;
|
|
|
|
Fetch::Fetcher::Ptr f = m_fetchers[m_fetchIndex];
|
|
|
|
if(f) {
|
|
|
|
f->stop(); // ends up calling slotDone();
|
|
|
|
} else {
|
|
|
|
slotDone();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EntryUpdater::handleResults() {
|
|
|
|
Data::EntryPtr entry = m_entriesToUpdate.front();
|
|
|
|
int best = 0;
|
|
|
|
ResultList matches;
|
|
|
|
for(ResultList::Iterator res = m_results.begin(); res != m_results.end(); ++res) {
|
|
|
|
Data::EntryPtr e = (*res).first->fetchEntry();
|
|
|
|
if(!e) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
m_fetchedEntries.append(e);
|
|
|
|
int match = m_coll->sameEntry(entry, e);
|
|
|
|
if(match) {
|
|
|
|
// myDebug() << e->title() << " matches by " << match << endl;
|
|
|
|
}
|
|
|
|
if(match > best) {
|
|
|
|
best = match;
|
|
|
|
matches.clear();
|
|
|
|
matches.append(*res);
|
|
|
|
} else if(match == best && best > 0) {
|
|
|
|
matches.append(*res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(best < Data::Collection::ENTRY_GOOD_MATCH) {
|
|
|
|
if(best > 0) {
|
|
|
|
myDebug() << "no good match (score > 10), best match = " << best << " (" << matches.count() << " matches)" << endl;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// myDebug() << "best match = " << best << " (" << matches.count() << " matches)" << endl;
|
|
|
|
UpdateResult match(0, true);
|
|
|
|
if(matches.count() == 1) {
|
|
|
|
match = matches.front();
|
|
|
|
} else if(matches.count() > 1) {
|
|
|
|
match = askUser(matches);
|
|
|
|
}
|
|
|
|
// askUser() could come back with nil
|
|
|
|
if(match.first) {
|
|
|
|
mergeCurrent(match.first->fetchEntry(), match.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Tellico::EntryUpdater::UpdateResult EntryUpdater::askUser(ResultList results) {
|
|
|
|
KDialogBase dlg(Kernel::self()->widget(), "entry updater dialog",
|
|
|
|
true, i18n("Select Match"), KDialogBase::Ok|KDialogBase::Cancel);
|
|
|
|
TQVBox* box = new TQVBox(&dlg);
|
|
|
|
box->setSpacing(10);
|
|
|
|
|
|
|
|
TQHBox* hbox = new TQHBox(box);
|
|
|
|
hbox->setSpacing(10);
|
|
|
|
TQLabel* icon = new TQLabel(hbox);
|
|
|
|
icon->setPixmap(KGlobal::iconLoader()->loadIcon(TQString::tqfromLatin1("network"), KIcon::Panel, 64));
|
|
|
|
TQString s = i18n("<qt><b>%1</b> returned multiple results which could match <b>%2</b>, "
|
|
|
|
"the entry currently in the collection. Please select the correct match.</qt>")
|
|
|
|
.tqarg(m_fetchers[m_fetchIndex]->source())
|
|
|
|
.tqarg(m_entriesToUpdate.front()->field(TQString::tqfromLatin1("title")));
|
|
|
|
GUI::RichTextLabel* l = new GUI::RichTextLabel(s, hbox);
|
|
|
|
hbox->setStretchFactor(l, 100);
|
|
|
|
|
|
|
|
KListView* view = new KListView(box);
|
|
|
|
view->setShowSortIndicator(true);
|
|
|
|
view->setAllColumnsShowFocus(true);
|
|
|
|
view->setResizeMode(TQListView::AllColumns);
|
|
|
|
view->setMinimumWidth(640);
|
|
|
|
view->addColumn(i18n("Title"));
|
|
|
|
view->addColumn(i18n("Description"));
|
|
|
|
TQMap<KListViewItem*, UpdateResult> map;
|
|
|
|
for(ResultList::Iterator res = results.begin(); res != results.end(); ++res) {
|
|
|
|
map.insert(new KListViewItem(view, (*res).first->fetchEntry()->title(), (*res).first->desc), *res);
|
|
|
|
}
|
|
|
|
|
|
|
|
dlg.setMainWidget(box);
|
|
|
|
if(dlg.exec() != TQDialog::Accepted) {
|
|
|
|
return UpdateResult(0, false);
|
|
|
|
}
|
|
|
|
KListViewItem* item = static_cast<KListViewItem*>(view->selectedItem());
|
|
|
|
if(!item) {
|
|
|
|
return UpdateResult(0, false);
|
|
|
|
}
|
|
|
|
return map[item];
|
|
|
|
}
|
|
|
|
|
|
|
|
void EntryUpdater::mergeCurrent(Data::EntryPtr entry_, bool overWrite_) {
|
|
|
|
Data::EntryPtr currEntry = m_entriesToUpdate.front();
|
|
|
|
if(entry_) {
|
|
|
|
m_matchedEntries.append(entry_);
|
|
|
|
Kernel::self()->updateEntry(currEntry, entry_, overWrite_);
|
|
|
|
if(m_entriesToUpdate.count() % CHECK_COLLECTION_IMAGES_STEP_SIZE == 1) {
|
|
|
|
// I don't want to remove any images in the entries that are getting
|
|
|
|
// updated since they'll reference them later and the command isn't
|
|
|
|
// executed until the command history group is finished
|
|
|
|
// so remove pointers to matched entries
|
|
|
|
Data::EntryVec nonUpdatedEntries = m_fetchedEntries;
|
|
|
|
for(Data::EntryVecIt match = m_matchedEntries.begin(); match != m_matchedEntries.end(); ++match) {
|
|
|
|
nonUpdatedEntries.remove(match);
|
|
|
|
}
|
|
|
|
Data::Document::self()->removeImagesNotInCollection(nonUpdatedEntries, m_matchedEntries);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EntryUpdater::slotCleanup() {
|
|
|
|
StatusBar::self()->cleartqStatus();
|
|
|
|
ProgressManager::self()->setDone(this);
|
|
|
|
Kernel::self()->endCommandGroup();
|
|
|
|
deleteLater();
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "entryupdater.moc"
|