diff options
Diffstat (limited to 'src/translators/bibtexexporter.cpp')
-rw-r--r-- | src/translators/bibtexexporter.cpp | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/src/translators/bibtexexporter.cpp b/src/translators/bibtexexporter.cpp new file mode 100644 index 0000000..2706ac8 --- /dev/null +++ b/src/translators/bibtexexporter.cpp @@ -0,0 +1,326 @@ +/*************************************************************************** + copyright : (C) 2003-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 "bibtexexporter.h" +#include "bibtexhandler.h" +#include "../document.h" +#include "../collections/bibtexcollection.h" +#include "../latin1literal.h" +#include "../filehandler.h" +#include "../stringset.h" +#include "../tellico_debug.h" + +#include <config.h> + +#include <klocale.h> +#include <kdebug.h> +#include <kconfig.h> +#include <kcombobox.h> + +#include <qregexp.h> +#include <qcheckbox.h> +#include <qlayout.h> +#include <qgroupbox.h> +#include <qwhatsthis.h> +#include <qlabel.h> +#include <qhbox.h> + +using Tellico::Export::BibtexExporter; + +BibtexExporter::BibtexExporter() : Tellico::Export::Exporter(), + m_expandMacros(false), + m_packageURL(true), + m_skipEmptyKeys(false), + m_widget(0) { +} + +QString BibtexExporter::formatString() const { + return i18n("Bibtex"); +} + +QString BibtexExporter::fileFilter() const { + return i18n("*.bib|Bibtex Files (*.bib)") + QChar('\n') + i18n("*|All Files"); +} + +bool BibtexExporter::exec() { + Data::CollPtr c = collection(); + if(!c || c->type() != Data::Collection::Bibtex) { + return false; + } + const Data::BibtexCollection* coll = static_cast<const Data::BibtexCollection*>(c.data()); + +// there are some special attributes +// the entry-type specifies the entry type - book, inproceedings, whatever + QString typeField; +// the key specifies the cite-key + QString keyField; +// the crossref bibtex field can reference another entry + QString crossRefField; + bool hasCrossRefs = false; + + const QString bibtex = QString::fromLatin1("bibtex"); +// keep a list of all the 'ordinary' fields to iterate through later + Data::FieldVec fields; + Data::FieldVec vec = coll->fields(); + for(Data::FieldVec::Iterator it = vec.begin(); it != vec.end(); ++it) { + QString bibtexField = it->property(bibtex); + if(bibtexField == Latin1Literal("entry-type")) { + typeField = it->name(); + } else if(bibtexField == Latin1Literal("key")) { + keyField = it->name(); + } else if(bibtexField == Latin1Literal("crossref")) { + fields.append(it); // still output crossref field + crossRefField = it->name(); + hasCrossRefs = true; + } else if(!bibtexField.isEmpty()) { + fields.append(it); + } + } + + if(typeField.isEmpty() || keyField.isEmpty()) { + kdWarning() << "BibtexExporter::exec() - the collection must have fields defining " + "the entry-type and the key of the entry" << endl; + return false; + } + if(fields.isEmpty()) { + kdWarning() << "BibtexExporter::exec() - no bibtex field mapping exists in the collection." << endl; + return false; + } + + QString text = QString::fromLatin1("@comment{Generated by Tellico ") + + QString::fromLatin1(VERSION) + + QString::fromLatin1("}\n\n"); + + if(!coll->preamble().isEmpty()) { + text += QString::fromLatin1("@preamble{") + coll->preamble() + QString::fromLatin1("}\n\n"); + } + + const QStringList macros = coll->macroList().keys(); + if(!m_expandMacros) { + QMap<QString, QString>::ConstIterator macroIt; + for(macroIt = coll->macroList().constBegin(); macroIt != coll->macroList().constEnd(); ++macroIt) { + if(!macroIt.data().isEmpty()) { + text += QString::fromLatin1("@string{") + + macroIt.key() + + QString::fromLatin1("=") + + BibtexHandler::exportText(macroIt.data(), macros) + + QString::fromLatin1("}\n\n"); + } + } + } + + // if anything is crossref'd, we have to do an initial scan through the + // whole collection first + StringSet crossRefKeys; + if(hasCrossRefs) { + for(Data::EntryVec::ConstIterator entryIt = entries().begin(); entryIt != entries().end(); ++entryIt) { + crossRefKeys.add(entryIt->field(crossRefField)); + } + } + + + StringSet usedKeys; + Data::ConstEntryVec crossRefs; + QString type, key, newKey, value; + for(Data::EntryVec::ConstIterator entryIt = entries().begin(); entryIt != entries().end(); ++entryIt) { + type = entryIt->field(typeField); + if(type.isEmpty()) { + kdWarning() << "BibtexExporter::text() - the entry for '" << entryIt->title() + << "' has no entry-type, skipping it!" << endl; + continue; + } + + key = entryIt->field(keyField); + if(key.isEmpty()) { + if(m_skipEmptyKeys) { + continue; + } + key = BibtexHandler::bibtexKey(entryIt.data()); + } else { + // check crossrefs, only counts for non-empty keys + // if this entry is crossref'd, add it to the list, and skip it + if(hasCrossRefs && crossRefKeys.has(key)) { + crossRefs.append(entryIt.data()); + continue; + } + } + + newKey = key; + char c = 'a'; + while(usedKeys.has(newKey)) { + // duplicate found! + newKey = key + c; + ++c; + } + key = newKey; + usedKeys.add(key); + + writeEntryText(text, fields, *entryIt, type, key); + } + + // now write out crossrefs + for(Data::ConstEntryVec::Iterator entryIt = crossRefs.begin(); entryIt != crossRefs.end(); ++entryIt) { + // no need to check type + + key = entryIt->field(keyField); + newKey = key; + char c = 'a'; + while(usedKeys.has(newKey)) { + // duplicate found! + newKey = key + c; + ++c; + } + key = newKey; + usedKeys.add(key); + + writeEntryText(text, fields, *entryIt, entryIt->field(typeField), key); + } + + return FileHandler::writeTextURL(url(), text, options() & ExportUTF8, options() & Export::ExportForce); +} + +QWidget* BibtexExporter::widget(QWidget* parent_, const char* name_/*=0*/) { + if(m_widget && m_widget->parent() == parent_) { + return m_widget; + } + + m_widget = new QWidget(parent_, name_); + QVBoxLayout* l = new QVBoxLayout(m_widget); + + QGroupBox* box = new QGroupBox(1, Qt::Horizontal, i18n("Bibtex Options"), m_widget); + l->addWidget(box); + + m_checkExpandMacros = new QCheckBox(i18n("Expand string macros"), box); + m_checkExpandMacros->setChecked(m_expandMacros); + QWhatsThis::add(m_checkExpandMacros, i18n("If checked, the string macros will be expanded and no " + "@string{} entries will be written.")); + + m_checkPackageURL = new QCheckBox(i18n("Use URL package"), box); + m_checkPackageURL->setChecked(m_packageURL); + QWhatsThis::add(m_checkPackageURL, i18n("If checked, any URL fields will be wrapped in a " + "\\url declaration.")); + + m_checkSkipEmpty = new QCheckBox(i18n("Skip entries with empty citation keys"), box); + m_checkSkipEmpty->setChecked(m_skipEmptyKeys); + QWhatsThis::add(m_checkSkipEmpty, i18n("If checked, any entries without a bibtex citation key " + "will be skipped.")); + + QHBox* hbox = new QHBox(box); + QLabel* l1 = new QLabel(i18n("Bibtex quotation style:") + ' ', hbox); // add a space for astheticss + m_cbBibtexStyle = new KComboBox(hbox); + m_cbBibtexStyle->insertItem(i18n("Braces")); + m_cbBibtexStyle->insertItem(i18n("Quotes")); + QString whats = i18n("<qt>The quotation style used when exporting bibtex. All field values will " + " be escaped with either braces or quotation marks.</qt>"); + QWhatsThis::add(l1, whats); + QWhatsThis::add(m_cbBibtexStyle, whats); + if(BibtexHandler::s_quoteStyle == BibtexHandler::BRACES) { + m_cbBibtexStyle->setCurrentItem(i18n("Braces")); + } else { + m_cbBibtexStyle->setCurrentItem(i18n("Quotes")); + } + + l->addStretch(1); + return m_widget; +} + +void BibtexExporter::readOptions(KConfig* config_) { + KConfigGroup group(config_, QString::fromLatin1("ExportOptions - %1").arg(formatString())); + m_expandMacros = group.readBoolEntry("Expand Macros", m_expandMacros); + m_packageURL = group.readBoolEntry("URL Package", m_packageURL); + m_skipEmptyKeys = group.readBoolEntry("Skip Empty Keys", m_skipEmptyKeys); + + if(group.readBoolEntry("Use Braces", true)) { + BibtexHandler::s_quoteStyle = BibtexHandler::BRACES; + } else { + BibtexHandler::s_quoteStyle = BibtexHandler::QUOTES; + } +} + +void BibtexExporter::saveOptions(KConfig* config_) { + KConfigGroup group(config_, QString::fromLatin1("ExportOptions - %1").arg(formatString())); + m_expandMacros = m_checkExpandMacros->isChecked(); + group.writeEntry("Expand Macros", m_expandMacros); + m_packageURL = m_checkPackageURL->isChecked(); + group.writeEntry("URL Package", m_packageURL); + m_skipEmptyKeys = m_checkSkipEmpty->isChecked(); + group.writeEntry("Skip Empty Keys", m_skipEmptyKeys); + + bool useBraces = m_cbBibtexStyle->currentText() == i18n("Braces"); + group.writeEntry("Use Braces", useBraces); + if(useBraces) { + BibtexHandler::s_quoteStyle = BibtexHandler::BRACES; + } else { + BibtexHandler::s_quoteStyle = BibtexHandler::QUOTES; + } +} + +void BibtexExporter::writeEntryText(QString& text_, const Data::FieldVec& fields_, const Data::Entry& entry_, + const QString& type_, const QString& key_) { + const QStringList macros = static_cast<const Data::BibtexCollection*>(Data::Document::self()->collection().data())->macroList().keys(); + const QString bibtex = QString::fromLatin1("bibtex"); + const QString bibtexSep = QString::fromLatin1("bibtex-separator"); + + text_ += '@' + type_ + '{' + key_; + + QString value; + Data::FieldVec::ConstIterator fIt, end = fields_.constEnd(); + bool format = options() & Export::ExportFormatted; + for(fIt = fields_.constBegin(); fIt != end; ++fIt) { + value = entry_.field(fIt->name(), format); + if(value.isEmpty()) { + continue; + } + + // If the entry is formatted as a name and allows multiple values + // insert "and" in between them (e.g. author and editor) + if(fIt->formatFlag() == Data::Field::FormatName + && fIt->flags() & Data::Field::AllowMultiple) { + value.replace(Data::Field::delimiter(), QString::fromLatin1(" and ")); + } else if(fIt->flags() & Data::Field::AllowMultiple) { + QString bibsep = fIt->property(bibtexSep); + if(!bibsep.isEmpty()) { + value.replace(Data::Field::delimiter(), bibsep); + } + } else if(fIt->type() == Data::Field::Para) { + // strip HTML from bibtex export + QRegExp stripHTML(QString::fromLatin1("<.*>"), true); + stripHTML.setMinimal(true); + value.remove(stripHTML); + } else if(fIt->property(bibtex) == Latin1Literal("pages")) { + QRegExp rx(QString::fromLatin1("(\\d)-(\\d)")); + for(int pos = rx.search(value); pos > -1; pos = rx.search(value, pos+2)) { + value.replace(pos, 3, rx.cap(1)+"--"+rx.cap(2)); + } + } + + if(m_packageURL && fIt->type() == Data::Field::URL) { + bool b = BibtexHandler::s_quoteStyle == BibtexHandler::BRACES; + value = (b ? QChar('{') : QChar('"')) + + QString::fromLatin1("\\url{") + BibtexHandler::exportText(value, macros) + QChar('}') + + (b ? QChar('}') : QChar('"')); + } else if(fIt->type() != Data::Field::Number) { + // numbers aren't escaped, nor will they have macros + // if m_expandMacros is true, then macros is empty, so this is ok even then + value = BibtexHandler::exportText(value, macros); + } + text_ += QString::fromLatin1(",\n ") + + fIt->property(bibtex) + + QString::fromLatin1(" = ") + + value; + } + text_ += QString::fromLatin1("\n}\n\n"); +} + +#include "bibtexexporter.moc" |