/*************************************************************************** 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 "gcstarpluginfetcher.h" #include "messagehandler.h" #include "fetchmanager.h" #include "../collection.h" #include "../entry.h" #include "../translators/tellicoimporter.h" #include "../gui/combobox.h" #include "../gui/collectiontypecombo.h" #include "../filehandler.h" #include "../tellico_kernel.h" #include "../tellico_debug.h" #include "../latin1literal.h" #include "../tellico_utils.h" #include #include #include #include #include #include #include #include #include using Tellico::Fetch::GCstarPluginFetcher; GCstarPluginFetcher::PluginMap GCstarPluginFetcher::pluginMap; GCstarPluginFetcher::PluginParse GCstarPluginFetcher::pluginParse = NotYet; //static GCstarPluginFetcher::PluginList GCstarPluginFetcher::plugins(int collType_) { if(!pluginMap.contains(collType_)) { GUI::CursorSaver cs; TQString gcstar = TDEStandardDirs::findExe(TQString::fromLatin1("gcstar")); if(pluginParse == NotYet) { KProcIO proc; proc << gcstar << TQString::fromLatin1("--version"); // wait 5 seconds at most, just a sanity thing, never want to block completely if(proc.start(TDEProcess::Block) && proc.wait(5)) { TQString output; proc.readln(output); if(!output.isEmpty()) { // always going to be x.y[.z] ? TQRegExp versionRx(TQString::fromLatin1("(\\d+)\\.(\\d+)(?:\\.(\\d+))?")); if(versionRx.search(output) > -1) { int x = versionRx.cap(1).toInt(); int y = versionRx.cap(2).toInt(); int z = versionRx.cap(3).toInt(); // ok to be empty myDebug() << TQString::fromLatin1("GCstarPluginFetcher() - found %1.%2.%3").arg(x).arg(y).arg(z) << endl; // --list-plugins argument was added for 1.3 release pluginParse = (x >= 1 && y >=3) ? New : Old; } } } // if still zero, then we should use old in future if(pluginParse == NotYet) { pluginParse = Old; } } if(pluginParse == New) { readPluginsNew(collType_, gcstar); } else { readPluginsOld(collType_, gcstar); } } return pluginMap.contains(collType_) ? pluginMap[collType_] : GCstarPluginFetcher::PluginList(); } void GCstarPluginFetcher::readPluginsNew(int collType_, const TQString& gcstar_) { PluginList plugins; TQString gcstarCollection = gcstarType(collType_); if(gcstarCollection.isEmpty()) { pluginMap.insert(collType_, plugins); return; } KProcIO proc; proc << gcstar_ << TQString::fromLatin1("-x") << TQString::fromLatin1("--list-plugins") << TQString::fromLatin1("--collection") << gcstarCollection; if(!proc.start(TDEProcess::Block)) { myWarning() << "GCstarPluginFetcher::readPluginsNew() - can't start" << endl; return; } bool hasName = false; PluginInfo info; TQString line; for(int length = 0; length > -1; length = proc.readln(line)) { if(line.isEmpty()) { if(hasName) { plugins << info; } hasName = false; info.clear(); } else { // authors have \t at beginning line = line.stripWhiteSpace(); if(!hasName) { info.insert(TQString::fromLatin1("name"), line); hasName = true; } else { info.insert(TQString::fromLatin1("author"), line); } // myDebug() << line << endl; } } pluginMap.insert(collType_, plugins); } void GCstarPluginFetcher::readPluginsOld(int collType_, const TQString& gcstar_) { TQDir dir(gcstar_, TQString::fromLatin1("GC*.pm")); dir.cd(TQString::fromLatin1("../../lib/gcstar/GCPlugins/")); TQRegExp rx(TQString::fromLatin1("get(Name|Author|Lang)\\s*\\{\\s*return\\s+['\"](.+)['\"]")); rx.setMinimal(true); PluginList plugins; TQString dirName = gcstarType(collType_); if(dirName.isEmpty()) { pluginMap.insert(collType_, plugins); return; } TQStringList files = dir.entryList(); for(TQStringList::ConstIterator file = files.begin(); file != files.end(); ++file) { KURL u; u.setPath(dir.filePath(*file)); PluginInfo info; TQString text = FileHandler::readTextFile(u); for(int pos = rx.search(text); pos > -1; pos = rx.search(text, pos+rx.matchedLength())) { info.insert(rx.cap(1).lower(), rx.cap(2)); } // only add if it has a name if(info.contains(TQString::fromLatin1("name"))) { plugins << info; } } // inserting empty map is ok pluginMap.insert(collType_, plugins); } TQString GCstarPluginFetcher::gcstarType(int collType_) { switch(collType_) { case Data::Collection::Book: return TQString::fromLatin1("GCbooks"); case Data::Collection::Video: return TQString::fromLatin1("GCfilms"); case Data::Collection::Game: return TQString::fromLatin1("GCgames"); case Data::Collection::Album: return TQString::fromLatin1("GCmusics"); case Data::Collection::Coin: return TQString::fromLatin1("GCcoins"); case Data::Collection::Wine: return TQString::fromLatin1("GCwines"); case Data::Collection::BoardGame: return TQString::fromLatin1("GCboardgames"); default: break; } return TQString(); } GCstarPluginFetcher::GCstarPluginFetcher(TQObject* parent_, const char* name_/*=0*/) : Fetcher(parent_, name_), m_started(false), m_collType(-1), m_process(0) { } GCstarPluginFetcher::~GCstarPluginFetcher() { stop(); } TQString GCstarPluginFetcher::defaultName() { return i18n("GCstar Plugin"); } TQString GCstarPluginFetcher::source() const { return m_name; } bool GCstarPluginFetcher::canFetch(int type_) const { return m_collType == -1 ? false : m_collType == type_; } void GCstarPluginFetcher::readConfigHook(const TDEConfigGroup& config_) { m_collType = config_.readNumEntry("CollectionType", -1); m_plugin = config_.readEntry("Plugin"); } void GCstarPluginFetcher::search(FetchKey key_, const TQString& value_) { m_started = true; m_data.truncate(0); if(key_ != Fetch::Title) { myDebug() << "GCstarPluginFetcher::search() - only Title searches are supported" << endl; stop(); return; } TQString gcstar = TDEStandardDirs::findExe(TQString::fromLatin1("gcstar")); if(gcstar.isEmpty()) { myWarning() << "GCstarPluginFetcher::search() - gcstar not found!" << endl; stop(); return; } TQString gcstarCollection = gcstarType(m_collType); if(m_plugin.isEmpty()) { myWarning() << "GCstarPluginFetcher::search() - no plugin name! " << endl; stop(); return; } m_process = new TDEProcess(); connect(m_process, TQ_SIGNAL(receivedStdout(TDEProcess*, char*, int)), TQ_SLOT(slotData(TDEProcess*, char*, int))); connect(m_process, TQ_SIGNAL(receivedStderr(TDEProcess*, char*, int)), TQ_SLOT(slotError(TDEProcess*, char*, int))); connect(m_process, TQ_SIGNAL(processExited(TDEProcess*)), TQ_SLOT(slotProcessExited(TDEProcess*))); TQStringList args; args << gcstar << TQString::fromLatin1("-x") << TQString::fromLatin1("--collection") << gcstarCollection << TQString::fromLatin1("--export") << TQString::fromLatin1("Tellico") << TQString::fromLatin1("--website") << m_plugin << TQString::fromLatin1("--download") << TDEProcess::quote(value_); myLog() << "GCstarPluginFetcher::search() - " << args.join(TQChar(' ')) << endl; *m_process << args; if(!m_process->start(TDEProcess::NotifyOnExit, TDEProcess::AllOutput)) { myDebug() << "GCstarPluginFetcher::startSearch() - process failed to start" << endl; stop(); } } void GCstarPluginFetcher::stop() { if(!m_started) { return; } if(m_process) { m_process->kill(); delete m_process; m_process = 0; } m_data.truncate(0); m_started = false; m_errors.clear(); emit signalDone(this); } void GCstarPluginFetcher::slotData(TDEProcess*, char* buffer_, int len_) { TQDataStream stream(m_data, IO_WriteOnly | IO_Append); stream.writeRawBytes(buffer_, len_); } void GCstarPluginFetcher::slotError(TDEProcess*, char* buffer_, int len_) { TQString msg = TQString::fromLocal8Bit(buffer_, len_); msg.prepend(source() + TQString::fromLatin1(": ")); myDebug() << "GCstarPluginFetcher::slotError() - " << msg << endl; m_errors << msg; } void GCstarPluginFetcher::slotProcessExited(TDEProcess*) { // myDebug() << "GCstarPluginFetcher::slotProcessExited()" << endl; if(!m_process->normalExit() || m_process->exitStatus()) { myDebug() << "GCstarPluginFetcher::slotProcessExited() - "<< source() << ": process did not exit successfully" << endl; if(!m_errors.isEmpty()) { message(m_errors.join(TQChar('\n')), MessageHandler::Error); } stop(); return; } if(!m_errors.isEmpty()) { message(m_errors.join(TQChar('\n')), MessageHandler::Warning); } if(m_data.isEmpty()) { myDebug() << "GCstarPluginFetcher::slotProcessExited() - "<< source() << ": no data" << endl; stop(); return; } Import::TellicoImporter imp(TQString::fromUtf8(m_data, m_data.size())); Data::CollPtr coll = imp.collection(); if(!coll) { if(!imp.statusMessage().isEmpty()) { message(imp.statusMessage(), MessageHandler::Status); } myDebug() << "GCstarPluginFetcher::slotProcessExited() - "<< source() << ": no collection pointer" << endl; stop(); return; } Data::EntryVec entries = coll->entries(); for(Data::EntryVec::Iterator entry = entries.begin(); entry != entries.end(); ++entry) { TQString desc; switch(coll->type()) { case Data::Collection::Book: case Data::Collection::Bibtex: desc = entry->field(TQString::fromLatin1("author")) + TQChar('/') + entry->field(TQString::fromLatin1("publisher")); if(!entry->field(TQString::fromLatin1("cr_year")).isEmpty()) { desc += TQChar('/') + entry->field(TQString::fromLatin1("cr_year")); } else if(!entry->field(TQString::fromLatin1("pub_year")).isEmpty()){ desc += TQChar('/') + entry->field(TQString::fromLatin1("pub_year")); } break; case Data::Collection::Video: desc = entry->field(TQString::fromLatin1("studio")) + TQChar('/') + entry->field(TQString::fromLatin1("director")) + TQChar('/') + entry->field(TQString::fromLatin1("year")) + TQChar('/') + entry->field(TQString::fromLatin1("medium")); break; case Data::Collection::Album: desc = entry->field(TQString::fromLatin1("artist")) + TQChar('/') + entry->field(TQString::fromLatin1("label")) + TQChar('/') + entry->field(TQString::fromLatin1("year")); break; case Data::Collection::Game: desc = entry->field(TQString::fromLatin1("platform")); break; case Data::Collection::ComicBook: desc = entry->field(TQString::fromLatin1("publisher")) + TQChar('/') + entry->field(TQString::fromLatin1("pub_year")); break; case Data::Collection::BoardGame: desc = entry->field(TQString::fromLatin1("designer")) + TQChar('/') + entry->field(TQString::fromLatin1("publisher")) + TQChar('/') + entry->field(TQString::fromLatin1("year")); break; default: break; } SearchResult* r = new SearchResult(this, entry->title(), desc, entry->field(TQString::fromLatin1("isbn"))); m_entries.insert(r->uid, entry); emit signalResultFound(r); } stop(); // be sure to call this } Tellico::Data::EntryPtr GCstarPluginFetcher::fetchEntry(uint uid_) { return m_entries[uid_]; } void GCstarPluginFetcher::updateEntry(Data::EntryPtr entry_) { // ry searching for title and rely on Collection::sameEntry() to figure things out TQString t = entry_->field(TQString::fromLatin1("title")); if(!t.isEmpty()) { search(Fetch::Title, t); return; } myDebug() << "GCstarPluginFetcher::updateEntry() - insufficient info to search" << endl; emit signalDone(this); // always need to emit this if not continuing with the search } Tellico::Fetch::ConfigWidget* GCstarPluginFetcher::configWidget(TQWidget* parent_) const { return new GCstarPluginFetcher::ConfigWidget(parent_, this); } GCstarPluginFetcher::ConfigWidget::ConfigWidget(TQWidget* parent_, const GCstarPluginFetcher* fetcher_/*=0*/) : Fetch::ConfigWidget(parent_), m_needPluginList(true) { TQGridLayout* l = new TQGridLayout(optionsWidget(), 3, 4); l->setSpacing(4); l->setColStretch(1, 10); int row = -1; TQLabel* label = new TQLabel(i18n("Collection &type:"), optionsWidget()); l->addWidget(label, ++row, 0); m_collCombo = new GUI::CollectionTypeCombo(optionsWidget()); connect(m_collCombo, TQ_SIGNAL(activated(int)), TQ_SLOT(slotSetModified())); connect(m_collCombo, TQ_SIGNAL(activated(int)), TQ_SLOT(slotTypeChanged())); l->addMultiCellWidget(m_collCombo, row, row, 1, 3); TQString w = i18n("Set the collection type of the data returned from the plugin."); TQWhatsThis::add(label, w); TQWhatsThis::add(m_collCombo, w); label->setBuddy(m_collCombo); label = new TQLabel(i18n("&Plugin: "), optionsWidget()); l->addWidget(label, ++row, 0); m_pluginCombo = new GUI::ComboBox(optionsWidget()); connect(m_pluginCombo, TQ_SIGNAL(activated(int)), TQ_SLOT(slotSetModified())); connect(m_pluginCombo, TQ_SIGNAL(activated(int)), TQ_SLOT(slotPluginChanged())); l->addMultiCellWidget(m_pluginCombo, row, row, 1, 3); w = i18n("Select the GCstar plugin used for the data source."); TQWhatsThis::add(label, w); TQWhatsThis::add(m_pluginCombo, w); label->setBuddy(m_pluginCombo); label = new TQLabel(i18n("Author: "), optionsWidget()); l->addWidget(label, ++row, 0); m_authorLabel = new TQLabel(optionsWidget()); l->addWidget(m_authorLabel, row, 1); // label = new TQLabel(i18n("Language: "), optionsWidget()); // l->addWidget(label, row, 2); // m_langLabel = new TQLabel(optionsWidget()); // l->addWidget(m_langLabel, row, 3); if(fetcher_ && fetcher_->m_collType > -1) { m_collCombo->setCurrentType(fetcher_->m_collType); } else { m_collCombo->setCurrentType(Kernel::self()->collectionType()); } if(fetcher_) { m_originalPluginName = fetcher_->m_plugin; } TDEAcceleratorManager::manage(optionsWidget()); } GCstarPluginFetcher::ConfigWidget::~ConfigWidget() { } void GCstarPluginFetcher::ConfigWidget::saveConfig(TDEConfigGroup& config_) { config_.writeEntry("CollectionType", m_collCombo->currentType()); config_.writeEntry("Plugin", m_pluginCombo->currentText()); } TQString GCstarPluginFetcher::ConfigWidget::preferredName() const { return TQString::fromLatin1("GCstar - ") + m_pluginCombo->currentText(); } void GCstarPluginFetcher::ConfigWidget::slotTypeChanged() { int collType = m_collCombo->currentType(); m_pluginCombo->clear(); TQStringList pluginNames; GCstarPluginFetcher::PluginList list = GCstarPluginFetcher::plugins(collType); for(GCstarPluginFetcher::PluginList::ConstIterator it = list.begin(); it != list.end(); ++it) { pluginNames << (*it)[TQString::fromLatin1("name")].toString(); m_pluginCombo->insertItem(pluginNames.last(), *it); } slotPluginChanged(); emit signalName(preferredName()); } void GCstarPluginFetcher::ConfigWidget::slotPluginChanged() { PluginInfo info = m_pluginCombo->currentData().toMap(); m_authorLabel->setText(info[TQString::fromLatin1("author")].toString()); // m_langLabel->setText(info[TQString::fromLatin1("lang")].toString()); emit signalName(preferredName()); } void GCstarPluginFetcher::ConfigWidget::showEvent(TQShowEvent*) { if(m_needPluginList) { m_needPluginList = false; slotTypeChanged(); // update plugin combo box if(!m_originalPluginName.isEmpty()) { m_pluginCombo->setCurrentText(m_originalPluginName); slotPluginChanged(); } } } #include "gcstarpluginfetcher.moc"