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 }