LibrePCB Developers Documentation
editablelistmodel.h
Go to the documentation of this file.
1/*
2 * LibrePCB - Professional EDA for everyone!
3 * Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
4 * https://librepcb.org/
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#ifndef LIBREPCB_EDITOR_EDITABLELISTMODEL_H
21#define LIBREPCB_EDITOR_EDITABLELISTMODEL_H
22
23/*******************************************************************************
24 * Includes
25 ******************************************************************************/
26#include "comboboxdelegate.h"
27
30
31#include <QtCore>
32#include <QtWidgets>
33
34#include <optional.hpp>
35
36/*******************************************************************************
37 * Namespace / Forward Declarations
38 ******************************************************************************/
39namespace librepcb {
40namespace editor {
41
42/*******************************************************************************
43 * Types
44 ******************************************************************************/
45
47
48/*******************************************************************************
49 * Class EditableListModel
50 ******************************************************************************/
51
71template <typename T,
73class EditableListModel final : public QAbstractTableModel {
74 typedef typename T::value_type ValueType;
75
76public:
78
79 // Constructors / Destructor
80 EditableListModel(const EditableListModel& other) noexcept = delete;
81 explicit EditableListModel(QObject* parent = nullptr) noexcept
82 : QAbstractTableModel(parent),
83 mChoices(),
84 mValues(),
86 mNewValue(),
89 ~EditableListModel() noexcept {}
90
91 // Getters
92 const T& getValues() const noexcept { return mValues; }
93
94 // Setters
95 void setDefaultValue(const ValueType& value) noexcept {
96 mDefaultValue = value;
97 mNewValue = value;
98 }
99
100 void setPlaceholderText(const QString& text) noexcept {
101 mPlaceholderText = text;
102 emit dataChanged(index(mValues.count(), COLUMN_TEXT),
103 index(mValues.count(), COLUMN_TEXT));
104 }
105
106 void setValues(const T& values) noexcept {
107 emit beginResetModel();
108 mValues = values;
109 emit endResetModel();
110 }
111
112 void setChoices(const T& choices) noexcept {
113 emit beginResetModel();
114 mChoices = choices;
116 emit endResetModel();
117 }
118
119 void setDisplayText(const ValueType& value, const QString& text) noexcept {
120 emit beginResetModel();
121 mDisplayTexts[value] = text;
122 emit endResetModel();
123 }
124
125 void setIcon(const ValueType& value, const QIcon& icon) noexcept {
126 emit beginResetModel();
127 mIcons[value] = icon;
128 emit endResetModel();
129 }
130
131 // Slots
132 void add(const QPersistentModelIndex& itemIndex) noexcept {
133 Q_UNUSED(itemIndex);
134
135 if (!mNewValue) {
136 QMessageBox::critical(nullptr, tr("Error"), tr("Invalid value."));
137 return;
138 }
139
140 if (mValues.contains(*mNewValue)) {
141 QMessageBox::critical(nullptr, tr("Error"),
142 tr("Value already contained in list."));
143 return;
144 }
145
146 beginInsertRows(QModelIndex(), mValues.count(), mValues.count());
147 mValues.append(*mNewValue);
148 endInsertRows();
149
151 emit dataChanged(index(mValues.count(), 0),
152 index(mValues.count(), _COLUMN_COUNT - 1));
153 }
154
155 void remove(const QPersistentModelIndex& itemIndex) noexcept {
156 int row = itemIndex.data(Qt::EditRole).toInt();
157 if ((row >= 0) && (row < mValues.count())) {
158 beginRemoveRows(QModelIndex(), row, row);
159 mValues.removeAt(row);
160 endRemoveRows();
161 }
162 }
163
164 void moveUp(const QPersistentModelIndex& itemIndex) noexcept {
165 int row = itemIndex.data(Qt::EditRole).toInt();
166 if (row >= 1) {
167 mValues.move(row, row - 1);
168 emit dataChanged(index(row - 1, 0), index(row, _COLUMN_COUNT - 1));
169 }
170 }
171
172 void moveDown(const QPersistentModelIndex& itemIndex) noexcept {
173 int row = itemIndex.data(Qt::EditRole).toInt();
174 if (row < (mValues.count() - 1)) {
175 mValues.move(row, row + 1);
176 emit dataChanged(index(row, 0), index(row + 1, _COLUMN_COUNT - 1));
177 }
178 }
179
180 // Inherited from QAbstractItemModel
181 int rowCount(const QModelIndex& parent = QModelIndex()) const override {
182 if (!parent.isValid()) {
183 return mValues.count() + 1;
184 }
185 return 0;
186 }
187
188 int columnCount(const QModelIndex& parent = QModelIndex()) const override {
189 if (!parent.isValid()) {
190 return _COLUMN_COUNT;
191 }
192 return 0;
193 }
194
195 QVariant data(const QModelIndex& index,
196 int role = Qt::DisplayRole) const override {
197 if (!index.isValid()) {
198 return QVariant();
199 }
200
201 tl::optional<ValueType> value = mNewValue;
202 if ((index.row() >= 0) && (index.row() < mValues.count())) {
203 value = mValues.at(index.row());
204 }
205 bool showPlaceholder = (index.row() == mValues.count()) &&
206 ((!value) || getDisplayText(*value).isEmpty());
207 switch (index.column()) {
208 case COLUMN_TEXT: {
209 switch (role) {
210 case Qt::DisplayRole:
211 return showPlaceholder
213 : (value ? getDisplayText(*value) : QString());
214 case Qt::DecorationRole:
215 return value ? mIcons.value(*value) : QIcon();
216 case Qt::EditRole:
217 return value ? getDataForValue(*value) : QVariant();
218 case Qt::ForegroundRole:
219 if (showPlaceholder) {
220 QColor color = qApp->palette().text().color();
221 color.setAlpha(128);
222 return QBrush(color);
223 } else {
224 return QVariant();
225 }
226 case Qt::UserRole:
227 return QVariant::fromValue(mComboBoxItems);
228 default:
229 return QVariant();
230 }
231 }
232 case COLUMN_ACTIONS: {
233 switch (role) {
234 case Qt::EditRole:
235 return index.row();
236 default:
237 return QVariant();
238 }
239 }
240 default:
241 return QVariant();
242 }
243
244 return QVariant();
245 }
246
247 QVariant headerData(int section, Qt::Orientation orientation,
248 int role = Qt::DisplayRole) const override {
249 if (orientation == Qt::Horizontal) {
250 if (role == Qt::DisplayRole) {
251 switch (section) {
252 case COLUMN_TEXT:
253 return tr("Item");
254 default:
255 return QVariant();
256 }
257 }
258 } else if (orientation == Qt::Vertical) {
259 bool isLastRow = section >= mValues.count();
260 if (role == Qt::DisplayRole) {
261 return isLastRow ? tr("New:") : QString::number(section + 1);
262 } else if (role == Qt::TextAlignmentRole) {
263 return QVariant(Qt::AlignRight | Qt::AlignVCenter);
264 }
265 }
266 return QVariant();
267 }
268
269 Qt::ItemFlags flags(const QModelIndex& index) const override {
270 Qt::ItemFlags f = QAbstractTableModel::flags(index);
271 if (index.isValid() && (index.row() == mValues.count())) {
272 f |= Qt::ItemIsEditable;
273 }
274 return f;
275 }
276
277 bool setData(const QModelIndex& index, const QVariant& value,
278 int role = Qt::EditRole) override {
279 bool isLastRow = index.row() >= mValues.count();
280
281 if ((index.column() == COLUMN_TEXT) && role == Qt::EditRole) {
282 if (isLastRow) {
284 }
285 emit dataChanged(index, index);
286 return true;
287 }
288
289 return false;
290 }
291
292 // Operator Overloadings
294
295private: // Methods
296 QString getDisplayText(const Uuid& value) const noexcept {
297 return mDisplayTexts.value(value, value.toStr());
298 }
299
300 QString getDisplayText(const QUrl& value) const noexcept {
301 return value.toDisplayString();
302 }
303
304 QString getDisplayText(const QString& value) const noexcept {
305 switch (TYPE) {
307 return Toolbox::prettyPrintLocale(value);
308 default:
309 return mDisplayTexts.value(value, value);
310 }
311 }
312
313 QVariant getDataForValue(const Uuid& value) const noexcept {
314 return value.toStr();
315 }
316
317 QVariant getDataForValue(const QUrl& value) const noexcept { return value; }
318
319 QVariant getDataForValue(const QString& value) const noexcept {
320 return value;
321 }
322
323 tl::optional<Uuid> convertInputValue(
324 const QVariant& input, const tl::optional<Uuid>& tag) const noexcept {
325 Q_UNUSED(tag); // used only for template tag dispatching
326 return Uuid::tryFromString(input.toString());
327 }
328
329 tl::optional<QString> convertInputValue(
330 const QVariant& input, const tl::optional<QString>& tag) const noexcept {
331 Q_UNUSED(tag); // used only for template tag dispatching
332 QString str = input.toString().trimmed();
333 return str.isEmpty() ? tl::nullopt : tl::make_optional(str);
334 }
335
336 tl::optional<QUrl> convertInputValue(
337 const QVariant& input, const tl::optional<QUrl>& tag) const noexcept {
338 Q_UNUSED(tag); // used only for template tag dispatching
339 QUrl url = QUrl::fromUserInput(input.toString());
340 return url.isValid() ? tl::make_optional(url) : tl::nullopt;
341 }
342
343 void updateComboBoxItems() noexcept {
344 mComboBoxItems.clear();
345 foreach (const ValueType& choice, mChoices) {
346 ComboBoxDelegate::Item item{getDisplayText(choice), mIcons[choice],
347 getDataForValue(choice)};
348 mComboBoxItems.append(item);
349 }
351 }
352
353private: // Data
356 tl::optional<ValueType> mDefaultValue;
357 tl::optional<ValueType> mNewValue;
359 QHash<ValueType, QString> mDisplayTexts;
360 QHash<ValueType, QIcon> mIcons;
362};
363
364/*******************************************************************************
365 * End of File
366 ******************************************************************************/
367
368} // namespace editor
369} // namespace librepcb
370
371#endif
static QString prettyPrintLocale(const QString &code) noexcept
Pretty print the name of a QLocale.
Definition: toolbox.cpp:265
The Uuid class is a replacement for QUuid to get UUID strings without {} braces.
Definition: uuid.h:58
static tl::optional< Uuid > tryFromString(const QString &str) noexcept
Try creating a Uuid from a string, returning empty optional if invalid.
Definition: uuid.cpp:119
A generic QAbstractTableModel subclass to view and edit list of various data types.
Definition: editablelistmodel.h:73
QHash< ValueType, QString > mDisplayTexts
Definition: editablelistmodel.h:359
QString getDisplayText(const QUrl &value) const noexcept
Definition: editablelistmodel.h:300
const T & getValues() const noexcept
Definition: editablelistmodel.h:92
tl::optional< ValueType > mDefaultValue
Definition: editablelistmodel.h:356
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
Definition: editablelistmodel.h:247
void setDisplayText(const ValueType &value, const QString &text) noexcept
Definition: editablelistmodel.h:119
void setIcon(const ValueType &value, const QIcon &icon) noexcept
Definition: editablelistmodel.h:125
tl::optional< ValueType > mNewValue
Definition: editablelistmodel.h:357
QVariant getDataForValue(const QUrl &value) const noexcept
Definition: editablelistmodel.h:317
EditableListModel & operator=(const EditableListModel &rhs) noexcept
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Definition: editablelistmodel.h:181
void moveDown(const QPersistentModelIndex &itemIndex) noexcept
Definition: editablelistmodel.h:172
Qt::ItemFlags flags(const QModelIndex &index) const override
Definition: editablelistmodel.h:269
EditableListModel(QObject *parent=nullptr) noexcept
Definition: editablelistmodel.h:81
void add(const QPersistentModelIndex &itemIndex) noexcept
Definition: editablelistmodel.h:132
void setPlaceholderText(const QString &text) noexcept
Definition: editablelistmodel.h:100
QVariant getDataForValue(const Uuid &value) const noexcept
Definition: editablelistmodel.h:313
void setValues(const T &values) noexcept
Definition: editablelistmodel.h:106
QString getDisplayText(const QString &value) const noexcept
Definition: editablelistmodel.h:304
QHash< ValueType, QIcon > mIcons
Definition: editablelistmodel.h:360
ComboBoxDelegate::Items mComboBoxItems
Definition: editablelistmodel.h:361
~EditableListModel() noexcept
Definition: editablelistmodel.h:89
tl::optional< Uuid > convertInputValue(const QVariant &input, const tl::optional< Uuid > &tag) const noexcept
Definition: editablelistmodel.h:323
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Definition: editablelistmodel.h:195
void moveUp(const QPersistentModelIndex &itemIndex) noexcept
Definition: editablelistmodel.h:164
void remove(const QPersistentModelIndex &itemIndex) noexcept
Definition: editablelistmodel.h:155
void setDefaultValue(const ValueType &value) noexcept
Definition: editablelistmodel.h:95
QVariant getDataForValue(const QString &value) const noexcept
Definition: editablelistmodel.h:319
QString mPlaceholderText
Definition: editablelistmodel.h:358
T::value_type ValueType
Definition: editablelistmodel.h:74
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
Definition: editablelistmodel.h:277
tl::optional< QString > convertInputValue(const QVariant &input, const tl::optional< QString > &tag) const noexcept
Definition: editablelistmodel.h:329
T mChoices
Definition: editablelistmodel.h:354
QString getDisplayText(const Uuid &value) const noexcept
Definition: editablelistmodel.h:296
int columnCount(const QModelIndex &parent=QModelIndex()) const override
Definition: editablelistmodel.h:188
T mValues
Definition: editablelistmodel.h:355
EditableListModel(const EditableListModel &other) noexcept=delete
void setChoices(const T &choices) noexcept
Definition: editablelistmodel.h:112
Column
Definition: editablelistmodel.h:77
@ _COLUMN_COUNT
Definition: editablelistmodel.h:77
@ COLUMN_TEXT
Definition: editablelistmodel.h:77
@ COLUMN_ACTIONS
Definition: editablelistmodel.h:77
void updateComboBoxItems() noexcept
Definition: editablelistmodel.h:343
tl::optional< QUrl > convertInputValue(const QVariant &input, const tl::optional< QUrl > &tag) const noexcept
Definition: editablelistmodel.h:336
EditableListModelType
Definition: editablelistmodel.h:46
Definition: occmodel.cpp:77
Definition: comboboxdelegate.h:47
Definition: comboboxdelegate.h:52
void sort() noexcept
Definition: comboboxdelegate.cpp:39