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 }