/*************************************************************************** copyright : (C) 2003-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 "alexandriaimporter.h" #include "../collections/bookcollection.h" #include "../entry.h" #include "../field.h" #include "../latin1literal.h" #include "../isbnvalidator.h" #include "../imagefactory.h" #include "../progressmanager.h" #include "../tellico_debug.h" #include #include #include #include #include #include using Tellico::Import::AlexandriaImporter; bool AlexandriaImporter::canImport(int type) const { return type == Data::Collection::Book; } Tellico::Data::CollPtr AlexandriaImporter::collection() { if(!m_widget || m_library->count() == 0) { return 0; } m_coll = new Data::BookCollection(true); TQDir dataDir = m_libraryDir; dataDir.cd(m_library->currentText()); dataDir.setFilter(TQDir::Files | TQDir::Readable | TQDir::NoSymLinks); const TQString title = TQString::tqfromLatin1("title"); const TQString author = TQString::tqfromLatin1("author"); const TQString year = TQString::tqfromLatin1("pub_year"); const TQString binding = TQString::tqfromLatin1("binding"); const TQString isbn = TQString::tqfromLatin1("isbn"); const TQString pub = TQString::tqfromLatin1("publisher"); const TQString rating = TQString::tqfromLatin1("rating"); const TQString cover = TQString::tqfromLatin1("cover"); const TQString comments = TQString::tqfromLatin1("comments"); // start with yaml files dataDir.setNameFilter(TQString::tqfromLatin1("*.yaml")); const TQStringList files = dataDir.entryList(); const uint numFiles = files.count(); const uint stepSize = TQMAX(s_stepSize, numFiles/100); const bool showProgress = options() & ImportProgress; ProgressItem& item = ProgressManager::self()->newProgressItem(this, progressLabel(), true); item.setTotalSteps(numFiles); connect(&item, TQT_SIGNAL(signalCancelled(ProgressItem*)), TQT_SLOT(slotCancel())); ProgressItem::Done done(this); TQStringList covers; covers << TQString::tqfromLatin1(".cover") << TQString::tqfromLatin1("_medium.jpg") << TQString::tqfromLatin1("_small.jpg"); TQTextStream ts; ts.setEncoding(TQTextStream::UnicodeUTF8); // YAML is always utf8? uint j = 0; for(TQStringList::ConstIterator it = files.begin(); !m_cancelled && it != files.end(); ++it, ++j) { TQFile file(dataDir.absFilePath(*it)); if(!file.open(IO_ReadOnly)) { continue; } Data::EntryPtr entry = new Data::Entry(m_coll); bool readNextLine = true; ts.unsetDevice(); ts.setDevice(TQT_TQIODEVICE(&file)); TQString line; while(!ts.atEnd()) { if(readNextLine) { line = ts.readLine(); } else { readNextLine = true; } // skip the line that starts with --- if(line.isEmpty() || line.startsWith(TQString::tqfromLatin1("---"))) { continue; } if(line.endsWith(TQChar('\\'))) { line.truncate(line.length()-1); // remove last character line += ts.readLine(); } cleanLine(line); TQString alexField = line.section(':', 0, 0); TQString alexValue = line.section(':', 1).stripWhiteSpace(); clean(alexValue); // Alexandria uses "n/a for empty values, and it is translated // only thing we can do is check for english value and continue if(alexValue == Latin1Literal("n/a")) { continue; } if(alexField == Latin1Literal("authors")) { TQStringList authors; line = ts.readLine(); TQRegExp begin(TQString::tqfromLatin1("^\\s*-\\s+")); while(!line.isNull() && line.find(begin) > -1) { line.remove(begin); authors += clean(line); line = ts.readLine(); } entry->setField(author, authors.join(TQString::tqfromLatin1("; "))); // the next line has already been read readNextLine = false; // Alexandria calls the edition the binding } else if(alexField == Latin1Literal("edition")) { // special case if it's "Hardcover" if(alexValue.lower() == Latin1Literal("hardcover")) { alexValue = i18n("Hardback"); } entry->setField(binding, alexValue); } else if(alexField == Latin1Literal("publishing_year")) { entry->setField(year, alexValue); } else if(alexField == Latin1Literal("isbn")) { const ISBNValidator val(0); val.fixup(alexValue); entry->setField(isbn, alexValue); // now find cover image KURL u; alexValue.remove('-'); for(TQStringList::Iterator ext = covers.begin(); ext != covers.end(); ++ext) { u.setPath(dataDir.absFilePath(alexValue + *ext)); if(!TQFile::exists(u.path())) { continue; } TQString id = ImageFactory::addImage(u, true); if(!id.isEmpty()) { entry->setField(cover, id); break; } } } else if(alexField == Latin1Literal("notes")) { entry->setField(comments, alexValue); // now try by name then title } else if(m_coll->fieldByName(alexField)) { entry->setField(alexField, alexValue); } else if(m_coll->fieldByTitle(alexField)) { entry->setField(m_coll->fieldByTitle(alexField), alexValue); } } m_coll->addEntries(entry); if(showProgress && j%stepSize == 0) { ProgressManager::self()->setProgress(this, j); kapp->processEvents(); } } return m_coll; } TQWidget* AlexandriaImporter::widget(TQWidget* parent_, const char* name_/*=0*/) { if(m_widget) { return m_widget; } m_libraryDir = TQDir::home(); m_libraryDir.setFilter(TQDir::Dirs | TQDir::Readable | TQDir::NoSymLinks); m_widget = new TQWidget(parent_, name_); TQVBoxLayout* l = new TQVBoxLayout(m_widget); TQGroupBox* box = new TQGroupBox(2, Qt::Horizontal, i18n("Alexandria Options"), m_widget); TQLabel* label = new TQLabel(i18n("&Library:"), box); m_library = new KComboBox(box); label->setBuddy(m_library); // .alexandria might not exist if(m_libraryDir.cd(TQString::tqfromLatin1(".alexandria"))) { TQStringList dirs = m_libraryDir.entryList(); dirs.remove(TQString::tqfromLatin1(".")); // why can't I tell TQDir not to include these? TQDir::Hidden doesn't work dirs.remove(TQString::tqfromLatin1("..")); m_library->insertStringList(dirs); } l->addWidget(box); l->addStretch(1); return m_widget; } TQString& AlexandriaImporter::cleanLine(TQString& str_) { static TQRegExp escRx(TQString::tqfromLatin1("\\\\x(\\w\\w)"), false); str_.remove(TQString::tqfromLatin1("\\r")); str_.replace(TQString::tqfromLatin1("\\n"), TQString::tqfromLatin1("\n")); str_.replace(TQString::tqfromLatin1("\\t"), TQString::tqfromLatin1("\t")); // YAML uses escape sequences like \xC3 int pos = escRx.search(str_); int origPos = pos; TQCString bytes; while(pos > -1) { bool ok; char c = escRx.cap(1).toInt(&ok, 16); if(ok) { bytes += c; } else { bytes = TQCString(); break; } pos = escRx.search(str_, pos+1); } if(!bytes.isEmpty()) { str_.replace(origPos, bytes.length()*4, TQString::fromUtf8(bytes.data())); } return str_; } TQString AlexandriaImporter::clean(TQString& str_) { const TQRegExp quote(TQString::tqfromLatin1("\\\\\"")); // equals \" if(str_.startsWith(TQChar('\'')) || str_.startsWith(TQChar('"'))) { str_.remove(0, 1); } if(str_.endsWith(TQChar('\'')) || str_.endsWith(TQChar('"'))) { str_.truncate(str_.length()-1); } // we ignore YAML tags, this is not actually a good parser, but will do for now str_.remove(TQRegExp(TQString::tqfromLatin1("^![^\\s]*\\s+"))); return str_.replace(quote, TQChar('"')); } void AlexandriaImporter::slotCancel() { m_cancelled = true; } #include "alexandriaimporter.moc"