summaryrefslogtreecommitdiffstats
path: root/src/fetchdialog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/fetchdialog.cpp')
-rw-r--r--src/fetchdialog.cpp753
1 files changed, 753 insertions, 0 deletions
diff --git a/src/fetchdialog.cpp b/src/fetchdialog.cpp
new file mode 100644
index 0000000..453b1b3
--- /dev/null
+++ b/src/fetchdialog.cpp
@@ -0,0 +1,753 @@
+/***************************************************************************
+ copyright : (C) 2003-2008 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 "fetchdialog.h"
+#include "fetch/fetchmanager.h"
+#include "fetch/fetcher.h"
+#include "entryview.h"
+#include "isbnvalidator.h"
+#include "upcvalidator.h"
+#include "tellico_kernel.h"
+#include "filehandler.h"
+#include "collection.h"
+#include "entry.h"
+#include "document.h"
+#include "tellico_debug.h"
+#include "gui/combobox.h"
+#include "gui/listview.h"
+#include "tellico_utils.h"
+#include "stringset.h"
+
+#include <klocale.h>
+#include <klineedit.h>
+#include <kpushbutton.h>
+#include <kstatusbar.h>
+#include <khtmlview.h>
+#include <kprogress.h>
+#include <kconfig.h>
+#include <kdialogbase.h>
+#include <kfiledialog.h>
+#include <kiconloader.h>
+#include <kaccelmanager.h>
+#include <ktextedit.h>
+
+#include <qlayout.h>
+#include <qhbox.h>
+#include <qvgroupbox.h>
+#include <qsplitter.h>
+#include <qtimer.h>
+#include <qwhatsthis.h>
+#include <qcheckbox.h>
+#include <qvbox.h>
+#include <qtimer.h>
+#include <qimage.h>
+
+#include <config.h>
+#ifdef ENABLE_WEBCAM
+#include "barcode/barcode.h"
+#endif
+
+namespace {
+ static const int FETCH_STATUS_ID = 0;
+ static const int FETCH_PROGRESS_ID = 0;
+ static const int FETCH_MIN_WIDTH = 600;
+
+ static const char* FETCH_STRING_SEARCH = I18N_NOOP("&Search");
+ static const char* FETCH_STRING_STOP = I18N_NOOP("&Stop");
+}
+
+using Tellico::FetchDialog;
+using barcodeRecognition::barcodeRecognitionThread;
+
+class FetchDialog::SearchResultItem : public Tellico::GUI::ListViewItem {
+ friend class FetchDialog;
+ // always add to end
+ SearchResultItem(GUI::ListView* lv, Fetch::SearchResult* r)
+ : GUI::ListViewItem(lv, lv->lastItem()), m_result(r) {
+ setText(1, r->title);
+ setText(2, r->desc);
+ setPixmap(3, Fetch::Manager::self()->fetcherIcon(r->fetcher.data()));
+ setText(3, r->fetcher->source());
+ }
+ Fetch::SearchResult* m_result;
+};
+
+FetchDialog::FetchDialog(QWidget* parent_, const char* name_)
+ : KDialogBase(parent_, name_, false, i18n("Internet Search"), 0),
+ m_timer(new QTimer(this)), m_started(false) {
+ m_collType = Kernel::self()->collectionType();
+
+ QWidget* mainWidget = new QWidget(this, "FetchDialog mainWidget");
+ setMainWidget(mainWidget);
+ QVBoxLayout* topLayout = new QVBoxLayout(mainWidget, 0, KDialog::spacingHint());
+
+ QVGroupBox* queryBox = new QVGroupBox(i18n("Search Query"), mainWidget, "FetchDialog queryBox");
+ topLayout->addWidget(queryBox);
+
+ QHBox* box1 = new QHBox(queryBox, "FetchDialog box1");
+ box1->setSpacing(KDialog::spacingHint());
+
+ QLabel* label = new QLabel(i18n("Start the search", "S&earch:"), box1);
+
+ m_valueLineEdit = new KLineEdit(box1);
+ label->setBuddy(m_valueLineEdit);
+ QWhatsThis::add(m_valueLineEdit, i18n("Enter a search value. An ISBN search must include the full ISBN."));
+ m_keyCombo = new GUI::ComboBox(box1);
+ Fetch::KeyMap map = Fetch::Manager::self()->keyMap();
+ for(Fetch::KeyMap::ConstIterator it = map.begin(); it != map.end(); ++it) {
+ m_keyCombo->insertItem(it.data(), it.key());
+ }
+ connect(m_keyCombo, SIGNAL(activated(int)), SLOT(slotKeyChanged(int)));
+ QWhatsThis::add(m_keyCombo, i18n("Choose the type of search"));
+
+ m_searchButton = new KPushButton(box1);
+ m_searchButton->setGuiItem(KGuiItem(i18n(FETCH_STRING_STOP),
+ SmallIconSet(QString::fromLatin1("cancel"))));
+ connect(m_searchButton, SIGNAL(clicked()), SLOT(slotSearchClicked()));
+ QWhatsThis::add(m_searchButton, i18n("Click to start or stop the search"));
+
+ // the search button's text changes from search to stop
+ // I don't want it resizing, so figure out the maximum size and set that
+ m_searchButton->polish();
+ int maxWidth = m_searchButton->sizeHint().width();
+ int maxHeight = m_searchButton->sizeHint().height();
+ m_searchButton->setGuiItem(KGuiItem(i18n(FETCH_STRING_SEARCH),
+ SmallIconSet(QString::fromLatin1("find"))));
+ maxWidth = QMAX(maxWidth, m_searchButton->sizeHint().width());
+ maxHeight = QMAX(maxHeight, m_searchButton->sizeHint().height());
+ m_searchButton->setMinimumWidth(maxWidth);
+ m_searchButton->setMinimumHeight(maxHeight);
+
+ QHBox* box2 = new QHBox(queryBox);
+ box2->setSpacing(KDialog::spacingHint());
+
+ m_multipleISBN = new QCheckBox(i18n("&Multiple ISBN/UPC search"), box2);
+ QWhatsThis::add(m_multipleISBN, i18n("Check this box to search for multiple ISBN or UPC values."));
+ connect(m_multipleISBN, SIGNAL(toggled(bool)), SLOT(slotMultipleISBN(bool)));
+
+ m_editISBN = new KPushButton(KGuiItem(i18n("Edit List..."), QString::fromLatin1("text_block")), box2);
+ m_editISBN->setEnabled(false);
+ QWhatsThis::add(m_editISBN, i18n("Click to open a text edit box for entering or editing multiple ISBN values."));
+ connect(m_editISBN, SIGNAL(clicked()), SLOT(slotEditMultipleISBN()));
+
+ // add for spacing
+ box2->setStretchFactor(new QWidget(box2), 10);
+
+ label = new QLabel(i18n("Search s&ource:"), box2);
+ m_sourceCombo = new KComboBox(box2);
+ label->setBuddy(m_sourceCombo);
+ Fetch::FetcherVec sources = Fetch::Manager::self()->fetchers(m_collType);
+ for(Fetch::FetcherVec::Iterator it = sources.begin(); it != sources.end(); ++it) {
+ m_sourceCombo->insertItem(Fetch::Manager::self()->fetcherIcon(it.data()), (*it).source());
+ }
+ connect(m_sourceCombo, SIGNAL(activated(const QString&)), SLOT(slotSourceChanged(const QString&)));
+ QWhatsThis::add(m_sourceCombo, i18n("Select the database to search"));
+
+ QSplitter* split = new QSplitter(QSplitter::Vertical, mainWidget);
+ topLayout->addWidget(split);
+
+ m_listView = new GUI::ListView(split);
+// topLayout->addWidget(m_listView);
+// topLayout->setStretchFactor(m_listView, 1);
+ m_listView->setSorting(10); // greater than number of columns, so not sorting until user clicks column header
+ m_listView->setShowSortIndicator(true);
+ m_listView->setAllColumnsShowFocus(true);
+ m_listView->setSelectionMode(QListView::Extended);
+ m_listView->addColumn(QString::null, 20); // will show a check mark when added
+ m_listView->setColumnAlignment(0, Qt::AlignHCenter); // align checkmark in middle
+// m_listView->setColumnWidthMode(0, QListView::Manual);
+ m_listView->addColumn(i18n("Title"));
+ m_listView->addColumn(i18n("Description"));
+ m_listView->addColumn(i18n("Source"));
+ m_listView->viewport()->installEventFilter(this);
+
+ connect(m_listView, SIGNAL(selectionChanged()), SLOT(slotShowEntry()));
+ // double clicking should add the entry
+ connect(m_listView, SIGNAL(doubleClicked(QListViewItem*)), SLOT(slotAddEntry()));
+ QWhatsThis::add(m_listView, i18n("As results are found, they are added to this list. Selecting one "
+ "will fetch the complete entry and show it in the view below."));
+
+ m_entryView = new EntryView(split, "entry_view");
+ // don't bother creating funky gradient images for compact view
+ m_entryView->setUseGradientImages(false);
+ // set the xslt file AFTER setting the gradient image option
+ m_entryView->setXSLTFile(QString::fromLatin1("Compact.xsl"));
+ QWhatsThis::add(m_entryView->view(), i18n("An entry may be shown here before adding it to the "
+ "current collection by selecting it in the list above"));
+
+ QHBox* box3 = new QHBox(mainWidget);
+ topLayout->addWidget(box3);
+ box3->setSpacing(KDialog::spacingHint());
+
+ m_addButton = new KPushButton(i18n("&Add Entry"), box3);
+ m_addButton->setEnabled(false);
+ m_addButton->setIconSet(UserIconSet(Kernel::self()->collectionTypeName()));
+ connect(m_addButton, SIGNAL(clicked()), SLOT(slotAddEntry()));
+ QWhatsThis::add(m_addButton, i18n("Add the selected entry to the current collection"));
+
+ m_moreButton = new KPushButton(KGuiItem(i18n("Get More Results"), SmallIconSet(QString::fromLatin1("find"))), box3);
+ m_moreButton->setEnabled(false);
+ connect(m_moreButton, SIGNAL(clicked()), SLOT(slotMoreClicked()));
+ QWhatsThis::add(m_moreButton, i18n("Fetch more results from the current data source"));
+
+ KPushButton* clearButton = new KPushButton(KStdGuiItem::clear(), box3);
+ connect(clearButton, SIGNAL(clicked()), SLOT(slotClearClicked()));
+ QWhatsThis::add(clearButton, i18n("Clear all search fields and results"));
+
+ QHBox* bottombox = new QHBox(mainWidget, "box");
+ topLayout->addWidget(bottombox);
+ bottombox->setSpacing(KDialog::spacingHint());
+
+ m_statusBar = new KStatusBar(bottombox, "statusbar");
+ m_statusBar->insertItem(QString::null, FETCH_STATUS_ID, 1, false);
+ m_statusBar->setItemAlignment(FETCH_STATUS_ID, AlignLeft | AlignVCenter);
+ m_progress = new QProgressBar(m_statusBar, "progress");
+ m_progress->setTotalSteps(0);
+ m_progress->setFixedHeight(fontMetrics().height()+2);
+ m_progress->hide();
+ m_statusBar->addWidget(m_progress, 0, true);
+
+ KPushButton* closeButton = new KPushButton(KStdGuiItem::close(), bottombox);
+ connect(closeButton, SIGNAL(clicked()), SLOT(slotClose()));
+
+ connect(m_timer, SIGNAL(timeout()), SLOT(slotMoveProgress()));
+
+ setMinimumWidth(QMAX(minimumWidth(), FETCH_MIN_WIDTH));
+ setStatus(i18n("Ready."));
+
+ resize(configDialogSize(QString::fromLatin1("Fetch Dialog Options")));
+
+ KConfigGroup config(kapp->config(), "Fetch Dialog Options");
+ QValueList<int> splitList = config.readIntListEntry("Splitter Sizes");
+ if(!splitList.empty()) {
+ split->setSizes(splitList);
+ }
+
+ connect(Fetch::Manager::self(), SIGNAL(signalResultFound(Tellico::Fetch::SearchResult*)),
+ SLOT(slotResultFound(Tellico::Fetch::SearchResult*)));
+ connect(Fetch::Manager::self(), SIGNAL(signalStatus(const QString&)),
+ SLOT(slotStatus(const QString&)));
+ connect(Fetch::Manager::self(), SIGNAL(signalDone()),
+ SLOT(slotFetchDone()));
+
+ // make sure to delete results afterwards
+ m_results.setAutoDelete(true);
+
+ KAcceleratorManager::manage(this);
+ // initialize combos
+ QTimer::singleShot(0, this, SLOT(slotInit()));
+
+#ifdef ENABLE_WEBCAM
+ // barcode recognition
+ m_barcodeRecognitionThread = new barcodeRecognitionThread();
+ if (m_barcodeRecognitionThread->isWebcamAvailable()) {
+ m_barcodePreview = new QLabel(0);
+ m_barcodePreview->move( QApplication::desktop()->width() - 350, 30 );
+ m_barcodePreview->setAutoResize( true );
+ m_barcodePreview->show();
+ } else {
+ m_barcodePreview = 0;
+ }
+ connect( m_barcodeRecognitionThread, SIGNAL(recognized(QString)), this, SLOT(slotBarcodeRecognized(QString)) );
+ connect( m_barcodeRecognitionThread, SIGNAL(gotImage(QImage&)), this, SLOT(slotBarcodeGotImage(QImage&)) );
+ m_barcodeRecognitionThread->start();
+/* //DEBUG
+ QImage img( "/home/sebastian/white.png", "PNG" );
+ m_barcodeRecognitionThread->recognizeBarcode( img );*/
+#endif
+}
+
+FetchDialog::~FetchDialog() {
+#ifdef ENABLE_WEBCAM
+ m_barcodeRecognitionThread->stop();
+ if (!m_barcodeRecognitionThread->wait( 1000 ))
+ m_barcodeRecognitionThread->terminate();
+ delete m_barcodeRecognitionThread;
+ if (m_barcodePreview)
+ delete m_barcodePreview;
+#endif
+
+ // we might have downloaded a lot of images we don't need to keep
+ Data::EntryVec entriesToCheck;
+ for(QMap<int, Data::EntryPtr>::Iterator it = m_entries.begin(); it != m_entries.end(); ++it) {
+ entriesToCheck.append(it.data());
+ }
+ // no additional entries to check images to keep though
+ Data::Document::self()->removeImagesNotInCollection(entriesToCheck, Data::EntryVec());
+
+ saveDialogSize(QString::fromLatin1("Fetch Dialog Options"));
+
+ KConfigGroup config(kapp->config(), "Fetch Dialog Options");
+ config.writeEntry("Splitter Sizes", static_cast<QSplitter*>(m_listView->parentWidget())->sizes());
+ config.writeEntry("Search Key", m_keyCombo->currentData().toInt());
+ config.writeEntry("Search Source", m_sourceCombo->currentText());
+}
+
+void FetchDialog::slotSearchClicked() {
+ m_valueLineEdit->selectAll();
+ if(m_started) {
+ setStatus(i18n("Cancelling the search..."));
+ Fetch::Manager::self()->stop();
+ slotFetchDone();
+ } else {
+ QString value = m_valueLineEdit->text().simplifyWhiteSpace();
+ if(value != m_oldSearch) {
+ m_listView->clear();
+ m_entryView->clear();
+ }
+ m_resultCount = 0;
+ m_oldSearch = value;
+ m_started = true;
+ m_searchButton->setGuiItem(KGuiItem(i18n(FETCH_STRING_STOP),
+ SmallIconSet(QString::fromLatin1("cancel"))));
+ startProgress();
+ setStatus(i18n("Searching..."));
+ kapp->processEvents();
+ Fetch::Manager::self()->startSearch(m_sourceCombo->currentText(),
+ static_cast<Fetch::FetchKey>(m_keyCombo->currentData().toInt()),
+ value);
+ }
+}
+
+void FetchDialog::slotClearClicked() {
+ slotFetchDone(false);
+ m_listView->clear();
+ m_entryView->clear();
+ Fetch::Manager::self()->stop();
+ m_multipleISBN->setChecked(false);
+ m_valueLineEdit->clear();
+ m_valueLineEdit->setFocus();
+ m_addButton->setEnabled(false);
+ m_moreButton->setEnabled(false);
+ m_isbnList.clear();
+ m_statusMessages.clear();
+ setStatus(i18n("Ready.")); // because slotFetchDone() writes text
+}
+
+void FetchDialog::slotStatus(const QString& status_) {
+ m_statusMessages.push_back(status_);
+ // if the queue was empty, start the timer
+ if(m_statusMessages.count() == 1) {
+ // wait 2 seconds
+ QTimer::singleShot(2000, this, SLOT(slotUpdateStatus()));
+ }
+}
+
+void FetchDialog::slotUpdateStatus() {
+ if(m_statusMessages.isEmpty()) {
+ return;
+ }
+ setStatus(m_statusMessages.front());
+ m_statusMessages.pop_front();
+ if(!m_statusMessages.isEmpty()) {
+ // wait 2 seconds
+ QTimer::singleShot(2000, this, SLOT(slotUpdateStatus()));
+ }
+}
+
+void FetchDialog::setStatus(const QString& text_) {
+ m_statusBar->changeItem(QChar(' ') + text_, FETCH_STATUS_ID);
+}
+
+void FetchDialog::slotFetchDone(bool checkISBN /* = true */) {
+// myDebug() << "FetchDialog::slotFetchDone()" << endl;
+ m_started = false;
+ m_searchButton->setGuiItem(KGuiItem(i18n(FETCH_STRING_SEARCH),
+ SmallIconSet(QString::fromLatin1("find"))));
+ stopProgress();
+ if(m_resultCount == 0) {
+ slotStatus(i18n("The search returned no items."));
+ } else {
+ /* TRANSLATORS: This is a plural form, you need to translate both lines (except "_n: ") */
+ slotStatus(i18n("The search returned 1 item.",
+ "The search returned %n items.",
+ m_resultCount));
+ }
+ m_moreButton->setEnabled(Fetch::Manager::self()->hasMoreResults());
+
+ // if we're not checking isbn values, then, ok to return
+ if(!checkISBN) {
+ return;
+ }
+
+ const Fetch::FetchKey key = static_cast<Fetch::FetchKey>(m_keyCombo->currentData().toInt());
+ // no way to currently check EAN/UPC values for non-book items
+ if(m_collType & (Data::Collection::Book | Data::Collection::Bibtex) &&
+ m_multipleISBN->isChecked() &&
+ (key == Fetch::ISBN || key == Fetch::UPC)) {
+ QStringList values = QStringList::split(QString::fromLatin1("; "),
+ m_oldSearch.simplifyWhiteSpace());
+ for(QStringList::Iterator it = values.begin(); it != values.end(); ++it) {
+ *it = ISBNValidator::cleanValue(*it);
+ }
+ for(QListViewItemIterator it(m_listView); it.current(); ++it) {
+ QString i = ISBNValidator::cleanValue(static_cast<SearchResultItem*>(it.current())->m_result->isbn);
+ values.remove(i);
+ if(i.length() > 10 && i.startsWith(QString::fromLatin1("978"))) {
+ values.remove(ISBNValidator::cleanValue(ISBNValidator::isbn10(i)));
+ }
+ }
+ if(!values.isEmpty()) {
+ for(QStringList::Iterator it = values.begin(); it != values.end(); ++it) {
+ ISBNValidator::staticFixup(*it);
+ }
+ // TODO dialog caption
+ KDialogBase* dlg = new KDialogBase(this, "isbn not found dialog", false, QString::null, KDialogBase::Ok);
+ QWidget* box = new QWidget(dlg);
+ QVBoxLayout* lay = new QVBoxLayout(box, KDialog::marginHint(), KDialog::spacingHint()*2);
+ QHBoxLayout* lay2 = new QHBoxLayout(lay);
+ QLabel* lab = new QLabel(box);
+ lab->setPixmap(KGlobal::iconLoader()->loadIcon(QString::fromLatin1("messagebox_info"),
+ KIcon::NoGroup,
+ KIcon::SizeMedium));
+ lay2->addWidget(lab);
+ QString s = i18n("No results were found for the following ISBN values:");
+ lay2->addWidget(new QLabel(s, box), 10);
+ KTextEdit* edit = new KTextEdit(box, "isbn list edit");
+ lay->addWidget(edit);
+ edit->setText(values.join(QChar('\n')));
+ QWhatsThis::add(edit, s);
+ connect(dlg, SIGNAL(okClicked()), dlg, SLOT(deleteLater()));
+ dlg->setMainWidget(box);
+ dlg->setMinimumWidth(QMAX(dlg->minimumWidth(), FETCH_MIN_WIDTH*2/3));
+ dlg->show();
+ }
+ }
+}
+
+void FetchDialog::slotResultFound(Tellico::Fetch::SearchResult* result_) {
+ m_results.append(result_);
+ (void) new SearchResultItem(m_listView, result_);
+ ++m_resultCount;
+ adjustColumnWidth();
+ kapp->processEvents();
+}
+
+void FetchDialog::slotAddEntry() {
+ GUI::CursorSaver cs;
+ Data::EntryVec vec;
+ for(QListViewItemIterator it(m_listView, QListViewItemIterator::Selected); it.current(); ++it) {
+ SearchResultItem* item = static_cast<SearchResultItem*>(it.current());
+
+ Fetch::SearchResult* r = item->m_result;
+ Data::EntryPtr entry = m_entries[r->uid];
+ if(!entry) {
+ setStatus(i18n("Fetching %1...").arg(r->title));
+ startProgress();
+ entry = r->fetchEntry();
+ if(!entry) {
+ continue;
+ }
+ m_entries.insert(r->uid, entry);
+ stopProgress();
+ setStatus(i18n("Ready."));
+ }
+ // add a copy, intentionally allowing multiple copies to be added
+ vec.append(new Data::Entry(*entry));
+ item->setPixmap(0, UserIcon(QString::fromLatin1("checkmark")));
+ }
+ if(!vec.isEmpty()) {
+ Kernel::self()->addEntries(vec, true);
+ }
+}
+
+void FetchDialog::slotMoreClicked() {
+ if(m_started) {
+ myDebug() << "FetchDialog::slotMoreClicked() - can't continue while running" << endl;
+ return;
+ }
+
+ m_started = true;
+ m_searchButton->setGuiItem(KGuiItem(i18n(FETCH_STRING_STOP),
+ SmallIconSet(QString::fromLatin1("cancel"))));
+ startProgress();
+ setStatus(i18n("Searching..."));
+ kapp->processEvents();
+ Fetch::Manager::self()->continueSearch();
+}
+
+void FetchDialog::slotShowEntry() {
+ // just in case
+ m_statusMessages.clear();
+
+ const GUI::ListViewItemList& items = m_listView->selectedItems();
+ if(items.isEmpty()) {
+ m_addButton->setEnabled(false);
+ return;
+ }
+
+ m_addButton->setEnabled(true);
+ if(items.count() > 1) {
+ m_entryView->clear();
+ return;
+ }
+
+ SearchResultItem* item = static_cast<SearchResultItem*>(items.getFirst());
+ Fetch::SearchResult* r = item->m_result;
+ setStatus(i18n("Fetching %1...").arg(r->title));
+ Data::EntryPtr entry = m_entries[r->uid];
+ if(!entry) {
+ GUI::CursorSaver cs;
+ startProgress();
+ entry = r->fetchEntry();
+ if(entry) { // might conceivably be null
+ m_entries.insert(r->uid, entry);
+ }
+ stopProgress();
+ }
+ setStatus(i18n("Ready."));
+
+ m_entryView->showEntry(entry);
+}
+
+void FetchDialog::startProgress() {
+ m_progress->show();
+ m_timer->start(100);
+}
+
+void FetchDialog::slotMoveProgress() {
+ m_progress->setProgress(m_progress->progress()+5);
+}
+
+void FetchDialog::stopProgress() {
+ m_timer->stop();
+ m_progress->hide();
+}
+
+void FetchDialog::slotInit() {
+ if(!Fetch::Manager::self()->canFetch()) {
+ m_searchButton->setEnabled(false);
+ Kernel::self()->sorry(i18n("No Internet sources are available for your current collection type."), this);
+ }
+
+ KConfigGroup config(kapp->config(), "Fetch Dialog Options");
+ int key = config.readNumEntry("Search Key", Fetch::FetchFirst);
+ // only change key if valid
+ if(key > Fetch::FetchFirst) {
+ m_keyCombo->setCurrentData(key);
+ }
+ slotKeyChanged(m_keyCombo->currentItem());
+
+ QString source = config.readEntry("Search Source");
+ if(!source.isEmpty()) {
+ m_sourceCombo->setCurrentItem(source);
+ }
+ slotSourceChanged(m_sourceCombo->currentText());
+
+ m_valueLineEdit->setFocus();
+ m_searchButton->setDefault(true);
+}
+
+void FetchDialog::slotKeyChanged(int idx_) {
+ int key = m_keyCombo->data(idx_).toInt();
+ if(key == Fetch::ISBN || key == Fetch::UPC || key == Fetch::LCCN) {
+ m_multipleISBN->setEnabled(true);
+ if(key == Fetch::ISBN) {
+ m_valueLineEdit->setValidator(new ISBNValidator(this));
+ } else {
+ UPCValidator* upc = new UPCValidator(this);
+ connect(upc, SIGNAL(signalISBN()), SLOT(slotUPC2ISBN()));
+ m_valueLineEdit->setValidator(upc);
+ // only want to convert to ISBN if ISBN is accepted by the fetcher
+ Fetch::KeyMap map = Fetch::Manager::self()->keyMap(m_sourceCombo->currentText());
+ upc->setCheckISBN(map.contains(Fetch::ISBN));
+ }
+ } else {
+ m_multipleISBN->setChecked(false);
+ m_multipleISBN->setEnabled(false);
+// slotMultipleISBN(false);
+ m_valueLineEdit->setValidator(0);
+ }
+}
+
+void FetchDialog::slotSourceChanged(const QString& source_) {
+ int curr = m_keyCombo->currentData().toInt();
+ m_keyCombo->clear();
+ Fetch::KeyMap map = Fetch::Manager::self()->keyMap(source_);
+ for(Fetch::KeyMap::ConstIterator it = map.begin(); it != map.end(); ++it) {
+ m_keyCombo->insertItem(it.data(), it.key());
+ }
+ m_keyCombo->setCurrentData(curr);
+ slotKeyChanged(m_keyCombo->currentItem());
+}
+
+void FetchDialog::slotMultipleISBN(bool toggle_) {
+ bool wasEnabled = m_valueLineEdit->isEnabled();
+ m_valueLineEdit->setEnabled(!toggle_);
+ if(!wasEnabled && m_valueLineEdit->isEnabled()) {
+ // if we enable it, it probably had multiple isbn values
+ // the validator doesn't like that, so only keep the first value
+ QString val = m_valueLineEdit->text().section(';', 0, 0);
+ m_valueLineEdit->setText(val);
+ }
+ m_editISBN->setEnabled(toggle_);
+}
+
+void FetchDialog::slotEditMultipleISBN() {
+ KDialogBase dlg(this, "isbn edit dialog", true, i18n("Edit ISBN/UPC Values"),
+ KDialogBase::Ok|KDialogBase::Cancel);
+ QVBox* box = new QVBox(&dlg);
+ box->setSpacing(10);
+ QString s = i18n("<qt>Enter the ISBN or UPC values, one per line.</qt>");
+ (void) new QLabel(s, box);
+ m_isbnTextEdit = new KTextEdit(box, "isbn text edit");
+ m_isbnTextEdit->setText(m_isbnList.join(QChar('\n')));
+ QWhatsThis::add(m_isbnTextEdit, s);
+ KPushButton* fromFileBtn = new KPushButton(SmallIconSet(QString::fromLatin1("fileopen")),
+ i18n("&Load From File..."), box);
+ QWhatsThis::add(fromFileBtn, i18n("<qt>Load the list from a text file.</qt>"));
+ connect(fromFileBtn, SIGNAL(clicked()), SLOT(slotLoadISBNList()));
+ dlg.setMainWidget(box);
+ dlg.setMinimumWidth(QMAX(dlg.minimumWidth(), FETCH_MIN_WIDTH*2/3));
+
+ if(dlg.exec() == QDialog::Accepted) {
+ m_isbnList = QStringList::split('\n', m_isbnTextEdit->text());
+ const QValidator* val = m_valueLineEdit->validator();
+ if(val) {
+ for(QStringList::Iterator it = m_isbnList.begin(); it != m_isbnList.end(); ++it) {
+ val->fixup(*it);
+ if((*it).isEmpty()) {
+ it = m_isbnList.remove(it);
+ // this is next item, shift backward
+ --it;
+ }
+ }
+ }
+ if(m_isbnList.count() > 100) {
+ Kernel::self()->sorry(i18n("<qt>An ISBN search can contain a maximum of 100 ISBN values. Only the "
+ "first 100 values in your list will be used.</qt>"), this);
+ }
+ while(m_isbnList.count() > 100) {
+ m_isbnList.pop_back();
+ }
+ m_valueLineEdit->setText(m_isbnList.join(QString::fromLatin1("; ")));
+ }
+ m_isbnTextEdit = 0; // gets auto-deleted
+}
+
+void FetchDialog::slotLoadISBNList() {
+ if(!m_isbnTextEdit) {
+ return;
+ }
+ KURL u = KFileDialog::getOpenURL(QString::null, QString::null, this);
+ if(u.isValid()) {
+ m_isbnTextEdit->setText(m_isbnTextEdit->text() + FileHandler::readTextFile(u));
+ m_isbnTextEdit->moveCursor(QTextEdit::MoveEnd, false);
+ m_isbnTextEdit->scrollToBottom();
+ }
+}
+
+void FetchDialog::slotUPC2ISBN() {
+ int key = m_keyCombo->currentData().toInt();
+ if(key == Fetch::UPC) {
+ m_keyCombo->setCurrentData(Fetch::ISBN);
+ slotKeyChanged(m_keyCombo->currentItem());
+ }
+}
+
+bool FetchDialog::eventFilter(QObject* obj_, QEvent* ev_) {
+ if(obj_ == m_listView->viewport() && ev_->type() == QEvent::Resize) {
+ adjustColumnWidth();
+ }
+ return false;
+}
+
+void FetchDialog::adjustColumnWidth() {
+ if(!m_listView || m_listView->childCount() == 0) {
+ return;
+ }
+
+ int sum1 = 0;
+ int sum2 = 0;
+ int w3 = 0;
+ for(QListViewItemIterator it(m_listView); it.current(); ++it) {
+ sum1 += it.current()->width(m_listView->fontMetrics(), m_listView, 1);
+ sum2 += it.current()->width(m_listView->fontMetrics(), m_listView, 2);
+ w3 = QMAX(w3, it.current()->width(m_listView->fontMetrics(), m_listView, 3));
+ }
+ // try to be smart about column width
+ // column 0 is fixed, the checkmark icon, give it 20
+ const int w0 = 20;
+ // column 3 is the source, say max is 25% of viewport
+ const int vw = m_listView->visibleWidth();
+ w3 = static_cast<int>(QMIN(1.1 * w3, 0.25 * vw));
+ // scale averages of col 1 and col 2
+ const int avg1 = sum1 / m_listView->childCount();
+ const int avg2 = sum2 / m_listView->childCount();
+ const int w2 = (vw - w0 - w3) * avg2 / (avg1 + avg2);
+ const int w1 = vw - w0 - w3 - w2;
+ m_listView->setColumnWidth(1, w1);
+ m_listView->setColumnWidth(2, w2);
+ m_listView->setColumnWidth(3, w3);
+}
+
+void FetchDialog::slotResetCollection() {
+ if(m_collType == Kernel::self()->collectionType()) {
+ return;
+ }
+ m_collType = Kernel::self()->collectionType();
+ m_sourceCombo->clear();
+ Fetch::FetcherVec sources = Fetch::Manager::self()->fetchers(m_collType);
+ for(Fetch::FetcherVec::Iterator it = sources.begin(); it != sources.end(); ++it) {
+ m_sourceCombo->insertItem(Fetch::Manager::self()->fetcherIcon(it.data()), (*it).source());
+ }
+
+ m_addButton->setIconSet(UserIconSet(Kernel::self()->collectionTypeName()));
+
+ if(Fetch::Manager::self()->canFetch()) {
+ m_searchButton->setEnabled(true);
+ } else {
+ m_searchButton->setEnabled(false);
+ Kernel::self()->sorry(i18n("No Internet sources are available for your current collection type."), this);
+ }
+}
+
+void FetchDialog::slotBarcodeRecognized( QString string )
+{
+ // attention: this slot is called in the context of another thread => do not use GUI-functions!
+ QCustomEvent *e = new QCustomEvent( QEvent::User );
+ QString *data = new QString( string );
+ e->setData( data );
+ qApp->postEvent( this, e ); // the event loop will call FetchDialog::customEvent() in the context of the GUI thread
+}
+
+void FetchDialog::slotBarcodeGotImage( QImage &img )
+{
+ // attention: this slot is called in the context of another thread => do not use GUI-functions!
+ QCustomEvent *e = new QCustomEvent( QEvent::User+1 );
+ QImage *data = new QImage( img.copy() );
+ e->setData( data );
+ qApp->postEvent( this, e ); // the event loop will call FetchDialog::customEvent() in the context of the GUI thread
+}
+
+void FetchDialog::customEvent( QCustomEvent *e )
+{
+ if (!e)
+ return;
+ if ((e->type() == QEvent::User) && e->data()) {
+ // slotBarcodeRecognized() queued call
+ QString temp = *(QString*)(e->data());
+ delete (QString*)(e->data());
+ qApp->beep();
+ m_valueLineEdit->setText( temp );
+ m_searchButton->animateClick();
+ }
+ if ((e->type() == QEvent::User+1) && e->data()) {
+ // slotBarcodegotImage() queued call
+ QImage temp = *(QImage*)(e->data());
+ delete (QImage*)(e->data());
+ m_barcodePreview->setPixmap( temp );
+ }
+}
+
+#include "fetchdialog.moc"