File indexing completed on 2024-04-28 16:32:01
0001 /*************************************************************************** 0002 Copyright (C) 2003-2014 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 <config.h> 0026 #include "importdialog.h" 0027 #include "document.h" 0028 #include "tellico_debug.h" 0029 #include "collection.h" 0030 #include "progressmanager.h" 0031 #include "utils/guiproxy.h" 0032 0033 #include "translators/importer.h" 0034 #include "translators/tellicoimporter.h" 0035 #include "translators/bibteximporter.h" 0036 #include "translators/bibtexmlimporter.h" 0037 #include "translators/csvimporter.h" 0038 #include "translators/xsltimporter.h" 0039 #include "translators/audiofileimporter.h" 0040 #include "translators/alexandriaimporter.h" 0041 #include "translators/freedbimporter.h" 0042 #include "translators/risimporter.h" 0043 #include "translators/gcstarimporter.h" 0044 #include "translators/filelistingimporter.h" 0045 #include "translators/amcimporter.h" 0046 #include "translators/griffithimporter.h" 0047 #include "translators/pdfimporter.h" 0048 #include "translators/referencerimporter.h" 0049 #include "translators/deliciousimporter.h" 0050 #include "translators/goodreadsimporter.h" 0051 #include "translators/ciwimporter.h" 0052 #include "translators/vinoxmlimporter.h" 0053 #include "translators/boardgamegeekimporter.h" 0054 #include "translators/librarythingimporter.h" 0055 #include "translators/collectorzimporter.h" 0056 #include "utils/datafileregistry.h" 0057 0058 #include <KLocalizedString> 0059 #include <KStandardGuiItem> 0060 0061 #include <QGroupBox> 0062 #include <QButtonGroup> 0063 #include <QRadioButton> 0064 #include <QCheckBox> 0065 #include <QTimer> 0066 #include <QVBoxLayout> 0067 #include <QDialogButtonBox> 0068 #include <QPushButton> 0069 0070 using Tellico::ImportDialog; 0071 0072 ImportDialog::ImportDialog(Tellico::Import::Format format_, const QList<QUrl>& urls_, QWidget* parent_) 0073 : QDialog(parent_), 0074 m_importer(importer(format_, urls_)) { 0075 setModal(true); 0076 setWindowTitle(i18n("Import Options")); 0077 0078 QVBoxLayout* mainLayout = new QVBoxLayout(); 0079 setLayout(mainLayout); 0080 0081 QWidget* widget = new QWidget(this); 0082 mainLayout->addWidget(widget); 0083 QVBoxLayout* topLayout = new QVBoxLayout(widget); 0084 0085 QGroupBox* groupBox = new QGroupBox(i18n("Import Options"), widget); 0086 QVBoxLayout* vlay = new QVBoxLayout(groupBox); 0087 topLayout->addWidget(groupBox, 0); 0088 0089 m_radioReplace = new QRadioButton(i18n("&Replace current collection"), groupBox); 0090 m_radioReplace->setWhatsThis(i18n("Replace the current collection with the contents " 0091 "of the imported file.")); 0092 m_radioAppend = new QRadioButton(i18n("A&ppend to current collection"), groupBox); 0093 m_radioAppend->setWhatsThis(i18n("Append the contents of the imported file to the " 0094 "current collection. This is only possible when the " 0095 "collection types match.")); 0096 m_radioMerge = new QRadioButton(i18n("&Merge with current collection"), groupBox); 0097 m_radioMerge->setWhatsThis(i18n("Merge the contents of the imported file to the " 0098 "current collection. This is only possible when the " 0099 "collection types match. Entries must match exactly " 0100 "in order to be merged.")); 0101 if(m_importer->canImport(Data::Document::self()->collection()->type())) { 0102 // append by default? 0103 m_radioAppend->setChecked(true); 0104 } else { 0105 m_radioReplace->setChecked(true); 0106 m_radioAppend->setEnabled(false); 0107 m_radioMerge->setEnabled(false); 0108 } 0109 0110 vlay->addWidget(m_radioReplace); 0111 vlay->addWidget(m_radioAppend); 0112 vlay->addWidget(m_radioMerge); 0113 0114 m_buttonGroup = new QButtonGroup(widget); 0115 m_buttonGroup->addButton(m_radioReplace, Import::Replace); 0116 m_buttonGroup->addButton(m_radioAppend, Import::Append); 0117 m_buttonGroup->addButton(m_radioMerge, Import::Merge); 0118 0119 QWidget* w = m_importer->widget(widget); 0120 // m_importer->readOptions(KSharedConfig::openConfig()); 0121 if(w) { 0122 w->layout()->setMargin(0); 0123 topLayout->addWidget(w, 0); 0124 } 0125 0126 #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) 0127 connect(m_buttonGroup, static_cast<void(QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), 0128 m_importer, &Tellico::Import::Importer::slotActionChanged); 0129 #else 0130 connect(m_buttonGroup, &QButtonGroup::idClicked, 0131 m_importer, &Tellico::Import::Importer::slotActionChanged); 0132 #endif 0133 0134 topLayout->addStretch(); 0135 0136 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); 0137 mainLayout->addWidget(buttonBox); 0138 QPushButton* okButton = buttonBox->button(QDialogButtonBox::Ok); 0139 okButton->setDefault(true); 0140 okButton->setShortcut(Qt::CTRL | Qt::Key_Return); 0141 connect(okButton, &QPushButton::clicked, this, &ImportDialog::slotOk); 0142 connect(buttonBox, &QDialogButtonBox::accepted, this, &ImportDialog::accept); 0143 connect(buttonBox, &QDialogButtonBox::rejected, this, &ImportDialog::reject); 0144 0145 KGuiItem ok = KStandardGuiItem::ok(); 0146 ok.setText(i18n("&Import")); 0147 KGuiItem::assign(okButton, ok); 0148 0149 // want to grab default button action, too 0150 // since the importer might do something with widgets, don't just call it, do it after layout is done 0151 QTimer::singleShot(0, this, &ImportDialog::slotUpdateAction); 0152 } 0153 0154 ImportDialog::~ImportDialog() { 0155 delete m_importer; 0156 m_importer = nullptr; 0157 } 0158 0159 Tellico::Data::CollPtr ImportDialog::collection() { 0160 if(m_importer && !m_coll) { 0161 ProgressItem& item = ProgressManager::self()->newProgressItem(m_importer, m_importer->progressLabel(), true); 0162 connect(m_importer, &Import::Importer::signalTotalSteps, 0163 ProgressManager::self(), &ProgressManager::setTotalSteps); 0164 connect(m_importer, &Import::Importer::signalProgress, 0165 ProgressManager::self(), &ProgressManager::setProgress); 0166 connect(&item, &ProgressItem::signalCancelled, m_importer, &Import::Importer::slotCancel); 0167 ProgressItem::Done done(m_importer); 0168 m_coll = m_importer->collection(); 0169 } 0170 return m_coll; 0171 } 0172 0173 QString ImportDialog::statusMessage() const { 0174 return m_importer ? m_importer->statusMessage() : QString(); 0175 } 0176 0177 Tellico::Import::Action ImportDialog::action() const { 0178 if(m_radioReplace->isChecked()) { 0179 return Import::Replace; 0180 } else if(m_radioAppend->isChecked()) { 0181 return Import::Append; 0182 } else { 0183 return Import::Merge; 0184 } 0185 } 0186 0187 // static 0188 Tellico::Import::Importer* ImportDialog::importer(Tellico::Import::Format format_, const QList<QUrl>& urls_) { 0189 #define CHECK_SIZE if(urls_.size() > 1) myWarning() << "only importing first URL" 0190 QUrl firstURL = urls_.isEmpty() ? QUrl() : urls_[0]; 0191 Import::Importer* importer = nullptr; 0192 switch(format_) { 0193 case Import::TellicoXML: 0194 CHECK_SIZE; 0195 importer = new Import::TellicoImporter(firstURL); 0196 break; 0197 0198 case Import::Bibtex: 0199 importer = new Import::BibtexImporter(urls_); 0200 #ifndef ENABLE_BTPARSE 0201 myLog() << "Bibtex importing is not available due to lack of btparse library"; 0202 #endif 0203 break; 0204 0205 case Import::Bibtexml: 0206 CHECK_SIZE; 0207 importer = new Import::BibtexmlImporter(firstURL); 0208 break; 0209 0210 case Import::CSV: 0211 CHECK_SIZE; 0212 importer = new Import::CSVImporter(firstURL); 0213 break; 0214 0215 case Import::XSLT: 0216 CHECK_SIZE; 0217 importer = new Import::XSLTImporter(firstURL); 0218 break; 0219 0220 case Import::MODS: 0221 CHECK_SIZE; 0222 importer = new Import::XSLTImporter(firstURL); 0223 { 0224 QString xsltFile = DataFileRegistry::self()->locate(QStringLiteral("mods2tellico.xsl")); 0225 if(!xsltFile.isEmpty()) { 0226 QUrl u = QUrl::fromLocalFile(xsltFile); 0227 static_cast<Import::XSLTImporter*>(importer)->setXSLTURL(u); 0228 } else { 0229 myWarning() << "unable to find mods2tellico.xml!"; 0230 } 0231 } 0232 break; 0233 0234 case Import::AudioFile: 0235 CHECK_SIZE; 0236 importer = new Import::AudioFileImporter(firstURL); 0237 break; 0238 0239 case Import::Alexandria: 0240 CHECK_SIZE; 0241 importer = new Import::AlexandriaImporter(); 0242 break; 0243 0244 case Import::FreeDB: 0245 CHECK_SIZE; 0246 importer = new Import::FreeDBImporter(); 0247 break; 0248 0249 case Import::RIS: 0250 importer = new Import::RISImporter(urls_); 0251 break; 0252 0253 case Import::GCstar: 0254 CHECK_SIZE; 0255 importer = new Import::GCstarImporter(firstURL); 0256 break; 0257 0258 case Import::FileListing: 0259 CHECK_SIZE; 0260 importer = new Import::FileListingImporter(firstURL); 0261 break; 0262 0263 case Import::AMC: 0264 CHECK_SIZE; 0265 importer = new Import::AMCImporter(firstURL); 0266 break; 0267 0268 case Import::Griffith: 0269 CHECK_SIZE; 0270 importer = new Import::GriffithImporter(firstURL); 0271 break; 0272 0273 case Import::PDF: 0274 importer = new Import::PDFImporter(urls_); 0275 break; 0276 0277 case Import::Referencer: 0278 CHECK_SIZE; 0279 importer = new Import::ReferencerImporter(firstURL); 0280 break; 0281 0282 case Import::Delicious: 0283 CHECK_SIZE; 0284 importer = new Import::DeliciousImporter(firstURL); 0285 break; 0286 0287 case Import::Goodreads: 0288 CHECK_SIZE; 0289 importer = new Import::GoodreadsImporter(); 0290 break; 0291 0292 case Import::GRS1: 0293 myDebug() << "GRS1 not implemented"; 0294 break; 0295 0296 case Import::CIW: 0297 importer = new Import::CIWImporter(urls_); 0298 break; 0299 0300 case Import::VinoXML: 0301 CHECK_SIZE; 0302 importer = new Import::VinoXMLImporter(firstURL); 0303 break; 0304 0305 case Import::BoardGameGeek: 0306 CHECK_SIZE; 0307 importer = new Import::BoardGameGeekImporter(); 0308 break; 0309 0310 case Import::LibraryThing: 0311 CHECK_SIZE; 0312 importer = new Import::LibraryThingImporter(); 0313 break; 0314 0315 case Import::Collectorz: 0316 CHECK_SIZE; 0317 importer = new Import::CollectorzImporter(firstURL); 0318 break; 0319 } 0320 if(!importer) { 0321 myWarning() << "importer not created!"; 0322 return nullptr; 0323 } 0324 importer->setCurrentCollection(Data::Document::self()->collection()); 0325 return importer; 0326 #undef CHECK_SIZE 0327 } 0328 0329 //static 0330 Tellico::Import::Importer* ImportDialog::importerForText(Tellico::Import::Format format_, const QString& text_) { 0331 Import::Importer* importer = nullptr; 0332 switch(format_) { 0333 case Import::Bibtex: 0334 importer = new Import::BibtexImporter(text_); 0335 break; 0336 0337 default: 0338 break; 0339 } 0340 0341 if(!importer) { 0342 myWarning() << "importer not created!"; 0343 return nullptr; 0344 } 0345 importer->setCurrentCollection(Data::Document::self()->collection()); 0346 return importer; 0347 } 0348 0349 // static 0350 QString ImportDialog::fileFilter(Tellico::Import::Format format_) { 0351 QString text; 0352 switch(format_) { 0353 case Import::TellicoXML: 0354 text = i18n("Tellico Files") + QLatin1String(" (*.tc *.bc)") + QLatin1String(";;"); 0355 text += i18n("XML Files") + QLatin1String(" (*.xml)") + QLatin1String(";;"); 0356 break; 0357 0358 case Import::Bibtex: 0359 text = i18n("Bibtex Files") + QLatin1String(" (*.bib)") + QLatin1String(";;"); 0360 break; 0361 0362 case Import::CSV: 0363 text = i18n("CSV Files") + QLatin1String(" (*.csv)") + QLatin1String(";;"); 0364 break; 0365 0366 case Import::Bibtexml: 0367 case Import::XSLT: 0368 case Import::MODS: 0369 case Import::Delicious: 0370 case Import::Griffith: 0371 case Import::Collectorz: 0372 text = i18n("XML Files") + QLatin1String(" (*.xml)") + QLatin1String(";;"); 0373 break; 0374 0375 case Import::RIS: 0376 text = i18n("RIS Files") + QLatin1String(" (*.ris)") + QLatin1String(";;"); 0377 break; 0378 0379 case Import::GCstar: 0380 text = i18n("GCstar Data Files") + QLatin1String(" (*.gcs *.gcf)") + QLatin1String(";;"); 0381 break; 0382 0383 case Import::AMC: 0384 text = i18n("AMC Data Files") + QLatin1String(" (*.amc)") + QLatin1String(";;"); 0385 break; 0386 0387 case Import::PDF: 0388 text = i18n("PDF Files") + QLatin1String(" (*.pdf)") + QLatin1String(";;"); 0389 break; 0390 0391 case Import::Referencer: 0392 text = i18n("Referencer Files") + QLatin1String(" (*.reflib)") + QLatin1String(";;"); 0393 break; 0394 0395 case Import::CIW: 0396 text = i18n("CIW Files") + QLatin1String(" (*.ciw)") + QLatin1String(";;"); 0397 break; 0398 0399 case Import::VinoXML: 0400 text = i18n("VinoXML Data Files") + QLatin1String(" (*.vinoxml)") + QLatin1String(";;"); 0401 text += i18n("XML Files") + QLatin1String(" (*.xml)") + QLatin1String(";;"); 0402 break; 0403 0404 case Import::AudioFile: 0405 case Import::Alexandria: 0406 case Import::FreeDB: 0407 case Import::FileListing: 0408 case Import::GRS1: 0409 case Import::Goodreads: 0410 case Import::BoardGameGeek: 0411 case Import::LibraryThing: 0412 break; 0413 } 0414 0415 return text + i18n("All Files") + QLatin1String(" (*)"); 0416 } 0417 0418 // audio files are imported by directory 0419 // alexandria is a defined location, as is freedb 0420 // all others are files 0421 Tellico::Import::Target ImportDialog::importTarget(Tellico::Import::Format format_) { 0422 switch(format_) { 0423 case Import::AudioFile: 0424 case Import::FileListing: 0425 return Import::Dir; 0426 case Import::Alexandria: 0427 case Import::FreeDB: 0428 case Import::Goodreads: 0429 case Import::BoardGameGeek: 0430 case Import::LibraryThing: 0431 return Import::None; 0432 default: 0433 return Import::File; 0434 } 0435 } 0436 0437 QString ImportDialog::startDir(Tellico::Import::Format format_) { 0438 if(format_ == Import::GCstar) { 0439 QDir dir = QDir::home(); 0440 // able to cd if exists and readable 0441 if(dir.cd(QStringLiteral(".local/share/gcstar/"))) { 0442 return dir.absolutePath(); 0443 } 0444 } 0445 return QString(); 0446 } 0447 0448 void ImportDialog::slotOk() { 0449 // some importers, like the CSV importer, can validate their settings 0450 if(!m_importer || m_importer->validImport()) { 0451 accept(); 0452 } else { 0453 myLog() << "not a valid import"; 0454 } 0455 } 0456 0457 void ImportDialog::slotUpdateAction() { 0458 m_importer->slotActionChanged(m_buttonGroup->checkedId()); 0459 } 0460 0461 // static 0462 Tellico::Data::CollPtr ImportDialog::importURL(Tellico::Import::Format format_, const QUrl& url_) { 0463 Import::Importer* imp = importer(format_, QList<QUrl>() << url_); 0464 if(!imp) { 0465 return Data::CollPtr(); 0466 } 0467 0468 ProgressItem& item = ProgressManager::self()->newProgressItem(imp, imp->progressLabel(), true); 0469 connect(imp, &Import::Importer::signalTotalSteps, 0470 ProgressManager::self(), &ProgressManager::setTotalSteps); 0471 connect(imp, &Import::Importer::signalProgress, 0472 ProgressManager::self(), &ProgressManager::setProgress); 0473 connect(&item, &ProgressItem::signalCancelled, imp, &Import::Importer::slotCancel); 0474 ProgressItem::Done done(imp); 0475 0476 Data::CollPtr c = imp->collection(); 0477 if(!c && !imp->statusMessage().isEmpty()) { 0478 GUI::Proxy::sorry(imp->statusMessage()); 0479 } 0480 delete imp; 0481 return c; 0482 } 0483 0484 Tellico::Data::CollPtr ImportDialog::importText(Tellico::Import::Format format_, const QString& text_) { 0485 Import::Importer* imp = importerForText(format_, text_); 0486 if(!imp) { 0487 return Data::CollPtr(); 0488 } 0489 0490 // the Done() constructor crashes for some reason, so just don't use it 0491 // 5/18/19 -> uncomment the progress Done again 0492 ProgressItem& item = ProgressManager::self()->newProgressItem(imp, imp->progressLabel(), true); 0493 connect(imp, &Import::Importer::signalTotalSteps, 0494 ProgressManager::self(), &ProgressManager::setTotalSteps); 0495 connect(imp, &Import::Importer::signalProgress, 0496 ProgressManager::self(), &ProgressManager::setProgress); 0497 connect(&item, &ProgressItem::signalCancelled, imp, &Import::Importer::slotCancel); 0498 ProgressItem::Done done(imp); 0499 0500 Data::CollPtr c = imp->collection(); 0501 if(!c && !imp->statusMessage().isEmpty()) { 0502 GUI::Proxy::sorry(imp->statusMessage()); 0503 } 0504 delete imp; 0505 return c; 0506 }