- 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 da9f2f2ff8
commit b4dc4d97f4
20 changed files with 382 additions and 178 deletions

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();
}