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.
327 lines
12 KiB
327 lines
12 KiB
15 years ago
|
/***************************************************************************
|
||
|
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"
|