summaryrefslogtreecommitdiffstats
path: root/src/translators/bibtexexporter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/translators/bibtexexporter.cpp')
-rw-r--r--src/translators/bibtexexporter.cpp326
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
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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"