/*************************************************************************** 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::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) { 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::fromLatin1("@comment{Generated by Tellico ") + TQString::fromLatin1(VERSION) + TQString::fromLatin1("}\n\n"); if(!coll->preamble().isEmpty()) { text += TQString::fromLatin1("@preamble{") + coll->preamble() + TQString::fromLatin1("}\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::fromLatin1("@string{") + macroIt.key() + TQString::fromLatin1("=") + BibtexHandler::exportText(macroIt.data(), macros) + TQString::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; 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->parent()) == 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::fromLatin1("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::fromLatin1("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::fromLatin1("bibtex"); const TQString bibtexSep = TQString::fromLatin1("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::fromLatin1(" 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::fromLatin1("<.*>"), true); stripHTML.setMinimal(true); value.remove(stripHTML); } else if(fIt->property(bibtex) == Latin1Literal("pages")) { TQRegExp rx(TQString::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 ? TQChar('{') : TQChar('"')) + TQString::fromLatin1("\\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::fromLatin1(",\n ") + fIt->property(bibtex) + TQString::fromLatin1(" = ") + value; } text_ += TQString::fromLatin1("\n}\n\n"); } #include "bibtexexporter.moc"