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 <tqregexp.h>
#include <tqcheckbox.h>
#include <tqlayout.h>
#include <tqgroupbox.h>
#include <tqwhatsthis.h>
#include <tqlabel.h>
#include <tqhbox.h>
using Tellico::Export::BibtexExporter;
BibtexExporter::BibtexExporter() : Tellico::Export::Exporter(),
m_expandMacros(false),
m_packageURL(true),
m_skipEmptyKeys(false),
m_widget(0) {
}
TQString BibtexExporter::formatString() const {
return i18n("Bibtex");
}
TQString BibtexExporter::fileFilter() const {
return i18n("*.bib|Bibtex Files (*.bib)") + TQChar('\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
TQString typeField;
// the key specifies the cite-key
TQString keyField;
// the crossref bibtex field can reference another entry
TQString crossRefField;
bool hasCrossRefs = false;
const TQString bibtex = TQString::tqfromLatin1("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) {
TQString 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;
}
TQString text = TQString::tqfromLatin1("@comment{Generated by Tellico ")
+ TQString::tqfromLatin1(VERSION)
+ TQString::tqfromLatin1("}\n\n");
if(!coll->preamble().isEmpty()) {
text += TQString::tqfromLatin1("@preamble{") + coll->preamble() + TQString::tqfromLatin1("}\n\n");
}
const TQStringList macros = coll->macroList().keys();
if(!m_expandMacros) {
TQMap<TQString, TQString>::ConstIterator macroIt;
for(macroIt = coll->macroList().constBegin(); macroIt != coll->macroList().constEnd(); ++macroIt) {
if(!macroIt.data().isEmpty()) {
text += TQString::tqfromLatin1("@string{")
+ macroIt.key()
+ TQString::tqfromLatin1("=")
+ BibtexHandler::exportText(macroIt.data(), macros)
+ TQString::tqfromLatin1("}\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;
TQString 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);
}
TQWidget* BibtexExporter::widget(TQWidget* parent_, const char* name_/*=0*/) {
if(m_widget && TQT_BASE_OBJECT(m_widget->tqparent()) == TQT_BASE_OBJECT(parent_)) {
return m_widget;
}
m_widget = new TQWidget(parent_, name_);
TQVBoxLayout* l = new TQVBoxLayout(m_widget);
TQGroupBox* box = new TQGroupBox(1, Qt::Horizontal, i18n("Bibtex Options"), m_widget);
l->addWidget(box);
m_checkExpandMacros = new TQCheckBox(i18n("Expand string macros"), box);
m_checkExpandMacros->setChecked(m_expandMacros);
TQWhatsThis::add(m_checkExpandMacros, i18n("If checked, the string macros will be expanded and no "
"@string{} entries will be written."));
m_checkPackageURL = new TQCheckBox(i18n("Use URL package"), box);
m_checkPackageURL->setChecked(m_packageURL);
TQWhatsThis::add(m_checkPackageURL, i18n("If checked, any URL fields will be wrapped in a "
"\\url declaration."));
m_checkSkipEmpty = new TQCheckBox(i18n("Skip entries with empty citation keys"), box);
m_checkSkipEmpty->setChecked(m_skipEmptyKeys);
TQWhatsThis::add(m_checkSkipEmpty, i18n("If checked, any entries without a bibtex citation key "
"will be skipped."));
TQHBox* hbox = new TQHBox(box);
TQLabel* l1 = new TQLabel(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"));
TQString whats = i18n("<qt>The quotation style used when exporting bibtex. All field values will "
" be escaped with either braces or quotation marks.</qt>");
TQWhatsThis::add(l1, whats);
TQWhatsThis::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_, TQString::tqfromLatin1("ExportOptions - %1").tqarg(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::TQUOTES;
}
}
void BibtexExporter::saveOptions(KConfig* config_) {
KConfigGroup group(config_, TQString::tqfromLatin1("ExportOptions - %1").tqarg(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::TQUOTES;
}
}
void BibtexExporter::writeEntryText(TQString& text_, const Data::FieldVec& fields_, const Data::Entry& entry_,
const TQString& type_, const TQString& key_) {
const TQStringList macros = static_cast<const Data::BibtexCollection*>(Data::Document::self()->collection().data())->macroList().keys();
const TQString bibtex = TQString::tqfromLatin1("bibtex");
const TQString bibtexSep = TQString::tqfromLatin1("bibtex-separator");
text_ += '@' + type_ + '{' + key_;
TQString 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(), TQString::tqfromLatin1(" and "));
} else if(fIt->flags() & Data::Field::AllowMultiple) {
TQString 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
TQRegExp stripHTML(TQString::tqfromLatin1("<.*>"), true);
stripHTML.setMinimal(true);
value.remove(stripHTML);
} else if(fIt->property(bibtex) == Latin1Literal("pages")) {
TQRegExp rx(TQString::tqfromLatin1("(\\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 ? TQChar('{') : TQChar('"'))
+ TQString::tqfromLatin1("\\url{") + BibtexHandler::exportText(value, macros) + TQChar('}')
+ (b ? TQChar('}') : TQChar('"'));
} 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_ += TQString::tqfromLatin1(",\n ")
+ fIt->property(bibtex)
+ TQString::tqfromLatin1(" = ")
+ value;
}
text_ += TQString::tqfromLatin1("\n}\n\n");
}
#include "bibtexexporter.moc"