File indexing completed on 2024-04-28 05:08:19

0001 /***************************************************************************
0002     Copyright (C) 2001-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 "entryeditdialog.h"
0026 #include "gui/fieldwidgetfactory.h"
0027 #include "gui/tabwidget.h"
0028 #include "collection.h"
0029 #include "controller.h"
0030 #include "field.h"
0031 #include "entry.h"
0032 #include "fieldformat.h"
0033 #include "tellico_kernel.h"
0034 #include "utils/cursorsaver.h"
0035 #include "tellico_debug.h"
0036 
0037 #include <KLocalizedString>
0038 #include <KMessageBox>
0039 #include <KAcceleratorManager>
0040 #include <KSharedConfig>
0041 #include <KWindowConfig>
0042 #include <KHelpClient>
0043 #include <kwidgetsaddons_version.h>
0044 
0045 #include <QStringList>
0046 #include <QObject>
0047 #include <QStyle>
0048 #include <QApplication>
0049 #include <QGridLayout>
0050 #include <QCloseEvent>
0051 #include <QVBoxLayout>
0052 #include <QPushButton>
0053 #include <QDialogButtonBox>
0054 #include <QWindow>
0055 
0056 namespace {
0057   // must be an even number
0058   static const int NCOLS = 2; // number of columns of GUI::FieldWidgets
0059   static const char* dialogOptionsString = "Edit Dialog Options";
0060 }
0061 
0062 using Tellico::EntryEditDialog;
0063 
0064 EntryEditDialog::EntryEditDialog(QWidget* parent_)
0065     : QDialog(parent_),
0066       m_tabs(new GUI::TabWidget(this)),
0067       m_modified(false),
0068       m_isOrphan(false),
0069       m_isWorking(false),
0070       m_needReset(false) {
0071   setWindowTitle(i18n("Edit Entry"));
0072 
0073   QVBoxLayout* mainLayout = new QVBoxLayout();
0074   setLayout(mainLayout);
0075   mainLayout->addWidget(m_tabs);
0076 
0077   QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Help|
0078                                                      QDialogButtonBox::Close|
0079                                                      QDialogButtonBox::Apply);
0080   connect(buttonBox, &QDialogButtonBox::helpRequested, this, &EntryEditDialog::slotHelp);
0081   mainLayout->addWidget(buttonBox);
0082 
0083   m_newButton = new QPushButton();
0084   buttonBox->addButton(m_newButton, QDialogButtonBox::ActionRole);
0085   m_newButton->setDefault(true);
0086   KGuiItem::assign(m_newButton, KGuiItem(i18n("&New Entry")));
0087 
0088   m_saveButton = buttonBox->button(QDialogButtonBox::Apply);
0089   m_saveButton->setEnabled(false);
0090   KGuiItem save = KStandardGuiItem::save();
0091   save.setText(i18n("Sa&ve Entry"));
0092   KGuiItem::assign(m_saveButton, save);
0093 
0094   connect(buttonBox->button(QDialogButtonBox::Close), &QAbstractButton::clicked, this, &EntryEditDialog::slotClose);
0095   connect(m_saveButton, &QAbstractButton::clicked, this, &EntryEditDialog::slotHandleSave);
0096   connect(m_newButton, &QAbstractButton::clicked, this, &EntryEditDialog::slotHandleNew);
0097 }
0098 
0099 EntryEditDialog::~EntryEditDialog() {
0100 }
0101 
0102 void EntryEditDialog::reject() {
0103   slotClose();
0104 }
0105 
0106 void EntryEditDialog::slotHelp() {
0107   KHelpClient::invokeHelp(QStringLiteral("entry-editor"));
0108 }
0109 
0110 void EntryEditDialog::slotClose() {
0111   // check to see if an entry should be saved before hiding
0112   // block signals so the entry view and selection isn't cleared
0113   if(m_modified && queryModified()) {
0114     accept();
0115     // make sure to reset values in the dialog
0116     m_needReset = true;
0117     setContents(m_currEntries);
0118     slotSetModified(false);
0119   } else if(!m_modified) {
0120     accept();
0121   }
0122 }
0123 
0124 void EntryEditDialog::slotReset() {
0125   if(m_isWorking) {
0126     return;
0127   }
0128   m_isWorking = true;
0129 
0130   slotSetModified(false);
0131   m_saveButton->setEnabled(false);
0132   m_saveButton->setText(i18n("Sa&ve Entry"));
0133   m_currColl = nullptr;
0134   m_currEntries.clear();
0135 
0136   while(m_tabs->count() > 0) {
0137     QWidget* widget = m_tabs->widget(0);
0138     m_tabs->removeTab(0);
0139     delete widget;
0140   }
0141   m_widgetDict.clear();
0142   m_isWorking = false;
0143 }
0144 
0145 void EntryEditDialog::resetLayout(Tellico::Data::CollPtr coll_) {
0146   if(!coll_ || m_isWorking) {
0147     return;
0148   }
0149 
0150   if(Kernel::self()->collectionType() == Data::Collection::Base) {
0151     m_newButton->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
0152   } else {
0153     m_newButton->setIcon(QIcon(QLatin1String(":/icons/") + Kernel::self()->collectionTypeName()));
0154   }
0155 
0156   setUpdatesEnabled(false);
0157   if(m_tabs->count() > 0) {
0158 //    myDebug() << "resetting contents.";
0159     slotReset();
0160   }
0161   m_isWorking = true;
0162 
0163   m_currColl = coll_;
0164 
0165   int maxHeight = 0;
0166   QList<QWidget*> gridList;
0167   bool noChoices = true;
0168 
0169   bool focusedFirst = false;
0170   QStringList catList = m_currColl->fieldCategories();
0171   for(QStringList::ConstIterator catIt = catList.constBegin(); catIt != catList.constEnd(); ++catIt) {
0172     Data::FieldList allCategoryfields = m_currColl->fieldsByCategory(*catIt);
0173     Data::FieldList fields;
0174     // remove fields which we don't plan to show
0175     foreach(Data::FieldPtr field, allCategoryfields) {
0176       // uneditabled and fields with derived values don't get widgets
0177       if(field->hasFlag(Data::Field::NoEdit) ||
0178          field->hasFlag(Data::Field::Derived)) {
0179         continue;
0180       }
0181       fields << field;
0182     }
0183 
0184     if(fields.isEmpty()) { // sanity check
0185       continue;
0186     }
0187 
0188     // if this layout model is changed, be sure to check slotUpdateField()
0189     QWidget* page = new QWidget(m_tabs);
0190     QBoxLayout* boxLayout = new QVBoxLayout(page);
0191 
0192     QWidget* grid = new QWidget(page);
0193     gridList.append(grid);
0194     // spacing gets a bit weird, if there are absolutely no Choice fields,
0195     // then spacing should be 5, which is set later
0196     QGridLayout* layout = new QGridLayout(grid);
0197 
0198     boxLayout->addWidget(grid, 0);
0199     // those with multiple, get a stretch
0200     if(fields.count() > 1 || !fields[0]->isSingleCategory()) {
0201       boxLayout->addStretch(1);
0202     }
0203 
0204     // keep track of which should expand
0205     QVector<bool> expands(NCOLS, false);
0206     QVector<int> maxWidth(NCOLS, 0);
0207 
0208     int count = 0;
0209     foreach(Data::FieldPtr field, fields) {
0210       if(field->type() == Data::Field::Choice) {
0211         noChoices = false;
0212       }
0213 
0214       GUI::FieldWidget* widget = GUI::FieldWidgetFactory::create(field, grid);
0215       if(!widget) {
0216         continue;
0217       }
0218       widget->insertDefault();
0219       connect(widget, &GUI::FieldWidget::valueChanged, this, &EntryEditDialog::fieldValueChanged);
0220       connect(widget, &GUI::FieldWidget::fieldChanged, this, &EntryEditDialog::fieldChanged);
0221       if(!focusedFirst && widget->focusPolicy() != Qt::NoFocus) {
0222         widget->setFocus();
0223         focusedFirst = true;
0224       }
0225 
0226       int r = count/NCOLS;
0227       int c = count%NCOLS;
0228       layout->addWidget(widget, r, c);
0229       layout->setRowStretch(r, 1);
0230 
0231       m_widgetDict.insert(QString::number(m_currColl->id()) + field->name(), widget);
0232 
0233       maxWidth[count%NCOLS] = qMax(maxWidth[count%NCOLS], widget->labelWidth());
0234       if(widget->expands()) {
0235         expands[count%NCOLS] = true;
0236       }
0237       widget->updateGeometry();
0238       if(!field->isSingleCategory()) {
0239         maxHeight = qMax(maxHeight, widget->minimumSizeHint().height());
0240       }
0241       ++count;
0242     }
0243 
0244     // now, the labels in a column should all be the same width
0245     count = 0;
0246     foreach(Data::FieldPtr field, fields) {
0247       GUI::FieldWidget* widget = m_widgetDict.value(QString::number(m_currColl->id()) + field->name());
0248       if(widget) {
0249         widget->setLabelWidth(maxWidth[count%NCOLS]);
0250         ++count;
0251       }
0252     }
0253 
0254     // update stretch factors for columns with a line edit
0255     for(int col = 0; col < NCOLS; ++col) {
0256       if(expands[col]) {
0257         layout->setColumnStretch(col, 1);
0258       }
0259     }
0260 
0261     m_tabs->addTab(page, *catIt);
0262   }
0263 
0264   // Now, go through and set all the field widgets to the same height
0265   foreach(QWidget* grid, gridList) {
0266     QGridLayout* l = static_cast<QGridLayout*>(grid->layout());
0267     if(noChoices) {
0268       l->setSpacing(5);
0269     }
0270     for(int row = 0; row < l->rowCount() && grid->children().count() > 1; ++row) {
0271       l->setRowMinimumHeight(row, maxHeight);
0272     }
0273     // I don't want anything to be hidden, Keramik has a bug if I don't do this
0274     grid->setMinimumHeight(grid->sizeHint().height());
0275     // the parent of the grid is the page that got added to the tabs
0276     grid->parentWidget()->layout()->invalidate();
0277     grid->parentWidget()->setMinimumHeight(grid->parentWidget()->sizeHint().height());
0278 
0279     // also, no accels for the field widgets
0280     KAcceleratorManager::setNoAccel(grid);
0281   }
0282 
0283   setUpdatesEnabled(true);
0284 // this doesn't seem to work
0285 //  setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
0286 // so do this instead
0287 //  layout()->invalidate(); // needed so the sizeHint() gets recalculated
0288   m_tabs->setMinimumHeight(m_tabs->minimumSizeHint().height());
0289   m_tabs->setMinimumWidth(m_tabs->sizeHint().width());
0290 
0291   m_tabs->setCurrentIndex(0);
0292 
0293   m_isWorking = false;
0294   slotHandleNew();
0295 }
0296 
0297 void EntryEditDialog::slotHandleNew() {
0298   if(!m_currColl || !queryModified()) {
0299     return;
0300   }
0301 
0302   m_tabs->setCurrentIndex(0);
0303   m_tabs->setFocusToFirstChild();
0304   clear();
0305   m_isWorking = true; // clear() will get called again
0306   if(!signalsBlocked()) {
0307     Controller::self()->slotClearSelection();
0308   }
0309   m_isWorking = false;
0310 
0311   Data::EntryPtr entry(new Data::Entry(m_currColl));
0312   m_currEntries.append(entry);
0313   m_isOrphan = true;
0314 }
0315 
0316 void EntryEditDialog::slotHandleSave() {
0317   if(!m_currColl || m_isWorking) {
0318     return;
0319   }
0320 
0321   m_isWorking = true;
0322 
0323   if(m_currEntries.isEmpty()) {
0324     myDebug() << "creating new entry";
0325     m_currEntries.append(Data::EntryPtr(new Data::Entry(m_currColl)));
0326     m_isOrphan = true;
0327   }
0328 
0329   // add a message box if multiple items are selected
0330   if(m_currEntries.count() > 1) {
0331     QStringList names;
0332     foreach(Data::EntryPtr entry, m_currEntries) {
0333       names += entry->title();
0334     }
0335     QString str(i18n("Do you really want to modify these entries?"));
0336     QString dontAsk = QStringLiteral("SaveMultipleBooks"); // don't change 'books', invisible anyway
0337 #if KWIDGETSADDONS_VERSION < QT_VERSION_CHECK(5, 100, 0)
0338     auto ret = KMessageBox::questionYesNoList(this, str, names, i18n("Modify Multiple Entries"),
0339                                               KStandardGuiItem::cont(), KStandardGuiItem::cancel(), dontAsk);
0340     if(ret != KMessageBox::Yes) {
0341 #else
0342     auto ret = KMessageBox::questionTwoActionsList(this, str, names, i18n("Modify Multiple Entries"),
0343                                                    KStandardGuiItem::cont(), KStandardGuiItem::cancel(), dontAsk);
0344     if(ret != KMessageBox::ButtonCode::PrimaryAction) {
0345 #endif
0346       m_isWorking = false;
0347       return;
0348     }
0349   }
0350 
0351   GUI::CursorSaver cs;
0352 
0353   Data::EntryList oldEntries;
0354   Data::FieldList fieldsRequiringValues;
0355   // boolean to keep track if any field gets changed
0356   bool modified = false;
0357   foreach(Data::EntryPtr entry, m_currEntries) {
0358     // if the entry is owned, then we're modifying an existing entry, keep a copy of the old one
0359     if(entry->isOwned()) {
0360       oldEntries.append(Data::EntryPtr(new Data::Entry(*entry)));
0361     }
0362     foreach(Data::FieldPtr field, m_modifiedFields) {
0363       QString key = QString::number(m_currColl->id()) + field->name();
0364       GUI::FieldWidget* widget = m_widgetDict.value(key);
0365       if(widget && widget->isEnabled()) {
0366         const QString temp = widget->text();
0367         // ok to set field empty string, just not all of them
0368         if(modified == false && entry->field(field) != temp) {
0369           modified = true;
0370         }
0371         entry->setField(field, temp);
0372         if(temp.isEmpty()) {
0373           const QString prop = field->property(QStringLiteral("required")).toLower();
0374           if(prop == QLatin1String("1") || prop == QLatin1String("true")) {
0375             fieldsRequiringValues.append(field);
0376           }
0377         }
0378       }
0379     }
0380   }
0381 
0382   if(!fieldsRequiringValues.isEmpty()) {
0383     GUI::CursorSaver cs2(Qt::ArrowCursor);
0384     QString str = i18n("A value is required for the following fields. Do you want to continue?");
0385     QStringList titles;
0386     foreach(Data::FieldPtr it, fieldsRequiringValues) {
0387       titles << it->title();
0388     }
0389     QString dontAsk = QStringLiteral("SaveWithoutRequired");
0390 #if KWIDGETSADDONS_VERSION < QT_VERSION_CHECK(5, 100, 0)
0391     auto ret = KMessageBox::questionYesNoList(this, str, titles, i18n("Modify Entries"),
0392                                               KStandardGuiItem::cont(), KStandardGuiItem::cancel(), dontAsk);
0393     if(ret != KMessageBox::Yes) {
0394 #else
0395     auto ret = KMessageBox::questionTwoActionsList(this, str, titles, i18n("Modify Entries"),
0396                                                    KStandardGuiItem::cont(), KStandardGuiItem::cancel(), dontAsk);
0397     if(ret != KMessageBox::ButtonCode::PrimaryAction) {
0398 #endif
0399       m_isWorking = false;
0400       return;
0401     }
0402   }
0403 
0404   // if something was not empty, signal a save
0405   if(modified) {
0406     m_isOrphan = false;
0407     if(oldEntries.isEmpty()) {
0408       Kernel::self()->addEntries(m_currEntries, false);
0409     } else {
0410       QStringList fieldNames;
0411       foreach(Data::FieldPtr field, m_modifiedFields) {
0412         fieldNames << field->name();
0413       }
0414       Kernel::self()->modifyEntries(oldEntries, m_currEntries, fieldNames);
0415     }
0416     if(!m_currEntries.isEmpty() && !m_currEntries[0]->title().isEmpty()) {
0417       setWindowTitle(i18n("Edit Entry") + QLatin1String(" - ") + m_currEntries[0]->title());
0418     }
0419   }
0420 
0421   m_isWorking = false;
0422   slotSetModified(false);
0423 }
0424 
0425 void EntryEditDialog::clear() {
0426   if(m_isWorking) {
0427     return;
0428   }
0429 
0430   m_isWorking = true;
0431   // clear the widgets
0432   foreach(GUI::FieldWidget* widget, m_widgetDict) {
0433     widget->setEnabled(true);
0434     widget->clear();
0435     widget->insertDefault();
0436   }
0437   m_modifiedFields.clear();
0438 
0439   setWindowTitle(i18n("Edit Entry"));
0440 
0441   if(m_isOrphan) {
0442     if(m_currEntries.count() > 1) {
0443       myWarning() << "is an orphan, but more than one";
0444     }
0445     m_isOrphan = false;
0446   }
0447   m_currEntries.clear();
0448 
0449   m_saveButton->setText(i18n("Sa&ve Entry"));
0450 
0451   m_isWorking = false;
0452   slotSetModified(false);
0453 }
0454 
0455 void EntryEditDialog::setContents(Tellico::Data::EntryList entries_) {
0456   // this slot might get called if we try to save multiple items, so just return
0457   if(m_isWorking) {
0458     return;
0459   }
0460 
0461   if(entries_.isEmpty()) {
0462     if(queryModified()) {
0463       blockSignals(true);
0464       slotHandleNew();
0465       blockSignals(false);
0466     }
0467     return;
0468   }
0469 
0470   // if some entries get selected in one view, then in another, don't reset
0471   if(!m_needReset && entries_ == m_currEntries) {
0472     return;
0473   }
0474   m_needReset = false;
0475 
0476   // first set contents to first item
0477   setEntry(entries_.front());
0478   // something weird...if list count can actually be 1 before the setContents call
0479   // and 0 after it. Why is that? It's const!
0480   if(entries_.count() < 2) {
0481     return;
0482   }
0483 
0484   // multiple entries, so don't set caption
0485   setWindowTitle(i18n("Edit Entries"));
0486 
0487   m_currEntries = entries_;
0488   m_isWorking = true;
0489   blockSignals(true);
0490 
0491   foreach(Data::FieldPtr fIt, m_currColl->fields()) {
0492     QString key = QString::number(m_currColl->id()) + fIt->name();
0493     GUI::FieldWidget* widget = m_widgetDict.value(key);
0494     if(!widget) { // probably read-only
0495       continue;
0496     }
0497     widget->editMultiple(true);
0498 
0499     QString value = entries_[0]->field(fIt);
0500     for(int i = 1; i < entries_.count(); ++i) {  // skip checking the first one
0501       if(entries_[i]->field(fIt) != value) {
0502         widget->setEnabled(false);
0503         break;
0504       }
0505     }
0506   } // end field loop
0507 
0508   blockSignals(false);
0509   m_isWorking = false;
0510 
0511   m_saveButton->setText(i18n("Sa&ve Entries"));
0512 }
0513 
0514 void EntryEditDialog::setEntry(Tellico::Data::EntryPtr entry_) {
0515   if(m_isWorking || !queryModified()) {
0516     return;
0517   }
0518 
0519   if(!entry_) {
0520     myDebug() << "null entry pointer";
0521     slotHandleNew();
0522     return;
0523   }
0524 
0525 //  myDebug() << entry_->title();
0526   blockSignals(true);
0527   clear();
0528   blockSignals(false);
0529 
0530   m_isWorking = true;
0531   m_currEntries.append(entry_);
0532 
0533   if(!entry_->title().isEmpty()) {
0534     setWindowTitle(i18n("Edit Entry") + QLatin1String(" - ") + entry_->title());
0535   }
0536 
0537   if(m_currColl != entry_->collection()) {
0538     myDebug() << "collections don't match";
0539     m_currColl = entry_->collection();
0540   }
0541 
0542   foreach(Data::FieldPtr field, m_currColl->fields()) {
0543     QString key = QString::number(m_currColl->id()) + field->name();
0544     GUI::FieldWidget* widget = m_widgetDict.value(key);
0545     if(!widget) { // is probably read-only
0546       continue;
0547     }
0548 
0549     widget->setText(entry_->field(field));
0550     widget->setEnabled(true);
0551     widget->editMultiple(false);
0552   } // end field loop
0553 
0554   if(entry_->isOwned()) {
0555     m_saveButton->setText(i18n("Sa&ve Entry"));
0556     slotSetModified(false);
0557   } else {
0558     // saving is necessary for unowned entries
0559     slotSetModified(true);
0560   }
0561   m_isWorking = false;
0562 }
0563 
0564 void EntryEditDialog::removeField(Tellico::Data::CollPtr, Tellico::Data::FieldPtr field_) {
0565   if(!field_) {
0566     return;
0567   }
0568 
0569 //  myDebug() << "name = " << field_->name();
0570   QString key = QString::number(m_currColl->id()) + field_->name();
0571   GUI::FieldWidget* widget = m_widgetDict.value(key);
0572   if(widget) {
0573     m_widgetDict.remove(key);
0574     // if this is the last field in the category, need to remove the tab page
0575     // this function is called after the field has been removed from the collection,
0576     // so the category should be gone from the category list
0577     if(m_currColl->fieldCategories().indexOf(field_->category()) == -1) {
0578 //      myDebug() << "last field in the category";
0579       // fragile, widget's parent is the grid, whose parent is the tab page
0580       QWidget* w = widget->parentWidget()->parentWidget();
0581       m_tabs->removeTab(m_tabs->indexOf(w));
0582       delete w; // automatically deletes child widget
0583     } else {
0584       // much of this replicates code in resetLayout()
0585       QGridLayout* layout = static_cast<QGridLayout*>(widget->parentWidget()->layout());
0586       delete widget; // automatically removes from layout
0587 
0588       QVector<bool> expands(NCOLS, false);
0589       QVector<int> maxWidth(NCOLS, 0);
0590 
0591       Data::FieldList vec = m_currColl->fieldsByCategory(field_->category());
0592       int count = 0;
0593       foreach(Data::FieldPtr field, vec) {
0594         GUI::FieldWidget* widget = m_widgetDict.value(QString::number(m_currColl->id()) + field->name());
0595         if(widget) {
0596           layout->removeWidget(widget);
0597           layout->addWidget(widget, count/NCOLS, count%NCOLS);
0598 
0599           maxWidth[count%NCOLS] = qMax(maxWidth[count%NCOLS], widget->labelWidth());
0600           if(widget->expands()) {
0601             expands[count%NCOLS] = true;
0602           }
0603           widget->updateGeometry();
0604           ++count;
0605         }
0606       }
0607 
0608       // now, the labels in a column should all be the same width
0609       count = 0;
0610       foreach(Data::FieldPtr field, vec) {
0611         GUI::FieldWidget* widget = m_widgetDict.value(QString::number(m_currColl->id()) + field->name());
0612         if(widget) {
0613           widget->setLabelWidth(maxWidth[count%NCOLS]);
0614           ++count;
0615         }
0616       }
0617 
0618       // update stretch factors for columns with a line edit
0619       for(int col = 0; col < NCOLS; ++col) {
0620         if(expands[col]) {
0621           layout->setColumnStretch(col, 1);
0622         }
0623       }
0624     }
0625   }
0626 }
0627 
0628 void EntryEditDialog::updateCompletions(Tellico::Data::EntryPtr entry_) {
0629 #ifndef NDEBUG
0630   if(m_currColl != entry_->collection()) {
0631     myDebug() << "inconsistent collection pointers!";
0632 //    m_currColl = entry_->collection();
0633   }
0634 #endif
0635 
0636   foreach(Data::FieldPtr f, m_currColl->fields()) {
0637     if(f->type() != Data::Field::Line
0638        || !f->hasFlag(Data::Field::AllowCompletion)) {
0639       continue;
0640     }
0641 
0642     QString key = QString::number(m_currColl->id()) + f->name();
0643     GUI::FieldWidget* widget = m_widgetDict.value(key);
0644     if(!widget) {
0645       continue;
0646     }
0647     if(f->hasFlag(Data::Field::AllowMultiple)) {
0648       QStringList items = FieldFormat::splitValue(entry_->field(f));
0649       for(QStringList::ConstIterator it = items.constBegin(); it != items.constEnd(); ++it) {
0650         widget->addCompletionObjectItem(*it);
0651       }
0652     } else {
0653       widget->addCompletionObjectItem(entry_->field(f));
0654     }
0655   }
0656 }
0657 
0658 void EntryEditDialog::slotSetModified(bool mod_/*=true*/) {
0659   m_modified = mod_;
0660   m_saveButton->setEnabled(mod_);
0661 }
0662 
0663 bool EntryEditDialog::queryModified() {
0664   bool ok = true;
0665   // assume that if the dialog is hidden, we shouldn't ask the user to modify changes
0666   if(!isVisible()) {
0667     m_modified = false;
0668   }
0669   if(m_modified) {
0670     QString str(i18n("The current entry has been modified.\n"
0671                       "Do you want to enter the changes?"));
0672     KGuiItem item = KStandardGuiItem::save();
0673     item.setText(i18n("Save Entry"));
0674 #if KWIDGETSADDONS_VERSION < QT_VERSION_CHECK(5, 100, 0)
0675     auto want_save = KMessageBox::warningYesNoCancel(this, str, i18n("Unsaved Changes"),
0676                                                      item, KStandardGuiItem::discard());
0677     switch(want_save) {
0678       case KMessageBox::Yes:
0679         slotHandleSave();
0680         ok = true;
0681         break;
0682 
0683       case KMessageBox::No:
0684         m_modified = false;
0685         ok = true;
0686         break;
0687 
0688       case KMessageBox::Cancel:
0689         ok = false;
0690         break;
0691     }
0692 #else
0693     auto want_save = KMessageBox::warningTwoActionsCancel(this, str, i18n("Unsaved Changes"),
0694                                                           item, KStandardGuiItem::discard());
0695     switch(want_save) {
0696       case KMessageBox::ButtonCode::PrimaryAction:
0697         slotHandleSave();
0698         ok = true;
0699         break;
0700 
0701       case KMessageBox::ButtonCode::SecondaryAction:
0702         m_modified = false;
0703         ok = true;
0704         break;
0705 
0706       case KMessageBox::ButtonCode::Cancel:
0707       default:
0708         ok = false;
0709         break;
0710     }
0711 #endif
0712   }
0713   return ok;
0714 }
0715 
0716 void EntryEditDialog::addField(Tellico::Data::CollPtr coll_, Tellico::Data::FieldPtr field_) {
0717   Q_ASSERT(coll_ == m_currColl);
0718   Q_UNUSED(field_);
0719   resetLayout(coll_);
0720 }
0721 
0722 // modified fields will always have the same name
0723 void EntryEditDialog::modifyField(Tellico::Data::CollPtr coll_, Tellico::Data::FieldPtr oldField_, Tellico::Data::FieldPtr newField_) {
0724 //  myDebug() << newField_->name();
0725 
0726   if(coll_ != m_currColl) {
0727     myDebug() << "wrong collection pointer!";
0728     m_currColl = coll_;
0729   }
0730 
0731   // if the field type changed, go ahead and redo the whole layout
0732   // also if the category changed for a non-single field, since a new tab must be created
0733   if(oldField_->type() != newField_->type()
0734      || (oldField_->category() != newField_->category() && !newField_->isSingleCategory())) {
0735     bool modified = m_modified;
0736     resetLayout(coll_);
0737     setContents(m_currEntries);
0738     m_modified = modified;
0739     return;
0740   }
0741 
0742   QString key = QString::number(coll_->id()) + oldField_->name();
0743   GUI::FieldWidget* widget = m_widgetDict.value(key);
0744   if(widget) {
0745     widget->updateField(oldField_, newField_);
0746     // need to update label widths
0747     if(newField_->title() != oldField_->title()) {
0748       int maxWidth = 0;
0749       QList<GUI::FieldWidget*> childList = widget->parentWidget()->findChildren<GUI::FieldWidget*>();
0750       foreach(GUI::FieldWidget* obj, childList) {
0751         maxWidth = qMax(maxWidth, obj->labelWidth());
0752       }
0753       foreach(GUI::FieldWidget* obj, childList) {
0754         obj->setLabelWidth(maxWidth);
0755       }
0756     }
0757     // this is very fragile!
0758     // field widgets's parent is the grid, whose parent is the tab page
0759     // this is for singleCategory fields
0760     if(newField_->category() != oldField_->category()) {
0761       int idx = m_tabs->indexOf(widget->parentWidget()->parentWidget());
0762       if(idx > -1) {
0763         m_tabs->setTabText(idx, newField_->category());
0764       }
0765     }
0766   }
0767 }
0768 
0769 void EntryEditDialog::addEntries(Tellico::Data::EntryList entries_) {
0770   foreach(Data::EntryPtr entry, entries_) {
0771     updateCompletions(entry);
0772   }
0773 }
0774 
0775 void EntryEditDialog::modifyEntries(Tellico::Data::EntryList entries_) {
0776   bool updateContents = false;
0777   foreach(Data::EntryPtr entry, entries_) {
0778     updateCompletions(entry);
0779     if(!updateContents && m_currEntries.contains(entry)) {
0780       updateContents = true;
0781     }
0782   }
0783   if(updateContents) {
0784     m_needReset = true;
0785     setContents(m_currEntries);
0786   }
0787 }
0788 
0789 void EntryEditDialog::fieldValueChanged(Tellico::Data::FieldPtr field_) {
0790   if(!m_modifiedFields.contains(field_)) {
0791     m_modifiedFields.append(field_);
0792   }
0793   slotSetModified(true);
0794 }
0795 
0796 void EntryEditDialog::fieldChanged(Tellico::Data::FieldPtr field_) {
0797   Kernel::self()->modifyField(field_);
0798 }
0799 
0800 void EntryEditDialog::showEvent(QShowEvent* event_) {
0801   QDialog::showEvent(event_);
0802   KConfigGroup config(KSharedConfig::openConfig(), QLatin1String(dialogOptionsString));
0803   KWindowConfig::restoreWindowSize(windowHandle(), config);
0804   resize(windowHandle()->size()); // workaround for QTBUG-40584
0805 }
0806 
0807 void EntryEditDialog::hideEvent(QHideEvent* event_) {
0808   KConfigGroup config(KSharedConfig::openConfig(), QLatin1String(dialogOptionsString));
0809   KWindowConfig::saveWindowSize(windowHandle(), config);
0810   config.sync();
0811 
0812   if(queryModified()) {
0813     QDialog::hideEvent(event_);
0814   } else {
0815     event_->ignore();
0816   }
0817 }
0818 
0819 void EntryEditDialog::closeEvent(QCloseEvent* event_) {
0820   // check to see if an entry should be saved before hiding
0821   // block signals so the entry view and selection isn't cleared
0822   if(queryModified()) {
0823     QDialog::closeEvent(event_);
0824   } else {
0825     event_->ignore();
0826   }
0827 }