File indexing completed on 2024-12-01 04:37:50
0001 /*************************************************************************** 0002 * SPDX-License-Identifier: GPL-2.0-or-later 0003 * * 0004 * SPDX-FileCopyrightText: 2004-2023 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 "fieldinput.h" 0021 0022 #include <QLayout> 0023 #include <QApplication> 0024 #include <QMenu> 0025 #include <QDate> 0026 #include <QSpinBox> 0027 #include <QPushButton> 0028 #include <QInputDialog> 0029 #include <QSignalBlocker> 0030 0031 #include <KLocalizedString> 0032 0033 #include <File> 0034 #include <Entry> 0035 #include <FileExporterBibTeX> 0036 #include "fieldlineedit.h" 0037 #include "fieldlistedit.h" 0038 #include "colorlabelwidget.h" 0039 #include "widgets/starrating.h" 0040 0041 class FieldInput::FieldInputPrivate 0042 { 0043 private: 0044 FieldInput *p; 0045 0046 public: 0047 ColorLabelWidget *colorWidget; 0048 StarRatingFieldInput *starRatingWidget; 0049 FieldLineEdit *fieldLineEdit; 0050 FieldListEdit *fieldListEdit; 0051 KBibTeX::FieldInputType fieldInputType; 0052 KBibTeX::TypeFlags typeFlags; 0053 KBibTeX::TypeFlag preferredTypeFlag; 0054 const File *bibtexFile; 0055 const Element *element; 0056 0057 FieldInputPrivate(FieldInput *parent) 0058 : p(parent), colorWidget(nullptr), starRatingWidget(nullptr), fieldLineEdit(nullptr), fieldListEdit(nullptr), fieldInputType(KBibTeX::FieldInputType::SingleLine), preferredTypeFlag(KBibTeX::TypeFlag::Source), bibtexFile(nullptr), element(nullptr) { 0059 /// nothing 0060 } 0061 0062 ~FieldInputPrivate() { 0063 if (colorWidget != nullptr) delete colorWidget; 0064 else if (starRatingWidget != nullptr) delete starRatingWidget; 0065 else if (fieldLineEdit != nullptr) delete fieldLineEdit; 0066 else if (fieldListEdit != nullptr) delete fieldListEdit; 0067 } 0068 0069 void createGUI() { 0070 QHBoxLayout *layout = new QHBoxLayout(p); 0071 layout->setContentsMargins(0, 0, 0, 0); 0072 0073 switch (fieldInputType) { 0074 case KBibTeX::FieldInputType::MultiLine: 0075 fieldLineEdit = new FieldLineEdit(preferredTypeFlag, typeFlags, true, p); 0076 connect(fieldLineEdit, &FieldLineEdit::modified, p, &FieldInput::modified); 0077 layout->addWidget(fieldLineEdit); 0078 break; 0079 case KBibTeX::FieldInputType::List: 0080 fieldListEdit = new FieldListEdit(preferredTypeFlag, typeFlags, p); 0081 connect(fieldListEdit, &FieldListEdit::modified, p, &FieldInput::modified); 0082 layout->addWidget(fieldListEdit); 0083 break; 0084 case KBibTeX::FieldInputType::Month: { 0085 fieldLineEdit = new FieldLineEdit(preferredTypeFlag, typeFlags, false, p); 0086 connect(fieldLineEdit, &FieldLineEdit::modified, p, &FieldInput::modified); 0087 layout->addWidget(fieldLineEdit); 0088 QPushButton *monthSelector = new QPushButton(QIcon::fromTheme(QStringLiteral("view-calendar-month")), QString()); 0089 monthSelector->setToolTip(i18n("Select a predefined month")); 0090 fieldLineEdit->prependWidget(monthSelector); 0091 0092 QMenu *monthMenu = new QMenu(monthSelector); 0093 for (int i = 1; i <= 12; ++i) { 0094 QAction *monthAction = monthMenu->addAction(QLocale::system().standaloneMonthName(i)); 0095 connect(monthAction, &QAction::triggered, p, [this, i]() { 0096 setMonth(i); 0097 }); 0098 } 0099 monthSelector->setMenu(monthMenu); 0100 } 0101 break; 0102 case KBibTeX::FieldInputType::Edition: { 0103 fieldLineEdit = new FieldLineEdit(preferredTypeFlag, typeFlags, false, p); 0104 connect(fieldLineEdit, &FieldLineEdit::modified, p, &FieldInput::modified); 0105 layout->addWidget(fieldLineEdit); 0106 QPushButton *editionSelector = new QPushButton(QIcon::fromTheme(QStringLiteral("clock")), QString()); 0107 editionSelector->setToolTip(i18n("Select a predefined edition")); 0108 fieldLineEdit->prependWidget(editionSelector); 0109 0110 QMenu *editionMenu = new QMenu(editionSelector); 0111 static const QStringList ordinals{i18n("1st"), i18n("2nd"), i18n("3rd"), i18n("4th"), i18n("5th"), i18n("6th"), i18n("7th"), i18n("8th"), i18n("9th"), i18n("10th"), i18n("11th"), i18n("12th"), i18n("13th"), i18n("14th"), i18n("15th"), i18n("16th")}; 0112 for (int i = 0; i < ordinals.length(); ++i) { 0113 QAction *editionAction = editionMenu->addAction(ordinals[i]); 0114 connect(editionAction, &QAction::triggered, p, [this, i]() { 0115 setEdition(i + 1); 0116 }); 0117 } 0118 editionSelector->setMenu(editionMenu); 0119 } 0120 break; 0121 case KBibTeX::FieldInputType::CrossRef: { 0122 fieldLineEdit = new FieldLineEdit(preferredTypeFlag, typeFlags, false, p); 0123 connect(fieldLineEdit, &FieldLineEdit::modified, p, &FieldInput::modified); 0124 layout->addWidget(fieldLineEdit); 0125 QPushButton *referenceSelector = new QPushButton(QIcon::fromTheme(QStringLiteral("flag-green")), QString()); ///< find better icon 0126 referenceSelector->setToolTip(i18n("Select an existing entry")); 0127 fieldLineEdit->prependWidget(referenceSelector); 0128 connect(referenceSelector, &QPushButton::clicked, p, &FieldInput::selectCrossRef); 0129 } 0130 break; 0131 case KBibTeX::FieldInputType::Color: { 0132 colorWidget = new ColorLabelWidget(p); 0133 connect(colorWidget, &ColorLabelWidget::modified, p, &FieldInput::modified); 0134 layout->addWidget(colorWidget, 0); 0135 } 0136 break; 0137 case KBibTeX::FieldInputType::StarRating: { 0138 starRatingWidget = new StarRatingFieldInput(p); 0139 connect(starRatingWidget, &StarRatingFieldInput::modified, p, &FieldInput::modified); 0140 layout->addWidget(starRatingWidget, 0); 0141 } 0142 break; 0143 case KBibTeX::FieldInputType::PersonList: 0144 fieldListEdit = new PersonListEdit(preferredTypeFlag, typeFlags, p); 0145 connect(fieldListEdit, &PersonListEdit::modified, p, &FieldInput::modified); 0146 layout->addWidget(fieldListEdit); 0147 break; 0148 case KBibTeX::FieldInputType::UrlList: 0149 fieldListEdit = new UrlListEdit(p); 0150 connect(fieldListEdit, &FieldListEdit::modified, p, &FieldInput::modified); 0151 layout->addWidget(fieldListEdit); 0152 break; 0153 case KBibTeX::FieldInputType::KeywordList: 0154 fieldListEdit = new KeywordListEdit(p); 0155 connect(fieldListEdit, &FieldListEdit::modified, p, &FieldInput::modified); 0156 layout->addWidget(fieldListEdit); 0157 break; 0158 default: 0159 fieldLineEdit = new FieldLineEdit(preferredTypeFlag, typeFlags, false, p); 0160 connect(fieldLineEdit, &FieldLineEdit::modified, p, &FieldInput::modified); 0161 layout->addWidget(fieldLineEdit); 0162 } 0163 } 0164 0165 void clear() { 0166 if (fieldLineEdit != nullptr) { 0167 const QSignalBlocker blocker(fieldLineEdit); 0168 fieldLineEdit->clear(); 0169 } else if (fieldListEdit != nullptr) { 0170 const QSignalBlocker blocker(fieldListEdit); 0171 fieldListEdit->clear(); 0172 } else if (colorWidget != nullptr) { 0173 const QSignalBlocker blocker(colorWidget); 0174 colorWidget->clear(); 0175 } else if (starRatingWidget != nullptr) { 0176 const QSignalBlocker blocker(starRatingWidget); 0177 starRatingWidget->unsetValue(); 0178 } 0179 } 0180 0181 bool reset(const Value &value) { 0182 bool result = false; 0183 if (fieldLineEdit != nullptr) { 0184 const QSignalBlocker blocker(fieldLineEdit); 0185 result = fieldLineEdit->reset(value); 0186 } else if (fieldListEdit != nullptr) { 0187 const QSignalBlocker blocker(fieldListEdit); 0188 result = fieldListEdit->reset(value); 0189 } else if (colorWidget != nullptr) { 0190 const QSignalBlocker blocker(colorWidget); 0191 result = colorWidget->reset(value); 0192 } else if (starRatingWidget != nullptr) { 0193 const QSignalBlocker blocker(starRatingWidget); 0194 result = starRatingWidget->reset(value); 0195 } 0196 0197 return result; 0198 } 0199 0200 bool apply(Value &value) const { 0201 bool result = false; 0202 if (fieldLineEdit != nullptr) 0203 result = fieldLineEdit->apply(value); 0204 else if (fieldListEdit != nullptr) 0205 result = fieldListEdit->apply(value); 0206 else if (colorWidget != nullptr) 0207 result = colorWidget->apply(value); 0208 else if (starRatingWidget != nullptr) 0209 result = starRatingWidget->apply(value); 0210 return result; 0211 } 0212 0213 bool validate(QWidget **widgetWithIssue, QString &message) const { 0214 if (fieldLineEdit != nullptr) 0215 return fieldLineEdit->validate(widgetWithIssue, message); 0216 else if (fieldListEdit != nullptr) 0217 return fieldListEdit->validate(widgetWithIssue, message); 0218 else if (colorWidget != nullptr) 0219 return colorWidget->validate(widgetWithIssue, message); 0220 else if (starRatingWidget != nullptr) 0221 return starRatingWidget->validate(widgetWithIssue, message); 0222 return false; 0223 } 0224 0225 void setReadOnly(bool isReadOnly) { 0226 if (fieldLineEdit != nullptr) 0227 fieldLineEdit->setReadOnly(isReadOnly); 0228 else if (fieldListEdit != nullptr) 0229 fieldListEdit->setReadOnly(isReadOnly); 0230 else if (colorWidget != nullptr) 0231 colorWidget->setReadOnly(isReadOnly); 0232 else if (starRatingWidget != nullptr) 0233 starRatingWidget->setReadOnly(isReadOnly); 0234 } 0235 0236 void setFile(const File *file) { 0237 bibtexFile = file; 0238 if (fieldLineEdit != nullptr) 0239 fieldLineEdit->setFile(file); 0240 if (fieldListEdit != nullptr) 0241 fieldListEdit->setFile(file); 0242 } 0243 0244 void setElement(const Element *element) { 0245 this->element = element; 0246 if (fieldLineEdit != nullptr) 0247 fieldLineEdit->setElement(element); 0248 if (fieldListEdit != nullptr) 0249 fieldListEdit->setElement(element); 0250 } 0251 0252 void setFieldKey(const QString &fieldKey) { 0253 if (fieldLineEdit != nullptr) 0254 fieldLineEdit->setFieldKey(fieldKey); 0255 if (fieldListEdit != nullptr) 0256 fieldListEdit->setFieldKey(fieldKey); 0257 } 0258 0259 void setCompletionItems(const QStringList &items) { 0260 if (fieldLineEdit != nullptr) 0261 fieldLineEdit->setCompletionItems(items); 0262 if (fieldListEdit != nullptr) 0263 fieldListEdit->setCompletionItems(items); 0264 } 0265 0266 bool selectCrossRef() { 0267 Q_ASSERT_X(fieldLineEdit != nullptr, "void FieldInput::FieldInputPrivate::selectCrossRef()", "fieldLineEdit is invalid"); 0268 if (bibtexFile == nullptr) return false; 0269 0270 /// create a standard input dialog with a list of all keys (ids of entries) 0271 bool ok = false; 0272 QStringList list = bibtexFile->allKeys(File::ElementType::Entry); 0273 list.sort(); 0274 0275 /// remove own id 0276 const Entry *entry = dynamic_cast<const Entry *>(element); 0277 if (entry != nullptr) list.removeOne(entry->id()); 0278 0279 QString crossRef = QInputDialog::getItem(p, i18n("Select Cross Reference"), i18n("Select the cross reference to another entry:"), list, 0, false, &ok); 0280 0281 if (ok && !crossRef.isEmpty()) { 0282 /// insert selected cross reference into edit widget 0283 Value value; 0284 value.append(QSharedPointer<VerbatimText>(new VerbatimText(crossRef))); 0285 reset(value); 0286 return true; 0287 } 0288 return false; 0289 } 0290 0291 void setMonth(int month) 0292 { 0293 Value value; 0294 value.append(QSharedPointer<MacroKey>(new MacroKey(KBibTeX::MonthsTriple[month - 1]))); 0295 reset(value); 0296 /// Instead of an 'emit' ... 0297 #if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) 0298 QMetaObject::invokeMethod(p, "modified", Qt::DirectConnection, QGenericReturnArgument()); 0299 #else // QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) 0300 QMetaObject::invokeMethod(p, "modified", Qt::DirectConnection, QMetaMethodReturnArgument()); 0301 #endif 0302 } 0303 0304 void setEdition(int edition) 0305 { 0306 const QString editionString = FileExporterBibTeX::editionNumberToString(edition); 0307 if (!editionString.isEmpty()) { 0308 Value value; 0309 value.append(QSharedPointer<PlainText>(new PlainText(editionString))); 0310 reset(value); 0311 /// Instead of an 'emit' ... 0312 #if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) 0313 QMetaObject::invokeMethod(p, "modified", Qt::DirectConnection, QGenericReturnArgument()); 0314 #else // QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) 0315 QMetaObject::invokeMethod(p, "modified", Qt::DirectConnection, QMetaMethodReturnArgument()); 0316 #endif 0317 } 0318 } 0319 }; 0320 0321 FieldInput::FieldInput(KBibTeX::FieldInputType fieldInputType, KBibTeX::TypeFlag preferredTypeFlag, KBibTeX::TypeFlags typeFlags, QWidget *parent) 0322 : QWidget(parent), d(new FieldInputPrivate(this)) 0323 { 0324 d->fieldInputType = fieldInputType; 0325 d->typeFlags = typeFlags; 0326 d->preferredTypeFlag = preferredTypeFlag; 0327 d->createGUI(); 0328 } 0329 0330 FieldInput::~FieldInput() 0331 { 0332 delete d; 0333 } 0334 0335 void FieldInput::clear() 0336 { 0337 d->clear(); 0338 } 0339 0340 bool FieldInput::reset(const Value &value) 0341 { 0342 return d->reset(value); 0343 } 0344 0345 bool FieldInput::apply(Value &value) const 0346 { 0347 return d->apply(value); 0348 } 0349 0350 bool FieldInput::validate(QWidget **widgetWithIssue, QString &message) const 0351 { 0352 return d->validate(widgetWithIssue, message); 0353 } 0354 0355 void FieldInput::setReadOnly(bool isReadOnly) 0356 { 0357 d->setReadOnly(isReadOnly); 0358 } 0359 0360 void FieldInput::setFile(const File *file) 0361 { 0362 d->setFile(file); 0363 } 0364 0365 void FieldInput::setElement(const Element *element) 0366 { 0367 d->setElement(element); 0368 } 0369 0370 void FieldInput::setFieldKey(const QString &fieldKey) 0371 { 0372 d->setFieldKey(fieldKey); 0373 } 0374 0375 void FieldInput::setCompletionItems(const QStringList &items) 0376 { 0377 d->setCompletionItems(items); 0378 } 0379 0380 QWidget *FieldInput::buddy() 0381 { 0382 if (d->fieldLineEdit != nullptr) 0383 return d->fieldLineEdit->buddy(); 0384 // TODO fieldListEdit 0385 else if (d->colorWidget != nullptr) 0386 return d->colorWidget; 0387 else if (d->starRatingWidget != nullptr) 0388 return d->starRatingWidget; 0389 return nullptr; 0390 } 0391 0392 void FieldInput::selectCrossRef() 0393 { 0394 if (d->selectCrossRef()) 0395 Q_EMIT modified(); 0396 }