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

0001 /***************************************************************************
0002     Copyright (C) 2003-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 "collectionfieldsdialog.h"
0026 #include "collection.h"
0027 #include "field.h"
0028 #include "fieldformat.h"
0029 #include "collectionfactory.h"
0030 #include "gui/listwidgetitem.h"
0031 #include "gui/stringmapdialog.h"
0032 #include "gui/combobox.h"
0033 #include "tellico_kernel.h"
0034 #include "translators/tellico_xml.h"
0035 #include "utils/string_utils.h"
0036 #include "tellico_debug.h"
0037 
0038 #include <KLocalizedString>
0039 #include <KMessageBox>
0040 #include <KAcceleratorManager>
0041 #include <KHelpClient>
0042 
0043 #include <QPushButton>
0044 #include <QLineEdit>
0045 #include <QLabel>
0046 #include <QRadioButton>
0047 #include <QGroupBox>
0048 #include <QCheckBox>
0049 #include <QRegularExpression>
0050 #include <QTimer>
0051 #include <QHBoxLayout>
0052 #include <QVBoxLayout>
0053 #include <QGridLayout>
0054 #include <QListWidget>
0055 #include <QDialogButtonBox>
0056 
0057 using namespace Tellico;
0058 using Tellico::FieldListItem;
0059 using Tellico::CollectionFieldsDialog;
0060 
0061 class Tellico::FieldListItem : public Tellico::GUI::ListWidgetItem {
0062 public:
0063   FieldListItem(QListWidget* parent_, Data::FieldPtr field_) : GUI::ListWidgetItem(field_->title(), parent_), m_field(field_) {}
0064 
0065   Data::FieldPtr field() const { return m_field; }
0066   void setField(Data::FieldPtr field) { m_field = field; }
0067 
0068 private:
0069   Data::FieldPtr m_field;
0070 };
0071 
0072 CollectionFieldsDialog::CollectionFieldsDialog(Tellico::Data::CollPtr coll_, QWidget* parent_)
0073     : QDialog(parent_),
0074       m_coll(coll_),
0075       m_defaultCollection(nullptr),
0076       m_currentField(nullptr),
0077       m_modified(false),
0078       m_updatingValues(false),
0079       m_reordered(false),
0080       m_oldIndex(-1),
0081       m_notifyMode(NotifyKernel) {
0082   setModal(false);
0083   setWindowTitle(i18n("Collection Fields"));
0084 
0085   QVBoxLayout* mainLayout = new QVBoxLayout();
0086   setLayout(mainLayout);
0087 
0088   QWidget* page = new QWidget(this);
0089   mainLayout->addWidget(page);
0090   QBoxLayout* topLayout = new QHBoxLayout(page);
0091   page->setLayout(topLayout);
0092 
0093   QGroupBox* fieldsGroup = new QGroupBox(i18n("Current Fields"), page);
0094   QBoxLayout* fieldsLayout = new QVBoxLayout(fieldsGroup);
0095   topLayout->addWidget(fieldsGroup, 1);
0096 
0097   m_fieldsWidget = new QListWidget(fieldsGroup);
0098   m_fieldsWidget->setMinimumWidth(150);
0099   fieldsLayout->addWidget(m_fieldsWidget);
0100 
0101   Data::FieldList fields = m_coll->fields();
0102   foreach(Data::FieldPtr field, fields) {
0103     // ignore fields which are not user-editable
0104     if(!field->hasFlag(Data::Field::NoEdit)) {
0105       (void) new FieldListItem(m_fieldsWidget, field);
0106     }
0107   }
0108   connect(m_fieldsWidget, &QListWidget::currentRowChanged, this, &CollectionFieldsDialog::slotHighlightedChanged);
0109 
0110   QWidget* hb1 = new QWidget(fieldsGroup);
0111   QHBoxLayout* hb1HBoxLayout = new QHBoxLayout(hb1);
0112   hb1HBoxLayout->setMargin(0);
0113   fieldsLayout->addWidget(hb1);
0114   m_btnNew = new QPushButton(i18nc("New Field", "&New"), hb1);
0115   hb1HBoxLayout->addWidget(m_btnNew);
0116   m_btnNew->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
0117   m_btnNew->setWhatsThis(i18n("Add a new field to the collection"));
0118   m_btnDelete = new QPushButton(i18nc("Delete Field", "Delete"), hb1);
0119   hb1HBoxLayout->addWidget(m_btnDelete);
0120   m_btnDelete->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
0121   m_btnDelete->setWhatsThis(i18n("Remove a field from the collection"));
0122 
0123   connect(m_btnNew, &QAbstractButton::clicked, this, &CollectionFieldsDialog::slotNew);
0124   connect(m_btnDelete, &QAbstractButton::clicked, this, &CollectionFieldsDialog::slotDelete);
0125 
0126   QWidget* hb2 = new QWidget(fieldsGroup);
0127   QHBoxLayout* hb2HBoxLayout = new QHBoxLayout(hb2);
0128   hb2HBoxLayout->setMargin(0);
0129   fieldsLayout->addWidget(hb2);
0130   m_btnUp = new QPushButton(hb2);
0131   hb2HBoxLayout->addWidget(m_btnUp);
0132   m_btnUp->setIcon(QIcon::fromTheme(QStringLiteral("go-up")));
0133   m_btnUp->setWhatsThis(i18n("Move this field up in the list. The list order is important "
0134                              "for the layout of the entry editor."));
0135   m_btnDown = new QPushButton(hb2);
0136   hb2HBoxLayout->addWidget(m_btnDown);
0137   m_btnDown->setIcon(QIcon::fromTheme(QStringLiteral("go-down")));
0138   m_btnDown->setWhatsThis(i18n("Move this field down in the list. The list order is important "
0139                                "for the layout of the entry editor."));
0140 
0141   connect(m_btnUp, &QAbstractButton::clicked, this, &CollectionFieldsDialog::slotMoveUp);
0142   connect(m_btnDown, &QAbstractButton::clicked, this, &CollectionFieldsDialog::slotMoveDown);
0143 
0144   QWidget* vbox = new QWidget(page);
0145   QVBoxLayout* vboxVBoxLayout = new QVBoxLayout(vbox);
0146   vboxVBoxLayout->setMargin(0);
0147   topLayout->addWidget(vbox, 2);
0148 
0149   QGroupBox* propGroup = new QGroupBox(i18n("Field Properties"), vbox);
0150   vboxVBoxLayout->addWidget(propGroup);
0151   QBoxLayout* propLayout = new QVBoxLayout(propGroup);
0152 
0153   QWidget* grid = new QWidget(propGroup);
0154   QGridLayout* layout = new QGridLayout(grid);
0155   propLayout->addWidget(grid);
0156 
0157   int row = -1;
0158   QLabel* label = new QLabel(i18n("&Title:"), grid);
0159   layout->addWidget(label, ++row, 0);
0160   m_titleEdit = new QLineEdit(grid);
0161   layout->addWidget(m_titleEdit, row, 1);
0162   label->setBuddy(m_titleEdit);
0163   QString whats = i18n("The title of the field");
0164   label->setWhatsThis(whats);
0165   m_titleEdit->setWhatsThis(whats);
0166   connect(m_titleEdit, &QLineEdit::textChanged, this, &CollectionFieldsDialog::slotModified);
0167 
0168   label = new QLabel(i18n("T&ype:"), grid);
0169   layout->addWidget(label, row, 2);
0170   m_typeCombo = new KComboBox(grid);
0171   layout->addWidget(m_typeCombo, row, 3);
0172   label->setBuddy(m_typeCombo);
0173   whats = QStringLiteral("<qt>");
0174   whats += i18n("The type of the field determines what values may be used. ");
0175   whats += i18n("<i>Simple Text</i> is used for most fields. ");
0176   whats += i18n("<i>Paragraph</i> is for large text blocks. ");
0177   whats += i18n("<i>Choice</i> limits the field to certain values. ");
0178   whats += i18n("<i>Checkbox</i> is for a simple yes/no value. ");
0179   whats += i18n("<i>Number</i> indicates that the field contains a numerical value. ");
0180   whats += i18n("<i>URL</i> is for fields which refer to URLs, including references to other files. ");
0181   whats += i18n("A <i>Table</i> may hold one or more columns of values. ");
0182   whats += i18n("An <i>Image</i> field holds a picture. ");
0183   whats += i18n("A <i>Date</i> field can be used for values with a day, month, and year. ");
0184   whats += i18n("A <i>Rating</i> field uses stars to show a rating number. ");
0185   whats += QLatin1String("</qt>");
0186   label->setWhatsThis(whats);
0187   m_typeCombo->setWhatsThis(whats);
0188   // the typeTitles match the fieldMap().values() but in a better order
0189   m_typeCombo->addItems(Data::Field::typeTitles());
0190   void (QComboBox::* activatedInt)(int) = &QComboBox::activated;
0191   connect(m_typeCombo, activatedInt, this, &CollectionFieldsDialog::slotModified);
0192 #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
0193   void (QComboBox::* activatedString)(const QString&) = &QComboBox::activated;
0194   connect(m_typeCombo, activatedString, this, &CollectionFieldsDialog::slotTypeChanged);
0195 #else
0196   connect(m_typeCombo, &QComboBox::textActivated, this, &CollectionFieldsDialog::slotTypeChanged);
0197 #endif
0198 
0199   label = new QLabel(i18n("Cate&gory:"), grid);
0200   layout->addWidget(label, ++row, 0);
0201   m_catCombo = new KComboBox(true, grid);
0202   layout->addWidget(m_catCombo, row, 1);
0203   label->setBuddy(m_catCombo);
0204   whats = i18n("The field category determines where the field is placed in the editor.");
0205   label->setWhatsThis(whats);
0206   m_catCombo->setWhatsThis(whats);
0207 
0208   // I don't want to include the categories for singleCategory fields
0209   QStringList cats;
0210   const QStringList allCats = m_coll->fieldCategories();
0211   foreach(const QString& cat, allCats) {
0212     Data::FieldList fields = m_coll->fieldsByCategory(cat);
0213     if(!fields.isEmpty() && !fields.at(0)->isSingleCategory()) {
0214       cats.append(cat);
0215     }
0216   }
0217   m_catCombo->addItems(cats);
0218   m_catCombo->setDuplicatesEnabled(false);
0219   connect(m_catCombo, &QComboBox::currentTextChanged, this, &CollectionFieldsDialog::slotModified);
0220 
0221   m_btnExtended = new QPushButton(i18n("Set &properties..."), grid);
0222   m_btnExtended->setIcon(QIcon::fromTheme(QStringLiteral("bookmarks")));
0223   layout->addWidget(m_btnExtended, row, 2, 1, 2);
0224   label->setBuddy(m_btnExtended);
0225   whats = i18n("Extended field properties are used to specify things such as the corresponding bibtex field.");
0226   label->setWhatsThis(whats);
0227   m_btnExtended->setWhatsThis(whats);
0228   connect(m_btnExtended, &QAbstractButton::clicked, this, &CollectionFieldsDialog::slotShowExtendedProperties);
0229 
0230   label = new QLabel(i18n("Description:"), grid);
0231   layout->addWidget(label, ++row, 0);
0232   m_descEdit = new QLineEdit(grid);
0233   m_descEdit->setMinimumWidth(150);
0234   layout->addWidget(m_descEdit, row, 1, 1, 3);
0235   label->setBuddy(m_descEdit);
0236 
0237   whats = i18n("The description is a useful reminder of what information is contained in the field.");
0238   label->setWhatsThis(whats);
0239   m_descEdit->setWhatsThis(whats);
0240   connect(m_descEdit, &QLineEdit::textChanged, this, &CollectionFieldsDialog::slotModified);
0241 
0242   QGroupBox* valueGroup = new QGroupBox(i18n("Value Options"), vbox);
0243   vboxVBoxLayout->addWidget(valueGroup);
0244   QGridLayout* valueLayout = new QGridLayout(valueGroup);
0245   int valueRow = -1;
0246 
0247   label = new QLabel(i18n("Default value:"), valueGroup);
0248   valueLayout->addWidget(label, ++valueRow, 0);
0249   m_defaultEdit = new QLineEdit(valueGroup);
0250   valueLayout->addWidget(m_defaultEdit, valueRow, 1, 1, 3);
0251   label->setBuddy(m_defaultEdit);
0252   whats = i18n("<qt>A default value can be set for new entries.</qt>");
0253   label->setWhatsThis(whats);
0254   m_defaultEdit->setWhatsThis(whats);
0255   connect(m_defaultEdit, &QLineEdit::textChanged, this, &CollectionFieldsDialog::slotModified);
0256 
0257   label = new QLabel(i18n("Value template:"), valueGroup);
0258   valueLayout->addWidget(label, ++valueRow, 0);
0259   m_derivedEdit = new QLineEdit(valueGroup);
0260   m_derivedEdit->setMinimumWidth(150);
0261   valueLayout->addWidget(m_derivedEdit, valueRow, 1);
0262   label->setBuddy(m_derivedEdit);
0263 
0264   /* TRANSLATORS: Do not translate %{year} and %{title}. */
0265   whats = i18n("Derived values are formed from the values of other fields according to the value template. "
0266                "Named fields, such as \"%{year} %{title}\", get substituted in the value.");
0267   label->setWhatsThis(whats);
0268   m_derivedEdit->setWhatsThis(whats);
0269   connect(m_derivedEdit, &QLineEdit::textChanged, this, &CollectionFieldsDialog::slotModified);
0270 
0271   m_derived = new QCheckBox(i18n("Use derived value"), valueGroup);
0272   m_derived->setWhatsThis(whats);
0273   valueLayout->addWidget(m_derived, valueRow, 2, 1, 2);
0274   connect(m_derived, &QAbstractButton::clicked, this, &CollectionFieldsDialog::slotDerivedChecked);
0275   connect(m_derived, &QAbstractButton::clicked, this, &CollectionFieldsDialog::slotModified);
0276 
0277   label = new QLabel(i18n("A&llowed values:"), valueGroup);
0278   valueLayout->addWidget(label, ++valueRow, 0);
0279   m_allowEdit = new QLineEdit(valueGroup);
0280   valueLayout->addWidget(m_allowEdit, valueRow, 1, 1, 3);
0281   label->setBuddy(m_allowEdit);
0282   whats = i18n("<qt>For <i>Choice</i>-type fields, these are the only values allowed. They are "
0283                "placed in a combo box. The possible values have to be separated by a semi-colon, "
0284                "for example: \"dog; cat; mouse\"</qt>");
0285   label->setWhatsThis(whats);
0286   m_allowEdit->setWhatsThis(whats);
0287   connect(m_allowEdit, &QLineEdit::textChanged, this, &CollectionFieldsDialog::slotModified);
0288 
0289   label = new QLabel(i18n("Format options:"), valueGroup);
0290   valueLayout->addWidget(label, ++valueRow, 0);
0291   m_formatCombo = new GUI::ComboBox(valueGroup);
0292   valueLayout->addWidget(m_formatCombo, valueRow, 1, 1, 3);
0293   label->setBuddy(m_formatCombo);
0294 
0295   m_formatCombo->addItem(i18n("No formatting"), FieldFormat::FormatNone);
0296   m_formatCombo->addItem(i18n("Allow auto-capitalization only"), FieldFormat::FormatPlain);
0297   m_formatCombo->addItem(i18n("Format as a title"), FieldFormat::FormatTitle);
0298   m_formatCombo->addItem(i18n("Format as a name"), FieldFormat::FormatName);
0299   void (QComboBox::* currentIndexChanged)(int) = &QComboBox::currentIndexChanged;
0300   connect(m_formatCombo, currentIndexChanged, this, &CollectionFieldsDialog::slotModified);
0301 
0302   QGroupBox* optionsGroup = new QGroupBox(i18n("Field Options"), vbox);
0303   vboxVBoxLayout->addWidget(optionsGroup);
0304   QBoxLayout* optionsLayout = new QVBoxLayout(optionsGroup);
0305   m_complete = new QCheckBox(i18n("Enable auto-completion"), optionsGroup);
0306   m_complete->setWhatsThis(i18n("If checked, KDE auto-completion will be enabled in the "
0307                                 "text edit box for this field."));
0308   m_multiple = new QCheckBox(i18n("Allow multiple values"), optionsGroup);
0309   m_multiple->setWhatsThis(i18n("If checked, Tellico will parse the values in the field "
0310                                 "for multiple values, separated by a semi-colon."));
0311   m_grouped = new QCheckBox(i18n("Allow grouping"), optionsGroup);
0312   m_grouped->setWhatsThis(i18n("If checked, this field may be used to group the entries in "
0313                                "the group view."));
0314   optionsLayout->addWidget(m_complete);
0315   optionsLayout->addWidget(m_multiple);
0316   optionsLayout->addWidget(m_grouped);
0317 
0318   connect(m_complete, &QAbstractButton::clicked, this, &CollectionFieldsDialog::slotModified);
0319   connect(m_multiple, &QAbstractButton::clicked, this, &CollectionFieldsDialog::slotModified);
0320   connect(m_grouped, &QAbstractButton::clicked, this, &CollectionFieldsDialog::slotModified);
0321 
0322   // need to stretch at bottom
0323   vboxVBoxLayout->addStretch(1);
0324 
0325   // keep a default collection
0326   m_defaultCollection = CollectionFactory::collection(m_coll->type(), true);
0327 
0328   m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|
0329                                      QDialogButtonBox::Cancel|
0330                                      QDialogButtonBox::Help|
0331                                      QDialogButtonBox::RestoreDefaults|
0332                                      QDialogButtonBox::Apply);
0333   mainLayout->addWidget(m_buttonBox);
0334 
0335   QPushButton* okButton = m_buttonBox->button(QDialogButtonBox::Ok);
0336   okButton->setDefault(true);
0337   okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
0338   connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
0339   connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0340   connect(m_buttonBox, &QDialogButtonBox::helpRequested, this, &CollectionFieldsDialog::slotHelp);
0341 
0342   m_buttonBox->button(QDialogButtonBox::RestoreDefaults)->setWhatsThis(i18n("Revert the selected field's properties to the default values."));
0343 
0344   connect(okButton, &QAbstractButton::clicked, this, &CollectionFieldsDialog::slotOk);
0345   connect(m_buttonBox->button(QDialogButtonBox::Apply), &QAbstractButton::clicked, this, &CollectionFieldsDialog::slotApply);
0346   connect(m_buttonBox->button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, this, &CollectionFieldsDialog::slotDefault);
0347 
0348   okButton->setEnabled(false);
0349   m_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
0350   m_buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(false);
0351 
0352   // initially the m_typeCombo is populated with all types, but as soon as something is
0353   // selected in the fields box, the combo box is cleared and filled with the allowable
0354   // new types. The problem is that when more types are added, the size of the combo box
0355   // doesn't change. So when everything is laid out, the combo box needs to have all the
0356   // items there.
0357   QTimer::singleShot(0, this, &CollectionFieldsDialog::slotSelectInitial);
0358 }
0359 
0360 CollectionFieldsDialog::~CollectionFieldsDialog() {
0361 }
0362 
0363 void CollectionFieldsDialog::setNotifyKernel(bool notify_) {
0364   if(notify_) {
0365     m_notifyMode = NotifyKernel;
0366   } else {
0367     m_notifyMode = NoNotification;
0368   }
0369 }
0370 
0371 void CollectionFieldsDialog::slotSelectInitial() {
0372   // the accel management is here so that it doesn't cause conflicts with the
0373   // ones explicitly set in the constructor
0374   KAcceleratorManager::manage(this);
0375   m_fieldsWidget->setCurrentRow(0);
0376 }
0377 
0378 void CollectionFieldsDialog::slotHelp() {
0379   KHelpClient::invokeHelp(QStringLiteral("fields-dialog"));
0380 }
0381 
0382 void CollectionFieldsDialog::slotOk() {
0383   slotApply();
0384   accept();
0385 }
0386 
0387 void CollectionFieldsDialog::slotApply() {
0388   updateField();
0389   if(!checkValues()) {
0390     return;
0391   }
0392 
0393   applyChanges();
0394 }
0395 
0396 void CollectionFieldsDialog::applyChanges() {
0397   // start a command group, "Modify" is a generic term here since the commands could be add, modify, or delete
0398   if(m_notifyMode == NotifyKernel) {
0399     Kernel::self()->beginCommandGroup(i18n("Modify Fields"));
0400   }
0401 
0402   foreach(Data::FieldPtr field, m_copiedFields) {
0403     // check for Choice fields with removed values to warn user
0404     if(field->type() == Data::Field::Choice || field->type() == Data::Field::Rating) {
0405       QStringList oldValues = m_coll->fieldByName(field->name())->allowed();
0406       QStringList newValues = field->allowed();
0407       for(QStringList::ConstIterator vIt = oldValues.constBegin(); vIt != oldValues.constEnd(); ++vIt) {
0408         if(newValues.contains(*vIt)) {
0409           continue;
0410         }
0411         int ret = KMessageBox::warningContinueCancel(this,
0412                                                      i18n("<qt>Removing allowed values from the <i>%1</i> field which "
0413                                                           "currently exist in the collection may cause data corruption. "
0414                                                           "Do you want to keep your modified values or cancel and revert "
0415                                                           "to the current ones?</qt>", field->title()),
0416                                                      QString(),
0417                                                      KGuiItem(i18n("Keep modified values")));
0418         if(ret != KMessageBox::Continue) {
0419           if(field->type() == Data::Field::Choice) {
0420             field->setAllowed(oldValues);
0421           } else { // rating field
0422             Data::FieldPtr oldField = m_coll->fieldByName(field->name());
0423             field->setProperty(QStringLiteral("minimum"), oldField->property(QStringLiteral("minimum")));
0424             field->setProperty(QStringLiteral("maximum"), oldField->property(QStringLiteral("maximum")));
0425           }
0426         }
0427         break;
0428       }
0429     }
0430     if(m_notifyMode == NotifyKernel) {
0431       Kernel::self()->modifyField(field);
0432     } else {
0433       m_coll->modifyField(field);
0434     }
0435   }
0436 
0437   foreach(Data::FieldPtr field, m_newFields) {
0438     if(m_notifyMode == NotifyKernel) {
0439       Kernel::self()->addField(field);
0440     } else {
0441       m_coll->addField(field);
0442     }
0443   }
0444 
0445   // set all text not to be colored, and get new list
0446   Data::FieldList fields;
0447   fields.reserve(m_fieldsWidget->count());
0448   for(int i = 0; i < m_fieldsWidget->count(); ++i) {
0449     FieldListItem* item = static_cast<FieldListItem*>(m_fieldsWidget->item(i));
0450     item->setColored(false);
0451     if(m_reordered) {
0452       Data::FieldPtr field = item->field();
0453       if(field) {
0454         fields.append(field);
0455       }
0456     }
0457   }
0458 
0459   // if reordering fields, need to add ReadOnly fields since they were not shown
0460   if(m_reordered) {
0461     foreach(Data::FieldPtr field, m_coll->fields()) {
0462       if(field->hasFlag(Data::Field::NoEdit)) {
0463         fields.append(field);
0464       }
0465     }
0466   }
0467 
0468   if(fields.count() > 0) {
0469     if(m_notifyMode == NotifyKernel) {
0470       Kernel::self()->reorderFields(fields);
0471     } else {
0472       m_coll->reorderFields(fields);
0473     }
0474   }
0475 
0476   // commit command group
0477   if(m_notifyMode == NotifyKernel) {
0478     Kernel::self()->endCommandGroup();
0479   }
0480 
0481   // now clear copied fields
0482   m_copiedFields.clear();
0483   // clear new ones, too
0484   m_newFields.clear();
0485 
0486   m_currentField = static_cast<FieldListItem*>(m_fieldsWidget->currentItem())->field();
0487 
0488   // the field type might have changed, so need to update the type combo list with possible values
0489   if(m_currentField) {
0490     // set the updating flag since the values are changing and slots are firing
0491     // but we don't care about UI indications of changes
0492     bool wasUpdating = m_updatingValues;
0493     m_updatingValues = true;
0494     QString currType = m_typeCombo->currentText();
0495     m_typeCombo->clear();
0496     m_typeCombo->addItems(newTypesAllowed(m_currentField->type()));
0497     m_typeCombo->setCurrentItem(currType);
0498     // template might have been changed for dependent fields
0499     m_derivedEdit->setText(m_currentField->property(QStringLiteral("template")));
0500     m_updatingValues = wasUpdating;
0501   }
0502   m_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
0503 }
0504 
0505 void CollectionFieldsDialog::slotNew() {
0506   // first update the current one with all the values from the edit widgets
0507   updateField();
0508 
0509   // next check old values
0510   if(!checkValues()) {
0511     return;
0512   }
0513 
0514   QString name = QLatin1String("custom") + QString::number(m_newFields.count()+1);
0515   int count = m_newFields.count() + 1;
0516   QString title = i18n("New Field %1", count);
0517   while(!m_fieldsWidget->findItems(title, Qt::MatchExactly).isEmpty()) {
0518     ++count;
0519     title = i18n("New Field %1", count);
0520   }
0521 
0522   Data::FieldPtr field(new Data::Field(name, title));
0523   m_newFields.append(field);
0524 //  myDebug() << "adding new field " << title;
0525 
0526   m_currentField = field;
0527 
0528   FieldListItem* item = new FieldListItem(m_fieldsWidget, field);
0529   item->setColored(true);
0530   m_fieldsWidget->setCurrentItem(item);
0531   m_fieldsWidget->scrollToItem(item);
0532   slotModified();
0533   m_titleEdit->setFocus();
0534   m_titleEdit->selectAll();
0535 }
0536 
0537 void CollectionFieldsDialog::slotDelete() {
0538   if(!m_currentField) {
0539     return;
0540   }
0541 
0542   if(m_newFields.contains(m_currentField)) {
0543     // remove field from vector before deleting item containing field
0544     m_newFields.removeAll(m_currentField);
0545   } else {
0546     if(m_notifyMode == NotifyKernel) {
0547       if(!Kernel::self()->removeField(m_currentField)) {
0548         return;
0549       }
0550     } else {
0551       m_coll->removeField(m_currentField);
0552     }
0553     emit signalCollectionModified();
0554     m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
0555   }
0556   int currentRow = m_fieldsWidget->currentRow();
0557   delete m_fieldsWidget->takeItem(currentRow);
0558   m_fieldsWidget->setCurrentRow(qMin(currentRow, m_fieldsWidget->count()-1));
0559   m_fieldsWidget->scrollToItem(m_fieldsWidget->currentItem());
0560   m_currentField = static_cast<FieldListItem*>(m_fieldsWidget->currentItem())->field(); // QSharedData gets auto-deleted
0561 }
0562 
0563 void CollectionFieldsDialog::slotTypeChanged(const QString& type_) {
0564   Data::Field::Type type = Data::Field::Undef;
0565   const Data::Field::FieldMap fieldMap = Data::Field::typeMap();
0566   for(Data::Field::FieldMap::ConstIterator it = fieldMap.begin(); it != fieldMap.end(); ++it) {
0567     if(it.value() == type_) {
0568       type = it.key();
0569       break;
0570     }
0571   }
0572   if(type == Data::Field::Undef) {
0573     myWarning() << "type name not recognized:" << type_;
0574     type = Data::Field::Line;
0575   }
0576 
0577   // only choice types gets allowed values
0578   m_allowEdit->setEnabled(type == Data::Field::Choice);
0579 
0580   // paragraphs, tables, and images are their own category
0581   bool isCategory = (type == Data::Field::Para || type == Data::Field::Table ||
0582                      type == Data::Field::Image);
0583   m_catCombo->setEnabled(!isCategory);
0584 
0585   // formatting is only applicable when the type is simple text or a table
0586   bool isText = (type == Data::Field::Line || type == Data::Field::Table);
0587   // formatNone is the default
0588   m_formatCombo->setEnabled(isText);
0589 
0590   // multiple is only applicable for simple text and number
0591   isText = (type == Data::Field::Line || type == Data::Field::Number);
0592   m_multiple->setEnabled(isText);
0593 
0594   // completion is only applicable for simple text, number, and URL
0595   isText = (isText || type == Data::Field::URL);
0596   m_complete->setEnabled(isText);
0597 
0598   // grouping is not possible with paragraphs or images
0599   m_grouped->setEnabled(type != Data::Field::Para && type != Data::Field::Image);
0600 }
0601 
0602 void CollectionFieldsDialog::slotHighlightedChanged(int index_) {
0603   if(index_ == m_oldIndex) {
0604     return; // state when checkValues() returns false
0605   }
0606 
0607   // use this instead of blocking signals everywhere
0608   m_updatingValues = true;
0609 
0610   // first update the current one with all the values from the edit widgets
0611   updateField();
0612 
0613   // next check old values
0614   if(!checkValues()) {
0615     // Other functions get called and change selection after this one. Use a SingleShot to revert
0616     QTimer::singleShot(0, this, &CollectionFieldsDialog::resetToCurrent);
0617     m_updatingValues = false;
0618     return;
0619   }
0620   m_oldIndex = index_;
0621 
0622   m_btnUp->setEnabled(index_ > 0);
0623   m_btnDown->setEnabled(index_ < static_cast<int>(m_fieldsWidget->count())-1);
0624 
0625   FieldListItem* item = dynamic_cast<FieldListItem*>(m_fieldsWidget->item(index_));
0626   if(!item) {
0627     return;
0628   }
0629 
0630   // need to get a pointer to the field with the new values to insert
0631   Data::FieldPtr field = item->field();
0632   if(!field) {
0633     myDebug() << "no field found!";
0634     return;
0635   }
0636 
0637   // type is limited to certain types, unless it's a new field
0638   m_typeCombo->clear();
0639   if(m_newFields.contains(field)) {
0640     m_typeCombo->addItems(newTypesAllowed(Data::Field::Undef));
0641   } else {
0642     m_typeCombo->addItems(newTypesAllowed(field->type()));
0643   }
0644   populate(field);
0645 
0646   // default button is enabled only if default collection contains the field
0647   if(m_defaultCollection) {
0648     const bool hasField = m_defaultCollection->hasField(field->name());
0649     m_buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(hasField);
0650   }
0651 
0652   m_currentField = field;
0653   m_updatingValues = false;
0654 }
0655 
0656 void CollectionFieldsDialog::updateField() {
0657   Data::FieldPtr field = m_currentField;
0658   if(!field || !m_modified) {
0659     return;
0660   }
0661 
0662   // only update name if it's one of the new ones
0663   if(m_newFields.contains(field)) {
0664     // name needs to be a valid XML element name
0665     QString name = XML::elementName(m_titleEdit->text().toLower());
0666     if(name.isEmpty()) { // might end up with empty string
0667       name = QLatin1String("custom") + QString::number(m_newFields.count()+1);
0668     }
0669     while(m_coll->hasField(name)) { // ensure name uniqueness
0670       name += QLatin1String("-new");
0671     }
0672     field->setName(name);
0673   }
0674 
0675   const QString title = m_titleEdit->text().simplified();
0676   updateTitle(title);
0677 
0678   const Data::Field::FieldMap& fieldMap = Data::Field::typeMap();
0679   for(Data::Field::FieldMap::ConstIterator it = fieldMap.begin(); it != fieldMap.end(); ++it) {
0680     if(it.value() == m_typeCombo->currentText()) {
0681       field->setType(it.key());
0682       break;
0683     }
0684   }
0685 
0686   if(field->type() == Data::Field::Choice) {
0687     const QRegularExpression rx(QLatin1String("\\s*;\\s*"));
0688 #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
0689     field->setAllowed(m_allowEdit->text().split(rx, QString::SkipEmptyParts));
0690 #else
0691     field->setAllowed(m_allowEdit->text().split(rx, Qt::SkipEmptyParts));
0692 #endif
0693     field->setProperty(QStringLiteral("minimum"), QString());
0694     field->setProperty(QStringLiteral("maximum"), QString());
0695   } else if(field->type() == Data::Field::Rating) {
0696     QString v = field->property(QStringLiteral("minimum"));
0697     if(v.isEmpty()) {
0698       field->setProperty(QStringLiteral("minimum"), QString::number(1));
0699     }
0700     v = field->property(QStringLiteral("maximum"));
0701     if(v.isEmpty()) {
0702       field->setProperty(QStringLiteral("maximum"), QString::number(5));
0703     }
0704   }
0705 
0706   if(field->isSingleCategory()) {
0707     field->setCategory(field->title());
0708   } else {
0709     QString category = m_catCombo->currentText().simplified();
0710     field->setCategory(category);
0711     m_catCombo->setCurrentItem(category, true); // if it doesn't exist, it's added
0712   }
0713 
0714   if(m_derived->isChecked()) {
0715     field->setProperty(QStringLiteral("template"), m_derivedEdit->text());
0716   }
0717   field->setDescription(m_descEdit->text());
0718   field->setDefaultValue(m_defaultEdit->text());
0719 
0720   if(m_formatCombo->isEnabled()) {
0721     field->setFormatType(static_cast<FieldFormat::Type>(m_formatCombo->currentData().toInt()));
0722   } else {
0723     field->setFormatType(FieldFormat::FormatNone);
0724   }
0725 
0726   int flags = 0;
0727   if(m_derived->isChecked()) {
0728     flags |= Data::Field::Derived;
0729   }
0730   if(m_complete->isChecked()) {
0731     flags |= Data::Field::AllowCompletion;
0732   }
0733   if(m_grouped->isChecked()) {
0734     flags |= Data::Field::AllowGrouped;
0735   }
0736   if(m_multiple->isChecked()) {
0737     flags |= Data::Field::AllowMultiple;
0738   }
0739   field->setFlags(flags);
0740 
0741   m_modified = false;
0742 }
0743 
0744 // The purpose here is to first set the modified flag. Then, if the field being edited is one
0745 // that exists in the collection already, a deep copy needs to be made.
0746 void CollectionFieldsDialog::slotModified() {
0747   // if I'm just updating the values, I don't care
0748   if(m_updatingValues) {
0749     return;
0750   }
0751 
0752   m_modified = true;
0753 
0754   m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
0755   m_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
0756 
0757   if(!m_currentField) {
0758     myDebug() << "no current field!";
0759     m_currentField = static_cast<FieldListItem*>(m_fieldsWidget->currentItem())->field();
0760   }
0761 
0762   // color the text
0763   static_cast<FieldListItem*>(m_fieldsWidget->currentItem())->setColored(true);
0764 
0765   // check if copy exists already
0766   if(m_copiedFields.contains(m_currentField)) {
0767     return;
0768   }
0769 
0770   // or, check if is a new field, in which case no copy is needed
0771   // check if copy exists already
0772   if(m_newFields.contains(m_currentField)) {
0773     return;
0774   }
0775 
0776   m_currentField = new Data::Field(*m_currentField);
0777   m_copiedFields.append(m_currentField);
0778   static_cast<FieldListItem*>(m_fieldsWidget->currentItem())->setField(m_currentField);
0779 }
0780 
0781 void CollectionFieldsDialog::updateTitle(const QString& title_) {
0782   if(m_currentField && m_currentField->title() != title_) {
0783     m_fieldsWidget->blockSignals(true);
0784     FieldListItem* oldItem = findItem(m_currentField);
0785     if(!oldItem) {
0786       return;
0787     }
0788     oldItem->setText(title_);
0789     // will always be colored since it's new
0790     oldItem->setColored(true);
0791 
0792     m_currentField->setTitle(title_);
0793     m_fieldsWidget->blockSignals(false);
0794   }
0795 }
0796 
0797 void CollectionFieldsDialog::slotDefault() {
0798   if(!m_currentField) {
0799     return;
0800   }
0801 
0802   Data::FieldPtr defaultField = m_defaultCollection->fieldByName(m_currentField->name());
0803   if(!defaultField) {
0804     return;
0805   }
0806 
0807   QString caption = i18n("Revert Field Properties");
0808   QString text = i18n("<qt><p>Do you really want to revert the properties for the <em>%1</em> "
0809                       "field back to their default values?</p></qt>", m_currentField->title());
0810   QString dontAsk = QStringLiteral("RevertFieldProperties");
0811   int ret = KMessageBox::warningContinueCancel(this, text, caption, KGuiItem(i18n("Revert")), KStandardGuiItem::cancel(), dontAsk);
0812   if(ret != KMessageBox::Continue) {
0813     return;
0814   }
0815 
0816   // now update all values with default
0817   m_updatingValues = true;
0818   populate(defaultField);
0819   m_updatingValues = false;
0820   slotModified();
0821 }
0822 
0823 void CollectionFieldsDialog::slotMoveUp() {
0824   int idx = m_fieldsWidget->currentRow();
0825   if(idx < 1) {
0826     return;
0827   }
0828   // takeItem ends up signalling that the current index changed
0829   // need to revert m_oldIndex after taking the item
0830   QListWidgetItem* item = m_fieldsWidget->takeItem(idx);
0831   m_oldIndex++;
0832   m_fieldsWidget->insertItem(idx-1, item);
0833   m_fieldsWidget->setCurrentItem(item);
0834   m_reordered = true;
0835   // don't call slotModified() since that creates a deep copy.
0836   m_modified = true;
0837 
0838   m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
0839   m_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
0840 }
0841 
0842 void CollectionFieldsDialog::slotMoveDown() {
0843   int idx = m_fieldsWidget->currentRow();
0844   if(idx > m_fieldsWidget->count()-1) {
0845     return;
0846   }
0847   // takeItem ends up signalling that the current index changed
0848   // need to revert m_oldIndex after taking the item
0849   QListWidgetItem* item = m_fieldsWidget->takeItem(idx);
0850   m_oldIndex--;
0851   m_fieldsWidget->insertItem(idx+1, item);
0852   m_fieldsWidget->setCurrentItem(item);
0853   m_reordered = true;
0854   // don't call slotModified() since that creates a deep copy.
0855   m_modified = true;
0856 
0857   m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
0858   m_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
0859 }
0860 
0861 Tellico::FieldListItem* CollectionFieldsDialog::findItem(Tellico::Data::FieldPtr field_) {
0862   for(int i = 0; i < m_fieldsWidget->count(); ++i) {
0863     FieldListItem* textItem = static_cast<FieldListItem*>(m_fieldsWidget->item(i));
0864     if(textItem->field() == field_) {
0865       return textItem;
0866     }
0867   }
0868   return nullptr;
0869 }
0870 
0871 bool CollectionFieldsDialog::slotShowExtendedProperties() {
0872   if(!m_currentField) {
0873     return false;
0874   }
0875 
0876   // the default value is included in properties, but it has a
0877   // separate edit box
0878   QString dv = m_currentField->defaultValue();
0879   QString dt = m_currentField->property(QStringLiteral("template"));
0880   StringMap props = m_currentField->propertyList();
0881   props.remove(QStringLiteral("default"));
0882   props.remove(QStringLiteral("template"));
0883 
0884   StringMapDialog dlg(props, this, true);
0885   dlg.setWindowTitle(i18n("Extended Field Properties"));
0886   dlg.setLabels(i18n("Property"), i18n("Value"));
0887   if(dlg.exec() == QDialog::Accepted) {
0888     props = dlg.stringMap();
0889     if(!dv.isEmpty()) {
0890       props.insert(QStringLiteral("default"), dv);
0891     }
0892     if(!dt.isEmpty()) {
0893       props.insert(QStringLiteral("template"), dt);
0894     }
0895     m_currentField->setPropertyList(props);
0896     slotModified();
0897     return true;
0898   }
0899   return false;
0900 }
0901 
0902 void CollectionFieldsDialog::slotDerivedChecked(bool checked_) {
0903   m_defaultEdit->setEnabled(!checked_);
0904   m_derivedEdit->setEnabled(checked_);
0905 }
0906 
0907 void CollectionFieldsDialog::resetToCurrent() {
0908   m_fieldsWidget->setCurrentRow(m_oldIndex);
0909 }
0910 
0911 bool CollectionFieldsDialog::checkValues() {
0912   if(!m_currentField) {
0913     return true;
0914   }
0915 
0916   const QString title = m_currentField->title();
0917   // find total number of boxes with this title in case multiple new ones with same title were added
0918   int titleCount = m_fieldsWidget->findItems(title, Qt::MatchExactly).count();
0919   if((m_coll->fieldByTitle(title) && m_coll->fieldNameByTitle(title) != m_currentField->name()) ||
0920      titleCount > 1) {
0921     // already have a field with this title
0922     KMessageBox::sorry(this, i18n("A field with this title already exists. Please enter a different title."));
0923     m_titleEdit->selectAll();
0924     return false;
0925   }
0926 
0927   const QString category = m_currentField->category();
0928   if(category.isEmpty()) {
0929     KMessageBox::sorry(this, i18n("<qt>The category may not be empty. Please enter a category.</qt>"));
0930     m_catCombo->lineEdit()->selectAll();
0931     return false;
0932   }
0933 
0934   Data::FieldList fields = m_coll->fieldsByCategory(category);
0935   if(!fields.isEmpty() && fields[0]->isSingleCategory() && fields[0]->name() != m_currentField->name()) {
0936     // can't have this category, cause it conflicts with a single-category field
0937     KMessageBox::sorry(this, i18n("<qt>A field may not be in the same category as a <em>Paragraph</em>, "
0938                                   "<em>Table</em> or <em>Image</em> field. Please enter a different category.</qt>"));
0939     m_catCombo->lineEdit()->selectAll();
0940     return false;
0941   }
0942 
0943   // the combobox is disabled for single-category fields
0944   if(!m_catCombo->isEnabled() && m_coll->fieldByTitle(title) && m_coll->fieldNameByTitle(title) != m_currentField->name()) {
0945     KMessageBox::sorry(this, i18n("A field's title may not be the same as an existing category. "
0946                                   "Please enter a different title."));
0947     m_titleEdit->selectAll();
0948     return false;
0949   }
0950 
0951   // check for rating values outside bounds
0952   if(m_currentField->type() == Data::Field::Rating) {
0953     bool ok; // ok to ignore this here
0954     int low = Tellico::toUInt(m_currentField->property(QStringLiteral("minimum")), &ok);
0955     int high = Tellico::toUInt(m_currentField->property(QStringLiteral("maximum")), &ok);
0956     while(low < 1 || low > 9 || high < 1 || high > 10 || low >= high) {
0957       KMessageBox::sorry(this, i18n("The range for a rating field must be between 1 and 10, "
0958                                     "and the lower bound must be less than the higher bound. "
0959                                     "Please enter different low and high properties."));
0960       if(slotShowExtendedProperties()) {
0961         low = Tellico::toUInt(m_currentField->property(QStringLiteral("minimum")), &ok);
0962         high = Tellico::toUInt(m_currentField->property(QStringLiteral("maximum")), &ok);
0963       } else {
0964         return false;
0965       }
0966     }
0967   } else if(m_currentField->type() == Data::Field::Table) {
0968     bool ok; // ok to ignore this here
0969     int ncols = Tellico::toUInt(m_currentField->property(QStringLiteral("columns")), &ok);
0970     // also enforced in GUI::TableFieldWidget
0971     if(ncols > 10) {
0972       KMessageBox::sorry(this, i18n("Tables are limited to a maximum of ten columns."));
0973       m_currentField->setProperty(QStringLiteral("columns"), QStringLiteral("10"));
0974     }
0975   }
0976 
0977   if(m_derived->isChecked() && !m_derivedEdit->text().contains(QLatin1Char('%'))) {
0978     KMessageBox::sorry(this, i18n("A field with a derived value must have a value template."));
0979     m_derivedEdit->setFocus();
0980     m_derivedEdit->selectAll();
0981     return false;
0982   }
0983 
0984   return true;
0985 }
0986 
0987 void CollectionFieldsDialog::populate(Data::FieldPtr field_) {
0988   m_titleEdit->setText(field_->title());
0989 
0990   // if the current name is not there, then this will change the list!
0991   const Data::Field::FieldMap& fieldMap = Data::Field::typeMap();
0992   int idx = m_typeCombo->findText(fieldMap[field_->type()]);
0993   m_typeCombo->setCurrentIndex(idx);
0994   slotTypeChanged(fieldMap[field_->type()]); // just setting the text doesn't emit the activated signal
0995 
0996   if(field_->type() == Data::Field::Choice) {
0997     m_allowEdit->setText(field_->allowed().join(FieldFormat::delimiterString()));
0998   } else {
0999     m_allowEdit->clear();
1000   }
1001 
1002   idx = m_catCombo->findText(field_->category());
1003   if(idx > -1) {
1004     m_catCombo->setCurrentIndex(idx); // have to do this here
1005   } else {
1006     m_catCombo->lineEdit()->setText(field_->category());
1007   }
1008   m_descEdit->setText(field_->description());
1009   if(field_->hasFlag(Data::Field::Derived)) {
1010     m_derivedEdit->setText(field_->property(QStringLiteral("template")));
1011     m_derived->setChecked(true);
1012     m_defaultEdit->clear();
1013   } else {
1014     m_derivedEdit->clear();
1015     m_derived->setChecked(false);
1016     m_defaultEdit->setText(field_->defaultValue());
1017   }
1018   slotDerivedChecked(m_derived->isChecked());
1019 
1020   m_formatCombo->setCurrentData(field_->formatType());
1021 
1022   m_complete->setChecked(field_->hasFlag(Data::Field::AllowCompletion));
1023   m_multiple->setChecked(field_->hasFlag(Data::Field::AllowMultiple));
1024   m_grouped->setChecked(field_->hasFlag(Data::Field::AllowGrouped));
1025 
1026   m_btnDelete->setEnabled(!field_->hasFlag(Data::Field::NoDelete));
1027 }
1028 
1029 // only certain type changes are allowed
1030 QStringList CollectionFieldsDialog::newTypesAllowed(int type_ /*=0*/) {
1031   // Undef means return all
1032   if(type_ == Data::Field::Undef) {
1033     return Data::Field::typeTitles();
1034   }
1035 
1036   const Data::Field::FieldMap& fieldMap = Data::Field::typeMap();
1037 
1038   QStringList newTypes;
1039   switch(type_) {
1040     case Data::Field::Line: // might not work if converted to a number or URL, but ok
1041     case Data::Field::Number:
1042     case Data::Field::URL:
1043       newTypes += fieldMap[Data::Field::Line];
1044       newTypes += fieldMap[Data::Field::Para];
1045       newTypes += fieldMap[Data::Field::Number];
1046       newTypes += fieldMap[Data::Field::URL];
1047       newTypes += fieldMap[Data::Field::Table];
1048       break;
1049 
1050     case Data::Field::Date:
1051       newTypes += fieldMap[Data::Field::Line];
1052       newTypes += fieldMap[Data::Field::Date];
1053       break;
1054 
1055     case Data::Field::Bool: // doesn't really make sense, but can't hurt
1056       newTypes += fieldMap[Data::Field::Line];
1057       newTypes += fieldMap[Data::Field::Para];
1058       newTypes += fieldMap[Data::Field::Bool];
1059       newTypes += fieldMap[Data::Field::Number];
1060       newTypes += fieldMap[Data::Field::URL];
1061       newTypes += fieldMap[Data::Field::Table];
1062       break;
1063 
1064     case Data::Field::Choice:
1065       newTypes += fieldMap[Data::Field::Line];
1066       newTypes += fieldMap[Data::Field::Para];
1067       newTypes += fieldMap[Data::Field::Choice];
1068       newTypes += fieldMap[Data::Field::Number];
1069       newTypes += fieldMap[Data::Field::URL];
1070       newTypes += fieldMap[Data::Field::Table];
1071       newTypes += fieldMap[Data::Field::Rating];
1072       break;
1073 
1074     case Data::Field::Table: // not really a good idea since the row delimiter will be exposed, but allow it
1075     case Data::Field::Table2:
1076       newTypes += fieldMap[Data::Field::Line];
1077       newTypes += fieldMap[Data::Field::Number];
1078       newTypes += fieldMap[Data::Field::Table];
1079       break;
1080 
1081     case Data::Field::Para:
1082       newTypes += fieldMap[Data::Field::Line];
1083       newTypes += fieldMap[Data::Field::Para];
1084       break;
1085 
1086     case Data::Field::Rating:
1087       newTypes += fieldMap[Data::Field::Choice];
1088       newTypes += fieldMap[Data::Field::Rating];
1089       break;
1090 
1091     // these can never be changed
1092     case Data::Field::Image:
1093       newTypes += fieldMap[static_cast<Data::Field::Type>(type_)];
1094       break;
1095 
1096     default:
1097       myDebug() << "no match for " << type_;
1098       newTypes = Data::Field::typeTitles();
1099       break;
1100   }
1101   return newTypes;
1102 }