File indexing completed on 2024-05-12 05:09:31
0001 /*************************************************************************** 0002 Copyright (C) 2005-2009 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 #include "execexternalfetcher.h" 0026 #include "fetchmanager.h" 0027 #include "../collection.h" 0028 #include "../entry.h" 0029 #include "../fieldformat.h" 0030 #include "../derivedvalue.h" 0031 #include "../tellico_debug.h" 0032 #include "../gui/combobox.h" 0033 #include "../gui/lineedit.h" 0034 #include "../gui/collectiontypecombo.h" 0035 #include "../utils/cursorsaver.h" 0036 #include "../newstuff/manager.h" 0037 #include "../translators/translators.h" 0038 #include "../translators/tellicoimporter.h" 0039 #include "../translators/bibteximporter.h" 0040 #include "../translators/xsltimporter.h" 0041 #include "../translators/risimporter.h" 0042 #include "../utils/datafileregistry.h" 0043 0044 #include <KLocalizedString> 0045 #include <KProcess> 0046 #include <KUrlRequester> 0047 #include <KAcceleratorManager> 0048 #include <KShell> 0049 #include <KConfigGroup> 0050 0051 #include <QLabel> 0052 #include <QGroupBox> 0053 #include <QGridLayout> 0054 #include <QStandardItemModel> 0055 0056 using Tellico::Fetch::ExecExternalFetcher; 0057 0058 QStringList ExecExternalFetcher::parseArguments(const QString& str_) { 0059 // matching escaped quotes is too hard... :( 0060 static const QRegularExpression quotes(QLatin1String("(['\"])(.*?)\\1")); 0061 static const QRegularExpression spaces(QLatin1String("\\s+?")); 0062 0063 QStringList args; 0064 QRegularExpressionMatch match; 0065 int pos = 0; 0066 for(int nextPos = str_.indexOf(quotes, pos, &match); nextPos > -1; pos = nextPos+1, nextPos = str_.indexOf(quotes, pos, &match)) { 0067 // a non-quotes arguments runs from pos to nextPos 0068 #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) 0069 args += str_.mid(pos, nextPos-pos).split(spaces, QString::SkipEmptyParts); 0070 #else 0071 args += str_.mid(pos, nextPos-pos).split(spaces, Qt::SkipEmptyParts); 0072 #endif 0073 // move nextpos marker to end of match 0074 nextPos += match.capturedLength(); 0075 args += match.captured(2); 0076 } 0077 // catch the end stuff 0078 #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) 0079 args += str_.mid(pos).split(spaces, QString::SkipEmptyParts); 0080 #else 0081 args += str_.mid(pos).split(spaces, Qt::SkipEmptyParts); 0082 #endif 0083 0084 return args; 0085 } 0086 0087 ExecExternalFetcher::ExecExternalFetcher(QObject* parent_) : Fetcher(parent_), 0088 m_started(false), m_collType(-1), m_formatType(-1), m_canUpdate(false), m_process(nullptr), m_deleteOnRemove(false) { 0089 } 0090 0091 ExecExternalFetcher::~ExecExternalFetcher() { 0092 if(m_process) { 0093 m_process->kill(); 0094 m_process->deleteLater(); 0095 } 0096 } 0097 0098 QString ExecExternalFetcher::source() const { 0099 return m_name; 0100 } 0101 0102 bool ExecExternalFetcher::canSearch(Fetch::FetchKey k) const { 0103 return m_args.contains(k) || (m_canUpdate && k == ExecUpdate); 0104 } 0105 0106 bool ExecExternalFetcher::canFetch(int type_) const { 0107 return m_collType == -1 ? false : m_collType == type_; 0108 } 0109 0110 void ExecExternalFetcher::readConfigHook(const KConfigGroup& config_) { 0111 QString s = config_.readPathEntry("ExecPath", QString()); 0112 if(!s.isEmpty()) { 0113 m_path = s; 0114 } 0115 QList<int> argKeys; 0116 if(config_.hasKey("ArgumentKeys")) { 0117 argKeys = config_.readEntry("ArgumentKeys", argKeys); 0118 } else { 0119 myDebug() << "appending default keyword argument"; 0120 argKeys.append(Keyword); 0121 } 0122 QStringList args = config_.readEntry("Arguments", QStringList()); 0123 if(argKeys.count() != args.count()) { 0124 myWarning() << "unequal number of arguments and keys"; 0125 } 0126 int n = qMin(argKeys.count(), args.count()); 0127 for(int i = 0; i < n; ++i) { 0128 m_args.insert(static_cast<FetchKey>(argKeys[i]), args[i]); 0129 } 0130 if(config_.hasKey("UpdateArgs")) { 0131 m_canUpdate = true; 0132 m_updateArgs = config_.readEntry("UpdateArgs"); 0133 } else { 0134 m_canUpdate = false; 0135 } 0136 m_collType = config_.readEntry("CollectionType", -1); 0137 m_formatType = config_.readEntry("FormatType", -1); 0138 m_deleteOnRemove = config_.readEntry("DeleteOnRemove", false); 0139 m_newStuffName = config_.readEntry("NewStuffName"); 0140 } 0141 0142 void ExecExternalFetcher::search() { 0143 m_started = true; 0144 0145 if(request().key() != ExecUpdate && !m_args.contains(request().key())) { 0146 myDebug() << "stopping: not an update and no matching argument for search key"; 0147 stop(); 0148 return; 0149 } 0150 0151 if(request().key() == ExecUpdate) { 0152 // because the rowDelimiterString() is used below 0153 QStringList args = FieldFormat::splitTable(request().value()); 0154 startSearch(args); 0155 return; 0156 } 0157 0158 // should KShell::quoteArg() be used? 0159 // %1 gets replaced by the search value, but since the arguments are going to be split 0160 // the search value needs to be enclosed in quotation marks 0161 // but first check to make sure the user didn't do that already 0162 // AND the "%1" wasn't used in the settings 0163 QString value = request().value(); 0164 if(request().key() == ISBN) { 0165 value.remove(QLatin1Char('-')); // remove hyphens from isbn values 0166 // shouldn't hurt and might keep from confusing stupid search sources 0167 } 0168 bool hasQuotes = value.startsWith(QLatin1Char('"')) && value.endsWith(QLatin1Char('"')); 0169 if(!hasQuotes) { 0170 hasQuotes = value.startsWith(QLatin1Char('\'')) && value.endsWith(QLatin1Char('\'')); 0171 } 0172 if(!hasQuotes) { 0173 value = QLatin1Char('"') + value + QLatin1Char('"'); 0174 } 0175 QString args = m_args.value(request().key()); 0176 static const QRegularExpression rx(QLatin1String("(['\"])%1\\1")); 0177 args.replace(rx, QStringLiteral("%1")); 0178 startSearch(parseArguments(args.arg(value))); // replace %1 with search value 0179 } 0180 0181 void ExecExternalFetcher::startSearch(const QStringList& args_) { 0182 if(m_path.isEmpty()) { 0183 Q_ASSERT(!m_path.isEmpty()); 0184 stop(); 0185 return; 0186 } 0187 0188 m_process = new KProcess(); 0189 connect(m_process, &QProcess::readyReadStandardOutput, this, &ExecExternalFetcher::slotData); 0190 connect(m_process, &QProcess::readyReadStandardError, this, &ExecExternalFetcher::slotError); 0191 void (QProcess::* finished)(int, QProcess::ExitStatus) = &QProcess::finished; 0192 connect(m_process, finished, this, &ExecExternalFetcher::slotProcessExited); 0193 m_process->setOutputChannelMode(KProcess::SeparateChannels); 0194 m_process->setProgram(m_path, args_); 0195 if(m_process && m_process->execute() < 0) { 0196 myDebug() << "process failed to start"; 0197 stop(); 0198 } 0199 } 0200 0201 void ExecExternalFetcher::stop() { 0202 if(!m_started) { 0203 return; 0204 } 0205 if(m_process) { 0206 m_process->kill(); 0207 m_process->deleteLater(); 0208 m_process = nullptr; 0209 } 0210 m_data.clear(); 0211 m_started = false; 0212 m_errors.clear(); 0213 emit signalDone(this); 0214 } 0215 0216 void ExecExternalFetcher::slotData() { 0217 m_data.append(m_process->readAllStandardOutput()); 0218 } 0219 0220 void ExecExternalFetcher::slotError() { 0221 GUI::CursorSaver cs(Qt::ArrowCursor); 0222 QString msg = QString::fromLocal8Bit(m_process->readAllStandardError()); 0223 msg.prepend(source() + QLatin1String(": ")); 0224 if(msg.endsWith(QChar::fromLatin1('\n'))) { 0225 msg.truncate(msg.length()-1); 0226 } 0227 myDebug() << msg; 0228 m_errors << msg; 0229 } 0230 0231 void ExecExternalFetcher::slotProcessExited() { 0232 // DEBUG_LINE; 0233 if(m_process->exitStatus() != QProcess::NormalExit || m_process->exitCode() != 0) { 0234 myDebug() << source() << ": process did not exit successfully"; 0235 if(!m_errors.isEmpty()) { 0236 message(m_errors.join(QChar::fromLatin1('\n')), MessageHandler::Error); 0237 } 0238 stop(); 0239 return; 0240 } 0241 if(!m_errors.isEmpty()) { 0242 message(m_errors.join(QChar::fromLatin1('\n')), MessageHandler::Warning); 0243 } 0244 0245 if(m_data.isEmpty()) { 0246 myDebug() << source() << ": no data"; 0247 stop(); 0248 return; 0249 } 0250 0251 const QString text = QString::fromUtf8(m_data.constData(), m_data.size()); 0252 #if 0 0253 myWarning() << "Remove debug from ExecExternalFetcher.cpp"; 0254 QFile f(QStringLiteral("/tmp/test-exec.txt")); 0255 if(f.open(QIODevice::WriteOnly)) { 0256 QTextStream t(&f); 0257 t.setCodec("UTF-8"); 0258 t << m_data; 0259 } 0260 f.close(); 0261 #endif 0262 Import::Format format = static_cast<Import::Format>(m_formatType > -1 ? m_formatType : Import::TellicoXML); 0263 Import::Importer* imp = nullptr; 0264 // only 4 formats re supported here 0265 switch(format) { 0266 case Import::TellicoXML: 0267 imp = new Import::TellicoImporter(text); 0268 break; 0269 0270 case Import::Bibtex: 0271 imp = new Import::BibtexImporter(text); 0272 break; 0273 0274 case Import::MODS: 0275 imp = new Import::XSLTImporter(text); 0276 { 0277 QString xsltFile = DataFileRegistry::self()->locate(QStringLiteral("mods2tellico.xsl")); 0278 if(!xsltFile.isEmpty()) { 0279 QUrl u = QUrl::fromLocalFile(xsltFile); 0280 static_cast<Import::XSLTImporter*>(imp)->setXSLTURL(u); 0281 } else { 0282 myWarning() << "unable to find mods2tellico.xml!"; 0283 delete imp; 0284 imp = nullptr; 0285 } 0286 } 0287 break; 0288 0289 case Import::RIS: 0290 imp = new Import::RISImporter(text); 0291 break; 0292 0293 default: 0294 break; 0295 } 0296 if(!imp) { 0297 stop(); 0298 return; 0299 } 0300 0301 Data::CollPtr coll = imp->collection(); 0302 if(!coll) { 0303 if(!imp->statusMessage().isEmpty()) { 0304 message(imp->statusMessage(), MessageHandler::Status); 0305 } 0306 myDebug() << source() << ": no collection pointer"; 0307 delete imp; 0308 stop(); 0309 return; 0310 } 0311 0312 delete imp; 0313 if(coll->entryCount() == 0) { 0314 // myDebug() << "no results"; 0315 stop(); 0316 return; 0317 } 0318 0319 Data::EntryList entries = coll->entries(); 0320 foreach(Data::EntryPtr entry, entries) { 0321 FetchResult* r = new FetchResult(this, entry); 0322 m_entries.insert(r->uid, entry); 0323 emit signalResultFound(r); 0324 } 0325 stop(); // be sure to call this 0326 } 0327 0328 Tellico::Data::EntryPtr ExecExternalFetcher::fetchEntryHook(uint uid_) { 0329 return m_entries[uid_]; 0330 } 0331 0332 Tellico::Fetch::FetchRequest ExecExternalFetcher::updateRequest(Data::EntryPtr entry_) { 0333 if(!m_canUpdate) { 0334 return FetchRequest(); 0335 } 0336 0337 QStringList args = parseArguments(m_updateArgs); 0338 for(QStringList::Iterator it = args.begin(); it != args.end(); ++it) { 0339 Data::DerivedValue dv(*it); 0340 *it = dv.value(entry_, false); 0341 } 0342 return FetchRequest(ExecUpdate, args.join(FieldFormat::rowDelimiterString())); 0343 } 0344 0345 Tellico::Fetch::ConfigWidget* ExecExternalFetcher::configWidget(QWidget* parent_) const { 0346 return new ExecExternalFetcher::ConfigWidget(parent_, this); 0347 } 0348 0349 QString ExecExternalFetcher::defaultName() { 0350 return i18n("External Application"); 0351 } 0352 0353 QString ExecExternalFetcher::defaultIcon() { 0354 return QStringLiteral("application-x-executable"); 0355 } 0356 0357 ExecExternalFetcher::ConfigWidget::ConfigWidget(QWidget* parent_, const ExecExternalFetcher* fetcher_/*=0*/) 0358 : Fetch::ConfigWidget(parent_), m_deleteOnRemove(false) { 0359 QGridLayout* l = new QGridLayout(optionsWidget()); 0360 l->setSpacing(4); 0361 l->setColumnStretch(1, 10); 0362 0363 int row = -1; 0364 0365 QLabel* label = new QLabel(i18n("Collection &type:"), optionsWidget()); 0366 l->addWidget(label, ++row, 0); 0367 m_collCombo = new GUI::CollectionTypeCombo(optionsWidget()); 0368 void (GUI::ComboBox::* activatedInt)(int) = &GUI::ComboBox::activated; 0369 connect(m_collCombo, activatedInt, this, &ConfigWidget::slotSetModified); 0370 l->addWidget(m_collCombo, row, 1); 0371 QString w = i18n("Set the collection type of the data returned from the external application."); 0372 label->setWhatsThis(w); 0373 m_collCombo->setWhatsThis(w); 0374 label->setBuddy(m_collCombo); 0375 0376 label = new QLabel(i18n("&Result type: "), optionsWidget()); 0377 l->addWidget(label, ++row, 0); 0378 m_formatCombo = new GUI::ComboBox(optionsWidget()); 0379 m_formatCombo->addItem(QStringLiteral("Tellico"), Import::TellicoXML); 0380 m_formatCombo->addItem(QStringLiteral("Bibtex"), Import::Bibtex); 0381 m_formatCombo->addItem(QStringLiteral("MODS"), Import::MODS); 0382 m_formatCombo->addItem(QStringLiteral("RIS"), Import::RIS); 0383 connect(m_formatCombo, activatedInt, this, &ExecExternalFetcher::ConfigWidget::slotSetModified); 0384 l->addWidget(m_formatCombo, row, 1); 0385 w = i18n("Set the result type of the data returned from the external application."); 0386 label->setWhatsThis(w); 0387 m_formatCombo->setWhatsThis(w); 0388 label->setBuddy(m_formatCombo); 0389 #ifndef ENABLE_BTPARSE 0390 // disable the option for bibtex 0391 auto formatModel = qobject_cast<const QStandardItemModel*>(m_formatCombo->model()); 0392 auto matchList = formatModel->match(formatModel->index(0, 0), Qt::UserRole, Import::Bibtex); 0393 if(!matchList.isEmpty()) { 0394 auto item = formatModel->itemFromIndex(matchList.front()); 0395 item->setEnabled(false); 0396 } 0397 #endif 0398 0399 label = new QLabel(i18n("Application &path: "), optionsWidget()); 0400 l->addWidget(label, ++row, 0); 0401 m_pathEdit = new KUrlRequester(optionsWidget()); 0402 connect(m_pathEdit, &KUrlRequester::textChanged, this, &ConfigWidget::slotSetModified); 0403 l->addWidget(m_pathEdit, row, 1); 0404 w = i18n("Set the path of the application to run that should output a valid Tellico data file."); 0405 label->setWhatsThis(w); 0406 m_pathEdit->setWhatsThis(w); 0407 label->setBuddy(m_pathEdit); 0408 0409 w = i18n("Select the search keys supported by the data source."); 0410 // in this string, the %1 is not a placeholder, it's an example 0411 QString w2 = i18n("Add any arguments that may be needed. <b>%1</b> will be replaced by the search term."); // krazy:exclude=i18ncheckarg 0412 QGroupBox* gbox = new QGroupBox(i18n("Arguments"), optionsWidget()); 0413 ++row; 0414 l->addWidget(gbox, row, 0, 1, 2); 0415 QGridLayout* gridLayout = new QGridLayout(gbox); 0416 gridLayout->setSpacing(2); 0417 row = -1; 0418 const Fetch::KeyMap keyMap = Fetch::Manager::self()->keyMap(); 0419 for(Fetch::KeyMap::ConstIterator it = keyMap.begin(); it != keyMap.end(); ++it) { 0420 FetchKey key = it.key(); 0421 if(key == Raw) { 0422 continue; 0423 } 0424 QCheckBox* cb = new QCheckBox(it.value(), gbox); 0425 gridLayout->addWidget(cb, ++row, 0); 0426 m_cbDict.insert(key, cb); 0427 GUI::LineEdit* le = new GUI::LineEdit(gbox); 0428 le->setPlaceholderText(QStringLiteral("%1")); // for example 0429 le->completionObject()->addItem(QStringLiteral("%1")); 0430 gridLayout->addWidget(le, row, 1); 0431 m_leDict.insert(key, le); 0432 if(fetcher_ && fetcher_->m_args.contains(key)) { 0433 cb->setChecked(true); 0434 le->setEnabled(true); 0435 le->setText(fetcher_->m_args.value(key)); 0436 } else { 0437 cb->setChecked(false); 0438 le->setEnabled(false); 0439 } 0440 connect(cb, &QAbstractButton::toggled, le, &QWidget::setEnabled); 0441 cb->setWhatsThis(w); 0442 le->setWhatsThis(w2); 0443 } 0444 m_cbUpdate = new QCheckBox(i18n("Update"), gbox); 0445 gridLayout->addWidget(m_cbUpdate, ++row, 0); 0446 m_leUpdate = new GUI::LineEdit(gbox); 0447 m_leUpdate->setPlaceholderText(QStringLiteral("%{title}")); // for example 0448 m_leUpdate->completionObject()->addItem(QStringLiteral("%{title}")); 0449 m_leUpdate->completionObject()->addItem(QStringLiteral("%{isbn}")); 0450 gridLayout->addWidget(m_leUpdate, row, 1); 0451 /* TRANSLATORS: Do not translate %{author}. */ 0452 w2 = i18n("<p>Enter the arguments which should be used to search for available updates to an entry.</p><p>" 0453 "The format is the same as for fields with derived values, where field names " 0454 "are contained inside braces, such as <i>%{author}</i>. See the documentation for details.</p>"); 0455 m_cbUpdate->setWhatsThis(w); 0456 m_leUpdate->setWhatsThis(w2); 0457 if(fetcher_ && fetcher_->m_canUpdate) { 0458 m_cbUpdate->setChecked(true); 0459 m_leUpdate->setEnabled(true); 0460 m_leUpdate->setText(fetcher_->m_updateArgs); 0461 } else { 0462 m_cbUpdate->setChecked(false); 0463 m_leUpdate->setEnabled(false); 0464 } 0465 connect(m_cbUpdate, &QAbstractButton::toggled, m_leUpdate, &QWidget::setEnabled); 0466 0467 l->setRowStretch(++row, 1); 0468 0469 if(fetcher_) { 0470 m_pathEdit->setUrl(QUrl::fromLocalFile(fetcher_->m_path)); 0471 m_newStuffName = fetcher_->m_newStuffName; 0472 } 0473 if(fetcher_ && fetcher_->m_collType > -1) { 0474 m_collCombo->setCurrentType(fetcher_->m_collType); 0475 } else { 0476 m_collCombo->setCurrentType(Data::Collection::Book); 0477 } 0478 if(fetcher_ && fetcher_->m_formatType > -1) { 0479 m_formatCombo->setCurrentData(fetcher_->m_formatType); 0480 } else { 0481 m_formatCombo->setCurrentData(Import::TellicoXML); 0482 } 0483 m_deleteOnRemove = fetcher_ && fetcher_->m_deleteOnRemove; 0484 KAcceleratorManager::manage(optionsWidget()); 0485 } 0486 0487 ExecExternalFetcher::ConfigWidget::~ConfigWidget() { 0488 } 0489 0490 void ExecExternalFetcher::ConfigWidget::readConfig(const KConfigGroup& config_) { 0491 m_pathEdit->setUrl(QUrl::fromLocalFile(config_.readPathEntry("ExecPath", QString()))); 0492 QList<int> argKeys = config_.readEntry("ArgumentKeys", QList<int>()); 0493 QStringList argValues = config_.readEntry("Arguments", QStringList()); 0494 if(argKeys.count() != argValues.count()) { 0495 myWarning() << "unequal number of arguments and keys"; 0496 } 0497 int n = qMin(argKeys.count(), argValues.count()); 0498 QMap<FetchKey, QString> args; 0499 for(int i = 0; i < n; ++i) { 0500 args[static_cast<FetchKey>(argKeys[i])] = argValues[i]; 0501 } 0502 for(QList<int>::Iterator it = argKeys.begin(); it != argKeys.end(); ++it) { 0503 if(*it == Raw) { 0504 continue; 0505 } 0506 FetchKey key = static_cast<FetchKey>(*it); 0507 QCheckBox* cb = m_cbDict[key]; 0508 QLineEdit* le = m_leDict[key]; 0509 if(cb && le) { 0510 if(args.contains(key)) { 0511 cb->setChecked(true); 0512 le->setEnabled(true); 0513 le->setText(args[key]); 0514 } else { 0515 cb->setChecked(false); 0516 le->setEnabled(false); 0517 le->clear(); 0518 } 0519 } 0520 } 0521 0522 if(config_.hasKey("UpdateArgs")) { 0523 m_cbUpdate->setChecked(true); 0524 m_leUpdate->setEnabled(true); 0525 m_leUpdate->setText(config_.readEntry("UpdateArgs")); 0526 } else { 0527 m_cbUpdate->setChecked(false); 0528 m_leUpdate->setEnabled(false); 0529 m_leUpdate->clear(); 0530 } 0531 0532 int collType = config_.readEntry("CollectionType", -1); 0533 m_collCombo->setCurrentType(collType); 0534 0535 int formatType = config_.readEntry("FormatType", -1); 0536 m_formatCombo->setCurrentData(static_cast<Import::Format>(formatType)); 0537 m_deleteOnRemove = config_.readEntry("DeleteOnRemove", false); 0538 m_name = config_.readEntry("Name"); 0539 m_newStuffName = config_.readEntry("NewStuffName"); 0540 } 0541 0542 void ExecExternalFetcher::ConfigWidget::saveConfigHook(KConfigGroup& config_) { 0543 QUrl u = m_pathEdit->url(); 0544 if(!u.isEmpty()) { 0545 config_.writePathEntry("ExecPath", u.path()); 0546 } 0547 QList<int> keys; 0548 QStringList args; 0549 QHash<int, QCheckBox*>::const_iterator it = m_cbDict.constBegin(); 0550 for( ; it != m_cbDict.constEnd(); ++it) { 0551 if(it.value()->isChecked()) { 0552 keys << it.key(); 0553 args << m_leDict[it.key()]->text(); 0554 } 0555 } 0556 config_.writeEntry("ArgumentKeys", keys); 0557 config_.writeEntry("Arguments", args); 0558 0559 if(m_cbUpdate->isChecked()) { 0560 config_.writeEntry("UpdateArgs", m_leUpdate->text()); 0561 } else { 0562 config_.deleteEntry("UpdateArgs"); 0563 } 0564 0565 config_.writeEntry("CollectionType", m_collCombo->currentType()); 0566 config_.writeEntry("FormatType", m_formatCombo->currentData().toInt()); 0567 config_.writeEntry("DeleteOnRemove", m_deleteOnRemove); 0568 if(!m_newStuffName.isEmpty()) { 0569 config_.writeEntry("NewStuffName", m_newStuffName); 0570 } 0571 } 0572 0573 void ExecExternalFetcher::ConfigWidget::removed() { 0574 if(!m_deleteOnRemove) { 0575 return; 0576 } 0577 if(!m_newStuffName.isEmpty()) { 0578 NewStuff::Manager::self()->removeScript(m_newStuffName); 0579 } 0580 } 0581 0582 QString ExecExternalFetcher::ConfigWidget::preferredName() const { 0583 return m_name.isEmpty() ? ExecExternalFetcher::defaultName() : m_name; 0584 }