myImmediateListeners[theMessage->eventID().eventText()]->processEvent(theMessage);
}
// if it is grouped message, just accumulate it
- if (isGroup) {
+ if (isGroup && myFlushed.find(theMessage->eventID().myID) == myFlushed.end()) {
boost::shared_ptr<Events_MessageGroup> aGroup =
boost::dynamic_pointer_cast<Events_MessageGroup>(theMessage);
if (aGroup) {
std::map<char*, boost::shared_ptr<Events_Message>>::iterator aMyGroup =
myGroups.find(theID.eventText());
if (aMyGroup != myGroups.end()) { // really sends
+ myFlushed.insert(theID.myID);
boost::shared_ptr<Events_Message> aGroup = aMyGroup->second;
myGroups.erase(aMyGroup);
send(aGroup, false);
+ myFlushed.erase(myFlushed.find(theID.myID));
}
}
{
myFlushActive = theActivate;
}
+
+void Events_Loop::clear(const Events_ID& theID)
+{
+ std::map<char*, boost::shared_ptr<Events_Message>>::iterator aMyGroup =
+ myGroups.find(theID.eventText());
+ if (aMyGroup != myGroups.end()) { // really sends
+ myGroups.erase(aMyGroup);
+ }
+}
+
+void Events_Loop::autoFlush(const Events_ID& theID, const bool theAuto)
+{
+ if (theAuto)
+ myFlushed.insert(theID.myID);
+ else
+ myFlushed.erase(myFlushed.find(theID.myID));
+}
#include <Events_Listener.h>
#include <map>
+#include <set>
#include <list>
class Events_MessageGroup;
/// map from event ID to groupped messages (accumulated on flush)
std::map<char*, boost::shared_ptr<Events_Message> > myGroups;
+ ///< set of messages that are flushed right now, so they are not grouped
+ std::set<char*> myFlushed;
+
/// to process flushes or not
bool myFlushActive;
//! Allows to disable flushes: needed in synchronization of document mechanism
//! (to synchronize all and only then flush create, update, etc in correct order)
EVENTS_EXPORT void activateFlushes(const bool theActivate);
+
+ //! Clears all collected messages
+ EVENTS_EXPORT void clear(const Events_ID& theID);
+
+ //! Enables flush without grouping for the given message
+ EVENTS_EXPORT void autoFlush(const Events_ID& theID, const bool theAuto = true);
};
#endif
void GeomData_Dir::setValue(const boost::shared_ptr<GeomAPI_Dir>& theDir)
{
setValue(theDir->x(), theDir->y(), theDir->z());
- owner()->data()->sendAttributeUpdated(this);
}
double GeomData_Dir::x() const
void GeomData_Point::setValue(const boost::shared_ptr<GeomAPI_Pnt>& thePoint)
{
setValue(thePoint->x(), thePoint->y(), thePoint->z());
- owner()->data()->sendAttributeUpdated(this);
}
double GeomData_Point::x() const
void GeomData_Point2D::setValue(const boost::shared_ptr<GeomAPI_Pnt2d>& thePoint)
{
setValue(thePoint->x(), thePoint->y());
- owner()->data()->sendAttributeUpdated(this);
}
double GeomData_Point2D::x() const
void Model_Document::finishOperation()
{
- // finish for all subs first: to avoid nested finishing and "isOperation" calls problems inside
- std::set<std::string>::iterator aSubIter = mySubs.begin();
- for (; aSubIter != mySubs.end(); aSubIter++)
- subDoc(*aSubIter)->finishOperation();
-
// just to be sure that everybody knows that changes were performed
if (!myDoc->HasOpenCommand() && myNestedNum != -1)
boost::static_pointer_cast<Model_Session>(Model_Session::get())
aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TOHIDE));
aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
+ // this must be here just after everything is finished but before real transaction stop
+ // to avoid messages about modifications outside of the transaction
+ // and to rebuild everything after all updates and creates
+ if (Model_Session::get()->moduleDocument().get() == this) { // once for root document
+ Events_Loop::loop()->autoFlush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
+ static boost::shared_ptr<Events_Message> aFinishMsg
+ (new Events_Message(Events_Loop::eventByName("FinishOperation")));
+ Events_Loop::loop()->send(aFinishMsg);
+ Events_Loop::loop()->autoFlush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED), false);
+ }
+ // to avoid "updated" message appearance by updater
+ //aLoop->clear(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
+
if (!myDoc->HasOpenCommand() && myNestedNum != -1)
boost::static_pointer_cast<Model_Session>(Model_Session::get())
->setCheckTransactions(true); // for nested transaction commit
+ // finish for all subs first: to avoid nested finishing and "isOperation" calls problems inside
+ std::set<std::string>::iterator aSubIter = mySubs.begin();
+ for (; aSubIter != mySubs.end(); aSubIter++)
+ subDoc(*aSubIter)->finishOperation();
+
if (myNestedNum != -1) // this nested transaction is owervritten
myNestedNum++;
if (!myDoc->HasOpenCommand()) {
const Events_ID& theEvent)
: ModelAPI_ObjectUpdatedMessage(theEvent, 0)
{
- if (theObject)
+ if (theObject) {
myObjects.insert(theObject);
+ }
}
const std::set<ObjectPtr>& Model_ObjectUpdatedMessage::objects() const
void Model_Session::startOperation()
{
ROOT_DOC->startOperation();
+ static boost::shared_ptr<Events_Message> aStartedMsg
+ (new Events_Message(Events_Loop::eventByName("StartOperation")));
+ Events_Loop::loop()->send(aStartedMsg);
}
void Model_Session::finishOperation()
{
ROOT_DOC->finishOperation();
- static boost::shared_ptr<Events_Message> aFinishMsg
- (new Events_Message(Events_Loop::eventByName("FinishOperation")));
- Events_Loop::loop()->send(aFinishMsg);
}
void Model_Session::abortOperation()
aLoop->registerListener(this, kOpFinishEvent);
static const Events_ID kOpAbortEvent = aLoop->eventByName("AbortOperation");
aLoop->registerListener(this, kOpAbortEvent);
+ static const Events_ID kOpStartEvent = aLoop->eventByName("StartOperation");
+ aLoop->registerListener(this, kOpStartEvent);
Config_PropManager::registerProp("Model update", "automatic_rebuild", "Rebuild automatically",
Config_Prop::Bool, "false");
static const Events_ID kUpdatedEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED);
static const Events_ID kOpFinishEvent = aLoop->eventByName("FinishOperation");
static const Events_ID kOpAbortEvent = aLoop->eventByName("AbortOperation");
+ static const Events_ID kOpStartEvent = aLoop->eventByName("StartOperation");
bool isAutomaticChanged = false;
if (theMessage->eventID() == kChangedEvent) { // automatic and manual rebuild flag is changed
isAutomatic =
} else if (theMessage->eventID() == kCreatedEvent || theMessage->eventID() == kUpdatedEvent) {
boost::shared_ptr<ModelAPI_ObjectUpdatedMessage> aMsg =
boost::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
- if (theMessage->eventID() == kCreatedEvent) {
- myJustCreatedOrUpdated.clear();
- }
const std::set<ObjectPtr>& anObjs = aMsg->objects();
std::set<ObjectPtr>::const_iterator anObjIter = anObjs.cbegin();
- for(; anObjIter != anObjs.cend(); anObjIter++)
+ for(; anObjIter != anObjs.cend(); anObjIter++) {
myJustCreatedOrUpdated.insert(*anObjIter);
- } else if (theMessage->eventID() == kOpFinishEvent || theMessage->eventID() == kOpAbortEvent) {
+ }
+ } else if (theMessage->eventID() == kOpStartEvent) {
myJustCreatedOrUpdated.clear();
- return;
+ return; // we don't need the update only on operation start (caused problems in PartSet_Listener::processEvent)
+ } else if (theMessage->eventID() == kOpFinishEvent || theMessage->eventID() == kOpAbortEvent) {
+ if (isAutomatic == false) { // Apply button now works as "Rebuild"
+ isAutomaticChanged = true;
+ isAutomatic = true;
+ }
}
if (isExecuted)
//Events_LongOp::start(this);
isExecuted = true;
- list<boost::shared_ptr<ModelAPI_Document> > aDocs;
+ std::list<boost::shared_ptr<ModelAPI_Document> > aDocs;
boost::shared_ptr<ModelAPI_ObjectUpdatedMessage> aMsg =
boost::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
if (aMsg) myInitial = aMsg->objects();
aDocs.push_back((*aFIter)->document());
}
// iterate all features of features-documents to update them (including hidden)
+ std::set<boost::shared_ptr<ModelAPI_Document> > alreadyUsed;
list<boost::shared_ptr<ModelAPI_Document> >::iterator aDIter = aDocs.begin();
for (; aDIter != aDocs.end(); aDIter++) {
+ if (alreadyUsed.find(*aDIter) != alreadyUsed.end())
+ continue;
+ alreadyUsed.insert(*aDIter);
int aNbFeatures = (*aDIter)->size(ModelAPI_Feature::group(), true);
for (int aFIndex = 0; aFIndex < aNbFeatures; aFIndex++) {
FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(
}
}
myUpdated.clear();
- if (theMessage->eventID() == kUpdatedEvent) {
- // flush updates without execution now (updates are caused by this process)
- aLoop->flush(kUpdatedEvent);
- }
// flush to update display
static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
aLoop->flush(EVENT_DISP);
//Events_LongOp::end(this);
if (isAutomaticChanged) isAutomatic = false;
+
+ if (theMessage->eventID() == kOpFinishEvent || theMessage->eventID() == kOpAbortEvent) {
+ myJustCreatedOrUpdated.clear();
+ }
+
isExecuted = false;
}
+void Model_Update::redisplayWithResults(FeaturePtr theFeature) {
+ // maske updated and redisplay all results
+ static Events_ID EVENT_DISP = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
+ const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
+ std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
+ for (; aRIter != aResults.cend(); aRIter++) {
+ boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
+ aRes->data()->mustBeUpdated(false);
+ myUpdated[aRes] = true;
+ ModelAPI_EventCreator::get()->sendUpdated(aRes, EVENT_DISP);
+ }
+ // to redisplay "presentable" feature (for ex. distance constraint)
+ ModelAPI_EventCreator::get()->sendUpdated(theFeature, EVENT_DISP);
+ theFeature->data()->mustBeUpdated(false);
+}
+
bool Model_Update::updateFeature(FeaturePtr theFeature)
{
// check it is already processed
// execute feature if it must be updated
if (aMustbeUpdated) {
-
if (boost::dynamic_pointer_cast<Model_Document>(theFeature->document())->executeFeatures() ||
!theFeature->isPersistentResult()) {
ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
boost::shared_ptr<ModelAPI_AttributeSelectionList> aSel =
boost::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(*aRefsIter);
for(int a = aSel->size() - 1; a >= 0; a--) {
- aSel->value(a)->update();
+ aSel->value(a)->update();
}
}
// execute in try-catch to avoid internal problems of the feature
"Feature " + theFeature->getKind() + " has failed during the execution");
theFeature->eraseResults();
}
- theFeature->data()->mustBeUpdated(false);
- const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
- std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
- for (; aRIter != aResults.cend(); aRIter++) {
- boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
- aRes->data()->mustBeUpdated(false);
- }
- } else {
+ redisplayWithResults(theFeature);
+ } else { // must be updatet, but not updated yet
theFeature->data()->mustBeUpdated(true);
const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
aRes->data()->mustBeUpdated(true);
}
- aMustbeUpdated = false;
}
} else {
theFeature->eraseResults();
+ redisplayWithResults(theFeature); // result also must be updated
}
+ } else { // for automatically updated features (on abort, etc) it is necessary to redisplay anyway
+ redisplayWithResults(theFeature);
}
- // redisplay all results
- static Events_ID EVENT_DISP = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
- const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
- std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
- for (; aRIter != aResults.cend(); aRIter++) {
- boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
- myUpdated[aRes] = true;
- ModelAPI_EventCreator::get()->sendUpdated(aRes, EVENT_DISP);
- }
- // to redisplay "presentable" feature (for ex. distance constraint)
- ModelAPI_EventCreator::get()->sendUpdated(theFeature, EVENT_DISP);
} else { // returns also true is results were updated: for sketch that refers to sub-features but results of sub-features were changed
const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
/// Recoursively checks and updates the object (result or feature) if needed (calls updateFeature)
/// Returns true if object was updated.
bool updateObject(boost::shared_ptr<ModelAPI_Object> theObject);
+ /// Sends the redisplay events for feature and results, updates the updated status
+ void redisplayWithResults(boost::shared_ptr<ModelAPI_Feature> theFeature);
};
#endif
FeaturePtr aFeature = aCreationOp->feature();
const std::list<ResultPtr>& aResults = aFeature->results();
boost::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
- boost::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
+ boost::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
std::set<ObjectPtr> aFeatures = aUpdMsg->objects();
std::set<ObjectPtr>::const_iterator aObjIt, aNoObj = aFeatures.cend();
void SketchPlugin_Line::attributeChanged() {
static bool myIsUpdated = false; // to avoid infinitive cycle on attrubtes change
boost::shared_ptr<GeomAPI_Shape> aSelection = data()->selection(EXTERNAL_ID())->value();
- if (aSelection && !myIsUpdated) { // update arguments due to the selection value
+ // update arguments due to the selection value
+ if (aSelection && !aSelection->isNull() && !myIsUpdated) {
myIsUpdated = true;
boost::shared_ptr<GeomAPI_Edge> anEdge( new GeomAPI_Edge(aSelection));
boost::shared_ptr<GeomDataAPI_Point2D> aStartAttr =
}
void SketchPlugin_Sketch::attributeChanged() {
- static bool myIsUpdated = false; // to avoid infinitive cycle on attrubtes change
+ static bool kIsUpdated = false; // to avoid infinitive cycle on attrubtes change
+ static bool kIsAttrChanged = false;
boost::shared_ptr<GeomAPI_Shape> aSelection =
data()->selection(SketchPlugin_Feature::EXTERNAL_ID())->value();
- if (aSelection && !myIsUpdated) { // update arguments due to the selection value
- myIsUpdated = true;
+ if (aSelection && !kIsUpdated) { // update arguments due to the selection value
+ kIsUpdated = true;
// update the sketch plane
boost::shared_ptr<GeomAPI_Pln> aPlane = GeomAlgoAPI_FaceBuilder::plane(aSelection);
if (aPlane) {
boost::shared_ptr<GeomAPI_Dir> aYDir(new GeomAPI_Dir(aNormDir->cross(aTempDir)));
boost::shared_ptr<GeomAPI_Dir> aXDir(new GeomAPI_Dir(aYDir->cross(aNormDir)));
- boost::shared_ptr<GeomDataAPI_Point> anOrigin = boost::dynamic_pointer_cast<GeomDataAPI_Point>(
- data()->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
+ kIsAttrChanged = false; // track the attributes were really changed during the plane update
+ boost::shared_ptr<GeomDataAPI_Point> anOrigin = boost::dynamic_pointer_cast
+ <GeomDataAPI_Point>(data()->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
anOrigin->setValue(anOrigPnt);
boost::shared_ptr<GeomDataAPI_Dir> aNormal = boost::dynamic_pointer_cast<GeomDataAPI_Dir>(
data()->attribute(SketchPlugin_Sketch::NORM_ID()));
data()->attribute(SketchPlugin_Sketch::DIRY_ID()));
aDirY->setValue(aYDir);
boost::shared_ptr<GeomAPI_Dir> aDir = aPlane->direction();
+
+ if (kIsAttrChanged) {
+ // the plane was changed, so reexecute sub-elements to update shapes (located in new plane)
+ ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
+ list<ObjectPtr> aSubs = data()->reflist(SketchPlugin_Sketch::FEATURES_ID())->list();
+ for(list<ObjectPtr>::iterator aSubIt = aSubs.begin(); aSubIt != aSubs.end(); aSubIt++) {
+ boost::shared_ptr<SketchPlugin_Feature> aFeature =
+ boost::dynamic_pointer_cast<SketchPlugin_Feature>(*aSubIt);
+ if (aFeature && aFactory->validate(aFeature)) {
+ aFeature->execute();
+ }
+ }
+ kIsAttrChanged = false;
+ }
}
- myIsUpdated = false;
+ kIsUpdated = false;
+ } else if (kIsUpdated) { // other attributes are updated during the selection comupation
+ kIsAttrChanged = true;
}
}