File indexing completed on 2024-11-24 04:34:18
0001 /*************************************************************************** 0002 * SPDX-License-Identifier: GPL-2.0-or-later 0003 * * 0004 * SPDX-FileCopyrightText: 2004-2022 Thomas Fischer <fischer@unix-ag.uni-kl.de> 0005 * * 0006 * This program is free software; you can redistribute it and/or modify * 0007 * it under the terms of the GNU General Public License as published by * 0008 * the Free Software Foundation; either version 2 of the License, or * 0009 * (at your option) any later version. * 0010 * * 0011 * This program is distributed in the hope that it will be useful, * 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0014 * GNU General Public License for more details. * 0015 * * 0016 * You should have received a copy of the GNU General Public License * 0017 * along with this program; if not, see <https://www.gnu.org/licenses/>. * 0018 ***************************************************************************/ 0019 0020 #include "fieldlineedit.h" 0021 0022 #include <typeinfo> 0023 0024 #include <QMenu> 0025 #include <QBuffer> 0026 #include <QFileInfo> 0027 #include <QDir> 0028 #include <QDragEnterEvent> 0029 #include <QDropEvent> 0030 #include <QPushButton> 0031 #include <QFontDatabase> 0032 #include <QUrl> 0033 #include <QMimeType> 0034 #include <QMimeData> 0035 #include <QRegularExpression> 0036 0037 #include <kio_version.h> 0038 #if KIO_VERSION >= QT_VERSION_CHECK(5, 71, 0) 0039 #include <KIO/OpenUrlJob> 0040 #if KIO_VERSION >= QT_VERSION_CHECK(5, 98, 0) 0041 #include <KIO/JobUiDelegateFactory> 0042 #else // < 5.98.0 0043 #include <KIO/JobUiDelegate> 0044 #endif // QT_VERSION_CHECK(5, 98, 0) 0045 #else // < 5.71.0 0046 #include <KRun> 0047 #endif // KIO_VERSION >= QT_VERSION_CHECK(5, 71, 0) 0048 #include <KMessageBox> 0049 #include <KLocalizedString> 0050 0051 #include <BibTeXFields> 0052 #include <Preferences> 0053 #include <File> 0054 #include <Entry> 0055 #include <Value> 0056 #include <FileInfo> 0057 #include <FileImporterBibTeX> 0058 #include <FileExporterBibTeX> 0059 #include <EncoderLaTeX> 0060 #include "logging_gui.h" 0061 0062 class FieldLineEdit::FieldLineEditPrivate 0063 { 0064 private: 0065 FieldLineEdit *parent; 0066 KBibTeX::TypeFlags typeFlags; 0067 QPushButton *buttonOpenUrl; 0068 0069 public: 0070 QMenu *menuTypes; 0071 const KBibTeX::TypeFlag preferredTypeFlag; 0072 KBibTeX::TypeFlag typeFlag; 0073 QUrl urlToOpen; 0074 const File *file; 0075 QString fieldKey; 0076 0077 FieldLineEditPrivate(KBibTeX::TypeFlag ptf, KBibTeX::TypeFlags tf, FieldLineEdit *p) 0078 : parent(p), typeFlags(tf), preferredTypeFlag(ptf), file(nullptr) { 0079 menuTypes = new QMenu(parent); 0080 setupMenu(); 0081 0082 buttonOpenUrl = new QPushButton(QIcon::fromTheme(QStringLiteral("document-open-remote")), QString(), parent); 0083 buttonOpenUrl->setVisible(false); 0084 buttonOpenUrl->setProperty("isConst", true); 0085 parent->appendWidget(buttonOpenUrl); 0086 connect(buttonOpenUrl, &QPushButton::clicked, parent, [this]() { 0087 openUrl(); 0088 }); 0089 0090 connect(p, &FieldLineEdit::textChanged, p, [this](const QString & text) { 0091 textChanged(text); 0092 }); 0093 0094 Value value; 0095 typeFlag = determineTypeFlag(value, preferredTypeFlag, typeFlags); 0096 updateGUI(typeFlag); 0097 } 0098 0099 bool reset(const Value &value, const KBibTeX::TypeFlag preferredTypeFlag) { 0100 bool result = false; 0101 QString text; 0102 typeFlag = determineTypeFlag(value, preferredTypeFlag, typeFlags); 0103 updateGUI(typeFlag); 0104 0105 if (!value.isEmpty()) { 0106 if (typeFlag == KBibTeX::TypeFlag::Source) { 0107 /// simple case: field's value is to be shown as BibTeX code, including surrounding curly braces 0108 FileExporterBibTeX exporter(parent); 0109 text = exporter.valueToBibTeX(value, Encoder::TargetEncoding::UTF8); 0110 result = true; 0111 } else { 0112 /// except for the source view type flag, type flag views do not support composed values, 0113 /// therefore only the first value will be shown 0114 const QSharedPointer<ValueItem> first = value.first(); 0115 0116 const QSharedPointer<PlainText> plainText = first.dynamicCast<PlainText>(); 0117 if (typeFlag == KBibTeX::TypeFlag::PlainText && !plainText.isNull()) { 0118 text = plainText->text(); 0119 result = true; 0120 } else { 0121 const QSharedPointer<Person> person = first.dynamicCast<Person>(); 0122 if (typeFlag == KBibTeX::TypeFlag::Person && !person.isNull()) { 0123 text = Person::transcribePersonName(person.data(), Preferences::instance().personNameFormat()); 0124 result = true; 0125 } else { 0126 const QSharedPointer<MacroKey> macroKey = first.dynamicCast<MacroKey>(); 0127 if (typeFlag == KBibTeX::TypeFlag::Reference && !macroKey.isNull()) { 0128 text = macroKey->text(); 0129 result = true; 0130 } else { 0131 const QSharedPointer<Keyword> keyword = first.dynamicCast<Keyword>(); 0132 if (typeFlag == KBibTeX::TypeFlag::Keyword && !keyword.isNull()) { 0133 text = keyword->text(); 0134 result = true; 0135 } else { 0136 const QSharedPointer<VerbatimText> verbatimText = first.dynamicCast<VerbatimText>(); 0137 if (typeFlag == KBibTeX::TypeFlag::Verbatim && !verbatimText.isNull()) { 0138 text = verbatimText->text(); 0139 result = true; 0140 } else 0141 qCWarning(LOG_KBIBTEX_GUI) << "Could not reset: " << typeFlag << " " << typeid((void)*first).name() << " : " << PlainTextValue::text(value); 0142 } 0143 } 0144 } 0145 } 0146 } 0147 } 0148 0149 updateURL(text); 0150 0151 parent->setText(text); 0152 return result; 0153 } 0154 0155 bool apply(Value &value) const { 0156 value.clear(); 0157 /// Remove unnecessary white space from input 0158 /// Exception: source and verbatim content is kept unmodified 0159 const QString text = typeFlag == KBibTeX::TypeFlag::Source || typeFlag == KBibTeX::TypeFlag::Verbatim ? parent->text() : parent->text().simplified(); 0160 if (text.isEmpty()) 0161 return true; 0162 0163 const EncoderLaTeX &encoder = EncoderLaTeX::instance(); 0164 const QString encodedText = encoder.decode(text); 0165 static const QRegularExpression invalidCharsForReferenceRegExp(QStringLiteral("[^-_:/a-zA-Z0-9]")); 0166 if (encodedText.isEmpty()) 0167 return true; 0168 else if (typeFlag == KBibTeX::TypeFlag::PlainText) { 0169 value.append(QSharedPointer<PlainText>(new PlainText(encodedText))); 0170 return true; 0171 } else if (typeFlag == KBibTeX::TypeFlag::Reference && !encodedText.contains(invalidCharsForReferenceRegExp)) { 0172 value.append(QSharedPointer<MacroKey>(new MacroKey(encodedText))); 0173 return true; 0174 } else if (typeFlag == KBibTeX::TypeFlag::Person) { 0175 QSharedPointer<Person> person = FileImporterBibTeX::personFromString(encodedText); 0176 if (!person.isNull()) 0177 value.append(person); 0178 return true; 0179 } else if (typeFlag == KBibTeX::TypeFlag::Keyword) { 0180 const QList<QSharedPointer<Keyword> > keywords = FileImporterBibTeX::splitKeywords(encodedText); 0181 for (const auto &keyword : keywords) 0182 value.append(keyword); 0183 return true; 0184 } else if (typeFlag == KBibTeX::TypeFlag::Source) { 0185 const QString key = typeFlags.testFlag(KBibTeX::TypeFlag::Person) ? QStringLiteral("author") : QStringLiteral("title"); 0186 FileImporterBibTeX importer(parent); 0187 const QString fakeBibTeXFile = QString(QStringLiteral("@article{dummy, %1=%2}")).arg(key, encodedText); 0188 0189 const QScopedPointer<const File> file(importer.fromString(fakeBibTeXFile)); 0190 if (!file.isNull() && file->count() == 1) { 0191 QSharedPointer<Entry> entry = file->first().dynamicCast<Entry>(); 0192 if (!entry.isNull()) { 0193 value = entry->value(key); 0194 return !value.isEmpty(); 0195 } else 0196 qCWarning(LOG_KBIBTEX_GUI) << "Parsing " << fakeBibTeXFile << " did not result in valid entry"; 0197 } 0198 } else if (typeFlag == KBibTeX::TypeFlag::Verbatim) { 0199 value.append(QSharedPointer<VerbatimText>(new VerbatimText(text))); 0200 return true; 0201 } 0202 0203 return false; 0204 } 0205 0206 int validateCurlyBracketContext(const QString &text) const { 0207 int openingCB = 0, closingCB = 0; 0208 0209 for (int i = 0; i < text.length(); ++i) { 0210 if (i == 0 || text[i - 1] != QLatin1Char('\\')) { 0211 if (text[i] == QLatin1Char('{')) ++openingCB; 0212 else if (text[i] == QLatin1Char('}')) ++closingCB; 0213 } 0214 } 0215 0216 return openingCB - closingCB; 0217 } 0218 0219 bool validate(QWidget **widgetWithIssue, QString &message) const { 0220 message.clear(); 0221 0222 /// Remove unnecessary white space from input 0223 /// Exception: source and verbatim content is kept unmodified 0224 const QString text = typeFlag == KBibTeX::TypeFlag::Source || typeFlag == KBibTeX::TypeFlag::Verbatim ? parent->text() : parent->text().simplified(); 0225 if (text.isEmpty()) 0226 return true; 0227 0228 const EncoderLaTeX &encoder = EncoderLaTeX::instance(); 0229 const QString encodedText = encoder.decode(text); 0230 if (encodedText.isEmpty()) 0231 return true; 0232 0233 bool result = false; 0234 if (typeFlag == KBibTeX::TypeFlag::PlainText || typeFlag == KBibTeX::TypeFlag::Person || typeFlag == KBibTeX::TypeFlag::Keyword) { 0235 result = validateCurlyBracketContext(text) == 0; 0236 if (!result) message = i18n("Opening and closing curly brackets do not match."); 0237 } else if (typeFlag == KBibTeX::TypeFlag::Reference) { 0238 static const QRegularExpression validReferenceRegExp(QStringLiteral("^[-_:/a-zA-Z0-9]+$")); 0239 const QRegularExpressionMatch validReferenceMatch = validReferenceRegExp.match(text); 0240 result = validReferenceMatch.hasMatch() && validReferenceMatch.captured() == text; 0241 if (!result) message = i18n("Reference contains characters outside of the allowed set."); 0242 } else if (typeFlag == KBibTeX::TypeFlag::Source) { 0243 const QString key = typeFlags.testFlag(KBibTeX::TypeFlag::Person) ? QStringLiteral("author") : QStringLiteral("title"); 0244 FileImporterBibTeX importer(parent); 0245 const QString fakeBibTeXFile = QString(QStringLiteral("@article{dummy, %1=%2}")).arg(key, encodedText); 0246 0247 const QScopedPointer<const File> file(importer.fromString(fakeBibTeXFile)); 0248 if (file.isNull() || file->count() != 1) return false; 0249 QSharedPointer<Entry> entry = file->first().dynamicCast<Entry>(); 0250 result = !entry.isNull() && entry->count() == 1; 0251 if (!result) message = i18n("Source code could not be parsed correctly."); 0252 } else if (typeFlag == KBibTeX::TypeFlag::Verbatim) { 0253 result = validateCurlyBracketContext(text) == 0; 0254 if (!result) message = i18n("Opening and closing curly brackets do not match."); 0255 } 0256 0257 if (!result && widgetWithIssue != nullptr) 0258 *widgetWithIssue = parent; 0259 0260 return result; 0261 } 0262 0263 void clear() { 0264 const KBibTeX::TypeFlag newTypeFlag = typeFlags.testFlag(preferredTypeFlag) ? preferredTypeFlag : KBibTeX::TypeFlag::Source; 0265 if (newTypeFlag != typeFlag) 0266 updateGUI(typeFlag = newTypeFlag); 0267 } 0268 0269 KBibTeX::TypeFlag determineTypeFlag(const Value &value, KBibTeX::TypeFlag preferredTypeFlag, KBibTeX::TypeFlags availableTypeFlags) { 0270 KBibTeX::TypeFlag result = KBibTeX::TypeFlag::Source; 0271 if (availableTypeFlags.testFlag(preferredTypeFlag) && typeFlagSupported(value, preferredTypeFlag)) 0272 result = preferredTypeFlag; 0273 else if (value.count() == 1) { 0274 int p = 1; 0275 for (int i = 1; i < 8; ++i, p <<= 1) { 0276 const KBibTeX::TypeFlag flag = static_cast<KBibTeX::TypeFlag>(p); 0277 if (availableTypeFlags.testFlag(flag) && typeFlagSupported(value, flag)) { 0278 result = flag; break; 0279 } 0280 } 0281 } 0282 return result; 0283 } 0284 0285 bool typeFlagSupported(const Value &value, KBibTeX::TypeFlag typeFlag) { 0286 if (value.isEmpty() || typeFlag == KBibTeX::TypeFlag::Source) 0287 return true; 0288 0289 const QSharedPointer<ValueItem> first = value.first(); 0290 if (value.count() > 1) 0291 return typeFlag == KBibTeX::TypeFlag::Source; 0292 else if (typeFlag == KBibTeX::TypeFlag::Keyword && Keyword::isKeyword(*first)) 0293 return true; 0294 else if (typeFlag == KBibTeX::TypeFlag::Person && Person::isPerson(*first)) 0295 return true; 0296 else if (typeFlag == KBibTeX::TypeFlag::PlainText && PlainText::isPlainText(*first)) 0297 return true; 0298 else if (typeFlag == KBibTeX::TypeFlag::Reference && MacroKey::isMacroKey(*first)) 0299 return true; 0300 else if (typeFlag == KBibTeX::TypeFlag::Verbatim && VerbatimText::isVerbatimText(*first)) 0301 return true; 0302 else 0303 return false; 0304 } 0305 0306 0307 void setupMenu() { 0308 menuTypes->clear(); 0309 0310 if (typeFlags.testFlag(KBibTeX::TypeFlag::PlainText)) { 0311 QAction *action = menuTypes->addAction(iconForTypeFlag(KBibTeX::TypeFlag::PlainText), i18n("Plain Text")); 0312 connect(action, &QAction::triggered, parent, [this]() { 0313 typeChanged(KBibTeX::TypeFlag::PlainText); 0314 }); 0315 } 0316 if (typeFlags.testFlag(KBibTeX::TypeFlag::Reference)) { 0317 QAction *action = menuTypes->addAction(iconForTypeFlag(KBibTeX::TypeFlag::Reference), i18n("Reference")); 0318 connect(action, &QAction::triggered, parent, [this]() { 0319 typeChanged(KBibTeX::TypeFlag::Reference); 0320 }); 0321 } 0322 if (typeFlags.testFlag(KBibTeX::TypeFlag::Person)) { 0323 QAction *action = menuTypes->addAction(iconForTypeFlag(KBibTeX::TypeFlag::Person), i18n("Person")); 0324 connect(action, &QAction::triggered, parent, [this]() { 0325 typeChanged(KBibTeX::TypeFlag::Person); 0326 }); 0327 } 0328 if (typeFlags.testFlag(KBibTeX::TypeFlag::Keyword)) { 0329 QAction *action = menuTypes->addAction(iconForTypeFlag(KBibTeX::TypeFlag::Keyword), i18n("Keyword")); 0330 connect(action, &QAction::triggered, parent, [this]() { 0331 typeChanged(KBibTeX::TypeFlag::Keyword); 0332 }); 0333 } 0334 if (typeFlags.testFlag(KBibTeX::TypeFlag::Source)) { 0335 QAction *action = menuTypes->addAction(iconForTypeFlag(KBibTeX::TypeFlag::Source), i18n("Source Code")); 0336 connect(action, &QAction::triggered, parent, [this]() { 0337 typeChanged(KBibTeX::TypeFlag::Source); 0338 }); 0339 } 0340 if (typeFlags.testFlag(KBibTeX::TypeFlag::Verbatim)) { 0341 QAction *action = menuTypes->addAction(iconForTypeFlag(KBibTeX::TypeFlag::Verbatim), i18n("Verbatim Text")); 0342 connect(action, &QAction::triggered, parent, [this]() { 0343 typeChanged(KBibTeX::TypeFlag::Verbatim); 0344 }); 0345 } 0346 } 0347 0348 QIcon iconForTypeFlag(KBibTeX::TypeFlag typeFlag) { 0349 switch (typeFlag) { 0350 case KBibTeX::TypeFlag::Invalid: return QIcon(); 0351 case KBibTeX::TypeFlag::PlainText: return QIcon::fromTheme(QStringLiteral("draw-text")); 0352 case KBibTeX::TypeFlag::Reference: return QIcon::fromTheme(QStringLiteral("emblem-symbolic-link")); 0353 case KBibTeX::TypeFlag::Person: return QIcon::fromTheme(QStringLiteral("user-identity")); 0354 case KBibTeX::TypeFlag::Keyword: return QIcon::fromTheme(QStringLiteral("edit-find")); 0355 case KBibTeX::TypeFlag::Source: return QIcon::fromTheme(QStringLiteral("code-context")); 0356 case KBibTeX::TypeFlag::Verbatim: return QIcon::fromTheme(QStringLiteral("preferences-desktop-keyboard")); 0357 } 0358 return QIcon(); //< should never happen as switch above covers all cases 0359 } 0360 0361 void updateGUI(KBibTeX::TypeFlag typeFlag) { 0362 parent->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); 0363 parent->setIcon(iconForTypeFlag(typeFlag)); 0364 switch (typeFlag) { 0365 case KBibTeX::TypeFlag::Invalid: parent->setButtonToolTip(QString()); break; 0366 case KBibTeX::TypeFlag::PlainText: parent->setButtonToolTip(i18n("Plain Text")); break; 0367 case KBibTeX::TypeFlag::Reference: parent->setButtonToolTip(i18n("Reference")); break; 0368 case KBibTeX::TypeFlag::Person: parent->setButtonToolTip(i18n("Person")); break; 0369 case KBibTeX::TypeFlag::Keyword: parent->setButtonToolTip(i18n("Keyword")); break; 0370 case KBibTeX::TypeFlag::Source: 0371 parent->setButtonToolTip(i18n("Source Code")); 0372 parent->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); 0373 break; 0374 case KBibTeX::TypeFlag::Verbatim: parent->setButtonToolTip(i18n("Verbatim Text")); break; 0375 } 0376 } 0377 0378 void openUrl() { 0379 if (urlToOpen.isValid()) { 0380 /// Guess mime type for url to open 0381 QMimeType mimeType = FileInfo::mimeTypeForUrl(urlToOpen); 0382 const QString mimeTypeName = mimeType.name(); 0383 /// Ask KDE subsystem to open url in viewer matching mime type 0384 #if KIO_VERSION < QT_VERSION_CHECK(5, 71, 0) 0385 KRun::runUrl(urlToOpen, mimeTypeName, parent, KRun::RunFlags()); 0386 #else // KIO_VERSION < QT_VERSION_CHECK(5, 71, 0) // >= 5.71.0 0387 KIO::OpenUrlJob *job = new KIO::OpenUrlJob(urlToOpen, mimeTypeName); 0388 #if KIO_VERSION < QT_VERSION_CHECK(5, 98, 0) // < 5.98.0 0389 job->setUiDelegate(new KIO::JobUiDelegate()); 0390 #else // KIO_VERSION < QT_VERSION_CHECK(5, 98, 0) // >= 5.98.0 0391 job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, parent)); 0392 #endif // KIO_VERSION < QT_VERSION_CHECK(5, 98, 0) 0393 job->start(); 0394 #endif // KIO_VERSION < QT_VERSION_CHECK(5, 71, 0) 0395 } 0396 } 0397 0398 bool convertValueType(Value &value, KBibTeX::TypeFlag destType) { 0399 if (value.isEmpty()) return true; /// simple case 0400 if (destType == KBibTeX::TypeFlag::Source) return true; /// simple case 0401 0402 bool result = true; 0403 const EncoderLaTeX &encoder = EncoderLaTeX::instance(); 0404 QString rawText; 0405 const QSharedPointer<ValueItem> first = value.first(); 0406 0407 const QSharedPointer<PlainText> plainText = first.dynamicCast<PlainText>(); 0408 if (!plainText.isNull()) 0409 rawText = encoder.encode(plainText->text(), Encoder::TargetEncoding::ASCII); 0410 else { 0411 const QSharedPointer<VerbatimText> verbatimText = first.dynamicCast<VerbatimText>(); 0412 if (!verbatimText.isNull()) 0413 rawText = verbatimText->text(); 0414 else { 0415 const QSharedPointer<MacroKey> macroKey = first.dynamicCast<MacroKey>(); 0416 if (!macroKey.isNull()) 0417 rawText = macroKey->text(); 0418 else { 0419 const QSharedPointer<Person> person = first.dynamicCast<Person>(); 0420 if (!person.isNull()) 0421 rawText = encoder.encode(QString(QStringLiteral("%1 %2")).arg(person->firstName(), person->lastName()), Encoder::TargetEncoding::ASCII); // FIXME proper name conversion 0422 else { 0423 const QSharedPointer<Keyword> keyword = first.dynamicCast<Keyword>(); 0424 if (!keyword.isNull()) 0425 rawText = encoder.encode(keyword->text(), Encoder::TargetEncoding::ASCII); 0426 else { 0427 // TODO case missed? 0428 result = false; 0429 } 0430 } 0431 } 0432 } 0433 } 0434 0435 switch (destType) { 0436 case KBibTeX::TypeFlag::PlainText: 0437 value.clear(); 0438 value.append(QSharedPointer<PlainText>(new PlainText(encoder.decode(rawText)))); 0439 break; 0440 case KBibTeX::TypeFlag::Verbatim: 0441 value.clear(); 0442 value.append(QSharedPointer<VerbatimText>(new VerbatimText(rawText))); 0443 break; 0444 case KBibTeX::TypeFlag::Person: 0445 value.clear(); 0446 value.append(QSharedPointer<Person>(FileImporterBibTeX::splitName(encoder.decode(rawText)))); 0447 break; 0448 case KBibTeX::TypeFlag::Reference: { 0449 MacroKey *macroKey = new MacroKey(rawText); 0450 if (macroKey->isValid()) { 0451 value.clear(); 0452 value.append(QSharedPointer<MacroKey>(macroKey)); 0453 } else { 0454 delete macroKey; 0455 result = false; 0456 } 0457 } 0458 break; 0459 case KBibTeX::TypeFlag::Keyword: 0460 value.clear(); 0461 value.append(QSharedPointer<Keyword>(new Keyword(encoder.decode(rawText)))); 0462 break; 0463 default: { 0464 // TODO 0465 result = false; 0466 } 0467 } 0468 0469 return result; 0470 } 0471 0472 void updateURL(const QString &text) { 0473 QSet<QUrl> urls; 0474 FileInfo::urlsInText(text, FileInfo::TestExistence::Yes, file != nullptr && file->property(File::Url).toUrl().isValid() ? QUrl(file->property(File::Url).toUrl()).path() : QString(), urls); 0475 QSet<QUrl>::ConstIterator urlsIt = urls.constBegin(); 0476 if (urlsIt != urls.constEnd() && (*urlsIt).isValid()) 0477 urlToOpen = (*urlsIt); 0478 else 0479 urlToOpen = QUrl(); 0480 0481 /// set special "open URL" button visible if URL (or file or DOI) found 0482 buttonOpenUrl->setVisible(urlToOpen.isValid()); 0483 buttonOpenUrl->setToolTip(i18n("Open '%1'", urlToOpen.url(QUrl::PreferLocalFile))); 0484 } 0485 0486 void textChanged(const QString &text) { 0487 updateURL(text); 0488 } 0489 0490 void typeChanged(const KBibTeX::TypeFlag newTypeFlag) 0491 { 0492 Value value; 0493 apply(value); 0494 0495 if (convertValueType(value, newTypeFlag)) { 0496 reset(value, newTypeFlag); 0497 #if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) 0498 QMetaObject::invokeMethod(parent, "modified", Qt::DirectConnection, QGenericReturnArgument()); 0499 #else // QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) 0500 QMetaObject::invokeMethod(parent, "modified", Qt::DirectConnection, QMetaMethodReturnArgument()); 0501 #endif 0502 } else 0503 KMessageBox::error(parent, i18n("The current text cannot be used as value of type '%1'.\n\nSwitching back to type '%2'.", BibTeXFields::typeFlagToString(newTypeFlag), BibTeXFields::typeFlagToString(typeFlag))); 0504 } 0505 }; 0506 0507 FieldLineEdit::FieldLineEdit(KBibTeX::TypeFlag preferredTypeFlag, KBibTeX::TypeFlags typeFlags, bool isMultiLine, QWidget *parent) 0508 : MenuLineEdit(isMultiLine, parent), d(new FieldLineEdit::FieldLineEditPrivate(preferredTypeFlag, typeFlags, this)) 0509 { 0510 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); 0511 setObjectName(QStringLiteral("FieldLineEdit")); 0512 setMenu(d->menuTypes); 0513 setChildAcceptDrops(false); 0514 setAcceptDrops(true); 0515 } 0516 0517 FieldLineEdit::~FieldLineEdit() 0518 { 0519 delete d; 0520 } 0521 0522 bool FieldLineEdit::apply(Value &value) const 0523 { 0524 return d->apply(value); 0525 } 0526 0527 bool FieldLineEdit::reset(const Value &value) 0528 { 0529 return d->reset(value, d->preferredTypeFlag); 0530 } 0531 0532 bool FieldLineEdit::validate(QWidget **widgetWithIssue, QString &message) const 0533 { 0534 return d->validate(widgetWithIssue, message); 0535 } 0536 0537 void FieldLineEdit::clear() 0538 { 0539 MenuLineEdit::clear(); 0540 d->clear(); 0541 } 0542 0543 void FieldLineEdit::setReadOnly(bool isReadOnly) 0544 { 0545 MenuLineEdit::setReadOnly(isReadOnly); 0546 } 0547 0548 void FieldLineEdit::setFile(const File *file) 0549 { 0550 d->file = file; 0551 } 0552 0553 void FieldLineEdit::setElement(const Element *element) 0554 { 0555 Q_UNUSED(element) 0556 } 0557 0558 void FieldLineEdit::setFieldKey(const QString &fieldKey) 0559 { 0560 d->fieldKey = fieldKey; 0561 } 0562 0563 void FieldLineEdit::dragEnterEvent(QDragEnterEvent *event) 0564 { 0565 if (event->mimeData()->hasFormat(QStringLiteral("text/plain")) || event->mimeData()->hasFormat(QStringLiteral("text/x-bibtex"))) 0566 event->acceptProposedAction(); 0567 } 0568 0569 void FieldLineEdit::dropEvent(QDropEvent *event) 0570 { 0571 const QString clipboardText = QString::fromUtf8(event->mimeData()->data(QStringLiteral("text/plain"))); 0572 event->acceptProposedAction(); 0573 if (clipboardText.isEmpty()) return; 0574 0575 bool success = false; 0576 if (!d->fieldKey.isEmpty() && clipboardText.startsWith(QStringLiteral("@"))) { 0577 FileImporterBibTeX importer(this); 0578 QScopedPointer<File> file(importer.fromString(clipboardText)); 0579 const QSharedPointer<Entry> entry = (!file.isNull() && file->count() == 1) ? file->first().dynamicCast<Entry>() : QSharedPointer<Entry>(); 0580 0581 if (!entry.isNull() && d->fieldKey == Entry::ftCrossRef) { 0582 /// handle drop on crossref line differently (use dropped entry's id) 0583 Value v; 0584 v.append(QSharedPointer<VerbatimText>(new VerbatimText(entry->id()))); 0585 reset(v); 0586 Q_EMIT textChanged(entry->id()); 0587 success = true; 0588 } else if (!entry.isNull() && entry->contains(d->fieldKey)) { 0589 /// case for "normal" fields like for journal, pages, ... 0590 reset(entry->value(d->fieldKey)); 0591 Q_EMIT textChanged(text()); 0592 success = true; 0593 } 0594 } 0595 0596 if (!success) { 0597 /// In case above cases were not met and thus 'success' is still false, 0598 /// clear this line edit and use the clipboad text as its content 0599 setText(clipboardText); 0600 Q_EMIT textChanged(clipboardText); 0601 } 0602 }