| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | #include <QLocale> |
| | #include <boost/algorithm/string/predicate.hpp> |
| | #include <boost/tokenizer.hpp> |
| | #include <iomanip> |
| | #include <sstream> |
| |
|
| |
|
| | #include <App/ExpressionParser.h> |
| | #include <Base/Console.h> |
| | #include <Base/Quantity.h> |
| | #include <Base/Reader.h> |
| | #include <Base/UnitsApi.h> |
| | #include <Base/Writer.h> |
| |
|
| | #include "Cell.h" |
| | #include "Sheet.h" |
| |
|
| |
|
| | FC_LOG_LEVEL_INIT("Spreadsheet", true, true) |
| |
|
| | #ifdef _MSC_VER |
| | # define __func__ __FUNCTION__ |
| | # ifdef PropertySheet |
| | # undef PropertySheet |
| | # endif |
| | #endif |
| |
|
| | using namespace App; |
| | using namespace Base; |
| | using namespace Spreadsheet; |
| |
|
| | |
| |
|
| | |
| | class ReaderPrivate: public Base::XMLReader |
| | { |
| | public: |
| | ReaderPrivate(const char* FileName, std::istream& is) |
| | : XMLReader(FileName, is) |
| | {} |
| |
|
| | bool read() |
| | { |
| | return XMLReader::read(); |
| | } |
| | }; |
| |
|
| | |
| |
|
| | |
| | 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; |
| |
|
| | |
| | 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; |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | 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; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | Cell::~Cell() = default; |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | void Cell::setExpression(App::ExpressionPtr&& expr) |
| | { |
| | PropertySheet::AtomicPropertyChange signaller(*owner); |
| |
|
| | owner->setDirty(address); |
| |
|
| | |
| | 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); |
| |
|
| | |
| | owner->addDependencies(address); |
| |
|
| | signaller.tryInvoke(); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | 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(); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | 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') { |
| | |
| | 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)); |
| | } |
| | } |
| |
|
| | |
| | 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) { |
| | |
| | |
| |
|
| | |
| | const bool isNumberNom = freecad_cast<NumberExpression*>( |
| | fraction->getLeft() |
| | ); |
| | const bool isNumberDenom = freecad_cast<NumberExpression*>( |
| | fraction->getRight() |
| | ); |
| |
|
| | |
| | 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; |
| |
|
| | |
| | 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())) { |
| | |
| | |
| | newExpr = std::move(parsedExpr); |
| | } |
| | } |
| | catch (...) { |
| | } |
| | } |
| | } |
| |
|
| | if (!newExpr && value && *value != '\0') { |
| | newExpr = std::make_unique<App::StringExpression>(sheet, value); |
| | } |
| |
|
| | |
| | } |
| |
|
| | |
| | |
| | setExpression(std::move(newExpr)); |
| | signaller.tryInvoke(); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | 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(); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | bool Cell::getAlignment(int& _alignment) const |
| | { |
| | _alignment = alignment; |
| | return isUsed(ALIGNMENT_SET); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | 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(); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | bool Cell::getStyle(std::set<std::string>& _style) const |
| | { |
| | _style = style; |
| | return isUsed(STYLE_SET); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | 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(); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | bool Cell::getForeground(Base::Color& color) const |
| | { |
| | color = foregroundColor; |
| | return isUsed(FOREGROUND_COLOR_SET); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | 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(); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | bool Cell::getBackground(Base::Color& color) const |
| | { |
| | color = backgroundColor; |
| | return isUsed(BACKGROUND_COLOR_SET); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | 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(); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | 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); |
| |
|
| | |
| | if (!n.empty()) { |
| | owner->aliasProp[address] = n; |
| | owner->revAliasProp[n] = address; |
| | } |
| | else { |
| | owner->aliasProp.erase(address); |
| | } |
| |
|
| | if (!alias.empty()) { |
| | |
| | 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); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | void Cell::setComputedUnit(const Base::Unit& unit) |
| | { |
| | PropertySheet::AtomicPropertyChange signaller(*owner); |
| |
|
| | computedUnit = unit; |
| | setUsed(COMPUTED_UNIT_SET, computedUnit != Unit::One); |
| | setDirty(); |
| |
|
| | signaller.tryInvoke(); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | bool Cell::getComputedUnit(Base::Unit& unit) const |
| | { |
| | unit = computedUnit; |
| | return isUsed(COMPUTED_UNIT_SET); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| |
|
| | 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(); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | 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); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | void Cell::moveAbsolute(CellAddress newAddress) |
| | { |
| | address = newAddress; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | 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; |
| |
|
| | |
| | |
| | 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); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | 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; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | void Cell::setUsed(int mask, bool state) |
| | { |
| | if (state) { |
| | used |= mask; |
| | } |
| | else { |
| | used &= ~mask; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | bool Cell::isUsed(int mask) const |
| | { |
| | return (used & mask) == mask; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | bool Cell::isUsed() const |
| | { |
| | return used != 0; |
| | } |
| |
|
| | void Cell::visit(App::ExpressionVisitor& v) |
| | { |
| | if (expression) { |
| | expression->visit(v); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | 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; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | 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; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | 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(); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | 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; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | 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; |
| | } |
| | } |
| |
|
| | |
| | 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(); |
| | } |
| |
|