From 1c18256b28680c2b23e8c1a90fd451b7b7413a08 Mon Sep 17 00:00:00 2001
From: andreas <andreas@5a81b35b-ba03-0410-adc8-b2c5c5119f08>
Date: Mon, 5 Apr 2010 04:49:19 +0000
Subject: [PATCH] new state machine based shell (new_shell branch)

git-svn-id: https://dng.biozentrum.unibas.ch/svn/openstructure/branches/new_shell@1919 5a81b35b-ba03-0410-adc8-b2c5c5119f08
---
 modules/gui/pymod/__init__.py                 |  17 +-
 modules/gui/src/CMakeLists.txt                |  18 +-
 modules/gui/src/gosty.cc                      |  13 +-
 modules/gui/src/gosty_app.cc                  |   3 +-
 .../plot_viewer/plot_data_graphics_item.cc    |   8 +-
 .../gui/src/python_shell/output_redirector.cc |  15 +-
 .../gui/src/python_shell/output_redirector.hh |   9 +-
 .../src/python_shell/python_interpreter.cc    | 325 ++--------
 .../src/python_shell/python_interpreter.hh    |  80 +--
 .../python_shell/python_interpreter_proxy.hh  |   1 +
 .../python_shell/python_interpreter_worker.cc | 132 ++++
 .../python_shell/python_interpreter_worker.hh |  53 ++
 .../python_namespace_tree_item.cc             |   2 +
 .../python_namespace_tree_model.cc            |   3 +-
 modules/gui/src/python_shell/python_shell.cc  |   4 -
 modules/gui/src/python_shell/python_shell.hh  |   1 -
 .../src/python_shell/python_shell_widget.cc   | 607 ++++++++++--------
 .../src/python_shell/python_shell_widget.hh   |  76 ++-
 modules/gui/src/python_shell/state.cc         |  84 +++
 modules/gui/src/python_shell/state.hh         |  45 ++
 modules/gui/src/python_shell/state_machine.cc |  55 ++
 modules/gui/src/python_shell/state_machine.hh |  30 +
 modules/gui/src/python_shell/transition.cc    |  91 +++
 modules/gui/src/python_shell/transition.hh    |  77 +++
 .../gui/src/python_shell/transition_guard.cc  |  64 ++
 .../gui/src/python_shell/transition_guard.hh  |  65 ++
 26 files changed, 1191 insertions(+), 687 deletions(-)
 create mode 100644 modules/gui/src/python_shell/python_interpreter_worker.cc
 create mode 100644 modules/gui/src/python_shell/python_interpreter_worker.hh
 create mode 100644 modules/gui/src/python_shell/state.cc
 create mode 100644 modules/gui/src/python_shell/state.hh
 create mode 100644 modules/gui/src/python_shell/state_machine.cc
 create mode 100644 modules/gui/src/python_shell/state_machine.hh
 create mode 100644 modules/gui/src/python_shell/transition.cc
 create mode 100644 modules/gui/src/python_shell/transition.hh
 create mode 100644 modules/gui/src/python_shell/transition_guard.cc
 create mode 100644 modules/gui/src/python_shell/transition_guard.hh

diff --git a/modules/gui/pymod/__init__.py b/modules/gui/pymod/__init__.py
index 517c21dae..3fb43183b 100644
--- a/modules/gui/pymod/__init__.py
+++ b/modules/gui/pymod/__init__.py
@@ -17,8 +17,21 @@
 # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 #------------------------------------------------------------------------------
 from _gui import *
+import sip
+
 
 ## \brief Opens a DataViewer 
 # \sa \ref fft_li.py "View Fourier Transform Example" \sa \ref modulate_image.py "Modulate Image Example"
-def CreateDataViewer(ih):
-      return GostyApp.Instance().CreateDataViewer(ih)
\ No newline at end of file
+def _close_event_override_(event):
+  print "close event"
+def _set_data_override_(data):
+  print "set data"
+
+def CreateDataViewer(ih,flag=True):
+      viewer=GostyApp.Instance().CreateDataViewer(ih)
+      if flag:
+        viewer.image_=ih
+        sip_viewer=sip.warpinstance(viewer.GetSipHandle())
+        sip_viewer.closeEvent=_close_event_override_
+        sip_viewer.setData=_set_data_override_
+      return viewer
diff --git a/modules/gui/src/CMakeLists.txt b/modules/gui/src/CMakeLists.txt
index e9e413416..fe8963e68 100644
--- a/modules/gui/src/CMakeLists.txt
+++ b/modules/gui/src/CMakeLists.txt
@@ -49,9 +49,13 @@ python_tokenizer.hh
 python_syntax_highlighter.hh
 text_logger.hh
 python_interpreter.hh
-python_interpreter_proxy.hh
+python_interpreter_worker.hh
 python_shell_text_document_layout.hh
 shell_history.hh
+state_machine.hh
+state.hh
+transition.hh
+transition_guard.hh
 )
 
 set(OST_GUI_SCENE_WIN_HEADERS
@@ -258,7 +262,7 @@ python_shell/path_completer.cc
 python_shell/python_context_parser.cc
 python_shell/python_completer.cc
 python_shell/python_interpreter.cc
-python_shell/python_interpreter_proxy.cc
+python_shell/python_interpreter_worker.cc
 python_shell/python_namespace_tree_model.cc
 python_shell/python_namespace_tree_item.cc
 python_shell/python_shell.cc
@@ -269,6 +273,10 @@ python_shell/python_syntax_highlighter.cc
 python_shell/shell_history.cc
 python_shell/string_literal_positions.cc
 python_shell/text_logger.cc
+python_shell/state_machine.cc
+python_shell/state.cc
+python_shell/transition.cc
+python_shell/transition_guard.cc
 scene_win/context_menu.cc
 scene_win/current_selection_node.cc
 scene_win/custom_part_node.cc
@@ -320,10 +328,16 @@ python_shell/gutter.hh
 python_shell/path_completer.hh
 python_shell/python_completer.hh
 python_shell/python_interpreter.hh
+python_shell/python_interpreter_worker.hh
 python_shell/python_namespace_tree_model.hh
 python_shell/python_shell.hh
 python_shell/python_shell_widget.hh
 python_shell/text_logger.hh
+python_shell/state_machine.hh
+python_shell/state.hh
+python_shell/transition.hh
+python_shell/transition_guard.hh
+python_shell/output_redirector.hh
 panel_bar/button_bar.hh
 panel_bar/drop_box.hh
 panel_bar/panel_bar.hh
diff --git a/modules/gui/src/gosty.cc b/modules/gui/src/gosty.cc
index f7d452b4d..08968ebb0 100644
--- a/modules/gui/src/gosty.cc
+++ b/modules/gui/src/gosty.cc
@@ -52,7 +52,7 @@ DelayedScriptExecutor::DelayedScriptExecutor()
 void DelayedScriptExecutor::Exec()
 {
   PythonInterpreter& interp=PythonInterpreter::Instance();  
-  interp.ExecRelease();
+  interp.Start();
 }
 
 }}}
@@ -127,9 +127,7 @@ int setup_resources(QApplication& app)
 int init_python_interpreter()
 {
   // the order of these two calls is important!
-  PythonInterpreter::Instance(true);
-  // following command would instanciate a multithreaded python interpreter.
-  // Untested, use at your own risk!! PythonInterpreter::Instance(true,true);
+  PythonInterpreter::Instance();
   reclaim_signals();
   //
   PythonInterpreter& py=PythonInterpreter::Instance();
@@ -179,11 +177,10 @@ int main(int argc, char** argv)
     return r;
   }
   PythonInterpreter& py_int=PythonInterpreter::Instance();
-  py_int.ExecWait();  
-  py_int.RunInitRC();
+  // todo remove RunInitRC and replace with general call to run script (with dngrc as argument)
+  //py_int.RunInitRC();
+  prepare_scripts(argc,argv,py_int);
   //  delay all execution of python scripts after app.exec() has been called.
   ost::gui::detail::DelayedScriptExecutor delayed_executor;
-
-  prepare_scripts(argc,argv,py_int);
   return app.exec();
 }
diff --git a/modules/gui/src/gosty_app.cc b/modules/gui/src/gosty_app.cc
index 8eed58bea..0a1769bb8 100644
--- a/modules/gui/src/gosty_app.cc
+++ b/modules/gui/src/gosty_app.cc
@@ -127,7 +127,8 @@ void GostyApp::SetupPyShellLogging()
 {
   TextLogger* console_logger=new TextLogger(stdout);
   this->ReadLoggerSettings("console", console_logger);
-  py_shell_->AddLogger(console_logger);  
+  // get log from Interpreter instead of shell
+  //py_shell_->AddLogger(console_logger);
   // TODO: Setup file logging
 }
 
diff --git a/modules/gui/src/plot_viewer/plot_data_graphics_item.cc b/modules/gui/src/plot_viewer/plot_data_graphics_item.cc
index 68f287076..81a0b78c7 100644
--- a/modules/gui/src/plot_viewer/plot_data_graphics_item.cc
+++ b/modules/gui/src/plot_viewer/plot_data_graphics_item.cc
@@ -132,10 +132,10 @@ void PlotDataGraphicsItem::Redraw()
 
 void PlotDataGraphicsItem::Callback(const PlotDataEntry& pde)
 {
-  PlotDataInfoPtr infoptr=dyn_cast<PlotDataInfo>(infoptr_);
-  if(infoptr->callback!=boost::python::object()){
-    PythonInterpreter::Instance().CallFunction(infoptr->callback,boost::python::make_tuple(pde.x,pde.y,pde.ex,pde.ey,pde.q,boost::python::str(pde.info.toStdString().c_str())));
-  }
+ // PlotDataInfoPtr infoptr=dyn_cast<PlotDataInfo>(infoptr_);
+ // if(infoptr->callback!=boost::python::object()){
+ //   PythonInterpreter::Instance().CallFunction(infoptr->callback,boost::python::make_tuple(pde.x,pde.y,pde.ex,pde.ey,pde.q,boost::python::str(pde.info.toStdString().c_str())));
+ // }
 }
 
 }}//ns
diff --git a/modules/gui/src/python_shell/output_redirector.cc b/modules/gui/src/python_shell/output_redirector.cc
index 2702b057a..ec052e0d3 100644
--- a/modules/gui/src/python_shell/output_redirector.cc
+++ b/modules/gui/src/python_shell/output_redirector.cc
@@ -27,16 +27,17 @@
 
 namespace ost { namespace gui {
 
-void OutputRedirector::Write( String const& str )
+OutputRedirector::OutputRedirector():
+ QObject()
 {
-  buffer_.append(str);
 }
-QString OutputRedirector::GetOutput()
+
+
+void OutputRedirector::Write( String const& str )
 {
-  QString  ret=QString::fromStdString(buffer_);
-  ret.chop(1); // chop off additional newline
-  buffer_.clear();
-  return ret;
+  //buffer_.append(str);
+  emit OnOutput(QString::fromStdString(str));
+
 }
 String  OutputRedirector::buffer_; 
 
diff --git a/modules/gui/src/python_shell/output_redirector.hh b/modules/gui/src/python_shell/output_redirector.hh
index 664c696cb..c19426505 100644
--- a/modules/gui/src/python_shell/output_redirector.hh
+++ b/modules/gui/src/python_shell/output_redirector.hh
@@ -27,14 +27,19 @@
 */
 
 #include <QString>
+#include <QObject>
 #include <ost/gui/module_config.hh>
 
 namespace ost { namespace gui {
 
-class DLLEXPORT_OST_GUI OutputRedirector {
+class DLLEXPORT_OST_GUI OutputRedirector: public QObject {
+Q_OBJECT
+
 public:
+  OutputRedirector();
   void Write(const String& str);
-  static QString GetOutput();
+signals:
+  void OnOutput(const QString& output);
 private:
     static String  buffer_; 
 };
diff --git a/modules/gui/src/python_shell/python_interpreter.cc b/modules/gui/src/python_shell/python_interpreter.cc
index 4af471f17..4b11055c4 100644
--- a/modules/gui/src/python_shell/python_interpreter.cc
+++ b/modules/gui/src/python_shell/python_interpreter.cc
@@ -32,8 +32,8 @@
 #include <boost/filesystem/convenience.hpp>
 
 #include <QDebug>
+#include <QFile>
 #include <QApplication>
-#include <QThread>
 #include <QStringList>
 #include <ost/log.hh>
 
@@ -43,258 +43,78 @@ PythonInterpreter::PythonInterpreter():
   QObject(),
   main_module_(),
   main_namespace_(),
-#ifndef _MSC_VER
-  sig_act_(),
-#endif
-  output_redirector_(),
-  main_thread_runner_(),
   running_(false),
-  mutex_(),
-  gstate_(),
-  main_thread_state_(NULL),
-  proxy_(this),
   compile_command_(),
-  exec_wait_(false),
-  exec_queue_(),
-  parse_expr_cmd_()  
+  worker_()
 {
+  connect(this,SIGNAL(WakeWorker()),&worker_,SLOT(Wake()),Qt::QueuedConnection);
+  connect(&worker_,SIGNAL(Output(unsigned int,const QString&)),this,SIGNAL(Output(unsigned int,const QString&)));
+  connect(&worker_,SIGNAL(ErrorOutput(unsigned int,const QString&)),this,SIGNAL(ErrorOutput(unsigned int,const QString&)));
+  connect(&worker_,SIGNAL(Finished(unsigned int, bool)),this,SIGNAL(Finished(unsigned int, bool)));
+  main_module_ = bp::import("__main__");
+  main_namespace_ = bp::extract<bp::dict>(main_module_.attr("__dict__"));
+  main_namespace_["os"]=bp::import("os");
+  main_namespace_["__builtin__"]=bp::import("__builtin__");
+  main_namespace_["keyword"]=bp::import("keyword");
+  bp::object code=bp::import("code");
+  compile_command_=code.attr("compile_command");
 }
 
 PythonInterpreter::~PythonInterpreter()
 {
- PyGILState_Release(gstate_);
- PyEval_RestoreThread(main_thread_state_);
 }
 
-PythonInterpreter& PythonInterpreter::Instance(bool register_sighandler, 
-                                               bool multithreaded)
+PythonInterpreter& PythonInterpreter::Instance()
 {
-  //todo handle register_sighandler;
-  static bool initialized=false;
   static PythonInterpreter instance;
-  static QThread thread;
-  if(!initialized){
-    initialized=true;
-    if(multithreaded){
-      instance.moveToThread(&thread);
-      thread.start();
-      instance.Init(true);
-    }else{
-      instance.Init(false);
-    }
-  }
   return instance;
 }
 
-void PythonInterpreter::Init(bool multithreaded)
-{
-  if(multithreaded){
-    //initialize over a blocking queued connection to get the correct thread context
-  /*  connect(this, SIGNAL(InitSignal(bool)),this, SLOT(InitSlot(bool)),Qt::BlockingQueuedConnection);
-    emit InitSignal(true);*/
-  }else{
-    // direct initalization
-    InitSlot(false);
-  }
-}
 
-void PythonInterpreter::RedirectOutput()
-{
-  main_namespace_["sys"].attr("stderr") = output_redirector_;
-  main_namespace_["sys"].attr("stdout") = output_redirector_;
-}
 
-void PythonInterpreter::InitSlot(bool multithreaded)
+void PythonInterpreter::Stop()
 {
-  if(multithreaded){
-    //main_thread_runner_.moveToThread(QApplication::instance()->thread());
-    //connect(this,SIGNAL(RunInMainThreadSignal(const QString&)),&main_thread_runner_,SLOT(Run(const QString&)),Qt::BlockingQueuedConnection);
-  }else{
-    connect(this,SIGNAL(RunInMainThreadSignal(const QString&)),&main_thread_runner_,SLOT(Run(const QString&)),Qt::DirectConnection);
-  }
-  Py_InitializeEx(1);
-  PyEval_InitThreads();
-  main_thread_state_ = PyEval_SaveThread();
-  gstate_ = PyGILState_Ensure();
-  //store signal handler
-  #ifndef _MSC_VER
-  sigaction(SIGINT,0,&sig_act_);
-  #endif
-  main_module_ = bp::import("__main__");
-  main_module_.attr("in_gui_mode")=true;  
-  main_namespace_ = bp::extract<bp::dict>(main_module_.attr("__dict__"));
-  parse_expr_cmd_=bp::import("parser").attr("expr");  
-  main_namespace_["OutputRedirector"] = bp::class_<OutputRedirector>("OutputRedirector", bp::init<>())
-                                          .def("write", &OutputRedirector::Write);
-  main_namespace_["PythonInterpreterProxy"] = bp::class_<PythonInterpreterProxy>("PythonInterpreterProxy", bp::init<PythonInterpreter*>())
-                                          .def("Run", &PythonInterpreterProxy::Run);
-
-  main_namespace_["os"]=bp::import("os");
-  main_namespace_["__builtin__"]=bp::import("__builtin__");
-  main_namespace_["keyword"]=bp::import("keyword");
-
-  parse_expr_cmd_=bp::import("parser").attr("expr");  
-
-  main_namespace_["sys"]=bp::import("sys");  
-
-  main_module_.attr("Proxy")=proxy_;
-  bp::object code=bp::import("code");
-  compile_command_=code.attr("compile_command");
-
+  running_=false;
 }
 
-namespace {
-#ifndef _MSC_VER
-  class SigIntHandler {
-  public:
-    SigIntHandler(const struct sigaction& act) {
-      sigaction(SIGINT,&act,&old_);
-    }
-    ~SigIntHandler() {
-      sigaction(SIGINT,&old_,0);
-    }
-  private:
-    struct sigaction old_;
-  };
-#endif
-}
-void PythonInterpreter::RunInitRC()
+void PythonInterpreter::Start()
 {
-  if(!getenv("HOME")) return;
-  String fname=String(getenv("HOME"))+"/.dngrc";
-  try {
-    if (boost::filesystem::exists(fname)) {
-      try {
-        bp::exec_file(bp::str(fname), main_module_.attr("__dict__"), 
-                      main_module_.attr("__dict__"));      
-      } catch(bp::error_already_set&) {
-        PyErr_Print();
-      }
-    }
-  } catch(std::exception&) {
-    // silently ignore. needed for boost 1.33.1
-  }  
+  running_=true;
+    emit WakeWorker();
 }
 
-void PythonInterpreter::ExecWait()
-{
-  ++exec_wait_;
-}
 
-void PythonInterpreter::ExecRelease()
-{
-  --exec_wait_;
-  assert(exec_wait_>=0);
-  if(exec_wait_==0) {
-    for(std::vector<QString>::const_iterator it=exec_queue_.begin();
-        it!=exec_queue_.end();++it) {
-      if(!RunCommand(*it)) break;        
-    }
-    exec_queue_.clear();
-  }
-}
 
-void PythonInterpreter::RunScript(const String& fname)
+unsigned int PythonInterpreter::RunScript(const QString& fname)
 {
-  std::ifstream script(fname.c_str());
-  if(!script) {
-    LOGN_ERROR("could not open " << fname);
-    return;
-  }
-
-  String line;
-  QString command("");
-  while(std::getline(script,line)) {
-    command.append(QString::fromStdString(line));
-    command.append('\n');
-    if(this->GetCodeBlockStatus(command)!=CODE_BLOCK_INCOMPLETE) {
-      if(!RunCommand(command)) break;
-      command=QString("");
-    }
+  QFile script(fname);
+  if (!script.open(QIODevice::ReadOnly | QIODevice::Text)){
+    LOGN_ERROR("could not open " << fname.toStdString());
+    return RunCommand("");
   }
+  QString command=script.readAll();
   command.append('\n');
   command.append('\n');
-  RunCommand(command);
+  return RunCommand(command);
 }
 
-bool PythonInterpreter::IsSimpleExpression(const QString& expr)
+unsigned int PythonInterpreter::RunCommand(const QString& command)
 {
-  try {
-    parse_expr_cmd_(bp::str(expr.toStdString()));
-    return true;
-  } catch(bp::error_already_set&) {
-    PyErr_Clear();
-    return false;
+  unsigned int id=worker_.AddCommand(command);
+  if(running_){
+    emit WakeWorker();
   }
-}
-
-bool PythonInterpreter::RunCommand(const QString& command)
-{
-  bool flag=true;
-  if(exec_wait_!=0) {
-    exec_queue_.push_back(command);
-    return flag;
-  }
-
-  // becore doing anything give Qt the chance to handle events for 100ms
-  QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents,100);
-  mutex_.lock();
-  running_=true;
-  mutex_.unlock();
-#ifndef _MSC_VER
-  SigIntHandler sh(sig_act_);
-#endif
-  try{
-    if(command.indexOf(QString("\n"))==-1){
-      if(command=="exit" || command=="quit"){
-        emit Exit();
-      }
-      if (this->IsSimpleExpression(command)) {
-        bp::object repr=main_module_.attr("__builtins__").attr("repr");
-        bp::object result=bp::eval(bp::str(command.toStdString()),
-                                   main_namespace_, main_namespace_);
-        String rstring=bp::extract<String>(repr(result));
-        if(rstring!="None"){
-          output_redirector_.Write(rstring);
-          output_redirector_.Write("\n");
-        }
-        emit Done(STATUS_OK,output_redirector_.GetOutput());
-        flag=true;
-      } else {
-        bp::exec(bp::str(command.toStdString()),main_namespace_,main_namespace_);
-        flag=true;
-      }
-    }else{
-      bp::exec(bp::str(command.toStdString()),main_namespace_,main_namespace_);
-    }
-    emit Done(STATUS_OK,output_redirector_.GetOutput());
-  }catch(bp::error_already_set){
-    if(PyErr_ExceptionMatches(PyExc_SystemExit)){
-      PyErr_Clear();
-      emit Exit();
-    } else{
-      PyErr_Print();
-      QString output=output_redirector_.GetOutput();
-      std::cout << output.toStdString() << std::endl;
-      emit Done(STATUS_ERROR,output);
-      flag=false;
-    }
-  }
-  mutex_.lock();
-  running_=false;
-  mutex_.unlock();
-  return flag;
+  return id;
 }
 
 CodeBlockStatus PythonInterpreter::GetCodeBlockStatus(const QString& command)
 {
+  // move to pure c++ to avoid clash with running worker
+  // move to worker class
   CodeBlockStatus status=CODE_BLOCK_COMPLETE;
-  mutex_.lock();
-  running_=true;
-  mutex_.unlock();
-#ifndef _MSC_VER
+/*#ifndef _MSC_VER
   SigIntHandler sh(sig_act_);
-#endif
+#endif*/
   QStringList lines=command.split("\n");  
   String cmd;  
   for (int i=0; i<lines.size(); ++i) {
@@ -328,70 +148,9 @@ CodeBlockStatus PythonInterpreter::GetCodeBlockStatus(const QString& command)
       }
     }
   }
-  mutex_.lock();
-  running_=false;
-  mutex_.unlock();
-  return status;
-}
-
-enum returncodes{
-    RC_OK=0,
-    RC_ERROR=2>>1,
-    RC_LOOP=2,
-    RC_EXIT=2<<1,
-};
-
-void PythonInterpreter::CallFunction(const bp::object& func,
-                                     const bp::object& args)
-{
-  unsigned int retcode=RC_OK;
-  bool result;
-  try{
-#ifndef _MSC_VER
-  SigIntHandler sh(sig_act_);
-#endif
-    if (len(args)>0) {
-      result=bp::extract<bool>(func(args));
-    } else {
-      result=bp::extract<bool>(func());
-    }
-    if (!result) {
-      retcode|=RC_ERROR;
-      PyErr_Print();
-    }
-  } catch(bp::error_already_set) {
-    if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)){
-      retcode|=RC_EXIT;
-    } else {
-      PyErr_Print();
-      retcode|=RC_ERROR;
-    }
-  }
+   return status;
 }
 
-bool PythonInterpreter::IsRunning()
-{
-  mutex_.lock();
-  bool state=running_;
-  mutex_.unlock();
-  return state;
-}
-
-void PythonInterpreter::Abort()
-{
-  if(IsRunning()){
-    raise(SIGINT);
-  }
-}
-
-bp::object PythonInterpreter::RunInMainThread(const QString& widget)
-{
-  PyGILState_Release(gstate_);
-  emit RunInMainThreadSignal(widget);
-  gstate_ = PyGILState_Ensure();
-  return main_module_.attr("Proxy").attr("_widget_");
-
-}
 
 void PythonInterpreter::AppendModulePath(const QString& entry)
 {
@@ -412,18 +171,6 @@ void PythonInterpreter::AppendCommandlineArgument(const QString& arg)
   RunCommand(init_code);
 }
 
-void PythonInterpreter::SetCommandLineArguments(const QList<QString>& args)
-{
-  QString init_code("");
-  init_code += "\nsys.argv=[]";  
-  for (QList<QString>::const_iterator i=args.begin(), e=args.end(); i!=e; ++i) {
-    init_code += "\nsys.argv.append('" ;
-    init_code += *i;    
-    init_code += "')\n";    
-  }
-
-  RunCommand(init_code);
-}
 bp::object PythonInterpreter::GetMainModule() const
 {
   return main_module_;
diff --git a/modules/gui/src/python_shell/python_interpreter.hh b/modules/gui/src/python_shell/python_interpreter.hh
index bb69f5fe9..01b3e3273 100644
--- a/modules/gui/src/python_shell/python_interpreter.hh
+++ b/modules/gui/src/python_shell/python_interpreter.hh
@@ -27,18 +27,16 @@
 #ifndef PYTHON_INTERPRETER_HH
 #define PYTHON_INTERPRETER_HH
 
-#include <csignal>
 #include <vector>
 
-#include <QMutex>
+#include <QQueue>
 #include <QMetaType>
 
-#include <boost/python.hpp>
 
 #include <ost/gui/module_config.hh>
 
 #include <ost/gui/module_config.hh>
-#include "python_interpreter_proxy.hh"
+#include "python_interpreter_worker.hh"
 #include "output_redirector.hh"
 #include "main_thread_runner.hh"
 
@@ -52,34 +50,22 @@ enum InterpreterStatus{
 
 
 typedef enum {
-   CODE_BLOCK_COMPLETE,
-   CODE_BLOCK_ERROR,
-   CODE_BLOCK_INCOMPLETE  
+   CODE_BLOCK_COMPLETE=1,
+   CODE_BLOCK_ERROR=2,
+   CODE_BLOCK_INCOMPLETE=4
 } CodeBlockStatus;
 
 class DLLEXPORT_OST_GUI PythonInterpreter: public QObject
 {
   Q_OBJECT
 public:
-  static PythonInterpreter& Instance(bool set_sigint_handler=true, 
-                                     bool multitreaded=false);
+  static PythonInterpreter& Instance();
 
   ~PythonInterpreter();
   bool IsRunning();
-  bp::object RunInMainThread(const QString& widget);
   void AppendModulePath(const QString& entry);
   
   void AppendCommandlineArgument(const QString& arg);
-  /// \brief set command line arguments
-  /// 
-  /// Sets sys.argv to the given list of arguments
-  /// 
-  /// The method internally uses RunCommand and is thus appended to the command
-  /// list when execution is delayed due to an open ExecWait() ExecRelease() 
-  /// block
-  /// 
-  /// \todo escape arguments
-  void SetCommandLineArguments(const QList<QString>& args);
   
   bp::dict GetMainNamespace() const;
   bp::object GetMainModule() const;
@@ -88,62 +74,34 @@ public:
   /// Determines whether the command contains errors, is incomplete or ready
   /// to be run.
   CodeBlockStatus GetCodeBlockStatus(const QString& command);
-  void RunInitRC();
 
-  void RunScript(const String& script);
   
-  /// \brief delay execution of Python commands
-  /// 
-  /// The execution of Python commands with RunCommand and methods that
-  /// make use of RunCommand is delayed until ExecRelease() is called.
-  /// ExecWait(), ExecRelease() calls can be nested.
+  /// \brief stop execution of Python commands
+  /// \sa Start
+  void Stop();
+  /// \brief start/continue execution of Python commands
   /// 
-  /// \sa ExecRelease
-  void ExecWait();
-  /// \brief execute pending Python commands
-  /// 
-  /// \sa ExecWait
-  void ExecRelease();
+  /// \sa Stop
+  void Start();
   
-  /// \brief turn on output redirection
-  void RedirectOutput();
-  /// \brief whether the string can be interpreted as a simple expression
-  /// 
-  /// import and any kind of control structures don't count as simple 
-  /// expressions and will return false.
-  bool IsSimpleExpression(const QString& expr);
 
 public slots:
   /// \brief execute python command
-  bool RunCommand(const QString& command);
-  void CallFunction(const bp::object& func,const bp::object& args);
-  void Abort();
-protected slots:
-  void InitSlot(bool multitreaded);
+  unsigned int RunScript(const QString& script);
+  unsigned int RunCommand(const QString& command);
 signals:
-  void Done(int status,const QString& output);
+  void Output(unsigned int id,const QString& output);
+  void ErrorOutput(unsigned int id,const QString& output);
+  void Finished(unsigned int id, bool error_state);
   void Exit();
-  void InitSignal(bool multitreaded);
-  void RunInMainThreadSignal(const QString& widget);
+  void WakeWorker();
 protected:
-  void Init(bool multitreaded);
   PythonInterpreter();
   bp::object main_module_;
   bp::dict main_namespace_;
-#ifndef _MSC_VER
-  struct sigaction sig_act_;
-#endif
-  OutputRedirector  output_redirector_;
-  MainThreadRunner main_thread_runner_;
   bool running_;
-  QMutex mutex_;
-  PyGILState_STATE gstate_;
-  PyThreadState* main_thread_state_;
-  PythonInterpreterProxy proxy_;
   bp::object compile_command_;
-  int exec_wait_;
-  std::vector<QString>      exec_queue_;
-  bp::object parse_expr_cmd_;  
+  PythonInterpreterWorker worker_;
 };
 
 }} // ns
diff --git a/modules/gui/src/python_shell/python_interpreter_proxy.hh b/modules/gui/src/python_shell/python_interpreter_proxy.hh
index 1cb54fa0b..30ba00be1 100644
--- a/modules/gui/src/python_shell/python_interpreter_proxy.hh
+++ b/modules/gui/src/python_shell/python_interpreter_proxy.hh
@@ -25,6 +25,7 @@
   Author: Andreas Schenk
 */
 
+#include <boost/python.hpp>
 #include <ost/gui/module_config.hh>
 
 namespace ost { namespace gui {
diff --git a/modules/gui/src/python_shell/python_interpreter_worker.cc b/modules/gui/src/python_shell/python_interpreter_worker.cc
new file mode 100644
index 000000000..8f1688804
--- /dev/null
+++ b/modules/gui/src/python_shell/python_interpreter_worker.cc
@@ -0,0 +1,132 @@
+#include "python_interpreter_worker.hh"
+#include "python_interpreter.hh"
+
+namespace ost { namespace gui {
+
+    namespace {
+#ifndef _MSC_VER
+  class SigIntHandler {
+  public:
+    SigIntHandler(const struct sigaction& act) {
+      sigaction(SIGINT,&act,&old_);
+    }
+    ~SigIntHandler() {
+      sigaction(SIGINT,&old_,0);
+    }
+  private:
+    struct sigaction old_;
+  };
+#endif
+}
+
+
+
+PythonInterpreterWorker::PythonInterpreterWorker():
+  exec_queue_(),
+  command_id_(0),
+  output_redirector_(new OutputRedirector),
+  error_redirector_(new OutputRedirector),
+  #ifndef _MSC_VER
+  sig_act_(),
+  #endif
+  parse_expr_cmd_(),
+  repr_(),
+  main_namespace_(),
+  current_id_()
+{
+  Py_InitializeEx(1);
+  parse_expr_cmd_=bp::import("parser").attr("expr");
+  main_namespace_ = bp::extract<bp::dict>(bp::import("__main__").attr("__dict__"));
+  repr_=bp::import("__main__").attr("__builtins__").attr("repr");
+  #ifndef _MSC_VER
+  sigaction(SIGINT,0,&sig_act_);
+  #endif
+  main_namespace_["OutputRedirector"] = bp::class_<OutputRedirector,boost::shared_ptr<OutputRedirector>, boost::noncopyable >("OutputRedirector", bp::init<>())
+                                          .def("write", &OutputRedirector::Write);
+  connect(output_redirector_.get(),SIGNAL(OnOutput(const QString&)),this,SLOT(handle_redirector_output(const QString&)));
+  connect(error_redirector_.get(),SIGNAL(OnOutput(const QString&)),this,SLOT(handle_redirector_error(const QString&)));
+  main_namespace_["sys"]=bp::import("sys");
+  main_namespace_["sys"].attr("stderr") = error_redirector_;
+  main_namespace_["sys"].attr("stdout") = output_redirector_;
+}
+
+void PythonInterpreterWorker::Wake()
+{
+  while (!exec_queue_.isEmpty()){
+    std::pair<unsigned int, QString> pair=exec_queue_.dequeue();
+    run_command_(pair);
+  }
+}
+
+unsigned int PythonInterpreterWorker::AddCommand(const QString& command)
+{
+  exec_queue_.enqueue(std::pair<unsigned int, QString>(++command_id_,command)); //wrap around in command_id_ intended
+  return command_id_;
+}
+
+bool PythonInterpreterWorker::is_simple_expression_(const QString& expr)
+{
+  try {
+    parse_expr_cmd_(bp::str(expr.toStdString()));
+    return true;
+  } catch(bp::error_already_set&) {
+    PyErr_Clear();
+    return false;
+  }
+}
+void PythonInterpreterWorker::run_command_(std::pair<unsigned int,QString> pair)
+{
+  #ifndef _MSC_VER
+  SigIntHandler sh(sig_act_);
+  #endif
+  try{
+    // todo handle quit and exit without parantheses
+    current_id_=pair.first;
+    QString command=pair.second;
+    if(is_simple_expression(command)){
+      bp::object result=bp::eval(bp::str(command.toStdString()),
+                                 main_namespace_, main_namespace_);
+      String rstring=bp::extract<String>(repr_(result));
+      if(rstring!="None"){
+        handle_redirector_output(QString::fromStdString(rstring)+"\n");
+      }
+    } else {
+      bp::exec(bp::str(command.toStdString()),main_namespace_,main_namespace_);
+    }
+    emit Finished(pair.first,true);
+    return;
+  }catch(bp::error_already_set){
+    if(PyErr_ExceptionMatches(PyExc_SystemExit)){
+      PyErr_Clear();
+      //emit Exit();
+    } else{
+      PyErr_Print();
+    }
+  }
+  emit Finished(pair.first,false);
+  return;
+}
+
+bool PythonInterpreterWorker::is_simple_expression(const QString& expr)
+{
+  try {
+    parse_expr_cmd_(bp::str(expr.toStdString()));
+    return true;
+  } catch(bp::error_already_set&) {
+    PyErr_Clear();
+    return false;
+  }
+}
+
+void PythonInterpreterWorker::handle_redirector_output(const QString& output)
+{
+  emit Output(current_id_,output);
+}
+
+void PythonInterpreterWorker::handle_redirector_error(const QString& output)
+{
+  emit ErrorOutput(current_id_,output);
+}
+
+}} //ns
+
diff --git a/modules/gui/src/python_shell/python_interpreter_worker.hh b/modules/gui/src/python_shell/python_interpreter_worker.hh
new file mode 100644
index 000000000..5093c61b9
--- /dev/null
+++ b/modules/gui/src/python_shell/python_interpreter_worker.hh
@@ -0,0 +1,53 @@
+#ifndef PYTHON_INTERPRETER_WORKER_HH
+#define PYTHON_INTERPRETER_WORKER_HH
+
+#include <csignal>
+#include <utility>
+#include <QObject>
+#include <QQueue>
+#include <boost/python.hpp>
+#include <boost/shared_ptr.hpp>
+#include "output_redirector.hh"
+
+namespace ost { namespace gui {
+namespace bp = boost::python;
+
+class PythonInterpreterWorker: public QObject
+{
+Q_OBJECT
+public:
+  PythonInterpreterWorker();
+  unsigned int AddCommand(const QString& command);
+
+signals:
+  void Finished(unsigned int id, bool error_state);
+  void Output(unsigned int id,const QString& output);
+  void ErrorOutput(unsigned int id,const QString& output);
+
+public slots:
+  void Wake();
+
+protected slots:
+  void handle_redirector_output(const QString& output);
+  void handle_redirector_error(const QString& output);
+
+protected:
+  bool is_simple_expression(const QString& expr);
+  void run_command_(std::pair<unsigned int,QString> pair);
+  bool is_simple_expression_(const QString& expr);
+  QQueue<std::pair<unsigned int,QString> > exec_queue_;
+  unsigned int command_id_;
+  boost::shared_ptr<OutputRedirector>  output_redirector_;
+  boost::shared_ptr<OutputRedirector>  error_redirector_;
+#ifndef _MSC_VER
+  struct sigaction sig_act_;
+#endif
+  bp::object parse_expr_cmd_;
+  bp::object repr_;
+  bp::dict main_namespace_;
+  unsigned int current_id_;
+};
+
+}} //ns
+
+#endif // PYTHON_INTERPRETER_WORKER_HH
diff --git a/modules/gui/src/python_shell/python_namespace_tree_item.cc b/modules/gui/src/python_shell/python_namespace_tree_item.cc
index c4af0f207..065eeab41 100644
--- a/modules/gui/src/python_shell/python_namespace_tree_item.cc
+++ b/modules/gui/src/python_shell/python_namespace_tree_item.cc
@@ -89,6 +89,8 @@ bool PythonNamespaceTreeItem::CanFetchMore() const
 
 void PythonNamespaceTreeItem::FetchMore()
 {
+  // todo should imediately return if worker thread is working
+  // todo fix completion for builtins
   initialized_=true;  
   bp::object dir=bp::import("__main__").attr("__builtins__").attr("dir");  
   bp::list keys=bp::extract<bp::list>(dir(namespace_)); 
diff --git a/modules/gui/src/python_shell/python_namespace_tree_model.cc b/modules/gui/src/python_shell/python_namespace_tree_model.cc
index c5b66e03d..1427a425d 100644
--- a/modules/gui/src/python_shell/python_namespace_tree_model.cc
+++ b/modules/gui/src/python_shell/python_namespace_tree_model.cc
@@ -26,7 +26,7 @@ PythonNamespaceTreeModel::PythonNamespaceTreeModel():
   QAbstractItemModel(),
   root_item_(new PythonNamespaceTreeItem(PythonInterpreter::Instance().GetMainModule(),"main"))
 {
-  connect(&PythonInterpreter::Instance(), SIGNAL(Done(int,const QString&)),
+  connect(&PythonInterpreter::Instance(), SIGNAL(Finished(unsigned int,bool)),
           this, SLOT(NamespaceChanged(void)));
 }
 
@@ -37,6 +37,7 @@ PythonNamespaceTreeModel::~PythonNamespaceTreeModel()
 
 void PythonNamespaceTreeModel::NamespaceChanged()
 {
+  // todo should only be called after all commands are executed
   delete root_item_;
   root_item_=new PythonNamespaceTreeItem(PythonInterpreter::Instance().GetMainModule(),"main");
   dataChanged(QModelIndex(),QModelIndex());
diff --git a/modules/gui/src/python_shell/python_shell.cc b/modules/gui/src/python_shell/python_shell.cc
index ae3030b8a..6999bb35c 100644
--- a/modules/gui/src/python_shell/python_shell.cc
+++ b/modules/gui/src/python_shell/python_shell.cc
@@ -63,10 +63,6 @@ bool PythonShell::Save(const QString& prefix)
   return true;  
 }
 
-void PythonShell::AddLogger(TextLogger* logger)
-{
-  this->PyShell()->AddLogger(logger);
-}
 
 PythonShellWidget* PythonShell::PyShell()
 {
diff --git a/modules/gui/src/python_shell/python_shell.hh b/modules/gui/src/python_shell/python_shell.hh
index c9246f076..84f8929ce 100644
--- a/modules/gui/src/python_shell/python_shell.hh
+++ b/modules/gui/src/python_shell/python_shell.hh
@@ -40,7 +40,6 @@ public:
   virtual bool Save(const QString& prefix);
   
 public:
-  void AddLogger(TextLogger* logger);  
   PythonShellWidget* PyShell();
 };
  
diff --git a/modules/gui/src/python_shell/python_shell_widget.cc b/modules/gui/src/python_shell/python_shell_widget.cc
index 6d8183bf9..2446daa93 100644
--- a/modules/gui/src/python_shell/python_shell_widget.cc
+++ b/modules/gui/src/python_shell/python_shell_widget.cc
@@ -16,8 +16,13 @@
 // along with this library; if not, write to the Free Software Foundation, Inc.,
 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 //------------------------------------------------------------------------------
+/*
+  Authors: Marco Biasini, Andreas Schenk
+ */
+
 #include <iostream>
 
+#include <QApplication>
 #include <QDebug>
 #include <QFontMetrics>
 #include <QClipboard>
@@ -26,7 +31,6 @@
 #include <QDirModel>
 #include <QStringList>
 
-#include <ost/gui/python_shell/text_logger.hh>
 #include "python_shell_widget.hh"
 
 #include "gutter.hh"
@@ -37,6 +41,7 @@
 
 #include "python_completer.hh"
 #include "path_completer.hh"
+#include "transition.hh"
 
 
 
@@ -58,9 +63,15 @@ PythonShellWidget::PythonShellWidget(QWidget* parent):
   output_visible_(true),
   completion_start_(0),
   completion_end_(0),
-  mode_(SHELL_INTERACTION_BASH)
+  mode_(SHELL_INTERACTION_BASH),
+  block_edit_start_(document()->begin()),
+  output_blocks_(),
+  machine_(new StateMachine(this)),
+  readonly_machine_(new StateMachine(this)),
+  readwrite_state_(new State),
+  multiline_active_state_(new State)
 {
-  this->setLineWrapMode(QPlainTextEdit::NoWrap);
+  setLineWrapMode(QPlainTextEdit::NoWrap);
   document()->setDocumentLayout(new PythonShellTextDocumentLayout(document()));
   setViewportMargins(Gutter::GUTTER_WIDTH, 0, 0, 0);
   setUndoRedoEnabled(false);
@@ -68,14 +79,17 @@ PythonShellWidget::PythonShellWidget(QWidget* parent):
   QFontMetrics metrics(font());
   setTabStopWidth(2*metrics.width(" "));
   setMaximumBlockCount(1000000);
-  connect(this, SIGNAL(updateRequest(QRect, int)) ,gutter_, 
-          SLOT(Update(QRect, int)));
-  connect(this, SIGNAL(Execute(QString)),&PythonInterpreter::Instance(), 
-          SLOT(RunCommand(QString)),Qt::QueuedConnection);
-  connect(&PythonInterpreter::Instance(), SIGNAL(Done(int,const QString&)),this, 
-          SLOT(AppendOutput(int,const QString&)),Qt::QueuedConnection);
+
   textCursor().block().setUserState(BLOCKTYPE_ACTIVE);
   completer_->setWidget(viewport());
+  connect(&PythonInterpreter::Instance(), SIGNAL(Output(unsigned int, const QString &)),
+          this,SLOT(AppendOutput(unsigned int, const QString &)));
+  connect(&PythonInterpreter::Instance(), SIGNAL(Finished(unsigned int, bool)),
+          this,SLOT(OutputFinished(unsigned int,bool)));
+  connect(&PythonInterpreter::Instance(), SIGNAL(ErrorOutput(unsigned int, const QString &)),
+          this,SLOT(AppendError(unsigned int, const QString &)));
+  connect(this,   SIGNAL(updateRequest(QRect, int)) ,
+          gutter_,SLOT(Update(QRect, int)));
   connect(completer_,SIGNAL(activated(const QString&)),this, 
           SLOT(InsertCompletion(const QString&)));
   connect(completer_,SIGNAL(recomplete(const QString&)),this, 
@@ -94,13 +108,216 @@ PythonShellWidget::PythonShellWidget(QWidget* parent):
   connect(this,SIGNAL(SetPathCompletionPrefix(const QString&)),path_completer_, 
           SLOT(setCompletionPrefix(const QString&)));
   if (mode_==SHELL_INTERACTION_BASH) {
-    SetBlockEditMode(EDITMODE_SINGLELINE);    
+    set_block_edit_mode_(EDITMODE_SINGLELINE);
   } else {
-    SetBlockEditMode(EDITMODE_MULTILINE_INACTIVE);
+    set_block_edit_mode_(EDITMODE_MULTILINE_INACTIVE);
   }
-  PythonInterpreter::Instance().RedirectOutput();
+  setup_readonly_state_machine_();
+  setup_state_machine_();
 }
 
+void PythonShellWidget::setup_readonly_state_machine_()
+{
+  State* readonly=new State;
+  readonly_machine_->addState(readonly);
+  readonly_machine_->addState(readwrite_state_);
+  readonly->addTransition((new SignalTransition(this,
+                                                SIGNAL(cursorPositionChanged()),
+                                                readwrite_state_,
+                                                new EditPositionGuard(this,EditPositionGuard::EQUAL |EditPositionGuard::BIGGER))));
+  readonly->addTransition((new KeyEventTransition(QEvent::KeyPress,
+                                                  Qt::Key_Any,
+                                                  Qt::NoModifier,
+                                                  readwrite_state_,
+                                                  false)));
+  readwrite_state_->addTransition((new SignalTransition(this,
+                                                      SIGNAL(cursorPositionChanged()),
+                                                      readonly,
+                                                      new EditPositionGuard(this,EditPositionGuard::SMALLER))));
+  readwrite_state_->addTransition((new KeyEventTransition(QEvent::KeyPress,
+                                                         Qt::Key_Backspace,
+                                                         Qt::NoModifier,
+                                                         readwrite_state_,
+                                                         true,
+                                                         new EditPositionGuard(this,EditPositionGuard::EQUAL))));
+  connect(readonly,SIGNAL(entered()),this,SLOT(OnReadonlyEntered()));
+  connect(readwrite_state_,SIGNAL(entered()),this,SLOT(OnReadwriteEntered()));
+  readonly_machine_->setInitialState(readwrite_state_);
+  readonly_machine_->start();
+}
+
+void PythonShellWidget::setup_state_machine_()
+{
+  State* single_line=new State;
+  State* multi_line_inactive=new State;
+  State* completing=new State;
+  State* executing=new State;
+  State* history_up=new State;
+  State* history_down=new State;
+  machine_->addState(single_line);
+  machine_->addState(multiline_active_state_);
+  machine_->addState(multi_line_inactive);
+  machine_->addState(completing);
+  machine_->addState(executing);
+  machine_->addState(history_up);
+  machine_->addState(history_down);
+
+  connect(single_line,SIGNAL(entered()),this,SLOT(OnSingleLineStateEntered()));
+  connect(multiline_active_state_,SIGNAL(entered()),this,SLOT(OnMultiLineActiveStateEntered()));
+  connect(multi_line_inactive,SIGNAL(entered()),this,SLOT(OnMultiLineInactiveStateEntered()));
+  connect(history_up,SIGNAL(entered()),this,SLOT(OnHistoryUpStateEntered()));
+  connect(history_down,SIGNAL(entered()),this,SLOT(OnHistoryDownStateEntered()));
+  connect(executing,SIGNAL(entered()),this,SLOT(OnExecuteStateEntered()));
+
+  if (mode_==SHELL_INTERACTION_BASH) {
+    KeyEventTransition* tr1=new KeyEventTransition(QEvent::KeyPress,
+                                                   Qt::Key_Return,
+                                                   Qt::NoModifier,
+                                                   executing,
+                                                   true,
+                                                   new BlockStatusGuard(this,CODE_BLOCK_COMPLETE | CODE_BLOCK_ERROR));
+    single_line->addTransition(tr1);
+    connect(tr1,SIGNAL(triggered()),this,SLOT(OnEnterTransition()));
+    KeyEventTransition* tr3=new KeyEventTransition(QEvent::KeyPress,
+                                                   Qt::Key_Return,
+                                                   Qt::NoModifier,
+                                                   multiline_active_state_,
+                                                   true,
+                                                   new BlockStatusGuard(this,CODE_BLOCK_INCOMPLETE));
+    single_line->addTransition(tr3);
+    connect(tr3,SIGNAL(triggered()),this,SLOT(OnEnterTransition()));
+    single_line->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Up,Qt::NoModifier,history_up));
+    single_line->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Down,Qt::NoModifier,history_down));
+
+    KeyEventTransition* tr4=new KeyEventTransition(QEvent::KeyPress,
+                                                   Qt::Key_Return,
+                                                   Qt::NoModifier,
+                                                   executing,
+                                                   true,
+                                                   new BlockStatusGuard(this,CODE_BLOCK_COMPLETE | CODE_BLOCK_ERROR));
+    multi_line_inactive->addTransition(tr4);
+    connect(tr4,SIGNAL(triggered()),this,SLOT(OnEnterTransition()));
+    KeyEventTransition* tr6=new KeyEventTransition(QEvent::KeyPress,
+                                                   Qt::Key_Return,
+                                                   Qt::NoModifier,
+                                                   multiline_active_state_,
+                                                   true,
+                                                   new BlockStatusGuard(this,CODE_BLOCK_INCOMPLETE));
+    multi_line_inactive->addTransition(tr6);
+    connect(tr6,SIGNAL(triggered()),this,SLOT(OnEnterTransition()));
+    multi_line_inactive->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Left,Qt::NoModifier,multiline_active_state_));
+    multi_line_inactive->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Right,Qt::NoModifier,multiline_active_state_));
+    multi_line_inactive->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Return,Qt::ControlModifier,multiline_active_state_));
+    multi_line_inactive->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Up,Qt::NoModifier,history_up));
+    multi_line_inactive->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Down,Qt::NoModifier,history_down));
+
+    KeyEventTransition* tr7=new KeyEventTransition(QEvent::KeyPress,
+                                                   Qt::Key_Return,
+                                                   Qt::NoModifier,
+                                                   executing,
+                                                   true,
+                                                   new BlockStatusGuard(this,CODE_BLOCK_COMPLETE | CODE_BLOCK_ERROR));
+    multiline_active_state_->addTransition(tr7);
+    connect(tr7,SIGNAL(triggered()),this,SLOT(OnEnterTransition()));
+    KeyEventTransition* tr8=new KeyEventTransition(QEvent::KeyPress,
+                                                   Qt::Key_Return,
+                                                   Qt::NoModifier,
+                                                   multiline_active_state_,
+                                                   true,
+                                                   new BlockStatusGuard(this,CODE_BLOCK_INCOMPLETE));
+    multiline_active_state_->addTransition(tr8);
+    connect(tr8,SIGNAL(triggered()),this,SLOT(OnEnterTransition()));
+
+    multiline_active_state_->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Escape,Qt::NoModifier,multi_line_inactive));
+    multiline_active_state_->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Up,Qt::ControlModifier,history_up));
+    multiline_active_state_->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Down,Qt::ControlModifier,history_down));
+
+    history_up->addTransition(new AutomaticTransition(multi_line_inactive,new HistoryGuard(&history_,EDITMODE_MULTILINE_INACTIVE)));
+    history_up->addTransition(new AutomaticTransition(single_line,new HistoryGuard(&history_,EDITMODE_SINGLELINE)));
+    history_down->addTransition(new AutomaticTransition(multi_line_inactive,new HistoryGuard(&history_,EDITMODE_MULTILINE_INACTIVE)));
+    history_down->addTransition(new AutomaticTransition(single_line,new HistoryGuard(&history_,EDITMODE_SINGLELINE)));
+
+    executing->addTransition(new AutomaticTransition(single_line));
+
+    machine_->setInitialState(single_line);
+  } else {
+    multi_line_inactive->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Return,Qt::ControlModifier,executing));
+    multi_line_inactive->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Return,Qt::NoModifier,multiline_active_state_));
+
+    multiline_active_state_->addTransition(new KeyEventTransition(QEvent::KeyPress,Qt::Key_Return,Qt::ControlModifier,executing));
+
+    executing->addTransition(new AutomaticTransition(multi_line_inactive));
+
+    history_up->addTransition(new AutomaticTransition(multi_line_inactive));
+    history_down->addTransition(new AutomaticTransition(multi_line_inactive));
+
+    machine_->setInitialState(multi_line_inactive);
+  }
+
+  machine_->start();
+}
+
+void PythonShellWidget::OnReadonlyEntered()
+{
+  setReadOnly(true);
+}
+
+void PythonShellWidget::OnReadwriteEntered()
+{
+  if(textCursor().position()< GetEditStartBlock().position()){
+    moveCursor(QTextCursor::End);
+  }
+  setReadOnly(false);
+}
+
+void PythonShellWidget::OnEnterTransition()
+{
+  QTextCursor cursor=textCursor();
+  cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::MoveAnchor);
+  setTextCursor(cursor);
+}
+void PythonShellWidget::OnSingleLineStateEntered()
+{
+  set_block_edit_mode_(EDITMODE_SINGLELINE);
+}
+void PythonShellWidget::OnMultiLineActiveStateEntered()
+{
+    insertPlainText(QString(QChar::ParagraphSeparator));
+    set_block_edit_mode_(EDITMODE_MULTILINE_ACTIVE);
+}
+void PythonShellWidget::OnMultiLineInactiveStateEntered()
+{
+  set_block_edit_mode_(EDITMODE_MULTILINE_INACTIVE);
+}
+void PythonShellWidget::OnHistoryUpStateEntered()
+{
+  --history_;
+  set_command_(history_.GetCommand());
+  set_block_edit_mode_(history_.GetCommandMode());
+}
+void PythonShellWidget::OnHistoryDownStateEntered()
+{
+  ++history_;
+  set_command_(history_.GetCommand());
+  set_block_edit_mode_(history_.GetCommandMode());
+}
+void PythonShellWidget::OnExecuteStateEntered()
+{
+  set_block_type_(block_edit_start_,textCursor().block(),BLOCKTYPE_CODE);
+  insertPlainText(QString(QChar::ParagraphSeparator));
+  QString command=GetCommand();
+  unsigned int id=PythonInterpreter::Instance().RunCommand(command);
+  output_blocks_.insert(id,textCursor().block());
+  command=command.trimmed();
+  if (command.size()>0) {
+    history_.AppendCommand(command,get_block_edit_mode_());
+  }
+  insertPlainText(QString(QChar::ParagraphSeparator));
+  block_edit_start_=textCursor().block();
+
+}
+
+
 void PythonShellWidget::SetTabWidth(int width) {
   tab_width_=width;
   QFontMetrics metrics(font());
@@ -115,14 +332,14 @@ int PythonShellWidget::GetTabWidth() const {
 
 
 
-void PythonShellWidget::WrapIntoFunction(const QString& command)
+void PythonShellWidget::wrap_into_function_(const QString& command)
 {
   QString tmp_command=command;
   tmp_command.replace(QString(QChar::LineSeparator),
                       QString(QChar::LineSeparator)+QString("\t"));
   QString tmp="def func():"+QString(QChar::LineSeparator)+"\t";
   tmp+=tmp_command;
-  SetCommand(tmp);
+  set_command_(tmp);
   QTextCursor tc=textCursor();
   tc.setPosition(document()->lastBlock().position()+QString("def ").length());
   tc.setPosition(document()->lastBlock().position()+
@@ -146,51 +363,79 @@ void  PythonShellWidget::InsertCompletion(const QString& completion)
 }
 
 
-void PythonShellWidget::AppendOutput(int status,const QString& output)
+void PythonShellWidget::AppendOutput(unsigned int id,const QString& output)
 {
-  QStringList output_list=output.split("\n");
-  if (output_list.size()>=maximumBlockCount ()){
-    QTextCursor cursor=textCursor();
-    cursor.movePosition(QTextCursor::Start);
-    cursor.movePosition(QTextCursor::End,QTextCursor::KeepAnchor);
-    cursor.removeSelectedText();
-    output_list.erase(output_list.begin(),output_list.begin()+
-                      output_list.size()+1-maximumBlockCount());
-  } else if (output_list.size()+blockCount()>maximumBlockCount()){
-    QTextCursor cursor=textCursor();
-    cursor.movePosition(QTextCursor::Start);
-    cursor.movePosition(QTextCursor::NextBlock,QTextCursor::KeepAnchor,
-                        output_list.size()+1+blockCount()-maximumBlockCount());
-    cursor.removeSelectedText();
+  OutputBlockList::iterator it=output_blocks_.find(id);
+  if(it==output_blocks_.end()){
+    return;
   }
-  moveCursor(QTextCursor::End);
-  QTextCursor cursor=textCursor();
-  cursor.beginEditBlock();
-  if (output!="" && output_list.size()>0) {
-    for(int i=0;i<output_list.size();++i){
-      document()->lastBlock().setUserState(status);
-      insertPlainText(output_list[i]);
-      insertPlainText(QString(QChar::ParagraphSeparator));
-    }
+  if(! it->isValid()){
+    return;
+  }
+  QTextCursor cursor(*it);
+  cursor.movePosition(QTextCursor::EndOfBlock);
+  cursor.insertText(output);
+  set_block_type_(output_blocks_[id],cursor.block(),BLOCKTYPE_OUTPUT);
+  output_blocks_[id]=cursor.block();
+  verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum);
+  ensureCursorVisible();
+  repaint();
+}
+
+void PythonShellWidget::AppendError(unsigned int id,const QString& output)
+{
+  OutputBlockList::iterator it=output_blocks_.find(id);
+  if(it==output_blocks_.end()){
+    return;
+  }
+  if(! it->isValid()){
+    return;
   }
-  document()->lastBlock().setUserState(BLOCKTYPE_ACTIVE);
-  cursor.endEditBlock();
+  QTextCursor cursor(*it);
+  cursor.movePosition(QTextCursor::EndOfBlock);
+  cursor.insertText(output);
+  set_block_type_(output_blocks_[id],cursor.block(),BLOCKTYPE_ERROR);
+  output_blocks_[id]=cursor.block();
   verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum);
-  this->ensureCursorVisible();  
+  ensureCursorVisible();
+  repaint();
 }
 
+void PythonShellWidget::set_block_type_(const QTextBlock& start, const QTextBlock& end, BlockType type)
+{
+  QTextBlock block=start;
+  while(block!=end){
+    block.setUserState(type);
+    block=block.next();
+  }
+  block.setUserState(type);
+  document()->markContentsDirty(start.position(),end.position()+end.length()-start.position());
+}
 
+void PythonShellWidget::OutputFinished(unsigned int id, bool error)
+{
+  OutputBlockList::iterator it=output_blocks_.find(id);
+  if(it==output_blocks_.end()){
+    return;
+  }
+  if(it->isValid()){
+    QTextCursor cursor(*it);
+    cursor.movePosition(QTextCursor::Left,QTextCursor::KeepAnchor);
+    cursor.removeSelectedText();
+  }
+  output_blocks_.erase(it);
+}
 
-void PythonShellWidget::SetCommand(const QString& command)
+void PythonShellWidget::set_command_(const QString& command)
 {
-  QTextCursor cursor=textCursor();
-  cursor.setPosition(document()->lastBlock().position());
+  QTextCursor cursor(block_edit_start_);
   cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
-  cursor.beginEditBlock();
   cursor.removeSelectedText();
+  if(block_edit_start_.isValid()){
+    block_edit_start_=document()->lastBlock();
+  }
   cursor.insertText(command);
-  cursor.endEditBlock();
-  QAbstractTextDocumentLayout* layout=document()->documentLayout();  
+  QAbstractTextDocumentLayout* layout=document()->documentLayout();
   dynamic_cast<PythonShellTextDocumentLayout*>(layout)->EmitSizeChange();
   verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum);
   ensureCursorVisible();
@@ -253,69 +498,35 @@ void PythonShellWidget::Complete(bool inline_completion)
   }
 }
 
-bool PythonShellWidget::HandleNav(QKeyEvent* event)
-{
-  if(event->key() == Qt::Key_Up){
-     if (GetBlockEditMode()!=EDITMODE_MULTILINE_ACTIVE) {
-       SanitizeCursorPosition();
-       if(history_.AtEnd()){
-         history_.SetCurrentCommand(textCursor().block().text(),
-                                    GetBlockEditMode());
-       }
-       --history_;
-       SetCommand(history_.GetCommand());
-       this->SetBlockEditMode(history_.GetCommandMode());
-       return true;
-     }else{
-       QTextCursor temporary_cursor=textCursor();
-       int temp_position_before=temporary_cursor.position();
-       temporary_cursor.movePosition(QTextCursor::Up);
-       if (temporary_cursor.position()<document()->lastBlock().position() && 
-           temp_position_before>=document()->lastBlock().position() && 
-           !(event->modifiers() & Qt::ShiftModifier)) {
-         return true;
-       }
-     }
-   }
-   if (event->key() == Qt::Key_Down && 
-       GetBlockEditMode()!=EDITMODE_MULTILINE_ACTIVE) {
-     SanitizeCursorPosition();
-     ++history_;
-     SetCommand(history_.GetCommand());
-     this->SetBlockEditMode(history_.GetCommandMode());     
-     return true;
-   }  
-  return false;
-}
 
-bool PythonShellWidget::HandleCustomCommands(QKeyEvent* event)
+
+bool PythonShellWidget::handle_custom_commands_(QKeyEvent* event)
 {
-  // see ticket #38
-  #if 0
+  /* deactivated until fix found
   if ((event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) && 
       event->key() == Qt::Key_H) {
-    SetOutputVisible(!output_visible_);
+    set_output_visible_(!output_visible_);
     return true;
-  }
-  #endif
+  }*/
+
   if ((event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) && 
       event->key() == Qt::Key_W) {
-    WrapIntoFunction(textCursor().selectedText());
+    wrap_into_function_(textCursor().selectedText());
     return true;
   }
   // custom handling of CTRL+A to select only text in edit or output section
   if (event->modifiers() == Qt::ControlModifier && 
       event->key() == Qt::Key_A) {
     QTextCursor cursor=textCursor();
-    if(cursor.position()<document()->lastBlock().position()){
+    if(cursor.position()<GetEditStartBlock().position()){
       // select all output
       cursor.setPosition(0);
-      cursor.setPosition(document()->lastBlock().position(),QTextCursor::KeepAnchor);
+      cursor.setPosition(GetEditStartBlock().position(),QTextCursor::KeepAnchor);
       setTextCursor(cursor);
     }else{
       //select whole edit section
-      cursor.movePosition(QTextCursor::StartOfBlock);
-      cursor.movePosition(QTextCursor::EndOfBlock,QTextCursor::KeepAnchor);
+      cursor.setPosition(GetEditStartBlock().position());
+      cursor.movePosition(QTextCursor::End,QTextCursor::KeepAnchor);
       setTextCursor(cursor);
     }
     return true;
@@ -323,81 +534,8 @@ bool PythonShellWidget::HandleCustomCommands(QKeyEvent* event)
   return false;
 }
 
-bool PythonShellWidget::HandleReturnKey(QKeyEvent* event)
-{
-  if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
-    QString command=textCursor().block().text();    
-    SanitizeCursorPosition();
-    command.replace(QString(QChar::LineSeparator),QString("\n"));
-    bool execute_cmd=false;
-    bool insert_new_line=false;
-    bool move_to_end=false;
-    if (mode_==SHELL_INTERACTION_MATHEMATICA) {
-      if (event->modifiers() & Qt::ControlModifier ||
-          (command.endsWith("\n") && textCursor().atEnd())) {
-        execute_cmd=true;
-        move_to_end=(event->modifiers() & Qt::ControlModifier)==false;
-      } else {
-        if (textCursor().block().text().trimmed().length()==0) {
-          return true;
-        }
-        insert_new_line=true;
-        move_to_end=true;
-      }
-    } else {
-      if (event->modifiers() & Qt::ControlModifier) {
-        insert_new_line=true;
-        move_to_end=false;
-      } else {
-        PythonInterpreter& pi=PythonInterpreter::Instance();
-        CodeBlockStatus status=pi.GetCodeBlockStatus(command);
-        if (status!=CODE_BLOCK_INCOMPLETE) {
-          execute_cmd=true;
-          move_to_end=true;
-        } else {
-          insert_new_line=true;
-          move_to_end=true;
-        }
-      }
-    }
-    QTextCursor cursor=this->textCursor();      
-    if (move_to_end) {
-      cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::MoveAnchor);
-      this->setTextCursor(cursor);
-    }    
-    if (insert_new_line) {
-      cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
-      bool new_indent=cursor.selectedText()==":";
-      cursor.movePosition(QTextCursor::StartOfLine);
-      cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
-      insertPlainText(QString(QChar::LineSeparator));
-      if(cursor.selectedText()[0].isSpace()){
-        insertPlainText(QString(cursor.selectedText()));
-      }
-      if (new_indent){
-        insertPlainText(QString("\t"));
-      }
-      QAbstractTextDocumentLayout* layout=document()->documentLayout();
-      dynamic_cast<PythonShellTextDocumentLayout*>(layout)->EmitSizeChange();
-      verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum);
-      SetBlockEditMode(EDITMODE_MULTILINE_ACTIVE);
-      return true;
-    }
-    if (execute_cmd) {
-      QKeyEvent new_event(event->type(),event->key(),
-                          Qt::NoModifier,
-                          event->text(),event->isAutoRepeat(),
-                          event->count());
-      QPlainTextEdit::keyPressEvent(&new_event);
-      ExecuteCommand();
-      return true;                          
-    }
-  }
-  return false;
-}
-
 
-bool PythonShellWidget::HandleCompletion(QKeyEvent* event)
+bool PythonShellWidget::handle_completion_(QKeyEvent* event)
 {
   if(event->key() == Qt::Key_Tab){
     QRegExp rx("^\\s*$"); // only white spaces from beginning of line
@@ -413,79 +551,32 @@ bool PythonShellWidget::HandleCompletion(QKeyEvent* event)
   return false;
 }
 
+QTextBlock PythonShellWidget::GetEditStartBlock()
+{
+  return block_edit_start_;
+}
+
 
 void PythonShellWidget::keyPressEvent(QKeyEvent* event)
 {
-  if (this->HandleCustomCommands(event))
-    return;
-    
-  if (this->HandleCompletion(event))
-    return;
-  if (this->HandleNav(event))
-    return;
-
-  if (this->HandleReturnKey(event))
-    return;
-  if ((event->text()!="" && !(event->modifiers() & Qt::ControlModifier)) || 
-      event->matches(QKeySequence::Cut)  ||  event->key()== Qt::Key_Delete){
-    SanitizeCursorPosition();
-    if(GetBlockEditMode()==EDITMODE_MULTILINE_INACTIVE){
-      SetBlockEditMode(EDITMODE_MULTILINE_ACTIVE);
-    }
-  }
-  if (GetBlockEditMode()==EDITMODE_MULTILINE_ACTIVE && event->key()==Qt::Key_Escape){
-    SetBlockEditMode(EDITMODE_MULTILINE_INACTIVE);
-  }
-  
-  if (GetBlockEditMode()==EDITMODE_MULTILINE_INACTIVE && (event->key()==Qt::Key_Right || 
-      event->key() == Qt::Key_Left)) {
-    SetBlockEditMode(EDITMODE_MULTILINE_ACTIVE);
-  }
-  
-  if (event->key()== Qt::Key_Backspace && 
-      textCursor().position()<=document()->lastBlock().position()
-     && !textCursor().hasSelection()){
+  if (this->handle_custom_commands_(event)){
     return;
   }
-  
-
-  if (event->key()== Qt::Key_Left && 
-      textCursor().position()==document()->lastBlock().position() && 
-      !(event->modifiers() & Qt::ShiftModifier)) {
+  if (this->handle_completion_(event)){
     return;
   }
   QPlainTextEdit::keyPressEvent(event);
 }
 
-void PythonShellWidget::ExecuteCommand()
-{
-  QString command=textCursor().block().previous().text();
-  textCursor().block().previous().setUserState(BLOCKTYPE_CODE);
-  document()->markContentsDirty(textCursor().block().previous().position(),
-                                textCursor().block().previous().length());
-  QString cmd=command;
-  cmd.replace(QString(QChar::LineSeparator),QString("\n"));
-  emit Execute(cmd);
-
-  command=command.trimmed();
-  if (command.size()>0) {
-    history_.AppendCommand(command,GetBlockEditMode());
-  }
-  if (mode_==SHELL_INTERACTION_MATHEMATICA) {
-    SetBlockEditMode(EDITMODE_MULTILINE_INACTIVE);
-  } else {
-    SetBlockEditMode(EDITMODE_SINGLELINE);
-  }  
-}
 
 void PythonShellWidget::mouseReleaseEvent(QMouseEvent * event)
 {
   QTextCursor mouse_cursor=cursorForPosition(event->pos());
-  if (GetBlockEditMode()==EDITMODE_MULTILINE_INACTIVE && 
+  if (get_block_edit_mode_()==EDITMODE_MULTILINE_INACTIVE &&
       event->button() == Qt::LeftButton &&
       mouse_cursor.position()>=document()->lastBlock().position()) {
     // switch to active edit mode upon mouse click in edit section
-    SetBlockEditMode(EDITMODE_MULTILINE_ACTIVE);
+    set_block_edit_mode_(EDITMODE_MULTILINE_ACTIVE);
   }
   if (event->button() == Qt::MidButton && 
       mouse_cursor.position()<document()->lastBlock().position()) {
@@ -496,51 +587,34 @@ void PythonShellWidget::mouseReleaseEvent(QMouseEvent * event)
   QPlainTextEdit::mouseReleaseEvent(event);
 }
 
-void PythonShellWidget::SanitizeCursorPosition()
-{
-  if(textCursor().position()<document()->lastBlock().position()){
-    if(textCursor().anchor()<document()->lastBlock().position()){
-      moveCursor(QTextCursor::End);
-    }else{
-      QTextCursor cursor=textCursor();
-      cursor.setPosition(document()->lastBlock().position(),
-                         QTextCursor::KeepAnchor);
-      setTextCursor(cursor);
-    }
-  }else{
-    if(textCursor().anchor()<document()->lastBlock().position()){
-      int current_position=textCursor().position();
-      QTextCursor cursor=textCursor();
-      cursor.setPosition(document()->lastBlock().position());
-      cursor.setPosition(current_position,QTextCursor::KeepAnchor);
-      setTextCursor(cursor);
-    }
-  }
-}
 
-void PythonShellWidget::SetBlockEditMode(BlockEditMode flag)
+
+void PythonShellWidget::set_block_edit_mode_(BlockEditMode flag)
 {
   block_edit_mode_=flag;
   if(flag==EDITMODE_MULTILINE_ACTIVE){
-    document()->lastBlock().setUserState(BLOCKTYPE_BLOCKEDIT);
+    set_block_type_(block_edit_start_,document()->lastBlock(),BLOCKTYPE_BLOCKEDIT);
   }else if(flag==EDITMODE_MULTILINE_INACTIVE){
-    document()->lastBlock().setUserState(BLOCKTYPE_ACTIVE);
+    set_block_type_(block_edit_start_,document()->lastBlock(),BLOCKTYPE_ACTIVE);
   }else {
-    document()->lastBlock().setUserState(BLOCKTYPE_ACTIVE);
+    set_block_type_(block_edit_start_,document()->lastBlock(),BLOCKTYPE_ACTIVE);
   }
   gutter_->update();
-  document()->markContentsDirty(document()->lastBlock().position(),
-                                document()->lastBlock().length());
 }
 
-BlockEditMode PythonShellWidget::GetBlockEditMode()
+BlockEditMode PythonShellWidget::get_block_edit_mode_()
 {
   return block_edit_mode_;
 }
 
 QString PythonShellWidget::GetCommand()
 {
-  return document()->lastBlock().text();
+
+  QTextCursor cursor(block_edit_start_);
+  cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
+  QString text= cursor.selectedText();
+  text.replace(QChar::ParagraphSeparator,"\n");
+  return text;
 }
 
 void PythonShellWidget::insertFromMimeData(const QMimeData * source)
@@ -549,7 +623,7 @@ void PythonShellWidget::insertFromMimeData(const QMimeData * source)
     return;
   }
   QString text=source->text();
-  SanitizeCursorPosition();
+  readonly_machine_->setActive(readwrite_state_);
   text=text.replace("\r\n", QString(1, QChar::LineSeparator))
            .replace('\n', QChar::LineSeparator)
            .replace('\r', QChar::LineSeparator);
@@ -570,18 +644,9 @@ void PythonShellWidget::insertFromMimeData(const QMimeData * source)
   text=lines.join(QString(1, QChar::LineSeparator));
   int line_sep=text.count(QChar::LineSeparator);
   if(line_sep>0){
-    QString command=GetCommand();
-    int last_block_start=document()->lastBlock().position();
-    int last_block_end=last_block_start+document()->lastBlock().length();    
-    int shifted_start=textCursor().selectionStart()-last_block_start;
-    int shifted_end=last_block_end-textCursor().selectionEnd();
-    QString rpl_text=command.left(shifted_start)+text+
-                     command.right(shifted_end-1);
-    this->SetCommand(rpl_text);
-    SetBlockEditMode(EDITMODE_MULTILINE_ACTIVE);
-  }else{
-    QPlainTextEdit::insertFromMimeData(source);
+    machine_->setActive(multiline_active_state_);
   }
+  QPlainTextEdit::insertFromMimeData(source);
 }
 
 GutterBlockList PythonShellWidget::GetGutterBlocks(const QRect& rect)
@@ -602,7 +667,7 @@ GutterBlockList PythonShellWidget::GetGutterBlocks(const QRect& rect)
   return result;
 }
 
-void PythonShellWidget::SetOutputVisible(bool flag)
+void PythonShellWidget::set_output_visible_(bool flag)
 {
   output_visible_=flag;
   for (QTextBlock i=document()->begin(); i!=document()->end(); i=i.next()) {
@@ -610,9 +675,10 @@ void PythonShellWidget::SetOutputVisible(bool flag)
       i.setVisible(flag);
     }
   }
+  repaint();
   gutter_->update();
-  viewport()->update();  
-  this->update();  
+  viewport()->update();
+  this->update();
 }
 
 void PythonShellWidget::resizeEvent(QResizeEvent* event)
@@ -636,14 +702,5 @@ void PythonShellWidget::AquireFocus()
   setFocus(Qt::OtherFocusReason);
 }
 
-void PythonShellWidget::AddLogger(TextLogger* logger)
-{
-  loggers_.push_back(logger);
-  logger->setParent(this);  
-  connect(this,SIGNAL(Execute(const QString&)),
-          logger,SLOT(AppendCode(const QString&)));
-  connect(&PythonInterpreter::Instance(),SIGNAL(Done(int,const QString&)),
-          logger,SLOT(AppendOutput(int,const QString&)));
-}
 
 }}
diff --git a/modules/gui/src/python_shell/python_shell_widget.hh b/modules/gui/src/python_shell/python_shell_widget.hh
index b4c34ce45..d55f8c3c3 100644
--- a/modules/gui/src/python_shell/python_shell_widget.hh
+++ b/modules/gui/src/python_shell/python_shell_widget.hh
@@ -16,23 +16,24 @@
 // along with this library; if not, write to the Free Software Foundation, Inc.,
 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 //------------------------------------------------------------------------------
-#ifndef OST_GUI_PYTHON_SHELL_WIDGET_HH
-#define OST_GUI_PYTHON_SHELL_WIDGET_HH
-
 /*
   Authors: Marco Biasini, Andreas Schenk
  */
 
-#include <ost/gui/module_config.hh>
 
+#ifndef OST_GUI_PYTHON_SHELL_WIDGET_HH
+#define OST_GUI_PYTHON_SHELL_WIDGET_HH
+
+
+#include <ost/gui/module_config.hh>
+#include <QPlainTextEdit>
 
 #include "python_interpreter.hh"
 #include "shell_history.hh"
 #include  "python_shell_fw.hh"
-
 #include "python_syntax_highlighter.hh"
-
-#include <QPlainTextEdit>
+#include "state_machine.hh"
+#include "state.hh"
 
 namespace ost { namespace gui {
 
@@ -40,7 +41,6 @@ namespace ost { namespace gui {
 class Gutter;
 class PythonCompleter;
 class PathCompleter;
-class TextLogger;
 
 typedef enum {
   // to execute a command, the command must end with an empty line, or
@@ -52,19 +52,21 @@ typedef enum {
 } ShellInteractionMode;
 
 class DLLEXPORT_OST_GUI PythonShellWidget : public QPlainTextEdit {
+
+  typedef QHash<unsigned int,QTextBlock> OutputBlockList;
   Q_OBJECT
+
 public:
   PythonShellWidget(QWidget* parent=NULL);
   GutterBlockList GetGutterBlocks(const QRect& rect);
-  void SetOutputVisible(bool flag=true);
-  
-  void AddLogger(TextLogger* logger);
-  
   void SetInteractionMode(ShellInteractionMode mode);
-
   void SetTabWidth(int width);
   int GetTabWidth() const;
+  QString GetCommand();
+  QTextBlock GetEditStartBlock();
+
   int tab_width_;
+
 signals:
   void Execute(const QString& command);
   void RequestCompletion(const QRect& rect,bool inline_completion);
@@ -74,30 +76,39 @@ signals:
 
 public slots:
   void InsertCompletion(const QString& completion);
-  void AppendOutput(int status,const QString& output);
+  void AppendOutput(unsigned int id,const QString& output);
+  void AppendError(unsigned int id,const QString& output);
+  void OutputFinished(unsigned int id, bool error);
   void AquireFocus();
   void Complete(bool inline_completion=true);
   void Recomplete(const QString& completion);
+  // slots for state machine
+  void OnSingleLineStateEntered();
+  void OnMultiLineActiveStateEntered();
+  void OnMultiLineInactiveStateEntered();
+  void OnHistoryUpStateEntered();
+  void OnHistoryDownStateEntered();
+  void OnExecuteStateEntered();
+  void OnEnterTransition();
+  void OnReadonlyEntered();
+  void OnReadwriteEntered();
 
 protected:
   virtual void mouseReleaseEvent (QMouseEvent* event );
   virtual void keyPressEvent (QKeyEvent* event );
   virtual void resizeEvent(QResizeEvent* event);
   virtual void showEvent(QShowEvent* event);
-
-  void WrapIntoFunction(const QString& command);
-  void SetCommand(const QString& command);
-  QString GetCommand();
-  void insertFromMimeData( const QMimeData * source );
-  void SanitizeCursorPosition();
-  void SetBlockEditMode(BlockEditMode flag);
-  
-  bool HandleNav(QKeyEvent* event);
-  bool HandleCompletion(QKeyEvent* event);
-  bool HandleCustomCommands(QKeyEvent* event);
-  bool HandleReturnKey(QKeyEvent* event);
-  BlockEditMode GetBlockEditMode();
-  void ExecuteCommand();
+  virtual void insertFromMimeData( const QMimeData * source );
+  void set_output_visible_(bool flag=true);
+  void setup_readonly_state_machine_();
+  void setup_state_machine_();
+  void wrap_into_function_(const QString& command);
+  void set_command_(const QString& command);
+  void set_block_edit_mode_(BlockEditMode flag);
+  bool handle_completion_(QKeyEvent* event);
+  bool handle_custom_commands_(QKeyEvent* event);
+  BlockEditMode get_block_edit_mode_();
+  void set_block_type_(const QTextBlock& start,const QTextBlock& end, BlockType type);
 
   PythonSyntaxHighlighter highlighter_;
   PythonCompleter* completer_;
@@ -108,8 +119,13 @@ protected:
   bool output_visible_;
   int completion_start_;
   int completion_end_;
-  std::vector<TextLogger*>  loggers_;  
-  ShellInteractionMode mode_;  
+   ShellInteractionMode mode_;
+  QTextBlock block_edit_start_;
+  OutputBlockList output_blocks_;
+  StateMachine* machine_;
+  StateMachine* readonly_machine_;
+  State* readwrite_state_;
+  State* multiline_active_state_;
 };
 
 }}//ns
diff --git a/modules/gui/src/python_shell/state.cc b/modules/gui/src/python_shell/state.cc
new file mode 100644
index 000000000..2d0c8e51c
--- /dev/null
+++ b/modules/gui/src/python_shell/state.cc
@@ -0,0 +1,84 @@
+#include <cassert>
+#include "state_machine.hh"
+#include "transition.hh"
+#include "state.hh"
+
+namespace ost { namespace gui {
+
+State::State():
+  QObject(),
+  mouse_event_transitions_(),
+  key_event_transitions_(),
+  automatic_transitions_()
+{
+}
+void State::addTransition(SignalTransition* transition)
+{
+  transition->setParent(this);
+}
+void State::addTransition(MouseEventTransition* transition)
+{
+  transition->setParent(this);
+  mouse_event_transitions_.append(transition);
+}
+void State::addTransition(KeyEventTransition* transition)
+{
+  transition->setParent(this);
+  key_event_transitions_.append(transition);
+}
+void State::addTransition(AutomaticTransition* transition)
+{
+  transition->setParent(this);
+  automatic_transitions_.append(transition);
+}
+bool State::isActive()
+{
+  return parent() && dynamic_cast<StateMachine*>(parent())->isActive(this);
+}
+void State::setActive()
+{
+  assert(parent());
+  dynamic_cast<StateMachine*>(parent())->setActive(this);
+}
+
+bool State::checkEvent(QKeyEvent* event)
+{
+  for(QList<KeyEventTransition*>::iterator it=key_event_transitions_.begin();it!=key_event_transitions_.end();++it){
+    std::pair<bool,bool> pair=(*it)->checkEvent(event);
+    if(pair.first){
+      return pair.second;
+    }
+  }
+  return false;
+}
+
+bool State::checkAutomaticTransitions()
+{
+  for(QList<AutomaticTransition*>::iterator it=automatic_transitions_.begin();it!=automatic_transitions_.end();++it){
+    if((*it)->checkTransition()){
+      return true;
+    }
+  }
+  return false;
+}
+
+bool State::checkEvent(QMouseEvent* event)
+{
+  for(QList<MouseEventTransition*>::iterator it=mouse_event_transitions_.begin();it!=mouse_event_transitions_.end();++it){
+    std::pair<bool,bool> pair=(*it)->checkEvent(event);
+    if(pair.first){
+      return pair.second;
+    }
+  }
+  return false;
+}
+void State::onEntry()
+{
+  emit entered();
+}
+void State::onExit()
+{
+  emit exited();
+}
+
+}}//ns
diff --git a/modules/gui/src/python_shell/state.hh b/modules/gui/src/python_shell/state.hh
new file mode 100644
index 000000000..a6c05c36b
--- /dev/null
+++ b/modules/gui/src/python_shell/state.hh
@@ -0,0 +1,45 @@
+#ifndef PYTHON_SHELL_STATE_HH
+#define PYTHON_SHELL_STATE_HH
+
+#include <QObject>
+#include <QList>
+
+//fw decl
+class QKeyEvent;
+class QMouseEvent;
+
+namespace ost { namespace gui {
+
+//fw decl
+class SignalTransition;
+class MouseEventTransition;
+class KeyEventTransition;
+class AutomaticTransition;
+
+class State: public QObject{
+Q_OBJECT
+public:
+  State();
+  void addTransition(SignalTransition * transition);
+  void addTransition(MouseEventTransition * transition);
+  void addTransition(KeyEventTransition* transition);
+  void addTransition(AutomaticTransition* transition);
+  bool isActive();
+  void setActive();
+  bool checkEvent(QKeyEvent* event);
+  bool checkEvent(QMouseEvent* event);
+  bool checkAutomaticTransitions();
+  virtual void onEntry();
+  virtual void onExit();
+signals:
+  void entered();
+  void exited();
+protected:
+  QList<MouseEventTransition*> mouse_event_transitions_;
+  QList<KeyEventTransition*> key_event_transitions_;
+  QList<AutomaticTransition*> automatic_transitions_;
+};
+
+
+}}//ns
+#endif // PYTHON_SHELL_STATE_HH
diff --git a/modules/gui/src/python_shell/state_machine.cc b/modules/gui/src/python_shell/state_machine.cc
new file mode 100644
index 000000000..9ce3f37bc
--- /dev/null
+++ b/modules/gui/src/python_shell/state_machine.cc
@@ -0,0 +1,55 @@
+#include <QEvent>
+#include <QKeyEvent>
+#include <QMouseEvent>
+#include "state.hh"
+#include "state_machine.hh"
+
+namespace ost { namespace gui {
+
+StateMachine::StateMachine(QObject* parent):
+  QObject(parent)
+{
+  parent->installEventFilter(this);
+}
+
+bool StateMachine::eventFilter (QObject * watched, QEvent * event)
+{
+  if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
+    QKeyEvent* key_event = dynamic_cast<QKeyEvent*>(event);
+    return active_state_->checkEvent(key_event);
+  } else if(event->type() == QEvent::MouseButtonDblClick ||
+            event->type() == QEvent::MouseButtonPress ||
+            event->type() == QEvent::MouseButtonRelease ||
+            event->type() == QEvent::MouseMove){
+    QMouseEvent *mouse_event = dynamic_cast<QMouseEvent*>(event);
+    return active_state_->checkEvent(mouse_event);
+  } else {
+    return false;
+  }
+}
+
+void StateMachine::addState(State* state)
+{
+  state->setParent(this);
+}
+void StateMachine::setInitialState(State* state)
+{
+  active_state_=state;
+}
+void StateMachine::start()
+{
+  active_state_->onEntry();
+}
+bool StateMachine::isActive(State* state)
+{
+  return state==active_state_;
+}
+void StateMachine::setActive(State* state)
+{
+  active_state_->onExit();
+  active_state_=state;
+  active_state_->onEntry();
+  active_state_->checkAutomaticTransitions();
+}
+
+}}//ns
diff --git a/modules/gui/src/python_shell/state_machine.hh b/modules/gui/src/python_shell/state_machine.hh
new file mode 100644
index 000000000..ac265dfd9
--- /dev/null
+++ b/modules/gui/src/python_shell/state_machine.hh
@@ -0,0 +1,30 @@
+#ifndef PYTHON_SHELL_STATE_MACHINE_HH
+#define PYTHON_SHELL_STATE_MACHINE_HH
+
+#include <QObject>
+
+//fw decl
+class QEvent;
+
+namespace ost { namespace gui {
+
+//fw decl
+class State;
+
+class StateMachine: public QObject{
+Q_OBJECT
+public:
+  StateMachine(QObject* parent);
+  void addState(State* state);
+  void setInitialState(State* state);
+  void start();
+  bool isActive(State* state);
+  void setActive(State* state);
+  bool eventFilter (QObject * watched, QEvent * event);
+protected:
+  State* active_state_;
+};
+
+}}//ns
+
+#endif // PYTHON_SHELL_STATE_MACHINE_HH
diff --git a/modules/gui/src/python_shell/transition.cc b/modules/gui/src/python_shell/transition.cc
new file mode 100644
index 000000000..57559da98
--- /dev/null
+++ b/modules/gui/src/python_shell/transition.cc
@@ -0,0 +1,91 @@
+#include <cassert>
+#include <QKeyEvent>
+#include <QMouseEvent>
+#include "python_shell_widget.hh"
+#include "state.hh"
+#include "transition.hh"
+
+namespace ost { namespace gui {
+
+TransitionBase::TransitionBase(State* target,  TransitionGuard* guard):
+  QObject(),
+  target_(target),
+  guard_(guard)
+{
+  guard_->setParent(this);
+}
+void TransitionBase::trigger_()
+{
+  emit triggered();
+  target_->setActive();
+}
+bool TransitionBase::is_active_()
+{
+  return parent() && dynamic_cast<State*>(parent())->isActive();
+}
+
+AutomaticTransition::AutomaticTransition(State* target,  TransitionGuard*  guard):
+  TransitionBase(target,guard)
+{
+}
+
+bool AutomaticTransition::checkTransition()
+{
+  if(guard_->check()){
+    trigger_();
+    return true;
+  }
+  return false;
+}
+
+SignalTransition::SignalTransition(QObject * sender,const char *  signal, State* target,  TransitionGuard*  guard):
+  TransitionBase(target,guard)
+{
+  connect(sender,signal,this,SLOT(onSignal()));
+}
+void SignalTransition::onSignal()
+{
+  if(is_active_() && guard_->check()){
+    trigger_();
+  }
+}
+
+KeyEventTransition::KeyEventTransition(QEvent::Type type,int key,Qt::KeyboardModifiers modifiers, State* target, bool swallow_event,  TransitionGuard*  guard):
+  TransitionBase(target,guard),
+  type_(type),
+  key_(key),
+  modifiers_(modifiers),
+  swallow_(swallow_event)
+{
+}
+std::pair<bool,bool> KeyEventTransition::checkEvent(QKeyEvent* event)
+{
+  assert(is_active_());
+  if(event->type()==type_ && (event->key()==key_ || key_==Qt::Key_Any) && event->modifiers() == modifiers_ && guard_->check()){
+    trigger_();
+    return std::pair<bool,bool>(true,swallow_);
+  }
+  return std::pair<bool,bool>(false,false);
+}
+
+MouseEventTransition::MouseEventTransition(QEvent::Type type,Qt::MouseButton button,Qt::KeyboardModifiers modifiers, State* target, bool swallow_event,  TransitionGuard*  guard):
+  TransitionBase(target,guard),
+  type_(type),
+  button_(button_),
+  modifiers_(modifiers),
+  swallow_(swallow_event)
+{
+}
+std::pair<bool,bool> MouseEventTransition::checkEvent(QMouseEvent* event)
+{
+// only gets called if active
+  assert(is_active_());
+  if(event->type()==type_ && event->button()==button_ && event->modifiers() == modifiers_ && guard_->check()){
+    trigger_();
+    return std::pair<bool,bool>(true,swallow_);
+  }
+  return std::pair<bool,bool>(false,false);
+}
+
+
+}}//ns
diff --git a/modules/gui/src/python_shell/transition.hh b/modules/gui/src/python_shell/transition.hh
new file mode 100644
index 000000000..9b832e1af
--- /dev/null
+++ b/modules/gui/src/python_shell/transition.hh
@@ -0,0 +1,77 @@
+#ifndef PYTHON_SHELL_TRANSITION_HH
+#define PYTHON_SHELL_TRANSITION_HH
+
+#include <utility>
+#include <QObject>
+#include <QEvent>
+#include <ost/gui/python_shell/python_interpreter.hh>
+#include "transition_guard.hh"
+
+//fw decl
+class QKeyEvent;
+class QMouseEvent;
+
+namespace ost { namespace gui {
+
+//fw decl
+class State;
+class PythonShellWidget;
+class ShellHistory;
+
+class TransitionBase: public QObject{
+Q_OBJECT
+public:
+  TransitionBase(State* target, TransitionGuard* guard=new TransitionGuard());
+signals:
+  void triggered();
+protected:
+  void trigger_();
+  bool is_active_();
+  State* target_;
+  TransitionGuard* guard_;
+};
+
+class AutomaticTransition: public TransitionBase{
+Q_OBJECT
+public:
+  AutomaticTransition(State* target, TransitionGuard* guard=new TransitionGuard());
+  bool checkTransition();
+};
+
+class SignalTransition: public TransitionBase{
+Q_OBJECT
+public:
+  SignalTransition(QObject * sender,const char *  signal, State* target, TransitionGuard* guard=new TransitionGuard());
+protected slots:
+  void onSignal();
+};
+
+class KeyEventTransition: public TransitionBase{
+Q_OBJECT
+public:
+  KeyEventTransition(QEvent::Type type,int key,Qt::KeyboardModifiers modifiers, State* target, bool swallow_event=true, TransitionGuard* guard=new TransitionGuard());
+  virtual std::pair<bool,bool> checkEvent(QKeyEvent* event);
+protected:
+  QEvent::Type type_;
+  int key_;
+  Qt::KeyboardModifiers modifiers_;
+  bool swallow_;
+};
+
+class MouseEventTransition: public TransitionBase{
+Q_OBJECT
+public:
+  MouseEventTransition(QEvent::Type type,Qt::MouseButton button,Qt::KeyboardModifiers modifiers, State* target, bool swallow_event=true, TransitionGuard* guard=new TransitionGuard());
+  virtual std::pair<bool,bool> checkEvent(QMouseEvent* event);
+protected:
+  QEvent::Type type_;
+  Qt::MouseButton button_;
+  Qt::KeyboardModifiers modifiers_;
+  bool swallow_;
+};
+
+
+
+
+}}//ns
+#endif // PYTHON_SHELL_TRANSITION_HH
diff --git a/modules/gui/src/python_shell/transition_guard.cc b/modules/gui/src/python_shell/transition_guard.cc
new file mode 100644
index 000000000..4409dfe3b
--- /dev/null
+++ b/modules/gui/src/python_shell/transition_guard.cc
@@ -0,0 +1,64 @@
+#include <iostream>
+#include "transition_guard.hh"
+#include "python_shell_widget.hh"
+#include "shell_history.hh"
+
+namespace ost { namespace gui {
+
+TransitionGuard::TransitionGuard():
+  QObject()
+{
+}
+
+bool TransitionGuard::check()
+{
+  return true;
+}
+
+HistoryGuard::HistoryGuard(ShellHistory* history, BlockEditMode mode):
+  TransitionGuard(),
+  history_(history),
+  mode_(mode)
+{
+}
+
+bool HistoryGuard::check()
+{
+  return history_->GetCommandMode()==mode_;
+}
+
+EditPositionGuard::EditPositionGuard(PythonShellWidget* shell, int flags):
+  TransitionGuard(),
+  shell_(shell),
+  flags_(flags)
+{
+}
+
+bool EditPositionGuard::check()
+{
+  bool returnflag=false;
+  if(flags_ & SMALLER){
+    returnflag |= shell_->textCursor().position()< shell_->GetEditStartBlock().position();
+  }
+  if(flags_ & EQUAL){
+    returnflag |= shell_->textCursor().position()== shell_->GetEditStartBlock().position();
+  }
+  if(flags_ & BIGGER){
+    returnflag |= shell_->textCursor().position()> shell_->GetEditStartBlock().position();
+  }
+  return returnflag;
+}
+
+BlockStatusGuard::BlockStatusGuard(PythonShellWidget* shell, int status):
+  TransitionGuard(),
+  shell_(shell),
+  status_(status)
+{
+}
+
+bool BlockStatusGuard::check()
+{
+  QString command=shell_->GetCommand();
+  return PythonInterpreter::Instance().GetCodeBlockStatus(command) & status_;
+}
+}} //ns
diff --git a/modules/gui/src/python_shell/transition_guard.hh b/modules/gui/src/python_shell/transition_guard.hh
new file mode 100644
index 000000000..d3e90b9a3
--- /dev/null
+++ b/modules/gui/src/python_shell/transition_guard.hh
@@ -0,0 +1,65 @@
+#ifndef TRANSITION_GUARD_HH
+#define TRANSITION_GUARD_HH
+
+#include <QObject>
+#include "python_shell_fw.hh"
+#include "python_interpreter.hh"
+
+namespace ost { namespace gui {
+
+
+//fw decl
+  class ShellHistory;
+  class PythonShellWidget;
+
+
+class TransitionGuard: public QObject
+{
+Q_OBJECT
+
+public:
+  TransitionGuard();
+  virtual bool check();
+};
+
+
+
+class HistoryGuard : public TransitionGuard
+{
+public:
+  HistoryGuard(ShellHistory* history, BlockEditMode mode);
+  virtual bool check();
+protected:
+   ShellHistory* history_;
+   BlockEditMode mode_;
+};
+
+class EditPositionGuard : public TransitionGuard
+{
+public:
+  enum FLAGS{
+    SMALLER=1,
+    EQUAL=2,
+    BIGGER=4
+  };
+    EditPositionGuard(PythonShellWidget* shell,int flags);
+    virtual bool check();
+protected:
+    PythonShellWidget* shell_;
+    int flags_;
+};
+
+class BlockStatusGuard : public TransitionGuard
+{
+public:
+    BlockStatusGuard(PythonShellWidget* shell, int status);
+    virtual bool check();
+protected:
+    PythonShellWidget* shell_;
+    int status_;
+};
+
+}} //ns
+
+
+#endif // TRANSITION_GUARD_HH
-- 
GitLab