/*************************************************************************** * Copyright (c) Eivind Kvedalen (eivind@kvedalen.name) 2015 * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ #include # include # include #endif #include #include "SheetModel.h" #include #include #include "../App/Sheet.h" #include #include #include #include using namespace SpreadsheetGui; using namespace Spreadsheet; using namespace App; SheetModel::SheetModel(Sheet *_sheet, QObject *parent) : QAbstractTableModel(parent) , sheet(_sheet) { cellUpdatedConnection = sheet->cellUpdated.connect(bind(&SheetModel::cellUpdated, this, _1)); ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Spreadsheet"); aliasBgColor = QColor(Base::Tools::fromStdString(hGrp->GetASCII("AliasedCellBackgroundColor", "#feff9e"))); } SheetModel::~SheetModel() { cellUpdatedConnection.disconnect(); } int SheetModel::rowCount(const QModelIndex &parent) const { return 16384; } int SheetModel::columnCount(const QModelIndex &parent) const { return 26 * 26 + 26; } static void appendUnit(int l, bool isNumerator, std::string unit, std::vector & v) { if (l == 0) return; if ((l < 0) ^ isNumerator ) { std::ostringstream s; s << unit; if (abs(l) > 1) s << "^" << abs(l); v.push_back(s.str()); } } static std::string getUnitString(const Base::Unit & unit) { std::vector numerator; std::vector denominator; const Base::UnitSignature & sig = unit.getSignature(); // Nominator appendUnit(sig.Length, true, "mm", numerator); appendUnit(sig.Mass, true, "kg", numerator); appendUnit(sig.Time, true, "s", numerator); appendUnit(sig.ElectricCurrent, true, "A", numerator); appendUnit(sig.ThermodynamicTemperature, true, "K", numerator); appendUnit(sig.AmountOfSubstance, true, "mol", numerator); appendUnit(sig.LuminoseIntensity, true, "cd", numerator); appendUnit(sig.Angle, true, "deg", numerator); // Denominator appendUnit(sig.Length, false, "mm", denominator); appendUnit(sig.Mass, false, "kg", denominator); appendUnit(sig.Time, false, "s", denominator); appendUnit(sig.ElectricCurrent, false, "A", denominator); appendUnit(sig.ThermodynamicTemperature, false, "K", denominator); appendUnit(sig.AmountOfSubstance, false, "mol", denominator); appendUnit(sig.LuminoseIntensity, false, "cd", denominator); appendUnit(sig.Angle, false, "deg", denominator); std::string unitStr; if (numerator.size() > 0) { for (std::size_t i = 0; i < numerator.size(); ++i) { if (i > 0) unitStr += "*"; unitStr += numerator[i]; } } if (denominator.size() > 0) { if (numerator.size() == 0) unitStr = "1"; unitStr += "/"; if (denominator.size() > 1) unitStr += "("; for (std::size_t i = 0; i < denominator.size(); ++i) { if (i > 0) unitStr += "*"; unitStr += denominator[i]; } if (denominator.size() > 1) unitStr += ")"; } return unitStr; } QVariant SheetModel::data(const QModelIndex &index, int role) const { static const Cell * emptyCell = new Cell(CellAddress(0, 0), 0); int row = index.row(); int col = index.column(); const Cell * cell = sheet->getCell(CellAddress(row, col)); if (cell == 0) cell = emptyCell; //#define DEBUG_DEPS #ifdef DEBUG_DEPS if (role == Qt::ToolTipRole) { QString v; std::set deps = sheet->dependsOn(CellAddress(row, col)); std::set provides; sheet->providesTo(CellAddress(row, col), provides); if (deps.size() > 0) { v += QString::fromUtf8("Depends on:"); for (std::set::const_iterator i = deps.begin(); i != deps.end(); ++i) v += QString::fromUtf8("\n\t") + Tools::fromStdString(*i); v += QString::fromUtf8("\n"); } if (provides.size() > 0) { v += QString::fromUtf8("Used by:"); for (std::set::const_iterator i = provides.begin(); i != provides.end(); ++i) v += QString::fromUtf8("\n\t") + Tools::fromStdString(*i); v += QString::fromUtf8("\n"); } return QVariant(v); } #else if (role == Qt::ToolTipRole) { std::string alias; if (cell->getAlias(alias)) return QVariant(Base::Tools::fromStdString(alias)); return QVariant(); } #endif if (cell->hasException()) { switch (role) { case Qt::ToolTipRole: return QVariant::fromValue(Base::Tools::fromStdString(cell->getException())); case Qt::DisplayRole: #ifdef DEBUG_DEPS return QVariant::fromValue(QString::fromUtf8("#ERR: %1").arg(Tools::fromStdString(cell->getException()))); #else return QVariant::fromValue(QString::fromUtf8("#ERR")); #endif case Qt::TextAlignmentRole: return QVariant(Qt::AlignVCenter | Qt::AlignLeft); default: break; } } // Get edit value by querying the sheet if (role == Qt::EditRole || role == Qt::StatusTipRole) { std::string str; if (cell->getStringContent(str)) return QVariant(QString::fromUtf8(str.c_str())); else return QVariant(); } // Get display value as computed property std::string address = CellAddress(row, col).toString(); Property * prop = sheet->getPropertyByName(address.c_str()); if (prop == 0) return QVariant(); Color color; if (role == Qt::TextColorRole || role == Qt::ForegroundRole) { if (cell->getForeground(color)) return QVariant::fromValue(QColor(255.0 * color.r, 255.0 * color.g, 255.0 * color.b, 255.0 * color.a)); else return QVariant(); } if (role == Qt::BackgroundRole) { if (cell->getBackground(color)) return QVariant::fromValue(QColor(255.0 * color.r, 255.0 * color.g, 255.0 * color.b, 255.0 * color.a)); else { std::string alias; if (cell->getAlias(alias)) { return QVariant::fromValue(aliasBgColor); } else return QVariant(); } } int qtAlignment = 0; int alignment; cell->getAlignment(alignment); if (alignment & Cell::ALIGNMENT_LEFT) qtAlignment |= Qt::AlignLeft; if (alignment & Cell::ALIGNMENT_HCENTER) qtAlignment |= Qt::AlignHCenter; if (alignment & Cell::ALIGNMENT_RIGHT) qtAlignment |= Qt::AlignRight; if (alignment & Cell::ALIGNMENT_TOP) qtAlignment |= Qt::AlignTop; if (alignment & Cell::ALIGNMENT_VCENTER) qtAlignment |= Qt::AlignVCenter; if (alignment & Cell::ALIGNMENT_BOTTOM) qtAlignment |= Qt::AlignBottom; std::set style; if (role == Qt::FontRole && cell->getStyle(style)) { QFont f; for (std::set::const_iterator i = style.begin(); i != style.end(); ++i) { if (*i == "bold") f.setBold(true); else if (*i == "italic") f.setItalic(true); else if (*i == "underline") f.setUnderline(true); } return QVariant::fromValue(f); } if (prop->isDerivedFrom(App::PropertyString::getClassTypeId())) { /* String */ const App::PropertyString * stringProp = static_cast(prop); switch (role) { case Qt::TextColorRole: return QVariant::fromValue(QColor(Qt::black)); case Qt::DisplayRole: return QVariant(QString::fromUtf8(stringProp->getValue())); case Qt::TextAlignmentRole: { if (alignment & Cell::ALIGNMENT_HIMPLIED) { qtAlignment &= ~(Qt::AlignLeft | Qt::AlignHCenter | Qt::AlignRight); qtAlignment |= Qt::AlignLeft; } if (alignment & Cell::ALIGNMENT_VIMPLIED) { qtAlignment &= ~(Qt::AlignTop | Qt::AlignVCenter | Qt::AlignBottom); qtAlignment |= Qt::AlignVCenter; } return QVariant::fromValue(qtAlignment); } default: return QVariant(); } } else if (prop->isDerivedFrom(App::PropertyQuantity::getClassTypeId())) { /* Number */ const App::PropertyQuantity * floatProp = static_cast(prop); switch (role) { case Qt::TextColorRole: if (floatProp->getValue() < 0) return QVariant::fromValue(QColor(Qt::red)); else return QVariant::fromValue(QColor(Qt::blue)); case Qt::TextAlignmentRole: { if (alignment & Cell::ALIGNMENT_HIMPLIED) { qtAlignment &= ~(Qt::AlignLeft | Qt::AlignHCenter | Qt::AlignRight); qtAlignment |= Qt::AlignRight; } if (alignment & Cell::ALIGNMENT_VIMPLIED) { qtAlignment &= ~(Qt::AlignTop | Qt::AlignVCenter | Qt::AlignBottom); qtAlignment |= Qt::AlignVCenter; } return QVariant::fromValue(qtAlignment); } case Qt::DisplayRole: { QString v; const Base::Unit & computedUnit = floatProp->getUnit(); DisplayUnit displayUnit; if (cell->getDisplayUnit(displayUnit)) { if (computedUnit.isEmpty() || computedUnit == displayUnit.unit) v = QString::number(floatProp->getValue() / displayUnit.scaler) + Base::Tools::fromStdString(" " + displayUnit.stringRep); else v = QString::fromUtf8("ERR: unit"); } else { if (!computedUnit.isEmpty()) v = QString::number(floatProp->getValue()) + Base::Tools::fromStdString(" " + getUnitString(computedUnit)); else v = QString::number(floatProp->getValue()); } return QVariant(v); } default: return QVariant(); } } else if (prop->isDerivedFrom(App::PropertyFloat::getClassTypeId())) { /* Number */ const App::PropertyFloat * floatProp = static_cast(prop); switch (role) { case Qt::TextColorRole: if (floatProp->getValue() < 0) return QVariant::fromValue(QColor(Qt::red)); else return QVariant::fromValue(QColor(Qt::blue)); case Qt::TextAlignmentRole: { if (alignment & Cell::ALIGNMENT_HIMPLIED) { qtAlignment &= ~(Qt::AlignLeft | Qt::AlignHCenter | Qt::AlignRight); qtAlignment |= Qt::AlignRight; } if (alignment & Cell::ALIGNMENT_VIMPLIED) { qtAlignment &= ~(Qt::AlignTop | Qt::AlignVCenter | Qt::AlignBottom); qtAlignment |= Qt::AlignVCenter; } return QVariant::fromValue(qtAlignment); } case Qt::DisplayRole: { QString v; DisplayUnit displayUnit; if (cell->getDisplayUnit(displayUnit)) v = QString::number(floatProp->getValue() / displayUnit.scaler) + Base::Tools::fromStdString(" " + displayUnit.stringRep); else v = QString::number(floatProp->getValue()); return QVariant(v); } default: return QVariant(); } } return QVariant(); } QVariant SheetModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::SizeHintRole) { if (orientation == Qt::Horizontal) return QVariant(QSize(sheet->getColumnWidth(section), PropertyRowHeights::defaultHeight)); else return QVariant(QSize(PropertyColumnWidths::defaultHeaderWidth, sheet->getRowHeight(section))); } if (role == Qt::DisplayRole) { if (orientation == Qt::Horizontal) { static QString labels = QString::fromUtf8("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); if (section < 26) { return QVariant(labels[section]); } else { section -= 26; return QVariant(QString(labels[section / 26]) + QString(labels[section % 26])); } } else { return QString::number(section + 1); } } return QVariant(); } bool SheetModel::setData(const QModelIndex & index, const QVariant & value, int role) { if (role == Qt::DisplayRole) { // Nothing to do, it will get updated by the sheet in the application logic } else if (role == Qt::EditRole) { CellAddress address(index.row(), index.column()); try { std::string strAddress = address.toString(); QString str = value.toString(); std::string content; Cell * cell = sheet->getCell(address); if (cell) cell->getStringContent(content); if ( content != Base::Tools::toStdString(str)) { str.replace(QString::fromUtf8("'"), QString::fromUtf8("\\'")); Gui::Command::openCommand("Edit cell"); Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.set('%s', '%s')", sheet->getNameInDocument(), strAddress.c_str(), str.toUtf8().constData()); Gui::Command::commitCommand(); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); } } catch (const Base::Exception& e) { QMessageBox::critical(qApp->activeWindow(), QObject::tr("Cell contents"), QString::fromUtf8(e.what())); Gui::Command::abortCommand(); } } return true; } Qt::ItemFlags SheetModel::flags(const QModelIndex & /*index*/) const { return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; } void SheetModel::cellUpdated(CellAddress address) { QModelIndex i = index(address.row(), address.col()); dataChanged(i, i); } #include "moc_SheetModel.cpp"