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_EDITABLELISTMODEL_H
21 #define LIBREPCB_EDITABLELISTMODEL_H
22 
23 /*******************************************************************************
24  * Includes
25  ******************************************************************************/
26 #include "../toolbox.h"
27 #include "../uuid.h"
28 #include "comboboxdelegate.h"
29 
30 #include <optional/tl/optional.hpp>
31 
32 #include <QtCore>
33 #include <QtWidgets>
34 
35 /*******************************************************************************
36  * Namespace / Forward Declarations
37  ******************************************************************************/
38 namespace librepcb {
39 
40 /*******************************************************************************
41  * Types
42  ******************************************************************************/
43 
45 
46 /*******************************************************************************
47  * Class EditableListModel
48  ******************************************************************************/
49 
69 template <typename T,
71 class EditableListModel final : public QAbstractTableModel {
72  typedef typename T::value_type ValueType;
73 
74 public:
75  enum Column { COLUMN_TEXT, COLUMN_ACTIONS, _COLUMN_COUNT };
76 
77  // Constructors / Destructor
78  EditableListModel(const EditableListModel& other) noexcept = delete;
79  explicit EditableListModel(QObject* parent = nullptr) noexcept
80  : QAbstractTableModel(parent),
81  mChoices(),
82  mValues(),
83  mDefaultValue(),
84  mNewValue(),
85  mPlaceholderText(),
86  mComboBoxItems() {}
87  ~EditableListModel() noexcept {}
88 
89  // Getters
90  const T& getValues() const noexcept { return mValues; }
91 
92  // Setters
93  void setDefaultValue(const ValueType& value) noexcept {
94  mDefaultValue = value;
95  mNewValue = value;
96  }
97 
98  void setPlaceholderText(const QString& text) noexcept {
99  mPlaceholderText = text;
100  emit dataChanged(index(mValues.count(), COLUMN_TEXT),
101  index(mValues.count(), COLUMN_TEXT));
102  }
103 
104  void setValues(const T& values) noexcept {
105  emit beginResetModel();
106  mValues = values;
107  emit endResetModel();
108  }
109 
110  void setChoices(const T& choices) noexcept {
111  emit beginResetModel();
112  mChoices = choices;
113  updateComboBoxItems();
114  emit endResetModel();
115  }
116 
117  void setDisplayText(const ValueType& value, const QString& text) noexcept {
118  emit beginResetModel();
119  mDisplayTexts[value] = text;
120  emit endResetModel();
121  }
122 
123  void setIcon(const ValueType& value, const QIcon& icon) noexcept {
124  emit beginResetModel();
125  mIcons[value] = icon;
126  emit endResetModel();
127  }
128 
129  // Slots
130  void addItem(const QVariant& editData) noexcept {
131  Q_UNUSED(editData);
132 
133  if (!mNewValue) {
134  QMessageBox::critical(nullptr, tr("Error"), tr("Invalid value."));
135  return;
136  }
137 
138  if (mValues.contains(*mNewValue)) {
139  QMessageBox::critical(nullptr, tr("Error"),
140  tr("Value already contained in list."));
141  return;
142  }
143 
144  beginInsertRows(QModelIndex(), mValues.count(), mValues.count());
145  mValues.append(*mNewValue);
146  endInsertRows();
147 
148  mNewValue = mDefaultValue;
149  emit dataChanged(index(mValues.count(), 0),
150  index(mValues.count(), _COLUMN_COUNT - 1));
151  }
152 
153  void removeItem(const QVariant& editData) noexcept {
154  int row = editData.toInt();
155  if ((row >= 0) && (row < mValues.count())) {
156  beginRemoveRows(QModelIndex(), row, row);
157  mValues.removeAt(row);
158  endRemoveRows();
159  }
160  }
161 
162  void moveItemUp(const QVariant& editData) noexcept {
163  int row = editData.toInt();
164  if (row >= 1) {
165  mValues.move(row, row - 1);
166  emit dataChanged(index(row - 1, 0), index(row, _COLUMN_COUNT - 1));
167  }
168  }
169 
170  void moveItemDown(const QVariant& editData) noexcept {
171  int row = editData.toInt();
172  if (row < (mValues.count() - 1)) {
173  mValues.move(row, row + 1);
174  emit dataChanged(index(row, 0), index(row + 1, _COLUMN_COUNT - 1));
175  }
176  }
177 
178  // Inherited from QAbstractItemModel
179  int rowCount(const QModelIndex& parent = QModelIndex()) const override {
180  if (!parent.isValid()) {
181  return mValues.count() + 1;
182  }
183  return 0;
184  }
185 
186  int columnCount(const QModelIndex& parent = QModelIndex()) const override {
187  if (!parent.isValid()) {
188  return _COLUMN_COUNT;
189  }
190  return 0;
191  }
192 
193  QVariant data(const QModelIndex& index,
194  int role = Qt::DisplayRole) const override {
195  if (!index.isValid()) {
196  return QVariant();
197  }
198 
199  tl::optional<ValueType> value = mNewValue;
200  if ((index.row() >= 0) && (index.row() < mValues.count())) {
201  value = mValues.at(index.row());
202  }
203  bool showPlaceholder = (index.row() == mValues.count()) &&
204  ((!value) || getDisplayText(*value).isEmpty());
205  switch (index.column()) {
206  case COLUMN_TEXT: {
207  switch (role) {
208  case Qt::DisplayRole:
209  return showPlaceholder
210  ? mPlaceholderText
211  : (value ? getDisplayText(*value) : QString());
212  case Qt::DecorationRole:
213  return value ? mIcons.value(*value) : QIcon();
214  case Qt::EditRole:
215  return value ? getDataForValue(*value) : QVariant();
216  case Qt::ForegroundRole:
217  if (showPlaceholder) {
218  QColor color = qApp->palette().text().color();
219  color.setAlpha(128);
220  return QBrush(color);
221  } else {
222  return QVariant();
223  }
224  case Qt::UserRole:
225  return QVariant::fromValue(mComboBoxItems);
226  default:
227  return QVariant();
228  }
229  }
230  case COLUMN_ACTIONS: {
231  switch (role) {
232  case Qt::EditRole:
233  return index.row();
234  default:
235  return QVariant();
236  }
237  }
238  default:
239  return QVariant();
240  }
241 
242  return QVariant();
243  }
244 
245  QVariant headerData(int section, Qt::Orientation orientation,
246  int role = Qt::DisplayRole) const override {
247  if (orientation == Qt::Horizontal) {
248  if (role == Qt::DisplayRole) {
249  switch (section) {
250  case COLUMN_TEXT:
251  return tr("Item");
252  default:
253  return QVariant();
254  }
255  }
256  } else if (orientation == Qt::Vertical) {
257  bool isLastRow = section >= mValues.count();
258  if (role == Qt::DisplayRole) {
259  return isLastRow ? tr("New:") : QString::number(section + 1);
260  } else if (role == Qt::TextAlignmentRole) {
261  return QVariant(Qt::AlignRight | Qt::AlignVCenter);
262  }
263  }
264  return QVariant();
265  }
266 
267  Qt::ItemFlags flags(const QModelIndex& index) const override {
268  Qt::ItemFlags f = QAbstractTableModel::flags(index);
269  if (index.isValid() && (index.row() == mValues.count())) {
270  f |= Qt::ItemIsEditable;
271  }
272  return f;
273  }
274 
275  bool setData(const QModelIndex& index, const QVariant& value,
276  int role = Qt::EditRole) override {
277  bool isLastRow = index.row() >= mValues.count();
278 
279  if ((index.column() == COLUMN_TEXT) && role == Qt::EditRole) {
280  if (isLastRow) {
281  mNewValue = convertInputValue(value, mDefaultValue);
282  }
283  emit dataChanged(index, index);
284  return true;
285  }
286 
287  return false;
288  }
289 
290  // Operator Overloadings
291  EditableListModel& operator=(const EditableListModel& rhs) noexcept;
292 
293 private: // Methods
294  QString getDisplayText(const Uuid& value) const noexcept {
295  return mDisplayTexts.value(value, value.toStr());
296  }
297 
298  QString getDisplayText(const QUrl& value) const noexcept {
299  return value.toDisplayString();
300  }
301 
302  QString getDisplayText(const QString& value) const noexcept {
303  switch (TYPE) {
305  return Toolbox::prettyPrintLocale(value);
306  default:
307  return mDisplayTexts.value(value, value);
308  }
309  }
310 
311  QVariant getDataForValue(const Uuid& value) const noexcept {
312  return value.toStr();
313  }
314 
315  QVariant getDataForValue(const QUrl& value) const noexcept { return value; }
316 
317  QVariant getDataForValue(const QString& value) const noexcept {
318  return value;
319  }
320 
321  tl::optional<Uuid> convertInputValue(const QVariant& input,
322  const tl::optional<Uuid>& tag) const
323  noexcept {
324  Q_UNUSED(tag); // used only for template tag dispatching
325  return Uuid::tryFromString(input.toString());
326  }
327 
328  tl::optional<QString> convertInputValue(
329  const QVariant& input, const tl::optional<QString>& tag) const noexcept {
330  Q_UNUSED(tag); // used only for template tag dispatching
331  QString str = input.toString().trimmed();
332  return str.isEmpty() ? tl::nullopt : tl::make_optional(str);
333  }
334 
335  tl::optional<QUrl> convertInputValue(const QVariant& input,
336  const tl::optional<QUrl>& tag) const
337  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  }
350  mComboBoxItems.sort();
351  }
352 
353 private: // 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 librepcb
369 
370 #endif // LIBREPCB_EDITABLELISTMODEL_H
Qt::ItemFlags flags(const QModelIndex &index) const override
Definition: editablelistmodel.h:267
QVariant getDataForValue(const QString &value) const noexcept
Definition: editablelistmodel.h:317
tl::optional< ValueType > mNewValue
Definition: editablelistmodel.h:357
Definition: editablelistmodel.h:75
void setDefaultValue(const ValueType &value) noexcept
Definition: editablelistmodel.h:93
tl::optional< QString > convertInputValue(const QVariant &input, const tl::optional< QString > &tag) const noexcept
Definition: editablelistmodel.h:328
Definition: airwiresbuilder.cpp:32
const T & getValues() const noexcept
Definition: editablelistmodel.h:90
void setChoices(const T &choices) noexcept
Definition: editablelistmodel.h:110
Column
Definition: editablelistmodel.h:75
QString getDisplayText(const QUrl &value) const noexcept
Definition: editablelistmodel.h:298
T mChoices
Definition: editablelistmodel.h:354
static QString prettyPrintLocale(const QString &code) noexcept
Pretty print the name of a QLocale.
Definition: toolbox.cpp:231
Definition: comboboxdelegate.h:48
Definition: comboboxdelegate.h:53
~EditableListModel() noexcept
Definition: editablelistmodel.h:87
QString mPlaceholderText
Definition: editablelistmodel.h:358
void setValues(const T &values) noexcept
Definition: editablelistmodel.h:104
#define qApp
Definition: application.h:49
void addItem(const QVariant &editData) noexcept
Definition: editablelistmodel.h:130
EditableListModel(QObject *parent=nullptr) noexcept
Definition: editablelistmodel.h:79
void setPlaceholderText(const QString &text) noexcept
Definition: editablelistmodel.h:98
tl::optional< ValueType > mDefaultValue
Definition: editablelistmodel.h:356
QString getDisplayText(const Uuid &value) const noexcept
Definition: editablelistmodel.h:294
T mValues
Definition: editablelistmodel.h:355
T::value_type ValueType
Definition: editablelistmodel.h:72
EditableListModelType
Definition: editablelistmodel.h:44
QHash< ValueType, QString > mDisplayTexts
Definition: editablelistmodel.h:359
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Definition: editablelistmodel.h:193
void updateComboBoxItems() noexcept
Definition: editablelistmodel.h:343
QVariant getDataForValue(const QUrl &value) const noexcept
Definition: editablelistmodel.h:315
ComboBoxDelegate::Items mComboBoxItems
Definition: editablelistmodel.h:361
void removeItem(const QVariant &editData) noexcept
Definition: editablelistmodel.h:153
QVariant getDataForValue(const Uuid &value) const noexcept
Definition: editablelistmodel.h:311
tl::optional< Uuid > convertInputValue(const QVariant &input, const tl::optional< Uuid > &tag) const noexcept
Definition: editablelistmodel.h:321
A generic QAbstractTableModel subclass to view and edit list of various data types.
Definition: editablelistmodel.h:71
QHash< ValueType, QIcon > mIcons
Definition: editablelistmodel.h:360
void setIcon(const ValueType &value, const QIcon &icon) noexcept
Definition: editablelistmodel.h:123
tl::optional< QUrl > convertInputValue(const QVariant &input, const tl::optional< QUrl > &tag) const noexcept
Definition: editablelistmodel.h:335
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Definition: editablelistmodel.h:179
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:116
QString getDisplayText(const QString &value) const noexcept
Definition: editablelistmodel.h:302
void moveItemDown(const QVariant &editData) noexcept
Definition: editablelistmodel.h:170
void moveItemUp(const QVariant &editData) noexcept
Definition: editablelistmodel.h:162
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
Definition: editablelistmodel.h:275
int columnCount(const QModelIndex &parent=QModelIndex()) const override
Definition: editablelistmodel.h:186
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
Definition: editablelistmodel.h:245
void setDisplayText(const ValueType &value, const QString &text) noexcept
Definition: editablelistmodel.h:117