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 }