"Fossies" - the Fresh Open Source Software Archive

Member "labplot-2.8.2/src/backend/spreadsheet/SpreadsheetModel.cpp" (24 Feb 2021, 17764 Bytes) of package /linux/privat/labplot-2.8.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "SpreadsheetModel.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.8.1_vs_2.8.2.

    1 /***************************************************************************
    2     File                 : SpreadsheetModel.cpp
    3     Project              : LabPlot
    4     Description          : Model for the access to a Spreadsheet
    5     --------------------------------------------------------------------
    6     Copyright            : (C) 2007 Tilman Benkert (thzs@gmx.net)
    7     Copyright            : (C) 2009 Knut Franke (knut.franke@gmx.de)
    8     Copyright            : (C) 2013-2017 Alexander Semke (alexander.semke@web.de)
    9 
   10  ***************************************************************************/
   11 
   12 /***************************************************************************
   13  *                                                                         *
   14  *  This program is free software; you can redistribute it and/or modify   *
   15  *  it under the terms of the GNU General Public License as published by   *
   16  *  the Free Software Foundation; either version 2 of the License, or      *
   17  *  (at your option) any later version.                                    *
   18  *                                                                         *
   19  *  This program is distributed in the hope that it will be useful,        *
   20  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
   21  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
   22  *  GNU General Public License for more details.                           *
   23  *                                                                         *
   24  *   You should have received a copy of the GNU General Public License     *
   25  *   along with this program; if not, write to the Free Software           *
   26  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
   27  *   Boston, MA  02110-1301  USA                                           *
   28  *                                                                         *
   29  ***************************************************************************/
   30 
   31 #include "backend/spreadsheet/Spreadsheet.h"
   32 #include "backend/spreadsheet/SpreadsheetModel.h"
   33 #include "backend/core/datatypes/Double2StringFilter.h"
   34 
   35 #include <QBrush>
   36 #include <QIcon>
   37 
   38 #include <KLocalizedString>
   39 
   40 /*!
   41     \class SpreadsheetModel
   42     \brief  Model for the access to a Spreadsheet
   43 
   44     This is a model in the sense of Qt4 model/view framework which is used
   45     to access a Spreadsheet object from any of Qt4s view classes, typically a QTableView.
   46     Its main purposes are translating Spreadsheet signals into QAbstractItemModel signals
   47     and translating calls to the QAbstractItemModel read/write API into calls
   48     in the public API of Spreadsheet. In many cases a pointer to the addressed column
   49     is obtained by calling Spreadsheet::column() and the manipulation is done using the
   50     public API of column.
   51 
   52     \ingroup backend
   53 */
   54 SpreadsheetModel::SpreadsheetModel(Spreadsheet* spreadsheet) : QAbstractItemModel(nullptr),
   55     m_spreadsheet(spreadsheet),
   56     m_rowCount(spreadsheet->rowCount()),
   57     m_columnCount(spreadsheet->columnCount()) {
   58 
   59     updateVerticalHeader();
   60     updateHorizontalHeader();
   61 
   62     connect(m_spreadsheet, &Spreadsheet::aspectAdded, this, &SpreadsheetModel::handleAspectAdded);
   63     connect(m_spreadsheet, &Spreadsheet::aspectAboutToBeRemoved, this, &SpreadsheetModel::handleAspectAboutToBeRemoved);
   64     connect(m_spreadsheet, &Spreadsheet::aspectRemoved, this, &SpreadsheetModel::handleAspectRemoved);
   65     connect(m_spreadsheet, &Spreadsheet::aspectDescriptionChanged, this, &SpreadsheetModel::handleDescriptionChange);
   66 
   67     for (int i = 0; i < spreadsheet->columnCount(); ++i) {
   68         beginInsertColumns(QModelIndex(), i, i);
   69         handleAspectAdded(spreadsheet->column(i));
   70     }
   71 
   72     m_spreadsheet->setModel(this);
   73 }
   74 
   75 void SpreadsheetModel::suppressSignals(bool value) {
   76     m_suppressSignals = value;
   77 
   78     //update the headers after all the data was added to the model
   79     //and we start listening to signals again
   80     if (!m_suppressSignals) {
   81         m_rowCount = m_spreadsheet->rowCount();
   82         m_columnCount = m_spreadsheet->columnCount();
   83         m_spreadsheet->emitColumnCountChanged();
   84         updateVerticalHeader();
   85         updateHorizontalHeader();
   86         beginResetModel();
   87         endResetModel();
   88     }
   89 }
   90 
   91 Qt::ItemFlags SpreadsheetModel::flags(const QModelIndex& index) const {
   92     if (index.isValid())
   93         return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
   94     else
   95         return Qt::ItemIsEnabled;
   96 }
   97 
   98 QVariant SpreadsheetModel::data(const QModelIndex& index, int role) const {
   99     if ( !index.isValid() )
  100         return QVariant();
  101 
  102     const int row = index.row();
  103     const int col = index.column();
  104     const Column* col_ptr = m_spreadsheet->column(col);
  105 
  106     if (!col_ptr)
  107         return QVariant();
  108 
  109     switch (role) {
  110     case Qt::ToolTipRole:
  111         if (col_ptr->isValid(row)) {
  112             if (col_ptr->isMasked(row))
  113                 return QVariant(i18n("%1, masked (ignored in all operations)", col_ptr->asStringColumn()->textAt(row)));
  114             else
  115                 return QVariant(col_ptr->asStringColumn()->textAt(row));
  116         } else {
  117             if (col_ptr->isMasked(row))
  118                 return QVariant(i18n("invalid cell, masked (ignored in all operations)"));
  119             else
  120                 return QVariant(i18n("invalid cell (ignored in all operations)"));
  121         }
  122     case Qt::EditRole:
  123         if (col_ptr->columnMode() == AbstractColumn::ColumnMode::Numeric) {
  124             double value = col_ptr->valueAt(row);
  125             if (std::isnan(value))
  126                 return QVariant("-");
  127             else if (std::isinf(value))
  128                 return QVariant(QLatin1String("inf"));
  129             else
  130                 return QVariant(col_ptr->asStringColumn()->textAt(row));
  131         }
  132 
  133         if (col_ptr->isValid(row))
  134             return QVariant(col_ptr->asStringColumn()->textAt(row));
  135 
  136         //m_formula_mode is not used at the moment
  137         //if (m_formula_mode)
  138         //  return QVariant(col_ptr->formula(row));
  139 
  140         break;
  141     case Qt::DisplayRole:
  142         if (col_ptr->columnMode() == AbstractColumn::ColumnMode::Numeric) {
  143             double value = col_ptr->valueAt(row);
  144             if (std::isnan(value))
  145                 return QVariant("-");
  146             else if (std::isinf(value))
  147                 return QVariant(UTF8_QSTRING("∞"));
  148             else
  149                 return QVariant(col_ptr->asStringColumn()->textAt(row));
  150         }
  151 
  152         if (!col_ptr->isValid(row))
  153             return QVariant("-");
  154 
  155         //m_formula_mode is not used at the moment
  156         //if (m_formula_mode)
  157         //  return QVariant(col_ptr->formula(row));
  158 
  159         return QVariant(col_ptr->asStringColumn()->textAt(row));
  160     case Qt::ForegroundRole:
  161         if (!col_ptr->isValid(row))
  162             return QVariant(QBrush(Qt::red));
  163         break;
  164     case static_cast<int>(CustomDataRole::MaskingRole):
  165         return QVariant(col_ptr->isMasked(row));
  166     case static_cast<int>(CustomDataRole::FormulaRole):
  167         return QVariant(col_ptr->formula(row));
  168 //  case Qt::DecorationRole:
  169 //      if (m_formula_mode)
  170 //          return QIcon(QPixmap(":/equals.png")); //TODO
  171     }
  172 
  173     return QVariant();
  174 }
  175 
  176 QVariant SpreadsheetModel::headerData(int section, Qt::Orientation orientation, int role) const {
  177     if ( (orientation == Qt::Horizontal && section > m_columnCount-1)
  178         || (orientation == Qt::Vertical && section > m_rowCount-1) )
  179         return QVariant();
  180 
  181     switch (orientation) {
  182     case Qt::Horizontal:
  183         switch (role) {
  184         case Qt::DisplayRole:
  185         case Qt::ToolTipRole:
  186         case Qt::EditRole:
  187             return m_horizontal_header_data.at(section);
  188         case Qt::DecorationRole:
  189             return m_spreadsheet->child<Column>(section)->icon();
  190         case static_cast<int>(CustomDataRole::CommentRole):
  191             return m_spreadsheet->child<Column>(section)->comment();
  192         }
  193         break;
  194     case Qt::Vertical:
  195         switch (role) {
  196         case Qt::DisplayRole:
  197         case Qt::ToolTipRole:
  198             return m_vertical_header_data.at(section);
  199         }
  200     }
  201 
  202     return QVariant();
  203 }
  204 
  205 int SpreadsheetModel::rowCount(const QModelIndex& parent) const {
  206     Q_UNUSED(parent)
  207     return m_rowCount;
  208 }
  209 
  210 int SpreadsheetModel::columnCount(const QModelIndex& parent) const {
  211     Q_UNUSED(parent)
  212     return m_columnCount;
  213 }
  214 
  215 bool SpreadsheetModel::setData(const QModelIndex& index, const QVariant& value, int role) {
  216     if (!index.isValid())
  217         return false;
  218 
  219     int row = index.row();
  220     Column* column = m_spreadsheet->column(index.column());
  221 
  222     SET_NUMBER_LOCALE
  223     //DEBUG("SpreadsheetModel::setData() value = " << STDSTRING(value.toString()))
  224 
  225     //don't do anything if no new value was provided
  226     if (column->columnMode() == AbstractColumn::ColumnMode::Numeric) {
  227         bool ok;
  228         double new_value = numberLocale.toDouble(value.toString(), &ok);
  229         if (ok) {
  230             if (column->valueAt(row) == new_value)
  231                 return false;
  232         } else {
  233             //an empty (non-numeric value) was provided
  234             if (std::isnan(column->valueAt(row)))
  235                 return false;
  236         }
  237     } else {
  238         if (column->asStringColumn()->textAt(row) == value.toString())
  239             return false;
  240     }
  241 
  242     switch (role) {
  243     case Qt::EditRole:
  244         // remark: the validity of the cell is determined by the input filter
  245         if (m_formula_mode)
  246             column->setFormula(row, value.toString());
  247         else
  248             column->asStringColumn()->setTextAt(row, value.toString());
  249         return true;
  250     case static_cast<int>(CustomDataRole::MaskingRole):
  251         m_spreadsheet->column(index.column())->setMasked(row, value.toBool());
  252         return true;
  253     case static_cast<int>(CustomDataRole::FormulaRole):
  254         m_spreadsheet->column(index.column())->setFormula(row, value.toString());
  255         return true;
  256     }
  257 
  258     return false;
  259 }
  260 
  261 QModelIndex SpreadsheetModel::index(int row, int column, const QModelIndex& parent) const {
  262     Q_UNUSED(parent)
  263     return createIndex(row, column);
  264 }
  265 
  266 QModelIndex SpreadsheetModel::parent(const QModelIndex& child) const {
  267     Q_UNUSED(child)
  268     return QModelIndex{};
  269 }
  270 
  271 bool SpreadsheetModel::hasChildren(const QModelIndex& parent) const {
  272     Q_UNUSED(parent)
  273     return false;
  274 }
  275 
  276 void SpreadsheetModel::handleAspectAdded(const AbstractAspect* aspect) {
  277     const Column* col = dynamic_cast<const Column*>(aspect);
  278     if (!col || aspect->parentAspect() != m_spreadsheet)
  279         return;
  280 
  281     connect(col, &Column::plotDesignationChanged, this, &SpreadsheetModel::handlePlotDesignationChange);
  282     connect(col, &Column::modeChanged, this, &SpreadsheetModel::handleDataChange);
  283     connect(col, &Column::dataChanged, this, &SpreadsheetModel::handleDataChange);
  284     connect(col, &Column::formatChanged, this, &SpreadsheetModel::handleDataChange);
  285     connect(col, &Column::modeChanged, this, &SpreadsheetModel::handleModeChange);
  286     connect(col, &Column::rowsInserted, this, &SpreadsheetModel::handleRowsInserted);
  287     connect(col, &Column::rowsRemoved, this, &SpreadsheetModel::handleRowsRemoved);
  288     connect(col, &Column::maskingChanged, this, &SpreadsheetModel::handleDataChange);
  289     connect(col->outputFilter(), &AbstractSimpleFilter::digitsChanged, this, &SpreadsheetModel::handleDigitsChange);
  290 
  291     if (!m_suppressSignals) {
  292         beginResetModel();
  293         updateVerticalHeader();
  294         updateHorizontalHeader();
  295         endResetModel();
  296 
  297         int index = m_spreadsheet->indexOfChild<AbstractAspect>(aspect);
  298         m_columnCount = m_spreadsheet->columnCount();
  299         m_spreadsheet->emitColumnCountChanged();
  300         emit headerDataChanged(Qt::Horizontal, index, m_columnCount - 1);
  301     }
  302 }
  303 
  304 void SpreadsheetModel::handleAspectAboutToBeRemoved(const AbstractAspect* aspect) {
  305     if (m_suppressSignals)
  306         return;
  307 
  308     const Column* col = dynamic_cast<const Column*>(aspect);
  309     if (!col || aspect->parentAspect() != m_spreadsheet)
  310         return;
  311 
  312     beginResetModel();
  313     disconnect(col, nullptr, this, nullptr);
  314 }
  315 
  316 void SpreadsheetModel::handleAspectRemoved(const AbstractAspect* parent, const AbstractAspect* before, const AbstractAspect* child) {
  317     Q_UNUSED(before)
  318     const Column* col = dynamic_cast<const Column*>(child);
  319     if (!col || parent != m_spreadsheet)
  320         return;
  321 
  322     updateVerticalHeader();
  323     updateHorizontalHeader();
  324 
  325     m_columnCount = m_spreadsheet->columnCount();
  326     m_spreadsheet->emitColumnCountChanged();
  327 
  328     endResetModel();
  329 }
  330 
  331 void SpreadsheetModel::handleDescriptionChange(const AbstractAspect* aspect) {
  332     if (m_suppressSignals)
  333         return;
  334 
  335     const Column* col = dynamic_cast<const Column*>(aspect);
  336     if (!col || aspect->parentAspect() != m_spreadsheet)
  337         return;
  338 
  339     if (!m_suppressSignals) {
  340         updateHorizontalHeader();
  341         int index = m_spreadsheet->indexOfChild<Column>(col);
  342         emit headerDataChanged(Qt::Horizontal, index, index);
  343     }
  344 }
  345 
  346 void SpreadsheetModel::handleModeChange(const AbstractColumn* col) {
  347     if (m_suppressSignals)
  348         return;
  349 
  350     updateHorizontalHeader();
  351     int index = m_spreadsheet->indexOfChild<Column>(col);
  352     emit headerDataChanged(Qt::Horizontal, index, index);
  353     handleDataChange(col);
  354 
  355     //output filter was changed after the mode change, update the signal-slot connection
  356     disconnect(nullptr, SIGNAL(digitsChanged()), this, SLOT(handledigitsChange()));
  357     connect(static_cast<const Column*>(col)->outputFilter(), &AbstractSimpleFilter::digitsChanged, this, &SpreadsheetModel::handleDigitsChange);
  358 }
  359 
  360 void SpreadsheetModel::handleDigitsChange() {
  361     if (m_suppressSignals)
  362         return;
  363 
  364     const auto* filter = dynamic_cast<const Double2StringFilter*>(QObject::sender());
  365     if (!filter)
  366         return;
  367 
  368     const AbstractColumn* col = filter->output(0);
  369     handleDataChange(col);
  370 }
  371 
  372 void SpreadsheetModel::handlePlotDesignationChange(const AbstractColumn* col) {
  373     if (m_suppressSignals)
  374         return;
  375 
  376     updateHorizontalHeader();
  377     int index = m_spreadsheet->indexOfChild<Column>(col);
  378     emit headerDataChanged(Qt::Horizontal, index, m_columnCount-1);
  379 }
  380 
  381 void SpreadsheetModel::handleDataChange(const AbstractColumn* col) {
  382     if (m_suppressSignals)
  383         return;
  384 
  385     int i = m_spreadsheet->indexOfChild<Column>(col);
  386     emit dataChanged(index(0, i), index(m_rowCount-1, i));
  387 }
  388 
  389 void SpreadsheetModel::handleRowsInserted(const AbstractColumn* col, int before, int count) {
  390     if (m_suppressSignals)
  391         return;
  392 
  393     Q_UNUSED(before) Q_UNUSED(count)
  394     int i = m_spreadsheet->indexOfChild<Column>(col);
  395     m_rowCount = col->rowCount();
  396     emit dataChanged(index(0, i), index(m_rowCount-1, i));
  397     updateVerticalHeader();
  398     m_spreadsheet->emitRowCountChanged();
  399 }
  400 
  401 void SpreadsheetModel::handleRowsRemoved(const AbstractColumn* col, int first, int count) {
  402     if (m_suppressSignals)
  403         return;
  404 
  405     Q_UNUSED(first) Q_UNUSED(count)
  406     int i = m_spreadsheet->indexOfChild<Column>(col);
  407     m_rowCount = col->rowCount();
  408     emit dataChanged(index(0, i), index(m_rowCount-1, i));
  409     updateVerticalHeader();
  410     m_spreadsheet->emitRowCountChanged();
  411 }
  412 
  413 void SpreadsheetModel::updateVerticalHeader() {
  414     int old_rows = m_vertical_header_data.size();
  415     int new_rows = m_rowCount;
  416 
  417     if (new_rows > old_rows) {
  418         beginInsertRows(QModelIndex(), old_rows, new_rows-1);
  419 
  420         for (int i = old_rows+1; i <= new_rows; i++)
  421             m_vertical_header_data << i;
  422 
  423         endInsertRows();
  424     } else if (new_rows < old_rows) {
  425         beginRemoveRows(QModelIndex(), new_rows, old_rows-1);
  426 
  427         while (m_vertical_header_data.size() > new_rows)
  428             m_vertical_header_data.removeLast();
  429 
  430         endRemoveRows();
  431     }
  432 }
  433 
  434 void SpreadsheetModel::updateHorizontalHeader() {
  435     int column_count = m_spreadsheet->childCount<Column>();
  436 
  437     while (m_horizontal_header_data.size() < column_count)
  438         m_horizontal_header_data << QString();
  439 
  440     while (m_horizontal_header_data.size() > column_count)
  441         m_horizontal_header_data.removeLast();
  442 
  443     KConfigGroup group = KSharedConfig::openConfig()->group("Settings_Spreadsheet");
  444     bool showColumnType = group.readEntry(QLatin1String("ShowColumnType"), true);
  445     bool showPlotDesignation = group.readEntry(QLatin1String("ShowPlotDesignation"), true);
  446 
  447     for (int i = 0; i < column_count; i++) {
  448         Column* col = m_spreadsheet->child<Column>(i);
  449         QString header = col->name();
  450 
  451         if (showColumnType) {
  452             switch (col->columnMode()) {
  453             case AbstractColumn::ColumnMode::Numeric:
  454                 header += QLatin1String(" {") + i18n("Numeric") + QLatin1Char('}');
  455                 break;
  456             case AbstractColumn::ColumnMode::Integer:
  457                 header += QLatin1String(" {") + i18n("Integer") + QLatin1Char('}');
  458                 break;
  459             case AbstractColumn::ColumnMode::BigInt:
  460                 header += QLatin1String(" {") + i18n("Big Integer") + QLatin1Char('}');
  461                 break;
  462             case AbstractColumn::ColumnMode::Text:
  463                 header += QLatin1String(" {") + i18n("Text") + QLatin1Char('}');
  464                 break;
  465             case AbstractColumn::ColumnMode::Month:
  466                 header += QLatin1String(" {") + i18n("Month Names") + QLatin1Char('}');
  467                 break;
  468             case AbstractColumn::ColumnMode::Day:
  469                 header += QLatin1String(" {") + i18n("Day Names") + QLatin1Char('}');
  470                 break;
  471             case AbstractColumn::ColumnMode::DateTime:
  472                 header += QLatin1String(" {") + i18n("Date and Time") + QLatin1Char('}');
  473                 break;
  474             }
  475         }
  476 
  477         if (showPlotDesignation) {
  478             switch (col->plotDesignation()) {
  479             case AbstractColumn::PlotDesignation::NoDesignation:
  480                 break;
  481             case AbstractColumn::PlotDesignation::X:
  482                 header += QLatin1String(" [X]");
  483                 break;
  484             case AbstractColumn::PlotDesignation::Y:
  485                 header += QLatin1String(" [Y]");
  486                 break;
  487             case AbstractColumn::PlotDesignation::Z:
  488                 header += QLatin1String(" [Z]");
  489                 break;
  490             case AbstractColumn::PlotDesignation::XError:
  491                 header += QLatin1String(" [") + i18n("X-error") + QLatin1Char(']');
  492                 break;
  493             case AbstractColumn::PlotDesignation::XErrorPlus:
  494                 header += QLatin1String(" [") + i18n("X-error +") + QLatin1Char(']');
  495                 break;
  496             case AbstractColumn::PlotDesignation::XErrorMinus:
  497                 header += QLatin1String(" [") + i18n("X-error -") + QLatin1Char(']');
  498                 break;
  499             case AbstractColumn::PlotDesignation::YError:
  500                 header += QLatin1String(" [") + i18n("Y-error") + QLatin1Char(']');
  501                 break;
  502             case AbstractColumn::PlotDesignation::YErrorPlus:
  503                 header += QLatin1String(" [") + i18n("Y-error +") + QLatin1Char(']');
  504                 break;
  505             case AbstractColumn::PlotDesignation::YErrorMinus:
  506                 header += QLatin1String(" [") + i18n("Y-error -") + QLatin1Char(']');
  507                 break;
  508             }
  509         }
  510         m_horizontal_header_data.replace(i, header);
  511     }
  512 }
  513 
  514 Column* SpreadsheetModel::column(int index) {
  515     return m_spreadsheet->column(index);
  516 }
  517 
  518 void SpreadsheetModel::activateFormulaMode(bool on) {
  519     if (m_formula_mode == on) return;
  520 
  521     m_formula_mode = on;
  522     if (m_rowCount > 0 && m_columnCount > 0)
  523         emit dataChanged(index(0,0), index(m_rowCount - 1, m_columnCount - 1));
  524 }
  525 
  526 bool SpreadsheetModel::formulaModeActive() const {
  527     return m_formula_mode;
  528 }