1 // Copyright (C) 2015-2016 OPEN CASCADE
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 // File : PyEditor_Completer.cxx
20 // Author : Sergey TELKOV, Open CASCADE S.A.S. (sergey.telkov@opencascade.com)
23 #include "PyEditor_Completer.h"
25 #include "PyEditor_Editor.h"
26 #include "PyEditor_Keywords.h"
31 #include <QTextCursor>
32 #include <QApplication>
33 #include <QAbstractItemView>
34 #include <QStandardItemModel>
39 PyEditor_Completer::PyEditor_Completer( PyEditor_Editor* editor,
40 PyEditor_Keywords* std, PyEditor_Keywords* user )
41 : QCompleter( editor ),
45 myUserKeywords( user )
48 setCompletionMode(QCompleter::PopupCompletion);
50 connect(this, SIGNAL( activated( const QString& ) ),
51 this, SLOT( onActivated( const QString& ) ) );
52 connect(editor, SIGNAL( textChanged() ), this, SLOT( onTextChanged() ) );
53 connect(myStdKeywords, SIGNAL( keywordsChanged() ),
54 this, SLOT( onKeywordsChanged() ) );
55 connect(myUserKeywords, SIGNAL( keywordsChanged() ),
56 this, SLOT( onKeywordsChanged() ) );
64 PyEditor_Completer::~PyEditor_Completer()
69 \brief Perform the completion if it possible.
71 void PyEditor_Completer::perform()
73 QString prefix = completionText();
74 setCompletionPrefix( prefix );
76 if ( !completionPrefix().isEmpty() &&
77 ( completionCount() > 1 || ( completionCount() == 1 &&
78 currentCompletion() != completionPrefix() ) ) )
79 complete(completionRect());
85 \brief Hide the completer's popup menu.
87 void PyEditor_Completer::uncomplete()
94 \brief Handling 'Enter' key.
96 bool PyEditor_Completer::eventFilter(QObject* o, QEvent* e)
99 if ( e->type() == QEvent::KeyPress && popup()->isVisible() ) {
100 QKeyEvent* ke = (QKeyEvent*)e;
101 if ( ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return ) {
103 setCurrentRow(popup()->currentIndex().row());
104 onActivated(currentCompletion());
109 res = QCompleter::eventFilter(o, e);
115 \brief Perform delayed completion.
117 void PyEditor_Completer::onTimeout()
123 \brief Invoked when text changed in editor.
125 void PyEditor_Completer::onTextChanged()
128 if ( myEditor->completionPolicy() == PyEditor_Editor::Auto ||
129 myEditor->completionPolicy() == PyEditor_Editor::Always )
134 \brief Invoked when keywords changed in editor.
136 void PyEditor_Completer::onKeywordsChanged()
142 \brief Insert selected completion into editor.
144 void PyEditor_Completer::onActivated( const QString& text)
146 QPoint rng = completionRange();
147 QTextCursor cursor = myEditor->textCursor();
148 cursor.setPosition(cursor.position() - rng.y() + rng.x() - 1,
149 QTextCursor::KeepAnchor);
150 cursor.insertText(text);
155 \brief Get the rectangle for completion popup.
156 \return completion popup rect
158 QRect PyEditor_Completer::completionRect() const
160 QRect res = myEditor->cursorRect(myEditor->textCursor());
161 res.setWidth(popup()->sizeHint().width());
162 res.translate(myEditor->document()->documentMargin(),
163 myEditor->document()->documentMargin());
168 \brief Get the current completion prefix from editor.
169 \return completion prefix string
171 QString PyEditor_Completer::completionText() const
175 QString txt = myEditor->textCursor().block().text();
176 if ( !txt.isEmpty() ) {
177 QPoint range = completionRange();
178 prefix = txt.mid( range.x(), range.y() - range.x() + 1 );
185 \brief Get position of completion prefix in editor.
186 \return begin and end of completion prefix
188 QPoint PyEditor_Completer::completionRange() const
193 QTextCursor cursor = myEditor->textCursor();
194 QString txt = cursor.block().text();
196 int end = cursor.positionInBlock() - 1;
198 QRegExp rx("[A-Za-z]{1}\\w*$");
199 int pos = rx.indexIn(txt.mid(beg, end - beg + 1));
204 range = QPoint(beg, end);
211 \brief Schedule the delayed completion.
213 void PyEditor_Completer::triggerComplete()
216 myTimer = new QTimer( this );
217 myTimer->setSingleShot( true );
218 myTimer->setInterval( 200 );
220 connect( myTimer, SIGNAL( timeout() ), this, SLOT( onTimeout() ) );
223 if ( myTimer->isActive() )
229 \brief Updates the keywords list in completer.
231 void PyEditor_Completer::updateKeywords()
233 QStandardItemModel* model = new QStandardItemModel( this );
234 KeywordMap kwMap = keywords();
235 for ( KeywordMap::const_iterator it = kwMap.begin(); it != kwMap.end(); ++it ) {
236 QStandardItem* item = new QStandardItem( it.key() );
237 if ( it.value().isValid() )
238 item->setForeground( it.value() );
239 model->appendRow( item );
245 \brief Gets the keywords list.
246 \return keyword string list
248 PyEditor_Completer::KeywordMap PyEditor_Completer::keywords() const
251 QList<PyEditor_Keywords*> kwDicts;
252 kwDicts << myStdKeywords << myUserKeywords;
254 for ( QList<PyEditor_Keywords*>::iterator itr = kwDicts.begin(); itr != kwDicts.end(); ++itr ) {
255 PyEditor_Keywords* dict = *itr;
256 QStringList kwList = dict->keywords();
257 for ( QStringList::const_iterator it = kwList.begin(); it != kwList.end(); ++it ) {
258 if ( !res.contains( *it ) ) {
259 res.insert( *it, dict->color( *it ) );