1 // Copyright (C) 2015-2024 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_Editor.cxx
20 // Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
23 #include "PyEditor_Editor.h"
24 #include "PyEditor_LineNumberArea.h"
25 #include "PyEditor_PyHighlighter.h"
26 #include "PyEditor_Completer.h"
27 #include "PyEditor_Settings.h"
28 #include "PyEditor_Keywords.h"
38 \class PyEditor_Editor
39 \brief Widget to show / edit Python scripts.
44 \param parent parent widget
46 PyEditor_Editor::PyEditor_Editor( QWidget* parent )
47 : QPlainTextEdit( parent ),
48 myCompletionPolicy( Always )
50 myStdKeywords = new PyEditor_StandardKeywords( this );
51 myUserKeywords = new PyEditor_Keywords( this );
52 myUserKeywords->append( "print", 0, Qt::red );
54 // Set up line number area
55 myLineNumberArea = new PyEditor_LineNumberArea( this );
56 myLineNumberArea->setMouseTracking( true );
58 // Set up syntax highighter
59 mySyntaxHighlighter = new PyEditor_PyHighlighter( this->document(),
60 myStdKeywords, myUserKeywords );
63 PyEditor_Settings* settings = PyEditor_Settings::settings();
65 setSettings( *settings );
67 myCompleter = new PyEditor_Completer( this, myStdKeywords, myUserKeywords );
70 connect( this, SIGNAL( blockCountChanged( int ) ), this, SLOT( updateLineNumberAreaWidth( int ) ) );
71 connect( this, SIGNAL( updateRequest( QRect, int ) ), this, SLOT( updateLineNumberArea( QRect, int ) ) );
72 connect( this, SIGNAL( cursorPositionChanged() ), this, SLOT( updateHighlightCurrentLine() ) );
73 connect( this, SIGNAL( cursorPositionChanged() ), this, SLOT( matchParentheses() ) );
79 PyEditor_Editor::~PyEditor_Editor()
84 \brief Get editor settings.
85 \return settings object
87 const PyEditor_Settings& PyEditor_Editor::settings() const
93 \brief Set editor settings.
94 \param settings new settings
96 void PyEditor_Editor::setSettings( const PyEditor_Settings& settings )
98 mySettings.copyFrom( settings );
101 QFont aFont = font();
102 aFont.setFamily( mySettings.font().family() );
103 aFont.setPointSize( mySettings.font().pointSize() );
106 // Set line wrap mode
107 setLineWrapMode( mySettings.textWrapping() ? QPlainTextEdit::WidgetWidth : QPlainTextEdit::NoWrap );
109 // Center the cursor on screen
110 setCenterOnScroll( mySettings.centerCursorOnScroll() );
112 // Set size white spaces
113 setTabStopWidth( mySettings.tabSize() * 10 );
115 // Set completion policy
116 setCompletionPolicy( (CompletionPolicy)mySettings.completionPolicy() );
118 // Update current line highlight
119 updateHighlightCurrentLine();
121 // Update line numbers area
122 updateLineNumberAreaWidth( 0 );
124 mySyntaxHighlighter->rehighlight();
125 viewport()->update();
129 \brief Gets the current completion policy
130 \return completion policy
132 PyEditor_Editor::CompletionPolicy PyEditor_Editor::completionPolicy() const
134 return myCompletionPolicy;
138 \brief Sets the current completion policy
139 \param policy completion policy
141 void PyEditor_Editor::setCompletionPolicy( const CompletionPolicy& policy )
143 myCompletionPolicy = policy;
147 \brief Gets the all user keywords.
148 \param event key press event
149 \return keyword string list
151 QStringList PyEditor_Editor::keywords() const
153 return myUserKeywords->keywords();
157 \brief Add the user keywords.
158 \param kws keywords string list
159 \param type keywords type
160 \param color keywords color
162 void PyEditor_Editor::appendKeywords( const QStringList& kws, int type, const QColor& color )
164 myUserKeywords->append( kws, type, color );
168 \brief Remove the user keywords.
169 \param kws keywords string list
171 void PyEditor_Editor::removeKeywords( const QStringList& kws )
173 myUserKeywords->remove( kws );
177 Delete current selection contents.
179 void PyEditor_Editor::deleteSelected()
181 QTextCursor aCursor = textCursor();
182 if ( aCursor.hasSelection() )
183 aCursor.removeSelectedText();
184 setTextCursor( aCursor );
188 \brief Process key press event.
189 Reimplemented from QPlainTextEdit.
190 \param event key press event
192 void PyEditor_Editor::keyPressEvent( QKeyEvent* event )
194 if ( event->type() == QEvent::KeyPress )
196 int aKey = event->key();
197 Qt::KeyboardModifiers aCtrl = event->modifiers() & Qt::ControlModifier;
198 Qt::KeyboardModifiers aShift = event->modifiers() & Qt::ShiftModifier;
200 if ( aKey == Qt::Key_Tab || ( aKey == Qt::Key_Backtab || ( aKey == Qt::Key_Tab && aShift ) ) )
202 QTextCursor aCursor = textCursor();
203 aCursor.beginEditBlock();
204 tabIndentation( aKey == Qt::Key_Backtab );
205 aCursor.endEditBlock();
208 else if ( aKey == Qt::Key_Enter || aKey == Qt::Key_Return )
210 QTextCursor aCursor = textCursor();
211 aCursor.beginEditBlock();
212 if ( lineIndent() == 0 )
214 QPlainTextEdit::keyPressEvent( event );
216 aCursor.endEditBlock();
219 else if ( aKey == Qt::Key_Space && aCtrl && !aShift &&
220 ( completionPolicy() == Manual || completionPolicy() == Always ) )
222 myCompleter->perform();
225 else if ( event == QKeySequence::MoveToStartOfLine || event == QKeySequence::SelectStartOfLine )
227 QTextCursor aCursor = this->textCursor();
228 if ( QTextLayout* aLayout = aCursor.block().layout() )
230 if ( aLayout->lineForTextPosition( aCursor.position() - aCursor.block().position() ).lineNumber() == 0 )
232 handleHome( event == QKeySequence::SelectStartOfLine );
236 else if ( ( aKey == Qt::Key_Colon || ( aKey == Qt::Key_Space && !aCtrl && !aShift ) ) &&
237 !textCursor().hasSelection() )
239 QTextCursor aCursor = textCursor();
240 aCursor.movePosition( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
242 QString aSelectedText = aCursor.selectedText();
243 int numSpaces = findFirstNonSpace( aSelectedText );
244 int amountChars = aSelectedText.size() - findFirstNonSpace( aSelectedText );
245 QString aLeadingText = aSelectedText.right( amountChars );
247 QStringList aReservedWords;
248 aReservedWords.append( "except" );
249 if ( aKey == Qt::Key_Colon )
251 aReservedWords.append( "else" );
252 aReservedWords.append( "finally" );
254 else if ( aKey == Qt::Key_Space )
256 aReservedWords.append( "elif" );
259 if ( aReservedWords.contains( aLeadingText ) )
261 QString aPreviousText = aCursor.block().previous().text();
262 int numSpacesPrevious = findFirstNonSpace( aPreviousText );
263 if ( numSpaces == numSpacesPrevious )
265 tabIndentation( true );
266 aCursor.movePosition( QTextCursor::EndOfBlock );
267 setTextCursor( aCursor );
270 QPlainTextEdit::keyPressEvent( event );
274 QPlainTextEdit::keyPressEvent( event );
280 \brief Handle resize event.
281 Reimplemented from QPlainTextEdit.
282 \param event resize event
284 void PyEditor_Editor::resizeEvent( QResizeEvent* event )
286 QPlainTextEdit::resizeEvent( event );
288 // Change size geometry of line number area
289 QRect aContentsRect = contentsRect();
290 myLineNumberArea->setGeometry( QRect( aContentsRect.left(),
292 lineNumberAreaWidth(),
293 aContentsRect.height() ) );
298 Reimplemented from QPlainTextEdit.
299 \param event paint event
301 void PyEditor_Editor::paintEvent( QPaintEvent* event )
303 QPlainTextEdit::paintEvent( event );
305 QTextBlock aBlock( firstVisibleBlock() );
306 QPointF anOffset( contentOffset() );
307 QPainter aPainter( this->viewport() );
309 int aTabSpaces = this->tabStopWidth() / 10;
311 // Visualization tab spaces
312 if ( mySettings.tabSpaceVisible() )
314 qreal aTop = blockBoundingGeometry( aBlock ).translated( anOffset ).top();
315 while ( aBlock.isValid() && aTop <= event->rect().bottom() )
317 if ( aBlock.isVisible() && blockBoundingGeometry( aBlock ).translated( anOffset ).toRect().intersects( event->rect() ) )
319 QString aText = aBlock.text();
320 if ( aText.contains( QRegExp( "\\w+" ) ) )
321 aText.remove( QRegExp( "(?!\\w+)\\s+$" ) );
325 while ( anIndex != -1 )
327 anIndex = aText.indexOf( QRegExp( QString( "^\\s{%1}" ).arg( aTabSpaces ) ), 0 );
330 aColumn = aColumn + aTabSpaces;
331 aText = aText.mid( aTabSpaces );
333 if ( aText.startsWith( ' ' ) )
335 QTextCursor aCursor( aBlock );
336 aCursor.setPosition( aBlock.position() + aColumn );
338 QRect aRect = cursorRect( aCursor );
339 aPainter.setPen( QPen( Qt::darkGray, 1, Qt::DotLine ) );
340 aPainter.drawLine( aRect.x() + 1, aRect.top(), aRect.x() + 1, aRect.bottom() );
345 aBlock = aBlock.next();
349 // Vertical edge line
350 if ( mySettings.verticalEdge() )
352 const QRect aRect = event->rect();
353 const QFont aFont = currentCharFormat().font();
354 int aNumberColumn = QFontMetrics( aFont ).averageCharWidth() * mySettings.numberColumns() + anOffset.x() + document()->documentMargin();
355 aPainter.setPen( QPen( Qt::lightGray, 1, Qt::SolidLine ) );
356 aPainter.drawLine( aNumberColumn, aRect.top(), aNumberColumn, aRect.bottom() );
360 void PyEditor_Editor::contextMenuEvent( QContextMenuEvent* event )
362 QMenu* menu = createStandardContextMenu();
363 emit customizeMenu( menu );
364 menu->exec( event->globalPos() );
369 \brief Indent and tab text.
370 \param isShift flag defines reverse tab direction
372 void PyEditor_Editor::tabIndentation( bool isShift )
374 QTextCursor aCursor = textCursor();
375 int aTabSpaces = this->tabStopWidth()/10;
377 if ( !aCursor.hasSelection() )
381 int N = aCursor.columnNumber() % aTabSpaces;
382 aCursor.insertText( QString( aTabSpaces - N, QLatin1Char( ' ' ) ) );
386 QTextBlock aCurrentBlock = document()->findBlock( aCursor.position() );
387 int anIndentPos = findFirstNonSpace( aCurrentBlock.text() );
388 aCursor.setPosition( aCurrentBlock.position() + anIndentPos );
389 setTextCursor( aCursor );
391 //if ( aCurrCursorColumnPos <= anIndentPos )
393 int aColumnPos = aCursor.columnNumber();
394 if ( aColumnPos != 0 )
396 int N = aCursor.columnNumber() % aTabSpaces;
397 if ( N == 0 ) N = aTabSpaces;
398 aCursor.movePosition( QTextCursor::Left, QTextCursor::KeepAnchor, N );
399 aCursor.removeSelectedText();
401 setTextCursor( aCursor );
407 indentSelection( isShift );
412 \brief Indent and tab selected text.
413 \param isShift flag defines reverse tab direction
415 void PyEditor_Editor::indentSelection( bool isShift )
417 QTextCursor aCursor = this->textCursor();
419 int aCursorStart = aCursor.selectionStart();
420 int aCursorEnd = aCursor.selectionEnd();
422 QTextBlock aStartBlock = document()->findBlock( aCursorStart );
423 QTextBlock anEndBlock = document()->findBlock( aCursorEnd - 1 ).next();
425 int aTabSpaces = this->tabStopWidth()/10;
427 for ( QTextBlock aBlock = aStartBlock; aBlock.isValid() && aBlock != anEndBlock; aBlock = aBlock.next() )
429 QString aText = aBlock.text();
430 int anIndentPos = findFirstNonSpace( aText );
431 int N = ( anIndentPos % aTabSpaces );
433 aCursor.setPosition( aBlock.position() + anIndentPos );
436 aCursor.insertText( QString( aTabSpaces - N, QLatin1Char( ' ' ) ) );
437 setTextCursor( aCursor );
441 int aColumnPos = aCursor.columnNumber();
442 if ( aColumnPos != 0 )
444 int blockN = aColumnPos % aTabSpaces;
445 if ( blockN == 0 ) blockN = aTabSpaces;
446 aCursor.movePosition( QTextCursor::Left, QTextCursor::KeepAnchor, blockN );
447 aCursor.removeSelectedText();
448 setTextCursor( aCursor );
453 // Reselect the selected lines
454 aCursor.setPosition( aStartBlock.position() );
455 aCursor.setPosition( anEndBlock.previous().position(), QTextCursor::KeepAnchor );
456 aCursor.movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
457 setTextCursor( aCursor );
461 \brief Find first non white-space symbol in text.
462 \param text input text
463 \return index of first non white-space symbol
465 int PyEditor_Editor::findFirstNonSpace( const QString& text )
468 while ( i < text.size() )
470 if ( !text.at(i).isSpace() )
481 int PyEditor_Editor::lineIndent()
483 int aTabSpaces = this->tabStopWidth() / 10;
485 QTextCursor aCursor = textCursor();
486 aCursor.insertBlock();
487 setTextCursor( aCursor );
489 QTextBlock aCurrentBlock = aCursor.block();
490 if ( aCurrentBlock == document()->begin() )
493 QTextBlock aPreviousBlock = aCurrentBlock.previous();
495 QString aPreviousText;
498 if ( aPreviousBlock == document()->begin() )
500 aPreviousText = aPreviousBlock.text();
501 if ( aPreviousText.isEmpty() && aPreviousText.trimmed().isEmpty() )
506 // If the text of this block is not empty then break the loop.
507 aPreviousText = aPreviousBlock.text();
508 if ( !aPreviousText.isEmpty() && !aPreviousText.trimmed().isEmpty() )
511 aPreviousBlock = aPreviousBlock.previous();
514 int aTabIndentation = 0;
515 int anAmountIndentation = -1;
517 while ( i < aPreviousText.size() )
519 if ( !aPreviousText.at(i).isSpace() )
521 anAmountIndentation = findFirstNonSpace( aPreviousText );
531 if ( anAmountIndentation == -1 )
533 if ( aTabIndentation > 0 )
534 anAmountIndentation = aTabIndentation;
539 const QString aPreviousTrimmed = aPreviousText.trimmed();
540 if ( aPreviousTrimmed.endsWith( ":" ) )
542 anAmountIndentation += aTabSpaces;
546 if ( aPreviousTrimmed == "continue"
547 || aPreviousTrimmed == "break"
548 || aPreviousTrimmed == "pass"
549 || aPreviousTrimmed == "return"
550 || aPreviousTrimmed == "raise"
551 || aPreviousTrimmed.startsWith( "raise " )
552 || aPreviousTrimmed.startsWith( "return " ) )
553 anAmountIndentation -= aTabSpaces;
556 aCursor.insertText( QString( anAmountIndentation, QLatin1Char(' ') ) );
557 setTextCursor( aCursor );
563 \brief Set text cursor on home position.
564 \param isExtendLine \c true to keep current anchor position
566 void PyEditor_Editor::handleHome( bool isExtendLine )
568 QTextCursor aCursor = textCursor();
569 QTextCursor::MoveMode aMode = QTextCursor::MoveAnchor;
572 aMode = QTextCursor::KeepAnchor;
574 int anInitPos = aCursor.position();
575 int aBlockPos = aCursor.block().position();
577 QChar aCharacter = document()->characterAt( aBlockPos );
578 while ( aCharacter.category() == QChar::Separator_Space )
581 if ( aBlockPos == anInitPos )
583 aCharacter = document()->characterAt( aBlockPos );
586 if ( aBlockPos == anInitPos )
587 aBlockPos = aCursor.block().position();
589 aCursor.setPosition( aBlockPos, aMode );
590 setTextCursor( aCursor );
594 \brief Update current line highlighting.
596 void PyEditor_Editor::updateHighlightCurrentLine()
598 QList<QTextEdit::ExtraSelection> anExtraSelections;
599 if ( !isReadOnly() && mySettings.highlightCurrentLine() )
601 QTextEdit::ExtraSelection selection;
603 QColor lineColor = QColor( Qt::gray ).lighter( 155 );
605 selection.format.setBackground( lineColor );
606 selection.format.setProperty( QTextFormat::FullWidthSelection, QVariant( true ) );
607 selection.cursor = textCursor();
608 selection.cursor.clearSelection();
609 anExtraSelections.append( selection );
611 setExtraSelections( anExtraSelections );
615 \brief Draw linne number area.
616 \param event paint event
618 void PyEditor_Editor::lineNumberAreaPaintEvent( QPaintEvent* event )
620 QPainter aPainter( myLineNumberArea );
621 aPainter.fillRect( event->rect(), QColor( Qt::lightGray ).lighter( 125 ) );
623 QTextBlock aBlock = firstVisibleBlock();
624 int aBlockNumber = aBlock.blockNumber();
625 int aTop = (int)blockBoundingGeometry( aBlock ).translated( contentOffset() ).top();
626 int aBottom = aTop + (int)blockBoundingRect( aBlock ).height();
627 int aCurrentLine = document()->findBlock( textCursor().position() ).blockNumber();
629 QFont aFont = aPainter.font();
630 aPainter.setPen( this->palette().color( QPalette::Text ) );
632 while ( aBlock.isValid() && aTop <= event->rect().bottom() )
634 if ( aBlock.isVisible() && aBottom >= event->rect().top() )
636 if ( aBlockNumber == aCurrentLine )
638 aPainter.setPen( Qt::darkGray );
639 aFont.setBold( true );
640 aPainter.setFont( aFont );
644 aPainter.setPen( Qt::gray ) ;
645 aFont.setBold( false );
646 aPainter.setFont( aFont );
648 QString aNumber = QString::number( aBlockNumber + 1 );
649 aPainter.drawText( 0, aTop, myLineNumberArea->width(), fontMetrics().height(), Qt::AlignRight, aNumber );
652 aBlock = aBlock.next();
654 aBottom = aTop + (int)blockBoundingRect( aBlock ).height();
660 \brief Get with of line number area.
661 \return width of line number area
663 int PyEditor_Editor::lineNumberAreaWidth()
668 int aMaximum = qMax( 1, blockCount() );
669 while ( aMaximum >= 10 )
675 if ( mySettings.lineNumberArea() )
676 aSpace += 5 + fontMetrics().width( QLatin1Char( '9' ) ) * aDigits;
682 \brief Update width of the line number area.
683 \param newBlockCount (not used)
685 void PyEditor_Editor::updateLineNumberAreaWidth( int /*newBlockCount*/ )
687 setViewportMargins( lineNumberAreaWidth(), 0, 0, 0 );
691 \brief Update line number area (when editor viewport is scrolled).
692 \param rect area being updated
693 \param dy scroll factor
695 void PyEditor_Editor::updateLineNumberArea( const QRect& rect, int dy )
698 myLineNumberArea->scroll( 0, dy );
700 myLineNumberArea->update( 0, rect.y(), myLineNumberArea->width(), rect.height() );
702 if ( rect.contains( viewport()->rect() ) )
703 updateLineNumberAreaWidth( 0 );
707 \brief Manage parentheses.
709 void PyEditor_Editor::matchParentheses()
711 PyEditor_PyHighlighter::TextBlockData* data =
712 static_cast<PyEditor_PyHighlighter::TextBlockData*>( textCursor().block().userData() );
716 QVector<PyEditor_PyHighlighter::ParenthesisInfo*> infoEntries = data->parentheses();
718 int aPos = textCursor().block().position();
720 for ( int i = 0; i < infoEntries.size(); ++i )
722 PyEditor_PyHighlighter::ParenthesisInfo* info = infoEntries.at(i);
724 int currentColumnPosition = textCursor().columnNumber();
725 if ( info->position == currentColumnPosition - 1 && isLeftBrackets( info->character ) )
727 if ( matchLeftParenthesis( textCursor().block(), i + 1, 0 ) )
728 createParenthesisSelection( aPos + info->position );
730 else if ( info->position == currentColumnPosition && isLeftBrackets( info->character ) )
734 if ( matchLeftParenthesis( textCursor().block(), i + 1, 0 ) )
735 createParenthesisSelection( aPos + info->position );
738 else if ( info->position == currentColumnPosition - 1 && isRightBrackets( info->character ) )
740 if ( matchRightParenthesis( textCursor().block(), i - 1, 0 ) )
741 createParenthesisSelection( aPos + info->position );
744 else if ( info->position == currentColumnPosition && isRightBrackets( info->character ) )
746 if ( matchRightParenthesis( textCursor().block(), i - 1, 0 ) )
747 createParenthesisSelection( aPos + info->position );
754 \brief Match left brackets.
755 \param currentBlock text block
757 \param numLeftParentheses number of left parentheses
758 \return \c true if the left match
760 bool PyEditor_Editor::matchLeftParenthesis( const QTextBlock& currentBlock,
761 int idx, int numLeftParentheses )
763 PyEditor_PyHighlighter::TextBlockData* data =
764 static_cast<PyEditor_PyHighlighter::TextBlockData*>( currentBlock.userData() );
765 QVector<PyEditor_PyHighlighter::ParenthesisInfo*> infos = data->parentheses();
767 int docPos = currentBlock.position();
768 for ( ; idx < infos.size(); ++idx )
770 PyEditor_PyHighlighter::ParenthesisInfo* info = infos.at( idx );
772 if ( isLeftBrackets( info->character ) )
774 ++numLeftParentheses;
778 if ( isRightBrackets( info->character ) && numLeftParentheses == 0 )
780 createParenthesisSelection( docPos + info->position );
784 --numLeftParentheses;
787 QTextBlock nextBlock = currentBlock.next();
788 if ( nextBlock.isValid() )
789 return matchLeftParenthesis( nextBlock, 0, numLeftParentheses );
795 \brief Match right brackets.
796 \param currentBlock text block
798 \param numRightParentheses number of right parentheses
799 \return \c true if the right match
801 bool PyEditor_Editor::matchRightParenthesis( const QTextBlock& currentBlock,
802 int idx, int numRightParentheses )
804 PyEditor_PyHighlighter::TextBlockData* data = static_cast<PyEditor_PyHighlighter::TextBlockData*>( currentBlock.userData() );
805 QVector<PyEditor_PyHighlighter::ParenthesisInfo*> parentheses = data->parentheses();
807 int docPos = currentBlock.position();
808 for ( ; idx > -1 && parentheses.size() > 0; --idx )
810 PyEditor_PyHighlighter::ParenthesisInfo* info = parentheses.at( idx );
811 if ( isRightBrackets( info->character ) )
813 ++numRightParentheses;
816 if ( isLeftBrackets( info->character ) && numRightParentheses == 0 )
818 createParenthesisSelection( docPos + info->position );
822 --numRightParentheses;
825 QTextBlock prevBlock = currentBlock.previous();
826 if ( prevBlock.isValid() )
828 PyEditor_PyHighlighter::TextBlockData* data = static_cast<PyEditor_PyHighlighter::TextBlockData*>( prevBlock.userData() );
829 QVector<PyEditor_PyHighlighter::ParenthesisInfo*> parentheses = data->parentheses();
830 return matchRightParenthesis( prevBlock, parentheses.size() - 1, numRightParentheses );
837 \brief Create brackets selection.
838 \param position cursor position
840 void PyEditor_Editor::createParenthesisSelection( int position )
842 QList<QTextEdit::ExtraSelection> selections = extraSelections();
844 QTextEdit::ExtraSelection selection;
846 QTextCharFormat format = selection.format;
847 format.setForeground( Qt::red );
848 format.setBackground( Qt::white );
849 selection.format = format;
851 QTextCursor cursor = textCursor();
852 cursor.setPosition( position );
853 cursor.movePosition( QTextCursor::NextCharacter, QTextCursor::KeepAnchor );
854 selection.cursor = cursor;
856 selections.append( selection );
857 setExtraSelections( selections );
861 \brief Check if symbol is a left bracket.
862 \param symbol text symbol
863 \return \c true if symbol is any left bracket
865 bool PyEditor_Editor::isLeftBrackets( QChar symbol )
867 return symbol == '(' || symbol == '{' || symbol == '[';
871 \brief Check if symbol is a right bracket.
872 \param symbol text symbol
873 \return \c true if symbol is any right bracket
875 bool PyEditor_Editor::isRightBrackets( QChar symbol )
877 return symbol == ')' || symbol == '}' || symbol == ']';
881 \brief Append new paragraph to the end of the editor's text.
882 \param text paragraph text
884 void PyEditor_Editor::append( const QString& text )
886 appendPlainText( text );
890 \brief Set text to the editor.
893 void PyEditor_Editor::setText( const QString& text )
895 setPlainText( text );
899 \brief Get current editor's content.
902 QString PyEditor_Editor::text() const
904 return toPlainText();
908 \brief Get user keywords dictionary.
909 \return keywords dictionary
911 PyEditor_Keywords* PyEditor_Editor::userKeywords() const
913 return myUserKeywords;
917 \brief Get standard keywords dictionary.
918 \return keywords dictionary
920 PyEditor_Keywords* PyEditor_Editor::standardKeywords() const
922 return myStdKeywords;
926 \brief Move cursor to the given line.
927 \note Line count starts from 1.
928 \param line Line number.
930 void PyEditor_Editor::setCurrentLine( int line )
932 QTextCursor cursor( document()->findBlockByLineNumber( line - 1 ) );
933 setTextCursor( cursor );
934 ensureCursorVisible();