]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Merge branch 'master' of newgeom:newgeom
authorvsv <vitaly.smetannikov@opencascade.com>
Wed, 23 Apr 2014 14:05:43 +0000 (18:05 +0400)
committervsv <vitaly.smetannikov@opencascade.com>
Wed, 23 Apr 2014 14:05:43 +0000 (18:05 +0400)
Conflicts:
src/ModuleBase/ModuleBase_Operation.h
src/PartSet/PartSet_OperationSketchBase.cpp

116 files changed:
CMakeLists.txt
src/Config/Config_FeatureMessage.cpp
src/Config/Config_FeatureMessage.h
src/Config/Config_FeatureReader.cpp
src/Config/plugin-Sketch.xml [deleted file]
src/ConstructionPlugin/CMakeLists.txt
src/ConstructionPlugin/ConstructionPlugin_Plugin.cpp [new file with mode: 0644]
src/ConstructionPlugin/ConstructionPlugin_Plugin.cxx [deleted file]
src/ConstructionPlugin/ConstructionPlugin_Point.cpp [new file with mode: 0644]
src/ConstructionPlugin/ConstructionPlugin_Point.cxx [deleted file]
src/Event/CMakeLists.txt
src/Event/Event_Listener.cpp [new file with mode: 0644]
src/Event/Event_Listener.cxx [deleted file]
src/Event/Event_Loop.cpp [new file with mode: 0644]
src/Event/Event_Loop.cxx [deleted file]
src/Event/Event_Message.cpp [new file with mode: 0644]
src/Event/Event_Message.cxx [deleted file]
src/GeomAPI/CMakeLists.txt [new file with mode: 0644]
src/GeomAPI/GeomAPI.h [new file with mode: 0644]
src/GeomAPI/GeomAPI.i [new file with mode: 0644]
src/GeomAPI/GeomAPI_Dir.cpp [new file with mode: 0644]
src/GeomAPI/GeomAPI_Dir.h [new file with mode: 0644]
src/GeomAPI/GeomAPI_Interface.cpp [new file with mode: 0644]
src/GeomAPI/GeomAPI_Interface.h [new file with mode: 0644]
src/GeomAPI/GeomAPI_Pnt.cpp [new file with mode: 0644]
src/GeomAPI/GeomAPI_Pnt.h [new file with mode: 0644]
src/GeomAPI/GeomAPI_Shape.cpp [new file with mode: 0644]
src/GeomAPI/GeomAPI_Shape.h [new file with mode: 0644]
src/GeomAlgoAPI/CMakeLists.txt [new file with mode: 0644]
src/GeomAlgoAPI/GeomAlgoAPI.h [new file with mode: 0644]
src/GeomAlgoAPI/GeomAlgoAPI.i [new file with mode: 0644]
src/GeomAlgoAPI/GeomAlgoAPI_FaceBuilder.cpp [new file with mode: 0644]
src/GeomAlgoAPI/GeomAlgoAPI_FaceBuilder.h [new file with mode: 0644]
src/Model/CMakeLists.txt
src/Model/Model_Application.cpp [new file with mode: 0644]
src/Model/Model_Application.cxx [deleted file]
src/Model/Model_AttributeDocRef.cpp [new file with mode: 0644]
src/Model/Model_AttributeDocRef.cxx [deleted file]
src/Model/Model_AttributeDouble.cpp [new file with mode: 0644]
src/Model/Model_AttributeDouble.cxx [deleted file]
src/Model/Model_Data.cpp [new file with mode: 0644]
src/Model/Model_Data.cxx [deleted file]
src/Model/Model_Document.cpp [new file with mode: 0644]
src/Model/Model_Document.cxx [deleted file]
src/Model/Model_Events.cpp [new file with mode: 0644]
src/Model/Model_Events.cxx [deleted file]
src/Model/Model_Iterator.cpp [new file with mode: 0644]
src/Model/Model_Iterator.cxx [deleted file]
src/Model/Model_PluginManager.cpp [new file with mode: 0644]
src/Model/Model_PluginManager.cxx [deleted file]
src/ModelAPI/CMakeLists.txt
src/ModelAPI/ModelAPI_PluginManager.cpp [new file with mode: 0644]
src/ModelAPI/ModelAPI_PluginManager.cxx [deleted file]
src/ModuleBase/ModuleBase_Operation.cpp
src/ModuleBase/ModuleBase_Operation.h
src/ModuleBase/ModuleBase_PropPanelOperation.h
src/PartSet/CMakeLists.txt
src/PartSet/PartSet_Module.cpp
src/PartSet/PartSet_Module.h
src/PartSet/PartSet_OperationSketch.cpp
src/PartSet/PartSet_OperationSketch.h
src/PartSet/PartSet_OperationSketchBase.cpp
src/PartSet/PartSet_OperationSketchBase.h
src/PartSetPlugin/CMakeLists.txt
src/PartSetPlugin/PartSetPlugin_Part.cpp [new file with mode: 0644]
src/PartSetPlugin/PartSetPlugin_Part.cxx [deleted file]
src/PartSetPlugin/PartSetPlugin_Plugin.cpp [new file with mode: 0644]
src/PartSetPlugin/PartSetPlugin_Plugin.cxx [deleted file]
src/PyConsole/CMakeLists.txt
src/PyConsole/PyConsole_Console.cpp [new file with mode: 0644]
src/PyConsole/PyConsole_Console.cxx [deleted file]
src/PyConsole/PyConsole_Editor.cpp [new file with mode: 0644]
src/PyConsole/PyConsole_Editor.cxx [deleted file]
src/PyConsole/PyConsole_EnhEditor.cpp [new file with mode: 0644]
src/PyConsole/PyConsole_EnhEditor.cxx [deleted file]
src/PyConsole/PyConsole_EnhInterp.cpp [new file with mode: 0644]
src/PyConsole/PyConsole_EnhInterp.cxx [deleted file]
src/PyConsole/PyConsole_Event.cpp [new file with mode: 0644]
src/PyConsole/PyConsole_Event.cxx [deleted file]
src/PyConsole/PyConsole_Interp.cpp [new file with mode: 0644]
src/PyConsole/PyConsole_Interp.cxx [deleted file]
src/PyConsole/PyConsole_Request.cpp [new file with mode: 0644]
src/PyConsole/PyConsole_Request.cxx [deleted file]
src/PyEvent/CMakeLists.txt
src/PyEvent/PyEvent_Event.cpp [new file with mode: 0644]
src/PyEvent/PyEvent_Event.cxx [deleted file]
src/PyEvent/PyEvent_EventFilter.cpp [new file with mode: 0644]
src/PyEvent/PyEvent_EventFilter.cxx [deleted file]
src/PyInterp/CMakeLists.txt
src/PyInterp/PyInterp_Dispatcher.cpp [new file with mode: 0644]
src/PyInterp/PyInterp_Dispatcher.cxx [deleted file]
src/PyInterp/PyInterp_Event.cpp [new file with mode: 0644]
src/PyInterp/PyInterp_Event.cxx [deleted file]
src/PyInterp/PyInterp_Interp.cpp [new file with mode: 0644]
src/PyInterp/PyInterp_Interp.cxx [deleted file]
src/PyInterp/PyInterp_Request.cpp [new file with mode: 0644]
src/PyInterp/PyInterp_Request.cxx [deleted file]
src/SketchPlugin/CMakeLists.txt
src/SketchPlugin/SketchPlugin_Feature.cpp
src/SketchPlugin/SketchPlugin_Feature.h
src/SketchPlugin/SketchPlugin_Sketch.cpp
src/SketchPlugin/SketchPlugin_Sketch.h
src/XGUI/CMakeLists.txt
src/XGUI/XGUI_Command.cpp
src/XGUI/XGUI_Command.h
src/XGUI/XGUI_Displayer.cpp
src/XGUI/XGUI_Displayer.h
src/XGUI/XGUI_MainMenu.cpp
src/XGUI/XGUI_MainMenu.h
src/XGUI/XGUI_MenuGroupPanel.cpp
src/XGUI/XGUI_MenuGroupPanel.h
src/XGUI/XGUI_Module.h
src/XGUI/XGUI_OperationMgr.cpp [new file with mode: 0644]
src/XGUI/XGUI_OperationMgr.h [new file with mode: 0644]
src/XGUI/XGUI_Workshop.cpp
src/XGUI/XGUI_Workshop.h

index 2d5d0d479cfaa33d78102ecc817a0255ba96c3d0..9f7435c67349d720898009651113ecc4b797faae 100644 (file)
@@ -24,11 +24,13 @@ ADD_SUBDIRECTORY (src/Config)
 ADD_SUBDIRECTORY (src/Event)
 ADD_SUBDIRECTORY (src/Model)
 ADD_SUBDIRECTORY (src/ModelAPI)
-ADD_SUBDIRECTORY (src/ModuleBase)
-ADD_SUBDIRECTORY (src/PartSet)
+ADD_SUBDIRECTORY (src/GeomAPI)
+ADD_SUBDIRECTORY (src/GeomAlgoAPI)
 ADD_SUBDIRECTORY (src/PartSetPlugin)
 ADD_SUBDIRECTORY (src/ConstructionPlugin)
 ADD_SUBDIRECTORY (src/SketchPlugin)
+ADD_SUBDIRECTORY (src/ModuleBase)
+ADD_SUBDIRECTORY (src/PartSet)
 ADD_SUBDIRECTORY (src/PyConsole)
 ADD_SUBDIRECTORY (src/PyEvent)
 ADD_SUBDIRECTORY (src/PyInterp)
index 85102857bea420483f1c2a7ef2f33cb9f9887ea6..209bb7aa2fef6119f383c7b4d26ca4b8ceea64ad 100644 (file)
@@ -95,3 +95,13 @@ void Config_FeatureMessage::setPluginLibrary(const std::string& myPluginLibrary)
 {
   this->myPluginLibrary = myPluginLibrary;
 }
+
+bool Config_FeatureMessage::isUseInput() const
+{
+  return myUseInput;
+}
+
+void Config_FeatureMessage::setUseInput(bool isUseInput)
+{
+  myUseInput = isUseInput;
+}
index 4185b4b9620831fa1bd01133e06b903339d6a530..900d547d3474239b9a8827a823c79aa6cc0b4867 100644 (file)
@@ -23,6 +23,8 @@ class Config_FeatureMessage: public Event_Message
   std::string myWorkbenchId;  //Id of feature's workbench\r
   std::string myPluginLibrary;  //Name of feature's library\r
 \r
+  bool myUseInput; //Action is being checked until user commit the operation\r
+\r
 public:\r
   //const Event_ID theID, const void* theSender = 0\r
   CONFIG_EXPORT Config_FeatureMessage(const Event_ID theId, const void* theParent = 0);\r
@@ -50,6 +52,8 @@ public:
   CONFIG_EXPORT void setGroupId(const std::string& groupId);\r
   CONFIG_EXPORT void setWorkbenchId(const std::string& workbenchId);\r
   CONFIG_EXPORT void setPluginLibrary(const std::string& thePluginLibrary);\r
+  CONFIG_EXPORT bool isUseInput() const;\r
+  CONFIG_EXPORT void setUseInput(bool isUseInput);\r
 };\r
 \r
 #endif // CONFIG_MESSAGE_H
index bb52b413fa964c3ecdf95c9b275ddd4a6a7b2fe6..50d72e99230ebcc07bf48e30e017140644a6d25d 100644 (file)
@@ -43,6 +43,8 @@ void Config_FeatureReader::processNode(xmlNodePtr theNode)
     Event_Loop* aEvLoop = Event_Loop::loop();
     Config_FeatureMessage aMessage(aMenuItemEvent, this);
     fillFeature(theNode, aMessage);
+    //If a feature has xml definition for it's widget:
+    aMessage.setUseInput(hasChild(theNode));
     aEvLoop->send(aMessage);
   }
   //The m_last* variables always defined before fillFeature() call. XML is a tree.
diff --git a/src/Config/plugin-Sketch.xml b/src/Config/plugin-Sketch.xml
deleted file mode 100644 (file)
index 755458b..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<plugin>
-  <workbench id="Sketch">
-    <group id="Basic">
-      <feature id="Sketch" text="New sketch" tooltip="Create a new sketch or edit an existing sketch" icon=":icons/sketch.png"/>
-    </group>
-    <group id="Operations">
-      <feature id="Point" text="New point" tooltip="Create a new point" icon=":icons/point.png">
-        <doublevalue id="x" label="X:" min="0" max="" step="0.1" default="0"
-               icon=":pictures/x_point.png" tooltip="Set X"/>
-        <doublevalue id="y" label="Y:" min="0" max="" step="0.1" default="1"
-               icon=":pictures/y_point.png" tooltip="Set Y"/>
-        <doublevalue id="z" label="Z:" min="0" max="10" step="0.1" default="2"
-               icon=":pictures/z_point.png" tooltip="Set Z"/>
-      </feature>
-      <feature id="Line" text="New line" tooltip="Create a new line" icon=":icons/line.png"/>
-    </group>
-  </workbench>
-</plugin>
index fff34c7241939b3afa9202a75364cbac4abc1ba9..430ffb9abd36f7f9c75fde70ee31944e18039e53 100644 (file)
@@ -7,8 +7,8 @@ SET(PROJECT_HEADERS
 )
 
 SET(PROJECT_SOURCES
-    ConstructionPlugin_Plugin.cxx
-    ConstructionPlugin_Point.cxx
+    ConstructionPlugin_Plugin.cpp
+    ConstructionPlugin_Point.cpp
 )
 
 ADD_DEFINITIONS(-DCONSTRUCTIONPLUGIN_EXPORTS ${BOOST_DEFINITIONS})
diff --git a/src/ConstructionPlugin/ConstructionPlugin_Plugin.cpp b/src/ConstructionPlugin/ConstructionPlugin_Plugin.cpp
new file mode 100644 (file)
index 0000000..5c1f39f
--- /dev/null
@@ -0,0 +1,24 @@
+#include "ConstructionPlugin_Plugin.h"
+#include "ConstructionPlugin_Point.h"
+#include <ModelAPI_PluginManager.h>
+#include <ModelAPI_Document.h>
+
+using namespace std;
+
+// the only created instance of this plugin
+static ConstructionPlugin_Plugin* MY_INSTANCE = new ConstructionPlugin_Plugin();
+
+ConstructionPlugin_Plugin::ConstructionPlugin_Plugin() 
+{
+  // register this plugin
+  ModelAPI_PluginManager::get()->registerPlugin(this);
+}
+
+boost::shared_ptr<ModelAPI_Feature> ConstructionPlugin_Plugin::createFeature(string theFeatureID)
+{
+  if (theFeatureID == "Point") {
+    return boost::shared_ptr<ModelAPI_Feature>(new ConstructionPlugin_Point);
+  }
+  // feature of such kind is not found
+  return boost::shared_ptr<ModelAPI_Feature>();
+}
diff --git a/src/ConstructionPlugin/ConstructionPlugin_Plugin.cxx b/src/ConstructionPlugin/ConstructionPlugin_Plugin.cxx
deleted file mode 100644 (file)
index 5c1f39f..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#include "ConstructionPlugin_Plugin.h"
-#include "ConstructionPlugin_Point.h"
-#include <ModelAPI_PluginManager.h>
-#include <ModelAPI_Document.h>
-
-using namespace std;
-
-// the only created instance of this plugin
-static ConstructionPlugin_Plugin* MY_INSTANCE = new ConstructionPlugin_Plugin();
-
-ConstructionPlugin_Plugin::ConstructionPlugin_Plugin() 
-{
-  // register this plugin
-  ModelAPI_PluginManager::get()->registerPlugin(this);
-}
-
-boost::shared_ptr<ModelAPI_Feature> ConstructionPlugin_Plugin::createFeature(string theFeatureID)
-{
-  if (theFeatureID == "Point") {
-    return boost::shared_ptr<ModelAPI_Feature>(new ConstructionPlugin_Point);
-  }
-  // feature of such kind is not found
-  return boost::shared_ptr<ModelAPI_Feature>();
-}
diff --git a/src/ConstructionPlugin/ConstructionPlugin_Point.cpp b/src/ConstructionPlugin/ConstructionPlugin_Point.cpp
new file mode 100644 (file)
index 0000000..ea61360
--- /dev/null
@@ -0,0 +1,31 @@
+// File:        ConstructionPlugin_Point.cxx
+// Created:     27 Mar 2014
+// Author:      Mikhail PONIKAROV
+
+#include "ConstructionPlugin_Point.h"
+#include "ModelAPI_PluginManager.h"
+#include "ModelAPI_Document.h"
+#include "ModelAPI_Data.h"
+#include "ModelAPI_AttributeDouble.h"
+
+using namespace std;
+
+ConstructionPlugin_Point::ConstructionPlugin_Point()
+{
+}
+
+void ConstructionPlugin_Point::initAttributes()
+{
+  data()->addAttribute(POINT_ATTR_X, ModelAPI_AttributeDouble::type());
+  data()->addAttribute(POINT_ATTR_Y, ModelAPI_AttributeDouble::type());
+  data()->addAttribute(POINT_ATTR_Z, ModelAPI_AttributeDouble::type());
+}
+
+// this is for debug only
+#include <iostream>
+void ConstructionPlugin_Point::execute() 
+{
+  // TODO: create a real shape for the point using OCC layer
+  cout<<"X="<<data()->real(POINT_ATTR_X)->value()<<" Y="<<data()->real(POINT_ATTR_Y)->value()
+      <<" Z="<<data()->real(POINT_ATTR_Z)->value()<<endl;
+}
diff --git a/src/ConstructionPlugin/ConstructionPlugin_Point.cxx b/src/ConstructionPlugin/ConstructionPlugin_Point.cxx
deleted file mode 100644 (file)
index ea61360..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-// File:        ConstructionPlugin_Point.cxx
-// Created:     27 Mar 2014
-// Author:      Mikhail PONIKAROV
-
-#include "ConstructionPlugin_Point.h"
-#include "ModelAPI_PluginManager.h"
-#include "ModelAPI_Document.h"
-#include "ModelAPI_Data.h"
-#include "ModelAPI_AttributeDouble.h"
-
-using namespace std;
-
-ConstructionPlugin_Point::ConstructionPlugin_Point()
-{
-}
-
-void ConstructionPlugin_Point::initAttributes()
-{
-  data()->addAttribute(POINT_ATTR_X, ModelAPI_AttributeDouble::type());
-  data()->addAttribute(POINT_ATTR_Y, ModelAPI_AttributeDouble::type());
-  data()->addAttribute(POINT_ATTR_Z, ModelAPI_AttributeDouble::type());
-}
-
-// this is for debug only
-#include <iostream>
-void ConstructionPlugin_Point::execute() 
-{
-  // TODO: create a real shape for the point using OCC layer
-  cout<<"X="<<data()->real(POINT_ATTR_X)->value()<<" Y="<<data()->real(POINT_ATTR_Y)->value()
-      <<" Z="<<data()->real(POINT_ATTR_Z)->value()<<endl;
-}
index 5b727db7c4b9fde0c76da817d5eec509ad9b7f30..63191667e9f92c093285d4c8cfcf6a5c8933ad66 100644 (file)
@@ -8,9 +8,9 @@ SET(PROJECT_HEADERS
 )
 
 SET(PROJECT_SOURCES
-    Event_Message.cxx
-    Event_Listener.cxx
-    Event_Loop.cxx
+    Event_Message.cpp
+    Event_Listener.cpp
+    Event_Loop.cpp
 )
 
 ADD_DEFINITIONS(-DEVENT_EXPORTS)
diff --git a/src/Event/Event_Listener.cpp b/src/Event/Event_Listener.cpp
new file mode 100644 (file)
index 0000000..ffb56c7
--- /dev/null
@@ -0,0 +1,5 @@
+// File:       Event_Listener.cxx
+// Created:    Thu Mar 13 2014
+// Author:     Mikhail PONIKAROV
+
+#include <Event_Listener.h>
diff --git a/src/Event/Event_Listener.cxx b/src/Event/Event_Listener.cxx
deleted file mode 100644 (file)
index ffb56c7..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-// File:       Event_Listener.cxx
-// Created:    Thu Mar 13 2014
-// Author:     Mikhail PONIKAROV
-
-#include <Event_Listener.h>
diff --git a/src/Event/Event_Loop.cpp b/src/Event/Event_Loop.cpp
new file mode 100644 (file)
index 0000000..c938dbe
--- /dev/null
@@ -0,0 +1,82 @@
+// File:       Event_Loop.hxx
+// Created:    Thu Mar 13 2014
+// Author:     Mikhail PONIKAROV
+
+#include <Event_Loop.h>
+
+#include <string>
+#include <cstring>
+
+using namespace std;
+
+Event_Loop* Event_Loop::loop()
+{
+  // initialized on initialization of the application
+  static Event_Loop MAIN_LOOP;
+  return &MAIN_LOOP;
+}
+
+Event_ID Event_Loop::eventByName(const char* theName)
+{
+  ///! All events created in this session, uniquely identified by the text and char pointer
+  static map<string, char*> CREATED_EVENTS;
+  char* aResult;
+  string aName(theName);
+  map<string, char*>::iterator aFound = CREATED_EVENTS.find(aName);
+  if (aFound == CREATED_EVENTS.end()) { //not created yet
+    aResult = strdup(theName); // copy to make unique internal pointer
+    CREATED_EVENTS[aName] = aResult;
+  } else
+    aResult = aFound->second;
+
+  return Event_ID(aResult);
+}
+
+void Event_Loop::send(Event_Message& theMessage)
+{
+  // TO DO: make it in thread and wit husage of semaphores
+
+  map<char*, map<void*, list<Event_Listener*> > >::iterator aFindID = myListeners.find(
+      theMessage.eventID().eventText());
+  if (aFindID != myListeners.end()) {
+    map<void*, list<Event_Listener*> >::iterator aFindSender = aFindID->second.find(
+        theMessage.sender());
+    if (aFindSender != aFindID->second.end()) {
+      list<Event_Listener*>& aListeners = aFindSender->second;
+      for(list<Event_Listener*>::iterator aL = aListeners.begin(); aL != aListeners.end(); aL++)
+        (*aL)->processEvent(&theMessage);
+    }
+    if (theMessage.sender()) { // also call for NULL senders registered
+      aFindSender = aFindID->second.find(NULL);
+      if (aFindSender != aFindID->second.end()) {
+        list<Event_Listener*>& aListeners = aFindSender->second;
+        for(list<Event_Listener*>::iterator aL = aListeners.begin(); aL != aListeners.end(); aL++)
+          (*aL)->processEvent(&theMessage);
+      }
+    }
+  }
+}
+
+void Event_Loop::registerListener(Event_Listener* theListener, const Event_ID theID,
+                                  void* theSender)
+{
+  map<char*, map<void*, list<Event_Listener*> > >::iterator aFindID = myListeners.find(
+      theID.eventText());
+  if (aFindID == myListeners.end()) { // create container associated with ID
+    myListeners[theID.eventText()] = map<void*, list<Event_Listener*> >();
+    aFindID = myListeners.find(theID.eventText());
+  }
+
+  map<void*, list<Event_Listener*> >::iterator aFindSender = aFindID->second.find(theSender);
+  if (aFindSender == aFindID->second.end()) { // create container associated with sender
+    aFindID->second[theSender] = list<Event_Listener*>();
+    aFindSender = aFindID->second.find(theSender);
+  }
+  // check that listener was not registered wit hsuch parameters before
+  list<Event_Listener*>& aListeners = aFindSender->second;
+  for(list<Event_Listener*>::iterator aL = aListeners.begin(); aL != aListeners.end(); aL++)
+    if (*aL == theListener)
+      return; // avoid duplicates
+
+  aListeners.push_back(theListener);
+}
diff --git a/src/Event/Event_Loop.cxx b/src/Event/Event_Loop.cxx
deleted file mode 100644 (file)
index c938dbe..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-// File:       Event_Loop.hxx
-// Created:    Thu Mar 13 2014
-// Author:     Mikhail PONIKAROV
-
-#include <Event_Loop.h>
-
-#include <string>
-#include <cstring>
-
-using namespace std;
-
-Event_Loop* Event_Loop::loop()
-{
-  // initialized on initialization of the application
-  static Event_Loop MAIN_LOOP;
-  return &MAIN_LOOP;
-}
-
-Event_ID Event_Loop::eventByName(const char* theName)
-{
-  ///! All events created in this session, uniquely identified by the text and char pointer
-  static map<string, char*> CREATED_EVENTS;
-  char* aResult;
-  string aName(theName);
-  map<string, char*>::iterator aFound = CREATED_EVENTS.find(aName);
-  if (aFound == CREATED_EVENTS.end()) { //not created yet
-    aResult = strdup(theName); // copy to make unique internal pointer
-    CREATED_EVENTS[aName] = aResult;
-  } else
-    aResult = aFound->second;
-
-  return Event_ID(aResult);
-}
-
-void Event_Loop::send(Event_Message& theMessage)
-{
-  // TO DO: make it in thread and wit husage of semaphores
-
-  map<char*, map<void*, list<Event_Listener*> > >::iterator aFindID = myListeners.find(
-      theMessage.eventID().eventText());
-  if (aFindID != myListeners.end()) {
-    map<void*, list<Event_Listener*> >::iterator aFindSender = aFindID->second.find(
-        theMessage.sender());
-    if (aFindSender != aFindID->second.end()) {
-      list<Event_Listener*>& aListeners = aFindSender->second;
-      for(list<Event_Listener*>::iterator aL = aListeners.begin(); aL != aListeners.end(); aL++)
-        (*aL)->processEvent(&theMessage);
-    }
-    if (theMessage.sender()) { // also call for NULL senders registered
-      aFindSender = aFindID->second.find(NULL);
-      if (aFindSender != aFindID->second.end()) {
-        list<Event_Listener*>& aListeners = aFindSender->second;
-        for(list<Event_Listener*>::iterator aL = aListeners.begin(); aL != aListeners.end(); aL++)
-          (*aL)->processEvent(&theMessage);
-      }
-    }
-  }
-}
-
-void Event_Loop::registerListener(Event_Listener* theListener, const Event_ID theID,
-                                  void* theSender)
-{
-  map<char*, map<void*, list<Event_Listener*> > >::iterator aFindID = myListeners.find(
-      theID.eventText());
-  if (aFindID == myListeners.end()) { // create container associated with ID
-    myListeners[theID.eventText()] = map<void*, list<Event_Listener*> >();
-    aFindID = myListeners.find(theID.eventText());
-  }
-
-  map<void*, list<Event_Listener*> >::iterator aFindSender = aFindID->second.find(theSender);
-  if (aFindSender == aFindID->second.end()) { // create container associated with sender
-    aFindID->second[theSender] = list<Event_Listener*>();
-    aFindSender = aFindID->second.find(theSender);
-  }
-  // check that listener was not registered wit hsuch parameters before
-  list<Event_Listener*>& aListeners = aFindSender->second;
-  for(list<Event_Listener*>::iterator aL = aListeners.begin(); aL != aListeners.end(); aL++)
-    if (*aL == theListener)
-      return; // avoid duplicates
-
-  aListeners.push_back(theListener);
-}
diff --git a/src/Event/Event_Message.cpp b/src/Event/Event_Message.cpp
new file mode 100644 (file)
index 0000000..764ed82
--- /dev/null
@@ -0,0 +1,5 @@
+// File:       Event_Message.cxx
+// Created:    Thu Mar 13 2014
+// Author:     Mikhail PONIKAROV
+
+#include <Event_Message.h>
diff --git a/src/Event/Event_Message.cxx b/src/Event/Event_Message.cxx
deleted file mode 100644 (file)
index 764ed82..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-// File:       Event_Message.cxx
-// Created:    Thu Mar 13 2014
-// Author:     Mikhail PONIKAROV
-
-#include <Event_Message.h>
diff --git a/src/GeomAPI/CMakeLists.txt b/src/GeomAPI/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1516267
--- /dev/null
@@ -0,0 +1,54 @@
+FIND_PACKAGE(SWIG REQUIRED)
+INCLUDE(FindCAS)
+
+INCLUDE(${SWIG_USE_FILE})
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+
+SET(PROJECT_HEADERS
+    GeomAPI.h
+    GeomAPI_Interface.h
+    GeomAPI_Pnt.h
+    GeomAPI_Dir.h
+    GeomAPI_Shape.h
+)
+
+SET(PROJECT_SOURCES
+    GeomAPI_Interface.cpp
+    GeomAPI_Pnt.cpp
+    GeomAPI_Dir.cpp
+    GeomAPI_Shape.cpp
+)
+
+ADD_DEFINITIONS(-DGEOMAPI_EXPORTS ${CAS_DEFINITIONS})
+ADD_LIBRARY(GeomAPI SHARED ${PROJECT_SOURCES} ${PROJECT_HEADERS})
+
+SET(CMAKE_SWIG_FLAGS "")
+
+SET_SOURCE_FILES_PROPERTIES(GeomAPI.i PROPERTIES CPLUSPLUS ON)
+SET_SOURCE_FILES_PROPERTIES(GeomAPI.i PROPERTIES SWIG_DEFINITIONS "-shadow")
+
+INCLUDE_DIRECTORIES(
+  ${CAS_INCLUDE_DIRS}
+)
+
+TARGET_LINK_LIBRARIES(GeomAPI ${PROJECT_LIBRARIES} ${CAS_KERNEL})
+
+SET(SWIG_SCRIPTS
+  ${CMAKE_CURRENT_BINARY_DIR}/GeomAPI.py
+)
+
+SET(SWIG_LINK_LIBRARIES
+  GeomAPI
+  ${PYTHON_LIBRARIES}
+)
+
+SWIG_ADD_MODULE(GeomAPI python GeomAPI.i ${PROJECT_HEADERS})
+SWIG_LINK_LIBRARIES(GeomAPI ${SWIG_LINK_LIBRARIES})
+
+IF(WIN32)
+  SET_TARGET_PROPERTIES(_GeomAPI PROPERTIES DEBUG_OUTPUT_NAME _GeomAPI_d)
+ENDIF(WIN32)
+
+INSTALL(TARGETS _GeomAPI DESTINATION swig)
+INSTALL(TARGETS GeomAPI DESTINATION bin)
+INSTALL(FILES ${SWIG_SCRIPTS} DESTINATION swig)
diff --git a/src/GeomAPI/GeomAPI.h b/src/GeomAPI/GeomAPI.h
new file mode 100644 (file)
index 0000000..f27fc48
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef GEOMAPI_H
+#define GEOMAPI_H
+
+#if defined GEOMAPI_EXPORTS
+#if defined WIN32
+#define GEOMAPI_EXPORT              __declspec( dllexport )
+#else
+#define GEOMAPI_EXPORT
+#endif
+#else
+#if defined WIN32
+#define GEOMAPI_EXPORT              __declspec( dllimport )
+#else
+#define GEOMAPI_EXPORT
+#endif
+#endif
+
+#endif
diff --git a/src/GeomAPI/GeomAPI.i b/src/GeomAPI/GeomAPI.i
new file mode 100644 (file)
index 0000000..3f65f05
--- /dev/null
@@ -0,0 +1,29 @@
+/* GeomAPI.i */
+%module GeomAPI
+%{
+  #include "boost/shared_ptr.hpp"
+  #include "GeomAPI.h"
+  #include "GeomAPI_Interface.h"
+  #include "GeomAPI_Pnt.h"
+  #include "GeomAPI_Shape.h"
+%}
+
+// to avoid error on this
+#define GEOMAPI_EXPORT
+
+// standard definitions
+%include "typemaps.i"
+%include "std_string.i"
+//%include <std_shared_ptr.i>
+%include <boost_shared_ptr.i>
+
+// boost pointers
+// %include <boost_shared_ptr.i>
+%shared_ptr(GeomAPI_Interface)
+%shared_ptr(GeomAPI_Pnt)
+%shared_ptr(GeomAPI_Shape)
+
+// all supported interfaces
+%include "GeomAPI_Interface.h"
+%include "GeomAPI_Pnt.h"
+%include "GeomAPI_Shape.h"
diff --git a/src/GeomAPI/GeomAPI_Dir.cpp b/src/GeomAPI/GeomAPI_Dir.cpp
new file mode 100644 (file)
index 0000000..bf0941c
--- /dev/null
@@ -0,0 +1,28 @@
+// File:        GeomAPI_Dir.cpp
+// Created:     23 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#include<GeomAPI_Dir.h>
+
+#include<gp_Dir.hxx>
+
+#define MY_DIR static_cast<gp_Pnt*>(myImpl)
+
+GeomAPI_Dir::GeomAPI_Dir(const double theX, const double theY, const double theZ)
+  : GeomAPI_Interface(new gp_Dir(theX, theY, theZ))
+{}
+
+double GeomAPI_Dir::x() const
+{
+  return MY_DIR->X();
+}
+
+double GeomAPI_Dir::y() const
+{
+  return MY_DIR->Y();
+}
+
+double GeomAPI_Dir::z() const
+{
+  return MY_DIR->Z();
+}
diff --git a/src/GeomAPI/GeomAPI_Dir.h b/src/GeomAPI/GeomAPI_Dir.h
new file mode 100644 (file)
index 0000000..a003d38
--- /dev/null
@@ -0,0 +1,30 @@
+// File:        GeomAPI_Dir.hxx
+// Created:     23 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef GeomAPI_Dir_HeaderFile
+#define GeomAPI_Dir_HeaderFile
+
+#include <GeomAPI_Interface.h>
+
+/**\class GeomAPI_Dir
+ * \ingroup DataModel
+ * \brief 3D direction defined by three normalized coordinates
+ */
+
+class GEOMAPI_EXPORT GeomAPI_Dir: public GeomAPI_Interface
+{
+public:
+  /// Creation of direction by coordinates
+  GeomAPI_Dir(const double theX, const double theY, const double theZ);
+
+  /// returns X coordinate
+  double x() const;
+  /// returns Y coordinate
+  double y() const;
+  /// returns Z coordinate
+  double z() const;
+};
+
+#endif
+
diff --git a/src/GeomAPI/GeomAPI_Interface.cpp b/src/GeomAPI/GeomAPI_Interface.cpp
new file mode 100644 (file)
index 0000000..5e9b257
--- /dev/null
@@ -0,0 +1,33 @@
+// File:        GeomAPI_Interface.cpp
+// Created:     23 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#include <GeomAPI_Interface.h>
+
+GeomAPI_Interface::GeomAPI_Interface()
+{
+  myImpl = 0;
+}
+
+GeomAPI_Interface::GeomAPI_Interface(void* theImpl)
+{
+  myImpl = theImpl;
+}
+
+GeomAPI_Interface::~GeomAPI_Interface()
+{
+  if (myImpl)
+    delete myImpl;
+}
+
+void* GeomAPI_Interface::implementation()
+{
+  return myImpl;
+}
+
+void GeomAPI_Interface::setImplementation(void* theImpl)
+{
+  if (myImpl)
+    delete myImpl;
+  myImpl = theImpl;
+}
diff --git a/src/GeomAPI/GeomAPI_Interface.h b/src/GeomAPI/GeomAPI_Interface.h
new file mode 100644 (file)
index 0000000..1a60559
--- /dev/null
@@ -0,0 +1,37 @@
+// File:        GeomAPI_Interface.hxx
+// Created:     23 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef GeomAPI_Interface_HeaderFile
+#define GeomAPI_Interface_HeaderFile
+
+#include <GeomAPI.h>
+
+/**\class GeomAPI_Interface
+ * \ingroup DataModel
+ * \brief General base class for all interfaces in this package
+ */
+
+class GEOMAPI_EXPORT GeomAPI_Interface
+{
+protected:
+  void* myImpl; ///< pointer to the internal implementation object
+
+public:
+  /// None - constructor
+  GeomAPI_Interface();
+
+  /// Constructor by the implementation pointer (used for internal needs)
+  GeomAPI_Interface(void* theImpl);
+  
+  /// Destructor
+  virtual ~GeomAPI_Interface();
+
+  /// Returns the pointer to the implementation
+  void* implementation();
+  /// Updates the implementation (deletes the old one)
+  void setImplementation(void* theImpl);
+};
+
+#endif
+
diff --git a/src/GeomAPI/GeomAPI_Pnt.cpp b/src/GeomAPI/GeomAPI_Pnt.cpp
new file mode 100644 (file)
index 0000000..6414a2d
--- /dev/null
@@ -0,0 +1,43 @@
+// File:        GeomAPI_Pnt.cpp
+// Created:     23 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#include<GeomAPI_Pnt.h>
+
+#include<gp_Pnt.hxx>
+
+#define MY_PNT static_cast<gp_Pnt*>(myImpl)
+
+GeomAPI_Pnt::GeomAPI_Pnt(const double theX, const double theY, const double theZ)
+  : GeomAPI_Interface(new gp_Pnt(theX, theY, theZ))
+{}
+
+double GeomAPI_Pnt::x() const
+{
+  return MY_PNT->X();
+}
+
+double GeomAPI_Pnt::y() const
+{
+  return MY_PNT->Y();
+}
+
+double GeomAPI_Pnt::z() const
+{
+  return MY_PNT->Z();
+}
+
+void GeomAPI_Pnt::setX(const double theX)
+{
+  return MY_PNT->SetX(theX);
+}
+
+void GeomAPI_Pnt::setY(const double theY)
+{
+  return MY_PNT->SetY(theY);
+}
+
+void GeomAPI_Pnt::setZ(const double theZ)
+{
+  return MY_PNT->SetZ(theZ);
+}
diff --git a/src/GeomAPI/GeomAPI_Pnt.h b/src/GeomAPI/GeomAPI_Pnt.h
new file mode 100644 (file)
index 0000000..8f80bef
--- /dev/null
@@ -0,0 +1,37 @@
+// File:        GeomAPI_Pnt.hxx
+// Created:     23 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef GeomAPI_Pnt_HeaderFile
+#define GeomAPI_Pnt_HeaderFile
+
+#include <GeomAPI_Interface.h>
+
+/**\class GeomAPI_Pnt
+ * \ingroup DataModel
+ * \brief 3D point defined by three coordinates
+ */
+
+class GEOMAPI_EXPORT GeomAPI_Pnt: public GeomAPI_Interface
+{
+public:
+  /// Creation of point by coordinates
+  GeomAPI_Pnt(const double theX, const double theY, const double theZ);
+
+  /// returns X coordinate
+  double x() const;
+  /// returns Y coordinate
+  double y() const;
+  /// returns Z coordinate
+  double z() const;
+
+  /// sets X coordinate
+  void setX(const double theX);
+  /// sets Y coordinate
+  void setY(const double theY);
+  /// sets Z coordinate
+  void setZ(const double theZ);
+};
+
+#endif
+
diff --git a/src/GeomAPI/GeomAPI_Shape.cpp b/src/GeomAPI/GeomAPI_Shape.cpp
new file mode 100644 (file)
index 0000000..e662ea2
--- /dev/null
@@ -0,0 +1,13 @@
+// File:        GeomAPI_Shape.cpp
+// Created:     23 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#include<GeomAPI_Shape.h>
+
+#include<TopoDS_Shape.hxx>
+
+#define MY_PNT static_cast<gp_Pnt*>(myImpl)
+
+GeomAPI_Shape::GeomAPI_Shape()
+  : GeomAPI_Interface(new TopoDS_Shape())
+{}
diff --git a/src/GeomAPI/GeomAPI_Shape.h b/src/GeomAPI/GeomAPI_Shape.h
new file mode 100644 (file)
index 0000000..8a9d3b2
--- /dev/null
@@ -0,0 +1,23 @@
+// File:        GeomAPI_Shape.hxx
+// Created:     23 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef GeomAPI_Shape_HeaderFile
+#define GeomAPI_Shape_HeaderFile
+
+#include <GeomAPI_Interface.h>
+
+/**\class GeomAPI_Shape
+ * \ingroup DataModel
+ * \brief Interface to the topological shape object
+ */
+
+class GEOMAPI_EXPORT GeomAPI_Shape: public GeomAPI_Interface
+{
+public:
+  /// Creation of empty (null) shape
+  GeomAPI_Shape();
+};
+
+#endif
+
diff --git a/src/GeomAlgoAPI/CMakeLists.txt b/src/GeomAlgoAPI/CMakeLists.txt
new file mode 100644 (file)
index 0000000..509d8ac
--- /dev/null
@@ -0,0 +1,49 @@
+FIND_PACKAGE(SWIG REQUIRED)
+INCLUDE(FindCAS)
+
+INCLUDE(${SWIG_USE_FILE})
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+
+SET(PROJECT_HEADERS
+    GeomAlgoAPI.h
+    GeomAlgoAPI_FaceBuilder.h
+)
+
+SET(PROJECT_SOURCES
+    GeomAlgoAPI_FaceBuilder.cpp
+)
+
+ADD_DEFINITIONS(-DGEOMALGOAPI_EXPORTS ${CAS_DEFINITIONS})
+ADD_LIBRARY(GeomAlgoAPI SHARED ${PROJECT_SOURCES} ${PROJECT_HEADERS})
+
+SET(CMAKE_SWIG_FLAGS "")
+
+SET_SOURCE_FILES_PROPERTIES(GeomAlgoAPI.i PROPERTIES CPLUSPLUS ON)
+SET_SOURCE_FILES_PROPERTIES(GeomAlgoAPI.i PROPERTIES SWIG_DEFINITIONS "-shadow")
+
+INCLUDE_DIRECTORIES(
+  ../GeomAPI
+  ${CAS_INCLUDE_DIRS}
+)
+
+TARGET_LINK_LIBRARIES(GeomAlgoAPI ${PROJECT_LIBRARIES} GeomAPI ${CAS_KERNEL} ${CAS_MODELER})
+
+SET(SWIG_SCRIPTS
+  ${CMAKE_CURRENT_BINARY_DIR}/GeomAlgoAPI.py
+)
+
+SET(SWIG_LINK_LIBRARIES
+  GeomAlgoAPI
+  ${PYTHON_LIBRARIES}
+)
+
+SWIG_ADD_MODULE(GeomAlgoAPI python GeomAlgoAPI.i ${PROJECT_HEADERS})
+SWIG_LINK_LIBRARIES(GeomAlgoAPI ${SWIG_LINK_LIBRARIES})
+
+IF(WIN32)
+  SET_TARGET_PROPERTIES(_GeomAlgoAPI PROPERTIES DEBUG_OUTPUT_NAME _GeomAlgoAPI_d)
+ENDIF(WIN32)
+
+INSTALL(TARGETS _GeomAlgoAPI DESTINATION swig)
+INSTALL(TARGETS GeomAlgoAPI DESTINATION bin)
+INSTALL(FILES ${SWIG_SCRIPTS} DESTINATION swig)
diff --git a/src/GeomAlgoAPI/GeomAlgoAPI.h b/src/GeomAlgoAPI/GeomAlgoAPI.h
new file mode 100644 (file)
index 0000000..fc7d33b
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef GEOMALGOAPI_H
+#define GEOMALGOAPI_H
+
+#if defined GEOMALGOAPI_EXPORTS
+#if defined WIN32
+#define GEOMALGOAPI_EXPORT              __declspec( dllexport )
+#else
+#define GEOMALGOAPI_EXPORT
+#endif
+#else
+#if defined WIN32
+#define GEOMALGOAPI_EXPORT              __declspec( dllimport )
+#else
+#define GEOMALGOAPI_EXPORT
+#endif
+#endif
+
+#endif
diff --git a/src/GeomAlgoAPI/GeomAlgoAPI.i b/src/GeomAlgoAPI/GeomAlgoAPI.i
new file mode 100644 (file)
index 0000000..99a815f
--- /dev/null
@@ -0,0 +1,19 @@
+/* GeomAPI.i */
+%module GeomAlgoAPI
+%{
+  #include "memory"
+  #include "GeomAlgoAPI.h"
+  #include "GeomAlgoAPI_FaceBuilder.h"
+%}
+
+// to avoid error on this
+#define GEOMALGOAPI_EXPORT
+
+// standard definitions
+%include "typemaps.i"
+%include "std_string.i"
+//%include <std_shared_ptr.i>
+%include <boost_shared_ptr.i>
+
+// all supported interfaces
+%include "GeomAlgoAPI_FaceBuilder.h"
diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_FaceBuilder.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_FaceBuilder.cpp
new file mode 100644 (file)
index 0000000..642e909
--- /dev/null
@@ -0,0 +1,23 @@
+// File:        GeomAlgoAPI_FaceBuilder.cpp
+// Created:     23 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#include <GeomAlgoAPI_FaceBuilder.h>
+#include <gp_Pln.hxx>
+#include <BRepBuilderAPI_MakeFace.hxx>
+#include <TopoDS_Face.hxx>
+
+boost::shared_ptr<GeomAPI_Shape> GeomAlgoAPI_FaceBuilder::square(
+  boost::shared_ptr<GeomAPI_Pnt> theCenter, boost::shared_ptr<GeomAPI_Dir> theNormal,
+  const double theSize)
+{
+  gp_Pnt* aCenter = static_cast<gp_Pnt*>(theCenter->implementation());
+  gp_Dir* aDir = static_cast<gp_Dir*>(theNormal->implementation());
+  gp_Pln aPlane(*aCenter, *aDir);
+  // half of the size in each direction from the center
+  BRepBuilderAPI_MakeFace aFaceBuilder(aPlane, 
+    -theSize / 2., theSize / 2., -theSize / 2., theSize / 2.);
+  boost::shared_ptr<GeomAPI_Shape> aRes(new GeomAPI_Shape);
+  aRes->setImplementation(new TopoDS_Shape(aFaceBuilder.Face()));
+  return aRes;
+}
diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_FaceBuilder.h b/src/GeomAlgoAPI/GeomAlgoAPI_FaceBuilder.h
new file mode 100644 (file)
index 0000000..61ebfae
--- /dev/null
@@ -0,0 +1,28 @@
+// File:        GeomAlgoAPI_FaceBuilder.h
+// Created:     23 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef GeomAlgoAPI_FaceBuilder_HeaderFile
+#define GeomAlgoAPI_FaceBuilder_HeaderFile
+
+#include <GeomAlgoAPI.h>
+#include <GeomAPI_Shape.h>
+#include <GeomAPI_Pnt.h>
+#include <GeomAPI_Dir.h>
+#include <boost/shared_ptr.hpp>
+
+/**\class GeomAlgoAPI_FaceBuilder
+ * \ingroup DataAlgo
+ * \brief Allows to create face-shapes by different parameters
+ */
+
+class GEOMALGOAPI_EXPORT GeomAlgoAPI_FaceBuilder
+{
+public:
+  /// Creates square planar face by given point of the center,
+  /// normal to the plane and size of square
+  static boost::shared_ptr<GeomAPI_Shape> square(boost::shared_ptr<GeomAPI_Pnt> theCenter,
+    boost::shared_ptr<GeomAPI_Dir> theNormal, const double theSize);
+};
+
+#endif
index 53f1ec956eef3537a14856c1f92268ea24832ef6..310c9f8a305d4a271dd6748fd9549dffff3635f7 100644 (file)
@@ -14,14 +14,14 @@ SET(PROJECT_HEADERS
 )
 
 SET(PROJECT_SOURCES
-    Model_Application.cxx
-    Model_Document.cxx
-    Model_PluginManager.cxx
-    Model_Data.cxx
-    Model_Iterator.cxx
-    Model_AttributeDouble.cxx
-    Model_AttributeDocRef.cxx
-    Model_Events.cxx
+    Model_Application.cpp
+    Model_Document.cpp
+    Model_PluginManager.cpp
+    Model_Data.cpp
+    Model_Iterator.cpp
+    Model_AttributeDouble.cpp
+    Model_AttributeDocRef.cpp
+    Model_Events.cpp
 )
 
 ADD_DEFINITIONS(-DMODEL_EXPORTS ${CAS_DEFINITIONS} ${BOOST_DEFINITIONS})
diff --git a/src/Model/Model_Application.cpp b/src/Model/Model_Application.cpp
new file mode 100644 (file)
index 0000000..166b4e1
--- /dev/null
@@ -0,0 +1,61 @@
+// File:       Model_Application.cxx
+// Created:    Fri Sep 2 2011
+// Author:     Mikhail PONIKAROV
+
+#include <Model_Application.h>
+#include <Model_Document.h>
+
+IMPLEMENT_STANDARD_HANDLE(Model_Application, TDocStd_Application)
+IMPLEMENT_STANDARD_RTTIEXT(Model_Application, TDocStd_Application)
+
+using namespace std;
+
+static Handle_Model_Application TheApplication = new Model_Application;
+
+//=======================================================================
+Handle(Model_Application) Model_Application::getApplication()
+{
+  return TheApplication;
+}
+
+//=======================================================================
+const boost::shared_ptr<Model_Document>& Model_Application::getDocument(string theDocID)
+{
+  if (myDocs.find(theDocID) != myDocs.end())
+    return myDocs[theDocID];
+
+  boost::shared_ptr<Model_Document> aNew(new Model_Document(theDocID));
+  myDocs[theDocID] = aNew;
+  return myDocs[theDocID];
+}
+
+void Model_Application::deleteDocument(string theDocID)
+{
+  myDocs.erase(theDocID);
+}
+
+//=======================================================================
+bool Model_Application::hasDocument(std::string theDocID)
+{
+  return myDocs.find(theDocID) != myDocs.end();
+}
+
+//=======================================================================
+Model_Application::Model_Application()
+{
+  // store handle to the application to avoid nullification
+  static Handle(Model_Application) TheKeepHandle;
+  TheKeepHandle = this;
+}
+
+//=======================================================================
+void Model_Application::Formats(TColStd_SequenceOfExtendedString& theFormats)
+{
+  theFormats.Append(TCollection_ExtendedString("BinOcaf")); // standard binary schema
+}
+
+//=======================================================================
+Standard_CString Model_Application::ResourcesName()
+{
+  return Standard_CString("Standard");
+}
diff --git a/src/Model/Model_Application.cxx b/src/Model/Model_Application.cxx
deleted file mode 100644 (file)
index 166b4e1..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-// File:       Model_Application.cxx
-// Created:    Fri Sep 2 2011
-// Author:     Mikhail PONIKAROV
-
-#include <Model_Application.h>
-#include <Model_Document.h>
-
-IMPLEMENT_STANDARD_HANDLE(Model_Application, TDocStd_Application)
-IMPLEMENT_STANDARD_RTTIEXT(Model_Application, TDocStd_Application)
-
-using namespace std;
-
-static Handle_Model_Application TheApplication = new Model_Application;
-
-//=======================================================================
-Handle(Model_Application) Model_Application::getApplication()
-{
-  return TheApplication;
-}
-
-//=======================================================================
-const boost::shared_ptr<Model_Document>& Model_Application::getDocument(string theDocID)
-{
-  if (myDocs.find(theDocID) != myDocs.end())
-    return myDocs[theDocID];
-
-  boost::shared_ptr<Model_Document> aNew(new Model_Document(theDocID));
-  myDocs[theDocID] = aNew;
-  return myDocs[theDocID];
-}
-
-void Model_Application::deleteDocument(string theDocID)
-{
-  myDocs.erase(theDocID);
-}
-
-//=======================================================================
-bool Model_Application::hasDocument(std::string theDocID)
-{
-  return myDocs.find(theDocID) != myDocs.end();
-}
-
-//=======================================================================
-Model_Application::Model_Application()
-{
-  // store handle to the application to avoid nullification
-  static Handle(Model_Application) TheKeepHandle;
-  TheKeepHandle = this;
-}
-
-//=======================================================================
-void Model_Application::Formats(TColStd_SequenceOfExtendedString& theFormats)
-{
-  theFormats.Append(TCollection_ExtendedString("BinOcaf")); // standard binary schema
-}
-
-//=======================================================================
-Standard_CString Model_Application::ResourcesName()
-{
-  return Standard_CString("Standard");
-}
diff --git a/src/Model/Model_AttributeDocRef.cpp b/src/Model/Model_AttributeDocRef.cpp
new file mode 100644 (file)
index 0000000..9df9c22
--- /dev/null
@@ -0,0 +1,31 @@
+// File:        ModelAPI_AttributeDocRef.cxx
+// Created:     2 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#include "Model_AttributeDocRef.h"
+#include "Model_Application.h"
+
+using namespace std;
+
+void Model_AttributeDocRef::setValue(boost::shared_ptr<ModelAPI_Document> theDoc)
+{
+  myComment->Set(TCollection_ExtendedString(theDoc->id().c_str()));
+}
+
+boost::shared_ptr<ModelAPI_Document> Model_AttributeDocRef::value()
+{
+  if (myComment->Get().Length())
+    return Model_Application::getApplication()->getDocument(
+      TCollection_AsciiString(myComment->Get()).ToCString());
+  // not initialized
+  return boost::shared_ptr<ModelAPI_Document>();
+}
+
+Model_AttributeDocRef::Model_AttributeDocRef(TDF_Label& theLabel)
+{
+  // check the attribute could be already presented in this doc (after load document)
+  if (!theLabel.FindAttribute(TDataStd_Comment::GetID(), myComment)) {
+    // create attribute: not initialized by value yet, just empty string
+    myComment = TDataStd_Comment::Set(theLabel, "");
+  }
+}
diff --git a/src/Model/Model_AttributeDocRef.cxx b/src/Model/Model_AttributeDocRef.cxx
deleted file mode 100644 (file)
index 9df9c22..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-// File:        ModelAPI_AttributeDocRef.cxx
-// Created:     2 Apr 2014
-// Author:      Mikhail PONIKAROV
-
-#include "Model_AttributeDocRef.h"
-#include "Model_Application.h"
-
-using namespace std;
-
-void Model_AttributeDocRef::setValue(boost::shared_ptr<ModelAPI_Document> theDoc)
-{
-  myComment->Set(TCollection_ExtendedString(theDoc->id().c_str()));
-}
-
-boost::shared_ptr<ModelAPI_Document> Model_AttributeDocRef::value()
-{
-  if (myComment->Get().Length())
-    return Model_Application::getApplication()->getDocument(
-      TCollection_AsciiString(myComment->Get()).ToCString());
-  // not initialized
-  return boost::shared_ptr<ModelAPI_Document>();
-}
-
-Model_AttributeDocRef::Model_AttributeDocRef(TDF_Label& theLabel)
-{
-  // check the attribute could be already presented in this doc (after load document)
-  if (!theLabel.FindAttribute(TDataStd_Comment::GetID(), myComment)) {
-    // create attribute: not initialized by value yet, just empty string
-    myComment = TDataStd_Comment::Set(theLabel, "");
-  }
-}
diff --git a/src/Model/Model_AttributeDouble.cpp b/src/Model/Model_AttributeDouble.cpp
new file mode 100644 (file)
index 0000000..23e15f0
--- /dev/null
@@ -0,0 +1,26 @@
+// File:        ModelAPI_AttributeDouble.cxx
+// Created:     2 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#include "Model_AttributeDouble.h"
+
+using namespace std;
+
+void Model_AttributeDouble::setValue(const double theValue)
+{
+  myReal->Set(theValue);
+}
+
+double Model_AttributeDouble::value()
+{
+  return myReal->Get();
+}
+
+Model_AttributeDouble::Model_AttributeDouble(TDF_Label& theLabel)
+{
+  // check the attribute could be already presented in this doc (after load document)
+  if (!theLabel.FindAttribute(TDataStd_Real::GetID(), myReal)) {
+    // create attribute: not initialized by value yet, just zero
+    myReal = TDataStd_Real::Set(theLabel, 0.);
+  }
+}
diff --git a/src/Model/Model_AttributeDouble.cxx b/src/Model/Model_AttributeDouble.cxx
deleted file mode 100644 (file)
index 23e15f0..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-// File:        ModelAPI_AttributeDouble.cxx
-// Created:     2 Apr 2014
-// Author:      Mikhail PONIKAROV
-
-#include "Model_AttributeDouble.h"
-
-using namespace std;
-
-void Model_AttributeDouble::setValue(const double theValue)
-{
-  myReal->Set(theValue);
-}
-
-double Model_AttributeDouble::value()
-{
-  return myReal->Get();
-}
-
-Model_AttributeDouble::Model_AttributeDouble(TDF_Label& theLabel)
-{
-  // check the attribute could be already presented in this doc (after load document)
-  if (!theLabel.FindAttribute(TDataStd_Real::GetID(), myReal)) {
-    // create attribute: not initialized by value yet, just zero
-    myReal = TDataStd_Real::Set(theLabel, 0.);
-  }
-}
diff --git a/src/Model/Model_Data.cpp b/src/Model/Model_Data.cpp
new file mode 100644 (file)
index 0000000..87c7be8
--- /dev/null
@@ -0,0 +1,77 @@
+// File:        Model_Data.hxx
+// Created:     21 Mar 2014
+// Author:      Mikhail PONIKAROV
+
+#include <Model_Data.h>
+#include <Model_AttributeDocRef.h>
+#include <Model_AttributeDouble.h>
+#include <TDataStd_Name.hxx>
+
+using namespace std;
+
+Model_Data::Model_Data()
+{
+}
+
+void Model_Data::setLabel(TDF_Label& theLab)
+{
+  myLab = theLab;
+}
+
+string Model_Data::getName()
+{
+  Handle(TDataStd_Name) aName;
+  if (myLab.FindAttribute(TDataStd_Name::GetID(), aName))
+    return string(TCollection_AsciiString(aName->Get()).ToCString());
+  return ""; // not defined
+}
+
+void Model_Data::setName(string theName)
+{
+  TDataStd_Name::Set(myLab, theName.c_str());
+}
+
+void Model_Data::addAttribute(string theID, string theAttrType)
+{
+  TDF_Label anAttrLab = myLab.FindChild(myAttrs.size() + 1);
+  ModelAPI_Attribute* anAttr = 0;
+  if (theAttrType == ModelAPI_AttributeDocRef::type())
+    anAttr = new Model_AttributeDocRef(anAttrLab);
+  else if (theAttrType == ModelAPI_AttributeDouble::type())
+    anAttr = new Model_AttributeDouble(anAttrLab);
+
+  if (anAttr)
+    myAttrs[theID] = boost::shared_ptr<ModelAPI_Attribute>(anAttr);
+  else
+    ; // TODO: generate error on unknown attribute request and/or add mechanism for customization
+}
+
+boost::shared_ptr<ModelAPI_AttributeDocRef> Model_Data::docRef(const string theID)
+{
+  map<string, boost::shared_ptr<ModelAPI_Attribute> >::iterator aFound = myAttrs.find(theID);
+  if (aFound == myAttrs.end()) {
+    // TODO: generate error on unknown attribute request and/or add mechanism for customization
+    return boost::shared_ptr<ModelAPI_AttributeDocRef>();
+  }
+  boost::shared_ptr<ModelAPI_AttributeDocRef> aRes = 
+    boost::dynamic_pointer_cast<ModelAPI_AttributeDocRef>(aFound->second);
+  if (!aRes) {
+    // TODO: generate error on invalid attribute type request
+  }
+  return aRes;
+}
+
+boost::shared_ptr<ModelAPI_AttributeDouble> Model_Data::real(const string theID)
+{
+  map<string, boost::shared_ptr<ModelAPI_Attribute> >::iterator aFound = myAttrs.find(theID);
+  if (aFound == myAttrs.end()) {
+    // TODO: generate error on unknown attribute request and/or add mechanism for customization
+    return boost::shared_ptr<ModelAPI_AttributeDouble>();
+  }
+  boost::shared_ptr<ModelAPI_AttributeDouble> aRes = 
+    boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(aFound->second);
+  if (!aRes) {
+    // TODO: generate error on invalid attribute type request
+  }
+  return aRes;
+}
diff --git a/src/Model/Model_Data.cxx b/src/Model/Model_Data.cxx
deleted file mode 100644 (file)
index 87c7be8..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-// File:        Model_Data.hxx
-// Created:     21 Mar 2014
-// Author:      Mikhail PONIKAROV
-
-#include <Model_Data.h>
-#include <Model_AttributeDocRef.h>
-#include <Model_AttributeDouble.h>
-#include <TDataStd_Name.hxx>
-
-using namespace std;
-
-Model_Data::Model_Data()
-{
-}
-
-void Model_Data::setLabel(TDF_Label& theLab)
-{
-  myLab = theLab;
-}
-
-string Model_Data::getName()
-{
-  Handle(TDataStd_Name) aName;
-  if (myLab.FindAttribute(TDataStd_Name::GetID(), aName))
-    return string(TCollection_AsciiString(aName->Get()).ToCString());
-  return ""; // not defined
-}
-
-void Model_Data::setName(string theName)
-{
-  TDataStd_Name::Set(myLab, theName.c_str());
-}
-
-void Model_Data::addAttribute(string theID, string theAttrType)
-{
-  TDF_Label anAttrLab = myLab.FindChild(myAttrs.size() + 1);
-  ModelAPI_Attribute* anAttr = 0;
-  if (theAttrType == ModelAPI_AttributeDocRef::type())
-    anAttr = new Model_AttributeDocRef(anAttrLab);
-  else if (theAttrType == ModelAPI_AttributeDouble::type())
-    anAttr = new Model_AttributeDouble(anAttrLab);
-
-  if (anAttr)
-    myAttrs[theID] = boost::shared_ptr<ModelAPI_Attribute>(anAttr);
-  else
-    ; // TODO: generate error on unknown attribute request and/or add mechanism for customization
-}
-
-boost::shared_ptr<ModelAPI_AttributeDocRef> Model_Data::docRef(const string theID)
-{
-  map<string, boost::shared_ptr<ModelAPI_Attribute> >::iterator aFound = myAttrs.find(theID);
-  if (aFound == myAttrs.end()) {
-    // TODO: generate error on unknown attribute request and/or add mechanism for customization
-    return boost::shared_ptr<ModelAPI_AttributeDocRef>();
-  }
-  boost::shared_ptr<ModelAPI_AttributeDocRef> aRes = 
-    boost::dynamic_pointer_cast<ModelAPI_AttributeDocRef>(aFound->second);
-  if (!aRes) {
-    // TODO: generate error on invalid attribute type request
-  }
-  return aRes;
-}
-
-boost::shared_ptr<ModelAPI_AttributeDouble> Model_Data::real(const string theID)
-{
-  map<string, boost::shared_ptr<ModelAPI_Attribute> >::iterator aFound = myAttrs.find(theID);
-  if (aFound == myAttrs.end()) {
-    // TODO: generate error on unknown attribute request and/or add mechanism for customization
-    return boost::shared_ptr<ModelAPI_AttributeDouble>();
-  }
-  boost::shared_ptr<ModelAPI_AttributeDouble> aRes = 
-    boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(aFound->second);
-  if (!aRes) {
-    // TODO: generate error on invalid attribute type request
-  }
-  return aRes;
-}
diff --git a/src/Model/Model_Document.cpp b/src/Model/Model_Document.cpp
new file mode 100644 (file)
index 0000000..c022362
--- /dev/null
@@ -0,0 +1,444 @@
+// File:        Model_Document.cxx
+// Created:     28 Feb 2014
+// Author:      Mikhail PONIKAROV
+
+#include <Model_Document.h>
+#include <ModelAPI_Feature.h>
+#include <Model_Data.h>
+#include <Model_Application.h>
+#include <Model_PluginManager.h>
+#include <Model_Iterator.h>
+#include <Model_Events.h>
+#include <Event_Loop.h>
+
+#include <TDataStd_Integer.hxx>
+#include <TDataStd_Comment.hxx>
+#include <TDF_ChildIDIterator.hxx>
+
+#include <climits>
+
+static const int UNDO_LIMIT = 10; // number of possible undo operations
+
+static const int TAG_GENERAL = 1; // general properties tag
+static const int TAG_OBJECTS = 2; // tag of the objects sub-tree (Root for Model_DatasMgr)
+static const int TAG_HISTORY = 3; // tag of the history sub-tree (Root for Model_History)
+
+using namespace std;
+
+bool Model_Document::load(const char* theFileName)
+{
+  bool myIsError = Standard_False;
+  /*
+   TCollection_ExtendedString aPath ((const Standard_CString)theFileName);
+   PCDM_ReaderStatus aStatus = (PCDM_ReaderStatus) -1;
+   try
+   {
+   Handle(TDocStd_Document) aDoc = this;
+   aStatus = Model_Application::GetApplication()->Open(aPath, aDoc);
+   }
+   catch (Standard_Failure)
+   {}
+   myIsError = aStatus != PCDM_RS_OK;
+   if (myIsError)
+   {
+   switch (aStatus)
+   {
+   case PCDM_RS_UnknownDocument: cout<<"OCAFApp_Appl_RUnknownDocument"<<endl; break;
+   case PCDM_RS_AlreadyRetrieved: cout<<"OCAFApp_Appl_RAlreadyRetrieved"<<endl; break;
+   case PCDM_RS_AlreadyRetrievedAndModified: cout<<"OCAFApp_Appl_RAlreadyRetrievedAndModified"<<endl; break;
+   case PCDM_RS_NoDriver: cout<<"OCAFApp_Appl_RNoDriver"<<endl; break;
+   case PCDM_RS_UnknownFileDriver: cout<<"OCAFApp_Appl_RNoDriver"<<endl; break;
+   case PCDM_RS_OpenError: cout<<"OCAFApp_Appl_ROpenError"<<endl; break;
+   case PCDM_RS_NoVersion: cout<<"OCAFApp_Appl_RNoVersion"<<endl; break;
+   case PCDM_RS_NoModel: cout<<"OCAFApp_Appl_RNoModel"<<endl; break;
+   case PCDM_RS_NoDocument: cout<<"OCAFApp_Appl_RNoDocument"<<endl; break;
+   case PCDM_RS_FormatFailure: cout<<"OCAFApp_Appl_RFormatFailure"<<endl; break;
+   case PCDM_RS_TypeNotFoundInSchema: cout<<"OCAFApp_Appl_RTypeNotFound"<<endl; break;
+   case PCDM_RS_UnrecognizedFileFormat: cout<<"OCAFApp_Appl_RBadFileFormat"<<endl; break;
+   case PCDM_RS_MakeFailure: cout<<"OCAFApp_Appl_RMakeFailure"<<endl; break;
+   case PCDM_RS_PermissionDenied: cout<<"OCAFApp_Appl_RPermissionDenied"<<endl; break;
+   case PCDM_RS_DriverFailure: cout<<"OCAFApp_Appl_RDriverFailure"<<endl; break;
+   default: cout<<"OCAFApp_Appl_RUnknownFail"<<endl; break;
+   }
+   }
+   SetUndoLimit(UNDO_LIMIT);
+   */
+  return !myIsError;
+}
+
+bool Model_Document::save(const char* theFileName)
+{
+  bool myIsError = true;
+  /*
+   TCollection_ExtendedString aPath ((const Standard_CString)theFileName);
+   PCDM_StoreStatus aStatus;
+   try {
+   Handle(TDocStd_Document) aDoc = this;
+   aStatus = Model_Application::GetApplication()->SaveAs (aDoc, aPath);
+   }
+   catch (Standard_Failure) {
+   Handle(Standard_Failure) aFail = Standard_Failure::Caught();
+   cout<<"OCAFApp_Engine:save Error: "<<aFail->GetMessageString()<<endl;
+   return false;
+   }
+   myIsError = aStatus != PCDM_SS_OK;
+   if (myIsError)
+   {
+   switch (aStatus)
+   {
+   case PCDM_SS_DriverFailure:
+   cout<<"OCAFApp_Appl_SDriverFailure"<<endl;
+   break;
+   case PCDM_SS_WriteFailure:
+   cout<<"OCAFApp_Appl_SWriteFailure"<<endl;
+   break;
+   case PCDM_SS_Failure:
+   default:
+   cout<<"OCAFApp_Appl_SUnknownFailure"<<endl;
+   break;
+   }
+   }
+   myTransactionsAfterSave = 0;
+   Standard::Purge(); // Release free memory
+   */
+  return !myIsError;
+}
+
+void Model_Document::close()
+{
+  // close all subs
+  set<string>::iterator aSubIter = mySubs.begin();
+  for(; aSubIter != mySubs.end(); aSubIter++)
+    subDocument(*aSubIter)->close();
+  mySubs.clear();
+  // close this
+  myDoc->Close();
+  Model_Application::getApplication()->deleteDocument(myID);
+}
+
+void Model_Document::startOperation()
+{
+  // new command for this
+  myDoc->NewCommand();
+  // new command for all subs
+  set<string>::iterator aSubIter = mySubs.begin();
+  for(; aSubIter != mySubs.end(); aSubIter++)
+    subDocument(*aSubIter)->startOperation();
+}
+
+void Model_Document::finishOperation()
+{
+  // returns false if delta is empty and no transaction was made
+  myIsEmptyTr[myTransactionsAfterSave] = !myDoc->CommitCommand();
+  myTransactionsAfterSave++;
+  // finish for all subs
+  set<string>::iterator aSubIter = mySubs.begin();
+  for(; aSubIter != mySubs.end(); aSubIter++)
+    subDocument(*aSubIter)->finishOperation();
+}
+
+void Model_Document::abortOperation()
+{
+  myDoc->AbortCommand();
+  // abort for all subs
+  set<string>::iterator aSubIter = mySubs.begin();
+  for(; aSubIter != mySubs.end(); aSubIter++)
+    subDocument(*aSubIter)->abortOperation();
+}
+
+bool Model_Document::isOperation()
+{
+  // operation is opened for all documents: no need to check subs
+  return myDoc->HasOpenCommand() == Standard_True ;
+}
+
+bool Model_Document::isModified()
+{
+  // is modified if at least one operation was commited and not undoed
+  return myTransactionsAfterSave > 0;
+}
+
+bool Model_Document::canUndo()
+{
+  if (myDoc->GetAvailableUndos() > 0)
+    return true;
+  // check other subs contains operation that can be undoed
+  set<string>::iterator aSubIter = mySubs.begin();
+  for(; aSubIter != mySubs.end(); aSubIter++)
+    if (subDocument(*aSubIter)->canUndo())
+      return true;
+  return false;
+}
+
+void Model_Document::undo()
+{
+  myTransactionsAfterSave--;
+  if (!myIsEmptyTr[myTransactionsAfterSave])
+    myDoc->Undo();
+  synchronizeFeatures();
+  // undo for all subs
+  set<string>::iterator aSubIter = mySubs.begin();
+  for(; aSubIter != mySubs.end(); aSubIter++)
+    subDocument(*aSubIter)->undo();
+}
+
+bool Model_Document::canRedo()
+{
+  if (myDoc->GetAvailableRedos() > 0)
+    return true;
+  // check other subs contains operation that can be redoed
+  set<string>::iterator aSubIter = mySubs.begin();
+  for(; aSubIter != mySubs.end(); aSubIter++)
+    if (subDocument(*aSubIter)->canRedo())
+      return true;
+  return false;
+}
+
+void Model_Document::redo()
+{
+  if (!myIsEmptyTr[myTransactionsAfterSave])
+    myDoc->Redo();
+  myTransactionsAfterSave++;
+  synchronizeFeatures();
+  // redo for all subs
+  set<string>::iterator aSubIter = mySubs.begin();
+  for(; aSubIter != mySubs.end(); aSubIter++)
+    subDocument(*aSubIter)->redo();
+}
+
+boost::shared_ptr<ModelAPI_Feature> Model_Document::addFeature(string theID)
+{
+  boost::shared_ptr<ModelAPI_Feature> aFeature = ModelAPI_PluginManager::get()->createFeature(theID);
+  if (aFeature) {
+    boost::dynamic_pointer_cast<Model_Document>(aFeature->documentToAdd())->addFeature(aFeature);
+  } else {
+    // TODO: generate error that feature is not created
+  }
+  return aFeature;
+}
+
+void Model_Document::addFeature(const boost::shared_ptr<ModelAPI_Feature> theFeature)
+{
+  const std::string& aGroup = theFeature->getGroup();
+  TDF_Label aGroupLab = groupLabel(aGroup);
+  TDF_Label anObjLab = aGroupLab.NewChild();
+  boost::shared_ptr<Model_Data> aData(new Model_Data);
+  aData->setLabel(anObjLab);
+  boost::shared_ptr<ModelAPI_Document> aThis = Model_Application::getApplication()->getDocument(myID);
+  aData->setDocument(aThis);
+  theFeature->setData(aData);
+  setUniqueName(theFeature);
+  theFeature->initAttributes();
+  // keep the feature ID to restore document later correctly
+  TDataStd_Comment::Set(anObjLab, theFeature->getKind().c_str());
+  // put index of the feature in the group in the tree
+  TDataStd_Integer::Set(anObjLab, myFeatures[aGroup].size());
+  myFeatures[aGroup].push_back(theFeature);
+
+  // event: feature is added
+  static Event_ID anEvent = Event_Loop::eventByName(EVENT_FEATURE_CREATED);
+  ModelAPI_FeatureUpdatedMessage aMsg(aThis, theFeature, anEvent);
+  Event_Loop::loop()->send(aMsg);
+}
+
+boost::shared_ptr<ModelAPI_Feature> Model_Document::feature(TDF_Label& theLabel)
+{
+  Handle(TDataStd_Integer) aFeatureIndex;
+  if (theLabel.FindAttribute(TDataStd_Integer::GetID(), aFeatureIndex)) {
+    Handle(TDataStd_Comment) aGroupID;
+    if (theLabel.Father().FindAttribute(TDataStd_Comment::GetID(), aGroupID)) {
+      string aGroup = TCollection_AsciiString(aGroupID->Get()).ToCString();
+      return myFeatures[aGroup][aFeatureIndex->Get()];
+    }
+  }
+  return boost::shared_ptr<ModelAPI_Feature>(); // not found
+}
+
+int Model_Document::featureIndex(boost::shared_ptr<ModelAPI_Feature> theFeature)
+{
+  if (theFeature->data()->document().get() != this) {
+    return theFeature->data()->document()->featureIndex(theFeature);
+  }
+  boost::shared_ptr<Model_Data> aData = boost::dynamic_pointer_cast<Model_Data>(theFeature->data());
+  Handle(TDataStd_Integer) aFeatureIndex;
+  if (aData->label().FindAttribute(TDataStd_Integer::GetID(), aFeatureIndex)) {
+    return aFeatureIndex->Get();
+  }
+  return -1; // not found
+}
+
+boost::shared_ptr<ModelAPI_Document> Model_Document::subDocument(string theDocID)
+{
+  // just store sub-document identifier here to manage it later
+  if (mySubs.find(theDocID) == mySubs.end())
+    mySubs.insert(theDocID);
+  return Model_Application::getApplication()->getDocument(theDocID);
+}
+
+boost::shared_ptr<ModelAPI_Iterator> Model_Document::featuresIterator(const string theGroup)
+{
+  boost::shared_ptr<Model_Document> aThis(Model_Application::getApplication()->getDocument(myID));
+  // create an empty iterator for not existing group 
+  // (to avoidance of attributes management outside the transaction)
+  if (myGroups.find(theGroup) == myGroups.end())
+    return boost::shared_ptr<ModelAPI_Iterator>(new Model_Iterator());
+  return boost::shared_ptr<ModelAPI_Iterator>(new Model_Iterator(aThis, groupLabel(theGroup)));
+}
+
+boost::shared_ptr<ModelAPI_Feature> Model_Document::feature(const string& theGroupID, const int theIndex)
+{
+  // TODO: optimize this method
+  boost::shared_ptr<ModelAPI_Iterator>  anIter = featuresIterator(theGroupID);
+  for(int a = 0; a != theIndex && anIter->more(); anIter->next()) a++;
+  return anIter->more() ? anIter->current() : boost::shared_ptr<ModelAPI_Feature>();
+}
+
+const vector<string>& Model_Document::getGroups() const
+{
+  return myGroupsNames;
+}
+
+Model_Document::Model_Document(const std::string theID)
+    : myID(theID), myDoc(new TDocStd_Document("BinOcaf")) // binary OCAF format
+{
+  myDoc->SetUndoLimit(UNDO_LIMIT);
+  myTransactionsAfterSave = 0;
+  // to avoid creation of tag outside of the transaction (by iterator, for example)
+  /*
+  if (!myDoc->Main().FindChild(TAG_OBJECTS).IsAttribute(TDF_TagSource::GetID()))
+    TDataStd_Comment::Set(myDoc->Main().FindChild(TAG_OBJECTS).NewChild(), "");
+    */
+}
+
+TDF_Label Model_Document::groupLabel(const string theGroup)
+{
+  if (myGroups.find(theGroup) == myGroups.end()) {
+    myGroups[theGroup] = myDoc->Main().FindChild(TAG_OBJECTS).NewChild();
+    myGroupsNames.push_back(theGroup);
+    // set to the group label the group idntifier to restore on "open"
+    TDataStd_Comment::Set(myGroups[theGroup], theGroup.c_str());
+    myFeatures[theGroup] = vector<boost::shared_ptr<ModelAPI_Feature> >();
+  }
+  return myGroups[theGroup];
+}
+
+void Model_Document::setUniqueName(boost::shared_ptr<ModelAPI_Feature> theFeature)
+{
+  // first count all objects of such kind to start with index = count + 1
+  int aNumObjects = 0;
+  boost::shared_ptr<ModelAPI_Iterator> anIter = featuresIterator(theFeature->getGroup());
+  for(; anIter->more(); anIter->next()) {
+    if (anIter->currentKind() == theFeature->getKind())
+      aNumObjects++;
+  }
+  // generate candidate name
+  stringstream aNameStream;
+  aNameStream<<theFeature->getKind()<<"_"<<aNumObjects + 1;
+  string aName = aNameStream.str();
+  // check this is unique, if not, increase index by 1
+  for(anIter = featuresIterator(theFeature->getGroup()); anIter->more();) {
+    if (anIter->currentName() == aName) {
+      aNumObjects++;
+      stringstream aNameStream;
+      aNameStream<<theFeature->getKind()<<"_"<<aNumObjects + 1;
+      // reinitialize iterator to make sure a new name is unique
+      anIter = featuresIterator(theFeature->getGroup());
+    } else anIter->next();
+  }
+
+  theFeature->data()->setName(aName);
+}
+
+void Model_Document::synchronizeFeatures()
+{
+  boost::shared_ptr<ModelAPI_Document> aThis = Model_Application::getApplication()->getDocument(myID);
+  // iterate groups labels
+  TDF_ChildIDIterator aGroupsIter(myDoc->Main().FindChild(TAG_OBJECTS),
+    TDataStd_Comment::GetID(), Standard_False);
+  vector<string>::iterator aGroupNamesIter = myGroupsNames.begin();
+  for(; aGroupsIter.More() && aGroupNamesIter != myGroupsNames.end();
+        aGroupsIter.Next(), aGroupNamesIter++) {
+    string aGroupName = TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(
+      aGroupsIter.Value())->Get()).ToCString();
+    if (*aGroupNamesIter != aGroupName) 
+      break; // all since there is a change this must be recreated from scratch
+  }
+  // delete all groups left after the data model groups iteration
+  while(aGroupNamesIter != myGroupsNames.end()) {
+    string aGroupName = *aGroupNamesIter;
+    myFeatures.erase(aGroupName);
+    myGroups.erase(aGroupName);
+    aGroupNamesIter = myGroupsNames.erase(aGroupNamesIter);
+    // say that features were deleted from group
+    ModelAPI_FeatureDeletedMessage aMsg(aThis, aGroupName);
+    Event_Loop::loop()->send(aMsg);
+  }
+  // create new groups basing on the following data model update
+  for(; aGroupsIter.More(); aGroupsIter.Next()) {
+    string aGroupName = TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(
+      aGroupsIter.Value())->Get()).ToCString();
+    myGroupsNames.push_back(aGroupName);
+    myGroups[aGroupName] = aGroupsIter.Value()->Label();
+    myFeatures[aGroupName] = vector<boost::shared_ptr<ModelAPI_Feature> >();
+  }
+  // update features group by group
+  aGroupsIter.Initialize(myDoc->Main().FindChild(TAG_OBJECTS),
+    TDataStd_Comment::GetID(), Standard_False);
+  for(; aGroupsIter.More(); aGroupsIter.Next()) {
+    string aGroupName = TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(
+      aGroupsIter.Value())->Get()).ToCString();
+    // iterate features in internal container
+    vector<boost::shared_ptr<ModelAPI_Feature> >& aFeatures = myFeatures[aGroupName];
+    vector<boost::shared_ptr<ModelAPI_Feature> >::iterator aFIter = aFeatures.begin();
+    // and in parallel iterate labels of features
+    TDF_ChildIDIterator aFLabIter(
+      aGroupsIter.Value()->Label(), TDataStd_Comment::GetID(), Standard_False);
+    while(aFIter != aFeatures.end() || aFLabIter.More()) {
+      static const int INFINITE_TAG = INT_MAX; // no label means that it exists somwhere in infinite
+      int aFeatureTag = INFINITE_TAG; 
+      if (aFIter != aFeatures.end()) { // existing tag for feature
+        boost::shared_ptr<Model_Data> aData = boost::dynamic_pointer_cast<Model_Data>((*aFIter)->data());
+        aFeatureTag = aData->label().Tag();
+      }
+      int aDSTag = INFINITE_TAG; 
+      if (aFLabIter.More()) { // next label in DS is existing
+        aDSTag = aFLabIter.Value()->Label().Tag();
+      }
+      if (aDSTag > aFeatureTag) { // feature is removed
+        aFIter = aFeatures.erase(aFIter);
+        // event: model is updated
+        ModelAPI_FeatureDeletedMessage aMsg(aThis, aGroupName);
+        Event_Loop::loop()->send(aMsg);
+      } else if (aDSTag < aFeatureTag) { // a new feature is inserted
+        // create a feature
+        boost::shared_ptr<ModelAPI_Feature> aFeature = ModelAPI_PluginManager::get()->createFeature(
+          TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(
+          aFLabIter.Value())->Get()).ToCString());
+
+        boost::shared_ptr<Model_Data> aData(new Model_Data);
+        TDF_Label aLab = aFLabIter.Value()->Label();
+        aData->setLabel(aLab);
+        aData->setDocument(Model_Application::getApplication()->getDocument(myID));
+        aFeature->setData(aData);
+        aFeature->initAttributes();
+        // event: model is updated
+        static Event_ID anEvent = Event_Loop::eventByName(EVENT_FEATURE_CREATED);
+        ModelAPI_FeatureUpdatedMessage aMsg(aThis, aFeature, anEvent);
+        Event_Loop::loop()->send(aMsg);
+
+        if (aFIter == aFeatures.end()) {
+          aFeatures.push_back(aFeature);
+          aFIter = aFeatures.end();
+        } else {
+          aFIter++;
+          aFeatures.insert(aFIter, aFeature);
+        }
+        // feature for this label is added, so go to the next label
+        aFLabIter.Next();
+      } else { // nothing is changed, both iterators are incremented
+        aFIter++;
+        aFLabIter.Next();
+      }
+    }
+  }
+}
diff --git a/src/Model/Model_Document.cxx b/src/Model/Model_Document.cxx
deleted file mode 100644 (file)
index c022362..0000000
+++ /dev/null
@@ -1,444 +0,0 @@
-// File:        Model_Document.cxx
-// Created:     28 Feb 2014
-// Author:      Mikhail PONIKAROV
-
-#include <Model_Document.h>
-#include <ModelAPI_Feature.h>
-#include <Model_Data.h>
-#include <Model_Application.h>
-#include <Model_PluginManager.h>
-#include <Model_Iterator.h>
-#include <Model_Events.h>
-#include <Event_Loop.h>
-
-#include <TDataStd_Integer.hxx>
-#include <TDataStd_Comment.hxx>
-#include <TDF_ChildIDIterator.hxx>
-
-#include <climits>
-
-static const int UNDO_LIMIT = 10; // number of possible undo operations
-
-static const int TAG_GENERAL = 1; // general properties tag
-static const int TAG_OBJECTS = 2; // tag of the objects sub-tree (Root for Model_DatasMgr)
-static const int TAG_HISTORY = 3; // tag of the history sub-tree (Root for Model_History)
-
-using namespace std;
-
-bool Model_Document::load(const char* theFileName)
-{
-  bool myIsError = Standard_False;
-  /*
-   TCollection_ExtendedString aPath ((const Standard_CString)theFileName);
-   PCDM_ReaderStatus aStatus = (PCDM_ReaderStatus) -1;
-   try
-   {
-   Handle(TDocStd_Document) aDoc = this;
-   aStatus = Model_Application::GetApplication()->Open(aPath, aDoc);
-   }
-   catch (Standard_Failure)
-   {}
-   myIsError = aStatus != PCDM_RS_OK;
-   if (myIsError)
-   {
-   switch (aStatus)
-   {
-   case PCDM_RS_UnknownDocument: cout<<"OCAFApp_Appl_RUnknownDocument"<<endl; break;
-   case PCDM_RS_AlreadyRetrieved: cout<<"OCAFApp_Appl_RAlreadyRetrieved"<<endl; break;
-   case PCDM_RS_AlreadyRetrievedAndModified: cout<<"OCAFApp_Appl_RAlreadyRetrievedAndModified"<<endl; break;
-   case PCDM_RS_NoDriver: cout<<"OCAFApp_Appl_RNoDriver"<<endl; break;
-   case PCDM_RS_UnknownFileDriver: cout<<"OCAFApp_Appl_RNoDriver"<<endl; break;
-   case PCDM_RS_OpenError: cout<<"OCAFApp_Appl_ROpenError"<<endl; break;
-   case PCDM_RS_NoVersion: cout<<"OCAFApp_Appl_RNoVersion"<<endl; break;
-   case PCDM_RS_NoModel: cout<<"OCAFApp_Appl_RNoModel"<<endl; break;
-   case PCDM_RS_NoDocument: cout<<"OCAFApp_Appl_RNoDocument"<<endl; break;
-   case PCDM_RS_FormatFailure: cout<<"OCAFApp_Appl_RFormatFailure"<<endl; break;
-   case PCDM_RS_TypeNotFoundInSchema: cout<<"OCAFApp_Appl_RTypeNotFound"<<endl; break;
-   case PCDM_RS_UnrecognizedFileFormat: cout<<"OCAFApp_Appl_RBadFileFormat"<<endl; break;
-   case PCDM_RS_MakeFailure: cout<<"OCAFApp_Appl_RMakeFailure"<<endl; break;
-   case PCDM_RS_PermissionDenied: cout<<"OCAFApp_Appl_RPermissionDenied"<<endl; break;
-   case PCDM_RS_DriverFailure: cout<<"OCAFApp_Appl_RDriverFailure"<<endl; break;
-   default: cout<<"OCAFApp_Appl_RUnknownFail"<<endl; break;
-   }
-   }
-   SetUndoLimit(UNDO_LIMIT);
-   */
-  return !myIsError;
-}
-
-bool Model_Document::save(const char* theFileName)
-{
-  bool myIsError = true;
-  /*
-   TCollection_ExtendedString aPath ((const Standard_CString)theFileName);
-   PCDM_StoreStatus aStatus;
-   try {
-   Handle(TDocStd_Document) aDoc = this;
-   aStatus = Model_Application::GetApplication()->SaveAs (aDoc, aPath);
-   }
-   catch (Standard_Failure) {
-   Handle(Standard_Failure) aFail = Standard_Failure::Caught();
-   cout<<"OCAFApp_Engine:save Error: "<<aFail->GetMessageString()<<endl;
-   return false;
-   }
-   myIsError = aStatus != PCDM_SS_OK;
-   if (myIsError)
-   {
-   switch (aStatus)
-   {
-   case PCDM_SS_DriverFailure:
-   cout<<"OCAFApp_Appl_SDriverFailure"<<endl;
-   break;
-   case PCDM_SS_WriteFailure:
-   cout<<"OCAFApp_Appl_SWriteFailure"<<endl;
-   break;
-   case PCDM_SS_Failure:
-   default:
-   cout<<"OCAFApp_Appl_SUnknownFailure"<<endl;
-   break;
-   }
-   }
-   myTransactionsAfterSave = 0;
-   Standard::Purge(); // Release free memory
-   */
-  return !myIsError;
-}
-
-void Model_Document::close()
-{
-  // close all subs
-  set<string>::iterator aSubIter = mySubs.begin();
-  for(; aSubIter != mySubs.end(); aSubIter++)
-    subDocument(*aSubIter)->close();
-  mySubs.clear();
-  // close this
-  myDoc->Close();
-  Model_Application::getApplication()->deleteDocument(myID);
-}
-
-void Model_Document::startOperation()
-{
-  // new command for this
-  myDoc->NewCommand();
-  // new command for all subs
-  set<string>::iterator aSubIter = mySubs.begin();
-  for(; aSubIter != mySubs.end(); aSubIter++)
-    subDocument(*aSubIter)->startOperation();
-}
-
-void Model_Document::finishOperation()
-{
-  // returns false if delta is empty and no transaction was made
-  myIsEmptyTr[myTransactionsAfterSave] = !myDoc->CommitCommand();
-  myTransactionsAfterSave++;
-  // finish for all subs
-  set<string>::iterator aSubIter = mySubs.begin();
-  for(; aSubIter != mySubs.end(); aSubIter++)
-    subDocument(*aSubIter)->finishOperation();
-}
-
-void Model_Document::abortOperation()
-{
-  myDoc->AbortCommand();
-  // abort for all subs
-  set<string>::iterator aSubIter = mySubs.begin();
-  for(; aSubIter != mySubs.end(); aSubIter++)
-    subDocument(*aSubIter)->abortOperation();
-}
-
-bool Model_Document::isOperation()
-{
-  // operation is opened for all documents: no need to check subs
-  return myDoc->HasOpenCommand() == Standard_True ;
-}
-
-bool Model_Document::isModified()
-{
-  // is modified if at least one operation was commited and not undoed
-  return myTransactionsAfterSave > 0;
-}
-
-bool Model_Document::canUndo()
-{
-  if (myDoc->GetAvailableUndos() > 0)
-    return true;
-  // check other subs contains operation that can be undoed
-  set<string>::iterator aSubIter = mySubs.begin();
-  for(; aSubIter != mySubs.end(); aSubIter++)
-    if (subDocument(*aSubIter)->canUndo())
-      return true;
-  return false;
-}
-
-void Model_Document::undo()
-{
-  myTransactionsAfterSave--;
-  if (!myIsEmptyTr[myTransactionsAfterSave])
-    myDoc->Undo();
-  synchronizeFeatures();
-  // undo for all subs
-  set<string>::iterator aSubIter = mySubs.begin();
-  for(; aSubIter != mySubs.end(); aSubIter++)
-    subDocument(*aSubIter)->undo();
-}
-
-bool Model_Document::canRedo()
-{
-  if (myDoc->GetAvailableRedos() > 0)
-    return true;
-  // check other subs contains operation that can be redoed
-  set<string>::iterator aSubIter = mySubs.begin();
-  for(; aSubIter != mySubs.end(); aSubIter++)
-    if (subDocument(*aSubIter)->canRedo())
-      return true;
-  return false;
-}
-
-void Model_Document::redo()
-{
-  if (!myIsEmptyTr[myTransactionsAfterSave])
-    myDoc->Redo();
-  myTransactionsAfterSave++;
-  synchronizeFeatures();
-  // redo for all subs
-  set<string>::iterator aSubIter = mySubs.begin();
-  for(; aSubIter != mySubs.end(); aSubIter++)
-    subDocument(*aSubIter)->redo();
-}
-
-boost::shared_ptr<ModelAPI_Feature> Model_Document::addFeature(string theID)
-{
-  boost::shared_ptr<ModelAPI_Feature> aFeature = ModelAPI_PluginManager::get()->createFeature(theID);
-  if (aFeature) {
-    boost::dynamic_pointer_cast<Model_Document>(aFeature->documentToAdd())->addFeature(aFeature);
-  } else {
-    // TODO: generate error that feature is not created
-  }
-  return aFeature;
-}
-
-void Model_Document::addFeature(const boost::shared_ptr<ModelAPI_Feature> theFeature)
-{
-  const std::string& aGroup = theFeature->getGroup();
-  TDF_Label aGroupLab = groupLabel(aGroup);
-  TDF_Label anObjLab = aGroupLab.NewChild();
-  boost::shared_ptr<Model_Data> aData(new Model_Data);
-  aData->setLabel(anObjLab);
-  boost::shared_ptr<ModelAPI_Document> aThis = Model_Application::getApplication()->getDocument(myID);
-  aData->setDocument(aThis);
-  theFeature->setData(aData);
-  setUniqueName(theFeature);
-  theFeature->initAttributes();
-  // keep the feature ID to restore document later correctly
-  TDataStd_Comment::Set(anObjLab, theFeature->getKind().c_str());
-  // put index of the feature in the group in the tree
-  TDataStd_Integer::Set(anObjLab, myFeatures[aGroup].size());
-  myFeatures[aGroup].push_back(theFeature);
-
-  // event: feature is added
-  static Event_ID anEvent = Event_Loop::eventByName(EVENT_FEATURE_CREATED);
-  ModelAPI_FeatureUpdatedMessage aMsg(aThis, theFeature, anEvent);
-  Event_Loop::loop()->send(aMsg);
-}
-
-boost::shared_ptr<ModelAPI_Feature> Model_Document::feature(TDF_Label& theLabel)
-{
-  Handle(TDataStd_Integer) aFeatureIndex;
-  if (theLabel.FindAttribute(TDataStd_Integer::GetID(), aFeatureIndex)) {
-    Handle(TDataStd_Comment) aGroupID;
-    if (theLabel.Father().FindAttribute(TDataStd_Comment::GetID(), aGroupID)) {
-      string aGroup = TCollection_AsciiString(aGroupID->Get()).ToCString();
-      return myFeatures[aGroup][aFeatureIndex->Get()];
-    }
-  }
-  return boost::shared_ptr<ModelAPI_Feature>(); // not found
-}
-
-int Model_Document::featureIndex(boost::shared_ptr<ModelAPI_Feature> theFeature)
-{
-  if (theFeature->data()->document().get() != this) {
-    return theFeature->data()->document()->featureIndex(theFeature);
-  }
-  boost::shared_ptr<Model_Data> aData = boost::dynamic_pointer_cast<Model_Data>(theFeature->data());
-  Handle(TDataStd_Integer) aFeatureIndex;
-  if (aData->label().FindAttribute(TDataStd_Integer::GetID(), aFeatureIndex)) {
-    return aFeatureIndex->Get();
-  }
-  return -1; // not found
-}
-
-boost::shared_ptr<ModelAPI_Document> Model_Document::subDocument(string theDocID)
-{
-  // just store sub-document identifier here to manage it later
-  if (mySubs.find(theDocID) == mySubs.end())
-    mySubs.insert(theDocID);
-  return Model_Application::getApplication()->getDocument(theDocID);
-}
-
-boost::shared_ptr<ModelAPI_Iterator> Model_Document::featuresIterator(const string theGroup)
-{
-  boost::shared_ptr<Model_Document> aThis(Model_Application::getApplication()->getDocument(myID));
-  // create an empty iterator for not existing group 
-  // (to avoidance of attributes management outside the transaction)
-  if (myGroups.find(theGroup) == myGroups.end())
-    return boost::shared_ptr<ModelAPI_Iterator>(new Model_Iterator());
-  return boost::shared_ptr<ModelAPI_Iterator>(new Model_Iterator(aThis, groupLabel(theGroup)));
-}
-
-boost::shared_ptr<ModelAPI_Feature> Model_Document::feature(const string& theGroupID, const int theIndex)
-{
-  // TODO: optimize this method
-  boost::shared_ptr<ModelAPI_Iterator>  anIter = featuresIterator(theGroupID);
-  for(int a = 0; a != theIndex && anIter->more(); anIter->next()) a++;
-  return anIter->more() ? anIter->current() : boost::shared_ptr<ModelAPI_Feature>();
-}
-
-const vector<string>& Model_Document::getGroups() const
-{
-  return myGroupsNames;
-}
-
-Model_Document::Model_Document(const std::string theID)
-    : myID(theID), myDoc(new TDocStd_Document("BinOcaf")) // binary OCAF format
-{
-  myDoc->SetUndoLimit(UNDO_LIMIT);
-  myTransactionsAfterSave = 0;
-  // to avoid creation of tag outside of the transaction (by iterator, for example)
-  /*
-  if (!myDoc->Main().FindChild(TAG_OBJECTS).IsAttribute(TDF_TagSource::GetID()))
-    TDataStd_Comment::Set(myDoc->Main().FindChild(TAG_OBJECTS).NewChild(), "");
-    */
-}
-
-TDF_Label Model_Document::groupLabel(const string theGroup)
-{
-  if (myGroups.find(theGroup) == myGroups.end()) {
-    myGroups[theGroup] = myDoc->Main().FindChild(TAG_OBJECTS).NewChild();
-    myGroupsNames.push_back(theGroup);
-    // set to the group label the group idntifier to restore on "open"
-    TDataStd_Comment::Set(myGroups[theGroup], theGroup.c_str());
-    myFeatures[theGroup] = vector<boost::shared_ptr<ModelAPI_Feature> >();
-  }
-  return myGroups[theGroup];
-}
-
-void Model_Document::setUniqueName(boost::shared_ptr<ModelAPI_Feature> theFeature)
-{
-  // first count all objects of such kind to start with index = count + 1
-  int aNumObjects = 0;
-  boost::shared_ptr<ModelAPI_Iterator> anIter = featuresIterator(theFeature->getGroup());
-  for(; anIter->more(); anIter->next()) {
-    if (anIter->currentKind() == theFeature->getKind())
-      aNumObjects++;
-  }
-  // generate candidate name
-  stringstream aNameStream;
-  aNameStream<<theFeature->getKind()<<"_"<<aNumObjects + 1;
-  string aName = aNameStream.str();
-  // check this is unique, if not, increase index by 1
-  for(anIter = featuresIterator(theFeature->getGroup()); anIter->more();) {
-    if (anIter->currentName() == aName) {
-      aNumObjects++;
-      stringstream aNameStream;
-      aNameStream<<theFeature->getKind()<<"_"<<aNumObjects + 1;
-      // reinitialize iterator to make sure a new name is unique
-      anIter = featuresIterator(theFeature->getGroup());
-    } else anIter->next();
-  }
-
-  theFeature->data()->setName(aName);
-}
-
-void Model_Document::synchronizeFeatures()
-{
-  boost::shared_ptr<ModelAPI_Document> aThis = Model_Application::getApplication()->getDocument(myID);
-  // iterate groups labels
-  TDF_ChildIDIterator aGroupsIter(myDoc->Main().FindChild(TAG_OBJECTS),
-    TDataStd_Comment::GetID(), Standard_False);
-  vector<string>::iterator aGroupNamesIter = myGroupsNames.begin();
-  for(; aGroupsIter.More() && aGroupNamesIter != myGroupsNames.end();
-        aGroupsIter.Next(), aGroupNamesIter++) {
-    string aGroupName = TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(
-      aGroupsIter.Value())->Get()).ToCString();
-    if (*aGroupNamesIter != aGroupName) 
-      break; // all since there is a change this must be recreated from scratch
-  }
-  // delete all groups left after the data model groups iteration
-  while(aGroupNamesIter != myGroupsNames.end()) {
-    string aGroupName = *aGroupNamesIter;
-    myFeatures.erase(aGroupName);
-    myGroups.erase(aGroupName);
-    aGroupNamesIter = myGroupsNames.erase(aGroupNamesIter);
-    // say that features were deleted from group
-    ModelAPI_FeatureDeletedMessage aMsg(aThis, aGroupName);
-    Event_Loop::loop()->send(aMsg);
-  }
-  // create new groups basing on the following data model update
-  for(; aGroupsIter.More(); aGroupsIter.Next()) {
-    string aGroupName = TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(
-      aGroupsIter.Value())->Get()).ToCString();
-    myGroupsNames.push_back(aGroupName);
-    myGroups[aGroupName] = aGroupsIter.Value()->Label();
-    myFeatures[aGroupName] = vector<boost::shared_ptr<ModelAPI_Feature> >();
-  }
-  // update features group by group
-  aGroupsIter.Initialize(myDoc->Main().FindChild(TAG_OBJECTS),
-    TDataStd_Comment::GetID(), Standard_False);
-  for(; aGroupsIter.More(); aGroupsIter.Next()) {
-    string aGroupName = TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(
-      aGroupsIter.Value())->Get()).ToCString();
-    // iterate features in internal container
-    vector<boost::shared_ptr<ModelAPI_Feature> >& aFeatures = myFeatures[aGroupName];
-    vector<boost::shared_ptr<ModelAPI_Feature> >::iterator aFIter = aFeatures.begin();
-    // and in parallel iterate labels of features
-    TDF_ChildIDIterator aFLabIter(
-      aGroupsIter.Value()->Label(), TDataStd_Comment::GetID(), Standard_False);
-    while(aFIter != aFeatures.end() || aFLabIter.More()) {
-      static const int INFINITE_TAG = INT_MAX; // no label means that it exists somwhere in infinite
-      int aFeatureTag = INFINITE_TAG; 
-      if (aFIter != aFeatures.end()) { // existing tag for feature
-        boost::shared_ptr<Model_Data> aData = boost::dynamic_pointer_cast<Model_Data>((*aFIter)->data());
-        aFeatureTag = aData->label().Tag();
-      }
-      int aDSTag = INFINITE_TAG; 
-      if (aFLabIter.More()) { // next label in DS is existing
-        aDSTag = aFLabIter.Value()->Label().Tag();
-      }
-      if (aDSTag > aFeatureTag) { // feature is removed
-        aFIter = aFeatures.erase(aFIter);
-        // event: model is updated
-        ModelAPI_FeatureDeletedMessage aMsg(aThis, aGroupName);
-        Event_Loop::loop()->send(aMsg);
-      } else if (aDSTag < aFeatureTag) { // a new feature is inserted
-        // create a feature
-        boost::shared_ptr<ModelAPI_Feature> aFeature = ModelAPI_PluginManager::get()->createFeature(
-          TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(
-          aFLabIter.Value())->Get()).ToCString());
-
-        boost::shared_ptr<Model_Data> aData(new Model_Data);
-        TDF_Label aLab = aFLabIter.Value()->Label();
-        aData->setLabel(aLab);
-        aData->setDocument(Model_Application::getApplication()->getDocument(myID));
-        aFeature->setData(aData);
-        aFeature->initAttributes();
-        // event: model is updated
-        static Event_ID anEvent = Event_Loop::eventByName(EVENT_FEATURE_CREATED);
-        ModelAPI_FeatureUpdatedMessage aMsg(aThis, aFeature, anEvent);
-        Event_Loop::loop()->send(aMsg);
-
-        if (aFIter == aFeatures.end()) {
-          aFeatures.push_back(aFeature);
-          aFIter = aFeatures.end();
-        } else {
-          aFIter++;
-          aFeatures.insert(aFIter, aFeature);
-        }
-        // feature for this label is added, so go to the next label
-        aFLabIter.Next();
-      } else { // nothing is changed, both iterators are incremented
-        aFIter++;
-        aFLabIter.Next();
-      }
-    }
-  }
-}
diff --git a/src/Model/Model_Events.cpp b/src/Model/Model_Events.cpp
new file mode 100644 (file)
index 0000000..a83e9ca
--- /dev/null
@@ -0,0 +1,25 @@
+// File:        Model_Events.cxx
+// Created:     10 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#include <Model_Events.h>
+#include <Event_Loop.h>
+
+ModelAPI_FeatureUpdatedMessage::ModelAPI_FeatureUpdatedMessage(
+  const boost::shared_ptr<ModelAPI_Document>& theDoc,
+  const boost::shared_ptr<ModelAPI_Feature>& theFeature, const Event_ID& theEvent)
+  : Event_Message(theEvent, 0), myFeature(theFeature), myDoc(theDoc)
+{}
+
+ModelAPI_FeatureDeletedMessage::ModelAPI_FeatureDeletedMessage(
+  const boost::shared_ptr<ModelAPI_Document>& theDoc, const std::string& theGroup)
+  : Event_Message(messageId(), 0), myDoc(theDoc), myGroup(theGroup)
+
+{
+}
+
+const Event_ID ModelAPI_FeatureDeletedMessage::messageId()
+{
+  static Event_ID MY_ID = Event_Loop::eventByName(EVENT_FEATURE_DELETED);
+  return MY_ID;
+}
diff --git a/src/Model/Model_Events.cxx b/src/Model/Model_Events.cxx
deleted file mode 100644 (file)
index a83e9ca..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-// File:        Model_Events.cxx
-// Created:     10 Apr 2014
-// Author:      Mikhail PONIKAROV
-
-#include <Model_Events.h>
-#include <Event_Loop.h>
-
-ModelAPI_FeatureUpdatedMessage::ModelAPI_FeatureUpdatedMessage(
-  const boost::shared_ptr<ModelAPI_Document>& theDoc,
-  const boost::shared_ptr<ModelAPI_Feature>& theFeature, const Event_ID& theEvent)
-  : Event_Message(theEvent, 0), myFeature(theFeature), myDoc(theDoc)
-{}
-
-ModelAPI_FeatureDeletedMessage::ModelAPI_FeatureDeletedMessage(
-  const boost::shared_ptr<ModelAPI_Document>& theDoc, const std::string& theGroup)
-  : Event_Message(messageId(), 0), myDoc(theDoc), myGroup(theGroup)
-
-{
-}
-
-const Event_ID ModelAPI_FeatureDeletedMessage::messageId()
-{
-  static Event_ID MY_ID = Event_Loop::eventByName(EVENT_FEATURE_DELETED);
-  return MY_ID;
-}
diff --git a/src/Model/Model_Iterator.cpp b/src/Model/Model_Iterator.cpp
new file mode 100644 (file)
index 0000000..ba59cdf
--- /dev/null
@@ -0,0 +1,67 @@
+// File:        Model_Iterator.hxx
+// Created:     1 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#include "Model_Iterator.h"
+#include "Model_Document.h"
+#include "ModelAPI_Feature.h"
+#include "Model_Data.h"
+#include <TDataStd_Comment.hxx>
+#include <TDataStd_Name.hxx>
+
+using namespace std;
+
+void Model_Iterator::next()
+{
+  return myIter.Next();
+}
+
+bool Model_Iterator::more()
+{
+  return myIter.More() == Standard_True;
+}
+
+boost::shared_ptr<ModelAPI_Feature> Model_Iterator::current()
+{
+  TDF_Label aLab = myIter.Value()->Label();
+  return myDoc->feature(aLab);
+}
+
+string Model_Iterator::currentKind()
+{
+  return string(TCollection_AsciiString(
+    Handle(TDataStd_Comment)::DownCast(myIter.Value())->Get()).ToCString());
+}
+
+string Model_Iterator::currentName()
+{
+  TDF_Label aLab = myIter.Value()->Label();
+  Handle(TDataStd_Name) aName;
+  if (aLab.FindAttribute(TDataStd_Name::GetID(), aName))
+    return string(TCollection_AsciiString(aName->Get()).ToCString());
+  return ""; // name is not found
+}
+
+int Model_Iterator::numIterationsLeft()
+{
+  int aResult = 0;
+  TDF_ChildIDIterator aTempIter(myIter);
+  for(; aTempIter.More(); aTempIter.Next())
+    aResult++;
+  return aResult;
+}
+
+bool Model_Iterator::isEqual(boost::shared_ptr<ModelAPI_Feature> theFeature)
+{
+  return (myIter.Value()->Label() == 
+    boost::dynamic_pointer_cast<Model_Data>(theFeature->data())->label()) == Standard_True;
+
+}
+
+Model_Iterator::Model_Iterator()
+{
+}
+
+Model_Iterator::Model_Iterator(boost::shared_ptr<Model_Document> theDoc, TDF_Label theLab)
+  : myDoc(theDoc), myIter(theLab, TDataStd_Comment::GetID(), Standard_False)
+{}
diff --git a/src/Model/Model_Iterator.cxx b/src/Model/Model_Iterator.cxx
deleted file mode 100644 (file)
index ba59cdf..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-// File:        Model_Iterator.hxx
-// Created:     1 Apr 2014
-// Author:      Mikhail PONIKAROV
-
-#include "Model_Iterator.h"
-#include "Model_Document.h"
-#include "ModelAPI_Feature.h"
-#include "Model_Data.h"
-#include <TDataStd_Comment.hxx>
-#include <TDataStd_Name.hxx>
-
-using namespace std;
-
-void Model_Iterator::next()
-{
-  return myIter.Next();
-}
-
-bool Model_Iterator::more()
-{
-  return myIter.More() == Standard_True;
-}
-
-boost::shared_ptr<ModelAPI_Feature> Model_Iterator::current()
-{
-  TDF_Label aLab = myIter.Value()->Label();
-  return myDoc->feature(aLab);
-}
-
-string Model_Iterator::currentKind()
-{
-  return string(TCollection_AsciiString(
-    Handle(TDataStd_Comment)::DownCast(myIter.Value())->Get()).ToCString());
-}
-
-string Model_Iterator::currentName()
-{
-  TDF_Label aLab = myIter.Value()->Label();
-  Handle(TDataStd_Name) aName;
-  if (aLab.FindAttribute(TDataStd_Name::GetID(), aName))
-    return string(TCollection_AsciiString(aName->Get()).ToCString());
-  return ""; // name is not found
-}
-
-int Model_Iterator::numIterationsLeft()
-{
-  int aResult = 0;
-  TDF_ChildIDIterator aTempIter(myIter);
-  for(; aTempIter.More(); aTempIter.Next())
-    aResult++;
-  return aResult;
-}
-
-bool Model_Iterator::isEqual(boost::shared_ptr<ModelAPI_Feature> theFeature)
-{
-  return (myIter.Value()->Label() == 
-    boost::dynamic_pointer_cast<Model_Data>(theFeature->data())->label()) == Standard_True;
-
-}
-
-Model_Iterator::Model_Iterator()
-{
-}
-
-Model_Iterator::Model_Iterator(boost::shared_ptr<Model_Document> theDoc, TDF_Label theLab)
-  : myDoc(theDoc), myIter(theLab, TDataStd_Comment::GetID(), Standard_False)
-{}
diff --git a/src/Model/Model_PluginManager.cpp b/src/Model/Model_PluginManager.cpp
new file mode 100644 (file)
index 0000000..e816fdd
--- /dev/null
@@ -0,0 +1,103 @@
+// File:        Model_PluginManager.cxx
+// Created:     20 Mar 2014
+// Author:      Mikhail PONIKAROV
+
+#include <Model_PluginManager.h>
+#include <ModelAPI_Feature.h>
+#include <ModelAPI_Plugin.h>
+#include <Model_Data.h>
+#include <Model_Document.h>
+#include <Model_Application.h>
+#include <Event_Loop.h>
+#include <Config_FeatureMessage.h>
+#include <Config_ModuleReader.h>
+
+using namespace std;
+
+static Model_PluginManager* myImpl = new Model_PluginManager();
+
+boost::shared_ptr<ModelAPI_Feature> Model_PluginManager::createFeature(string theFeatureID)
+{
+  if (this != myImpl) return myImpl->createFeature(theFeatureID);
+
+  LoadPluginsInfo();
+  if (myPlugins.find(theFeatureID) != myPlugins.end()) {
+    myCurrentPluginName = myPlugins[theFeatureID];
+    if (myPluginObjs.find(myCurrentPluginName) == myPluginObjs.end()) {
+      // load plugin library if not yet done
+      loadLibrary(myCurrentPluginName);
+    }
+    if (myPluginObjs.find(myCurrentPluginName) != myPluginObjs.end()) {
+      boost::shared_ptr<ModelAPI_Feature> aCreated = 
+        myPluginObjs[myCurrentPluginName]->createFeature(theFeatureID);
+      return aCreated;
+    }
+  }
+
+  return boost::shared_ptr<ModelAPI_Feature>(); // return nothing
+}
+
+boost::shared_ptr<ModelAPI_Document> Model_PluginManager::rootDocument()
+{
+  return boost::shared_ptr<ModelAPI_Document>(
+    Model_Application::getApplication()->getDocument("root"));
+}
+
+bool Model_PluginManager::hasRootDocument()
+{
+  return Model_Application::getApplication()->hasDocument("root");
+}
+
+boost::shared_ptr<ModelAPI_Document> Model_PluginManager::currentDocument()
+{
+  if (!myCurrentDoc)
+    myCurrentDoc = rootDocument();
+  return myCurrentDoc;
+}
+
+void Model_PluginManager::setCurrentDocument(boost::shared_ptr<ModelAPI_Document> theDoc)
+{
+  myCurrentDoc = theDoc;
+}
+
+Model_PluginManager::Model_PluginManager()
+{
+  myPluginsInfoLoaded = false;
+  //TODO(sbh): Implement static method to extract event id [SEID]
+  static Event_ID aFeatureEvent = Event_Loop::eventByName("FeatureRegisterEvent");
+
+  ModelAPI_PluginManager::SetPluginManager(boost::shared_ptr<ModelAPI_PluginManager>(this));
+  // register the configuration reading listener
+  Event_Loop* aLoop = Event_Loop::loop();
+  aLoop->registerListener(this, aFeatureEvent);
+}
+
+void Model_PluginManager::processEvent(const Event_Message* theMessage)
+{
+  const Config_FeatureMessage* aMsg =
+    dynamic_cast<const Config_FeatureMessage*>(theMessage);
+  if (aMsg) {
+    // proccess the plugin info, load plugin
+    if (myPlugins.find(aMsg->id()) == myPlugins.end()) {
+      myPlugins[aMsg->id()] = aMsg->pluginLibrary();
+    }
+  }
+  // plugins information was started to load, so, it will be loaded
+  myPluginsInfoLoaded = true;
+}
+
+void Model_PluginManager::LoadPluginsInfo()
+{
+  if (myPluginsInfoLoaded) // nothing to do
+    return;
+
+  // Read plugins information from XML files
+  Config_ModuleReader aXMLReader("FeatureRegisterEvent");
+  aXMLReader.setAutoImport(true);
+  aXMLReader.readAll();
+}
+
+void Model_PluginManager::registerPlugin(ModelAPI_Plugin* thePlugin)
+{
+  myPluginObjs[myCurrentPluginName] = thePlugin;
+}
diff --git a/src/Model/Model_PluginManager.cxx b/src/Model/Model_PluginManager.cxx
deleted file mode 100644 (file)
index 435ba3f..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-// File:        Model_PluginManager.cxx
-// Created:     20 Mar 2014
-// Author:      Mikhail PONIKAROV
-
-#include <Model_PluginManager.h>
-#include <ModelAPI_Feature.h>
-#include <ModelAPI_Plugin.h>
-#include <Model_Data.h>
-#include <Model_Document.h>
-#include <Model_Application.h>
-#include <Event_Loop.h>
-#include <Config_FeatureMessage.h>
-#include <Config_ModuleReader.h>
-
-using namespace std;
-
-static Model_PluginManager* myImpl = new Model_PluginManager();
-
-boost::shared_ptr<ModelAPI_Feature> Model_PluginManager::createFeature(string theFeatureID)
-{
-  if (this != myImpl) return myImpl->createFeature(theFeatureID);
-
-  LoadPluginsInfo();
-  if (myPlugins.find(theFeatureID) != myPlugins.end()) {
-    if (myPluginObjs.find(myPlugins[theFeatureID]) == myPluginObjs.end()) {
-      // load plugin library if not yet done
-      myCurrentPluginName = myPlugins[theFeatureID];
-      loadLibrary(myCurrentPluginName);
-    }
-    if (myPluginObjs.find(myCurrentPluginName) != myPluginObjs.end()) {
-      boost::shared_ptr<ModelAPI_Feature> aCreated = 
-        myPluginObjs[myCurrentPluginName]->createFeature(theFeatureID);
-      return aCreated;
-    }
-  }
-
-  return boost::shared_ptr<ModelAPI_Feature>(); // return nothing
-}
-
-boost::shared_ptr<ModelAPI_Document> Model_PluginManager::rootDocument()
-{
-  return boost::shared_ptr<ModelAPI_Document>(
-    Model_Application::getApplication()->getDocument("root"));
-}
-
-bool Model_PluginManager::hasRootDocument()
-{
-  return Model_Application::getApplication()->hasDocument("root");
-}
-
-boost::shared_ptr<ModelAPI_Document> Model_PluginManager::currentDocument()
-{
-  if (!myCurrentDoc)
-    myCurrentDoc = rootDocument();
-  return myCurrentDoc;
-}
-
-void Model_PluginManager::setCurrentDocument(boost::shared_ptr<ModelAPI_Document> theDoc)
-{
-  myCurrentDoc = theDoc;
-}
-
-Model_PluginManager::Model_PluginManager()
-{
-  myPluginsInfoLoaded = false;
-  //TODO(sbh): Implement static method to extract event id [SEID]
-  static Event_ID aFeatureEvent = Event_Loop::eventByName("FeatureRegisterEvent");
-
-  ModelAPI_PluginManager::SetPluginManager(boost::shared_ptr<ModelAPI_PluginManager>(this));
-  // register the configuration reading listener
-  Event_Loop* aLoop = Event_Loop::loop();
-  aLoop->registerListener(this, aFeatureEvent);
-}
-
-void Model_PluginManager::processEvent(const Event_Message* theMessage)
-{
-  const Config_FeatureMessage* aMsg =
-    dynamic_cast<const Config_FeatureMessage*>(theMessage);
-  if (aMsg) {
-    // proccess the plugin info, load plugin
-    if (myPlugins.find(aMsg->id()) == myPlugins.end()) {
-      myPlugins[aMsg->id()] = aMsg->pluginLibrary();
-    }
-  }
-  // plugins information was started to load, so, it will be loaded
-  myPluginsInfoLoaded = true;
-}
-
-void Model_PluginManager::LoadPluginsInfo()
-{
-  if (myPluginsInfoLoaded) // nothing to do
-    return;
-
-  // Read plugins information from XML files
-  Config_ModuleReader aXMLReader("FeatureRegisterEvent");
-  aXMLReader.setAutoImport(true);
-  aXMLReader.readAll();
-}
-
-void Model_PluginManager::registerPlugin(ModelAPI_Plugin* thePlugin)
-{
-  myPluginObjs[myCurrentPluginName] = thePlugin;
-}
index 075928b17ff057914d5712923a7abcaa7b316db2..b5a717b02be548139bd653856089a71b0c1b397e 100644 (file)
@@ -16,7 +16,7 @@ SET(PROJECT_HEADERS
 )
 
 SET(PROJECT_SOURCES
-    ModelAPI_PluginManager.cxx
+    ModelAPI_PluginManager.cpp
 )
 
 ADD_DEFINITIONS(-DMODELAPI_EXPORTS)
diff --git a/src/ModelAPI/ModelAPI_PluginManager.cpp b/src/ModelAPI/ModelAPI_PluginManager.cpp
new file mode 100644 (file)
index 0000000..772ac8d
--- /dev/null
@@ -0,0 +1,94 @@
+// File:        ModelAPI_PluginManager.hxx
+// Created:     20 Mar 2014
+// Author:      Mikhail PONIKAROV
+
+#include <ModelAPI_PluginManager.h>
+// to avoid unresolved ModelAPI_Document()
+#include <ModelAPI_Document.h>
+// to avoid unresolved ModelAPI_Feature()
+#include <ModelAPI_Feature.h>
+// to avoid unresolved ModelAPI_Data()
+#include <ModelAPI_Data.h>
+// to avoid unresolved ModelAPI_Plugin()
+#include <ModelAPI_Plugin.h>
+// to avoid unresolved ModelAPI_Iterator()
+#include <ModelAPI_Iterator.h>
+// to avoid unresolved ModelAPI_Iterator()
+#include <ModelAPI_Iterator.h>
+#include <ModelAPI_Attribute.h>
+#include <ModelAPI_AttributeDocRef.h>
+#include <ModelAPI_AttributeDouble.h>
+
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <dlfcn.h>
+#endif
+
+using namespace std;
+
+/// Converts library name to the operation system file name
+string library(const string& theLibName);
+
+/// Manager that will be initialized from Model package, one per application
+boost::shared_ptr<ModelAPI_PluginManager> MY_MANAGER;
+
+ModelAPI_PluginManager::ModelAPI_PluginManager()
+{
+}
+
+void ModelAPI_PluginManager::SetPluginManager(
+  boost::shared_ptr<ModelAPI_PluginManager> theManager)
+{
+  MY_MANAGER = theManager;
+}
+
+boost::shared_ptr<ModelAPI_PluginManager> ModelAPI_PluginManager::get()
+{
+  if (!MY_MANAGER) { // import Model library that implements this interface of ModelAPI
+    loadLibrary("Model");
+  }
+  return MY_MANAGER;
+}
+
+string library(const string& theLibName)
+{
+  string aLibName = theLibName;
+
+#ifndef WIN32
+  static string aLibExt( ".so" );
+  if (aLibName.size() < 3 || aLibName.substr(0, 3) !="lib")
+    aLibName = "lib" + aLibName;
+#else
+  static string aLibExt( ".dll" );
+#endif
+
+  string anExt = aLibName.substr(aLibName.size() - 4);
+
+  if ( anExt != aLibExt)
+    aLibName += aLibExt;
+
+  return aLibName;
+}
+
+// for debug purpose only (cerr), before the error management system is implemented
+#include <iostream>
+void ModelAPI_PluginManager::loadLibrary(const string theLibName)
+{
+  string aFileName = library(theLibName);
+  if ( aFileName.empty() )
+  {
+    cerr<<"Library "<<theLibName.c_str()<<" can not be imported"<<endl;
+    return;
+  }
+
+#ifdef WIN32
+  HINSTANCE aModLib = ::LoadLibrary( aFileName.c_str() ); 
+  if (!aModLib)
+    cerr<<"Failed to load "<<aFileName.c_str()<<endl;
+#else
+  void* aModLib = dlopen( aFileName.c_str(), RTLD_LAZY );
+  if ( !aModLib )
+    cerr<<"Failed to load "<<aFileName.c_str()<<endl;
+#endif
+}
diff --git a/src/ModelAPI/ModelAPI_PluginManager.cxx b/src/ModelAPI/ModelAPI_PluginManager.cxx
deleted file mode 100644 (file)
index 772ac8d..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-// File:        ModelAPI_PluginManager.hxx
-// Created:     20 Mar 2014
-// Author:      Mikhail PONIKAROV
-
-#include <ModelAPI_PluginManager.h>
-// to avoid unresolved ModelAPI_Document()
-#include <ModelAPI_Document.h>
-// to avoid unresolved ModelAPI_Feature()
-#include <ModelAPI_Feature.h>
-// to avoid unresolved ModelAPI_Data()
-#include <ModelAPI_Data.h>
-// to avoid unresolved ModelAPI_Plugin()
-#include <ModelAPI_Plugin.h>
-// to avoid unresolved ModelAPI_Iterator()
-#include <ModelAPI_Iterator.h>
-// to avoid unresolved ModelAPI_Iterator()
-#include <ModelAPI_Iterator.h>
-#include <ModelAPI_Attribute.h>
-#include <ModelAPI_AttributeDocRef.h>
-#include <ModelAPI_AttributeDouble.h>
-
-#ifdef WIN32
-#include <windows.h>
-#else
-#include <dlfcn.h>
-#endif
-
-using namespace std;
-
-/// Converts library name to the operation system file name
-string library(const string& theLibName);
-
-/// Manager that will be initialized from Model package, one per application
-boost::shared_ptr<ModelAPI_PluginManager> MY_MANAGER;
-
-ModelAPI_PluginManager::ModelAPI_PluginManager()
-{
-}
-
-void ModelAPI_PluginManager::SetPluginManager(
-  boost::shared_ptr<ModelAPI_PluginManager> theManager)
-{
-  MY_MANAGER = theManager;
-}
-
-boost::shared_ptr<ModelAPI_PluginManager> ModelAPI_PluginManager::get()
-{
-  if (!MY_MANAGER) { // import Model library that implements this interface of ModelAPI
-    loadLibrary("Model");
-  }
-  return MY_MANAGER;
-}
-
-string library(const string& theLibName)
-{
-  string aLibName = theLibName;
-
-#ifndef WIN32
-  static string aLibExt( ".so" );
-  if (aLibName.size() < 3 || aLibName.substr(0, 3) !="lib")
-    aLibName = "lib" + aLibName;
-#else
-  static string aLibExt( ".dll" );
-#endif
-
-  string anExt = aLibName.substr(aLibName.size() - 4);
-
-  if ( anExt != aLibExt)
-    aLibName += aLibExt;
-
-  return aLibName;
-}
-
-// for debug purpose only (cerr), before the error management system is implemented
-#include <iostream>
-void ModelAPI_PluginManager::loadLibrary(const string theLibName)
-{
-  string aFileName = library(theLibName);
-  if ( aFileName.empty() )
-  {
-    cerr<<"Library "<<theLibName.c_str()<<" can not be imported"<<endl;
-    return;
-  }
-
-#ifdef WIN32
-  HINSTANCE aModLib = ::LoadLibrary( aFileName.c_str() ); 
-  if (!aModLib)
-    cerr<<"Failed to load "<<aFileName.c_str()<<endl;
-#else
-  void* aModLib = dlopen( aFileName.c_str(), RTLD_LAZY );
-  if ( !aModLib )
-    cerr<<"Failed to load "<<aFileName.c_str()<<endl;
-#endif
-}
index 6d1dac2cccc2d6510842415eeb0a8c778d187122..8a1ddc82d8026a2fd0e0da390f0e1ccbe2a02985 100644 (file)
@@ -176,28 +176,6 @@ int ModuleBase_Operation::execStatus() const
   return myExecStatus;
 }
 
-/*!
- *  \brief Returns XML representation of the operation's widget.
- *  \return XML QString
- *
- *  Returns XML representation of the operation's widget.
- */
-const QString& ModuleBase_Operation::xmlRepresentation() const
-{
-  return myXmlRepr;
-}
-
-/*!
- *  \brief Sets XML representation of the operation's widget.
- *  \param xmlRepr - XML QString
- *
- *  Sets XML representation of the operation's widget.
- */
-void ModuleBase_Operation::setXmlRepresentation(const QString& xmlRepr)
-{
-  myXmlRepr = xmlRepr;
-}
-
 /*!
  * \brief Starts operation
  *
@@ -252,6 +230,19 @@ void ModuleBase_Operation::commit()
   emit stopped();
 }
 
+/*
+ * \brief Alias for start/abort slots
+ *
+ * Public slot. Aborts operation if false, else does nothing.
+ * Provided for S/S compatibility with QAction's toggle(bool)
+ */
+void ModuleBase_Operation::setRunning(bool on)
+{
+  if (!on) {
+    abort();
+  }
+}
+
 /*!
  * \brief Stores a real value in model.
  * \param theValue - to store
index d7441c339f5acad50cace04bf61e82de73b6a307..75724ea34e6e418524ebf2230986c3594fee9ac2 100644 (file)
@@ -91,10 +91,6 @@ public:
 
   int execStatus() const;
 
-  // Widget processing.
-  const QString& xmlRepresentation() const;
-  void setXmlRepresentation(const QString& xmlRepr);
-
 signals:
   void started();
   void aborted();
@@ -108,6 +104,10 @@ public slots:
   void abort();
   void commit();
 
+  //true = do nothing, false = abort()
+  //Provided for S/S compatibility with QAction's toggle(bool)
+  void setRunning(bool);
+
   // Data model operations.
   void storeReal(double);
 
@@ -131,7 +131,6 @@ private:
 
   //!< Next fields could be extracted into a subclass;
   QString myOperationId;
-  QString myXmlRepr;
   boost::shared_ptr<ModelAPI_Feature> myFeature;
 };
 
index c6dc06814d25a8b2d10e6c040973a10179101016..29ced2bd3ae5d3ec045762ae5eccaf591e6f3eb4 100644 (file)
@@ -29,6 +29,15 @@ public:
   ModuleBase_PropPanelOperation(const QString& theId = "", QObject* parent = 0);
   virtual ~ModuleBase_PropPanelOperation();
 
+  /*!
+   * \brief Replied whether the operation should be commited after the start, or the operation itself
+   *  do that. The default realization provides the check by the operation having the xml prepresentation 
+   *  @return the boolean value
+   */
+  virtual bool isPerformedImmediately() const
+  {
+    return xmlRepresentation().isEmpty();
+  }
   /*!
    *  \brief Returns XML representation of the operation's widget.
    *  \return XML QString
index c4d25a782b504e5b99d66092d66f8f95f72c9f05..1554472a2eef3d4f0a14f7d6a9154cc5504576ef 100644 (file)
@@ -7,7 +7,7 @@ SET(PROJECT_HEADERS
        PartSet.h
        PartSet_Module.h
        PartSet_OperationSketchBase.h
-        PartSet_OperationSketch.h
+    PartSet_OperationSketch.h
 )
 
 SET(PROJECT_SOURCES
@@ -27,6 +27,7 @@ SET(TEXT_RESOURCES
 SET(PROJECT_LIBRARIES
     ModuleBase
     Config
+    GeomAPI
     ${QT_LIBRARIES}
     ${CAS_KERNEL}
 )
@@ -47,6 +48,7 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/XGUI
                     ${CMAKE_SOURCE_DIR}/src/ModuleBase
                     ${CMAKE_SOURCE_DIR}/src/ModelAPI
                     ${CMAKE_SOURCE_DIR}/src/SketchPlugin
+                    ${CMAKE_SOURCE_DIR}/src/GeomAPI
                     ${CAS_INCLUDE_DIRS}
 )
 
index 7a32a90bbf3d4c11ed9e73e72baa1ffb37b64e04..f82eee4b2249fe239766cb03c221710028381f39 100644 (file)
@@ -5,6 +5,9 @@
 
 #include <XGUI_MainWindow.h>
 #include <XGUI_Displayer.h>
+#include <XGUI_Viewer.h>
+#include <XGUI_Workshop.h>
+#include <XGUI_OperationMgr.h>
 
 #include <Config_PointerMessage.h>
 #include <Config_ModuleReader.h>
@@ -29,6 +32,9 @@ extern "C" PARTSET_EXPORT XGUI_Module* createModule(XGUI_Workshop* theWshop)
 PartSet_Module::PartSet_Module(XGUI_Workshop* theWshop)
 {
   myWorkshop = theWshop;
+  XGUI_OperationMgr* anOperationMgr = myWorkshop->operationMgr();
+
+  connect(anOperationMgr, SIGNAL(beforeOperationStart()), this, SLOT(onBeforeOperationStart()));
 }
 
 PartSet_Module::~PartSet_Module()
@@ -37,52 +43,60 @@ PartSet_Module::~PartSet_Module()
 
 void PartSet_Module::createFeatures()
 {
-  Config_ModuleReader* aXMLReader = new Config_ModuleReader();
-  aXMLReader->setAutoImport(true);
-  aXMLReader->readAll();
-  delete aXMLReader;
+  Config_ModuleReader aXMLReader = Config_ModuleReader();
+  aXMLReader.setAutoImport(true);
+  aXMLReader.readAll();
 }
 
 void PartSet_Module::featureCreated(XGUI_Command* theFeature)
 {
-  QString aFtId = theFeature->id();
   theFeature->connectTo(this, SLOT(onFeatureTriggered()));
 }
 
-void PartSet_Module::onFeatureTriggered()
+std::string PartSet_Module::modulePlugin()
 {
   Config_ModuleReader aModuleReader = Config_ModuleReader();
   aModuleReader.readAll();
-  std::map<std::string, std::string> aPluginMap = aModuleReader.plugins();
+  std::map < std::string, std::string > aPluginMap = aModuleReader.plugins();
   std::string aPluginName = aPluginMap["PartSetPlugin"];
+  return aPluginName;
+}
+
+/*
+ *
+ */
+void PartSet_Module::onFeatureTriggered()
+{
+  std::string aPluginName = modulePlugin();
   Config_WidgetReader aWdgReader = Config_WidgetReader(aPluginName);
   aWdgReader.readAll();
   XGUI_Command* aCmd = dynamic_cast<XGUI_Command*>(sender());
   QString aCmdId = aCmd->id();
   std::string aXmlCfg = aWdgReader.featureWidgetCfg(aCmdId.toStdString());
   std::string aDescription = aWdgReader.featureDescription(aCmdId.toStdString());
-  //TODO(sbh): Implement static method to extract event id [SEID]
-  static Event_ID aModuleEvent = Event_Loop::eventByName("PartSetModuleEvent");
-  Config_PointerMessage aMessage(aModuleEvent, this);
   ModuleBase_PropPanelOperation* aPartSetOp;
-  if (aCmdId == "Sketch" )
+  if (aCmdId == "Sketch" ) {
     aPartSetOp = new PartSet_OperationSketch(aCmdId, this);
-  else
+  } else {
     aPartSetOp = new ModuleBase_PropPanelOperation(aCmdId, this);
-
-  PartSet_OperationSketchBase* aPreviewOp = dynamic_cast<PartSet_OperationSketchBase*>(aPartSetOp);
-  if (aPreviewOp)
-    connect(aPreviewOp, SIGNAL(visualizePreview()), this, SLOT(onVisualizePreview()));
-
+  }
   aPartSetOp->setXmlRepresentation(QString::fromStdString(aXmlCfg));
   aPartSetOp->setDescription(QString::fromStdString(aDescription));
+
+  //TODO(sbh): Implement static method to extract event id [SEID]
+  static Event_ID aModuleEvent = Event_Loop::eventByName("PartSetModuleEvent");
+  Config_PointerMessage aMessage(aModuleEvent, this);
   aMessage.setPointer(aPartSetOp);
   Event_Loop::loop()->send(aMessage);
 }
 
-void PartSet_Module::onVisualizePreview()
+/**
+ * Slot that is called by the operation requiring of preview display or erase
+ * \param isDisplay the display or erase state
+*/
+void PartSet_Module::onVisualizePreview(bool isDisplay)
 {
-  ModuleBase_Operation* anOperation = myWorkshop->currentOperation();
+  ModuleBase_Operation* anOperation = myWorkshop->operationMgr()->currentOperation();
   if (!anOperation)
     return;
 
@@ -90,5 +104,35 @@ void PartSet_Module::onVisualizePreview()
   if (!aPreviewOp)
     return;
 
-  myWorkshop->displayer()->Display(anOperation->feature(), aPreviewOp->preview());
+  if (isDisplay)
+    myWorkshop->displayer()->Display(anOperation->feature(), aPreviewOp->preview());
+  else
+    myWorkshop->displayer()->Erase(anOperation->feature(), aPreviewOp->preview());
+}
+
+void PartSet_Module::onBeforeOperationStart()
+{
+  ModuleBase_Operation* anOperation = myWorkshop->operationMgr()->currentOperation();
+
+  PartSet_OperationSketchBase* aPreviewOp = dynamic_cast<PartSet_OperationSketchBase*>(anOperation);
+  if (aPreviewOp) {
+    connect(anOperation, SIGNAL(stopped()), this, SLOT(onOperationStopped()));
+    connect(aPreviewOp, SIGNAL(visualizePreview(bool)), this, SLOT(onVisualizePreview(bool)));
+    connect(myWorkshop->mainWindow()->viewer(), SIGNAL(selectionChanged()),
+            aPreviewOp, SLOT(onViewSelectionChanged()));
+  }
+}
+
+void PartSet_Module::onOperationStopped()
+{
+  ModuleBase_PropPanelOperation* anOperation = dynamic_cast<ModuleBase_PropPanelOperation*>(sender());
+  if (!anOperation)
+    return;
+
+  PartSet_OperationSketchBase* aPreviewOp = dynamic_cast<PartSet_OperationSketchBase*>(anOperation);
+  if (aPreviewOp) {
+    disconnect(aPreviewOp, SIGNAL(visualizePreview(bool)), this, SLOT(onVisualizePreview(bool)));
+    disconnect(myWorkshop->mainWindow()->viewer(), SIGNAL(selectionChanged()),
+               aPreviewOp, SLOT(onViewSelectionChanged()));
+  }
 }
index b33d2ad7dac976aef554ba00e91c16c73c33182c..dc22f1f440137b4a13912da4ca494081584acf26 100644 (file)
@@ -11,6 +11,8 @@
 class PARTSET_EXPORT PartSet_Module: public QObject, public XGUI_Module
 {
 Q_OBJECT
+  std::string modulePlugin();
+
 public:
   PartSet_Module(XGUI_Workshop* theWshop);
   virtual ~PartSet_Module();
@@ -20,7 +22,9 @@ public:
 
 public slots:
   void onFeatureTriggered();
-  void onVisualizePreview();
+  void onBeforeOperationStart();
+  void onOperationStopped();
+  void onVisualizePreview(bool isDisplay);
 
 private:
   XGUI_Workshop* myWorkshop;
index b5aa1874753ff6b137242c411307295eff1910a8..3b4e5bd8d89b49ab98a2a5e8c4f1175ce350a656 100644 (file)
@@ -20,3 +20,12 @@ PartSet_OperationSketch::PartSet_OperationSketch(const QString& theId,
 PartSet_OperationSketch::~PartSet_OperationSketch()
 {
 }
+
+/**
+ * The sketch can not be created immediately, firstly a plane should be set
+ * \return false value
+ */
+bool PartSet_OperationSketch::isPerformedImmediately() const
+{
+  return false;
+}
index 99cacfbb68c7f2ff90ba8c661d40bcdd11fb5b25..679f74afa739013a653404c2612d27f99c48732c 100644 (file)
@@ -18,6 +18,8 @@ Q_OBJECT
 public:
   PartSet_OperationSketch(const QString& theId, QObject* theParent);
   virtual ~PartSet_OperationSketch();
+
+  virtual bool isPerformedImmediately() const;
 };
 
 #endif
index 69bbe52dce430078739c7e84f908e9bc99f61e78..ec0d695d529d49eaf43aa6d4bb347b3663931221 100644 (file)
@@ -32,7 +32,7 @@ PartSet_OperationSketchBase::~PartSet_OperationSketchBase()
 const TopoDS_Shape& PartSet_OperationSketchBase::preview() const
 {
   boost::shared_ptr<SketchPlugin_Feature> aFeature = boost::dynamic_pointer_cast<SketchPlugin_Feature>(feature());
-  return aFeature->preview();
+  return *(static_cast<TopoDS_Shape*>(aFeature->preview()->implementation()));
 }
 
 /*!
@@ -42,5 +42,15 @@ void PartSet_OperationSketchBase::startOperation()
 {
   ModuleBase_PropPanelOperation::startOperation();
 
-  emit visualizePreview();
+  emit visualizePreview(true);
+}
+
+/*!
+ * Perform the operation stop and emit signal about visualization stop of the operation preview
+ */
+void PartSet_OperationSketchBase::stopOperation()
+{
+  ModuleBase_PropPanelOperation::stopOperation();
+
+  emit visualizePreview(false);
 }
index ceb88026415606c8113319a9fc575c441f44e732..de7c1ce03e4f647b1a7163f30bc55351c14718e9 100644 (file)
@@ -24,10 +24,15 @@ public:
   const TopoDS_Shape& preview() const;
 
 signals:
-  void visualizePreview();
+  /**
+   * The signal about preview visualization.
+   * \param isDisplay a state whether the preview should be displayed or erased
+   */
+  void visualizePreview(bool isDisplay);
 
 protected:
   virtual void startOperation();
+  virtual void stopOperation();
 };
 
 #endif
index 1e52d1570473a6c4ba8132fc2f25ef8c5d6a42bd..d9715d5901bb3c088e605ae9d799a08705bc283c 100644 (file)
@@ -7,8 +7,8 @@ SET(PROJECT_HEADERS
 )
 
 SET(PROJECT_SOURCES
-    PartSetPlugin_Plugin.cxx
-    PartSetPlugin_Part.cxx
+    PartSetPlugin_Plugin.cpp
+    PartSetPlugin_Part.cpp
 )
 
 ADD_DEFINITIONS(-DPARTSETPLUGIN_EXPORTS ${BOOST_DEFINITIONS})
diff --git a/src/PartSetPlugin/PartSetPlugin_Part.cpp b/src/PartSetPlugin/PartSetPlugin_Part.cpp
new file mode 100644 (file)
index 0000000..1444117
--- /dev/null
@@ -0,0 +1,34 @@
+// File:        PartSetPlugin_Part.cxx
+// Created:     27 Mar 2014
+// Author:      Mikhail PONIKAROV
+
+#include "PartSetPlugin_Part.h"
+#include "ModelAPI_PluginManager.h"
+#include "ModelAPI_Document.h"
+#include "ModelAPI_Data.h"
+#include "ModelAPI_Iterator.h"
+#include "ModelAPI_AttributeDocRef.h"
+
+using namespace std;
+
+PartSetPlugin_Part::PartSetPlugin_Part()
+{
+}
+
+void PartSetPlugin_Part::initAttributes()
+{
+  data()->addAttribute(PART_ATTR_DOC_REF, ModelAPI_AttributeDocRef::type());
+}
+
+void PartSetPlugin_Part::execute() 
+{
+  boost::shared_ptr<ModelAPI_AttributeDocRef> aDocRef = data()->docRef(PART_ATTR_DOC_REF);
+  if (!aDocRef->value()) { // create a document if not yet created
+    boost::shared_ptr<ModelAPI_Document> aPartSetDoc = ModelAPI_PluginManager::get()->rootDocument();
+    aDocRef->setValue(aPartSetDoc->subDocument(data()->getName()));
+  }
+}
+
+boost::shared_ptr<ModelAPI_Document> PartSetPlugin_Part::documentToAdd() {
+  return ModelAPI_PluginManager::get()->rootDocument();
+}
diff --git a/src/PartSetPlugin/PartSetPlugin_Part.cxx b/src/PartSetPlugin/PartSetPlugin_Part.cxx
deleted file mode 100644 (file)
index 1444117..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-// File:        PartSetPlugin_Part.cxx
-// Created:     27 Mar 2014
-// Author:      Mikhail PONIKAROV
-
-#include "PartSetPlugin_Part.h"
-#include "ModelAPI_PluginManager.h"
-#include "ModelAPI_Document.h"
-#include "ModelAPI_Data.h"
-#include "ModelAPI_Iterator.h"
-#include "ModelAPI_AttributeDocRef.h"
-
-using namespace std;
-
-PartSetPlugin_Part::PartSetPlugin_Part()
-{
-}
-
-void PartSetPlugin_Part::initAttributes()
-{
-  data()->addAttribute(PART_ATTR_DOC_REF, ModelAPI_AttributeDocRef::type());
-}
-
-void PartSetPlugin_Part::execute() 
-{
-  boost::shared_ptr<ModelAPI_AttributeDocRef> aDocRef = data()->docRef(PART_ATTR_DOC_REF);
-  if (!aDocRef->value()) { // create a document if not yet created
-    boost::shared_ptr<ModelAPI_Document> aPartSetDoc = ModelAPI_PluginManager::get()->rootDocument();
-    aDocRef->setValue(aPartSetDoc->subDocument(data()->getName()));
-  }
-}
-
-boost::shared_ptr<ModelAPI_Document> PartSetPlugin_Part::documentToAdd() {
-  return ModelAPI_PluginManager::get()->rootDocument();
-}
diff --git a/src/PartSetPlugin/PartSetPlugin_Plugin.cpp b/src/PartSetPlugin/PartSetPlugin_Plugin.cpp
new file mode 100644 (file)
index 0000000..85ea1c3
--- /dev/null
@@ -0,0 +1,24 @@
+#include "PartSetPlugin_Plugin.h"
+#include "PartSetPlugin_Part.h"
+#include <ModelAPI_PluginManager.h>
+#include <ModelAPI_Document.h>
+
+using namespace std;
+
+// the only created instance of this plugin
+static PartSetPlugin_Plugin* MY_INSTANCE = new PartSetPlugin_Plugin();
+
+PartSetPlugin_Plugin::PartSetPlugin_Plugin() 
+{
+  // register this plugin
+  ModelAPI_PluginManager::get()->registerPlugin(this);
+}
+
+boost::shared_ptr<ModelAPI_Feature> PartSetPlugin_Plugin::createFeature(string theFeatureID)
+{
+  if (theFeatureID == "Part") {
+    return boost::shared_ptr<ModelAPI_Feature>(new PartSetPlugin_Part);
+  }
+  // feature of such kind is not found
+  return boost::shared_ptr<ModelAPI_Feature>();
+}
diff --git a/src/PartSetPlugin/PartSetPlugin_Plugin.cxx b/src/PartSetPlugin/PartSetPlugin_Plugin.cxx
deleted file mode 100644 (file)
index 85ea1c3..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#include "PartSetPlugin_Plugin.h"
-#include "PartSetPlugin_Part.h"
-#include <ModelAPI_PluginManager.h>
-#include <ModelAPI_Document.h>
-
-using namespace std;
-
-// the only created instance of this plugin
-static PartSetPlugin_Plugin* MY_INSTANCE = new PartSetPlugin_Plugin();
-
-PartSetPlugin_Plugin::PartSetPlugin_Plugin() 
-{
-  // register this plugin
-  ModelAPI_PluginManager::get()->registerPlugin(this);
-}
-
-boost::shared_ptr<ModelAPI_Feature> PartSetPlugin_Plugin::createFeature(string theFeatureID)
-{
-  if (theFeatureID == "Part") {
-    return boost::shared_ptr<ModelAPI_Feature>(new PartSetPlugin_Part);
-  }
-  // feature of such kind is not found
-  return boost::shared_ptr<ModelAPI_Feature>();
-}
index 0ee0c35cffae8f798a1b62ec3e0cd88a9345f15a..4ba2ef12446d2f19ed99e6ca212e5fab04049d95 100644 (file)
@@ -25,13 +25,13 @@ SET(TEXT_RESOURCES
 )
 # sources / static
 SET(PROJECT_SOURCES
-  PyConsole_Console.cxx
-  PyConsole_Editor.cxx
-  PyConsole_EnhEditor.cxx
-  PyConsole_EnhInterp.cxx
-  PyConsole_Event.cxx
-  PyConsole_Interp.cxx
-  PyConsole_Request.cxx
+  PyConsole_Console.cpp
+  PyConsole_Editor.cpp
+  PyConsole_EnhEditor.cpp
+  PyConsole_EnhInterp.cpp
+  PyConsole_Event.cpp
+  PyConsole_Interp.cpp
+  PyConsole_Request.cpp
 )
 
 SET(PROJECT_LIBRARIES
diff --git a/src/PyConsole/PyConsole_Console.cpp b/src/PyConsole/PyConsole_Console.cpp
new file mode 100644 (file)
index 0000000..5439147
--- /dev/null
@@ -0,0 +1,341 @@
+
+/*!
+  \class PyConsole_Console
+  \brief Python console widget.
+*/  
+
+#include "PyConsole_Interp.h"   /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!!
+#include "PyConsole_Console.h"
+#include "PyConsole_EnhEditor.h"
+#include "PyConsole_EnhInterp.h"
+
+//#include <Qtx.h>
+
+#include <QAction>
+#include <QApplication>
+#include <QClipboard>
+#include <QEvent>
+#include <QMenu>
+#include <QVBoxLayout>
+
+/*!
+  \brief Constructor.
+
+  Creates new python console widget.
+  \param parent parent widget
+  \param interp python interpreter
+*/
+PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* interp )
+: QWidget( parent )
+{
+  // create python interpreter
+  myInterp = interp;
+  if ( !myInterp )
+    myInterp = new PyConsole_Interp();
+  
+  // initialize Python interpretator
+  myInterp->initialize();
+  
+  // create editor console
+  QVBoxLayout* lay = new QVBoxLayout( this );
+  lay->setMargin( 0 );
+  myEditor = new PyConsole_Editor( myInterp, this );
+  char* synchronous = getenv("PYTHON_CONSOLE_SYNC");
+  if (synchronous && atoi(synchronous))
+  {
+      myEditor->setIsSync(true);
+  }
+  myEditor->viewport()->installEventFilter( this );
+  lay->addWidget( myEditor );
+
+  createActions();
+}
+
+/**
+ * Protected constructor.
+ */
+PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* i,  PyConsole_Editor* e)
+  : QWidget (parent), myEditor(e), myInterp(i)
+{}
+
+/*!
+  \brief Destructor.
+
+  Does nothing for the moment.
+*/
+PyConsole_Console::~PyConsole_Console()
+{
+}
+
+/*!
+  \brief Execute python command in the interpreter.
+  \param command string with command and arguments
+*/
+void PyConsole_Console::exec( const QString& command )
+{
+  if ( myEditor )
+    myEditor->exec( command );
+}
+
+/*!
+  \brief Execute python command in the interpreter 
+         and wait until it is finished.
+  
+  Block execution of main application until the python command is executed.
+  \param command string with command and arguments
+*/
+void PyConsole_Console::execAndWait( const QString& command )
+{
+  if ( myEditor )
+    myEditor->execAndWait( command );
+}
+
+/*!
+  \brief Get synchronous mode flag value.
+  
+  \sa setIsSync()
+  \return True if python console works in synchronous mode
+*/
+bool PyConsole_Console::isSync() const
+{
+  return myEditor->isSync();
+}
+
+/*!
+  \brief Set synchronous mode flag value.
+
+  In synhronous mode the Python commands are executed in the GUI thread
+  and the GUI is blocked until the command is finished. In the asynchronous
+  mode each Python command is executed in the separate thread that does not
+  block the main GUI loop.
+
+  \param on synhronous mode flag
+*/
+void PyConsole_Console::setIsSync( const bool on )
+{
+  myEditor->setIsSync( on );
+}
+
+/*!
+  \brief Get suppress output flag value.
+  
+  \sa setIsSuppressOutput()
+  \return True if python console output is suppressed.
+*/
+bool PyConsole_Console::isSuppressOutput() const
+{
+  return myEditor->isSuppressOutput();
+}
+
+/*!
+  \brief Set suppress output flag value.
+
+  In case if suppress output flag is true, the python 
+  console output suppressed.
+
+  \param on suppress output flag
+*/
+void PyConsole_Console::setIsSuppressOutput( const bool on )
+{
+  myEditor->setIsSuppressOutput(on);
+}
+
+/*!
+  \brief Get 'show banner' flag value.
+  
+  \sa setIsShowBanner()
+  \return \c true if python console shows banner
+*/
+bool PyConsole_Console::isShowBanner() const
+{
+  return myEditor->isShowBanner();
+}
+
+/*!
+  \brief Set 'show banner' flag value.
+
+  The banner is shown in the top of the python console window.
+
+  \sa isShowBanner()
+  \param on 'show banner' flag
+*/
+void PyConsole_Console::setIsShowBanner( const bool on )
+{
+  myEditor->setIsShowBanner( on );
+}
+
+/*!
+  \brief Change the python console's font.
+  \param f new font
+*/
+void PyConsole_Console::setFont( const QFont& f )
+{
+  if( myEditor )
+    myEditor->setFont( f );
+}
+
+/*!
+  \brief Get python console font.
+  \return current python console's font
+*/
+QFont PyConsole_Console::font() const
+{
+  QFont res;
+  if( myEditor )
+    res = myEditor->font();
+  return res;
+}
+
+/*!
+  \brief Event handler.
+
+  Handles context menu request event.
+
+  \param o object
+  \param e event
+  \return True if the event is processed and further processing should be stopped
+*/
+bool PyConsole_Console::eventFilter( QObject* o, QEvent* e )
+{
+  if ( o == myEditor->viewport() && e->type() == QEvent::ContextMenu )
+  {
+    //contextMenuRequest( (QContextMenuEvent*)e );
+    return true;
+  }
+  return QWidget::eventFilter( o, e );
+}
+
+/*!
+  \brief Create the context popup menu.
+
+  Fill in the popup menu with the commands.
+
+  \param menu context popup menu
+*/
+void PyConsole_Console::contextMenuPopup( QMenu* menu )
+{
+  if ( myEditor->isReadOnly() )
+    return;
+
+  menu->addAction( myActions[CopyId] );
+  menu->addAction( myActions[PasteId] );
+  menu->addAction( myActions[ClearId] );
+  menu->addSeparator();
+  menu->addAction( myActions[SelectAllId] );
+  menu->addSeparator();
+  menu->addAction( myActions[DumpCommandsId] );
+
+  //Qtx::simplifySeparators( menu );
+
+  updateActions();
+}
+
+/*!
+  \brief Set actions to be visible in the context popup menu.
+  
+  Actions, which IDs are set in \a flags parameter, will be shown in the 
+  context popup menu. Other actions will not be shown.
+
+  \param flags ORed together actions flags
+*/
+void PyConsole_Console::setMenuActions( const int flags )
+{
+  myActions[CopyId]->setVisible( flags & CopyId );
+  myActions[PasteId]->setVisible( flags & PasteId );
+  myActions[ClearId]->setVisible( flags & ClearId );
+  myActions[SelectAllId]->setVisible( flags & SelectAllId );
+  myActions[DumpCommandsId]->setVisible( flags & DumpCommandsId );
+}
+
+/*!
+  \brief Get menu actions which are currently visible in the context popup menu.
+  \return ORed together actions flags
+  \sa setMenuActions()
+*/
+int PyConsole_Console::menuActions() const
+{
+  int ret = 0;
+  ret = ret | ( myActions[CopyId]->isVisible() ? CopyId : 0 );
+  ret = ret | ( myActions[PasteId]->isVisible() ? PasteId : 0 );
+  ret = ret | ( myActions[ClearId]->isVisible() ? ClearId : 0 );
+  ret = ret | ( myActions[SelectAllId]->isVisible() ? SelectAllId : 0 );
+  ret = ret | ( myActions[DumpCommandsId]->isVisible() ? DumpCommandsId : 0 );
+  return ret;
+}
+
+/*!
+  \brief Create menu actions.
+
+  Create context popup menu actions.
+*/
+void PyConsole_Console::createActions()
+{
+  QAction* a = new QAction( tr( "EDIT_COPY_CMD" ), this );
+  a->setStatusTip( tr( "EDIT_COPY_CMD" ) );
+  connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( copy() ) );
+  myActions.insert( CopyId, a );
+
+  a = new QAction( tr( "EDIT_PASTE_CMD" ), this );
+  a->setStatusTip( tr( "EDIT_PASTE_CMD" ) );
+  connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( paste() ) );
+  myActions.insert( PasteId, a );
+
+  a = new QAction( tr( "EDIT_CLEAR_CMD" ), this );
+  a->setStatusTip( tr( "EDIT_CLEAR_CMD" ) );
+  connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( clear() ) );
+  myActions.insert( ClearId, a );
+
+  a = new QAction( tr( "EDIT_SELECTALL_CMD" ), this );
+  a->setStatusTip( tr( "EDIT_SELECTALL_CMD" ) );
+  connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( selectAll() ) );
+  myActions.insert( SelectAllId, a );
+  
+//  a = new QAction( tr( "EDIT_DUMPCOMMANDS_CMD" ), this );
+//  a->setStatusTip( tr( "EDIT_DUMPCOMMANDS_CMD" ) );
+//  connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( dump() ) );
+//  myActions.insert( DumpCommandsId, a );
+}
+
+/*!
+  \brief Update menu actions.
+
+  Update context popup menu action state.
+*/
+void PyConsole_Console::updateActions()
+{
+  myActions[CopyId]->setEnabled( myEditor->textCursor().hasSelection() );
+  myActions[PasteId]->setEnabled( !myEditor->isReadOnly() && !QApplication::clipboard()->text().isEmpty() );
+  myActions[SelectAllId]->setEnabled( !myEditor->document()->isEmpty() );
+}
+
+/**
+ * Similar to constructor of the base class but using enhanced objects.
+ * TODO: this should really be done in a factory to avoid code duplication.
+ * @param parent
+ * @param interp
+ */
+PyConsole_EnhConsole::PyConsole_EnhConsole( QWidget* parent, PyConsole_EnhInterp* interp)
+  : PyConsole_Console(parent, interp, 0)
+{
+  // create python interpreter
+  myInterp = interp;
+  if ( !myInterp )
+    myInterp = new PyConsole_EnhInterp();
+
+  // initialize Python interpretator
+  myInterp->initialize();
+
+  // create editor console
+  QVBoxLayout* lay = new QVBoxLayout( this );
+  lay->setMargin( 0 );
+  myEditor = new PyConsole_EnhEditor( static_cast<PyConsole_EnhInterp*>(myInterp), this );
+  char* synchronous = getenv("PYTHON_CONSOLE_SYNC");
+  if (synchronous && atoi(synchronous))
+  {
+      myEditor->setIsSync(true);
+  }
+  myEditor->viewport()->installEventFilter( this );
+  lay->addWidget( myEditor );
+
+  createActions();
+}
diff --git a/src/PyConsole/PyConsole_Console.cxx b/src/PyConsole/PyConsole_Console.cxx
deleted file mode 100644 (file)
index 5439147..0000000
+++ /dev/null
@@ -1,341 +0,0 @@
-
-/*!
-  \class PyConsole_Console
-  \brief Python console widget.
-*/  
-
-#include "PyConsole_Interp.h"   /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!!
-#include "PyConsole_Console.h"
-#include "PyConsole_EnhEditor.h"
-#include "PyConsole_EnhInterp.h"
-
-//#include <Qtx.h>
-
-#include <QAction>
-#include <QApplication>
-#include <QClipboard>
-#include <QEvent>
-#include <QMenu>
-#include <QVBoxLayout>
-
-/*!
-  \brief Constructor.
-
-  Creates new python console widget.
-  \param parent parent widget
-  \param interp python interpreter
-*/
-PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* interp )
-: QWidget( parent )
-{
-  // create python interpreter
-  myInterp = interp;
-  if ( !myInterp )
-    myInterp = new PyConsole_Interp();
-  
-  // initialize Python interpretator
-  myInterp->initialize();
-  
-  // create editor console
-  QVBoxLayout* lay = new QVBoxLayout( this );
-  lay->setMargin( 0 );
-  myEditor = new PyConsole_Editor( myInterp, this );
-  char* synchronous = getenv("PYTHON_CONSOLE_SYNC");
-  if (synchronous && atoi(synchronous))
-  {
-      myEditor->setIsSync(true);
-  }
-  myEditor->viewport()->installEventFilter( this );
-  lay->addWidget( myEditor );
-
-  createActions();
-}
-
-/**
- * Protected constructor.
- */
-PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* i,  PyConsole_Editor* e)
-  : QWidget (parent), myEditor(e), myInterp(i)
-{}
-
-/*!
-  \brief Destructor.
-
-  Does nothing for the moment.
-*/
-PyConsole_Console::~PyConsole_Console()
-{
-}
-
-/*!
-  \brief Execute python command in the interpreter.
-  \param command string with command and arguments
-*/
-void PyConsole_Console::exec( const QString& command )
-{
-  if ( myEditor )
-    myEditor->exec( command );
-}
-
-/*!
-  \brief Execute python command in the interpreter 
-         and wait until it is finished.
-  
-  Block execution of main application until the python command is executed.
-  \param command string with command and arguments
-*/
-void PyConsole_Console::execAndWait( const QString& command )
-{
-  if ( myEditor )
-    myEditor->execAndWait( command );
-}
-
-/*!
-  \brief Get synchronous mode flag value.
-  
-  \sa setIsSync()
-  \return True if python console works in synchronous mode
-*/
-bool PyConsole_Console::isSync() const
-{
-  return myEditor->isSync();
-}
-
-/*!
-  \brief Set synchronous mode flag value.
-
-  In synhronous mode the Python commands are executed in the GUI thread
-  and the GUI is blocked until the command is finished. In the asynchronous
-  mode each Python command is executed in the separate thread that does not
-  block the main GUI loop.
-
-  \param on synhronous mode flag
-*/
-void PyConsole_Console::setIsSync( const bool on )
-{
-  myEditor->setIsSync( on );
-}
-
-/*!
-  \brief Get suppress output flag value.
-  
-  \sa setIsSuppressOutput()
-  \return True if python console output is suppressed.
-*/
-bool PyConsole_Console::isSuppressOutput() const
-{
-  return myEditor->isSuppressOutput();
-}
-
-/*!
-  \brief Set suppress output flag value.
-
-  In case if suppress output flag is true, the python 
-  console output suppressed.
-
-  \param on suppress output flag
-*/
-void PyConsole_Console::setIsSuppressOutput( const bool on )
-{
-  myEditor->setIsSuppressOutput(on);
-}
-
-/*!
-  \brief Get 'show banner' flag value.
-  
-  \sa setIsShowBanner()
-  \return \c true if python console shows banner
-*/
-bool PyConsole_Console::isShowBanner() const
-{
-  return myEditor->isShowBanner();
-}
-
-/*!
-  \brief Set 'show banner' flag value.
-
-  The banner is shown in the top of the python console window.
-
-  \sa isShowBanner()
-  \param on 'show banner' flag
-*/
-void PyConsole_Console::setIsShowBanner( const bool on )
-{
-  myEditor->setIsShowBanner( on );
-}
-
-/*!
-  \brief Change the python console's font.
-  \param f new font
-*/
-void PyConsole_Console::setFont( const QFont& f )
-{
-  if( myEditor )
-    myEditor->setFont( f );
-}
-
-/*!
-  \brief Get python console font.
-  \return current python console's font
-*/
-QFont PyConsole_Console::font() const
-{
-  QFont res;
-  if( myEditor )
-    res = myEditor->font();
-  return res;
-}
-
-/*!
-  \brief Event handler.
-
-  Handles context menu request event.
-
-  \param o object
-  \param e event
-  \return True if the event is processed and further processing should be stopped
-*/
-bool PyConsole_Console::eventFilter( QObject* o, QEvent* e )
-{
-  if ( o == myEditor->viewport() && e->type() == QEvent::ContextMenu )
-  {
-    //contextMenuRequest( (QContextMenuEvent*)e );
-    return true;
-  }
-  return QWidget::eventFilter( o, e );
-}
-
-/*!
-  \brief Create the context popup menu.
-
-  Fill in the popup menu with the commands.
-
-  \param menu context popup menu
-*/
-void PyConsole_Console::contextMenuPopup( QMenu* menu )
-{
-  if ( myEditor->isReadOnly() )
-    return;
-
-  menu->addAction( myActions[CopyId] );
-  menu->addAction( myActions[PasteId] );
-  menu->addAction( myActions[ClearId] );
-  menu->addSeparator();
-  menu->addAction( myActions[SelectAllId] );
-  menu->addSeparator();
-  menu->addAction( myActions[DumpCommandsId] );
-
-  //Qtx::simplifySeparators( menu );
-
-  updateActions();
-}
-
-/*!
-  \brief Set actions to be visible in the context popup menu.
-  
-  Actions, which IDs are set in \a flags parameter, will be shown in the 
-  context popup menu. Other actions will not be shown.
-
-  \param flags ORed together actions flags
-*/
-void PyConsole_Console::setMenuActions( const int flags )
-{
-  myActions[CopyId]->setVisible( flags & CopyId );
-  myActions[PasteId]->setVisible( flags & PasteId );
-  myActions[ClearId]->setVisible( flags & ClearId );
-  myActions[SelectAllId]->setVisible( flags & SelectAllId );
-  myActions[DumpCommandsId]->setVisible( flags & DumpCommandsId );
-}
-
-/*!
-  \brief Get menu actions which are currently visible in the context popup menu.
-  \return ORed together actions flags
-  \sa setMenuActions()
-*/
-int PyConsole_Console::menuActions() const
-{
-  int ret = 0;
-  ret = ret | ( myActions[CopyId]->isVisible() ? CopyId : 0 );
-  ret = ret | ( myActions[PasteId]->isVisible() ? PasteId : 0 );
-  ret = ret | ( myActions[ClearId]->isVisible() ? ClearId : 0 );
-  ret = ret | ( myActions[SelectAllId]->isVisible() ? SelectAllId : 0 );
-  ret = ret | ( myActions[DumpCommandsId]->isVisible() ? DumpCommandsId : 0 );
-  return ret;
-}
-
-/*!
-  \brief Create menu actions.
-
-  Create context popup menu actions.
-*/
-void PyConsole_Console::createActions()
-{
-  QAction* a = new QAction( tr( "EDIT_COPY_CMD" ), this );
-  a->setStatusTip( tr( "EDIT_COPY_CMD" ) );
-  connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( copy() ) );
-  myActions.insert( CopyId, a );
-
-  a = new QAction( tr( "EDIT_PASTE_CMD" ), this );
-  a->setStatusTip( tr( "EDIT_PASTE_CMD" ) );
-  connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( paste() ) );
-  myActions.insert( PasteId, a );
-
-  a = new QAction( tr( "EDIT_CLEAR_CMD" ), this );
-  a->setStatusTip( tr( "EDIT_CLEAR_CMD" ) );
-  connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( clear() ) );
-  myActions.insert( ClearId, a );
-
-  a = new QAction( tr( "EDIT_SELECTALL_CMD" ), this );
-  a->setStatusTip( tr( "EDIT_SELECTALL_CMD" ) );
-  connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( selectAll() ) );
-  myActions.insert( SelectAllId, a );
-  
-//  a = new QAction( tr( "EDIT_DUMPCOMMANDS_CMD" ), this );
-//  a->setStatusTip( tr( "EDIT_DUMPCOMMANDS_CMD" ) );
-//  connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( dump() ) );
-//  myActions.insert( DumpCommandsId, a );
-}
-
-/*!
-  \brief Update menu actions.
-
-  Update context popup menu action state.
-*/
-void PyConsole_Console::updateActions()
-{
-  myActions[CopyId]->setEnabled( myEditor->textCursor().hasSelection() );
-  myActions[PasteId]->setEnabled( !myEditor->isReadOnly() && !QApplication::clipboard()->text().isEmpty() );
-  myActions[SelectAllId]->setEnabled( !myEditor->document()->isEmpty() );
-}
-
-/**
- * Similar to constructor of the base class but using enhanced objects.
- * TODO: this should really be done in a factory to avoid code duplication.
- * @param parent
- * @param interp
- */
-PyConsole_EnhConsole::PyConsole_EnhConsole( QWidget* parent, PyConsole_EnhInterp* interp)
-  : PyConsole_Console(parent, interp, 0)
-{
-  // create python interpreter
-  myInterp = interp;
-  if ( !myInterp )
-    myInterp = new PyConsole_EnhInterp();
-
-  // initialize Python interpretator
-  myInterp->initialize();
-
-  // create editor console
-  QVBoxLayout* lay = new QVBoxLayout( this );
-  lay->setMargin( 0 );
-  myEditor = new PyConsole_EnhEditor( static_cast<PyConsole_EnhInterp*>(myInterp), this );
-  char* synchronous = getenv("PYTHON_CONSOLE_SYNC");
-  if (synchronous && atoi(synchronous))
-  {
-      myEditor->setIsSync(true);
-  }
-  myEditor->viewport()->installEventFilter( this );
-  lay->addWidget( myEditor );
-
-  createActions();
-}
diff --git a/src/PyConsole/PyConsole_Editor.cpp b/src/PyConsole/PyConsole_Editor.cpp
new file mode 100644 (file)
index 0000000..62ef9e8
--- /dev/null
@@ -0,0 +1,1105 @@
+
+/*!
+  \class PyConsole_Editor
+  \brief Python command line interpreter front-end GUI widget.
+  
+  This class provides simple GUI interface to the Python interpreter, including basic 
+  navigation operations, executing commands (both interactively and programmatically), 
+  copy-paste operations, history of the commands and so on.
+
+  Here below is the shortcut keyboard boundings used for navigation and other operations:
+  - <Enter>              : execute current command
+  - <Ctrl><Break>        : clear current command
+  - <Escape>             : clear current command
+  - <Up>                 : previous command in the history
+  - <Shift><Up>          : move cursor one row up with selection
+  - <Ctrl><Up>           : move cursor one row up without selection
+  - <Ctrl><Shift><Up>    : move cursor one row up with selection
+  - <Down>               : next command in the history
+  - <Shift><Down>        : move cursor one row down with selection
+  - <Ctrl><Down>         : move cursor one row down without selection
+  - <Ctrl><Shift><Down>  : move cursor one row down with selection
+  - <Left>               : move one symbol left without selection
+  - <Shift><Left>        : move one symbol left with selection
+  - <Ctrl><Left>         : move one word left without selection
+  - <Ctrl><Shift><Left>  : move one word left with selection
+  - <Right>              : move one symbol right without selection
+  - <Shift><Right>       : move one symbol right with selection
+  - <Ctrl><Right>        : move one word right without selection
+  - <Ctrl><Shift><Right> : move one word right with selection
+  - <PgUp>               : first command in the history
+  - <Shift><PgUp>        : move one page up with selection
+  - <Ctrl><PgUp>         : move one page up without selection
+  - <Ctrl><Shift><PgUp>  : scroll one page up
+  - <PgDn>               : last command in the history
+  - <Shift><PgDn>        : move one page down with selection
+  - <Ctrl><PgDn>         : move one page down without selection
+  - <Ctrl><Shift><PgDn>  : scroll one page down
+  - <Home>               : move to the beginning of the line without selection
+  - <Shift><Home>        : move to the beginning of the line with selection
+  - <Ctrl><Home>         : move to the very first symbol without selection
+  - <Ctrl><Shift><Home>  : move to the very first symbol with selection
+  - <End>                : move to the end of the line without selection
+  - <Shift><End>         : move to the end of the line with selection
+  - <Ctrl><End>          : move to the very last symbol without selection
+  - <Ctrl><Shift><End>   : move to the very last symbol with selection
+  - <Backspace>          : delete symbol before the cursor
+                           / remove selected text and put it to the clipboard (cut)
+  - <Shift><Backspace>   : delete previous word
+                           / remove selected text and put it to the clipboard (cut)
+  - <Ctrl><Backspace>    : delete text from the cursor to the beginning of the line 
+                           / remove selected text and put it to the clipboard (cut)
+  - <Delete>             : delete symbol after the cursor 
+                           / remove selected text and put it to the clipboard (cut)
+  - <Shift><Delete>      : delete next word
+                           / remove selected text and put it to the clipboard (cut)
+  - <Ctrl><Delete>       : delete text from the cursor to the end of the line
+                           / remove selected text and put it to the clipboard (cut)
+  - <Ctrl><Insert>       : copy
+  - <Shift><Insert>      : paste
+  - <Ctrl><V>            : paste
+  - <Ctrl><C>            : copy
+  - <Ctrl><X>            : cut
+  - <Ctrl><V>            : paste
+*/
+
+#include "PyConsole_Interp.h"   // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
+#include "PyConsole_Editor.h"
+#include "PyConsole_Event.h"
+#include "PyInterp_Event.h"
+#include "PyInterp_Dispatcher.h"
+#include "PyConsole_Request.h"
+
+//#include <SUIT_Tools.h>
+//#include <SUIT_FileDlg.h>
+//#include <SUIT_MessageBox.h>
+//#include <SUIT_FileValidator.h>
+
+#include <QApplication>
+#include <QClipboard>
+#include <QDropEvent>
+#include <QEvent>
+#include <QKeyEvent>
+#include <QMouseEvent>
+#include <QScrollBar>
+#include <QTextBlock>
+#include <QTextCursor>
+#include <QTextDocument>
+#include <QTextStream>
+#include <QChar>
+
+static QString READY_PROMPT = ">>> ";
+static QString DOTS_PROMPT  = "... ";
+
+/*class DumpCommandsFileValidator : public SUIT_FileValidator
+{
+ public:
+  DumpCommandsFileValidator( QWidget* parent = 0 ) : SUIT_FileValidator ( parent ) {};
+  virtual ~DumpCommandsFileValidator() {};
+  virtual bool canSave( const QString& file, bool permissions );
+};
+
+bool DumpCommandsFileValidator::canSave(const QString& file, bool permissions)
+{
+  QFileInfo fi( file );
+  if ( !QRegExp( "[A-Za-z_][A-Za-z0-9_]*" ).exactMatch( fi.completeBaseName() ) ) {
+    SUIT_MessageBox::critical( parent(),
+                               QObject::tr("WRN_WARNING"),
+                               QObject::tr("WRN_FILE_NAME_BAD") );
+    return false;
+  }
+  return SUIT_FileValidator::canSave( file, permissions);
+}
+*/
+void staticCallbackStdout( void* data, char* c )
+{
+  if(!((PyConsole_Editor*)data)->isSuppressOutput())
+    QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c, false ) );
+}
+
+void staticCallbackStderr( void* data, char* c )
+{
+  if(!((PyConsole_Editor*)data)->isSuppressOutput())
+    QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c, true ) );
+}
+
+
+/*!
+  \brief Constructor. 
+  
+  Creates python editor window.
+  \param theInterp python interper
+  \param theParent parent widget
+*/
+PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp, 
+                                    QWidget*          theParent )
+: QTextEdit( theParent ),
+  myInterp( 0 ),
+  myCmdInHistory( -1 ),
+  myEventLoop( 0 ),
+  myShowBanner( true ),
+  myIsSync( false ),
+  myIsSuppressOutput( false )
+{
+  //QString fntSet( "" );
+  QFont aFont = QFont( "Courier", 11 );//SUIT_Tools::stringToFont( fntSet );
+  setFont( aFont );
+  setUndoRedoEnabled( false );
+
+  myPrompt = READY_PROMPT;
+  setLineWrapMode( QTextEdit::WidgetWidth );
+  setWordWrapMode( QTextOption::WrapAnywhere );
+  setAcceptRichText( false );
+
+  theInterp->setvoutcb( staticCallbackStdout, this );
+  theInterp->setverrcb( staticCallbackStderr, this );
+
+  // san - This is necessary for troubleless initialization
+  onPyInterpChanged( theInterp );
+}
+
+/*!
+  \brief Destructor.
+
+  Does nothing for the moment.
+*/
+PyConsole_Editor::~PyConsole_Editor()
+{
+}
+
+/*!
+  \brief Get synchronous mode flag value.
+  
+  \sa setIsSync()
+  \return True if python console works in synchronous mode
+*/
+bool PyConsole_Editor::isSync() const
+{
+  return myIsSync;
+}
+
+/*!
+  \brief Set synchronous mode flag value.
+
+  In synhronous mode the Python commands are executed in the GUI thread
+  and the GUI is blocked until the command is finished. In the asynchronous
+  mode each Python command is executed in the separate thread that does not
+  block the main GUI loop.
+
+  \param on synhronous mode flag
+*/
+void PyConsole_Editor::setIsSync( const bool on )
+{
+  myIsSync = on;
+}
+
+/*!
+  \brief Get suppress output flag value.
+  
+  \sa setIsSuppressOutput()
+  \return \c true if python console output is suppressed.
+*/
+bool PyConsole_Editor::isSuppressOutput() const
+{
+  return myIsSuppressOutput;
+}
+
+/*!
+  \brief Set suppress output flag value.
+
+  In case if suppress output flag is true, the python 
+  console output suppressed.
+
+  \param on suppress output flag
+*/
+void PyConsole_Editor::setIsSuppressOutput( const bool on )
+{
+  myIsSuppressOutput = on;
+}
+
+/*!
+  \brief Get 'show banner' flag value.
+  
+  \sa setIsShowBanner()
+  \return \c true if python console shows banner
+*/
+bool PyConsole_Editor::isShowBanner() const
+{
+  return myShowBanner;
+}
+
+/*!
+  \brief Set 'show banner' flag value.
+
+  The banner is shown in the top of the python console window.
+
+  \sa isShowBanner()
+  \param on 'show banner' flag
+*/
+void PyConsole_Editor::setIsShowBanner( const bool on )
+{
+  if ( myShowBanner != on ) {
+    myShowBanner = on;
+    clear();
+  }
+}
+
+/*!
+  \brief Get size hint for the Python console window
+  \return size hint value
+*/
+QSize PyConsole_Editor::sizeHint() const
+{
+  QFontMetrics fm( font() );
+  int nbLines = ( isShowBanner() ? myBanner.split("\n").count() : 0 ) + 1;
+  QSize s(100, fm.lineSpacing()*nbLines);
+  return s;
+}
+
+/*!
+  \brief Put the string \a str to the python editor.
+  \param str string to be put in the command line of the editor
+  \param newBlock if True, then the string is printed on a new line
+  \param isError if true, the text is printed in dark red
+*/
+void PyConsole_Editor::addText( const QString& str, 
+                                const bool     newBlock,
+                                const bool    isError)
+{
+  QTextCursor theCursor(textCursor());
+  QTextCharFormat cf;
+
+  moveCursor( QTextCursor::End );
+  if ( newBlock )
+    theCursor.insertBlock();
+  if (isError)
+      cf.setForeground(QBrush(Qt::red));
+  else
+      cf.setForeground(QBrush(Qt::black));
+  theCursor.insertText( str, cf);
+  moveCursor( QTextCursor::End );
+  ensureCursorVisible();
+}
+
+/*!
+  \brief Convenient method for executing a Python command,
+  as if the user typed it manually.
+  \param command python command to be executed
+
+  !!! WARNING: doesn't work properly with multi-line commands. !!!
+*/
+void PyConsole_Editor::exec( const QString& command )
+{
+  if ( isReadOnly() ) {
+    // some interactive command is being executed in this editor...
+    // shedule the command to the queue
+    myQueue.push_back( command );
+    return;
+  }
+  // remove last line
+  moveCursor( QTextCursor::End );
+  moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
+  textCursor().removeSelectedText();
+  // set "ready" prompt
+  myPrompt = READY_PROMPT;
+  // clear command buffer
+  myCommandBuffer.truncate( 0 );
+  // unset history browsing mode
+  myCmdInHistory = -1;
+  // print command line by line
+  QString cmd = command;
+  if ( !cmd.endsWith( "\n" ) ) cmd += "\n";
+  QStringList lines = command.split( "\n" );
+  for ( int i = 0; i < lines.size(); i++ ) {
+    if ( !lines[i].trimmed().isEmpty() )
+      myHistory.push_back( lines[i] );
+    addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 );
+  }
+  // IPAL20182
+  addText( "", true );
+  // set read-only mode
+  setReadOnly( true );
+  // set busy cursor
+  setCursor( Qt::BusyCursor );
+  
+  // post a request to execute Python command;
+  // editor will be informed via a custom event that execution has been completed
+  PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) );
+}
+
+/*!
+  \brief Create request to the python dispatcher for the command execution.
+
+  \param command python command to be executed
+ */
+PyInterp_Request* PyConsole_Editor::createRequest( const QString& command )
+{
+  return new ExecCommand( myInterp, command, this, isSync() );
+}
+
+/*!
+  \brief Execute command in the python interpreter
+  and wait until it is finished.
+
+  \param command python command to be executed
+ */
+void PyConsole_Editor::execAndWait( const QString& command )
+{
+  // already running ?
+  if( myEventLoop )
+    return;
+
+  // create new event loop
+  myEventLoop = new QEventLoop( this );
+  // execute command
+  exec( command );
+  // run event loop
+  myEventLoop->exec();
+  // delete event loop after command is processed
+  delete myEventLoop;
+  myEventLoop = 0;
+}
+
+/*!
+  \brief Process "Enter" key press event. 
+  
+  Execute the command entered by the user.
+*/
+void PyConsole_Editor::handleReturn()
+{
+  // Position cursor at the end
+  QTextCursor curs(textCursor());
+  curs.movePosition(QTextCursor::End);
+  setTextCursor(curs);
+
+  // get last line
+  QTextBlock par = document()->end().previous();
+  if ( !par.isValid() ) return;
+
+  // get command
+  QString cmd = par.text().remove( 0, promptSize() );
+  // extend the command buffer with the current command 
+  myCommandBuffer.append( cmd );
+  // add command to the history
+  if ( !cmd.trimmed().isEmpty() )
+    myHistory.push_back( cmd );
+
+  // IPAL19397
+  addText( "", true ); 
+  
+  // set read-only mode
+  setReadOnly( true );
+  // set busy cursor
+  setCursor( Qt::BusyCursor );
+  
+  // post a request to execute Python command;
+  // editor will be informed via a custom event that execution has been completed
+  PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) );
+}
+
+/*!
+  \brief Process drop event.
+
+  Paste dragged text.
+  \param event drop event
+*/
+void PyConsole_Editor::dropEvent( QDropEvent* event )
+{
+  // get the initial drop position
+  QPoint pos = event->pos();
+  QTextCursor cur = cursorForPosition( event->pos() );
+  // if the position is not in the last line move it to the end of the command line
+  if ( cur.position() < document()->end().previous().position() + promptSize() ) {
+    moveCursor( QTextCursor::End );
+    pos = cursorRect().center();
+  }
+  // create new drop event and use it instead of the original
+  QDropEvent de( pos,
+                 event->possibleActions(),
+                 event->mimeData(),
+                 event->mouseButtons(),
+                 event->keyboardModifiers(),
+                 event->type() );
+  QTextEdit::dropEvent( &de );
+  // accept the original event
+  event->acceptProposedAction();
+}
+
+/*!
+  \brief Process mouse button release event.
+
+  Left mouse button: copy selection to the clipboard.
+  Middle mouse button: paste clipboard's contents.
+  \param event mouse event
+*/
+void PyConsole_Editor::mouseReleaseEvent( QMouseEvent* event )
+{
+  if ( event->button() == Qt::LeftButton ) {
+    QTextEdit::mouseReleaseEvent( event );
+    //copy();
+  }
+  else if ( event->button() == Qt::MidButton ) {
+    QTextCursor cur = cursorForPosition( event->pos() );
+    // if the position is not in the last line move it to the end of the command line
+    if ( cur.position() < document()->end().previous().position() + promptSize() ) {
+      moveCursor( QTextCursor::End );
+    }
+    else {
+      setTextCursor( cur );
+    }
+    const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ? 
+                                                              QClipboard::Selection : QClipboard::Clipboard );
+    if ( md )
+      insertFromMimeData( md );
+  }
+  else {
+    QTextEdit::mouseReleaseEvent( event );
+  }
+}
+
+/*!
+  \brief Check if the string is command.
+  
+  Return True if the string \a str is likely to be the command
+  (i.e. it is started from the '>>>' or '...').
+  \param str string to be checked
+*/
+bool PyConsole_Editor::isCommand( const QString& str ) const
+{
+  return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT );
+}
+
+/*!
+  \brief Handle keyboard event.
+
+  Implement navigation, history browsing, copy/paste and other common
+  operations.
+  \param event keyboard event
+*/
+void PyConsole_Editor::keyPressEvent( QKeyEvent* event )
+{
+  // get cursor position
+  QTextCursor cur = textCursor();
+  int curLine = cur.blockNumber();
+  int curCol  = cur.columnNumber();
+
+  // get last edited line
+  int endLine = document()->blockCount()-1;
+
+  // get pressed key code
+  int aKey = event->key();
+
+  // check if <Ctrl> is pressed
+  bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
+  // check if <Shift> is pressed
+  bool shftPressed = event->modifiers() & Qt::ShiftModifier;
+
+  if ( aKey == Qt::Key_Escape || ( ctrlPressed && aKey == -1 ) ) {
+    // process <Ctrl>+<Break> key-binding and <Escape> key: clear current command
+    myCommandBuffer.truncate( 0 );
+    myPrompt = READY_PROMPT;
+    addText( myPrompt, true );
+    horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
+    return;
+  }
+  else if ( ctrlPressed && aKey == Qt::Key_C ) {
+    // process <Ctrl>+<C> key-binding : copy
+    copy();
+    return;
+  }
+  else if ( ctrlPressed && aKey == Qt::Key_X ) {
+    // process <Ctrl>+<X> key-binding : cut
+    cut();
+    return;
+  }
+  else if ( ctrlPressed && aKey == Qt::Key_V ) {
+    // process <Ctrl>+<V> key-binding : paste
+    paste();
+    return;
+  }
+
+  // check for printed key
+  // #### aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0;
+  // Better:
+  aKey = !(QChar(aKey).isPrint()) ? aKey : 0;
+
+  switch ( aKey ) {
+  case 0 :
+    // any printed key: just print it
+    {
+      if ( curLine < endLine || curCol < promptSize() ) {
+        moveCursor( QTextCursor::End );
+      }
+      QTextEdit::keyPressEvent( event );
+      break;
+    }
+  case Qt::Key_Return:
+  case Qt::Key_Enter:
+    // <Enter> key: process the current command
+    {
+      handleReturn();
+      break;
+    }
+  case Qt::Key_Up:
+    // <Up> arrow key: process as follows:
+    // - without <Ctrl>, <Shift> modifiers: previous command in history
+    // - with <Ctrl> modifier key pressed:  move cursor one row up without selection
+    // - with <Shift> modifier key pressed: move cursor one row up with selection
+    // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row up
+    {
+      if ( ctrlPressed && shftPressed ) {
+        int value   = verticalScrollBar()->value();
+        int spacing = fontMetrics().lineSpacing();
+        verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 );
+      }
+      else if ( shftPressed || ctrlPressed ) {
+        if ( curLine > 0 )
+          moveCursor( QTextCursor::Up, 
+                      shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
+      }
+      else { 
+        if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
+          // set history browsing mode
+          myCmdInHistory = myHistory.count();
+          // remember current command
+          QTextBlock par = document()->end().previous();
+          myCurrentCommand = par.text().remove( 0, promptSize() );
+        }
+        if ( myCmdInHistory > 0 ) {
+          myCmdInHistory--;
+          // get previous command in the history
+          QString previousCommand = myHistory.at( myCmdInHistory );
+          // print previous command
+          moveCursor( QTextCursor::End );
+          moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
+          textCursor().removeSelectedText();
+          addText( myPrompt + previousCommand ); 
+          // move cursor to the end
+          moveCursor( QTextCursor::End );
+        }
+      }
+      break;
+    }
+  case Qt::Key_Down:
+    // <Down> arrow key: process as follows:
+    // - without <Ctrl>, <Shift> modifiers: next command in history
+    // - with <Ctrl> modifier key pressed:  move cursor one row down without selection
+    // - with <Shift> modifier key pressed: move cursor one row down with selection
+    // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row down
+    {
+      if ( ctrlPressed && shftPressed ) {
+        int value   = verticalScrollBar()->value();
+        int maxval  = verticalScrollBar()->maximum();
+        int spacing = fontMetrics().lineSpacing();
+        verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval );
+      }
+      else if ( shftPressed || ctrlPressed) {
+        if ( curLine < endLine )
+          moveCursor( QTextCursor::Down, 
+                      shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
+      }
+      else { 
+        if ( myCmdInHistory >= 0 ) {
+          // get next command in the history
+          myCmdInHistory++;
+          QString nextCommand;
+          if ( myCmdInHistory < myHistory.count() ) {
+            // next command in history
+            nextCommand = myHistory.at( myCmdInHistory );
+          }
+          else {
+            // end of history is reached
+            // last printed command
+            nextCommand = myCurrentCommand;
+            // unset history browsing mode
+            myCmdInHistory = -1;
+          }
+          // print next or current command
+          moveCursor( QTextCursor::End );
+          moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
+          textCursor().removeSelectedText();
+          addText( myPrompt + nextCommand );
+          // move cursor to the end
+          moveCursor( QTextCursor::End );
+        }
+      }
+      break;
+    }
+  case Qt::Key_Left:
+    // <Left> arrow key: process as follows:
+    // - without <Ctrl>, <Shift> modifiers: move one symbol left (taking into account prompt)
+    // - with <Ctrl> modifier key pressed:  move one word left (taking into account prompt)
+    // - with <Shift> modifier key pressed: move one symbol left with selection
+    // - with <Ctrl>+<Shift> modifier keys pressed: move one word left with selection
+    {
+      QString txt = textCursor().block().text();
+      if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) {
+        moveCursor( QTextCursor::Up );
+        moveCursor( QTextCursor::EndOfBlock );
+      }
+      else {
+        QTextEdit::keyPressEvent( event );
+      }
+      break;
+    }
+  case Qt::Key_Right:
+    // <Right> arrow key: process as follows:
+    // - without <Ctrl>, <Shift> modifiers: move one symbol right (taking into account prompt)
+    // - with <Ctrl> modifier key pressed:  move one word right (taking into account prompt)
+    // - with <Shift> modifier key pressed: move one symbol right with selection
+    // - with <Ctrl>+<Shift> modifier keys pressed: move one word right with selection
+    {
+      QString txt = textCursor().block().text();
+      if ( !shftPressed ) {
+        if ( curCol < txt.length() ) {
+          if ( isCommand( txt ) && curCol < promptSize() ) {
+            cur.setPosition( cur.block().position() + promptSize() );
+            setTextCursor( cur );
+            break;
+          }
+        }
+        else {
+          if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) {
+            cur.setPosition( cur.position() + promptSize()+1 );
+            setTextCursor( cur );
+            horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
+            break;
+          }
+        }
+      }
+      QTextEdit::keyPressEvent( event );
+      break;
+    }
+  case Qt::Key_PageUp:
+    // <PageUp> key: process as follows:
+    // - without <Ctrl>, <Shift> modifiers: first command in history
+    // - with <Ctrl> modifier key pressed:  move cursor one page up without selection
+    // - with <Shift> modifier key pressed: move cursor one page up with selection
+    // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page up
+    {
+      if ( ctrlPressed && shftPressed ) {
+        verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
+      }
+      else if ( shftPressed || ctrlPressed ) {
+        bool moved = false;
+        qreal lastY = cursorRect( cur ).top();
+        qreal distance = 0;
+        // move using movePosition to keep the cursor's x
+        do {
+          qreal y = cursorRect( cur ).top();
+          distance += qAbs( y - lastY );
+          lastY = y;
+          moved = cur.movePosition( QTextCursor::Up, 
+                                    shftPressed ? QTextCursor::KeepAnchor : 
+                                                  QTextCursor::MoveAnchor );
+        } while ( moved && distance < viewport()->height() );
+        if ( moved ) {
+          cur.movePosition( QTextCursor::Down, 
+                            shftPressed ? QTextCursor::KeepAnchor : 
+                                          QTextCursor::MoveAnchor );
+          verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
+        }
+        setTextCursor( cur );
+      }
+      else { 
+        if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
+          // set history browsing mode
+          myCmdInHistory = myHistory.count();
+          // remember current command
+          QTextBlock par = document()->end().previous();
+          myCurrentCommand = par.text().remove( 0, promptSize() );
+        }
+        if ( myCmdInHistory > 0 ) {
+          myCmdInHistory = 0;
+          // get very first command in the history
+          QString firstCommand = myHistory.at( myCmdInHistory );
+          // print first command
+          moveCursor( QTextCursor::End );
+          moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
+          textCursor().removeSelectedText();
+          addText( myPrompt + firstCommand ); 
+          // move cursor to the end
+          moveCursor( QTextCursor::End );
+        }
+      }
+      break;
+    }
+  case Qt::Key_PageDown:
+    // <PageDown> key: process as follows:
+    // - without <Ctrl>, <Shift> modifiers: last command in history
+    // - with <Ctrl> modifier key pressed:  move cursor one page down without selection
+    // - with <Shift> modifier key pressed: move cursor one page down with selection
+    // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page down
+    {
+      if ( ctrlPressed && shftPressed ) {
+        verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
+      }
+      else if ( shftPressed || ctrlPressed ) {
+        bool moved = false;
+        qreal lastY = cursorRect( cur ).top();
+        qreal distance = 0;
+        // move using movePosition to keep the cursor's x
+        do {
+          qreal y = cursorRect( cur ).top();
+          distance += qAbs( y - lastY );
+          lastY = y;
+          moved = cur.movePosition( QTextCursor::Down, 
+                                    shftPressed ? QTextCursor::KeepAnchor : 
+                                                  QTextCursor::MoveAnchor );
+        } while ( moved && distance < viewport()->height() );
+        if ( moved ) {
+          cur.movePosition( QTextCursor::Up, 
+                            shftPressed ? QTextCursor::KeepAnchor : 
+                                          QTextCursor::MoveAnchor );
+          verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
+        }
+        setTextCursor( cur );
+      }
+      else { 
+        if ( myCmdInHistory >= 0 ) {
+          // unset history browsing mode
+          myCmdInHistory = -1;
+          // print current command
+          moveCursor( QTextCursor::End );
+          moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
+          textCursor().removeSelectedText();
+          addText( myPrompt + myCurrentCommand ); 
+          // move cursor to the end
+          moveCursor( QTextCursor::End );
+        }
+      }
+      break;
+    }
+  case Qt::Key_Home: 
+    // <Home> key: process as follows:
+    // - without <Ctrl>, <Shift> modifiers: move cursor to the beginning of the current line without selection
+    // - with <Ctrl> modifier key pressed:  move cursor to the very first symbol without selection
+    // - with <Shift> modifier key pressed: move cursor to the beginning of the current line with selection
+    // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very first symbol with selection
+    {
+      if ( ctrlPressed ) { 
+        moveCursor( QTextCursor::Start, 
+                    shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
+      }
+      else {
+        QString txt = textCursor().block().text();
+        if ( isCommand( txt ) ) {
+          if ( shftPressed ) {
+            if ( curCol > promptSize() ) {
+              cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor );
+              cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() );
+            }
+          }
+          else {
+            cur.movePosition( QTextCursor::StartOfLine );
+            cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() );
+          }
+          setTextCursor( cur );
+        }
+        else {
+          moveCursor( QTextCursor::StartOfBlock, 
+                      shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
+        }
+        horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
+      }
+      break;
+    }
+  case Qt::Key_End:
+    // <End> key: process as follows:
+    // - without <Ctrl>, <Shift> modifiers: move cursor to the end of the current line without selection
+    // - with <Ctrl> modifier key pressed:  move cursor to the very last symbol without selection
+    // - with <Shift> modifier key pressed: move cursor to the end of the current line with selection
+    // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very last symbol with selection
+    {
+      moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock, 
+                  shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
+      break;
+    }  
+  case Qt::Key_Backspace :
+    // <Backspace> key: process as follows
+    // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt)
+    // - with <Shift> modifier key pressed: delete previous word
+    // - with <Ctrl> modifier key pressed: delete text from the cursor to the line beginning
+    // works only for last (command) line
+    {
+      if ( cur.hasSelection() ) {
+        cut();
+      }
+      else if ( cur.position() > document()->end().previous().position() + promptSize() ) {
+        if ( shftPressed ) {
+          moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor );
+          textCursor().removeSelectedText();
+        }
+        else if ( ctrlPressed ) {
+          cur.setPosition( document()->end().previous().position() + promptSize(),
+                           QTextCursor::KeepAnchor );
+          setTextCursor( cur );
+          textCursor().removeSelectedText();
+        }
+        else {
+          QTextEdit::keyPressEvent( event );
+        }
+      }
+      else {
+        cur.setPosition( document()->end().previous().position() + promptSize() );
+        setTextCursor( cur );
+        horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
+      }
+      break;
+    }
+  case Qt::Key_Delete :
+    // <Delete> key: process as follows
+    // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt)
+    // - with <Shift> modifier key pressed: delete next word
+    // - with <Ctrl> modifier key pressed: delete text from the cursor to the end of line
+    // works only for last (command) line
+    {
+      if ( cur.hasSelection() ) {
+        cut();
+      }
+      else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) {
+        if ( shftPressed ) {
+          moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor );
+          textCursor().removeSelectedText();
+        }
+        else if ( ctrlPressed ) {
+          moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
+          textCursor().removeSelectedText();
+        }
+        else {
+          QTextEdit::keyPressEvent( event );
+        }
+      }
+      else {
+        cur.setPosition( document()->end().previous().position() + promptSize() );
+        setTextCursor( cur );
+        horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
+      }
+      break;
+    }
+  case Qt::Key_Insert :
+    // <Insert> key: process as follows
+    // - with <Ctrl> modifier key pressed:  copy()
+    // - with <Shift> modifier key pressed: paste() to the command line
+    {
+      if ( ctrlPressed ) {
+        copy();
+      }
+      else if ( shftPressed ) {
+        paste();
+      }
+      else
+        QTextEdit::keyPressEvent( event );
+      break;
+    }
+  }
+}
+
+/*!
+  \brief Handle notification event coming from Python dispatcher.
+  \param event notification event
+*/
+void PyConsole_Editor::customEvent( QEvent* event )
+{
+  switch( event->type() )
+  {
+  case PrintEvent::EVENT_ID:
+    {
+      PrintEvent* pe=(PrintEvent*)event;
+      addText( pe->text(), false, pe->isError());
+      return;
+    }
+  case PyInterp_Event::ES_OK:
+  case PyInterp_Event::ES_ERROR:
+  {
+    // clear command buffer
+    myCommandBuffer.truncate( 0 );
+    // add caret return line if necessary
+    QTextBlock par = document()->end().previous();
+    QString txt = par.text();
+    txt.truncate( txt.length() - 1 );
+    // IPAL19397 : addText moved to handleReturn() method
+    //if ( !txt.isEmpty() )
+    //  addText( "", true );
+    // set "ready" prompt
+    myPrompt = READY_PROMPT;
+    addText( myPrompt );
+    // unset busy cursor
+    unsetCursor();
+    // stop event loop (if running)
+    if ( myEventLoop )
+      myEventLoop->exit();
+    break;
+  }
+  case PyInterp_Event::ES_INCOMPLETE:
+  {
+    // extend command buffer (multi-line command)
+    myCommandBuffer.append( "\n" );
+    // add caret return line if necessary
+    QTextBlock par = document()->end().previous();
+    QString txt = par.text();
+    txt.truncate( txt.length() - 1 );
+    // IPAL19397 : addText moved to handleReturn() method
+    //if ( !txt.isEmpty() )
+    //  addText( "", true );
+    // set "dot" prompt
+    myPrompt = DOTS_PROMPT;
+    addText( myPrompt/*, true*/ ); // IPAL19397
+    // unset busy cursor
+    unsetCursor();
+    // stop event loop (if running)
+    if ( myEventLoop )
+      myEventLoop->exit();
+    break;
+  }
+  default:
+    QTextEdit::customEvent( event );
+  }
+  
+  // unset read-only state
+  setReadOnly( false );
+  // unset history browsing mode
+  myCmdInHistory = -1;
+
+  if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 )
+  {
+    // process the next sheduled command from the queue (if there is any)
+    QString nextcmd = myQueue[0];
+    myQueue.pop_front();
+    exec( nextcmd );
+  }
+}
+
+/*!
+  \brief Handle Python interpreter change.
+
+  Perform initialization actions if the interpreter is changed.
+  \param interp python interpreter is being set
+*/
+void PyConsole_Editor::onPyInterpChanged( PyConsole_Interp* interp )
+{
+  if ( myInterp != interp 
+       // Force read-only state and wait cursor when myInterp is NULL
+      || !myInterp ) {
+    myInterp = interp;
+    if ( myInterp ) {
+      // print banner
+      myBanner = myInterp->getbanner().c_str();
+      if ( isShowBanner() )
+       addText( myBanner );
+      // clear command buffer
+      myCommandBuffer.truncate(0);
+      // unset read-only state
+      setReadOnly( false );
+      // unset history browsing mode
+      myCmdInHistory = -1;
+      // add prompt
+      addText( myPrompt );
+      // unset busy cursor
+      viewport()->unsetCursor();
+      // stop event loop (if running)
+      if( myEventLoop)
+        myEventLoop->exit();
+    }
+    else {
+      // clear contents
+      clear();
+      // set read-only state
+      setReadOnly( true );
+      // set busy cursor
+      setCursor( Qt::WaitCursor );
+    }
+  }
+}
+
+/*!
+  \brief "Copy" operation.
+  
+  Reimplemented from Qt.
+  Warning! In Qt4 this method is not virtual.
+ */
+void PyConsole_Editor::cut()
+{
+  QTextCursor cur = textCursor();
+  if ( cur.hasSelection() ) {
+    QApplication::clipboard()->setText( cur.selectedText() );
+    int startSelection = cur.selectionStart();
+    if ( startSelection < document()->end().previous().position() + promptSize() )
+      startSelection = document()->end().previous().position() + promptSize();
+    int endSelection = cur.selectionEnd();
+    if ( endSelection < document()->end().previous().position() + promptSize() )
+      endSelection = document()->end().previous().position() + promptSize();
+    cur.setPosition( startSelection );
+    cur.setPosition( endSelection, QTextCursor::KeepAnchor );
+    horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
+    setTextCursor( cur );
+    textCursor().removeSelectedText();
+  }
+}
+
+/*!
+  \brief "Paste" operation.
+
+  Reimplemented from Qt.
+  Warning! In Qt4 this method is not virtual.
+ */
+void PyConsole_Editor::paste()
+{
+  QTextCursor cur = textCursor();
+  if ( cur.hasSelection() ) {
+    int startSelection = cur.selectionStart();
+    if ( startSelection < document()->end().previous().position() + promptSize() )
+      startSelection = document()->end().previous().position() + promptSize();
+    int endSelection = cur.selectionEnd();
+    if ( endSelection < document()->end().previous().position() + promptSize() )
+      endSelection = document()->end().previous().position() + promptSize();
+    cur.setPosition( startSelection );
+    cur.setPosition( endSelection, QTextCursor::KeepAnchor );
+    horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
+    setTextCursor( cur );
+    textCursor().removeSelectedText();
+  }
+  if ( textCursor().position() < document()->end().previous().position() + promptSize() )
+    moveCursor( QTextCursor::End );
+  QTextEdit::paste();
+}
+
+/*!
+  \brief "Clear" operation.
+
+  Reimplemented from Qt.
+  Warning! In Qt4 this method is not virtual.
+ */
+void PyConsole_Editor::clear()
+{
+  QTextEdit::clear();
+  if ( isShowBanner() )
+    addText( myBanner );
+  myPrompt = READY_PROMPT;
+  addText( myPrompt );
+}
+
+/*!
+  \brief "Dump commands" operation.
+ */
+/*void PyConsole_Editor::dump()
+{
+  QStringList aFilters;
+  aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
+  
+  QString fileName = SUIT_FileDlg::getFileName( this, QString(),
+                                    aFilters, tr( "TOT_DUMP_PYCOMMANDS" ),
+                                    false, true, new DumpCommandsFileValidator( this ) );
+  if ( fileName != "" ) {
+    QFile file( fileName ); 
+    if ( !file.open( QFile::WriteOnly ) )
+      return;
+
+    QTextStream out (&file);
+  
+    for( int i=0; i<myHistory.count(); i++ ) {
+         out<<myHistory[i]<<endl;
+    }
+    file.close();
+  }
+}*/
diff --git a/src/PyConsole/PyConsole_Editor.cxx b/src/PyConsole/PyConsole_Editor.cxx
deleted file mode 100644 (file)
index 62ef9e8..0000000
+++ /dev/null
@@ -1,1105 +0,0 @@
-
-/*!
-  \class PyConsole_Editor
-  \brief Python command line interpreter front-end GUI widget.
-  
-  This class provides simple GUI interface to the Python interpreter, including basic 
-  navigation operations, executing commands (both interactively and programmatically), 
-  copy-paste operations, history of the commands and so on.
-
-  Here below is the shortcut keyboard boundings used for navigation and other operations:
-  - <Enter>              : execute current command
-  - <Ctrl><Break>        : clear current command
-  - <Escape>             : clear current command
-  - <Up>                 : previous command in the history
-  - <Shift><Up>          : move cursor one row up with selection
-  - <Ctrl><Up>           : move cursor one row up without selection
-  - <Ctrl><Shift><Up>    : move cursor one row up with selection
-  - <Down>               : next command in the history
-  - <Shift><Down>        : move cursor one row down with selection
-  - <Ctrl><Down>         : move cursor one row down without selection
-  - <Ctrl><Shift><Down>  : move cursor one row down with selection
-  - <Left>               : move one symbol left without selection
-  - <Shift><Left>        : move one symbol left with selection
-  - <Ctrl><Left>         : move one word left without selection
-  - <Ctrl><Shift><Left>  : move one word left with selection
-  - <Right>              : move one symbol right without selection
-  - <Shift><Right>       : move one symbol right with selection
-  - <Ctrl><Right>        : move one word right without selection
-  - <Ctrl><Shift><Right> : move one word right with selection
-  - <PgUp>               : first command in the history
-  - <Shift><PgUp>        : move one page up with selection
-  - <Ctrl><PgUp>         : move one page up without selection
-  - <Ctrl><Shift><PgUp>  : scroll one page up
-  - <PgDn>               : last command in the history
-  - <Shift><PgDn>        : move one page down with selection
-  - <Ctrl><PgDn>         : move one page down without selection
-  - <Ctrl><Shift><PgDn>  : scroll one page down
-  - <Home>               : move to the beginning of the line without selection
-  - <Shift><Home>        : move to the beginning of the line with selection
-  - <Ctrl><Home>         : move to the very first symbol without selection
-  - <Ctrl><Shift><Home>  : move to the very first symbol with selection
-  - <End>                : move to the end of the line without selection
-  - <Shift><End>         : move to the end of the line with selection
-  - <Ctrl><End>          : move to the very last symbol without selection
-  - <Ctrl><Shift><End>   : move to the very last symbol with selection
-  - <Backspace>          : delete symbol before the cursor
-                           / remove selected text and put it to the clipboard (cut)
-  - <Shift><Backspace>   : delete previous word
-                           / remove selected text and put it to the clipboard (cut)
-  - <Ctrl><Backspace>    : delete text from the cursor to the beginning of the line 
-                           / remove selected text and put it to the clipboard (cut)
-  - <Delete>             : delete symbol after the cursor 
-                           / remove selected text and put it to the clipboard (cut)
-  - <Shift><Delete>      : delete next word
-                           / remove selected text and put it to the clipboard (cut)
-  - <Ctrl><Delete>       : delete text from the cursor to the end of the line
-                           / remove selected text and put it to the clipboard (cut)
-  - <Ctrl><Insert>       : copy
-  - <Shift><Insert>      : paste
-  - <Ctrl><V>            : paste
-  - <Ctrl><C>            : copy
-  - <Ctrl><X>            : cut
-  - <Ctrl><V>            : paste
-*/
-
-#include "PyConsole_Interp.h"   // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
-#include "PyConsole_Editor.h"
-#include "PyConsole_Event.h"
-#include "PyInterp_Event.h"
-#include "PyInterp_Dispatcher.h"
-#include "PyConsole_Request.h"
-
-//#include <SUIT_Tools.h>
-//#include <SUIT_FileDlg.h>
-//#include <SUIT_MessageBox.h>
-//#include <SUIT_FileValidator.h>
-
-#include <QApplication>
-#include <QClipboard>
-#include <QDropEvent>
-#include <QEvent>
-#include <QKeyEvent>
-#include <QMouseEvent>
-#include <QScrollBar>
-#include <QTextBlock>
-#include <QTextCursor>
-#include <QTextDocument>
-#include <QTextStream>
-#include <QChar>
-
-static QString READY_PROMPT = ">>> ";
-static QString DOTS_PROMPT  = "... ";
-
-/*class DumpCommandsFileValidator : public SUIT_FileValidator
-{
- public:
-  DumpCommandsFileValidator( QWidget* parent = 0 ) : SUIT_FileValidator ( parent ) {};
-  virtual ~DumpCommandsFileValidator() {};
-  virtual bool canSave( const QString& file, bool permissions );
-};
-
-bool DumpCommandsFileValidator::canSave(const QString& file, bool permissions)
-{
-  QFileInfo fi( file );
-  if ( !QRegExp( "[A-Za-z_][A-Za-z0-9_]*" ).exactMatch( fi.completeBaseName() ) ) {
-    SUIT_MessageBox::critical( parent(),
-                               QObject::tr("WRN_WARNING"),
-                               QObject::tr("WRN_FILE_NAME_BAD") );
-    return false;
-  }
-  return SUIT_FileValidator::canSave( file, permissions);
-}
-*/
-void staticCallbackStdout( void* data, char* c )
-{
-  if(!((PyConsole_Editor*)data)->isSuppressOutput())
-    QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c, false ) );
-}
-
-void staticCallbackStderr( void* data, char* c )
-{
-  if(!((PyConsole_Editor*)data)->isSuppressOutput())
-    QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c, true ) );
-}
-
-
-/*!
-  \brief Constructor. 
-  
-  Creates python editor window.
-  \param theInterp python interper
-  \param theParent parent widget
-*/
-PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp, 
-                                    QWidget*          theParent )
-: QTextEdit( theParent ),
-  myInterp( 0 ),
-  myCmdInHistory( -1 ),
-  myEventLoop( 0 ),
-  myShowBanner( true ),
-  myIsSync( false ),
-  myIsSuppressOutput( false )
-{
-  //QString fntSet( "" );
-  QFont aFont = QFont( "Courier", 11 );//SUIT_Tools::stringToFont( fntSet );
-  setFont( aFont );
-  setUndoRedoEnabled( false );
-
-  myPrompt = READY_PROMPT;
-  setLineWrapMode( QTextEdit::WidgetWidth );
-  setWordWrapMode( QTextOption::WrapAnywhere );
-  setAcceptRichText( false );
-
-  theInterp->setvoutcb( staticCallbackStdout, this );
-  theInterp->setverrcb( staticCallbackStderr, this );
-
-  // san - This is necessary for troubleless initialization
-  onPyInterpChanged( theInterp );
-}
-
-/*!
-  \brief Destructor.
-
-  Does nothing for the moment.
-*/
-PyConsole_Editor::~PyConsole_Editor()
-{
-}
-
-/*!
-  \brief Get synchronous mode flag value.
-  
-  \sa setIsSync()
-  \return True if python console works in synchronous mode
-*/
-bool PyConsole_Editor::isSync() const
-{
-  return myIsSync;
-}
-
-/*!
-  \brief Set synchronous mode flag value.
-
-  In synhronous mode the Python commands are executed in the GUI thread
-  and the GUI is blocked until the command is finished. In the asynchronous
-  mode each Python command is executed in the separate thread that does not
-  block the main GUI loop.
-
-  \param on synhronous mode flag
-*/
-void PyConsole_Editor::setIsSync( const bool on )
-{
-  myIsSync = on;
-}
-
-/*!
-  \brief Get suppress output flag value.
-  
-  \sa setIsSuppressOutput()
-  \return \c true if python console output is suppressed.
-*/
-bool PyConsole_Editor::isSuppressOutput() const
-{
-  return myIsSuppressOutput;
-}
-
-/*!
-  \brief Set suppress output flag value.
-
-  In case if suppress output flag is true, the python 
-  console output suppressed.
-
-  \param on suppress output flag
-*/
-void PyConsole_Editor::setIsSuppressOutput( const bool on )
-{
-  myIsSuppressOutput = on;
-}
-
-/*!
-  \brief Get 'show banner' flag value.
-  
-  \sa setIsShowBanner()
-  \return \c true if python console shows banner
-*/
-bool PyConsole_Editor::isShowBanner() const
-{
-  return myShowBanner;
-}
-
-/*!
-  \brief Set 'show banner' flag value.
-
-  The banner is shown in the top of the python console window.
-
-  \sa isShowBanner()
-  \param on 'show banner' flag
-*/
-void PyConsole_Editor::setIsShowBanner( const bool on )
-{
-  if ( myShowBanner != on ) {
-    myShowBanner = on;
-    clear();
-  }
-}
-
-/*!
-  \brief Get size hint for the Python console window
-  \return size hint value
-*/
-QSize PyConsole_Editor::sizeHint() const
-{
-  QFontMetrics fm( font() );
-  int nbLines = ( isShowBanner() ? myBanner.split("\n").count() : 0 ) + 1;
-  QSize s(100, fm.lineSpacing()*nbLines);
-  return s;
-}
-
-/*!
-  \brief Put the string \a str to the python editor.
-  \param str string to be put in the command line of the editor
-  \param newBlock if True, then the string is printed on a new line
-  \param isError if true, the text is printed in dark red
-*/
-void PyConsole_Editor::addText( const QString& str, 
-                                const bool     newBlock,
-                                const bool    isError)
-{
-  QTextCursor theCursor(textCursor());
-  QTextCharFormat cf;
-
-  moveCursor( QTextCursor::End );
-  if ( newBlock )
-    theCursor.insertBlock();
-  if (isError)
-      cf.setForeground(QBrush(Qt::red));
-  else
-      cf.setForeground(QBrush(Qt::black));
-  theCursor.insertText( str, cf);
-  moveCursor( QTextCursor::End );
-  ensureCursorVisible();
-}
-
-/*!
-  \brief Convenient method for executing a Python command,
-  as if the user typed it manually.
-  \param command python command to be executed
-
-  !!! WARNING: doesn't work properly with multi-line commands. !!!
-*/
-void PyConsole_Editor::exec( const QString& command )
-{
-  if ( isReadOnly() ) {
-    // some interactive command is being executed in this editor...
-    // shedule the command to the queue
-    myQueue.push_back( command );
-    return;
-  }
-  // remove last line
-  moveCursor( QTextCursor::End );
-  moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
-  textCursor().removeSelectedText();
-  // set "ready" prompt
-  myPrompt = READY_PROMPT;
-  // clear command buffer
-  myCommandBuffer.truncate( 0 );
-  // unset history browsing mode
-  myCmdInHistory = -1;
-  // print command line by line
-  QString cmd = command;
-  if ( !cmd.endsWith( "\n" ) ) cmd += "\n";
-  QStringList lines = command.split( "\n" );
-  for ( int i = 0; i < lines.size(); i++ ) {
-    if ( !lines[i].trimmed().isEmpty() )
-      myHistory.push_back( lines[i] );
-    addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 );
-  }
-  // IPAL20182
-  addText( "", true );
-  // set read-only mode
-  setReadOnly( true );
-  // set busy cursor
-  setCursor( Qt::BusyCursor );
-  
-  // post a request to execute Python command;
-  // editor will be informed via a custom event that execution has been completed
-  PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) );
-}
-
-/*!
-  \brief Create request to the python dispatcher for the command execution.
-
-  \param command python command to be executed
- */
-PyInterp_Request* PyConsole_Editor::createRequest( const QString& command )
-{
-  return new ExecCommand( myInterp, command, this, isSync() );
-}
-
-/*!
-  \brief Execute command in the python interpreter
-  and wait until it is finished.
-
-  \param command python command to be executed
- */
-void PyConsole_Editor::execAndWait( const QString& command )
-{
-  // already running ?
-  if( myEventLoop )
-    return;
-
-  // create new event loop
-  myEventLoop = new QEventLoop( this );
-  // execute command
-  exec( command );
-  // run event loop
-  myEventLoop->exec();
-  // delete event loop after command is processed
-  delete myEventLoop;
-  myEventLoop = 0;
-}
-
-/*!
-  \brief Process "Enter" key press event. 
-  
-  Execute the command entered by the user.
-*/
-void PyConsole_Editor::handleReturn()
-{
-  // Position cursor at the end
-  QTextCursor curs(textCursor());
-  curs.movePosition(QTextCursor::End);
-  setTextCursor(curs);
-
-  // get last line
-  QTextBlock par = document()->end().previous();
-  if ( !par.isValid() ) return;
-
-  // get command
-  QString cmd = par.text().remove( 0, promptSize() );
-  // extend the command buffer with the current command 
-  myCommandBuffer.append( cmd );
-  // add command to the history
-  if ( !cmd.trimmed().isEmpty() )
-    myHistory.push_back( cmd );
-
-  // IPAL19397
-  addText( "", true ); 
-  
-  // set read-only mode
-  setReadOnly( true );
-  // set busy cursor
-  setCursor( Qt::BusyCursor );
-  
-  // post a request to execute Python command;
-  // editor will be informed via a custom event that execution has been completed
-  PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) );
-}
-
-/*!
-  \brief Process drop event.
-
-  Paste dragged text.
-  \param event drop event
-*/
-void PyConsole_Editor::dropEvent( QDropEvent* event )
-{
-  // get the initial drop position
-  QPoint pos = event->pos();
-  QTextCursor cur = cursorForPosition( event->pos() );
-  // if the position is not in the last line move it to the end of the command line
-  if ( cur.position() < document()->end().previous().position() + promptSize() ) {
-    moveCursor( QTextCursor::End );
-    pos = cursorRect().center();
-  }
-  // create new drop event and use it instead of the original
-  QDropEvent de( pos,
-                 event->possibleActions(),
-                 event->mimeData(),
-                 event->mouseButtons(),
-                 event->keyboardModifiers(),
-                 event->type() );
-  QTextEdit::dropEvent( &de );
-  // accept the original event
-  event->acceptProposedAction();
-}
-
-/*!
-  \brief Process mouse button release event.
-
-  Left mouse button: copy selection to the clipboard.
-  Middle mouse button: paste clipboard's contents.
-  \param event mouse event
-*/
-void PyConsole_Editor::mouseReleaseEvent( QMouseEvent* event )
-{
-  if ( event->button() == Qt::LeftButton ) {
-    QTextEdit::mouseReleaseEvent( event );
-    //copy();
-  }
-  else if ( event->button() == Qt::MidButton ) {
-    QTextCursor cur = cursorForPosition( event->pos() );
-    // if the position is not in the last line move it to the end of the command line
-    if ( cur.position() < document()->end().previous().position() + promptSize() ) {
-      moveCursor( QTextCursor::End );
-    }
-    else {
-      setTextCursor( cur );
-    }
-    const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ? 
-                                                              QClipboard::Selection : QClipboard::Clipboard );
-    if ( md )
-      insertFromMimeData( md );
-  }
-  else {
-    QTextEdit::mouseReleaseEvent( event );
-  }
-}
-
-/*!
-  \brief Check if the string is command.
-  
-  Return True if the string \a str is likely to be the command
-  (i.e. it is started from the '>>>' or '...').
-  \param str string to be checked
-*/
-bool PyConsole_Editor::isCommand( const QString& str ) const
-{
-  return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT );
-}
-
-/*!
-  \brief Handle keyboard event.
-
-  Implement navigation, history browsing, copy/paste and other common
-  operations.
-  \param event keyboard event
-*/
-void PyConsole_Editor::keyPressEvent( QKeyEvent* event )
-{
-  // get cursor position
-  QTextCursor cur = textCursor();
-  int curLine = cur.blockNumber();
-  int curCol  = cur.columnNumber();
-
-  // get last edited line
-  int endLine = document()->blockCount()-1;
-
-  // get pressed key code
-  int aKey = event->key();
-
-  // check if <Ctrl> is pressed
-  bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
-  // check if <Shift> is pressed
-  bool shftPressed = event->modifiers() & Qt::ShiftModifier;
-
-  if ( aKey == Qt::Key_Escape || ( ctrlPressed && aKey == -1 ) ) {
-    // process <Ctrl>+<Break> key-binding and <Escape> key: clear current command
-    myCommandBuffer.truncate( 0 );
-    myPrompt = READY_PROMPT;
-    addText( myPrompt, true );
-    horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
-    return;
-  }
-  else if ( ctrlPressed && aKey == Qt::Key_C ) {
-    // process <Ctrl>+<C> key-binding : copy
-    copy();
-    return;
-  }
-  else if ( ctrlPressed && aKey == Qt::Key_X ) {
-    // process <Ctrl>+<X> key-binding : cut
-    cut();
-    return;
-  }
-  else if ( ctrlPressed && aKey == Qt::Key_V ) {
-    // process <Ctrl>+<V> key-binding : paste
-    paste();
-    return;
-  }
-
-  // check for printed key
-  // #### aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0;
-  // Better:
-  aKey = !(QChar(aKey).isPrint()) ? aKey : 0;
-
-  switch ( aKey ) {
-  case 0 :
-    // any printed key: just print it
-    {
-      if ( curLine < endLine || curCol < promptSize() ) {
-        moveCursor( QTextCursor::End );
-      }
-      QTextEdit::keyPressEvent( event );
-      break;
-    }
-  case Qt::Key_Return:
-  case Qt::Key_Enter:
-    // <Enter> key: process the current command
-    {
-      handleReturn();
-      break;
-    }
-  case Qt::Key_Up:
-    // <Up> arrow key: process as follows:
-    // - without <Ctrl>, <Shift> modifiers: previous command in history
-    // - with <Ctrl> modifier key pressed:  move cursor one row up without selection
-    // - with <Shift> modifier key pressed: move cursor one row up with selection
-    // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row up
-    {
-      if ( ctrlPressed && shftPressed ) {
-        int value   = verticalScrollBar()->value();
-        int spacing = fontMetrics().lineSpacing();
-        verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 );
-      }
-      else if ( shftPressed || ctrlPressed ) {
-        if ( curLine > 0 )
-          moveCursor( QTextCursor::Up, 
-                      shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
-      }
-      else { 
-        if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
-          // set history browsing mode
-          myCmdInHistory = myHistory.count();
-          // remember current command
-          QTextBlock par = document()->end().previous();
-          myCurrentCommand = par.text().remove( 0, promptSize() );
-        }
-        if ( myCmdInHistory > 0 ) {
-          myCmdInHistory--;
-          // get previous command in the history
-          QString previousCommand = myHistory.at( myCmdInHistory );
-          // print previous command
-          moveCursor( QTextCursor::End );
-          moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
-          textCursor().removeSelectedText();
-          addText( myPrompt + previousCommand ); 
-          // move cursor to the end
-          moveCursor( QTextCursor::End );
-        }
-      }
-      break;
-    }
-  case Qt::Key_Down:
-    // <Down> arrow key: process as follows:
-    // - without <Ctrl>, <Shift> modifiers: next command in history
-    // - with <Ctrl> modifier key pressed:  move cursor one row down without selection
-    // - with <Shift> modifier key pressed: move cursor one row down with selection
-    // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row down
-    {
-      if ( ctrlPressed && shftPressed ) {
-        int value   = verticalScrollBar()->value();
-        int maxval  = verticalScrollBar()->maximum();
-        int spacing = fontMetrics().lineSpacing();
-        verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval );
-      }
-      else if ( shftPressed || ctrlPressed) {
-        if ( curLine < endLine )
-          moveCursor( QTextCursor::Down, 
-                      shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
-      }
-      else { 
-        if ( myCmdInHistory >= 0 ) {
-          // get next command in the history
-          myCmdInHistory++;
-          QString nextCommand;
-          if ( myCmdInHistory < myHistory.count() ) {
-            // next command in history
-            nextCommand = myHistory.at( myCmdInHistory );
-          }
-          else {
-            // end of history is reached
-            // last printed command
-            nextCommand = myCurrentCommand;
-            // unset history browsing mode
-            myCmdInHistory = -1;
-          }
-          // print next or current command
-          moveCursor( QTextCursor::End );
-          moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
-          textCursor().removeSelectedText();
-          addText( myPrompt + nextCommand );
-          // move cursor to the end
-          moveCursor( QTextCursor::End );
-        }
-      }
-      break;
-    }
-  case Qt::Key_Left:
-    // <Left> arrow key: process as follows:
-    // - without <Ctrl>, <Shift> modifiers: move one symbol left (taking into account prompt)
-    // - with <Ctrl> modifier key pressed:  move one word left (taking into account prompt)
-    // - with <Shift> modifier key pressed: move one symbol left with selection
-    // - with <Ctrl>+<Shift> modifier keys pressed: move one word left with selection
-    {
-      QString txt = textCursor().block().text();
-      if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) {
-        moveCursor( QTextCursor::Up );
-        moveCursor( QTextCursor::EndOfBlock );
-      }
-      else {
-        QTextEdit::keyPressEvent( event );
-      }
-      break;
-    }
-  case Qt::Key_Right:
-    // <Right> arrow key: process as follows:
-    // - without <Ctrl>, <Shift> modifiers: move one symbol right (taking into account prompt)
-    // - with <Ctrl> modifier key pressed:  move one word right (taking into account prompt)
-    // - with <Shift> modifier key pressed: move one symbol right with selection
-    // - with <Ctrl>+<Shift> modifier keys pressed: move one word right with selection
-    {
-      QString txt = textCursor().block().text();
-      if ( !shftPressed ) {
-        if ( curCol < txt.length() ) {
-          if ( isCommand( txt ) && curCol < promptSize() ) {
-            cur.setPosition( cur.block().position() + promptSize() );
-            setTextCursor( cur );
-            break;
-          }
-        }
-        else {
-          if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) {
-            cur.setPosition( cur.position() + promptSize()+1 );
-            setTextCursor( cur );
-            horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
-            break;
-          }
-        }
-      }
-      QTextEdit::keyPressEvent( event );
-      break;
-    }
-  case Qt::Key_PageUp:
-    // <PageUp> key: process as follows:
-    // - without <Ctrl>, <Shift> modifiers: first command in history
-    // - with <Ctrl> modifier key pressed:  move cursor one page up without selection
-    // - with <Shift> modifier key pressed: move cursor one page up with selection
-    // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page up
-    {
-      if ( ctrlPressed && shftPressed ) {
-        verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
-      }
-      else if ( shftPressed || ctrlPressed ) {
-        bool moved = false;
-        qreal lastY = cursorRect( cur ).top();
-        qreal distance = 0;
-        // move using movePosition to keep the cursor's x
-        do {
-          qreal y = cursorRect( cur ).top();
-          distance += qAbs( y - lastY );
-          lastY = y;
-          moved = cur.movePosition( QTextCursor::Up, 
-                                    shftPressed ? QTextCursor::KeepAnchor : 
-                                                  QTextCursor::MoveAnchor );
-        } while ( moved && distance < viewport()->height() );
-        if ( moved ) {
-          cur.movePosition( QTextCursor::Down, 
-                            shftPressed ? QTextCursor::KeepAnchor : 
-                                          QTextCursor::MoveAnchor );
-          verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
-        }
-        setTextCursor( cur );
-      }
-      else { 
-        if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
-          // set history browsing mode
-          myCmdInHistory = myHistory.count();
-          // remember current command
-          QTextBlock par = document()->end().previous();
-          myCurrentCommand = par.text().remove( 0, promptSize() );
-        }
-        if ( myCmdInHistory > 0 ) {
-          myCmdInHistory = 0;
-          // get very first command in the history
-          QString firstCommand = myHistory.at( myCmdInHistory );
-          // print first command
-          moveCursor( QTextCursor::End );
-          moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
-          textCursor().removeSelectedText();
-          addText( myPrompt + firstCommand ); 
-          // move cursor to the end
-          moveCursor( QTextCursor::End );
-        }
-      }
-      break;
-    }
-  case Qt::Key_PageDown:
-    // <PageDown> key: process as follows:
-    // - without <Ctrl>, <Shift> modifiers: last command in history
-    // - with <Ctrl> modifier key pressed:  move cursor one page down without selection
-    // - with <Shift> modifier key pressed: move cursor one page down with selection
-    // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page down
-    {
-      if ( ctrlPressed && shftPressed ) {
-        verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
-      }
-      else if ( shftPressed || ctrlPressed ) {
-        bool moved = false;
-        qreal lastY = cursorRect( cur ).top();
-        qreal distance = 0;
-        // move using movePosition to keep the cursor's x
-        do {
-          qreal y = cursorRect( cur ).top();
-          distance += qAbs( y - lastY );
-          lastY = y;
-          moved = cur.movePosition( QTextCursor::Down, 
-                                    shftPressed ? QTextCursor::KeepAnchor : 
-                                                  QTextCursor::MoveAnchor );
-        } while ( moved && distance < viewport()->height() );
-        if ( moved ) {
-          cur.movePosition( QTextCursor::Up, 
-                            shftPressed ? QTextCursor::KeepAnchor : 
-                                          QTextCursor::MoveAnchor );
-          verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
-        }
-        setTextCursor( cur );
-      }
-      else { 
-        if ( myCmdInHistory >= 0 ) {
-          // unset history browsing mode
-          myCmdInHistory = -1;
-          // print current command
-          moveCursor( QTextCursor::End );
-          moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
-          textCursor().removeSelectedText();
-          addText( myPrompt + myCurrentCommand ); 
-          // move cursor to the end
-          moveCursor( QTextCursor::End );
-        }
-      }
-      break;
-    }
-  case Qt::Key_Home: 
-    // <Home> key: process as follows:
-    // - without <Ctrl>, <Shift> modifiers: move cursor to the beginning of the current line without selection
-    // - with <Ctrl> modifier key pressed:  move cursor to the very first symbol without selection
-    // - with <Shift> modifier key pressed: move cursor to the beginning of the current line with selection
-    // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very first symbol with selection
-    {
-      if ( ctrlPressed ) { 
-        moveCursor( QTextCursor::Start, 
-                    shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
-      }
-      else {
-        QString txt = textCursor().block().text();
-        if ( isCommand( txt ) ) {
-          if ( shftPressed ) {
-            if ( curCol > promptSize() ) {
-              cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor );
-              cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() );
-            }
-          }
-          else {
-            cur.movePosition( QTextCursor::StartOfLine );
-            cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() );
-          }
-          setTextCursor( cur );
-        }
-        else {
-          moveCursor( QTextCursor::StartOfBlock, 
-                      shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
-        }
-        horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
-      }
-      break;
-    }
-  case Qt::Key_End:
-    // <End> key: process as follows:
-    // - without <Ctrl>, <Shift> modifiers: move cursor to the end of the current line without selection
-    // - with <Ctrl> modifier key pressed:  move cursor to the very last symbol without selection
-    // - with <Shift> modifier key pressed: move cursor to the end of the current line with selection
-    // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very last symbol with selection
-    {
-      moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock, 
-                  shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
-      break;
-    }  
-  case Qt::Key_Backspace :
-    // <Backspace> key: process as follows
-    // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt)
-    // - with <Shift> modifier key pressed: delete previous word
-    // - with <Ctrl> modifier key pressed: delete text from the cursor to the line beginning
-    // works only for last (command) line
-    {
-      if ( cur.hasSelection() ) {
-        cut();
-      }
-      else if ( cur.position() > document()->end().previous().position() + promptSize() ) {
-        if ( shftPressed ) {
-          moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor );
-          textCursor().removeSelectedText();
-        }
-        else if ( ctrlPressed ) {
-          cur.setPosition( document()->end().previous().position() + promptSize(),
-                           QTextCursor::KeepAnchor );
-          setTextCursor( cur );
-          textCursor().removeSelectedText();
-        }
-        else {
-          QTextEdit::keyPressEvent( event );
-        }
-      }
-      else {
-        cur.setPosition( document()->end().previous().position() + promptSize() );
-        setTextCursor( cur );
-        horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
-      }
-      break;
-    }
-  case Qt::Key_Delete :
-    // <Delete> key: process as follows
-    // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt)
-    // - with <Shift> modifier key pressed: delete next word
-    // - with <Ctrl> modifier key pressed: delete text from the cursor to the end of line
-    // works only for last (command) line
-    {
-      if ( cur.hasSelection() ) {
-        cut();
-      }
-      else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) {
-        if ( shftPressed ) {
-          moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor );
-          textCursor().removeSelectedText();
-        }
-        else if ( ctrlPressed ) {
-          moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
-          textCursor().removeSelectedText();
-        }
-        else {
-          QTextEdit::keyPressEvent( event );
-        }
-      }
-      else {
-        cur.setPosition( document()->end().previous().position() + promptSize() );
-        setTextCursor( cur );
-        horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
-      }
-      break;
-    }
-  case Qt::Key_Insert :
-    // <Insert> key: process as follows
-    // - with <Ctrl> modifier key pressed:  copy()
-    // - with <Shift> modifier key pressed: paste() to the command line
-    {
-      if ( ctrlPressed ) {
-        copy();
-      }
-      else if ( shftPressed ) {
-        paste();
-      }
-      else
-        QTextEdit::keyPressEvent( event );
-      break;
-    }
-  }
-}
-
-/*!
-  \brief Handle notification event coming from Python dispatcher.
-  \param event notification event
-*/
-void PyConsole_Editor::customEvent( QEvent* event )
-{
-  switch( event->type() )
-  {
-  case PrintEvent::EVENT_ID:
-    {
-      PrintEvent* pe=(PrintEvent*)event;
-      addText( pe->text(), false, pe->isError());
-      return;
-    }
-  case PyInterp_Event::ES_OK:
-  case PyInterp_Event::ES_ERROR:
-  {
-    // clear command buffer
-    myCommandBuffer.truncate( 0 );
-    // add caret return line if necessary
-    QTextBlock par = document()->end().previous();
-    QString txt = par.text();
-    txt.truncate( txt.length() - 1 );
-    // IPAL19397 : addText moved to handleReturn() method
-    //if ( !txt.isEmpty() )
-    //  addText( "", true );
-    // set "ready" prompt
-    myPrompt = READY_PROMPT;
-    addText( myPrompt );
-    // unset busy cursor
-    unsetCursor();
-    // stop event loop (if running)
-    if ( myEventLoop )
-      myEventLoop->exit();
-    break;
-  }
-  case PyInterp_Event::ES_INCOMPLETE:
-  {
-    // extend command buffer (multi-line command)
-    myCommandBuffer.append( "\n" );
-    // add caret return line if necessary
-    QTextBlock par = document()->end().previous();
-    QString txt = par.text();
-    txt.truncate( txt.length() - 1 );
-    // IPAL19397 : addText moved to handleReturn() method
-    //if ( !txt.isEmpty() )
-    //  addText( "", true );
-    // set "dot" prompt
-    myPrompt = DOTS_PROMPT;
-    addText( myPrompt/*, true*/ ); // IPAL19397
-    // unset busy cursor
-    unsetCursor();
-    // stop event loop (if running)
-    if ( myEventLoop )
-      myEventLoop->exit();
-    break;
-  }
-  default:
-    QTextEdit::customEvent( event );
-  }
-  
-  // unset read-only state
-  setReadOnly( false );
-  // unset history browsing mode
-  myCmdInHistory = -1;
-
-  if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 )
-  {
-    // process the next sheduled command from the queue (if there is any)
-    QString nextcmd = myQueue[0];
-    myQueue.pop_front();
-    exec( nextcmd );
-  }
-}
-
-/*!
-  \brief Handle Python interpreter change.
-
-  Perform initialization actions if the interpreter is changed.
-  \param interp python interpreter is being set
-*/
-void PyConsole_Editor::onPyInterpChanged( PyConsole_Interp* interp )
-{
-  if ( myInterp != interp 
-       // Force read-only state and wait cursor when myInterp is NULL
-      || !myInterp ) {
-    myInterp = interp;
-    if ( myInterp ) {
-      // print banner
-      myBanner = myInterp->getbanner().c_str();
-      if ( isShowBanner() )
-       addText( myBanner );
-      // clear command buffer
-      myCommandBuffer.truncate(0);
-      // unset read-only state
-      setReadOnly( false );
-      // unset history browsing mode
-      myCmdInHistory = -1;
-      // add prompt
-      addText( myPrompt );
-      // unset busy cursor
-      viewport()->unsetCursor();
-      // stop event loop (if running)
-      if( myEventLoop)
-        myEventLoop->exit();
-    }
-    else {
-      // clear contents
-      clear();
-      // set read-only state
-      setReadOnly( true );
-      // set busy cursor
-      setCursor( Qt::WaitCursor );
-    }
-  }
-}
-
-/*!
-  \brief "Copy" operation.
-  
-  Reimplemented from Qt.
-  Warning! In Qt4 this method is not virtual.
- */
-void PyConsole_Editor::cut()
-{
-  QTextCursor cur = textCursor();
-  if ( cur.hasSelection() ) {
-    QApplication::clipboard()->setText( cur.selectedText() );
-    int startSelection = cur.selectionStart();
-    if ( startSelection < document()->end().previous().position() + promptSize() )
-      startSelection = document()->end().previous().position() + promptSize();
-    int endSelection = cur.selectionEnd();
-    if ( endSelection < document()->end().previous().position() + promptSize() )
-      endSelection = document()->end().previous().position() + promptSize();
-    cur.setPosition( startSelection );
-    cur.setPosition( endSelection, QTextCursor::KeepAnchor );
-    horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
-    setTextCursor( cur );
-    textCursor().removeSelectedText();
-  }
-}
-
-/*!
-  \brief "Paste" operation.
-
-  Reimplemented from Qt.
-  Warning! In Qt4 this method is not virtual.
- */
-void PyConsole_Editor::paste()
-{
-  QTextCursor cur = textCursor();
-  if ( cur.hasSelection() ) {
-    int startSelection = cur.selectionStart();
-    if ( startSelection < document()->end().previous().position() + promptSize() )
-      startSelection = document()->end().previous().position() + promptSize();
-    int endSelection = cur.selectionEnd();
-    if ( endSelection < document()->end().previous().position() + promptSize() )
-      endSelection = document()->end().previous().position() + promptSize();
-    cur.setPosition( startSelection );
-    cur.setPosition( endSelection, QTextCursor::KeepAnchor );
-    horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
-    setTextCursor( cur );
-    textCursor().removeSelectedText();
-  }
-  if ( textCursor().position() < document()->end().previous().position() + promptSize() )
-    moveCursor( QTextCursor::End );
-  QTextEdit::paste();
-}
-
-/*!
-  \brief "Clear" operation.
-
-  Reimplemented from Qt.
-  Warning! In Qt4 this method is not virtual.
- */
-void PyConsole_Editor::clear()
-{
-  QTextEdit::clear();
-  if ( isShowBanner() )
-    addText( myBanner );
-  myPrompt = READY_PROMPT;
-  addText( myPrompt );
-}
-
-/*!
-  \brief "Dump commands" operation.
- */
-/*void PyConsole_Editor::dump()
-{
-  QStringList aFilters;
-  aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
-  
-  QString fileName = SUIT_FileDlg::getFileName( this, QString(),
-                                    aFilters, tr( "TOT_DUMP_PYCOMMANDS" ),
-                                    false, true, new DumpCommandsFileValidator( this ) );
-  if ( fileName != "" ) {
-    QFile file( fileName ); 
-    if ( !file.open( QFile::WriteOnly ) )
-      return;
-
-    QTextStream out (&file);
-  
-    for( int i=0; i<myHistory.count(); i++ ) {
-         out<<myHistory[i]<<endl;
-    }
-    file.close();
-  }
-}*/
diff --git a/src/PyConsole/PyConsole_EnhEditor.cpp b/src/PyConsole/PyConsole_EnhEditor.cpp
new file mode 100644 (file)
index 0000000..d5c47df
--- /dev/null
@@ -0,0 +1,464 @@
+
+
+#include "PyConsole.h"
+#include <Python.h>
+
+#include <QKeyEvent>
+#include <QTextBlock>
+#include <QTextCursor>
+#include <QTextCharFormat>
+#include <QRegExp>
+#include <QMimeData>
+
+#include "PyConsole_EnhEditor.h"
+#include "PyConsole_EnhInterp.h"
+#include "PyConsole_Request.h"
+#include "PyInterp_Dispatcher.h"
+
+// Initialize list of valid separators
+static const char * tmp_a[] = {" ", "(", "[","+", "-", "*", "/", ";", "^", "="};
+const std::vector<QString> PyConsole_EnhEditor::SEPARATORS = \
+    std::vector<QString>(tmp_a, tmp_a + sizeof(tmp_a)/sizeof(tmp_a[0]));
+
+/**
+ * Constructor.
+ * @param interp the interpreter linked to the editor
+ * @param parent parent widget
+ */
+PyConsole_EnhEditor::PyConsole_EnhEditor(PyConsole_EnhInterp * interp, QWidget * parent) :
+     PyConsole_Editor(interp, parent),
+     _tab_mode(false),
+     _cursor_pos(-1),
+     _multi_line_paste(false),
+     _multi_line_content()
+{
+  document()->setUndoRedoEnabled(true);
+}
+
+/**
+ * Overrides. Catches the TAB and Ctrl+TAB combinations.
+ * @param event
+ */
+void PyConsole_EnhEditor::keyPressEvent ( QKeyEvent* event)
+{
+  // check if <Ctrl> is pressed
+  bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
+  // check if <Shift> is pressed
+  bool shftPressed = event->modifiers() & Qt::ShiftModifier;
+
+  if (event->key() == Qt::Key_Tab && !shftPressed)
+    {
+      if (!ctrlPressed)
+        handleTab();
+      else
+        {
+          clearCompletion();
+          handleBackTab();
+        }
+      PyConsole_Editor::keyPressEvent(event);
+    }
+  else
+    {
+      // If ctrl is not pressed (and sth else is pressed with it),
+      // or if ctrl is not pressed alone
+      if (!ctrlPressed || (ctrlPressed && event->key() != Qt::Key_Control))
+        {
+          clearCompletion();
+          _cursor_pos = -1;
+        }
+      // Discard ctrl pressed alone:
+      if (event->key() != Qt::Key_Control)
+        PyConsole_Editor::keyPressEvent(event);
+    }
+}
+
+/**
+ * Whenever the mouse is clicked, clear the completion.
+ * @param e
+ */
+void PyConsole_EnhEditor::mousePressEvent(QMouseEvent* e)
+{
+  clearCompletion();
+  _cursor_pos = -1;
+  PyConsole_Editor::mousePressEvent(e);
+}
+
+/**
+ * Clear in the editor the block of text displayed after having hit <TAB>.
+ */
+void PyConsole_EnhEditor::clearCompletion()
+{
+  // Delete completion text if present
+  if (_tab_mode)
+    {
+      // Remove completion display
+      document()->undo();
+      // Remove trailing line return:
+      QTextCursor tc(textCursor());
+      tc.setPosition(document()->characterCount()-1);
+      setTextCursor(tc);
+      textCursor().deletePreviousChar();
+      // TODO: before wait for any TAB event to be completed
+      static_cast<PyConsole_EnhInterp *>(myInterp)->clearCompletion();
+    }
+  _tab_mode = false;
+}
+
+/**
+ * Handle the sequence of events after having hit <TAB>
+ */
+void PyConsole_EnhEditor::handleTab()
+{
+  if (_tab_mode)
+    {
+      // Already tab mode - nothing to do !
+      return;
+    }
+
+  QTextCursor cursor(textCursor());
+
+  // Cursor at end of input
+  cursor.movePosition(QTextCursor::End);
+  setTextCursor(cursor);
+
+  // Save cursor position if needed
+  if (_cursor_pos == -1)
+    _cursor_pos = textCursor().position();
+
+  // get last line
+  QTextBlock par = document()->end().previous();
+  if ( !par.isValid() ) return;
+
+  // Switch to completion mode
+  _tab_mode = true;
+
+  QString cmd = par.text().mid(promptSize());
+
+  // Post completion request
+  // Editor will be informed via a custom event that completion has been run
+  PyInterp_Request* req = createTabRequest(cmd);
+  PyInterp_Dispatcher::Get()->Exec(req);
+}
+
+/**
+ * Handles what happens after hitting Ctrl-TAB
+ */
+void PyConsole_EnhEditor::handleBackTab()
+{
+  QTextCursor cursor(textCursor());
+
+  if (_cursor_pos == -1)
+    {
+      // Invalid cursor position - we can't do anything:
+      return;
+    }
+  // Ensure cursor is at the end of command line
+  cursor.setPosition(_cursor_pos);
+  cursor.movePosition(QTextCursor::EndOfLine);
+  //setCursor(cursor);
+
+  // Delete last completed text
+  int i = cursor.position() - _cursor_pos;
+  cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, i);
+  cursor.removeSelectedText();
+  _cursor_pos = -1;
+}
+
+/**
+ * Create the Python requested that will be posted to the interpreter to
+ * get the completions.
+ * @param input line typed by the user at the time TAB was hit
+ * @return a CompletionCommand
+ * @sa CompletionCommand
+ */
+PyInterp_Request* PyConsole_EnhEditor::createTabRequest( const QString& input )
+{
+  // Parse input to extract on what part the dir() has to be executed
+  QString input2(input);
+
+  // Split up to the last syntaxical separator
+  int lastSp = -1;
+  for (std::vector<QString>::const_iterator i = SEPARATORS.begin(); i != SEPARATORS.end(); i++)
+    {
+      int j = input2.lastIndexOf(*i);
+      if (j > lastSp)
+        lastSp = j;
+    }
+  if (lastSp >= 0)
+    input2 = input.mid(lastSp+1);
+
+  // Detect a qualified name (with a point)
+  int lastPt = input2.lastIndexOf(QString("."));
+
+  // Split the 2 surrounding parts of the qualified name
+  if (lastPt != -1)
+    {
+      _compl_before_point = input2.left(lastPt);
+      _compl_after_point = input2.mid(lastPt+1);
+    }
+  else
+    {
+      // No point found - do a global matching -
+      // (the following will call dir() with an empty string)
+      _compl_after_point = input2;
+      _compl_before_point = QString("");
+    }
+
+  return new CompletionCommand( static_cast<PyConsole_EnhInterp *>(myInterp), _compl_before_point,
+                               _compl_after_point, this, isSync() );
+}
+
+/**
+ * Format completion results - this is where we should create 3 columns etc ...
+ * @param matches list of possible completions
+ * @param result return value
+ */
+void PyConsole_EnhEditor::formatCompletion(const std::vector<QString> & matches, QString & result) const
+{
+  int sz = matches.size();
+
+  if (sz > MAX_COMPLETIONS)
+    {
+      sz = MAX_COMPLETIONS;
+      result.append("[Too many matches! Displaying first ones only ...]\n");
+    }
+
+  for (int i = 0; i < sz; ++i)
+    {
+      result.append(matches[i]);
+      result.append("\n");
+    }
+}
+
+/**
+ * Override. Catches the events generated by the enhanced interpreter after the execution
+ * of a completion request.
+ * @param event
+ */
+void PyConsole_EnhEditor::customEvent( QEvent* event )
+{
+  std::vector<QString> matches;
+  QString first_match, comple_text, doc, base;
+  QTextCursor cursor(textCursor());
+  QTextBlockFormat bf;
+  QTextCharFormat cf;
+  PyConsole_EnhInterp * interp = static_cast<PyConsole_EnhInterp *>(myInterp);
+  int cursorPos;
+
+  switch( event->type() )
+  {
+    case PyInterp_Event::ES_TAB_COMPLETE_OK:
+      // Extract corresponding matches from the interpreter
+      matches = interp->getLastMatches();
+
+      if (matches.size() == 0)
+        {
+          // Completion successful but nothing returned.
+          _tab_mode = false;
+          _cursor_pos = -1;
+          return;
+        }
+
+      // Only one match - complete directly and update doc string window
+      doc = interp->getDocStr();
+      if (matches.size() == 1)
+        {
+          first_match = matches[0].mid(_compl_after_point.size());
+          cursor.insertText(first_match);
+          _tab_mode = false;
+          if (doc == QString(""))
+            emit updateDoc(formatDocHTML("(no documentation available)\n"));
+          else
+            emit updateDoc(formatDocHTML(doc));
+        }
+      else
+        {
+          // Detect if there is a common base to all available completion
+          // In this case append this base to the text already
+          extractCommon(matches, base);
+          first_match = base.mid(_compl_after_point.size());
+          cursor.insertText(first_match);
+
+          // If this happens to match exaclty the first completion
+          // also provide doc
+          if (base == matches[0])
+            {
+              doc = formatDocHTML(doc);
+              emit updateDoc(doc);
+            }
+
+          // Print all matching completion in a "undo-able" block
+          cursorPos = cursor.position();
+          cursor.insertBlock();
+          cursor.beginEditBlock();
+
+          // Insert all matches
+          QTextCharFormat cf;
+          cf.setForeground(QBrush(Qt::darkGreen));
+          cursor.setCharFormat(cf);
+          formatCompletion(matches, comple_text);
+          cursor.insertText(comple_text);
+          cursor.endEditBlock();
+
+          // Position cursor where it was before inserting the completion list:
+          cursor.setPosition(cursorPos);
+          setTextCursor(cursor);
+        }
+      break;
+    case PyInterp_Event::ES_TAB_COMPLETE_ERR:
+      // Tab completion was unsuccessful, switch off mode:
+      _tab_mode = false;
+      _cursor_pos = -1;
+      break;
+    case PyInterp_Event::ES_OK:
+    case PyInterp_Event::ES_ERROR:
+    case PyInterp_Event::ES_INCOMPLETE:
+      // Before everything else, call super()
+      PyConsole_Editor::customEvent(event);
+      // If we are in multi_paste_mode, process the next item:
+      multiLineProcessNextLine();
+      break;
+    default:
+      PyConsole_Editor::customEvent( event );
+      break;
+  }
+}
+
+/**
+ * Extract the common leading part of all strings in matches.
+ * @param matches
+ * @param result
+ */
+void PyConsole_EnhEditor::extractCommon(const std::vector<QString> & matches, QString & result) const
+{
+  result = "";
+  int charIdx = 0;
+
+  if (matches.size() < 2)
+    return;
+
+  while (true)
+    {
+      if (charIdx >= matches[0].size())
+        return;
+      QChar ch = matches[0][charIdx];
+      for (size_t j = 1; j < matches.size(); j++)
+        if (charIdx >= matches[j].size() || matches[j][charIdx] != ch)
+          return;
+      result += ch;
+      charIdx++;
+    }
+}
+
+/**
+ * Format the doc string in HTML format with the first line in bold blue
+ * @param doc initial doc string
+ * @return HTML string
+ */
+QString PyConsole_EnhEditor::formatDocHTML(const QString & doc) const
+{
+  QString templ = QString("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" ") +
+      QString(" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n ") +
+      QString("<html><head><meta name=\"qrichtext\" content=\"1\" /> ") +
+      QString("<style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style> ") +
+      QString("</head><body style=\" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;\">\n") +
+      QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">") +
+      QString("<span style=\" font-weight:600; color:#0000ff;\">%1</span></p>") +
+      QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%2</p>") +
+      QString("</body></html>");
+
+  QString fst, rest("");
+
+  // Extract first line of doc
+  int idx = doc.indexOf("\n");
+  if (idx > 0)
+    {
+      fst = doc.left(idx);
+      rest = doc.mid(idx+1);
+    }
+  else
+    {
+      fst = doc;
+    }
+
+  fst = fst.replace("\n", " ");
+  rest = rest.replace("\n", " ");
+  return templ.arg(fst).arg(rest);
+}
+
+/**
+ * Handle properly multi-line pasting. Qt4 doc recommends overriding this function.
+ * If the pasted text doesn't contain a line return, no special treatment is done.
+ * @param source
+ */
+void PyConsole_EnhEditor::insertFromMimeData(const QMimeData * source)
+{
+  if (_multi_line_paste)
+    return;
+
+  if (source->hasText())
+    {
+      QString s = source->text();
+      if (s.contains("\n"))
+        multilinePaste(s);
+      else
+        PyConsole_Editor::insertFromMimeData(source);
+    }
+  else
+    {
+      PyConsole_Editor::insertFromMimeData(source);
+    }
+}
+
+
+void PyConsole_EnhEditor::multilinePaste(const QString & s)
+{
+  // Turn on multi line pasting mode
+  _multi_line_paste = true;
+
+  // Split the string:
+  QString s2 = s;
+  s2.replace("\r", ""); // Windows string format converted to Unix style
+
+  QStringList lst = s2.split(QChar('\n'), QString::KeepEmptyParts);
+
+  // Perform the proper paste operation for the first line to handle the case where
+  // sth was already there:
+  QMimeData source;
+  source.setText(lst[0]);
+  PyConsole_Editor::insertFromMimeData(&source);
+
+  // Prepare what will have to be executed after the first line:
+  _multi_line_content = std::queue<QString>();
+  for (int i = 1; i < lst.size(); ++i)
+    _multi_line_content.push(lst[i]);
+
+  // Trigger the execution of the first (mixed) line
+  handleReturn();
+
+  // See customEvent() and multiLineProcessNext() for the rest of the handling.
+}
+
+/**
+ * Process the next line in the queue of a multiple copy/paste:
+ */
+void PyConsole_EnhEditor::multiLineProcessNextLine()
+{
+  if (!_multi_line_paste)
+    return;
+
+  QString line(_multi_line_content.front());
+  _multi_line_content.pop();
+  if (!_multi_line_content.size())
+    {
+      // last line in the queue, just paste it
+      addText(line, false, false);
+      _multi_line_paste = false;
+    }
+  else
+    {
+      // paste the line and simulate a <RETURN> key stroke
+      addText(line, false, false);
+      handleReturn();
+    }
+}
diff --git a/src/PyConsole/PyConsole_EnhEditor.cxx b/src/PyConsole/PyConsole_EnhEditor.cxx
deleted file mode 100644 (file)
index d5c47df..0000000
+++ /dev/null
@@ -1,464 +0,0 @@
-
-
-#include "PyConsole.h"
-#include <Python.h>
-
-#include <QKeyEvent>
-#include <QTextBlock>
-#include <QTextCursor>
-#include <QTextCharFormat>
-#include <QRegExp>
-#include <QMimeData>
-
-#include "PyConsole_EnhEditor.h"
-#include "PyConsole_EnhInterp.h"
-#include "PyConsole_Request.h"
-#include "PyInterp_Dispatcher.h"
-
-// Initialize list of valid separators
-static const char * tmp_a[] = {" ", "(", "[","+", "-", "*", "/", ";", "^", "="};
-const std::vector<QString> PyConsole_EnhEditor::SEPARATORS = \
-    std::vector<QString>(tmp_a, tmp_a + sizeof(tmp_a)/sizeof(tmp_a[0]));
-
-/**
- * Constructor.
- * @param interp the interpreter linked to the editor
- * @param parent parent widget
- */
-PyConsole_EnhEditor::PyConsole_EnhEditor(PyConsole_EnhInterp * interp, QWidget * parent) :
-     PyConsole_Editor(interp, parent),
-     _tab_mode(false),
-     _cursor_pos(-1),
-     _multi_line_paste(false),
-     _multi_line_content()
-{
-  document()->setUndoRedoEnabled(true);
-}
-
-/**
- * Overrides. Catches the TAB and Ctrl+TAB combinations.
- * @param event
- */
-void PyConsole_EnhEditor::keyPressEvent ( QKeyEvent* event)
-{
-  // check if <Ctrl> is pressed
-  bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
-  // check if <Shift> is pressed
-  bool shftPressed = event->modifiers() & Qt::ShiftModifier;
-
-  if (event->key() == Qt::Key_Tab && !shftPressed)
-    {
-      if (!ctrlPressed)
-        handleTab();
-      else
-        {
-          clearCompletion();
-          handleBackTab();
-        }
-      PyConsole_Editor::keyPressEvent(event);
-    }
-  else
-    {
-      // If ctrl is not pressed (and sth else is pressed with it),
-      // or if ctrl is not pressed alone
-      if (!ctrlPressed || (ctrlPressed && event->key() != Qt::Key_Control))
-        {
-          clearCompletion();
-          _cursor_pos = -1;
-        }
-      // Discard ctrl pressed alone:
-      if (event->key() != Qt::Key_Control)
-        PyConsole_Editor::keyPressEvent(event);
-    }
-}
-
-/**
- * Whenever the mouse is clicked, clear the completion.
- * @param e
- */
-void PyConsole_EnhEditor::mousePressEvent(QMouseEvent* e)
-{
-  clearCompletion();
-  _cursor_pos = -1;
-  PyConsole_Editor::mousePressEvent(e);
-}
-
-/**
- * Clear in the editor the block of text displayed after having hit <TAB>.
- */
-void PyConsole_EnhEditor::clearCompletion()
-{
-  // Delete completion text if present
-  if (_tab_mode)
-    {
-      // Remove completion display
-      document()->undo();
-      // Remove trailing line return:
-      QTextCursor tc(textCursor());
-      tc.setPosition(document()->characterCount()-1);
-      setTextCursor(tc);
-      textCursor().deletePreviousChar();
-      // TODO: before wait for any TAB event to be completed
-      static_cast<PyConsole_EnhInterp *>(myInterp)->clearCompletion();
-    }
-  _tab_mode = false;
-}
-
-/**
- * Handle the sequence of events after having hit <TAB>
- */
-void PyConsole_EnhEditor::handleTab()
-{
-  if (_tab_mode)
-    {
-      // Already tab mode - nothing to do !
-      return;
-    }
-
-  QTextCursor cursor(textCursor());
-
-  // Cursor at end of input
-  cursor.movePosition(QTextCursor::End);
-  setTextCursor(cursor);
-
-  // Save cursor position if needed
-  if (_cursor_pos == -1)
-    _cursor_pos = textCursor().position();
-
-  // get last line
-  QTextBlock par = document()->end().previous();
-  if ( !par.isValid() ) return;
-
-  // Switch to completion mode
-  _tab_mode = true;
-
-  QString cmd = par.text().mid(promptSize());
-
-  // Post completion request
-  // Editor will be informed via a custom event that completion has been run
-  PyInterp_Request* req = createTabRequest(cmd);
-  PyInterp_Dispatcher::Get()->Exec(req);
-}
-
-/**
- * Handles what happens after hitting Ctrl-TAB
- */
-void PyConsole_EnhEditor::handleBackTab()
-{
-  QTextCursor cursor(textCursor());
-
-  if (_cursor_pos == -1)
-    {
-      // Invalid cursor position - we can't do anything:
-      return;
-    }
-  // Ensure cursor is at the end of command line
-  cursor.setPosition(_cursor_pos);
-  cursor.movePosition(QTextCursor::EndOfLine);
-  //setCursor(cursor);
-
-  // Delete last completed text
-  int i = cursor.position() - _cursor_pos;
-  cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, i);
-  cursor.removeSelectedText();
-  _cursor_pos = -1;
-}
-
-/**
- * Create the Python requested that will be posted to the interpreter to
- * get the completions.
- * @param input line typed by the user at the time TAB was hit
- * @return a CompletionCommand
- * @sa CompletionCommand
- */
-PyInterp_Request* PyConsole_EnhEditor::createTabRequest( const QString& input )
-{
-  // Parse input to extract on what part the dir() has to be executed
-  QString input2(input);
-
-  // Split up to the last syntaxical separator
-  int lastSp = -1;
-  for (std::vector<QString>::const_iterator i = SEPARATORS.begin(); i != SEPARATORS.end(); i++)
-    {
-      int j = input2.lastIndexOf(*i);
-      if (j > lastSp)
-        lastSp = j;
-    }
-  if (lastSp >= 0)
-    input2 = input.mid(lastSp+1);
-
-  // Detect a qualified name (with a point)
-  int lastPt = input2.lastIndexOf(QString("."));
-
-  // Split the 2 surrounding parts of the qualified name
-  if (lastPt != -1)
-    {
-      _compl_before_point = input2.left(lastPt);
-      _compl_after_point = input2.mid(lastPt+1);
-    }
-  else
-    {
-      // No point found - do a global matching -
-      // (the following will call dir() with an empty string)
-      _compl_after_point = input2;
-      _compl_before_point = QString("");
-    }
-
-  return new CompletionCommand( static_cast<PyConsole_EnhInterp *>(myInterp), _compl_before_point,
-                               _compl_after_point, this, isSync() );
-}
-
-/**
- * Format completion results - this is where we should create 3 columns etc ...
- * @param matches list of possible completions
- * @param result return value
- */
-void PyConsole_EnhEditor::formatCompletion(const std::vector<QString> & matches, QString & result) const
-{
-  int sz = matches.size();
-
-  if (sz > MAX_COMPLETIONS)
-    {
-      sz = MAX_COMPLETIONS;
-      result.append("[Too many matches! Displaying first ones only ...]\n");
-    }
-
-  for (int i = 0; i < sz; ++i)
-    {
-      result.append(matches[i]);
-      result.append("\n");
-    }
-}
-
-/**
- * Override. Catches the events generated by the enhanced interpreter after the execution
- * of a completion request.
- * @param event
- */
-void PyConsole_EnhEditor::customEvent( QEvent* event )
-{
-  std::vector<QString> matches;
-  QString first_match, comple_text, doc, base;
-  QTextCursor cursor(textCursor());
-  QTextBlockFormat bf;
-  QTextCharFormat cf;
-  PyConsole_EnhInterp * interp = static_cast<PyConsole_EnhInterp *>(myInterp);
-  int cursorPos;
-
-  switch( event->type() )
-  {
-    case PyInterp_Event::ES_TAB_COMPLETE_OK:
-      // Extract corresponding matches from the interpreter
-      matches = interp->getLastMatches();
-
-      if (matches.size() == 0)
-        {
-          // Completion successful but nothing returned.
-          _tab_mode = false;
-          _cursor_pos = -1;
-          return;
-        }
-
-      // Only one match - complete directly and update doc string window
-      doc = interp->getDocStr();
-      if (matches.size() == 1)
-        {
-          first_match = matches[0].mid(_compl_after_point.size());
-          cursor.insertText(first_match);
-          _tab_mode = false;
-          if (doc == QString(""))
-            emit updateDoc(formatDocHTML("(no documentation available)\n"));
-          else
-            emit updateDoc(formatDocHTML(doc));
-        }
-      else
-        {
-          // Detect if there is a common base to all available completion
-          // In this case append this base to the text already
-          extractCommon(matches, base);
-          first_match = base.mid(_compl_after_point.size());
-          cursor.insertText(first_match);
-
-          // If this happens to match exaclty the first completion
-          // also provide doc
-          if (base == matches[0])
-            {
-              doc = formatDocHTML(doc);
-              emit updateDoc(doc);
-            }
-
-          // Print all matching completion in a "undo-able" block
-          cursorPos = cursor.position();
-          cursor.insertBlock();
-          cursor.beginEditBlock();
-
-          // Insert all matches
-          QTextCharFormat cf;
-          cf.setForeground(QBrush(Qt::darkGreen));
-          cursor.setCharFormat(cf);
-          formatCompletion(matches, comple_text);
-          cursor.insertText(comple_text);
-          cursor.endEditBlock();
-
-          // Position cursor where it was before inserting the completion list:
-          cursor.setPosition(cursorPos);
-          setTextCursor(cursor);
-        }
-      break;
-    case PyInterp_Event::ES_TAB_COMPLETE_ERR:
-      // Tab completion was unsuccessful, switch off mode:
-      _tab_mode = false;
-      _cursor_pos = -1;
-      break;
-    case PyInterp_Event::ES_OK:
-    case PyInterp_Event::ES_ERROR:
-    case PyInterp_Event::ES_INCOMPLETE:
-      // Before everything else, call super()
-      PyConsole_Editor::customEvent(event);
-      // If we are in multi_paste_mode, process the next item:
-      multiLineProcessNextLine();
-      break;
-    default:
-      PyConsole_Editor::customEvent( event );
-      break;
-  }
-}
-
-/**
- * Extract the common leading part of all strings in matches.
- * @param matches
- * @param result
- */
-void PyConsole_EnhEditor::extractCommon(const std::vector<QString> & matches, QString & result) const
-{
-  result = "";
-  int charIdx = 0;
-
-  if (matches.size() < 2)
-    return;
-
-  while (true)
-    {
-      if (charIdx >= matches[0].size())
-        return;
-      QChar ch = matches[0][charIdx];
-      for (size_t j = 1; j < matches.size(); j++)
-        if (charIdx >= matches[j].size() || matches[j][charIdx] != ch)
-          return;
-      result += ch;
-      charIdx++;
-    }
-}
-
-/**
- * Format the doc string in HTML format with the first line in bold blue
- * @param doc initial doc string
- * @return HTML string
- */
-QString PyConsole_EnhEditor::formatDocHTML(const QString & doc) const
-{
-  QString templ = QString("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" ") +
-      QString(" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n ") +
-      QString("<html><head><meta name=\"qrichtext\" content=\"1\" /> ") +
-      QString("<style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style> ") +
-      QString("</head><body style=\" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;\">\n") +
-      QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">") +
-      QString("<span style=\" font-weight:600; color:#0000ff;\">%1</span></p>") +
-      QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">%2</p>") +
-      QString("</body></html>");
-
-  QString fst, rest("");
-
-  // Extract first line of doc
-  int idx = doc.indexOf("\n");
-  if (idx > 0)
-    {
-      fst = doc.left(idx);
-      rest = doc.mid(idx+1);
-    }
-  else
-    {
-      fst = doc;
-    }
-
-  fst = fst.replace("\n", " ");
-  rest = rest.replace("\n", " ");
-  return templ.arg(fst).arg(rest);
-}
-
-/**
- * Handle properly multi-line pasting. Qt4 doc recommends overriding this function.
- * If the pasted text doesn't contain a line return, no special treatment is done.
- * @param source
- */
-void PyConsole_EnhEditor::insertFromMimeData(const QMimeData * source)
-{
-  if (_multi_line_paste)
-    return;
-
-  if (source->hasText())
-    {
-      QString s = source->text();
-      if (s.contains("\n"))
-        multilinePaste(s);
-      else
-        PyConsole_Editor::insertFromMimeData(source);
-    }
-  else
-    {
-      PyConsole_Editor::insertFromMimeData(source);
-    }
-}
-
-
-void PyConsole_EnhEditor::multilinePaste(const QString & s)
-{
-  // Turn on multi line pasting mode
-  _multi_line_paste = true;
-
-  // Split the string:
-  QString s2 = s;
-  s2.replace("\r", ""); // Windows string format converted to Unix style
-
-  QStringList lst = s2.split(QChar('\n'), QString::KeepEmptyParts);
-
-  // Perform the proper paste operation for the first line to handle the case where
-  // sth was already there:
-  QMimeData source;
-  source.setText(lst[0]);
-  PyConsole_Editor::insertFromMimeData(&source);
-
-  // Prepare what will have to be executed after the first line:
-  _multi_line_content = std::queue<QString>();
-  for (int i = 1; i < lst.size(); ++i)
-    _multi_line_content.push(lst[i]);
-
-  // Trigger the execution of the first (mixed) line
-  handleReturn();
-
-  // See customEvent() and multiLineProcessNext() for the rest of the handling.
-}
-
-/**
- * Process the next line in the queue of a multiple copy/paste:
- */
-void PyConsole_EnhEditor::multiLineProcessNextLine()
-{
-  if (!_multi_line_paste)
-    return;
-
-  QString line(_multi_line_content.front());
-  _multi_line_content.pop();
-  if (!_multi_line_content.size())
-    {
-      // last line in the queue, just paste it
-      addText(line, false, false);
-      _multi_line_paste = false;
-    }
-  else
-    {
-      // paste the line and simulate a <RETURN> key stroke
-      addText(line, false, false);
-      handleReturn();
-    }
-}
diff --git a/src/PyConsole/PyConsole_EnhInterp.cpp b/src/PyConsole/PyConsole_EnhInterp.cpp
new file mode 100644 (file)
index 0000000..4332f31
--- /dev/null
@@ -0,0 +1,135 @@
+
+
+
+#include "PyConsole.h"
+
+#include "PyConsole_EnhInterp.h"
+
+#include <pythonrun.h>
+#include <string>
+#include <QRegExp>
+
+static const char * tmp_k[] = {"and",  "as", "assert", "break",  "class",
+    "continue", "def",  "del",
+    "elif", "else", "except", "exec", "finally",  "for",  "from", "global", "if",
+    "import", "in", "is", "lambda", "not",  "or", "pass", "print",  "raise",
+    "return", "try",  "while",  "with", "yield"};
+
+const std::vector<QString> PyConsole_EnhInterp::PYTHON_KEYWORDS = \
+      std::vector<QString>(tmp_k, tmp_k+sizeof(tmp_k)/sizeof(tmp_k[0]));
+
+/*!
+  \brief Run Python dir() command and saves the result internally in _lastPy
+  \param dirArgument Python expression to pass to the dir command. The parsing of what the
+  user actually started typing is dedicated to the caller
+  \param startMatch string representing the begining of the patter to be completed. For example when
+  the user types "a_string_variable.rsp <TAB>", this is "rsp".
+  \return command exit status - 0 = success
+*/
+int PyConsole_EnhInterp::runDirCommand(const QString & dirArgument, const QString & startMatch)
+{
+  int ret;
+  std::vector<QString> v;
+
+  clearCompletion();
+  if ( (ret = runDirAndExtract(dirArgument, startMatch, _last_matches)) )
+      return ret;
+
+  // If dirArgument is empty, we append the __builtins__
+  if (dirArgument.isEmpty())
+    {
+      if ( (ret = runDirAndExtract(QString("__builtins__"), startMatch, _last_matches, false)) )
+            return ret;
+
+      // ... and we match on Python's keywords as well:
+      for (std::vector<QString>::const_iterator it = PYTHON_KEYWORDS.begin(); it != PYTHON_KEYWORDS.end(); it++)
+          if ((*it).startsWith(startMatch))
+            _last_matches.push_back(*it);
+    }
+
+  // Try to get doc string of the first match
+  if (_last_matches.size() > 0)
+    {
+      QString cmd("");
+      if (dirArgument.trimmed() != "")
+        cmd = dirArgument + ".";
+      cmd += _last_matches[0] + ".__doc__";
+      PyObject * str = PyRun_String(cmd.toStdString().c_str(), Py_eval_input, _g, _g);
+      if (!str || str == Py_None || !PyString_Check(str))
+        {
+          if (!str)
+            PyErr_Clear();
+          _doc_str = "";
+        }
+      else
+        _doc_str = QString(PyString_AsString(str));
+      Py_XDECREF(str);
+    }
+
+  // The command has been successfully executed
+  return 0;
+}
+
+/**
+ * See runDirCommand().
+ * @param dirArgument see runDirCommand()
+ * @param startMatch  see runDirCommand()
+ * @param[out] result the list of matches
+ * @param discardSwig if true a regular expression is used to discard all static method generated
+ * by SWIG. typically: MEDCouplingUMesh_Blabla
+ * @return -1 if the call to dir() or the parsing of the result failed, 0 otherwise.
+ */
+int PyConsole_EnhInterp::runDirAndExtract(const QString& dirArgument,
+       const QString & startMatch, std::vector<QString> & result,
+       bool discardSwig) const
+{
+  QRegExp re("^[A-Z].+_[A-Z]+[a-z]+.+$");  // discard SWIG static method, e.g. MEDCouplingUMesh_Blabla
+  QString command("dir(" + dirArgument + ")");
+  PyObject * plst = PyRun_String(command.toStdString().c_str(), Py_eval_input, _g, _g);
+  if(!plst || plst == Py_None) {
+    if(!plst)
+      PyErr_Clear();
+
+    Py_XDECREF(plst);
+    return -1;
+  }
+
+  // Extract the returned list and convert it to a vector<>
+  if (!PySequence_Check(plst))
+    {
+      // Should never happen ...
+      //std::cerr << "not a list!" << std::endl;
+      Py_XDECREF(plst);
+      return -1;
+    }
+
+  // Convert plst to a vector of QString
+  int n = PySequence_Length(plst);
+  for (int i = 0; i < n; i++)
+    {
+      PyObject * it;
+      it = PySequence_GetItem(plst, i);
+      QString s(PyString_AsString(it));
+      // if the method is not from swig, not static (guessed from the reg exp) and matches
+      // what is already there
+      if (s.startsWith(startMatch))
+        if(!discardSwig || (!re.exactMatch(s) && !s.contains("swig")))
+          result.push_back(s);
+      Py_DECREF(it);
+    }
+  Py_DECREF(plst);
+
+  return 0;
+}
+
+/**
+ * Clear internal members containing the last completion results.
+ */
+void PyConsole_EnhInterp::clearCompletion()
+{
+  _last_matches.resize(0);
+  _doc_str = QString("");
+}
+
+
+
diff --git a/src/PyConsole/PyConsole_EnhInterp.cxx b/src/PyConsole/PyConsole_EnhInterp.cxx
deleted file mode 100644 (file)
index 4332f31..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-
-
-
-#include "PyConsole.h"
-
-#include "PyConsole_EnhInterp.h"
-
-#include <pythonrun.h>
-#include <string>
-#include <QRegExp>
-
-static const char * tmp_k[] = {"and",  "as", "assert", "break",  "class",
-    "continue", "def",  "del",
-    "elif", "else", "except", "exec", "finally",  "for",  "from", "global", "if",
-    "import", "in", "is", "lambda", "not",  "or", "pass", "print",  "raise",
-    "return", "try",  "while",  "with", "yield"};
-
-const std::vector<QString> PyConsole_EnhInterp::PYTHON_KEYWORDS = \
-      std::vector<QString>(tmp_k, tmp_k+sizeof(tmp_k)/sizeof(tmp_k[0]));
-
-/*!
-  \brief Run Python dir() command and saves the result internally in _lastPy
-  \param dirArgument Python expression to pass to the dir command. The parsing of what the
-  user actually started typing is dedicated to the caller
-  \param startMatch string representing the begining of the patter to be completed. For example when
-  the user types "a_string_variable.rsp <TAB>", this is "rsp".
-  \return command exit status - 0 = success
-*/
-int PyConsole_EnhInterp::runDirCommand(const QString & dirArgument, const QString & startMatch)
-{
-  int ret;
-  std::vector<QString> v;
-
-  clearCompletion();
-  if ( (ret = runDirAndExtract(dirArgument, startMatch, _last_matches)) )
-      return ret;
-
-  // If dirArgument is empty, we append the __builtins__
-  if (dirArgument.isEmpty())
-    {
-      if ( (ret = runDirAndExtract(QString("__builtins__"), startMatch, _last_matches, false)) )
-            return ret;
-
-      // ... and we match on Python's keywords as well:
-      for (std::vector<QString>::const_iterator it = PYTHON_KEYWORDS.begin(); it != PYTHON_KEYWORDS.end(); it++)
-          if ((*it).startsWith(startMatch))
-            _last_matches.push_back(*it);
-    }
-
-  // Try to get doc string of the first match
-  if (_last_matches.size() > 0)
-    {
-      QString cmd("");
-      if (dirArgument.trimmed() != "")
-        cmd = dirArgument + ".";
-      cmd += _last_matches[0] + ".__doc__";
-      PyObject * str = PyRun_String(cmd.toStdString().c_str(), Py_eval_input, _g, _g);
-      if (!str || str == Py_None || !PyString_Check(str))
-        {
-          if (!str)
-            PyErr_Clear();
-          _doc_str = "";
-        }
-      else
-        _doc_str = QString(PyString_AsString(str));
-      Py_XDECREF(str);
-    }
-
-  // The command has been successfully executed
-  return 0;
-}
-
-/**
- * See runDirCommand().
- * @param dirArgument see runDirCommand()
- * @param startMatch  see runDirCommand()
- * @param[out] result the list of matches
- * @param discardSwig if true a regular expression is used to discard all static method generated
- * by SWIG. typically: MEDCouplingUMesh_Blabla
- * @return -1 if the call to dir() or the parsing of the result failed, 0 otherwise.
- */
-int PyConsole_EnhInterp::runDirAndExtract(const QString& dirArgument,
-       const QString & startMatch, std::vector<QString> & result,
-       bool discardSwig) const
-{
-  QRegExp re("^[A-Z].+_[A-Z]+[a-z]+.+$");  // discard SWIG static method, e.g. MEDCouplingUMesh_Blabla
-  QString command("dir(" + dirArgument + ")");
-  PyObject * plst = PyRun_String(command.toStdString().c_str(), Py_eval_input, _g, _g);
-  if(!plst || plst == Py_None) {
-    if(!plst)
-      PyErr_Clear();
-
-    Py_XDECREF(plst);
-    return -1;
-  }
-
-  // Extract the returned list and convert it to a vector<>
-  if (!PySequence_Check(plst))
-    {
-      // Should never happen ...
-      //std::cerr << "not a list!" << std::endl;
-      Py_XDECREF(plst);
-      return -1;
-    }
-
-  // Convert plst to a vector of QString
-  int n = PySequence_Length(plst);
-  for (int i = 0; i < n; i++)
-    {
-      PyObject * it;
-      it = PySequence_GetItem(plst, i);
-      QString s(PyString_AsString(it));
-      // if the method is not from swig, not static (guessed from the reg exp) and matches
-      // what is already there
-      if (s.startsWith(startMatch))
-        if(!discardSwig || (!re.exactMatch(s) && !s.contains("swig")))
-          result.push_back(s);
-      Py_DECREF(it);
-    }
-  Py_DECREF(plst);
-
-  return 0;
-}
-
-/**
- * Clear internal members containing the last completion results.
- */
-void PyConsole_EnhInterp::clearCompletion()
-{
-  _last_matches.resize(0);
-  _doc_str = QString("");
-}
-
-
-
diff --git a/src/PyConsole/PyConsole_Event.cpp b/src/PyConsole/PyConsole_Event.cpp
new file mode 100644 (file)
index 0000000..a3e2b05
--- /dev/null
@@ -0,0 +1,3 @@
+
+
+#include "PyConsole_Event.h"
diff --git a/src/PyConsole/PyConsole_Event.cxx b/src/PyConsole/PyConsole_Event.cxx
deleted file mode 100644 (file)
index a3e2b05..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-#include "PyConsole_Event.h"
diff --git a/src/PyConsole/PyConsole_Interp.cpp b/src/PyConsole/PyConsole_Interp.cpp
new file mode 100644 (file)
index 0000000..21af783
--- /dev/null
@@ -0,0 +1,118 @@
+
+#include "PyConsole_Interp.h"
+
+/*!
+  \class PyConsole_Interp
+  \brief Python interpreter to be embedded to the SALOME study's GUI.
+
+  Python interpreter is created one per SALOME study.
+
+  Call initialize method defined in the base class PyInterp_Interp,
+  to intialize interpreter after instance creation.
+
+  The method initialize() calls virtuals methods
+  - initPython()  to initialize global Python interpreter
+  - initState()   to initialize embedded interpreter state
+  - initContext() to initialize interpreter internal context
+  - initRun()     to prepare interpreter for running commands
+
+  /EDF-CCAR/
+  When SALOME uses multi Python interpreter feature, 
+  every study has its own interpreter and thread state (_tstate = Py_NewInterpreter()).
+  This is fine because every study has its own modules (sys.modules) stdout and stderr.
+
+  <b>But</b> some Python modules must be imported only once. In multi interpreter 
+  context Python modules (*.py) are imported several times.
+  For example, the PyQt module must be imported only once because 
+  it registers classes in a C module.
+
+  It's quite the same with omniorb modules (internals and generated with omniidl).
+
+  This problem is handled with "shared modules" defined in salome_shared_modules.py.
+  These "shared modules" are imported only once and only copied in all 
+  the other interpreters.
+
+  <b>But</b> it's not the only problem. Every interpreter has its own 
+  __builtin__ module. That's fine but if we have copied some modules 
+  and imported others problems may arise with operations that are not allowed
+  in restricted execution environment. So we must impose that all interpreters
+  have identical __builtin__ module.
+*/
+
+/*!
+  \brief Constructor.
+
+  Creates new python interpreter.
+*/
+PyConsole_Interp::PyConsole_Interp(): PyInterp_Interp()
+{
+}
+
+/*!
+  \brief Destructor.
+
+  Does nothing for the moment.
+*/
+PyConsole_Interp::~PyConsole_Interp()
+{
+}
+/*!
+  \brief Initialize internal Python interpreter state.
+
+  When calling initState the GIL is not held
+  It must not be held on exit
+
+  \return \c true on success
+*/
+bool PyConsole_Interp::initState()
+{
+  PyEval_AcquireLock();
+  _tstate = Py_NewInterpreter(); // create an interpreter and save current state
+  PySys_SetArgv(PyInterp_Interp::_argc,PyInterp_Interp::_argv); // initialize sys.argv
+  
+  if(!builtinmodule) // PAL18041: deepcopy function don't work in Salome
+  {
+    //builtinmodule is static member of PyInterp class
+    //If it is not NULL (initialized to the builtin module of the main interpreter
+    //all the sub interpreters will have the same builtin
+    //_interp is a static member and is the main interpreter
+    //The first time we initialized it to the builtin of main interpreter
+    builtinmodule=PyDict_GetItemString(_interp->modules, "__builtin__");
+  }
+
+  //If builtinmodule has been initialized all the sub interpreters
+  // will have the same __builtin__ module
+  if(builtinmodule){ 
+    PyObject *m = PyImport_GetModuleDict();
+    PyDict_SetItemString(m, "__builtin__", builtinmodule);
+    _tstate->interp->builtins = PyModule_GetDict(builtinmodule);
+    Py_INCREF(_tstate->interp->builtins);
+  }
+  PyEval_ReleaseThread(_tstate);
+  return true;
+}
+
+/*!
+  \brief Initialize python interpeter context.
+
+  The GIL is assumed to be held.
+  It is the caller responsability to acquire the GIL.
+  It must still be held on initContext() exit.
+
+  \return \c true on success
+*/
+bool PyConsole_Interp::initContext()
+{
+  PyObject *m = PyImport_AddModule("__main__");  // interpreter main module (module context)
+  if(!m){
+    PyErr_Print();
+    return false;
+  }  
+  _g = PyModule_GetDict(m);          // get interpreter dictionnary context
+
+  if(builtinmodule){
+    PyDict_SetItemString(_g, "__builtins__", builtinmodule); // assign singleton __builtin__ module
+  }
+  return true;
+}
diff --git a/src/PyConsole/PyConsole_Interp.cxx b/src/PyConsole/PyConsole_Interp.cxx
deleted file mode 100644 (file)
index 21af783..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-
-#include "PyConsole_Interp.h"
-
-/*!
-  \class PyConsole_Interp
-  \brief Python interpreter to be embedded to the SALOME study's GUI.
-
-  Python interpreter is created one per SALOME study.
-
-  Call initialize method defined in the base class PyInterp_Interp,
-  to intialize interpreter after instance creation.
-
-  The method initialize() calls virtuals methods
-  - initPython()  to initialize global Python interpreter
-  - initState()   to initialize embedded interpreter state
-  - initContext() to initialize interpreter internal context
-  - initRun()     to prepare interpreter for running commands
-
-  /EDF-CCAR/
-  When SALOME uses multi Python interpreter feature, 
-  every study has its own interpreter and thread state (_tstate = Py_NewInterpreter()).
-  This is fine because every study has its own modules (sys.modules) stdout and stderr.
-
-  <b>But</b> some Python modules must be imported only once. In multi interpreter 
-  context Python modules (*.py) are imported several times.
-  For example, the PyQt module must be imported only once because 
-  it registers classes in a C module.
-
-  It's quite the same with omniorb modules (internals and generated with omniidl).
-
-  This problem is handled with "shared modules" defined in salome_shared_modules.py.
-  These "shared modules" are imported only once and only copied in all 
-  the other interpreters.
-
-  <b>But</b> it's not the only problem. Every interpreter has its own 
-  __builtin__ module. That's fine but if we have copied some modules 
-  and imported others problems may arise with operations that are not allowed
-  in restricted execution environment. So we must impose that all interpreters
-  have identical __builtin__ module.
-*/
-
-/*!
-  \brief Constructor.
-
-  Creates new python interpreter.
-*/
-PyConsole_Interp::PyConsole_Interp(): PyInterp_Interp()
-{
-}
-
-/*!
-  \brief Destructor.
-
-  Does nothing for the moment.
-*/
-PyConsole_Interp::~PyConsole_Interp()
-{
-}
-/*!
-  \brief Initialize internal Python interpreter state.
-
-  When calling initState the GIL is not held
-  It must not be held on exit
-
-  \return \c true on success
-*/
-bool PyConsole_Interp::initState()
-{
-  PyEval_AcquireLock();
-  _tstate = Py_NewInterpreter(); // create an interpreter and save current state
-  PySys_SetArgv(PyInterp_Interp::_argc,PyInterp_Interp::_argv); // initialize sys.argv
-  
-  if(!builtinmodule) // PAL18041: deepcopy function don't work in Salome
-  {
-    //builtinmodule is static member of PyInterp class
-    //If it is not NULL (initialized to the builtin module of the main interpreter
-    //all the sub interpreters will have the same builtin
-    //_interp is a static member and is the main interpreter
-    //The first time we initialized it to the builtin of main interpreter
-    builtinmodule=PyDict_GetItemString(_interp->modules, "__builtin__");
-  }
-
-  //If builtinmodule has been initialized all the sub interpreters
-  // will have the same __builtin__ module
-  if(builtinmodule){ 
-    PyObject *m = PyImport_GetModuleDict();
-    PyDict_SetItemString(m, "__builtin__", builtinmodule);
-    _tstate->interp->builtins = PyModule_GetDict(builtinmodule);
-    Py_INCREF(_tstate->interp->builtins);
-  }
-  PyEval_ReleaseThread(_tstate);
-  return true;
-}
-
-/*!
-  \brief Initialize python interpeter context.
-
-  The GIL is assumed to be held.
-  It is the caller responsability to acquire the GIL.
-  It must still be held on initContext() exit.
-
-  \return \c true on success
-*/
-bool PyConsole_Interp::initContext()
-{
-  PyObject *m = PyImport_AddModule("__main__");  // interpreter main module (module context)
-  if(!m){
-    PyErr_Print();
-    return false;
-  }  
-  _g = PyModule_GetDict(m);          // get interpreter dictionnary context
-
-  if(builtinmodule){
-    PyDict_SetItemString(_g, "__builtins__", builtinmodule); // assign singleton __builtin__ module
-  }
-  return true;
-}
diff --git a/src/PyConsole/PyConsole_Request.cpp b/src/PyConsole/PyConsole_Request.cpp
new file mode 100644 (file)
index 0000000..3e31b90
--- /dev/null
@@ -0,0 +1,99 @@
+
+
+#include "PyConsole_Request.h"
+
+#include "PyInterp_Event.h"
+#include "PyConsole_Event.h"
+#include "PyConsole_EnhInterp.h"
+#include "PyConsole_EnhEditor.h"
+
+#include <QCoreApplication>
+
+/**
+ * Constructor.
+ * @param theInterp interpreter that will execute the command
+ * @param theCommand command text
+ * @param theListener editor object that will receive the response events after execution
+ * of the request
+ * @param sync
+ */
+ExecCommand::ExecCommand( PyInterp_Interp*        theInterp,
+               const QString&          theCommand,
+               PyConsole_Editor*       theListener,
+               bool                    sync )
+    : PyInterp_LockRequest( theInterp, theListener, sync ),
+      myCommand( theCommand ), myState( PyInterp_Event::ES_OK )
+  {}
+
+/**
+ * Execute the command by calling the run() method of the embedded interpreter.
+ */
+void ExecCommand::execute()
+{
+  if ( myCommand != "" )
+    {
+      int ret = getInterp()->run( myCommand.toUtf8().data() );
+      if ( ret < 0 )
+        myState = PyInterp_Event::ES_ERROR;
+      else if ( ret > 0 )
+        myState = PyInterp_Event::ES_INCOMPLETE;
+    }
+}
+
+/**
+ * Create the event indicating the status of the request execution.
+ * @return a QEvent
+ */
+QEvent* ExecCommand::createEvent()
+{
+  if ( IsSync() )
+    QCoreApplication::sendPostedEvents( listener(), PrintEvent::EVENT_ID );
+  return new PyInterp_Event( myState, this );
+}
+
+
+/*!
+  Constructor.
+  Creates a new python completion request.
+  \param theInterp   python interpreter
+  \param input  string containing the dir() command to be executed
+  \param startMatch  part to be matched with the results of the dir() command
+  \param theListener widget to get the notification messages
+  \param sync        if True the request is processed synchronously
+*/
+CompletionCommand::CompletionCommand( PyConsole_EnhInterp*  theInterp,
+               const QString&          input,
+               const QString&         startMatch,
+               PyConsole_EnhEditor*           theListener,
+               bool                    sync)
+     : PyInterp_LockRequest( theInterp, theListener, sync ),
+       _tabSuccess(false), _dirArg(input), _startMatch(startMatch)
+{}
+
+/**
+ * Execute the completion command by wrapping the runDirCommand() of the
+ * embedded enhanced interpreter.
+ */
+void CompletionCommand::execute()
+{
+  PyConsole_EnhInterp * interp = static_cast<PyConsole_EnhInterp *>(getInterp());
+    int ret = interp->runDirCommand( _dirArg,  _startMatch);
+    if (ret == 0)
+      _tabSuccess = true;
+    else
+      _tabSuccess = false;
+}
+
+/**
+ * Create the event indicating the return value of the completion command.
+ * @return
+ */
+QEvent* CompletionCommand::createEvent()
+{
+  int typ = _tabSuccess ? PyInterp_Event::ES_TAB_COMPLETE_OK : PyInterp_Event::ES_TAB_COMPLETE_ERR;
+
+  return new PyInterp_Event( typ, this);
+}
+
+
+
diff --git a/src/PyConsole/PyConsole_Request.cxx b/src/PyConsole/PyConsole_Request.cxx
deleted file mode 100644 (file)
index 3e31b90..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-
-
-#include "PyConsole_Request.h"
-
-#include "PyInterp_Event.h"
-#include "PyConsole_Event.h"
-#include "PyConsole_EnhInterp.h"
-#include "PyConsole_EnhEditor.h"
-
-#include <QCoreApplication>
-
-/**
- * Constructor.
- * @param theInterp interpreter that will execute the command
- * @param theCommand command text
- * @param theListener editor object that will receive the response events after execution
- * of the request
- * @param sync
- */
-ExecCommand::ExecCommand( PyInterp_Interp*        theInterp,
-               const QString&          theCommand,
-               PyConsole_Editor*       theListener,
-               bool                    sync )
-    : PyInterp_LockRequest( theInterp, theListener, sync ),
-      myCommand( theCommand ), myState( PyInterp_Event::ES_OK )
-  {}
-
-/**
- * Execute the command by calling the run() method of the embedded interpreter.
- */
-void ExecCommand::execute()
-{
-  if ( myCommand != "" )
-    {
-      int ret = getInterp()->run( myCommand.toUtf8().data() );
-      if ( ret < 0 )
-        myState = PyInterp_Event::ES_ERROR;
-      else if ( ret > 0 )
-        myState = PyInterp_Event::ES_INCOMPLETE;
-    }
-}
-
-/**
- * Create the event indicating the status of the request execution.
- * @return a QEvent
- */
-QEvent* ExecCommand::createEvent()
-{
-  if ( IsSync() )
-    QCoreApplication::sendPostedEvents( listener(), PrintEvent::EVENT_ID );
-  return new PyInterp_Event( myState, this );
-}
-
-
-/*!
-  Constructor.
-  Creates a new python completion request.
-  \param theInterp   python interpreter
-  \param input  string containing the dir() command to be executed
-  \param startMatch  part to be matched with the results of the dir() command
-  \param theListener widget to get the notification messages
-  \param sync        if True the request is processed synchronously
-*/
-CompletionCommand::CompletionCommand( PyConsole_EnhInterp*  theInterp,
-               const QString&          input,
-               const QString&         startMatch,
-               PyConsole_EnhEditor*           theListener,
-               bool                    sync)
-     : PyInterp_LockRequest( theInterp, theListener, sync ),
-       _tabSuccess(false), _dirArg(input), _startMatch(startMatch)
-{}
-
-/**
- * Execute the completion command by wrapping the runDirCommand() of the
- * embedded enhanced interpreter.
- */
-void CompletionCommand::execute()
-{
-  PyConsole_EnhInterp * interp = static_cast<PyConsole_EnhInterp *>(getInterp());
-    int ret = interp->runDirCommand( _dirArg,  _startMatch);
-    if (ret == 0)
-      _tabSuccess = true;
-    else
-      _tabSuccess = false;
-}
-
-/**
- * Create the event indicating the return value of the completion command.
- * @return
- */
-QEvent* CompletionCommand::createEvent()
-{
-  int typ = _tabSuccess ? PyInterp_Event::ES_TAB_COMPLETE_OK : PyInterp_Event::ES_TAB_COMPLETE_ERR;
-
-  return new PyInterp_Event( typ, this);
-}
-
-
-
index 2d5d28bd8b2fd687f253a7effc40622285c3f91c..0b54ed93e6dc16d17a7115928694318f81e49d3d 100644 (file)
@@ -6,8 +6,8 @@ SET(PROJECT_HEADERS
 )
 
 SET(PROJECT_SOURCES 
-       PyEvent_Event.cxx 
-       PyEvent_EventFilter.cxx
+       PyEvent_Event.cpp
+       PyEvent_EventFilter.cpp
 )
 
 #INCLUDE_DIRECTORIES(${Qt5Widgets_INCLUDES})
diff --git a/src/PyEvent/PyEvent_Event.cpp b/src/PyEvent/PyEvent_Event.cpp
new file mode 100644 (file)
index 0000000..fef847f
--- /dev/null
@@ -0,0 +1,287 @@
+
+#include "PyEvent_Event.h"
+
+#include <QSemaphore>
+#include <QApplication>
+
+// asv 21.02.05 : introducing multi-platform approach of thread comparison
+// - on Unix using pthread_t type for storing ThreadId
+// - on Win32 using integer type for storing ThreadId
+// NOT using integer ThreadId on both Unix and Win32 because (from documentation):
+// "...Do not allow your program to rely on the internal structure or size of the pthread_t..."
+
+#ifdef WIN32
+#include <windows.h>
+static DWORD myThread;
+#else
+#include <pthread.h>
+static pthread_t myThread;
+#endif
+
+/*!
+  \class InitEvent
+  \brief Helper event class responsible for initializing PyEvent_Event
+  mechanism by the main thread ID
+ */
+class InitEvent : public PyEvent_Event
+{
+public:
+  InitEvent();
+  virtual      ~InitEvent();
+  virtual void Execute();
+};
+
+/*!
+  \brief Constructor, initializes the event mechanism by the current thread ID.
+  It is asssumed to be the main thread ID, so be careful!
+*/
+InitEvent::InitEvent()
+{
+  GetSessionThread();
+}
+
+/*!
+  \brief Destructor, does nothing.
+*/
+InitEvent::~InitEvent()
+{
+}
+
+/*!
+  \brief Nothing to be executed for this kind of event.
+*/
+void InitEvent::Execute()
+{
+}
+
+// NOTE: Here the SALOME event mechanism is initalized by the 
+// current thread ID that is always assumed to be the main thread ID.
+// This should be revised as soon as the application library is no longer
+// linked against the Event library (i.e. this static object is not created or created 
+// outside the main thread).
+static InitEvent myInitEvent;
+
+/*!
+  \class PyEvent_CustomEvent
+  \brief Generic event class for user-defined events
+  
+  This class contains a generic void* data member that may be used
+  for transferring event-specific data to the receiver.
+
+  \warning The internal data is not destroyed by the class destructor.
+*/
+
+/*!
+  \brief Constructor.
+  \param type event type
+*/
+PyEvent_CustomEvent::PyEvent_CustomEvent( int type )
+: QEvent( (QEvent::Type)type ), d( 0 )
+{
+}
+
+/*!
+  \brief Constructor.
+  \param type event type
+  \param data custom data
+*/
+PyEvent_CustomEvent::PyEvent_CustomEvent( QEvent::Type type, void* data )
+: QEvent( type ), d( data )
+{
+}
+
+/*!
+  \brief Get custom data.
+  \return pointer to the internal data
+*/
+void* PyEvent_CustomEvent::data() const
+{
+  return d;
+}
+
+/*!
+  \brief Set custom data.
+  \param data pointer to the internal data
+*/
+void PyEvent_CustomEvent::setData( void* data )
+{
+  d = data;
+}
+
+/*!
+  \class PyEvent_Event
+  \brief The class which encapsulates data and functionality required for 
+         posting component-specific events to perform arbitrary operations 
+         in the main GUI thread. 
+
+  PyEvent_Event objects can be posted by any thread belonging to the GUI process.
+  
+  It is necessary to derive a custom event class from PyEvent_Event and 
+  re-implement virtual Execute() method. This method should actually perform 
+  the desirable operation. To pass all the required data to Execute() and 
+  store a return value, arbitrary data fields can be added to the custom 
+  event class. There is no need to protect such fields with a mutex, for only
+  one thread working with a PyEvent_Event object is active at any moment.
+  
+  Usage:
+  - Create PyEvent_Event. Components can derive their own event class from 
+  PyEvent_Event in order to pass custom data to the event handler.
+  - Call process() method to post the event. After process() execution
+  it is possible to examine fields of your custom event object.
+  - Perform delete operator on the event to wake up the desktop (you can also 
+  set <autoRelease>  parameter to \c true to automatically wake up desktop after 
+  process().
+  
+  The method processed() is used by the desktop to signal that event processing 
+  has been completed.
+
+  To make all this work, it is necessary to call static method GetSessionThread()
+  during the application initialization, i.e. from main() function.
+  It is important to call this method from the primary application thread.
+
+  Caveats: 
+  - there are no.
+*/
+
+//! Total number of semaphore resources
+const int NumberOfResources = 2;
+
+/*!
+  \brief Initialize event mechanism.
+
+  This function sets up the main application thread. It should be called
+  during the application initialization, i.e. main() function.
+*/
+void PyEvent_Event::GetSessionThread(){
+#ifdef WIN32
+  myThread = ::GetCurrentThreadId();
+#else
+  myThread = pthread_self();
+#endif
+}
+
+/*!
+  \brief Check if the processing is in the main application thread.
+  \return \c true if this method is called from the main application thread
+*/
+bool PyEvent_Event::IsSessionThread(){
+  bool aResult = false;
+#ifdef WIN32
+  aResult = myThread == ::GetCurrentThreadId();
+#else
+  aResult = myThread == pthread_self();
+#endif
+  return aResult;
+}
+
+/*!
+  \brief Constructor.
+*/
+PyEvent_Event::PyEvent_Event(){
+  // Prepare the semaphore 
+  mySemaphore = new QSemaphore( NumberOfResources );
+  mySemaphore->acquire( NumberOfResources );
+}
+
+/*!
+  \brief Destructor.
+*/
+PyEvent_Event::~PyEvent_Event(){
+  if ( mySemaphore->available() < NumberOfResources )
+    mySemaphore->release( NumberOfResources - mySemaphore->available() );
+  delete mySemaphore;
+}
+
+/*!
+  \brief This method should be called by the main GUI thread
+  in order to execute the code specific for this event and finally
+  to inform the calling thread that the event 
+  has been processed waking it up with help of the semaphore .
+ */
+void PyEvent_Event::ExecutePostedEvent()
+{
+  // Diagnose incorrect usage of PyEvent_Event API
+  if ( !IsSessionThread() ){
+    qWarning( "PyEvent_Event::ExecutePostedEvent() is called from a secondary thread that might mean an error in application logic!" );
+  }
+  // Actual execution specific for particular kind of event
+  Execute();
+  // Signal the calling thread that the event has been processed
+  processed();
+}
+
+/*!
+  \brief Post the event and wait for its completion.
+  process() should be called from a secondary thread only. 
+  \sa processed()
+*/
+void PyEvent_Event::process()
+{
+  // Diagnose incorrect usage of PyEvent_Event API
+  if ( IsSessionThread() ){
+    qWarning( "PyEvent_Event::process() is called from the main GUI thread that might mean an error in application logic!" );
+  }
+
+  QApplication::postEvent( qApp, new PyEvent_CustomEvent( PyEvent_EVENT, (void*)this ) );
+  mySemaphore->acquire( 1 );
+}
+
+/*!
+  \brief Use this method to signal that this event has been processed.
+*/
+void PyEvent_Event::processed()
+{
+  mySemaphore->release( 1 );
+}
+
+/*!
+  \fn virtual void PyEvent_Event::Execute();
+  \brief This method should be redefined in the successor classes
+         to do real work.
+*/
+  
+/*!
+  \class TMemFunEvent
+  \brief Template class for event which calls the function
+  without arguments and returning result.
+*/
+
+/*!
+  \class TVoidMemFunEvent
+  \brief Template class for event which calls the function
+  without arguments and without return value.
+*/
+
+/*!
+  \class TMemFun1ArgEvent
+  \brief Template class for event which calls the function
+  with one argument and returning result.
+*/
+
+/*!
+  \class TVoidMemFun1ArgEvent
+  \brief Template class for event which calls the function
+  with one argument and without return value.
+*/
+
+/*!
+  \class TMemFun2ArgEvent
+  \brief Template class for event which calls the function
+  with two arguments and returning result.
+*/
+
+/*!
+  \class TVoidMemFun2ArgEvent
+  \brief Template class for event which calls the function
+  with two arguments and without return value.
+*/
+
+/*!
+  \fn ProcessEvent
+  \brief Template function for processing events with return value.
+*/
+
+/*!
+  \fn ProcessVoidEvent
+  \brief Template function for processing events without return value.
+*/
diff --git a/src/PyEvent/PyEvent_Event.cxx b/src/PyEvent/PyEvent_Event.cxx
deleted file mode 100644 (file)
index fef847f..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-
-#include "PyEvent_Event.h"
-
-#include <QSemaphore>
-#include <QApplication>
-
-// asv 21.02.05 : introducing multi-platform approach of thread comparison
-// - on Unix using pthread_t type for storing ThreadId
-// - on Win32 using integer type for storing ThreadId
-// NOT using integer ThreadId on both Unix and Win32 because (from documentation):
-// "...Do not allow your program to rely on the internal structure or size of the pthread_t..."
-
-#ifdef WIN32
-#include <windows.h>
-static DWORD myThread;
-#else
-#include <pthread.h>
-static pthread_t myThread;
-#endif
-
-/*!
-  \class InitEvent
-  \brief Helper event class responsible for initializing PyEvent_Event
-  mechanism by the main thread ID
- */
-class InitEvent : public PyEvent_Event
-{
-public:
-  InitEvent();
-  virtual      ~InitEvent();
-  virtual void Execute();
-};
-
-/*!
-  \brief Constructor, initializes the event mechanism by the current thread ID.
-  It is asssumed to be the main thread ID, so be careful!
-*/
-InitEvent::InitEvent()
-{
-  GetSessionThread();
-}
-
-/*!
-  \brief Destructor, does nothing.
-*/
-InitEvent::~InitEvent()
-{
-}
-
-/*!
-  \brief Nothing to be executed for this kind of event.
-*/
-void InitEvent::Execute()
-{
-}
-
-// NOTE: Here the SALOME event mechanism is initalized by the 
-// current thread ID that is always assumed to be the main thread ID.
-// This should be revised as soon as the application library is no longer
-// linked against the Event library (i.e. this static object is not created or created 
-// outside the main thread).
-static InitEvent myInitEvent;
-
-/*!
-  \class PyEvent_CustomEvent
-  \brief Generic event class for user-defined events
-  
-  This class contains a generic void* data member that may be used
-  for transferring event-specific data to the receiver.
-
-  \warning The internal data is not destroyed by the class destructor.
-*/
-
-/*!
-  \brief Constructor.
-  \param type event type
-*/
-PyEvent_CustomEvent::PyEvent_CustomEvent( int type )
-: QEvent( (QEvent::Type)type ), d( 0 )
-{
-}
-
-/*!
-  \brief Constructor.
-  \param type event type
-  \param data custom data
-*/
-PyEvent_CustomEvent::PyEvent_CustomEvent( QEvent::Type type, void* data )
-: QEvent( type ), d( data )
-{
-}
-
-/*!
-  \brief Get custom data.
-  \return pointer to the internal data
-*/
-void* PyEvent_CustomEvent::data() const
-{
-  return d;
-}
-
-/*!
-  \brief Set custom data.
-  \param data pointer to the internal data
-*/
-void PyEvent_CustomEvent::setData( void* data )
-{
-  d = data;
-}
-
-/*!
-  \class PyEvent_Event
-  \brief The class which encapsulates data and functionality required for 
-         posting component-specific events to perform arbitrary operations 
-         in the main GUI thread. 
-
-  PyEvent_Event objects can be posted by any thread belonging to the GUI process.
-  
-  It is necessary to derive a custom event class from PyEvent_Event and 
-  re-implement virtual Execute() method. This method should actually perform 
-  the desirable operation. To pass all the required data to Execute() and 
-  store a return value, arbitrary data fields can be added to the custom 
-  event class. There is no need to protect such fields with a mutex, for only
-  one thread working with a PyEvent_Event object is active at any moment.
-  
-  Usage:
-  - Create PyEvent_Event. Components can derive their own event class from 
-  PyEvent_Event in order to pass custom data to the event handler.
-  - Call process() method to post the event. After process() execution
-  it is possible to examine fields of your custom event object.
-  - Perform delete operator on the event to wake up the desktop (you can also 
-  set <autoRelease>  parameter to \c true to automatically wake up desktop after 
-  process().
-  
-  The method processed() is used by the desktop to signal that event processing 
-  has been completed.
-
-  To make all this work, it is necessary to call static method GetSessionThread()
-  during the application initialization, i.e. from main() function.
-  It is important to call this method from the primary application thread.
-
-  Caveats: 
-  - there are no.
-*/
-
-//! Total number of semaphore resources
-const int NumberOfResources = 2;
-
-/*!
-  \brief Initialize event mechanism.
-
-  This function sets up the main application thread. It should be called
-  during the application initialization, i.e. main() function.
-*/
-void PyEvent_Event::GetSessionThread(){
-#ifdef WIN32
-  myThread = ::GetCurrentThreadId();
-#else
-  myThread = pthread_self();
-#endif
-}
-
-/*!
-  \brief Check if the processing is in the main application thread.
-  \return \c true if this method is called from the main application thread
-*/
-bool PyEvent_Event::IsSessionThread(){
-  bool aResult = false;
-#ifdef WIN32
-  aResult = myThread == ::GetCurrentThreadId();
-#else
-  aResult = myThread == pthread_self();
-#endif
-  return aResult;
-}
-
-/*!
-  \brief Constructor.
-*/
-PyEvent_Event::PyEvent_Event(){
-  // Prepare the semaphore 
-  mySemaphore = new QSemaphore( NumberOfResources );
-  mySemaphore->acquire( NumberOfResources );
-}
-
-/*!
-  \brief Destructor.
-*/
-PyEvent_Event::~PyEvent_Event(){
-  if ( mySemaphore->available() < NumberOfResources )
-    mySemaphore->release( NumberOfResources - mySemaphore->available() );
-  delete mySemaphore;
-}
-
-/*!
-  \brief This method should be called by the main GUI thread
-  in order to execute the code specific for this event and finally
-  to inform the calling thread that the event 
-  has been processed waking it up with help of the semaphore .
- */
-void PyEvent_Event::ExecutePostedEvent()
-{
-  // Diagnose incorrect usage of PyEvent_Event API
-  if ( !IsSessionThread() ){
-    qWarning( "PyEvent_Event::ExecutePostedEvent() is called from a secondary thread that might mean an error in application logic!" );
-  }
-  // Actual execution specific for particular kind of event
-  Execute();
-  // Signal the calling thread that the event has been processed
-  processed();
-}
-
-/*!
-  \brief Post the event and wait for its completion.
-  process() should be called from a secondary thread only. 
-  \sa processed()
-*/
-void PyEvent_Event::process()
-{
-  // Diagnose incorrect usage of PyEvent_Event API
-  if ( IsSessionThread() ){
-    qWarning( "PyEvent_Event::process() is called from the main GUI thread that might mean an error in application logic!" );
-  }
-
-  QApplication::postEvent( qApp, new PyEvent_CustomEvent( PyEvent_EVENT, (void*)this ) );
-  mySemaphore->acquire( 1 );
-}
-
-/*!
-  \brief Use this method to signal that this event has been processed.
-*/
-void PyEvent_Event::processed()
-{
-  mySemaphore->release( 1 );
-}
-
-/*!
-  \fn virtual void PyEvent_Event::Execute();
-  \brief This method should be redefined in the successor classes
-         to do real work.
-*/
-  
-/*!
-  \class TMemFunEvent
-  \brief Template class for event which calls the function
-  without arguments and returning result.
-*/
-
-/*!
-  \class TVoidMemFunEvent
-  \brief Template class for event which calls the function
-  without arguments and without return value.
-*/
-
-/*!
-  \class TMemFun1ArgEvent
-  \brief Template class for event which calls the function
-  with one argument and returning result.
-*/
-
-/*!
-  \class TVoidMemFun1ArgEvent
-  \brief Template class for event which calls the function
-  with one argument and without return value.
-*/
-
-/*!
-  \class TMemFun2ArgEvent
-  \brief Template class for event which calls the function
-  with two arguments and returning result.
-*/
-
-/*!
-  \class TVoidMemFun2ArgEvent
-  \brief Template class for event which calls the function
-  with two arguments and without return value.
-*/
-
-/*!
-  \fn ProcessEvent
-  \brief Template function for processing events with return value.
-*/
-
-/*!
-  \fn ProcessVoidEvent
-  \brief Template function for processing events without return value.
-*/
diff --git a/src/PyEvent/PyEvent_EventFilter.cpp b/src/PyEvent/PyEvent_EventFilter.cpp
new file mode 100644 (file)
index 0000000..ac12e20
--- /dev/null
@@ -0,0 +1,60 @@
+
+#include "PyEvent_EventFilter.h"
+#include "PyEvent_Event.h"
+
+#include <QApplication>
+
+PyEvent_EventFilter* PyEvent_EventFilter::myFilter = NULL;
+
+/*!Constructor.*/
+PyEvent_EventFilter::PyEvent_EventFilter()
+: QObject()
+{
+  /* VSR 13/01/03 : installing global event filter for the application */
+  qApp->installEventFilter( this );
+}
+
+/*!Destructor.*/
+PyEvent_EventFilter::~PyEvent_EventFilter()
+{
+  qApp->removeEventFilter( this );
+}
+
+/*!
+  Custom event filter
+*/
+bool PyEvent_EventFilter::eventFilter( QObject* o, QEvent* e )
+{
+  if ( e->type() == PyEvent_EVENT )
+  { 
+    PyEvent_Event* aSE = (PyEvent_Event*)((PyEvent_CustomEvent*)e)->data();
+    processEvent(aSE);
+    ((PyEvent_CustomEvent*)e)->setData( 0 );
+    return true;
+  }
+  return QObject::eventFilter( o, e );
+}
+
+/*!Process event.*/
+void PyEvent_EventFilter::processEvent( PyEvent_Event* theEvent )
+{
+  if(theEvent)
+    theEvent->ExecutePostedEvent();
+}
+
+/*!Create new instance of PyEvent_EventFilter*/
+void PyEvent_EventFilter::Init()
+{
+  if( myFilter==NULL )
+    myFilter = new PyEvent_EventFilter();
+}
+
+/*!Destroy filter.*/
+void PyEvent_EventFilter::Destroy()
+{
+  if( myFilter )
+  {
+    delete myFilter;
+    myFilter = NULL;
+  }
+}
diff --git a/src/PyEvent/PyEvent_EventFilter.cxx b/src/PyEvent/PyEvent_EventFilter.cxx
deleted file mode 100644 (file)
index ac12e20..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-
-#include "PyEvent_EventFilter.h"
-#include "PyEvent_Event.h"
-
-#include <QApplication>
-
-PyEvent_EventFilter* PyEvent_EventFilter::myFilter = NULL;
-
-/*!Constructor.*/
-PyEvent_EventFilter::PyEvent_EventFilter()
-: QObject()
-{
-  /* VSR 13/01/03 : installing global event filter for the application */
-  qApp->installEventFilter( this );
-}
-
-/*!Destructor.*/
-PyEvent_EventFilter::~PyEvent_EventFilter()
-{
-  qApp->removeEventFilter( this );
-}
-
-/*!
-  Custom event filter
-*/
-bool PyEvent_EventFilter::eventFilter( QObject* o, QEvent* e )
-{
-  if ( e->type() == PyEvent_EVENT )
-  { 
-    PyEvent_Event* aSE = (PyEvent_Event*)((PyEvent_CustomEvent*)e)->data();
-    processEvent(aSE);
-    ((PyEvent_CustomEvent*)e)->setData( 0 );
-    return true;
-  }
-  return QObject::eventFilter( o, e );
-}
-
-/*!Process event.*/
-void PyEvent_EventFilter::processEvent( PyEvent_Event* theEvent )
-{
-  if(theEvent)
-    theEvent->ExecutePostedEvent();
-}
-
-/*!Create new instance of PyEvent_EventFilter*/
-void PyEvent_EventFilter::Init()
-{
-  if( myFilter==NULL )
-    myFilter = new PyEvent_EventFilter();
-}
-
-/*!Destroy filter.*/
-void PyEvent_EventFilter::Destroy()
-{
-  if( myFilter )
-  {
-    delete myFilter;
-    myFilter = NULL;
-  }
-}
index 6373f65efa15354a80ce3d852cdd5fa833913761..b540f098bd2e19c09bedc6e8892e3466bcd3f619 100644 (file)
@@ -17,10 +17,10 @@ SET(PROJECT_AUTOMOC
 
 # sources / static
 SET(PROJECT_SOURCES
-  PyInterp_Dispatcher.cxx
-  PyInterp_Event.cxx
-  PyInterp_Interp.cxx
-  PyInterp_Request.cxx
+  PyInterp_Dispatcher.cpp
+  PyInterp_Event.cpp
+  PyInterp_Interp.cpp
+  PyInterp_Request.cpp
 )
 
 SET(PROJECT_LIBRARIES
diff --git a/src/PyInterp/PyInterp_Dispatcher.cpp b/src/PyInterp/PyInterp_Dispatcher.cpp
new file mode 100644 (file)
index 0000000..1999e71
--- /dev/null
@@ -0,0 +1,199 @@
+
+#include "PyInterp_Dispatcher.h"   // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
+#include "PyInterp_Interp.h"
+#include "PyInterp_Watcher.h"
+#include "PyInterp_Request.h"
+
+#include <QObject>
+#include <QCoreApplication>
+
+PyInterp_Dispatcher* PyInterp_Dispatcher::myInstance = 0;
+
+void PyInterp_Request::process()
+{
+  safeExecute();
+
+  bool isSync = IsSync();
+
+  if ( !isSync )
+    myMutex.lock();
+
+  if ( listener() )
+    processEvent( listener() );
+
+  if ( !isSync )
+    myMutex.unlock();
+}
+
+void PyInterp_Request::safeExecute()
+{
+  //ProcessVoidEvent( new PyInterp_ExecuteEvent( this ) );
+  execute();
+}
+
+void PyInterp_Request::Destroy( PyInterp_Request* request )
+{
+  // Lock and unlock the mutex to avoid errors on its deletion
+  request->myMutex.lock();
+  request->myMutex.unlock();
+  delete request;
+}
+
+QEvent* PyInterp_Request::createEvent()
+{
+  return new PyInterp_Event( PyInterp_Event::ES_NOTIFY, this );
+}
+
+void PyInterp_Request::processEvent( QObject* o )
+{
+  if ( !o )
+    return;
+
+  QEvent* e = createEvent();
+  if ( !e )
+    return;
+
+  if ( !IsSync() )
+    QCoreApplication::postEvent( o, e );
+  else
+  {
+    QCoreApplication::sendEvent( o, e );
+    delete e;
+  }
+}
+
+void PyInterp_Request::setListener( QObject* o )
+{
+  myMutex.lock();
+  myListener = o;
+  myMutex.unlock();
+}
+
+void PyInterp_LockRequest::safeExecute()
+{
+  if ( getInterp() ){
+    PyLockWrapper aLock = getInterp()->GetLockWrapper();
+    //ProcessVoidEvent( new PyInterp_ExecuteEvent( this ) );
+    execute();
+  }
+}
+
+PyInterp_Event::~PyInterp_Event()
+{
+  PyInterp_Request::Destroy( myRequest );
+  myRequest = 0;
+}
+
+PyInterp_Dispatcher* PyInterp_Dispatcher::Get()
+{
+  if ( !myInstance )
+    myInstance = new PyInterp_Dispatcher();
+  return myInstance;
+}
+
+PyInterp_Dispatcher::PyInterp_Dispatcher() 
+: QThread()
+{
+  myWatcher = new PyInterp_Watcher();
+}
+
+PyInterp_Dispatcher::~PyInterp_Dispatcher()
+{
+  // Clear the request queue
+  myQueueMutex.lock();
+
+  QListIterator<RequestPtr> it( myQueue );
+  while ( it.hasNext() )
+    PyInterp_Request::Destroy( it.next() );
+  myQueue.clear();
+
+  myQueueMutex.unlock();
+
+  // Wait for run() to finish
+  wait();
+
+  delete myWatcher;
+  myWatcher = 0;
+}
+
+bool PyInterp_Dispatcher::IsBusy() const
+{
+  return isRunning();
+}
+
+void PyInterp_Dispatcher::Exec( PyInterp_Request* theRequest )
+{
+  if ( !theRequest )
+    return;
+
+  //if ( theRequest->IsSync() && !IsBusy() ) // synchronous processing - nothing is done if dispatcher is busy!
+  if ( theRequest->IsSync() ) // synchronous processing
+    processRequest( theRequest );
+  else // asynchronous processing
+  {
+    myQueueMutex.lock();
+    myQueue.enqueue( theRequest );
+    if ( theRequest->listener() )
+      QObject::connect( theRequest->listener(), SIGNAL( destroyed( QObject* ) ), myWatcher, SLOT( onDestroyed( QObject* ) ) );
+    myQueueMutex.unlock();  
+
+    if ( !IsBusy() )
+      start();
+  }
+}
+
+void PyInterp_Dispatcher::run()
+{
+//  MESSAGE("*** PyInterp_Dispatcher::run(): STARTED")
+  PyInterp_Request* aRequest;
+
+  // prepare for queue size check
+  myQueueMutex.lock();
+
+  while( myQueue.size() ) {
+//    MESSAGE("*** PyInterp_Dispatcher::run(): next request taken from the queue")
+    aRequest = myQueue.head();
+
+    // let other threads append their requests to the end of the queue
+    myQueueMutex.unlock();
+
+    // processRequest() may delete a request, so this pointer must not be used
+    // after request is processed!
+    processRequest( aRequest );
+
+    // prepare for removal of the first request in the queue
+    myQueueMutex.lock();
+    // IMPORTANT: the first item could have been removed by objectDestroyed() --> we have to check it
+    if ( myQueue.head() == aRequest ) // It's still here --> remove it
+      myQueue.dequeue();
+
+//    MESSAGE("*** PyInterp_Dispatcher::run(): request processed")
+  }
+
+  myQueueMutex.unlock();
+//  MESSAGE("*** PyInterp_Dispatcher::run(): FINISHED")
+}
+
+void PyInterp_Dispatcher::processRequest( PyInterp_Request* theRequest )
+{
+  theRequest->process();
+}
+
+void PyInterp_Dispatcher::objectDestroyed( const QObject* o )
+{
+  // prepare for modification of the queue
+  myQueueMutex.lock();
+
+  QMutableListIterator<RequestPtr> it( myQueue );
+  while ( it.hasNext() )
+  {
+    RequestPtr r = it.next();
+    if ( o == r->listener() )
+    {
+      r->setListener( 0 ); // to prevent event posting
+      it.remove();
+    }
+  }
+
+  myQueueMutex.unlock();
+}
diff --git a/src/PyInterp/PyInterp_Dispatcher.cxx b/src/PyInterp/PyInterp_Dispatcher.cxx
deleted file mode 100644 (file)
index 1999e71..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-
-#include "PyInterp_Dispatcher.h"   // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
-#include "PyInterp_Interp.h"
-#include "PyInterp_Watcher.h"
-#include "PyInterp_Request.h"
-
-#include <QObject>
-#include <QCoreApplication>
-
-PyInterp_Dispatcher* PyInterp_Dispatcher::myInstance = 0;
-
-void PyInterp_Request::process()
-{
-  safeExecute();
-
-  bool isSync = IsSync();
-
-  if ( !isSync )
-    myMutex.lock();
-
-  if ( listener() )
-    processEvent( listener() );
-
-  if ( !isSync )
-    myMutex.unlock();
-}
-
-void PyInterp_Request::safeExecute()
-{
-  //ProcessVoidEvent( new PyInterp_ExecuteEvent( this ) );
-  execute();
-}
-
-void PyInterp_Request::Destroy( PyInterp_Request* request )
-{
-  // Lock and unlock the mutex to avoid errors on its deletion
-  request->myMutex.lock();
-  request->myMutex.unlock();
-  delete request;
-}
-
-QEvent* PyInterp_Request::createEvent()
-{
-  return new PyInterp_Event( PyInterp_Event::ES_NOTIFY, this );
-}
-
-void PyInterp_Request::processEvent( QObject* o )
-{
-  if ( !o )
-    return;
-
-  QEvent* e = createEvent();
-  if ( !e )
-    return;
-
-  if ( !IsSync() )
-    QCoreApplication::postEvent( o, e );
-  else
-  {
-    QCoreApplication::sendEvent( o, e );
-    delete e;
-  }
-}
-
-void PyInterp_Request::setListener( QObject* o )
-{
-  myMutex.lock();
-  myListener = o;
-  myMutex.unlock();
-}
-
-void PyInterp_LockRequest::safeExecute()
-{
-  if ( getInterp() ){
-    PyLockWrapper aLock = getInterp()->GetLockWrapper();
-    //ProcessVoidEvent( new PyInterp_ExecuteEvent( this ) );
-    execute();
-  }
-}
-
-PyInterp_Event::~PyInterp_Event()
-{
-  PyInterp_Request::Destroy( myRequest );
-  myRequest = 0;
-}
-
-PyInterp_Dispatcher* PyInterp_Dispatcher::Get()
-{
-  if ( !myInstance )
-    myInstance = new PyInterp_Dispatcher();
-  return myInstance;
-}
-
-PyInterp_Dispatcher::PyInterp_Dispatcher() 
-: QThread()
-{
-  myWatcher = new PyInterp_Watcher();
-}
-
-PyInterp_Dispatcher::~PyInterp_Dispatcher()
-{
-  // Clear the request queue
-  myQueueMutex.lock();
-
-  QListIterator<RequestPtr> it( myQueue );
-  while ( it.hasNext() )
-    PyInterp_Request::Destroy( it.next() );
-  myQueue.clear();
-
-  myQueueMutex.unlock();
-
-  // Wait for run() to finish
-  wait();
-
-  delete myWatcher;
-  myWatcher = 0;
-}
-
-bool PyInterp_Dispatcher::IsBusy() const
-{
-  return isRunning();
-}
-
-void PyInterp_Dispatcher::Exec( PyInterp_Request* theRequest )
-{
-  if ( !theRequest )
-    return;
-
-  //if ( theRequest->IsSync() && !IsBusy() ) // synchronous processing - nothing is done if dispatcher is busy!
-  if ( theRequest->IsSync() ) // synchronous processing
-    processRequest( theRequest );
-  else // asynchronous processing
-  {
-    myQueueMutex.lock();
-    myQueue.enqueue( theRequest );
-    if ( theRequest->listener() )
-      QObject::connect( theRequest->listener(), SIGNAL( destroyed( QObject* ) ), myWatcher, SLOT( onDestroyed( QObject* ) ) );
-    myQueueMutex.unlock();  
-
-    if ( !IsBusy() )
-      start();
-  }
-}
-
-void PyInterp_Dispatcher::run()
-{
-//  MESSAGE("*** PyInterp_Dispatcher::run(): STARTED")
-  PyInterp_Request* aRequest;
-
-  // prepare for queue size check
-  myQueueMutex.lock();
-
-  while( myQueue.size() ) {
-//    MESSAGE("*** PyInterp_Dispatcher::run(): next request taken from the queue")
-    aRequest = myQueue.head();
-
-    // let other threads append their requests to the end of the queue
-    myQueueMutex.unlock();
-
-    // processRequest() may delete a request, so this pointer must not be used
-    // after request is processed!
-    processRequest( aRequest );
-
-    // prepare for removal of the first request in the queue
-    myQueueMutex.lock();
-    // IMPORTANT: the first item could have been removed by objectDestroyed() --> we have to check it
-    if ( myQueue.head() == aRequest ) // It's still here --> remove it
-      myQueue.dequeue();
-
-//    MESSAGE("*** PyInterp_Dispatcher::run(): request processed")
-  }
-
-  myQueueMutex.unlock();
-//  MESSAGE("*** PyInterp_Dispatcher::run(): FINISHED")
-}
-
-void PyInterp_Dispatcher::processRequest( PyInterp_Request* theRequest )
-{
-  theRequest->process();
-}
-
-void PyInterp_Dispatcher::objectDestroyed( const QObject* o )
-{
-  // prepare for modification of the queue
-  myQueueMutex.lock();
-
-  QMutableListIterator<RequestPtr> it( myQueue );
-  while ( it.hasNext() )
-  {
-    RequestPtr r = it.next();
-    if ( o == r->listener() )
-    {
-      r->setListener( 0 ); // to prevent event posting
-      it.remove();
-    }
-  }
-
-  myQueueMutex.unlock();
-}
diff --git a/src/PyInterp/PyInterp_Event.cpp b/src/PyInterp/PyInterp_Event.cpp
new file mode 100644 (file)
index 0000000..524d06a
--- /dev/null
@@ -0,0 +1,9 @@
+
+
+#include "PyInterp_Event.h"
+#include "PyInterp_Request.h"
+
+void PyInterp_ExecuteEvent::Execute()
+{
+  myRequest->execute();
+}
diff --git a/src/PyInterp/PyInterp_Event.cxx b/src/PyInterp/PyInterp_Event.cxx
deleted file mode 100644 (file)
index 524d06a..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-#include "PyInterp_Event.h"
-#include "PyInterp_Request.h"
-
-void PyInterp_ExecuteEvent::Execute()
-{
-  myRequest->execute();
-}
diff --git a/src/PyInterp/PyInterp_Interp.cpp b/src/PyInterp/PyInterp_Interp.cpp
new file mode 100644 (file)
index 0000000..7e633b5
--- /dev/null
@@ -0,0 +1,533 @@
+
+#include "PyInterp_Interp.h"  // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
+#include <pythread.h>
+
+#include <cStringIO.h>
+#include <structmember.h>
+
+#include <string>
+#include <vector>
+#include <map>
+#include <iostream>
+#include <algorithm>
+
+#define TOP_HISTORY_PY   "--- top of history ---"
+#define BEGIN_HISTORY_PY "--- begin of history ---"
+
+// a map to store python thread states that have been created for a given system thread (key=thread id,value=thread state)
+std::map<long,PyThreadState*> currentThreadMap;
+
+/*!
+  \class PyLockWrapper
+  \brief Python GIL wrapper.
+*/
+
+/*!
+  \brief Constructor. Automatically acquires GIL.
+  \param theThreadState python thread state
+*/
+PyLockWrapper::PyLockWrapper(PyThreadState* theThreadState):
+  myThreadState(theThreadState),
+  mySaveThreadState(0)
+{
+  if (myThreadState->interp == PyInterp_Interp::_interp)
+    _savestate = PyGILState_Ensure();
+  else
+    PyEval_AcquireThread(myThreadState);
+}
+
+/*!
+  \brief Destructor. Automatically releases GIL.
+*/
+PyLockWrapper::~PyLockWrapper()
+{
+  if (myThreadState->interp == PyInterp_Interp::_interp)
+    PyGILState_Release(_savestate);
+  else
+    PyEval_ReleaseThread(myThreadState);
+}
+
+/*!
+  \brief Get Python GIL wrapper.
+  \return GIL lock wrapper (GIL is automatically acquired here)
+*/
+PyLockWrapper PyInterp_Interp::GetLockWrapper()
+{
+  if (_tstate->interp == PyInterp_Interp::_interp)
+    return _tstate;
+
+  // If we are here, we have a secondary python interpreter. Try to get a thread state synchronized with the system thread
+  long currentThreadid=PyThread_get_thread_ident(); // the system thread id
+  PyThreadState* theThreadState;
+  if(currentThreadMap.count(currentThreadid) != 0)
+    {
+      //a thread state exists for this thread id
+      PyThreadState* oldThreadState=currentThreadMap[currentThreadid];
+      if(_tstate->interp ==oldThreadState->interp)
+        {
+          //The old thread state has the same python interpreter as this one : reuse the threadstate
+          theThreadState=oldThreadState;
+        }
+      else
+        {
+          //The old thread state has not the same python interpreter as this one : delete the old threadstate and create a new one
+          PyEval_AcquireLock();
+          PyThreadState_Clear(oldThreadState);
+          PyThreadState_Delete(oldThreadState);
+          PyEval_ReleaseLock();
+          theThreadState=PyThreadState_New(_tstate->interp);
+          currentThreadMap[currentThreadid]=theThreadState;
+        }
+    }
+  else
+    {
+      // no old thread state for this thread id : create a new one
+      theThreadState=PyThreadState_New(_tstate->interp);
+      currentThreadMap[currentThreadid]=theThreadState;
+    }
+  return theThreadState;
+}
+
+/*
+  The following functions are used to hook the Python
+  interpreter output.
+*/
+
+static void
+PyStdOut_dealloc(PyStdOut *self)
+{
+  PyObject_Del(self);
+}
+
+static PyObject*
+PyStdOut_write(PyStdOut *self, PyObject *args)
+{
+  char *c;
+  int l;
+  if (!PyArg_ParseTuple(args, "t#:write",&c, &l))
+    return NULL;
+  if(self->_cb==NULL) {
+    if ( self->_iscerr )
+      std::cerr << c ;
+    else
+      std::cout << c ;
+  }
+  else {
+    self->_cb(self->_data,c);
+  }
+  Py_INCREF(Py_None);
+  return Py_None;
+}
+
+static PyObject*
+PyStdOut_flush(PyStdOut *self)
+{
+  Py_INCREF(Py_None);
+  return Py_None;
+}
+
+static PyMethodDef PyStdOut_methods[] = {
+  {"write",  (PyCFunction)PyStdOut_write,  METH_VARARGS, PyDoc_STR("write(string) -> None")},
+  {"flush",  (PyCFunction)PyStdOut_flush,  METH_NOARGS,  PyDoc_STR("flush() -> None")},
+  {NULL,    NULL}   /* sentinel */
+};
+
+static PyMemberDef PyStdOut_memberlist[] = {
+  {(char*)"softspace", T_INT,  offsetof(PyStdOut, softspace), 0,
+   (char*)"flag indicating that a space needs to be printed; used by print"},
+  {NULL} /* Sentinel */
+};
+
+static PyTypeObject PyStdOut_Type = {
+  /* The ob_type field must be initialized in the module init function
+   * to be portable to Windows without using C++. */
+  PyObject_HEAD_INIT(NULL)
+  0,                            /*ob_size*/
+  "PyOut",                      /*tp_name*/
+  sizeof(PyStdOut),             /*tp_basicsize*/
+  0,                            /*tp_itemsize*/
+  /* methods */
+  (destructor)PyStdOut_dealloc, /*tp_dealloc*/
+  0,                            /*tp_print*/
+  0,                            /*tp_getattr*/
+  0,                            /*tp_setattr*/
+  0,                            /*tp_compare*/
+  0,                            /*tp_repr*/
+  0,                            /*tp_as_number*/
+  0,                            /*tp_as_sequence*/
+  0,                            /*tp_as_mapping*/
+  0,                            /*tp_hash*/
+  0,                            /*tp_call*/
+  0,                            /*tp_str*/
+  PyObject_GenericGetAttr,      /*tp_getattro*/
+  /* softspace is writable:  we must supply tp_setattro */
+  PyObject_GenericSetAttr,      /* tp_setattro */
+  0,                            /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT,           /*tp_flags*/
+  0,                            /*tp_doc*/
+  0,                            /*tp_traverse*/
+  0,                            /*tp_clear*/
+  0,                            /*tp_richcompare*/
+  0,                            /*tp_weaklistoffset*/
+  0,                            /*tp_iter*/
+  0,                            /*tp_iternext*/
+  PyStdOut_methods,             /*tp_methods*/
+  PyStdOut_memberlist,          /*tp_members*/
+  0,                            /*tp_getset*/
+  0,                            /*tp_base*/
+  0,                            /*tp_dict*/
+  0,                            /*tp_descr_get*/
+  0,                            /*tp_descr_set*/
+  0,                            /*tp_dictoffset*/
+  0,                            /*tp_init*/
+  0,                            /*tp_alloc*/
+  0,                            /*tp_new*/
+  0,                            /*tp_free*/
+  0,                            /*tp_is_gc*/
+};
+
+#define PyStdOut_Check(v)  ((v)->ob_type == &PyStdOut_Type)
+
+static PyStdOut* newPyStdOut( bool iscerr )
+{
+  PyStdOut *self;
+  self = PyObject_New(PyStdOut, &PyStdOut_Type);
+  if (self == NULL)
+    return NULL;
+  self->softspace = 0;
+  self->_cb = NULL;
+  self->_iscerr = iscerr;
+  return self;
+}
+
+/*!
+  \class PyInterp_Interp
+  \brief Generic embedded Python interpreter.
+*/
+
+int   PyInterp_Interp::_argc   = 1;
+char* PyInterp_Interp::_argv[] = {(char*)""};
+PyObject*           PyInterp_Interp::builtinmodule = NULL;
+PyThreadState*      PyInterp_Interp::_gtstate      = NULL;
+PyInterpreterState* PyInterp_Interp::_interp       = NULL;
+
+/*!
+  \brief Basic constructor.
+
+  After construction the interpreter instance successor classes
+  must call virtual method initalize().
+*/
+PyInterp_Interp::PyInterp_Interp():
+  _tstate(0), _vout(0), _verr(0), _g(0)
+{
+}
+
+/*!
+  \brief Destructor.
+*/
+PyInterp_Interp::~PyInterp_Interp()
+{
+}
+
+/*!
+  \brief Initialize embedded interpreter.
+
+  This method shoud be called after construction of the interpreter.
+  The method initialize() calls virtuals methods
+  - initPython()  to initialize global Python interpreter
+  - initState()   to initialize embedded interpreter state
+  - initContext() to initialize interpreter internal context
+  - initRun()     to prepare interpreter for running commands
+  which should be implemented in the successor classes, according to the
+  embedded Python interpreter policy (mono or multi interpreter, etc).
+*/
+void PyInterp_Interp::initialize()
+{
+  _history.clear();       // start a new list of user's commands
+  _ith = _history.begin();
+
+  initPython();
+  // Here the global lock is released
+
+  initState();
+
+  PyEval_AcquireThread(_tstate);
+
+  initContext();
+
+  // used to interpret & compile commands
+  PyObjWrapper m(PyImport_ImportModule("codeop"));
+  if(!m) {
+    PyErr_Print();
+    PyEval_ReleaseThread(_tstate);
+    return;
+  }
+
+  // Create python objects to capture stdout and stderr
+  _vout=(PyObject*)newPyStdOut( false ); // stdout
+  _verr=(PyObject*)newPyStdOut( true );  // stderr
+
+  // All the initRun outputs are redirected to the standard output (console)
+  initRun();
+  PyEval_ReleaseThread(_tstate);
+}
+
+/*!
+  \brief Initialize Python interpreter.
+
+  In case if Python is not initialized, it sets program name, initializes the interpreter, sets program arguments,
+  initializes threads.
+  Otherwise, it just obtains the global interpreter and thread states. This is important for light SALOME configuration,
+  as in full SALOME this is done at SalomeApp level.
+  \sa SalomeApp_PyInterp class
+ */
+void PyInterp_Interp::initPython()
+{
+  if (!Py_IsInitialized()){
+    // Python is not initialized
+    Py_SetProgramName(_argv[0]);
+    Py_Initialize(); // Initialize the interpreter
+    PySys_SetArgv(_argc, _argv);
+    PyEval_InitThreads(); // Create (and acquire) the interpreter lock
+  }
+
+  if ( _interp == NULL )
+    _interp = PyThreadState_Get()->interp;
+  if (PyType_Ready(&PyStdOut_Type) < 0) {
+    PyErr_Print();
+  }
+  if ( _gtstate == NULL )
+    _gtstate = PyEval_SaveThread(); // Release global thread state
+}
+
+/*!
+  \brief Get embedded Python interpreter banner.
+  \return banner string
+ */
+std::string PyInterp_Interp::getbanner()
+{
+ // Should we take the lock ?
+ // PyEval_RestoreThread(_tstate);
+  std::string aBanner("Python ");
+  aBanner = aBanner + Py_GetVersion() + " on " + Py_GetPlatform() ;
+  aBanner = aBanner + "\ntype help to get general information on environment\n";
+  //PyEval_SaveThread();
+  return aBanner;
+}
+
+/*!
+  \brief Initialize run command.
+
+  This method is used to prepare interpreter for running
+  Python commands.
+
+  \return \c true on success and \c false on error
+*/
+bool PyInterp_Interp::initRun()
+{
+  //
+  // probably all below code isn't required
+  //
+  /*
+  PySys_SetObject("stderr",_verr);
+  PySys_SetObject("stdout",_vout);
+
+  //PyObject *m = PyImport_GetModuleDict();
+
+  PySys_SetObject("stdout",PySys_GetObject("__stdout__"));
+  PySys_SetObject("stderr",PySys_GetObject("__stderr__"));
+  */
+  return true;
+}
+
+/*!
+  \brief Compile Python command and evaluate it in the
+         python dictionary context if possible.
+  \internal
+  \param command Python command string
+  \param context Python context (dictionary)
+  \return -1 on fatal error, 1 if command is incomplete and 0
+         if command is executed successfully
+ */
+static int run_command(const char *command, PyObject *context)
+{
+  PyObject *m = PyImport_AddModule("codeop");
+  if(!m) { // Fatal error. No way to go on.
+    PyErr_Print();
+    return -1;
+  }
+  PyObjWrapper v(PyObject_CallMethod(m,(char*)"compile_command",(char*)"s",command));
+  if(!v) {
+    // Error encountered. It should be SyntaxError,
+    //so we don't write out traceback
+    PyObjWrapper exception, value, tb;
+    PyErr_Fetch(&exception, &value, &tb);
+    PyErr_NormalizeException(&exception, &value, &tb);
+    PyErr_Display(exception, value, NULL);
+    return -1;
+  }
+  else if (v == Py_None) {
+    // Incomplete text we return 1 : we need a complete text to execute
+    return 1;
+  }
+  else {
+    // Complete and correct text. We evaluate it.
+    //#if PY_VERSION_HEX < 0x02040000 // python version earlier than 2.4.0
+    //    PyObjWrapper r(PyEval_EvalCode(v,context,context));
+    //#else
+    PyObjWrapper r(PyEval_EvalCode((PyCodeObject *)(void *)v,context,context));
+    //#endif
+    if(!r) {
+      // Execution error. We return -1
+      PyErr_Print();
+      return -1;
+    }
+    // The command has been successfully executed. Return 0
+    return 0;
+  }
+}
+
+void replaceAll(std::string& str, const std::string& from, const std::string& to) {
+    if(from.empty())
+        return;
+    size_t start_pos = 0;
+    while((start_pos = str.find(from, start_pos)) != std::string::npos) {
+        str.replace(start_pos, from.length(), to);
+        start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
+    }
+}
+/*!
+  \brief Compile Python command and evaluate it in the
+         python dictionary context if possible. Command might correspond to
+         the execution of a script with optional arguments.
+         In this case, command is:
+         execfile(r"/absolute/path/to/script.py [args:arg1,...,argn]")
+  \internal
+  \param command Python command string
+  \param context Python context (dictionary)
+  \return -1 on fatal error, 1 if command is incomplete and 0
+         if command is executed successfully
+ */
+static int compile_command(const char *command,PyObject *context)
+{
+  // First guess if command is execution of a script with args, or a simple Python command
+  std::string singleCommand = command;
+  std::string commandArgs = "";
+
+  std::size_t pos = std::string(command).find("args:");
+  if (pos != std::string::npos) {
+    commandArgs = singleCommand.substr(pos+5);
+    commandArgs = commandArgs.substr(0, commandArgs.length()-3);
+    singleCommand = singleCommand.substr(0, pos-1)+"\")";
+  }
+
+  if (commandArgs.empty()) {
+    // process command: expression
+    // process command: execfile(r"/absolute/path/to/script.py") (no args)
+    return run_command(singleCommand.c_str(), context);
+  }
+  else {
+    // process command: execfile(r"/absolute/path/to/script.py [args:arg1,...,argn]")
+    std::string script = singleCommand.substr(11); // remove leading execfile(r"
+    script = script.substr(0, script.length()-2); // remove trailing ")
+
+    std::string preCommandBegin = "import sys; save_argv = sys.argv; sys.argv=[";
+    std::string preCommandEnd = "];";
+    replaceAll(commandArgs, ",", "\",\"");
+    commandArgs = "\""+commandArgs+"\"";
+    std::string completeCommand = preCommandBegin+"\""+script+"\","+commandArgs+preCommandEnd+singleCommand+";sys.argv=save_argv";
+    return run_command(completeCommand.c_str(), context);
+  }
+}
+
+/*!
+  \brief Run Python command.
+  \param command Python command
+  \return command status
+*/
+int PyInterp_Interp::run(const char *command)
+{
+  beforeRun();
+  return simpleRun(command);
+}
+
+/*!
+  \brief Run Python command (used internally).
+  \param command Python command
+  \param addToHistory if \c true (default), the command is added to the commands history
+  \return command status
+*/
+int PyInterp_Interp::simpleRun(const char *command, const bool addToHistory)
+{
+  if( addToHistory && strcmp(command,"") != 0 ) {
+    _history.push_back(command);
+    _ith = _history.end();
+  }
+
+  // We come from C++ to enter Python world
+  // We need to acquire the Python global lock
+  //PyLockWrapper aLock(_tstate); // san - lock is centralized now
+
+  // Reset redirected outputs before treatment
+  PySys_SetObject((char*)"stderr",_verr);
+  PySys_SetObject((char*)"stdout",_vout);
+
+  int ier = compile_command(command,_g);
+
+  // Outputs are redirected on standards outputs (console)
+  PySys_SetObject((char*)"stdout",PySys_GetObject((char*)"__stdout__"));
+  PySys_SetObject((char*)"stderr",PySys_GetObject((char*)"__stderr__"));
+
+  return ier;
+}
+
+/*!
+  \brief Get previous command in the commands history.
+  \return previous command
+*/
+const char * PyInterp_Interp::getPrevious()
+{
+  if(_ith != _history.begin()){
+    _ith--;
+    return (*_ith).c_str();
+  }
+  else
+    return BEGIN_HISTORY_PY;
+}
+
+/*!
+  \brief Get next command in the commands history.
+  \return next command
+*/
+const char * PyInterp_Interp::getNext()
+{
+  if(_ith != _history.end()){
+    _ith++;
+  }
+  if (_ith == _history.end())
+    return TOP_HISTORY_PY;
+  else
+    return (*_ith).c_str();
+}
+
+/*!
+  \brief Set Python standard output device hook.
+  \param cb callback function
+  \param data callback function parameters
+*/
+void PyInterp_Interp::setvoutcb(PyOutChanged* cb, void* data)
+{
+  ((PyStdOut*)_vout)->_cb=cb;
+  ((PyStdOut*)_vout)->_data=data;
+}
+
+/*!
+  \brief Set Python standard error device hook.
+  \param cb callback function
+  \param data callback function parameters
+*/
+void PyInterp_Interp::setverrcb(PyOutChanged* cb, void* data)
+{
+  ((PyStdOut*)_verr)->_cb=cb;
+  ((PyStdOut*)_verr)->_data=data;
+}
diff --git a/src/PyInterp/PyInterp_Interp.cxx b/src/PyInterp/PyInterp_Interp.cxx
deleted file mode 100644 (file)
index 7e633b5..0000000
+++ /dev/null
@@ -1,533 +0,0 @@
-
-#include "PyInterp_Interp.h"  // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
-#include <pythread.h>
-
-#include <cStringIO.h>
-#include <structmember.h>
-
-#include <string>
-#include <vector>
-#include <map>
-#include <iostream>
-#include <algorithm>
-
-#define TOP_HISTORY_PY   "--- top of history ---"
-#define BEGIN_HISTORY_PY "--- begin of history ---"
-
-// a map to store python thread states that have been created for a given system thread (key=thread id,value=thread state)
-std::map<long,PyThreadState*> currentThreadMap;
-
-/*!
-  \class PyLockWrapper
-  \brief Python GIL wrapper.
-*/
-
-/*!
-  \brief Constructor. Automatically acquires GIL.
-  \param theThreadState python thread state
-*/
-PyLockWrapper::PyLockWrapper(PyThreadState* theThreadState):
-  myThreadState(theThreadState),
-  mySaveThreadState(0)
-{
-  if (myThreadState->interp == PyInterp_Interp::_interp)
-    _savestate = PyGILState_Ensure();
-  else
-    PyEval_AcquireThread(myThreadState);
-}
-
-/*!
-  \brief Destructor. Automatically releases GIL.
-*/
-PyLockWrapper::~PyLockWrapper()
-{
-  if (myThreadState->interp == PyInterp_Interp::_interp)
-    PyGILState_Release(_savestate);
-  else
-    PyEval_ReleaseThread(myThreadState);
-}
-
-/*!
-  \brief Get Python GIL wrapper.
-  \return GIL lock wrapper (GIL is automatically acquired here)
-*/
-PyLockWrapper PyInterp_Interp::GetLockWrapper()
-{
-  if (_tstate->interp == PyInterp_Interp::_interp)
-    return _tstate;
-
-  // If we are here, we have a secondary python interpreter. Try to get a thread state synchronized with the system thread
-  long currentThreadid=PyThread_get_thread_ident(); // the system thread id
-  PyThreadState* theThreadState;
-  if(currentThreadMap.count(currentThreadid) != 0)
-    {
-      //a thread state exists for this thread id
-      PyThreadState* oldThreadState=currentThreadMap[currentThreadid];
-      if(_tstate->interp ==oldThreadState->interp)
-        {
-          //The old thread state has the same python interpreter as this one : reuse the threadstate
-          theThreadState=oldThreadState;
-        }
-      else
-        {
-          //The old thread state has not the same python interpreter as this one : delete the old threadstate and create a new one
-          PyEval_AcquireLock();
-          PyThreadState_Clear(oldThreadState);
-          PyThreadState_Delete(oldThreadState);
-          PyEval_ReleaseLock();
-          theThreadState=PyThreadState_New(_tstate->interp);
-          currentThreadMap[currentThreadid]=theThreadState;
-        }
-    }
-  else
-    {
-      // no old thread state for this thread id : create a new one
-      theThreadState=PyThreadState_New(_tstate->interp);
-      currentThreadMap[currentThreadid]=theThreadState;
-    }
-  return theThreadState;
-}
-
-/*
-  The following functions are used to hook the Python
-  interpreter output.
-*/
-
-static void
-PyStdOut_dealloc(PyStdOut *self)
-{
-  PyObject_Del(self);
-}
-
-static PyObject*
-PyStdOut_write(PyStdOut *self, PyObject *args)
-{
-  char *c;
-  int l;
-  if (!PyArg_ParseTuple(args, "t#:write",&c, &l))
-    return NULL;
-  if(self->_cb==NULL) {
-    if ( self->_iscerr )
-      std::cerr << c ;
-    else
-      std::cout << c ;
-  }
-  else {
-    self->_cb(self->_data,c);
-  }
-  Py_INCREF(Py_None);
-  return Py_None;
-}
-
-static PyObject*
-PyStdOut_flush(PyStdOut *self)
-{
-  Py_INCREF(Py_None);
-  return Py_None;
-}
-
-static PyMethodDef PyStdOut_methods[] = {
-  {"write",  (PyCFunction)PyStdOut_write,  METH_VARARGS, PyDoc_STR("write(string) -> None")},
-  {"flush",  (PyCFunction)PyStdOut_flush,  METH_NOARGS,  PyDoc_STR("flush() -> None")},
-  {NULL,    NULL}   /* sentinel */
-};
-
-static PyMemberDef PyStdOut_memberlist[] = {
-  {(char*)"softspace", T_INT,  offsetof(PyStdOut, softspace), 0,
-   (char*)"flag indicating that a space needs to be printed; used by print"},
-  {NULL} /* Sentinel */
-};
-
-static PyTypeObject PyStdOut_Type = {
-  /* The ob_type field must be initialized in the module init function
-   * to be portable to Windows without using C++. */
-  PyObject_HEAD_INIT(NULL)
-  0,                            /*ob_size*/
-  "PyOut",                      /*tp_name*/
-  sizeof(PyStdOut),             /*tp_basicsize*/
-  0,                            /*tp_itemsize*/
-  /* methods */
-  (destructor)PyStdOut_dealloc, /*tp_dealloc*/
-  0,                            /*tp_print*/
-  0,                            /*tp_getattr*/
-  0,                            /*tp_setattr*/
-  0,                            /*tp_compare*/
-  0,                            /*tp_repr*/
-  0,                            /*tp_as_number*/
-  0,                            /*tp_as_sequence*/
-  0,                            /*tp_as_mapping*/
-  0,                            /*tp_hash*/
-  0,                            /*tp_call*/
-  0,                            /*tp_str*/
-  PyObject_GenericGetAttr,      /*tp_getattro*/
-  /* softspace is writable:  we must supply tp_setattro */
-  PyObject_GenericSetAttr,      /* tp_setattro */
-  0,                            /*tp_as_buffer*/
-  Py_TPFLAGS_DEFAULT,           /*tp_flags*/
-  0,                            /*tp_doc*/
-  0,                            /*tp_traverse*/
-  0,                            /*tp_clear*/
-  0,                            /*tp_richcompare*/
-  0,                            /*tp_weaklistoffset*/
-  0,                            /*tp_iter*/
-  0,                            /*tp_iternext*/
-  PyStdOut_methods,             /*tp_methods*/
-  PyStdOut_memberlist,          /*tp_members*/
-  0,                            /*tp_getset*/
-  0,                            /*tp_base*/
-  0,                            /*tp_dict*/
-  0,                            /*tp_descr_get*/
-  0,                            /*tp_descr_set*/
-  0,                            /*tp_dictoffset*/
-  0,                            /*tp_init*/
-  0,                            /*tp_alloc*/
-  0,                            /*tp_new*/
-  0,                            /*tp_free*/
-  0,                            /*tp_is_gc*/
-};
-
-#define PyStdOut_Check(v)  ((v)->ob_type == &PyStdOut_Type)
-
-static PyStdOut* newPyStdOut( bool iscerr )
-{
-  PyStdOut *self;
-  self = PyObject_New(PyStdOut, &PyStdOut_Type);
-  if (self == NULL)
-    return NULL;
-  self->softspace = 0;
-  self->_cb = NULL;
-  self->_iscerr = iscerr;
-  return self;
-}
-
-/*!
-  \class PyInterp_Interp
-  \brief Generic embedded Python interpreter.
-*/
-
-int   PyInterp_Interp::_argc   = 1;
-char* PyInterp_Interp::_argv[] = {(char*)""};
-PyObject*           PyInterp_Interp::builtinmodule = NULL;
-PyThreadState*      PyInterp_Interp::_gtstate      = NULL;
-PyInterpreterState* PyInterp_Interp::_interp       = NULL;
-
-/*!
-  \brief Basic constructor.
-
-  After construction the interpreter instance successor classes
-  must call virtual method initalize().
-*/
-PyInterp_Interp::PyInterp_Interp():
-  _tstate(0), _vout(0), _verr(0), _g(0)
-{
-}
-
-/*!
-  \brief Destructor.
-*/
-PyInterp_Interp::~PyInterp_Interp()
-{
-}
-
-/*!
-  \brief Initialize embedded interpreter.
-
-  This method shoud be called after construction of the interpreter.
-  The method initialize() calls virtuals methods
-  - initPython()  to initialize global Python interpreter
-  - initState()   to initialize embedded interpreter state
-  - initContext() to initialize interpreter internal context
-  - initRun()     to prepare interpreter for running commands
-  which should be implemented in the successor classes, according to the
-  embedded Python interpreter policy (mono or multi interpreter, etc).
-*/
-void PyInterp_Interp::initialize()
-{
-  _history.clear();       // start a new list of user's commands
-  _ith = _history.begin();
-
-  initPython();
-  // Here the global lock is released
-
-  initState();
-
-  PyEval_AcquireThread(_tstate);
-
-  initContext();
-
-  // used to interpret & compile commands
-  PyObjWrapper m(PyImport_ImportModule("codeop"));
-  if(!m) {
-    PyErr_Print();
-    PyEval_ReleaseThread(_tstate);
-    return;
-  }
-
-  // Create python objects to capture stdout and stderr
-  _vout=(PyObject*)newPyStdOut( false ); // stdout
-  _verr=(PyObject*)newPyStdOut( true );  // stderr
-
-  // All the initRun outputs are redirected to the standard output (console)
-  initRun();
-  PyEval_ReleaseThread(_tstate);
-}
-
-/*!
-  \brief Initialize Python interpreter.
-
-  In case if Python is not initialized, it sets program name, initializes the interpreter, sets program arguments,
-  initializes threads.
-  Otherwise, it just obtains the global interpreter and thread states. This is important for light SALOME configuration,
-  as in full SALOME this is done at SalomeApp level.
-  \sa SalomeApp_PyInterp class
- */
-void PyInterp_Interp::initPython()
-{
-  if (!Py_IsInitialized()){
-    // Python is not initialized
-    Py_SetProgramName(_argv[0]);
-    Py_Initialize(); // Initialize the interpreter
-    PySys_SetArgv(_argc, _argv);
-    PyEval_InitThreads(); // Create (and acquire) the interpreter lock
-  }
-
-  if ( _interp == NULL )
-    _interp = PyThreadState_Get()->interp;
-  if (PyType_Ready(&PyStdOut_Type) < 0) {
-    PyErr_Print();
-  }
-  if ( _gtstate == NULL )
-    _gtstate = PyEval_SaveThread(); // Release global thread state
-}
-
-/*!
-  \brief Get embedded Python interpreter banner.
-  \return banner string
- */
-std::string PyInterp_Interp::getbanner()
-{
- // Should we take the lock ?
- // PyEval_RestoreThread(_tstate);
-  std::string aBanner("Python ");
-  aBanner = aBanner + Py_GetVersion() + " on " + Py_GetPlatform() ;
-  aBanner = aBanner + "\ntype help to get general information on environment\n";
-  //PyEval_SaveThread();
-  return aBanner;
-}
-
-/*!
-  \brief Initialize run command.
-
-  This method is used to prepare interpreter for running
-  Python commands.
-
-  \return \c true on success and \c false on error
-*/
-bool PyInterp_Interp::initRun()
-{
-  //
-  // probably all below code isn't required
-  //
-  /*
-  PySys_SetObject("stderr",_verr);
-  PySys_SetObject("stdout",_vout);
-
-  //PyObject *m = PyImport_GetModuleDict();
-
-  PySys_SetObject("stdout",PySys_GetObject("__stdout__"));
-  PySys_SetObject("stderr",PySys_GetObject("__stderr__"));
-  */
-  return true;
-}
-
-/*!
-  \brief Compile Python command and evaluate it in the
-         python dictionary context if possible.
-  \internal
-  \param command Python command string
-  \param context Python context (dictionary)
-  \return -1 on fatal error, 1 if command is incomplete and 0
-         if command is executed successfully
- */
-static int run_command(const char *command, PyObject *context)
-{
-  PyObject *m = PyImport_AddModule("codeop");
-  if(!m) { // Fatal error. No way to go on.
-    PyErr_Print();
-    return -1;
-  }
-  PyObjWrapper v(PyObject_CallMethod(m,(char*)"compile_command",(char*)"s",command));
-  if(!v) {
-    // Error encountered. It should be SyntaxError,
-    //so we don't write out traceback
-    PyObjWrapper exception, value, tb;
-    PyErr_Fetch(&exception, &value, &tb);
-    PyErr_NormalizeException(&exception, &value, &tb);
-    PyErr_Display(exception, value, NULL);
-    return -1;
-  }
-  else if (v == Py_None) {
-    // Incomplete text we return 1 : we need a complete text to execute
-    return 1;
-  }
-  else {
-    // Complete and correct text. We evaluate it.
-    //#if PY_VERSION_HEX < 0x02040000 // python version earlier than 2.4.0
-    //    PyObjWrapper r(PyEval_EvalCode(v,context,context));
-    //#else
-    PyObjWrapper r(PyEval_EvalCode((PyCodeObject *)(void *)v,context,context));
-    //#endif
-    if(!r) {
-      // Execution error. We return -1
-      PyErr_Print();
-      return -1;
-    }
-    // The command has been successfully executed. Return 0
-    return 0;
-  }
-}
-
-void replaceAll(std::string& str, const std::string& from, const std::string& to) {
-    if(from.empty())
-        return;
-    size_t start_pos = 0;
-    while((start_pos = str.find(from, start_pos)) != std::string::npos) {
-        str.replace(start_pos, from.length(), to);
-        start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
-    }
-}
-/*!
-  \brief Compile Python command and evaluate it in the
-         python dictionary context if possible. Command might correspond to
-         the execution of a script with optional arguments.
-         In this case, command is:
-         execfile(r"/absolute/path/to/script.py [args:arg1,...,argn]")
-  \internal
-  \param command Python command string
-  \param context Python context (dictionary)
-  \return -1 on fatal error, 1 if command is incomplete and 0
-         if command is executed successfully
- */
-static int compile_command(const char *command,PyObject *context)
-{
-  // First guess if command is execution of a script with args, or a simple Python command
-  std::string singleCommand = command;
-  std::string commandArgs = "";
-
-  std::size_t pos = std::string(command).find("args:");
-  if (pos != std::string::npos) {
-    commandArgs = singleCommand.substr(pos+5);
-    commandArgs = commandArgs.substr(0, commandArgs.length()-3);
-    singleCommand = singleCommand.substr(0, pos-1)+"\")";
-  }
-
-  if (commandArgs.empty()) {
-    // process command: expression
-    // process command: execfile(r"/absolute/path/to/script.py") (no args)
-    return run_command(singleCommand.c_str(), context);
-  }
-  else {
-    // process command: execfile(r"/absolute/path/to/script.py [args:arg1,...,argn]")
-    std::string script = singleCommand.substr(11); // remove leading execfile(r"
-    script = script.substr(0, script.length()-2); // remove trailing ")
-
-    std::string preCommandBegin = "import sys; save_argv = sys.argv; sys.argv=[";
-    std::string preCommandEnd = "];";
-    replaceAll(commandArgs, ",", "\",\"");
-    commandArgs = "\""+commandArgs+"\"";
-    std::string completeCommand = preCommandBegin+"\""+script+"\","+commandArgs+preCommandEnd+singleCommand+";sys.argv=save_argv";
-    return run_command(completeCommand.c_str(), context);
-  }
-}
-
-/*!
-  \brief Run Python command.
-  \param command Python command
-  \return command status
-*/
-int PyInterp_Interp::run(const char *command)
-{
-  beforeRun();
-  return simpleRun(command);
-}
-
-/*!
-  \brief Run Python command (used internally).
-  \param command Python command
-  \param addToHistory if \c true (default), the command is added to the commands history
-  \return command status
-*/
-int PyInterp_Interp::simpleRun(const char *command, const bool addToHistory)
-{
-  if( addToHistory && strcmp(command,"") != 0 ) {
-    _history.push_back(command);
-    _ith = _history.end();
-  }
-
-  // We come from C++ to enter Python world
-  // We need to acquire the Python global lock
-  //PyLockWrapper aLock(_tstate); // san - lock is centralized now
-
-  // Reset redirected outputs before treatment
-  PySys_SetObject((char*)"stderr",_verr);
-  PySys_SetObject((char*)"stdout",_vout);
-
-  int ier = compile_command(command,_g);
-
-  // Outputs are redirected on standards outputs (console)
-  PySys_SetObject((char*)"stdout",PySys_GetObject((char*)"__stdout__"));
-  PySys_SetObject((char*)"stderr",PySys_GetObject((char*)"__stderr__"));
-
-  return ier;
-}
-
-/*!
-  \brief Get previous command in the commands history.
-  \return previous command
-*/
-const char * PyInterp_Interp::getPrevious()
-{
-  if(_ith != _history.begin()){
-    _ith--;
-    return (*_ith).c_str();
-  }
-  else
-    return BEGIN_HISTORY_PY;
-}
-
-/*!
-  \brief Get next command in the commands history.
-  \return next command
-*/
-const char * PyInterp_Interp::getNext()
-{
-  if(_ith != _history.end()){
-    _ith++;
-  }
-  if (_ith == _history.end())
-    return TOP_HISTORY_PY;
-  else
-    return (*_ith).c_str();
-}
-
-/*!
-  \brief Set Python standard output device hook.
-  \param cb callback function
-  \param data callback function parameters
-*/
-void PyInterp_Interp::setvoutcb(PyOutChanged* cb, void* data)
-{
-  ((PyStdOut*)_vout)->_cb=cb;
-  ((PyStdOut*)_vout)->_data=data;
-}
-
-/*!
-  \brief Set Python standard error device hook.
-  \param cb callback function
-  \param data callback function parameters
-*/
-void PyInterp_Interp::setverrcb(PyOutChanged* cb, void* data)
-{
-  ((PyStdOut*)_verr)->_cb=cb;
-  ((PyStdOut*)_verr)->_data=data;
-}
diff --git a/src/PyInterp/PyInterp_Request.cpp b/src/PyInterp/PyInterp_Request.cpp
new file mode 100644 (file)
index 0000000..a4ce099
--- /dev/null
@@ -0,0 +1,4 @@
+
+
+#include "PyInterp_Request.h"
+
diff --git a/src/PyInterp/PyInterp_Request.cxx b/src/PyInterp/PyInterp_Request.cxx
deleted file mode 100644 (file)
index a4ce099..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-#include "PyInterp_Request.h"
-
index f9e47c7a8608186c549be337a2117c65bd373da2..c075da118ed41a99d4bdb784058506ec5c116ec1 100644 (file)
@@ -1,5 +1,4 @@
 INCLUDE(Common)
-INCLUDE(FindCAS)
 
 SET(PROJECT_HEADERS
     SketchPlugin.h
@@ -15,17 +14,18 @@ SET(PROJECT_SOURCES
 )
 
 SET(PROJECT_LIBRARIES
-    ${CAS_KERNEL}
-    ${CAS_MODELER}
+    GeomAPI
+    GeomAlgoAPI
 )
 
-ADD_DEFINITIONS(-DSKETCHPLUGIN_EXPORTS ${BOOST_DEFINITIONS} ${CAS_DEFINITIONS})
+ADD_DEFINITIONS(-DSKETCHPLUGIN_EXPORTS ${BOOST_DEFINITIONS})
 ADD_LIBRARY(SketchPlugin SHARED ${PROJECT_SOURCES} ${PROJECT_HEADERS})
-TARGET_LINK_LIBRARIES(SketchPlugin ${PROJECT_LIBRARIES} ModelAPI)
+TARGET_LINK_LIBRARIES(SketchPlugin ${PROJECT_LIBRARIES} ModelAPI GeomAPI GeomAlgoAPI)
 
 INCLUDE_DIRECTORIES(
-  ${CAS_INCLUDE_DIRS}
   ../ModelAPI
+  ../GeomAPI
+  ../GeomAlgoAPI
 )
 
 SET(XML_RESOURCES
index 57bc3582d406a411e1e1cecd1029b40c14d2077c..8586525f792f4a46811caef7d1c1bfaaaae2201d 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * Returns the sketch preview
  */
-const TopoDS_Shape& SketchPlugin_Feature::preview()
+const boost::shared_ptr<GeomAPI_Shape>& SketchPlugin_Feature::preview()
 {
   return myPreview;
 }
@@ -12,7 +12,7 @@ const TopoDS_Shape& SketchPlugin_Feature::preview()
  * Set the shape to the internal preview field
  * \param theShape a preview shape
  */
-void SketchPlugin_Feature::setPreview(const TopoDS_Shape& theShape)
+void SketchPlugin_Feature::setPreview(const boost::shared_ptr<GeomAPI_Shape>& theShape)
 {
   myPreview = theShape;
 }
index 4b91631c9ed43e799a43ae00cf83959c8ffa2f5a..964abe80e4f8b97c299afb162abcfbb95146b044 100644 (file)
@@ -8,7 +8,7 @@
 #include "SketchPlugin.h"
 #include <ModelAPI_Feature.h>
 
-#include "TopoDS_Shape.hxx"
+#include <GeomAPI_Shape.h>
 
 /**\class SketchPlugin_Feature
  * \ingroup DataModel
 class SketchPlugin_Feature: public ModelAPI_Feature
 {
 public:
- SKETCHPLUGIN_EXPORT virtual const TopoDS_Shape& preview() = 0;
+ SKETCHPLUGIN_EXPORT virtual const boost::shared_ptr<GeomAPI_Shape>& preview() = 0;
 
 protected:
-  void setPreview(const TopoDS_Shape& theShape); ///< the preview shape
+  void setPreview(const boost::shared_ptr<GeomAPI_Shape>& theShape); ///< the preview shape
 
 private:
-  TopoDS_Shape myPreview; ///< the preview shape
+  boost::shared_ptr<GeomAPI_Shape> myPreview; ///< the preview shape
 };
 
 #endif
index 7fb20f0798af5a74af220aebc6a835e99173ccda..f450ec74660b28ce3eaa72c5bd68d5a50e6a595b 100644 (file)
@@ -3,25 +3,13 @@
 // Author:      Mikhail PONIKAROV
 
 #include "SketchPlugin_Sketch.h"
-#include "ModelAPI_Data.h"
-#include "ModelAPI_AttributeDocRef.h"
+#include <ModelAPI_Data.h>
+#include <GeomAlgoAPI_FaceBuilder.h>
 
 using namespace std;
-#include <gp_Pln.hxx>
-#include <gp_Dir.hxx>
-#include <gp_Vec.hxx>
 
-#include <TopoDS.hxx>
-#include <TopoDS_Shape.hxx>
-
-#include <BRep_Tool.hxx>
-#include <BRep_Builder.hxx>
-#include <BRepBuilderAPI_MakeFace.hxx>
-
-const double PLANE_U_MIN = -100;
-const double PLANE_U_MAX = 100;
-const double PLANE_V_MIN = -100;
-const double PLANE_V_MAX = 100;
+// face of the square-face displayed for selection of general plane
+const double PLANE_SIZE = 200;
 
 SketchPlugin_Sketch::SketchPlugin_Sketch()
 {
@@ -29,28 +17,23 @@ SketchPlugin_Sketch::SketchPlugin_Sketch()
 
 void SketchPlugin_Sketch::initAttributes()
 {
-  data()->addAttribute(PART_ATTR_DOC_REF, ModelAPI_AttributeDocRef::type());
+  //data()->addAttribute(PART_ATTR_DOC_REF, ModelAPI_AttributeDocRef::type());
 }
 
 void SketchPlugin_Sketch::execute() 
 {
-  /*shared_ptr<ModelAPI_AttributeDocRef> aDocRef = data()->docRef(PART_ATTR_DOC_REF);
-  if (!aDocRef->value()) { // create a document if not yet created
-    shared_ptr<ModelAPI_Document> aPartSetDoc = ModelAPI_PluginManager::get()->rootDocument();
-    aDocRef->setValue(aPartSetDoc->subDocument(data()->getName()));
-  }*/
 }
 
-const TopoDS_Shape& SketchPlugin_Sketch::preview()
+const boost::shared_ptr<GeomAPI_Shape>& SketchPlugin_Sketch::preview()
 {
-  if (SketchPlugin_Feature::preview().IsNull())
+  if (!SketchPlugin_Feature::preview())
   {
-    gp_Pnt anOrigin(0, 0, 0);
-    gp_Dir aDir(gp_Vec(gp_Pnt(0,0,0), gp_Pnt(1,0,0)));
-    gp_Pln aPlane(anOrigin, aDir);
-    BRepBuilderAPI_MakeFace aFaceBuilder(aPlane, PLANE_U_MIN, PLANE_U_MAX, PLANE_V_MIN,
-                                         PLANE_V_MAX);
-    setPreview(aFaceBuilder.Face());
+
+    boost::shared_ptr<GeomAPI_Pnt> anOrigin(new GeomAPI_Pnt(0, 0, 0));
+    boost::shared_ptr<GeomAPI_Dir> aNormal(new GeomAPI_Dir(1, 0, 0));
+    boost::shared_ptr<GeomAPI_Shape> aFace = 
+      GeomAlgoAPI_FaceBuilder::square(anOrigin, aNormal, PLANE_SIZE);
+    setPreview(aFace);
   }
   return SketchPlugin_Feature::preview();
 }
index a2b63388a83d7d4a75af49ae21cd2071671865e1..4cceee37f4b7e1fca07f95f199119c773b0411d3 100644 (file)
@@ -8,8 +8,6 @@
 #include "SketchPlugin.h"
 #include <SketchPlugin_Feature.h>
 
-#include <TopoDS_Shape.hxx>
-
 /// part reference attribute
 const std::string PART_ATTR_DOC_REF = "SketchDocument";
 
@@ -35,7 +33,7 @@ public:
  SKETCHPLUGIN_EXPORT virtual void initAttributes();
 
   /// Returns the sketch preview
-  SKETCHPLUGIN_EXPORT virtual const TopoDS_Shape& preview();
+  SKETCHPLUGIN_EXPORT virtual const boost::shared_ptr<GeomAPI_Shape>& preview();
 
   /// Use plugin manager for features creation
   SketchPlugin_Sketch();
index 30949838013fffa5d394850023ac14e976158a47..becc47ee3f97d01ea89c62a6c75d3b4dd48c5280 100644 (file)
@@ -9,6 +9,7 @@ SET(PROJECT_HEADERS
        XGUI_MainMenu.h
        XGUI_MainWindow.h
        XGUI_MenuGroupPanel.h
+       XGUI_Module.h
        XGUI_Tools.h
        XGUI_Workbench.h
        XGUI_Workshop.h
@@ -22,6 +23,7 @@ SET(PROJECT_HEADERS
        XGUI_DocumentDataModel.h
        XGUI_PartDataModel.h
        XGUI_ObjectsBrowser.h
+       XGUI_OperationMgr.h
     XGUI_DataTreeModel.h
     XGUI_SelectionMgr.h
     XGUI_SwitchWidget.h
@@ -49,6 +51,7 @@ SET(PROJECT_SOURCES
        XGUI_DocumentDataModel.cpp
        XGUI_PartDataModel.cpp
        XGUI_ObjectsBrowser.cpp
+       XGUI_OperationMgr.cpp
     XGUI_SelectionMgr.cpp
     XGUI_SwitchWidget.cpp
 )
index 5e9ffb8fb8113df363de9eaa668aa6cb2d5a669d..ffd6ab103f7338b833a05020b60e635534532867 100644 (file)
@@ -2,14 +2,14 @@
 
 #include <QPushButton>
 
-XGUI_Command::XGUI_Command(const QString& theId, QObject * parent)
-    : QWidgetAction(parent), myId(theId)
+XGUI_Command::XGUI_Command(const QString& theId, QObject * parent, bool isCheckable)
+    : QWidgetAction(parent), myId(theId), myCheckable(isCheckable)
 {
 }
 
 XGUI_Command::XGUI_Command(const QString& theId, const QIcon& icon, const QString& text,
-                           QObject* parent)
-    : QWidgetAction(parent), myId(theId)
+                           QObject* parent, bool isCheckable)
+    : QWidgetAction(parent), myId(theId), myCheckable(isCheckable)
 {
   setIcon(icon);
   setText(text);
@@ -22,22 +22,26 @@ XGUI_Command::~XGUI_Command()
 QWidget* XGUI_Command::createWidget(QWidget* theParent)
 {
   if (theParent->inherits("XGUI_MenuGroupPanel")) {
-    QPushButton* aBtn = new QPushButton(theParent);
-    aBtn->setIcon(icon());
-    aBtn->setText(text());
-    aBtn->setStyleSheet("text-align: left");
+    QPushButton* aButton = new QPushButton(theParent);
+    aButton->setIcon(icon());
+    aButton->setText(text());
+    aButton->setStyleSheet("text-align: left");
     QKeySequence aKeys = shortcut();
     QString aToolTip = toolTip();
     if (!aKeys.isEmpty())
       aToolTip = aToolTip + " (" + aKeys.toString() + ")";
     if (!aToolTip.isEmpty())
-      aBtn->setToolTip(aToolTip);
+      aButton->setToolTip(aToolTip);
 
-    aBtn->addAction(this);
-    connect(aBtn, SIGNAL(clicked()), this, SLOT(trigger()));
-    aBtn->setFlat(true);
-    aBtn->setMinimumSize(MIN_BUTTON_WIDTH, MIN_BUTTON_HEIGHT);
-    return aBtn;
+    aButton->addAction(this);
+    connect(aButton, SIGNAL(clicked()), this, SLOT(trigger()));
+    connect(this, SIGNAL(toggled(bool)), aButton, SLOT(setChecked(bool)));
+    aButton->setFlat(true);
+    aButton->setCheckable(myCheckable);
+    this->setCheckable(myCheckable);
+    aButton->setMinimumSize(MIN_BUTTON_WIDTH, MIN_BUTTON_HEIGHT);
+
+    return aButton;
   }
   return QWidgetAction::createWidget(theParent);
 }
@@ -59,5 +63,15 @@ void XGUI_Command::disable()
 
 void XGUI_Command::connectTo(const QObject* theResiver, const char* theSlot)
 {
-  connect(this, SIGNAL(triggered()), theResiver, theSlot);
+    connect(this, SIGNAL(triggered()), theResiver, theSlot);
+}
+
+const QStringList& XGUI_Command::unblockableCommands() const
+{
+  return myUnblockableCommands;
+}
+
+void XGUI_Command::setUnblockableCommands(const QStringList& myUnblockableCommands)
+{
+  this->myUnblockableCommands = myUnblockableCommands;
 }
index 34c5f63f6dee6b8c0d55ac06211d1a1fc50804d3..380b64ae4b8ce90eb1761995cc724d67dc5f81ca 100644 (file)
@@ -15,8 +15,8 @@ class XGUI_EXPORT XGUI_Command: public QWidgetAction
 {
 Q_OBJECT
 public:
-  XGUI_Command(const QString& theId, QObject * parent);
-  XGUI_Command(const QString& theId, const QIcon& icon, const QString& text, QObject* parent);
+  XGUI_Command(const QString& theId, QObject * parent, bool isCheckable = false);
+  XGUI_Command(const QString& theId, const QIcon& icon, const QString& text, QObject* parent, bool isCheckable = false);
   ~XGUI_Command();
 
   //! Returns true if the command is enabled
@@ -34,6 +34,9 @@ public:
     return myId;
   }
 
+  const QStringList& unblockableCommands() const;
+  void setUnblockableCommands(const QStringList& myUnblockableCommands);
+
   //! Connect the command to a slot
   virtual void connectTo(const QObject* theResiver, const char* theSlot);
 
@@ -43,6 +46,9 @@ protected:
 
 private:
   QString myId;
+  bool myCheckable;
+  //! List of Ids of commands which WILL NOT be blocked when the command is on.
+  QStringList myUnblockableCommands;
 };
 
 #endif
index 5300fe5c15a66b68775e2ef721a074c41628659a..8fb825b5bd30d03e02fb398fc82a8d14f012de68 100644 (file)
@@ -45,3 +45,15 @@ void XGUI_Displayer::Display(boost::shared_ptr<ModelAPI_Feature> theFeature,
 
   aContext->UpdateCurrentViewer();
 }
+
+/*!
+ * Erase the feature and a shape.
+ * \param theFeature a feature instance
+ * \param theFeature a shape
+ */
+void XGUI_Displayer::Erase(boost::shared_ptr<ModelAPI_Feature> theFeature,
+                           const TopoDS_Shape& theShape)
+{
+  Handle(AIS_InteractiveContext) aContext = myViewer->AISContext();
+  aContext->EraseAll();
+}
index 10480ef9732bd2fd468f729f7bede378742a0bd1..dc09d99bd06da1a1cedfb6852e0327aad04a4c52 100644 (file)
@@ -25,6 +25,8 @@ public:
 
   void Display(boost::shared_ptr<ModelAPI_Feature> theFeature, const TopoDS_Shape& theShape);
 
+  void Erase(boost::shared_ptr<ModelAPI_Feature> theFeature, const TopoDS_Shape& theShape);
+
 protected:
   XGUI_Viewer* myViewer; ///< the viewer
 };
index 7d8e81f351779bb8b9ffa40c29b44deccf5dfd11..96106a9111357da303ce4d14993cd204893aa057 100644 (file)
@@ -1,6 +1,7 @@
-#include "XGUI_MainMenu.h"
-#include "XGUI_Workbench.h"
-#include "XGUI_MainWindow.h"
+#include <XGUI_MainMenu.h>
+#include <XGUI_Workbench.h>
+#include <XGUI_MainWindow.h>
+#include <XGUI_Command.h>
 
 #include <QLayout>
 #include <QTabWidget>
@@ -88,4 +89,50 @@ QList<XGUI_Command*> XGUI_MainMenu::features() const
     aList.append(aWbn->features());
   }
   return aList;
-}
\ No newline at end of file
+}
+
+void XGUI_MainMenu::onFeatureChecked(bool isChecked)
+{
+  if (!isChecked) {
+    restoreCommandState();
+    return;
+  }
+
+  saveCommandsState();
+  QStringList aSkippedIds;
+  XGUI_Command* aToggledFeature = dynamic_cast<XGUI_Command*>(sender());
+  aSkippedIds.append(aToggledFeature->unblockableCommands());
+//  aSkippedIds.append(aToggledFeature->id());
+  XGUI_Workbench* aGeneralWB = findWorkbench(tr("General"));
+  foreach(XGUI_Command* eachFeature, aGeneralWB->features()) {
+    aSkippedIds.append(eachFeature->id());
+  }
+  QList<XGUI_Command*> allFeatures = features();
+  foreach(XGUI_Command* eachFeature, allFeatures) {
+    QString aFeatureId = eachFeature->id();
+    if (aSkippedIds.removeAll(aFeatureId) > 0) {
+      continue;
+    }
+    eachFeature->setEnabled(false);
+  }
+}
+
+void XGUI_MainMenu::saveCommandsState()
+{
+  myCommandState.clear();
+  QList<XGUI_Command*> allFeatures = features();
+  XGUI_Command* eachFeature = NULL;
+  foreach(eachFeature, allFeatures) {
+    myCommandState.insert(eachFeature, eachFeature->enabled());
+  }
+}
+
+void XGUI_MainMenu::restoreCommandState()
+{
+  QList<XGUI_Command*> allFeatures = features();
+  XGUI_Command* eachFeature = NULL;
+  foreach(eachFeature, allFeatures) {
+    eachFeature->setChecked(false);
+    eachFeature->setEnabled(myCommandState[eachFeature]);
+  }
+}
index c281422a6ba8f1c9bf51c646ef2a5391cca553e6..0fed1a07ff64a11c83d68665cd2b1e11b11aac21 100644 (file)
@@ -4,6 +4,7 @@
 #include "XGUI.h"
 #include <QObject>
 #include <QList>
+#include <QMap>
 
 class XGUI_Command;
 class XGUI_MainWindow;
@@ -16,6 +17,7 @@ class QAction;
 class QDockWidget;
 class QEvent;
 
+
 /**\class XGUI_MainMenu
  * \ingroup GUI
  * \brief Class for creation of main menu (set of workbenches)
@@ -45,13 +47,20 @@ public:
   //! Returns list of created commands
   QList<XGUI_Command*> features() const;
 
-protected:
+public slots:
+  void onFeatureChecked(bool);
+
+  void saveCommandsState();
+  void restoreCommandState();
+
   virtual bool eventFilter(QObject *theWatched, QEvent *theEvent);
 
 private:
   XGUI_MainWindow* myDesktop;
   QList<QDockWidget*> myMenuTabs;
   XGUI_Workbench* myGeneralPage;
+
+  QMap<XGUI_Command*, bool> myCommandState;
 };
 
 #endif
index 00d131278ca54b0036977d42da2d37a1bac2587f..4f4673470417dad3dcb02a3d1ac5f13080df9201 100644 (file)
@@ -7,6 +7,7 @@
 #include <QResizeEvent>
 
 #include <math.h>
+#include <iostream>
 
 XGUI_MenuGroupPanel::XGUI_MenuGroupPanel(QWidget *parent)
     : QWidget(parent), myNewRow(0), myNewCol(0), myMaxRow(1)
@@ -61,9 +62,9 @@ void XGUI_MenuGroupPanel::resizeEvent(QResizeEvent* theEvent)
 
 XGUI_Command* XGUI_MenuGroupPanel::addFeature(const QString& theId, const QString& theTitle,
                                               const QString& theTip, const QIcon& theIcon,
-                                              const QKeySequence& theKeys)
+                                              const QKeySequence& theKeys, bool isCheckable)
 {
-  XGUI_Command* aCommand = new XGUI_Command(theId, theIcon, theTitle, this);
+  XGUI_Command* aCommand = new XGUI_Command(theId, theIcon, theTitle, this, isCheckable);
   aCommand->setToolTip(theTip);
   if (!theKeys.isEmpty())
     aCommand->setShortcut(theKeys);
@@ -80,4 +81,4 @@ XGUI_Command* XGUI_MenuGroupPanel::feature(const QString& theId) const
     if ((*aIt)->id() == theId)
       return (*aIt);
   return 0;
-}
\ No newline at end of file
+}
index a1398132698b7bdcd5e4ad19055f80bf709181de..b43ef75c6e7175e3561c7294da3ee111c6791841 100644 (file)
@@ -20,7 +20,8 @@ public:
 
   //! Adding a new feature (Command) in the group
   XGUI_Command* addFeature(const QString& theId, const QString& theTitle, const QString& theTip,
-                           const QIcon& theIcon, const QKeySequence& theKeys = QKeySequence());
+                           const QIcon& theIcon, const QKeySequence& theKeys = QKeySequence(),
+                           bool isCheckable = false);
 
   //! Returns already created command by its ID
   XGUI_Command* feature(const QString& theId) const;
index 374e057eac601e58a43c6fed73001c402d79aa68..347cc061a6c485ac53ff1117fae152679d5bbd46 100644 (file)
@@ -22,4 +22,4 @@ typedef XGUI_Module* (*CREATE_FUNC)(XGUI_Workshop*);
 \r
 #define CREATE_MODULE "createModule"\r
 \r
-#endif //XGUI_Module\r
\ No newline at end of file
+#endif //XGUI_Module\r
diff --git a/src/XGUI/XGUI_OperationMgr.cpp b/src/XGUI/XGUI_OperationMgr.cpp
new file mode 100644 (file)
index 0000000..ae5aeb0
--- /dev/null
@@ -0,0 +1,99 @@
+#include "XGUI_OperationMgr.h"
+
+#include "ModuleBase_Operation.h"
+
+#include <QMessageBox>
+
+/*!
+ \brief Constructor
+ */
+XGUI_OperationMgr::XGUI_OperationMgr(QObject* theParent)
+: QObject(theParent)
+{
+}
+
+/*!
+ \brief Destructor
+ */
+XGUI_OperationMgr::~XGUI_OperationMgr()
+{
+}
+
+/*!
+ \brief Returns the current operation or NULL
+ * \return the current operation
+ */
+ModuleBase_Operation* XGUI_OperationMgr::currentOperation() const
+{
+  return myOperations.count() > 0 ? myOperations.last() : 0;
+}
+
+/*!
+ \brief Sets the current operation or NULL
+ * \return the current operation
+ */
+bool XGUI_OperationMgr::startOperation(ModuleBase_Operation* theOperation)
+{
+  if (!canStartOperation(theOperation))
+    return false;
+
+  myOperations.append(theOperation);
+  emit beforeOperationStart();
+
+  connect(theOperation, SIGNAL(stopped()), this, SLOT(onOperationStopped()));
+  theOperation->start();
+
+  emit afterOperationStart();
+  return true;
+}
+
+bool XGUI_OperationMgr::canStartOperation(ModuleBase_Operation* theOperation)
+{
+  bool aCanStart = true;
+  ModuleBase_Operation* aCurrentOp = currentOperation();
+  if (aCurrentOp && !theOperation->isGranted())
+  {
+    int anAnswer = QMessageBox::question(0, tr("Operation launch"),
+                                tr("Previous operation is not finished and will be aborted"),
+                                QMessageBox::Ok, QMessageBox::Cancel);
+    if (anAnswer == QMessageBox::Ok)
+      aCurrentOp->abort();
+    else
+      aCanStart = false;
+  }
+  return aCanStart;
+}
+
+void XGUI_OperationMgr::commitCurrentOperation()
+{
+  ModuleBase_Operation* anOperation = currentOperation();
+  if (!anOperation)
+    return;
+
+  anOperation->commit();
+}
+
+void XGUI_OperationMgr::onOperationStopped()
+{
+  ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
+  ModuleBase_Operation* anOperation = currentOperation();
+  if (!aSenderOperation || !anOperation || aSenderOperation != anOperation )
+    return;
+
+  myOperations.removeAll(anOperation);
+
+  // get last operation which can be resumed
+  ModuleBase_Operation* aResultOp = 0;
+  QListIterator<ModuleBase_Operation*> anIt(myOperations);
+  anIt.toBack();
+  while(anIt.hasPrevious())
+  {
+    ModuleBase_Operation* anOp = anIt.previous();
+    if (anOp) {
+      aResultOp = anOp;
+      break;
+    }
+  }
+  if (aResultOp)
+    startOperation(aResultOp);
+}
diff --git a/src/XGUI/XGUI_OperationMgr.h b/src/XGUI/XGUI_OperationMgr.h
new file mode 100644 (file)
index 0000000..4fabac8
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef XGUI_OperationMgr_H
+#define XGUI_OperationMgr_H
+
+#include "XGUI.h"
+
+#include <ModuleBase_Operation.h>
+
+#include <QList>
+#include <QObject>
+
+/**\class XGUI_OperationMgr
+ * \ingroup GUI
+ * \brief Operation manager. Servers to manupulate to the workshop operations. Contains a stack
+ *   of started operations and uses the upper one as a current.
+ */
+class XGUI_EXPORT XGUI_OperationMgr : public QObject
+{
+  Q_OBJECT
+public:
+  XGUI_OperationMgr(QObject* theParent);
+  virtual ~XGUI_OperationMgr();
+
+  ModuleBase_Operation* currentOperation() const;
+  bool startOperation(ModuleBase_Operation* theOperation);
+
+  void commitCurrentOperation();
+
+signals:
+  void beforeOperationStart();
+  void afterOperationStart();
+
+protected:
+  bool canStartOperation(ModuleBase_Operation* theOperation);
+
+protected slots:
+  void onOperationStopped();
+
+private:
+  typedef QList<ModuleBase_Operation*> Operations;
+  Operations myOperations;
+};
+
+#endif
index 9e2586a77546b9d3eaa3edc25178848508ad9c08..f606615a7b4eed8787ef791d5433bcb4fd070608 100644 (file)
@@ -12,6 +12,7 @@
 #include "XGUI_SelectionMgr.h"
 #include "XGUI_ObjectsBrowser.h"
 #include "XGUI_Displayer.h"
+#include "XGUI_OperationMgr.h"
 
 #include <ModelAPI_PluginManager.h>
 #include <ModelAPI_Feature.h>
 
 XGUI_Workshop::XGUI_Workshop()
   : QObject(), 
-  myCurrentOperation(NULL),
   myPartSetModule(NULL)
 {
   myMainWindow = new XGUI_MainWindow();
   mySelector = new XGUI_SelectionMgr(this);
   myDisplayer = new XGUI_Displayer(myMainWindow->viewer());
+  myOperationMgr = new XGUI_OperationMgr(this);
+  connect(myOperationMgr, SIGNAL(beforeOperationStart()), this, SLOT(onBeforeOperationStart()));
+  connect(myOperationMgr, SIGNAL(afterOperationStart()),  this, SLOT(onAfterOperationStart()));
 }
 
 //******************************************************
@@ -150,15 +153,36 @@ void XGUI_Workshop::processEvent(const Event_Message* theMessage)
   const Config_PointerMessage* aPartSetMsg =
       dynamic_cast<const Config_PointerMessage*>(theMessage);
   if (aPartSetMsg) {
-    ModuleBase_PropPanelOperation* aOperation =
+    ModuleBase_PropPanelOperation* anOperation =
         (ModuleBase_PropPanelOperation*)(aPartSetMsg->pointer());
-    setCurrentOperation(aOperation);
-    if(aOperation->xmlRepresentation().isEmpty()) { //!< No need for property panel
-      myCurrentOperation->start();
-      myCurrentOperation->commit();
-      updateCommandStatus();
-    } else {
-      fillPropertyPanel(aOperation);
+
+    if (myOperationMgr->startOperation(anOperation))
+    {
+      if (anOperation->isPerformedImmediately())
+      {
+        myOperationMgr->commitCurrentOperation();
+        updateCommandStatus();
+      }
+      /*if(anOperation->xmlRepresentation().isEmpty()) { //!< No need for property panel
+        //connectToPropertyPanel(anOperation);
+
+        anOperation->start();
+
+        if (anOperation->isPerformedImmediately()) {
+          anOperation->commit();
+          updateCommandStatus();
+        }
+      } else {
+        connectToPropertyPanel(anOperation);
+        QWidget* aPropWidget = myMainWindow->findChild<QWidget*>(XGUI::PROP_PANEL_WDG);
+        qDeleteAll(aPropWidget->children());
+
+        anOperation->start();
+        
+        XGUI_WidgetFactory aFactory = XGUI_WidgetFactory(anOperation);
+        aFactory.createWidget(aPropWidget);
+        myMainWindow->setPropertyPannelTitle(anOperation->description());
+      }*/
     }
     return;
   }
@@ -170,6 +194,34 @@ void XGUI_Workshop::processEvent(const Event_Message* theMessage)
 
 }
 
+void XGUI_Workshop::onBeforeOperationStart()
+{
+  ModuleBase_PropPanelOperation* aOperation =
+        (ModuleBase_PropPanelOperation*)(myOperationMgr->currentOperation());
+
+  if(aOperation->xmlRepresentation().isEmpty()) { //!< No need for property panel
+    //myPartSetModule->connectToPropertyPanel(aOperation);
+  } else {
+    connectWithOperation(aOperation);
+    QWidget* aPropWidget = myMainWindow->findChild<QWidget*>(XGUI::PROP_PANEL_WDG);
+    qDeleteAll(aPropWidget->children());
+  }
+}
+
+void XGUI_Workshop::onAfterOperationStart()
+{
+  ModuleBase_PropPanelOperation* aOperation =
+        (ModuleBase_PropPanelOperation*)(myOperationMgr->currentOperation());
+
+  if(aOperation->xmlRepresentation().isEmpty()) { //!< No need for property panel
+  } else {
+    XGUI_WidgetFactory aFactory = XGUI_WidgetFactory(aOperation);
+    QWidget* aPropWidget = myMainWindow->findChild<QWidget*>(XGUI::PROP_PANEL_WDG);
+    aFactory.createWidget(aPropWidget);
+    myMainWindow->setPropertyPannelTitle(aOperation->description());
+  }
+}
+
 /*
  *
  */
@@ -194,50 +246,39 @@ void XGUI_Workshop::addFeature(const Config_FeatureMessage* theMessage)
   if (!aGroup) {
     aGroup = aPage->addGroup(aGroupName);
   }
+  bool isUsePropPanel = theMessage->isUseInput();
   //Create feature...
-  QString aFeatureId = QString::fromStdString(theMessage->id());
   XGUI_Command* aCommand = aGroup->addFeature(QString::fromStdString(theMessage->id()),
                                               QString::fromStdString(theMessage->text()),
                                               QString::fromStdString(theMessage->tooltip()),
-                                              QIcon(theMessage->icon().c_str())
-                                              //TODO(sbh): QKeySequence
-                                                  );
+                                              QIcon(theMessage->icon().c_str()),
+                                              QKeySequence(), isUsePropPanel);
+  
+  connect(aCommand,                   SIGNAL(toggled(bool)),
+          myMainWindow->menuObject(), SLOT(onFeatureChecked(bool)));
   myPartSetModule->featureCreated(aCommand);
 }
 
 /*
  *
  */
-void XGUI_Workshop::fillPropertyPanel(ModuleBase_PropPanelOperation* theOperation)
+/*void XGUI_Workshop::fillPropertyPanel(ModuleBase_PropPanelOperation* theOperation)
 {
-  connectToPropertyPanel(theOperation);
+  connectWithOperation(theOperation);
   QWidget* aPropWidget = myMainWindow->findChild<QWidget*>(XGUI::PROP_PANEL_WDG);
   qDeleteAll(aPropWidget->children());
   theOperation->start();
   XGUI_WidgetFactory aFactory = XGUI_WidgetFactory(theOperation);
   aFactory.createWidget(aPropWidget);
   myMainWindow->setPropertyPannelTitle(theOperation->description());
-}
-
-void XGUI_Workshop::setCurrentOperation(ModuleBase_Operation* theOperation)
-{
-  //FIXME: Ask user about aborting of current operation?
-  if (myCurrentOperation) {
-    //TODO get isOperation from document
-    if (myCurrentOperation->isRunning())
-      myCurrentOperation->abort();
-
-    myCurrentOperation->deleteLater();
-  }
-  myCurrentOperation = theOperation;
-}
+}*/
 
 /*
  * Makes a signal/slot connections between Property Panel
  * and given operation. The given operation becomes a
  * current operation and previous operation if exists
  */
-void XGUI_Workshop::connectToPropertyPanel(ModuleBase_Operation* theOperation)
+void XGUI_Workshop::connectWithOperation(ModuleBase_Operation* theOperation)
 {
   QDockWidget* aPanel = myMainWindow->findChild<QDockWidget*>(XGUI::PROP_PANEL);
   QPushButton* aOkBtn = aPanel->findChild<QPushButton*>(XGUI::PROP_PANEL_OK);
@@ -248,6 +289,14 @@ void XGUI_Workshop::connectToPropertyPanel(ModuleBase_Operation* theOperation)
   connect(theOperation, SIGNAL(started()), myMainWindow, SLOT(showPropertyPanel()));
   connect(theOperation, SIGNAL(stopped()), myMainWindow, SLOT(hidePropertyPanel()));
   connect(theOperation, SIGNAL(stopped()), this, SLOT(updateCommandStatus()));
+
+  XGUI_MainMenu* aMenu = myMainWindow->menuObject();
+  connect(theOperation, SIGNAL(stopped()), aMenu, SLOT(restoreCommandState()));
+
+  XGUI_Command* aCommand = aMenu->feature(theOperation->operationId());
+  //Abort operation on uncheck the command
+  connect(aCommand, SIGNAL(toggled(bool)), theOperation, SLOT(setRunning(bool)));
+
 }
 
 //******************************************************
@@ -312,7 +361,6 @@ void XGUI_Workshop::onRedo()
   updateCommandStatus();
 }
 
-
 //******************************************************
 XGUI_Module* XGUI_Workshop::loadModule(const QString& theModule)
 {
index 50e0a44cc0759dc0702f330cc29d552712a5c36c..a1c79c0b3ece2d859c9be45fde0d430ab48a3c46 100644 (file)
@@ -15,6 +15,7 @@ class XGUI_Module;
 class XGUI_Workbench;
 class XGUI_SelectionMgr;
 class XGUI_Displayer;
+class XGUI_OperationMgr;
 class ModuleBase_Operation;
 class ModuleBase_PropPanelOperation;
 
@@ -48,12 +49,12 @@ public:
   //! Returns displayer
   XGUI_Displayer* displayer() const { return myDisplayer; }
 
+  //! ! Returns operation manager.
+  XGUI_OperationMgr* operationMgr() const { return myOperationMgr; }
+
   //! Creates and adds a new workbench (menu group) with the given name and returns it
   XGUI_Workbench* addWorkbench(const QString& theName);
 
-  //! Returns the current operation or NULL
-  ModuleBase_Operation* currentOperation() { return myCurrentOperation; }
-
   //! Redefinition of Event_Listener method
   virtual void processEvent(const Event_Message* theMessage);
 
@@ -71,9 +72,11 @@ public slots:
 protected:
   //Event-loop processing methods:
   void addFeature(const Config_FeatureMessage*);
-  void fillPropertyPanel(ModuleBase_PropPanelOperation* theOperation);
-  void connectToPropertyPanel(ModuleBase_Operation* theOperation);
-  void setCurrentOperation(ModuleBase_Operation* theOperation);
+  void connectWithOperation(ModuleBase_Operation* theOperation);
+
+protected slots:
+  void onBeforeOperationStart();
+  void onAfterOperationStart();
 
 private:
   void initMenu();
@@ -87,7 +90,7 @@ private:
   XGUI_SelectionMgr* mySelector;
   XGUI_Displayer* myDisplayer;
 
-  ModuleBase_Operation* myCurrentOperation;
+  XGUI_OperationMgr* myOperationMgr;
 };
 
 #endif