]> SALOME platform Git repositories - modules/gui.git/commitdiff
Salome HOME
PyEditor: implement Find/Replace feature
authorvsr <vsr@opencascade.com>
Mon, 22 May 2017 11:16:04 +0000 (14:16 +0300)
committervsr <vsr@opencascade.com>
Wed, 24 May 2017 15:43:45 +0000 (18:43 +0300)
26 files changed:
src/PyViewer/CMakeLists.txt
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
tools/PyEditor/src/CMakeLists.txt
tools/PyEditor/src/PyEditor.cxx
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_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 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 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 2715e6ebd125d1c960a9ee6d41eb214b5f92b7e1..53f50bbea2934b4e44f3172bc8953bc70382b151 100644 (file)
@@ -43,11 +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
 )
 
@@ -84,6 +86,7 @@ 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
@@ -91,6 +94,7 @@ SET(_other_SOURCES
   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_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_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 499ee9574a5c91bf2ce2ac04a34452df83a1e4f9..d6b530be9ca28e79e624d1f9f9d843a0b467021b 100644 (file)
@@ -103,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 75e476a52431ee5eeb59321ca662de4e4e2da4d5..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>
@@ -35,3 +47,4 @@ The behavior of the editor can be customized via the <b>Preferences</b> dialog.
 <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 d1a9f40ad2b3014b1218b3c81e327da7c0769679..b692da78dc3f529a931752e63a8b4eddb388c16b 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>
     </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 17aad7262edea82f1d9893d7cd91cc61b7113438..fc2decc6a8d4c59f50c3e1a10bc0f8ee174326a5 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>
     </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 199ba7d46768efcdf9f4ff63fee5f0da50b9be75..ec1846668f2baa5ef8b9455102c5570f567b199a 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>
     </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>