File indexing completed on 2024-04-28 16:31:57

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