File indexing completed on 2024-05-12 05:09:22
0001 /*************************************************************************** 0002 Copyright (C) 2003-2009 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 #include "bibtexcollection.h" 0026 #include "../entrycomparison.h" 0027 #include "../utils/bibtexhandler.h" 0028 #include "../fieldformat.h" 0029 #include "../tellico_debug.h" 0030 0031 #include <KLocalizedString> 0032 #include <KStringHandler> 0033 0034 using namespace Tellico; 0035 using Tellico::Data::BibtexCollection; 0036 0037 namespace { 0038 static const char* bibtex_general = I18N_NOOP("General"); 0039 static const char* bibtex_publishing = I18N_NOOP("Publishing"); 0040 static const char* bibtex_misc = I18N_NOOP("Miscellaneous"); 0041 } 0042 0043 BibtexCollection::BibtexCollection(bool addDefaultFields_, const QString& title_) 0044 : Collection(title_.isEmpty() ? i18n("Bibliography") : title_) { 0045 setDefaultGroupField(QStringLiteral("author")); 0046 if(addDefaultFields_) { 0047 addFields(defaultFields()); 0048 } 0049 0050 // Bibtex has some default macros for the months 0051 addMacro(QStringLiteral("jan"), QString()); 0052 addMacro(QStringLiteral("feb"), QString()); 0053 addMacro(QStringLiteral("mar"), QString()); 0054 addMacro(QStringLiteral("apr"), QString()); 0055 addMacro(QStringLiteral("may"), QString()); 0056 addMacro(QStringLiteral("jun"), QString()); 0057 addMacro(QStringLiteral("jul"), QString()); 0058 addMacro(QStringLiteral("aug"), QString()); 0059 addMacro(QStringLiteral("sep"), QString()); 0060 addMacro(QStringLiteral("oct"), QString()); 0061 addMacro(QStringLiteral("nov"), QString()); 0062 addMacro(QStringLiteral("dec"), QString()); 0063 } 0064 0065 Tellico::Data::FieldList BibtexCollection::defaultFields() { 0066 FieldList list; 0067 FieldPtr field; 0068 0069 const QString bibtex = QStringLiteral("bibtex"); 0070 0071 /******************* General ****************************/ 0072 0073 field = Field::createDefaultField(Field::TitleField); 0074 field->setProperty(bibtex, QStringLiteral("title")); 0075 list.append(field); 0076 0077 QStringList types; 0078 types << QStringLiteral("article") << QStringLiteral("book") 0079 << QStringLiteral("booklet") << QStringLiteral("inbook") 0080 << QStringLiteral("incollection") << QStringLiteral("inproceedings") 0081 << QStringLiteral("manual") << QStringLiteral("mastersthesis") 0082 << QStringLiteral("misc") << QStringLiteral("phdthesis") 0083 << QStringLiteral("proceedings") << QStringLiteral("techreport") 0084 << QStringLiteral("unpublished") << QStringLiteral("periodical") 0085 << QStringLiteral("conference"); 0086 field = new Field(QStringLiteral("entry-type"), i18n("Entry Type"), types); 0087 field->setProperty(bibtex, QStringLiteral("entry-type")); 0088 field->setCategory(i18n(bibtex_general)); 0089 field->setFlags(Field::AllowGrouped | Field::NoDelete); 0090 field->setDescription(i18n("These entry types are specific to bibtex. See the bibtex documentation.")); 0091 list.append(field); 0092 0093 field = new Field(QStringLiteral("author"), i18n("Author")); 0094 field->setProperty(bibtex, QStringLiteral("author")); 0095 field->setCategory(i18n(bibtex_general)); 0096 field->setFlags(Field::AllowCompletion | Field::AllowMultiple | Field::AllowGrouped); 0097 field->setFormatType(FieldFormat::FormatName); 0098 list.append(field); 0099 0100 field = new Field(QStringLiteral("bibtex-key"), i18n("Bibtex Key")); 0101 field->setProperty(bibtex, QStringLiteral("key")); 0102 field->setCategory(i18n("General")); 0103 field->setFlags(Field::NoDelete); 0104 list.append(field); 0105 0106 field = new Field(QStringLiteral("booktitle"), i18n("Book Title")); 0107 field->setProperty(bibtex, QStringLiteral("booktitle")); 0108 field->setCategory(i18n(bibtex_general)); 0109 field->setFormatType(FieldFormat::FormatTitle); 0110 list.append(field); 0111 0112 field = new Field(QStringLiteral("editor"), i18n("Editor")); 0113 field->setProperty(bibtex, QStringLiteral("editor")); 0114 field->setCategory(i18n(bibtex_general)); 0115 field->setFlags(Field::AllowCompletion | Field::AllowMultiple | Field::AllowGrouped); 0116 field->setFormatType(FieldFormat::FormatName); 0117 list.append(field); 0118 0119 field = new Field(QStringLiteral("organization"), i18n("Organization")); 0120 field->setProperty(bibtex, QStringLiteral("organization")); 0121 field->setCategory(i18n(bibtex_general)); 0122 field->setFlags(Field::AllowCompletion | Field::AllowGrouped); 0123 field->setFormatType(FieldFormat::FormatPlain); 0124 list.append(field); 0125 0126 // field = new Field(QLatin1String("institution"), i18n("Institution")); 0127 // field->setProperty(QLatin1String("bibtex"), QLatin1String("institution")); 0128 // field->setCategory(i18n(bibtex_general)); 0129 // field->setFlags(Field::AllowDelete); 0130 // field->setFormatType(FieldFormat::FormatTitle); 0131 // list.append(field); 0132 0133 /******************* Publishing ****************************/ 0134 field = new Field(QStringLiteral("publisher"), i18n("Publisher")); 0135 field->setProperty(bibtex, QStringLiteral("publisher")); 0136 field->setCategory(i18n(bibtex_publishing)); 0137 field->setFlags(Field::AllowCompletion | Field::AllowGrouped); 0138 field->setFormatType(FieldFormat::FormatPlain); 0139 list.append(field); 0140 0141 field = new Field(QStringLiteral("address"), i18n("Address")); 0142 field->setProperty(bibtex, QStringLiteral("address")); 0143 field->setCategory(i18n(bibtex_publishing)); 0144 field->setFlags(Field::AllowCompletion | Field::AllowGrouped); 0145 list.append(field); 0146 0147 field = new Field(QStringLiteral("edition"), i18n("Edition")); 0148 field->setProperty(bibtex, QStringLiteral("edition")); 0149 field->setCategory(i18n(bibtex_publishing)); 0150 field->setFlags(Field::AllowCompletion); 0151 list.append(field); 0152 0153 // don't make it a number, it could have latex processing commands in it 0154 field = new Field(QStringLiteral("pages"), i18n("Pages")); 0155 field->setProperty(bibtex, QStringLiteral("pages")); 0156 field->setCategory(i18n(bibtex_publishing)); 0157 list.append(field); 0158 0159 field = new Field(QStringLiteral("year"), i18n("Year"), Field::Number); 0160 field->setProperty(bibtex, QStringLiteral("year")); 0161 field->setCategory(i18n(bibtex_publishing)); 0162 field->setFlags(Field::AllowGrouped); 0163 list.append(field); 0164 0165 field = Field::createDefaultField(Field::IsbnField); 0166 field->setProperty(bibtex, QStringLiteral("isbn")); 0167 field->setCategory(i18n(bibtex_publishing)); 0168 list.append(field); 0169 0170 field = new Field(QStringLiteral("journal"), i18n("Journal")); 0171 field->setProperty(bibtex, QStringLiteral("journal")); 0172 field->setCategory(i18n(bibtex_publishing)); 0173 field->setFlags(Field::AllowCompletion | Field::AllowGrouped); 0174 field->setFormatType(FieldFormat::FormatPlain); 0175 list.append(field); 0176 0177 field = new Field(QStringLiteral("doi"), i18n("DOI")); 0178 field->setProperty(bibtex, QStringLiteral("doi")); 0179 field->setCategory(i18n(bibtex_publishing)); 0180 field->setDescription(i18n("Digital Object Identifier")); 0181 list.append(field); 0182 0183 // could make this a string list, but since bibtex import could have funky values 0184 // keep it an editbox 0185 field = new Field(QStringLiteral("month"), i18n("Month")); 0186 field->setProperty(bibtex, QStringLiteral("month")); 0187 field->setCategory(i18n(bibtex_publishing)); 0188 field->setFlags(Field::AllowCompletion); 0189 list.append(field); 0190 0191 field = new Field(QStringLiteral("number"), i18n("Number"), Field::Number); 0192 field->setProperty(bibtex, QStringLiteral("number")); 0193 field->setCategory(i18n(bibtex_publishing)); 0194 list.append(field); 0195 0196 field = new Field(QStringLiteral("howpublished"), i18n("How Published")); 0197 field->setProperty(bibtex, QStringLiteral("howpublished")); 0198 field->setCategory(i18n(bibtex_publishing)); 0199 list.append(field); 0200 0201 // field = new Field(QLatin1String("school"), i18n("School")); 0202 // field->setProperty(QLatin1String("bibtex"), QLatin1String("school")); 0203 // field->setCategory(i18n(bibtex_publishing)); 0204 // field->setFlags(Field::AllowCompletion | Field::AllowGrouped); 0205 // list.append(field); 0206 0207 /******************* Classification ****************************/ 0208 field = new Field(QStringLiteral("chapter"), i18n("Chapter"), Field::Number); 0209 field->setProperty(bibtex, QStringLiteral("chapter")); 0210 field->setCategory(i18n(bibtex_misc)); 0211 list.append(field); 0212 0213 field = new Field(QStringLiteral("series"), i18n("Series")); 0214 field->setProperty(bibtex, QStringLiteral("series")); 0215 field->setCategory(i18n(bibtex_misc)); 0216 field->setFlags(Field::AllowCompletion | Field::AllowGrouped); 0217 field->setFormatType(FieldFormat::FormatTitle); 0218 list.append(field); 0219 0220 field = new Field(QStringLiteral("volume"), i18nc("A number field in a bibliography", "Volume"), Field::Number); 0221 field->setProperty(bibtex, QStringLiteral("volume")); 0222 field->setCategory(i18n(bibtex_misc)); 0223 list.append(field); 0224 0225 field = new Field(QStringLiteral("crossref"), i18n("Cross-Reference")); 0226 field->setProperty(bibtex, QStringLiteral("crossref")); 0227 field->setCategory(i18n(bibtex_misc)); 0228 list.append(field); 0229 0230 // field = new Field(QLatin1String("annote"), i18n("Annotation")); 0231 // field->setProperty(QLatin1String("bibtex"), QLatin1String("annote")); 0232 // field->setCategory(i18n(bibtex_misc)); 0233 // list.append(field); 0234 0235 field = new Field(QStringLiteral("keyword"), i18n("Keywords")); 0236 field->setProperty(bibtex, QStringLiteral("keywords")); 0237 field->setCategory(i18n(bibtex_misc)); 0238 field->setFlags(Field::AllowCompletion | Field::AllowMultiple | Field::AllowGrouped); 0239 list.append(field); 0240 0241 field = new Field(QStringLiteral("url"), i18n("URL"), Field::URL); 0242 field->setProperty(bibtex, QStringLiteral("url")); 0243 field->setCategory(i18n(bibtex_misc)); 0244 list.append(field); 0245 0246 field = new Field(QStringLiteral("abstract"), i18n("Abstract"), Field::Para); 0247 field->setProperty(bibtex, QStringLiteral("abstract")); 0248 list.append(field); 0249 0250 field = new Field(QStringLiteral("note"), i18n("Notes"), Field::Para); 0251 field->setProperty(bibtex, QStringLiteral("note")); 0252 list.append(field); 0253 0254 field = Field::createDefaultField(Field::IDField); 0255 field->setCategory(i18n(bibtex_misc)); 0256 list.append(field); 0257 0258 field = Field::createDefaultField(Field::CreatedDateField); 0259 field->setCategory(i18n(bibtex_misc)); 0260 list.append(field); 0261 0262 field = Field::createDefaultField(Field::ModifiedDateField); 0263 field->setCategory(i18n(bibtex_misc)); 0264 list.append(field); 0265 0266 return list; 0267 } 0268 0269 bool BibtexCollection::addField(Tellico::Data::FieldPtr field_) { 0270 if(!field_) { 0271 return false; 0272 } 0273 bool success = Collection::addField(field_); 0274 if(success) { 0275 const QString bibtex = field_->property(QStringLiteral("bibtex")); 0276 if(!bibtex.isEmpty()) { 0277 m_bibtexFieldDict.insert(bibtex, field_.data()); 0278 } 0279 } 0280 return success; 0281 } 0282 0283 bool BibtexCollection::modifyField(Tellico::Data::FieldPtr newField_) { 0284 if(!newField_) { 0285 return false; 0286 } 0287 // myDebug(); 0288 const QString bibtex = QStringLiteral("bibtex"); 0289 bool success = Collection::modifyField(newField_); 0290 FieldPtr oldField = fieldByName(newField_->name()); 0291 QString oldBibtex = oldField->property(bibtex); 0292 // if the field was edited in place, can't just look at the property value 0293 if(oldField == newField_) { 0294 // have to look at all fields in the hash to update the key 0295 auto i = m_bibtexFieldDict.constBegin(); 0296 for( ; i != m_bibtexFieldDict.constEnd(); ++i) { 0297 if(oldField == i.value()) { 0298 oldBibtex = i.key(); 0299 } 0300 } 0301 } 0302 success &= (m_bibtexFieldDict.remove(oldBibtex) > 0); 0303 0304 const QString newBibtex = newField_->property(bibtex); 0305 if(!newBibtex.isEmpty()) { 0306 oldField->setProperty(bibtex, newBibtex); 0307 m_bibtexFieldDict.insert(newBibtex, oldField.data()); 0308 } 0309 return success; 0310 } 0311 0312 bool BibtexCollection::removeField(Tellico::Data::FieldPtr field_, bool force_) { 0313 if(!field_) { 0314 return false; 0315 } 0316 // myDebug(); 0317 bool success = true; 0318 const QString bibtex = field_->property(QStringLiteral("bibtex")); 0319 if(!bibtex.isEmpty()) { 0320 success &= (m_bibtexFieldDict.remove(bibtex) != 0); 0321 } 0322 return success && Collection::removeField(field_, force_); 0323 } 0324 0325 bool BibtexCollection::removeField(const QString& name_, bool force_) { 0326 return removeField(fieldByName(name_), force_); 0327 } 0328 0329 Tellico::Data::FieldPtr BibtexCollection::fieldByBibtexName(const QString& bibtex_) const { 0330 return FieldPtr(m_bibtexFieldDict.contains(bibtex_) ? m_bibtexFieldDict.value(bibtex_) : nullptr); 0331 } 0332 0333 Tellico::Data::EntryPtr BibtexCollection::entryByBibtexKey(const QString& key_) const { 0334 EntryPtr entry; 0335 // we do assume unique keys 0336 foreach(EntryPtr e, entries()) { 0337 if(e->field(QStringLiteral("bibtex-key")) == key_) { 0338 entry = e; 0339 break; 0340 } 0341 } 0342 return entry; 0343 } 0344 0345 QString BibtexCollection::prepareText(const QString& text_) const { 0346 QString text = text_; 0347 BibtexHandler::cleanText(text); 0348 return text; 0349 } 0350 0351 int BibtexCollection::sameEntry(Tellico::Data::EntryPtr entry1_, Tellico::Data::EntryPtr entry2_) const { 0352 // equal identifiers are easy, give it a weight of 100 0353 if(EntryComparison::score(entry1_, entry2_, QStringLiteral("isbn"), this) > 0 || 0354 EntryComparison::score(entry1_, entry2_, QStringLiteral("lccn"), this) > 0 || 0355 EntryComparison::score(entry1_, entry2_, QStringLiteral("doi"), this) > 0 || 0356 EntryComparison::score(entry1_, entry2_, QStringLiteral("pmid"), this) > 0 || 0357 EntryComparison::score(entry1_, entry2_, QStringLiteral("arxiv"), this) > 0) { 0358 return EntryComparison::ENTRY_PERFECT_MATCH; 0359 } 0360 int res = 3*EntryComparison::score(entry1_, entry2_, QStringLiteral("title"), this); 0361 res += EntryComparison::score(entry1_, entry2_, QStringLiteral("author"), this); 0362 res += EntryComparison::score(entry1_, entry2_, QStringLiteral("entry-type"), this); 0363 if(res >= EntryComparison::ENTRY_PERFECT_MATCH) return res; 0364 0365 res += EntryComparison::score(entry1_, entry2_, QStringLiteral("year"), this); 0366 if(res >= EntryComparison::ENTRY_PERFECT_MATCH) return res; 0367 0368 res += EntryComparison::score(entry1_, entry2_, QStringLiteral("publisher"), this); 0369 if(res >= EntryComparison::ENTRY_PERFECT_MATCH) return res; 0370 0371 res += EntryComparison::score(entry1_, entry2_, QStringLiteral("binding"), this); 0372 if(res >= EntryComparison::ENTRY_PERFECT_MATCH) return res; 0373 return res; 0374 } 0375 0376 // static 0377 Tellico::Data::CollPtr BibtexCollection::convertBookCollection(Tellico::Data::CollPtr coll_) { 0378 const QString bibtex = QStringLiteral("bibtex"); 0379 BibtexCollection* coll = new BibtexCollection(false, coll_->title()); 0380 CollPtr collPtr(coll); 0381 FieldList fields = coll_->fields(); 0382 foreach(FieldPtr fIt, fields) { 0383 FieldPtr field(new Field(*fIt)); 0384 0385 // if it already has a bibtex property, skip it 0386 if(!field->property(bibtex).isEmpty()) { 0387 coll->addField(field); 0388 continue; 0389 } 0390 0391 // be sure to set bibtex property before adding it though 0392 QString name = field->name(); 0393 // this first group has bibtex field names the same as their own field name 0394 if(name == QLatin1String("title") 0395 || name == QLatin1String("author") 0396 || name == QLatin1String("editor") 0397 || name == QLatin1String("edition") 0398 || name == QLatin1String("publisher") 0399 || name == QLatin1String("isbn") 0400 || name == QLatin1String("lccn") 0401 || name == QLatin1String("url") 0402 || name == QLatin1String("language") 0403 || name == QLatin1String("pages") 0404 || name == QLatin1String("series")) { 0405 field->setProperty(bibtex, name); 0406 } else if(name == QLatin1String("series_num")) { 0407 field->setProperty(bibtex, QStringLiteral("number")); 0408 } else if(name == QLatin1String("pur_price")) { 0409 field->setProperty(bibtex, QStringLiteral("price")); 0410 } else if(name == QLatin1String("cr_year")) { 0411 field->setProperty(bibtex, QStringLiteral("year")); 0412 } else if(name == QLatin1String("bibtex-id")) { 0413 field->setProperty(bibtex, QStringLiteral("key")); 0414 } else if(name == QLatin1String("keyword")) { 0415 field->setProperty(bibtex, QStringLiteral("keywords")); 0416 } else if(name == QLatin1String("comments")) { 0417 field->setProperty(bibtex, QStringLiteral("note")); 0418 } 0419 coll->addField(field); 0420 } 0421 0422 // also need to add required fields, those with NoDelete set 0423 foreach(FieldPtr defaultField, coll->defaultFields()) { 0424 if(!coll->hasField(defaultField->name()) && defaultField->hasFlag(Field::NoDelete)) { 0425 // but don't add a Bibtex Key if there's already a bibtex-id 0426 if(defaultField->property(bibtex) != QLatin1String("key") 0427 || !coll->hasField(QStringLiteral("bibtex-id"))) { 0428 coll->addField(defaultField); 0429 } 0430 } 0431 } 0432 0433 // set the entry-type to book 0434 FieldPtr field = coll->fieldByBibtexName(QStringLiteral("entry-type")); 0435 QString entryTypeName; 0436 if(field) { 0437 entryTypeName = field->name(); 0438 } else { 0439 myWarning() << "there must be an entry type field"; 0440 } 0441 0442 EntryList newEntries; 0443 foreach(EntryPtr entry, coll_->entries()) { 0444 EntryPtr newEntry(new Entry(*entry)); 0445 newEntry->setCollection(collPtr); 0446 if(!entryTypeName.isEmpty()) { 0447 newEntry->setField(entryTypeName, QStringLiteral("book")); 0448 } 0449 newEntries.append(newEntry); 0450 } 0451 coll->addEntries(newEntries); 0452 0453 return collPtr; 0454 } 0455 0456 bool BibtexCollection::setFieldValue(Data::EntryPtr entry_, const QString& bibtexField_, const QString& value_, Data::CollPtr existingColl_) { 0457 Q_ASSERT(entry_->collection()->type() == Collection::Bibtex); 0458 BibtexCollection* c = static_cast<BibtexCollection*>(entry_->collection().data()); 0459 FieldPtr field = c->fieldByBibtexName(bibtexField_); 0460 // special-case: "keyword" and "keywords" should be the same field. 0461 if(!field && bibtexField_ == QLatin1String("keyword")) { 0462 field = c->fieldByBibtexName(QStringLiteral("keywords")); 0463 } 0464 if(!field) { 0465 // it was the case that the default bibliography did not have a bibtex property for keywords 0466 // so a "keywords" field would get created in the imported collection 0467 // but the existing collection had a field "keyword" so the values would not get imported 0468 // here, check to see if the current collection has a field with the same bibtex name and 0469 // use it instead of creating a new one 0470 BibtexCollection* existingColl = dynamic_cast<BibtexCollection*>(existingColl_.data()); 0471 FieldPtr existingField; 0472 if(existingColl && existingColl->type() == Collection::Bibtex) { 0473 existingField = existingColl->fieldByBibtexName(bibtexField_); 0474 } 0475 if(existingField) { 0476 field = new Field(*existingField); 0477 } else if(value_.length() < 100) { 0478 // arbitrarily say if the value has more than 100 chars, then it's a paragraph 0479 QString vlower = value_.toLower(); 0480 // special case, try to detect URLs 0481 if(bibtexField_ == QLatin1String("url") 0482 || vlower.startsWith(QLatin1String("http")) // may also be https 0483 || vlower.startsWith(QLatin1String("ftp:/")) 0484 || vlower.startsWith(QLatin1String("file:/")) 0485 || vlower.startsWith(QLatin1String("/"))) { // assume this indicates a local path 0486 myDebug() << "creating a URL field for" << bibtexField_; 0487 field = new Field(bibtexField_, KStringHandler::capwords(bibtexField_), Field::URL); 0488 } else { 0489 myDebug() << "creating a LINE field for" << bibtexField_; 0490 field = new Field(bibtexField_, KStringHandler::capwords(bibtexField_), Field::Line); 0491 } 0492 field->setCategory(i18n("Unknown")); 0493 } else { 0494 myDebug() << "creating a PARA field for" << bibtexField_; 0495 field = new Field(bibtexField_, KStringHandler::capwords(bibtexField_), Field::Para); 0496 } 0497 field->setProperty(QStringLiteral("bibtex"), bibtexField_); 0498 c->addField(field); 0499 } 0500 // special case keywords, replace commas with semi-colons so they get separated 0501 QString value = value_; 0502 Q_ASSERT(field); 0503 static const QRegularExpression spaceCommaRx(QLatin1String("\\s*,\\s*")); 0504 if(bibtexField_.startsWith(QLatin1String("keyword"))) { 0505 value.replace(spaceCommaRx, FieldFormat::delimiterString()); 0506 // special case refbase bibtex export, with multiple keywords fields 0507 QString oValue = entry_->field(field); 0508 if(!oValue.isEmpty()) { 0509 value = oValue + FieldFormat::delimiterString() + value; 0510 } 0511 // special case for tilde, since it's a non-breaking space in LateX 0512 // replace it EXCEPT for URL or DOI fields 0513 } else if(bibtexField_ != QLatin1String("doi") && field->type() != Field::URL) { 0514 value.replace(QLatin1Char('~'), QChar(0xA0)); 0515 } else if(field->type() == Field::URL || bibtexField_ == QLatin1String("url")) { 0516 // special case for url package 0517 if(value.startsWith(QLatin1String("\\url{")) && value.endsWith(QLatin1Char('}'))) { 0518 value.remove(0, 5).chop(1); 0519 } 0520 } 0521 return entry_->setField(field, value); 0522 } 0523 0524 Tellico::Data::EntryList BibtexCollection::duplicateBibtexKeys() const { 0525 QSet<EntryPtr> dupes; 0526 QHash<QString, EntryPtr> keyHash; 0527 0528 const QString keyField = QStringLiteral("bibtex-key"); 0529 QString keyValue; 0530 foreach(EntryPtr entry, entries()) { 0531 keyValue = entry->field(keyField); 0532 if(keyHash.contains(keyValue)) { 0533 dupes << keyHash.value(keyValue) << entry; 0534 } else { 0535 keyHash.insert(keyValue, entry); 0536 } 0537 } 0538 return dupes.values(); 0539 }