[office/tellico] /: Add gui class for multi-value Choice fields
Robby Stephenson
null at kde.org
Mon Mar 18 01:31:51 GMT 2024
Git commit 9948ebd5c3790617be86fa5679c17f9985ca5418 by Robby Stephenson.
Committed on 18/03/2024 at 01:31.
Pushed by rstephenson into branch 'master'.
Add gui class for multi-value Choice fields
Taken from the QGIS project at
https://api.qgis.org/api/qgscheckablecombobox_8h_source.html
BUG:483831
FIXED-IN:4.0
M +4 -0 ChangeLog
M +2 -2 doc/details.docbook
M +11 -6 src/collection.cpp
M +13 -9 src/collectionfieldsdialog.cpp
M +3 -4 src/entry.cpp
M +1 -0 src/gui/CMakeLists.txt
A +241 -0 src/gui/checkablecombobox.cpp [License: GPL (v2/3)]
A +104 -0 src/gui/checkablecombobox.h [License: GPL (v2/3)]
M +79 -28 src/gui/choicefieldwidget.cpp
M +9 -0 src/gui/choicefieldwidget.h
https://invent.kde.org/office/tellico/-/commit/9948ebd5c3790617be86fa5679c17f9985ca5418
diff --git a/ChangeLog b/ChangeLog
index 3605776a0..6c78614f3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2024-03-17 Robby Stephenson <robby at periapsis.org>
+
+ * Updated Choice fields to allow multiple values (Bug 483831).
+
2024-03-16 Robby Stephenson <robby at periapsis.org>
* Updated FilmAffinity data source.
diff --git a/doc/details.docbook b/doc/details.docbook
index 772174715..5774185dc 100644
--- a/doc/details.docbook
+++ b/doc/details.docbook
@@ -82,8 +82,8 @@ summaries, or reviews should be entered using this field type.</para>
<para>
When a field should be limited to a few preset values, a
<emphasis>Choice</emphasis> type is used. The acceptable values are
-presented in a drop down box for selection. Obviously, multiple values
-are not applicable. Fields such as bibliography type or personal rating
+presented in a drop down box for selection. Multiple values can be allowed.
+Fields such as bibliography type or personal rating
are <emphasis>Choice</emphasis>-type fields.</para>
<para>Semi-colons should be used to separated the allowed values.</para>
diff --git a/src/collection.cpp b/src/collection.cpp
index 5095d2b32..7e7fb1f5a 100644
--- a/src/collection.cpp
+++ b/src/collection.cpp
@@ -637,19 +637,24 @@ bool Collection::isAllowed(const QString& field_, const QString& value_) const {
return true;
}
- // find the field with a name of 'key_'
FieldPtr field = fieldByName(field_);
-
- // if the type is not multiple choice or if value_ is allowed, return true
- if(field && (field->type() != Field::Choice || field->allowed().contains(value_))) {
+ if(!field) return false;
+ // non-choice or single-value choice fields with alowed values
+ if(field->type() != Field::Choice ||
+ (!field->hasFlag(Field::AllowMultiple) && field->allowed().contains(value_))) {
return true;
}
- return false;
+ // now have to split the text into separate values and check each one
+ const auto values = FieldFormat::splitValue(value_, FieldFormat::StringSplit);
+ for(const auto& value : values) {
+ if(!field->allowed().contains(value)) return false;
+ }
+
+ return true;
}
Tellico::Data::EntryGroupDict* Collection::entryGroupDictByName(const QString& name_) {
-// myDebug() << name_;
m_lastGroupField = name_; // keep track, even if it's invalid
if(name_.isEmpty() || !m_entryGroupDicts.contains(name_) || m_entries.isEmpty()) {
return nullptr;
diff --git a/src/collectionfieldsdialog.cpp b/src/collectionfieldsdialog.cpp
index 6b5f479e3..d375c29ed 100644
--- a/src/collectionfieldsdialog.cpp
+++ b/src/collectionfieldsdialog.cpp
@@ -575,25 +575,29 @@ void CollectionFieldsDialog::slotTypeChanged(const QString& type_) {
m_allowEdit->setEnabled(type == Data::Field::Choice);
// paragraphs, tables, and images are their own category
- bool isCategory = (type == Data::Field::Para || type == Data::Field::Table ||
+ bool isCategory = (type == Data::Field::Para ||
+ type == Data::Field::Table ||
type == Data::Field::Image);
m_catCombo->setEnabled(!isCategory);
// formatting is only applicable when the type is simple text or a table
- bool isText = (type == Data::Field::Line || type == Data::Field::Table);
// formatNone is the default
- m_formatCombo->setEnabled(isText);
+ m_formatCombo->setEnabled(type == Data::Field::Line ||
+ type == Data::Field::Table);
- // multiple is only applicable for simple text and number
- isText = (type == Data::Field::Line || type == Data::Field::Number);
- m_multiple->setEnabled(isText);
+ // multiple is only applicable for simple text, number, and choice
+ m_multiple->setEnabled(type == Data::Field::Line ||
+ type == Data::Field::Number ||
+ type == Data::Field::Choice);
// completion is only applicable for simple text, number, and URL
- isText = (isText || type == Data::Field::URL);
- m_complete->setEnabled(isText);
+ m_complete->setEnabled(type == Data::Field::Line ||
+ type == Data::Field::Number ||
+ type == Data::Field::URL);
// grouping is not possible with paragraphs or images
- m_grouped->setEnabled(type != Data::Field::Para && type != Data::Field::Image);
+ m_grouped->setEnabled(type != Data::Field::Para &&
+ type != Data::Field::Image);
}
void CollectionFieldsDialog::slotHighlightedChanged(int index_) {
diff --git a/src/entry.cpp b/src/entry.cpp
index f7af90e09..583949f08 100644
--- a/src/entry.cpp
+++ b/src/entry.cpp
@@ -241,15 +241,14 @@ bool Entry::setFieldImpl(const QString& name_, const QString& value_) {
return false;
}
- // the string store is probable only useful for fields with auto-completion or choice/number/bool
+ // the string store is probably only useful for fields with auto-completion or choice/number/bool
bool shareType = f->type() == Field::Choice ||
f->type() == Field::Bool ||
f->type() == Field::Image ||
f->type() == Field::Rating ||
f->type() == Field::Number;
- if(!(f->hasFlag(Field::AllowMultiple)) &&
- (shareType ||
- (f->type() == Field::Line && (f->flags() & Field::AllowCompletion)))) {
+ if(!f->hasFlag(Field::AllowMultiple) &&
+ (shareType || (f->type() == Field::Line && f->hasFlag(Field::AllowCompletion)))) {
m_fieldValues.insert(Tellico::shareString(name_), Tellico::shareString(value_));
} else {
m_fieldValues.insert(Tellico::shareString(name_), value_);
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index b19cbbaa5..0157293a5 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -2,6 +2,7 @@
SET(gui_STAT_SRCS
boolfieldwidget.cpp
+ checkablecombobox.cpp
choicefieldwidget.cpp
collectiontemplatedialog.cpp
collectiontypecombo.cpp
diff --git a/src/gui/checkablecombobox.cpp b/src/gui/checkablecombobox.cpp
new file mode 100644
index 000000000..cf04f3d03
--- /dev/null
+++ b/src/gui/checkablecombobox.cpp
@@ -0,0 +1,241 @@
+/***************************************************************************
+ Copyright (C) 2024 Robby Stephenson <robby at periapsis.org>
+ Adapted from code (C) 2017 by Alexander Bruy
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License as *
+ * published by the Free Software Foundation; either version 2 of *
+ * the License or (at your option) version 3 or any later version *
+ * accepted by the membership of KDE e.V. (or its successor approved *
+ * by the membership of KDE e.V.), which shall act as a proxy *
+ * defined in Section 14 of version 3 of the license. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ * *
+ ***************************************************************************/
+
+#include "checkablecombobox.h"
+
+#include <QEvent>
+#include <QMouseEvent>
+#include <QLineEdit>
+#include <QPoint>
+#include <QAbstractItemView>
+
+using Tellico::GUI::CheckableItemModel;
+using Tellico::GUI::CheckBoxDelegate;
+using Tellico::GUI::CheckableComboBox;
+
+CheckableItemModel::CheckableItemModel(QObject* parent_)
+ : QStandardItemModel(0, 1, parent_) {
+}
+
+Qt::ItemFlags CheckableItemModel::flags(const QModelIndex& index_) const {
+ return index_.row() > 0 ? QStandardItemModel::flags(index_) | Qt::ItemIsUserCheckable :
+ QStandardItemModel::flags(index_);
+}
+
+QVariant CheckableItemModel::data(const QModelIndex& index_, int role_) const {
+ QVariant value = QStandardItemModel::data(index_, role_);
+ if(index_.isValid() && role_ == Qt::CheckStateRole && !value.isValid()) {
+ value = Qt::Unchecked;
+ }
+ return value;
+}
+
+bool CheckableItemModel::setData(const QModelIndex& index_, const QVariant& value_, int role_) {
+ const bool ok = QStandardItemModel::setData(index_, value_, role_);
+ if(ok && role_ == Qt::CheckStateRole) {
+ emit itemCheckStateChanged(index_);
+ }
+ emit dataChanged(index_, index_);
+ return ok;
+}
+
+/**************************************************************/
+
+CheckBoxDelegate::CheckBoxDelegate(QObject* parent_)
+ : QStyledItemDelegate(parent_) {
+}
+
+void CheckBoxDelegate::paint(QPainter* painter_, const QStyleOptionViewItem& option_, const QModelIndex& index_) const {
+ QStyleOptionViewItem& nonConstOpt = const_cast<QStyleOptionViewItem &>(option_);
+ nonConstOpt.showDecorationSelected = false;
+ QStyledItemDelegate::paint(painter_, nonConstOpt, index_);
+}
+
+/**************************************************************/
+
+CheckableComboBox::CheckableComboBox(QWidget* parent_)
+ : QComboBox(parent_)
+ , m_separator(QStringLiteral(", ")) {
+ setModel(new CheckableItemModel(this));
+ setItemDelegate(new CheckBoxDelegate(this));
+
+ auto lineEdit = new QLineEdit(this);
+ lineEdit->setReadOnly(true);
+ setLineEdit(lineEdit);
+ lineEdit->installEventFilter(this);
+ view()->viewport()->installEventFilter(this);
+
+ connect(model(), &QStandardItemModel::rowsInserted, this, [=](const QModelIndex&, int, int) { updateDisplayText(); });
+ connect(model(), &QStandardItemModel::rowsRemoved, this, [=](const QModelIndex&, int, int) { updateDisplayText(); });
+ connect(model(), &QStandardItemModel::dataChanged, this, [=](const QModelIndex&, const QModelIndex&, const QVector<int> &) { updateDisplayText(); });
+
+ connect(this, &QComboBox::editTextChanged,
+ this, &CheckableComboBox::setCheckedDataText);
+}
+
+QString CheckableComboBox::separator() const {
+ return m_separator;
+}
+
+void CheckableComboBox::setSeparator(const QString& separator_) {
+ if(m_separator != separator_) {
+ m_separator = separator_;
+ updateDisplayText();
+ }
+}
+
+QStringList CheckableComboBox::checkedItems() const {
+ QStringList items;
+ if(auto* lModel = model()) {
+ const QModelIndex index = lModel->index(0, modelColumn(), rootModelIndex());
+ const QModelIndexList indexes = lModel->match(index, Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchExactly);
+ for(const QModelIndex& index : indexes) {
+ items += index.data().toString();
+ }
+ }
+ return items;
+}
+
+QVariantList CheckableComboBox::checkedItemsData() const {
+ QVariantList data;
+ if(auto* lModel = model()) {
+ const QModelIndex index = lModel->index(0, modelColumn(), rootModelIndex());
+ const QModelIndexList indexes = lModel->match(index, Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchExactly);
+ for(const QModelIndex& index : indexes) {
+ data += index.data(Qt::UserRole).toString();
+ }
+ }
+ return data;
+}
+
+Qt::CheckState CheckableComboBox::itemCheckState(int index_) const {
+ return static_cast<Qt::CheckState>(itemData(index_, Qt::CheckStateRole).toInt());
+}
+
+void CheckableComboBox::setAllCheckState(Qt::CheckState checkState_) {
+ for(int i = 0; i < count(); i++) {
+ setItemData(i, checkState_, Qt::CheckStateRole);
+ }
+ updateCheckedItems();
+}
+
+void CheckableComboBox::hidePopup() {
+ if(!m_skipHide) {
+ QComboBox::hidePopup();
+ }
+ m_skipHide = false;
+}
+
+bool CheckableComboBox::eventFilter(QObject* obj_, QEvent* ev_) {
+ if(obj_ == lineEdit()) {
+ if(ev_->type() == QEvent::MouseButtonPress && static_cast<QMouseEvent*>(ev_)->button() == Qt::LeftButton) {
+ m_skipHide = true;
+ showPopup();
+ }
+ } else if((ev_->type() == QEvent::MouseButtonPress || ev_->type() == QEvent::MouseButtonRelease)
+ && obj_ == view()->viewport()) {
+ m_skipHide = true;
+ if(ev_->type() == QEvent::MouseButtonRelease && static_cast<QMouseEvent*>(ev_)->button() == Qt::RightButton) {
+ return true;
+ }
+
+ if(ev_->type() == QEvent::MouseButtonRelease) {
+ const QModelIndex index = view()->indexAt(static_cast<QMouseEvent*>(ev_)->pos());
+ if(index.isValid()) {
+ auto item = static_cast<CheckableItemModel*>(model())->itemFromIndex(index);
+ // uncheck the first item, if a different item was checked
+ // uncheckk all other items if the first item was checked
+ // remember the chech state hasn't been changed yet. Do this check first so that the state changed signal
+ // gets fired after all items are updated
+ blockSignals(true);
+ if(item->checkState() == Qt::Unchecked) {
+ if(index.row() == 0) {
+ for(int i = 1; i < count(); i++) {
+ setItemData(i, Qt::Unchecked, Qt::CheckStateRole);
+ }
+ } else {
+ setItemData(0, Qt::Unchecked, Qt::CheckStateRole);
+ }
+ }
+ item->setCheckState(item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked);
+ blockSignals(false);
+ updateCheckedItems();
+ }
+ return true;
+ }
+ }
+
+ return QComboBox::eventFilter(obj_, ev_);
+}
+
+void CheckableComboBox::setCheckedData(const QStringList& values_) {
+ blockSignals(true);
+ setItemData(0, values_.isEmpty() ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
+ for(int i = 1; i < count(); i++) {
+ setItemData(i, values_.contains(itemData(i).toString()) ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
+ }
+ blockSignals(false);
+ updateCheckedItems();
+}
+
+// have to update the checked item state after setting the custom text
+void CheckableComboBox::setCheckedDataText(const QString& text_) {
+ blockSignals(true);
+ const auto values = text_.split(m_separator);
+ setItemData(0, text_.isEmpty() ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
+ for(int i = 1; i < count(); i++) {
+ const bool hasValue = values.contains(itemData(i).toString());
+ setItemData(i, hasValue ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
+ }
+ blockSignals(false);
+ updateCheckedItems();
+}
+
+void CheckableComboBox::resizeEvent(QResizeEvent* ev_) {
+ QComboBox::resizeEvent(ev_);
+ updateDisplayText();
+}
+
+void CheckableComboBox::updateCheckedItems() {
+ const QStringList items = checkedItems();
+ updateDisplayText();
+ emit checkedItemsChanged(items);
+}
+
+void CheckableComboBox::updateDisplayText() {
+ // There is only a line edit if the combobox is in editable state
+ if(!lineEdit()) return;
+
+ const QString oldText = lineEdit()->text();
+ QString text;
+ QStringList items = checkedItems();
+ items.removeOne(QString()); // skip empty string
+ if(!items.isEmpty()) {
+ text = items.join(m_separator);
+ }
+ if(oldText != text) {
+ setEditText(text);
+ }
+}
diff --git a/src/gui/checkablecombobox.h b/src/gui/checkablecombobox.h
new file mode 100644
index 000000000..908c9246b
--- /dev/null
+++ b/src/gui/checkablecombobox.h
@@ -0,0 +1,104 @@
+/***************************************************************************
+ Copyright (C) 2024 Robby Stephenson <robby at periapsis.org>
+ Adapted from code (C) 2017 by Alexander Bruy
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License as *
+ * published by the Free Software Foundation; either version 2 of *
+ * the License or (at your option) version 3 or any later version *
+ * accepted by the membership of KDE e.V. (or its successor approved *
+ * by the membership of KDE e.V.), which shall act as a proxy *
+ * defined in Section 14 of version 3 of the license. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ * *
+ ***************************************************************************/
+
+#ifndef TELLICO_GUI_CHECKABLECOMBOBOX_H
+#define TELLICO_GUI_CHECKABLECOMBOBOX_H
+
+#include <QComboBox>
+#include <QMenu>
+#include <QStandardItemModel>
+#include <QStyledItemDelegate>
+
+class QEvent;
+
+namespace Tellico {
+ namespace GUI {
+
+class CheckableItemModel : public QStandardItemModel {
+Q_OBJECT
+
+public:
+ CheckableItemModel(QObject* parent = nullptr);
+
+ Qt::ItemFlags flags(const QModelIndex& index) const Q_DECL_OVERRIDE;
+
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
+ bool setData(const QModelIndex& index, const QVariant &value, int role = Qt::EditRole) Q_DECL_OVERRIDE;
+
+Q_SIGNALS:
+ void itemCheckStateChanged(const QModelIndex& index);
+};
+
+class CheckBoxDelegate : public QStyledItemDelegate {
+Q_OBJECT
+
+public:
+ CheckBoxDelegate(QObject* parent = nullptr);
+
+ void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const Q_DECL_OVERRIDE;
+};
+
+class CheckableComboBox : public QComboBox {
+Q_OBJECT
+
+public:
+ CheckableComboBox(QWidget* parent = nullptr);
+
+ QString separator() const;
+ void setSeparator(const QString& separator);
+
+ QStringList checkedItems() const;
+ QVariantList checkedItemsData() const;
+
+ Qt::CheckState itemCheckState(int index) const;
+ void setAllCheckState(Qt::CheckState checkState);
+
+ void hidePopup() override;
+ bool eventFilter(QObject* object, QEvent* event) Q_DECL_OVERRIDE;
+
+Q_SIGNALS:
+ void checkedItemsChanged(const QStringList& items);
+
+public Q_SLOTS:
+ void setCheckedDataText(const QString& text);
+ void setCheckedData(const QStringList& values);
+
+protected:
+ void resizeEvent(QResizeEvent* event) override;
+
+protected:
+
+private:
+ void updateCheckedItems();
+ void updateDisplayText();
+
+ QString m_separator;
+ bool m_skipHide = false;
+};
+
+ } // end namespace
+} //end namespace
+
+#endif
diff --git a/src/gui/choicefieldwidget.cpp b/src/gui/choicefieldwidget.cpp
index 2efecd153..7cfcb434c 100644
--- a/src/gui/choicefieldwidget.cpp
+++ b/src/gui/choicefieldwidget.cpp
@@ -23,11 +23,12 @@
***************************************************************************/
#include "choicefieldwidget.h"
+#include "checkablecombobox.h"
#include "../field.h"
-#include <QComboBox>
#include <QGuiApplication>
#include <QScreen>
+#include <QBoxLayout>
namespace {
const double MAX_FRACTION_SCREEN_WIDTH = 0.4;
@@ -36,11 +37,31 @@ namespace {
using Tellico::GUI::ChoiceFieldWidget;
ChoiceFieldWidget::ChoiceFieldWidget(Tellico::Data::FieldPtr field_, QWidget* parent_)
- : FieldWidget(field_, parent_), m_comboBox(nullptr) {
+ : FieldWidget(field_, parent_), m_comboBox(nullptr), m_checkableComboBox(nullptr) {
+ if(field_->hasFlag(Data::Field::AllowMultiple)) {
+ initCheckableComboBox();
+ } else {
+ initComboBox();
+ }
+ initCommon(field_);
+
+ registerWidget();
+}
+
+void ChoiceFieldWidget::initComboBox() {
m_comboBox = new QComboBox(this);
void (QComboBox::* indexChanged)(int) = &QComboBox::currentIndexChanged;
connect(m_comboBox, indexChanged, this, &ChoiceFieldWidget::checkModified);
+}
+
+void ChoiceFieldWidget::initCheckableComboBox() {
+ m_checkableComboBox = new CheckableComboBox(this);
+ m_checkableComboBox->setSeparator(FieldFormat::delimiterString());
+ connect(m_checkableComboBox, &CheckableComboBox::checkedItemsChanged, this, &ChoiceFieldWidget::checkModified);
+}
+
+void ChoiceFieldWidget::initCommon(Tellico::Data::FieldPtr field_) {
m_maxTextWidth = MAX_FRACTION_SCREEN_WIDTH * QGuiApplication::primaryScreen()->size().width();
QStringList values = field_->allowed();
@@ -48,54 +69,84 @@ ChoiceFieldWidget::ChoiceFieldWidget(Tellico::Data::FieldPtr field_, QWidget* pa
values.removeAll(QString());
const QFontMetrics& fm = fontMetrics();
- m_comboBox->addItem(QString(), QString());
+ auto cb = comboBox(); // everything here applies to either type of combobox;
+ cb->addItem(QString(), QString());
foreach(const QString& value, values) {
- m_comboBox->addItem(fm.elidedText(value, Qt::ElideMiddle, m_maxTextWidth), value);
+ cb->addItem(fm.elidedText(value, Qt::ElideMiddle, m_maxTextWidth), value);
}
-
- m_comboBox->setMinimumWidth(5*fm.maxWidth());
- registerWidget();
+ cb->setMinimumWidth(5*fm.maxWidth());
}
QString ChoiceFieldWidget::text() const {
- return m_comboBox->currentData().toString();
+ return isMultiSelect() ? comboBox()->currentText()
+ : comboBox()->currentData().toString();
}
void ChoiceFieldWidget::setTextImpl(const QString& text_) {
- int idx = m_comboBox->findData(text_);
- if(idx < 0) {
- m_comboBox->addItem(fontMetrics().elidedText(text_, Qt::ElideMiddle, m_maxTextWidth), text_);
- m_comboBox->setCurrentIndex(m_comboBox->count()-1);
+ auto cb = comboBox();
+ if(isMultiSelect()) {
+ // first uncheck all the boxes
+ m_checkableComboBox->setAllCheckState(Qt::Unchecked);
+ const auto values = FieldFormat::splitValue(text_, FieldFormat::StringSplit);
+ for(const auto& value : qAsConst(values)) {
+ int idx = cb->findData(value);
+ if(idx < 0) {
+ m_checkableComboBox->addItem(fontMetrics().elidedText(text_, Qt::ElideMiddle, m_maxTextWidth), text_);
+ idx = m_checkableComboBox->count()-1;
+ }
+ m_checkableComboBox->setItemData(idx, Qt::Checked, Qt::CheckStateRole);
+ }
} else {
- m_comboBox->setCurrentIndex(idx);
+ int idx = cb->findData(text_);
+ if(idx < 0) {
+ m_comboBox->addItem(fontMetrics().elidedText(text_, Qt::ElideMiddle, m_maxTextWidth), text_);
+ m_comboBox->setCurrentIndex(m_comboBox->count()-1);
+ } else {
+ m_comboBox->setCurrentIndex(idx);
+ }
}
}
void ChoiceFieldWidget::clearImpl() {
- m_comboBox->setCurrentIndex(0); // first item is empty
+ comboBox()->setCurrentIndex(0); // first item is empty
editMultiple(false);
}
void ChoiceFieldWidget::updateFieldHook(Tellico::Data::FieldPtr, Tellico::Data::FieldPtr newField_) {
- const QString oldValue = text();
- int idx = m_comboBox->currentIndex();
- m_comboBox->blockSignals(true);
- m_comboBox->clear();
+ bool wasMultiSelect = isMultiSelect();
+ bool nowMultiSelect = newField_->hasFlag(Data::Field::AllowMultiple);
- QStringList values = newField_->allowed();
- values.removeAll(QString());
+ const QString oldValue = text();
- const QFontMetrics& fm = fontMetrics();
- // always have empty choice
- m_comboBox->addItem(QString(), QString());
- foreach(const QString& value, values) {
- m_comboBox->addItem(fm.elidedText(value, Qt::ElideMiddle, m_maxTextWidth), value);
+ const int widgetIndex = layout()->indexOf(widget());
+ Q_ASSERT(widgetIndex > -1);
+
+ if(wasMultiSelect && !nowMultiSelect) {
+ layout()->removeWidget(m_checkableComboBox);
+ delete m_checkableComboBox;
+ m_checkableComboBox = nullptr;
+ initComboBox();
+ } else if(!wasMultiSelect && nowMultiSelect) {
+ layout()->removeWidget(m_comboBox);
+ delete m_comboBox;
+ m_comboBox = nullptr;
+ initCheckableComboBox();
+ } else {
+ // remove existing values
+ comboBox()->clear();
}
- m_comboBox->setCurrentIndex(idx);
- m_comboBox->blockSignals(false);
+ initCommon(newField_);
+
+ static_cast<QBoxLayout*>(layout())->insertWidget(widgetIndex, widget(), 1 /*stretch*/);
+ widget()->show();
+
setTextImpl(oldValue); // set back to previous value
}
+QComboBox* ChoiceFieldWidget::comboBox() const {
+ return isMultiSelect() ? m_checkableComboBox : m_comboBox;
+}
+
QWidget* ChoiceFieldWidget::widget() {
- return m_comboBox;
+ return comboBox();
}
diff --git a/src/gui/choicefieldwidget.h b/src/gui/choicefieldwidget.h
index ac97973a5..e06071e7c 100644
--- a/src/gui/choicefieldwidget.h
+++ b/src/gui/choicefieldwidget.h
@@ -34,6 +34,8 @@ class FieldWidgetTest;
namespace Tellico {
namespace GUI {
+class CheckableComboBox;
+
/**
* @author Robby Stephenson
*/
@@ -57,7 +59,14 @@ protected:
virtual void updateFieldHook(Data::FieldPtr oldField, Data::FieldPtr newField) Q_DECL_OVERRIDE;
private:
+ QComboBox* comboBox() const;
+ bool isMultiSelect() const { return (m_checkableComboBox != nullptr); }
+ void initComboBox();
+ void initCheckableComboBox();
+ void initCommon(Data::FieldPtr field);
+
QComboBox* m_comboBox;
+ CheckableComboBox* m_checkableComboBox;
int m_maxTextWidth;
};
More information about the kde-doc-english
mailing list