]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Merge branch 'master' of newgeom:newgeom.git
authorsbh <sergey.belash@opencascade.com>
Thu, 3 Apr 2014 14:35:05 +0000 (18:35 +0400)
committersbh <sergey.belash@opencascade.com>
Thu, 3 Apr 2014 14:35:05 +0000 (18:35 +0400)
110 files changed:
CMakeCommon/FindPython.cmake
CMakeLists.txt
src/Config/Config_FeatureMessage.h
src/Config/plugin-PartSet.xml
src/Model/CMakeLists.txt
src/Model/Model_Application.cxx
src/Model/Model_Application.h
src/Model/Model_AttributeDocRef.cxx [new file with mode: 0644]
src/Model/Model_AttributeDocRef.h [new file with mode: 0644]
src/Model/Model_AttributeDouble.cxx [new file with mode: 0644]
src/Model/Model_AttributeDouble.h [new file with mode: 0644]
src/Model/Model_Document.cxx
src/Model/Model_Document.h
src/Model/Model_Feature.cxx [deleted file]
src/Model/Model_Feature.h [deleted file]
src/Model/Model_Iterator.cxx [new file with mode: 0644]
src/Model/Model_Iterator.h [new file with mode: 0644]
src/Model/Model_Object.cxx [new file with mode: 0644]
src/Model/Model_Object.h [new file with mode: 0644]
src/Model/Model_PluginManager.cxx
src/Model/Model_PluginManager.h
src/ModelAPI/CMakeLists.txt
src/ModelAPI/ModelAPI.i
src/ModelAPI/ModelAPI_Attribute.h [new file with mode: 0644]
src/ModelAPI/ModelAPI_AttributeDocRef.h [new file with mode: 0644]
src/ModelAPI/ModelAPI_AttributeDouble.h [new file with mode: 0644]
src/ModelAPI/ModelAPI_Document.h
src/ModelAPI/ModelAPI_Feature.h
src/ModelAPI/ModelAPI_Iterator.h [new file with mode: 0644]
src/ModelAPI/ModelAPI_Object.h [new file with mode: 0644]
src/ModelAPI/ModelAPI_Plugin.h
src/ModelAPI/ModelAPI_PluginManager.cxx
src/ModelAPI/ModelAPI_PluginManager.h
src/PartSetPlugin/CMakeLists.txt
src/PartSetPlugin/PartSetPlugin_NewPart.cxx [deleted file]
src/PartSetPlugin/PartSetPlugin_NewPart.h [deleted file]
src/PartSetPlugin/PartSetPlugin_NewPart.hxx [deleted file]
src/PartSetPlugin/PartSetPlugin_Part.cxx [new file with mode: 0644]
src/PartSetPlugin/PartSetPlugin_Part.h [new file with mode: 0644]
src/PartSetPlugin/PartSetPlugin_Plugin.cxx
src/PartSetPlugin/PartSetPlugin_Plugin.h
src/PartSetPlugin/PartSetPlugin_Point.cxx [new file with mode: 0644]
src/PartSetPlugin/PartSetPlugin_Point.h [new file with mode: 0644]
src/PyConsole/CMakeLists.txt [new file with mode: 0644]
src/PyConsole/PyConsole.h [new file with mode: 0644]
src/PyConsole/PyConsole_Console.cxx [new file with mode: 0644]
src/PyConsole/PyConsole_Console.h [new file with mode: 0644]
src/PyConsole/PyConsole_Editor.cxx [new file with mode: 0644]
src/PyConsole/PyConsole_Editor.h [new file with mode: 0644]
src/PyConsole/PyConsole_EnhEditor.cxx [new file with mode: 0644]
src/PyConsole/PyConsole_EnhEditor.h [new file with mode: 0644]
src/PyConsole/PyConsole_EnhInterp.cxx [new file with mode: 0644]
src/PyConsole/PyConsole_EnhInterp.h [new file with mode: 0644]
src/PyConsole/PyConsole_Event.cxx [new file with mode: 0644]
src/PyConsole/PyConsole_Event.h [new file with mode: 0644]
src/PyConsole/PyConsole_Interp.cxx [new file with mode: 0644]
src/PyConsole/PyConsole_Interp.h [new file with mode: 0644]
src/PyConsole/PyConsole_Request.cxx [new file with mode: 0644]
src/PyConsole/PyConsole_Request.h [new file with mode: 0644]
src/PyConsole/resources/PyConsole_msg_en.ts [new file with mode: 0644]
src/PyConsole/resources/PyConsole_msg_fr.ts [new file with mode: 0644]
src/PyConsole/resources/PyConsole_msg_ja.ts [new file with mode: 0644]
src/PyEvent/CMakeLists.txt [new file with mode: 0644]
src/PyEvent/PyEvent.h [new file with mode: 0644]
src/PyEvent/PyEvent_Event.cxx [new file with mode: 0644]
src/PyEvent/PyEvent_Event.h [new file with mode: 0644]
src/PyEvent/PyEvent_EventFilter.cxx [new file with mode: 0644]
src/PyEvent/PyEvent_EventFilter.h [new file with mode: 0644]
src/PyInterp/CMakeLists.txt [new file with mode: 0644]
src/PyInterp/PyInterp.h [new file with mode: 0644]
src/PyInterp/PyInterp_Dispatcher.cxx [new file with mode: 0644]
src/PyInterp/PyInterp_Dispatcher.h [new file with mode: 0644]
src/PyInterp/PyInterp_Event.cxx [new file with mode: 0644]
src/PyInterp/PyInterp_Event.h [new file with mode: 0644]
src/PyInterp/PyInterp_Interp.cxx [new file with mode: 0644]
src/PyInterp/PyInterp_Interp.h [new file with mode: 0644]
src/PyInterp/PyInterp_Request.cxx [new file with mode: 0644]
src/PyInterp/PyInterp_Request.h [new file with mode: 0644]
src/PyInterp/PyInterp_Watcher.h [new file with mode: 0644]
src/XGUI/CMakeLists.txt
src/XGUI/XGUI_MainMenu.cpp
src/XGUI/XGUI_MainMenu.h
src/XGUI/XGUI_MainWindow.cpp
src/XGUI/XGUI_MainWindow.h
src/XGUI/XGUI_ViewPort.cpp
src/XGUI/XGUI_ViewPort.h
src/XGUI/XGUI_ViewWindow.cpp
src/XGUI/XGUI_ViewWindow.h
src/XGUI/XGUI_Viewer.cpp
src/XGUI/XGUI_Workshop.cpp
src/XGUI/XGUI_msg_en.ts
src/XGUI/XGUI_pictures.qrc
src/XGUI/pictures/occ_view_ambient.png [deleted file]
src/XGUI/pictures/occ_view_anticlockwise.png [deleted file]
src/XGUI/pictures/occ_view_clipping.png [deleted file]
src/XGUI/pictures/occ_view_clipping_pressed.png [deleted file]
src/XGUI/pictures/occ_view_clockwise.png [deleted file]
src/XGUI/pictures/occ_view_graduated_axes.png [deleted file]
src/XGUI/pictures/occ_view_maximized.png [deleted file]
src/XGUI/pictures/occ_view_minimized.png [deleted file]
src/XGUI/pictures/occ_view_preselection.png [deleted file]
src/XGUI/pictures/occ_view_presets.png [deleted file]
src/XGUI/pictures/occ_view_return_3d_view.png [deleted file]
src/XGUI/pictures/occ_view_rotation_point.png [deleted file]
src/XGUI/pictures/occ_view_scaling.png [deleted file]
src/XGUI/pictures/occ_view_selection.png [deleted file]
src/XGUI/pictures/occ_view_shoot.png [deleted file]
src/XGUI/pictures/occ_view_style_switch.png [deleted file]
src/XGUI/pictures/occ_view_triedre.png [deleted file]
src/XGUI/pictures/occ_view_zooming_style_switch.png [deleted file]

index b1aa8c5315b803b449d7e5360729cd98b4638169..543d830ada048fd32345d8d085271ded7ab764ca 100644 (file)
@@ -9,8 +9,6 @@ ENDIF()
 FILE(TO_CMAKE_PATH "$ENV{PYTHON_LIB_DIR}/${PYTHON_LIBRARY_DLL}" PYTHON_LIBRARY)
 FILE(TO_CMAKE_PATH $ENV{PYTHON_INC_DIR} PYTHON_INCLUDE_DIR)
 
-# TOOD: Clean a mess with python interpreter
-# FIND_PACKAGE(PythonInterp)
 FIND_PACKAGE(PythonLibs)
 
 INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS})
index 68d6a4f2e8f4fc2fe277b10bfdf91c89e7a90a6d..f13e37375395277ad7d5135a8cc57b0797eedbd2 100644 (file)
@@ -6,6 +6,7 @@ SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMakeCommon" ${CMAKE_MODULE_PATH})
 
 INCLUDE(Common)
 INCLUDE(FindQt5)
+INCLUDE(FindPython)
 
 ADD_SUBDIRECTORY (src/Event)
 ADD_SUBDIRECTORY (src/Model)
@@ -13,4 +14,7 @@ ADD_SUBDIRECTORY (src/ModelAPI)
 ADD_SUBDIRECTORY (src/Config)
 ADD_SUBDIRECTORY (src/PartSet)
 ADD_SUBDIRECTORY (src/PartSetPlugin)
+ADD_SUBDIRECTORY (src/PyEvent)
+ADD_SUBDIRECTORY (src/PyInterp)
+ADD_SUBDIRECTORY (src/PyConsole)
 ADD_SUBDIRECTORY (src/XGUI)
index 1309ae898f4a3c8ede56e790c7401e93179c96ac..8fce1b17369d6feee884351efea7d80c5bf76f6d 100644 (file)
@@ -9,7 +9,7 @@
 /*\r
  * Class to pass a feature entry extracted from xml file.\r
  * Example of the feature entry:\r
- * <feature id="new_part" text="Part" tooltip="Creates a new part" icon=":pictures/part_ico.png"/>\r
+ * <feature id="Part" text="New part" tooltip="Creates a new part" icon=":pictures/part_ico.png"/>\r
  */\r
 class CONFIG_EXPORT Config_FeatureMessage: public Event_Message\r
 {\r
index fff9a54aa47c345d3fc74197e92336f54f8a24b9..76c67c75c1d1f43003fb156c03bc7d511b7da112 100644 (file)
@@ -1,20 +1,20 @@
 <plugin>
   <workbench id="Part">
     <group id="Operations">
-      <feature id="new_part" text="Part" tooltip="Creates a new part" icon=":pictures/part_ico.png"/>
+      <feature id="Part" text="New part" tooltip="Creates a new part" icon=":pictures/part_ico.png"/>
       <feature id="duplicate" text="Duplicate" tooltip="Duplicate selected object" icon=":icons/duplicate.png"/>
       <feature id="remove" text="Remove"  tooltip="Remove selected object" icon=":icons/remove.png"/>
     </group>
   </workbench>
   <workbench id="Construction">
     <group id="Basic">
-      <feature id="new_point" text="Point" tooltip="Create a new point" icon=":icons/point.png">
+      <feature id="Point" text="Point" tooltip="Create a new point" icon=":icons/point.png">
         <value id="x" type="double" label="X:" min="0" max="" step="0.1" default="0"/>
         <value id="y" type="double" label="Y:" min="0" max="" step="0.1" default="1"/>
         <value id="z" type="double" label="Z:" min="0" max="10" step="0.1" default="2"/>
       </feature>
-      <feature id="new_axis" text="Axis" tooltip="Create a new axis" icon=":icons/axis.png" keysequence=""/>
-      <feature id="new_plane" text="Plane" tooltip="Create a new plane" icon=":icons/plane.png" keysequence=""/>
+      <feature id="Axis" text="Axis" tooltip="Create a new axis" icon=":icons/axis.png" keysequence=""/>
+      <feature id="Plane" text="Plane" tooltip="Create a new plane" icon=":icons/plane.png" keysequence=""/>
     </group>
   </workbench>  
-</plugin>
\ No newline at end of file
+</plugin>
index 42ee4f4c64d9fc1f2895cb1c2b53d3b8cd86917a..c6dfa0f7e75521f908c48396019004f9c71aab3c 100644 (file)
@@ -2,22 +2,29 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11)
 
 INCLUDE(Common)
 INCLUDE(FindCAS)
-INCLUDE(FindBoost)
 
 SET(PROJECT_HEADERS
     Model.h
+    Model_Application.h
     Model_Document.h
     Model_PluginManager.h
-    Model_Feature.h
+    Model_Object.h
+    Model_Iterator.h
+    Model_AttributeDouble.h
+    Model_AttributeDocRef.h
 )
 
 SET(PROJECT_SOURCES
+    Model_Application.cxx
     Model_Document.cxx
     Model_PluginManager.cxx
-    Model_Feature.cxx
+    Model_Object.cxx
+    Model_Iterator.cxx
+    Model_AttributeDouble.cxx
+    Model_AttributeDocRef.cxx
 )
 
-ADD_DEFINITIONS(-DMODEL_EXPORTS ${CAS_DEFINITIONS} ${BOOST_DEFINITIONS})
+ADD_DEFINITIONS(-DMODEL_EXPORTS ${CAS_DEFINITIONS})
 ADD_LIBRARY(Model SHARED ${PROJECT_SOURCES} ${PROJECT_HEADERS})
 TARGET_LINK_LIBRARIES(Model ${PROJECT_LIBRARIES} ${CAS_OCAF} ModelAPI Event Config)
 
index dab193d13b3cf3a2734c3e8dafea19a93b5f3640..aab352e75c7ecd02a8052759619e1afd879e7462 100644 (file)
@@ -2,8 +2,8 @@
 // Created:    Fri Sep 2 2011
 // Author:     Mikhail PONIKAROV
 
-#include <Model_Application.hxx>
-#include <Model_Document.hxx>
+#include <Model_Application.h>
+#include <Model_Document.h>
 
 IMPLEMENT_STANDARD_HANDLE(Model_Application, TDocStd_Application)
 IMPLEMENT_STANDARD_RTTIEXT(Model_Application, TDocStd_Application)
@@ -14,7 +14,7 @@ static Handle_Model_Application TheApplication = new Model_Application;
 //function : getApplication
 //purpose  : 
 //=======================================================================
-Handle_Model_Application Model_Application::GetApplication()
+Handle(Model_Application) Model_Application::getApplication()
 {
   return TheApplication;
 }
@@ -23,13 +23,14 @@ Handle_Model_Application Model_Application::GetApplication()
 //function : getDocument
 //purpose  : 
 //=======================================================================
-ModelAPI_Document* Model_Application::GetMainDocument()
+std::shared_ptr<Model_Document> Model_Application::getDocument(std::string theDocID)
 {
+  if (myDocs.find(theDocID) != myDocs.end())
+    return myDocs[theDocID];
 
-  if (myMainDoc.IsNull()) {
-    myMainDoc = new Model_Document("BinOcaf");
-  }
-  return *myMainDoc;
+  std::shared_ptr<Model_Document> aNew(new Model_Document(theDocID));
+  myDocs[theDocID] = aNew;
+  return aNew;
 }
 
 //=======================================================================
index 77d669257079fc13b303f0bd4203334cf570a16b..75aa172afbaae4798eac9f331427a73551aa4362 100644 (file)
@@ -6,9 +6,9 @@
 #ifndef Model_Application_HeaderFile
 #define Model_Application_HeaderFile
 
-#include <Model_Document.hxx> 
-#include <ModelAPI_Application.hxx> 
+#include <Model_Document.h> 
 #include <TDocStd_Application.hxx>
+#include <map>
 
 // Define handle class 
 DEFINE_STANDARD_HANDLE(Model_Application, TDocStd_Application)
@@ -19,19 +19,18 @@ DEFINE_STANDARD_HANDLE(Model_Application, TDocStd_Application)
  * Application supports the formats and document management. It is uses OCAF-lke
  * architecture and just implements specific features of the module.
  */
-class Model_Application: public TDocStd_Application, public ModelAPI_Application
+class Model_Application: public TDocStd_Application
 {
 public:
   // useful methods inside of the module
 
   // CASCADE RTTI
-  DEFINE_STANDARD_RTTI(Model_Application)
-  ;
+  DEFINE_STANDARD_RTTI(Model_Application);
 
   //! Retuns the application: one per process    
-  MODEL_EXPORT static Handle_Model_Application GetApplication();
-  //! Returns the main document (on first call creates it)
-  MODEL_EXPORT ModelAPI_Document* GetMainDocument();
+  MODEL_EXPORT static Handle_Model_Application getApplication();
+  //! Returns the main document (on first call creates it) by the string identifier
+  MODEL_EXPORT std::shared_ptr<Model_Document> getDocument(std::string theDocID);
 
 public:
   // Redefined OCAF methods
@@ -46,8 +45,8 @@ public:
   Model_Application();
 
 private:
-
-  Handle_Model_Document myMainDoc; ///< main document of an application
+  /// Map from string identifiers to created documents of an application
+  std::map<std::string, std::shared_ptr<Model_Document> > myDocs;
 };
 
 #endif
diff --git a/src/Model/Model_AttributeDocRef.cxx b/src/Model/Model_AttributeDocRef.cxx
new file mode 100644 (file)
index 0000000..07712d0
--- /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(std::shared_ptr<ModelAPI_Document> theDoc)
+{
+  myComment->Set(TCollection_ExtendedString(theDoc->id().c_str()));
+}
+
+std::shared_ptr<ModelAPI_Document> Model_AttributeDocRef::value()
+{
+  if (myComment->Get().Length())
+    return Model_Application::getApplication()->getDocument(
+      TCollection_AsciiString(myComment->Get()).ToCString());
+  // not initialized
+  return std::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.h b/src/Model/Model_AttributeDocRef.h
new file mode 100644 (file)
index 0000000..2218557
--- /dev/null
@@ -0,0 +1,35 @@
+// File:        Model_AttributeDocRef.h
+// Created:     2 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef Model_AttributeDocRef_HeaderFile
+#define Model_AttributeDocRef_HeaderFile
+
+#include "Model.h"
+#include "ModelAPI_AttributeDocRef.h"
+#include <TDataStd_Comment.hxx>
+#include <TDF_Label.hxx>
+
+/**\class Model_AttributeDocRef
+ * \ingroup DataModel
+ * \brief Attribute that contains reference to another document.
+ */
+
+class MODEL_EXPORT Model_AttributeDocRef : public ModelAPI_AttributeDocRef
+{
+  Handle_TDataStd_Comment myComment; ///< reference to document is identified as string-id
+public:
+  /// Defines the document referenced from this attribute
+  virtual void setValue(std::shared_ptr<ModelAPI_Document> theDoc);
+
+  /// Returns document referenced from this attribute
+  virtual std::shared_ptr<ModelAPI_Document> value();
+
+protected:
+  /// Initializes attibutes
+  Model_AttributeDocRef(TDF_Label& theLabel);
+
+  friend class Model_Object;
+};
+
+#endif
diff --git a/src/Model/Model_AttributeDouble.cxx b/src/Model/Model_AttributeDouble.cxx
new file mode 100644 (file)
index 0000000..7e2ed83
--- /dev/null
@@ -0,0 +1,27 @@
+// 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.h b/src/Model/Model_AttributeDouble.h
new file mode 100644 (file)
index 0000000..d498bc4
--- /dev/null
@@ -0,0 +1,35 @@
+// File:        Model_AttributeDouble.h
+// Created:     2 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef Model_AttributeDouble_HeaderFile
+#define Model_AttributeDouble_HeaderFile
+
+#include "Model.h"
+#include "ModelAPI_AttributeDouble.h"
+#include <TDataStd_Real.hxx>
+#include <TDF_Label.hxx>
+
+/**\class Model_AttributeDouble
+ * \ingroup DataModel
+ * \brief Attribute that contains real value with double precision.
+ */
+
+class MODEL_EXPORT Model_AttributeDouble : public ModelAPI_AttributeDouble
+{
+  Handle_TDataStd_Real myReal; ///< double is Real attribute
+public:
+  /// Defines the double value
+  virtual void setValue(const double theValue);
+
+  /// Returns the double value
+  virtual double value();
+
+protected:
+  /// Initializes attibutes
+  Model_AttributeDouble(TDF_Label& theLabel);
+
+  friend class Model_Object;
+};
+
+#endif
index dd34e598b24aeb0e165af8b104c162084fa69c21..8a3f0bf45c3419d5cab8f61f03194d6f50dacb87 100644 (file)
@@ -1,25 +1,26 @@
 // File:        Model_Document.cxx
-// Created:     28 Dec 2011
+// Created:     28 Feb 2014
 // Author:      Mikhail PONIKAROV
-// Copyright:   CEA 2011
 
 #include <Model_Document.h>
+#include <ModelAPI_Feature.h>
+#include <Model_Object.h>
+#include <Model_Application.h>
+#include <Model_PluginManager.h>
+#include <Model_Iterator.h>
 
 #include <TDataStd_Integer.hxx>
+#include <TDataStd_Comment.hxx>
 
-IMPLEMENT_STANDARD_HANDLE(Model_Document, MMgt_TShared)
-IMPLEMENT_STANDARD_RTTIEXT(Model_Document, MMgt_TShared)
-
-static const int UNDO_LIMIT = 10; // number of possible undo operations in the module
+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_ObjectsMgr)
 static const int TAG_HISTORY = 3; // tag of the history sub-tree (Root for Model_History)
-static const int TAG_ISOTOPES = 4; // tag of the isotopes sub-tree (Root for MaterialMC_Isotope)
 
 using namespace std;
 
-bool Model_Document::Load(const char* theFileName)
+bool Model_Document::load(const char* theFileName)
 {
   bool myIsError = Standard_False;
   /*
@@ -60,7 +61,7 @@ bool Model_Document::Load(const char* theFileName)
   return !myIsError;
 }
 
-bool Model_Document::Save(const char* theFileName)
+bool Model_Document::save(const char* theFileName)
 {
   bool myIsError = true;
   /*
@@ -98,66 +99,137 @@ bool Model_Document::Save(const char* theFileName)
   return !myIsError;
 }
 
-void Model_Document::Close()
+void Model_Document::close()
 {
-  TDocStd_Document::Close();
+  myDoc->Close();
 }
 
-void Model_Document::StartOperation()
+void Model_Document::startOperation()
 {
-  TDocStd_Document::NewCommand();
+  myDoc->NewCommand();
 }
 
-void Model_Document::FinishOperation()
+void Model_Document::finishOperation()
 {
-  TDocStd_Document::CommitCommand();
+  myDoc->CommitCommand();
   myTransactionsAfterSave++;
 }
 
-void Model_Document::AbortOperation()
+void Model_Document::abortOperation()
 {
-  TDocStd_Document::AbortCommand();
+  myDoc->AbortCommand();
 }
 
-bool Model_Document::IsOperation()
+bool Model_Document::isOperation()
 {
-  return TDocStd_Document::HasOpenCommand() == Standard_True ;
+  return myDoc->HasOpenCommand() == Standard_True ;
 }
 
-bool Model_Document::IsModified()
+bool Model_Document::isModified()
 {
   return myTransactionsAfterSave != 0;
 }
 
-bool Model_Document::CanUndo()
+bool Model_Document::canUndo()
 {
-  return TDocStd_Document::GetAvailableUndos() > 0;
+  return myDoc->GetAvailableUndos() > 0;
 }
 
-void Model_Document::Undo()
+void Model_Document::undo()
 {
-  TDocStd_Document::Undo();
+  myDoc->Undo();
   myTransactionsAfterSave--;
 }
 
-bool Model_Document::CanRedo()
+bool Model_Document::canRedo()
 {
-  return TDocStd_Document::GetAvailableRedos() > 0;
+  return myDoc->GetAvailableRedos() > 0;
 }
 
-void Model_Document::Redo()
+void Model_Document::redo()
 {
-  TDocStd_Document::Redo();
+  myDoc->Redo();
   myTransactionsAfterSave++;
 }
 
-Model_Document::Model_Document(const TCollection_ExtendedString& theStorageFormat)
-    : TDocStd_Document(theStorageFormat)
+void Model_Document::addFeature(
+  std::shared_ptr<ModelAPI_Feature> theFeature, const std::string theGroupID)
+{
+  TDF_Label aGroupLab = groupLabel(theGroupID);
+  TDF_Label anObjLab = aGroupLab.NewChild();
+  std::shared_ptr<Model_Object> aData(new Model_Object);
+  aData->setLabel(anObjLab);
+  theFeature->setData(aData);
+  setUniqueName(theFeature, theGroupID);
+  theFeature->initAttributes();
+  TDataStd_Comment::Set(anObjLab, theFeature->getKind().c_str());
+}
+
+std::shared_ptr<ModelAPI_Feature> Model_Document::feature(TDF_Label& theLabel)
+{
+  Handle(TDataStd_Comment) aFeatureID;
+  if (theLabel.FindAttribute(TDataStd_Comment::GetID(), aFeatureID)) {
+    string anID(TCollection_AsciiString(aFeatureID->Get()).ToCString());
+    std::shared_ptr<ModelAPI_Feature> aResult = Model_PluginManager::get()->createFeature(anID);
+    std::shared_ptr<Model_Object> aData(new Model_Object);
+    aData->setLabel(theLabel);
+    aResult->setData(aData);
+    aResult->initAttributes();
+    return aResult;
+  }
+  return std::shared_ptr<ModelAPI_Feature>(); // not found
+}
+
+shared_ptr<ModelAPI_Document> Model_Document::subDocument(string theDocID)
+{
+  return Model_Application::getApplication()->getDocument(theDocID);
+}
+
+shared_ptr<ModelAPI_Iterator> Model_Document::featuresIterator(const string theGroup)
+{
+  shared_ptr<Model_Document> aThis(Model_Application::getApplication()->getDocument(myID));
+  return shared_ptr<ModelAPI_Iterator>(new Model_Iterator(aThis, groupLabel(theGroup)));
+}
+
+Model_Document::Model_Document(const std::string theID)
+    : myID(theID), myDoc(new TDocStd_Document("BinOcaf")) // binary OCAF format
 {
-  SetUndoLimit(UNDO_LIMIT);
+  myDoc->SetUndoLimit(UNDO_LIMIT);
   myTransactionsAfterSave = 0;
 }
 
-Model_Document::~Model_Document()
+TDF_Label Model_Document::groupLabel(const string theGroup)
+{
+  if (myGroups.find(theGroup) == myGroups.end()) {
+    myGroups[theGroup] = myDoc->Main().FindChild(TAG_OBJECTS).NewChild();
+  }
+  return myGroups[theGroup];
+}
+
+void Model_Document::setUniqueName(
+  shared_ptr<ModelAPI_Feature> theFeature, const string theGroupID)
 {
+  // first count all objects of such kind to start with index = count + 1
+  int aNumObjects = 0;
+  shared_ptr<ModelAPI_Iterator> anIter = featuresIterator(theGroupID);
+  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(theGroupID); 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(theGroupID);
+    } else anIter->next();
+  }
+
+  theFeature->data()->setName(aName);
 }
index bdd5d8e0c510d8d04b28d42b4860da287dfb3180..bc7d6db7422b77c2832bdf815ae936999e0c3374 100644 (file)
@@ -1,7 +1,6 @@
-// File:        Model_Document.hxx
-// Created:     28 Dec 2011
+// File:        Model_Document.h
+// Created:     28 Feb 2014
 // Author:      Mikhail PONIKAROV
-// Copyright:   CEA 2011
 
 #ifndef Model_Document_HeaderFile
 #define Model_Document_HeaderFile
@@ -9,6 +8,7 @@
 #include <Model.h>
 #include <ModelAPI_Document.h>
 #include <TDocStd_Document.hxx>
+#include <map>
 
 class Handle_Model_Document;
 
@@ -21,59 +21,83 @@ class Handle_Model_Document;
  * to provide access to all stored data.
  */
 
-class Model_Document: public TDocStd_Document, public ModelAPI_Document
+class Model_Document: public ModelAPI_Document
 {
 public:
 
-  DEFINE_STANDARD_RTTI(Model_Document)
-  ;
-
-  //! Creates new document by the format string of a storage
-  Model_Document(const TCollection_ExtendedString& theStorageFormat);
-  //! Deletes all high-level data, managed this document
-  ~Model_Document();
-
   //! Loads the OCAF document from the file.
   //! \param theFileName full name of the file to load
   //! \param theStudyID identifier of the SALOME study to associate with loaded file
   //! \returns true if file was loaded successfully
-  MODEL_EXPORT bool Load(const char* theFileName);
+  MODEL_EXPORT bool load(const char* theFileName);
 
   //! Saves the OCAF document to the file.
   //! \param theFileName full name of the file to store
   //! \returns true if file was stored successfully
-  MODEL_EXPORT bool Save(const char* theFileName);
+  MODEL_EXPORT bool save(const char* theFileName);
 
   //! Removes document data
-  MODEL_EXPORT void Close();
+  MODEL_EXPORT void close();
 
   //! Starts a new operation (opens a tansaction)
-  MODEL_EXPORT void StartOperation();
+  MODEL_EXPORT void startOperation();
   //! Finishes the previously started operation (closes the transaction)
-  MODEL_EXPORT void FinishOperation();
+  MODEL_EXPORT void finishOperation();
   //! Aborts the operation 
-  MODEL_EXPORT void AbortOperation();
+  MODEL_EXPORT void abortOperation();
   //! Returns true if operation has been started, but not yet finished or aborted
-  MODEL_EXPORT bool IsOperation();
+  MODEL_EXPORT bool isOperation();
   //! Returns true if document was modified (since creation/opening)
-  MODEL_EXPORT bool IsModified();
+  MODEL_EXPORT bool isModified();
 
   //! Returns True if there are available Undos
-  MODEL_EXPORT bool CanUndo();
+  MODEL_EXPORT bool canUndo();
   //! Undoes last operation
-  MODEL_EXPORT void Undo();
+  MODEL_EXPORT void undo();
   //! Returns True if there are available Redos
-  MODEL_EXPORT bool CanRedo();
+  MODEL_EXPORT bool canRedo();
   //! Redoes last operation
-  MODEL_EXPORT void Redo();
+  MODEL_EXPORT void redo();
+
+  //! Adds to the document the new feature of the given group id
+  //! \param theFeature a feature object that will be connected to the document in this method
+  //! \param theGroupID identifier of the groups of objects (must be greater than zero)
+  MODEL_EXPORT virtual void addFeature(std::shared_ptr<ModelAPI_Feature> theFeature,
+    const std::string theGroupID);
+
+  //! Returns the existing feature by the label
+  //! \param theLabel base label of the feature
+  MODEL_EXPORT virtual std::shared_ptr<ModelAPI_Feature> feature(TDF_Label& theLabel);
+
+  //! Adds a new sub-document by the identifier, or returns existing one if it is already exist
+  MODEL_EXPORT virtual std::shared_ptr<ModelAPI_Document> subDocument(std::string theDocID);
+
+  //! Creates an iterator of the features by the specific groups
+  MODEL_EXPORT virtual std::shared_ptr<ModelAPI_Iterator> featuresIterator(
+    const std::string theGroup);
+
+  MODEL_EXPORT virtual const std::string& id() const {return myID;}
 
 protected:
 
+  //! Returns (creates if needed) the group label
+  TDF_Label groupLabel(const std::string theGroup);
+
+  //! Initializes feature with a unique name in this group (unique name is generated as 
+  //! feature type + "_" + index
+  void setUniqueName(
+    std::shared_ptr<ModelAPI_Feature> theFeature, const std::string theGroupID);
+
+  //! Creates new document with binary file format
+  Model_Document(const std::string theID);
+
+  friend class Model_Application;
+
 private:
+  std::string myID; ///< identifier of the document in the application
+  Handle_TDocStd_Document myDoc; ///< OCAF document
   int myTransactionsAfterSave; ///< number of transactions after the last "save" call, used for "IsModified" method
+  std::map<std::string, TDF_Label> myGroups; ///< root labels of the features groups identified by names
 };
 
-// Define handle class 
-DEFINE_STANDARD_HANDLE(Model_Document, TDocStd_Document)
-
 #endif
diff --git a/src/Model/Model_Feature.cxx b/src/Model/Model_Feature.cxx
deleted file mode 100644 (file)
index 0ff2838..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-// File:        Model_Feature.hxx
-// Created:     21 Mar 2014
-// Author:      Mikhail PONIKAROV
-
-#include <Model_Feature.h>
-
-using namespace std;
-
-Model_Feature::Model_Feature()
-{
-}
-
-string Model_Feature::GetKind()
-{
-  return "Point";
-}
diff --git a/src/Model/Model_Feature.h b/src/Model/Model_Feature.h
deleted file mode 100644 (file)
index 0b51f72..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-// File:        Model_Feature.hxx
-// Created:     21 Mar 2014
-// Author:      Mikhail PONIKAROV
-
-#ifndef Model_Feature_HeaderFile
-#define Model_Feature_HeaderFile
-
-#include "Model.h"
-#include <ModelAPI_Feature.h>
-
-/**\class Model_Feature
- * \ingroup DataModel
- * \brief General object of the application that allows
- * to get/set attributes from the document and compute result of an operation.
- */
-
-class Model_Feature: public ModelAPI_Feature
-{
-  Model_Feature();
-
-  friend class Model_PluginManager;
-public:
-  /// Returns the kind of a feature (like "Point")
-  virtual std::string GetKind();
-};
-
-#endif
diff --git a/src/Model/Model_Iterator.cxx b/src/Model/Model_Iterator.cxx
new file mode 100644 (file)
index 0000000..ae62fa9
--- /dev/null
@@ -0,0 +1,54 @@
+// File:        Model_Iterator.hxx
+// Created:     1 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#include "Model_Iterator.h"
+#include "Model_Document.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();
+}
+
+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;
+}
+
+Model_Iterator::Model_Iterator(std::shared_ptr<Model_Document> theDoc, TDF_Label theLab)
+  : myDoc(theDoc), myIter(theLab, TDataStd_Comment::GetID(), Standard_False)
+{}
diff --git a/src/Model/Model_Iterator.h b/src/Model/Model_Iterator.h
new file mode 100644 (file)
index 0000000..4da78e2
--- /dev/null
@@ -0,0 +1,49 @@
+// File:        Model_Iterator.h
+// Created:     1 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef Model_Iterator_HeaderFile
+#define Model_Iterator_HeaderFile
+
+#include "Model.h"
+#include "ModelAPI_Iterator.h"
+#include <TDF_Label.hxx>
+#include <TDF_ChildIDIterator.hxx>
+
+class Model_Document;
+
+/**\class Model_Iterator
+ * \ingroup DataModel
+ * \brief Allows to iterate features of the document. Is created by the document
+ * (see method featuresIterator).
+ */
+
+class MODEL_EXPORT Model_Iterator : public ModelAPI_Iterator
+{
+  std::shared_ptr<Model_Document> myDoc; ///< the document of iterated objects
+  TDF_ChildIDIterator myIter; ///< iterator of the features-labels
+public:
+  /// Iterates to the next feature
+  virtual void next();
+  /// Returns true if the current iteration is valid and next iteration is possible
+  virtual bool more();
+  /// Returns the currently iterated feature
+  virtual std::shared_ptr<ModelAPI_Feature> current();
+  /// Returns the kind of the current feature (faster than Current()->getKind())
+  virtual std::string currentKind();
+  /// Returns the name of the current feature (faster than Current()->getName())
+  virtual std::string currentName();
+  /// Don't changes the current position of iterator. Not fast: iterates the left items.
+  /// \returns number of left iterations
+  virtual int numIterationsLeft();
+
+protected:
+  /// Initializes iterator
+  /// \param theDoc document where the iteration is performed
+  /// \param theLab label of the features group to iterate
+  Model_Iterator(std::shared_ptr<Model_Document> theDoc, TDF_Label theLab);
+
+  friend class Model_Document;
+};
+
+#endif
diff --git a/src/Model/Model_Object.cxx b/src/Model/Model_Object.cxx
new file mode 100644 (file)
index 0000000..0312a0c
--- /dev/null
@@ -0,0 +1,77 @@
+// File:        Model_Object.hxx
+// Created:     21 Mar 2014
+// Author:      Mikhail PONIKAROV
+
+#include <Model_Object.h>
+#include <Model_AttributeDocRef.h>
+#include <Model_AttributeDouble.h>
+#include <TDataStd_Name.hxx>
+
+using namespace std;
+
+Model_Object::Model_Object()
+{
+}
+
+void Model_Object::setLabel(TDF_Label& theLab)
+{
+  myLab = theLab;
+}
+
+string Model_Object::getName()
+{
+  Handle(TDataStd_Name) aName;
+  if (myLab.FindAttribute(TDataStd_Name::GetID(), aName))
+    return string(TCollection_AsciiString(aName->Get()).ToCString());
+  return ""; // not defined
+}
+
+void Model_Object::setName(string theName)
+{
+  TDataStd_Name::Set(myLab, theName.c_str());
+}
+
+void Model_Object::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] = std::shared_ptr<ModelAPI_Attribute>(anAttr);
+  else
+    ; // TODO: generate error on unknown attribute request and/or add mechanism for customization
+}
+
+shared_ptr<ModelAPI_AttributeDocRef> Model_Object::docRef(const string theID)
+{
+  map<string, 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 std::shared_ptr<ModelAPI_AttributeDocRef>();
+  }
+  shared_ptr<ModelAPI_AttributeDocRef> aRes = 
+    dynamic_pointer_cast<ModelAPI_AttributeDocRef>(aFound->second);
+  if (!aRes) {
+    // TODO: generate error on invalid attribute type request
+  }
+  return aRes;
+}
+
+shared_ptr<ModelAPI_AttributeDouble> Model_Object::real(const string theID)
+{
+  map<string, 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 std::shared_ptr<ModelAPI_AttributeDouble>();
+  }
+  shared_ptr<ModelAPI_AttributeDouble> aRes = 
+    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_Object.h b/src/Model/Model_Object.h
new file mode 100644 (file)
index 0000000..ecaca89
--- /dev/null
@@ -0,0 +1,52 @@
+// File:        Model_Object.hxx
+// Created:     21 Mar 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef Model_Object_HeaderFile
+#define Model_Object_HeaderFile
+
+#include "Model.h"
+#include <ModelAPI_Object.h>
+#include <TDF_Label.hxx>
+
+#include <map>
+
+class ModelAPI_Attribute;
+
+/**\class Model_Object
+ * \ingroup DataModel
+ * \brief General object of the application that allows
+ * to get/set attributes from the document and compute result of an operation.
+ */
+
+class Model_Object: public ModelAPI_Object
+{
+  TDF_Label myLab; ///< label of the feature in the document
+  /// All attributes of the object identified by the attribute ID
+  std::map<std::string, std::shared_ptr<ModelAPI_Attribute> > myAttrs;
+
+  Model_Object();
+
+  friend class Model_Document;
+
+public:
+  /// Returns the name of the feature visible by the user in the object browser
+  virtual std::string getName();
+  /// Defines the name of the feature visible by the user in the object browser
+  virtual void setName(std::string theName);
+  /// Returns the attribute that references to another document
+  virtual std::shared_ptr<ModelAPI_AttributeDocRef> docRef(const std::string theID);
+  /// Returns the attribute that contains real value with double precision
+  virtual std::shared_ptr<ModelAPI_AttributeDouble> real(const std::string theID);
+
+  /// Initializes object by the attributes: must be called just after the object is created
+  /// for each attribute of the object
+  /// \param theID identifier of the attribute that can be referenced by this ID later
+  /// \param theAttrType type of the created attribute (received from the type method)
+  virtual void addAttribute(std::string theID, std::string theAttrType);
+
+  /// Puts feature to the document data sub-structure
+  void setLabel(TDF_Label& theLab);
+};
+
+#endif
index daeb60176f6d47f49b147aa21cea8f14874bf38a..ce53ba069235590fa25c98e975f96dcbafcebf57 100644 (file)
@@ -5,7 +5,9 @@
 #include <Model_PluginManager.h>
 #include <ModelAPI_Feature.h>
 #include <ModelAPI_Plugin.h>
-#include <Model_Feature.h>
+#include <Model_Object.h>
+#include <Model_Document.h>
+#include <Model_Application.h>
 #include <Event_Loop.h>
 #include <Config_FeatureMessage.h>
 #include <Config_ModuleReader.h>
@@ -14,7 +16,7 @@ using namespace std;
 
 static Model_PluginManager* myImpl = new Model_PluginManager();
 
-boost::shared_ptr<ModelAPI_Feature> Model_PluginManager::createFeature(string theFeatureID)
+std::shared_ptr<ModelAPI_Feature> Model_PluginManager::createFeature(string theFeatureID)
 {
   if (this != myImpl) return myImpl->createFeature(theFeatureID);
 
@@ -26,11 +28,31 @@ boost::shared_ptr<ModelAPI_Feature> Model_PluginManager::createFeature(string th
       loadLibrary(myCurrentPluginName);
     }
     if (myPluginObjs.find(myCurrentPluginName) != myPluginObjs.end()) {
-      return myPluginObjs[myCurrentPluginName]->createFeature(theFeatureID);
+      std::shared_ptr<ModelAPI_Feature> aCreated = 
+        myPluginObjs[myCurrentPluginName]->createFeature(theFeatureID);
+      return aCreated;
     }
   }
 
-  return boost::shared_ptr<ModelAPI_Feature>(); // return nothing
+  return std::shared_ptr<ModelAPI_Feature>(); // return nothing
+}
+
+std::shared_ptr<ModelAPI_Document> Model_PluginManager::rootDocument()
+{
+  return std::shared_ptr<ModelAPI_Document>(
+    Model_Application::getApplication()->getDocument("root"));
+}
+
+shared_ptr<ModelAPI_Document> Model_PluginManager::currentDocument()
+{
+  if (!myCurrentDoc)
+    myCurrentDoc = rootDocument();
+  return myCurrentDoc;
+}
+
+void Model_PluginManager::setCurrentDocument(shared_ptr<ModelAPI_Document> theDoc)
+{
+  myCurrentDoc = theDoc;
 }
 
 Model_PluginManager::Model_PluginManager()
@@ -39,7 +61,7 @@ Model_PluginManager::Model_PluginManager()
   //TODO(sbh): Implement static method to extract event id [SEID]
   static Event_ID aFeatureEvent = Event_Loop::eventByName("FeatureEvent");
 
-  ModelAPI_PluginManager::SetPluginManager(boost::shared_ptr<ModelAPI_PluginManager>(this));
+  ModelAPI_PluginManager::SetPluginManager(std::shared_ptr<ModelAPI_PluginManager>(this));
   // register the configuration reading listener
   Event_Loop* aLoop = Event_Loop::loop();
   aLoop->registerListener(this, aFeatureEvent);
@@ -52,7 +74,7 @@ void Model_PluginManager::processEvent(const Event_Message* theMessage)
   if (aMsg) {
     // proccess the plugin info, load plugin
     if (myPlugins.find(aMsg->id()) == myPlugins.end()) {
-      myPlugins[aMsg->id()] = aMsg->pluginLibrary(); // TO DO: plugin name must be also imported from XMLs
+      myPlugins[aMsg->id()] = aMsg->pluginLibrary();
     }
   }
   // plugins information was started to load, so, it will be loaded
index 5a847abce16589cfe4f65c7c2b274afcbfe8586d..c0cc31e5f4232af4aecd1e076fa1d3d9d29d5eaa 100644 (file)
 #include <Event_Listener.h>
 #include <map>
 
+class Model_Document;
+
 /**\class Model_PluginManager
  * \ingroup DataModel
  * \brief Object that knows (from the initial XML file) which
  * plugin contains which feature, loads and stores reference to loaded plugins by
  * the feature functionality request.
  */
-
 class Model_PluginManager : public ModelAPI_PluginManager, public Event_Listener
 {
   bool myPluginsInfoLoaded; ///< it true if plugins information is loaded
-  /// map of feature IDs to plugin name and object
+  /// map of feature IDs to plugin name
   std::map<std::string, std::string> myPlugins;
   std::map<std::string, ModelAPI_Plugin*> myPluginObjs; ///< instances of the already plugins
   std::string myCurrentPluginName; ///< name of the plugin that must be loaded currently
+  std::shared_ptr<ModelAPI_Document> myCurrentDoc; ///< current working document
 public:
   /// Creates the feature object using plugins functionality
-  MODEL_EXPORT virtual boost::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID);
+  MODEL_EXPORT virtual std::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID);
+
+  /// Returns the root document of the application (that may contains sub-documents)
+  MODEL_EXPORT virtual std::shared_ptr<ModelAPI_Document> rootDocument();
+
+  /// Returns the current document that used for current work in the application
+  MODEL_EXPORT virtual std::shared_ptr<ModelAPI_Document> currentDocument();
+
+  /// Defines the current document that used for current work in the application
+  MODEL_EXPORT virtual void setCurrentDocument(std::shared_ptr<ModelAPI_Document> theDoc);
 
   /// Registers the plugin that creates features.
   /// It is obligatory for each plugin to call this function on loading to be found by 
   /// the plugin manager on call of the feature)
-  virtual void registerPlugin(ModelAPI_Plugin* thePlugin);
+  MODEL_EXPORT virtual void registerPlugin(ModelAPI_Plugin* thePlugin);
 
   /// Processes the configuration file reading
   MODEL_EXPORT virtual void processEvent(const Event_Message* theMessage);
index 33fe1bcf872463b28e92cc516c38076fd8bf63ae..017acf05d4d7cff9675407b7347ec0330fad48a5 100644 (file)
@@ -1,10 +1,7 @@
 CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11)
 
-INCLUDE(Common)
 FIND_PACKAGE(SWIG REQUIRED)
 INCLUDE(${SWIG_USE_FILE})
-INCLUDE(FindPython)
-INCLUDE(FindBoost)
 INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
 
 SET(PROJECT_HEADERS
@@ -13,7 +10,12 @@ SET(PROJECT_HEADERS
     ModelAPI_PluginManager.h
     ModelAPI_Plugin.h
     ModelAPI_Feature.h
+    ModelAPI_Iterator.h
+    ModelAPI_Object.h
     ModelAPI_Document.h
+    ModelAPI_Attribute.h
+    ModelAPI_AttributeDouble.h
+    ModelAPI_AttributeDocRef.h
 )
 
 SET(PROJECT_SOURCES
index 6deac8560d12022d94d480dbdb03a81772a588b1..ad58d4228849d4ab6ebe79c500a7af6f952d5e42 100644 (file)
@@ -5,6 +5,10 @@
   #include "ModelAPI_Document.h"
   #include "ModelAPI_PluginManager.h"
   #include "ModelAPI_Feature.h"
+  #include "ModelAPI_Object.h"
+  #include "ModelAPI_Attribute.h"
+  #include "ModelAPI_AttributeDocRef.h"
+  #include "ModelAPI_AttributeDouble.h"
 %}
 
 // to avoid error on this
 // standard definitions
 %include "typemaps.i"
 %include "std_string.i"
+%include <std_shared_ptr.i>
 
 // boost pointers
-%include <boost_shared_ptr.i>
+// %include <boost_shared_ptr.i>
+%shared_ptr(ModelAPI_Document)
 %shared_ptr(ModelAPI_PluginManager)
 %shared_ptr(ModelAPI_Feature)
+%shared_ptr(ModelAPI_Object)
+%shared_ptr(ModelAPI_Attribute)
+%shared_ptr(ModelAPI_AttributeDocRef)
+%shared_ptr(ModelAPI_AttributeDouble)
 
 // all supported interfaces
 %include "ModelAPI_Document.h"
 %include "ModelAPI_PluginManager.h"
 %include "ModelAPI_Feature.h"
+%include "ModelAPI_Object.h"
+%include "ModelAPI_Attribute.h"
+%include "ModelAPI_AttributeDocRef.h"
+%include "ModelAPI_AttributeDouble.h"
diff --git a/src/ModelAPI/ModelAPI_Attribute.h b/src/ModelAPI/ModelAPI_Attribute.h
new file mode 100644 (file)
index 0000000..cd7d36a
--- /dev/null
@@ -0,0 +1,29 @@
+// File:        ModelAPI_Attribute.h
+// Created:     2 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef ModelAPI_Attribute_HeaderFile
+#define ModelAPI_Attribute_HeaderFile
+
+#include "ModelAPI.h"
+#include <string>
+
+/**\class ModelAPI_Attribute
+ * \ingroup DataModel
+ * \brief Generic attribute of the Object.
+ */
+
+class MODELAPI_EXPORT ModelAPI_Attribute
+{
+public:
+  
+  /// Returns the type of this class of attributes, not static method
+  virtual std::string attributeType() = 0;
+
+protected:
+  /// Objects are created for features automatically
+  ModelAPI_Attribute()
+  {}
+};
+
+#endif
diff --git a/src/ModelAPI/ModelAPI_AttributeDocRef.h b/src/ModelAPI/ModelAPI_AttributeDocRef.h
new file mode 100644 (file)
index 0000000..e9a602d
--- /dev/null
@@ -0,0 +1,37 @@
+// File:        ModelAPI_AttributeDocRef.h
+// Created:     2 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef ModelAPI_AttributeDocRef_HeaderFile
+#define ModelAPI_AttributeDocRef_HeaderFile
+
+#include "ModelAPI_Attribute.h"
+#include "ModelAPI_Document.h"
+
+/**\class ModelAPI_AttributeDocRef
+ * \ingroup DataModel
+ * \brief Attribute that contains reference to another document.
+ */
+
+class MODELAPI_EXPORT ModelAPI_AttributeDocRef : public ModelAPI_Attribute
+{
+public:
+  /// Defines the document referenced from this attribute
+  virtual void setValue(std::shared_ptr<ModelAPI_Document> theDoc) = 0;
+
+  /// Returns document referenced from this attribute
+  virtual std::shared_ptr<ModelAPI_Document> value() = 0;
+
+  /// Returns the type of this class of attributes
+  static std::string type() {return "DocRef";}
+
+  /// Returns the type of this class of attributes, not static method
+  virtual std::string attributeType() {return type();}
+
+protected:
+  /// Objects are created for features automatically
+  ModelAPI_AttributeDocRef()
+  {}
+};
+
+#endif
diff --git a/src/ModelAPI/ModelAPI_AttributeDouble.h b/src/ModelAPI/ModelAPI_AttributeDouble.h
new file mode 100644 (file)
index 0000000..7dfb882
--- /dev/null
@@ -0,0 +1,36 @@
+// File:        ModelAPI_AttributeDouble.h
+// Created:     2 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef ModelAPI_AttributeDouble_HeaderFile
+#define ModelAPI_AttributeDouble_HeaderFile
+
+#include "ModelAPI_Attribute.h"
+
+/**\class ModelAPI_AttributeDouble
+ * \ingroup DataModel
+ * \brief Attribute that contains real value with double precision.
+ */
+
+class MODELAPI_EXPORT ModelAPI_AttributeDouble : public ModelAPI_Attribute
+{
+public:
+  /// Defines the double value
+  virtual void setValue(double theValue) = 0;
+
+  /// Returns the double value
+  virtual double value() = 0;
+
+  /// Returns the type of this class of attributes
+  static std::string type() {return "Double";}
+
+  /// Returns the type of this class of attributes, not static method
+  virtual std::string attributeType() {return type();}
+
+protected:
+  /// Objects are created for features automatically
+  ModelAPI_AttributeDouble()
+  {}
+};
+
+#endif
index 911aca0680be06183f0093859ee21bebed285c9a..90a6a9184341b7a7c1b0519892ba32aad4c8b39c 100644 (file)
@@ -1,12 +1,24 @@
-// File:        Model_Document.hxx
-// Created:     28 Dec 2011
+// File:        ModelAPI_Document.cxx
+// Created:     28 Feb 2014
 // Author:      Mikhail PONIKAROV
-// Copyright:   CEA 2011
 
 #ifndef ModelAPI_Document_HeaderFile
 #define ModelAPI_Document_HeaderFile
 
 #include <ModelAPI.h>
+#include <string>
+#include <memory>
+
+class ModelAPI_Feature;
+class ModelAPI_Iterator;
+
+/// Common groups identifiers
+/// Group of parameters
+static const std::string PARAMETERS_GROUP = "Parameters";
+/// Group of constructions
+static const std::string CONSTRUCTIONS_GROUP = "Construction";
+/// Group of parts
+static const std::string PARTS_GROUP = "Parts";
 
 /**\class Model_Document
  * \ingroup DataModel
  * Also it provides acces to this data: open/save, transactions management etc.
  * to provide access to all stored data.
  */
-
 class ModelAPI_Document
 {
 public:
-
   //! Loads the OCAF document from the file.
   //! \param theFileName full name of the file to load
   //! \param theStudyID identifier of the SALOME study to associate with loaded file
   //! \returns true if file was loaded successfully
-  MODELAPI_EXPORT virtual bool Load(const char* theFileName) = 0;
+  MODELAPI_EXPORT virtual bool load(const char* theFileName) = 0;
 
   //! Saves the OCAF document to the file.
   //! \param theFileName full name of the file to store
   //! \returns true if file was stored successfully
-  MODELAPI_EXPORT virtual bool Save(const char* theFileName) = 0;
+  MODELAPI_EXPORT virtual bool save(const char* theFileName) = 0;
 
   //! Removes document data
-  MODELAPI_EXPORT virtual void Close() = 0;
+  MODELAPI_EXPORT virtual void close() = 0;
 
   //! Starts a new operation (opens a tansaction)
-  MODELAPI_EXPORT virtual void StartOperation() = 0;
+  MODELAPI_EXPORT virtual void startOperation() = 0;
   //! Finishes the previously started operation (closes the transaction)
-  MODELAPI_EXPORT virtual void FinishOperation() = 0;
+  MODELAPI_EXPORT virtual void finishOperation() = 0;
   //! Aborts the operation 
-  MODELAPI_EXPORT virtual void AbortOperation() = 0;
+  MODELAPI_EXPORT virtual void abortOperation() = 0;
   //! Returns true if operation has been started, but not yet finished or aborted
-  MODELAPI_EXPORT virtual bool IsOperation() = 0;
+  MODELAPI_EXPORT virtual bool isOperation() = 0;
   //! Returns true if document was modified (since creation/opening)
-  MODELAPI_EXPORT virtual bool IsModified() = 0;
+  MODELAPI_EXPORT virtual bool isModified() = 0;
 
   //! Returns True if there are available Undos
-  MODELAPI_EXPORT virtual bool CanUndo() = 0;
+  MODELAPI_EXPORT virtual bool canUndo() = 0;
   //! Undoes last operation
-  MODELAPI_EXPORT virtual void Undo() = 0;
+  MODELAPI_EXPORT virtual void undo() = 0;
   //! Returns True if there are available Redos
-  MODELAPI_EXPORT virtual bool CanRedo() = 0;
+  MODELAPI_EXPORT virtual bool canRedo() = 0;
   //! Redoes last operation
-  MODELAPI_EXPORT virtual void Redo() = 0;
+  MODELAPI_EXPORT virtual void redo() = 0;
+
+  //! Adds to the document the new feature of the given group id
+  //! \param theFeature a feature object that will be connected to the document in this method
+  //! \param theGroupID identifier of the groups of object
+  MODELAPI_EXPORT virtual void addFeature(std::shared_ptr<ModelAPI_Feature> theFeature,
+    const std::string theGroupID) = 0;
+
+  ///! Adds a new sub-document by the identifier, or returns existing one if it is already exist
+  MODELAPI_EXPORT virtual std::shared_ptr<ModelAPI_Document> subDocument(std::string theDocID) = 0;
+
+  ///! Creates an iterator of the features by the specific groups
+  MODELAPI_EXPORT virtual std::shared_ptr<ModelAPI_Iterator> featuresIterator(
+    const std::string theGroup) = 0;
+
+  MODELAPI_EXPORT virtual const std::string& id() const = 0;
 
 protected:
   /// Only for SWIG wrapping it is here
index 2c9339c7410305e9336e93fa85ae8442ff97c44c..a33d05084ea4c88675d15d9b6a653e2ebf9eecdd 100644 (file)
@@ -7,27 +7,42 @@
 
 #include "ModelAPI.h"
 #include <string>
+#include <memory>
 
-class ModelAPI_Feature;
+class ModelAPI_Object;
 
 /**\class ModelAPI_Feature
  * \ingroup DataModel
- * \brief General object of the application that allows
- * to get/set attributes from the document and compute result of an operation.
+ * \brief Functionality of the model object: to update result,
+ * to initialize attributes, etc.
  */
 
 class MODELAPI_EXPORT ModelAPI_Feature
 {
+  std::shared_ptr<ModelAPI_Object> myData; ///< manager of the data model of a feature
+
 public:
   /// Returns the kind of a feature (like "Point")
-  virtual std::string GetKind() = 0;
+  virtual std::string getKind() = 0;
+
+  /// Request for initialization of data model of the feature: adding all attributes
+  virtual void initAttributes() = 0;
+
+  /// Computes or recomputes the result
+  virtual void execute() = 0;
+
+  /// Returns the data manager of this feature
+  std::shared_ptr<ModelAPI_Object> data() {return myData;}
 
 protected:
   /// Use plugin manager for features creation: this method is 
   /// defined here only for SWIG-wrapping
   ModelAPI_Feature()
-  {
-  }
+  {}
+
+  /// Sets the data manager of an object (document does)
+  void setData(std::shared_ptr<ModelAPI_Object> theData) {myData = theData;}
+  friend class Model_Document;
 };
 
 #endif
diff --git a/src/ModelAPI/ModelAPI_Iterator.h b/src/ModelAPI/ModelAPI_Iterator.h
new file mode 100644 (file)
index 0000000..22d2066
--- /dev/null
@@ -0,0 +1,45 @@
+// File:        ModelAPI_Iterator.hxx
+// Created:     1 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef ModelAPI_Iterator_HeaderFile
+#define ModelAPI_Iterator_HeaderFile
+
+#include "ModelAPI.h"
+#include <string>
+#include <memory>
+
+class ModelAPI_Feature;
+class ModelAPI_Document;
+
+/**\class ModelAPI_Iterator
+ * \ingroup DataModel
+ * \brief Allows to iterate features of the document. Is created by the document
+ * (see method featuresIterator).
+ */
+
+class MODELAPI_EXPORT ModelAPI_Iterator
+{
+public:
+  /// Iterates to the next feature
+  virtual void next() = 0;
+  /// Returns true if the current iteration is valid and next iteration is possible
+  virtual bool more() = 0;
+  /// Returns the currently iterated feature
+  virtual std::shared_ptr<ModelAPI_Feature> current() = 0;
+  /// Returns the kind of the current feature (faster than Current()->getKind())
+  virtual std::string currentKind() = 0;
+  /// Returns the name of the current feature (faster than Current()->getName())
+  virtual std::string currentName() = 0;
+  /// Don't changes the current position of iterator. Not fast: iterates the left items.
+  /// \returns number of left iterations
+  virtual int numIterationsLeft() = 0;
+
+protected:
+  /// Use plugin manager for features creation: this method is 
+  /// defined here only for SWIG-wrapping
+  ModelAPI_Iterator()
+  {}
+};
+
+#endif
diff --git a/src/ModelAPI/ModelAPI_Object.h b/src/ModelAPI/ModelAPI_Object.h
new file mode 100644 (file)
index 0000000..7f78a35
--- /dev/null
@@ -0,0 +1,48 @@
+// File:        ModelAPI_Object.hxx
+// Created:     21 Mar 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef ModelAPI_Object_HeaderFile
+#define ModelAPI_Object_HeaderFile
+
+#include "ModelAPI.h"
+#include <string>
+#include <memory>
+
+class ModelAPI_AttributeDocRef;
+class ModelAPI_AttributeDouble;
+
+/**\class ModelAPI_Object
+ * \ingroup DataModel
+ * \brief General object of the application that allows
+ * to get/set attributes from the document and compute result of an operation.
+ */
+
+class MODELAPI_EXPORT ModelAPI_Object
+{
+public:
+
+  /// Returns the name of the feature visible by the user in the object browser
+  virtual std::string getName() = 0;
+
+  /// Defines the name of the feature visible by the user in the object browser
+  virtual void setName(std::string theName) = 0;
+
+  /// Returns the attribute that references to another document
+  virtual std::shared_ptr<ModelAPI_AttributeDocRef> docRef(const std::string theID) = 0;
+  /// Returns the attribute that contains real value with double precision
+  virtual std::shared_ptr<ModelAPI_AttributeDouble> real(const std::string theID) = 0;
+
+  /// Initializes object by the attributes: must be called just after the object is created
+  /// for each attribute of the object
+  /// \param theID identifier of the attribute that can be referenced by this ID later
+  /// \param theAttrType type of the created attribute (received from the type method)
+  virtual void addAttribute(std::string theID, std::string theAttrType) = 0;
+
+protected:
+  /// Objects are created for features automatically
+  ModelAPI_Object()
+  {}
+};
+
+#endif
index 5d2d0430ceec5c39db9222803d1f184a9869b76c..0605d8b6f1997c5bedc6f5a18b965ec709dc5e71 100644 (file)
@@ -7,7 +7,7 @@
 
 #include "ModelAPI.h"
 #include <string>
-#include <boost/shared_ptr.hpp>
+#include <memory>
 
 class ModelAPI_Feature;
 
@@ -20,7 +20,7 @@ class MODELAPI_EXPORT ModelAPI_Plugin
 {
 public:
   /// Creates the feature object of this plugin by the feature string ID
-  virtual boost::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID) = 0;
+  virtual std::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID) = 0;
 
 protected:
   /// Is needed for python wrapping by swig
index d4a20feaeeed02c0db864f2e2f50e7dc7524ab63..6f9834f6ad14c94d83f7e5dae40eabbe5bf453d4 100644 (file)
@@ -7,8 +7,17 @@
 #include <ModelAPI_Document.h>
 // to avoid unresolved ModelAPI_Feature()
 #include <ModelAPI_Feature.h>
+// to avoid unresolved ModelAPI_Object()
+#include <ModelAPI_Object.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>
@@ -22,19 +31,19 @@ using namespace std;
 string library(const string& theLibName);
 
 /// Manager that will be initialized from Model package, one per application
-boost::shared_ptr<ModelAPI_PluginManager> MY_MANAGER;
+std::shared_ptr<ModelAPI_PluginManager> MY_MANAGER;
 
 ModelAPI_PluginManager::ModelAPI_PluginManager()
 {
 }
 
 void ModelAPI_PluginManager::SetPluginManager(
-  boost::shared_ptr<ModelAPI_PluginManager> theManager)
+  std::shared_ptr<ModelAPI_PluginManager> theManager)
 {
   MY_MANAGER = theManager;
 }
 
-boost::shared_ptr<ModelAPI_PluginManager> ModelAPI_PluginManager::get()
+std::shared_ptr<ModelAPI_PluginManager> ModelAPI_PluginManager::get()
 {
   if (!MY_MANAGER) { // import Model library that implements this interface of ModelAPI
     loadLibrary("Model");
@@ -62,6 +71,8 @@ string library(const string& theLibName)
   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);
index 620501bfd07e97d18d1cbf8dc82236ba6a5fafce..34e5cca94d86107731235dd0cc50172140ca623d 100644 (file)
@@ -7,10 +7,11 @@
 
 #include "ModelAPI.h"
 #include <string>
-#include <boost/shared_ptr.hpp>
+#include <memory>
 
 class ModelAPI_Feature;
 class ModelAPI_Plugin;
+class ModelAPI_Document;
 
 /**\class ModelAPI_PluginManager
  * \ingroup DataModel
@@ -23,16 +24,25 @@ class MODELAPI_EXPORT ModelAPI_PluginManager
 {
 public:
   /// Creates the feature object using plugins functionality
-  virtual boost::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID) = 0;
+  virtual std::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID) = 0;
 
   /// Returns the real implementation (the alone instance per application) of the plugin manager
-  static boost::shared_ptr<ModelAPI_PluginManager> get();
+  static std::shared_ptr<ModelAPI_PluginManager> get();
 
   /// Registers the plugin that creates features.
   /// It is obligatory for each plugin to call this function on loading to be found by 
   /// the plugin manager on call of the feature)
   virtual void registerPlugin(ModelAPI_Plugin* thePlugin) = 0;
 
+  /// Returns the root document of the application (that may contains sub-documents)
+  virtual std::shared_ptr<ModelAPI_Document> rootDocument() = 0;
+
+  /// Returns the current document that used for current work in the application
+  virtual std::shared_ptr<ModelAPI_Document> currentDocument() = 0;
+
+  /// Defines the current document that used for current work in the application
+  virtual void setCurrentDocument(std::shared_ptr<ModelAPI_Document> theDoc) = 0;
+
   /// loads the library with specific name, appends "lib*.dll" or "*.so" depending on the platform
   static void ModelAPI_PluginManager::loadLibrary(const std::string theLibName);
 
@@ -40,7 +50,7 @@ public:
   ModelAPI_PluginManager();
 
 protected:
-  static void SetPluginManager(boost::shared_ptr<ModelAPI_PluginManager> theManager);
+  static void SetPluginManager(std::shared_ptr<ModelAPI_PluginManager> theManager);
 };
 
 #endif
index ab07cdc88c95c8d495c2127794685c3ff07cf55c..ab420ebe9e44639b838e026a5a54f993f16594d1 100644 (file)
@@ -1,17 +1,18 @@
 CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11)
 
 INCLUDE(Common)
-INCLUDE(FindBoost)
 
 SET(PROJECT_HEADERS
     PartSetPlugin.h
     PartSetPlugin_Plugin.h
-    PartSetPlugin_NewPart.h
+    PartSetPlugin_Part.h
+    PartSetPlugin_Point.h
 )
 
 SET(PROJECT_SOURCES
     PartSetPlugin_Plugin.cxx
-    PartSetPlugin_NewPart.cxx
+    PartSetPlugin_Part.cxx
+    PartSetPlugin_Point.cxx
 )
 
 ADD_DEFINITIONS(-DPARTSETPLUGIN_EXPORTS ${BOOST_DEFINITIONS})
diff --git a/src/PartSetPlugin/PartSetPlugin_NewPart.cxx b/src/PartSetPlugin/PartSetPlugin_NewPart.cxx
deleted file mode 100644 (file)
index 7959ef0..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-// File:        PartSetPlugin_NewPart.cxx
-// Created:     27 Mar 2014
-// Author:      Mikhail PONIKAROV
-
-#include "PartSetPlugin_NewPart.hxx"
-
-PartSetPlugin_NewPart::PartSetPlugin_NewPart()
-{
-}
diff --git a/src/PartSetPlugin/PartSetPlugin_NewPart.h b/src/PartSetPlugin/PartSetPlugin_NewPart.h
deleted file mode 100644 (file)
index 9b824c0..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-// File:        PartSetPlugin_NewPart.hxx
-// Created:     27 Mar 2014
-// Author:      Mikhail PONIKAROV
-
-#ifndef PartSetPlugin_NewPart_HeaderFile
-#define PartSetPlugin_NewPart_HeaderFile
-
-#include "PartSetPlugin.h"
-#include <ModelAPI_Feature.h>
-
-/**\class PartSetPlugin_NewPart
- * \ingroup DataModel
- * \brief Feature for creation of the new part in PartSet.
- */
-
-class PartSetPlugin_NewPart: public ModelAPI_Feature
-{
-public:
-  /// Returns the kind of a feature
-  PARTSETPLUGIN_EXPORT virtual std::string GetKind() {return "new_part";}
-
-  /// Use plugin manager for features creation
-  PartSetPlugin_NewPart();
-};
-
-#endif
diff --git a/src/PartSetPlugin/PartSetPlugin_NewPart.hxx b/src/PartSetPlugin/PartSetPlugin_NewPart.hxx
deleted file mode 100644 (file)
index 9b824c0..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-// File:        PartSetPlugin_NewPart.hxx
-// Created:     27 Mar 2014
-// Author:      Mikhail PONIKAROV
-
-#ifndef PartSetPlugin_NewPart_HeaderFile
-#define PartSetPlugin_NewPart_HeaderFile
-
-#include "PartSetPlugin.h"
-#include <ModelAPI_Feature.h>
-
-/**\class PartSetPlugin_NewPart
- * \ingroup DataModel
- * \brief Feature for creation of the new part in PartSet.
- */
-
-class PartSetPlugin_NewPart: public ModelAPI_Feature
-{
-public:
-  /// Returns the kind of a feature
-  PARTSETPLUGIN_EXPORT virtual std::string GetKind() {return "new_part";}
-
-  /// Use plugin manager for features creation
-  PartSetPlugin_NewPart();
-};
-
-#endif
diff --git a/src/PartSetPlugin/PartSetPlugin_Part.cxx b/src/PartSetPlugin/PartSetPlugin_Part.cxx
new file mode 100644 (file)
index 0000000..fd9d69d
--- /dev/null
@@ -0,0 +1,30 @@
+// 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_Object.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() 
+{
+  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()));
+  }
+}
diff --git a/src/PartSetPlugin/PartSetPlugin_Part.h b/src/PartSetPlugin/PartSetPlugin_Part.h
new file mode 100644 (file)
index 0000000..c40f163
--- /dev/null
@@ -0,0 +1,34 @@
+// File:        PartSetPlugin_Part.h
+// Created:     27 Mar 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef PartSetPlugin_Part_HeaderFile
+#define PartSetPlugin_Part_HeaderFile
+
+#include "PartSetPlugin.h"
+#include <ModelAPI_Feature.h>
+
+/// part reference attribute
+const std::string PART_ATTR_DOC_REF = "PartDocument";
+
+/**\class PartSetPlugin_Part
+ * \ingroup DataModel
+ * \brief Feature for creation of the new part in PartSet.
+ */
+class PartSetPlugin_Part: public ModelAPI_Feature
+{
+public:
+  /// Returns the kind of a feature
+  PARTSETPLUGIN_EXPORT virtual std::string getKind() {return "Part";}
+
+  /// Creates a new part document if needed
+  PARTSETPLUGIN_EXPORT virtual void execute();
+
+  /// Request for initialization of data model of the feature: adding all attributes
+  PARTSETPLUGIN_EXPORT virtual void initAttributes();
+
+  /// Use plugin manager for features creation
+  PartSetPlugin_Part();
+};
+
+#endif
index d3d295dc72f8c90fc1e81cf81b3d84e83c6cbf9c..c1998aefa4ffe117e1eab7afbcbd663e5e8453ed 100644 (file)
@@ -1,6 +1,8 @@
 #include "PartSetPlugin_Plugin.h"
-#include "PartSetPlugin_NewPart.h"
+#include "PartSetPlugin_Part.h"
+#include "PartSetPlugin_Point.h"
 #include <ModelAPI_PluginManager.h>
+#include <ModelAPI_Document.h>
 
 using namespace std;
 
@@ -13,11 +15,24 @@ PartSetPlugin_Plugin::PartSetPlugin_Plugin()
   ModelAPI_PluginManager::get()->registerPlugin(this);
 }
 
-boost::shared_ptr<ModelAPI_Feature> PartSetPlugin_Plugin::createFeature(string theFeatureID)
+std::shared_ptr<ModelAPI_Feature> PartSetPlugin_Plugin::createFeature(string theFeatureID)
 {
-  if (theFeatureID == "new_part") {
-    return boost::shared_ptr<ModelAPI_Feature>(new PartSetPlugin_NewPart());
+  std::shared_ptr<ModelAPI_Feature> aCreated;
+  bool isCurrent = true; // to create a feature in the current document
+  if (theFeatureID == "Part") {
+    aCreated = std::shared_ptr<ModelAPI_Feature>(new PartSetPlugin_Part);
+    isCurrent = false; // allways create in the root document
+  } else if (theFeatureID == "Point") {
+    aCreated = std::shared_ptr<ModelAPI_Feature>(new PartSetPlugin_Point);
+  }
+
+  // add to a root document for the current moment
+  if (aCreated) {
+    shared_ptr<ModelAPI_Document> aDoc = isCurrent ? 
+      ModelAPI_PluginManager::get()->currentDocument() :
+      ModelAPI_PluginManager::get()->rootDocument();
+    aDoc->addFeature(aCreated, PARTS_GROUP);
   }
   // feature of such kind is not found
-  return boost::shared_ptr<ModelAPI_Feature>();
+  return aCreated;
 }
index ff577f79a3adec8e2003a43ece502a805e536255..74f6e73c02d90caa136d918152b0ce52d2f5c650 100644 (file)
@@ -13,7 +13,7 @@ class PARTSETPLUGIN_EXPORT PartSetPlugin_Plugin: public ModelAPI_Plugin
 {
 public:
   /// Creates the feature object of this plugin by the feature string ID
-  virtual boost::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID);
+  virtual std::shared_ptr<ModelAPI_Feature> createFeature(std::string theFeatureID);
 
 public:
   /// Is needed for python wrapping by swig
diff --git a/src/PartSetPlugin/PartSetPlugin_Point.cxx b/src/PartSetPlugin/PartSetPlugin_Point.cxx
new file mode 100644 (file)
index 0000000..be95256
--- /dev/null
@@ -0,0 +1,31 @@
+// File:        PartSetPlugin_Point.cxx
+// Created:     27 Mar 2014
+// Author:      Mikhail PONIKAROV
+
+#include "PartSetPlugin_Point.h"
+#include "ModelAPI_PluginManager.h"
+#include "ModelAPI_Document.h"
+#include "ModelAPI_Object.h"
+#include "ModelAPI_AttributeDouble.h"
+
+using namespace std;
+
+PartSetPlugin_Point::PartSetPlugin_Point()
+{
+}
+
+void PartSetPlugin_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 PartSetPlugin_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/PartSetPlugin/PartSetPlugin_Point.h b/src/PartSetPlugin/PartSetPlugin_Point.h
new file mode 100644 (file)
index 0000000..c5f3d2e
--- /dev/null
@@ -0,0 +1,38 @@
+// File:        PartSetPlugin_Point.h
+// Created:     3 Apr 2014
+// Author:      Mikhail PONIKAROV
+
+#ifndef PartSetPlugin_Point_HeaderFile
+#define PartSetPlugin_Point_HeaderFile
+
+#include "PartSetPlugin.h"
+#include <ModelAPI_Feature.h>
+
+/// attribute name for X coordinate
+const std::string POINT_ATTR_X = "x";
+/// attribute name for Y coordinate
+const std::string POINT_ATTR_Y = "y";
+/// attribute name for Z coordinate
+const std::string POINT_ATTR_Z = "z";
+
+/**\class PartSetPlugin_Point
+ * \ingroup DataModel
+ * \brief Feature for creation of the new part in PartSet.
+ */
+class PartSetPlugin_Point: public ModelAPI_Feature
+{
+public:
+  /// Returns the kind of a feature
+  PARTSETPLUGIN_EXPORT virtual std::string getKind() {return "Point";}
+
+  /// Creates a new part document if needed
+  PARTSETPLUGIN_EXPORT virtual void execute();
+
+  /// Request for initialization of data model of the feature: adding all attributes
+  PARTSETPLUGIN_EXPORT virtual void initAttributes();
+
+  /// Use plugin manager for features creation
+  PartSetPlugin_Point();
+};
+
+#endif
diff --git a/src/PyConsole/CMakeLists.txt b/src/PyConsole/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5d8b582
--- /dev/null
@@ -0,0 +1,64 @@
+
+SET(CMAKE_AUTOMOC ON)
+
+# header files 
+SET(PROJECT_HEADERS
+  PyConsole.h
+  PyConsole_Console.h
+  PyConsole_Editor.h
+  PyConsole_EnhEditor.h
+  PyConsole_EnhInterp.h
+  PyConsole_Event.h
+  PyConsole_Interp.h
+  PyConsole_Request.h
+)
+
+SET(PROJECT_AUTOMOC 
+    ${CMAKE_CURRENT_BINARY_DIR}/PyConsole_automoc.cpp
+)
+
+# resource files / to be processed by lrelease
+SET(TEXT_RESOURCES
+  resources/PyConsole_msg_en.ts
+  resources/PyConsole_msg_fr.ts
+  resources/PyConsole_msg_ja.ts
+)
+# 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
+)
+
+SET(PROJECT_LIBRARIES
+       PyInterp
+       ${Qt5Widgets_LIBRARIES}
+)
+
+QT5_ADD_TRANSLATION(QM_RESOURCES ${TEXT_RESOURCES})
+
+SOURCE_GROUP ("Generated Files" FILES ${PROJECT_AUTOMOC} ${QM_RESOURCES})
+
+INCLUDE_DIRECTORIES(
+       ${PROJECT_SOURCE_DIR}/src/PyEvent
+       ${PROJECT_SOURCE_DIR}/src/PyInterp
+)
+
+ADD_DEFINITIONS(-DPYCONSOLE_EXPORTS -DHAVE_DEBUG_PYTHON)
+
+ADD_LIBRARY(PyConsole STATIC
+       ${PROJECT_HEADERS}
+       ${PROJECT_SOURCES}
+       ${TEXT_RESOURCES} 
+       ${QM_RESOURCES}
+)
+
+TARGET_LINK_LIBRARIES(PyConsole ${PROJECT_LIBRARIES})
+
+#INSTALL(TARGETS PyConsole DESTINATION bin)
+
+
diff --git a/src/PyConsole/PyConsole.h b/src/PyConsole/PyConsole.h
new file mode 100644 (file)
index 0000000..1df9180
--- /dev/null
@@ -0,0 +1,24 @@
+
+#if !defined ( PYCONSOLE_H )
+#define PYCONSOLE_H
+
+// ========================================================
+// set dllexport type for Win platform 
+#ifdef WIN32
+#  if defined PYCONSOLE_EXPORTS || defined PyConsole_EXPORTS
+#    define PYCONSOLE_EXPORT __declspec(dllexport)
+#  else
+#    define PYCONSOLE_EXPORT __declspec(dllimport)
+#  endif
+#else   // WIN32
+#  define PYCONSOLE_EXPORT
+#endif  // WIN32
+
+// ========================================================
+// avoid warning messages
+#ifdef WIN32
+#pragma warning (disable : 4786)
+#pragma warning (disable : 4251)
+#endif
+
+#endif // PYCONSOLE_H
diff --git a/src/PyConsole/PyConsole_Console.cxx b/src/PyConsole/PyConsole_Console.cxx
new file mode 100644 (file)
index 0000000..04e244e
--- /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.h b/src/PyConsole/PyConsole_Console.h
new file mode 100644 (file)
index 0000000..0f2a9be
--- /dev/null
@@ -0,0 +1,88 @@
+
+#ifndef PYCONSOLE_CONSOLE_H
+#define PYCONSOLE_CONSOLE_H
+
+#include "PyConsole.h"
+
+//#include <SUIT_PopupClient.h>
+#include <QWidget>
+#include <QMap>
+
+class PyConsole_Interp;
+class PyConsole_Editor;
+class PyConsole_EnhInterp;
+class QMenu;
+
+class PYCONSOLE_EXPORT PyConsole_Console : public QWidget//, public SUIT_PopupClient
+{
+  Q_OBJECT
+
+public:
+  //! Context popup menu actions flags
+  enum
+  {
+    CopyId         = 0x01,                                           //!< "Copy" menu action
+    PasteId        = 0x02,                                           //!< "Paste" menu action
+    ClearId        = 0x04,                                           //!< "Clear" menu action
+    SelectAllId    = 0x08,                                           //!< "Select All" menu action
+    DumpCommandsId = 0x16,                                           //!< "DumpCommands" menu action
+    All = CopyId | PasteId | ClearId | SelectAllId | DumpCommandsId  //!< all menu actions
+  };
+
+public:
+  PyConsole_Console( QWidget* parent, PyConsole_Interp* interp = 0 );
+  virtual ~PyConsole_Console();
+
+  //! \brief Get python interperter
+  PyConsole_Interp*   getInterp() { return myInterp; } 
+  QFont               font() const;
+  virtual void        setFont( const QFont& );
+
+  bool                isSync() const;
+  void                setIsSync( const bool );
+
+  bool                isSuppressOutput() const;
+  void                setIsSuppressOutput( const bool );
+
+  bool                isShowBanner() const;
+  void                setIsShowBanner( const bool );
+
+  void                exec( const QString& );
+  void                execAndWait( const QString& );
+
+  virtual bool        eventFilter( QObject*, QEvent* );
+
+  //! \brief Get popup client symbolic name
+  virtual QString     popupClientType() const { return QString( "PyConsole" ); }
+  virtual void        contextMenuPopup( QMenu* );
+
+  void                setMenuActions( const int );
+  int                 menuActions() const;
+
+protected:
+  void                createActions();
+  void                updateActions();
+
+  PyConsole_Console( QWidget* parent, PyConsole_Interp*,  PyConsole_Editor*);
+
+
+  PyConsole_Interp*   myInterp;    //!< python interpreter
+  PyConsole_Editor*   myEditor;    //!< python console editor widget
+  QMap<int, QAction*> myActions;   //!< menu actions list
+};
+
+/**
+ * Enhance console object providing auto-completion.
+ * Similar to PyConsole_Console except that an enhanced interpreter and enhanced editor
+ * are encapsulated.
+ */
+class PYCONSOLE_EXPORT PyConsole_EnhConsole: public PyConsole_Console
+{
+  Q_OBJECT
+
+public:
+  PyConsole_EnhConsole( QWidget* parent, PyConsole_EnhInterp* interp = 0);
+  virtual ~PyConsole_EnhConsole() {}
+};
+
+#endif // PYCONSOLE_CONSOLE_H
diff --git a/src/PyConsole/PyConsole_Editor.cxx b/src/PyConsole/PyConsole_Editor.cxx
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.h b/src/PyConsole/PyConsole_Editor.h
new file mode 100644 (file)
index 0000000..f3324dc
--- /dev/null
@@ -0,0 +1,72 @@
+
+#ifndef PYCONSOLE_EDITOR_H
+#define PYCONSOLE_EDITOR_H
+
+#include "PyConsole.h"
+
+#include <QTextEdit>
+
+class PyConsole_Interp;
+class PyInterp_Request;
+class QEventLoop;
+
+class PYCONSOLE_EXPORT PyConsole_Editor : public QTextEdit
+{
+  Q_OBJECT;
+
+public:
+  PyConsole_Editor( PyConsole_Interp* theInterp, QWidget *theParent = 0 );
+  ~PyConsole_Editor();
+  
+  virtual void   addText( const QString& str, const bool newBlock = false, const bool isError = false );
+  bool           isCommand( const QString& str ) const;
+
+  virtual void   exec( const QString& command );
+  void           execAndWait( const QString& command );
+
+  bool           isSync() const;
+  void           setIsSync( const bool );
+
+  bool           isSuppressOutput() const;
+  void           setIsSuppressOutput(const bool);
+
+  bool           isShowBanner() const;
+  void           setIsShowBanner( const bool );
+
+  virtual QSize  sizeHint() const;
+
+public slots:
+    void           cut();
+    void           paste();
+    void           clear();
+    void           handleReturn();
+    void           onPyInterpChanged( PyConsole_Interp* );
+    //.void           dump();
+
+protected:
+  virtual void   dropEvent( QDropEvent* event );
+  virtual void   mouseReleaseEvent( QMouseEvent* event );
+  virtual void   keyPressEvent ( QKeyEvent* event);
+  virtual void   customEvent( QEvent* event);
+
+  virtual PyInterp_Request* createRequest( const QString& );
+
+  /** Convenience function */
+  inline int promptSize() const { return myPrompt.size(); }
+
+  PyConsole_Interp* myInterp;           //!< python interpreter
+
+  QString           myCommandBuffer;    //!< python command buffer
+  QString           myCurrentCommand;   //!< currently being printed command
+  QString           myPrompt;           //!< current command line prompt
+  int               myCmdInHistory;     //!< current history command index
+  QStringList       myHistory;          //!< commands history buffer
+  QEventLoop*       myEventLoop;        //!< internal event loop
+  QString           myBanner;           //!< current banner
+  bool              myShowBanner;       //!< 'show banner' flag
+  QStringList       myQueue;            //!< python commands queue
+  bool              myIsSync;           //!< synchronous mode flag
+  bool              myIsSuppressOutput; //!< suppress output flag
+};
+
+#endif // PYCONSOLE_EDITOR_H
diff --git a/src/PyConsole/PyConsole_EnhEditor.cxx b/src/PyConsole/PyConsole_EnhEditor.cxx
new file mode 100644 (file)
index 0000000..2dd8207
--- /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 (int 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.h b/src/PyConsole/PyConsole_EnhEditor.h
new file mode 100644 (file)
index 0000000..03b5e19
--- /dev/null
@@ -0,0 +1,78 @@
+
+
+#ifndef PYCONSOLE_ENHEDITOR_H_
+#define PYCONSOLE_ENHEDITOR_H_
+
+#include "PyConsole.h"
+
+#include "PyConsole_Editor.h"
+#include <QObject>
+#include <queue>
+
+class PyConsole_EnhInterp;
+
+/**
+ * Enhanced Python editor handling tab completion.
+ */
+class PYCONSOLE_EXPORT PyConsole_EnhEditor: public PyConsole_Editor
+{
+  Q_OBJECT;
+
+public:
+  PyConsole_EnhEditor(PyConsole_EnhInterp * interp, QWidget * parent=0);
+  virtual ~PyConsole_EnhEditor() {}
+
+signals:
+  /**
+   * Signal emitted by the editor widget when the doc string should be updated.
+   * @param doc a HTML block with the formatted doc string.
+   * TODO: for now this signal is left uncaught.
+   */
+  void updateDoc(QString doc);
+
+protected:
+  /** List of separators identifying the last parsable token for completion */
+  static const std::vector<QString> SEPARATORS;
+
+  /** Maximum number of completions shown at once */
+  static const int MAX_COMPLETIONS = 70;
+
+  /** Are we in completion mode */
+  bool _tab_mode;
+
+  /** String on which the dir() comamnd is executed */
+  QString _compl_before_point;
+  /** String on which the results of the dir() are matched */
+  QString _compl_after_point;
+
+  /** Cursor position when <TAB> is hit */
+  int _cursor_pos;
+
+  /** Are we currently pasting several lines */
+  bool _multi_line_paste;
+
+  /** Queue of lines being pasted */
+  std::queue<QString> _multi_line_content;
+
+  // Overrides:
+  virtual void   keyPressEvent ( QKeyEvent* event);
+  virtual void   customEvent( QEvent* event);
+  virtual void   mousePressEvent( QMouseEvent* event );
+  virtual void   insertFromMimeData(const QMimeData * source);
+
+  virtual PyInterp_Request* createTabRequest( const QString& input );
+  virtual void handleTab();
+  virtual void handleBackTab();
+  virtual void clearCompletion();
+  virtual void formatCompletion(const std::vector<QString> & matches, QString & result) const;
+  virtual QString formatDocHTML(const QString & doc) const;
+
+  virtual void multilinePaste(const QString & s);
+  virtual void multiLineProcessNextLine();
+
+private:
+  void extractCommon(const std::vector<QString> & matches, QString & result) const;
+
+};
+
+#endif /* PYCONSOLE_ENHEDITOR_H_ */
diff --git a/src/PyConsole/PyConsole_EnhInterp.cxx b/src/PyConsole/PyConsole_EnhInterp.cxx
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.h b/src/PyConsole/PyConsole_EnhInterp.h
new file mode 100644 (file)
index 0000000..1af977c
--- /dev/null
@@ -0,0 +1,47 @@
+
+
+#ifndef PYCONSOLE_ENHINTERP_H_
+#define PYCONSOLE_ENHINTERP_H_
+
+#include "PyConsole.h"
+
+#include <Python.h>
+#include "PyConsole_Interp.h"
+
+#include <vector>
+#include <QString>
+
+/**
+ * Enhanced Python interpreter used for auto-completion.
+ * This extends PyConsole_Interp with an API wrapping the Python dir() command nicely.
+ */
+class PYCONSOLE_EXPORT PyConsole_EnhInterp: public PyConsole_Interp
+{
+public:
+  PyConsole_EnhInterp()
+    : PyConsole_Interp(), _last_matches(0), _doc_str("")
+    {}
+
+  virtual ~PyConsole_EnhInterp() {}
+
+  const std::vector<QString>& getLastMatches() const { return _last_matches; }
+  const QString & getDocStr() const                  { return _doc_str; }
+
+  virtual int runDirCommand(const QString& dirArgument, const QString& startMatch);
+  virtual void clearCompletion();
+
+protected:
+  /** Hard coded list of Python keywords */
+  static const std::vector<QString> PYTHON_KEYWORDS;
+
+  /** Last computed matches */
+  std::vector<QString> _last_matches;
+  /** Doc string of the first match - when only one match it will be displayed by the Editor*/
+  QString _doc_str;
+
+  virtual int runDirAndExtract(const QString& dirArgument, const QString & startMatch,
+      std::vector<QString> & result, bool discardSwig=true) const;
+
+};
+
+#endif /* PYCONSOLE_ENHINTERP_H_ */
diff --git a/src/PyConsole/PyConsole_Event.cxx b/src/PyConsole/PyConsole_Event.cxx
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.h b/src/PyConsole/PyConsole_Event.h
new file mode 100644 (file)
index 0000000..84771b8
--- /dev/null
@@ -0,0 +1,48 @@
+
+
+#ifndef PYCONSOLE_EVENT_H
+#define PYCONSOLE_EVENT_H
+
+#include "PyConsole.h"
+
+#include <QEvent>
+#include <QString>
+
+/*!
+  \class PrintEvent
+  \brief Python command output backend event.
+  \internal
+*/
+class PrintEvent : public QEvent
+{
+public:
+  static const int EVENT_ID = 65432;
+
+  /*!
+    \brief Constructor
+    \param c message text (python trace)
+    \param isError default to false - if true indicates that an error is being printed.
+  */
+  PrintEvent( const char* c, bool isError = false) :
+    QEvent( (QEvent::Type)EVENT_ID ), myText( c ), errorFlag(isError)
+  {}
+
+  /*!
+    \brief Get message
+    \return message text (python trace)
+  */
+  QString text() const { return myText; }
+
+  /**
+   * @return true if this is an error message
+   */
+  bool isError() const { return errorFlag; }
+
+protected:
+  QString myText; //!< Event message (python trace)
+
+  /** Set to true if an error msg is to be displayed */
+  bool errorFlag;
+};
+
+#endif // PYCONSOLE_EVENT_H
diff --git a/src/PyConsole/PyConsole_Interp.cxx b/src/PyConsole/PyConsole_Interp.cxx
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.h b/src/PyConsole/PyConsole_Interp.h
new file mode 100644 (file)
index 0000000..d65c9e6
--- /dev/null
@@ -0,0 +1,20 @@
+
+#ifndef PYCONSOLE_INTERP_H
+#define PYCONSOLE_INTERP_H
+
+#include "PyConsole.h"
+
+#include <PyInterp_Interp.h>   /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!!
+
+class PYCONSOLE_EXPORT PyConsole_Interp : public PyInterp_Interp
+{
+public:
+  PyConsole_Interp();
+  ~PyConsole_Interp();
+
+protected:
+  virtual bool initState();
+  virtual bool initContext();  
+};
+
+#endif // PYCONSOLE_INTERP_H
diff --git a/src/PyConsole/PyConsole_Request.cxx b/src/PyConsole/PyConsole_Request.cxx
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.h b/src/PyConsole/PyConsole_Request.h
new file mode 100644 (file)
index 0000000..da5f94a
--- /dev/null
@@ -0,0 +1,86 @@
+
+
+
+#ifndef PYCONSOLE_REQUEST_H_
+#define PYCONSOLE_REQUEST_H_
+
+#include "PyConsole.h"
+#include "PyInterp_Request.h"
+
+#include <vector>
+#include <QString>
+#include <QEvent>
+
+class PyInterp_Interp;
+class PyConsole_Editor;
+
+/*!
+  \class ExecCommand
+  \brief Python command execution request.
+  \internal
+*/
+class ExecCommand : public PyInterp_LockRequest
+{
+public:
+  /*!
+    \brief Constructor.
+
+    Creates new python command execution request.
+    \param theInterp   python interpreter
+    \param theCommand  python command
+    \param theListener widget to get the notification messages
+    \param sync        if True the request is processed synchronously
+  */
+  ExecCommand( PyInterp_Interp*        theInterp,
+               const QString&          theCommand,
+               PyConsole_Editor*       theListener,
+               bool                    sync = false );
+
+protected:
+  /*!
+    \brief Execute the python command in the interpreter and
+           get its execution status.
+  */
+  virtual void execute();
+
+  /*!
+    \brief Create and return a notification event.
+    \return new notification event
+  */
+  virtual QEvent* createEvent();
+
+private:
+  QString myCommand;   //!< Python command
+  int     myState;     //!< Python command execution status
+};
+
+class PyConsole_EnhInterp;
+class PyConsole_EnhEditor;
+
+class CompletionCommand : public PyInterp_LockRequest
+{
+public:
+  CompletionCommand( PyConsole_EnhInterp*      theInterp,
+               const QString&          input,
+               const QString&          startMatch,
+               PyConsole_EnhEditor*    theListener,
+               bool                    sync = false );
+
+
+protected:
+  /** List of separators identifying the last parsable token for completion */
+  static const std::vector<QString> SEPARATORS;
+
+  /** String to be passed to the dir() command */
+  QString _dirArg;
+  /** Begining of the command (as typed by the user) */
+  QString _startMatch;
+  /** was the completion command successful */
+  bool _tabSuccess;
+
+  virtual void execute();
+  virtual QEvent* createEvent();
+
+};
+
+#endif /* PYCONSOLE_REQUEST_H_ */
diff --git a/src/PyConsole/resources/PyConsole_msg_en.ts b/src/PyConsole/resources/PyConsole_msg_en.ts
new file mode 100644 (file)
index 0000000..85393a2
--- /dev/null
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="en_US">
+<context>
+    <name>PyConsole_Console</name>
+    <message>
+        <location filename="../PyConsole_Console.cxx" line="216"/>
+        <source>EDIT_COPY_CMD</source>
+        <translation>&amp;Copy</translation>
+    </message>
+    <message>
+        <location filename="../PyConsole_Console.cxx" line="221"/>
+        <source>EDIT_PASTE_CMD</source>
+        <translation>&amp;Paste</translation>
+    </message>
+    <message>
+        <location filename="../PyConsole_Console.cxx" line="226"/>
+        <source>EDIT_CLEAR_CMD</source>
+        <translation>Clea&amp;r</translation>
+    </message>
+    <message>
+        <location filename="../PyConsole_Console.cxx" line="231"/>
+        <source>EDIT_SELECTALL_CMD</source>
+        <translation>Select &amp;All</translation>
+    </message>
+    <message>
+        <source>EDIT_DUMPCOMMANDS_CMD</source>
+        <translation>D&amp;ump commands</translation>
+    </message>
+</context>
+<context>
+    <name>PyConsole_Editor</name>
+    <message>
+        <source>TOT_DUMP_PYCOMMANDS</source>
+        <translation>Dump commands</translation>
+    </message>
+    <message>
+        <source>PYTHON_FILES_FILTER</source>
+        <translation>PYTHON Files (*.py)</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/PyConsole/resources/PyConsole_msg_fr.ts b/src/PyConsole/resources/PyConsole_msg_fr.ts
new file mode 100644 (file)
index 0000000..9109115
--- /dev/null
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="fr_FR">
+<context>
+    <name>PyConsole_Console</name>
+    <message>
+        <location filename="../PyConsole_Console.cxx" line="216"/>
+        <source>EDIT_COPY_CMD</source>
+        <translation>&amp;Copier</translation>
+    </message>
+    <message>
+        <location filename="../PyConsole_Console.cxx" line="221"/>
+        <source>EDIT_PASTE_CMD</source>
+        <translation>C&amp;oller</translation>
+    </message>
+    <message>
+        <location filename="../PyConsole_Console.cxx" line="226"/>
+        <source>EDIT_CLEAR_CMD</source>
+        <translation>&amp;Effacer</translation>
+    </message>
+    <message>
+        <location filename="../PyConsole_Console.cxx" line="231"/>
+        <source>EDIT_SELECTALL_CMD</source>
+        <translation>&amp;Tout sélectionner</translation>
+    </message>
+    <message>
+        <source>EDIT_DUMPCOMMANDS_CMD</source>
+        <translation>&amp;Générer le script des commandes</translation>
+    </message>
+</context>
+<context>
+    <name>PyConsole_Editor</name>
+    <message>
+        <source>TOT_DUMP_PYCOMMANDS</source>
+        <translation>&amp;Générer le script des commandes</translation>
+    </message>
+    <message>
+        <source>PYTHON_FILES_FILTER</source>
+        <translation>Fichiers PYTHON (*.py)</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/PyConsole/resources/PyConsole_msg_ja.ts b/src/PyConsole/resources/PyConsole_msg_ja.ts
new file mode 100644 (file)
index 0000000..c8d0fb1
--- /dev/null
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS>
+  <context>
+    <name>PyConsole_Console</name>
+    <message>
+      <location filename="../PyConsole_Console.cxx" line="216"/>
+      <source>EDIT_COPY_CMD</source>
+      <translation>コピー(&amp;C)</translation>
+    </message>
+    <message>
+      <location filename="../PyConsole_Console.cxx" line="221"/>
+      <source>EDIT_PASTE_CMD</source>
+      <translation>貼り付け(&amp;P)</translation>
+    </message>
+    <message>
+      <location filename="../PyConsole_Console.cxx" line="226"/>
+      <source>EDIT_CLEAR_CMD</source>
+      <translation>削除(&amp;r)</translation>
+    </message>
+    <message>
+      <location filename="../PyConsole_Console.cxx" line="231"/>
+      <source>EDIT_SELECTALL_CMD</source>
+      <translation>すべて選択します。(&amp;A)</translation>
+    </message>
+    <message>
+      <source>EDIT_DUMPCOMMANDS_CMD</source>
+      <translation>スクリプト コマンドを生成します。(&amp;u)</translation>
+    </message>
+  </context>
+  <context>
+    <name>PyConsole_Editor</name>
+    <message>
+      <source>TOT_DUMP_PYCOMMANDS</source>
+      <translation>スクリプト コマンドを生成します。</translation>
+    </message>
+    <message>
+      <source>PYTHON_FILES_FILTER</source>
+      <translation>ファイル (*.py) PYTHON</translation>
+    </message>
+  </context>
+</TS>
diff --git a/src/PyEvent/CMakeLists.txt b/src/PyEvent/CMakeLists.txt
new file mode 100644 (file)
index 0000000..393b797
--- /dev/null
@@ -0,0 +1,24 @@
+
+SET(PROJECT_HEADERS 
+       PyEvent.h 
+       PyEvent_Event.h 
+       PyEvent_EventFilter.h
+)
+
+SET(PROJECT_SOURCES 
+       PyEvent_Event.cxx 
+       PyEvent_EventFilter.cxx
+)
+
+#INCLUDE_DIRECTORIES(${Qt5Widgets_INCLUDES})
+
+ADD_DEFINITIONS(-DPYEVENT_EXPORTS)
+
+ADD_LIBRARY(PyEvent STATIC 
+       ${PROJECT_SOURCES} 
+       ${PROJECT_HEADERS} 
+)
+
+TARGET_LINK_LIBRARIES(PyEvent ${Qt5Widgets_LIBRARIES})
+
+#INSTALL(TARGETS PyEvent DESTINATION bin)
diff --git a/src/PyEvent/PyEvent.h b/src/PyEvent/PyEvent.h
new file mode 100644 (file)
index 0000000..9c2f34f
--- /dev/null
@@ -0,0 +1,19 @@
+
+#if !defined ( PYEVENT_H )
+#define PYEVENT_H
+
+#ifdef WIN32
+#  if defined PYEVENT_EXPORTS || defined PyEvent_EXPORTS
+#    define PYEVENT_EXPORT __declspec(dllexport)
+#  else
+#    define PYEVENT_EXPORT __declspec(dllimport)
+#  endif
+#else               //WIN32
+#  define PYEVENT_EXPORT
+#endif              //WIN32
+
+#if defined WIN32
+#pragma warning ( disable: 4251 )
+#endif
+
+#endif // PYEVENT_H
diff --git a/src/PyEvent/PyEvent_Event.cxx b/src/PyEvent/PyEvent_Event.cxx
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.h b/src/PyEvent/PyEvent_Event.h
new file mode 100644 (file)
index 0000000..ed1ea7b
--- /dev/null
@@ -0,0 +1,204 @@
+
+#ifndef PyEvent_PYEVENT_H
+#define PyEvent_PYEVENT_H
+
+#include "PyEvent.h"
+
+#include <QEvent>
+
+//! SALOME custom event type
+#define PyEvent_EVENT QEvent::Type( QEvent::User + 10000 )
+
+class PYEVENT_EXPORT PyEvent_CustomEvent : public QEvent
+{
+public:
+  PyEvent_CustomEvent( int type );
+  PyEvent_CustomEvent( QEvent::Type type, void* data );
+
+  void* data() const;
+  void  setData( void* data );
+
+private:
+  void *d;   //!< internal data
+};
+
+class QSemaphore;
+
+class PYEVENT_EXPORT PyEvent_Event
+{
+public:
+  PyEvent_Event();
+  virtual ~PyEvent_Event();
+
+  void            ExecutePostedEvent();
+  virtual void    Execute() = 0;
+
+  static bool     IsSessionThread();
+  void            process();
+
+protected:
+  void            processed();
+  static void     GetSessionThread();
+
+private:
+  QSemaphore*     mySemaphore;     //!< internal semaphore
+};
+
+template<class TObject, typename TRes> class TMemFunEvent : public PyEvent_Event
+{
+public:
+  typedef TRes TResult;
+  TResult myResult;
+  typedef TResult (TObject::* TAction)();
+  TMemFunEvent(TObject* theObject, TAction theAction, 
+               TResult theResult = TResult()):
+    myObject(theObject),
+    myAction(theAction),
+    myResult(theResult)
+  {}
+  virtual void Execute()
+  {
+    myResult = (myObject->*myAction)();
+  }
+private:
+  TObject* myObject;
+  TAction  myAction;
+};
+
+template<class TObject> class TVoidMemFunEvent : public PyEvent_Event
+{
+public:
+  typedef void (TObject::* TAction)();
+  TVoidMemFunEvent(TObject* theObject, TAction theAction):
+    myObject(theObject),
+    myAction(theAction)
+  {}
+  virtual void Execute()
+  {
+    (myObject->*myAction)();
+  }
+private:
+  TObject* myObject;
+  TAction myAction;
+};
+
+template<class TObject, typename TRes, typename TArg, typename TStoreArg = TArg> 
+class TMemFun1ArgEvent : public PyEvent_Event
+{
+public:
+  typedef TRes TResult;
+  TResult myResult;
+  typedef TResult (TObject::* TAction)(TArg);
+  TMemFun1ArgEvent(TObject* theObject, TAction theAction, TArg theArg, 
+                   TResult theResult = TResult()):
+    myObject(theObject),
+    myAction(theAction),
+    myResult(theResult),
+    myArg(theArg)
+  {}
+  virtual void Execute()
+  {
+    myResult = (myObject->*myAction)(myArg);
+  }
+private:
+  TObject* myObject;
+  TAction myAction;
+  TStoreArg myArg;
+};
+
+template<class TObject, typename TArg, typename TStoreArg = TArg> 
+class TVoidMemFun1ArgEvent : public PyEvent_Event
+{
+public:
+  typedef void (TObject::* TAction)(TArg);
+  TVoidMemFun1ArgEvent(TObject* theObject, TAction theAction, TArg theArg):
+    myObject(theObject),
+    myAction(theAction),
+    myArg(theArg)
+  {}
+  virtual void Execute()
+  {
+    (myObject->*myAction)(myArg);
+  }
+private:
+  TObject* myObject;
+  TAction myAction;
+  TStoreArg myArg;
+};
+
+template<class TObject, typename TRes, typename TArg, typename TArg1, typename TStoreArg = TArg, typename TStoreArg1 = TArg1>
+class TMemFun2ArgEvent: public PyEvent_Event
+{
+public:
+  typedef TRes TResult;
+  TResult myResult;
+  typedef TResult (TObject::* TAction)(TArg,TArg1);
+  TMemFun2ArgEvent(TObject* theObject, TAction theAction, 
+                   TArg theArg, TArg1 theArg1,
+                   TResult theResult = TResult()):
+    myObject(theObject),
+    myAction(theAction),
+    myResult(theResult),
+    myArg(theArg),
+    myArg1(theArg1)
+  {}
+  virtual void Execute()
+  {
+    myResult = (myObject->*myAction)(myArg,myArg1);
+  }
+private:
+  TObject* myObject;
+  TAction myAction;
+  TStoreArg myArg;
+  TStoreArg1 myArg1;
+};
+
+template<class TObject, typename TArg, typename TArg1, typename TStoreArg = TArg, typename TStoreArg1 = TArg1>
+class TVoidMemFun2ArgEvent : public PyEvent_Event
+{
+public:
+  typedef void (TObject::* TAction)(TArg,TArg1);
+  TVoidMemFun2ArgEvent(TObject* theObject, TAction theAction, TArg theArg, TArg1 theArg1):
+    myObject(theObject),
+    myAction(theAction),
+    myArg(theArg),
+    myArg1(theArg1)
+  {}
+  virtual void Execute()
+  {
+    (myObject->*myAction)(myArg,myArg1);
+  }
+private:
+  TObject* myObject;
+  TAction myAction;
+  TStoreArg myArg;
+  TStoreArg1 myArg1;
+};
+
+template<class TEvent> inline typename TEvent::TResult ProcessEvent(TEvent* theEvent)
+{
+  typename TEvent::TResult aResult;
+  if(PyEvent_Event::IsSessionThread()) {
+    theEvent->Execute();
+    aResult = theEvent->myResult;
+  }
+  else {
+    theEvent->process();
+    aResult = theEvent->myResult;
+  }
+  delete theEvent;
+  return aResult;
+}
+
+inline void ProcessVoidEvent(PyEvent_Event* theEvent)
+{
+  if(PyEvent_Event::IsSessionThread()) {
+    theEvent->Execute();
+  }
+  else {
+    theEvent->process();
+  }
+  delete theEvent;
+}
+
+#endif // PyEvent_PYEVENT_H
diff --git a/src/PyEvent/PyEvent_EventFilter.cxx b/src/PyEvent/PyEvent_EventFilter.cxx
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.h b/src/PyEvent/PyEvent_EventFilter.h
new file mode 100644 (file)
index 0000000..23132de
--- /dev/null
@@ -0,0 +1,43 @@
+
+#ifndef PyEvent_EVENTFILTER_H
+#define PyEvent_EVENTFILTER_H
+
+#include "PyEvent.h"
+#include <QObject>
+
+#if defined WIN32
+#pragma warning( disable: 4251 )
+#endif
+
+class PyEvent_Event;
+
+/*!
+  Event filter class for QApplication object that handles custom events posted by PyEvent_Event objects.
+  It assumes that such custom events are alwys posted, not sent. 
+  This event filter can be installed by any application that intends to use PyEvent_Event mechanism asynchronously.
+  This class replaced SalomeApp_EventFilter.
+*/
+class PYEVENT_EXPORT PyEvent_EventFilter: public QObject 
+{
+public:
+  static void Init();
+  static void Destroy();
+
+protected:
+  PyEvent_EventFilter();
+  virtual ~PyEvent_EventFilter();
+
+private:
+  /*! global event filter for qapplication */
+  virtual bool eventFilter( QObject* o, QEvent* e );
+  void processEvent( PyEvent_Event* );
+
+private:
+  static PyEvent_EventFilter* myFilter;
+};
+
+#if defined WIN32
+#pragma warning( default: 4251 )
+#endif
+
+#endif
diff --git a/src/PyInterp/CMakeLists.txt b/src/PyInterp/CMakeLists.txt
new file mode 100644 (file)
index 0000000..09585eb
--- /dev/null
@@ -0,0 +1,46 @@
+
+SET(CMAKE_AUTOMOC ON)
+
+# header files 
+SET(PROJECT_HEADERS
+  PyInterp.h
+  PyInterp_Dispatcher.h
+  PyInterp_Event.h
+  PyInterp_Interp.h
+  PyInterp_Request.h
+  PyInterp_Watcher.h
+)
+
+SET(PROJECT_AUTOMOC 
+    ${CMAKE_CURRENT_BINARY_DIR}/PyInterp_automoc.cpp
+)
+
+# sources / static
+SET(PROJECT_SOURCES
+  PyInterp_Dispatcher.cxx
+  PyInterp_Event.cxx
+  PyInterp_Interp.cxx
+  PyInterp_Request.cxx
+)
+
+SET(PROJECT_LIBRARIES
+       PyEvent
+       ${Qt5Core_LIBRARIES}
+)
+
+SOURCE_GROUP ("Generated Files" FILES ${PROJECT_AUTOMOC})
+
+ADD_DEFINITIONS(-DPYINTERP_EXPORTS -DHAVE_DEBUG_PYTHON)
+
+INCLUDE_DIRECTORIES(
+       ${PROJECT_SOURCE_DIR}/src/PyEvent
+)
+
+ADD_LIBRARY(PyInterp STATIC    
+       ${PROJECT_SOURCES} 
+       ${PROJECT_HEADERS} 
+)
+
+TARGET_LINK_LIBRARIES(PyInterp ${PROJECT_LIBRARIES})
+
+#INSTALL(TARGETS PyInterp DESTINATION bin)
diff --git a/src/PyInterp/PyInterp.h b/src/PyInterp/PyInterp.h
new file mode 100644 (file)
index 0000000..4977fce
--- /dev/null
@@ -0,0 +1,40 @@
+
+#if !defined ( PYINTERP_H )
+#define PYINTERP_H
+
+// ========================================================
+// set dllexport type for Win platform 
+#ifdef WIN32
+# if defined PYINTERP_EXPORTS || defined PyInterp_EXPORTS
+#  define PYINTERP_EXPORT __declspec(dllexport)
+# else
+#  define PYINTERP_EXPORT __declspec(dllimport)
+# endif
+#else   // WIN32
+# define PYINTERP_EXPORT
+#endif  // WIN32
+
+// ========================================================
+// little trick - if we do not have debug python libraries
+#ifdef _DEBUG
+ #ifndef HAVE_DEBUG_PYTHON
+  #undef _DEBUG
+ #endif
+#endif
+
+#include <Python.h>
+
+#ifdef _DEBUG
+ #ifndef HAVE_DEBUG_PYTHON
+  #define _DEBUG
+ #endif
+#endif
+
+// ========================================================
+// avoid warning messages
+#ifdef WIN32
+#pragma warning (disable : 4786)
+#pragma warning (disable : 4251)
+#endif
+
+#endif // PYINTERP_H
diff --git a/src/PyInterp/PyInterp_Dispatcher.cxx b/src/PyInterp/PyInterp_Dispatcher.cxx
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.h b/src/PyInterp/PyInterp_Dispatcher.h
new file mode 100644 (file)
index 0000000..fa98d30
--- /dev/null
@@ -0,0 +1,46 @@
+
+#ifndef PYINTERP_DISPATCHER_H
+#define PYINTERP_DISPATCHER_H
+
+#include "PyInterp.h"   // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
+
+#include "PyInterp_Request.h"   // full include instead of forward declaration
+                                // everyone inc'ing the Dispatcher will get the requests for free.
+
+#include <QMutex>
+#include <QThread>
+#include <QQueue>
+
+class QObject;
+class PyInterp_Watcher;
+
+class PYINTERP_EXPORT PyInterp_Dispatcher : protected QThread
+{
+  PyInterp_Dispatcher(); // private constructor
+
+public:
+  static PyInterp_Dispatcher* Get();
+
+  virtual                     ~PyInterp_Dispatcher();
+
+  bool                        IsBusy() const;  
+  void                        Exec( PyInterp_Request* );
+
+private:
+  virtual void                run();
+  void                        processRequest( PyInterp_Request* );
+  void                        objectDestroyed( const QObject* );
+
+private:
+  typedef PyInterp_Request*   RequestPtr;
+
+  QQueue<RequestPtr>          myQueue;
+  QMutex                      myQueueMutex;
+  PyInterp_Watcher*           myWatcher;
+
+  static PyInterp_Dispatcher* myInstance;
+
+  friend class PyInterp_Watcher;
+};
+
+#endif // PYINTERP_DISPATCHER_H
diff --git a/src/PyInterp/PyInterp_Event.cxx b/src/PyInterp/PyInterp_Event.cxx
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.h b/src/PyInterp/PyInterp_Event.h
new file mode 100644 (file)
index 0000000..01bab92
--- /dev/null
@@ -0,0 +1,52 @@
+
+
+#ifndef PYINTERP_EVENT_H
+#define PYINTERP_EVENT_H
+
+#include "PyInterp.h"
+
+#include <PyEvent_Event.h>
+
+#include <QEvent>
+
+class PyInterp_Request;
+
+class PyInterp_ExecuteEvent: public PyEvent_Event
+{
+public:
+  PyInterp_ExecuteEvent( PyInterp_Request* r )
+    : myRequest( r ) {}
+
+  virtual void Execute();
+
+protected:
+  PyInterp_Request* myRequest;
+};
+
+/**
+ * Events thrown by the interpreter having executed a command and indicating
+ * the return status.
+ */
+class PYINTERP_EXPORT PyInterp_Event : public QEvent
+{
+  PyInterp_Event();
+  PyInterp_Event( const PyInterp_Event& );
+
+public:
+  //Execution state
+  enum { ES_NOTIFY = QEvent::User + 5000, ES_OK, ES_ERROR, ES_INCOMPLETE,
+         ES_TAB_COMPLETE_OK, ES_TAB_COMPLETE_ERR, ES_LAST };
+
+  PyInterp_Event( int type, PyInterp_Request* request )
+    : QEvent( (QEvent::Type)type ), myRequest( request ) {}
+
+  virtual ~PyInterp_Event();
+
+  PyInterp_Request* GetRequest() const { return myRequest; }
+  operator PyInterp_Request*() const { return myRequest; }
+
+private:
+  PyInterp_Request* myRequest;
+};
+
+#endif // PYINTERP_EVENT_H
diff --git a/src/PyInterp/PyInterp_Interp.cxx b/src/PyInterp/PyInterp_Interp.cxx
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.h b/src/PyInterp/PyInterp_Interp.h
new file mode 100644 (file)
index 0000000..d92bc4f
--- /dev/null
@@ -0,0 +1,95 @@
+
+#ifndef PYINTERP_INTERP_H
+#define PYINTERP_INTERP_H
+
+#include "PyInterp.h"   // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
+
+#include <list>
+#include <string>
+
+class PYINTERP_EXPORT PyLockWrapper
+{
+  PyThreadState* myThreadState;
+  PyThreadState* mySaveThreadState;
+  PyGILState_STATE _savestate;
+public:
+  PyLockWrapper(PyThreadState* theThreadState);
+  ~PyLockWrapper();
+};
+
+typedef void PyOutChanged(void* data,char * c);
+
+class PYINTERP_EXPORT PyInterp_Interp
+{
+public:
+  static int _argc;
+  static char* _argv[];
+  static PyObject *builtinmodule;
+  static PyThreadState *_gtstate;
+  static PyInterpreterState *_interp;
+  
+  PyInterp_Interp();
+  virtual ~PyInterp_Interp();
+  
+  void initialize();
+
+  virtual int run(const char *command); 
+
+  PyLockWrapper GetLockWrapper();
+
+  std::string getbanner(); 
+  void setverrcb(PyOutChanged*,void*);
+  void setvoutcb(PyOutChanged*,void*);
+
+  const char * getPrevious();
+  const char * getNext();    
+
+protected:
+  PyThreadState * _tstate;
+  PyObject * _vout;
+  PyObject * _verr;
+  PyObject * _g;
+  PyObject * _codeop;
+  std::list<std::string> _history;
+  std::list<std::string>::iterator _ith;
+
+  virtual int beforeRun() { return 0; }
+  int simpleRun(const char* command, const bool addToHistory = true);
+
+  virtual bool initRun();
+  virtual void initPython();
+  virtual bool initState() = 0;
+  virtual bool initContext() = 0;  
+};
+
+class PYINTERP_EXPORT PyObjWrapper
+{
+  PyObject* myObject;
+public:
+  PyObjWrapper(PyObject* theObject) : myObject(theObject) {}
+  PyObjWrapper() : myObject(0) {}
+  virtual ~PyObjWrapper() { Py_XDECREF(myObject); }
+
+  operator PyObject*()    { return myObject;  }
+  PyObject* operator->()  { return myObject;  }
+  PyObject* get()         { return myObject;  }
+  bool operator!()        { return !myObject; }
+  bool operator==(PyObject* theObject) { return myObject == theObject; }
+  PyObject** operator&()  { return &myObject; }
+  PyObjWrapper& operator=(PyObjWrapper* theObjWrapper)
+  {
+    Py_XDECREF(myObject);
+    myObject = theObjWrapper->myObject;
+    return *this;
+  }
+};
+
+typedef struct {
+  PyObject_HEAD
+  int softspace;
+  PyOutChanged* _cb;
+  void* _data;
+  bool _iscerr;
+} PyStdOut;
+
+#endif // PYINTERP_INTERP_H
diff --git a/src/PyInterp/PyInterp_Request.cxx b/src/PyInterp/PyInterp_Request.cxx
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.h b/src/PyInterp/PyInterp_Request.h
new file mode 100644 (file)
index 0000000..9cc3a68
--- /dev/null
@@ -0,0 +1,81 @@
+
+
+#ifndef PYINTERP_REQUEST_H
+#define PYINTERP_REQUEST_H
+
+#include "PyInterp.h"
+#include "PyInterp_Event.h"
+
+#include <QMutex>
+#include <QObject>
+
+class PyInterp_Interp;
+class PyInterp_Watcher;
+class PyInterp_Dispatcher;
+class PyInterp_ExecuteEvent;
+class PyConsole_Editor;
+
+class PYINTERP_EXPORT PyInterp_Request
+{
+  friend class PyInterp_Dispatcher;
+  friend class PyInterp_ExecuteEvent;
+
+  PyInterp_Request();
+  PyInterp_Request( const PyInterp_Request& );
+
+protected:
+  virtual ~PyInterp_Request() {};
+  // protected destructor - to control deletion of requests
+
+public:
+  PyInterp_Request( QObject* listener, bool sync = false )
+    : myIsSync( sync ), myListener( listener ) {};
+
+  static void     Destroy( PyInterp_Request* );
+  // Deletes a request
+
+  bool            IsSync() const { return myIsSync; }
+  // Returns true if this request should be processed synchronously,
+  // without putting it to a queue
+
+protected:
+  virtual void    safeExecute();
+
+  virtual void    execute() = 0;
+  // Should be redefined in successors, contains actual request code
+
+  virtual QEvent* createEvent();
+  // This method can be overridden to customize notification event creation
+
+  virtual void    processEvent( QObject* );
+
+  QObject*        listener() const { return myListener; }
+  void            setListener( QObject* );
+
+private:
+  void            process();
+
+private:
+  QMutex          myMutex;
+  bool            myIsSync;
+  QObject*        myListener;
+};
+
+class PYINTERP_EXPORT PyInterp_LockRequest : public PyInterp_Request
+{
+public:
+
+  PyInterp_LockRequest( PyInterp_Interp* interp, QObject* listener = 0, bool sync = false )
+    : PyInterp_Request( listener, sync ), myInterp( interp )
+  {}
+
+protected:
+  PyInterp_Interp*  getInterp() const { return myInterp; }
+
+  virtual void      safeExecute();
+
+private:
+  PyInterp_Interp*  myInterp;
+};
+
+#endif // PYINTERP_REQUEST_H
diff --git a/src/PyInterp/PyInterp_Watcher.h b/src/PyInterp/PyInterp_Watcher.h
new file mode 100644 (file)
index 0000000..91126b7
--- /dev/null
@@ -0,0 +1,24 @@
+
+#ifndef PYINTERP_WATCHER_H
+#define PYINTERP_WATCHER_H
+
+#include "PyInterp.h"   // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
+
+#include "PyInterp_Dispatcher.h"
+
+#include <QObject>
+
+// Private class that keeps track of destructions of request listeners
+class PYINTERP_EXPORT PyInterp_Watcher : public QObject
+{                                           
+  Q_OBJECT
+
+public:
+  PyInterp_Watcher() : QObject( 0 ) {}
+  virtual ~PyInterp_Watcher() {}
+
+public slots:
+  void onDestroyed( QObject* o ) { PyInterp_Dispatcher::Get()->objectDestroyed( o ); }
+};
+
+#endif // PYINTERP_WATCHER_H
index 5ac769adc3ad6bb765d0f36310e2972802e48830..38deae12299035d6f11d2a10c2d0f7cf3b3e0dd8 100644 (file)
@@ -1,6 +1,5 @@
 CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11)
 
-INCLUDE(Common)
 INCLUDE(FindCAS)
 
 SET(CMAKE_AUTOMOC ON)
@@ -58,6 +57,9 @@ SET(PROJECT_LIBRARIES
        ${Qt5Widgets_LIBRARIES}
        ${CAS_VIEWER}
        ${CAS_KERNEL}
+       PyConsole
+       PyInterp
+       PyEvent
 )
 
 QT5_ADD_RESOURCES(PROJECT_COMPILED_RESOURCES ${PROJECT_RESOURCES})
@@ -70,8 +72,12 @@ ADD_DEFINITIONS(${CAS_DEFINITIONS} )
 
 INCLUDE_DIRECTORIES (${PROJECT_SOURCE_DIR}/src/Event
                                         ${PROJECT_SOURCE_DIR}/src/Config
+                                        ${PROJECT_SOURCE_DIR}/src/PyInterp
+                                        ${PROJECT_SOURCE_DIR}/src/PyConsole
                                         ${CAS_INCLUDE_DIRS})
 
+LINK_DIRECTORIES($ENV{PYTHON_LIB_DIR})
+
 ADD_EXECUTABLE(XGUI WIN32 
        ${PROJECT_SOURCES} 
        ${PROJECT_HEADERS} 
index f6b7036113ed3885023f8af016a243e3719dcf4a..bb7bbd582910f650335e9cfd4ce48615353d3ae2 100644 (file)
@@ -11,6 +11,7 @@ XGUI_MainMenu::XGUI_MainMenu(XGUI_MainWindow *parent)
     : QObject(parent), myDesktop(parent)
 {
   parent->setTabPosition(Qt::TopDockWidgetArea, QTabWidget::North);
+  myGeneralPage = addWorkbench(tr("GEN_MENU_TITLE"));
 }
 
 XGUI_MainMenu::~XGUI_MainMenu(void)
index 42db895d6784e6dd30bc1c6bb7c201f8bd82fea2..c5837e757d0d50f9a87f05334a83ae4807ba7c61 100644 (file)
@@ -24,9 +24,14 @@ public:
   XGUI_Workbench* addWorkbench(const QString& theId, const QString& theText = "");
   XGUI_Workbench* findWorkbench(const QString& theId);
 
+  XGUI_Workbench* generalPage() const { return myGeneralPage; }
+
+  QDockWidget* getLastDockWindow() const { return myMenuTabs.last(); }
+
 private:
   XGUI_MainWindow* myDesktop;
   QList<QDockWidget*> myMenuTabs;
+  XGUI_Workbench* myGeneralPage;
 };
 
 #endif
index 6841b795e93d022e0a94717c284a01b7259bd97f..0e6ee35b8e93f2b1c26a53ac2cac8ce072367f34 100644 (file)
@@ -3,6 +3,9 @@
 #include "XGUI_ViewWindow.h"
 #include "XGUI_Viewer.h"
 
+#include <PyConsole_Console.h>
+#include <PyConsole_EnhInterp.h>
+
 #include <QMdiArea>
 #include <QTreeWidget>
 #include <QDockWidget>
@@ -21,7 +24,9 @@
 #include <QComboBox>
 
 XGUI_MainWindow::XGUI_MainWindow(QWidget* parent)
-    : QMainWindow(parent), myObjectBrowser(0)
+    : QMainWindow(parent), 
+    myObjectBrowser(0), 
+    myPythonConsole(0)
 {
   setWindowTitle(tr("WINDOW_TITLE"));
   myMenuBar = new XGUI_MainMenu(this);
@@ -36,25 +41,13 @@ XGUI_MainWindow::XGUI_MainWindow(QWidget* parent)
   addDockWidget(Qt::LeftDockWidgetArea, aDoc);
   //aDoc->hide();
 
-  aDoc = new QDockWidget(this);
-  aDoc->setFeatures(QDockWidget::AllDockWidgetFeatures | QDockWidget::DockWidgetVerticalTitleBar);
-  aDoc->setMinimumHeight(0);
-  aDoc->setWindowTitle("Console");
-  QTextEdit* aTextEdt = new QTextEdit(aDoc);
-  aTextEdt->setText(">>>");
-  aDoc->setWidget(aTextEdt);
-  aTextEdt->setMinimumHeight(0);
-  addDockWidget(Qt::BottomDockWidgetArea, aDoc);
-
   QMdiArea* aMdiArea = new QMdiArea(this);
   setCentralWidget(aMdiArea);
 
   myViewer = new XGUI_Viewer(this);
-  //aMdiArea->addSubWindow(new XGUI_ViewWindow(), Qt::FramelessWindowHint);
-  //aMdiArea->addSubWindow(new XGUI_ViewWindow(), Qt::FramelessWindowHint);
 
-  fillObjectBrowser();
-  addPropertyPanel();
+  //fillObjectBrowser();
+  //addPropertyPanel();
 }
 
 XGUI_MainWindow::~XGUI_MainWindow(void)
@@ -79,6 +72,40 @@ void XGUI_MainWindow::hideObjectBrowser()
   myObjectBrowser->parentWidget()->hide();
 }
 
+//******************************************************
+void XGUI_MainWindow::showPythonConsole()
+{
+  if (!myPythonConsole) {
+
+    QDockWidget* aDoc = new QDockWidget(this);
+    aDoc->setFeatures(QDockWidget::AllDockWidgetFeatures | QDockWidget::DockWidgetVerticalTitleBar);
+    aDoc->setMinimumHeight(0);
+    aDoc->setWindowTitle("Console");
+    myPythonConsole = new PyConsole_EnhConsole( aDoc, new PyConsole_EnhInterp());
+    //myPythonConsole = new QTextEdit(aDoc);
+    //myPythonConsole->setGeometry(0,0,200, 50);
+    //myPythonConsole->setText(">>>");
+    aDoc->setWidget(myPythonConsole);
+    //myPythonConsole->setMinimumHeight(0);
+    addDockWidget(Qt::TopDockWidgetArea, aDoc);
+    tabifyDockWidget(myMenuBar->getLastDockWindow(), aDoc);
+  }
+  myPythonConsole->parentWidget()->show();
+}
+
+//******************************************************
+void XGUI_MainWindow::hidePythonConsole()
+{
+  if (myPythonConsole)
+    myPythonConsole->parentWidget()->hide();
+}
+
+
+
+//******************************************************
+
+// TEST FUNCTIONS
+
 //******************************************************
 void XGUI_MainWindow::fillObjectBrowser()
 {
index a4b05f3d915aa34d0d7b7cc4446fb08053d40a9d..ec5da26a6bdeaa59134c9de3c429d4dc8f1f6227 100644 (file)
@@ -7,6 +7,7 @@ class XGUI_MainMenu;
 class XGUI_Viewer;
 class QTreeWidget;
 class QMdiArea;
+class PyConsole_EnhConsole;
 
 class XGUI_MainWindow: public QMainWindow
 {
@@ -25,6 +26,7 @@ public:
   {
     return myObjectBrowser;
   }
+
   void showObjectBrowser();
   void hideObjectBrowser();
 
@@ -35,10 +37,11 @@ public:
     return myViewer;
   }
 
+  void showPythonConsole();
+  void hidePythonConsole();
+
 private:
   //!! For test purposes only
-  //QWidget* getSubWindow();
-
   void fillObjectBrowser();
   void addPropertyPanel();
 
@@ -46,6 +49,8 @@ private:
   QTreeWidget* myObjectBrowser;
 
   XGUI_Viewer* myViewer;
+
+  PyConsole_EnhConsole* myPythonConsole;
 };
 
 #endif
index 2202683d58d01ceac9b7d94707e96723984f173d..5a8b5cb7c58d1b1b6eee6610bedc66711edcca1c 100644 (file)
@@ -272,8 +272,6 @@ XGUI_ViewPort::XGUI_ViewPort(XGUI_ViewWindow* theParent, const Handle(V3d_Viewer
     myActiveView = myPerspView;
   }
   myActiveView->SetSurfaceDetail(V3d_TEX_ALL);
-
-  //setBackground( Qtx::BackgroundData( Qt::black ) ); // set default background
 }
 
 //***********************************************
@@ -741,3 +739,41 @@ void XGUI_ViewPort::fitAll(bool theKeepScale, bool theWithZ, bool theUpd)
   activeView()->SetZSize(0.);
   emit vpTransformed( );
 }
+
+void XGUI_ViewPort::syncronizeWith( const XGUI_ViewPort* ref )
+{
+  Handle(V3d_View) refView = ref->getView();
+  Handle(V3d_View) tgtView = getView();
+
+  /*  The following params are copied:
+      - view type( ortho/persp )
+      - position of view point
+      - orientation of high point
+      - position of the eye
+      - projection vector
+      - view center ( 2D )
+      - view twist
+      - view scale
+  */
+
+  /* we'll update after setting all params */
+  tgtView->SetImmediateUpdate( Standard_False );
+
+  /* perspective */
+  if ( refView->Type() == V3d_PERSPECTIVE )
+    tgtView->SetFocale( refView->Focale() );
+
+  /* copy params */
+  Standard_Real x, y, z;
+  refView->At( x, y, z ); tgtView->SetAt( x, y, z );
+  refView->Up( x, y, z ); tgtView->SetUp( x, y, z );
+  refView->Eye( x, y, z ); tgtView->SetEye( x, y, z );
+  refView->Proj( x, y, z ); tgtView->SetProj( x, y, z );
+  refView->Center( x, y ); tgtView->SetCenter( x, y );
+  tgtView->SetScale( refView->Scale() );
+  tgtView->SetTwist( refView->Twist() );
+
+  /* update */
+  tgtView->Update();
+  tgtView->SetImmediateUpdate( Standard_True );
+}
index ccffe4f1a3f151059586973994053b23c490d751..5d73031a1605825dceac5624a8f84ba057fd6f1a 100644 (file)
@@ -55,8 +55,11 @@ public:
   {
     return myBackground;
   }
+
   void setBackground(const XGUI_ViewBackground& bgData);
 
+  void syncronizeWith( const XGUI_ViewPort* ref );
+
 signals:
   void vpChangeBackground(const XGUI_ViewBackground&);
   void vpClosed();
index 74356cac6f0747c60d6ceb97fdb90e31ee86798b..c536c7c410d674a2d1db6acadc903ec0fc0c7b07 100644 (file)
@@ -13,7 +13,7 @@
 #include <QMdiArea>
 #include <QMdiSubWindow>
 #include <QPainter>
-//#include <QTime>
+#include <QTimer>
 #include <QFileDialog>
 
 #include <TopoDS_Shape.hxx>
@@ -78,33 +78,40 @@ const char* imageCrossCursor[] = { "32 32 3 1", ". c None", "a c #000000", "# c
     "................................", "................................" };
 
 //**************************************************************************
-void ViewerToolbar::paintEvent(QPaintEvent* theEvent)
+void ViewerToolbar::repaintBackground()
 {
-  //QTime aTime;
-  //aTime.start();
   QRect aRect = rect();
   QRect aVPRect = myVPort->rect();
   QPoint aGlobPnt = mapToGlobal(aRect.topLeft());
   QPoint aPnt = myVPort->mapFromGlobal(aGlobPnt);
 
-  QRect aImgRect(
-      QRect(aPnt.x(), aPnt.y() + aVPRect.height() - aRect.height(), aRect.width(), aRect.height()));
+  QRect aImgRect(QRect(aPnt.x(), aPnt.y() + aVPRect.height() - aRect.height(), 
+                       aRect.width(), aRect.height()));
   QPainter(this).drawImage(aRect, myVPort->dumpView(aImgRect, false));
-  //QString aMsg = QString("### Painted in %1").arg(aTime.elapsed());
-  //qDebug(qPrintable(aMsg));
+}
+
+void ViewerToolbar::paintEvent(QPaintEvent* theEvent)
+{
+  repaintBackground();
+  QToolBar::paintEvent(theEvent);
 }
 
 //**************************************************************************
-void ViewerLabel::paintEvent(QPaintEvent* theEvent)
+void ViewerLabel::repaintBackground()
 {
   QRect aRect = rect();
   QRect aVPRect = myVPort->rect();
   QPoint aGlobPnt = mapToGlobal(aRect.topLeft());
   QPoint aPnt = myVPort->mapFromGlobal(aGlobPnt);
 
-  QRect aImgRect(
-      QRect(aPnt.x(), aPnt.y() + aVPRect.height() - aRect.height(), aRect.width(), aRect.height()));
+  QRect aImgRect(QRect(aPnt.x(), aPnt.y() + aVPRect.height() - aRect.height(), 
+                 aRect.width(), aRect.height()));
   QPainter(this).drawImage(aRect, myVPort->dumpView(aImgRect, false));
+}
+
+void ViewerLabel::paintEvent(QPaintEvent* theEvent)
+{
+  repaintBackground();
   QLabel::paintEvent(theEvent);
 }
 
@@ -126,7 +133,7 @@ XGUI_ViewWindow::XGUI_ViewWindow(XGUI_Viewer* theViewer, V3d_TypeOfView theType)
     myCurrPointType(XGUI::GRAVITY), 
     myPrevPointType(XGUI::GRAVITY), 
     myRotationPointSelection(false),
-    myClosable(false)
+    myClosable(true)
 {
   mySelectedPoint = gp_Pnt(0., 0., 0.);
   setFrameStyle(QFrame::Raised);
@@ -153,79 +160,75 @@ XGUI_ViewWindow::XGUI_ViewWindow(XGUI_Viewer* theViewer, V3d_TypeOfView theType)
   myGripWgt->setGeometry(BORDER_SIZE + 2, BORDER_SIZE + 2, 19, 32);
   myGripWgt->setMouseTracking(true);
   myGripWgt->installEventFilter(this);
-  connect(myViewPort, SIGNAL(vpTransformed()), myGripWgt, SLOT(update()));
-  connect(myViewPort, SIGNAL(vpUpdated()), myGripWgt, SLOT(update()));
 
     // Create Viewer management buttons
   myViewBar = new ViewerToolbar(this, myViewPort);
 
   QAction* aBtn;
 
-    // Dump view
-    aBtn = new QAction(QIcon(":pictures/occ_view_camera_dump.png"), tr("DUMP_VIEW"), myViewBar);
-    connect(aBtn, SIGNAL(triggered()), SLOT(dumpView()));
-    myViewBar->addAction(aBtn);
-    // Fit all
-    aBtn = new QAction(QIcon(":pictures/occ_view_fitall.png"), tr("FIT_ALL"), myViewBar);
-    connect(aBtn, SIGNAL(triggered()), SLOT(fitAll()));
-    myViewBar->addAction(aBtn);
-    // Fit area
-    aBtn = new QAction(QIcon(":pictures/occ_view_fitarea.png"), tr("FIT_AREA"), myViewBar);
-    connect(aBtn, SIGNAL(triggered()), SLOT(activateWindowFit()));
-    myViewBar->addAction(aBtn);
-    // Zoom
-    aBtn = new QAction(QIcon(":pictures/occ_view_zoom.png"), tr("ZOOM_VIEW"), myViewBar);
-    connect(aBtn, SIGNAL(triggered()), SLOT(activateZoom()));
-    myViewBar->addAction(aBtn);
-    // Pan
-    aBtn = new QAction(QIcon(":pictures/occ_view_pan.png"), tr("PAN_VIEW"), myViewBar);
-    connect(aBtn, SIGNAL(triggered()), SLOT(activatePanning()));
-    myViewBar->addAction(aBtn);
-    // Global Panning
-    aBtn = new QAction(QIcon(":pictures/occ_view_glpan.png"), tr("GLOB_PAN_VIEW"), myViewBar);
-    connect(aBtn, SIGNAL(triggered()), SLOT(activateGlobalPanning()));
-    myViewBar->addAction(aBtn);
-    // Rotation
-    aBtn = new QAction(QIcon(":pictures/occ_view_rotate.png"), tr("ROTATE_VIEW"), myViewBar);
-    connect(aBtn, SIGNAL(triggered()), SLOT(activateRotation()));
-    myViewBar->addAction(aBtn);
-    // Front view
-    aBtn = new QAction(QIcon(":pictures/occ_view_front.png"), tr("FRONT_VIEW"), myViewBar);
-    connect(aBtn, SIGNAL(triggered()), SLOT(frontView()));
-    myViewBar->addAction(aBtn);
-    // Back view
-    aBtn = new QAction(QIcon(":pictures/occ_view_back.png"), tr("BACK_VIEW"), myViewBar);
-    connect(aBtn, SIGNAL(triggered()), SLOT(backView()));
-    myViewBar->addAction(aBtn);
-    // Top view
-    aBtn = new QAction(QIcon(":pictures/occ_view_top.png"), tr("TOP_VIEW"), myViewBar);
-    connect(aBtn, SIGNAL(triggered()), SLOT(topView()));
-    myViewBar->addAction(aBtn);
-    // Bottom view
-    aBtn = new QAction(QIcon(":pictures/occ_view_bottom.png"), tr("BOTTOM_VIEW"), myViewBar);
-    connect(aBtn, SIGNAL(triggered()), SLOT(bottomView()));
-    myViewBar->addAction(aBtn);
-    // Left view
-    aBtn = new QAction(QIcon(":pictures/occ_view_left.png"), tr("LEFT_VIEW"), myViewBar);
-    connect(aBtn, SIGNAL(triggered()), SLOT(leftView()));
-    myViewBar->addAction(aBtn);
-    // Right view
-    aBtn = new QAction(QIcon(":pictures/occ_view_right.png"), tr("RIGHT_VIEW"), myViewBar);
-    connect(aBtn, SIGNAL(triggered()), SLOT(rightView()));
-    myViewBar->addAction(aBtn);
-    // Clone view
-    aBtn = new QAction(QIcon(":pictures/occ_view_clone.png"), tr("CLONE_VIEW"), myViewBar);
-    connect(aBtn, SIGNAL(triggered()), SLOT(cloneView()));
-    myViewBar->addAction(aBtn);
-
-    //Support copy of background on updating of viewer
-  connect(myViewPort, SIGNAL(vpTransformed()), myViewBar, SLOT(update()));
-  connect(myViewPort, SIGNAL(vpUpdated()), myViewBar, SLOT(update()));
+  // Dump view
+  aBtn = new QAction(QIcon(":pictures/occ_view_camera_dump.png"), tr("DUMP_VIEW"), myViewBar);
+  connect(aBtn, SIGNAL(triggered()), SLOT(dumpView()));
+  myViewBar->addAction(aBtn);
+  // Fit all
+  aBtn = new QAction(QIcon(":pictures/occ_view_fitall.png"), tr("FIT_ALL"), myViewBar);
+  connect(aBtn, SIGNAL(triggered()), SLOT(fitAll()));
+  myViewBar->addAction(aBtn);
+  // Fit area
+  aBtn = new QAction(QIcon(":pictures/occ_view_fitarea.png"), tr("FIT_AREA"), myViewBar);
+  connect(aBtn, SIGNAL(triggered()), SLOT(activateWindowFit()));
+  myViewBar->addAction(aBtn);
+  // Zoom
+  aBtn = new QAction(QIcon(":pictures/occ_view_zoom.png"), tr("ZOOM_VIEW"), myViewBar);
+  connect(aBtn, SIGNAL(triggered()), SLOT(activateZoom()));
+  myViewBar->addAction(aBtn);
+  // Pan
+  aBtn = new QAction(QIcon(":pictures/occ_view_pan.png"), tr("PAN_VIEW"), myViewBar);
+  connect(aBtn, SIGNAL(triggered()), SLOT(activatePanning()));
+  myViewBar->addAction(aBtn);
+  // Global Panning
+  aBtn = new QAction(QIcon(":pictures/occ_view_glpan.png"), tr("GLOB_PAN_VIEW"), myViewBar);
+  connect(aBtn, SIGNAL(triggered()), SLOT(activateGlobalPanning()));
+  myViewBar->addAction(aBtn);
+  // Rotation
+  aBtn = new QAction(QIcon(":pictures/occ_view_rotate.png"), tr("ROTATE_VIEW"), myViewBar);
+  connect(aBtn, SIGNAL(triggered()), SLOT(activateRotation()));
+  myViewBar->addAction(aBtn);
+  // Reset
+  aBtn = new QAction(QIcon(":pictures/occ_view_reset.png"), tr("RESET_VIEW"), myViewBar);
+  connect(aBtn, SIGNAL(triggered()), SLOT(reset()));
+  myViewBar->addAction(aBtn);
+  // Front view
+  aBtn = new QAction(QIcon(":pictures/occ_view_front.png"), tr("FRONT_VIEW"), myViewBar);
+  connect(aBtn, SIGNAL(triggered()), SLOT(frontView()));
+  myViewBar->addAction(aBtn);
+  // Back view
+  aBtn = new QAction(QIcon(":pictures/occ_view_back.png"), tr("BACK_VIEW"), myViewBar);
+  connect(aBtn, SIGNAL(triggered()), SLOT(backView()));
+  myViewBar->addAction(aBtn);
+  // Top view
+  aBtn = new QAction(QIcon(":pictures/occ_view_top.png"), tr("TOP_VIEW"), myViewBar);
+  connect(aBtn, SIGNAL(triggered()), SLOT(topView()));
+  myViewBar->addAction(aBtn);
+  // Bottom view
+  aBtn = new QAction(QIcon(":pictures/occ_view_bottom.png"), tr("BOTTOM_VIEW"), myViewBar);
+  connect(aBtn, SIGNAL(triggered()), SLOT(bottomView()));
+  myViewBar->addAction(aBtn);
+  // Left view
+  aBtn = new QAction(QIcon(":pictures/occ_view_left.png"), tr("LEFT_VIEW"), myViewBar);
+  connect(aBtn, SIGNAL(triggered()), SLOT(leftView()));
+  myViewBar->addAction(aBtn);
+  // Right view
+  aBtn = new QAction(QIcon(":pictures/occ_view_right.png"), tr("RIGHT_VIEW"), myViewBar);
+  connect(aBtn, SIGNAL(triggered()), SLOT(rightView()));
+  myViewBar->addAction(aBtn);
+  // Clone view
+  aBtn = new QAction(QIcon(":pictures/occ_view_clone.png"), tr("CLONE_VIEW"), myViewBar);
+  connect(aBtn, SIGNAL(triggered()), SLOT(cloneView()));
+  myViewBar->addAction(aBtn);
 
     // Create Window management buttons
   myWindowBar = new ViewerToolbar(this, myViewPort);
-  connect(myViewPort, SIGNAL(vpTransformed()), myWindowBar, SLOT(update()));
-  connect(myViewPort, SIGNAL(vpUpdated()), myWindowBar, SLOT(update()));
 
   myMinimizeBtn = new QAction(myWindowBar);
   myMinimizeBtn->setIcon(MinimizeIco);
@@ -245,6 +248,13 @@ XGUI_ViewWindow::XGUI_ViewWindow(XGUI_Viewer* theViewer, V3d_TypeOfView theType)
   myViewBar->hide();
   myWindowBar->hide();
   myGripWgt->hide();
+
+  //Support copy of background on updating of viewer
+  connect(myViewPort, SIGNAL(vpTransformed()), this, SLOT(updateToolBar()));
+  connect(myViewPort, SIGNAL(vpUpdated()), this, SLOT(updateToolBar()));
+  connect(this, SIGNAL(vpTransformationFinished(XGUI_ViewWindow::OperationType)), 
+          this, SLOT(updateToolBar()));
+
 }
 
 //****************************************************************
@@ -294,13 +304,13 @@ void XGUI_ViewWindow::changeEvent(QEvent* theEvent)
 //****************************************************************
 void XGUI_ViewWindow::onClose()
 {
-    if (parentWidget()) {
-        emit tryClosing(this);
-        if (closable()) {
-            emit closed(static_cast<QMdiSubWindow*>(parentWidget()));
-            parentWidget()->close();
-        }
+  if (parentWidget()) {
+    emit tryClosing(this);
+    if (closable()) {
+      emit closed(static_cast<QMdiSubWindow*>(parentWidget()));
+      parentWidget()->close();
     }
+  }
 }
 
 //****************************************************************
@@ -308,7 +318,7 @@ void XGUI_ViewWindow::enterEvent(QEvent* theEvent)
 {
   if (!isMinimized()) {
     myViewBar->show();
-        myWindowBar->show();
+    myWindowBar->show();
     if (!isMaximized())
       myGripWgt->show();
   }
@@ -447,8 +457,9 @@ bool XGUI_ViewWindow::eventFilter(QObject *theObj, QEvent *theEvent)
     if (processWindowControls(theObj, theEvent))
       return true;
   } else if (theObj == myViewPort) {
-    if (processViewPort(theEvent))
+    if (processViewPort(theEvent)) {
       return true;
+    }
   }
   return QFrame::eventFilter(theObj, theEvent);
 }
@@ -928,41 +939,42 @@ void XGUI_ViewWindow::setBackground(const XGUI_ViewBackground& theBackground)
 void XGUI_ViewWindow::cloneView()
 {
   QMdiSubWindow* vw = myViewer->createView();
-  //vw->show();
+  XGUI_ViewWindow* aNewWnd = static_cast<XGUI_ViewWindow*>(vw->widget());
+  aNewWnd->viewPort()->syncronizeWith(myViewPort);
   emit viewCloned( vw );
 }
 
 void XGUI_ViewWindow::dumpView()
 {
-    QString aFilter(tr("OCC_IMAGE_FILES"));
-    QString aSelectedFilter;
-    QString aFileName = QFileDialog::getSaveFileName(this, "Save picture", QString(), aFilter, &aSelectedFilter);
-    if (!aFileName.isNull()) {
-        QApplication::setOverrideCursor( Qt::WaitCursor );
-        QImage aPicture = myViewPort->dumpView();
-
-        QString aFmt = extension(aFileName).toUpper();
-        if( aFmt.isEmpty() )
-            aFmt = QString( "BMP" ); // default format
-        else if( aFmt == "JPG" )
-            aFmt = "JPEG";
-          
-        Handle(Visual3d_View) a3dView = myViewPort->getView()->View();
-        if (aFmt == "PS")
-            a3dView->Export(strdup(qPrintable(aFileName)), Graphic3d_EF_PostScript);
-        else if (aFmt == "EPS")
-            a3dView->Export(strdup(qPrintable(aFileName)), Graphic3d_EF_EnhPostScript);
-        else
-            aPicture.save( aFileName, aFmt.toLatin1() );
-        QApplication::restoreOverrideCursor();
-    }
+  QString aFilter(tr("OCC_IMAGE_FILES"));
+  QString aSelectedFilter;
+  QString aFileName = QFileDialog::getSaveFileName(this, "Save picture", QString(), aFilter, &aSelectedFilter);
+  if (!aFileName.isNull()) {
+    QApplication::setOverrideCursor( Qt::WaitCursor );
+    QImage aPicture = myViewPort->dumpView();
+
+    QString aFmt = extension(aFileName).toUpper();
+    if( aFmt.isEmpty() )
+      aFmt = QString( "BMP" ); // default format
+    else if( aFmt == "JPG" )
+      aFmt = "JPEG";
+
+    Handle(Visual3d_View) a3dView = myViewPort->getView()->View();
+    if (aFmt == "PS")
+      a3dView->Export(strdup(qPrintable(aFileName)), Graphic3d_EF_PostScript);
+    else if (aFmt == "EPS")
+      a3dView->Export(strdup(qPrintable(aFileName)), Graphic3d_EF_EnhPostScript);
+    else
+      aPicture.save( aFileName, aFmt.toLatin1() );
+    QApplication::restoreOverrideCursor();
+  }
 }
 
 void XGUI_ViewWindow::fitAll()
 {
-    emit vpTransformationStarted( FITALLVIEW );
-    myViewPort->fitAll();
-    emit vpTransformationFinished( FITALLVIEW );
+  emit vpTransformationStarted( FITALLVIEW );
+  myViewPort->fitAll();
+  emit vpTransformationFinished( FITALLVIEW );
 }
 
 /*!
@@ -977,8 +989,7 @@ void XGUI_ViewWindow::activateWindowFit()
 
   if ( myOperation != WINDOWFIT ) {
     QCursor handCursor (Qt::PointingHandCursor);
-    if( setTransformRequested ( WINDOWFIT ) )
-    {
+    if( setTransformRequested ( WINDOWFIT ) ) {
       myViewPort->setCursor ( handCursor );
       myCursorIsHand = true;
     }
@@ -993,7 +1004,8 @@ void XGUI_ViewWindow::frontView()
 {
   emit vpTransformationStarted ( FRONTVIEW );
   Handle(V3d_View) aView3d = myViewPort->getView();
-  if ( !aView3d.IsNull() ) aView3d->SetProj (V3d_Xpos);
+  if ( !aView3d.IsNull() ) 
+    aView3d->SetProj (V3d_Xpos);
   myViewPort->fitAll();
   emit vpTransformationFinished ( FRONTVIEW );
 }
@@ -1005,7 +1017,8 @@ void XGUI_ViewWindow::backView()
 {
   emit vpTransformationStarted ( BACKVIEW );
   Handle(V3d_View) aView3d = myViewPort->getView();
-  if ( !aView3d.IsNull() ) aView3d->SetProj (V3d_Xneg);
+  if ( !aView3d.IsNull() ) 
+    aView3d->SetProj (V3d_Xneg);
   myViewPort->fitAll();
   emit vpTransformationFinished ( BACKVIEW );
 }
@@ -1017,7 +1030,8 @@ void XGUI_ViewWindow::topView()
 {
   emit vpTransformationStarted ( TOPVIEW );
   Handle(V3d_View) aView3d = myViewPort->getView();
-  if ( !aView3d.IsNull() ) aView3d->SetProj (V3d_Zpos);
+  if ( !aView3d.IsNull() ) 
+    aView3d->SetProj (V3d_Zpos);
   myViewPort->fitAll();
   emit vpTransformationFinished ( TOPVIEW );
 }
@@ -1029,7 +1043,8 @@ void XGUI_ViewWindow::bottomView()
 {
   emit vpTransformationStarted ( BOTTOMVIEW );
   Handle(V3d_View) aView3d = myViewPort->getView();
-  if ( !aView3d.IsNull() ) aView3d->SetProj (V3d_Zneg);
+  if ( !aView3d.IsNull() ) 
+    aView3d->SetProj (V3d_Zneg);
   myViewPort->fitAll();
   emit vpTransformationFinished ( BOTTOMVIEW );
 }
@@ -1041,7 +1056,8 @@ void XGUI_ViewWindow::leftView()
 {
   emit vpTransformationStarted ( LEFTVIEW );
   Handle(V3d_View) aView3d = myViewPort->getView();
-  if ( !aView3d.IsNull() ) aView3d->SetProj (V3d_Yneg);
+  if ( !aView3d.IsNull() ) 
+    aView3d->SetProj (V3d_Yneg);
   myViewPort->fitAll();
   emit vpTransformationFinished ( LEFTVIEW );
 }
@@ -1053,7 +1069,36 @@ void XGUI_ViewWindow::rightView()
 {
   emit vpTransformationStarted ( RIGHTVIEW );
   Handle(V3d_View) aView3d = myViewPort->getView();
-  if ( !aView3d.IsNull() ) aView3d->SetProj (V3d_Ypos);
+  if ( !aView3d.IsNull() ) 
+    aView3d->SetProj (V3d_Ypos);
   myViewPort->fitAll();
   emit vpTransformationFinished ( RIGHTVIEW );
 }
+
+void XGUI_ViewWindow::reset()
+{
+  emit vpTransformationStarted( RESETVIEW );
+  bool upd = myViewPort->getView()->SetImmediateUpdate( false );
+  myViewPort->getView()->Reset( false );
+  myViewPort->fitAll( false, true, false );
+  myViewPort->getView()->SetImmediateUpdate( upd );
+  myViewPort->getView()->Update();
+  emit vpTransformationFinished( RESETVIEW );
+}
+
+
+void XGUI_ViewWindow::updateToolBar()
+{
+  myGripWgt->update();
+  myViewBar->update();
+  myWindowBar->update();
+  //QTimer::singleShot(50, Qt::VeryCoarseTimer, this, SLOT(repaintToolBar()));
+}
+
+/*void XGUI_ViewWindow::repaintToolBar()
+{
+  QApplication::sync();
+  myGripWgt->repaint();
+  myViewBar->repaint();
+  myWindowBar->repaint();
+}*/
index de6837a5a4e0770c84aad9f4e434a5ff80162cc2..3efd7bb87b02eb73b7f14e627313da7bd8e5f447 100644 (file)
@@ -64,48 +64,51 @@ public:
   XGUI_ViewBackground background() const;
   void setBackground(const XGUI_ViewBackground& theBackground);
 
-    bool closable() const { return myClosable; }
-    void setClosable( const bool isClosable ) { myClosable = isClosable; }
+  bool closable() const { return myClosable; }
+  void setClosable( const bool isClosable ) { myClosable = isClosable; }
 
 signals:
-  void vpTransformationStarted(XGUI_ViewWindow::OperationType type);
-  void vpTransformationFinished(XGUI_ViewWindow::OperationType type);
+    void vpTransformationStarted(XGUI_ViewWindow::OperationType type);
+    void vpTransformationFinished(XGUI_ViewWindow::OperationType type);
 
-  void Show(QShowEvent *);
-  void Hide(QHideEvent *);
-  void maximized(XGUI_ViewWindow*, bool);
-  void returnedTo3d();
+    void Show(QShowEvent *);
+    void Hide(QHideEvent *);
+    void maximized(XGUI_ViewWindow*, bool);
+    void returnedTo3d();
 
-  void tryClosing(XGUI_ViewWindow*);
+    void tryClosing(XGUI_ViewWindow*);
     void closed( QMdiSubWindow* );
-  void mousePressed(XGUI_ViewWindow*, QMouseEvent*);
-  void mouseReleased(XGUI_ViewWindow*, QMouseEvent*);
-  void mouseDoubleClicked(XGUI_ViewWindow*, QMouseEvent*);
-  void mouseMoving(XGUI_ViewWindow*, QMouseEvent*);
-  void keyPressed(XGUI_ViewWindow*, QKeyEvent*);
-  void keyReleased(XGUI_ViewWindow*, QKeyEvent*);
-  void contextMenuRequested(QContextMenuEvent *e);
-
-  void viewModified(XGUI_ViewWindow*);
+    void mousePressed(XGUI_ViewWindow*, QMouseEvent*);
+    void mouseReleased(XGUI_ViewWindow*, QMouseEvent*);
+    void mouseDoubleClicked(XGUI_ViewWindow*, QMouseEvent*);
+    void mouseMoving(XGUI_ViewWindow*, QMouseEvent*);
+    void keyPressed(XGUI_ViewWindow*, QKeyEvent*);
+    void keyReleased(XGUI_ViewWindow*, QKeyEvent*);
+    void contextMenuRequested(QContextMenuEvent *e);
+
+    void viewModified(XGUI_ViewWindow*);
     void viewCloned( QMdiSubWindow* theView );
 
 public slots:
   void activateZoom();
   void activateRotation();
   void activatePanning();
-    void activateWindowFit();
-    void activateGlobalPanning();
-
-    void cloneView();
-    void dumpView();
-    void fitAll();
-    
-    void frontView();
-    void backView();
-    void topView();
-    void bottomView();
-    void leftView();
-    void rightView();
+  void activateWindowFit();
+  void activateGlobalPanning();
+
+  void cloneView();
+  void dumpView();
+  void fitAll();
+
+  void frontView();
+  void backView();
+  void topView();
+  void bottomView();
+  void leftView();
+  void rightView();
+
+  void reset();
+
 
 protected:
   virtual void resizeEvent(QResizeEvent* theEvent);
@@ -122,6 +125,9 @@ private slots:
   void onMinimize();
   void onMaximize();
 
+  void updateToolBar();
+//  void repaintToolBar();
+
 private:
   enum WindowState
   {
@@ -191,7 +197,7 @@ private:
   bool myCursorIsHand;
   bool myIsKeyFree;
   bool myEventStarted;       // set when transformation is in process 
-    bool myClosable;
+  bool myClosable;
 
   QCursor myCursor;
 
@@ -216,8 +222,13 @@ public:
   ViewerToolbar(QWidget* theParent, XGUI_ViewPort* thePort)
       : QToolBar(theParent), myVPort(thePort)
   {
+    setBackgroundRole(QPalette::NoRole);
+    setAttribute(Qt::WA_NoSystemBackground);
+    setAttribute(Qt::WA_PaintOnScreen);
   }
 
+  void repaintBackground();
+
 protected:
   virtual void paintEvent(QPaintEvent* theEvent);
 
@@ -233,8 +244,11 @@ public:
   ViewerLabel(QWidget* theParent, XGUI_ViewPort* thePort)
       : QLabel(theParent), myVPort(thePort)
   {
+    setAttribute(Qt::WA_NoSystemBackground);
   }
 
+  void repaintBackground();
+
 protected:
   virtual void paintEvent(QPaintEvent* theEvent);
 
index 147c7937e5fbcef877e21096bade377ae79c48bd..881b91f8723f846346cda4dbc7b62535a186059f 100644 (file)
@@ -169,7 +169,8 @@ QMdiSubWindow* XGUI_Viewer::createView(V3d_TypeOfView theType)
     if (myViews.size() == 0) 
         setTrihedronShown(true);
 
-  view->setBackground(XGUI_ViewBackground(XGUI::VerticalGradient, Qt::green, Qt::blue));
+    view->setBackground(XGUI_ViewBackground(XGUI::VerticalGradient, Qt::white, QColor(Qt::blue).lighter()));
+  //view->setBackground(XGUI_ViewBackground(Qt::black));
 
   QMdiArea* aMDI = myMainWindow->mdiArea();
   QMdiSubWindow* aWnd = aMDI->addSubWindow(view, Qt::FramelessWindowHint);
index 28519d6dead5b63c1a79503a22d5e1ade8589c04..b083080f9a26d888817a31ab876753811f5a7af8 100644 (file)
@@ -55,12 +55,13 @@ void XGUI_Workshop::startApplication()
   myMainWindow->show();
   QMdiSubWindow* aWnd = myMainWindow->viewer()->createView();
   aWnd->showMaximized();
+  myMainWindow->showPythonConsole();
 }
 
 //******************************************************
 void XGUI_Workshop::initMenu()
 {
-  XGUI_Workbench* aPage = addWorkbench(tr("GEN_MENU_TITLE"));
+  XGUI_Workbench* aPage = myMainWindow->menuObject()->generalPage();
 
   // File commands group
   XGUI_MenuGroupPanel* aGroup = aPage->addGroup("Default");
index f9090d15d24e2465561be66ac6b35824d9f5944d..afe0ddcae08abca81d669c72ce9e0fb307c7c7fb 100644 (file)
@@ -6,10 +6,6 @@
 </context>
 <context>
     <name>XGUI_Workshop</name>
-    <message>
-        <source>GEN_MENU_TITLE</source>
-        <translation>General</translation>
-    </message>
     <message>
         <source>NEW_MENU</source>
         <translation>New</translation>
         <source>MENU_TITLE</source>
         <translation>Menu</translation>
     </message>
+    <message>
+        <source>GEN_MENU_TITLE</source>
+        <translation>General</translation>
+    </message>
 </context>
 <context>
     <name>XGUI_Viewer</name>
         <source>RIGHT_VIEW</source>
         <translation>Right</translation>
     </message>
+    <message>
+        <source>RESET_VIEW</source>
+        <translation>Reset</translation>
+    </message>
 </context>
 </TS>
index 0645767cc9a21f1e126b0a14d771e28fd52658bb..283bb434bc36bf3be83868977e25ef12faa6f1e6 100644 (file)
 
      <file>pictures/ViewPort.png</file>
      
-     <file>pictures/occ_view_ambient.png</file>
-     <file>pictures/occ_view_anticlockwise.png</file>
      <file>pictures/occ_view_back.png</file>
      <file>pictures/occ_view_bottom.png</file>
      <file>pictures/occ_view_camera_dump.png</file>
-     <file>pictures/occ_view_clipping.png</file>
-     <file>pictures/occ_view_clipping_pressed.png</file>
-     <file>pictures/occ_view_clockwise.png</file>
      <file>pictures/occ_view_clone.png</file>
      <file>pictures/occ_view_fitall.png</file>
      <file>pictures/occ_view_fitarea.png</file>
      <file>pictures/occ_view_front.png</file>
-     <file>pictures/occ_view_glpan.png</file>
-     <file>pictures/occ_view_graduated_axes.png</file>
      <file>pictures/occ_view_left.png</file>
-     <file>pictures/occ_view_maximized.png</file>
-     <file>pictures/occ_view_minimized.png</file>
      <file>pictures/occ_view_pan.png</file>
-     <file>pictures/occ_view_preselection.png</file>
-     <file>pictures/occ_view_presets.png</file>
+     <file>pictures/occ_view_glpan.png</file>
      <file>pictures/occ_view_reset.png</file>
-     <file>pictures/occ_view_return_3d_view.png</file>
      <file>pictures/occ_view_right.png</file>
      <file>pictures/occ_view_rotate.png</file>
-     <file>pictures/occ_view_rotation_point.png</file>
-     <file>pictures/occ_view_scaling.png</file>
-     <file>pictures/occ_view_selection.png</file>
-     <file>pictures/occ_view_shoot.png</file>
-     <file>pictures/occ_view_style_switch.png</file>
      <file>pictures/occ_view_top.png</file>
-     <file>pictures/occ_view_triedre.png</file>
      <file>pictures/occ_view_zoom.png</file>
-     <file>pictures/occ_view_zooming_style_switch.png</file>
      
      <file>pictures/wnd_close.png</file>
      <file>pictures/wnd_minimize.png</file>
diff --git a/src/XGUI/pictures/occ_view_ambient.png b/src/XGUI/pictures/occ_view_ambient.png
deleted file mode 100644 (file)
index 7f41153..0000000
Binary files a/src/XGUI/pictures/occ_view_ambient.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_anticlockwise.png b/src/XGUI/pictures/occ_view_anticlockwise.png
deleted file mode 100644 (file)
index 991586f..0000000
Binary files a/src/XGUI/pictures/occ_view_anticlockwise.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_clipping.png b/src/XGUI/pictures/occ_view_clipping.png
deleted file mode 100644 (file)
index 0356a9a..0000000
Binary files a/src/XGUI/pictures/occ_view_clipping.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_clipping_pressed.png b/src/XGUI/pictures/occ_view_clipping_pressed.png
deleted file mode 100644 (file)
index e38a8aa..0000000
Binary files a/src/XGUI/pictures/occ_view_clipping_pressed.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_clockwise.png b/src/XGUI/pictures/occ_view_clockwise.png
deleted file mode 100644 (file)
index b25471e..0000000
Binary files a/src/XGUI/pictures/occ_view_clockwise.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_graduated_axes.png b/src/XGUI/pictures/occ_view_graduated_axes.png
deleted file mode 100644 (file)
index 2b44c06..0000000
Binary files a/src/XGUI/pictures/occ_view_graduated_axes.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_maximized.png b/src/XGUI/pictures/occ_view_maximized.png
deleted file mode 100644 (file)
index 8e19d6a..0000000
Binary files a/src/XGUI/pictures/occ_view_maximized.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_minimized.png b/src/XGUI/pictures/occ_view_minimized.png
deleted file mode 100644 (file)
index b38bbeb..0000000
Binary files a/src/XGUI/pictures/occ_view_minimized.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_preselection.png b/src/XGUI/pictures/occ_view_preselection.png
deleted file mode 100644 (file)
index cd5baec..0000000
Binary files a/src/XGUI/pictures/occ_view_preselection.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_presets.png b/src/XGUI/pictures/occ_view_presets.png
deleted file mode 100644 (file)
index b436f94..0000000
Binary files a/src/XGUI/pictures/occ_view_presets.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_return_3d_view.png b/src/XGUI/pictures/occ_view_return_3d_view.png
deleted file mode 100644 (file)
index 01de30b..0000000
Binary files a/src/XGUI/pictures/occ_view_return_3d_view.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_rotation_point.png b/src/XGUI/pictures/occ_view_rotation_point.png
deleted file mode 100644 (file)
index fc303e3..0000000
Binary files a/src/XGUI/pictures/occ_view_rotation_point.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_scaling.png b/src/XGUI/pictures/occ_view_scaling.png
deleted file mode 100644 (file)
index fa8cbbc..0000000
Binary files a/src/XGUI/pictures/occ_view_scaling.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_selection.png b/src/XGUI/pictures/occ_view_selection.png
deleted file mode 100644 (file)
index 21d18e9..0000000
Binary files a/src/XGUI/pictures/occ_view_selection.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_shoot.png b/src/XGUI/pictures/occ_view_shoot.png
deleted file mode 100644 (file)
index fa5ba75..0000000
Binary files a/src/XGUI/pictures/occ_view_shoot.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_style_switch.png b/src/XGUI/pictures/occ_view_style_switch.png
deleted file mode 100644 (file)
index b0a9c80..0000000
Binary files a/src/XGUI/pictures/occ_view_style_switch.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_triedre.png b/src/XGUI/pictures/occ_view_triedre.png
deleted file mode 100644 (file)
index bc5894d..0000000
Binary files a/src/XGUI/pictures/occ_view_triedre.png and /dev/null differ
diff --git a/src/XGUI/pictures/occ_view_zooming_style_switch.png b/src/XGUI/pictures/occ_view_zooming_style_switch.png
deleted file mode 100644 (file)
index 8f3a486..0000000
Binary files a/src/XGUI/pictures/occ_view_zooming_style_switch.png and /dev/null differ