1# -*- coding: utf-8 -*- 2 3# Copyright (c) 2004 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> 4# 5 6""" 7Module implementing a compatability interface class to QsciScintilla. 8""" 9 10import contextlib 11 12from PyQt5.QtCore import pyqtSignal, Qt, QPoint 13from PyQt5.QtGui import QPalette, QColor 14from PyQt5.QtWidgets import QApplication, QListWidget 15from PyQt5.Qsci import ( 16 QsciScintillaBase, QsciScintilla, 17 QSCINTILLA_VERSION as QSCIQSCINTILLA_VERSION 18) 19 20############################################################################### 21 22 23def QSCINTILLA_VERSION(): 24 """ 25 Module function to return the QScintilla version. 26 27 @return QScintilla version (integer) 28 """ 29 return QSCIQSCINTILLA_VERSION 30 31############################################################################### 32 33 34class QsciScintillaCompat(QsciScintilla): 35 """ 36 Class implementing a compatability interface to QsciScintilla. 37 38 This class implements all the functions, that were added to 39 QsciScintilla incrementally. This class ensures compatibility 40 to older versions of QsciScintilla. 41 42 @signal zoomValueChanged(int) emitted to signal a change of the zoom value 43 """ 44 zoomValueChanged = pyqtSignal(int) 45 46 ArrowFoldStyle = QsciScintilla.FoldStyle.BoxedTreeFoldStyle + 1 47 ArrowTreeFoldStyle = ArrowFoldStyle + 1 48 49 UserSeparator = '\x04' 50 51 IndicatorStyleMax = QsciScintilla.INDIC_GRADIENTCENTRE 52 53 def __init__(self, parent=None): 54 """ 55 Constructor 56 57 @param parent parent widget (QWidget) 58 """ 59 super().__init__(parent) 60 61 self.zoom = 0 62 63 self.__targetSearchFlags = 0 64 self.__targetSearchExpr = "" 65 self.__targetSearchStart = 0 66 self.__targetSearchEnd = -1 67 self.__targetSearchActive = False 68 69 self.__modified = False 70 71 self.userListActivated.connect(self.__completionListSelected) 72 self.modificationChanged.connect(self.__modificationChanged) 73 74 self.setAutoCompletionWidgetSize(40, 5) 75 76 def __modificationChanged(self, m): 77 """ 78 Private slot to handle the modificationChanged signal. 79 80 @param m modification status (boolean) 81 """ 82 self.__modified = m 83 84 def isModified(self): 85 """ 86 Public method to return the modification status. 87 88 @return flag indicating the modification status (boolean) 89 """ 90 return self.__modified 91 92 def setModified(self, m): 93 """ 94 Public slot to set the modification status. 95 96 @param m new modification status (boolean) 97 """ 98 self.__modified = m 99 super().setModified(m) 100 self.modificationChanged.emit(m) 101 102 def setLexer(self, lex=None): 103 """ 104 Public method to set the lexer. 105 106 @param lex the lexer to be set or None to reset it. 107 """ 108 super().setLexer(lex) 109 if lex is None: 110 self.clearStyles() 111 112 def clearStyles(self): 113 """ 114 Public method to set the styles according the selected Qt style. 115 """ 116 palette = QApplication.palette() 117 self.SendScintilla(QsciScintilla.SCI_STYLESETFORE, 118 QsciScintilla.STYLE_DEFAULT, 119 palette.color(QPalette.ColorRole.Text)) 120 self.SendScintilla(QsciScintilla.SCI_STYLESETBACK, 121 QsciScintilla.STYLE_DEFAULT, 122 palette.color(QPalette.ColorRole.Base)) 123 self.SendScintilla(QsciScintilla.SCI_STYLECLEARALL) 124 self.SendScintilla(QsciScintilla.SCI_CLEARDOCUMENTSTYLE) 125 126 def monospacedStyles(self, font): 127 """ 128 Public method to set the current style to be monospaced. 129 130 @param font font to be used (QFont) 131 """ 132 try: 133 rangeLow = list(range(self.STYLE_DEFAULT)) 134 except AttributeError: 135 rangeLow = list(range(32)) 136 try: 137 rangeHigh = list(range(self.STYLE_LASTPREDEFINED + 1, 138 self.STYLE_MAX + 1)) 139 except AttributeError: 140 rangeHigh = list(range(40, 128)) 141 142 f = font.family().encode("utf-8") 143 ps = font.pointSize() 144 weight = -font.weight() 145 italic = font.italic() 146 underline = font.underline() 147 bold = font.bold() 148 for style in rangeLow + rangeHigh: 149 self.SendScintilla(QsciScintilla.SCI_STYLESETFONT, style, f) 150 self.SendScintilla(QsciScintilla.SCI_STYLESETSIZE, style, ps) 151 try: 152 self.SendScintilla( 153 QsciScintilla.SCI_STYLESETWEIGHT, style, weight) 154 except AttributeError: 155 self.SendScintilla(QsciScintilla.SCI_STYLESETBOLD, style, bold) 156 self.SendScintilla(QsciScintilla.SCI_STYLESETITALIC, style, italic) 157 self.SendScintilla( 158 QsciScintilla.SCI_STYLESETUNDERLINE, style, underline) 159 160 def linesOnScreen(self): 161 """ 162 Public method to get the amount of visible lines. 163 164 @return amount of visible lines (integer) 165 """ 166 return self.SendScintilla(QsciScintilla.SCI_LINESONSCREEN) 167 168 def lineAt(self, pos): 169 """ 170 Public method to calculate the line at a position. 171 172 This variant is able to calculate the line for positions in the 173 margins and for empty lines. 174 175 @param pos position to calculate the line for (integer or QPoint) 176 @return linenumber at position or -1, if there is no line at pos 177 (integer, zero based) 178 """ 179 scipos = ( 180 pos 181 if isinstance(pos, int) else 182 self.SendScintilla(QsciScintilla.SCI_POSITIONFROMPOINT, 183 pos.x(), pos.y()) 184 ) 185 line = self.SendScintilla(QsciScintilla.SCI_LINEFROMPOSITION, scipos) 186 if line >= self.lines(): 187 line = -1 188 return line 189 190 def currentPosition(self): 191 """ 192 Public method to get the current position. 193 194 @return absolute position of the cursor (integer) 195 """ 196 return self.SendScintilla(QsciScintilla.SCI_GETCURRENTPOS) 197 198 def styleAt(self, pos): 199 """ 200 Public method to get the style at a position in the text. 201 202 @param pos position in the text (integer) 203 @return style at the requested position or 0, if the position 204 is negative or past the end of the document (integer) 205 """ 206 return self.SendScintilla(QsciScintilla.SCI_GETSTYLEAT, pos) 207 208 def currentStyle(self): 209 """ 210 Public method to get the style at the current position. 211 212 @return style at the current position (integer) 213 """ 214 return self.styleAt(self.currentPosition()) 215 216 def getSubStyleRange(self, styleNr): 217 """ 218 Public method to get the sub style range for given style number. 219 220 @param styleNr Number of the base style 221 @type int 222 @return start index of the sub style and their count 223 @rtype int, int 224 """ 225 start = self.SendScintilla(QsciScintilla.SCI_GETSUBSTYLESSTART, 226 styleNr) 227 count = self.SendScintilla(QsciScintilla.SCI_GETSUBSTYLESLENGTH, 228 styleNr) 229 return start, count 230 231 def getEndStyled(self): 232 """ 233 Public method to get the last styled position. 234 235 @return end position of the last styling run (integer) 236 """ 237 return self.SendScintilla(QsciScintilla.SCI_GETENDSTYLED) 238 239 def startStyling(self, pos, mask): 240 """ 241 Public method to prepare styling. 242 243 @param pos styling positition to start at (integer) 244 @param mask mask of bits to use for styling (integer) 245 """ 246 self.SendScintilla(QsciScintilla.SCI_STARTSTYLING, pos, mask) 247 248 def setStyling(self, length, style): 249 """ 250 Public method to style some text. 251 252 @param length length of text to style (integer) 253 @param style style to set for text (integer) 254 """ 255 self.SendScintilla(QsciScintilla.SCI_SETSTYLING, length, style) 256 257 def charAt(self, pos): 258 """ 259 Public method to get the character at a position in the text observing 260 multibyte characters. 261 262 @param pos position in the text (integer) 263 @return character at the requested position or empty string, if the 264 position is negative or past the end of the document (string) 265 """ 266 ch = self.byteAt(pos) 267 if ch and ord(ch) > 127 and self.isUtf8(): 268 if (ch[0] & 0xF0) == 0xF0: 269 utf8Len = 4 270 elif (ch[0] & 0xE0) == 0xE0: 271 utf8Len = 3 272 elif (ch[0] & 0xC0) == 0xC0: 273 utf8Len = 2 274 else: 275 utf8Len = 1 276 while len(ch) < utf8Len: 277 pos += 1 278 ch += self.byteAt(pos) 279 try: 280 return ch.decode('utf8') 281 except UnicodeDecodeError: 282 if pos > 0: 283 # try it one position before; maybe we are in the 284 # middle of a unicode character 285 return self.charAt(pos - 1) 286 else: 287 return "" 288 else: 289 return ch.decode() 290 291 def byteAt(self, pos): 292 """ 293 Public method to get the raw character (bytes) at a position in the 294 text. 295 296 @param pos position in the text (integer) 297 @return raw character at the requested position or empty bytes, if the 298 position is negative or past the end of the document (bytes) 299 """ 300 char = self.SendScintilla(QsciScintilla.SCI_GETCHARAT, pos) 301 if char == 0: 302 return bytearray() 303 if char < 0: 304 char += 256 305 return bytearray((char,)) 306 307 def foldLevelAt(self, line): 308 """ 309 Public method to get the fold level of a line of the document. 310 311 @param line line number (integer) 312 @return fold level of the given line (integer) 313 """ 314 lvl = self.SendScintilla(QsciScintilla.SCI_GETFOLDLEVEL, line) 315 return ( 316 (lvl & QsciScintilla.SC_FOLDLEVELNUMBERMASK) - 317 QsciScintilla.SC_FOLDLEVELBASE 318 ) 319 320 def foldFlagsAt(self, line): 321 """ 322 Public method to get the fold flags of a line of the document. 323 324 @param line line number (integer) 325 @return fold flags of the given line (integer) 326 """ 327 lvl = self.SendScintilla(QsciScintilla.SCI_GETFOLDLEVEL, line) 328 return lvl & ~QsciScintilla.SC_FOLDLEVELNUMBERMASK 329 330 def foldHeaderAt(self, line): 331 """ 332 Public method to determine, if a line of the document is a fold header 333 line. 334 335 @param line line number (integer) 336 @return flag indicating a fold header line (boolean) 337 """ 338 lvl = self.SendScintilla(QsciScintilla.SCI_GETFOLDLEVEL, line) 339 return lvl & QsciScintilla.SC_FOLDLEVELHEADERFLAG 340 341 def foldExpandedAt(self, line): 342 """ 343 Public method to determine, if a fold is expanded. 344 345 @param line line number (integer) 346 @return flag indicating the fold expansion state of the line (boolean) 347 """ 348 return self.SendScintilla(QsciScintilla.SCI_GETFOLDEXPANDED, line) 349 350 def setIndentationGuideView(self, view): 351 """ 352 Public method to set the view of the indentation guides. 353 354 @param view view of the indentation guides (SC_IV_NONE, SC_IV_REAL, 355 SC_IV_LOOKFORWARD or SC_IV_LOOKBOTH) 356 """ 357 self.SendScintilla(QsciScintilla.SCI_SETINDENTATIONGUIDES, view) 358 359 def indentationGuideView(self): 360 """ 361 Public method to get the indentation guide view. 362 363 @return indentation guide view (SC_IV_NONE, SC_IV_REAL, 364 SC_IV_LOOKFORWARD or SC_IV_LOOKBOTH) 365 """ 366 return self.SendScintilla(QsciScintilla.SCI_GETINDENTATIONGUIDES) 367 368 ########################################################################### 369 ## methods below are missing from QScintilla 370 ########################################################################### 371 372 def setAutoCompletionWidgetSize(self, chars, lines): 373 """ 374 Public method to set the size of completion and user lists. 375 376 @param chars max. number of chars to show 377 @type int 378 @param lines max. number of lines to show 379 @type int 380 """ 381 self.SendScintilla(QsciScintilla.SCI_AUTOCSETMAXWIDTH, chars) 382 self.SendScintilla(QsciScintilla.SCI_AUTOCSETMAXHEIGHT, lines) 383 384 def zoomIn(self, zoom=1): 385 """ 386 Public method used to increase the zoom factor. 387 388 @param zoom zoom factor increment (integer) 389 """ 390 super().zoomIn(zoom) 391 392 def zoomOut(self, zoom=1): 393 """ 394 Public method used to decrease the zoom factor. 395 396 @param zoom zoom factor decrement (integer) 397 """ 398 super().zoomOut(zoom) 399 400 def zoomTo(self, zoom): 401 """ 402 Public method used to zoom to a specific zoom factor. 403 404 @param zoom zoom factor (integer) 405 """ 406 self.zoom = zoom 407 super().zoomTo(zoom) 408 self.zoomValueChanged.emit(self.zoom) 409 410 def getZoom(self): 411 """ 412 Public method used to retrieve the current zoom factor. 413 414 @return zoom factor (integer) 415 """ 416 return self.zoom 417 418 def editorCommand(self, cmd): 419 """ 420 Public method to perform a simple editor command. 421 422 @param cmd the scintilla command to be performed (integer) 423 """ 424 self.SendScintilla(cmd) 425 426 def scrollVertical(self, lines): 427 """ 428 Public method to scroll the text area. 429 430 @param lines number of lines to scroll (negative scrolls up, 431 positive scrolls down) (integer) 432 """ 433 self.SendScintilla(QsciScintilla.SCI_LINESCROLL, 0, lines) 434 435 def moveCursorToEOL(self): 436 """ 437 Public method to move the cursor to the end of line. 438 """ 439 self.SendScintilla(QsciScintilla.SCI_LINEEND) 440 441 def moveCursorLeft(self): 442 """ 443 Public method to move the cursor left. 444 """ 445 self.SendScintilla(QsciScintilla.SCI_CHARLEFT) 446 447 def moveCursorRight(self): 448 """ 449 Public method to move the cursor right. 450 """ 451 self.SendScintilla(QsciScintilla.SCI_CHARRIGHT) 452 453 def moveCursorWordLeft(self): 454 """ 455 Public method to move the cursor left one word. 456 """ 457 self.SendScintilla(QsciScintilla.SCI_WORDLEFT) 458 459 def moveCursorWordRight(self): 460 """ 461 Public method to move the cursor right one word. 462 """ 463 self.SendScintilla(QsciScintilla.SCI_WORDRIGHT) 464 465 def newLineBelow(self): 466 """ 467 Public method to insert a new line below the current one. 468 """ 469 self.SendScintilla(QsciScintilla.SCI_LINEEND) 470 self.SendScintilla(QsciScintilla.SCI_NEWLINE) 471 472 def deleteBack(self): 473 """ 474 Public method to delete the character to the left of the cursor. 475 """ 476 self.SendScintilla(QsciScintilla.SCI_DELETEBACK) 477 478 def delete(self): 479 """ 480 Public method to delete the character to the right of the cursor. 481 """ 482 self.SendScintilla(QsciScintilla.SCI_CLEAR) 483 484 def deleteWordLeft(self): 485 """ 486 Public method to delete the word to the left of the cursor. 487 """ 488 self.SendScintilla(QsciScintilla.SCI_DELWORDLEFT) 489 490 def deleteWordRight(self): 491 """ 492 Public method to delete the word to the right of the cursor. 493 """ 494 self.SendScintilla(QsciScintilla.SCI_DELWORDRIGHT) 495 496 def deleteLineLeft(self): 497 """ 498 Public method to delete the line to the left of the cursor. 499 """ 500 self.SendScintilla(QsciScintilla.SCI_DELLINELEFT) 501 502 def deleteLineRight(self): 503 """ 504 Public method to delete the line to the right of the cursor. 505 """ 506 self.SendScintilla(QsciScintilla.SCI_DELLINERIGHT) 507 508 def extendSelectionLeft(self): 509 """ 510 Public method to extend the selection one character to the left. 511 """ 512 self.SendScintilla(QsciScintilla.SCI_CHARLEFTEXTEND) 513 514 def extendSelectionRight(self): 515 """ 516 Public method to extend the selection one character to the right. 517 """ 518 self.SendScintilla(QsciScintilla.SCI_CHARRIGHTEXTEND) 519 520 def extendSelectionWordLeft(self): 521 """ 522 Public method to extend the selection one word to the left. 523 """ 524 self.SendScintilla(QsciScintilla.SCI_WORDLEFTEXTEND) 525 526 def extendSelectionWordRight(self): 527 """ 528 Public method to extend the selection one word to the right. 529 """ 530 self.SendScintilla(QsciScintilla.SCI_WORDRIGHTEXTEND) 531 532 def extendSelectionToBOL(self): 533 """ 534 Public method to extend the selection to the beginning of the line. 535 """ 536 self.SendScintilla(QsciScintilla.SCI_VCHOMEEXTEND) 537 538 def extendSelectionToEOL(self): 539 """ 540 Public method to extend the selection to the end of the line. 541 """ 542 self.SendScintilla(QsciScintilla.SCI_LINEENDEXTEND) 543 544 def hasSelection(self): 545 """ 546 Public method to check for a selection. 547 548 @return flag indicating the presence of a selection (boolean) 549 """ 550 return self.getSelection()[0] != -1 551 552 def hasSelectedText(self): 553 """ 554 Public method to indicate the presence of selected text. 555 556 This is an overriding method to cope with a bug in QsciScintilla. 557 558 @return flag indicating the presence of selected text (boolean) 559 """ 560 return bool(self.selectedText()) 561 562 def selectionIsRectangle(self): 563 """ 564 Public method to check, if the current selection is rectangular. 565 566 @return flag indicating a rectangular selection (boolean) 567 """ 568 startLine, startIndex, endLine, endIndex = self.getSelection() 569 return ( 570 startLine != -1 and 571 startLine != endLine and 572 self.SendScintilla(QsciScintilla.SCI_SELECTIONISRECTANGLE) 573 ) 574 575 def getRectangularSelection(self): 576 """ 577 Public method to retrieve the start and end of a rectangular selection. 578 579 @return tuple with start line and index and end line and index 580 (tuple of four int) 581 """ 582 if not self.selectionIsRectangle(): 583 return (-1, -1, -1, -1) 584 585 startPos = self.SendScintilla( 586 QsciScintilla.SCI_GETRECTANGULARSELECTIONANCHOR) 587 endPos = self.SendScintilla( 588 QsciScintilla.SCI_GETRECTANGULARSELECTIONCARET) 589 startLine, startIndex = self.lineIndexFromPosition(startPos) 590 endLine, endIndex = self.lineIndexFromPosition(endPos) 591 592 return (startLine, startIndex, endLine, endIndex) 593 594 def setRectangularSelection(self, startLine, startIndex, endLine, 595 endIndex): 596 """ 597 Public method to set a rectangular selection. 598 599 @param startLine line number of the start of the selection (int) 600 @param startIndex index number of the start of the selection (int) 601 @param endLine line number of the end of the selection (int) 602 @param endIndex index number of the end of the selection (int) 603 """ 604 startPos = self.positionFromLineIndex(startLine, startIndex) 605 endPos = self.positionFromLineIndex(endLine, endIndex) 606 607 self.SendScintilla( 608 QsciScintilla.SCI_SETRECTANGULARSELECTIONANCHOR, startPos) 609 self.SendScintilla( 610 QsciScintilla.SCI_SETRECTANGULARSELECTIONCARET, endPos) 611 612 def getSelectionCount(self): 613 """ 614 Public method to get the number of active selections. 615 616 @return number of active selection (integer) 617 """ 618 return self.SendScintilla(QsciScintilla.SCI_GETSELECTIONS) 619 620 def getSelectionN(self, index): 621 """ 622 Public method to get the start and end of a selection given by its 623 index. 624 625 @param index index of the selection (integer) 626 @return tuple with start line and index and end line and index 627 (tuple of four int) for the given selection 628 """ 629 startPos = self.SendScintilla( 630 QsciScintilla.SCI_GETSELECTIONNSTART, index) 631 endPos = self.SendScintilla(QsciScintilla.SCI_GETSELECTIONNEND, index) 632 startLine, startIndex = self.lineIndexFromPosition(startPos) 633 endLine, endIndex = self.lineIndexFromPosition(endPos) 634 635 return (startLine, startIndex, endLine, endIndex) 636 637 def getSelections(self): 638 """ 639 Public method to get the start and end coordinates of all active 640 selections. 641 642 @return list of tuples with start line and index and end line and index 643 of each active selection (list of tuples of four int) 644 """ 645 selections = [] 646 for index in range(self.getSelectionCount()): 647 selections.append(self.getSelectionN(index)) 648 return selections 649 650 def setVirtualSpaceOptions(self, options): 651 """ 652 Public method to set the virtual space usage options. 653 654 @param options usage options to set (integer, 0 to 3) 655 """ 656 self.SendScintilla(QsciScintilla.SCI_SETVIRTUALSPACEOPTIONS, options) 657 658 def getLineSeparator(self): 659 """ 660 Public method to get the line separator for the current eol mode. 661 662 @return eol string (string) 663 """ 664 m = self.eolMode() 665 if m == QsciScintilla.EolMode.EolWindows: 666 eol = '\r\n' 667 elif m == QsciScintilla.EolMode.EolUnix: 668 eol = '\n' 669 elif m == QsciScintilla.EolMode.EolMac: 670 eol = '\r' 671 else: 672 eol = '' 673 return eol 674 675 def getEolIndicator(self): 676 """ 677 Public method to get the eol indicator for the current eol mode. 678 679 @return eol indicator (string) 680 """ 681 m = self.eolMode() 682 if m == QsciScintilla.EolMode.EolWindows: 683 eol = 'CRLF' 684 elif m == QsciScintilla.EolMode.EolUnix: 685 eol = 'LF' 686 elif m == QsciScintilla.EolMode.EolMac: 687 eol = 'CR' 688 else: 689 eol = '' 690 return eol 691 692 def setEolModeByEolString(self, eolStr): 693 """ 694 Public method to set the eol mode given the eol string. 695 696 @param eolStr eol string (string) 697 """ 698 if eolStr == '\r\n': 699 self.setEolMode( 700 QsciScintilla.EolMode(QsciScintilla.EolMode.EolWindows)) 701 elif eolStr == '\n': 702 self.setEolMode( 703 QsciScintilla.EolMode(QsciScintilla.EolMode.EolUnix)) 704 elif eolStr == '\r': 705 self.setEolMode( 706 QsciScintilla.EolMode(QsciScintilla.EolMode.EolMac)) 707 708 def detectEolString(self, txt): 709 """ 710 Public method to determine the eol string used. 711 712 @param txt text from which to determine the eol string (string) 713 @return eol string (string) 714 """ 715 if len(txt.split("\r\n", 1)) == 2: 716 return '\r\n' 717 elif len(txt.split("\n", 1)) == 2: 718 return '\n' 719 elif len(txt.split("\r", 1)) == 2: 720 return '\r' 721 else: 722 return None 723 724 def getCursorFlashTime(self): 725 """ 726 Public method to get the flash (blink) time of the cursor in 727 milliseconds. 728 729 The flash time is the time required to display, invert and restore the 730 caret display. Usually the text cursor is displayed for half the cursor 731 flash time, then hidden for the same amount of time. 732 733 @return flash time of the cursor in milliseconds (integer) 734 """ 735 return 2 * self.SendScintilla(QsciScintilla.SCI_GETCARETPERIOD) 736 737 def setCursorFlashTime(self, time): 738 """ 739 Public method to set the flash (blink) time of the cursor in 740 milliseconds. 741 742 The flash time is the time required to display, invert and restore the 743 caret display. Usually the text cursor is displayed for half the cursor 744 flash time, then hidden for the same amount of time. 745 746 @param time flash time of the cursor in milliseconds (integer) 747 """ 748 self.SendScintilla(QsciScintilla.SCI_SETCARETPERIOD, time // 2) 749 750 def getCaretLineAlwaysVisible(self): 751 """ 752 Public method to determine, if the caret line is visible even if 753 the editor doesn't have the focus. 754 755 @return flag indicating an always visible caret line (boolean) 756 """ 757 try: 758 return self.SendScintilla( 759 QsciScintilla.SCI_GETCARETLINEVISIBLEALWAYS) 760 except AttributeError: 761 return False 762 763 def setCaretLineAlwaysVisible(self, alwaysVisible): 764 """ 765 Public method to set the caret line visible even if the editor doesn't 766 have the focus. 767 768 @param alwaysVisible flag indicating that the caret line shall be 769 visible even if the editor doesn't have the focus (boolean) 770 """ 771 with contextlib.suppress(AttributeError): 772 self.SendScintilla( 773 QsciScintilla.SCI_SETCARETLINEVISIBLEALWAYS, alwaysVisible) 774 775 def canPaste(self): 776 """ 777 Public method to test, if the paste action is available (i.e. if the 778 clipboard contains some text). 779 780 @return flag indicating the availability of 'paste' 781 @rtype bool 782 """ 783 return self.SendScintilla(QsciScintilla.SCI_CANPASTE) 784 785 ########################################################################### 786 ## methods to perform searches in target range 787 ########################################################################### 788 789 def positionFromPoint(self, point): 790 """ 791 Public method to calculate the scintilla position from a point in the 792 window. 793 794 @param point point in the window (QPoint) 795 @return scintilla position (integer) or -1 to indicate, that the point 796 is not near any character 797 """ 798 return self.SendScintilla(QsciScintilla.SCI_POSITIONFROMPOINTCLOSE, 799 point.x(), point.y()) 800 801 def positionBefore(self, pos): 802 """ 803 Public method to get the position before the given position taking into 804 account multibyte characters. 805 806 @param pos position (integer) 807 @return position before the given one (integer) 808 """ 809 return self.SendScintilla(QsciScintilla.SCI_POSITIONBEFORE, pos) 810 811 def positionAfter(self, pos): 812 """ 813 Public method to get the position after the given position taking into 814 account multibyte characters. 815 816 @param pos position (integer) 817 @return position after the given one (integer) 818 """ 819 return self.SendScintilla(QsciScintilla.SCI_POSITIONAFTER, pos) 820 821 def lineEndPosition(self, line): 822 """ 823 Public method to determine the line end position of the given line. 824 825 @param line line number (integer) 826 @return position of the line end disregarding line end characters 827 (integer) 828 """ 829 return self.SendScintilla(QsciScintilla.SCI_GETLINEENDPOSITION, line) 830 831 def __doSearchTarget(self): 832 """ 833 Private method to perform the search in target. 834 835 @return flag indicating a successful search (boolean) 836 """ 837 if self.__targetSearchStart == self.__targetSearchEnd: 838 self.__targetSearchActive = False 839 return False 840 841 self.SendScintilla(QsciScintilla.SCI_SETTARGETSTART, 842 self.__targetSearchStart) 843 self.SendScintilla(QsciScintilla.SCI_SETTARGETEND, 844 self.__targetSearchEnd) 845 self.SendScintilla(QsciScintilla.SCI_SETSEARCHFLAGS, 846 self.__targetSearchFlags) 847 targetSearchExpr = self._encodeString(self.__targetSearchExpr) 848 pos = self.SendScintilla(QsciScintilla.SCI_SEARCHINTARGET, 849 len(targetSearchExpr), 850 targetSearchExpr) 851 852 if pos == -1: 853 self.__targetSearchActive = False 854 return False 855 856 targend = self.SendScintilla(QsciScintilla.SCI_GETTARGETEND) 857 self.__targetSearchStart = targend 858 859 return True 860 861 def getFoundTarget(self): 862 """ 863 Public method to get the recently found target. 864 865 @return found target as a tuple of starting position and target length 866 (integer, integer) 867 """ 868 if self.__targetSearchActive: 869 spos = self.SendScintilla(QsciScintilla.SCI_GETTARGETSTART) 870 epos = self.SendScintilla(QsciScintilla.SCI_GETTARGETEND) 871 return (spos, epos - spos) 872 else: 873 return (0, 0) 874 875 def findFirstTarget(self, expr_, re_, cs_, wo_, 876 begline=-1, begindex=-1, endline=-1, endindex=-1, 877 ws_=False, posix=False, cxx11=False): 878 """ 879 Public method to search in a specified range of text without 880 setting the selection. 881 882 @param expr_ search expression 883 @type str 884 @param re_ flag indicating a regular expression 885 @type bool 886 @param cs_ flag indicating a case sensitive search 887 @type bool 888 @param wo_ flag indicating a word only search 889 @type bool 890 @param begline line number to start from (-1 to indicate current 891 position) 892 @type int 893 @param begindex index to start from (-1 to indicate current position) 894 @type int 895 @param endline line number to stop at (-1 to indicate end of document) 896 @type int 897 @param endindex index number to stop at (-1 to indicate end of 898 document) 899 @type int 900 @param ws_ flag indicating a word start search (boolean) 901 @type bool 902 @param posix 903 @type bool 904 @param cxx11 905 @type bool 906 @return flag indicating a successful search 907 @rtype bool 908 """ 909 self.__targetSearchFlags = 0 910 if re_: 911 self.__targetSearchFlags |= QsciScintilla.SCFIND_REGEXP 912 if cs_: 913 self.__targetSearchFlags |= QsciScintilla.SCFIND_MATCHCASE 914 if wo_: 915 self.__targetSearchFlags |= QsciScintilla.SCFIND_WHOLEWORD 916 if ws_: 917 self.__targetSearchFlags |= QsciScintilla.SCFIND_WORDSTART 918 if posix: 919 self.__targetSearchFlags |= QsciScintilla.SCFIND_POSIX 920 with contextlib.suppress(AttributeError): 921 if cxx11: 922 self.__targetSearchFlags |= QsciScintilla.SCFIND_CXX11REGEX 923 # defined for QScintilla >= 2.11.0 924 925 if begline < 0 or begindex < 0: 926 self.__targetSearchStart = self.SendScintilla( 927 QsciScintilla.SCI_GETCURRENTPOS) 928 else: 929 self.__targetSearchStart = self.positionFromLineIndex( 930 begline, begindex) 931 932 if endline < 0 or endindex < 0: 933 self.__targetSearchEnd = self.SendScintilla( 934 QsciScintilla.SCI_GETTEXTLENGTH) 935 else: 936 self.__targetSearchEnd = self.positionFromLineIndex( 937 endline, endindex) 938 939 self.__targetSearchExpr = expr_ 940 941 if self.__targetSearchExpr: 942 self.__targetSearchActive = True 943 944 return self.__doSearchTarget() 945 946 return False 947 948 def findNextTarget(self): 949 """ 950 Public method to find the next occurrence in the target range. 951 952 @return flag indicating a successful search (boolean) 953 """ 954 if not self.__targetSearchActive: 955 return False 956 957 return self.__doSearchTarget() 958 959 def replaceTarget(self, replaceStr): 960 """ 961 Public method to replace the string found by the last search in target. 962 963 @param replaceStr replacement string or regexp (string) 964 """ 965 if not self.__targetSearchActive: 966 return 967 968 cmd = ( 969 QsciScintilla.SCI_REPLACETARGETRE 970 if self.__targetSearchFlags & QsciScintilla.SCFIND_REGEXP else 971 QsciScintilla.SCI_REPLACETARGET 972 ) 973 r = self._encodeString(replaceStr) 974 975 start = self.SendScintilla(QsciScintilla.SCI_GETTARGETSTART) 976 self.SendScintilla(cmd, len(r), r) 977 978 self.__targetSearchStart = start + len(r) 979 980 ########################################################################### 981 ## indicator handling methods 982 ########################################################################### 983 984 def indicatorDefine(self, indicator, style, color): 985 """ 986 Public method to define the appearance of an indicator. 987 988 @param indicator number of the indicator (integer, 989 QsciScintilla.INDIC_CONTAINER .. QsciScintilla.INDIC_MAX) 990 @param style style to be used for the indicator 991 (QsciScintilla.INDIC_PLAIN, QsciScintilla.INDIC_SQUIGGLE, 992 QsciScintilla.INDIC_TT, QsciScintilla.INDIC_DIAGONAL, 993 QsciScintilla.INDIC_STRIKE, QsciScintilla.INDIC_HIDDEN, 994 QsciScintilla.INDIC_BOX, QsciScintilla.INDIC_ROUNDBOX, 995 QsciScintilla.INDIC_STRAIGHTBOX, QsciScintilla.INDIC_FULLBOX, 996 QsciScintilla.INDIC_DASH, QsciScintilla.INDIC_DOTS, 997 QsciScintilla.INDIC_SQUIGGLELOW, QsciScintilla.INDIC_DOTBOX, 998 QsciScintilla.INDIC_GRADIENT, QsciScintilla.INDIC_GRADIENTCENTRE, 999 QsciScintilla.INDIC_SQUIGGLEPIXMAP, 1000 QsciScintilla.INDIC_COMPOSITIONTHICK, 1001 QsciScintilla.INDIC_COMPOSITIONTHIN, QsciScintilla.INDIC_TEXTFORE, 1002 QsciScintilla.INDIC_POINT, QsciScintilla.INDIC_POINTCHARACTER 1003 depending upon QScintilla version) 1004 @param color color to be used by the indicator (QColor) 1005 @exception ValueError the indicator or style are not valid 1006 """ 1007 if ( 1008 indicator < QsciScintilla.INDIC_CONTAINER or 1009 indicator > QsciScintilla.INDIC_MAX 1010 ): 1011 raise ValueError("indicator number out of range") 1012 1013 if ( 1014 style < QsciScintilla.INDIC_PLAIN or 1015 style > self.IndicatorStyleMax 1016 ): 1017 raise ValueError("style out of range") 1018 1019 self.SendScintilla(QsciScintilla.SCI_INDICSETSTYLE, indicator, style) 1020 self.SendScintilla(QsciScintilla.SCI_INDICSETFORE, indicator, color) 1021 with contextlib.suppress(AttributeError): 1022 self.SendScintilla(QsciScintilla.SCI_INDICSETALPHA, indicator, 1023 color.alpha()) 1024 if style in ( 1025 QsciScintilla.INDIC_ROUNDBOX, QsciScintilla.INDIC_STRAIGHTBOX, 1026 QsciScintilla.INDIC_DOTBOX, QsciScintilla.INDIC_FULLBOX, 1027 ): 1028 # set outline alpha less transparent 1029 self.SendScintilla(QsciScintilla.SCI_INDICSETOUTLINEALPHA, 1030 indicator, color.alpha() + 20) 1031 1032 def setCurrentIndicator(self, indicator): 1033 """ 1034 Public method to set the current indicator. 1035 1036 @param indicator number of the indicator (integer, 1037 QsciScintilla.INDIC_CONTAINER .. QsciScintilla.INDIC_MAX) 1038 @exception ValueError the indicator or style are not valid 1039 """ 1040 if ( 1041 indicator < QsciScintilla.INDIC_CONTAINER or 1042 indicator > QsciScintilla.INDIC_MAX 1043 ): 1044 raise ValueError("indicator number out of range") 1045 1046 self.SendScintilla(QsciScintilla.SCI_SETINDICATORCURRENT, indicator) 1047 1048 def setIndicatorRange(self, indicator, spos, length): 1049 """ 1050 Public method to set an indicator for the given range. 1051 1052 @param indicator number of the indicator (integer, 1053 QsciScintilla.INDIC_CONTAINER .. QsciScintilla.INDIC_MAX) 1054 @param spos position of the indicator start (integer) 1055 @param length length of the indicator (integer) 1056 """ 1057 self.setCurrentIndicator(indicator) 1058 self.SendScintilla(QsciScintilla.SCI_INDICATORFILLRANGE, spos, length) 1059 1060 def setIndicator(self, indicator, sline, sindex, eline, eindex): 1061 """ 1062 Public method to set an indicator for the given range. 1063 1064 @param indicator number of the indicator (integer, 1065 QsciScintilla.INDIC_CONTAINER .. QsciScintilla.INDIC_MAX) 1066 @param sline line number of the indicator start (integer) 1067 @param sindex index of the indicator start (integer) 1068 @param eline line number of the indicator end (integer) 1069 @param eindex index of the indicator end (integer) 1070 """ 1071 spos = self.positionFromLineIndex(sline, sindex) 1072 epos = self.positionFromLineIndex(eline, eindex) 1073 self.setIndicatorRange(indicator, spos, epos - spos) 1074 1075 def clearIndicatorRange(self, indicator, spos, length): 1076 """ 1077 Public method to clear an indicator for the given range. 1078 1079 @param indicator number of the indicator (integer, 1080 QsciScintilla.INDIC_CONTAINER .. QsciScintilla.INDIC_MAX) 1081 @param spos position of the indicator start (integer) 1082 @param length length of the indicator (integer) 1083 """ 1084 self.setCurrentIndicator(indicator) 1085 self.SendScintilla(QsciScintilla.SCI_INDICATORCLEARRANGE, spos, length) 1086 1087 def clearIndicator(self, indicator, sline, sindex, eline, eindex): 1088 """ 1089 Public method to clear an indicator for the given range. 1090 1091 @param indicator number of the indicator (integer, 1092 QsciScintilla.INDIC_CONTAINER .. QsciScintilla.INDIC_MAX) 1093 @param sline line number of the indicator start (integer) 1094 @param sindex index of the indicator start (integer) 1095 @param eline line number of the indicator end (integer) 1096 @param eindex index of the indicator end (integer) 1097 """ 1098 spos = self.positionFromLineIndex(sline, sindex) 1099 epos = self.positionFromLineIndex(eline, eindex) 1100 self.clearIndicatorRange(indicator, spos, epos - spos) 1101 1102 def clearAllIndicators(self, indicator): 1103 """ 1104 Public method to clear all occurrences of an indicator. 1105 1106 @param indicator number of the indicator (integer, 1107 QsciScintilla.INDIC_CONTAINER .. QsciScintilla.INDIC_MAX) 1108 """ 1109 self.clearIndicatorRange(indicator, 0, self.length()) 1110 1111 def hasIndicator(self, indicator, pos): 1112 """ 1113 Public method to test for the existence of an indicator. 1114 1115 @param indicator number of the indicator (integer, 1116 QsciScintilla.INDIC_CONTAINER .. QsciScintilla.INDIC_MAX) 1117 @param pos position to test (integer) 1118 @return flag indicating the existence of the indicator (boolean) 1119 """ 1120 res = self.SendScintilla(QsciScintilla.SCI_INDICATORVALUEAT, 1121 indicator, pos) 1122 return res 1123 1124 def showFindIndicator(self, sline, sindex, eline, eindex): 1125 """ 1126 Public method to show the find indicator for the given range. 1127 1128 @param sline line number of the indicator start (integer) 1129 @param sindex index of the indicator start (integer) 1130 @param eline line number of the indicator end (integer) 1131 @param eindex index of the indicator end (integer) 1132 """ 1133 if hasattr(QsciScintilla, "SCI_FINDINDICATORSHOW"): 1134 spos = self.positionFromLineIndex(sline, sindex) 1135 epos = self.positionFromLineIndex(eline, eindex) 1136 self.SendScintilla(QsciScintilla.SCI_FINDINDICATORSHOW, spos, epos) 1137 1138 def flashFindIndicator(self, sline, sindex, eline, eindex): 1139 """ 1140 Public method to flash the find indicator for the given range. 1141 1142 @param sline line number of the indicator start (integer) 1143 @param sindex index of the indicator start (integer) 1144 @param eline line number of the indicator end (integer) 1145 @param eindex index of the indicator end (integer) 1146 """ 1147 if hasattr(QsciScintilla, "SCI_FINDINDICATORFLASH"): 1148 spos = self.positionFromLineIndex(sline, sindex) 1149 epos = self.positionFromLineIndex(eline, eindex) 1150 self.SendScintilla(QsciScintilla.SCI_FINDINDICATORFLASH, 1151 spos, epos) 1152 1153 def hideFindIndicator(self): 1154 """ 1155 Public method to hide the find indicator. 1156 """ 1157 if hasattr(QsciScintilla, "SCI_FINDINDICATORHIDE"): 1158 self.SendScintilla(QsciScintilla.SCI_FINDINDICATORHIDE) 1159 1160 def getIndicatorStartPos(self, indicator, pos): 1161 """ 1162 Public method to get the start position of an indicator at a position. 1163 1164 @param indicator ID of the indicator (integer) 1165 @param pos position within the indicator (integer) 1166 @return start position of the indicator (integer) 1167 """ 1168 return self.SendScintilla(QsciScintilla.SCI_INDICATORSTART, 1169 indicator, pos) 1170 1171 def getIndicatorEndPos(self, indicator, pos): 1172 """ 1173 Public method to get the end position of an indicator at a position. 1174 1175 @param indicator ID of the indicator (integer) 1176 @param pos position within the indicator (integer) 1177 @return end position of the indicator (integer) 1178 """ 1179 return self.SendScintilla(QsciScintilla.SCI_INDICATOREND, 1180 indicator, pos) 1181 1182 def gotoPreviousIndicator(self, indicator, wrap): 1183 """ 1184 Public method to move the cursor to the previous position of an 1185 indicator. 1186 1187 This method ensures, that the position found is visible (i.e. unfolded 1188 and inside the visible range). The text containing the indicator is 1189 selected. 1190 1191 @param indicator ID of the indicator to search (integer) 1192 @param wrap flag indicating to wrap around at the beginning of the 1193 text (boolean) 1194 @return flag indicating if the indicator was found (boolean) 1195 """ 1196 pos = self.SendScintilla(QsciScintilla.SCI_GETCURRENTPOS) 1197 docLen = self.SendScintilla(QsciScintilla.SCI_GETTEXTLENGTH) 1198 isInIndicator = self.hasIndicator(indicator, pos) 1199 posStart = self.getIndicatorStartPos(indicator, pos) 1200 posEnd = self.getIndicatorEndPos(indicator, pos) 1201 1202 if posStart == 0 and posEnd == docLen - 1: 1203 # indicator does not exist 1204 return False 1205 1206 if posStart <= 0: 1207 if not wrap: 1208 return False 1209 1210 isInIndicator = self.hasIndicator(indicator, docLen - 1) 1211 posStart = self.getIndicatorStartPos(indicator, docLen - 1) 1212 1213 if isInIndicator: 1214 # get out of it 1215 posStart = self.getIndicatorStartPos(indicator, posStart - 1) 1216 if posStart <= 0: 1217 if not wrap: 1218 return False 1219 1220 posStart = self.getIndicatorStartPos(indicator, docLen - 1) 1221 1222 newPos = posStart - 1 1223 posStart = self.getIndicatorStartPos(indicator, newPos) 1224 posEnd = self.getIndicatorEndPos(indicator, newPos) 1225 1226 if self.hasIndicator(indicator, posStart): 1227 # found it 1228 line, index = self.lineIndexFromPosition(posEnd) 1229 self.ensureLineVisible(line) 1230 self.SendScintilla(QsciScintilla.SCI_SETSEL, posEnd, posStart) 1231 self.SendScintilla(QsciScintilla.SCI_SCROLLCARET) 1232 return True 1233 1234 return False 1235 1236 def gotoNextIndicator(self, indicator, wrap): 1237 """ 1238 Public method to move the cursor to the next position of an indicator. 1239 1240 This method ensures, that the position found is visible (i.e. unfolded 1241 and inside the visible range). The text containing the indicator is 1242 selected. 1243 1244 @param indicator ID of the indicator to search (integer) 1245 @param wrap flag indicating to wrap around at the beginning of the 1246 text (boolean) 1247 @return flag indicating if the indicator was found (boolean) 1248 """ 1249 pos = self.SendScintilla(QsciScintilla.SCI_GETCURRENTPOS) 1250 docLen = self.SendScintilla(QsciScintilla.SCI_GETTEXTLENGTH) 1251 isInIndicator = self.hasIndicator(indicator, pos) 1252 posStart = self.getIndicatorStartPos(indicator, pos) 1253 posEnd = self.getIndicatorEndPos(indicator, pos) 1254 1255 if posStart == 0 and posEnd == docLen - 1: 1256 # indicator does not exist 1257 return False 1258 1259 if posEnd >= docLen: 1260 if not wrap: 1261 return False 1262 1263 isInIndicator = self.hasIndicator(indicator, 0) 1264 posEnd = self.getIndicatorEndPos(indicator, 0) 1265 1266 if isInIndicator: 1267 # get out of it 1268 posEnd = self.getIndicatorEndPos(indicator, posEnd) 1269 if posEnd >= docLen: 1270 if not wrap: 1271 return False 1272 1273 posEnd = self.getIndicatorEndPos(indicator, 0) 1274 1275 newPos = posEnd + 1 1276 posStart = self.getIndicatorStartPos(indicator, newPos) 1277 posEnd = self.getIndicatorEndPos(indicator, newPos) 1278 1279 if self.hasIndicator(indicator, posStart): 1280 # found it 1281 line, index = self.lineIndexFromPosition(posEnd) 1282 self.ensureLineVisible(line) 1283 self.SendScintilla(QsciScintilla.SCI_SETSEL, posStart, posEnd) 1284 self.SendScintilla(QsciScintilla.SCI_SCROLLCARET) 1285 return True 1286 1287 return False 1288 1289 ########################################################################### 1290 ## methods to perform folding related stuff 1291 ########################################################################### 1292 1293 def __setFoldMarker(self, marknr, mark=QsciScintilla.SC_MARK_EMPTY): 1294 """ 1295 Private method to define a fold marker. 1296 1297 @param marknr marker number to define (integer) 1298 @param mark fold mark symbol to be used (integer) 1299 """ 1300 self.SendScintilla(QsciScintilla.SCI_MARKERDEFINE, marknr, mark) 1301 1302 if mark != QsciScintilla.SC_MARK_EMPTY: 1303 self.SendScintilla(QsciScintilla.SCI_MARKERSETFORE, 1304 marknr, QColor(Qt.GlobalColor.white)) 1305 self.SendScintilla(QsciScintilla.SCI_MARKERSETBACK, 1306 marknr, QColor(Qt.GlobalColor.black)) 1307 1308 def setFolding(self, style, margin=2): 1309 """ 1310 Public method to set the folding style and margin. 1311 1312 @param style folding style to set (integer) 1313 @param margin margin number (integer) 1314 """ 1315 if style < self.ArrowFoldStyle: 1316 super().setFolding(style, margin) 1317 else: 1318 super().setFolding( 1319 QsciScintilla.FoldStyle.PlainFoldStyle, margin) 1320 1321 if style == self.ArrowFoldStyle: 1322 self.__setFoldMarker(QsciScintilla.SC_MARKNUM_FOLDER, 1323 QsciScintilla.SC_MARK_ARROW) 1324 self.__setFoldMarker(QsciScintilla.SC_MARKNUM_FOLDEROPEN, 1325 QsciScintilla.SC_MARK_ARROWDOWN) 1326 self.__setFoldMarker(QsciScintilla.SC_MARKNUM_FOLDERSUB) 1327 self.__setFoldMarker(QsciScintilla.SC_MARKNUM_FOLDERTAIL) 1328 self.__setFoldMarker(QsciScintilla.SC_MARKNUM_FOLDEREND) 1329 self.__setFoldMarker(QsciScintilla.SC_MARKNUM_FOLDEROPENMID) 1330 self.__setFoldMarker(QsciScintilla.SC_MARKNUM_FOLDERMIDTAIL) 1331 elif style == self.ArrowTreeFoldStyle: 1332 self.__setFoldMarker(QsciScintilla.SC_MARKNUM_FOLDER, 1333 QsciScintilla.SC_MARK_ARROW) 1334 self.__setFoldMarker(QsciScintilla.SC_MARKNUM_FOLDEROPEN, 1335 QsciScintilla.SC_MARK_ARROWDOWN) 1336 self.__setFoldMarker(QsciScintilla.SC_MARKNUM_FOLDERSUB, 1337 QsciScintilla.SC_MARK_VLINE) 1338 self.__setFoldMarker(QsciScintilla.SC_MARKNUM_FOLDERTAIL, 1339 QsciScintilla.SC_MARK_LCORNER) 1340 self.__setFoldMarker(QsciScintilla.SC_MARKNUM_FOLDEREND, 1341 QsciScintilla.SC_MARK_ARROW) 1342 self.__setFoldMarker(QsciScintilla.SC_MARKNUM_FOLDEROPENMID, 1343 QsciScintilla.SC_MARK_ARROWDOWN) 1344 self.__setFoldMarker(QsciScintilla.SC_MARKNUM_FOLDERMIDTAIL, 1345 QsciScintilla.SC_MARK_TCORNER) 1346 1347 def setFoldMarkersColors(self, foreColor, backColor): 1348 """ 1349 Public method to set the foreground and background colors of the 1350 fold markers. 1351 1352 @param foreColor foreground color (QColor) 1353 @param backColor background color (QColor) 1354 """ 1355 self.SendScintilla(QsciScintilla.SCI_MARKERSETFORE, 1356 QsciScintilla.SC_MARKNUM_FOLDER, foreColor) 1357 self.SendScintilla(QsciScintilla.SCI_MARKERSETBACK, 1358 QsciScintilla.SC_MARKNUM_FOLDER, backColor) 1359 1360 self.SendScintilla(QsciScintilla.SCI_MARKERSETFORE, 1361 QsciScintilla.SC_MARKNUM_FOLDEROPEN, foreColor) 1362 self.SendScintilla(QsciScintilla.SCI_MARKERSETBACK, 1363 QsciScintilla.SC_MARKNUM_FOLDEROPEN, backColor) 1364 1365 self.SendScintilla(QsciScintilla.SCI_MARKERSETFORE, 1366 QsciScintilla.SC_MARKNUM_FOLDEROPENMID, foreColor) 1367 self.SendScintilla(QsciScintilla.SCI_MARKERSETBACK, 1368 QsciScintilla.SC_MARKNUM_FOLDEROPENMID, backColor) 1369 1370 self.SendScintilla(QsciScintilla.SCI_MARKERSETFORE, 1371 QsciScintilla.SC_MARKNUM_FOLDERSUB, foreColor) 1372 self.SendScintilla(QsciScintilla.SCI_MARKERSETBACK, 1373 QsciScintilla.SC_MARKNUM_FOLDERSUB, backColor) 1374 1375 self.SendScintilla(QsciScintilla.SCI_MARKERSETFORE, 1376 QsciScintilla.SC_MARKNUM_FOLDERTAIL, foreColor) 1377 self.SendScintilla(QsciScintilla.SCI_MARKERSETBACK, 1378 QsciScintilla.SC_MARKNUM_FOLDERTAIL, backColor) 1379 1380 self.SendScintilla(QsciScintilla.SCI_MARKERSETFORE, 1381 QsciScintilla.SC_MARKNUM_FOLDERMIDTAIL, foreColor) 1382 self.SendScintilla(QsciScintilla.SCI_MARKERSETBACK, 1383 QsciScintilla.SC_MARKNUM_FOLDERMIDTAIL, backColor) 1384 1385 self.SendScintilla(QsciScintilla.SCI_MARKERSETFORE, 1386 QsciScintilla.SC_MARKNUM_FOLDEREND, foreColor) 1387 self.SendScintilla(QsciScintilla.SCI_MARKERSETBACK, 1388 QsciScintilla.SC_MARKNUM_FOLDEREND, backColor) 1389 1390 def getVisibleLineFromDocLine(self, docLine): 1391 """ 1392 Public method to convert a document line number to a visible line 1393 number (i.e. respect folded lines and annotations). 1394 1395 @param docLine document line number to be converted 1396 @type int 1397 @return visible line number 1398 @rtype int 1399 """ 1400 return self.SendScintilla(QsciScintilla.SCI_VISIBLEFROMDOCLINE, 1401 docLine) 1402 1403 def getDocLineFromVisibleLine(self, displayLine): 1404 """ 1405 Public method to convert a visible line number to a document line 1406 number (i.e. respect folded lines and annotations). 1407 1408 @param displayLine display line number to be converted 1409 @type int 1410 @return document line number 1411 @rtype int 1412 """ 1413 return self.SendScintilla(QsciScintilla.SCI_DOCLINEFROMVISIBLE, 1414 displayLine) 1415 1416 ########################################################################### 1417 ## interface methods to the standard keyboard command set 1418 ########################################################################### 1419 1420 def clearKeys(self): 1421 """ 1422 Public method to clear the key commands. 1423 """ 1424 # call into the QsciCommandSet 1425 self.standardCommands().clearKeys() 1426 1427 def clearAlternateKeys(self): 1428 """ 1429 Public method to clear the alternate key commands. 1430 """ 1431 # call into the QsciCommandSet 1432 self.standardCommands().clearAlternateKeys() 1433 1434 ########################################################################### 1435 ## specialized event handlers 1436 ########################################################################### 1437 1438 def focusOutEvent(self, event): 1439 """ 1440 Protected method called when the editor loses focus. 1441 1442 @param event event object (QFocusEvent) 1443 """ 1444 if self.isListActive(): 1445 if event.reason() in [ 1446 Qt.FocusReason.ActiveWindowFocusReason, 1447 Qt.FocusReason.OtherFocusReason 1448 ]: 1449 aw = QApplication.activeWindow() 1450 if aw is None or aw.parent() is not self: 1451 self.cancelList() 1452 else: 1453 self.cancelList() 1454 1455 if self.isCallTipActive(): 1456 if event.reason() in [ 1457 Qt.FocusReason.ActiveWindowFocusReason, 1458 Qt.FocusReason.OtherFocusReason 1459 ]: 1460 aw = QApplication.activeWindow() 1461 if aw is None or aw.parent() is not self: 1462 self.SendScintilla(QsciScintilla.SCI_CALLTIPCANCEL) 1463 else: 1464 self.SendScintilla(QsciScintilla.SCI_CALLTIPCANCEL) 1465 1466 super().focusOutEvent(event) 1467 1468 def event(self, evt): 1469 """ 1470 Public method to handle events. 1471 1472 Note: We are not interested in the standard QsciScintilla event 1473 handling because we do it ourselves. 1474 1475 @param evt event object to handle (QEvent) 1476 @return result of the event handling (boolean) 1477 """ 1478 return QsciScintillaBase.event(self, evt) 1479 1480 ########################################################################### 1481 ## interface methods to the mini editor 1482 ########################################################################### 1483 1484 def getFileName(self): 1485 """ 1486 Public method to return the name of the file being displayed. 1487 1488 @return filename of the displayed file (string) 1489 """ 1490 p = self.parent() 1491 if p is None: 1492 return "" 1493 else: 1494 try: 1495 return p.getFileName() 1496 except AttributeError: 1497 return "" 1498 1499 ########################################################################### 1500 ## replacements for buggy methods 1501 ########################################################################### 1502 1503 def showUserList(self, listId, lst): 1504 """ 1505 Public method to show a user supplied list. 1506 1507 @param listId id of the list (integer) 1508 @param lst list to be show (list of strings) 1509 """ 1510 if listId <= 0: 1511 return 1512 1513 # Setup seperator for user lists 1514 self.SendScintilla( 1515 QsciScintilla.SCI_AUTOCSETSEPARATOR, ord(self.UserSeparator)) 1516 self.SendScintilla( 1517 QsciScintilla.SCI_USERLISTSHOW, listId, 1518 self._encodeString(self.UserSeparator.join(lst))) 1519 1520 self.updateUserListSize() 1521 1522 def autoCompleteFromDocument(self): 1523 """ 1524 Public method to resize list box after creation. 1525 """ 1526 super().autoCompleteFromDocument() 1527 self.updateUserListSize() 1528 1529 def autoCompleteFromAPIs(self): 1530 """ 1531 Public method to resize list box after creation. 1532 """ 1533 super().autoCompleteFromAPIs() 1534 self.updateUserListSize() 1535 1536 def autoCompleteFromAll(self): 1537 """ 1538 Public method to resize list box after creation. 1539 """ 1540 super().autoCompleteFromAll() 1541 self.updateUserListSize() 1542 1543 ########################################################################### 1544 ## work-around for buggy behavior 1545 ########################################################################### 1546 1547 def updateUserListSize(self): 1548 """ 1549 Public method to resize the completion list to fit with contents. 1550 """ 1551 children = self.findChildren(QListWidget) 1552 if children: 1553 userListWidget = children[-1] 1554 hScrollbar = userListWidget.horizontalScrollBar() 1555 if hScrollbar.isVisible(): 1556 hScrollbarHeight = hScrollbar.sizeHint().height() 1557 1558 geom = userListWidget.geometry() 1559 geom.setHeight(geom.height() + hScrollbarHeight) 1560 1561 charPos = self.SendScintilla(QsciScintilla.SCI_GETCURRENTPOS) 1562 currentYPos = self.SendScintilla( 1563 QsciScintilla.SCI_POINTYFROMPOSITION, 0, charPos) 1564 if geom.y() < currentYPos: 1565 geom.setY(geom.y() - hScrollbarHeight) 1566 moveY = True 1567 else: 1568 moveY = False 1569 1570 userListWidget.setGeometry(geom) 1571 if moveY: 1572 userListWidget.move(geom.x(), geom.y() - hScrollbarHeight) 1573 1574 def __completionListSelected(self, listId, txt): 1575 """ 1576 Private slot to handle the selection from the completion list. 1577 1578 Note: This works around an issue of some window managers taking 1579 focus away from the application when clicked inside a completion 1580 list but not giving it back when an item is selected via a 1581 double-click. 1582 1583 @param listId the ID of the user list (integer) 1584 @param txt the selected text (string) 1585 """ 1586 self.activateWindow() 1587 1588 def updateVerticalScrollBar(self): 1589 """ 1590 Public method to update the vertical scroll bar to reflect the 1591 additional lines added by annotations. 1592 """ 1593 # Workaround because Scintilla.Redraw isn't implemented 1594 self.SendScintilla(QsciScintilla.SCI_SETVSCROLLBAR, 0) 1595 self.SendScintilla(QsciScintilla.SCI_SETVSCROLLBAR, 1) 1596 1597 ########################################################################### 1598 ## utility methods 1599 ########################################################################### 1600 1601 def _encodeString(self, string): 1602 """ 1603 Protected method to encode a string depending on the current mode. 1604 1605 @param string string to be encoded (str) 1606 @return encoded string (bytes) 1607 """ 1608 if isinstance(string, bytes): 1609 return string 1610 else: 1611 if self.isUtf8(): 1612 return string.encode("utf-8") 1613 else: 1614 return string.encode("latin-1") 1615 1616 ########################################################################### 1617 ## methods to implement workarounds for broken things 1618 ########################################################################### 1619 1620 def positionFromLineIndex(self, line, index): 1621 """ 1622 Public method to convert line and index to an absolute position. 1623 1624 @param line line number (integer) 1625 @param index index number (integer) 1626 @return absolute position in the editor (integer) 1627 """ 1628 pos = self.SendScintilla(QsciScintilla.SCI_POSITIONFROMLINE, line) 1629 return pos + index 1630 1631 def lineIndexFromPosition(self, pos): 1632 """ 1633 Public method to convert an absolute position to line and index. 1634 1635 @param pos absolute position in the editor (integer) 1636 @return tuple of line number (integer) and index number (integer) 1637 """ 1638 lin = self.SendScintilla(QsciScintilla.SCI_LINEFROMPOSITION, pos) 1639 linpos = self.SendScintilla( 1640 QsciScintilla.SCI_POSITIONFROMLINE, lin) 1641 return lin, pos - linpos 1642 1643 ########################################################################### 1644 ## methods below have been added to QScintilla starting with version 2.5 1645 ########################################################################### 1646 1647 if "contractedFolds" not in QsciScintilla.__dict__: 1648 def contractedFolds(self): 1649 """ 1650 Public method to get a list of line numbers of collapsed folds. 1651 1652 @return list of line numbers of folded lines (list of integer) 1653 """ 1654 line = 0 1655 folds = [] 1656 maxline = self.lines() 1657 while line < maxline: 1658 if self.foldHeaderAt(line) and not self.foldExpandedAt(line): 1659 folds.append(line) 1660 line += 1 1661 return folds 1662 1663 if "setContractedFolds" not in QsciScintilla.__dict__: 1664 def setContractedFolds(self, folds): 1665 """ 1666 Public method to set a list of line numbers of collapsed folds. 1667 1668 @param folds list of line numbers of folded lines (list of integer) 1669 """ 1670 for line in folds: 1671 self.foldLine(line) 1672 1673 ######################################################################### 1674 ## Methods below are missing from QScintilla. 1675 ######################################################################### 1676 1677 if "setWrapStartIndent" not in QsciScintilla.__dict__: 1678 def setWrapStartIndent(self, indent): 1679 """ 1680 Public method to set a the amount of characters wrapped sublines 1681 shall be indented. 1682 1683 @param indent amount of characters to indent 1684 @type int 1685 """ 1686 self.SendScintilla(QsciScintilla.SCI_SETWRAPSTARTINDENT, indent) 1687 1688 if "getGlobalCursorPosition" not in QsciScintilla.__dict__: 1689 def getGlobalCursorPosition(self): 1690 """ 1691 Public method to determine the point of the cursor. 1692 1693 @return point of the cursor 1694 @rtype QPoint 1695 """ 1696 pos = self.currentPosition() 1697 x = self.SendScintilla(QsciScintilla.SCI_POINTXFROMPOSITION, 1698 0, pos) 1699 y = self.SendScintilla(QsciScintilla.SCI_POINTYFROMPOSITION, 1700 0, pos) 1701 return QPoint(x, y) 1702 1703## ######################################################################### 1704## ## Methods below have been added to QScintilla starting with version 2.x. 1705## ######################################################################### 1706## 1707## if "newMethod" not in QsciScintilla.__dict__: 1708## def newMethod(self, param): 1709## """ 1710## Public method to do something. 1711## 1712## @param param parameter for method 1713## """ 1714## pass 1715## 1716