AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
// 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 *
* *
***************************************************************************/
#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 // Microsoft's #define conflicts with the use below
# endif
#endif
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();
}