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