Spreadsheet: convert PropertySheet to link type property

PropertySheet is changed to derive from PropertyExpressionContainer,
which makes it a link type property that is capable of external linking.
It now relies on the unified link property API to manage object
depenency, and tracking of object life time, relabeling, etc.

This patch also includes various fix and improvement of Spreadsheet,
such as improved recompute efficiency, correct handling of document
label change, etc.
This commit is contained in:
Zheng, Lei
2019-06-29 17:36:37 +08:00
committed by wmayer
parent ced27a69c6
commit b4751145b4
6 changed files with 1024 additions and 654 deletions

View File

@@ -25,16 +25,21 @@
#ifndef _PreComp_
#endif
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include "Cell.h"
#include "Utils.h"
#include <boost/tokenizer.hpp>
#include <Base/Reader.h>
#include <Base/Quantity.h>
#include <Base/Writer.h>
#include <Base/Console.h>
#include <App/Expression.h>
#include "Sheet.h"
#include <iomanip>
FC_LOG_LEVEL_INIT("Spreadsheet",true,true);
#ifdef _MSC_VER
#define __func__ __FUNCTION__
#endif
@@ -43,6 +48,22 @@ 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();
}
};
///////////////////////////////////////////////////////////
const int Cell::EXPRESSION_SET = 1;
const int Cell::ALIGNMENT_SET = 4;
const int Cell::STYLE_SET = 8;
@@ -85,7 +106,7 @@ Cell::Cell(const CellAddress &_address, PropertySheet *_owner)
, alignment(ALIGNMENT_HIMPLIED | ALIGNMENT_LEFT | ALIGNMENT_VIMPLIED | ALIGNMENT_VCENTER)
, style()
, foregroundColor(0, 0, 0, 1)
, backgroundColor(1, 1, 1, 0)
, backgroundColor(1, 1, 1, 1)
, displayUnit()
, alias()
, computedUnit()
@@ -100,7 +121,7 @@ Cell::Cell(PropertySheet *_owner, const Cell &other)
: address(other.address)
, owner(_owner)
, used(other.used)
, expression(other.expression ? other.expression->copy() : 0)
, expression(other.expression ? other.expression->copy() : nullptr)
, alignment(other.alignment)
, style(other.style)
, foregroundColor(other.foregroundColor)
@@ -112,6 +133,7 @@ Cell::Cell(PropertySheet *_owner, const Cell &other)
, colSpan(other.colSpan)
{
setUsed(MARK_SET, false);
setDirty();
}
Cell &Cell::operator =(const Cell &rhs)
@@ -131,7 +153,9 @@ Cell &Cell::operator =(const Cell &rhs)
setSpans(rhs.rowSpan, rhs.colSpan);
setUsed(MARK_SET, false);
setDirty();
signaller.tryInvoke();
return *this;
}
@@ -155,18 +179,40 @@ void Cell::setExpression(App::Expression *expr)
{
PropertySheet::AtomicPropertyChange signaller(*owner);
owner->setDirty(address);
/* Remove dependencies */
owner->removeDependencies(address);
if(expr && expr->comment.size()) {
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();
}
if (expression)
delete expression;
expression = expr;
setUsed(EXPRESSION_SET, expression != 0);
setUsed(EXPRESSION_SET, !!expression);
/* Update dependencies */
owner->addDependencies(address);
owner->rebuildDocDepList();
signaller.tryInvoke();
}
/**
@@ -174,9 +220,23 @@ void Cell::setExpression(App::Expression *expr)
*
*/
const App::Expression *Cell::getExpression() const
const App::Expression *Cell::getExpression(bool withFormat) const
{
return expression;
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();
}
/**
@@ -184,7 +244,7 @@ const App::Expression *Cell::getExpression() const
*
*/
bool Cell::getStringContent(std::string & s) const
bool Cell::getStringContent(std::string & s, bool persistent) const
{
if (expression) {
if (freecad_dynamic_cast<App::StringExpression>(expression)) {
@@ -211,13 +271,24 @@ bool Cell::getStringContent(std::string & s) const
}
}
void Cell::afterRestore() {
auto expr = freecad_dynamic_cast<StringExpression>(expression);
if(expr)
setContent(expr->getText().c_str());
}
void Cell::setContent(const char * value)
{
PropertySheet::AtomicPropertyChange signaller(*owner);
App::Expression * expr = 0;
setUsed(PARSE_EXCEPTION_SET, false);
clearException();
if (value != 0) {
if(owner->sheet()->isRestoring()) {
expression = new App::StringExpression(owner->sheet(),value);
setUsed(EXPRESSION_SET, true);
return;
}
if (*value == '=') {
try {
expr = App::ExpressionParser::parse(owner->sheet(), value + 1);
@@ -248,7 +319,21 @@ void Cell::setContent(const char * value)
}
}
setExpression(expr);
try {
setExpression(expr);
signaller.tryInvoke();
} catch (Base::Exception &e) {
if(value) {
std::string _value;
if(*value != '=') {
_value = "=";
_value += value;
value = _value.c_str();
}
setExpression(new App::StringExpression(owner->sheet(), value));
setParseException(e.what());
}
}
}
/**
@@ -265,6 +350,8 @@ void Cell::setAlignment(int _alignment)
alignment = _alignment;
setUsed(ALIGNMENT_SET, alignment != (ALIGNMENT_HIMPLIED | ALIGNMENT_LEFT | ALIGNMENT_VIMPLIED | ALIGNMENT_VCENTER));
setDirty();
signaller.tryInvoke();
}
}
@@ -291,6 +378,9 @@ void Cell::setStyle(const std::set<std::string> & _style)
style = _style;
setUsed(STYLE_SET, style.size() > 0);
setDirty();
signaller.tryInvoke();
}
}
@@ -317,6 +407,9 @@ void Cell::setForeground(const App::Color &color)
foregroundColor = color;
setUsed(FOREGROUND_COLOR_SET, foregroundColor != App::Color(0, 0, 0, 1));
setDirty();
signaller.tryInvoke();
}
}
@@ -343,6 +436,9 @@ void Cell::setBackground(const App::Color &color)
backgroundColor = color;
setUsed(BACKGROUND_COLOR_SET, backgroundColor != App::Color(1, 1, 1, 0));
setDirty();
signaller.tryInvoke();
}
}
@@ -380,6 +476,9 @@ void Cell::setDisplayUnit(const std::string &unit)
displayUnit = newDisplayUnit;
setUsed(DISPLAY_UNIT_SET, !displayUnit.isEmpty());
setDirty();
signaller.tryInvoke();
}
}
@@ -414,7 +513,9 @@ void Cell::setAlias(const std::string &n)
owner->aliasProp.erase(address);
setUsed(ALIAS_SET, !alias.empty());
setDirty();
signaller.tryInvoke();
}
}
@@ -435,6 +536,9 @@ void Cell::setComputedUnit(const Base::Unit &unit)
computedUnit = unit;
setUsed(COMPUTED_UNIT_SET, !computedUnit.isEmpty());
setDirty();
signaller.tryInvoke();
}
/**
@@ -465,6 +569,8 @@ void Cell::setSpans(int rows, int columns)
colSpan = (columns == -1 ? 1 : columns);
setUsed(SPANS_SET, (rowSpan != 1 || colSpan != 1) );
setUsed(SPANS_UPDATED);
setDirty();
signaller.tryInvoke();
}
}
@@ -482,18 +588,30 @@ bool Cell::getSpans(int &rows, int &columns) const
void Cell::setException(const std::string &e)
{
if(e.size() && 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.size() && 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.size() && owner && owner->sheet()) {
FC_LOG(owner->sheet()->getFullName() << '.'
<< address.toString() << ": " << e);
}
exceptionStr = e;
setUsed(RESOLVE_EXCEPTION_SET);
}
@@ -505,14 +623,22 @@ void Cell::clearResolveException()
void Cell::clearException()
{
if (!isUsed(PARSE_EXCEPTION_SET))
exceptionStr = "";
exceptionStr.clear();
setUsed(EXCEPTION_SET, false);
setUsed(RESOLVE_EXCEPTION_SET, false);
setUsed(PARSE_EXCEPTION_SET, false);
}
void Cell::clearDirty()
{
owner->clearDirty(address);
if(owner)
owner->clearDirty(address);
}
void Cell::setDirty()
{
if(owner)
owner->setDirty(address);
}
/**
@@ -530,7 +656,7 @@ void Cell::moveAbsolute(CellAddress newAddress)
*
*/
void Cell::restore(Base::XMLReader &reader)
void Cell::restore(Base::XMLReader &reader, bool checkAlias)
{
const char* style = reader.hasAttribute("style") ? reader.getAttribute("style") : 0;
const char* alignment = reader.hasAttribute("alignment") ? reader.getAttribute("alignment") : 0;
@@ -585,7 +711,7 @@ void Cell::restore(Base::XMLReader &reader)
}
if (displayUnit)
setDisplayUnit(displayUnit);
if (alias)
if (alias && (!checkAlias || !owner->revAliasProp.count(alias)))
setAlias(alias);
if (rowSpan || colSpan) {
@@ -601,46 +727,55 @@ void Cell::restore(Base::XMLReader &reader)
*
*/
void Cell::save(Base::Writer &writer) const
{
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;
writer.Stream() << writer.ind() << "<Cell ";
os << indent << "<Cell ";
writer.Stream() << "address=\"" << address.toString() << "\" ";
if (!noContent) {
os << "address=\"" << address.toString() << "\" ";
if (isUsed(EXPRESSION_SET)) {
std::string content;
getStringContent(content);
writer.Stream() << "content=\"" << App::Property::encodeAttribute(content) << "\" ";
if(isUsed(EXPRESSION_SET)) {
std::string content;
getStringContent(content,true);
os << "content=\"" << App::Property::encodeAttribute(content) << "\" ";
}
}
if (isUsed(ALIGNMENT_SET))
writer.Stream() << "alignment=\"" << encodeAlignment(alignment) << "\" ";
os << "alignment=\"" << encodeAlignment(alignment) << "\" ";
if (isUsed(STYLE_SET))
writer.Stream() << "style=\"" << encodeStyle(style) << "\" ";
os << "style=\"" << encodeStyle(style) << "\" ";
if (isUsed(FOREGROUND_COLOR_SET))
writer.Stream() << "foregroundColor=\"" << encodeColor(foregroundColor) << "\" ";
os << "foregroundColor=\"" << encodeColor(foregroundColor) << "\" ";
if (isUsed(BACKGROUND_COLOR_SET))
writer.Stream() << "backgroundColor=\"" << encodeColor(backgroundColor) << "\" ";
os << "backgroundColor=\"" << encodeColor(backgroundColor) << "\" ";
if (isUsed(DISPLAY_UNIT_SET))
writer.Stream() << "displayUnit=\"" << App::Property::encodeAttribute(displayUnit.stringRep) << "\" ";
os << "displayUnit=\"" << App::Property::encodeAttribute(displayUnit.stringRep) << "\" ";
if (isUsed(ALIAS_SET))
writer.Stream() << "alias=\"" << App::Property::encodeAttribute(alias) << "\" ";
os << "alias=\"" << App::Property::encodeAttribute(alias) << "\" ";
if (isUsed(SPANS_SET)) {
writer.Stream() << "rowSpan=\"" << rowSpan<< "\" ";
writer.Stream() << "colSpan=\"" << colSpan << "\" ";
os << "rowSpan=\"" << rowSpan<< "\" ";
os << "colSpan=\"" << colSpan << "\" ";
}
writer.Stream() << "/>" << std::endl;
if(editMode)
os << "editMode=\"" << editMode << "\" ";
os << "/>";
if(!noContent)
os << std::endl;
}
/**
@@ -654,8 +789,6 @@ void Cell::setUsed(int mask, bool state)
used |= mask;
else
used &= ~mask;
owner->setDirty(address);
}
/**
@@ -696,23 +829,27 @@ void Cell::visit(App::ExpressionVisitor &v)
int Cell::decodeAlignment(const std::string & itemStr, int alignment)
{
if (itemStr == "himplied")
alignment = (alignment & ~Cell::ALIGNMENT_HORIZONTAL) | Cell::ALIGNMENT_HIMPLIED;
else if (itemStr == "left")
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")
alignment = (alignment & ~Cell::ALIGNMENT_VERTICAL) | Cell::ALIGNMENT_VIMPLIED;
else if (itemStr == "top")
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
else if(itemStr.size())
throw Base::ValueError("Invalid alignment.");
return alignment;

View File

@@ -27,6 +27,7 @@
#include <set>
#include <App/Material.h>
#include <App/Range.h>
#include <App/Expression.h>
#include "DisplayUnit.h"
#include "Utils.h"
@@ -59,9 +60,9 @@ public:
~Cell();
const App::Expression * getExpression() const;
const App::Expression * getExpression(bool withFormat=false) const;
bool getStringContent(std::string & s) const;
bool getStringContent(std::string & s, bool persistent=false) const;
void setContent(const char * value);
@@ -95,6 +96,8 @@ public:
void clearDirty();
void setDirty();
void setResolveException(const std::string &e);
void clearResolveException();
@@ -105,9 +108,12 @@ public:
void moveAbsolute(App::CellAddress newAddress);
void restore(Base::XMLReader &reader);
void restore(Base::XMLReader &reader, bool checkAlias=false);
void afterRestore();
void save(Base::Writer &writer) const;
void save(std::ostream &os, const char *indent, bool noContent) const;
bool isUsed() const;
@@ -190,6 +196,7 @@ private:
int colSpan;
std::string exceptionStr;
App::CellAddress anchor;
friend class PropertySheet;
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -26,7 +26,7 @@
#include <map>
#include <App/DocumentObserver.h>
#include <App/DocumentObject.h>
#include <App/Property.h>
#include <App/PropertyLinks.h>
#include <App/PropertyLinks.h>
#include "Cell.h"
@@ -37,7 +37,8 @@ class Sheet;
class PropertySheet;
class SheetObserver;
class PropertySheet : public App::Property, private App::AtomicPropertyChangeInterface<PropertySheet> {
class SpreadsheetExport PropertySheet : public App::PropertyExpressionContainer
, private App::AtomicPropertyChangeInterface<PropertySheet> {
TYPESYSTEM_HEADER();
public:
@@ -45,6 +46,24 @@ public:
~PropertySheet();
virtual std::map<App::ObjectIdentifier, const App::Expression*> getExpressions() const override;
virtual void setExpressions(std::map<App::ObjectIdentifier, App::ExpressionPtr> &&exprs) override;
virtual void onRelabeledDocument(const App::Document &doc) override;
virtual void updateElementReference(
App::DocumentObject *feature,bool reverse=false,bool notify=false) override;
virtual bool referenceChanged() const override;
virtual bool adjustLink(const std::set<App::DocumentObject *> &inList) override;
virtual Property *CopyOnImportExternal(const std::map<std::string,std::string> &nameMap) const override;
virtual Property *CopyOnLabelChange(App::DocumentObject *obj,
const std::string &ref, const char *newLabel) const override;
virtual Property *CopyOnLinkReplace(const App::DocumentObject *parent,
App::DocumentObject *oldObj, App::DocumentObject *newObj) const override;
virtual void breakLink(App::DocumentObject *obj, bool clear) override;
virtual void afterRestore() override;
virtual void onContainerRestored() override;
virtual Property *Copy(void) const;
virtual void Paste(const Property &from);
@@ -53,6 +72,10 @@ public:
virtual void Restore(Base::XMLReader & reader);
void copyCells(Base::Writer &writer, const std::vector<App::Range> &ranges) const;
void pasteCells(Base::XMLReader &reader, const App::CellAddress &addr);
Cell *createCell(App::CellAddress address);
void setValue() { }
@@ -95,6 +118,8 @@ public:
void setDirty(App::CellAddress address);
void setDirty();
void clearDirty(App::CellAddress key) { dirty.erase(key); }
void clearDirty() { dirty.clear(); purgeTouched(); }
@@ -103,6 +128,8 @@ public:
void moveCell(App::CellAddress currPos, App::CellAddress newPos, std::map<App::ObjectIdentifier, App::ObjectIdentifier> &renames);
void pasteCells(const std::map<App::CellAddress, std::string> &cells, int rowOffset, int colOffset);
void insertRows(int row, int count);
void removeRows(int row, int count);
@@ -127,26 +154,31 @@ public:
const std::set<std::string> &getDeps(App::CellAddress pos) const;
const std::set<App::DocumentObject*> & getDocDeps() const { return docDeps; }
void recomputeDependencies(App::CellAddress key);
PyObject *getPyObject(void);
void resolveAll();
void setPyObject(PyObject *);
void invalidateDependants(const App::DocumentObject *docObj);
void renamedDocumentObject(const App::DocumentObject *docObj);
void renamedDocument(const App::Document *doc);
void renameObjectIdentifiers(const std::map<App::ObjectIdentifier, App::ObjectIdentifier> &paths);
void deletedDocumentObject(const App::DocumentObject *docObj);
void documentSet();
std::string getRow(int offset=0) const;
std::string getColumn(int offset=0) const;
protected:
virtual void hasSetValue() override;
virtual void hasSetChildValue(App::Property &prop) override;
virtual void onBreakLink(App::DocumentObject *obj) override;
virtual void onAddDep(App::DocumentObject *obj) override;
virtual void onRemoveDep(App::DocumentObject *obj) override;
private:
PropertySheet(const PropertySheet & other);
@@ -191,11 +223,8 @@ private:
void removeDependencies(App::CellAddress key);
void recomputeDependants(const App::Property * prop);
void recomputeDependants(const App::DocumentObject * docObj);
void rebuildDocDepList();
void slotChangedObject(const App::DocumentObject &obj, const App::Property &prop);
void recomputeDependants(const App::DocumentObject *obj, const char *propName);
/*! Cell dependencies, i.e when a change occurs to property given in key,
the set of addresses needs to be recomputed.
@@ -213,15 +242,6 @@ private:
/*! DocumentObject this cell depends on */
std::map<App::CellAddress, std::set< std::string > > cellToDocumentObjectMap;
/*! Other document objects the sheet depends on */
std::set<App::DocumentObject*> docDeps;
/*! Name of document objects, used for renaming */
std::map<const App::DocumentObject*, std::string> documentObjectName;
/*! Name of documents, used for renaming */
std::map<const App::Document*, std::string> documentName;
/*! Mapping of cell position to alias property */
std::map<App::CellAddress, std::string> aliasProp;
@@ -230,6 +250,11 @@ private:
/*! The associated python object */
Py::Object PythonObject;
std::map<const App::DocumentObject*, boost::signals2::scoped_connection> depConnections;
int updateCount;
bool restoring = false;
};
}

View File

@@ -25,6 +25,7 @@
#ifndef _PreComp_
#endif
#include <boost/regex.hpp>
#include <boost/tokenizer.hpp>
#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/copy.hpp>
@@ -41,6 +42,7 @@
#include <Base/Stream.h>
#include <Base/Writer.h>
#include <Base/Tools.h>
#include <Base/Console.h>
#include "Sheet.h"
#include "SheetObserver.h"
#include "Utils.h"
@@ -53,6 +55,8 @@
#include <boost/bind.hpp>
#include <deque>
FC_LOG_LEVEL_INIT("Spreadsheet",true,true);
using namespace Base;
using namespace App;
using namespace Spreadsheet;
@@ -78,19 +82,13 @@ typedef Traits::edge_descriptor Edge;
Sheet::Sheet()
: DocumentObject()
, props(this)
, props(PropertyContainer::dynamicProps)
, cells(this)
{
ADD_PROPERTY_TYPE(docDeps, (0), "Spreadsheet", (PropertyType)(Prop_Transient|Prop_ReadOnly|Prop_Hidden), "Dependencies");
ADD_PROPERTY_TYPE(cells, (), "Spreadsheet", (PropertyType)(Prop_ReadOnly|Prop_Hidden), "Cell contents");
ADD_PROPERTY_TYPE(columnWidths, (), "Spreadsheet", (PropertyType)(Prop_ReadOnly|Prop_Hidden), "Column widths");
ADD_PROPERTY_TYPE(cells, (), "Spreadsheet", (PropertyType)(Prop_Hidden), "Cell contents");
ADD_PROPERTY_TYPE(columnWidths, (), "Spreadsheet", (PropertyType)(Prop_ReadOnly|Prop_Hidden|Prop_Output), "Column widths");
ADD_PROPERTY_TYPE(rowHeights, (), "Spreadsheet", (PropertyType)(Prop_ReadOnly|Prop_Hidden|Prop_Output), "Row heights");
ADD_PROPERTY_TYPE(rowHeights, (), "Spreadsheet", (PropertyType)(Prop_ReadOnly|Prop_Hidden), "Row heights");
docDeps.setSize(0);
docDeps.setScope(LinkScope::Global);
onRenamedDocumentConnection = GetApplication().signalRenameDocument.connect(boost::bind(&Spreadsheet::Sheet::onRenamedDocument, this, _1));
onRelabledDocumentConnection = GetApplication().signalRelabelDocument.connect(boost::bind(&Spreadsheet::Sheet::onRelabledDocument, this, _1));
}
/**
@@ -123,7 +121,6 @@ void Sheet::clearAll()
columnWidths.clear();
rowHeights.clear();
removedAliases.clear();
docDeps.setValues(std::vector<DocumentObject*>());
for (ObserverMap::iterator i = observers.begin(); i != observers.end(); ++i)
delete i->second;
@@ -175,12 +172,14 @@ bool Sheet::importFromFile(const std::string &filename, char delimiter, char quo
}
}
catch (...) {
signaller.tryInvoke();
return false;
}
++row;
}
file.close();
signaller.tryInvoke();
return true;
}
else
@@ -365,16 +364,7 @@ void Sheet::setCell(CellAddress address, const char * value)
return;
}
// Update expression, delete old first if necessary
Cell * cell = getNewCell(address);
if (cell->getExpression()) {
setContent(address, 0);
}
setContent(address, value);
// Recompute dependencies
touch();
}
/**
@@ -421,14 +411,15 @@ Property * Sheet::getProperty(const char * addr) const
*
*/
void Sheet::getCellAddress(const Property *prop, CellAddress & address)
bool Sheet::getCellAddress(const Property *prop, CellAddress & address)
{
std::map<const Property*, CellAddress >::const_iterator i = propAddress.find(prop);
if (i != propAddress.end())
if (i != propAddress.end()) {
address = i->second;
else
throw Base::TypeError("Property is not a cell");
return true;
}
return false;
}
/**
@@ -488,7 +479,7 @@ void Sheet::onSettingDocument()
Property * Sheet::setFloatProperty(CellAddress key, double value)
{
Property * prop = props.getPropertyByName(key.toString().c_str());
Property * prop = props.getDynamicPropertyByName(key.toString().c_str());
PropertyFloat * floatProp;
if (!prop || prop->getTypeId() != PropertyFloat::getClassTypeId()) {
@@ -496,7 +487,7 @@ Property * Sheet::setFloatProperty(CellAddress key, double value)
this->removeDynamicProperty(key.toString().c_str());
propAddress.erase(prop);
}
floatProp = freecad_dynamic_cast<PropertyFloat>(props.addDynamicProperty("App::PropertyFloat", key.toString().c_str(), 0, 0, Prop_ReadOnly | Prop_Hidden | Prop_Transient));
floatProp = freecad_dynamic_cast<PropertyFloat>(addDynamicProperty("App::PropertyFloat", key.toString().c_str(), 0, 0, Prop_ReadOnly | Prop_Hidden | Prop_NoPersist));
}
else
floatProp = static_cast<PropertyFloat*>(prop);
@@ -519,7 +510,7 @@ Property * Sheet::setFloatProperty(CellAddress key, double value)
Property * Sheet::setQuantityProperty(CellAddress key, double value, const Base::Unit & unit)
{
Property * prop = props.getPropertyByName(key.toString().c_str());
Property * prop = props.getDynamicPropertyByName(key.toString().c_str());
PropertySpreadsheetQuantity * quantityProp;
if (!prop || prop->getTypeId() != PropertySpreadsheetQuantity::getClassTypeId()) {
@@ -527,7 +518,7 @@ Property * Sheet::setQuantityProperty(CellAddress key, double value, const Base:
this->removeDynamicProperty(key.toString().c_str());
propAddress.erase(prop);
}
Property * p = props.addDynamicProperty("Spreadsheet::PropertySpreadsheetQuantity", key.toString().c_str(), 0, 0, Prop_ReadOnly | Prop_Hidden | Prop_Transient);
Property * p = addDynamicProperty("Spreadsheet::PropertySpreadsheetQuantity", key.toString().c_str(), 0, 0, Prop_ReadOnly | Prop_Hidden | Prop_NoPersist);
quantityProp = freecad_dynamic_cast<PropertySpreadsheetQuantity>(p);
}
else
@@ -553,7 +544,7 @@ Property * Sheet::setQuantityProperty(CellAddress key, double value, const Base:
Property * Sheet::setStringProperty(CellAddress key, const std::string & value)
{
Property * prop = props.getPropertyByName(key.toString().c_str());
Property * prop = props.getDynamicPropertyByName(key.toString().c_str());
PropertyString * stringProp = freecad_dynamic_cast<PropertyString>(prop);
if (!stringProp) {
@@ -561,7 +552,7 @@ Property * Sheet::setStringProperty(CellAddress key, const std::string & value)
this->removeDynamicProperty(key.toString().c_str());
propAddress.erase(prop);
}
stringProp = freecad_dynamic_cast<PropertyString>(props.addDynamicProperty("App::PropertyString", key.toString().c_str(), 0, 0, Prop_ReadOnly | Prop_Hidden | Prop_Transient));
stringProp = freecad_dynamic_cast<PropertyString>(addDynamicProperty("App::PropertyString", key.toString().c_str(), 0, 0, Prop_ReadOnly | Prop_Hidden | Prop_NoPersist));
}
propAddress[stringProp] = key;
@@ -597,13 +588,31 @@ void Sheet::updateAlias(CellAddress key)
}
}
if (!aliasProp)
aliasProp = props.addDynamicProperty(prop->getTypeId().getName(), alias.c_str(), 0, 0, Prop_ReadOnly | Prop_Transient);
if (!aliasProp) {
aliasProp = addDynamicProperty(prop->getTypeId().getName(), alias.c_str(), 0, 0, Prop_ReadOnly | Prop_NoPersist);
aliasProp->setStatus(App::Property::Hidden,true);
}
aliasProp->Paste(*prop);
}
}
struct CurrentAddressLock {
CurrentAddressLock(int &r, int &c, const CellAddress &addr)
:row(r),col(c)
{
row = addr.row();
col = addr.col();
}
~CurrentAddressLock() {
row = -1;
col = -1;
}
int &row;
int &col;
};
/**
* Update the Property given by \a key. This will also eventually trigger recomputations of cells depending on \a key.
*
@@ -616,11 +625,12 @@ void Sheet::updateProperty(CellAddress key)
Cell * cell = getCell(key);
if (cell != 0) {
Expression * output;
std::unique_ptr<Expression> output;
const Expression * input = cell->getExpression();
if (input) {
output = input->eval();
CurrentAddressLock lock(currentRow,currentCol,key);
output = cells.eval(input);
}
else {
std::string s;
@@ -641,8 +651,6 @@ void Sheet::updateProperty(CellAddress key)
}
else
setStringProperty(key, freecad_dynamic_cast<StringExpression>(output)->getText().c_str());
delete output;
}
else
clear(key);
@@ -661,6 +669,12 @@ void Sheet::updateProperty(CellAddress key)
Property *Sheet::getPropertyByName(const char* name) const
{
std::string _name;
CellAddress addr;
if(addr.parseAbsoluteAddress(name)) {
_name = addr.toString(true);
name = _name.c_str();
}
Property * prop = getProperty(name);
if (prop)
@@ -669,20 +683,10 @@ Property *Sheet::getPropertyByName(const char* name) const
return DocumentObject::getPropertyByName(name);
}
/**
* @brief Get name of a property, given a pointer to it.
* @param prop Pointer to property.
* @return Pointer to string.
*/
const char *Sheet::getPropertyName(const Property *prop) const
{
const char * name = props.getPropertyName(prop);
if (name)
return name;
else
return PropertyContainer::getPropertyName(prop);
void Sheet::touchCells(Range range) {
do {
cells.setDirty(*range);
}while(range.next());
}
/**
@@ -693,18 +697,20 @@ const char *Sheet::getPropertyName(const Property *prop) const
void Sheet::recomputeCell(CellAddress p)
{
Cell * cell = cells.getValue(p);
std::string docName = getDocument()->Label.getValue();
std::string docObjName = std::string(getNameInDocument());
std::string name = docName + "#" + docObjName + "." + p.toString();
try {
if (cell) {
cell->clearException();
cell->clearResolveException();
if (cell && cell->hasException()) {
std::string content;
cell->getStringContent(content);
cell->setContent(content.c_str());
}
updateProperty(p);
cells.clearDirty(p);
cellErrors.erase(p);
if(!cell || !cell->hasException()) {
cells.clearDirty(p);
cellErrors.erase(p);
}
}
catch (const Base::Exception & e) {
QString msg = QString::fromUtf8("ERR: %1").arg(QString::fromUtf8(e.what()));
@@ -712,9 +718,15 @@ void Sheet::recomputeCell(CellAddress p)
setStringProperty(p, Base::Tools::toStdString(msg));
if (cell)
cell->setException(e.what());
else
e.ReportException();
// Mark as erroneous
cellErrors.insert(p);
cellUpdated(p);
if(e.isDerivedFrom(Base::AbortException::getClassTypeId()))
throw;
}
updateAlias(p);
@@ -742,78 +754,117 @@ DocumentObjectExecReturn *Sheet::execute(void)
dirtyCells.insert(*i);
}
// Push dirty cells onto queue
for (std::set<CellAddress>::const_iterator i = dirtyCells.begin(); i != dirtyCells.end(); ++i) {
// Create queue and a graph structure to compute order of evaluation
std::deque<CellAddress> workQueue;
DependencyList graph;
std::map<CellAddress, Vertex> VertexList;
std::map<Vertex, CellAddress> VertexIndexList;
DependencyList graph;
std::map<CellAddress, Vertex> VertexList;
std::map<Vertex, CellAddress> VertexIndexList;
std::deque<CellAddress> workQueue(dirtyCells.begin(),dirtyCells.end());
while(workQueue.size()) {
CellAddress currPos = workQueue.front();
workQueue.pop_front();
workQueue.push_back(*i);
// Insert into map of CellPos -> Index, if it doesn't exist already
auto res = VertexList.emplace(currPos,Vertex());
if(res.second) {
res.first->second = add_vertex(graph);
VertexIndexList[res.first->second] = currPos;
}
while (workQueue.size() > 0) {
CellAddress currPos = workQueue.front();
std::set<CellAddress> s;
// Get other cells that depends on the current cell (currPos)
providesTo(currPos, s);
workQueue.pop_front();
// Insert into map of CellPos -> Index, if it doesn't exist already
if (VertexList.find(currPos) == VertexList.end()) {
VertexList[currPos] = add_vertex(graph);
VertexIndexList[VertexList[currPos]] = currPos;
// Process cells that depend on the current cell
for(auto &dep : providesTo(currPos)) {
auto resDep = VertexList.emplace(dep,Vertex());
if(resDep.second) {
resDep.first->second = add_vertex(graph);
VertexIndexList[resDep.first->second] = dep;
if(dirtyCells.insert(dep).second)
workQueue.push_back(dep);
}
// Add edge to graph to signal dependency
add_edge(res.first->second, resDep.first->second, graph);
}
}
// Compute cells
std::list<Vertex> make_order;
// Sort graph topologically to find evaluation order
try {
boost::topological_sort(graph, std::front_inserter(make_order));
// Recompute cells
FC_LOG("recomputing " << getFullName());
for(auto &pos : make_order) {
const auto &addr = VertexIndexList[pos];
FC_LOG(addr.toString());
recomputeCell(addr);
}
} catch (std::exception&) {
for(auto &v : VertexList) {
Cell * cell = cells.getValue(v.first);
// Mark as erroneous
cellErrors.insert(v.first);
if (cell)
cell->setException("Pending computation due to cyclic dependency");
updateProperty(v.first);
updateAlias(v.first);
}
// Try to be more user friendly by finding individual loops
while(dirtyCells.size()) {
std::deque<CellAddress> workQueue;
DependencyList graph;
std::map<CellAddress, Vertex> VertexList;
std::map<Vertex, CellAddress> VertexIndexList;
CellAddress currentAddr = *dirtyCells.begin();
workQueue.push_back(currentAddr);
dirtyCells.erase(dirtyCells.begin());
while (workQueue.size() > 0) {
CellAddress currPos = workQueue.front();
workQueue.pop_front();
// Process cells that depend on the current cell
std::set<CellAddress>::const_iterator i = s.begin();
while (i != s.end()) {
// Insert into map of CellPos -> Index, if it doesn't exist already
if (VertexList.find(*i) == VertexList.end()) {
VertexList[*i] = add_vertex(graph);
VertexIndexList[VertexList[*i]] = *i;
workQueue.push_back(*i);
auto res = VertexList.emplace(currPos,Vertex());
if(res.second) {
res.first->second = add_vertex(graph);
VertexIndexList[res.first->second] = currPos;
}
// Process cells that depend on the current cell
for(auto &dep : providesTo(currPos)) {
auto resDep = VertexList.emplace(dep,Vertex());
if(resDep.second) {
resDep.first->second = add_vertex(graph);
VertexIndexList[resDep.first->second] = dep;
workQueue.push_back(dep);
dirtyCells.erase(dep);
}
// Add edge to graph to signal dependency
add_edge(res.first->second, resDep.first->second, graph);
}
}
std::list<Vertex> make_order;
try {
boost::topological_sort(graph, std::front_inserter(make_order));
} catch (std::exception&) {
// Cycle detected; flag all with errors
std::ostringstream ss;
ss << "Cyclic dependency" << std::endl;
int count = 0;
for(auto &v : VertexList) {
if(count==20)
ss << std::endl;
else
ss << ", ";
ss << v.first.toString();
}
std::string msg = ss.str();
for(auto &v : VertexList) {
Cell * cell = cells.getValue(v.first);
if (cell)
cell->setException(msg.c_str());
}
// Add edge to graph to signal dependency
add_edge(VertexList[currPos], VertexList[*i], graph);
++i;
}
}
// Compute cells
std::list<Vertex> make_order;
// Sort graph topologically to find evaluation order
try {
boost::topological_sort(graph, std::front_inserter(make_order));
// Recompute cells
std::list<Vertex>::const_iterator i = make_order.begin();
while (i != make_order.end()) {
recomputeCell(VertexIndexList[*i]);
++i;
}
}
catch (std::exception&) {
// Cycle detected; flag all with errors
std::map<CellAddress, Vertex>::const_iterator i = VertexList.begin();
while (i != VertexList.end()) {
Cell * cell = cells.getValue(i->first);
// Mark as erroneous
cellErrors.insert(i->first);
if (cell)
cell->setException("Circular dependency.");
updateProperty(i->first);
updateAlias(i->first);
++i;
}
}
}
// Signal update of column widths
@@ -832,16 +883,6 @@ DocumentObjectExecReturn *Sheet::execute(void)
rowHeights.clearDirty();
columnWidths.clearDirty();
std::set<DocumentObject*> ds(cells.getDocDeps());
// Make sure we don't reference ourselves
ds.erase(this);
std::vector<DocumentObject*> dv(ds.begin(), ds.end());
docDeps.setValues(dv);
purgeTouched();
if (cellErrors.size() == 0)
return DocumentObject::StdReturn;
else
@@ -855,12 +896,9 @@ DocumentObjectExecReturn *Sheet::execute(void)
short Sheet::mustExecute(void) const
{
if (cellErrors.size() > 0 || cells.isTouched() || columnWidths.isTouched() || rowHeights.isTouched())
if (cellErrors.size() > 0 || cells.isDirty())
return 1;
else if (cells.getDocDeps().size() == 0)
return 0;
else
return -1;
return DocumentObject::mustExecute();
}
@@ -888,15 +926,6 @@ void Sheet::clear(CellAddress address, bool /*all*/)
cells.clear(address);
// Update dependencies
std::set<DocumentObject*> ds(cells.getDocDeps());
// Make sure we don't reference ourselves
ds.erase(this);
std::vector<DocumentObject*> dv(ds.begin(), ds.end());
docDeps.setValues(dv);
propAddress.erase(prop);
this->removeDynamicProperty(addr.c_str());
}
@@ -993,6 +1022,48 @@ std::vector<std::string> Sheet::getUsedCells() const
return usedCells;
}
void Sheet::updateColumnsOrRows(bool horizontal, int section, int count)
{
auto &hiddenProp = horizontal?hiddenColumns:hiddenRows;
const auto &hidden = hiddenProp.getValues();
auto it = hidden.lower_bound(section);
if(it!=hidden.end()) {
std::set<long> newHidden(hidden.begin(),it);
if(count>0) {
for(;it!=hidden.end();++it)
newHidden.insert(*it + count);
} else {
it = hidden.lower_bound(section-count);
if(it!=hidden.end()) {
for(;it!=hidden.end();++it)
newHidden.insert(*it+count);
}
}
hiddenProp.setValues(newHidden);
}
const auto &sizes = horizontal?columnWidths.getValues():rowHeights.getValues();
auto iter = sizes.lower_bound(section);
if(iter!=sizes.end()) {
std::map<int,int> newsizes(sizes.begin(),iter);
if(count>0) {
for(;iter!=sizes.end();++iter)
newsizes.emplace(iter->first + count, iter->second);
} else {
iter = sizes.lower_bound(section-count);
if(iter!=sizes.end()) {
for(;iter!=sizes.end();++iter)
newsizes.emplace(iter->first+count, iter->second);
}
}
if(horizontal) {
columnWidths.setValues(newsizes);
} else {
rowHeights.setValues(newsizes);
}
}
}
/**
* Insert \a count columns at before column \a col in the spreadsheet.
*
@@ -1003,8 +1074,8 @@ std::vector<std::string> Sheet::getUsedCells() const
void Sheet::insertColumns(int col, int count)
{
cells.insertColumns(col, count);
updateColumnsOrRows(true,col,count);
}
/**
@@ -1018,6 +1089,7 @@ void Sheet::insertColumns(int col, int count)
void Sheet::removeColumns(int col, int count)
{
cells.removeColumns(col, count);
updateColumnsOrRows(true,col,-count);
}
/**
@@ -1031,6 +1103,7 @@ void Sheet::removeColumns(int col, int count)
void Sheet::insertRows(int row, int count)
{
cells.insertRows(row, count);
updateColumnsOrRows(false,row,count);
}
/**
@@ -1044,6 +1117,7 @@ void Sheet::insertRows(int row, int count)
void Sheet::removeRows(int row, int count)
{
cells.removeRows(row, count);
updateColumnsOrRows(false,row,-count);
}
/**
@@ -1202,17 +1276,6 @@ void Sheet::setSpans(CellAddress address, int rows, int columns)
cells.setSpans(address, rows, columns);
}
/**
* @brief Called when a document object is renamed.
* @param docObj Renamed document object.
*/
void Sheet::renamedDocumentObject(const DocumentObject * docObj)
{
cells.renamedDocumentObject(docObj);
cells.touch();
}
/**
* @brief Called when alias \a alias at \a address is removed.
* @param address Address of alias.
@@ -1243,13 +1306,11 @@ std::set<std::string> Sheet::dependsOn(CellAddress address) const
void Sheet::providesTo(CellAddress address, std::set<std::string> & result) const
{
const char * docName = getDocument()->Label.getValue();
const char * docObjName = getNameInDocument();
std::string fullName = std::string(docName) + "#" + std::string(docObjName) + "." + address.toString();
std::set<CellAddress> tmpResult = cells.getDeps(fullName);
std::string fullName = getFullName() + ".";
std::set<CellAddress> tmpResult = cells.getDeps(fullName + address.toString());
for (std::set<CellAddress>::const_iterator i = tmpResult.begin(); i != tmpResult.end(); ++i)
result.insert(std::string(docName) + "#" + std::string(docObjName) + "." + i->toString());
result.insert(fullName + i->toString());
}
/**
@@ -1258,38 +1319,18 @@ void Sheet::providesTo(CellAddress address, std::set<std::string> & result) cons
* @param result Set of links.
*/
void Sheet::providesTo(CellAddress address, std::set<CellAddress> & result) const
std::set<CellAddress> Sheet::providesTo(CellAddress address) const
{
const char * docName = getDocument()->Label.getValue();
const char * docObjName = getNameInDocument();
std::string fullName = std::string(docName) + "#" + std::string(docObjName) + "." + address.toString();
result = cells.getDeps(fullName);
return cells.getDeps(getFullName()+"."+address.toString());
}
void Sheet::onDocumentRestored()
{
cells.resolveAll();
execute();
}
/**
* @brief Slot called when a document is relabelled.
* @param document Relabelled document.
*/
void Sheet::onRelabledDocument(const Document &document)
{
cells.renamedDocument(&document);
cells.purgeTouched();
}
/**
* @brief Unimplemented.
* @param document
*/
void Sheet::onRenamedDocument(const Document & /*document*/)
{
auto ret = execute();
if(ret!=DocumentObject::StdReturn) {
FC_ERR("Failed to restore " << getFullName() << ": " << ret->Why);
delete ret;
}
}
/**
@@ -1299,6 +1340,11 @@ void Sheet::onRenamedDocument(const Document & /*document*/)
void Sheet::observeDocument(Document * document)
{
// observer is no longer required as PropertySheet is now derived from
// PropertyLinkBase and will handle all the link related behavior
#if 1
(void)document;
#else
ObserverMap::const_iterator it = observers.find(document->getName());
if (it != observers.end()) {
@@ -1311,6 +1357,7 @@ void Sheet::observeDocument(Document * document)
observers[document->getName()] = observer;
}
#endif
}
void Sheet::renameObjectIdentifiers(const std::map<ObjectIdentifier, ObjectIdentifier> &paths)
@@ -1319,6 +1366,45 @@ void Sheet::renameObjectIdentifiers(const std::map<ObjectIdentifier, ObjectIdent
cells.renameObjectIdentifiers(paths);
}
std::string Sheet::getRow(int offset) const {
if(currentRow < 0)
throw Base::RuntimeError("No current row");
int row = currentRow + offset;
if(row<0 || row>CellAddress::MAX_ROWS)
throw Base::ValueError("Out of range");
return std::to_string(row+1);
}
std::string Sheet::getColumn(int offset) const {
if(currentCol < 0)
throw Base::RuntimeError("No current column");
int col = currentCol + offset;
if(col<0 || col>CellAddress::MAX_COLUMNS)
throw Base::ValueError("Out of range");
if (col < 26) {
char txt[2];
txt[0] = (char)('A' + col);
txt[1] = 0;
return txt;
}
col -= 26;
char txt[3];
txt[0] = (char)('A' + (col / 26));
txt[1] = (char)('A' + (col % 26));
txt[2] = 0;
return txt;
}
void Sheet::onChanged(const App::Property *prop) {
if(!isRestoring() && getDocument() && !getDocument()->isPerformingTransaction()) {
if(prop == &PythonMode)
cells.setDirty();
}
App::DocumentObject::onChanged(prop);
}
///////////////////////////////////////////////////////////////////////////////
TYPESYSTEM_SOURCE(Spreadsheet::PropertySpreadsheetQuantity, App::PropertyQuantity);

View File

@@ -154,20 +154,26 @@ public:
PyObject *getPyObject();
App::Property *getPropertyByName(const char *name) const;
PropertySheet *getCells() { return &cells; }
const char* getPropertyName(const App::Property* prop) const;
App::Property *getPropertyByName(const char *name) const;
virtual short mustExecute(void) const;
App::DocumentObjectExecReturn *execute(void);
void getCellAddress(const App::Property *prop, App::CellAddress &address);
bool getCellAddress(const App::Property *prop, App::CellAddress &address);
std::map<int, int> getColumnWidths() const;
std::map<int, int> getRowHeights() const;
std::string getRow(int offset=0) const;
std::string getColumn(int offset=0) const;
void touchCells(App::Range range);
// Signals
boost::signals2::signal<void (App::CellAddress)> cellUpdated;
@@ -178,70 +184,20 @@ public:
boost::signals2::signal<void (int, int)> rowHeightChanged;
/** @name Access properties */
//@{
App::Property* addDynamicProperty(
const char* type, const char* name=0,
const char* group=0, const char* doc=0,
short attr=0, bool ro=false, bool hidden=false) {
return props.addDynamicProperty(type, name, group, doc, attr, ro, hidden);
}
virtual bool removeDynamicProperty(const char* name) {
App::DocumentObject::onAboutToRemoveProperty(name);
return props.removeDynamicProperty(name);
}
std::vector<std::string> getDynamicPropertyNames() const {
return props.getDynamicPropertyNames();
}
App::Property *getDynamicPropertyByName(const char* name) const {
return props.getDynamicPropertyByName(name);
}
virtual void addDynamicProperties(const App::PropertyContainer* cont) {
return props.addDynamicProperties(cont);
}
/// get all properties of the class (including properties of the parent)
virtual void getPropertyList(std::vector<App::Property*> &List) const {
props.getPropertyList(List);
}
/// get all properties of the class (including parent)
void getPropertyMap(std::map<std::string,App::Property*> &Map) const {
props.getPropertyMap(Map);
}
short getPropertyType(const App::Property *prop) const {
return props.getPropertyType(prop);
}
/// get the group of a property
const char* getPropertyGroup(const App::Property* prop) const {
return props.getPropertyGroup(prop);
}
/// get the documentation of a property
const char* getPropertyDocumentation(const App::Property* prop) const {
return props.getPropertyDocumentation(prop);
}
/// get the name of a property
virtual const char* getName(const App::Property* prop) const {
return props.getPropertyName(prop);
}
//@}
void observeDocument(App::Document *document);
virtual void renameObjectIdentifiers(const std::map<App::ObjectIdentifier, App::ObjectIdentifier> & paths);
protected:
void providesTo(App::CellAddress address, std::set<App::CellAddress> & result) const;
virtual void onChanged(const App::Property *prop);
void updateColumnsOrRows(bool horizontal, int section, int count) ;
std::set<App::CellAddress> providesTo(App::CellAddress address) const;
void onDocumentRestored();
void onRelabledDocument(const App::Document & document);
void onRenamedDocument(const App::Document & document);
void recomputeCell(App::CellAddress p);
App::Property *getProperty(App::CellAddress key) const;
@@ -258,8 +214,6 @@ protected:
App::Property *setQuantityProperty(App::CellAddress key, double value, const Base::Unit &unit);
void renamedDocumentObject(const App::DocumentObject * docObj);
void aliasRemoved(App::CellAddress address, const std::string &alias);
void removeAliases();
@@ -267,7 +221,7 @@ protected:
virtual void onSettingDocument();
/* Properties for used cells */
App::DynamicProperty props;
App::DynamicProperty &props;
/* Mapping of properties to cell position */
std::map<const App::Property*, App::CellAddress > propAddress;
@@ -289,15 +243,12 @@ protected:
/* Row heights */
PropertyRowHeights rowHeights;
/* Dependencies to other documents */
App::PropertyLinkList docDeps;
/* Document observers to track changes to external properties */
typedef std::map<std::string, SheetObserver* > ObserverMap;
ObserverMap observers;
boost::signals2::scoped_connection onRelabledDocumentConnection;
boost::signals2::scoped_connection onRenamedDocumentConnection;
int currentRow = -1;
int currentCol = -1;
friend class SheetObserver;