Download src/Mod/Spreadsheet/App/Cell.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 32.6 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Spreadsheet/App/Cell.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Spreadsheet/App/Cell.cpp
-
curl -L -o Cell.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Spreadsheet/App/Cell.cpp
32.6 kB
| // SPDX-License-Identifier: LGPL-2.1-or-later | |
| /*************************************************************************** | |
| * Copyright (c) 2015 Eivind Kvedalen <eivind@kvedalen.name> * | |
| * * | |
| * 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 * | |
| * * | |
| ***************************************************************************/ | |
| FC_LOG_LEVEL_INIT("Spreadsheet", true, true) | |
| using namespace App; | |
| using namespace Base; | |
| using namespace Spreadsheet; | |
| ///////////////////////////////////////////////////////// | |
| // expose the read() function for simpler partial xml reading in setExpression() | |
| class ReaderPrivate: public Base::XMLReader | |
| { | |
| public: | |
| ReaderPrivate(const char* FileName, std::istream& is) | |
| : XMLReader(FileName, is) | |
| {} | |
| bool read() | |
| { | |
| return XMLReader::read(); | |
| } | |
| }; | |
| /////////////////////////////////////////////////////////// | |
| // clang-format off | |
| const int Cell::EXPRESSION_SET = 1; | |
| const int Cell::ALIGNMENT_SET = 4; | |
| const int Cell::STYLE_SET = 8; | |
| const int Cell::BACKGROUND_COLOR_SET = 0x10; | |
| const int Cell::FOREGROUND_COLOR_SET = 0x20; | |
| const int Cell::DISPLAY_UNIT_SET = 0x40; | |
| const int Cell::COMPUTED_UNIT_SET = 0x80; | |
| const int Cell::ALIAS_SET = 0x100; | |
| const int Cell::SPANS_SET = 0x200; | |
| const int Cell::MARK_SET = 0x40000000; | |
| const int Cell::EXCEPTION_SET = 0x20000000; | |
| const int Cell::PARSE_EXCEPTION_SET = 0x80000000; | |
| const int Cell::RESOLVE_EXCEPTION_SET= 0x01000000; | |
| /* Alignment */ | |
| const int Cell::ALIGNMENT_LEFT = 0x01; | |
| const int Cell::ALIGNMENT_HCENTER = 0x02; | |
| const int Cell::ALIGNMENT_RIGHT = 0x04; | |
| const int Cell::ALIGNMENT_HIMPLIED = 0x08; | |
| const int Cell::ALIGNMENT_HORIZONTAL = 0x0f; | |
| const int Cell::ALIGNMENT_TOP = 0x10; | |
| const int Cell::ALIGNMENT_VCENTER = 0x20; | |
| const int Cell::ALIGNMENT_BOTTOM = 0x40; | |
| const int Cell::ALIGNMENT_VIMPLIED = 0x80; | |
| const int Cell::ALIGNMENT_VERTICAL = 0xf0; | |
| // clang-format on | |
| /** | |
| * Construct a CellContent object. | |
| * @param _address The address of the cell (i.e. row and column) | |
| * @param _owner The spreadsheet that owns this cell. | |
| * | |
| */ | |
| Cell::Cell(const CellAddress& _address, PropertySheet* _owner) | |
| : address(_address) | |
| , owner(_owner) | |
| , used(0) | |
| , alignment(ALIGNMENT_HIMPLIED | ALIGNMENT_LEFT | ALIGNMENT_VIMPLIED | ALIGNMENT_VCENTER) | |
| , style() | |
| , foregroundColor(0, 0, 0, 1) | |
| , backgroundColor(1, 1, 1, 1) | |
| , displayUnit() | |
| , alias() | |
| , computedUnit() | |
| , rowSpan(1) | |
| , colSpan(1) | |
| , anchor() | |
| { | |
| assert(address.isValid()); | |
| } | |
| Cell::Cell(PropertySheet* _owner, const Cell& other) | |
| : address(other.address) | |
| , owner(_owner) | |
| , used(other.used) | |
| , expression(other.expression ? other.expression->copy() : nullptr) | |
| , alignment(other.alignment) | |
| , style(other.style) | |
| , foregroundColor(other.foregroundColor) | |
| , backgroundColor(other.backgroundColor) | |
| , displayUnit(other.displayUnit) | |
| , computedUnit(other.computedUnit) | |
| , rowSpan(other.rowSpan) | |
| , colSpan(other.colSpan) | |
| { | |
| setUsed(MARK_SET, false); | |
| setAlias(other.alias); | |
| setDirty(); | |
| } | |
| Cell& Cell::operator=(const Cell& rhs) | |
| { | |
| PropertySheet::AtomicPropertyChange signaller(*owner); | |
| address = rhs.address; | |
| setExpression(App::ExpressionPtr(rhs.expression ? rhs.expression->copy() : nullptr)); | |
| setAlignment(rhs.alignment); | |
| setStyle(rhs.style); | |
| setBackground(rhs.backgroundColor); | |
| setForeground(rhs.foregroundColor); | |
| setDisplayUnit(rhs.displayUnit.stringRep); | |
| setComputedUnit(rhs.computedUnit); | |
| setAlias(rhs.alias); | |
| setSpans(rhs.rowSpan, rhs.colSpan); | |
| setUsed(MARK_SET, false); | |
| setDirty(); | |
| signaller.tryInvoke(); | |
| return *this; | |
| } | |
| /** | |
| * Destroy a CellContent object. | |
| * | |
| */ | |
| Cell::~Cell() = default; | |
| /** | |
| * Set the expression tree to \a expr. | |
| * | |
| */ | |
| void Cell::setExpression(App::ExpressionPtr&& expr) | |
| { | |
| PropertySheet::AtomicPropertyChange signaller(*owner); | |
| owner->setDirty(address); | |
| /* Remove dependencies */ | |
| owner->removeDependencies(address); | |
| if (expr && !expr->comment.empty()) { | |
| if (!boost::starts_with(expr->comment, "<Cell ")) { | |
| FC_WARN( | |
| "Unknown style of cell " << owner->sheet()->getFullName() << '.' << address.toString() | |
| ); | |
| } | |
| else { | |
| try { | |
| std::istringstream in(expr->comment); | |
| ReaderPrivate reader("<memory>", in); | |
| reader.read(); | |
| restore(reader, true); | |
| } | |
| catch (Base::Exception& e) { | |
| e.reportException(); | |
| FC_ERR( | |
| "Failed to restore style of cell " << owner->sheet()->getFullName() << '.' | |
| << address.toString() << ": " << e.what() | |
| ); | |
| } | |
| } | |
| expr->comment.clear(); | |
| } | |
| expression = std::move(expr); | |
| setUsed(EXPRESSION_SET, !!expression); | |
| /* Update dependencies */ | |
| owner->addDependencies(address); | |
| signaller.tryInvoke(); | |
| } | |
| /** | |
| * Get the expression tree. | |
| * | |
| */ | |
| const App::Expression* Cell::getExpression(bool withFormat) const | |
| { | |
| if (withFormat && expression) { | |
| if ((used | |
| & (ALIGNMENT_SET | STYLE_SET | FOREGROUND_COLOR_SET | BACKGROUND_COLOR_SET | |
| | DISPLAY_UNIT_SET | ALIAS_SET | SPANS_SET))) { | |
| std::ostringstream ss; | |
| save(ss, "", true); | |
| expression->comment = ss.str(); | |
| } | |
| } | |
| return expression.get(); | |
| } | |
| /** | |
| * Get string content. | |
| * | |
| */ | |
| bool Cell::getStringContent(std::string& s, bool persistent) const | |
| { | |
| if (expression) { | |
| s.clear(); | |
| if (expression->hasComponent()) { | |
| s = "=" + expression->toString(persistent); | |
| } | |
| else if (freecad_cast<App::StringExpression*>(expression.get())) { | |
| s = static_cast<App::StringExpression*>(expression.get())->getText(); | |
| s = "'" + s; | |
| } | |
| else if (freecad_cast<App::ConstantExpression*>(expression.get())) { | |
| s = "=" + expression->toString(); | |
| } | |
| else if (freecad_cast<App::NumberExpression*>(expression.get())) { | |
| s = expression->toString(); | |
| } | |
| else { | |
| s = "=" + expression->toString(persistent); | |
| } | |
| return true; | |
| } | |
| else { | |
| s = ""; | |
| return false; | |
| } | |
| } | |
| void Cell::afterRestore() | |
| { | |
| auto expr = freecad_cast<StringExpression*>(expression.get()); | |
| if (expr) { | |
| setContent(expr->getText().c_str()); | |
| } | |
| } | |
| void Cell::setContent(const char* value) | |
| { | |
| PropertySheet::AtomicPropertyChange signaller(*owner); | |
| ExpressionPtr newExpr; | |
| clearException(); | |
| if (value) { | |
| Sheet* sheet = owner->sheet(); | |
| if (sheet && sheet->isRestoring()) { | |
| if (value[0] == '\0' || (value[0] == '\'' && value[1] == '\0')) { | |
| return; | |
| } | |
| expression = std::make_unique<App::StringExpression>(sheet, value); | |
| setUsed(EXPRESSION_SET, true); | |
| return; | |
| } | |
| if (*value == '=') { | |
| try { | |
| newExpr = ExpressionPtr(App::ExpressionParser::parse(sheet, value + 1)); | |
| } | |
| catch (Base::Exception& e) { | |
| newExpr = std::make_unique<App::StringExpression>(sheet, value); | |
| setParseException(e.what()); | |
| } | |
| } | |
| else if (*value == '\'') { | |
| if (value[1] == '\0') { | |
| value = nullptr; | |
| } | |
| else { | |
| newExpr = std::make_unique<App::StringExpression>(sheet, value + 1); | |
| } | |
| } | |
| else if (*value != '\0') { | |
| // check if value is just a number | |
| char* end; | |
| errno = 0; | |
| const double float_value = strtod(value, &end); | |
| if (errno == 0) { | |
| const bool isEndEmpty = *end == '\0' || strspn(end, " \t\n\r") == strlen(end); | |
| if (isEndEmpty) { | |
| newExpr = std::make_unique<App::NumberExpression>(sheet, Quantity(float_value)); | |
| } | |
| } | |
| // if not a float, check if it is a quantity or compatible fraction | |
| const bool isStartingWithNumber = value != end; | |
| if (!newExpr && isStartingWithNumber) { | |
| try { | |
| ExpressionPtr parsedExpr(App::ExpressionParser::parse(sheet, value)); | |
| if (const auto fraction = freecad_cast<OperatorExpression*>(parsedExpr.get())) { | |
| if (fraction->getOperator() == OperatorExpression::UNIT) { | |
| const auto left = freecad_cast<NumberExpression*>(fraction->getLeft()); | |
| const auto right = freecad_cast<UnitExpression*>(fraction->getRight()); | |
| if (left && right) { | |
| newExpr = std::move(parsedExpr); | |
| } | |
| } | |
| else if (fraction->getOperator() == OperatorExpression::DIV) { | |
| // only the following types of fractions are ok: | |
| // 1/2, 1m/2, 1/2s, 1m/2s, 1/m | |
| // check for numbers in (de)nominator | |
| const bool isNumberNom = freecad_cast<NumberExpression*>( | |
| fraction->getLeft() | |
| ); | |
| const bool isNumberDenom = freecad_cast<NumberExpression*>( | |
| fraction->getRight() | |
| ); | |
| // check for numbers with units in (de)nominator | |
| const auto opNom = freecad_cast<OperatorExpression*>(fraction->getLeft()); | |
| const auto opDenom = freecad_cast<OperatorExpression*>( | |
| fraction->getRight() | |
| ); | |
| const bool isQuantityNom = opNom | |
| && opNom->getOperator() == OperatorExpression::UNIT; | |
| const bool isQuantityDenom = opDenom | |
| && opDenom->getOperator() == OperatorExpression::UNIT; | |
| // check for units in denomainator | |
| const auto uDenom = freecad_cast<UnitExpression*>(fraction->getRight()); | |
| const bool isUnitDenom = uDenom && uDenom->is<UnitExpression>(); | |
| const bool isNomValid = isNumberNom || isQuantityNom; | |
| const bool isDenomValid = isNumberDenom || isQuantityDenom || isUnitDenom; | |
| if (isNomValid && isDenomValid) { | |
| newExpr = std::move(parsedExpr); | |
| } | |
| } | |
| } | |
| else if (const auto number = freecad_cast<NumberExpression*>(parsedExpr.get())) { | |
| // NumbersExpressions can accept more than can be parsed with strtod. | |
| // Example: 12.34 and 12,34 are both valid NumberExpressions | |
| newExpr = std::move(parsedExpr); | |
| } | |
| } | |
| catch (...) { | |
| } | |
| } | |
| } | |
| if (!newExpr && value && *value != '\0') { | |
| newExpr = std::make_unique<App::StringExpression>(sheet, value); | |
| } | |
| // trying to add an empty string will make newExpr = nullptr | |
| } | |
| // set expression, or delete the current expression by setting nullptr if empty string was | |
| // entered | |
| setExpression(std::move(newExpr)); | |
| signaller.tryInvoke(); | |
| } | |
| /** | |
| * Set alignment of this cell. Alignment is the or'ed value of | |
| * vertical and horizontal alignment, given by the constants | |
| * defined in the class. | |
| * | |
| */ | |
| void Cell::setAlignment(int _alignment) | |
| { | |
| if (_alignment != alignment) { | |
| PropertySheet::AtomicPropertyChange signaller(*owner); | |
| alignment = _alignment; | |
| setUsed( | |
| ALIGNMENT_SET, | |
| alignment != (ALIGNMENT_HIMPLIED | ALIGNMENT_LEFT | ALIGNMENT_VIMPLIED | ALIGNMENT_VCENTER) | |
| ); | |
| setDirty(); | |
| signaller.tryInvoke(); | |
| } | |
| } | |
| /** | |
| * Get alignment. | |
| * | |
| */ | |
| bool Cell::getAlignment(int& _alignment) const | |
| { | |
| _alignment = alignment; | |
| return isUsed(ALIGNMENT_SET); | |
| } | |
| /** | |
| * Set style to the given set \a _style. | |
| * | |
| */ | |
| void Cell::setStyle(const std::set<std::string>& _style) | |
| { | |
| if (_style != style) { | |
| PropertySheet::AtomicPropertyChange signaller(*owner); | |
| style = _style; | |
| setUsed(STYLE_SET, !style.empty()); | |
| setDirty(); | |
| signaller.tryInvoke(); | |
| } | |
| } | |
| /** | |
| * Get the style of the cell. | |
| * | |
| */ | |
| bool Cell::getStyle(std::set<std::string>& _style) const | |
| { | |
| _style = style; | |
| return isUsed(STYLE_SET); | |
| } | |
| /** | |
| * Set foreground (i.e text) color of the cell to \a color. | |
| * | |
| */ | |
| void Cell::setForeground(const Base::Color& color) | |
| { | |
| if (color != foregroundColor) { | |
| PropertySheet::AtomicPropertyChange signaller(*owner); | |
| foregroundColor = color; | |
| setUsed(FOREGROUND_COLOR_SET, foregroundColor != Base::Color(0, 0, 0, 1)); | |
| setDirty(); | |
| signaller.tryInvoke(); | |
| } | |
| } | |
| /** | |
| * Get foreground color of the cell. | |
| * | |
| */ | |
| bool Cell::getForeground(Base::Color& color) const | |
| { | |
| color = foregroundColor; | |
| return isUsed(FOREGROUND_COLOR_SET); | |
| } | |
| /** | |
| * Set background color of the cell to \a color. | |
| * | |
| */ | |
| void Cell::setBackground(const Base::Color& color) | |
| { | |
| if (color != backgroundColor) { | |
| PropertySheet::AtomicPropertyChange signaller(*owner); | |
| backgroundColor = color; | |
| setUsed(BACKGROUND_COLOR_SET, backgroundColor != Base::Color(1, 1, 1, 0)); | |
| setDirty(); | |
| signaller.tryInvoke(); | |
| } | |
| } | |
| /** | |
| * Get the background color of the cell into \a color. | |
| * | |
| * @returns true if the background color was previously set. | |
| * | |
| */ | |
| bool Cell::getBackground(Base::Color& color) const | |
| { | |
| color = backgroundColor; | |
| return isUsed(BACKGROUND_COLOR_SET); | |
| } | |
| /** | |
| * Set the display unit for the cell. | |
| * | |
| */ | |
| void Cell::setDisplayUnit(const std::string& unit) | |
| { | |
| DisplayUnit newDisplayUnit; | |
| if (!unit.empty()) { | |
| std::shared_ptr<App::UnitExpression> e( | |
| ExpressionParser::parseUnit(owner->sheet(), unit.c_str()) | |
| ); | |
| if (!e) { | |
| throw Base::UnitsMismatchError("Invalid unit"); | |
| } | |
| newDisplayUnit = DisplayUnit(unit, e->getUnit(), e->getScaler()); | |
| } | |
| if (newDisplayUnit != displayUnit) { | |
| PropertySheet::AtomicPropertyChange signaller(*owner); | |
| displayUnit = std::move(newDisplayUnit); | |
| setUsed(DISPLAY_UNIT_SET, !displayUnit.isEmpty()); | |
| setDirty(); | |
| signaller.tryInvoke(); | |
| } | |
| } | |
| /** | |
| * Get the display unit for the cell into unit. | |
| * | |
| * @returns true if the display unit was previously set. | |
| * | |
| */ | |
| bool Cell::getDisplayUnit(DisplayUnit& unit) const | |
| { | |
| unit = displayUnit; | |
| return isUsed(DISPLAY_UNIT_SET); | |
| } | |
| void Cell::setAlias(const std::string& n) | |
| { | |
| if (alias != n) { | |
| PropertySheet::AtomicPropertyChange signaller(*owner); | |
| owner->revAliasProp.erase(alias); | |
| // Update owner | |
| if (!n.empty()) { | |
| owner->aliasProp[address] = n; | |
| owner->revAliasProp[n] = address; | |
| } | |
| else { | |
| owner->aliasProp.erase(address); | |
| } | |
| if (!alias.empty()) { | |
| // The property may have been added in Sheet::updateAlias | |
| auto* docObj = static_cast<App::DocumentObject*>(owner->getContainer()); | |
| docObj->removeDynamicProperty(alias.c_str()); | |
| } | |
| alias = n; | |
| setUsed(ALIAS_SET, !alias.empty()); | |
| setDirty(); | |
| signaller.tryInvoke(); | |
| } | |
| } | |
| bool Cell::getAlias(std::string& n) const | |
| { | |
| n = alias; | |
| return isUsed(ALIAS_SET); | |
| } | |
| /** | |
| * Set the computed unit for the cell to \a unit. | |
| * | |
| */ | |
| void Cell::setComputedUnit(const Base::Unit& unit) | |
| { | |
| PropertySheet::AtomicPropertyChange signaller(*owner); | |
| computedUnit = unit; | |
| setUsed(COMPUTED_UNIT_SET, computedUnit != Unit::One); | |
| setDirty(); | |
| signaller.tryInvoke(); | |
| } | |
| /** | |
| * Get the computed unit into \a unit. | |
| * | |
| * @returns true if the computed unit was previously set. | |
| * | |
| */ | |
| bool Cell::getComputedUnit(Base::Unit& unit) const | |
| { | |
| unit = computedUnit; | |
| return isUsed(COMPUTED_UNIT_SET); | |
| } | |
| /** | |
| * Set the cell's row and column span to \a rows and \a columns. This | |
| * is done when cells are merged. | |
| * | |
| */ | |
| void Cell::setSpans(int rows, int columns) | |
| { | |
| if (rows != rowSpan || columns != colSpan) { | |
| PropertySheet::AtomicPropertyChange signaller(*owner); | |
| rowSpan = (rows == -1 ? 1 : rows); | |
| colSpan = (columns == -1 ? 1 : columns); | |
| setUsed(SPANS_SET, (rowSpan != 1 || colSpan != 1)); | |
| setDirty(); | |
| signaller.tryInvoke(); | |
| } | |
| } | |
| /** | |
| * Get the row and column spans for the cell into \a rows and \a columns. | |
| * | |
| */ | |
| bool Cell::getSpans(int& rows, int& columns) const | |
| { | |
| rows = rowSpan; | |
| columns = colSpan; | |
| return isUsed(SPANS_SET); | |
| } | |
| void Cell::setException(const std::string& e, bool silent) | |
| { | |
| if (!silent && !e.empty() && owner && owner->sheet()) { | |
| FC_ERR(owner->sheet()->getFullName() << '.' << address.toString() << ": " << e); | |
| } | |
| exceptionStr = e; | |
| setUsed(EXCEPTION_SET); | |
| } | |
| void Cell::setParseException(const std::string& e) | |
| { | |
| if (!e.empty() && owner && owner->sheet()) { | |
| FC_ERR(owner->sheet()->getFullName() << '.' << address.toString() << ": " << e); | |
| } | |
| exceptionStr = e; | |
| setUsed(PARSE_EXCEPTION_SET); | |
| } | |
| void Cell::setResolveException(const std::string& e) | |
| { | |
| if (!e.empty() && owner && owner->sheet()) { | |
| FC_LOG(owner->sheet()->getFullName() << '.' << address.toString() << ": " << e); | |
| } | |
| exceptionStr = e; | |
| setUsed(RESOLVE_EXCEPTION_SET); | |
| } | |
| void Cell::clearResolveException() | |
| { | |
| setUsed(RESOLVE_EXCEPTION_SET, false); | |
| } | |
| void Cell::clearException() | |
| { | |
| exceptionStr.clear(); | |
| setUsed(EXCEPTION_SET, false); | |
| setUsed(RESOLVE_EXCEPTION_SET, false); | |
| setUsed(PARSE_EXCEPTION_SET, false); | |
| } | |
| void Cell::clearDirty() | |
| { | |
| if (owner) { | |
| owner->clearDirty(address); | |
| } | |
| } | |
| void Cell::setDirty() | |
| { | |
| if (owner) { | |
| owner->setDirty(address); | |
| } | |
| } | |
| /** | |
| * Move the cell to a new position given by \a _row and \a _col. | |
| * | |
| */ | |
| void Cell::moveAbsolute(CellAddress newAddress) | |
| { | |
| address = newAddress; | |
| } | |
| /** | |
| * Restore cell contents from \a reader. | |
| * | |
| */ | |
| void Cell::restore(Base::XMLReader& reader, bool checkAlias) | |
| { | |
| const char* style = reader.hasAttribute("style") ? reader.getAttribute<const char*>("style") | |
| : nullptr; | |
| const char* alignment = reader.hasAttribute("alignment") | |
| ? reader.getAttribute<const char*>("alignment") | |
| : nullptr; | |
| const char* content = reader.hasAttribute("content") | |
| ? reader.getAttribute<const char*>("content") | |
| : ""; | |
| const char* foregroundColor = reader.hasAttribute("foregroundColor") | |
| ? reader.getAttribute<const char*>("foregroundColor") | |
| : nullptr; | |
| const char* backgroundColor = reader.hasAttribute("backgroundColor") | |
| ? reader.getAttribute<const char*>("backgroundColor") | |
| : nullptr; | |
| const char* displayUnit = reader.hasAttribute("displayUnit") | |
| ? reader.getAttribute<const char*>("displayUnit") | |
| : nullptr; | |
| const char* alias = reader.hasAttribute("alias") ? reader.getAttribute<const char*>("alias") | |
| : nullptr; | |
| const char* rowSpan = reader.hasAttribute("rowSpan") | |
| ? reader.getAttribute<const char*>("rowSpan") | |
| : nullptr; | |
| const char* colSpan = reader.hasAttribute("colSpan") | |
| ? reader.getAttribute<const char*>("colSpan") | |
| : nullptr; | |
| // Don't trigger multiple updates below; wait until everything is loaded by calling unfreeze() | |
| // below. | |
| PropertySheet::AtomicPropertyChange signaller(*owner); | |
| if (content) { | |
| setContent(content); | |
| } | |
| if (style) { | |
| using namespace boost; | |
| std::set<std::string> styleSet; | |
| escaped_list_separator<char> e('\0', '|', '\0'); | |
| std::string line = std::string(style); | |
| tokenizer<escaped_list_separator<char>> tok(line, e); | |
| for (tokenizer<escaped_list_separator<char>>::iterator i = tok.begin(); i != tok.end(); ++i) { | |
| styleSet.insert(*i); | |
| } | |
| setStyle(styleSet); | |
| } | |
| if (alignment) { | |
| int alignmentCode = 0; | |
| using namespace boost; | |
| escaped_list_separator<char> e('\0', '|', '\0'); | |
| std::string line = std::string(alignment); | |
| tokenizer<escaped_list_separator<char>> tok(line, e); | |
| for (tokenizer<escaped_list_separator<char>>::iterator i = tok.begin(); i != tok.end(); ++i) { | |
| alignmentCode = decodeAlignment(*i, alignmentCode); | |
| } | |
| setAlignment(alignmentCode); | |
| } | |
| if (foregroundColor) { | |
| Base::Color color = decodeColor(foregroundColor, Base::Color(0, 0, 0, 1)); | |
| setForeground(color); | |
| } | |
| if (backgroundColor) { | |
| Base::Color color = decodeColor(backgroundColor, Base::Color(1, 1, 1, 1)); | |
| setBackground(color); | |
| } | |
| if (displayUnit) { | |
| setDisplayUnit(displayUnit); | |
| } | |
| if (alias && (!checkAlias || !owner->revAliasProp.count(alias))) { | |
| setAlias(alias); | |
| } | |
| if (rowSpan || colSpan) { | |
| int rs = rowSpan ? atoi(rowSpan) : 1; | |
| int cs = colSpan ? atoi(colSpan) : 1; | |
| setSpans(rs, cs); | |
| } | |
| } | |
| /** | |
| * Save cell contents into \a writer. | |
| * | |
| */ | |
| void Cell::save(Base::Writer& writer) const | |
| { | |
| save(writer.Stream(), writer.ind(), false); | |
| } | |
| void Cell::save(std::ostream& os, const char* indent, bool noContent) const | |
| { | |
| if (!isUsed()) { | |
| return; | |
| } | |
| os << indent << "<Cell "; | |
| if (!noContent) { | |
| os << "address=\"" << address.toString() << "\" "; | |
| if (isUsed(EXPRESSION_SET)) { | |
| std::string content; | |
| getStringContent(content, true); | |
| os << "content=\"" << App::Property::encodeAttribute(content) << "\" "; | |
| } | |
| } | |
| if (isUsed(ALIGNMENT_SET)) { | |
| os << "alignment=\"" << encodeAlignment(alignment) << "\" "; | |
| } | |
| if (isUsed(STYLE_SET)) { | |
| os << "style=\"" << encodeStyle(style) << "\" "; | |
| } | |
| if (isUsed(FOREGROUND_COLOR_SET)) { | |
| os << "foregroundColor=\"" << encodeColor(foregroundColor) << "\" "; | |
| } | |
| if (isUsed(BACKGROUND_COLOR_SET)) { | |
| os << "backgroundColor=\"" << encodeColor(backgroundColor) << "\" "; | |
| } | |
| if (isUsed(DISPLAY_UNIT_SET)) { | |
| os << "displayUnit=\"" << App::Property::encodeAttribute(displayUnit.stringRep) << "\" "; | |
| } | |
| if (isUsed(ALIAS_SET)) { | |
| os << "alias=\"" << App::Property::encodeAttribute(alias) << "\" "; | |
| } | |
| if (isUsed(SPANS_SET)) { | |
| os << "rowSpan=\"" << rowSpan << "\" "; | |
| os << "colSpan=\"" << colSpan << "\" "; | |
| } | |
| os << "/>"; | |
| if (!noContent) { | |
| os << std::endl; | |
| } | |
| } | |
| /** | |
| * Update the \a used member variable with mask (bitwise or'ed). | |
| * | |
| */ | |
| void Cell::setUsed(int mask, bool state) | |
| { | |
| if (state) { | |
| used |= mask; | |
| } | |
| else { | |
| used &= ~mask; | |
| } | |
| } | |
| /** | |
| * Determine whether the bits in \a mask are set in the \a used member variable. | |
| * | |
| */ | |
| bool Cell::isUsed(int mask) const | |
| { | |
| return (used & mask) == mask; | |
| } | |
| /** | |
| * Determine if the any of the contents of the cell is set a non-default value. | |
| * | |
| */ | |
| bool Cell::isUsed() const | |
| { | |
| return used != 0; | |
| } | |
| void Cell::visit(App::ExpressionVisitor& v) | |
| { | |
| if (expression) { | |
| expression->visit(v); | |
| } | |
| } | |
| /** | |
| * Decode alignment into its internal value. | |
| * | |
| * @param itemStr Alignment as a string | |
| * @param alignment Current alignment. This is or'ed with the one in \a itemStr. | |
| * | |
| * @returns New alignment. | |
| * | |
| */ | |
| int Cell::decodeAlignment(const std::string& itemStr, int alignment) | |
| { | |
| if (itemStr == "himplied") { | |
| if (!(alignment & ALIGNMENT_HORIZONTAL)) { | |
| alignment |= ALIGNMENT_LEFT; | |
| } | |
| alignment |= Cell::ALIGNMENT_HIMPLIED; | |
| } | |
| else if (itemStr == "left") { | |
| alignment = (alignment & ~Cell::ALIGNMENT_HORIZONTAL) | Cell::ALIGNMENT_LEFT; | |
| } | |
| else if (itemStr == "center") { | |
| alignment = (alignment & ~Cell::ALIGNMENT_HORIZONTAL) | Cell::ALIGNMENT_HCENTER; | |
| } | |
| else if (itemStr == "right") { | |
| alignment = (alignment & ~Cell::ALIGNMENT_HORIZONTAL) | Cell::ALIGNMENT_RIGHT; | |
| } | |
| else if (itemStr == "vimplied") { | |
| if (!(alignment & ALIGNMENT_VERTICAL)) { | |
| alignment |= ALIGNMENT_VCENTER; | |
| } | |
| alignment |= Cell::ALIGNMENT_VIMPLIED; | |
| } | |
| else if (itemStr == "top") { | |
| alignment = (alignment & ~Cell::ALIGNMENT_VERTICAL) | Cell::ALIGNMENT_TOP; | |
| } | |
| else if (itemStr == "vcenter") { | |
| alignment = (alignment & ~Cell::ALIGNMENT_VERTICAL) | Cell::ALIGNMENT_VCENTER; | |
| } | |
| else if (itemStr == "bottom") { | |
| alignment = (alignment & ~Cell::ALIGNMENT_VERTICAL) | Cell::ALIGNMENT_BOTTOM; | |
| } | |
| else if (!itemStr.empty()) { | |
| throw Base::ValueError("Invalid alignment."); | |
| } | |
| return alignment; | |
| } | |
| /** | |
| * Encode internal alignment value as a string. | |
| * | |
| * @param alignment Alignment as a binary value. | |
| * | |
| * @returns Alignment represented as a string. | |
| * | |
| */ | |
| std::string Cell::encodeAlignment(int alignment) | |
| { | |
| std::string s; | |
| if (alignment & Cell::ALIGNMENT_LEFT) { | |
| s += "left"; | |
| } | |
| if (alignment & Cell::ALIGNMENT_HCENTER) { | |
| s += "center"; | |
| } | |
| if (alignment & Cell::ALIGNMENT_RIGHT) { | |
| s += "right"; | |
| } | |
| if (alignment & Cell::ALIGNMENT_HIMPLIED) { | |
| s += "|himplied"; | |
| } | |
| if (alignment & Cell::ALIGNMENT_VERTICAL) { | |
| s += "|"; | |
| } | |
| if (alignment & Cell::ALIGNMENT_TOP) { | |
| s += "top"; | |
| } | |
| if (alignment & Cell::ALIGNMENT_VCENTER) { | |
| s += "vcenter"; | |
| } | |
| if (alignment & Cell::ALIGNMENT_BOTTOM) { | |
| s += "bottom"; | |
| } | |
| if (alignment & Cell::ALIGNMENT_VIMPLIED) { | |
| s += "|vimplied"; | |
| } | |
| return s; | |
| } | |
| /** | |
| * Encode \a color as a \#rrggbbaa string. | |
| * | |
| * @param color Color to encode. | |
| * | |
| * @returns String with encoded color. | |
| * | |
| */ | |
| std::string Cell::encodeColor(const Base::Color& color) | |
| { | |
| std::stringstream tmp; | |
| tmp << "#" << std::hex << std::setw(2) << std::setfill('0') << int(color.r * 255.0) << std::hex | |
| << std::setw(2) << std::setfill('0') << int(color.g * 255.0) << std::hex << std::setw(2) | |
| << std::setfill('0') << int(color.b * 255.0) << std::hex << std::setw(2) | |
| << std::setfill('0') << int(color.a * 255.0); | |
| return tmp.str(); | |
| } | |
| /** | |
| * Encode set of styles as a string. | |
| * | |
| * @param style Set of string describing the style. | |
| * | |
| * @returns Set encoded as a string. | |
| * | |
| */ | |
| std::string Cell::encodeStyle(const std::set<std::string>& style) | |
| { | |
| std::string s; | |
| std::set<std::string>::const_iterator j = style.begin(); | |
| std::set<std::string>::const_iterator j_end = style.end(); | |
| while (j != j_end) { | |
| s += *j; | |
| ++j; | |
| if (j != j_end) { | |
| s += "|"; | |
| } | |
| } | |
| return s; | |
| } | |
| /** | |
| * Decode a string of the format \#rrggbb or \#rrggbbaa into a Color. | |
| * | |
| * @param color The color to decode. | |
| * @param defaultColor A default color in case the decoding fails. | |
| * | |
| * @returns Decoded color. | |
| * | |
| */ | |
| Base::Color Cell::decodeColor(const std::string& color, const Base::Color& defaultColor) | |
| { | |
| if (color.size() == 7 || color.size() == 9) { | |
| Base::Color c; | |
| if (color[0] != '#') { | |
| return defaultColor; | |
| } | |
| unsigned int value = strtoul(color.c_str() + 1, nullptr, 16); | |
| if (color.size() == 7) { | |
| value = (value << 8) | 0xff; | |
| } | |
| c.setPackedValue(value); | |
| return c; | |
| } | |
| else { | |
| return defaultColor; | |
| } | |
| } | |
| // roughly based on Spreadsheet/Gui/SheetModel.cpp | |
| std::string Cell::getFormattedQuantity() | |
| { | |
| QString qFormatted; | |
| App::CellAddress thisCell = getAddress(); | |
| Property* prop = owner->sheet()->getPropertyByName(thisCell.toString().c_str()); | |
| if (prop->isDerivedFrom<App::PropertyString>()) { | |
| const App::PropertyString* stringProp = static_cast<const App::PropertyString*>(prop); | |
| qFormatted = QString::fromUtf8(stringProp->getValue()); | |
| } | |
| else if (prop->isDerivedFrom<App::PropertyQuantity>()) { | |
| double rawVal = static_cast<App::PropertyQuantity*>(prop)->getValue(); | |
| const App::PropertyQuantity* floatProp = static_cast<const App::PropertyQuantity*>(prop); | |
| DisplayUnit du; | |
| bool hasDisplayUnit = getDisplayUnit(du); | |
| double duScale = du.scaler; | |
| const Base::Unit& computedUnit = floatProp->getUnit(); | |
| qFormatted = QLocale().toString(rawVal, 'f', Base::UnitsApi::getDecimals()); | |
| if (hasDisplayUnit) { | |
| if (computedUnit == Unit::One || computedUnit == du.unit) { | |
| QString number | |
| = QLocale().toString(rawVal / duScale, 'f', Base::UnitsApi::getDecimals()); | |
| qFormatted = number + QString::fromStdString(" " + displayUnit.stringRep); | |
| } | |
| } | |
| } | |
| else if (prop->isDerivedFrom<App::PropertyFloat>()) { | |
| double rawVal = static_cast<const App::PropertyFloat*>(prop)->getValue(); | |
| DisplayUnit du; | |
| bool hasDisplayUnit = getDisplayUnit(du); | |
| double duScale = du.scaler; | |
| qFormatted = QLocale().toString(rawVal, 'f', Base::UnitsApi::getDecimals()); | |
| if (hasDisplayUnit) { | |
| QString number = QLocale().toString(rawVal / duScale, 'f', Base::UnitsApi::getDecimals()); | |
| qFormatted = number + QString::fromStdString(" " + displayUnit.stringRep); | |
| } | |
| } | |
| else if (prop->isDerivedFrom<App::PropertyInteger>()) { | |
| double rawVal = static_cast<const App::PropertyInteger*>(prop)->getValue(); | |
| DisplayUnit du; | |
| bool hasDisplayUnit = getDisplayUnit(du); | |
| double duScale = du.scaler; | |
| int iRawVal = std::round(rawVal); | |
| qFormatted = QLocale().toString(iRawVal); | |
| if (hasDisplayUnit) { | |
| QString number = QLocale().toString(rawVal / duScale, 'f', Base::UnitsApi::getDecimals()); | |
| qFormatted = number + QString::fromStdString(" " + displayUnit.stringRep); | |
| } | |
| } | |
| return qFormatted.toStdString(); | |
| } | |