- Self-reference bug

- Refactoring/clean-up of code
- Dependency tracking of aliased cells
- Various resolution errors
- Rewriting of ranges when columns/rows are inserted/removed
- References to aliases keep their units.
This commit is contained in:
Eivind Kvedalen 2015-03-04 22:14:35 +01:00 committed by wmayer
parent 1b7c0e2a51
commit 3743008cda
20 changed files with 382 additions and 178 deletions

View File

@ -31,6 +31,7 @@ void SpreadsheetExport initSpreadsheet() {
(void) Py_InitModule("Spreadsheet", Spreadsheet_methods); /* mod name, table ptr */
Base::Console().Log("Loading Spreadsheet module... done\n");
Spreadsheet::PropertySpreadsheetQuantity::init();
Spreadsheet::PropertyColumnWidths::init();
Spreadsheet::PropertyRowHeights::init();
Spreadsheet::PropertySheet::init();

View File

@ -395,8 +395,20 @@ void Cell::setAlias(const std::string &n)
if (alias != n) {
PropertySheet::Signaller signaller(*owner);
owner->revAliasProp.erase(alias);
alias = n;
// Update owner
if (alias != "") {
owner->aliasProp[address] = n;
owner->revAliasProp[n] = address;
}
else
owner->aliasProp.erase(address);
setUsed(ALIAS_SET, !alias.empty());
}
}
@ -590,7 +602,7 @@ void Cell::save(Base::Writer &writer) const
writer.Stream() << writer.ind() << "<Cell ";
writer.Stream() << "address=\"" << addressToString(address) << "\" ";
writer.Stream() << "address=\"" << address.toString() << "\" ";
if (isUsed(EXPRESSION_SET)) {
std::string content;
@ -663,7 +675,7 @@ bool Cell::isUsed() const
void Cell::visit(ExpressionVisitor &v)
{
if (expression)
v.visit(expression);
expression->visit(v);
}
/**

View File

@ -116,6 +116,8 @@ public:
void visit(ExpressionVisitor & v);
CellAddress getAddress() const { return address; }
/* Alignment */
static const int ALIGNMENT_LEFT;
static const int ALIGNMENT_HCENTER;

View File

@ -176,7 +176,7 @@ std::string Path::getPythonAccessor() const
const Property * prop = getProperty();
if (!prop)
throw Exception("Property not found");
throw Exception(std::string("Property '") + getPropertyName() + std::string("' not found."));
const DocumentObject * docObj = freecad_dynamic_cast<DocumentObject>(prop->getContainer());
@ -1309,6 +1309,16 @@ Document * Path::getDocument() const
return doc;
}
const DocumentObject *Path::getDocumentObject() const
{
const App::Document * doc = getDocument();
if (!doc)
return 0;
return getDocumentObject(doc, documentObjectName);
}
const Property *Path::getProperty() const
{
const App::Document * doc = getDocument();
@ -1346,7 +1356,7 @@ const Property * VariableExpression::getProperty() const
if (prop)
return prop;
else
throw Base::Exception("Property not found.");
throw Base::Exception(std::string("Property '") + var.getPropertyName() + std::string("' not found."));
}
/**
@ -1785,6 +1795,11 @@ Expression *RangeExpression::simplify() const
return copy();
}
void RangeExpression::setRange(const Range &r)
{
range = r;
}
}
/**

View File

@ -158,6 +158,8 @@ public:
App::Document *getDocument() const;
const App::DocumentObject *getDocumentObject() const;
protected:
const App::DocumentObject *getDocumentObject(const App::Document *doc, const std::string &name) const;
@ -367,6 +369,8 @@ public:
Range getRange() const { return range; }
void setRange(const Range & r);
protected:
Range range;
};

View File

@ -155,3 +155,14 @@ PyObject *PropertyColumnWidths::getPyObject()
}
return Py::new_reference_to(PythonObject);
}
void PropertyColumnWidths::clear()
{
std::map<int, int>::const_iterator i = begin();
while (i != end()) {
dirty.insert(i->first);
++i;
}
std::map<int,int>::clear();
}

View File

@ -35,8 +35,6 @@ class SpreadsheetExport PropertyColumnWidths : public App::Property, std::map<in
public:
PropertyColumnWidths();
PropertyColumnWidths(const PropertyColumnWidths & other);
void setValue() { }
void setValue(int col, int width);
@ -66,11 +64,15 @@ public:
PyObject *getPyObject(void);
void clear();
static const int defaultWidth;
static const int defaultHeaderWidth;
private:
PropertyColumnWidths(const PropertyColumnWidths & other);
std::set<int> dirty;
Py::Object PythonObject;

View File

@ -148,3 +148,14 @@ PyObject *PropertyRowHeights::getPyObject()
}
return Py::new_reference_to(PythonObject);
}
void PropertyRowHeights::clear()
{
std::map<int, int>::const_iterator i = begin();
while (i != end()) {
dirty.insert(i->first);
++i;
}
std::map<int,int>::clear();
}

View File

@ -35,8 +35,6 @@ class SpreadsheetExport PropertyRowHeights : public App::Property, std::map<int,
public:
PropertyRowHeights();
PropertyRowHeights(const PropertyRowHeights & other);
void setValue() { }
void setValue(int row, int height);
@ -68,8 +66,12 @@ public:
static const int defaultHeight;
void clear();
private:
PropertyRowHeights(const PropertyRowHeights & other);
std::set<int> dirty;
Py::Object PythonObject;

View File

@ -149,6 +149,8 @@ void PropertySheet::clear()
propertyNameToCellMap.clear();
documentObjectToCellMap.clear();
docDeps.clear();
aliasProp.clear();
revAliasProp.clear();
}
Cell *PropertySheet::getValue(CellAddress key)
@ -429,8 +431,30 @@ void PropertySheet::setDisplayUnit(CellAddress address, const std::string &unit)
void PropertySheet::setAlias(CellAddress address, const std::string &alias)
{
assert(nonNullCellAt(address) != 0);
nonNullCellAt(address)->setAlias(alias);
Cell * cell = nonNullCellAt(address);
assert(cell != 0);
/* Mark cells depending on this cell dirty; they need to be resolved when an alias changes or disappears */
const char * docName = owner->getDocument()->Label.getValue();
const char * docObjName = owner->getNameInDocument();
std::string fullName = std::string(docName) + "#" + std::string(docObjName) + "." + address.toString();
std::map<std::string, std::set< CellAddress > >::const_iterator j = propertyNameToCellMap.find(fullName);
if (j != propertyNameToCellMap.end()) {
std::set< CellAddress >::const_iterator k = j->second.begin();
while (k != j->second.end()) {
setDirty(*k);
++k;
}
}
std::string oldAlias;
if (cell->getAlias(oldAlias))
owner->aliasRemoved(address, oldAlias);
cell->setAlias(alias);
}
void PropertySheet::setComputedUnit(CellAddress address, const Base::Unit &unit)
@ -464,6 +488,13 @@ void PropertySheet::clear(CellAddress address)
// Mark as dirty
dirty.insert(i->first);
// Remove alias if it exists
std::map<CellAddress, std::string>::iterator j = aliasProp.find(address);
if (j != aliasProp.end()) {
revAliasProp.erase(j->second);
aliasProp.erase(j);
}
// Erase from internal struct
data.erase(i);
@ -524,13 +555,15 @@ public:
bool changed() const { return mChanged; }
void visit(Expression * node) {
VariableExpression *expr = freecad_dynamic_cast<VariableExpression>(node);
VariableExpression *varExpr = freecad_dynamic_cast<VariableExpression>(node);
RangeExpression *rangeExpr = freecad_dynamic_cast<RangeExpression>(node);
if (expr) {
if (varExpr) {
static const boost::regex e("(\\${0,1})([A-Za-z]+)(\\${0,1})([0-9]+)");
boost::cmatch cm;
if (boost::regex_match(expr->name().c_str(), cm, e)) {
if (boost::regex_match(varExpr->name().c_str(), cm, e)) {
const boost::sub_match<const char *> colstr = cm[2];
const boost::sub_match<const char *> rowstr = cm[4];
int thisRow, thisCol;
@ -541,11 +574,26 @@ public:
if (thisRow >= mRow || thisCol >= mCol) {
thisRow += mRowCount;
thisCol += mColCount;
expr->setName(columnName(thisCol) + rowName(thisRow));
varExpr->setName(columnName(thisCol) + rowName(thisRow));
mChanged = true;
}
}
}
else if (rangeExpr) {
Range r = rangeExpr->getRange();
CellAddress from(r.from());
CellAddress to(r.to());
if (from.row() >= mRow || from.col() >= mCol) {
from = CellAddress(from.row() + mRowCount, from.col() + mColCount);
mChanged = true;
}
if (to.row() >= mRow || to.col() >= mCol) {
to = CellAddress(to.row() + mRowCount, to.col() + mColCount);
mChanged = true;
}
rangeExpr->setRange(Range(from, to));
}
}
private:
int mRow;
@ -832,13 +880,13 @@ void PropertySheet::addDependencies(CellAddress key)
std::set<Path>::const_iterator i = expressionDeps.begin();
while (i != expressionDeps.end()) {
const Property * prop = (*i).getProperty();
DocumentObject * docObj = prop ? freecad_dynamic_cast<DocumentObject>(prop->getContainer()) : 0;
Document * doc = docObj ? docObj->getDocument() : 0;
const Property * prop = i->getProperty();
const DocumentObject * docObj = i->getDocumentObject();
Document * doc = i->getDocument();
std::string docName = doc ? doc->Label.getValue() : (*i).getDocumentName().getString();
std::string docObjName = docName + "#" + (docObj ? docObj->getNameInDocument() : (*i).getDocumentObjectName().getString());
std::string propName = docObjName + "." + (*i).getPropertyName();
std::string docName = doc ? doc->Label.getValue() : i->getDocumentName().getString();
std::string docObjName = docName + "#" + (docObj ? docObj->getNameInDocument() : i->getDocumentObjectName().getString());
std::string propName = docObjName + "." + i->getPropertyName();
if (!prop)
cell->setResolveException("Unresolved dependency");
@ -857,6 +905,19 @@ void PropertySheet::addDependencies(CellAddress key)
propertyNameToCellMap[propName].insert(key);
cellToPropertyNameMap[key].insert(propName);
// Also an alias?
if (docObj == owner) {
std::map<std::string, CellAddress>::const_iterator j = revAliasProp.find(i->getPropertyName());
if (j != revAliasProp.end()) {
propName = docObjName + "." + j->second.toString();
// Insert into maps
propertyNameToCellMap[propName].insert(key);
cellToPropertyNameMap[key].insert(propName);
}
}
documentObjectToCellMap[docObjName].insert(key);
cellToDocumentObjectMap[key].insert(docObjName);
@ -874,50 +935,47 @@ void PropertySheet::addDependencies(CellAddress key)
void PropertySheet::removeDependencies(CellAddress key)
{
std::map<CellAddress, std::set< std::string > >::iterator i1 = cellToPropertyNameMap.find(key);
std::set< std::string >::iterator j;
/* Remove from Property <-> Key maps */
if (i1 == cellToPropertyNameMap.end())
return;
std::map<CellAddress, std::set< std::string > >::iterator i1 = cellToPropertyNameMap.find(key);
j = i1->second.begin();
if (i1 != cellToPropertyNameMap.end()) {
std::set< std::string >::const_iterator j = i1->second.begin();
while (j != i1->second.end()) {
std::map<std::string, std::set< CellAddress > >::iterator k = propertyNameToCellMap.find(*j);
while (j != i1->second.end()) {
std::map<std::string, std::set< CellAddress > >::iterator k = propertyNameToCellMap.find(*j);
assert(k != propertyNameToCellMap.end());
assert(k != propertyNameToCellMap.end());
k->second.erase(key);
++j;
k->second.erase(key);
++j;
}
cellToPropertyNameMap.erase(i1);
}
cellToPropertyNameMap.erase(i1);
/* Remove from DocumentObject <-> Key maps */
std::map<CellAddress, std::set< std::string > >::iterator i2 = cellToDocumentObjectMap.find(key);
if (i2 == cellToDocumentObjectMap.end())
return;
if (i2 != cellToDocumentObjectMap.end()) {
std::set< std::string >::const_iterator j = i2->second.begin();
j = i2->second.begin();
while (j != i2->second.end()) {
std::map<std::string, std::set< CellAddress > >::iterator k = documentObjectToCellMap.find(*j);
while (j != i2->second.end()) {
std::map<std::string, std::set< CellAddress > >::iterator k = documentObjectToCellMap.find(*j);
assert(k != documentObjectToCellMap.end());
assert(k != documentObjectToCellMap.end());
k->second.erase(key);
k->second.erase(key);
if (k->second.size() == 0)
documentObjectToCellMap.erase(*j);
if (k->second.size() == 0)
documentObjectToCellMap.erase(*j);
++j;
}
++j;
cellToDocumentObjectMap.erase(i2);
}
cellToDocumentObjectMap.erase(i2);
}
/**
@ -1028,6 +1086,11 @@ void PropertySheet::renamedDocument(const Document * doc)
}
}
void PropertySheet::documentSet()
{
documentName[owner->getDocument()] = owner->getDocument()->Label.getValue();
}
void PropertySheet::recomputeDependants(const DocumentObject *docObj)
{
const char * docName = docObj->getDocument()->Label.getValue();

View File

@ -43,8 +43,6 @@ public:
PropertySheet(Sheet * _owner = 0);
PropertySheet(const PropertySheet & other);
~PropertySheet();
virtual Property *Copy(void) const;
@ -145,8 +143,12 @@ public:
void renamedDocument(const App::Document *doc);
void documentSet();
private:
PropertySheet(const PropertySheet & other);
friend class Signaller;
friend class SheetObserver;
@ -211,6 +213,12 @@ private:
/* Name of documents, used for renaming */
std::map<const App::Document*, std::string> documentName;
/* Mapping of cell position to alias property */
std::map<CellAddress, std::string> aliasProp;
/* Mapping of alias property to cell position */
std::map<std::string, CellAddress> revAliasProp;
int signalCounter;
Py::Object PythonObject;

View File

@ -66,6 +66,16 @@ Range::Range(int _row_begin, int _col_begin, int _row_end, int _col_end)
{
}
Range::Range(const CellAddress &from, const CellAddress &to)
: row_curr(from.row())
, col_curr(from.col())
, row_begin(from.row())
, col_begin(from.col())
, row_end(to.row())
, col_end(to.col())
{
}
bool Range::next()
{
if (row_curr < row_end) {

View File

@ -45,6 +45,8 @@ public:
Range(int _row_begin, int _col_begin, int _row_end, int _col_end);
Range(const CellAddress & from, const CellAddress & to);
bool next();
/** Current row */
@ -60,17 +62,17 @@ public:
inline CellAddress to() const { return CellAddress(row_end, col_end); }
/** Start of range as a string */
inline std::string fromCellString() const { return addressToString(CellAddress(row_begin, col_begin)); }
inline std::string fromCellString() const { return CellAddress(row_begin, col_begin).toString(); }
/** End of range as a string */
inline std::string toCellString() const { return addressToString(CellAddress(row_end, col_end)); }
inline std::string toCellString() const { return CellAddress(row_end, col_end).toString(); }
/** Current cell as a string */
inline std::string address() const { return addressToString(CellAddress(row_curr, col_curr)); }
inline std::string address() const { return CellAddress(row_curr, col_curr).toString(); }
/** The raneg as a string */
inline std::string rangeString() const {
return addressToString(CellAddress(row_begin, col_begin)) + ":" + addressToString(CellAddress(row_end, col_end));
return CellAddress(row_begin, col_begin).toString() + ":" + CellAddress(row_end, col_end).toString();
}
CellAddress operator*() const { return CellAddress(row_curr, col_curr); }

View File

@ -110,9 +110,15 @@ void Sheet::clearAll()
props.removeDynamicProperty((*i).c_str());
propAddress.clear();
cellErrors.clear();
columnWidths.clear();
rowHeights.clear();
removedAliases.clear();
docDeps.setValues(std::vector<DocumentObject*>());
for (ObserverMap::iterator i = observers.begin(); i != observers.end(); ++i)
delete i->second;
observers.clear();
}
/**
@ -315,19 +321,6 @@ Cell *Sheet::getNewCell(CellAddress address)
return cell;
}
/**
* Convert given \a key address to string.
*
* @param key Address of cell.
*
* @returns Address given as a string.
*/
std::string Sheet::toAddress(CellAddress key)
{
return Spreadsheet::addressToString(key);
}
/**
* Set cell given by \a address to \a contents.
*
@ -400,7 +393,7 @@ PyObject *Sheet::getPyObject(void)
Property * Sheet::getProperty(CellAddress key) const
{
return props.getDynamicPropertyByName(toAddress(key).c_str());
return props.getDynamicPropertyByName(key.toString().c_str());
}
Property * Sheet::getProperty(const char * addr) const
@ -442,6 +435,22 @@ void Sheet::setPosition(CellAddress address)
currColumn.purgeTouched();
}
void Sheet::removeAliases()
{
std::map<CellAddress, std::string>::iterator i = removedAliases.begin();
while (i != removedAliases.end()) {
props.removeDynamicProperty(i->second.c_str());
++i;
}
removedAliases.clear();
}
void Sheet::onSettingDocument()
{
cells.documentSet();
}
/**
* Set the property for cell \key to a PropertyFloat with the value \a value.
* If the Property exists, but of wrong type, the previous Property is destroyed and recreated as the correct type.
@ -453,21 +462,15 @@ void Sheet::setPosition(CellAddress address)
Property * Sheet::setFloatProperty(CellAddress key, double value)
{
Property * prop = props.getPropertyByName(toAddress(key).c_str());
Property * prop = props.getPropertyByName(key.toString().c_str());
PropertyFloat * floatProp;
if (!prop || prop->getTypeId() != PropertyFloat::getClassTypeId()) {
if (prop) {
props.removeDynamicProperty(toAddress(key).c_str());
props.removeDynamicProperty(key.toString().c_str());
propAddress.erase(prop);
std::map<CellAddress, App::Property*>::iterator i = aliasProp.find(key);
if (i != aliasProp.end()) {
props.removeDynamicProperty(props.getPropertyName(i->second));
aliasProp.erase(i);
}
}
floatProp = freecad_dynamic_cast<PropertyFloat>(props.addDynamicProperty("App::PropertyFloat", toAddress(key).c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true));
floatProp = freecad_dynamic_cast<PropertyFloat>(props.addDynamicProperty("App::PropertyFloat", key.toString().c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true));
floatProp->StatusBits.set(3);
}
else
@ -491,26 +494,20 @@ Property * Sheet::setFloatProperty(CellAddress key, double value)
Property * Sheet::setQuantityProperty(CellAddress key, double value, const Base::Unit & unit)
{
Property * prop = props.getPropertyByName(toAddress(key).c_str());
PropertyQuantity * quantityProp;
Property * prop = props.getPropertyByName(key.toString().c_str());
PropertySpreadsheetQuantity * quantityProp;
if (!prop || prop->getTypeId() != PropertyQuantity::getClassTypeId()) {
if (!prop || prop->getTypeId() != PropertySpreadsheetQuantity::getClassTypeId()) {
if (prop) {
props.removeDynamicProperty(toAddress(key).c_str());
props.removeDynamicProperty(key.toString().c_str());
propAddress.erase(prop);
std::map<CellAddress, App::Property*>::iterator i = aliasProp.find(key);
if (i != aliasProp.end()) {
props.removeDynamicProperty(props.getPropertyName(i->second));
aliasProp.erase(i);
}
}
Property * p = props.addDynamicProperty("App::PropertyQuantity", toAddress(key).c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true);
quantityProp = freecad_dynamic_cast<PropertyQuantity>(p);
Property * p = props.addDynamicProperty("Spreadsheet::PropertySpreadsheetQuantity", key.toString().c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true);
quantityProp = freecad_dynamic_cast<PropertySpreadsheetQuantity>(p);
quantityProp->StatusBits.set(3);
}
else
quantityProp = static_cast<PropertyQuantity*>(prop);
quantityProp = static_cast<PropertySpreadsheetQuantity*>(prop);
propAddress[quantityProp] = key;
quantityProp->setValue(value);
@ -532,21 +529,15 @@ Property * Sheet::setQuantityProperty(CellAddress key, double value, const Base:
Property * Sheet::setStringProperty(CellAddress key, const std::string & value)
{
Property * prop = props.getPropertyByName(toAddress(key).c_str());
Property * prop = props.getPropertyByName(key.toString().c_str());
PropertyString * stringProp = freecad_dynamic_cast<PropertyString>(prop);
if (!stringProp) {
if (prop) {
props.removeDynamicProperty(toAddress(key).c_str());
props.removeDynamicProperty(key.toString().c_str());
propAddress.erase(prop);
std::map<CellAddress, App::Property*>::iterator i = aliasProp.find(key);
if (i != aliasProp.end()) {
props.removeDynamicProperty(props.getPropertyName(i->second));
aliasProp.erase(i);
}
}
stringProp = freecad_dynamic_cast<PropertyString>(props.addDynamicProperty("App::PropertyString", toAddress(key).c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true));
stringProp = freecad_dynamic_cast<PropertyString>(props.addDynamicProperty("App::PropertyString", key.toString().c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true));
stringProp->StatusBits.set(3);
}
@ -556,6 +547,35 @@ Property * Sheet::setStringProperty(CellAddress key, const std::string & value)
return stringProp;
}
void Sheet::updateAlias(CellAddress key)
{
std::string alias;
Property * prop = props.getDynamicPropertyByName(key.toString().c_str());
if (!prop)
return;
Cell * cell = getCell(key);
if (cell && cell->getAlias(alias)) {
App::Property * aliasProp = props.getDynamicPropertyByName(alias.c_str());
/* Update or create alias? */
if (aliasProp) {
// Type of alias and property must always be the same
if (aliasProp->getTypeId() != prop->getTypeId()) {
props.removeDynamicProperty(alias.c_str());
aliasProp = 0;
}
}
if (!aliasProp)
aliasProp = props.addDynamicProperty(prop->getTypeId().getName(), alias.c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true);
aliasProp->Paste(*prop);
}
}
/**
* Update the Propery given by \a key. This will also eventually trigger recomputations of cells depending on \a key.
*
@ -566,10 +586,8 @@ Property * Sheet::setStringProperty(CellAddress key, const std::string & value)
void Sheet::updateProperty(CellAddress key)
{
const Property * prop;
const char * aliasType = 0;
Cell * cell = getCell(key);
std::string alias;
if (cell != 0) {
Expression * output;
@ -590,40 +608,19 @@ void Sheet::updateProperty(CellAddress key)
/* Eval returns either NumberExpression or StringExpression objects */
if (freecad_dynamic_cast<NumberExpression>(output)) {
NumberExpression * number = static_cast<NumberExpression*>(output);
if (number->getUnit().isEmpty()) {
if (number->getUnit().isEmpty())
prop = setFloatProperty(key, number->getValue());
aliasType = "App::PropertyFloat";
}
else {
else
prop = setQuantityProperty(key, number->getValue(), number->getUnit());
aliasType = "App::PropertyQuantity";
}
}
else {
else
prop = setStringProperty(key, freecad_dynamic_cast<StringExpression>(output)->getText().c_str());
aliasType = "App::PropertyString";
}
delete output;
}
else
clear(key);
if (cell && cell->getAlias(alias)) {
/* Update or create alias? */
if (aliasProp.find(key) == aliasProp.end())
aliasProp[key] = props.addDynamicProperty(aliasType, alias.c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true);
aliasProp[key]->Paste(*prop);
}
else {
/* Remove alias? */
std::map<CellAddress, App::Property*>::iterator i = aliasProp.find(key);
if (i != aliasProp.end()) {
props.removeDynamicProperty(props.getPropertyName(i->second));
aliasProp.erase(i);
}
}
cellUpdated(key);
}
@ -661,7 +658,7 @@ 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 + "." + toAddress(p);
std::string name = docName + "#" + docObjName + "." + p.toString();
try {
if (cell) {
@ -683,6 +680,8 @@ void Sheet::recomputeCell(CellAddress p)
cellErrors.insert(p);
}
updateAlias(p);
if (!cell || cell->spansChanged())
cellSpanChanged(p);
}
@ -694,6 +693,9 @@ void Sheet::recomputeCell(CellAddress p)
App::DocumentObjectExecReturn *Sheet::execute(void)
{
// Remove all aliases first
removeAliases();
// Get dirty cells that we have to recompute
std::set<CellAddress> dirtyCells = cells.getDirty();
@ -769,6 +771,8 @@ App::DocumentObjectExecReturn *Sheet::execute(void)
if (cell)
cell->setException("Circular dependency.");
updateProperty(i->first);
updateAlias(i->first);
++i;
}
}
@ -797,6 +801,10 @@ App::DocumentObjectExecReturn *Sheet::execute(void)
currColumn.purgeTouched();
std::set<App::DocumentObject*> ds(cells.getDocDeps());
// Make sure we don't reference ourselves
ds.erase(this);
std::vector<App::DocumentObject*> dv(ds.begin(), ds.end());
docDeps.setValues(dv);
@ -837,9 +845,15 @@ short Sheet::mustExecute(void) const
void Sheet::clear(CellAddress address, bool all)
{
std::string addr = toAddress(address);
Cell * cell = getCell(address);
std::string addr = address.toString();
Property * prop = props.getDynamicPropertyByName(addr.c_str());
// Remove alias, if defined
std::string aliasStr;
if (cell && cell->getAlias(aliasStr))
props.removeDynamicProperty(aliasStr.c_str());
cells.clear(address);
propAddress.erase(prop);
@ -911,7 +925,7 @@ std::vector<std::string> Sheet::getUsedCells() const
std::set<CellAddress> usedSet = cells.getUsedCells();
for (std::set<CellAddress>::const_iterator i = usedSet.begin(); i != usedSet.end(); ++i)
usedCells.push_back(toAddress(*i));
usedCells.push_back(i->toString());
return usedCells;
}
@ -1031,6 +1045,11 @@ void Sheet::renamedDocumentObject(const App::DocumentObject * docObj)
cells.touch();
}
void Sheet::aliasRemoved(CellAddress address, const std::string & alias)
{
removedAliases[address] = alias;
}
std::set<std::string> Sheet::dependsOn(CellAddress address) const
{
return cells.getDeps(address);
@ -1040,18 +1059,18 @@ void Sheet::providesTo(CellAddress address, std::set<std::string> & result) cons
{
const char * docName = getDocument()->Label.getValue();
const char * docObjName = getNameInDocument();
std::string fullName = std::string(docName) + "#" + std::string(docObjName) + "." + toAddress(address);
std::string fullName = std::string(docName) + "#" + std::string(docObjName) + "." + address.toString();
std::set<CellAddress> tmpResult = cells.getDeps(fullName);
for (std::set<CellAddress>::const_iterator i = tmpResult.begin(); i != tmpResult.end(); ++i)
result.insert(std::string(docName) + "#" + std::string(docObjName) + "." + toAddress(*i));
result.insert(std::string(docName) + "#" + std::string(docObjName) + "." + i->toString());
}
void Sheet::providesTo(CellAddress address, std::set<CellAddress> & result) const
{
const char * docName = getDocument()->Label.getValue();
const char * docObjName = getNameInDocument();
std::string fullName = std::string(docName) + "#" + std::string(docObjName) + "." + toAddress(address);
std::string fullName = std::string(docName) + "#" + std::string(docObjName) + "." + address.toString();
result = cells.getDeps(fullName);
}
@ -1086,3 +1105,23 @@ void Sheet::observeDocument(Document * document)
observers[document->getName()] = observer;
}
}
TYPESYSTEM_SOURCE(Spreadsheet::PropertySpreadsheetQuantity, App::PropertyQuantity);
Property *PropertySpreadsheetQuantity::Copy() const
{
PropertySpreadsheetQuantity * obj = new PropertySpreadsheetQuantity();
obj->_dValue = _dValue;
obj->_Unit = _Unit;
return obj;
}
void PropertySpreadsheetQuantity::Paste(const Property &from)
{
aboutToSetValue();
_dValue = static_cast<const PropertySpreadsheetQuantity*>(&from)->_dValue;
_Unit = static_cast<const PropertySpreadsheetQuantity*>(&from)->_Unit;
hasSetValue();
}

View File

@ -52,10 +52,25 @@ class Expression;
class Range;
class SheetObserver;
/** Spreadsheet quantity property
* This is a property for quantities, and unlike its ancestor implements
* Copy() and Paste() methods. It is used by the spreadsheet to
* create aliases in a generic way.
*/
class SpreadsheetExport PropertySpreadsheetQuantity : public App::PropertyQuantity
{
TYPESYSTEM_HEADER();
public:
PropertySpreadsheetQuantity(void){}
virtual ~PropertySpreadsheetQuantity(){}
virtual Property *Copy(void) const;
virtual void Paste(const Property &from);
};
class SpreadsheetExport Sheet : public App::DocumentObject
{
PROPERTY_HEADER(Sheet::Sheet);
PROPERTY_HEADER(Spreadsheet::Sheet);
public:
@ -220,6 +235,8 @@ protected:
App::Property *getProperty(const char * addr) const;
void updateAlias(CellAddress key);
void updateProperty(CellAddress key);
App::Property *setStringProperty(CellAddress key, const std::string & value) ;
@ -228,20 +245,24 @@ protected:
App::Property *setQuantityProperty(CellAddress key, double value, const Base::Unit &unit);
static std::string toAddress(CellAddress key);
void moveCell(CellAddress currPos, CellAddress newPos);
void renamedDocumentObject(const App::DocumentObject * docObj);
void aliasRemoved(CellAddress address, const std::string &alias);
void removeAliases();
virtual void onSettingDocument();
/* Properties for used cells */
App::DynamicProperty props;
/* Mapping of properties to cell position */
std::map<const App::Property*, CellAddress > propAddress;
/* Mapping of cell position to alias property */
std::map<CellAddress, App::Property*> aliasProp;
/* Removed (unprocessed) aliases */
std::map<CellAddress, std::string> removedAliases;
/* Set of cells with errors */
std::set<CellAddress> cellErrors;

View File

@ -161,33 +161,6 @@ Spreadsheet::CellAddress Spreadsheet::stringToAddress(const char * strAddress)
throw Base::Exception("Invalid cell specifier.");
}
/**
* Convert given \a row and \a col into a string address.
*
* @param row Row address.
* @param col Column address.
*
* @returns Address given as a string.
*/
std::string Spreadsheet::addressToString(const CellAddress &address)
{
std::stringstream s;
if (address.col() < 26)
s << (char)('A' + address.col());
else {
int col = address.col() - 26;
s << (char)('A' + (col / 26));
s << (char)('A' + (col % 26));
}
s << (address.row() + 1);
return s.str();
}
void Spreadsheet::createRectangles(std::set<std::pair<int, int> > & cells, std::map<std::pair<int, int>, std::pair<int, int> > & rectangles)
{
while (cells.size() != 0) {
@ -342,3 +315,28 @@ std::string Spreadsheet::unquote(const std::string & input)
return output;
}
/**
* Convert given \a cell address into its string representation.
*
* @returns Address given as a string.
*/
std::string Spreadsheet::CellAddress::toString() const
{
std::stringstream s;
if (col() < 26)
s << (char)('A' + col());
else {
int colnum = col() - 26;
s << (char)('A' + (colnum / 26));
s << (char)('A' + (colnum % 26));
}
s << (row() + 1);
return s.str();
}

View File

@ -42,12 +42,9 @@ SpreadsheetExport void createRectangles(std::set<std::pair<int, int> > & cells,
SpreadsheetExport std::string quote(const std::string &input);
SpreadsheetExport std::string unquote(const std::string & input);
SpreadsheetExport std::string addressToString(const CellAddress & address);
struct SpreadsheetExport CellAddress {
struct CellAddress {
CellAddress(unsigned int _value) : value(_value) { }
CellAddress(int row = -1, int col = -1) : value(((row & 0xffff) << 16) | (col & 0xffff)) { }
CellAddress(int row = -1, int col = -1) : _row(row), _col(col) { }
CellAddress(const char * address) {
*this = stringToAddress(address);
@ -57,18 +54,20 @@ struct CellAddress {
*this = stringToAddress(address.c_str());
}
inline int row() const { return (value >> 16) & 0xffff; }
inline int row() const { return _row; }
inline int col() const { return value & 0xffff; }
inline int col() const { return _col; }
inline bool operator<(const CellAddress & other) const { return value < other.value; }
inline bool operator<(const CellAddress & other) const { return asInt() < other.asInt(); }
inline bool operator==(const CellAddress & other) const { return value == other.value; }
inline bool operator==(const CellAddress & other) const { return asInt() == other.asInt(); }
inline bool operator!=(const CellAddress & other) const { return value != other.value; }
inline bool operator!=(const CellAddress & other) const { return asInt() != other.asInt(); }
inline bool isValid() { return (row() >=0 && row() < MAX_ROWS && col() >= 0 && col() < MAX_COLUMNS); }
std::string toString() const;
// Static members
static const int MAX_ROWS;
@ -76,7 +75,11 @@ struct CellAddress {
static const int MAX_COLUMNS;
protected:
unsigned int value;
inline unsigned int asInt() const { return ((_row << 16) | _col); }
short _row;
short _col;
};
template<typename T> T * freecad_dynamic_cast(Base::BaseClass * t)

View File

@ -125,7 +125,7 @@ void CmdSpreadsheetSplitCell::activated(int iMsg)
QModelIndex current = sheetView->currentIndex();
if (current.isValid()) {
std::string address = addressToString(CellAddress(current.row(), current.column()));
std::string address = CellAddress(current.row(), current.column()).toString();
Gui::Command::openCommand("Split cell");
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.splitCell('%s')", sheet->getNameInDocument(),
address.c_str());

View File

@ -147,10 +147,10 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const
if (role == Qt::ToolTipRole) {
QString v;
std::set<std::string> deps = sheet->dependsOn(row, col);
std::set<std::string> deps = sheet->dependsOn(CellAddress(row, col));
std::set<std::string> provides;
sheet->providesTo(row, col, provides);
sheet->providesTo(CellAddress(row, col), provides);
if (deps.size() > 0) {
v += QString::fromUtf8("Depends on:");
@ -196,7 +196,7 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const
}
// Get display value as computed property
std::string address = addressToString(CellAddress(row, col));
std::string address = CellAddress(row, col).toString();
Property * prop = sheet->getPropertyByName(address.c_str());
if (prop == 0)
@ -394,8 +394,8 @@ bool SheetModel::setData(const QModelIndex & index, const QVariant & value, int
CellAddress address(index.row(), index.column());
try {
std::string strAddress = addressToString(address);
std::string next_address = addressToString(CellAddress(address.row() + 1, address.col()));
std::string strAddress = address.toString();
std::string next_address = CellAddress(address.row() + 1, address.col()).toString();
QString str = value.toString();
std::string content;
Cell * cell = sheet->getCell(address);

View File

@ -158,7 +158,7 @@ bool ViewProviderSheet::onDelete(const std::vector<std::string> &)
if (selection.size() > 0) {
Gui::Command::openCommand("Clear cell(s)");
for (QModelIndexList::const_iterator it = selection.begin(); it != selection.end(); ++it) {
std::string address = Spreadsheet::addressToString(CellAddress((*it).row(), (*it).column()));
std::string address = CellAddress((*it).row(), (*it).column()).toString();
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.clear('%s')", sheet->getNameInDocument(),
address.c_str());
}