You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tellico/src/translators/bibtexexporter.cpp

327 lines
12 KiB

/***************************************************************************
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 "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"