GCC Code Coverage Report


Directory: ./
File: qtpdcom/src/TableModel.cpp
Date: 2024-11-17 04:08:36
Exec Total Coverage
Lines: 0 310 0.0%
Branches: 0 412 0.0%

Line Branch Exec Source
1 /*****************************************************************************
2 *
3 * Copyright (C) 2012-2022 Florian Pose <fp@igh.de>
4 * 2013 Dr. Wilhelm Hagemeister <hm@igh-essen.com>
5 *
6 * This file is part of the QtPdCom library.
7 *
8 * The QtPdCom library is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * The QtPdCom library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16 * License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with the QtPdCom Library. If not, see <http://www.gnu.org/licenses/>.
20 *
21 ****************************************************************************/
22
23 #include "TableModel.h"
24 #include "TableModelImpl.h"
25
26 using QtPdCom::TableModel;
27
28 #include <QDebug>
29
30 /****************************************************************************/
31
32 /** Constructor.
33 */
34 TableModel::TableModel(QObject *parent):
35 QAbstractTableModel(parent),
36 impl(new TableModel::Impl(this))
37 {
38 connect(&impl->valueHighlightRow,
39 SIGNAL(valueChanged()),
40 this,
41 SLOT(highlightRowChanged()));
42 connect(&impl->visibleRowCount,
43 SIGNAL(valueChanged()),
44 this,
45 SLOT(visibleRowCountChanged()));
46 }
47
48 /****************************************************************************/
49
50 /** Destructor.
51 */
52 TableModel::~TableModel()
53 {
54 impl->valueHighlightRow.clearVariable();
55 clearColumns();
56 }
57
58 /****************************************************************************/
59
60 void TableModel::insertColumn(TableColumn *col, int position)
61 {
62 if (position < 0 || position > impl->columnVector.count()) {
63 position = impl->columnVector.count();
64 }
65
66 beginInsertColumns(QModelIndex(), position, position);
67 impl->columnVector.insert(position, col);
68 endInsertColumns();
69
70 QObject::connect(
71 col,
72 SIGNAL(dimensionChanged()),
73 this,
74 SLOT(dimensionChanged()));
75 QObject::connect(
76 col,
77 SIGNAL(headerChanged()),
78 this,
79 SLOT(columnHeaderChanged()));
80 QObject::connect(col, SIGNAL(valueChanged()), this, SLOT(valueChanged()));
81
82 impl->updateRows();
83 }
84
85 /****************************************************************************/
86
87 /** Adds a column.
88 */
89 void TableModel::addColumn(TableColumn *column)
90 {
91 insertColumn(column, -1);
92 }
93
94 /****************************************************************************/
95
96 void QtPdCom::TableModel::removeColumn(TableColumn *column)
97 {
98 const auto idx = impl->columnVector.indexOf(column);
99 if (idx == -1) {
100 return;
101 }
102
103 beginRemoveColumns(QModelIndex(), idx, idx);
104 impl->columnVector.remove(idx);
105 endRemoveColumns();
106
107 QObject::disconnect(
108 column,
109 SIGNAL(dimensionChanged()),
110 this,
111 SLOT(dimensionChanged()));
112 QObject::disconnect(
113 column,
114 SIGNAL(headerChanged()),
115 this,
116 SLOT(columnHeaderChanged()));
117 QObject::disconnect(
118 column,
119 SIGNAL(valueChanged()),
120 this,
121 SLOT(valueChanged()));
122
123 impl->updateRows();
124 }
125
126 /****************************************************************************/
127
128 /** Clears the Columns.
129 */
130 void TableModel::clearColumns()
131 {
132 beginRemoveColumns(QModelIndex(), 0, impl->columnVector.count() - 1);
133 impl->columnVector.clear();
134 endRemoveColumns();
135
136 for (const auto column : impl->columnVector) {
137 QObject::disconnect(
138 column,
139 SIGNAL(dimensionChanged()),
140 this,
141 SLOT(dimensionChanged()));
142 QObject::disconnect(
143 column,
144 SIGNAL(headerChanged()),
145 this,
146 SLOT(columnHeaderChanged()));
147 QObject::disconnect(
148 column,
149 SIGNAL(valueChanged()),
150 this,
151 SLOT(valueChanged()));
152 }
153
154 impl->updateRows();
155 }
156
157 /****************************************************************************/
158
159 bool TableModel::isEditing() const
160 {
161 bool editing = false;
162
163 for (const auto column : impl->columnVector) {
164 if (column->isEditing()) {
165 editing = true;
166 break;
167 }
168 }
169
170 return editing;
171 }
172
173 /****************************************************************************/
174
175 unsigned int TableModel::getRowCapacity() const
176 {
177 return impl->rowCapacity;
178 }
179
180 /****************************************************************************/
181
182 bool TableModel::hasVisibleRowsVariable() const
183 {
184 return impl->visibleRowCount.hasVariable();
185 }
186
187 /****************************************************************************/
188
189 /** Implements the model interface.
190 *
191 * \returns Number of rows.
192 */
193 int TableModel::rowCount(const QModelIndex &index) const
194 {
195 if (!index.isValid()) {
196 return impl->rows;
197 }
198 else {
199 return 0;
200 }
201 }
202
203 /****************************************************************************/
204
205 /** Implements the model interface.
206 *
207 * \returns Number of columns.
208 */
209 int TableModel::columnCount(const QModelIndex &index) const
210 {
211 if (!index.isValid()) {
212 return impl->columnVector.count();
213 }
214 else {
215 return 0;
216 }
217 }
218
219 /****************************************************************************/
220
221 /** Implements the Model interface.
222 */
223 QVariant TableModel::data(const QModelIndex &index, int role) const
224 {
225 if (!index.isValid()) {
226 return QVariant();
227 }
228
229 return impl->columnVector[index.column()]->data(index.row(), role);
230 }
231
232 /****************************************************************************/
233
234 /** Implements the Model interface.
235 */
236 QVariant
237 TableModel::headerData(int section, Qt::Orientation o, int role) const
238 {
239 if (o != Qt::Horizontal) {
240 return QVariant();
241 }
242
243 return impl->columnVector[section]->headerData(role);
244 }
245
246 /****************************************************************************/
247
248 /** Implements the Model interface.
249 */
250 Qt::ItemFlags TableModel::flags(const QModelIndex &index) const
251 {
252 Qt::ItemFlags f;
253
254 if (index.isValid()) {
255 f = impl->columnVector[index.column()]->flags(index.row());
256 f |= Qt::ItemIsSelectable;
257 }
258
259 return f;
260 }
261
262 /****************************************************************************/
263
264 bool TableModel::setData(
265 const QModelIndex &index,
266 const QVariant &value,
267 int role)
268 {
269 if (!index.isValid()) {
270 return false;
271 }
272
273 bool ret = impl->columnVector[index.column()]->setData(
274 index.row(),
275 value.toString(),
276 role);
277
278 emit editingChanged(isEditing());
279
280 return ret;
281 }
282
283 /****************************************************************************/
284
285 bool TableModel::removeRows(int row, int count, const QModelIndex &parent)
286 {
287 if (row < 0 || count <= 0 || parent.isValid()) {
288 return false;
289 }
290
291 if (!hasVisibleRowsVariable()) {
292 return false;
293 }
294
295 if (row + count > impl->visibleRows) {
296 return false;
297 }
298
299 impl->ensureEditing();
300
301 beginRemoveRows(parent, row, row + count - 1);
302 for (const auto column : impl->columnVector) {
303 column->impl->deleteRow(row, count);
304 }
305 endRemoveRows();
306 impl->visibleRows -= count;
307 impl->updateRows();
308 return true;
309 }
310
311 /****************************************************************************/
312
313 void TableModel::setHighlightRowVariable(
314 PdCom::Variable pv,
315 const PdCom::Selector &selector,
316 const Transmission &transmission)
317 {
318 clearHighlightRowVariable();
319
320 if (pv.empty()) {
321 return;
322 }
323
324 impl->valueHighlightRow.setVariable(pv, selector, transmission);
325 }
326
327 /****************************************************************************/
328
329 void TableModel::setHighlightRowVariable(
330 PdCom::Process *process,
331 const QString &path,
332 const PdCom::Selector &selector,
333 const Transmission &transmission)
334 {
335 clearHighlightRowVariable();
336
337 if (not process or path.isEmpty()) {
338 return;
339 }
340
341 impl->valueHighlightRow
342 .setVariable(process, path, selector, transmission);
343 }
344
345 /****************************************************************************/
346
347 void TableModel::clearHighlightRowVariable()
348 {
349 impl->valueHighlightRow.clearVariable();
350
351 for (const auto column : impl->columnVector) {
352 column->setHighlightRow(-1);
353 }
354 }
355
356 /****************************************************************************/
357
358 int TableModel::fromCsv(
359 const QString csvData,
360 QChar separator,
361 int startRow,
362 int startColumn,
363 const QLocale &locale)
364 {
365 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
366 const auto lines = csvData.split(QChar('\n'), Qt::SkipEmptyParts);
367 #else
368 const auto lines = csvData.split(QChar('\n'), QString::SkipEmptyParts);
369 #endif
370 if (startRow < 0 || startRow >= lines.size() || startColumn < 0) {
371 return -ERANGE;
372 }
373 int row = 0;
374 const bool wasEditing = isEditing();
375
376 for (; row < (lines.size() - startRow); ++row) {
377 const auto line = lines[row + startRow].split(separator);
378 if (startColumn >= line.size()) {
379 return -ERANGE;
380 }
381 const auto columnEnd = std::min<int>(
382 impl->columnVector.size(),
383 line.size() - startColumn);
384 for (int column = 0; column < columnEnd; ++column) {
385 impl->columnVector[column]->impl->setRow(
386 line[column + startColumn],
387 row,
388 locale);
389 }
390 }
391 if (!wasEditing && isEditing()) {
392 emit editingChanged(true);
393 }
394
395 impl->visibleRows =
396 std::min<int>(row, impl->visibleRows + impl->rowCapacity);
397 impl->updateRows();
398
399 emit dataChanged(
400 index(0, 0),
401 index(rowCount({}) - 1, impl->columnVector.size() - 1));
402 return row;
403 }
404
405 /****************************************************************************/
406
407 void TableModel::setVisibleRowsVariable(
408 PdCom::Variable pv,
409 const PdCom::Selector &selector,
410 const Transmission &transmission)
411 {
412 clearVisibleRowsVariable();
413
414 if (pv.empty()) {
415 return;
416 }
417
418 impl->visibleRowCount.setVariable(pv, selector, transmission);
419 }
420
421 /****************************************************************************/
422
423 void TableModel::setVisibleRowsVariable(
424 PdCom::Process *process,
425 const QString &path,
426 const PdCom::Selector &selector,
427 const Transmission &transmission)
428 {
429 clearVisibleRowsVariable();
430
431 if (not process or path.isEmpty()) {
432 return;
433 }
434
435 impl->visibleRowCount.setVariable(process, path, selector, transmission);
436 }
437
438 /****************************************************************************/
439
440 void TableModel::setHighlightColor(QColor hc, int idx)
441 {
442 if (idx <= -1) {
443 for (const auto column : impl->columnVector) {
444 column->setHighlightColor(hc);
445 }
446 }
447 else if (idx < impl->columnVector.size()) {
448 impl->columnVector[idx]->setHighlightColor(hc);
449 }
450 }
451
452 /****************************************************************************/
453
454 void TableModel::setDisabledColor(QColor dc, int idx)
455 {
456 if (idx <= -1) {
457 for (const auto column : impl->columnVector) {
458 column->setDisabledColor(dc);
459 }
460 }
461 else if (idx < impl->columnVector.size()) {
462 impl->columnVector[idx]->setDisabledColor(dc);
463 }
464 }
465
466 /****************************************************************************/
467
468 void TableModel::clearVisibleRowsVariable()
469 {
470 impl->visibleRowCount.clearVariable();
471 impl->visibleRows = UINT_MAX;
472 impl->updateRows();
473 }
474
475 /****************************************************************************/
476
477 /** Commits all edited data.
478 */
479 void TableModel::commit()
480 {
481 for (const auto column : impl->columnVector) {
482 column->commit();
483 }
484
485 if (impl->visibleRowCount.hasVariable()
486 && impl->visibleRowCount.getVariable().isWriteable()) {
487 impl->visibleRowCount.writeValue(impl->visibleRows);
488 }
489
490 emit editingChanged(false);
491 }
492
493 /****************************************************************************/
494
495 /** Reverts all edited data.
496 */
497 void TableModel::revert()
498 {
499 for (const auto column : impl->columnVector) {
500 column->revert();
501 }
502
503 if (impl->visibleRowCount.hasData()) {
504 impl->visibleRows = impl->visibleRowCount.getValue();
505 }
506 else {
507 impl->visibleRows = UINT_MAX;
508 }
509 impl->updateRows();
510
511 emit editingChanged(false);
512 }
513
514 /****************************************************************************/
515
516 /** updates the visibleRowCount variable.
517
518 */
519 void TableModel::addRow()
520 {
521 if (impl->rowCapacity > 0) {
522 ++impl->visibleRows;
523 impl->updateRows();
524
525 impl->ensureEditing();
526 }
527 }
528
529 /** updates the visibleRowCount variable with initialization.
530 if value is a scalar all columns will be initialized with the same value
531 otherwise must be a QVariantList
532 */
533 void TableModel::addRowAndCopyLast()
534 {
535 if (impl->rowCapacity <= 0) {
536 return;
537 }
538
539 if (impl->visibleRows <= 0) {
540 // no row available, so nothing to copy
541 addRow();
542 return;
543 }
544
545 const bool wasEditing = isEditing();
546
547 /* and initialize with prev row data */
548 for (const auto &column : impl->columnVector) {
549 const auto columnRows = column->getRows();
550 if (columnRows == 0 || impl->visibleRows >= columnRows) {
551 continue;
552 }
553 const auto v = column->data(impl->visibleRows - 1, Qt::DisplayRole)
554 .toString();
555 column->setData(impl->visibleRows, v, Qt::EditRole);
556 }
557 ++impl->visibleRows;
558 impl->updateRows();
559
560 if (!wasEditing) {
561 emit dataChanged(
562 index(0, 0),
563 index(impl->visibleRows - 1, impl->columnVector.count() - 1),
564 {Qt::BackgroundRole});
565
566 emit editingChanged(true);
567 }
568 }
569
570 /****************************************************************************/
571
572 /** updates the visibleRowCount variable.
573
574 */
575 bool TableModel::insertRows(
576 int position,
577 int count,
578 const QModelIndex &parent)
579 {
580 if (impl->rowCapacity < count || impl->columnVector.empty()) {
581 return false;
582 }
583 if (parent.isValid() || !hasVisibleRowsVariable()) {
584 return false;
585 }
586 const bool wasEditing = isEditing();
587 for (const auto column : impl->columnVector) {
588 column->impl->insertRow(position, count);
589 }
590
591 QModelIndex topLeft = index(position, 0);
592 QModelIndex bottomRight =
593 index(impl->visibleRows - 1, impl->columnVector.count() - 1);
594 emit dataChanged(topLeft, bottomRight);
595 impl->visibleRowCount.writeValue(impl->visibleRows + 1);
596
597 if (!wasEditing) {
598 emit dataChanged(
599 index(0, 0),
600 index(impl->visibleRows - 1, impl->columnVector.count() - 1),
601 {Qt::BackgroundRole});
602
603 emit editingChanged(true);
604 }
605 return true;
606 }
607
608 /****************************************************************************/
609
610 void TableModel::remRow()
611 {
612 removeRows(impl->visibleRows - 1, 1, {});
613 }
614
615 /****************************************************************************/
616
617 /** Calculates the number of table rows.
618 */
619 void TableModel::Impl::updateRows()
620 {
621 ColumnVector::const_iterator it;
622 unsigned int maxRows = 0;
623
624 for (it = columnVector.begin(); it != columnVector.end(); it++) {
625 unsigned int r = (*it)->getRows();
626 if (r > maxRows) {
627 maxRows = r;
628 }
629 }
630
631 if (maxRows > visibleRows) {
632 rowCapacity = maxRows - visibleRows;
633 maxRows = visibleRows;
634 }
635 else {
636 rowCapacity = 0;
637 }
638
639 if (maxRows > rows) {
640 parent->beginInsertRows(QModelIndex(), rows, maxRows - 1);
641 rows = maxRows;
642 parent->endInsertRows();
643 }
644 else if (maxRows < rows) {
645 parent->beginRemoveRows(QModelIndex(), maxRows, rows - 1);
646 rows = maxRows;
647 parent->endRemoveRows();
648 }
649 }
650
651 /****************************************************************************/
652
653 void QtPdCom::TableModel::Impl::ensureEditing()
654 {
655 if (parent->isEditing()) {
656 return;
657 }
658
659 for (const auto column : columnVector) {
660 column->impl->ensureEditData();
661 }
662
663 emit parent->dataChanged(
664 parent->index(0, 0),
665 parent->index(visibleRows - 1, columnVector.count() - 1),
666 {Qt::BackgroundRole});
667
668 emit parent->editingChanged(true);
669 }
670
671 /****************************************************************************/
672
673 /** Reacts on process variable dimension changes.
674 */
675 void TableModel::dimensionChanged()
676 {
677 impl->updateRows();
678 }
679
680 /****************************************************************************/
681
682 /** Reacts on header data changes.
683 */
684 void TableModel::columnHeaderChanged()
685 {
686 TableColumn *col = dynamic_cast<TableColumn *>(sender());
687 int j = impl->columnVector.indexOf(col);
688
689 if (j > -1) {
690 headerDataChanged(Qt::Horizontal, j, j);
691 }
692 }
693
694 /****************************************************************************/
695
696 /** Reacts on process variable changes.
697 */
698 void TableModel::valueChanged()
699 {
700 TableColumn *col = dynamic_cast<TableColumn *>(sender());
701
702 int j = impl->columnVector.indexOf(col);
703 if (j > -1) {
704 QModelIndex topLeft = index(0, j);
705 QModelIndex bottomRight =
706 index(qMin(col->getRows(), impl->rows) - 1, j);
707 emit dataChanged(topLeft, bottomRight);
708 /* qDebug() << "Table changes: " << topLeft.row()
709 * << topLeft.column() << bottomRight.row()
710 * << bottomRight.column(); */
711 }
712 }
713
714 /****************************************************************************/
715
716 void TableModel::highlightRowChanged()
717 {
718 unsigned int row = -1;
719
720 if (impl->valueHighlightRow.hasData()) {
721 row = impl->valueHighlightRow.getValue();
722 }
723
724 for (const auto column : impl->columnVector) {
725 column->setHighlightRow(row);
726 }
727
728 if ((impl->columnVector.count() > 0) && (row < impl->rows)) {
729 QModelIndex topLeft = index(row, 0);
730 QModelIndex bottomRight = index(row, impl->columnVector.count() - 1);
731 emit dataChanged(topLeft, bottomRight);
732 }
733 }
734
735 /****************************************************************************/
736
737 void TableModel::visibleRowCountChanged()
738 {
739 if (impl->visibleRowCount.hasData() && !isEditing()) {
740 impl->visibleRows = impl->visibleRowCount.getValue();
741 impl->updateRows();
742 }
743 }
744
745 /****************************************************************************/
746
747 QHash<int, QByteArray> TableModel::roleNames() const
748 {
749 // default role names
750 auto roles = QAbstractTableModel::roleNames();
751
752 // extended role names
753 roles[TableColumn::HighlightRole] = "highlight";
754 roles[TableColumn::ValidRole] = "valid";
755 roles[TableColumn::IsEnabledRole] = "isEnabled";
756 roles[TableColumn::IsEditingRole] = "isEditing";
757 roles[TableColumn::DecimalsRole] = "decimals";
758 roles[TableColumn::LowerLimitRole] = "lowerLimit";
759 roles[TableColumn::UpperLimitRole] = "upperLimit";
760 return roles;
761 }
762
763 /****************************************************************************/
764
765 QString TableModel::toCsv(
766 bool includeHeader,
767 QChar seperator,
768 const QLocale &locale) const
769 {
770 QString ans;
771
772 if (impl->columnVector.empty()) {
773 return "";
774 }
775
776 const auto sanitziedHeader = [this](int idx) {
777 QString ans = impl->columnVector[idx]->getHeader();
778 ans.replace('\n', ' ');
779 ans.replace('\r', QString());
780 return ans;
781 };
782
783 if (includeHeader) {
784 ans += sanitziedHeader(0);
785 for (int column = 1; column < impl->columnVector.count(); ++column) {
786 ans += seperator + sanitziedHeader(column);
787 }
788 ans += '\n';
789 }
790
791 for (int row = 0; row < rowCount({}); ++row) {
792 ans += impl->columnVector[0]->impl->getRow(row, locale);
793 for (int column = 1; column < impl->columnVector.count(); ++column) {
794 ans += seperator
795 + impl->columnVector[column]->impl->getRow(row, locale);
796 }
797 ans += '\n';
798 }
799 return ans;
800 }
801
802 /****************************************************************************/
803