|
|
|
/***************************************************************************
|
|
|
|
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 <kcombobox.h>
|
|
|
|
#include <kapplication.h>
|
|
|
|
#include <kstringhandler.h>
|
|
|
|
|
|
|
|
#include <tqlayout.h>
|
|
|
|
#include <tqlabel.h>
|
|
|
|
#include <tqgroupbox.h>
|
|
|
|
|
|
|
|
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::fromLatin1("title");
|
|
|
|
const TQString author = TQString::fromLatin1("author");
|
|
|
|
const TQString year = TQString::fromLatin1("pub_year");
|
|
|
|
const TQString binding = TQString::fromLatin1("binding");
|
|
|
|
const TQString isbn = TQString::fromLatin1("isbn");
|
|
|
|
const TQString pub = TQString::fromLatin1("publisher");
|
|
|
|
const TQString rating = TQString::fromLatin1("rating");
|
|
|
|
const TQString cover = TQString::fromLatin1("cover");
|
|
|
|
const TQString comments = TQString::fromLatin1("comments");
|
|
|
|
|
|
|
|
// start with yaml files
|
|
|
|
dataDir.setNameFilter(TQString::fromLatin1("*.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::fromLatin1(".cover")
|
|
|
|
<< TQString::fromLatin1("_medium.jpg")
|
|
|
|
<< TQString::fromLatin1("_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::fromLatin1("---"))) {
|
|
|
|
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::fromLatin1("^\\s*-\\s+"));
|
|
|
|
while(!line.isNull() && line.find(begin) > -1) {
|
|
|
|
line.remove(begin);
|
|
|
|
authors += clean(line);
|
|
|
|
line = ts.readLine();
|
|
|
|
}
|
|
|
|
entry->setField(author, authors.join(TQString::fromLatin1("; ")));
|
|
|
|
// 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::fromLatin1(".alexandria"))) {
|
|
|
|
TQStringList dirs = m_libraryDir.entryList();
|
|
|
|
dirs.remove(TQString::fromLatin1(".")); // why can't I tell TQDir not to include these? TQDir::Hidden doesn't work
|
|
|
|
dirs.remove(TQString::fromLatin1(".."));
|
|
|
|
m_library->insertStringList(dirs);
|
|
|
|
}
|
|
|
|
|
|
|
|
l->addWidget(box);
|
|
|
|
l->addStretch(1);
|
|
|
|
return m_widget;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString& AlexandriaImporter::cleanLine(TQString& str_) {
|
|
|
|
static TQRegExp escRx(TQString::fromLatin1("\\\\x(\\w\\w)"), false);
|
|
|
|
str_.remove(TQString::fromLatin1("\\r"));
|
|
|
|
str_.replace(TQString::fromLatin1("\\n"), TQString::fromLatin1("\n"));
|
|
|
|
str_.replace(TQString::fromLatin1("\\t"), TQString::fromLatin1("\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::fromLatin1("\\\\\"")); // 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::fromLatin1("^![^\\s]*\\s+")));
|
|
|
|
return str_.replace(quote, TQChar('"'));
|
|
|
|
}
|
|
|
|
|
|
|
|
void AlexandriaImporter::slotCancel() {
|
|
|
|
m_cancelled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "alexandriaimporter.moc"
|