Salome HOME
PyEditor: introduce auto-completion feature
authorstv <sergey.telkov@opencascade.com>
Wed, 24 May 2017 10:50:33 +0000 (13:50 +0300)
committerstv <sergey.telkov@opencascade.com>
Wed, 24 May 2017 10:50:33 +0000 (13:50 +0300)
21 files changed:
src/LightApp/resources/LightApp.xml
src/PyViewer/PyViewer_Settings.cxx
tools/PyEditor/src/CMakeLists.txt
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_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/python/PyEditorPy.sip
tools/PyEditor/src/resources/about.txt
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 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 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 94f88dae6efefe83dff84ee03767c69a16d28a70..2715e6ebd125d1c960a9ee6d41eb214b5f92b7e1 100644 (file)
@@ -44,6 +44,8 @@ SET(_link_LIBRARIES ${PLATFORM_LIBS} ${QT_LIBRARIES})
 SET(_moc_HEADERS
   PyEditor_Editor.h
   PyEditor_LineNumberArea.h
+  PyEditor_Keywords.h
+  PyEditor_Completer.h
   PyEditor_PyHighlighter.h
   PyEditor_SettingsDlg.h
   PyEditor_Window.h
@@ -83,6 +85,8 @@ QT_ADD_RESOURCES(_rcc_SOURCES ${_rcc_RESOURCES})
 SET(_other_SOURCES
   PyEditor_Editor.cxx
   PyEditor_LineNumberArea.cxx
+  PyEditor_Keywords.cxx
+  PyEditor_Completer.cxx
   PyEditor_PyHighlighter.cxx
   PyEditor_Settings.cxx
   PyEditor_SettingsDlg.cxx
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_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();
index 372cc2898b7fc123bc3b38bbf4594d7e0b10265e..499ee9574a5c91bf2ce2ac04a34452df83a1e4f9 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& );  
index aeb24ff7c043e5e9c656b32096f18463cbd7b30f..75e476a52431ee5eeb59321ca662de4e4e2da4d5 100644 (file)
@@ -30,6 +30,7 @@ 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>
index c32adddb261c6814a968533d7f3529f080d98b86..d1a9f40ad2b3014b1218b3c81e327da7c0769679 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>
index 96cd1e001aab4bad2843ec1fd6f846cde28490a7..17aad7262edea82f1d9893d7cd91cc61b7113438 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>
index e77eaa1343fc7b295791d89e61eee78ef2dc33d2..199ba7d46768efcdf9f4ff63fee5f0da50b9be75 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>