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 }