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