Salome HOME
Merge branch 'V8_3_BR'
authorvsr <vsr@opencascade.com>
Wed, 31 May 2017 10:43:31 +0000 (13:43 +0300)
committervsr <vsr@opencascade.com>
Wed, 31 May 2017 10:43:31 +0000 (13:43 +0300)
48 files changed:
src/LightApp/LightApp_Application.cxx
src/LightApp/resources/LightApp.xml
src/LightApp/resources/LightApp_msg_en.ts
src/LightApp/resources/LightApp_msg_fr.ts
src/LightApp/resources/LightApp_msg_ja.ts
src/PyViewer/CMakeLists.txt
src/PyViewer/PyViewer_Settings.cxx
src/PyViewer/PyViewer_ViewWindow.cxx
src/PyViewer/PyViewer_ViewWindow.h
src/PyViewer/resources/PyViewer_images.ts
src/PyViewer/resources/PyViewer_msg_en.ts
src/PyViewer/resources/PyViewer_msg_fr.ts
src/PyViewer/resources/PyViewer_msg_ja.ts
src/SALOME_PYQT/SalomePyQt/SalomePyQt.cxx
src/SALOME_PYQT/SalomePyQt/SalomePyQt.h
src/SALOME_PYQT/SalomePyQt/SalomePyQt.sip
tools/PyEditor/src/CMakeLists.txt
tools/PyEditor/src/PyEditor.cxx
tools/PyEditor/src/PyEditor_Completer.cxx [new file with mode: 0644]
tools/PyEditor/src/PyEditor_Completer.h [new file with mode: 0644]
tools/PyEditor/src/PyEditor_Editor.cxx
tools/PyEditor/src/PyEditor_Editor.h
tools/PyEditor/src/PyEditor_FindTool.cxx [new file with mode: 0644]
tools/PyEditor/src/PyEditor_FindTool.h [new file with mode: 0644]
tools/PyEditor/src/PyEditor_Keywords.cxx [new file with mode: 0644]
tools/PyEditor/src/PyEditor_Keywords.h [new file with mode: 0644]
tools/PyEditor/src/PyEditor_PyHighlighter.cxx
tools/PyEditor/src/PyEditor_PyHighlighter.h
tools/PyEditor/src/PyEditor_Settings.cxx
tools/PyEditor/src/PyEditor_Settings.h
tools/PyEditor/src/PyEditor_SettingsDlg.cxx
tools/PyEditor/src/PyEditor_SettingsDlg.h
tools/PyEditor/src/PyEditor_StdSettings.cxx
tools/PyEditor/src/PyEditor_Widget.cxx [new file with mode: 0644]
tools/PyEditor/src/PyEditor_Widget.h [new file with mode: 0644]
tools/PyEditor/src/PyEditor_Window.cxx
tools/PyEditor/src/PyEditor_Window.h
tools/PyEditor/src/python/PyEditorPy.sip
tools/PyEditor/src/resources/PyEditor.qrc
tools/PyEditor/src/resources/about.txt
tools/PyEditor/src/resources/images/py_find.png [new file with mode: 0644]
tools/PyEditor/src/resources/images/py_find_next.png [new file with mode: 0644]
tools/PyEditor/src/resources/images/py_find_previous.png [new file with mode: 0644]
tools/PyEditor/src/resources/images/py_replace.png [new file with mode: 0644]
tools/PyEditor/src/resources/images/py_search.png [new file with mode: 0644]
tools/PyEditor/src/resources/translations/PyEditor_msg_en.ts
tools/PyEditor/src/resources/translations/PyEditor_msg_fr.ts
tools/PyEditor/src/resources/translations/PyEditor_msg_ja.ts

index da1936388a8bfbf9467eef8fe3fb833be68010a0..1005ae4f4b87f9c9fc2da90fbcafb1991976bdd0 100644 (file)
@@ -2932,6 +2932,20 @@ void LightApp_Application::createPreferences( LightApp_Preferences* pref )
   pref->addPreference( tr( "PREF_PY_LINE_NUMBS_AREA" ), pyDispGroup,
     LightApp_Preferences::Bool, "PyEditor", "LineNumberArea" );
   // ... "Display settings" group <<end>>
+
+  // ... "Editor settings" group <<start>>
+  int pyEditGroup = pref->addPreference( tr( "PREF_GROUP_PY_EDITOR" ), pyeditTab );
+  // ... -> navigation mode
+  int pyCompletion = pref->addPreference( tr( "PREF_PY_COMPLETION_MODE" ), pyEditGroup,
+                                          LightApp_Preferences::Selector, "PyEditor", "CompletionPolicy" );
+  aValuesList.clear();
+  anIndicesList.clear();
+  aValuesList   << tr("PREF_PY_NONE") << tr("PREF_PY_AUTO") << tr("PREF_PY_MANUAL") << tr("PREF_PY_ALWAYS");
+  anIndicesList << 0                  << 1                  << 2                    << 3                   ;
+  pref->setItemProperty( "strings", aValuesList, pyCompletion );
+  pref->setItemProperty( "indexes", anIndicesList, pyCompletion );
+  // ... "Editor settings" group <<end>>
+
   // ... "Tab settings" group <<start>>
   int pyTabGroup = pref->addPreference( tr( "PREF_GROUP_PY_TAB" ), pyeditTab );
   pref->setItemProperty( "columns", 2, pyTabGroup );
index ace4c880f822f02be8c06593a352322910866a24..3f74985a9951e75808b6eee8d134f1bc0598571d 100644 (file)
     <parameter name="TabSize"              value="4" />
     <parameter name="VerticalEdge"         value="true" />
     <parameter name="NumberColumns"        value="90" />
+    <parameter name="CompletionPolicy"     value="3" />
   </section>
   <section name="GUI" >
     <parameter name="documentation"     value="gui_help"/>
index 52869a881afdef5f5d0923efdc4d372bde9204d5..fd899055d5edd1ab1ba8821063051945f5ab55c8 100644 (file)
@@ -1041,6 +1041,30 @@ File does not exist</translation>
     <source>PREF_PY_LINE_NUMBS_AREA</source>
     <translation>Display line numbers area</translation>
   </message>
+  <message>
+    <source>PREF_GROUP_PY_EDITOR</source>
+    <translation>Editor settings</translation>
+  </message>
+  <message>
+    <source>PREF_PY_COMPLETION_MODE</source>
+    <translation>Completion mode</translation>
+  </message>
+  <message>
+    <source>PREF_PY_NONE</source>
+    <translation>None</translation>
+  </message>
+  <message>
+    <source>PREF_PY_AUTO</source>
+    <translation>Auto</translation>
+  </message>
+  <message>
+    <source>PREF_PY_MANUAL</source>
+    <translation>Manual</translation>
+  </message>
+  <message>
+    <source>PREF_PY_ALWAYS</source>
+    <translation>Always</translation>
+  </message>
   <message>
     <source>PREF_GROUP_PY_TAB</source>
     <translation>Tab settings</translation>
index c19a0da0feb5b22a20a8019f5e8b831218621ea8..b91790d59cb55def86d2ff787702ab6eeb8c973d 100755 (executable)
@@ -1041,6 +1041,30 @@ Le fichier n&apos;existe pas</translation>
         <source>PREF_PY_LINE_NUMBS_AREA</source>
         <translation>Affiche les numéros de ligne</translation>
     </message>
+    <message>
+        <source>PREF_GROUP_PY_EDITOR</source>
+       <translation type="unfinished">Editor settings</translation>
+    </message>
+    <message>
+        <source>PREF_PY_COMPLETION_MODE</source>
+       <translation type="unfinished">Completion mode</translation>
+    </message>
+    <message>
+        <source>PREF_PY_NONE</source>
+       <translation type="unfinished">None</translation>
+    </message>
+    <message>
+        <source>PREF_PY_AUTO</source>
+       <translation type="unfinished">Auto</translation>
+    </message>
+    <message>
+        <source>PREF_PY_MANUAL</source>
+       <translation type="unfinished">Manual</translation>
+    </message>
+    <message>
+        <source>PREF_PY_ALWAYS</source>
+       <translation type="unfinished">Always</translation>
+    </message>
     <message>
         <source>PREF_GROUP_PY_TAB</source>
         <translation>Indentation</translation>
index 4fb5afcc26a38529ebf2c614f3c17ab62ca523a9..02fa314f2647521f2980250ebcec7d88d61f5449 100644 (file)
@@ -1040,6 +1040,30 @@ Pythonファイルは、文字、数字、アンダースコアが含まれて
       <source>PREF_PY_LINE_NUMBS_AREA</source>
       <translation>ライン数エリアの表示</translation>
     </message>
+    <message>
+        <source>PREF_GROUP_PY_EDITOR</source>
+       <translation type="unfinished">Editor settings</translation>
+    </message>
+    <message>
+        <source>PREF_PY_COMPLETION_MODE</source>
+       <translation type="unfinished">Completion mode</translation>
+    </message>
+    <message>
+        <source>PREF_PY_NONE</source>
+       <translation type="unfinished">None</translation>
+    </message>
+    <message>
+        <source>PREF_PY_AUTO</source>
+       <translation type="unfinished">Auto</translation>
+    </message>
+    <message>
+        <source>PREF_PY_MANUAL</source>
+       <translation type="unfinished">Manual</translation>
+    </message>
+    <message>
+        <source>PREF_PY_ALWAYS</source>
+       <translation type="unfinished">Always</translation>
+    </message>
     <message>
       <source>PREF_GROUP_PY_TAB</source>
       <translation>設定タブ</translation>
index bc4fbe352241854562a013b552301d359d78f50b..85bcc87ee1c72240ec0ab185f3a261d58270bc1b 100644 (file)
@@ -69,12 +69,14 @@ SET(_other_RESOURCES
   ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_copy.png
   ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_cut.png
   ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_delete.png
+  ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_find.png
   ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_help.png
   ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_new.png
   ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_open.png
   ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_paste.png
   ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_preferences.png
   ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_redo.png
+  ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_replace.png
   ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_save.png
   ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_save_as.png
   ${PROJECT_SOURCE_DIR}/tools/PyEditor/src/resources/images/py_select_all.png
index d86c8373e8779035e95592bb14fd33cfe14eb18a..90259e380abe6222cf90b6f2ba1a393566d95ee8 100644 (file)
@@ -50,6 +50,8 @@ void PyViewer_Settings::load()
   setTabSpaceVisible( myResMgr->booleanValue( group, option( snTabSpaceVisible ), tabSpaceVisible() ) );
   setTabSize( myResMgr->integerValue( group, option( snTabSize ), tabSize() ) );
   setFont( myResMgr->fontValue( group, option( snFont ), font() ) );
+  setCompletionPolicy( myResMgr->integerValue( group, option( snCompletionPolicy ),
+                                              completionPolicy() ) );
 }
 
 void PyViewer_Settings::save()
@@ -65,4 +67,5 @@ void PyViewer_Settings::save()
   myResMgr->setValue( group, option( snTabSpaceVisible ), tabSpaceVisible() );
   myResMgr->setValue( group, option( snTabSize ), tabSize() );
   myResMgr->setValue( group, option( snFont ), font() );
+  myResMgr->setValue( group, option( snCompletionPolicy ), completionPolicy() );
 }
index f7c37be7ef38db8dbe6a9ed786a4a63d812f0276..a9e6c79c2a040c9690c827ecd7595f818f9f0b92 100644 (file)
@@ -22,7 +22,7 @@
 
 #include "PyViewer_ViewWindow.h"
 
-#include "PyEditor_Editor.h"
+#include "PyEditor_Widget.h"
 #include "PyEditor_SettingsDlg.h"
 
 #include "SUIT_Session.h"
 #include "QtxActionToolMgr.h"
 
 #include <QApplication>
+#include <QCloseEvent>
 #include <QFileDialog>
 #include <QMessageBox>
 #include <QTextStream>
+#include <QVBoxLayout>
 
 /*!
   \class PyViewer_ViewWindow
@@ -48,9 +50,9 @@
 PyViewer_ViewWindow::PyViewer_ViewWindow( SUIT_Desktop* desktop ) :
   SUIT_ViewWindow( desktop )
 {
-  // Create editor and set it as a central widget.
-  myTextEditor = new PyEditor_Editor( this );
-  setCentralWidget( myTextEditor );
+  // Create central widget.
+  myEditor = new PyEditor_Widget( this );
+  setCentralWidget( myEditor );
 
   // Create actions.
   SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
@@ -82,7 +84,7 @@ PyViewer_ViewWindow::PyViewer_ViewWindow( SUIT_Desktop* desktop ) :
   action->setShortcut( QKeySequence::Save );
   connect( action, SIGNAL( triggered( bool ) ), this, SLOT( onSave() ) );
   action->setEnabled( false );
-  connect( myTextEditor->document(), SIGNAL( modificationChanged( bool ) ),
+  connect( myEditor, SIGNAL( modificationChanged( bool ) ),
            action, SLOT( setEnabled( bool ) ) );
   toolMgr()->registerAction( action, SaveId );
   
@@ -101,9 +103,9 @@ PyViewer_ViewWindow::PyViewer_ViewWindow( SUIT_Desktop* desktop ) :
                           tr( "ACT_UNDO" ), 0, this );
   action->setStatusTip( tr( "DSC_UNDO" ) );
   action->setShortcut( QKeySequence::Undo );
-  connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( undo() ) );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( undo() ) );
   action->setEnabled( false );
-  connect( myTextEditor->document(), SIGNAL( undoAvailable( bool ) ),
+  connect( myEditor, SIGNAL( undoAvailable( bool ) ),
            action, SLOT( setEnabled( bool ) ) );
   toolMgr()->registerAction( action, UndoId );
 
@@ -113,9 +115,9 @@ PyViewer_ViewWindow::PyViewer_ViewWindow( SUIT_Desktop* desktop ) :
                           tr( "ACT_REDO" ), 0, this );
   action->setStatusTip( tr( "DSC_REDO" ) );
   action->setShortcut( QKeySequence::Redo );
-  connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( redo() ) );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( redo() ) );
   action->setEnabled( false );
-  connect( myTextEditor->document(), SIGNAL( redoAvailable( bool ) ),
+  connect( myEditor, SIGNAL( redoAvailable( bool ) ),
            action, SLOT( setEnabled( bool ) ) );
   toolMgr()->registerAction( action, RedoId );
 
@@ -125,9 +127,9 @@ PyViewer_ViewWindow::PyViewer_ViewWindow( SUIT_Desktop* desktop ) :
                           tr( "ACT_CUT" ), 0, this );
   action->setStatusTip( tr( "DSC_CUT" ) );
   action->setShortcut( QKeySequence::Cut );
-  connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( cut() ) );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( cut() ) );
   action->setEnabled( false );
-  connect( myTextEditor, SIGNAL( copyAvailable( bool ) ),
+  connect( myEditor, SIGNAL( copyAvailable( bool ) ),
            action, SLOT( setEnabled( bool ) ) );
   toolMgr()->registerAction( action, CutId );
 
@@ -137,9 +139,9 @@ PyViewer_ViewWindow::PyViewer_ViewWindow( SUIT_Desktop* desktop ) :
                           tr( "ACT_COPY" ), 0, this );
   action->setStatusTip( tr( "DSC_COPY" ) );
   action->setShortcut( QKeySequence::Copy );
-  connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( copy() ) );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( copy() ) );
   action->setEnabled( false );
-  connect( myTextEditor, SIGNAL( copyAvailable( bool ) ),
+  connect( myEditor, SIGNAL( copyAvailable( bool ) ),
            action, SLOT( setEnabled( bool ) ) );
   toolMgr()->registerAction( action, CopyId );
 
@@ -149,7 +151,7 @@ PyViewer_ViewWindow::PyViewer_ViewWindow( SUIT_Desktop* desktop ) :
                           tr( "ACT_PASTE" ), 0, this );
   action->setStatusTip( tr( "DSC_PASTE" ) );
   action->setShortcut( QKeySequence::Paste );
-  connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( paste() ) );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( paste() ) );
   toolMgr()->registerAction( action, PasteId );
 
   // . Delete
@@ -158,9 +160,9 @@ PyViewer_ViewWindow::PyViewer_ViewWindow( SUIT_Desktop* desktop ) :
                           tr( "ACT_DELETE" ), 0, this );
   action->setStatusTip( tr( "DSC_DELETE" ) );
   action->setShortcut( QKeySequence::Delete );
-  connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( deleteSelected() ) );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( deleteSelected() ) );
   action->setEnabled( false );
-  connect( myTextEditor, SIGNAL( copyAvailable( bool ) ),
+  connect( myEditor, SIGNAL( copyAvailable( bool ) ),
            action, SLOT( setEnabled( bool ) ) );
   toolMgr()->registerAction( action, DeleteId );
 
@@ -170,9 +172,29 @@ PyViewer_ViewWindow::PyViewer_ViewWindow( SUIT_Desktop* desktop ) :
                           tr( "ACT_SELECT_ALL" ), 0, this );
   action->setStatusTip( tr( "DSC_SELECT_ALL" ) );
   action->setShortcut( QKeySequence::SelectAll );
-  connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( selectAll() ) );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( selectAll() ) );
   toolMgr()->registerAction( action, SelectAllId );
 
+  // . Find
+  action = new QtxAction( tr( "TTP_FIND" ),
+                          resMgr->loadPixmap( "PyViewer", tr( "ICON_FIND" ) ),
+                          tr( "ACT_FIND" ), 0, this );
+  action->setStatusTip( tr( "DSC_FIND" ) );
+  action->setShortcut( QKeySequence::Find );
+  action->setShortcutContext( Qt::WidgetShortcut );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( find() ) );
+  toolMgr()->registerAction( action, FindId );
+
+  // . Replace
+  action = new QtxAction( tr( "TTP_REPLACE" ),
+                          resMgr->loadPixmap( "PyViewer", tr( "ICON_REPLACE" ) ),
+                          tr( "ACT_REPLACE" ), 0, this );
+  action->setStatusTip( tr( "DSC_REPLACE" ) );
+  action->setShortcut( QKeySequence::Replace );
+  action->setShortcutContext( Qt::WidgetShortcut );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( replace() ) );
+  toolMgr()->registerAction( action, ReplaceId );
+
   // . Preferences
   action = new QtxAction( tr( "TTP_PREFERENCES" ),
                           resMgr->loadPixmap( "PyViewer", tr( "ICON_PREFERENCES" ) ),
@@ -205,6 +227,9 @@ PyViewer_ViewWindow::PyViewer_ViewWindow( SUIT_Desktop* desktop ) :
   toolMgr()->append( DeleteId, idTB );
   toolMgr()->append( SelectAllId, idTB );
   toolMgr()->append( toolMgr()->separator(), idTB );
+  toolMgr()->append( FindId, idTB );
+  toolMgr()->append( ReplaceId, idTB );
+  toolMgr()->append( toolMgr()->separator(), idTB );
   toolMgr()->append( PreferencesId, idTB );
   toolMgr()->append( toolMgr()->separator(), idTB );
   toolMgr()->append( HelpId, idTB );
@@ -239,7 +264,7 @@ void PyViewer_ViewWindow::onNew()
 {
   if ( whetherSave() )
   {
-    myTextEditor->clear();
+    myEditor->clear();
     setCurrentFile( QString() );
   }
 }
@@ -299,7 +324,7 @@ bool PyViewer_ViewWindow::onSaveAs()
 */
 void PyViewer_ViewWindow::onPreferences()
 {
-  PyEditor_SettingsDlg dlg( myTextEditor, true, this );
+  PyEditor_SettingsDlg dlg( myEditor->editor(), true, this );
   connect( &dlg, SIGNAL( help() ), this, SLOT( onHelp() ) );
   dlg.exec();
 }
@@ -311,7 +336,7 @@ void PyViewer_ViewWindow::onPreferences()
 void PyViewer_ViewWindow::setCurrentFile( const QString& filePath )
 {
   myURL = filePath;
-  myTextEditor->document()->setModified( false );
+  myEditor->setModified( false );
 }
 
 /*!
@@ -321,7 +346,7 @@ void PyViewer_ViewWindow::setCurrentFile( const QString& filePath )
 */
 bool PyViewer_ViewWindow::whetherSave()
 {
-  if ( myTextEditor->document()->isModified() )
+  if ( myEditor->isModified() )
   {
     QMessageBox::StandardButton answer =  QMessageBox::warning( this,
                                                                 tr( "NAME_PYEDITOR" ),
@@ -358,7 +383,7 @@ void PyViewer_ViewWindow::loadFile( const QString& filePath )
 
   QTextStream anInput( &aFile );
   QApplication::setOverrideCursor( Qt::WaitCursor );
-  myTextEditor->setPlainText( anInput.readAll() );
+  myEditor->setText( anInput.readAll() );
   QApplication::restoreOverrideCursor();
 
   setCurrentFile( filePath );
@@ -382,7 +407,7 @@ bool PyViewer_ViewWindow::saveFile( const QString& filePath )
 
   QTextStream anOutput( &aFile );
   QApplication::setOverrideCursor( Qt::WaitCursor );
-  anOutput << myTextEditor->toPlainText();
+  anOutput << myEditor->text();
   QApplication::restoreOverrideCursor();
 
   setCurrentFile( filePath );
index b96ea8b2c307f7000118566beb8ef4214f6936ac..229721d682a099edc3bed733e87b522d5661c1af 100644 (file)
@@ -27,7 +27,7 @@
 
 #include <SUIT_ViewWindow.h>
 
-class PyEditor_Editor;
+class PyEditor_Widget;
 
 class PYVIEWER_EXPORT PyViewer_ViewWindow : public SUIT_ViewWindow
 {
@@ -36,6 +36,7 @@ class PYVIEWER_EXPORT PyViewer_ViewWindow : public SUIT_ViewWindow
 public:
   enum { NewId, OpenId, SaveId, SaveAsId,
          UndoId, RedoId, CutId, CopyId, PasteId, DeleteId, SelectAllId,
+         FindId, ReplaceId,
          PreferencesId, HelpId };
 
   PyViewer_ViewWindow( SUIT_Desktop* = 0 );
@@ -61,7 +62,7 @@ private:
   QString     defaultName() const;
 
 private:
-  PyEditor_Editor*  myTextEditor;
+  PyEditor_Widget*  myEditor;
   QString           myURL;
 };
 
index 639f1b5847dea025c6c58f91e7632765087ed098..6ac464e6c6cc25eabec3b4f499195a0a51e98a36 100644 (file)
         <source>ICON_SELECT_ALL</source>
         <translation>py_select_all.png</translation>
     </message>
+    <message>
+        <source>ICON_FIND</source>
+        <translation>py_find.png</translation>
+    </message>
+    <message>
+        <source>ICON_REPLACE</source>
+        <translation>py_replace.png</translation>
+    </message>
     <message>
         <source>ICON_PREFERENCES</source>
         <translation>py_preferences.png</translation>
index 0f4355ee35de0d23aea0a39317411a14a157fa0a..20e989a1a3299521100b800ec36d898702e52424 100644 (file)
       <source>DSC_SELECT_ALL</source>
       <translation>Select all the contents</translation>
     </message>
+    <message>
+      <source>ACT_FIND</source>
+      <translation>Find</translation>
+    </message>
+    <message>
+      <source>TTP_FIND</source>
+      <translation>Find</translation>
+    </message>
+    <message>
+      <source>DSC_FIND</source>
+      <translation>Find text</translation>
+    </message>
+    <message>
+      <source>ACT_REPLACE</source>
+      <translation>Replace</translation>
+    </message>
+    <message>
+      <source>TTP_REPLACE</source>
+      <translation>Find &amp; Replace</translation>
+    </message>
+    <message>
+      <source>DSC_REPLACE</source>
+      <translation>Find and replace text</translation>
+    </message>
     <message>
       <source>ACT_PREFERENCES</source>
       <translation>Pre&amp;ferences</translation>
index c0fc973cd1417ceb345c47f9f241d585ccde4e21..7a52883c0318cccda83f0e597cddf8d777ef6a3a 100644 (file)
       <source>DSC_SELECT_ALL</source>
       <translation>Sélectionne tout le contenu</translation>
     </message>
+    <message>
+      <source>ACT_FIND</source>
+      <translation type="unfinished">Find</translation>
+    </message>
+    <message>
+      <source>TTP_FIND</source>
+      <translation type="unfinished">Find</translation>
+    </message>
+    <message>
+      <source>DSC_FIND</source>
+      <translation type="unfinished">Find text</translation>
+    </message>
+    <message>
+      <source>ACT_REPLACE</source>
+      <translation type="unfinished">Replace</translation>
+    </message>
+    <message>
+      <source>TTP_REPLACE</source>
+      <translation type="unfinished">Find &amp; Replace</translation>
+    </message>
+    <message>
+      <source>DSC_REPLACE</source>
+      <translation type="unfinished">Find and replace text</translation>
+    </message>
     <message>
       <source>ACT_PREFERENCES</source>
       <translation>Préférences</translation>
index 6b8691da33a4be694cabaa9913f50bb2322e6cb1..35c4e054e10ce35cfbfb5cb8120e67775105a298 100644 (file)
       <source>DSC_SELECT_ALL</source>
       <translation>全選択</translation>
     </message>
+    <message>
+      <source>ACT_FIND</source>
+      <translation type="unfinished">Find</translation>
+    </message>
+    <message>
+      <source>TTP_FIND</source>
+      <translation type="unfinished">Find</translation>
+    </message>
+    <message>
+      <source>DSC_FIND</source>
+      <translation type="unfinished">Find text</translation>
+    </message>
+    <message>
+      <source>ACT_REPLACE</source>
+      <translation type="unfinished">Replace</translation>
+    </message>
+    <message>
+      <source>TTP_REPLACE</source>
+      <translation type="unfinished">Find &amp; Replace</translation>
+    </message>
+    <message>
+      <source>DSC_REPLACE</source>
+      <translation type="unfinished">Find and replace text</translation>
+    </message>
     <message>
       <source>ACT_PREFERENCES</source>
       <translation>環境設定 (&amp;f)</translation>
index 4748bde6982715b6c778880625545e9b1a41ccf9..699ce585fe5122057558b27bed50a58d235c49e7 100644 (file)
 #include <QApplication>
 #include <QPaintEvent>
 #include <QCoreApplication>
+#include <QVBoxLayout>
 
 #include <utilities.h>
+
 namespace
 {
   /*!
@@ -329,6 +331,90 @@ void SALOME_Selection::ClearFilters()
   ProcessVoidEvent( new TEvent( mySelMgr ) );
 }
 
+/*!
+  \class UserDefinedContent
+  \brief The class represents base class for user defined widget that
+  can be inserted to the Preferences dialog.
+*/
+
+/*!
+  \brief Constructor
+*/
+UserDefinedContent::UserDefinedContent()
+  : QWidget()
+{
+}
+
+/*!
+  \brief Called from Preferences dialog to store settings to the resource file.
+*/
+void UserDefinedContent::store()
+{
+}
+
+/*!
+  \brief Called from Preferences dialog to restore settings from the resource file.
+*/
+void UserDefinedContent::retrieve()
+{
+}
+
+/*!
+  \class SgPyQtUserDefinedContent
+  \brief A Wrapper for UserDefinedContent class.
+  \internal
+*/
+class SgPyQtUserDefinedContent: public QtxUserDefinedContent
+{
+public:
+  SgPyQtUserDefinedContent(UserDefinedContent*);
+  virtual ~SgPyQtUserDefinedContent();
+
+  void store( QtxResourceMgr*, QtxPreferenceMgr* );
+  void retrieve( QtxResourceMgr*, QtxPreferenceMgr* );
+
+private:
+  UserDefinedContent* myContent;
+};
+
+/*!
+  \brief Create custom item for Preferences dialog wrapping widget passed from Python.
+  \internal
+*/
+SgPyQtUserDefinedContent::SgPyQtUserDefinedContent(UserDefinedContent* content)
+  : QtxUserDefinedContent( 0 ), myContent( content )
+{
+  QVBoxLayout* l = new QVBoxLayout( this );
+  l->setContentsMargins( 0, 0, 0, 0 );
+  l->addWidget( myContent );
+}
+
+/*!
+  \brief Destructor.
+  \internal
+*/
+SgPyQtUserDefinedContent::~SgPyQtUserDefinedContent()
+{
+}
+
+/*!
+  \brief Called from Preferences dialog to store settings to the resource file.
+  \internal
+*/
+void SgPyQtUserDefinedContent::store( QtxResourceMgr*, QtxPreferenceMgr* )
+{
+  myContent->store();
+}
+
+/*!
+  \brief Called from Preferences dialog to restore settings from the resource file.
+  \internal
+*/
+void SgPyQtUserDefinedContent::retrieve( QtxResourceMgr*, QtxPreferenceMgr* )
+{
+  myContent->retrieve();
+}
+
 /*!
   \class SalomePyQt
   \brief The class provides utility functions which can be used in the Python
@@ -2440,7 +2526,39 @@ void SalomePyQt::setPreferenceProperty( const int id,
       }
     }
   };
-  ProcessVoidEvent( new TEvent( id, prop, var) );
+  ProcessVoidEvent( new TEvent( id, prop, var ) );
+}
+
+/*!
+  \brief Set specific widget as a custom preferences item.
+  \param id preferences identifier
+  \param prop preferences property name
+  \param widget custom widget
+*/
+void SalomePyQt::setPreferencePropertyWg( const int id, 
+                                          const QString& prop,
+                                          UserDefinedContent* widget )
+{
+  class TEvent: public SALOME_Event
+  {
+    int      myId;
+    QString  myProp;
+    UserDefinedContent* myWidget;
+  public:
+    TEvent( const int id, const QString& prop, UserDefinedContent* widget ) 
+      : myId( id ), myProp( prop ), myWidget( widget ) {}
+    virtual void Execute() 
+    {
+      LightApp_Module* module = getActiveModule();
+      if ( module ) {
+       LightApp_Preferences* pref = module->getApp()->preferences();
+       if ( pref ) {
+         pref->setItemProperty( myProp, (qint64) new SgPyQtUserDefinedContent( myWidget ), myId );
+        }
+      }
+    }
+  };
+  ProcessVoidEvent( new TEvent( id, prop, widget ) );
 }
 
 /*!
index d31a8600fcee4365ef0f0ff845b9534a6afbb556..a64b01e897527e70aaa2049c7c76440ec33fad1a 100644 (file)
@@ -114,6 +114,23 @@ enum {
   PT_Font     = LightApp_Preferences::Font, 
   PT_DirList  = LightApp_Preferences::DirList, 
   PT_File     = LightApp_Preferences::File, 
+  PT_Slider       = LightApp_Preferences::Slider, 
+  PT_Shortcut     = LightApp_Preferences::Shortcut, 
+  PT_ShortcutTree = LightApp_Preferences::ShortcutTree, 
+  PT_BiColor      = LightApp_Preferences::BiColor, 
+  PT_Background   = LightApp_Preferences::Background, 
+  PT_UserDefined  = LightApp_Preferences::UserDefined, 
+};
+
+class UserDefinedContent: public QWidget
+{
+  Q_OBJECT
+
+public:
+  explicit UserDefinedContent();
+
+  virtual void store();
+  virtual void retrieve();
 };
 
 //! Orientation
@@ -277,6 +294,7 @@ public:
                                           const QString& = QString() );
   static QVariant          preferenceProperty( const int, const QString& );
   static void              setPreferenceProperty( const int, const QString&, const QVariant& );
+  static void              setPreferencePropertyWg( const int, const QString&, UserDefinedContent* );
   static void              addPreferenceProperty( const int, const QString&, const int, const QVariant& );
 
   static void              message( const QString&, bool = true );
index 59309bcfdb0481a4edcd55f08717c6f53a83b678..ca70e94c574d53085f758500c7bd885902332afd 100644 (file)
@@ -102,6 +102,12 @@ enum PrefType {
   PT_Font, 
   PT_DirList, 
   PT_File, 
+  PT_Slider,
+  PT_Shortcut,
+  PT_ShortcutTree,
+  PT_BiColor,
+  PT_Background,
+  PT_UserDefined
 };
 
 enum Orientation {
@@ -237,6 +243,26 @@ private:
   QtxTreeView( const QtxTreeView& );
 };
 
+class UserDefinedContent : public QWidget
+{
+%TypeHeaderCode
+#include <SalomePyQt.h>
+%End
+
+%ConvertToSubClassCode
+    if ( qobject_cast<UserDefinedContent*>( sipCpp ) )
+      sipClass = sipClass_UserDefinedContent;
+    else
+      sipClass = NULL;
+%End
+
+public:
+  explicit UserDefinedContent();
+
+  virtual void store();
+  virtual void retrieve();
+};
+
 enum VisibilityState 
 {
   ShownState,
@@ -391,6 +417,9 @@ public:
   static void              setPreferenceProperty( const int, 
                                                   const QString&,
                                                   const QVariant& ) /ReleaseGIL/ ;
+  static void              setPreferencePropertyWg( const int, 
+                                                    const QString&,    
+                                                    UserDefinedContent* ) /ReleaseGIL/ ;
   static void              addPreferenceProperty( const int, 
                                                   const QString&, 
                                                  const int, 
index 94f88dae6efefe83dff84ee03767c69a16d28a70..53f50bbea2934b4e44f3172bc8953bc70382b151 100644 (file)
@@ -43,9 +43,13 @@ SET(_link_LIBRARIES ${PLATFORM_LIBS} ${QT_LIBRARIES})
 # header files / to be processed by moc
 SET(_moc_HEADERS
   PyEditor_Editor.h
+  PyEditor_FindTool.h
   PyEditor_LineNumberArea.h
+  PyEditor_Keywords.h
+  PyEditor_Completer.h
   PyEditor_PyHighlighter.h
   PyEditor_SettingsDlg.h
+  PyEditor_Widget.h
   PyEditor_Window.h
 )
 
@@ -82,11 +86,15 @@ QT_ADD_RESOURCES(_rcc_SOURCES ${_rcc_RESOURCES})
 # sources / static
 SET(_other_SOURCES
   PyEditor_Editor.cxx
+  PyEditor_FindTool.cxx
   PyEditor_LineNumberArea.cxx
+  PyEditor_Keywords.cxx
+  PyEditor_Completer.cxx
   PyEditor_PyHighlighter.cxx
   PyEditor_Settings.cxx
   PyEditor_SettingsDlg.cxx
   PyEditor_StdSettings.cxx
+  PyEditor_Widget.cxx
   PyEditor_Window.cxx
 )
 
index 03a29b8e98f72335d6e81dd017f0d5a94cb9a5af..5a213338de0723da06f0de98c9156078ae83298b 100644 (file)
@@ -24,6 +24,7 @@
 #include "PyEditor_StdSettings.h"
 
 #include <QApplication>
+#include <QCommandLineParser>
 #include <QDir>
 #include <QLibraryInfo>
 #include <QLocale>
@@ -67,11 +68,11 @@ int main( int argc, char *argv[] )
   app.setOrganizationDomain( "www.salome-platform.org" );
   app.setApplicationName( "pyeditor" );
   
+  QLocale locale;
+
   PyEditor_StdSettings* settings = new PyEditor_StdSettings();
   PyEditor_Settings::setSettings( settings );
   
-  QString language = settings->language();
-  
   // Load Qt translations.
   QString qtDirTrSet = QLibraryInfo::location( QLibraryInfo::TranslationsPath );
   QString qtDirTrEnv = qtTrDir();
@@ -85,7 +86,11 @@ int main( int argc, char *argv[] )
   foreach( QString qtTrFile, qtTrFiles ) {
     foreach ( QString qtTrDir, qtTrDirs ) {
       QTranslator* translator = new QTranslator;
-      if ( translator->load( QString("%1_%2").arg( qtTrFile ).arg( language ), qtTrDir ) ) {
+      if ( translator->load( locale, QString("%1").arg( qtTrFile ), "_", qtTrDir ) ) {
+        app.installTranslator( translator );
+        break;
+      }
+      else if ( translator->load( QString("%1_en").arg( qtTrFile ), qtTrDir ) ) {
         app.installTranslator( translator );
         break;
       }
@@ -97,13 +102,27 @@ int main( int argc, char *argv[] )
   
   // Load application's translations.
   QTranslator translator;
-  if ( translator.load( QString( "PyEditor_msg_%1" ).arg( language ), resourceDir() ) )
+  if ( translator.load( locale, QString( "PyEditor_msg" ), "_", resourceDir() ) )
     app.installTranslator( &translator );
-  
+  else if ( translator.load( QString( "PyEditor_msg_en" ), resourceDir() ) )
+    app.installTranslator( &translator );
+
+  QCommandLineParser parser;
+  parser.setApplicationDescription( QApplication::translate( "PyEditor", "PROGRAM_DESCRIPTION" ) );
+  parser.addHelpOption();
+  parser.addPositionalArgument( QApplication::translate( "PyEditor", "FILE_PARAM_NAME" ),
+                                QApplication::translate( "PyEditor", "FILE_PARAM_DESCRIPTION" ) );
+
+  parser.process( app );
+  const QStringList args = parser.positionalArguments();
+
   PyEditor_Window window;
   window.setWindowIcon( QIcon( ":/images/py_editor.png" ) );
   window.resize( 650, 700 );
   window.show();
+
+  if ( args.count() > 0 )
+    window.loadFile( args[0], false );
   
   return app.exec();
 }
diff --git a/tools/PyEditor/src/PyEditor_Completer.cxx b/tools/PyEditor/src/PyEditor_Completer.cxx
new file mode 100644 (file)
index 0000000..bee9d99
--- /dev/null
@@ -0,0 +1,264 @@
+// Copyright (C) 2015-2016  OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File   : PyEditor_Completer.cxx
+// Author : Sergey TELKOV, Open CASCADE S.A.S. (sergey.telkov@opencascade.com)
+//
+
+#include "PyEditor_Completer.h"
+
+#include "PyEditor_Editor.h"
+#include "PyEditor_Keywords.h"
+
+#include <QSet>
+#include <QTimer>
+#include <QTextBlock>
+#include <QTextCursor>
+#include <QApplication>
+#include <QAbstractItemView>
+#include <QStandardItemModel>
+
+/*!
+  \brief Constructor.
+*/
+PyEditor_Completer::PyEditor_Completer( PyEditor_Editor* editor,
+                                       PyEditor_Keywords* std, PyEditor_Keywords* user )
+  : QCompleter( editor ),
+    myEditor( editor ),
+    myTimer( 0 ),
+    myStdKeywords( std ),
+    myUserKeywords( user )
+{
+  setWidget( editor );
+  setCompletionMode(QCompleter::PopupCompletion);
+
+  connect(this, SIGNAL( activated( const QString& ) ),
+         this, SLOT( onActivated( const QString& ) ) );
+  connect(editor, SIGNAL( textChanged() ), this, SLOT( onTextChanged() ) );
+  connect(myStdKeywords, SIGNAL( keywordsChanged() ),
+         this, SLOT( onKeywordsChanged() ) );
+  connect(myUserKeywords, SIGNAL( keywordsChanged() ),
+         this, SLOT( onKeywordsChanged() ) );
+
+  updateKeywords();
+}
+
+/*!
+  \brief Destructor.
+*/
+PyEditor_Completer::~PyEditor_Completer()
+{
+}
+
+/*!
+  \brief Perform the completion if it possible.
+*/
+void PyEditor_Completer::perform()
+{
+  QString prefix = completionText();
+  setCompletionPrefix( prefix );
+
+  if ( !completionPrefix().isEmpty() && 
+       ( completionCount() > 1 || ( completionCount() == 1 && 
+                                   currentCompletion() != completionPrefix() ) ) )
+    complete(completionRect());
+  else
+    uncomplete();
+}
+
+/*!
+  \brief Hide the completer's popup menu.
+*/
+void PyEditor_Completer::uncomplete()
+{
+  if ( popup() )
+    popup()->hide();
+}
+
+/*!
+  \brief Handling 'Enter' key.
+*/
+bool PyEditor_Completer::eventFilter(QObject* o, QEvent* e)
+{
+  bool res = false;
+  if ( e->type() == QEvent::KeyPress && popup()->isVisible() ) {
+    QKeyEvent* ke = (QKeyEvent*)e;
+    if ( ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return ) {
+      res = true;
+      setCurrentRow(popup()->currentIndex().row());
+      onActivated(currentCompletion());
+    }
+  }
+
+  if ( !res )
+    res = QCompleter::eventFilter(o, e);
+
+  return res;
+}
+
+/*!
+  \brief Perform delayed completion.
+*/
+void PyEditor_Completer::onTimeout()
+{
+  perform();
+}
+
+/*!
+  \brief Invoked when text changed in editor.
+*/
+void PyEditor_Completer::onTextChanged()
+{
+  uncomplete();
+  if ( myEditor->completionPolicy() == PyEditor_Editor::Auto ||
+       myEditor->completionPolicy() == PyEditor_Editor::Always )
+    triggerComplete();
+}
+
+/*!
+  \brief Invoked when keywords changed in editor.
+*/
+void PyEditor_Completer::onKeywordsChanged()
+{
+  updateKeywords();
+}
+
+/*!
+  \brief Insert selected completion into editor.
+*/
+void PyEditor_Completer::onActivated( const QString& text)
+{
+  QPoint rng = completionRange();
+  QTextCursor cursor = myEditor->textCursor();
+  cursor.setPosition(cursor.position() - rng.y() + rng.x() - 1,
+                    QTextCursor::KeepAnchor);
+  cursor.insertText(text);
+  uncomplete();
+}
+
+/*!
+  \brief Get the rectangle for completion popup.
+  \return completion popup rect
+*/
+QRect PyEditor_Completer::completionRect() const
+{
+  QRect res = myEditor->cursorRect(myEditor->textCursor());
+  res.setWidth(popup()->sizeHint().width());
+  res.translate(myEditor->document()->documentMargin(),
+               myEditor->document()->documentMargin());
+  return res;
+}
+
+/*!
+  \brief Get the current completion prefix from editor.
+  \return completion prefix string
+*/
+QString PyEditor_Completer::completionText() const
+{
+  QString prefix;
+  if ( myEditor ) {
+    QString txt = myEditor->textCursor().block().text();
+    if ( !txt.isEmpty() ) {
+      QPoint range = completionRange();
+      prefix = txt.mid( range.x(), range.y() - range.x() + 1 );
+    }
+  }
+  return prefix;
+}
+
+/*!
+  \brief Get position of completion prefix in editor.
+  \return begin and end of completion prefix
+*/
+QPoint PyEditor_Completer::completionRange() const
+{
+  QPoint range;
+
+  if ( myEditor ) {
+    QTextCursor cursor = myEditor->textCursor();
+    QString txt = cursor.block().text();
+    int beg = 0;
+    int end = cursor.positionInBlock() - 1;
+
+    QRegExp rx("[A-Za-z]{1}\\w*$");
+    int pos = rx.indexIn(txt.mid(beg, end - beg + 1));
+
+    if ( pos >= 0 )
+      beg = pos;
+
+    range = QPoint(beg, end);
+  }
+
+  return range;
+}
+
+/*!
+  \brief Schedule the delayed completion.
+*/
+void PyEditor_Completer::triggerComplete()
+{
+  if ( !myTimer ) {
+    myTimer = new QTimer( this );
+    myTimer->setSingleShot( true );
+    myTimer->setInterval( 200 );
+
+    connect( myTimer, SIGNAL( timeout() ), this, SLOT( onTimeout() ) );
+  }
+
+  if ( myTimer->isActive() )
+    myTimer->stop();
+  myTimer->start();
+}
+
+/*!
+  \brief Updates the keywords list in completer.
+*/
+void PyEditor_Completer::updateKeywords()
+{
+  QStandardItemModel* model = new QStandardItemModel( this );
+  KeywordMap kwMap = keywords();
+  for ( KeywordMap::const_iterator it = kwMap.begin(); it != kwMap.end(); ++it ) {
+    QStandardItem* item = new QStandardItem( it.key() );
+    if ( it.value().isValid() )
+      item->setForeground( it.value() );
+    model->appendRow( item );
+  }
+  setModel( model );
+}
+
+/*!
+  \brief Gets the keywords list.
+  \return keyword string list
+*/
+PyEditor_Completer::KeywordMap PyEditor_Completer::keywords() const
+{
+  KeywordMap res;
+  QList<PyEditor_Keywords*> kwDicts;
+  kwDicts << myStdKeywords << myUserKeywords;
+
+  for ( QList<PyEditor_Keywords*>::iterator itr = kwDicts.begin(); itr != kwDicts.end(); ++itr ) {
+    PyEditor_Keywords* dict = *itr;
+    QStringList kwList = dict->keywords();
+    for ( QStringList::const_iterator it = kwList.begin(); it != kwList.end(); ++it ) {
+      if ( !res.contains( *it ) ) {
+       res.insert( *it, dict->color( *it ) );
+      }
+    }
+  }
+  return res;
+}
diff --git a/tools/PyEditor/src/PyEditor_Completer.h b/tools/PyEditor/src/PyEditor_Completer.h
new file mode 100644 (file)
index 0000000..01e0f4e
--- /dev/null
@@ -0,0 +1,73 @@
+// Copyright (C) 2015-2016  OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File   : PyEditor_Completer.h
+// Author : Sergey TELKOV, Open CASCADE S.A.S. (sergey.telkov@opencascade.com)
+//
+
+#ifndef PYEDITOR_COMPLETER_H
+#define PYEDITOR_COMPLETER_H
+
+#include <QCompleter>
+
+class QTimer;
+class PyEditor_Editor;
+class PyEditor_Keywords;
+
+class PyEditor_Completer : public QCompleter
+{
+  Q_OBJECT
+
+public:
+  PyEditor_Completer( PyEditor_Editor*,
+                     PyEditor_Keywords*, PyEditor_Keywords* );
+  virtual ~PyEditor_Completer();
+
+  void               perform();
+
+  void               uncomplete();
+
+  virtual bool       eventFilter(QObject*, QEvent*);
+
+private Q_SLOTS:
+  void               onTimeout();
+  void               onTextChanged();
+  void               onKeywordsChanged();
+  void               onActivated( const QString& );
+
+protected:
+  QRect              completionRect() const;
+  QString            completionText() const;
+  QPoint             completionRange() const;
+
+private:
+  typedef QMap<QString, QColor> KeywordMap;
+
+private:
+  KeywordMap         keywords() const;
+  void               updateKeywords();
+  void               triggerComplete();
+
+private:
+  PyEditor_Editor*   myEditor;
+  QTimer*            myTimer;
+  PyEditor_Keywords* myStdKeywords;
+  PyEditor_Keywords* myUserKeywords;
+};
+
+#endif
index fff2e04254d89b13770fcb7b767597ef27413cd4..6fe34f1960928ee616d6aa81d686922843803007 100644 (file)
 #include "PyEditor_Editor.h"
 #include "PyEditor_LineNumberArea.h"
 #include "PyEditor_PyHighlighter.h"
+#include "PyEditor_Completer.h"
 #include "PyEditor_Settings.h"
+#include "PyEditor_Keywords.h"
 
 #include <QPainter>
 #include <QTextBlock>
 
+#include <iostream>
+
 /*!
   \class PyEditor_Editor
   \brief Widget to show / edit Python scripts.
   \param parent parent widget
 */
 PyEditor_Editor::PyEditor_Editor( QWidget* parent )
-  : QPlainTextEdit( parent )
+  : QPlainTextEdit( parent ),
+    myCompletionPolicy( Always )
 {
+  myStdKeywords = new PyEditor_StandardKeywords( this );
+  myUserKeywords = new PyEditor_Keywords( this );
+  myUserKeywords->append( "print", 0, Qt::red );
+
   // Set up line number area
   myLineNumberArea = new PyEditor_LineNumberArea( this );
   myLineNumberArea->setMouseTracking( true );
 
   // Set up syntax highighter
-  mySyntaxHighlighter = new PyEditor_PyHighlighter( this->document() );
+  mySyntaxHighlighter = new PyEditor_PyHighlighter( this->document(),
+                                                   myStdKeywords, myUserKeywords );
 
   // Set-up settings
   PyEditor_Settings* settings = PyEditor_Settings::settings();
   if ( settings )
     setSettings( *settings );
 
+  myCompleter = new PyEditor_Completer( this, myStdKeywords, myUserKeywords );
+
   // Connect signals
   connect( this, SIGNAL( blockCountChanged( int ) ), this, SLOT( updateLineNumberAreaWidth( int ) ) );
   connect( this, SIGNAL( updateRequest( QRect, int ) ), this, SLOT( updateLineNumberArea( QRect, int ) ) );
@@ -98,6 +110,9 @@ void PyEditor_Editor::setSettings( const PyEditor_Settings& settings )
   // Set size white spaces
   setTabStopWidth( mySettings.tabSize() * 10 );
 
+  // Set completion policy
+  setCompletionPolicy( (CompletionPolicy)mySettings.completionPolicy() );
+
   // Update current line highlight
   updateHighlightCurrentLine();
 
@@ -108,6 +123,54 @@ void PyEditor_Editor::setSettings( const PyEditor_Settings& settings )
   viewport()->update();
 }
 
+/*!
+  \brief Gets the current completion policy
+  \return completion policy
+*/
+PyEditor_Editor::CompletionPolicy PyEditor_Editor::completionPolicy() const
+{
+  return myCompletionPolicy;
+}
+
+/*!
+  \brief Sets the current completion policy
+  \param policy completion policy
+*/
+void PyEditor_Editor::setCompletionPolicy( const CompletionPolicy& policy )
+{
+  myCompletionPolicy = policy;
+}
+
+/*!
+  \brief Gets the all user keywords.
+  \param event key press event
+  \return keyword string list
+*/
+QStringList PyEditor_Editor::keywords() const
+{
+  return myUserKeywords->keywords();
+}
+
+/*!
+  \brief Add the user keywords.
+  \param kws keywords string list
+  \param type keywords type
+  \param color keywords color
+*/
+void PyEditor_Editor::appendKeywords( const QStringList& kws, int type, const QColor& color )
+{
+  myUserKeywords->append( kws, type, color );
+}
+
+/*!
+  \brief Remove the user keywords.
+  \param kws keywords string list
+*/
+void PyEditor_Editor::removeKeywords( const QStringList& kws )
+{
+  myUserKeywords->remove( kws );
+}
+
 /*!
   Delete current selection contents.
 */
@@ -151,6 +214,12 @@ void PyEditor_Editor::keyPressEvent( QKeyEvent* event )
       aCursor.endEditBlock();
       event->accept();
     }
+    else if ( aKey == Qt::Key_Space && aCtrl && !aShift &&
+             ( completionPolicy() == Manual || completionPolicy() == Always ) )
+    {
+      myCompleter->perform();
+      event->accept();
+    }
     else if ( event == QKeySequence::MoveToStartOfLine || event == QKeySequence::SelectStartOfLine )
     {
       QTextCursor aCursor = this->textCursor();
@@ -824,3 +893,21 @@ QString PyEditor_Editor::text() const
 {
   return toPlainText();
 }
+
+/*!
+  \brief Get user keywords dictionary.
+  \return keywords dictionary
+*/
+PyEditor_Keywords* PyEditor_Editor::userKeywords() const
+{
+  return myUserKeywords;
+}
+
+/*!
+  \brief Get standard keywords dictionary.
+  \return keywords dictionary
+*/
+PyEditor_Keywords* PyEditor_Editor::standardKeywords() const
+{
+  return myStdKeywords;
+}
index 0266356739745819a616078748b5463437b0268c..d37c1a86056da70e36382c2ea4b9278727ff4b44 100644 (file)
 
 #include <QPlainTextEdit>
 
+class PyEditor_Keywords;
+class PyEditor_Completer;
 class PyEditor_PyHighlighter;
 
 class PYEDITOR_EXPORT PyEditor_Editor : public QPlainTextEdit
 {
   Q_OBJECT
 
+public:
+  typedef enum { None, Auto, Manual, Always } CompletionPolicy;
+
 public:
   PyEditor_Editor( QWidget* = 0 );
   virtual ~PyEditor_Editor();
   
-  void setSettings( const PyEditor_Settings& );
+  void    setSettings( const PyEditor_Settings& );
   const PyEditor_Settings& settings() const;
   QString text() const;
 
+  QStringList keywords() const;
+  void        appendKeywords( const QStringList&, int, const QColor& = QColor() );
+  void        removeKeywords( const QStringList& );
+
+  CompletionPolicy completionPolicy() const;
+  void             setCompletionPolicy( const CompletionPolicy& );
+
 public Q_SLOTS:
   void deleteSelected();
   void append( const QString& );  
@@ -51,7 +63,10 @@ protected:
   virtual void keyPressEvent( QKeyEvent* );
   virtual void resizeEvent( QResizeEvent* );
   virtual void paintEvent( QPaintEvent* );
-    
+
+  PyEditor_Keywords* userKeywords() const;
+  PyEditor_Keywords* standardKeywords() const;
+
 private Q_SLOTS:
   void updateHighlightCurrentLine();
   void matchParentheses();
@@ -72,13 +87,19 @@ private:
   int  lineIndent();
   void tabIndentation( bool );
   void indentSelection( bool );
-  
+
   int findFirstNonSpace( const QString& );
-  
+
   QWidget*                myLineNumberArea;
   PyEditor_PyHighlighter* mySyntaxHighlighter;
+  PyEditor_Completer*     myCompleter;
   PyEditor_Settings       mySettings;
 
+  PyEditor_Keywords*      myStdKeywords;
+  PyEditor_Keywords*      myUserKeywords;
+
+  CompletionPolicy        myCompletionPolicy;
+
   friend class PyEditor_LineNumberArea;
 };
 
diff --git a/tools/PyEditor/src/PyEditor_FindTool.cxx b/tools/PyEditor/src/PyEditor_FindTool.cxx
new file mode 100644 (file)
index 0000000..de6be82
--- /dev/null
@@ -0,0 +1,612 @@
+// Copyright (C) 2015-2016  OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File   : PyEditor_FindTool.cxx
+// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
+//
+
+#include "PyEditor_FindTool.h"
+#include "PyEditor_Editor.h"
+
+#include <QAction>
+#include <QCompleter>
+#include <QEvent>
+#include <QGridLayout>
+#include <QIcon>
+#include <QLabel>
+#include <QLineEdit>
+#include <QMenu>
+#include <QSignalMapper>
+#include <QToolButton>
+
+/*!
+  \class PyEditor_FindTool
+  \brief Find / Replace widget for PyEditor
+*/
+
+/*!
+  \brief Constructor.
+  \param editor Python editor widget.
+  \param parent Parent widget.
+*/
+PyEditor_FindTool::PyEditor_FindTool( PyEditor_Editor* editor, QWidget* parent )
+  : QWidget( parent ), myEditor( editor )
+{
+  QLabel* findLabel = new QLabel( tr( "FIND_LABEL" ), this );
+  myFindEdit = new QLineEdit( this );
+  myFindEdit->setClearButtonEnabled( true );
+  myFindEdit->installEventFilter( this );
+  connect( myFindEdit, SIGNAL( textChanged( const QString& ) ), this, SLOT( find( const QString& ) ) );
+  connect( myFindEdit, SIGNAL( returnPressed() ), this, SLOT( findNext() ) );
+  myFindEdit->setCompleter( new QCompleter( myFindEdit ) );
+  myFindEdit->completer()->setModel( &myFindCompletion );
+
+  QLabel* replaceLabel = new QLabel( tr( "REPLACE_LABEL" ), this );
+  myReplaceEdit = new QLineEdit( this );
+  myReplaceEdit->setClearButtonEnabled( true );
+  myReplaceEdit->installEventFilter( this );
+  myReplaceEdit->setCompleter( new QCompleter( myReplaceEdit ) );
+  myReplaceEdit->completer()->setModel( &myReplaceCompletion );
+
+  myInfoLabel = new QLabel( this );
+  myInfoLabel->setAlignment( Qt::AlignVCenter | Qt::AlignRight );
+
+  QToolButton* prevBtn = new QToolButton( this );
+  prevBtn->setIcon( QIcon( ":images/py_find_previous.png" ) );
+  prevBtn->setAutoRaise( true );
+  connect( prevBtn, SIGNAL( clicked() ), this, SLOT( findPrevious() ) );
+
+  QToolButton* nextBtn = new QToolButton( this );
+  nextBtn->setIcon( QIcon( ":images/py_find_next.png" ) );
+  nextBtn->setAutoRaise( true );
+  connect( nextBtn, SIGNAL( clicked() ), this, SLOT( findNext() ) );
+
+  QToolButton* replaceBtn = new QToolButton();
+  replaceBtn->setText( tr( "REPLACE_BTN" ) );
+  replaceBtn->setAutoRaise( true );
+  connect( replaceBtn, SIGNAL( clicked() ), this, SLOT( replace() ) );
+
+  QToolButton* replaceAllBtn = new QToolButton();
+  replaceAllBtn->setText( tr( "REPLACE_ALL_BTN" ) );
+  replaceAllBtn->setAutoRaise( true );
+  connect( replaceAllBtn, SIGNAL( clicked() ), this, SLOT( replaceAll() ) );
+
+  QHBoxLayout* hl = new QHBoxLayout;
+  hl->setContentsMargins( 0, 0, 0, 0 );
+  hl->setSpacing( 0 );
+  hl->addWidget( prevBtn );
+  hl->addWidget( nextBtn );
+
+  QGridLayout* l = new QGridLayout( this );
+  l->setContentsMargins( 6, 2, 6, 2 );
+  l->setSpacing( 2 );
+  l->addWidget( findLabel, 0, 0 );
+  l->addWidget( myFindEdit, 0, 1 );
+  l->addLayout( hl, 0, 2 );
+  l->addWidget( myInfoLabel, 0, 3 );
+  l->addWidget( replaceLabel, 1, 0 );
+  l->addWidget( myReplaceEdit, 1, 1 );
+  l->addWidget( replaceBtn, 1, 2 );
+  l->addWidget( replaceAllBtn, 1, 3 );
+
+  QAction* menuAction = myFindEdit->addAction( QIcon(":images/py_search.png"), QLineEdit::LeadingPosition );
+  connect( menuAction, SIGNAL( triggered( bool ) ), this, SLOT( showMenu() ) );
+
+  addAction( new QAction( tr( "CASE_SENSITIVE_CHECK" ), this ) );
+  addAction( new QAction( tr( "WHOLE_WORDS_CHECK" ), this ) );
+  addAction( new QAction( tr( "REGEX_CHECK" ), this ) );
+  addAction( new QAction( tr( "Find" ), this ) );
+  addAction( new QAction( tr( "FindPrevious" ), this ) );
+  addAction( new QAction( tr( "FindNext" ), this ) );
+  addAction( new QAction( tr( "Replace" ), this ) );
+
+  foreach ( QAction* action, actions().mid( CaseSensitive, RegExp+1 ) )
+  {
+    action->setCheckable( true );
+    connect( action, SIGNAL( toggled( bool ) ), this, SLOT( update() ) );
+  }
+
+  QSignalMapper* mapper = new QSignalMapper( this );
+  connect( mapper, SIGNAL( mapped( int ) ), this, SLOT( activate( int ) ) );
+
+  for ( int i = Find; i < actions().count(); i++ )
+  {
+    QAction* action = actions()[i];
+    action->setShortcuts( shortcuts( i ) );
+    action->setShortcutContext( Qt::WidgetWithChildrenShortcut );
+    connect( action, SIGNAL( triggered( bool ) ), mapper, SLOT( map() ) );
+    mapper->setMapping( action, i );
+    myEditor->addAction( action );
+  }
+
+  myEditor->installEventFilter( this );
+
+  hide();
+}
+
+/*!
+  \brief Process events for this widget,
+  \param e Event being processed.
+  \return true if event's processing should be stopped; false otherwise.
+*/
+bool PyEditor_FindTool::event( QEvent* e )
+{
+  if ( e->type() == QEvent::EnabledChange )
+  {
+    updateShortcuts();
+  }
+  else if ( e->type() == QEvent::KeyPress )
+  {
+    QKeyEvent* ke = (QKeyEvent*)e;
+    switch ( ke->key() )
+    {
+    case Qt::Key_Escape:
+      hide();
+      break;
+    default:
+      break;
+    }
+  }
+  else if ( e->type() == QEvent::Hide )
+  {
+    addCompletion( myFindEdit->text(), false );
+    addCompletion( myReplaceEdit->text(), true );
+    myEditor->setFocus();
+  }
+  return QWidget::event( e );
+}
+
+/*!
+  \brief Filter events from watched objects.
+  \param o Object being watched.
+  \param e Event being processed.
+  \return true if event should be filtered out; false otherwise.
+*/
+bool PyEditor_FindTool::eventFilter( QObject* o, QEvent* e )
+{
+  if ( o == myFindEdit )
+  {
+    if ( e->type() == QEvent::KeyPress )
+    {
+      QKeyEvent* keyEvent = (QKeyEvent*)e;
+      if ( keyEvent->key() == Qt::Key_Escape && !myFindEdit->text().isEmpty() )
+      {
+        addCompletion( myFindEdit->text(), false );
+        myFindEdit->clear();
+        return true;
+      }
+    }
+  }
+  else if ( o == myReplaceEdit )
+  {
+    if ( e->type() == QEvent::KeyPress )
+    {
+      QKeyEvent* keyEvent = (QKeyEvent*)e;
+      if ( keyEvent->key() == Qt::Key_Escape && !myReplaceEdit->text().isEmpty() )
+      {
+        myReplaceEdit->clear();
+        return true;
+      }
+    }
+  }
+  else if ( o == myEditor )
+  {
+    if ( e->type() == QEvent::EnabledChange )
+    {
+      setEnabled( myEditor->isEnabled() );
+    }
+    else if ( e->type() == QEvent::Hide )
+    {
+      hide();
+    }
+    else if ( e->type() == QEvent::KeyPress )
+    {
+      QKeyEvent* ke = (QKeyEvent*)e;
+      switch ( ke->key() )
+      {
+      case Qt::Key_Escape:
+        if ( isVisible() )
+          hide();
+        break;
+      default:
+        break;
+      }
+    }
+  }
+  return QWidget::eventFilter( o, e );
+}
+
+/*!
+  \brief Slot: activate 'Find' dialog.
+*/
+void PyEditor_FindTool::activateFind()
+{
+  activate( Find );
+}
+
+/*!
+  \brief Slot: activate 'Replace' dialog.
+*/
+void PyEditor_FindTool::activateReplace()
+{
+  activate( Replace );
+}
+
+/*!
+  \brief Slot: show context menu with search options.
+  \internal
+*/
+void PyEditor_FindTool::showMenu()
+{
+  QMenu::exec( actions().mid( CaseSensitive, RegExp+1 ), QCursor::pos() );
+}
+/*!
+  \brief Slot: find text being typed in the 'Find' control.
+  \param text Text entered by the user.
+  \internal
+*/
+void PyEditor_FindTool::find( const QString& text )
+{
+  find( text, 0 );
+}
+
+/*!
+  \brief Slot: find text entered in the 'Find' control.
+  \internal
+  \overload
+*/
+void PyEditor_FindTool::find()
+{
+  find( myFindEdit->text(), 0 );
+}
+
+/*!
+  \brief Slot: find previous matched item; called when user presses 'Previous' button.
+  \internal
+*/
+void PyEditor_FindTool::findPrevious()
+{
+  find( myFindEdit->text(), -1 );
+}
+
+/*!
+  \brief Slot: find next matched item; called when user presses 'Next' button.
+  \internal
+*/
+void PyEditor_FindTool::findNext()
+{
+  find( myFindEdit->text(), 1 );
+}
+
+/*!
+  \brief Slot: replace currently selected match; called when user presses 'Replace' button.
+  \internal
+*/
+void PyEditor_FindTool::replace()
+{
+  QString text = myFindEdit->text();
+  QString replacement = myReplaceEdit->text();
+
+  QTextCursor editor = myEditor->textCursor();
+  if ( editor.hasSelection() && editor.selectedText() == text )
+  {
+    editor.beginEditBlock();
+    editor.removeSelectedText();
+    editor.insertText( replacement );
+    editor.endEditBlock();
+    find();
+  }
+}
+
+/*!
+  \brief Slot: replace all matches; called when user presses 'Replace All' button.
+  \internal
+*/
+void PyEditor_FindTool::replaceAll()
+{
+  QString text = myFindEdit->text();
+  QString replacement = myReplaceEdit->text();
+  QList<QTextCursor> results = matches( text );
+  if ( !results.isEmpty() )
+  {
+    QTextCursor editor( myEditor->document() );
+    editor.beginEditBlock();
+    foreach ( QTextCursor cursor, results )
+    {
+      editor.setPosition( cursor.anchor() );
+      editor.setPosition( cursor.position(), QTextCursor::KeepAnchor );
+      editor.removeSelectedText();
+      editor.insertText( replacement );
+    }
+    editor.endEditBlock();
+    find();
+  }
+}
+
+/*!
+  \brief Slot: restart search; called when search options are changed.
+  \internal
+*/
+void PyEditor_FindTool::update()
+{
+  find();
+}
+
+/*!
+  \brief Slot: activate action; called when user types corresponding shortcut.
+  \param action Action being activated.
+  \internal
+*/
+void PyEditor_FindTool::activate( int action )
+{
+  QTextCursor cursor = myEditor->textCursor();
+  cursor.movePosition( QTextCursor::StartOfWord );
+  cursor.movePosition( QTextCursor::EndOfWord, QTextCursor::KeepAnchor );
+  QString word = cursor.selectedText();
+
+  switch ( action )
+  {
+  case Find:
+  case Replace:
+    showReplaceControls( action == Replace );
+    show();
+    if ( !word.isEmpty() ) {
+      myFindEdit->setText( word );
+      myEditor->setTextCursor( cursor );
+    }
+    myFindEdit->setFocus();
+    myFindEdit->selectAll();
+    find( myFindEdit->text() );
+    break;
+  case FindPrevious:
+    findPrevious();
+    break;
+  case FindNext:
+    findNext();
+    break;
+  default:
+    break;
+  }
+}
+
+/*!
+  \brief Get shortcuts for given action.
+  \param action Editor's action.
+  \return List of shortcuts.
+  \internal
+*/
+QList<QKeySequence> PyEditor_FindTool::shortcuts( int action ) const
+{
+  QList<QKeySequence> bindings;
+  switch ( action )
+  {
+  case Find:
+    bindings << QKeySequence( QKeySequence::Find );
+    break;
+  case FindPrevious:
+    bindings << QKeySequence( QKeySequence::FindPrevious );
+    break;
+  case FindNext:
+    bindings << QKeySequence( QKeySequence::FindNext );
+    break;
+  case Replace:
+    bindings << QKeySequence( QKeySequence::Replace );
+    bindings << QKeySequence( "Ctrl+H" );
+    break;
+  default:
+    break;
+  }
+  return bindings;
+}
+
+/*!
+  \brief Update shortcuts when widget is enabled / disabled.
+  \internal
+*/
+void PyEditor_FindTool::updateShortcuts()
+{
+  foreach ( QAction* action, actions().mid( Find ) )
+  {
+    action->setEnabled( isEnabled() && myEditor->isEnabled() );
+  }
+}
+
+/*!
+  \brief Show / hide 'Replace' controls.
+  \param on Visibility flag.
+  \internal
+*/
+void PyEditor_FindTool::showReplaceControls( bool on )
+{
+  QGridLayout* l = qobject_cast<QGridLayout*>( layout() );
+  for ( int j = 0; j < l->columnCount(); j++ )
+  {
+    if ( l->itemAtPosition( 1, j )->widget() )
+      l->itemAtPosition( 1, j )->widget()->setVisible( on );
+  }
+}
+
+/*!
+  \brief Set palette for 'Find' tool depending on results of search.
+  \param found Search result: true in case of success; false otherwise.
+  \internal
+*/
+void PyEditor_FindTool::setSearchResult( bool found )
+{
+  QPalette pal = myFindEdit->palette();
+  QPalette ref = myReplaceEdit->palette();
+  pal.setColor( QPalette::Active, QPalette::Text,
+                found ? ref.color( QPalette::Active, QPalette::Text ) : QColor( 255, 0, 0 ) );
+  myFindEdit->setPalette( pal );
+}
+
+/*!
+  \brief Get 'Use regular expression' search option.
+  \return true if option is switched on; false otherwise.
+  \internal
+*/
+bool PyEditor_FindTool::isRegExp() const
+{
+  return actions()[RegExp]->isChecked();
+}
+
+/*!
+  \brief Get 'Case sensitive search' search option.
+  \return true if option is switched on; false otherwise.
+  \internal
+*/
+bool PyEditor_FindTool::isCaseSensitive() const
+{
+  return actions()[CaseSensitive]->isChecked();
+}
+
+/*!
+  \brief Get 'Whole words only' search option.
+  \return true if option is switched on; false otherwise.
+  \internal
+*/
+bool PyEditor_FindTool::isWholeWord() const
+{
+  return actions()[WholeWord]->isChecked();
+}
+
+/*!
+  \brief Get search options.
+  \param back Search direction: backward if false; forward otherwise.
+  \return List of options
+  \internal
+*/
+QTextDocument::FindFlags PyEditor_FindTool::searchFlags( bool back ) const
+{
+  QTextDocument::FindFlags flags = 0;
+  if ( isCaseSensitive() )
+    flags |= QTextDocument::FindCaseSensitively;
+  if ( isWholeWord() )
+    flags |= QTextDocument::FindWholeWords;
+  if ( back )
+    flags |= QTextDocument::FindBackward;
+  return flags;
+}
+
+/*!
+  \brief Get all matches from Python editor.
+  \param text Text being searched.
+  \return List of all matches.
+  \internal
+*/
+QList<QTextCursor> PyEditor_FindTool::matches( const QString& text ) const
+{
+  QList<QTextCursor> results;
+
+  QTextDocument* document = myEditor->document();
+
+  QTextCursor cursor( document );
+  while ( !cursor.isNull() )
+  {
+    cursor = isRegExp() ? 
+      document->find( QRegExp( text, isCaseSensitive() ?
+                               Qt::CaseSensitive : Qt::CaseInsensitive ),
+                      cursor, searchFlags() ) : 
+      document->find( text, cursor, searchFlags() );
+    if ( !cursor.isNull() )
+      results.append( cursor );
+  }
+  return results;
+}
+
+/*!
+  \brief Find specified text.
+  \param text Text being searched.
+  \param delta Search direction.
+  \internal
+*/
+void PyEditor_FindTool::find( const QString& text, int delta )
+{
+  QTextCursor cursor = myEditor->textCursor();
+  int position = qMin( cursor.position(), cursor.anchor() ) + delta;
+  cursor.setPosition( position );
+  myEditor->setTextCursor( cursor );
+
+  QList<QTextCursor> results = matches( text );
+
+  int index = -1;
+  if ( !results.isEmpty() )
+  {
+    if ( delta >= 0 )
+    {
+      // search forward
+      if ( position > results.last().anchor() )
+        position = 0;
+      for ( int i = 0; i < results.count() && index == -1; i++ )
+      {
+        QTextCursor result = results[i];
+        if ( result.hasSelection() && position <= result.anchor() )
+        {
+          index = i;
+        }
+      }
+    }
+    else
+    {
+      // search backward
+      if ( position < results.first().position() )
+        position = results.last().position();
+
+      for ( int i = results.count()-1; i >= 0 && index == -1; i-- )
+      {
+        QTextCursor result = results[i];
+        if ( result.hasSelection() && position >= result.position() )
+        {
+          index = i;
+        }
+      }
+    }
+  }
+  if ( index != -1 )
+  {
+    myInfoLabel->setText( tr( "NB_MATCHED_LABEL" ).arg( index+1 ).arg( results.count() ) );
+    myEditor->setTextCursor( results[index] );
+  }
+  else
+  {
+    myInfoLabel->clear();
+    cursor.clearSelection();
+    myEditor->setTextCursor( cursor );
+  }
+
+  setSearchResult( text.isEmpty() || !results.isEmpty() );
+}
+
+/*!
+  \brief Add completion.
+  \param text Completeion being added.
+  \param replace true to add 'Replace' completion; false to add 'Find' completion.
+  \internal
+*/
+void PyEditor_FindTool::addCompletion( const QString& text, bool replace )
+{
+  QStringListModel& model = replace ? myReplaceCompletion : myFindCompletion;
+
+  QStringList completions = model.stringList();
+  if ( !text.isEmpty() and !completions.contains( text ) )
+  {
+    completions.prepend( text );
+    model.setStringList( completions );
+  }
+}
diff --git a/tools/PyEditor/src/PyEditor_FindTool.h b/tools/PyEditor/src/PyEditor_FindTool.h
new file mode 100644 (file)
index 0000000..e650170
--- /dev/null
@@ -0,0 +1,89 @@
+// Copyright (C) 2015-2016  OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File   : PyEditor_FindTool.h
+// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
+//
+
+#ifndef PYEDITOR_FINDTOOL_H
+#define PYEDITOR_FINDTOOL_H
+
+#include "PyEditor.h"
+
+#include <QTextDocument>
+#include <QStringListModel>
+#include <QWidget>
+
+class PyEditor_Editor;
+class QAction;
+class QLabel;
+class QLineEdit;
+
+class PYEDITOR_EXPORT PyEditor_FindTool : public QWidget
+{
+Q_OBJECT
+
+  enum { CaseSensitive, WholeWord, RegExp, Find, FindPrevious, FindNext, Replace };
+
+public:
+  PyEditor_FindTool( PyEditor_Editor*, QWidget* = 0 );
+
+  bool event( QEvent* );
+  bool eventFilter( QObject*, QEvent* );
+
+public slots:
+  void activateFind();
+  void activateReplace();
+
+private slots:
+  void showMenu();
+  void find( const QString& );
+  void find();
+  void findPrevious();
+  void findNext();
+  void replace();
+  void replaceAll();
+  void update();
+  void activate( int );
+
+private:
+  QList<QKeySequence> shortcuts( int ) const;
+  void updateShortcuts();
+
+  void showReplaceControls( bool );
+  void setSearchResult( bool );
+
+  bool isRegExp() const;
+  bool isCaseSensitive() const;
+  bool isWholeWord() const;
+  QTextDocument::FindFlags searchFlags( bool = false ) const;
+
+  QList<QTextCursor> matches( const QString& ) const;
+  void find( const QString&, int );
+
+  void addCompletion( const QString&, bool );
+
+private:
+  PyEditor_Editor* myEditor;
+  QLineEdit* myFindEdit;
+  QLineEdit* myReplaceEdit;
+  QLabel* myInfoLabel;
+  QStringListModel myFindCompletion, myReplaceCompletion;
+};
+
+#endif // PYEDITOR_FINDTOOL_H
diff --git a/tools/PyEditor/src/PyEditor_Keywords.cxx b/tools/PyEditor/src/PyEditor_Keywords.cxx
new file mode 100644 (file)
index 0000000..ecc9fe6
--- /dev/null
@@ -0,0 +1,243 @@
+// Copyright (C) 2015-2016  OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File   : PyEditor_Keywords.cxx
+// Author : Sergey TELKOV, Open CASCADE S.A.S. (sergey.telkov@opencascade.com)
+//
+
+#include "PyEditor_Keywords.h"
+
+#include <QSet>
+
+/*!
+  \brief PyEditor_Keywords
+*/
+
+/*!
+  \brief Constructor.
+*/
+PyEditor_Keywords::PyEditor_Keywords( QObject* parent )
+  : QObject( parent )
+{
+}
+
+/*!
+  \brief Destructor.
+*/
+PyEditor_Keywords::~PyEditor_Keywords()
+{
+}
+
+
+QList<int> PyEditor_Keywords::types() const
+{
+  QMap<int, bool> map;
+  for ( KeywordMap::const_iterator it = myKeywords.begin(); it != myKeywords.end(); ++it ) {
+    map.insert( it.value().type, false );
+  }
+  return map.keys();
+}
+
+/*!
+  \brief Gets the colors list.
+  \return color list
+*/
+QList<QColor> PyEditor_Keywords::colors() const
+{
+  QList<QColor> list;
+  QSet<QRgb> set;
+  for ( KeywordMap::const_iterator it = myKeywords.begin(); it != myKeywords.end(); ++it ) {
+    const QColor& color = it.value().color;
+    if ( !set.contains( color.rgba() ) ) {
+      list.append( color );
+      set.insert( color.rgba() );
+    }
+  }
+  return list;
+}
+
+/*!
+  \brief Gets the keyword type.
+  \return type number
+*/
+int PyEditor_Keywords::type( const QString& keyword ) const
+{
+  return myKeywords.contains(keyword) ? myKeywords[keyword].type : -1;
+}
+
+/*!
+  \brief Gets the keyword color.
+  \return color
+*/
+QColor PyEditor_Keywords::color( const QString& keyword ) const
+{
+  return myKeywords.contains(keyword) ? myKeywords[keyword].color : QColor();
+}
+
+/*!
+  \brief Gets all keywords.
+  \return keywords string list
+*/
+QStringList PyEditor_Keywords::keywords() const
+{
+  return myKeywords.keys();
+}
+
+/*!
+  \brief Gets all keywords of specified type.
+  \return keywords string list
+*/
+QStringList PyEditor_Keywords::keywords( int type ) const
+{
+  QStringList keywords;
+  for ( KeywordMap::const_iterator it = myKeywords.begin(); it != myKeywords.end(); ++it ) {
+    if ( it.value().type == type )
+      keywords.append( it.key() );
+  }
+  return keywords;
+}
+
+/*!
+  \brief Gets all keywords with specified color.
+  \return keywords string list
+*/
+QStringList PyEditor_Keywords::keywords( const QColor& color ) const
+{
+  QStringList keywords;
+  for ( KeywordMap::const_iterator it = myKeywords.begin(); it != myKeywords.end(); ++it ) {
+    if ( it.value().color == color )
+      keywords.append( it.key() );
+  }
+  return keywords;
+}
+
+/*!
+  \brief Append keyword with type and color.
+*/
+void PyEditor_Keywords::append( const QString& keyword,
+                               int type, const QColor& color )
+{
+  append( QStringList() << keyword, type, color );
+}
+
+/*!
+  \brief Append keyword list with type and color.
+*/
+void PyEditor_Keywords::append( const QStringList& keywords,
+                               int type, const QColor& color )
+{
+  bool modif = false;
+  for ( QStringList::const_iterator it = keywords.begin(); it != keywords.end(); ++it ) {
+    const QString& kw = *it;
+    bool changed = false;
+    if ( !myKeywords.contains( kw ) ) {
+      myKeywords.insert( kw, KeywordInfo() );
+      changed = true;
+    }
+    KeywordInfo& info = myKeywords[kw];
+    changed = changed || info.type != type || info.color != color;
+    info.type = type;
+    info.color = color;
+
+    modif = modif || changed;
+  }
+
+  if ( modif )
+    Q_EMIT keywordsChanged();
+}
+
+/*!
+  \brief Remove all keywords with specified type.
+*/
+void PyEditor_Keywords::remove( int type )
+{
+  remove( keywords( type ) );
+}
+
+/*!
+  \brief Remove keyword.
+*/
+void PyEditor_Keywords::remove( const QString& keyword )
+{
+  remove( QStringList() << keyword );
+}
+
+/*!
+  \brief Remove keywords.
+*/
+void PyEditor_Keywords::remove( const QStringList& keywords )
+{
+  bool changed = false;
+  for ( QStringList::const_iterator it = keywords.begin(); it != keywords.end(); ++it ) {
+    if ( myKeywords.contains( *it ) ) {
+      myKeywords.remove( *it );
+      changed = true;
+    }
+  }
+  if ( changed )
+    Q_EMIT keywordsChanged();
+}
+
+/*!
+  \brief Remove all keywords.
+*/
+void PyEditor_Keywords::clear()
+{
+  if ( !myKeywords.isEmpty() ) {
+    myKeywords.clear();
+    Q_EMIT keywordsChanged();
+  }
+}
+
+/*!
+  \brief PyEditor_StandardKeywords
+*/
+
+PyEditor_StandardKeywords::PyEditor_StandardKeywords( QObject* parent )
+  : PyEditor_Keywords( parent )
+{
+  QStringList aBase;
+  aBase << "and" << "as" << "assert" << "break" << "class" << "continue"
+       << "def" << "elif" << "else" << "except" << "exec" << "finally"
+       << "False" << "for" << "from" << "global" << "if" << "import"
+       << "in" << "is" << "lambda" << "None" << "not" << "or" << "pass"
+       << "print" << "raise" << "return" << "True" << "try" << "while"
+       << "with" << "yield";
+  append( aBase, Base, Qt::blue );
+
+  QStringList anExcept;
+  anExcept << "ArithmeticError" << "AssertionError" << "AttributeError"
+          << "EnvironmentError" << "EOFError" << "Exception"
+          << "FloatingPointError" << "ImportError" << "IndentationError"
+          << "IndexError" << "IOError" << "KeyboardInterrupt" << "KeyError"
+          << "LookupError" << "MemoryError" << "NameError" << "OSError"
+          << "NotImplementedError" << "OverflowError" << "ReferenceError"
+          << "RuntimeError" << "StandardError" << "StopIteration"
+          << "SyntaxError" << "SystemError" << "SystemExit" << "TabError"
+          << "TypeError" << "UnboundLocalError" << "UnicodeDecodeError"
+          << "UnicodeEncodeError" << "UnicodeError" << "UnicodeTranslateError"
+          << "ValueError" << "WindowsError" << "ZeroDivisionError"
+          << "Warning" << "UserWarning" << "DeprecationWarning"
+          << "PendingDeprecationWarning" << "SyntaxWarning"
+          << "OverflowWarning" << "RuntimeWarning" << "FutureWarning";
+  append( anExcept, Exceptions, Qt::magenta );
+}
+
+PyEditor_StandardKeywords::~PyEditor_StandardKeywords()
+{
+}
diff --git a/tools/PyEditor/src/PyEditor_Keywords.h b/tools/PyEditor/src/PyEditor_Keywords.h
new file mode 100644 (file)
index 0000000..03f5506
--- /dev/null
@@ -0,0 +1,81 @@
+// Copyright (C) 2015-2016  OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File   : PyEditor_Keywords.h
+// Author : Sergey TELKOV, Open CASCADE S.A.S. (sergey.telkov@opencascade.com)
+//
+
+#ifndef PYEDITOR_KEYWORDS_H
+#define PYEDITOR_KEYWORDS_H
+
+#include <QObject>
+#include <QColor>
+#include <QList>
+#include <QMap>
+
+class PyEditor_Keywords : public QObject
+{
+  Q_OBJECT
+
+public:
+  PyEditor_Keywords( QObject* = 0 );
+  virtual ~PyEditor_Keywords();
+
+  QList<int>       types() const;
+  QList<QColor>    colors() const;
+
+  int              type( const QString& ) const;
+  QColor           color( const QString& ) const;
+
+  QStringList      keywords() const;
+  QStringList      keywords( int ) const;
+  QStringList      keywords( const QColor& ) const;
+
+  void             append( const QString&, int, const QColor& = QColor() );
+  void             append( const QStringList&, int, const QColor& = QColor() );
+
+  void             remove( int );
+  void             remove( const QString& );
+  void             remove( const QStringList& );
+
+  void             clear();
+
+Q_SIGNALS:
+  void             keywordsChanged();
+
+private:
+  typedef struct { int type; QColor color; } KeywordInfo;
+  typedef QMap<QString, KeywordInfo>         KeywordMap;
+
+private:
+  KeywordMap       myKeywords;
+};
+
+class PyEditor_StandardKeywords : public PyEditor_Keywords
+{
+  Q_OBJECT
+
+public:
+  typedef enum { Base, Exceptions } KeywordType;
+
+public:
+  PyEditor_StandardKeywords( QObject* = 0 );
+  virtual ~PyEditor_StandardKeywords();
+};
+
+#endif
index 70ff0186a03e50ad8e03ccbdb0e112be9602c8c5..70a92748b072c7aa8b4fc1ecfccd4e1d28be1111 100644 (file)
 
 #include "PyEditor_PyHighlighter.h"
 
+#include "PyEditor_Keywords.h"
+
+#include <QSet>
+
 #define NORMAL 0
 #define TRIPLESINGLE 1
 #define TRIPLEDOUBLE 2
@@ -53,192 +57,20 @@ void PyEditor_PyHighlighter::TextBlockData::insert( PyEditor_PyHighlighter::Pare
   \brief Constructor.
   \param theDocument container for structured rich text documents.
 */
-PyEditor_PyHighlighter::PyEditor_PyHighlighter( QTextDocument* theDocument )
-  : QSyntaxHighlighter( theDocument )
-{
-  initialize();
-}
-
-/*!
-  \brief Initialization rules.
-*/
-void PyEditor_PyHighlighter::initialize()
+PyEditor_PyHighlighter::PyEditor_PyHighlighter( QTextDocument* theDocument,
+                                               PyEditor_Keywords* std,
+                                               PyEditor_Keywords* user )
+  : QSyntaxHighlighter( theDocument ),
+    myStdKeywords( std ),
+    myUserKeywords( user )
 {
-  HighlightingRule aRule;
-
-  // Keywords
-  keywordFormat.setForeground( Qt::blue );
-  QStringList aKeywords = keywords();
-  foreach ( const QString& keyword, aKeywords )
-  {
-    aRule.pattern = QRegExp( QString( "\\b%1\\b" ).arg( keyword ) );
-    aRule.format = keywordFormat;
-    aRule.capture = 0;
-    highlightingRules.append( aRule );
-  }
-
-  // Special keywords
-  specialFromat.setForeground( Qt::magenta );
-  QStringList aSpecialKeywords = specialKeywords();
-  foreach ( const QString& keyword, aSpecialKeywords )
-  {
-    aRule.pattern = QRegExp( QString( "\\b%1\\b" ).arg( keyword ) );
-    aRule.format = specialFromat;
-    aRule.capture = 0;
-    highlightingRules.append( aRule );
-  }
-
-  // Reference to the current instance of the class
-  referenceClassFormat.setForeground( QColor( 179, 143, 0 ) );
-  referenceClassFormat.setFontItalic( true );
-  aRule.pattern = QRegExp( "\\bself\\b" );
-  aRule.format = referenceClassFormat;
-  aRule.capture = 0;
-  highlightingRules.append( aRule );
-
-  // Numbers
-  numberFormat.setForeground( Qt::darkMagenta );
-  aRule.pattern = QRegExp( "\\b([-+])?(\\d+(\\.)?\\d*|\\d*(\\.)?\\d+)(([eE]([-+])?)?\\d+)?\\b" );
-  aRule.format = numberFormat;
-  aRule.capture = 0;
-  highlightingRules.append( aRule );
-
-  // String qoutation
-  quotationFormat.setForeground( Qt::darkGreen );
-  aRule.pattern = QRegExp( "(?:'[^']*'|\"[^\"]*\")" );
-  aRule.pattern.setMinimal( true );
-  aRule.format = quotationFormat;
-  aRule.capture = 0;
-  highlightingRules.append( aRule );
 
-  // Function names
-  functionFormat.setFontWeight( QFont::Bold );
-  aRule.pattern = QRegExp( "(?:def\\s*)(\\b[A-Za-z0-9_]+)(?=[\\W])" );
-  aRule.capture = 1;
-  aRule.format = functionFormat;
-  highlightingRules.append( aRule );
-
-  // Class names
-  classFormat.setForeground( Qt::darkBlue );
-  classFormat.setFontWeight( QFont::Bold );
-  aRule.pattern = QRegExp( "(?:class\\s*)(\\b[A-Za-z0-9_]+)(?=[\\W])" );
-  aRule.capture = 1;
-  aRule.format = classFormat;
-  highlightingRules.append( aRule );
-
-  // Multi line comments
-  multiLineCommentFormat.setForeground( Qt::darkRed );
-  tripleQuotesExpression = QRegExp( "(:?\"[\"]\".*\"[\"]\"|'''.*''')" );
-  aRule.pattern = tripleQuotesExpression;
-  aRule.pattern.setMinimal( true );
-  aRule.format = multiLineCommentFormat;
-  aRule.capture = 0;
-  highlightingRules.append( aRule );
-
-  tripleSingleExpression = QRegExp( "'''(?!\")" );
-  tripleDoubleExpression = QRegExp( "\"\"\"(?!')" );
-
-  // Single comments
-  singleLineCommentFormat.setForeground( Qt::darkGray );
-  aRule.pattern = QRegExp( "#[^\n]*" );
-  aRule.format = singleLineCommentFormat;
-  aRule.capture = 0;
-  highlightingRules.append( aRule );
-}
-
-/*!
-  \return string list of Python keywords.
- */
-QStringList PyEditor_PyHighlighter::keywords()
-{
-  QStringList aKeywords;
-  aKeywords << "and"
-            << "as"
-            << "assert"
-            << "break"
-            << "class"
-            << "continue"
-            << "def"
-            << "elif"
-            << "else"
-            << "except"
-            << "exec"
-            << "finally"
-            << "False"
-            << "for"
-            << "from"
-            << "global"
-            << "if"
-            << "import"
-            << "in"
-            << "is"
-            << "lambda"
-            << "None"
-            << "not"
-            << "or"
-            << "pass"
-            << "print"
-            << "raise"
-            << "return"
-            << "True"
-            << "try"
-            << "while"
-            << "with"
-            << "yield";
-  return aKeywords;
-}
+  connect(myStdKeywords, SIGNAL( keywordsChanged() ),
+         this, SLOT( onKeywordsChanged() ) );
+  connect(myUserKeywords, SIGNAL( keywordsChanged() ),
+         this, SLOT( onKeywordsChanged() ) );
 
-/*!
-  \return string list of special Python keywords.
-*/
-QStringList PyEditor_PyHighlighter::specialKeywords()
-{
-  QStringList aSpecialKeywords;
-  aSpecialKeywords << "ArithmeticError"
-                   << "AssertionError"
-                   << "AttributeError"
-                   << "EnvironmentError"
-                   << "EOFError"
-                   << "Exception"
-                   << "FloatingPointError"
-                   << "ImportError"
-                   << "IndentationError"
-                   << "IndexError"
-                   << "IOError"
-                   << "KeyboardInterrupt"
-                   << "KeyError"
-                   << "LookupError"
-                   << "MemoryError"
-                   << "NameError"
-                   << "NotImplementedError"
-                   << "OSError"
-                   << "OverflowError"
-                   << "ReferenceError"
-                   << "RuntimeError"
-                   << "StandardError"
-                   << "StopIteration"
-                   << "SyntaxError"
-                   << "SystemError"
-                   << "SystemExit"
-                   << "TabError"
-                   << "TypeError"
-                   << "UnboundLocalError"
-                   << "UnicodeDecodeError"
-                   << "UnicodeEncodeError"
-                   << "UnicodeError"
-                   << "UnicodeTranslateError"
-                   << "ValueError"
-                   << "WindowsError"
-                   << "ZeroDivisionError"
-                   << "Warning"
-                   << "UserWarning"
-                   << "DeprecationWarning"
-                   << "PendingDeprecationWarning"
-                   << "SyntaxWarning"
-                   << "OverflowWarning"
-                   << "RuntimeWarning"
-                   << "FutureWarning";
-  return aSpecialKeywords;
+  updateHighlight();
 }
 
 void PyEditor_PyHighlighter::highlightBlock( const QString& theText )
@@ -353,3 +185,100 @@ void PyEditor_PyHighlighter::insertBracketsData( Brackets theBrackets,
 
   insertBracketsData( leftChar, rightChar, theData, theText );
 }
+
+void PyEditor_PyHighlighter::onKeywordsChanged()
+{
+  updateHighlight();
+  rehighlight();
+}
+
+void PyEditor_PyHighlighter::updateHighlight()
+{
+  highlightingRules.clear();
+
+  HighlightingRule aRule;
+
+  QList<PyEditor_Keywords*> dictList;
+  dictList << myStdKeywords << myUserKeywords;
+
+  // Keywords
+  QSet<QString> existing;
+  for ( QList<PyEditor_Keywords*>::const_iterator it = dictList.begin();
+       it != dictList.end(); ++it ) {
+    PyEditor_Keywords* kwDict = *it;
+    QList<QColor> colors = kwDict->colors();
+    for ( QList<QColor>::const_iterator itr = colors.begin(); itr != colors.end(); ++itr ) {
+      QColor color = *itr;
+      QTextCharFormat format;
+      format.setForeground( color );
+      QStringList keywords = kwDict->keywords( color );
+      foreach ( const QString& keyword, keywords ) {
+       if ( existing.contains( keyword ) )
+         continue;
+
+       aRule.pattern = QRegExp( QString( "\\b%1\\b" ).arg( keyword ) );
+       aRule.format = format;
+       aRule.capture = 0;
+       highlightingRules.append( aRule );
+       existing.insert( keyword );
+      }
+    }
+  }
+
+  // Reference to the current instance of the class
+  referenceClassFormat.setForeground( QColor( 179, 143, 0 ) );
+  referenceClassFormat.setFontItalic( true );
+  aRule.pattern = QRegExp( "\\bself\\b" );
+  aRule.format = referenceClassFormat;
+  aRule.capture = 0;
+  highlightingRules.append( aRule );
+
+  // Numbers
+  numberFormat.setForeground( Qt::darkMagenta );
+  aRule.pattern = QRegExp( "\\b([-+])?(\\d+(\\.)?\\d*|\\d*(\\.)?\\d+)(([eE]([-+])?)?\\d+)?\\b" );
+  aRule.format = numberFormat;
+  aRule.capture = 0;
+  highlightingRules.append( aRule );
+
+  // String qoutation
+  quotationFormat.setForeground( Qt::darkGreen );
+  aRule.pattern = QRegExp( "(?:'[^']*'|\"[^\"]*\")" );
+  aRule.pattern.setMinimal( true );
+  aRule.format = quotationFormat;
+  aRule.capture = 0;
+  highlightingRules.append( aRule );
+
+  // Function names
+  functionFormat.setFontWeight( QFont::Bold );
+  aRule.pattern = QRegExp( "(?:def\\s*)(\\b[A-Za-z0-9_]+)(?=[\\W])" );
+  aRule.capture = 1;
+  aRule.format = functionFormat;
+  highlightingRules.append( aRule );
+
+  // Class names
+  classFormat.setForeground( Qt::darkBlue );
+  classFormat.setFontWeight( QFont::Bold );
+  aRule.pattern = QRegExp( "(?:class\\s*)(\\b[A-Za-z0-9_]+)(?=[\\W])" );
+  aRule.capture = 1;
+  aRule.format = classFormat;
+  highlightingRules.append( aRule );
+
+  // Multi line comments
+  multiLineCommentFormat.setForeground( Qt::darkRed );
+  tripleQuotesExpression = QRegExp( "(:?\"[\"]\".*\"[\"]\"|'''.*''')" );
+  aRule.pattern = tripleQuotesExpression;
+  aRule.pattern.setMinimal( true );
+  aRule.format = multiLineCommentFormat;
+  aRule.capture = 0;
+  highlightingRules.append( aRule );
+
+  tripleSingleExpression = QRegExp( "'''(?!\")" );
+  tripleDoubleExpression = QRegExp( "\"\"\"(?!')" );
+
+  // Single comments
+  singleLineCommentFormat.setForeground( Qt::darkGray );
+  aRule.pattern = QRegExp( "#[^\n]*" );
+  aRule.format = singleLineCommentFormat;
+  aRule.capture = 0;
+  highlightingRules.append( aRule );
+}
index 961616fa28993a371ea395e80f37f323efddb1e3..07ac00611f750db94539ff4deffba30d199c85e0 100644 (file)
@@ -26,6 +26,7 @@
 #include <QSyntaxHighlighter>
 
 class QTextDocument;
+class PyEditor_Keywords;
 
 class PyEditor_PyHighlighter : public QSyntaxHighlighter
 {
@@ -52,11 +53,14 @@ public:
   };
 
 public:
-  PyEditor_PyHighlighter( QTextDocument* = 0 );
+  PyEditor_PyHighlighter( QTextDocument*,
+                         PyEditor_Keywords*, PyEditor_Keywords* );
 
-  void initialize();
-  QStringList keywords();
-  QStringList specialKeywords();
+private Q_SLOTS:
+  void        onKeywordsChanged();
+
+protected:
+  void        updateHighlight();
 
 protected:
   struct HighlightingRule
@@ -76,8 +80,6 @@ protected:
   QTextCharFormat classFormat;
   QTextCharFormat referenceClassFormat;
   QTextCharFormat functionFormat;
-  QTextCharFormat keywordFormat;
-  QTextCharFormat specialFromat;
   QTextCharFormat numberFormat;
   QTextCharFormat singleLineCommentFormat;
   QTextCharFormat multiLineCommentFormat;
@@ -86,6 +88,10 @@ protected:
   void highlightBlock( const QString& );
   void insertBracketsData( char, char, TextBlockData*, const QString& );
   void insertBracketsData( Brackets, TextBlockData*, const QString& );
+
+private:
+  PyEditor_Keywords* myStdKeywords;
+  PyEditor_Keywords* myUserKeywords;
 };
 
 #endif // PYEDITOR_PYHIGHLIGHTER_H
index d9fd52fd173343a696322e831a8448ae0b4d9c09..64a43d700e8d4237db26bb62f7d5cd08012cf413 100644 (file)
@@ -22,6 +22,8 @@
 
 #include "PyEditor_Settings.h"
 
+#include "PyEditor_Editor.h"
+
 /*!
   \class PyEditor_Settings
   \brief Manager of setting values.
@@ -61,7 +63,8 @@ PyEditor_Settings::PyEditor_Settings()
     myNumberColumns( 80 ),
     myTabSpaceVisible( true ),
     myTabSize( 4 ),
-    myFont( "Courier", 10 )
+    myFont( "Courier", 10 ),
+    myCompletionPolicy( PyEditor_Editor::Always )
 {
 }
 
@@ -227,6 +230,24 @@ QFont PyEditor_Settings::font() const
   return myFont;
 }
 
+/*!
+  \brief Set "completionPolicy" option.
+  \param completion policy option value
+*/
+void PyEditor_Settings::setCompletionPolicy( int value )
+{
+  myCompletionPolicy = value;
+}
+
+/*!
+  \brief Get "completionPolicy" option.
+  \return option value
+*/
+int PyEditor_Settings::completionPolicy() const
+{
+  return myCompletionPolicy;
+}
+
 /*!
   \brief Read settings from the persistence storage.
   Base implementation does nothing; it should be reimplemented in successors.
@@ -258,6 +279,8 @@ void PyEditor_Settings::copyFrom( const PyEditor_Settings& other )
   setVerticalEdge( other.verticalEdge() );
   setNumberColumns( other.numberColumns() );
   setFont( other.font() );
+  setCompletionPolicy( other.completionPolicy() );
+
   save();
 }
 
@@ -278,6 +301,7 @@ QString PyEditor_Settings::option( Option option )
     "TabSpaceVisible",
     "TabSize",
     "Font",
+    "CompletionPolicy"
   };
-  return option >= 0 && option <= snFont ? options[option] : "Unknown";
+  return option >= 0 && option <= snCompletionPolicy ? options[option] : "Unknown";
 }
index aa6de041985d28231a8c35dc462622eb299de966..7b7e4b7c2e38f441145e786c7c9c6cb8368c0770 100644 (file)
@@ -39,7 +39,8 @@ protected:
                 snNumberColumns,
                 snTabSpaceVisible,
                 snTabSize,
-                snFont };
+                snFont,
+                snCompletionPolicy };
 
 public:
   static PyEditor_Settings* settings();
@@ -75,6 +76,9 @@ public:
   void setFont( const QFont& );
   QFont font() const;
 
+  void setCompletionPolicy( int );
+  int completionPolicy() const;
+
   virtual void load();
   virtual void save();
 
@@ -101,6 +105,9 @@ private:
   // Font settings
   QFont myFont;
 
+  // Completion settings
+  int myCompletionPolicy;
+
   static PyEditor_Settings* myGlobalSettings;
 };
 
index fc2c94b6495a7cbf954a71b5140183af7a4b75e0..4b021a070b58e9455e943636787a14ed5b81b4cd 100644 (file)
@@ -85,6 +85,16 @@ PyEditor_SettingsDlg::PyEditor_SettingsDlg( PyEditor_Editor* theEditor,
   aMainLayout->addWidget( aDisplaySetBox );
   // . Display settings <end>
 
+  // . Editor settings <start>
+  QGroupBox* aEditorSetBox = new QGroupBox( tr( "GR_EDIT_SET" ), this );
+  QGridLayout* aEditorSetLayout = new QGridLayout( aEditorSetBox );
+  aEditorSetLayout->addWidget( new QLabel( tr( "LBL_COMPLETION_MODE" ), aEditorSetBox ), 0, 0 );
+  aEditorSetLayout->addWidget( myCompletionPolicy = new QComboBox( aEditorSetBox ), 0, 1 );
+  myCompletionPolicy->addItems( QStringList() << tr( "LBL_NONE" ) << tr( "LBL_AUTO" )
+                               << tr( "LBL_MANUAL" ) << tr( "LBL_ALWAYS" ) );
+  aMainLayout->addWidget( aEditorSetBox );
+  // . Editor settings <end>
+
   // . Tab settings <start>
   QGroupBox* aTabSetBox = new QGroupBox( tr( "GR_TAB_SET" ), this );
   QVBoxLayout* aTabSetLayout = new QVBoxLayout( aTabSetBox );
@@ -237,6 +247,7 @@ void PyEditor_SettingsDlg::settingsFromGui()
   settings.setVerticalEdge( myVerticalEdge->isChecked() );
   settings.setNumberColumns( myNumberColumns->value() );
   settings.setFont( font );
+  settings.setCompletionPolicy( myCompletionPolicy->currentIndex() );
   myEditor->setSettings(settings); // updateContent()
 
   PyEditor_Settings* globals = PyEditor_Settings::settings();
@@ -261,6 +272,7 @@ void PyEditor_SettingsDlg::settingsToGui()
   myNumberColumns->setValue( settings.numberColumns() );
   myFontFamily->setCurrentFont( settings.font() );
   setFontSize( QString::number( settings.font().pointSize() ) );
+  myCompletionPolicy->setCurrentIndex( settings.completionPolicy() );
 
   onVerticalEdgeChecked();
   onFontChanged();
index 9f3dfa98695a1a2c29f4e5db6d73f408e97b6cac..24e4c6768ba2acc59ef7c7ca8412117dd4186679 100644 (file)
@@ -39,7 +39,7 @@ class PYEDITOR_EXPORT PyEditor_SettingsDlg : public QDialog
 
 public:
   PyEditor_SettingsDlg( PyEditor_Editor*, bool = false, QWidget* = 0 );
-  ~PyEditor_SettingsDlg();
+  virtual ~PyEditor_SettingsDlg();
 
 private Q_SLOTS:
   void onVerticalEdgeChecked();
@@ -71,6 +71,8 @@ private:
   QFontComboBox*    myFontFamily;
   QComboBox*        myFontSize;
 
+  QComboBox*        myCompletionPolicy;
+
   QCheckBox*        myDefaultCheck;
 
   PyEditor_Editor*  myEditor;
index 816db36535ca1c827ae6959a789fa826c5a9cb71..06a2b68eb185c5ea7e37556a9a3bc6edb34c055f 100644 (file)
@@ -59,15 +59,24 @@ void PyEditor_StdSettings::load()
 {
   mySettings.beginGroup( myGroup.isEmpty() ? option( snEditor ) : myGroup );
 
-  setHighlightCurrentLine( mySettings.value( option( snHighlightCurrentLine ), highlightCurrentLine() ).toBool() );
-  setTextWrapping( mySettings.value( option( snTextWrapping ), textWrapping() ).toBool() );
-  setCenterCursorOnScroll( mySettings.value( option( snCenterCursorOnScroll ), centerCursorOnScroll() ).toBool() );
-  setLineNumberArea( mySettings.value( option( snLineNumberArea ), lineNumberArea() ).toBool() );
-  setVerticalEdge( mySettings.value( option( snVerticalEdge  ), verticalEdge() ).toBool() );
-  setNumberColumns( mySettings.value( option( snNumberColumns ), numberColumns() ).toInt() );
-  setTabSpaceVisible( mySettings.value( option( snTabSpaceVisible ), tabSpaceVisible() ).toBool() );
+  setHighlightCurrentLine( mySettings.value( option( snHighlightCurrentLine ),
+                                            highlightCurrentLine() ).toBool() );
+  setTextWrapping( mySettings.value( option( snTextWrapping ),
+                                    textWrapping() ).toBool() );
+  setCenterCursorOnScroll( mySettings.value( option( snCenterCursorOnScroll ),
+                                            centerCursorOnScroll() ).toBool() );
+  setLineNumberArea( mySettings.value( option( snLineNumberArea ),
+                                      lineNumberArea() ).toBool() );
+  setVerticalEdge( mySettings.value( option( snVerticalEdge  ),
+                                    verticalEdge() ).toBool() );
+  setNumberColumns( mySettings.value( option( snNumberColumns ),
+                                     numberColumns() ).toInt() );
+  setTabSpaceVisible( mySettings.value( option( snTabSpaceVisible ),
+                                       tabSpaceVisible() ).toBool() );
   setTabSize( mySettings.value( option( snTabSize ), tabSize() ).toInt() );
   setFont( mySettings.value( option( snFont ), font() ).value<QFont>() );
+  setCompletionPolicy( mySettings.value( option( snCompletionPolicy ),
+                                        completionPolicy() ).toInt() );
   setLanguage( mySettings.value( "language", language() ).toString() );
 
   mySettings.endGroup();
@@ -86,6 +95,7 @@ void PyEditor_StdSettings::save()
   mySettings.setValue( option( snTabSpaceVisible ), tabSpaceVisible() );
   mySettings.setValue( option( snTabSize ), tabSize() );
   mySettings.setValue( option( snFont ), font() );
+  mySettings.setValue( option( snCompletionPolicy ), completionPolicy() );
   mySettings.setValue( "language", language() );
 
   mySettings.endGroup();
diff --git a/tools/PyEditor/src/PyEditor_Widget.cxx b/tools/PyEditor/src/PyEditor_Widget.cxx
new file mode 100644 (file)
index 0000000..badf973
--- /dev/null
@@ -0,0 +1,251 @@
+// Copyright (C) 2015-2016  OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File   : PyEditor_Widget.cxx
+// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
+//
+
+#include "PyEditor_Editor.h"
+#include "PyEditor_FindTool.h"
+#include "PyEditor_Widget.h"
+
+#include <QVBoxLayout>
+
+/*!
+  \class PyEditor_Widget
+  \brief Wraps Python editor with the find/replace functionality to a single widget.
+*/
+
+/*!
+  \brief Constructor.
+  \param parent Parent widget.
+*/
+PyEditor_Widget::PyEditor_Widget( QWidget* parent )
+{
+  // Create editor.
+  myEditor = new PyEditor_Editor( this );
+
+  // Create find tool.
+  myFindTool = new PyEditor_FindTool( myEditor, this );
+
+  // Set-up layout
+  QVBoxLayout* layout = new QVBoxLayout( this );
+  layout->setContentsMargins( 0, 0, 0, 0 );
+  layout->setSpacing( 3 );
+  layout->addWidget( myEditor );
+  layout->addWidget( myFindTool );
+
+  connect( myEditor, SIGNAL( modificationChanged( bool ) ),
+           this, SIGNAL( modificationChanged( bool ) ) );
+  connect( myEditor, SIGNAL( undoAvailable( bool ) ),
+           this, SIGNAL( undoAvailable( bool ) ) );
+  connect( myEditor, SIGNAL( redoAvailable( bool ) ),
+           this, SIGNAL( redoAvailable( bool ) ) );
+  connect( myEditor, SIGNAL( copyAvailable( bool ) ),
+           this, SIGNAL( copyAvailable( bool ) ) );
+
+  connect( myEditor, SIGNAL( selectionChanged() ),
+           this, SIGNAL( selectionChanged() ) );
+  connect( myEditor, SIGNAL( textChanged() ),
+           this, SIGNAL( textChanged() ) );
+  connect( myEditor, SIGNAL( cursorPositionChanged() ),
+           this, SIGNAL( cursorPositionChanged() ) );
+
+  setFocusProxy( myEditor );
+}
+
+/*!
+  \brief Get editor.
+  \return Pointer to editor.
+*/
+PyEditor_Editor* PyEditor_Widget::editor()
+{
+  return myEditor;
+}
+
+/*!
+  \brief Get find tool.
+  \return Pointer to find tool.
+*/
+PyEditor_FindTool* PyEditor_Widget::findTool()
+{
+  return myFindTool;
+}
+
+/*!
+  \brief Get all custom keywords from editor.
+  \return List of keywords.
+*/
+QStringList PyEditor_Widget::keywords() const
+{
+  return myEditor->keywords();
+}
+
+/*!
+  \brief Set custom keywords to editor.
+  \param keywords List of keywords.
+  \param type Type of keywords (group id).
+  \param color Color of keywords.
+*/
+void PyEditor_Widget::appendKeywords( const QStringList& keywords, int type, const QColor& color )
+{
+  myEditor->appendKeywords( keywords, type, color );
+}
+
+/*!
+  \brief Remove given custom keywords from editor.
+  \param keywords List of keywords to remove.
+*/
+void PyEditor_Widget::removeKeywords( const QStringList& keywords )
+{
+  myEditor->removeKeywords( keywords );
+}
+
+/*!
+  \brief Get current editor's completion policy.
+  \return Completion policy (see PyEditor_Editor::CompletionPolicy).
+*/
+int PyEditor_Widget::completionPolicy() const
+{
+  return (int) myEditor->completionPolicy();
+}
+
+/*!
+  \brief Set editor's completion policy.
+  \param policy Completion policy (see PyEditor_Editor::CompletionPolicy).
+*/
+void PyEditor_Widget::setCompletionPolicy( int policy )
+{
+  myEditor->setCompletionPolicy( (PyEditor_Editor::CompletionPolicy) policy );
+}
+
+/*!
+  \brief Activate Find dialog.
+*/
+void PyEditor_Widget::find()
+{
+  myFindTool->activateFind();
+}
+
+/*!
+  \brief Activate Replace dialog.
+*/
+void PyEditor_Widget::replace()
+{
+  myFindTool->activateReplace();
+}
+
+/*!
+  \brief Undo last editor's operation.
+*/
+void PyEditor_Widget::undo()
+{
+  myEditor->undo();
+}
+
+/*!
+  \brief Redo last undone editor's operation.
+*/
+void PyEditor_Widget::redo()
+{
+  myEditor->redo();
+}
+
+/*!
+  \brief Cut text selected in editor and put it into clipboard.
+*/
+void PyEditor_Widget::cut()
+{
+  myEditor->cut();
+}
+
+/*!
+  \brief Copy text selected in editor into clipboard.
+*/
+void PyEditor_Widget::copy()
+{
+  myEditor->copy();
+}
+
+/*!
+  \brief Paste text from clipboard into editor.
+*/
+void PyEditor_Widget::paste()
+{
+  myEditor->paste();
+}
+
+/*!
+  \brief Delete text selected in editor.
+*/
+void PyEditor_Widget::deleteSelected()
+{
+  myEditor->deleteSelected();
+}
+
+/*!
+  \brief Select all text in editor.
+*/
+void PyEditor_Widget::selectAll()
+{
+  myEditor->selectAll();
+}
+
+/*!
+  \brief Clear content of editor.
+*/
+void PyEditor_Widget::clear()
+{
+  myEditor->clear();
+}
+
+/*!
+  \brief Set/clear modified flag of editor.
+  \param on 'Modified' flag's value.
+*/
+void PyEditor_Widget::setModified( bool on )
+{
+  myEditor->document()->setModified( on );
+}
+
+/*!
+  \brief Get modified flag of editor.
+  \return 'Modified' flag's value.
+*/
+bool PyEditor_Widget::isModified()
+{
+  return myEditor->document()->isModified();
+}
+
+/*!
+  \brief Set text to editor.
+  \param text Text to be put into editor.
+*/
+void PyEditor_Widget::setText( const QString& text )
+{
+  myEditor->setPlainText( text );
+}
+
+/*!
+  \brief Get text from editor.
+  \return Current editor contents.
+*/
+QString PyEditor_Widget::text() const
+{
+  return myEditor->toPlainText();
+}
diff --git a/tools/PyEditor/src/PyEditor_Widget.h b/tools/PyEditor/src/PyEditor_Widget.h
new file mode 100644 (file)
index 0000000..0927a9f
--- /dev/null
@@ -0,0 +1,85 @@
+// Copyright (C) 2015-2016  OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File   : PyEditor_Widget.h
+// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
+//
+
+#ifndef PYEDITOR_WIDGET_H
+#define PYEDITOR_WIDGET_H
+
+#include "PyEditor.h"
+
+#include <QWidget>
+
+class PyEditor_Editor;
+class PyEditor_FindTool;
+
+class PYEDITOR_EXPORT PyEditor_Widget : public QWidget
+{
+  Q_OBJECT
+
+public:
+  PyEditor_Widget( QWidget* = 0 );
+
+  PyEditor_Editor* editor();
+  PyEditor_FindTool* findTool();
+
+  bool isModified();
+
+  QString text() const;
+
+  QStringList keywords() const;
+  void appendKeywords( const QStringList&, int, const QColor& = QColor() );
+  void removeKeywords( const QStringList& );
+
+  int completionPolicy() const;
+  void setCompletionPolicy( int );
+
+public slots:
+  void find();
+  void replace();
+
+  void undo();
+  void redo();
+  void cut();
+  void copy();
+  void paste();
+  void deleteSelected();
+  void selectAll();
+  void clear();
+
+  void setModified( bool );
+
+  void setText( const QString& );
+
+signals:
+  void modificationChanged( bool );
+  void undoAvailable( bool );
+  void redoAvailable( bool );
+  void copyAvailable( bool );
+  void selectionChanged();
+  void textChanged();
+  void cursorPositionChanged();
+
+private:
+  PyEditor_Editor* myEditor;
+  PyEditor_FindTool* myFindTool;
+};
+
+#endif // PYEDITOR_WIDGET_H
index 74c0be04202c9940d92dad7785d6e873891ce5d8..125a27b818a4e8f119166ea39adeabdc8fe4bde6 100644 (file)
 //
 
 #include "PyEditor_Window.h"
-#include "PyEditor_Editor.h"
+#include "PyEditor_Widget.h"
 #include "PyEditor_Settings.h"
 #include "PyEditor_SettingsDlg.h"
 
 #include <QAction>
 #include <QApplication>
+#include <QCloseEvent>
 #include <QFileDialog>
 #include <QMenuBar>
 #include <QMessageBox>
@@ -48,9 +49,9 @@ PyEditor_Window::PyEditor_Window( QWidget* parent ) :
 {
   Q_INIT_RESOURCE( PyEditor );
 
-  // Create editor and set it as a central widget.
-  myTextEditor = new PyEditor_Editor( this );
-  setCentralWidget( myTextEditor );
+  // Create central widget.
+  myEditor = new PyEditor_Widget( this );
+  setCentralWidget( myEditor );
 
   // Create actions.
   QAction* action;
@@ -81,7 +82,7 @@ PyEditor_Window::PyEditor_Window( QWidget* parent ) :
   action->setShortcut( QKeySequence::Save );
   connect( action, SIGNAL( triggered( bool ) ), this, SLOT( onSave() ) );
   action->setEnabled( false );
-  connect( myTextEditor->document(), SIGNAL( modificationChanged( bool ) ),
+  connect( myEditor, SIGNAL( modificationChanged( bool ) ),
            action, SLOT( setEnabled( bool ) ) );
   myActions[ SaveId ] = action;
 
@@ -109,9 +110,9 @@ PyEditor_Window::PyEditor_Window( QWidget* parent ) :
   action->setToolTip( tr( "TTP_UNDO" ) );
   action->setStatusTip( tr( "DSC_UNDO" ) );
   action->setShortcut( QKeySequence::Undo );
-  connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( undo() ) );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( undo() ) );
   action->setEnabled( false );
-  connect( myTextEditor->document(), SIGNAL( undoAvailable( bool ) ),
+  connect( myEditor, SIGNAL( undoAvailable( bool ) ),
            action, SLOT( setEnabled( bool ) ) );
   myActions[ UndoId ] = action;
 
@@ -121,9 +122,9 @@ PyEditor_Window::PyEditor_Window( QWidget* parent ) :
   action->setToolTip( tr( "TTP_REDO" ) );
   action->setStatusTip( tr( "DSC_REDO" ) );
   action->setShortcut( QKeySequence::Redo );
-  connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( redo() ) );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( redo() ) );
   action->setEnabled( false );
-  connect( myTextEditor->document(), SIGNAL( redoAvailable( bool ) ),
+  connect( myEditor, SIGNAL( redoAvailable( bool ) ),
            action, SLOT( setEnabled( bool ) ) );
   myActions[ RedoId ] = action;
 
@@ -133,9 +134,9 @@ PyEditor_Window::PyEditor_Window( QWidget* parent ) :
   action->setToolTip( tr( "TTP_CUT" ) );
   action->setStatusTip( tr( "DSC_CUT" ) );
   action->setShortcut( QKeySequence::Cut );
-  connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( cut() ) );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( cut() ) );
   action->setEnabled( false );
-  connect( myTextEditor, SIGNAL( copyAvailable( bool ) ),
+  connect( myEditor, SIGNAL( copyAvailable( bool ) ),
            action, SLOT( setEnabled( bool ) ) );
   myActions[ CutId ] = action;
 
@@ -145,9 +146,9 @@ PyEditor_Window::PyEditor_Window( QWidget* parent ) :
   action->setToolTip( tr( "TTP_COPY" ) );
   action->setStatusTip( tr( "DSC_COPY" ) );
   action->setShortcut( QKeySequence::Copy );
-  connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( copy() ) );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( copy() ) );
   action->setEnabled( false );
-  connect( myTextEditor, SIGNAL( copyAvailable( bool ) ),
+  connect( myEditor, SIGNAL( copyAvailable( bool ) ),
            action, SLOT( setEnabled( bool ) ) );
   myActions[ CopyId ] = action;
 
@@ -157,7 +158,7 @@ PyEditor_Window::PyEditor_Window( QWidget* parent ) :
   action->setToolTip( tr( "TTP_PASTE" ) );
   action->setStatusTip( tr( "DSC_PASTE" ) );
   action->setShortcut( QKeySequence::Paste );
-  connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( paste() ) );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( paste() ) );
   myActions[ PasteId ] = action;
 
   // . Delete
@@ -166,9 +167,9 @@ PyEditor_Window::PyEditor_Window( QWidget* parent ) :
   action->setToolTip( tr( "TTP_DELETE" ) );
   action->setStatusTip( tr( "DSC_DELETE" ) );
   action->setShortcut( QKeySequence::Delete );
-  connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( deleteSelected() ) );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( deleteSelected() ) );
   action->setEnabled( false );
-  connect( myTextEditor, SIGNAL( copyAvailable( bool ) ),
+  connect( myEditor, SIGNAL( copyAvailable( bool ) ),
            action, SLOT( setEnabled( bool ) ) );
   myActions[ DeleteId ] = action;
 
@@ -178,9 +179,29 @@ PyEditor_Window::PyEditor_Window( QWidget* parent ) :
   action->setToolTip( tr( "TTP_SELECT_ALL" ) );
   action->setStatusTip( tr( "DSC_SELECT_ALL" ) );
   action->setShortcut( QKeySequence::SelectAll );
-  connect( action, SIGNAL( triggered( bool ) ), myTextEditor, SLOT( selectAll() ) );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( selectAll() ) );
   myActions[ SelectAllId ] = action;
 
+  // . Find
+  action = new QAction( QIcon( ":/images/py_find.png" ),
+                        tr( "ACT_FIND" ), this );
+  action->setToolTip( tr( "TTP_FIND" ) );
+  action->setStatusTip( tr( "DSC_FIND" ) );
+  action->setShortcut( QKeySequence::Find );
+  action->setShortcutContext( Qt::WidgetShortcut );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( find() ) );
+  myActions[ FindId ] = action;
+
+  // . Replace
+  action = new QAction( QIcon( ":/images/py_replace.png" ),
+                        tr( "ACT_REPLACE" ), this );
+  action->setToolTip( tr( "TTP_REPLACE" ) );
+  action->setStatusTip( tr( "DSC_REPLACE" ) );
+  action->setShortcut( QKeySequence::Replace );
+  action->setShortcutContext( Qt::WidgetShortcut );
+  connect( action, SIGNAL( triggered( bool ) ), myEditor, SLOT( replace() ) );
+  myActions[ ReplaceId ] = action;
+
   // . Preferences
   action = new QAction( QIcon( ":/images/py_preferences.png" ),
                         tr( "ACT_PREFERENCES" ), this );
@@ -218,6 +239,9 @@ PyEditor_Window::PyEditor_Window( QWidget* parent ) :
   menu->addSeparator();
   menu->addAction( myActions[ SelectAllId ] );
   menu->addSeparator();
+  menu->addAction( myActions[ FindId ] );
+  menu->addAction( myActions[ ReplaceId ] );
+  menu->addSeparator();
   menu->addAction( myActions[ PreferencesId ] );
 
   menu = menuBar()->addMenu( tr( "MNU_HELP" ) );
@@ -242,6 +266,9 @@ PyEditor_Window::PyEditor_Window( QWidget* parent ) :
   toolbar->addAction( myActions[ DeleteId ] );
   toolbar->addAction( myActions[ SelectAllId ] );
   toolbar->addSeparator();
+  toolbar->addAction( myActions[ FindId ] );
+  toolbar->addAction( myActions[ ReplaceId ] );
+  toolbar->addSeparator();
   toolbar->addAction( myActions[ PreferencesId ] );
   toolbar->addSeparator();
   toolbar->addAction( myActions[ HelpId ] );
@@ -250,7 +277,7 @@ PyEditor_Window::PyEditor_Window( QWidget* parent ) :
   setCurrentFile( QString() );
 
   // Additional set-up for main window.
-  connect( myTextEditor->document(), SIGNAL( modificationChanged( bool ) ),
+  connect( myEditor, SIGNAL( modificationChanged( bool ) ),
            this, SLOT( setWindowModified( bool ) ) );
 
   // Initialize status bar.
@@ -283,7 +310,7 @@ void PyEditor_Window::onNew()
 {
   if ( whetherSave() )
   {
-    myTextEditor->clear();
+    myEditor->clear();
     setCurrentFile( QString() );
   }
 }
@@ -343,7 +370,7 @@ bool PyEditor_Window::onSaveAs()
 */
 void PyEditor_Window::onPreferences()
 {
-  PyEditor_SettingsDlg dlg( myTextEditor, true, this );
+  PyEditor_SettingsDlg dlg( myEditor->editor(), true, this );
   connect( &dlg, SIGNAL( help() ), this, SLOT( onHelp() ) );
   dlg.exec();
 }
@@ -355,7 +382,7 @@ void PyEditor_Window::onPreferences()
 void PyEditor_Window::setCurrentFile( const QString& filePath )
 {
   myURL = filePath;
-  myTextEditor->document()->setModified( false );
+  myEditor->setModified( false );
 
   setWindowModified( false );
 
@@ -369,7 +396,7 @@ void PyEditor_Window::setCurrentFile( const QString& filePath )
 */
 bool PyEditor_Window::whetherSave()
 {
-  if ( myTextEditor->document()->isModified() )
+  if ( myEditor->isModified() )
   {
     QMessageBox::StandardButton answer =  QMessageBox::warning( this,
                                                                 tr( "NAME_PYEDITOR" ),
@@ -394,19 +421,20 @@ bool PyEditor_Window::whetherSave()
   \brief Open file.
   \param filePath file path
 */
-void PyEditor_Window::loadFile( const QString& filePath )
+void PyEditor_Window::loadFile( const QString& filePath, bool verbose )
 {
   QFile aFile( filePath );
   if ( !aFile.open(QFile::ReadOnly | QFile::Text) )
   {
-    QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
-                          tr( "WRN_READ_FILE" ).arg( filePath ).arg( aFile.errorString() ) );
+    if ( verbose )
+      QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
+                            tr( "WRN_READ_FILE" ).arg( filePath ).arg( aFile.errorString() ) );
     return;
   }
 
   QTextStream anInput( &aFile );
   QApplication::setOverrideCursor( Qt::WaitCursor );
-  myTextEditor->setPlainText( anInput.readAll() );
+  myEditor->setText( anInput.readAll() );
   QApplication::restoreOverrideCursor();
 
   setCurrentFile( filePath );
@@ -419,19 +447,20 @@ void PyEditor_Window::loadFile( const QString& filePath )
   \brief Save file.
   \param filePath file path
 */
-bool PyEditor_Window::saveFile( const QString& filePath )
+bool PyEditor_Window::saveFile( const QString& filePath, bool verbose )
 {
   QFile aFile( filePath );
   if ( !aFile.open( QFile::WriteOnly | QFile::Text ) )
   {
-    QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
-                          tr( "WRN_WRITE_FILE" ).arg( filePath ).arg( aFile.errorString() ) );
+    if ( verbose )
+      QMessageBox::warning( this, tr( "NAME_PYEDITOR" ),
+                            tr( "WRN_WRITE_FILE" ).arg( filePath ).arg( aFile.errorString() ) );
     return false;
   }
 
   QTextStream anOutput( &aFile );
   QApplication::setOverrideCursor( Qt::WaitCursor );
-  anOutput << myTextEditor->toPlainText();
+  anOutput << myEditor->text();
   QApplication::restoreOverrideCursor();
 
   setCurrentFile( filePath );
index 209739e7718139d203342524f1600e44bdff0d86..1369db1cdaee63dc81e8523c734b679be8f5d066 100644 (file)
@@ -29,7 +29,7 @@
 #include <QMap>
 
 class QAction;
-class PyEditor_Editor;
+class PyEditor_Widget;
 
 class PYEDITOR_EXPORT PyEditor_Window : public QMainWindow
 {
@@ -38,11 +38,15 @@ class PYEDITOR_EXPORT PyEditor_Window : public QMainWindow
 public:
   enum { NewId, OpenId, SaveId, SaveAsId, ExitId,
          UndoId, RedoId, CutId, CopyId, PasteId, DeleteId, SelectAllId,
+         FindId, ReplaceId,
          PreferencesId, HelpId };
 
   PyEditor_Window( QWidget* = 0 );
   ~PyEditor_Window();
 
+  void        loadFile( const QString&, bool = true );
+  bool        saveFile( const QString&, bool = true );
+  
 protected:
   virtual void closeEvent( QCloseEvent* );
 
@@ -54,16 +58,12 @@ private Q_SLOTS:
   void        onPreferences();
   void        onHelp();
 
-private:
-  void        loadFile( const QString& );
-  bool        saveFile( const QString& );
-  
   void        setCurrentFile( const QString& );
   bool        whetherSave();
   QString     defaultName() const;
 
 private:
-  PyEditor_Editor*    myTextEditor;
+  PyEditor_Widget*    myEditor;
   QString             myURL;
   QMap<int, QAction*> myActions;
 };
index 372cc2898b7fc123bc3b38bbf4594d7e0b10265e..d6b530be9ca28e79e624d1f9f9d843a0b467021b 100644 (file)
@@ -66,6 +66,9 @@ public:
 
   void setFont( const QFont& );
   QFont font() const;
+
+  void setCompletionPolicy( int );
+  int completionPolicy() const;
 };
 
 class PyEditor_Editor : QPlainTextEdit
@@ -82,6 +85,10 @@ public:
   const PyEditor_Settings& settings() const;
   QString text() const;
 
+  QStringList keywords() const;
+  void        appendKeywords( const QStringList&, int, const QColor& = QColor() );
+  void        removeKeywords( const QStringList& );
+
 public slots:
   void deleteSelected();
   void append( const QString& );  
@@ -96,3 +103,75 @@ private:
   PyEditor_Editor( const PyEditor_Editor& );
   PyEditor_Editor& operator=( const PyEditor_Editor& );
 };
+
+class PyEditor_FindTool : public QWidget
+{ 
+%TypeHeaderCode
+#include <PyEditor_FindTool.h>
+%End
+
+public:
+  explicit PyEditor_FindTool( PyEditor_Editor* /TransferThis/, QWidget* /TransferThis/ = 0 );
+
+public slots:
+  void activateFind();
+  void activateReplace();
+
+private:
+  PyEditor_FindTool( const PyEditor_FindTool& );
+  PyEditor_FindTool& operator=( const PyEditor_FindTool& );
+};
+
+class PyEditor_Widget : public QWidget
+{ 
+%TypeHeaderCode
+#include <PyEditor_Widget.h>
+%End
+
+public:
+  explicit PyEditor_Widget( QWidget* /TransferThis/ = 0 );
+
+  PyEditor_Editor* editor();
+  PyEditor_FindTool* findTool();
+
+  bool isModified();
+
+  QString text() const;
+
+  QStringList keywords() const;
+  void appendKeywords( const QStringList&, int, const QColor& = QColor() );
+  void removeKeywords( const QStringList& );
+
+  int completionPolicy() const;
+  void setCompletionPolicy( int );
+
+public slots:
+  void find();
+  void replace();
+
+  void undo();
+  void redo();
+  void cut();
+  void copy();
+  void paste();
+  void deleteSelected();
+  void selectAll();
+  void clear();
+
+  void setModified( bool );
+
+  void setText( const QString& );
+
+signals:
+  void modificationChanged( bool );
+  void undoAvailable( bool );
+  void redoAvailable( bool );
+  void copyAvailable( bool );
+  void selectionChanged();
+  void textChanged();
+  void cursorPositionChanged();
+
+private:
+  PyEditor_Widget( const PyEditor_Widget& );
+  PyEditor_Widget& operator=( const PyEditor_Widget& );
+};
index 7df3d3208c23e5e9dabf2771cdcf11bbbfe14179..d2ab431f78e8594188b28abff7e085fb810469f5 100644 (file)
@@ -5,14 +5,19 @@
     <file>images/py_delete.png</file>
     <file>images/py_editor.png</file>
     <file>images/py_exit.png</file>
+    <file>images/py_find.png</file>
+    <file>images/py_find_next.png</file>
+    <file>images/py_find_previous.png</file>
     <file>images/py_help.png</file>
     <file>images/py_new.png</file>
     <file>images/py_open.png</file>
     <file>images/py_paste.png</file>
     <file>images/py_preferences.png</file>
     <file>images/py_redo.png</file>
+    <file>images/py_replace.png</file>
     <file>images/py_save.png</file>
     <file>images/py_save_as.png</file>
+    <file>images/py_search.png</file>
     <file>images/py_select_all.png</file>
     <file>images/py_undo.png</file>
     <file>about.txt</file>
index aeb24ff7c043e5e9c656b32096f18463cbd7b30f..9f914c70c6ca26569af45d21c81f1f2d0d05a96d 100644 (file)
@@ -1,10 +1,16 @@
 <b>Python Editor</b>
 <hr>
 
+<p>
 Python Editor is a simple program for writing Python scripts.
+</p>
+
+<p>
 Program provides standard editing operations like copy/cut/paste, undo/redo, select all, delete, etc.
-Also it supports syntax highlighting and auto-indentation of Python code.
+It supports syntax highlighting and auto-indentation of Python code.
+</p>
 
+<p>
 Most often used editing operations are available via the toolbar:
 <ul>
 <li><b>New</b>: creates new document.</li>
@@ -22,7 +28,13 @@ Most often used editing operations are available via the toolbar:
 <li><b>Preferences</b>: opens Preferences dialog that allows specifying advanced parameters for the program.</li>
 <li><b>Help</b>: shows this help information.</li>
 </ul>
+</p>
+
+<p>
+Also, editor supports standard <b>Find</b> and <b>Replace</b> operations.
+</p>
 
+<p>
 The behavior of the editor can be customized via the <b>Preferences</b> dialog. The following options can be customized:
 <ul>
 <li><b>Font settings</b>: choose the font.</li>
@@ -30,7 +42,9 @@ The behavior of the editor can be customized via the <b>Preferences</b> dialog.
 <li><b>Enable text wrapping</b>: allows wrapping text at the border of the editor's window.</li>
 <li><b>Center cursor on scroll</b>: allows scrolling the script vertically to make the cursor visible at the center of the viewer.</li>
 <li><b>Display line numbers area</b>: shows line numbers at the left border of the editor.</li>
+<li><b>Completion mode</b>: select the completion mode for inputted keywords.</li>
 <li><b>Vertical edge settings</b>: draws vertical line at the specified column of the viewer.</li>
 <li><b>Display tab delimiters</b>: displays tab marks at a given number of white spaces.</li>
 <li><b>Save settings as default</b>: saves chosen options as a default ones. These settings will be restored after application restart.</li>
 </ul>
+</p>
diff --git a/tools/PyEditor/src/resources/images/py_find.png b/tools/PyEditor/src/resources/images/py_find.png
new file mode 100644 (file)
index 0000000..be705fd
Binary files /dev/null and b/tools/PyEditor/src/resources/images/py_find.png differ
diff --git a/tools/PyEditor/src/resources/images/py_find_next.png b/tools/PyEditor/src/resources/images/py_find_next.png
new file mode 100644 (file)
index 0000000..66a1cfe
Binary files /dev/null and b/tools/PyEditor/src/resources/images/py_find_next.png differ
diff --git a/tools/PyEditor/src/resources/images/py_find_previous.png b/tools/PyEditor/src/resources/images/py_find_previous.png
new file mode 100644 (file)
index 0000000..d236de9
Binary files /dev/null and b/tools/PyEditor/src/resources/images/py_find_previous.png differ
diff --git a/tools/PyEditor/src/resources/images/py_replace.png b/tools/PyEditor/src/resources/images/py_replace.png
new file mode 100644 (file)
index 0000000..2e0c0ec
Binary files /dev/null and b/tools/PyEditor/src/resources/images/py_replace.png differ
diff --git a/tools/PyEditor/src/resources/images/py_search.png b/tools/PyEditor/src/resources/images/py_search.png
new file mode 100644 (file)
index 0000000..989d1af
Binary files /dev/null and b/tools/PyEditor/src/resources/images/py_search.png differ
index c32adddb261c6814a968533d7f3529f080d98b86..b692da78dc3f529a931752e63a8b4eddb388c16b 100644 (file)
       <source>LBL_LINE_NUMBS_AREA</source>
       <translation>Display line numbers area</translation>
     </message>
+    <message>
+      <source>GR_EDIT_SET</source>
+      <translation>Editor settings</translation>
+    </message>
+    <message>
+      <source>LBL_COMPLETION_MODE</source>
+      <translation>Completion mode</translation>
+    </message>
+    <message>
+      <source>LBL_NONE</source>
+      <translation>None</translation>
+    </message>
+    <message>
+      <source>LBL_AUTO</source>
+      <translation>Auto</translation>
+    </message>
+    <message>
+      <source>LBL_MANUAL</source>
+      <translation>Manual</translation>
+    </message>
+    <message>
+      <source>LBL_ALWAYS</source>
+      <translation>Always</translation>
+    </message>
     <message>
       <source>GR_TAB_SET</source>
       <translation>Tab settings</translation>
       <source>DSC_SELECT_ALL</source>
       <translation>Select all the contents</translation>
     </message>
+    <message>
+      <source>ACT_FIND</source>
+      <translation>Find</translation>
+    </message>
+    <message>
+      <source>TTP_FIND</source>
+      <translation>Find</translation>
+    </message>
+    <message>
+      <source>DSC_FIND</source>
+      <translation>Find text</translation>
+    </message>
+    <message>
+      <source>ACT_REPLACE</source>
+      <translation>Replace</translation>
+    </message>
+    <message>
+      <source>TTP_REPLACE</source>
+      <translation>Find &amp; Replace</translation>
+    </message>
+    <message>
+      <source>DSC_REPLACE</source>
+      <translation>Find and replace text</translation>
+    </message>
     <message>
       <source>ACT_PREFERENCES</source>
       <translation>Pre&amp;ferences</translation>
     </message>
     <message>
       <source>WRN_READ_FILE</source>
-      <translation>Cannot read file %1:\n%2.</translation>
+      <translation>Cannot read file %1:
+%2.</translation>
     </message>
     <message>
       <source>WRN_WRITE_FILE</source>
-      <translation>Cannot write file %1:\n%2.</translation>
+      <translation>Cannot write file %1:
+%2.</translation>
     </message>
     <message>
       <source>STS_READY</source>
       <translation>Noname.py</translation>
     </message>
   </context>
+  <context>
+    <name>PyEditor</name>
+    <message>
+      <source>PROGRAM_DESCRIPTION</source>
+      <translation>Simple Python editor</translation>
+    </message>
+    <message>
+      <source>FILE_PARAM_NAME</source>
+      <translation>file</translation>
+    </message>
+    <message>
+      <source>FILE_PARAM_DESCRIPTION</source>
+      <translation>File to edit.</translation>
+    </message>
+  </context>
+  <context>
+    <name>PyEditor_FindTool</name>
+    <message>
+      <source>FIND_LABEL</source>
+      <translation>Find:</translation>
+    </message>
+    <message>
+      <source>REPLACE_LABEL</source>
+      <translation>Replace with:</translation>
+    </message>
+    <message>
+      <source>REPLACE_BTN</source>
+      <translation>Replace</translation>
+    </message>
+    <message>
+      <source>REPLACE_ALL_BTN</source>
+      <translation>Replace All</translation>
+    </message>
+    <message>
+      <source>CASE_SENSITIVE_CHECK</source>
+      <translation>Case Sensitive</translation>
+    </message>
+    <message>
+      <source>WHOLE_WORDS_CHECK</source>
+      <translation>Whole Words Only</translation>
+    </message>
+    <message>
+      <source>REGEX_CHECK</source>
+      <translation>Use Regular Expressions</translation>
+    </message>
+    <message>
+      <source>NB_MATCHED_LABEL</source>
+      <translation>%1 of %2 matches</translation>
+    </message>
+  </context>
 </TS>
index 96cd1e001aab4bad2843ec1fd6f846cde28490a7..fc2decc6a8d4c59f50c3e1a10bc0f8ee174326a5 100644 (file)
       <source>LBL_LINE_NUMBS_AREA</source>
       <translation>Affiche les numéros de ligne</translation>
     </message>
+    <message>
+      <source>GR_EDIT_SET</source>
+      <translation type="unfinished">Editor settings</translation>
+    </message>
+    <message>
+      <source>LBL_COMPLETION_MODE</source>
+      <translation type="unfinished">Completion mode</translation>
+    </message>
+    <message>
+      <source>LBL_NONE</source>
+      <translation type="unfinished">None</translation>
+    </message>
+    <message>
+      <source>LBL_AUTO</source>
+      <translation type="unfinished">Auto</translation>
+    </message>
+    <message>
+      <source>LBL_MANUAL</source>
+      <translation type="unfinished">Manual</translation>
+    </message>
+    <message>
+      <source>LBL_ALWAYS</source>
+      <translation type="unfinished">Always</translation>
+    </message>
     <message>
       <source>GR_TAB_SET</source>
       <translation>Indentation</translation>
       <source>DSC_SELECT_ALL</source>
       <translation>Sélectionne tout le contenu</translation>
     </message>
+    <message>
+      <source>ACT_FIND</source>
+      <translation type="unfinished">Find</translation>
+    </message>
+    <message>
+      <source>TTP_FIND</source>
+      <translation type="unfinished">Find</translation>
+    </message>
+    <message>
+      <source>DSC_FIND</source>
+      <translation type="unfinished">Find text</translation>
+    </message>
+    <message>
+      <source>ACT_REPLACE</source>
+      <translation type="unfinished">Replace</translation>
+    </message>
+    <message>
+      <source>TTP_REPLACE</source>
+      <translation type="unfinished">Find &amp; Replace</translation>
+    </message>
+    <message>
+      <source>DSC_REPLACE</source>
+      <translation type="unfinished">Find and replace text</translation>
+    </message>
     <message>
       <source>ACT_PREFERENCES</source>
       <translation>Préférences</translation>
     </message>
     <message>
       <source>WRN_READ_FILE</source>
-      <translation>Impossible de lire le fichier %1:\n%2.</translation>
+      <translation>Impossible de lire le fichier %1:
+%2.</translation>
     </message>
     <message>
       <source>WRN_WRITE_FILE</source>
-      <translation>Impossible d'écrire le fichier %1:\n%2.</translation>
+      <translation>Impossible d'écrire le fichier %1:
+%2.</translation>
     </message>
     <message>
       <source>STS_READY</source>
       <translation>Noname.py</translation>
     </message>
   </context>
+  <context>
+    <name>PyEditor</name>
+    <message>
+      <source>PROGRAM_DESCRIPTION</source>
+      <translation>Editeur python</translation>
+    </message>
+    <message>
+      <source>FILE_PARAM_NAME</source>
+      <translation type="unfinished">file</translation>
+    </message>
+    <message>
+      <source>FILE_PARAM_DESCRIPTION</source>
+      <translation type="unfinished">File to edit.</translation>
+    </message>
+  </context>
+  <context>
+    <name>PyEditor_FindTool</name>
+    <message>
+      <source>FIND_LABEL</source>
+      <translation type="unfinished">Find:</translation>
+    </message>
+    <message>
+      <source>REPLACE_LABEL</source>
+      <translation type="unfinished">Replace with:</translation>
+    </message>
+    <message>
+      <source>REPLACE_BTN</source>
+      <translation type="unfinished">Replace</translation>
+    </message>
+    <message>
+      <source>REPLACE_ALL_BTN</source>
+      <translation type="unfinished">Replace All</translation>
+    </message>
+    <message>
+      <source>CASE_SENSITIVE_CHECK</source>
+      <translation type="unfinished">Case Sensitive</translation>
+    </message>
+    <message>
+      <source>WHOLE_WORDS_CHECK</source>
+      <translation type="unfinished">Whole Words Only</translation>
+    </message>
+    <message>
+      <source>REGEX_CHECK</source>
+      <translation type="unfinished">Use Regular Expressions</translation>
+    </message>
+    <message>
+      <source>NB_MATCHED_LABEL</source>
+      <translation type="unfinished">%1 of %2 matches</translation>
+    </message>
+  </context>
 </TS>
index e77eaa1343fc7b295791d89e61eee78ef2dc33d2..ec1846668f2baa5ef8b9455102c5570f567b199a 100644 (file)
       <source>LBL_LINE_NUMBS_AREA</source>
       <translation>ライン番号エリアの表示</translation>
     </message>
+    <message>
+      <source>GR_EDIT_SET</source>
+      <translation type="unfinished">Editor settings</translation>
+    </message>
+    <message>
+      <source>LBL_COMPLETION_MODE</source>
+      <translation type="unfinished">Completion mode</translation>
+    </message>
+    <message>
+      <source>LBL_NONE</source>
+      <translation type="unfinished">None</translation>
+    </message>
+    <message>
+      <source>LBL_AUTO</source>
+      <translation type="unfinished">Auto</translation>
+    </message>
+    <message>
+      <source>LBL_MANUAL</source>
+      <translation type="unfinished">Manual</translation>
+    </message>
+    <message>
+      <source>LBL_ALWAYS</source>
+      <translation type="unfinished">Always</translation>
+    </message>
     <message>
       <source>GR_TAB_SET</source>
       <translation>タブ設定</translation>
       <source>DSC_SELECT_ALL</source>
       <translation>全選択</translation>
     </message>
+    <message>
+      <source>ACT_FIND</source>
+      <translation type="unfinished">Find</translation>
+    </message>
+    <message>
+      <source>TTP_FIND</source>
+      <translation type="unfinished">Find</translation>
+    </message>
+    <message>
+      <source>DSC_FIND</source>
+      <translation type="unfinished">Find text</translation>
+    </message>
+    <message>
+      <source>ACT_REPLACE</source>
+      <translation type="unfinished">Replace</translation>
+    </message>
+    <message>
+      <source>TTP_REPLACE</source>
+      <translation type="unfinished">Find &amp; Replace</translation>
+    </message>
+    <message>
+      <source>DSC_REPLACE</source>
+      <translation type="unfinished">Find and replace text</translation>
+    </message>
     <message>
       <source>ACT_PREFERENCES</source>
       <translation>環境設定 (&amp;f)</translation>
     </message>
     <message>
       <source>WRN_READ_FILE</source>
-      <translation>ファイルが読めません %1:\n%2.</translation>
+      <translation>ファイルが読めません %1:
+%2.</translation>
     </message>
     <message>
       <source>WRN_WRITE_FILE</source>
-      <translation>ファイルが書き込めません %1:\n%2.</translation>
+      <translation>ファイルが書き込めません %1:
+%2.</translation>
     </message>
     <message>
       <source>STS_READY</source>
       <translation>Noname. py</translation>
     </message>
   </context>
+  <context>
+    <name>PyEditor</name>
+    <message>
+      <source>PROGRAM_DESCRIPTION</source>
+      <translation>Pythonのエディタ</translation>
+    </message>
+    <message>
+      <source>FILE_PARAM_NAME</source>
+      <translation type="unfinished">file</translation>
+    </message>
+    <message>
+      <source>FILE_PARAM_DESCRIPTION</source>
+      <translation type="unfinished">File to edit.</translation>
+    </message>
+  </context>
+  <context>
+    <name>PyEditor_FindTool</name>
+    <message>
+      <source>FIND_LABEL</source>
+      <translation type="unfinished">Find:</translation>
+    </message>
+    <message>
+      <source>REPLACE_LABEL</source>
+      <translation type="unfinished">Replace with:</translation>
+    </message>
+    <message>
+      <source>REPLACE_BTN</source>
+      <translation type="unfinished">Replace</translation>
+    </message>
+    <message>
+      <source>REPLACE_ALL_BTN</source>
+      <translation type="unfinished">Replace All</translation>
+    </message>
+    <message>
+      <source>CASE_SENSITIVE_CHECK</source>
+      <translation type="unfinished">Case Sensitive</translation>
+    </message>
+    <message>
+      <source>WHOLE_WORDS_CHECK</source>
+      <translation type="unfinished">Whole Words Only</translation>
+    </message>
+    <message>
+      <source>REGEX_CHECK</source>
+      <translation type="unfinished">Use Regular Expressions</translation>
+    </message>
+    <message>
+      <source>NB_MATCHED_LABEL</source>
+      <translation type="unfinished">%1 of %2 matches</translation>
+    </message>
+  </context>
 </TS>