diff --git a/src/Mod/Sketcher/App/Constraint.cpp b/src/Mod/Sketcher/App/Constraint.cpp index bf13188cb7..b808adf6cb 100644 --- a/src/Mod/Sketcher/App/Constraint.cpp +++ b/src/Mod/Sketcher/App/Constraint.cpp @@ -27,7 +27,7 @@ #include #include - +#include #include "Constraint.h" #include "ConstraintPy.h" @@ -42,10 +42,10 @@ TYPESYSTEM_SOURCE(Sketcher::Constraint, Base::Persistence) const int Constraint::GeoUndef = -2000; Constraint::Constraint() -: Type(None), +: Value(0.0), + Type(None), AlignmentType(Undef), Name(""), - Value(0.0), First(GeoUndef), FirstPos(none), Second(GeoUndef), @@ -56,13 +56,24 @@ Constraint::Constraint() LabelPosition(0.f), isDriving(true) { + // Initialize a random number generator, to avoid Valgrind false positives. + static boost::mt19937 ran; + static bool seeded = false; + + if (!seeded) { + ran.seed(QDateTime::currentMSecsSinceEpoch() & 0xffffffff); + seeded = true; + } + static boost::uuids::basic_random_generator gen(&ran); + + tag = gen(); } Constraint::Constraint(const Constraint& from) -: Type(from.Type), +: Value(from.Value), + Type(from.Type), AlignmentType(from.AlignmentType), Name(from.Name), - Value(from.Value), First(from.First), FirstPos(from.FirstPos), Second(from.Second), @@ -71,7 +82,8 @@ Constraint::Constraint(const Constraint& from) ThirdPos(from.ThirdPos), LabelDistance(from.LabelDistance), LabelPosition(from.LabelPosition), - isDriving(from.isDriving) + isDriving(from.isDriving), + tag(from.tag) { } @@ -89,6 +101,31 @@ PyObject *Constraint::getPyObject(void) return new ConstraintPy(new Constraint(*this)); } +void Constraint::setValue(double newValue) +{ + Value = newValue; +} + +double Constraint::getValue() const +{ + switch (Type) { + case Distance: + case Radius: + return std::abs(Value); + case DistanceX: + case DistanceY: + if (FirstPos == Sketcher::none || Second != Sketcher::Constraint::GeoUndef) + return std::abs(Value); + else + return Value; + case Angle: + case SnellsLaw: + return Value; + default: + return Value; + } +} + unsigned int Constraint::getMemSize (void) const { return 0; diff --git a/src/Mod/Sketcher/App/Constraint.h b/src/Mod/Sketcher/App/Constraint.h index 82737de95d..53dc5fc6e4 100644 --- a/src/Mod/Sketcher/App/Constraint.h +++ b/src/Mod/Sketcher/App/Constraint.h @@ -26,6 +26,8 @@ #include +#include +#include namespace Sketcher { @@ -80,13 +82,18 @@ public: virtual PyObject *getPyObject(void); - friend class Sketch; + void setValue(double newValue); + double getValue() const; + friend class Sketch; + friend class PropertyConstraintList; + +private: + double Value; public: ConstraintType Type; InternalAlignmentType AlignmentType; std::string Name; - double Value; int First; PointPos FirstPos; int Second; @@ -96,6 +103,8 @@ public: float LabelDistance; float LabelPosition; bool isDriving; +protected: + boost::uuids::uuid tag; }; } //namespace Sketcher diff --git a/src/Mod/Sketcher/App/ConstraintPyImp.cpp b/src/Mod/Sketcher/App/ConstraintPyImp.cpp index 669b6d447d..126453c0a0 100644 --- a/src/Mod/Sketcher/App/ConstraintPyImp.cpp +++ b/src/Mod/Sketcher/App/ConstraintPyImp.cpp @@ -150,7 +150,7 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/) } if (valid) { this->getConstraintPtr()->First = FirstIndex; - this->getConstraintPtr()->Value = Value; + this->getConstraintPtr()->setValue(Value); return 0; } } @@ -218,7 +218,7 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/) this->getConstraintPtr()->Type = Angle; this->getConstraintPtr()->First = FirstIndex; this->getConstraintPtr()->Second = SecondIndex; - this->getConstraintPtr()->Value = Value; + this->getConstraintPtr()->setValue(Value); return 0; } else if (strcmp("DistanceX",ConstraintType) == 0) { @@ -227,7 +227,7 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/) this->getConstraintPtr()->Type = DistanceX; this->getConstraintPtr()->First = FirstIndex; this->getConstraintPtr()->FirstPos = (Sketcher::PointPos) FirstPos; - this->getConstraintPtr()->Value = Value; + this->getConstraintPtr()->setValue(Value); return 0; } else if (strcmp("DistanceY",ConstraintType) == 0) { @@ -236,7 +236,7 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/) this->getConstraintPtr()->Type = DistanceY; this->getConstraintPtr()->First = FirstIndex; this->getConstraintPtr()->FirstPos = (Sketcher::PointPos) FirstPos; - this->getConstraintPtr()->Value = Value; + this->getConstraintPtr()->setValue(Value); return 0; } } @@ -306,7 +306,7 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/) this->getConstraintPtr()->First = intArg1; this->getConstraintPtr()->FirstPos = (Sketcher::PointPos) intArg2; this->getConstraintPtr()->Second = intArg3; - this->getConstraintPtr()->Value = Value; + this->getConstraintPtr()->setValue(Value); return 0; } } @@ -366,7 +366,7 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/) this->getConstraintPtr()->SecondPos = Sketcher::none; this->getConstraintPtr()->Third = intArg3; this->getConstraintPtr()->ThirdPos = (Sketcher::PointPos) intArg4; - this->getConstraintPtr()->Value = Value; + this->getConstraintPtr()->setValue(Value); return 0; } if (valid) { @@ -374,7 +374,7 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/) this->getConstraintPtr()->FirstPos = (Sketcher::PointPos) intArg2; this->getConstraintPtr()->Second = intArg3; this->getConstraintPtr()->SecondPos = (Sketcher::PointPos) intArg4; - this->getConstraintPtr()->Value = Value; + this->getConstraintPtr()->setValue(Value); return 0; } } @@ -406,7 +406,7 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/) this->getConstraintPtr()->SecondPos = (Sketcher::PointPos) SecondPos; this->getConstraintPtr()->Third = ThirdIndex; this->getConstraintPtr()->ThirdPos = none; - this->getConstraintPtr()->Value = Value; + this->getConstraintPtr()->setValue(Value); return 0; } } diff --git a/src/Mod/Sketcher/App/PropertyConstraintList.cpp b/src/Mod/Sketcher/App/PropertyConstraintList.cpp index 6e9bb35c2c..604b8c052b 100644 --- a/src/Mod/Sketcher/App/PropertyConstraintList.cpp +++ b/src/Mod/Sketcher/App/PropertyConstraintList.cpp @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include "PropertyConstraintList.h" #include "ConstraintPy.h" @@ -63,10 +65,41 @@ PropertyConstraintList::~PropertyConstraintList() if (*it) delete *it; } +App::ObjectIdentifier PropertyConstraintList::makeArrayPath(int idx) +{ + return App::ObjectIdentifier(getContainer()) << App::ObjectIdentifier::Component::ArrayComponent(ObjectIdentifier::String(getName()), idx); +} + +App::ObjectIdentifier PropertyConstraintList::makeSimplePath(const Constraint * c) +{ + return App::ObjectIdentifier(getContainer()) << App::ObjectIdentifier::Component::SimpleComponent(getName()) + << App::ObjectIdentifier::Component::SimpleComponent(App::ObjectIdentifier::String(c->Name, !ExpressionParser::isTokenAnIndentifier(c->Name))); +} + +App::ObjectIdentifier PropertyConstraintList::makePath(int idx, const Constraint * c) +{ + return c->Name.size() == 0 ? makeArrayPath(idx) : makeSimplePath(c); +} + void PropertyConstraintList::setSize(int newSize) { + std::set removed; + + /* Collect information about erased elements */ + for (unsigned int i = newSize; i < _lValueList.size(); i++) { + valueMap.erase(_lValueList[i]->tag); + removed.insert(makePath(i, _lValueList[i])); + } + + /* Signal removed elements */ + if (removed.size() > 0) + signalConstraintsRemoved(removed); + + /* Actually delete them */ for (unsigned int i = newSize; i < _lValueList.size(); i++) delete _lValueList[i]; + + /* Resize array to new size */ _lValueList.resize(newSize); } @@ -81,7 +114,18 @@ void PropertyConstraintList::set1Value(const int idx, const Constraint* lValue) aboutToSetValue(); Constraint* oldVal = _lValueList[idx]; Constraint* newVal = lValue->clone(); + + if (oldVal->Name != newVal->Name) { + std::map renamed; + + renamed[makePath(idx, _lValueList[idx])] = makePath(idx, lValue); + if (renamed.size() > 0) + signalConstraintsRenamed(renamed); + } + _lValueList[idx] = newVal; + valueMap.erase(oldVal->tag); + valueMap[newVal->tag] = idx; delete oldVal; hasSetValue(); } @@ -92,10 +136,38 @@ void PropertyConstraintList::setValue(const Constraint* lValue) if (lValue) { aboutToSetValue(); Constraint* newVal = lValue->clone(); + std::set removed; + std::map renamed; + int start = 0; + + /* Determine if it is a rename or not * */ + if (_lValueList.size() > 0 && lValue->tag == _lValueList[0]->tag) { + renamed[makePath(0, _lValueList[0])] = makePath(0, lValue); + start = 1; + } + + /* Signal rename changes */ + if (renamed.size() > 0) + signalConstraintsRenamed(renamed); + + /* Collect infor about removals */ + for (unsigned int i = start; i < _lValueList.size(); i++) { + valueMap.erase(_lValueList[i]->tag); + removed.insert(makePath(i, _lValueList[i])); + } + + /* Signal removes */ + if (removed.size() > 0) + signalConstraintsRemoved(removed); + + // Cleanup for (unsigned int i = 0; i < _lValueList.size(); i++) delete _lValueList[i]; + + /* Set new data */ _lValueList.resize(1); _lValueList[0] = newVal; + valueMap[_lValueList[0]->tag] = 0; hasSetValue(); } } @@ -110,10 +182,46 @@ void PropertyConstraintList::setValues(const std::vector& lValue) void PropertyConstraintList::applyValues(const std::vector& lValue) { std::vector oldVals(_lValueList); + std::map renamed; + std::set removed; + + /* Check for renames */ + for (unsigned int i = 0; i < lValue.size(); i++) { + boost::unordered_map::const_iterator j = valueMap.find(lValue[i]->tag); + + if (j != valueMap.end() && (i != j->second || _lValueList[j->second]->Name != lValue[i]->Name) ) + renamed[makePath(j->second, _lValueList[j->second] )] = makePath(i, lValue[i]); + } + + /* Update value map with new tags from new array */ + valueMap.clear(); + for (std::size_t i = 0; i < lValue.size(); i++) + valueMap[lValue[i]->tag] = i; + + /* Signal renames */ + if (renamed.size() > 0) + signalConstraintsRenamed(renamed); + + /* Collect info about removed elements */ + for (std::size_t i = 0; i < oldVals.size(); i++) { + boost::unordered_map::const_iterator j = valueMap.find(oldVals[i]->tag); + + if (j == valueMap.end()) + removed.insert(makePath(i, oldVals[i])); + } + + /* Signal removes */ + if (removed.size() > 0) + signalConstraintsRemoved(removed); + + /* Resize array to new size */ _lValueList.resize(lValue.size()); - // copy all objects + + /* copy all objects */ for (unsigned int i = 0; i < lValue.size(); i++) _lValueList[i] = lValue[i]->clone(); + + /* Clean-up; remove old values */ for (unsigned int i = 0; i < oldVals.size(); i++) delete oldVals[i]; } @@ -268,5 +376,125 @@ bool PropertyConstraintList::scanGeometry(const std::vector &G return true; } +string PropertyConstraintList::getConstraintName(const std::string & name, int i) +{ + if (name != "") + return name; + else + return getConstraintName(i); +} + +string PropertyConstraintList::getConstraintName(int i) +{ + std::stringstream str; + + str << "Constraint" << (i + 1); + return str.str(); +} + +bool PropertyConstraintList::validConstraintName(const std::string & name) +{ + return name.size() > 0; +} + +ObjectIdentifier PropertyConstraintList::createPath(int ConstrNbr) const +{ + return App::ObjectIdentifier(getContainer()) + << App::ObjectIdentifier::Component::ArrayComponent(App::ObjectIdentifier::String(getName()), ConstrNbr); +} + +int PropertyConstraintList::getIndexFromConstraintName(const string &name) +{ + return std::atoi(name.substr(10,4000).c_str()) - 1; +} + +void PropertyConstraintList::setValue(const ObjectIdentifier &path, const boost::any &value) +{ + const ObjectIdentifier::Component & c0 = path.getPropertyComponent(0); + double dvalue; + + if (value.type() == typeid(double)) + dvalue = boost::any_cast(value); + else if (value.type() == typeid(Quantity)) + dvalue = (boost::any_cast(value)).getValue(); + else + throw std::bad_cast(); + + if (c0.isArray() && path.numSubComponents() == 1) { + if (c0.getIndex() < 0 || c0.getIndex() >= _lValueList.size()) + throw Base::Exception("Array out of bounds"); + aboutToSetValue(); + _lValueList[c0.getIndex()]->setValue(dvalue); + hasSetValue(); + return; + } + else if (c0.isSimple() && path.numSubComponents() == 2) { + ObjectIdentifier::Component c1 = path.getPropertyComponent(1); + + for (std::vector::const_iterator it = _lValueList.begin(); it != _lValueList.end(); ++it) { + if ((*it)->Name == c1.getName()) { + aboutToSetValue(); + _lValueList[it - _lValueList.begin()]->setValue(dvalue); + hasSetValue(); + return; + } + } + } + throw Base::Exception("Invalid constraint"); +} + +const Constraint * PropertyConstraintList::getConstraint(const ObjectIdentifier &path) const +{ + const ObjectIdentifier::Component & c0 = path.getPropertyComponent(0); + + if (c0.isArray() && path.numSubComponents() == 1) { + if (c0.getIndex() < 0 || c0.getIndex() >= _lValueList.size()) + throw Base::Exception("Array out of bounds"); + + return _lValueList[c0.getIndex()]; + } + else if (c0.isSimple() && path.numSubComponents() == 2) { + ObjectIdentifier::Component c1 = path.getPropertyComponent(1); + + for (std::vector::const_iterator it = _lValueList.begin(); it != _lValueList.end(); ++it) { + if ((*it)->Name == c1.getName()) + return *it; + } + } + throw Base::Exception("Invalid constraint"); +} + +const boost::any PropertyConstraintList::getValue(const ObjectIdentifier &path) const +{ + return boost::any(getConstraint(path)->getValue()); +} + +const ObjectIdentifier PropertyConstraintList::canonicalPath(const ObjectIdentifier &p) const +{ + const ObjectIdentifier::Component & c0 = p.getPropertyComponent(0); + + if (c0.isArray() && p.numSubComponents() == 1) { + if (c0.getIndex() >= 0 && c0.getIndex() < _lValueList.size() && _lValueList[c0.getIndex()]->Name.size() > 0) + return ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName()) + << ObjectIdentifier::Component::SimpleComponent(_lValueList[c0.getIndex()]->Name); + return p; + } + else if (c0.isSimple() && p.numSubComponents() == 2) { + ObjectIdentifier::Component c1 = p.getPropertyComponent(1); + + if (c1.isSimple()) + return p; + } + throw Base::Exception("Invalid constraint"); +} + +void PropertyConstraintList::getPaths(std::vector &paths) const +{ + for (std::vector::const_iterator it = _lValueList.begin(); it != _lValueList.end(); ++it) { + if ((*it)->Name.size() > 0) + paths.push_back(ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName()) + << ObjectIdentifier::Component::SimpleComponent((*it)->Name)); + } +} std::vector PropertyConstraintList::_emptyValueList(0); diff --git a/src/Mod/Sketcher/App/PropertyConstraintList.h b/src/Mod/Sketcher/App/PropertyConstraintList.h index 1726d7500e..8d6e59cea6 100644 --- a/src/Mod/Sketcher/App/PropertyConstraintList.h +++ b/src/Mod/Sketcher/App/PropertyConstraintList.h @@ -32,6 +32,8 @@ #include #include #include "Constraint.h" +#include +#include namespace Base { class Writer; @@ -99,8 +101,35 @@ public: bool scanGeometry(const std::vector &GeoList) const; bool isGeometryInvalid(){return invalidGeometry;} + + const Constraint *getConstraint(const App::ObjectIdentifier &path) const; + virtual void setValue(const App::ObjectIdentifier & path, const boost::any & value); + virtual const boost::any getValue(const App::ObjectIdentifier & path) const; + virtual const App::ObjectIdentifier canonicalPath(const App::ObjectIdentifier & p) const; + virtual void getPaths(std::vector & paths) const; + + typedef std::pair ConstraintInfo ; + + boost::signal &)> signalConstraintsRenamed; + boost::signal &)> signalConstraintsRemoved; + + static std::string getConstraintName(const std::string &name, int i); + + static std::string getConstraintName(int i); + + static int getIndexFromConstraintName(const std::string & name); + + static bool validConstraintName(const std::string &name); + + App::ObjectIdentifier createPath(int ConstrNbr) const; + private: + App::ObjectIdentifier makeArrayPath(int idx); + App::ObjectIdentifier makeSimplePath(const Constraint *c); + App::ObjectIdentifier makePath(int idx, const Constraint *c); + std::vector _lValueList; + boost::unordered_map valueMap; std::vector validGeometryKeys; bool invalidGeometry; diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 249616f1c8..49459d4210 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -46,6 +46,9 @@ # include #endif // #ifndef _PreComp_ +#include + +#include #include #include #include @@ -93,6 +96,11 @@ SketchObject::SketchObject() solverNeedsUpdate=false; noRecomputes=false; + + ExpressionEngine.setValidator(boost::bind(&Sketcher::SketchObject::validateExpression, this, _1, _2)); + + constraintsRemovedConn = Constraints.signalConstraintsRemoved.connect(boost::bind(&Sketcher::SketchObject::constraintsRemoved, this, _1)); + constraintsRenamedConn = Constraints.signalConstraintsRenamed.connect(boost::bind(&Sketcher::SketchObject::constraintsRenamed, this, _1)); } SketchObject::~SketchObject() @@ -247,7 +255,7 @@ int SketchObject::setDatum(int ConstrId, double Datum) std::vector newVals(vals); // clone the changed Constraint Constraint *constNew = vals[ConstrId]->clone(); - constNew->Value = Datum; + constNew->setValue(Datum); newVals[ConstrId] = constNew; this->Constraints.setValues(newVals); delete constNew; @@ -286,6 +294,8 @@ int SketchObject::setDriving(int ConstrId, bool isdriving) constNew->isDriving = isdriving; newVals[ConstrId] = constNew; this->Constraints.setValues(newVals); + if (isdriving) + setExpression(Constraints.createPath(ConstrId), boost::shared_ptr()); delete constNew; if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver @@ -342,6 +352,8 @@ int SketchObject::toggleDriving(int ConstrId) constNew->isDriving = !constNew->isDriving; newVals[ConstrId] = constNew; this->Constraints.setValues(newVals); + if (constNew->isDriving) + setExpression(Constraints.createPath(ConstrId), boost::shared_ptr()); delete constNew; if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver @@ -2268,7 +2280,7 @@ int SketchObject::addCopy(const std::vector &geoIdList, const Base::Vector3 constNew->Type = Sketcher::Distance; constNew->First = rowrefgeoid; constNew->FirstPos = Sketcher::none; - constNew->Value = perpendicularDisplacement.Length(); + constNew->setValue(perpendicularDisplacement.Length()); newconstrVals.push_back(constNew); } @@ -2329,14 +2341,14 @@ int SketchObject::addCopy(const std::vector &geoIdList, const Base::Vector3 constNew->Type = Sketcher::Distance; constNew->First = colrefgeoid; constNew->FirstPos = Sketcher::none; - constNew->Value = displacement.Length(); + constNew->setValue(displacement.Length()); newconstrVals.push_back(constNew); constNew = new Constraint(); constNew->Type = Sketcher::Angle; constNew->First = colrefgeoid; constNew->FirstPos = Sketcher::none; - constNew->Value = atan2(displacement.y,displacement.x); + constNew->setValue(atan2(displacement.y,displacement.x)); newconstrVals.push_back(constNew); } else { // any other element @@ -3499,6 +3511,38 @@ void SketchObject::validateConstraints() } } +std::string SketchObject::validateExpression(const App::ObjectIdentifier &path, boost::shared_ptr expr) +{ + const App::Property * prop = path.getProperty(); + + assert(expr != 0); + + if (!prop) + return "Property not found"; + + if (prop == &Constraints) { + const Constraint * constraint = Constraints.getConstraint(path); + + if (!constraint->isDriving) + return "Reference constraints cannot be set!"; + } + + std::set deps; + expr->getDeps(deps); + + for (std::set::const_iterator i = deps.begin(); i != deps.end(); ++i) { + const App::Property * prop = (*i).getProperty(); + + if (prop == &Constraints) { + const Constraint * constraint = Constraints.getConstraint(*i); + + if (!constraint->isDriving) + return "Reference constraint from this sketch cannot be used in this expression."; + } + } + return ""; +} + //This function is necessary for precalculation of an angle when adding // an angle constraint. It is also used here, in SketchObject, to // lock down the type of tangency/perpendicularity. @@ -3546,6 +3590,23 @@ double SketchObject::calculateAngleViaPoint(int GeoId1, int GeoId2, double px, d */ } +void SketchObject::constraintsRenamed(const std::map &renamed) +{ + ExpressionEngine.renameExpressions(renamed); + + getDocument()->renameObjectIdentifiers(renamed); +} + +void SketchObject::constraintsRemoved(const std::set &removed) +{ + std::set::const_iterator i = removed.begin(); + + while (i != removed.end()) { + ExpressionEngine.setValue(*i, boost::shared_ptr(), 0); + ++i; + } +} + //Tests if the provided point lies exactly in a curve (satisfies // point-on-object constraint). It is used to decide whether it is nesessary to // constrain a point onto curves when 3-element selection tangent-via-point-like @@ -3841,10 +3902,10 @@ bool SketchObject::AutoLockTangencyAndPerpty(Constraint *cstr, bool bForce, bool { try{ //assert ( cstr->Type == Tangent || cstr->Type == Perpendicular); - if(cstr->Value != 0.0 && ! bForce) /*tangency type already set. If not bForce - don't touch.*/ + if(cstr->getValue() != 0.0 && ! bForce) /*tangency type already set. If not bForce - don't touch.*/ return true; if(!bLock){ - cstr->Value=0.0;//reset + cstr->setValue(0.0);//reset } else { //decide on tangency type. Write the angle value into the datum field of the constraint. int geoId1, geoId2, geoIdPt; @@ -3880,7 +3941,7 @@ bool SketchObject::AutoLockTangencyAndPerpty(Constraint *cstr, bool bForce, bool if(fabs(angleErr) > M_PI/2 ) angleDesire += M_PI; - cstr->Value = angleDesire + angleOffset; //external tangency. The angle stored is offset by Pi/2 so that a value of 0.0 is invalid and threated as "undecided". + cstr->setValue(angleDesire + angleOffset); //external tangency. The angle stored is offset by Pi/2 so that a value of 0.0 is invalid and threated as "undecided". } } } catch (Base::Exception& e){ @@ -3891,6 +3952,15 @@ bool SketchObject::AutoLockTangencyAndPerpty(Constraint *cstr, bool bForce, bool return true; } +void SketchObject::setExpression(const App::ObjectIdentifier &path, boost::shared_ptr expr, const char * comment) +{ + DocumentObject::setExpression(path, expr, comment); + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver, constraints and UI + solve(); +} + + // Python Sketcher feature --------------------------------------------------------- namespace App { diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index 43061bce0b..c060a9d935 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -23,6 +23,7 @@ #ifndef SKETCHER_SKETCHOBJECT_H #define SKETCHER_SKETCHOBJECT_H +#include #include #include #include @@ -248,6 +249,13 @@ protected: /// get called by the container when a property has changed virtual void onChanged(const App::Property* /*prop*/); virtual void onDocumentRestored(); + + virtual void setExpression(const App::ObjectIdentifier &path, boost::shared_ptr expr, const char * comment = 0); + + std::string validateExpression(const App::ObjectIdentifier &path, boost::shared_ptr expr); + + void constraintsRenamed(const std::map &renamed); + void constraintsRemoved(const std::set &removed); private: std::vector ExternalGeo; @@ -271,6 +279,9 @@ private: std::vector lastConflicting; std::vector lastRedundant; + boost::signals::scoped_connection constraintsRenamedConn; + boost::signals::scoped_connection constraintsRemovedConn; + bool AutoLockTangencyAndPerpty(Constraint* cstr, bool bForce = false, bool bLock = true); }; diff --git a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp index 03d628c700..bdaabbc66a 100644 --- a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp +++ b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp @@ -329,6 +329,24 @@ PyObject* SketchObjectPy::renameConstraint(PyObject *args) return 0; } + if (strcmp(Name, "") != 0) { + + if (!Sketcher::PropertyConstraintList::validConstraintName(Name)) { + std::stringstream str; + str << "Invalid constraint name with the given index: " << Index; + PyErr_SetString(PyExc_IndexError, str.str().c_str()); + return 0; + } + + const std::vector< Sketcher::Constraint * > &vals = getSketchObjectPtr()->Constraints.getValues(); + for (std::size_t i = 0; i < vals.size(); ++i) { + if (static_cast(i) != Index && Name == vals[i]->Name) { + PyErr_SetString(PyExc_ValueError, "Duplicate constraint not allowed"); + return 0; + } + } + } + Constraint* copy = this->getSketchObjectPtr()->Constraints[Index]->clone(); copy->Name = Name; this->getSketchObjectPtr()->Constraints.set1Value(Index, copy); @@ -537,15 +555,9 @@ PyObject* SketchObjectPy::getDatum(PyObject *args) PyErr_Clear(); char* name; if (PyArg_ParseTuple(args,"s", &name)) { - int id = 1; + int id = 0; for (std::vector::const_iterator it = vals.begin(); it != vals.end(); ++it, ++id) { - std::string constrName = (*it)->Name; - if (constrName.empty()) { - std::stringstream str; - str << "Constraint" << id; - constrName = str.str(); - } - if (constrName == name) { + if (Sketcher::PropertyConstraintList::getConstraintName((*it)->Name, id) == name) { constr = *it; break; } @@ -579,7 +591,7 @@ PyObject* SketchObjectPy::getDatum(PyObject *args) } Base::Quantity datum; - datum.setValue(constr->Value); + datum.setValue(constr->getValue()); if (type == Angle) { datum.setValue(Base::toDegrees(datum.getValue())); datum.setUnit(Base::Unit::Angle); diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 84901095b7..10e703c5d0 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -95,7 +95,7 @@ void openEditDatumDialog(Sketcher::SketchObject* sketch, int ConstrNbr) Ui::InsertDatum ui_ins_datum; ui_ins_datum.setupUi(&dlg); - double datum = Constr->Value; + double datum = Constr->getValue(); Base::Quantity init_val; if (Constr->Type == Sketcher::Angle) { @@ -135,6 +135,8 @@ void openEditDatumDialog(Sketcher::SketchObject* sketch, int ConstrNbr) ui_ins_datum.labelEdit->setValue(init_val); ui_ins_datum.labelEdit->selectNumber(); + ui_ins_datum.labelEdit->bind(sketch->Constraints.createPath(ConstrNbr)); + ui_ins_datum.name->setText(Base::Tools::fromStdString(Constr->Name)); if (dlg.exec()) { Base::Quantity newQuant = ui_ins_datum.labelEdit->value(); @@ -154,14 +156,30 @@ void openEditDatumDialog(Sketcher::SketchObject* sketch, int ConstrNbr) } try { - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.setDatum(%i,App.Units.Quantity('%f %s'))", - sketch->getNameInDocument(), - ConstrNbr, newDatum, (const char*)newQuant.getUnit().getString().toUtf8()); + if (ui_ins_datum.labelEdit->hasExpression()) + ui_ins_datum.labelEdit->apply(); + else + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.setDatum(%i,App.Units.Quantity('%f %s'))", + sketch->getNameInDocument(), + ConstrNbr, newDatum, (const char*)newQuant.getUnit().getString().toUtf8()); + + QString constraintName = ui_ins_datum.name->text().trimmed(); + if (Base::Tools::toStdString(constraintName) != sketch->Constraints[ConstrNbr]->Name) { + std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(constraintName.toUtf8().constData()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.renameConstraint(%d, '%s')", + sketch->getNameInDocument(), + ConstrNbr, escapedstr.c_str()); + } Gui::Command::commitCommand(); ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); bool autoRecompute = hGrp->GetBool("AutoRecompute",false); + if (sketch->noRecomputes && sketch->ExpressionEngine.depsAreTouched()) { + sketch->ExpressionEngine.execute(); + sketch->solve(); + } + if(autoRecompute) Gui::Command::updateActive(); } @@ -2406,16 +2424,28 @@ void CmdSketcherConstrainRadius::activated(int iMsg) ui_Datum.labelEdit->setValue(init_val); ui_Datum.labelEdit->selectNumber(); + if (constrainEqual || geoIdRadiusMap.size() == 1) + ui_Datum.labelEdit->bind(Obj->Constraints.createPath(indexConstr)); + else + ui_Datum.name->setDisabled(true); if (dlg.exec() == QDialog::Accepted) { Base::Quantity newQuant = ui_Datum.labelEdit->value(); double newRadius = newQuant.getValue(); try { - if (constrainEqual) { + if (constrainEqual || geoIdRadiusMap.size() == 1) { doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.setDatum(%i,App.Units.Quantity('%f %s'))", Obj->getNameInDocument(), indexConstr, newRadius, (const char*)newQuant.getUnit().getString().toUtf8()); + + QString constraintName = ui_Datum.name->text().trimmed(); + if (Base::Tools::toStdString(constraintName) != Obj->Constraints[indexConstr]->Name) { + std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(constraintName.toUtf8().constData()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.renameConstraint(%d, '%s')", + Obj->getNameInDocument(), + indexConstr, escapedstr.c_str()); + } } else { for (std::size_t i=0; iGetBool("AutoRecompute",false); + if (Obj->noRecomputes && Obj->ExpressionEngine.depsAreTouched()) { + Obj->ExpressionEngine.execute(); + Obj->solve(); + } + if(autoRecompute) Gui::Command::updateActive(); @@ -3142,6 +3178,7 @@ void CmdSketcherConstrainSnellsLaw::activated(int iMsg) ui_Datum.labelEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/SketcherRefrIndexRatio")); ui_Datum.labelEdit->setToLastUsedValue(); ui_Datum.labelEdit->selectNumber(); + // Unable to bind, because the constraint does not yet exist if (dlg.exec() != QDialog::Accepted) return; ui_Datum.labelEdit->pushToHistory(); @@ -3711,7 +3748,7 @@ void CmdSketcherToggleDrivingConstraint::activated(int iMsg) for (std::vector::const_iterator it=SubNames.begin();it!=SubNames.end();++it){ // only handle constraints if (it->size() > 10 && it->substr(0,10) == "Constraint") { - int ConstrId = std::atoi(it->substr(10,4000).c_str()) - 1; + int ConstrId = Sketcher::PropertyConstraintList::getIndexFromConstraintName(*it); try { // issue the actual commands to toggle doCommand(Doc,"App.ActiveDocument.%s.toggleDriving(%d) ",selection[0].getFeatName(),ConstrId); diff --git a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp index 08e42b6805..ef223931a0 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp @@ -319,7 +319,6 @@ void CmdSketcherSelectConstraints::activated(int iMsg) std::string doc_name = Obj->getDocument()->getName(); std::string obj_name = Obj->getNameInDocument(); - std::stringstream ss; getSelection().clearSelection(); @@ -330,13 +329,11 @@ void CmdSketcherSelectConstraints::activated(int iMsg) int GeoId = std::atoi(it->substr(4,4000).c_str()) - 1; // push all the constraints - int i=1; + int i = 0; for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin(); it != vals.end(); ++it,++i) { if ( (*it)->First == GeoId || (*it)->Second == GeoId || (*it)->Third == GeoId){ - ss.str(std::string()); - ss << "Constraint" << i; - Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str(), ss.str().c_str()); + Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str(), Sketcher::PropertyConstraintList::getConstraintName(i).c_str()); } } } @@ -507,7 +504,6 @@ void CmdSketcherSelectRedundantConstraints::activated(int iMsg) std::string doc_name = Obj->getDocument()->getName(); std::string obj_name = Obj->getNameInDocument(); - std::stringstream ss; // get the needed lists and objects const std::vector< int > &solverredundant = dynamic_cast(doc->getInEdit())->getSketchObject()->getLastRedundant(); @@ -516,13 +512,11 @@ void CmdSketcherSelectRedundantConstraints::activated(int iMsg) getSelection().clearSelection(); // push the constraints - int i=1; + int i = 0; for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();it != vals.end(); ++it,++i) { for(std::vector< int >::const_iterator itc= solverredundant.begin();itc != solverredundant.end(); ++itc) { - if ( (*itc) == i){ - ss.str(std::string()); - ss << "Constraint" << i; - Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str(), ss.str().c_str()); + if ( (*itc) - 1 == i){ + Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str(), Sketcher::PropertyConstraintList::getConstraintName(i).c_str()); break; } } @@ -571,13 +565,11 @@ void CmdSketcherSelectConflictingConstraints::activated(int iMsg) getSelection().clearSelection(); // push the constraints - int i=1; + int i = 0; for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();it != vals.end(); ++it,++i) { for(std::vector< int >::const_iterator itc= solverconflicting.begin();itc != solverconflicting.end(); ++itc) { - if ( (*itc) == i){ - ss.str(std::string()); - ss << "Constraint" << i; - Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str(), ss.str().c_str()); + if ( (*itc) - 1 == i){ + Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str(), Sketcher::PropertyConstraintList::getConstraintName(i).c_str()); break; } } @@ -632,7 +624,7 @@ void CmdSketcherSelectElementsAssociatedWithConstraints::activated(int iMsg) for (std::vector::const_iterator it=SubNames.begin(); it != SubNames.end(); ++it) { // only handle constraints if (it->size() > 10 && it->substr(0,10) == "Constraint") { - int ConstrId = std::atoi(it->substr(10,4000).c_str()) - 1; + int ConstrId = Sketcher::PropertyConstraintList::getIndexFromConstraintName(*it); if(ConstrId < static_cast(vals.size())){ if(vals[ConstrId]->First!=Constraint::GeoUndef){ diff --git a/src/Mod/Sketcher/Gui/EditDatumDialog.cpp b/src/Mod/Sketcher/Gui/EditDatumDialog.cpp index ae53f487f2..adc62d3187 100644 --- a/src/Mod/Sketcher/Gui/EditDatumDialog.cpp +++ b/src/Mod/Sketcher/Gui/EditDatumDialog.cpp @@ -91,7 +91,7 @@ void EditDatumDialog::exec(bool atCursor) Ui::InsertDatum ui_ins_datum; ui_ins_datum.setupUi(&dlg); - double datum = Constr->Value; + double datum = Constr->getValue(); Base::Quantity init_val; if (Constr->Type == Sketcher::Angle) { @@ -132,8 +132,13 @@ void EditDatumDialog::exec(bool atCursor) else // show negative sign init_val.setValue(datum); + // Enable label if we are modifying a driving constraint + ui_ins_datum.labelEdit->setEnabled(Constr->isDriving); + ui_ins_datum.labelEdit->setValue(init_val); ui_ins_datum.labelEdit->selectNumber(); + ui_ins_datum.labelEdit->bind(sketch->Constraints.createPath(ConstrNbr)); + ui_ins_datum.name->setText(Base::Tools::fromStdString(Constr->Name)); if (atCursor) dlg.setGeometry(QCursor::pos().x() - dlg.geometry().width() / 2, QCursor::pos().y(), dlg.geometry().width(), dlg.geometry().height()); @@ -157,14 +162,34 @@ void EditDatumDialog::exec(bool atCursor) try { Gui::Command::openCommand("Modify sketch constraints"); - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.setDatum(%i,App.Units.Quantity('%f %s'))", - sketch->getNameInDocument(), - ConstrNbr, newDatum, (const char*)newQuant.getUnit().getString().toUtf8()); + + if (Constr->isDriving) { + if (ui_ins_datum.labelEdit->hasExpression()) + ui_ins_datum.labelEdit->apply(); + else + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.setDatum(%i,App.Units.Quantity('%f %s'))", + sketch->getNameInDocument(), + ConstrNbr, newDatum, (const char*)newQuant.getUnit().getString().toUtf8()); + } + + QString constraintName = ui_ins_datum.name->text().trimmed(); + if (Base::Tools::toStdString(constraintName) != sketch->Constraints[ConstrNbr]->Name) { + std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(constraintName.toUtf8().constData()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.renameConstraint(%d, '%s')", + sketch->getNameInDocument(), + ConstrNbr, escapedstr.c_str()); + } + Gui::Command::commitCommand(); ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); bool autoRecompute = hGrp->GetBool("AutoRecompute",false); + if (sketch->noRecomputes && sketch->ExpressionEngine.depsAreTouched()) { + sketch->ExpressionEngine.execute(); + sketch->solve(); + } + if(autoRecompute) Gui::Command::updateActive(); } diff --git a/src/Mod/Sketcher/Gui/InsertDatum.ui b/src/Mod/Sketcher/Gui/InsertDatum.ui index 93dea8e736..faa86b007b 100644 --- a/src/Mod/Sketcher/Gui/InsertDatum.ui +++ b/src/Mod/Sketcher/Gui/InsertDatum.ui @@ -9,8 +9,8 @@ 0 0 - 178 - 72 + 344 + 122 @@ -18,16 +18,33 @@ - - + + datum: - - + + + + + 0 + 0 + + + + + + + + Name (optional) + + + + + @@ -43,16 +60,15 @@ - - - Gui::PrefQuantitySpinBox - QWidget -
Gui/PrefWidgets.h
-
+ + Gui::PrefQuantitySpinBox + QWidget +
Gui/PrefWidgets.h
+
- - + + buttonBox accepted() diff --git a/src/Mod/Sketcher/Gui/PropertyConstraintListItem.cpp b/src/Mod/Sketcher/Gui/PropertyConstraintListItem.cpp index 7ea6dad2db..1dcd339f22 100644 --- a/src/Mod/Sketcher/Gui/PropertyConstraintListItem.cpp +++ b/src/Mod/Sketcher/Gui/PropertyConstraintListItem.cpp @@ -150,13 +150,13 @@ QVariant PropertyConstraintListItem::value(const App::Property* prop) const Base::Quantity quant; if ((*it)->Type == Sketcher::Angle ) { - double datum = Base::toDegrees((*it)->Value); + double datum = Base::toDegrees((*it)->getValue()); quant.setUnit(Base::Unit::Angle); quant.setValue(datum); } else { quant.setUnit(Base::Unit::Length); - quant.setValue((*it)->Value); + quant.setValue((*it)->getValue()); } quantities.append(quant); @@ -228,7 +228,7 @@ bool PropertyConstraintListItem::event (QEvent* ev) double datum = quant.getValue(); if ((*it)->Type == Sketcher::Angle) datum = Base::toRadians(datum); - const_cast((*it))->Value = datum; + const_cast((*it))->setValue(datum); item->set1Value(id,(*it)); break; } diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp b/src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp index ed582d7e87..e8667e1abe 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstrains.cpp @@ -29,6 +29,10 @@ # include # include # include +# include +# include +# include +# include #endif #include "TaskSketcherConstrains.h" @@ -46,9 +50,9 @@ #include #include #include -#include #include #include +#include using namespace SketcherGui; using namespace Gui::TaskView; @@ -73,13 +77,10 @@ void ConstraintView::FUNC(){ \ class ConstraintItem : public QListWidgetItem { public: - ConstraintItem(const QIcon & icon, const QString & text,int ConstNbr,Sketcher::ConstraintType t,bool isdriving=true, bool isenforceable=true) - : QListWidgetItem(icon,text),ConstraintNbr(ConstNbr),Type(t),isDriving(isdriving),isEnforceable(isenforceable) - { - this->setFlags(this->flags() | Qt::ItemIsEditable); - } - ConstraintItem(const QString & text,int ConstNbr,Sketcher::ConstraintType t,bool isdriving=true, bool isenforceable=true) - : QListWidgetItem(text),ConstraintNbr(ConstNbr),Type(t),isDriving(isdriving),isEnforceable(isenforceable) + ConstraintItem(const Sketcher::SketchObject * s, int ConstNbr) + : QListWidgetItem(QString()), + sketch(s), + ConstraintNbr(ConstNbr) { this->setFlags(this->flags() | Qt::ItemIsEditable); } @@ -88,36 +89,270 @@ public: } void setData(int role, const QVariant & value) { - if (role == Qt::UserRole) { - quantity = value; - return; - } + if (role == Qt::EditRole) + this->value = value; + QListWidgetItem::setData(role, value); } + QVariant data (int role) const { - if (role == Qt::UserRole) { - return quantity; + if (ConstraintNbr < 0 || ConstraintNbr >= sketch->Constraints.getSize()) + return QVariant(); + + const Sketcher::Constraint * constraint = sketch->Constraints[ConstraintNbr]; + + if (role == Qt::EditRole) { + if (value.isValid()) + return value; + else + return Base::Tools::fromStdString(Sketcher::PropertyConstraintList::getConstraintName(constraint->Name, ConstraintNbr)); } - else if (role == Qt::DisplayRole && quantity.isValid()) { - return quantity; + else if (role == Qt::DisplayRole) { + QString name = Base::Tools::fromStdString(Sketcher::PropertyConstraintList::getConstraintName(constraint->Name, ConstraintNbr)); + + switch (constraint->Type) { + case Sketcher::Horizontal: + case Sketcher::Vertical: + case Sketcher::Coincident: + case Sketcher::PointOnObject: + case Sketcher::Parallel: + case Sketcher::Perpendicular: + case Sketcher::Tangent: + case Sketcher::Equal: + case Sketcher::Symmetric: + break; + case Sketcher::Distance: + name = QString::fromLatin1("%1 (%2)").arg(name).arg(Base::Quantity(constraint->getValue(),Base::Unit::Length).getUserString()); + break; + case Sketcher::DistanceX: + name = QString::fromLatin1("%1 (%2)").arg(name).arg(Base::Quantity(std::abs(constraint->getValue()),Base::Unit::Length).getUserString()); + break; + case Sketcher::DistanceY: + name = QString::fromLatin1("%1 (%2)").arg(name).arg(Base::Quantity(std::abs(constraint->getValue()),Base::Unit::Length).getUserString()); + break; + case Sketcher::Radius: + name = QString::fromLatin1("%1 (%2)").arg(name).arg(Base::Quantity(constraint->getValue(),Base::Unit::Length).getUserString()); + break; + case Sketcher::Angle: + name = QString::fromLatin1("%1 (%2)").arg(name).arg(Base::Quantity(Base::toDegrees(std::abs(constraint->getValue())),Base::Unit::Angle).getUserString()); + break; + case Sketcher::SnellsLaw: { + double v = constraint->getValue(); + double n1 = 1.0; + double n2 = 1.0; + if (fabs(v) >= 1) { + n2 = v; + } else { + n1 = 1/v; + } + name = QString::fromLatin1("%1 (%2/%3)").arg(name).arg(n2).arg(n1); + break; + } + case Sketcher::InternalAlignment: + break; + default: + break; + } + return name; } - return QListWidgetItem::data(role); + else if (role == Qt::DecorationRole) { + static QIcon hdist( Gui::BitmapFactory().pixmap("Constraint_HorizontalDistance") ); + static QIcon vdist( Gui::BitmapFactory().pixmap("Constraint_VerticalDistance") ); + static QIcon horiz( Gui::BitmapFactory().pixmap("Constraint_Horizontal") ); + static QIcon vert ( Gui::BitmapFactory().pixmap("Constraint_Vertical") ); + static QIcon lock ( Gui::BitmapFactory().pixmap("Sketcher_ConstrainLock") ); + static QIcon coinc( Gui::BitmapFactory().pixmap("Constraint_PointOnPoint") ); + static QIcon para ( Gui::BitmapFactory().pixmap("Constraint_Parallel") ); + static QIcon perp ( Gui::BitmapFactory().pixmap("Constraint_Perpendicular") ); + static QIcon tang ( Gui::BitmapFactory().pixmap("Constraint_Tangent") ); + static QIcon dist ( Gui::BitmapFactory().pixmap("Constraint_Length") ); + static QIcon radi ( Gui::BitmapFactory().pixmap("Constraint_Radius") ); + static QIcon majradi ( Gui::BitmapFactory().pixmap("Constraint_Ellipse_Major_Radius") ); + static QIcon minradi ( Gui::BitmapFactory().pixmap("Constraint_Ellipse_Minor_Radius") ); + static QIcon angl ( Gui::BitmapFactory().pixmap("Constraint_InternalAngle") ); + static QIcon ellipseXUAngl ( Gui::BitmapFactory().pixmap("Constraint_Ellipse_Axis_Angle") ); + static QIcon equal( Gui::BitmapFactory().pixmap("Constraint_EqualLength") ); + static QIcon pntoo( Gui::BitmapFactory().pixmap("Constraint_PointOnObject") ); + static QIcon symm ( Gui::BitmapFactory().pixmap("Constraint_Symmetric") ); + static QIcon snell ( Gui::BitmapFactory().pixmap("Constraint_SnellsLaw") ); + static QIcon iaellipseminoraxis ( Gui::BitmapFactory().pixmap("Constraint_InternalAlignment_Ellipse_MinorAxis") ); + static QIcon iaellipsemajoraxis ( Gui::BitmapFactory().pixmap("Constraint_InternalAlignment_Ellipse_MajorAxis") ); + static QIcon iaellipsefocus1 ( Gui::BitmapFactory().pixmap("Constraint_InternalAlignment_Ellipse_Focus1") ); + static QIcon iaellipsefocus2 ( Gui::BitmapFactory().pixmap("Constraint_InternalAlignment_Ellipse_Focus2") ); + static QIcon iaellipseother ( Gui::BitmapFactory().pixmap("Constraint_InternalAlignment") ); + + static QIcon hdist_driven ( Gui::BitmapFactory().pixmap("Constraint_HorizontalDistance_Driven") ); + static QIcon vdist_driven( Gui::BitmapFactory().pixmap("Constraint_VerticalDistance_Driven") ); + static QIcon dist_driven ( Gui::BitmapFactory().pixmap("Constraint_Length_Driven") ); + static QIcon radi_driven ( Gui::BitmapFactory().pixmap("Constraint_Radius_Driven") ); + static QIcon angl_driven ( Gui::BitmapFactory().pixmap("Constraint_InternalAngle_Driven") ); + static QIcon snell_driven ( Gui::BitmapFactory().pixmap("Constraint_SnellsLaw_Driven") ); + + switch(constraint->Type){ + case Sketcher::Horizontal: + return horiz; + case Sketcher::Vertical: + return vert; + case Sketcher::Coincident: + return coinc; + case Sketcher::PointOnObject: + return pntoo; + case Sketcher::Parallel: + return para; + case Sketcher::Perpendicular: + return perp; + case Sketcher::Tangent: + return tang; + case Sketcher::Equal: + return equal; + case Sketcher::Symmetric: + return symm; + case Sketcher::Distance: + return constraint->isDriving ? dist : dist_driven; + case Sketcher::DistanceX: + return constraint->isDriving ? hdist : hdist_driven; + case Sketcher::DistanceY: + return constraint->isDriving ? vdist : vdist_driven; + case Sketcher::Radius: + return constraint->isDriving ? radi : radi_driven; + case Sketcher::Angle: + return constraint->isDriving ? angl : angl_driven; + case Sketcher::SnellsLaw: + return constraint->isDriving ? snell : snell_driven; + case Sketcher::InternalAlignment: + switch(constraint->AlignmentType){ + case Sketcher::EllipseMajorDiameter: + return iaellipsemajoraxis; + case Sketcher::EllipseMinorDiameter: + return iaellipseminoraxis; + case Sketcher::EllipseFocus1: + return iaellipsefocus1; + case Sketcher::EllipseFocus2: + return iaellipsefocus2; + case Sketcher::Undef: + default: + return iaellipseother; + } + default: + return QVariant(); + } + } + else if (role == Qt::ToolTipRole) { + App::ObjectIdentifier path = sketch->Constraints.createPath(ConstraintNbr); + App::PropertyExpressionEngine::ExpressionInfo expr_info = sketch->getExpression(path); + + if (expr_info.expression) + return Base::Tools::fromStdString(expr_info.expression->toString()); + else + return QVariant(); + } + else + return QListWidgetItem::data(role); } - int ConstraintNbr; - Sketcher::ConstraintType Type; - bool isDriving; - bool isEnforceable; + Sketcher::ConstraintType constraintType() const { + assert(ConstraintNbr >= 0 && ConstraintNbr < sketch->Constraints.getSize()); + return sketch->Constraints[ConstraintNbr]->Type; + } -private: - QVariant quantity; - + bool isEnforceable() const { + assert(ConstraintNbr >= 0 && ConstraintNbr < sketch->Constraints.getSize()); + + const Sketcher::Constraint * constraint = sketch->Constraints[ConstraintNbr]; + + switch (constraint->Type) { + case Sketcher::None: + assert( false ); + return false; + case Sketcher::Horizontal: + case Sketcher::Vertical: + case Sketcher::Coincident: + case Sketcher::PointOnObject: + case Sketcher::Parallel: + case Sketcher::Perpendicular: + case Sketcher::Tangent: + case Sketcher::Equal: + case Sketcher::Symmetric: + return true; + case Sketcher::Distance: + case Sketcher::DistanceX: + case Sketcher::DistanceY: + case Sketcher::Radius: + case Sketcher::Angle: + case Sketcher::SnellsLaw: + return ( constraint->First >= 0 || constraint->Second >= 0 || constraint->Third >= 0 ); + case Sketcher::InternalAlignment: + return true; + } + return false; + } + + bool isDriving() const { + assert(ConstraintNbr >= 0 && ConstraintNbr < sketch->Constraints.getSize()); + + return sketch->Constraints[ConstraintNbr]->isDriving; + } + + const Sketcher::SketchObject * sketch; + int ConstraintNbr; + QVariant value; +}; + +class ExpressionDelegate : public QStyledItemDelegate +{ +public: + ExpressionDelegate(QListWidget * _view) : view(_view) { } +protected: + QPixmap getIcon(const char* name, const QSize& size) const + { + QString key = QString::fromAscii("%1_%2x%3") + .arg(QString::fromAscii(name)) + .arg(size.width()) + .arg(size.height()); + QPixmap icon; + if (QPixmapCache::find(key, icon)) + return icon; + + icon = Gui::BitmapFactory().pixmapFromSvg(name, size); + if (!icon.isNull()) + QPixmapCache::insert(key, icon); + return icon; + } + + void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const { + QStyleOptionViewItemV4 options = option; + initStyleOption(&options, index); + + options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter); + + ConstraintItem * item = dynamic_cast(view->item(index.row())); + App::ObjectIdentifier path = item->sketch->Constraints.createPath(item->ConstraintNbr); + App::PropertyExpressionEngine::ExpressionInfo expr_info = item->sketch->getExpression(path); + + if (item->sketch->Constraints[item->ConstraintNbr]->isDriving && expr_info.expression) { + // Paint pixmap + int s = 2 * options.rect.height() / 4; + int margin = s; + QPixmap pixmap = getIcon(":/icons/bound-expression.svg", QSize(s, s)); + QRect r(options.rect); + + r.setTop(r.top() + (r.height() - s) / 2); + r.setLeft(r.right() - s); + r.setHeight(s); + r.moveLeft(r.left() - margin); + painter->drawPixmap(r, pixmap); + } + } + + QListWidget * view; }; ConstraintView::ConstraintView(QWidget *parent) : QListWidget(parent) { + ExpressionDelegate * delegate = new ExpressionDelegate(this); + setItemDelegate(delegate); } ConstraintView::~ConstraintView() @@ -140,13 +375,12 @@ void ConstraintView::contextMenuEvent (QContextMenuEvent* event) QAction* driven = menu.addAction(tr("Toggle to/from reference"), this, SLOT(updateDrivingStatus())); // if its the right constraint - if ((it->Type == Sketcher::Distance || - it->Type == Sketcher::DistanceX || - it->Type == Sketcher::DistanceY || - it->Type == Sketcher::Radius || - it->Type == Sketcher::Angle || - it->Type == Sketcher::SnellsLaw) && it->isEnforceable) { - + if ((it->constraintType() == Sketcher::Distance || + it->constraintType() == Sketcher::DistanceX || + it->constraintType() == Sketcher::DistanceY || + it->constraintType() == Sketcher::Radius || + it->constraintType() == Sketcher::Angle || + it->constraintType() == Sketcher::SnellsLaw) && it->isEnforceable()) { driven->setEnabled(true); } else{ @@ -156,7 +390,7 @@ void ConstraintView::contextMenuEvent (QContextMenuEvent* event) QAction* change = menu.addAction(tr("Change value"), this, SLOT(modifyCurrentItem())); QVariant v = item ? item->data(Qt::UserRole) : QVariant(); - change->setEnabled(v.isValid() && it->isDriving); + change->setEnabled(v.isValid() && it->isDriving()); } QAction* rename = menu.addAction(tr("Rename"), this, SLOT(renameCurrentItem()) @@ -172,6 +406,10 @@ void ConstraintView::contextMenuEvent (QContextMenuEvent* event) QAction* remove = menu.addAction(tr("Delete"), this, SLOT(deleteSelectedItems()), QKeySequence(QKeySequence::Delete)); remove->setEnabled(!items.isEmpty()); + + QAction* swap = menu.addAction(tr("Swap constraint names"), this, SLOT(swapNamedOfSelectedItems())); + swap->setEnabled(items.size() == 2); + menu.exec(event->globalPos()); } @@ -184,7 +422,7 @@ void ConstraintView::updateDrivingStatus() if (item){ ConstraintItem *it = dynamic_cast(item); - onUpdateDrivingStatus(item, !it->isDriving); + onUpdateDrivingStatus(item, !it->isDriving()); } } @@ -222,6 +460,35 @@ void ConstraintView::deleteSelectedItems() doc->commitTransaction(); } +void ConstraintView::swapNamedOfSelectedItems() +{ + QList items = selectedItems(); + + if (items.size() != 2) + return; + + ConstraintItem * item1 = static_cast(items[0]); + std::string escapedstr1 = Base::Tools::escapedUnicodeFromUtf8(item1->sketch->Constraints[item1->ConstraintNbr]->Name.c_str()); + ConstraintItem * item2 = static_cast(items[1]); + std::string escapedstr2 = Base::Tools::escapedUnicodeFromUtf8(item2->sketch->Constraints[item2->ConstraintNbr]->Name.c_str()); + std::stringstream ss; + + ss << "DummyConstraint" << rand(); + std::string tmpname = ss.str(); + + Gui::Command::openCommand("Swap constraint names"); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.renameConstraint(%d, '%s')", + item1->sketch->getNameInDocument(), + item1->ConstraintNbr, tmpname.c_str()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.renameConstraint(%d, '%s')", + item2->sketch->getNameInDocument(), + item2->ConstraintNbr, escapedstr1.c_str()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.renameConstraint(%d, '%s')", + item1->sketch->getNameInDocument(), + item1->ConstraintNbr, escapedstr2.c_str()); + Gui::Command::commitCommand(); +} + // ---------------------------------------------------------------------------- TaskSketcherConstrains::TaskSketcherConstrains(ViewProviderSketch *sketchView) @@ -338,9 +605,9 @@ void TaskSketcherConstrains::on_listWidgetConstraints_itemSelectionChanged(void) Gui::Selection().clearSelection(); QList items = ui->listWidgetConstraints->selectedItems(); for (QList::iterator it = items.begin(); it != items.end(); ++it) { - std::stringstream ss; - ss << "Constraint" << static_cast(*it)->ConstraintNbr + 1; - Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str(), ss.str().c_str()); + std::string constraint_name(Sketcher::PropertyConstraintList::getConstraintName(static_cast(*it)->ConstraintNbr)); + + Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str(), constraint_name.c_str()); } this->blockConnection(block); } @@ -351,12 +618,12 @@ void TaskSketcherConstrains::on_listWidgetConstraints_itemActivated(QListWidgetI if (!item) return; // if its the right constraint - if ((it->Type == Sketcher::Distance || - it->Type == Sketcher::DistanceX || - it->Type == Sketcher::DistanceY || - it->Type == Sketcher::Radius || - it->Type == Sketcher::Angle || - it->Type == Sketcher::SnellsLaw) && it->isDriving) { + if (it->constraintType() == Sketcher::Distance || + it->constraintType() == Sketcher::DistanceX || + it->constraintType() == Sketcher::DistanceY || + it->constraintType() == Sketcher::Radius || + it->constraintType() == Sketcher::Angle || + it->constraintType() == Sketcher::SnellsLaw) { EditDatumDialog *editDatumDialog = new EditDatumDialog(this->sketchView, it->ConstraintNbr); editDatumDialog->exec(false); @@ -377,227 +644,111 @@ void TaskSketcherConstrains::on_listWidgetConstraints_itemChanged(QListWidgetIte { if (!item || inEditMode) return; - ConstraintItem *it = dynamic_cast(item); - const std::vector< Sketcher::Constraint * > &vals = sketchView->getSketchObject()->Constraints.getValues(); - Sketcher::Constraint* v = vals[it->ConstraintNbr]; - QString name = it->data(Qt::EditRole).toString(); - if (name.isEmpty()) - name = QString::fromLatin1("Constraint%1").arg(it->ConstraintNbr+1); + inEditMode = true; - QString unitStr; - switch(v->Type) { - case Sketcher::Distance: - case Sketcher::DistanceX: - case Sketcher::DistanceY: - case Sketcher::Radius: - unitStr = Base::Quantity(v->Value,Base::Unit::Length).getUserString(); - break; - case Sketcher::Angle: - unitStr = Base::Quantity(Base::toDegrees(std::abs(v->Value)),Base::Unit::Angle).getUserString(); - break; - case Sketcher::SnellsLaw: - { - double n1 = 1.0; - double n2 = 1.0; - if (fabs(v->Value) >= 1) { - n2 = v->Value; - } else { - n1 = 1/v->Value; - } - unitStr = QString::fromLatin1("%1/%2").arg(n2).arg(n1); + const ConstraintItem *it = dynamic_cast(item); + const Sketcher::SketchObject * sketch = sketchView->getSketchObject(); + const std::vector< Sketcher::Constraint * > &vals = sketch->Constraints.getValues(); + const Sketcher::Constraint* v = vals[it->ConstraintNbr]; + const std::string currConstraintName = v->Name; + + std::string newName(Sketcher::PropertyConstraintList::getConstraintName(Base::Tools::toStdString(it->data(Qt::EditRole).toString()), it->ConstraintNbr)); + + if (newName != currConstraintName) { + std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(newName.c_str()); + + Gui::Command::openCommand("Rename sketch constraint"); + try { + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.renameConstraint(%d, '%s')", + sketch->getNameInDocument(), + it->ConstraintNbr, escapedstr.c_str()); + Gui::Command::commitCommand(); + } + catch (const Base::Exception & e) { + Gui::Command::abortCommand(); + + QMessageBox::critical(Gui::MainWindow::getInstance(), QString::fromAscii("Error"), + QString::fromAscii(e.what()), QMessageBox::Ok, QMessageBox::Ok); } - break; - default: - break; } - v->Name = (const char*)name.toUtf8(); - if (!unitStr.isEmpty()) { - inEditMode = true; - item->setData(Qt::UserRole, QString::fromLatin1("%1 (%2)") - .arg(name) - .arg(unitStr)); - inEditMode = false; - } + inEditMode = false; } void TaskSketcherConstrains::slotConstraintsChanged(void) { - QIcon hdist( Gui::BitmapFactory().pixmap("Constraint_HorizontalDistance") ); - QIcon vdist( Gui::BitmapFactory().pixmap("Constraint_VerticalDistance") ); - QIcon horiz( Gui::BitmapFactory().pixmap("Constraint_Horizontal") ); - QIcon vert ( Gui::BitmapFactory().pixmap("Constraint_Vertical") ); - QIcon lock ( Gui::BitmapFactory().pixmap("Sketcher_ConstrainLock") ); - QIcon coinc( Gui::BitmapFactory().pixmap("Constraint_PointOnPoint") ); - QIcon para ( Gui::BitmapFactory().pixmap("Constraint_Parallel") ); - QIcon perp ( Gui::BitmapFactory().pixmap("Constraint_Perpendicular") ); - QIcon tang ( Gui::BitmapFactory().pixmap("Constraint_Tangent") ); - QIcon dist ( Gui::BitmapFactory().pixmap("Constraint_Length") ); - QIcon radi ( Gui::BitmapFactory().pixmap("Constraint_Radius") ); - QIcon majradi ( Gui::BitmapFactory().pixmap("Constraint_Ellipse_Major_Radius") ); - QIcon minradi ( Gui::BitmapFactory().pixmap("Constraint_Ellipse_Minor_Radius") ); - QIcon angl ( Gui::BitmapFactory().pixmap("Constraint_InternalAngle") ); - QIcon ellipseXUAngl ( Gui::BitmapFactory().pixmap("Constraint_Ellipse_Axis_Angle") ); - QIcon equal( Gui::BitmapFactory().pixmap("Constraint_EqualLength") ); - QIcon pntoo( Gui::BitmapFactory().pixmap("Constraint_PointOnObject") ); - QIcon symm ( Gui::BitmapFactory().pixmap("Constraint_Symmetric") ); - QIcon snell ( Gui::BitmapFactory().pixmap("Constraint_SnellsLaw") ); - QIcon iaellipseminoraxis ( Gui::BitmapFactory().pixmap("Constraint_InternalAlignment_Ellipse_MinorAxis") ); - QIcon iaellipsemajoraxis ( Gui::BitmapFactory().pixmap("Constraint_InternalAlignment_Ellipse_MajorAxis") ); - QIcon iaellipsefocus1 ( Gui::BitmapFactory().pixmap("Constraint_InternalAlignment_Ellipse_Focus1") ); - QIcon iaellipsefocus2 ( Gui::BitmapFactory().pixmap("Constraint_InternalAlignment_Ellipse_Focus2") ); - QIcon iaellipseother ( Gui::BitmapFactory().pixmap("Constraint_InternalAlignment") ); - - QIcon hdist_driven ( Gui::BitmapFactory().pixmap("Constraint_HorizontalDistance_Driven") ); - QIcon vdist_driven( Gui::BitmapFactory().pixmap("Constraint_VerticalDistance_Driven") ); - QIcon dist_driven ( Gui::BitmapFactory().pixmap("Constraint_Length_Driven") ); - QIcon radi_driven ( Gui::BitmapFactory().pixmap("Constraint_Radius_Driven") ); - QIcon angl_driven ( Gui::BitmapFactory().pixmap("Constraint_InternalAngle_Driven") ); - QIcon snell_driven ( Gui::BitmapFactory().pixmap("Constraint_SnellsLaw_Driven") ); - assert(sketchView); // Build up ListView with the constraints - const std::vector< Sketcher::Constraint * > &vals = sketchView->getSketchObject()->Constraints.getValues(); + const Sketcher::SketchObject * sketch = sketchView->getSketchObject(); + const std::vector< Sketcher::Constraint * > &vals = sketch->Constraints.getValues(); - ui->listWidgetConstraints->clear(); - QString name; + /* Update constraint number */ + for (int i = 0; i < ui->listWidgetConstraints->count(); ++i) { + ConstraintItem * it = dynamic_cast(ui->listWidgetConstraints->item(i)); + assert(it != 0); + + it->ConstraintNbr = i; + it->value = QVariant(); + } + + /* Remove entries, if any */ + for (std::size_t i = ui->listWidgetConstraints->count(); i > vals.size(); --i) + delete ui->listWidgetConstraints->takeItem(i - 1); + + /* Add new entries, if any */ + for (std::size_t i = ui->listWidgetConstraints->count(); i < vals.size(); ++i) + ui->listWidgetConstraints->addItem(new ConstraintItem(sketch, i)); + + /* Update filtering */ int Filter = ui->comboBoxFilter->currentIndex(); + for(std::size_t i = 0; i < vals.size(); ++i) { + const Sketcher::Constraint * constraint = vals[i]; + ConstraintItem * it = static_cast(ui->listWidgetConstraints->item(i)); + bool visible = true; - int i=1; - for(std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();it!=vals.end();++it,++i){ - if ((*it)->Name.empty()) - name = QString::fromLatin1("Constraint%1").arg(i); - else - name = QString::fromUtf8((*it)->Name.c_str()); + /* Filter + 0 <=> All + 1 <=> Normal + 2 <=> Datums + 3 <=> Named + 4 <=> Non-Driving + */ - /* Filter - 0 <=> All - 1 <=> Normal - 2 <=> Datums - 3 <=> Named - 4 <=> Non-Driving - */ - switch((*it)->Type){ - case Sketcher::Horizontal: - if (Filter<2 || (Filter==3 && !(*it)->Name.empty())) - ui->listWidgetConstraints->addItem(new ConstraintItem(horiz,name,i-1,(*it)->Type)); - break; - case Sketcher::Vertical: - if (Filter<2 || (Filter==3 && !(*it)->Name.empty())) - ui->listWidgetConstraints->addItem(new ConstraintItem(vert,name,i-1,(*it)->Type)); - break; - case Sketcher::Coincident: - if (Filter<1 || (Filter==3 && !(*it)->Name.empty())) - ui->listWidgetConstraints->addItem(new ConstraintItem(coinc,name,i-1,(*it)->Type)); - break; - case Sketcher::PointOnObject: - if (Filter<2 || (Filter==3 && !(*it)->Name.empty())) - ui->listWidgetConstraints->addItem(new ConstraintItem(pntoo,name,i-1,(*it)->Type)); - break; - case Sketcher::Parallel: - if (Filter<2 || (Filter==3 && !(*it)->Name.empty())) - ui->listWidgetConstraints->addItem(new ConstraintItem(para,name,i-1,(*it)->Type)); - break; - case Sketcher::Perpendicular: - if (Filter<2 || (Filter==3 && !(*it)->Name.empty())) - ui->listWidgetConstraints->addItem(new ConstraintItem(perp,name,i-1,(*it)->Type)); - break; - case Sketcher::Tangent: - if (Filter<2 || (Filter==3 && !(*it)->Name.empty())) - ui->listWidgetConstraints->addItem(new ConstraintItem(tang,name,i-1,(*it)->Type)); - break; - case Sketcher::Equal: - if (Filter<2 || (Filter==3 && !(*it)->Name.empty())) - ui->listWidgetConstraints->addItem(new ConstraintItem(equal,name,i-1,(*it)->Type)); - break; - case Sketcher::Symmetric: - if (Filter<2 || (Filter==3 && !(*it)->Name.empty())) - ui->listWidgetConstraints->addItem(new ConstraintItem(symm,name,i-1,(*it)->Type)); - break; - case Sketcher::Distance: - if (((Filter<3 || !(*it)->Name.empty())) || (Filter==4 && !(*it)->isDriving)) { - ConstraintItem* item = new ConstraintItem((*it)->isDriving?dist:dist_driven,name,i-1,(*it)->Type,(*it)->isDriving, ((*it)->First>=0 || (*it)->Second>=0 || (*it)->Third>=0)); - name = QString::fromLatin1("%1 (%2)").arg(name).arg(Base::Quantity((*it)->Value,Base::Unit::Length).getUserString()); - item->setData(Qt::UserRole, name); - ui->listWidgetConstraints->addItem(item); - } - break; - case Sketcher::DistanceX: - if (((Filter<3 || !(*it)->Name.empty())) || (Filter==4 && !(*it)->isDriving)) { - ConstraintItem* item = new ConstraintItem((*it)->isDriving?hdist:hdist_driven,name,i-1,(*it)->Type,(*it)->isDriving, ((*it)->First>=0 || (*it)->Second>=0 || (*it)->Third>=0)); - name = QString::fromLatin1("%1 (%2)").arg(name).arg(Base::Quantity(std::abs((*it)->Value),Base::Unit::Length).getUserString()); - item->setData(Qt::UserRole, name); - ui->listWidgetConstraints->addItem(item); - } - break; - case Sketcher::DistanceY: - if (((Filter<3 || !(*it)->Name.empty())) || (Filter==4 && !(*it)->isDriving)) { - ConstraintItem* item = new ConstraintItem((*it)->isDriving?vdist:vdist_driven,name,i-1,(*it)->Type,(*it)->isDriving, ((*it)->First>=0 || (*it)->Second>=0 || (*it)->Third>=0)); - name = QString::fromLatin1("%1 (%2)").arg(name).arg(Base::Quantity(std::abs((*it)->Value),Base::Unit::Length).getUserString()); - item->setData(Qt::UserRole, name); - ui->listWidgetConstraints->addItem(item); - } - break; - case Sketcher::Radius: - if (((Filter<3 || !(*it)->Name.empty())) || (Filter==4 && !(*it)->isDriving)) { - ConstraintItem* item = new ConstraintItem((*it)->isDriving?radi:radi_driven,name,i-1,(*it)->Type,(*it)->isDriving, ((*it)->First>=0 || (*it)->Second>=0 || (*it)->Third>=0)); - name = QString::fromLatin1("%1 (%2)").arg(name).arg(Base::Quantity((*it)->Value,Base::Unit::Length).getUserString()); - item->setData(Qt::UserRole, name); - ui->listWidgetConstraints->addItem(item); - } - break; - case Sketcher::Angle: - if (((Filter<3 || !(*it)->Name.empty())) || (Filter==4 && !(*it)->isDriving)) { - ConstraintItem* item = new ConstraintItem((*it)->isDriving?angl:angl_driven,name,i-1,(*it)->Type,(*it)->isDriving, ((*it)->First>=0 || (*it)->Second>=0 || (*it)->Third>=0)); - name = QString::fromLatin1("%1 (%2)").arg(name).arg(Base::Quantity(Base::toDegrees(std::abs((*it)->Value)),Base::Unit::Angle).getUserString()); - item->setData(Qt::UserRole, name); - ui->listWidgetConstraints->addItem(item); - } - break; - case Sketcher::SnellsLaw: - if (((Filter<3 || !(*it)->Name.empty())) || (Filter==4 && !(*it)->isDriving)) { - ConstraintItem* item = new ConstraintItem((*it)->isDriving?snell:snell_driven,name,i-1,(*it)->Type,(*it)->isDriving, ((*it)->First>=0 || (*it)->Second>=0 || (*it)->Third>=0)); + bool showNormal = (Filter < 2); + bool showDatums = (Filter < 3); + bool showNamed = (Filter == 3 && !(constraint->Name.empty())); + bool showNonDriving = (Filter == 4 && !constraint->isDriving); - double v = (*it)->Value; - double n1 = 1.0; - double n2 = 1.0; - if (fabs(v) >= 1) { - n2 = v; - } else { - n1 = 1/v; - } - name = QString::fromLatin1("%1 (%2/%3)").arg(name).arg(n2).arg(n1); - item->setData(Qt::UserRole, name); - ui->listWidgetConstraints->addItem(item); - } - break; - case Sketcher::InternalAlignment: - if (Filter<2 || (Filter==3 && !(*it)->Name.empty())) - switch((*it)->AlignmentType){ - case Sketcher::EllipseMajorDiameter: - ui->listWidgetConstraints->addItem(new ConstraintItem(iaellipsemajoraxis,name,i-1,(*it)->Type)); - break; - case Sketcher::EllipseMinorDiameter: - ui->listWidgetConstraints->addItem(new ConstraintItem(iaellipseminoraxis,name,i-1,(*it)->Type)); - break; - case Sketcher::EllipseFocus1: - ui->listWidgetConstraints->addItem(new ConstraintItem(iaellipsefocus1,name,i-1,(*it)->Type)); - break; - case Sketcher::EllipseFocus2: - ui->listWidgetConstraints->addItem(new ConstraintItem(iaellipsefocus2,name,i-1,(*it)->Type)); - break; - case Sketcher::Undef: - default: - ui->listWidgetConstraints->addItem(new ConstraintItem(iaellipseother,name,i-1,(*it)->Type)); - break; - } - break; - default: - ui->listWidgetConstraints->addItem(new ConstraintItem(name,i-1,(*it)->Type)); - break; + switch(constraint->Type) { + case Sketcher::Horizontal: + case Sketcher::Vertical: + case Sketcher::Coincident: + case Sketcher::PointOnObject: + case Sketcher::Parallel: + case Sketcher::Perpendicular: + case Sketcher::Tangent: + case Sketcher::Equal: + case Sketcher::Symmetric: + visible = showNormal || showNamed; + break; + case Sketcher::Distance: + case Sketcher::DistanceX: + case Sketcher::DistanceY: + case Sketcher::Radius: + case Sketcher::Angle: + case Sketcher::SnellsLaw: + visible = (showDatums || showNamed || showNonDriving); + break; + case Sketcher::InternalAlignment: + visible = (showNormal || showNamed); + default: + break; } + + it->setHidden(!visible); + it->setData(Qt::EditRole, Base::Tools::fromStdString(constraint->Name)); } } diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstrains.h b/src/Mod/Sketcher/Gui/TaskSketcherConstrains.h index ee5dbd490c..04f8a81512 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstrains.h +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstrains.h @@ -60,6 +60,7 @@ protected Q_SLOTS: void deleteSelectedItems(); void doSelectConstraints(); void updateDrivingStatus(); + void swapNamedOfSelectedItems(); }; class TaskSketcherConstrains : public Gui::TaskView::TaskBox, public Gui::SelectionObserver diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index 5721d5b80e..dc9bc0d3fd 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -689,19 +689,18 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe case STATUS_SELECT_Constraint: if (pp) { for(std::set::iterator it = edit->PreselectConstraintSet.begin(); it != edit->PreselectConstraintSet.end(); ++it) { - std::stringstream ss; - ss << "Constraint" << *it + 1; + std::string constraintName(Sketcher::PropertyConstraintList::getConstraintName(*it)); // If the constraint already selected remove if (Gui::Selection().isSelected(getSketchObject()->getDocument()->getName() - ,getSketchObject()->getNameInDocument(),ss.str().c_str()) ) { + ,getSketchObject()->getNameInDocument(),constraintName.c_str()) ) { Gui::Selection().rmvSelection(getSketchObject()->getDocument()->getName() - ,getSketchObject()->getNameInDocument(), ss.str().c_str()); + ,getSketchObject()->getNameInDocument(), constraintName.c_str()); } else { // Add constraint to current selection Gui::Selection().addSelection(getSketchObject()->getDocument()->getName() ,getSketchObject()->getNameInDocument() - ,ss.str().c_str() + ,constraintName.c_str() ,pp->getPoint()[0] ,pp->getPoint()[1] ,pp->getPoint()[2]); @@ -1443,7 +1442,7 @@ void ViewProviderSketch::onSelectionChanged(const Gui::SelectionChanges& msg) this->updateColor(); } else if (shapetype.size() > 10 && shapetype.substr(0,10) == "Constraint") { - int ConstrId = std::atoi(&shapetype[10]) - 1; + int ConstrId = Sketcher::PropertyConstraintList::getIndexFromConstraintName(shapetype); edit->SelConstraintSet.insert(ConstrId); this->drawConstraintIcons(); this->updateColor(); @@ -1488,7 +1487,7 @@ void ViewProviderSketch::onSelectionChanged(const Gui::SelectionChanges& msg) this->updateColor(); } else if (shapetype.size() > 10 && shapetype.substr(0,10) == "Constraint") { - int ConstrId = std::atoi(&shapetype[10]) - 1; + int ConstrId = Sketcher::PropertyConstraintList::getIndexFromConstraintName(shapetype); edit->SelConstraintSet.erase(ConstrId); this->drawConstraintIcons(); this->updateColor(); @@ -1742,12 +1741,12 @@ bool ViewProviderSketch::detectPreselection(const SoPickedPoint *Point, } else if (constrIndices.empty() == false && constrIndices != edit->PreselectConstraintSet) { // if a constraint is hit bool accepted = true; for(std::set::iterator it = constrIndices.begin(); it != constrIndices.end(); ++it) { - std::stringstream ss; - ss << "Constraint" << *it + 1; + std::string constraintName(Sketcher::PropertyConstraintList::getConstraintName(*it)); + accepted &= Gui::Selection().setPreselect(getSketchObject()->getDocument()->getName() ,getSketchObject()->getNameInDocument() - ,ss.str().c_str() + ,constraintName.c_str() ,Point->getPoint()[0] ,Point->getPoint()[1] ,Point->getPoint()[2]); @@ -3525,9 +3524,9 @@ Restart: if ((Constr->Type == DistanceX || Constr->Type == DistanceY) && Constr->FirstPos != Sketcher::none && Constr->Second == Constraint::GeoUndef) // display negative sign for absolute coordinates - asciiText->string = SbString(Base::Quantity(Constr->Value,Base::Unit::Length).getUserString().toUtf8().constData()); + asciiText->string = SbString(Base::Quantity(Constr->getValue(),Base::Unit::Length).getUserString().toUtf8().constData()); else // hide negative sign - asciiText->string = SbString(Base::Quantity(std::abs(Constr->Value),Base::Unit::Length).getUserString().toUtf8().constData()); + asciiText->string = SbString(Base::Quantity(std::abs(Constr->getValue()),Base::Unit::Length).getUserString().toUtf8().constData()); if (Constr->Type == Distance) asciiText->datumtype = SoDatumLabel::DISTANCE; @@ -3826,7 +3825,7 @@ Restart: break; SoDatumLabel *asciiText = dynamic_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL)); - asciiText->string = SbString(Base::Quantity(Base::toDegrees(std::abs(Constr->Value)),Base::Unit::Angle).getUserString().toUtf8().constData()); + asciiText->string = SbString(Base::Quantity(Base::toDegrees(std::abs(Constr->getValue())),Base::Unit::Angle).getUserString().toUtf8().constData()); asciiText->datumtype = SoDatumLabel::ANGLE; asciiText->param1 = Constr->LabelDistance; asciiText->param2 = startangle; @@ -3880,7 +3879,7 @@ Restart: SbVec3f p2(pnt2.x,pnt2.y,zConstr); SoDatumLabel *asciiText = dynamic_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL)); - asciiText->string = SbString(Base::Quantity(Constr->Value,Base::Unit::Length).getUserString().toUtf8().constData()); + asciiText->string = SbString(Base::Quantity(Constr->getValue(),Base::Unit::Length).getUserString().toUtf8().constData()); asciiText->datumtype = SoDatumLabel::RADIUS; asciiText->param1 = Constr->LabelDistance; @@ -4129,7 +4128,9 @@ void ViewProviderSketch::updateData(const App::Property *prop) if(getSketchObject()->getExternalGeometryCount()+getSketchObject()->getHighestCurveIndex() + 1 == getSketchObject()->getSolvedSketch().getGeometrySize()) { - draw(false); + Gui::MDIView *mdi = Gui::Application::Instance->activeDocument()->getActiveView(); + if (mdi->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) + draw(false); signalConstraintsChanged(); signalElementsChanged(); @@ -4778,7 +4779,7 @@ bool ViewProviderSketch::onDelete(const std::vector &subList) } else if (*it == "RootPoint") { delCoincidents.insert(-1); } else if (it->size() > 10 && it->substr(0,10) == "Constraint") { - int ConstrId = std::atoi(it->substr(10,4000).c_str()) - 1; + int ConstrId = Sketcher::PropertyConstraintList::getIndexFromConstraintName(*it); delConstraints.insert(ConstrId); } }