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 /*******************************************************************************
21 * Includes
22 ******************************************************************************/
23 #include "componentsymbolvariantlistmodel.h"
24
25 #include <librepcb/common/undostack.h>
26 #include <librepcb/library/cmp/cmd/cmdcomponentsymbolvariantedit.h>
27
28 #include <QtCore>
29
30 /*******************************************************************************
31 * Namespace
32 ******************************************************************************/
33 namespace librepcb {
34 namespace library {
35 namespace editor {
36
37 /*******************************************************************************
38 * Constructors / Destructor
39 ******************************************************************************/
40
ComponentSymbolVariantListModel(QObject * parent)41 ComponentSymbolVariantListModel::ComponentSymbolVariantListModel(
42 QObject* parent) noexcept
43 : QAbstractTableModel(parent),
44 mSymbolVariantList(nullptr),
45 mUndoStack(nullptr),
46 mNewName(),
47 mNewDescription(),
48 mNewNorm(),
49 mOnEditedSlot(*this,
50 &ComponentSymbolVariantListModel::symbolVariantListEdited) {
51 }
52
~ComponentSymbolVariantListModel()53 ComponentSymbolVariantListModel::~ComponentSymbolVariantListModel() noexcept {
54 }
55
56 /*******************************************************************************
57 * Setters
58 ******************************************************************************/
59
setSymbolVariantList(ComponentSymbolVariantList * list)60 void ComponentSymbolVariantListModel::setSymbolVariantList(
61 ComponentSymbolVariantList* list) noexcept {
62 emit beginResetModel();
63
64 if (mSymbolVariantList) {
65 mSymbolVariantList->onEdited.detach(mOnEditedSlot);
66 }
67
68 mSymbolVariantList = list;
69
70 if (mSymbolVariantList) {
71 mSymbolVariantList->onEdited.attach(mOnEditedSlot);
72 }
73
74 emit endResetModel();
75 }
76
setUndoStack(UndoStack * stack)77 void ComponentSymbolVariantListModel::setUndoStack(UndoStack* stack) noexcept {
78 mUndoStack = stack;
79 }
80
81 /*******************************************************************************
82 * Slots
83 ******************************************************************************/
84
addSymbolVariant(const QVariant & editData)85 void ComponentSymbolVariantListModel::addSymbolVariant(
86 const QVariant& editData) noexcept {
87 Q_UNUSED(editData);
88 if (!mSymbolVariantList) {
89 return;
90 }
91
92 try {
93 std::shared_ptr<ComponentSymbolVariant> sv =
94 std::make_shared<ComponentSymbolVariant>(Uuid::createRandom(), mNewNorm,
95 validateNameOrThrow(mNewName),
96 mNewDescription);
97 execCmd(new CmdComponentSymbolVariantInsert(*mSymbolVariantList, sv));
98 mNewName = QString();
99 mNewDescription = QString();
100 mNewNorm = QString();
101 } catch (const Exception& e) {
102 QMessageBox::critical(0, tr("Error"), e.getMsg());
103 }
104 }
105
removeSymbolVariant(const QVariant & editData)106 void ComponentSymbolVariantListModel::removeSymbolVariant(
107 const QVariant& editData) noexcept {
108 if (!mSymbolVariantList) {
109 return;
110 }
111
112 try {
113 Uuid uuid = Uuid::fromString(editData.toString());
114 std::shared_ptr<ComponentSymbolVariant> sv = mSymbolVariantList->get(uuid);
115 execCmd(new CmdComponentSymbolVariantRemove(*mSymbolVariantList, sv.get()));
116 } catch (const Exception& e) {
117 QMessageBox::critical(0, tr("Error"), e.getMsg());
118 }
119 }
120
moveSymbolVariantUp(const QVariant & editData)121 void ComponentSymbolVariantListModel::moveSymbolVariantUp(
122 const QVariant& editData) noexcept {
123 if (!mSymbolVariantList) {
124 return;
125 }
126
127 try {
128 Uuid uuid = Uuid::fromString(editData.toString());
129 int index = mSymbolVariantList->indexOf(uuid);
130 if ((index >= 1) && (index < mSymbolVariantList->count())) {
131 execCmd(new CmdComponentSymbolVariantsSwap(*mSymbolVariantList, index,
132 index - 1));
133 }
134 } catch (const Exception& e) {
135 QMessageBox::critical(0, tr("Error"), e.getMsg());
136 }
137 }
138
moveSymbolVariantDown(const QVariant & editData)139 void ComponentSymbolVariantListModel::moveSymbolVariantDown(
140 const QVariant& editData) noexcept {
141 if (!mSymbolVariantList) {
142 return;
143 }
144
145 try {
146 Uuid uuid = Uuid::fromString(editData.toString());
147 int index = mSymbolVariantList->indexOf(uuid);
148 if ((index >= 0) && (index < mSymbolVariantList->count() - 1)) {
149 execCmd(new CmdComponentSymbolVariantsSwap(*mSymbolVariantList, index,
150 index + 1));
151 }
152 } catch (const Exception& e) {
153 QMessageBox::critical(0, tr("Error"), e.getMsg());
154 }
155 }
156
157 /*******************************************************************************
158 * Inherited from QAbstractItemModel
159 ******************************************************************************/
160
rowCount(const QModelIndex & parent) const161 int ComponentSymbolVariantListModel::rowCount(const QModelIndex& parent) const {
162 if (!parent.isValid() && mSymbolVariantList) {
163 return mSymbolVariantList->count() + 1;
164 }
165 return 0;
166 }
167
columnCount(const QModelIndex & parent) const168 int ComponentSymbolVariantListModel::columnCount(
169 const QModelIndex& parent) const {
170 if (!parent.isValid()) {
171 return _COLUMN_COUNT;
172 }
173 return 0;
174 }
175
data(const QModelIndex & index,int role) const176 QVariant ComponentSymbolVariantListModel::data(const QModelIndex& index,
177 int role) const {
178 if (!index.isValid() || !mSymbolVariantList) {
179 return QVariant();
180 }
181
182 std::shared_ptr<ComponentSymbolVariant> item =
183 mSymbolVariantList->value(index.row());
184 switch (index.column()) {
185 case COLUMN_NAME: {
186 QString name = item ? *item->getNames().getDefaultValue() : mNewName;
187 bool showHint = (!item) && mNewName.isEmpty();
188 QString hint = tr("Symbol variant name");
189 switch (role) {
190 case Qt::DisplayRole:
191 if (item && (index.row() == 0) && (mSymbolVariantList->count() > 1)) {
192 return name + " [" + tr("default") + "]";
193 } else {
194 return showHint ? hint : name;
195 }
196 case Qt::ToolTipRole:
197 return showHint ? hint : QVariant();
198 case Qt::EditRole:
199 return name;
200 case Qt::ForegroundRole:
201 if (showHint) {
202 QColor color = qApp->palette().text().color();
203 color.setAlpha(128);
204 return QBrush(color);
205 } else {
206 return QVariant();
207 }
208 default:
209 return QVariant();
210 }
211 }
212 case COLUMN_DESCRIPTION: {
213 switch (role) {
214 case Qt::DisplayRole:
215 case Qt::EditRole:
216 return item ? item->getDescriptions().getDefaultValue()
217 : mNewDescription;
218 default:
219 return QVariant();
220 }
221 }
222 case COLUMN_NORM: {
223 switch (role) {
224 case Qt::DisplayRole:
225 case Qt::EditRole:
226 return item ? item->getNorm() : mNewNorm;
227 default:
228 return QVariant();
229 }
230 }
231 case COLUMN_SYMBOLCOUNT: {
232 switch (role) {
233 case Qt::DisplayRole:
234 return item ? item->getSymbolItems().count() : QVariant();
235 case Qt::TextAlignmentRole:
236 return Qt::AlignCenter;
237 default:
238 return QVariant();
239 }
240 }
241 case COLUMN_ACTIONS: {
242 switch (role) {
243 case Qt::EditRole:
244 return item ? item->getUuid().toStr() : QVariant();
245 default:
246 return QVariant();
247 }
248 }
249 default:
250 return QVariant();
251 }
252
253 return QVariant();
254 }
255
headerData(int section,Qt::Orientation orientation,int role) const256 QVariant ComponentSymbolVariantListModel::headerData(
257 int section, Qt::Orientation orientation, int role) const {
258 if (orientation == Qt::Horizontal) {
259 if (role == Qt::DisplayRole) {
260 switch (section) {
261 case COLUMN_NAME:
262 return tr("Name");
263 case COLUMN_DESCRIPTION:
264 return tr("Description");
265 case COLUMN_NORM:
266 return tr("Norm");
267 case COLUMN_SYMBOLCOUNT:
268 return tr("Symbols");
269 default:
270 return QVariant();
271 }
272 }
273 } else if (orientation == Qt::Vertical) {
274 if (mSymbolVariantList && (role == Qt::DisplayRole)) {
275 std::shared_ptr<ComponentSymbolVariant> item =
276 mSymbolVariantList->value(section);
277 return item ? item->getUuid().toStr().left(8) : tr("New:");
278 } else if (mSymbolVariantList && (role == Qt::ToolTipRole)) {
279 std::shared_ptr<ComponentSymbolVariant> item =
280 mSymbolVariantList->value(section);
281 return item ? item->getUuid().toStr() : tr("Add a new symbol variant");
282 } else if (role == Qt::TextAlignmentRole) {
283 return QVariant(Qt::AlignRight | Qt::AlignVCenter);
284 } else if (role == Qt::FontRole) {
285 QFont f = QAbstractTableModel::headerData(section, orientation, role)
286 .value<QFont>();
287 f.setStyleHint(QFont::Monospace); // ensure fixed column width
288 f.setFamily("Monospace");
289 return f;
290 }
291 }
292 return QVariant();
293 }
294
flags(const QModelIndex & index) const295 Qt::ItemFlags ComponentSymbolVariantListModel::flags(
296 const QModelIndex& index) const {
297 Qt::ItemFlags f = QAbstractTableModel::flags(index);
298 if (index.isValid() && (index.column() != COLUMN_SYMBOLCOUNT) &&
299 (index.column() != COLUMN_ACTIONS)) {
300 f |= Qt::ItemIsEditable;
301 }
302 return f;
303 }
304
setData(const QModelIndex & index,const QVariant & value,int role)305 bool ComponentSymbolVariantListModel::setData(const QModelIndex& index,
306 const QVariant& value, int role) {
307 if (!mSymbolVariantList) {
308 return false;
309 }
310
311 try {
312 std::shared_ptr<ComponentSymbolVariant> item =
313 mSymbolVariantList->value(index.row());
314 QScopedPointer<CmdComponentSymbolVariantEdit> cmd;
315 if (item) {
316 cmd.reset(new CmdComponentSymbolVariantEdit(*item));
317 }
318 if ((index.column() == COLUMN_NAME) && role == Qt::EditRole) {
319 QString name = value.toString().trimmed();
320 QString cleanedName = cleanElementName(name);
321 if (cmd) {
322 LocalizedNameMap names = item->getNames();
323 if (cleanedName != names.getDefaultValue()) {
324 names.setDefaultValue(validateNameOrThrow(cleanedName));
325 cmd->setNames(names);
326 }
327 } else {
328 mNewName = cleanedName;
329 }
330 } else if ((index.column() == COLUMN_DESCRIPTION) && role == Qt::EditRole) {
331 QString description = value.toString().trimmed();
332 if (cmd) {
333 LocalizedDescriptionMap descriptions = item->getDescriptions();
334 descriptions.setDefaultValue(description);
335 cmd->setDescriptions(descriptions);
336 } else {
337 mNewDescription = description;
338 }
339 } else if ((index.column() == COLUMN_NORM) && role == Qt::EditRole) {
340 QString norm = value.toString().trimmed();
341 if (cmd) {
342 cmd->setNorm(norm);
343 } else {
344 mNewNorm = norm;
345 }
346 } else {
347 return false; // do not execute command!
348 }
349 if (cmd) {
350 execCmd(cmd.take());
351 } else if (!item) {
352 emit dataChanged(index, index);
353 }
354 return true;
355 } catch (const Exception& e) {
356 QMessageBox::critical(0, tr("Error"), e.getMsg());
357 }
358 return false;
359 }
360
361 /*******************************************************************************
362 * Private Methods
363 ******************************************************************************/
364
symbolVariantListEdited(const ComponentSymbolVariantList & list,int index,const std::shared_ptr<const ComponentSymbolVariant> & variant,ComponentSymbolVariantList::Event event)365 void ComponentSymbolVariantListModel::symbolVariantListEdited(
366 const ComponentSymbolVariantList& list, int index,
367 const std::shared_ptr<const ComponentSymbolVariant>& variant,
368 ComponentSymbolVariantList::Event event) noexcept {
369 Q_UNUSED(list);
370 Q_UNUSED(variant);
371 switch (event) {
372 case ComponentSymbolVariantList::Event::ElementAdded:
373 beginInsertRows(QModelIndex(), index, index);
374 endInsertRows();
375 break;
376 case ComponentSymbolVariantList::Event::ElementRemoved:
377 beginRemoveRows(QModelIndex(), index, index);
378 endRemoveRows();
379 break;
380 case ComponentSymbolVariantList::Event::ElementEdited:
381 dataChanged(this->index(index, 0), this->index(index, _COLUMN_COUNT - 1));
382 break;
383 default:
384 qWarning()
385 << "Unhandled switch-case in "
386 "ComponentSymbolVariantListModel::symbolVariantListEdited()";
387 break;
388 }
389 }
390
execCmd(UndoCommand * cmd)391 void ComponentSymbolVariantListModel::execCmd(UndoCommand* cmd) {
392 if (mUndoStack) {
393 mUndoStack->execCmd(cmd);
394 } else {
395 QScopedPointer<UndoCommand> cmdGuard(cmd);
396 cmdGuard->execute();
397 }
398 }
399
validateNameOrThrow(const QString & name) const400 ElementName ComponentSymbolVariantListModel::validateNameOrThrow(
401 const QString& name) const {
402 if (mSymbolVariantList && mSymbolVariantList->contains(name)) {
403 throw RuntimeError(
404 __FILE__, __LINE__,
405 tr("There is already a symbol variant with the name \"%1\".")
406 .arg(name));
407 }
408 return ElementName(name); // can throw
409 }
410
411 /*******************************************************************************
412 * End of File
413 ******************************************************************************/
414
415 } // namespace editor
416 } // namespace library
417 } // namespace librepcb
418