File indexing completed on 2024-05-12 03:47:55

0001 /*
0002     File                 : SpreadsheetModel.cpp
0003     Project              : LabPlot
0004     Description          : Model for the access to a Spreadsheet
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2007 Tilman Benkert <thzs@gmx.net>
0007     SPDX-FileCopyrightText: 2009 Knut Franke <knut.franke@gmx.de>
0008     SPDX-FileCopyrightText: 2013-2022 Alexander Semke <alexander.semke@web.de>
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #include "backend/spreadsheet/SpreadsheetModel.h"
0013 #include "backend/core/Settings.h"
0014 #include "backend/core/datatypes/Double2StringFilter.h"
0015 #include "backend/lib/macros.h"
0016 #include "backend/lib/trace.h"
0017 #include "backend/spreadsheet/Spreadsheet.h"
0018 
0019 #include <KConfigGroup>
0020 #include <KLocalizedString>
0021 
0022 #include <QBrush>
0023 #include <QIcon>
0024 #include <QPalette>
0025 
0026 /*!
0027     \class SpreadsheetModel
0028     \brief  Model for the access to a Spreadsheet
0029 
0030     This is a model in the sense of Qt4 model/view framework which is used
0031     to access a Spreadsheet object from any of Qt4s view classes, typically a QTableView.
0032     Its main purposes are translating Spreadsheet signals into QAbstractItemModel signals
0033     and translating calls to the QAbstractItemModel read/write API into calls
0034     in the public API of Spreadsheet. In many cases a pointer to the addressed column
0035     is obtained by calling Spreadsheet::column() and the manipulation is done using the
0036     public API of column.
0037 
0038     \ingroup backend
0039 */
0040 SpreadsheetModel::SpreadsheetModel(Spreadsheet* spreadsheet)
0041     : QAbstractItemModel(nullptr)
0042     , m_spreadsheet(spreadsheet)
0043     , m_rowCount(spreadsheet->rowCount())
0044     , m_verticalHeaderCount(spreadsheet->rowCount())
0045     , m_columnCount(spreadsheet->columnCount()) {
0046     updateVerticalHeader();
0047     updateHorizontalHeader(false);
0048     connect(m_spreadsheet, &Spreadsheet::aspectDescriptionChanged, this, &SpreadsheetModel::handleDescriptionChange);
0049 
0050     // Used when single columns get deleted or added
0051     connect(m_spreadsheet,
0052             QOverload<const AbstractAspect*, int, const AbstractAspect*>::of(&Spreadsheet::childAspectAboutToBeAdded),
0053             this,
0054             &SpreadsheetModel::handleAspectAboutToBeAdded);
0055     connect(m_spreadsheet, &Spreadsheet::childAspectAdded, this, &SpreadsheetModel::handleAspectAdded);
0056     connect(m_spreadsheet, &Spreadsheet::childAspectAboutToBeRemoved, this, &SpreadsheetModel::handleAspectAboutToBeRemoved);
0057     connect(m_spreadsheet, &Spreadsheet::childAspectRemoved, this, &SpreadsheetModel::handleAspectRemoved);
0058 
0059     // Used when changing the column count
0060     connect(m_spreadsheet, &Spreadsheet::aspectsAboutToBeInserted, this, &SpreadsheetModel::handleAspectsAboutToBeInserted);
0061     connect(m_spreadsheet, &Spreadsheet::aspectsAboutToBeRemoved, this, &SpreadsheetModel::handleAspectsAboutToBeRemoved);
0062     connect(m_spreadsheet, &Spreadsheet::aspectsInserted, this, &SpreadsheetModel::handleAspectsInserted);
0063     connect(m_spreadsheet, &Spreadsheet::aspectsRemoved, this, &SpreadsheetModel::handleAspectsRemoved);
0064 
0065     connect(m_spreadsheet, &Spreadsheet::rowsAboutToBeInserted, this, &SpreadsheetModel::handleRowsAboutToBeInserted);
0066     connect(m_spreadsheet, &Spreadsheet::rowsAboutToBeRemoved, this, &SpreadsheetModel::handleRowsAboutToBeRemoved);
0067     connect(m_spreadsheet, &Spreadsheet::rowsInserted, this, &SpreadsheetModel::handleRowsInserted);
0068     connect(m_spreadsheet, &Spreadsheet::rowsRemoved, this, &SpreadsheetModel::handleRowsRemoved);
0069 
0070     m_suppressSignals = true;
0071     handleAspectsAboutToBeInserted(0, spreadsheet->columnCount() - 1);
0072     handleAspectsInserted(0, spreadsheet->columnCount() - 1); // make connections
0073     m_suppressSignals = false;
0074 
0075     m_spreadsheet->setModel(this);
0076 }
0077 
0078 void SpreadsheetModel::suppressSignals(bool value) {
0079     m_suppressSignals = value;
0080 
0081     // update the headers after all the data was added to the model
0082     // and we start listening to signals again
0083     if (m_suppressSignals)
0084         beginResetModel();
0085     else {
0086         m_rowCount = m_spreadsheet->rowCount();
0087         m_columnCount = m_spreadsheet->columnCount();
0088         updateHorizontalHeader(false);
0089         endResetModel();
0090     }
0091 }
0092 
0093 Qt::ItemFlags SpreadsheetModel::flags(const QModelIndex& index) const {
0094     if (index.isValid())
0095         return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
0096     else
0097         return Qt::ItemIsEnabled;
0098 }
0099 
0100 void SpreadsheetModel::setSearchText(const QString& text) {
0101     m_searchText = text;
0102 }
0103 
0104 QModelIndex SpreadsheetModel::index(const QString& text) const {
0105     const int colCount = m_spreadsheet->columnCount();
0106     const int rowCount = m_spreadsheet->rowCount();
0107     for (int col = 0; col < colCount; ++col) {
0108         auto* column = m_spreadsheet->column(col)->asStringColumn();
0109         for (int row = 0; row < rowCount; ++row) {
0110             if (column->textAt(row).indexOf(text) != -1)
0111                 return createIndex(row, col);
0112         }
0113     }
0114 
0115     return createIndex(-1, -1);
0116 }
0117 
0118 QVariant SpreadsheetModel::data(const QModelIndex& index, int role) const {
0119     if (!index.isValid())
0120         return {};
0121 
0122     const int row = index.row();
0123     const int col = index.column();
0124     const Column* col_ptr = m_spreadsheet->column(col);
0125 
0126     if (!col_ptr)
0127         return {};
0128 
0129     switch (role) {
0130     case Qt::ToolTipRole:
0131         if (col_ptr->isValid(row)) {
0132             if (col_ptr->isMasked(row))
0133                 return {i18n("%1, masked (ignored in all operations)", col_ptr->asStringColumn()->textAt(row))};
0134             else
0135                 return {col_ptr->asStringColumn()->textAt(row)};
0136         } else {
0137             if (col_ptr->isMasked(row))
0138                 return {i18n("invalid cell, masked (ignored in all operations)")};
0139             else
0140                 return {i18n("invalid cell (ignored in all operations)")};
0141         }
0142     case Qt::EditRole:
0143         if (col_ptr->columnMode() == AbstractColumn::ColumnMode::Double) {
0144             double value = col_ptr->valueAt(row);
0145             if (std::isnan(value))
0146                 return {QStringLiteral("-")};
0147             else if (std::isinf(value))
0148                 return {QStringLiteral("inf")};
0149             else
0150                 return {col_ptr->asStringColumn()->textAt(row)};
0151         }
0152 
0153         if (col_ptr->isValid(row))
0154             return {col_ptr->asStringColumn()->textAt(row)};
0155 
0156         // m_formula_mode is not used at the moment
0157         // if (m_formula_mode)
0158         //  return QVariant(col_ptr->formula(row));
0159 
0160         break;
0161     case Qt::DisplayRole:
0162         if (col_ptr->columnMode() == AbstractColumn::ColumnMode::Double) {
0163             double value = col_ptr->valueAt(row);
0164             if (std::isnan(value))
0165                 return {QStringLiteral("-")};
0166             else if (std::isinf(value))
0167                 return {UTF8_QSTRING("∞")};
0168             else
0169                 return {col_ptr->asStringColumn()->textAt(row)};
0170         }
0171 
0172         if (!col_ptr->isValid(row))
0173             return {QStringLiteral("-")};
0174 
0175         // m_formula_mode is not used at the moment
0176         // if (m_formula_mode)
0177         //  return QVariant(col_ptr->formula(row));
0178 
0179         return {col_ptr->asStringColumn()->textAt(row)};
0180     case Qt::ForegroundRole:
0181         if (!col_ptr->isValid(row))
0182             return {QBrush(Qt::red)};
0183         return color(col_ptr, row, AbstractColumn::Formatting::Foreground);
0184     case Qt::BackgroundRole:
0185         if (m_searchText.isEmpty())
0186             return color(col_ptr, row, AbstractColumn::Formatting::Background);
0187         else {
0188             if (col_ptr->asStringColumn()->textAt(row).indexOf(m_searchText) == -1)
0189                 return color(col_ptr, row, AbstractColumn::Formatting::Background);
0190             else
0191                 return {QApplication::palette().color(QPalette::Highlight)};
0192         }
0193     case static_cast<int>(CustomDataRole::MaskingRole):
0194         return {col_ptr->isMasked(row)};
0195     case static_cast<int>(CustomDataRole::FormulaRole):
0196         return {col_ptr->formula(row)};
0197     case Qt::DecorationRole:
0198         return color(col_ptr, row, AbstractColumn::Formatting::Icon);
0199         //      if (m_formula_mode)
0200         //          return QIcon(QPixmap(":/equals.png")); //TODO
0201     }
0202 
0203     return {};
0204 }
0205 
0206 QVariant SpreadsheetModel::headerData(int section, Qt::Orientation orientation, int role) const {
0207     if ((orientation == Qt::Horizontal && section > m_columnCount - 1) || (orientation == Qt::Vertical && section > m_rowCount - 1))
0208         return {};
0209 
0210     switch (orientation) {
0211     case Qt::Horizontal:
0212         switch (role) {
0213         case Qt::DisplayRole:
0214         case Qt::ToolTipRole:
0215         case Qt::EditRole:
0216             return m_horizontal_header_data.at(section);
0217         case Qt::DecorationRole:
0218             return m_spreadsheet->child<Column>(section)->icon();
0219         case static_cast<int>(CustomDataRole::CommentRole):
0220             return m_spreadsheet->child<Column>(section)->comment();
0221         }
0222         break;
0223     case Qt::Vertical:
0224         switch (role) {
0225         case Qt::DisplayRole:
0226         case Qt::ToolTipRole:
0227             return section + 1;
0228         }
0229     }
0230 
0231     return {};
0232 }
0233 
0234 int SpreadsheetModel::rowCount(const QModelIndex& /*parent*/) const {
0235     return m_rowCount;
0236 }
0237 
0238 int SpreadsheetModel::columnCount(const QModelIndex& /*parent*/) const {
0239     return m_columnCount;
0240 }
0241 
0242 bool SpreadsheetModel::setData(const QModelIndex& index, const QVariant& value, int role) {
0243     if (!index.isValid())
0244         return false;
0245 
0246     int row = index.row();
0247     auto* column = m_spreadsheet->column(index.column());
0248 
0249     // DEBUG("SpreadsheetModel::setData() value = " << STDSTRING(value.toString()))
0250 
0251     // don't do anything if no new value was provided
0252     if (column->columnMode() == AbstractColumn::ColumnMode::Double) {
0253         bool ok;
0254         double new_value = QLocale().toDouble(value.toString(), &ok);
0255         if (ok) {
0256             if (column->valueAt(row) == new_value)
0257                 return false;
0258         } else {
0259             // an empty (non-numeric value) was provided
0260             if (std::isnan(column->valueAt(row)))
0261                 return false;
0262         }
0263     } else {
0264         if (column->asStringColumn()->textAt(row) == value.toString())
0265             return false;
0266     }
0267 
0268     switch (role) {
0269     case Qt::EditRole:
0270         // remark: the validity of the cell is determined by the input filter
0271         if (m_formula_mode)
0272             column->setFormula(row, value.toString());
0273         else
0274             column->asStringColumn()->setTextAt(row, value.toString());
0275         return true;
0276     case static_cast<int>(CustomDataRole::MaskingRole):
0277         m_spreadsheet->column(index.column())->setMasked(row, value.toBool());
0278         return true;
0279     case static_cast<int>(CustomDataRole::FormulaRole):
0280         m_spreadsheet->column(index.column())->setFormula(row, value.toString());
0281         return true;
0282     }
0283 
0284     return false;
0285 }
0286 
0287 QModelIndex SpreadsheetModel::index(int row, int column, const QModelIndex& /*parent*/) const {
0288     return createIndex(row, column);
0289 }
0290 
0291 QModelIndex SpreadsheetModel::parent(const QModelIndex& /*child*/) const {
0292     return QModelIndex{};
0293 }
0294 
0295 bool SpreadsheetModel::hasChildren(const QModelIndex& /*parent*/) const {
0296     return false;
0297 }
0298 
0299 void SpreadsheetModel::handleAspectsAboutToBeInserted(int first, int last) {
0300     if (m_suppressSignals)
0301         return;
0302     m_spreadsheetColumnCountChanging = true;
0303     beginInsertColumns(QModelIndex(), first, last);
0304 }
0305 
0306 void SpreadsheetModel::handleAspectAboutToBeAdded(const AbstractAspect* parent, int index, const AbstractAspect* aspect) {
0307     if (m_spreadsheetColumnCountChanging || m_suppressSignals)
0308         return;
0309     const Column* col = dynamic_cast<const Column*>(aspect);
0310     if (!col || parent != m_spreadsheet)
0311         return;
0312     beginInsertColumns(QModelIndex(), index, index);
0313 }
0314 
0315 void SpreadsheetModel::handleAspectsInserted(int first, int last) {
0316     const auto& children = m_spreadsheet->children<Column>();
0317     if (first < 0 || first >= children.count() || last >= children.count() || first > last)
0318         return;
0319 
0320     for (int i = first; i <= last; i++) {
0321         const auto* col = children.at(i);
0322         connect(col, &Column::plotDesignationChanged, this, &SpreadsheetModel::handlePlotDesignationChange);
0323         connect(col, &Column::modeChanged, this, &SpreadsheetModel::handleDataChange);
0324         connect(col, &Column::dataChanged, this, &SpreadsheetModel::handleDataChange);
0325         connect(col, &Column::formatChanged, this, &SpreadsheetModel::handleDataChange);
0326         connect(col, &Column::modeChanged, this, &SpreadsheetModel::handleModeChange);
0327         connect(col, &Column::maskingChanged, this, &SpreadsheetModel::handleDataChange);
0328         connect(col, &Column::formulaChanged, this, &SpreadsheetModel::handlePlotDesignationChange); // we can re-use the same slot to update the header here
0329         connect(col->outputFilter(), &AbstractSimpleFilter::digitsChanged, this, &SpreadsheetModel::handleDigitsChange);
0330     }
0331 
0332     handleAspectCountChanged();
0333     if (!m_suppressSignals)
0334         endInsertColumns();
0335     m_spreadsheetColumnCountChanging = false;
0336 }
0337 
0338 void SpreadsheetModel::handleAspectAdded(const AbstractAspect* aspect) {
0339     // PERFTRACE(Q_FUNC_INFO);
0340     if (m_spreadsheetColumnCountChanging)
0341         return;
0342     const Column* col = dynamic_cast<const Column*>(aspect);
0343     if (!col || aspect->parentAspect() != m_spreadsheet)
0344         return;
0345     int index = m_spreadsheet->indexOfChild<Column>(aspect);
0346     handleAspectsInserted(index, index);
0347 }
0348 
0349 void SpreadsheetModel::handleAspectsAboutToBeRemoved(int first, int last) {
0350     if (m_suppressSignals)
0351         return;
0352 
0353     const auto& children = m_spreadsheet->children<Column>();
0354     if (first < 0 || first >= children.count() || last >= children.count() || first > last)
0355         return;
0356 
0357     m_spreadsheetColumnCountChanging = true;
0358 
0359     beginRemoveColumns(QModelIndex(), first, last);
0360     for (int i = first; i <= last; i++)
0361         disconnect(children.at(i), nullptr, this, nullptr);
0362 }
0363 
0364 void SpreadsheetModel::handleAspectAboutToBeRemoved(const AbstractAspect* aspect) {
0365     if (m_suppressSignals || m_spreadsheetColumnCountChanging)
0366         return;
0367 
0368     const Column* col = dynamic_cast<const Column*>(aspect);
0369     if (!col || aspect->parentAspect() != m_spreadsheet)
0370         return;
0371 
0372     const int index = m_spreadsheet->indexOfChild<AbstractAspect>(aspect);
0373     beginRemoveColumns(QModelIndex(), index, index);
0374     disconnect(col, nullptr, this, nullptr);
0375 }
0376 
0377 void SpreadsheetModel::handleAspectsRemoved() {
0378     if (m_suppressSignals)
0379         return;
0380     handleAspectCountChanged();
0381     endRemoveColumns();
0382     m_spreadsheetColumnCountChanging = false;
0383 }
0384 
0385 void SpreadsheetModel::handleAspectRemoved(const AbstractAspect* parent, const AbstractAspect* /*before*/, const AbstractAspect* child) {
0386     // same conditions as in handleAspectAboutToBeRemoved()
0387     if (m_spreadsheetColumnCountChanging || child->type() != AspectType::Column || parent != m_spreadsheet)
0388         return;
0389 
0390     handleAspectsRemoved();
0391 }
0392 
0393 void SpreadsheetModel::handleAspectCountChanged() {
0394     if (m_suppressSignals)
0395         return;
0396 
0397     m_columnCount = m_spreadsheet->columnCount();
0398     updateHorizontalHeader(false);
0399 }
0400 
0401 void SpreadsheetModel::handleDescriptionChange(const AbstractAspect* aspect) {
0402     if (m_suppressSignals)
0403         return;
0404 
0405     const Column* col = dynamic_cast<const Column*>(aspect);
0406     if (!col || aspect->parentAspect() != m_spreadsheet)
0407         return;
0408 
0409     if (!m_suppressSignals) {
0410         updateHorizontalHeader(false);
0411         int index = m_spreadsheet->indexOfChild<Column>(col);
0412         Q_EMIT headerDataChanged(Qt::Horizontal, index, index);
0413     }
0414 }
0415 
0416 void SpreadsheetModel::handleModeChange(const AbstractColumn* col) {
0417     if (m_suppressSignals)
0418         return;
0419 
0420     updateHorizontalHeader(false);
0421     int index = m_spreadsheet->indexOfChild<Column>(col);
0422     Q_EMIT headerDataChanged(Qt::Horizontal, index, index);
0423     handleDataChange(col);
0424 
0425     // output filter was changed after the mode change, update the signal-slot connection
0426     disconnect(nullptr, SIGNAL(digitsChanged()), this, SLOT(handledigitsChange()));
0427     connect(static_cast<const Column*>(col)->outputFilter(), &AbstractSimpleFilter::digitsChanged, this, &SpreadsheetModel::handleDigitsChange);
0428 }
0429 
0430 void SpreadsheetModel::handleDigitsChange() {
0431     if (m_suppressSignals)
0432         return;
0433 
0434     const auto* filter = dynamic_cast<const Double2StringFilter*>(QObject::sender());
0435     if (!filter)
0436         return;
0437 
0438     const AbstractColumn* col = filter->output(0);
0439     handleDataChange(col);
0440 }
0441 
0442 void SpreadsheetModel::handlePlotDesignationChange(const AbstractColumn* col) {
0443     if (m_suppressSignals)
0444         return;
0445 
0446     updateHorizontalHeader(false);
0447     int index = m_spreadsheet->indexOfChild<Column>(col);
0448     Q_EMIT headerDataChanged(Qt::Horizontal, index, m_columnCount - 1);
0449 }
0450 
0451 void SpreadsheetModel::handleDataChange(const AbstractColumn* col) {
0452     if (m_suppressSignals)
0453         return;
0454 
0455     int i = m_spreadsheet->indexOfChild<Column>(col);
0456     Q_EMIT dataChanged(index(0, i), index(m_rowCount - 1, i));
0457 }
0458 
0459 void SpreadsheetModel::handleRowsAboutToBeInserted(int before, int last) {
0460     if (m_suppressSignals)
0461         return;
0462     beginInsertRows(QModelIndex(), before, last);
0463 }
0464 
0465 void SpreadsheetModel::handleRowsAboutToBeRemoved(int first, int last) {
0466     if (m_suppressSignals)
0467         return;
0468     beginRemoveRows(QModelIndex(), first, last);
0469 }
0470 
0471 void SpreadsheetModel::handleRowsInserted(int newRowCount) {
0472     handleRowCountChanged(newRowCount);
0473     if (m_suppressSignals)
0474         return;
0475     endInsertRows();
0476 }
0477 
0478 void SpreadsheetModel::handleRowsRemoved(int newRowCount) {
0479     handleRowCountChanged(newRowCount);
0480     if (m_suppressSignals)
0481         return;
0482     endRemoveRows();
0483 }
0484 
0485 void SpreadsheetModel::handleRowCountChanged(int newRowCount) {
0486     if (m_suppressSignals)
0487         return;
0488     m_rowCount = newRowCount;
0489     updateVerticalHeader();
0490 }
0491 
0492 void SpreadsheetModel::updateVerticalHeader() {
0493     m_verticalHeaderCount = m_rowCount;
0494 }
0495 
0496 void SpreadsheetModel::updateHorizontalHeader(bool sendSignal) {
0497     int column_count = m_spreadsheet->childCount<Column>();
0498 
0499     while (m_horizontal_header_data.size() < column_count)
0500         m_horizontal_header_data << QString();
0501 
0502     while (m_horizontal_header_data.size() > column_count)
0503         m_horizontal_header_data.removeLast();
0504 
0505     KConfigGroup group = Settings::group(QStringLiteral("Settings_Spreadsheet"));
0506     bool showColumnType = group.readEntry(QLatin1String("ShowColumnType"), true);
0507     bool showPlotDesignation = group.readEntry(QLatin1String("ShowPlotDesignation"), true);
0508 
0509     for (int i = 0; i < column_count; i++) {
0510         Column* col = m_spreadsheet->child<Column>(i);
0511         QString header;
0512         if (!col->formula().isEmpty() && col->formulaAutoUpdate())
0513             header += QLatin1String("*");
0514         header += col->name();
0515 
0516         if (showColumnType)
0517             header += QLatin1String(" {") + col->columnModeString() + QLatin1Char('}');
0518 
0519         if (showPlotDesignation) {
0520             if (col->plotDesignation() != AbstractColumn::PlotDesignation::NoDesignation)
0521                 header += QLatin1String(" ") + col->plotDesignationString();
0522         }
0523 
0524         m_horizontal_header_data.replace(i, header);
0525     }
0526 
0527     if (sendSignal)
0528         Q_EMIT headerDataChanged(Qt::Horizontal, 0, column_count - 1);
0529 }
0530 
0531 Column* SpreadsheetModel::column(int index) {
0532     return m_spreadsheet->column(index);
0533 }
0534 
0535 void SpreadsheetModel::activateFormulaMode(bool on) {
0536     if (m_formula_mode == on)
0537         return;
0538 
0539     m_formula_mode = on;
0540     if (m_rowCount > 0 && m_columnCount > 0)
0541         Q_EMIT dataChanged(index(0, 0), index(m_rowCount - 1, m_columnCount - 1));
0542 }
0543 
0544 bool SpreadsheetModel::formulaModeActive() const {
0545     return m_formula_mode;
0546 }
0547 
0548 QVariant SpreadsheetModel::color(const AbstractColumn* column, int row, AbstractColumn::Formatting type) const {
0549     if ((!column->isNumeric() && column->columnMode() != AbstractColumn::ColumnMode::Text) || !column->isValid(row) || !column->hasHeatmapFormat())
0550         return {};
0551 
0552     const auto& format = column->heatmapFormat();
0553     if (format.type != type || format.colors.isEmpty())
0554         return {};
0555 
0556     int index = 0;
0557     if (column->isNumeric()) {
0558         double value = column->valueAt(row);
0559         double range = (format.max - format.min) / format.colors.count();
0560 
0561         if (value > format.max)
0562             index = format.colors.count() - 1;
0563         else {
0564             for (int i = 0; i < format.colors.count(); ++i) {
0565                 if (value <= format.min + (i + 1) * range) {
0566                     index = i;
0567                     break;
0568                 }
0569             }
0570         }
0571     } else {
0572         index = column->dictionaryIndex(row);
0573     }
0574 
0575     if (index < format.colors.count())
0576         return {QColor(format.colors.at(index))};
0577     else
0578         return {QColor(format.colors.constLast())};
0579 }