Ejemplo n.º 1
0
class ConsoleFontStyle(object):
    def __init__(self, foregroundcolor, backgroundcolor, 
                 bold, italic, underline):
        self.foregroundcolor = foregroundcolor
        self.backgroundcolor = backgroundcolor
        self.bold = bold
        self.italic = italic
        self.underline = underline
        self.format = None
        
    def apply_style(self, font, light_background, is_default):
        self.format = QTextCharFormat()
        self.format.setFont(font)
        foreground = QColor(self.foregroundcolor)
        if not light_background and is_default:
            inverse_color(foreground)
        self.format.setForeground(foreground)
        background = QColor(self.backgroundcolor)
        if not light_background:
            inverse_color(background)
        self.format.setBackground(background)
        font = self.format.font()
        font.setBold(self.bold)
        font.setItalic(self.italic)
        font.setUnderline(self.underline)
        self.format.setFont(font)
Ejemplo n.º 2
0
    def setDateBackground(self, color):
        brush = QBrush(color)

        frmt = QTextCharFormat()
        frmt.setBackground(brush)

        return frmt
Ejemplo n.º 3
0
def css2fmt(d, f=None):
    """Convert a css dictionary to a QTextCharFormat."""
    if f is None:
        f = QTextCharFormat()
    v = d.get('font-style')
    if v:
        f.setFontItalic(v in ('oblique', 'italic'))
    v = d.get('font-weight')
    if v:
        if v == 'bold':
            f.setFontWeight(QFont.Bold)
        elif v == 'normal':
            f.setFontWeight(QFont.Normal)
        elif v.isdigit():
            f.setFontWeight(int(v) / 10)
    v = d.get('color')
    if v:
        f.setForeground(QColor(v))
    v = d.get('background')
    if v:
        f.setBackground(QColor(v))
    v = d.get('text-decoration')
    if v:
        f.setFontUnderline(v == 'underline')
    v = d.get('text-decoration-color')
    if v:
        f.setUnderlineColor(QColor(v))
    return f
Ejemplo n.º 4
0
class ViewHighlighter(widgets.arbitraryhighlighter.ArbitraryHighlighter, plugin.Plugin):
    def __init__(self, view):
        super(ViewHighlighter, self).__init__(view)
        self._cursorFormat = QTextCharFormat()
        self._cursorFormat.setProperty(QTextFormat.FullWidthSelection, True)
        app.settingsChanged.connect(self.readSettings)
        self.readSettings()
        bookmarks.bookmarks(view.document()).marksChanged.connect(self.updateMarkedLines)
        self.updateMarkedLines()
        view.cursorPositionChanged.connect(self.updateCursor)
        view.installEventFilter(self)

    def updateMarkedLines(self):
        """Called when something changes in the bookmarks."""
        for type, marks in bookmarks.bookmarks(self.parent().document()).marks().items():
            self.highlight(type, marks, -1)
    
    def eventFilter(self, view, ev):
        if ev.type() in (QEvent.FocusIn, QEvent.FocusOut):
            self.updateCursor(view)
        return False
    
    def updateCursor(self, view=None):
        """Called when the textCursor has moved. Highlights the current line.
        
        If view is None (the default), our parent() is assumed to be the
        view. The eventFilter() method calls us with the view, this is
        done because the event filter is sometimes called very late in
        the destructor phase, when our parent is possibly not valid
        anymore.
        
        """
        if view is None:
			view = self.parent()
        # highlight current line
        color = QColor(self._baseColors['current'])
        color.setAlpha(200 if view.hasFocus() else 100)
        self._cursorFormat.setBackground(color)
        cursor = view.textCursor()
        cursor.clearSelection()
        self.highlight(self._cursorFormat, [cursor], 0)
        
    def readSettings(self):
        data = textformats.formatData('editor')
        self._baseColors = data.baseColors
        self.updateCursor()
        self.reload()

    def textFormat(self, name):
        """(Internal) Returns a QTextCharFormat setup according to the preferences.
        
        For bookmarks and the current line, FullWidthSelection is automatically enabled.
        
        """
        f = QTextCharFormat()
        f.setBackground(self._baseColors[name])
        if name in ('current', 'mark', 'error'):
            f.setProperty(QTextFormat.FullWidthSelection, True)
        return f
Ejemplo n.º 5
0
 def write_info(self, text):
     old_format = self.currentCharFormat();
     format = QTextCharFormat()
     format.setForeground(QColor("white"))
     format.setBackground(QColor("darkgreen"))
     self.setCurrentCharFormat(format);
     self.appendPlainText("Done.\n");
     self.setCurrentCharFormat(old_format);
Ejemplo n.º 6
0
class Matcher(widgets.matcher.Matcher):
    def __init__(self, edit):
        super(Matcher, self).__init__(edit)
        self.readSettings()
        app.settingsChanged.connect(self.readSettings)
    
    def readSettings(self):
        self.format = QTextCharFormat()
        self.format.setBackground(textformats.formatData('editor').baseColors['match'])
Ejemplo n.º 7
0
 def mkformat(self, fg=None, bg=None, bold=False):
     fmt = QTextCharFormat()
     if fg:
         fmt.setForeground(fg)
     if bg:
         fmt.setBackground(bg)
     if bold:
         fmt.setFontWeight(QFont.Bold)
     return fmt
Ejemplo n.º 8
0
class QtANSIEscapeCodeHandler(ANSIEscapeCodeHandler):
    def __init__(self):
        ANSIEscapeCodeHandler.__init__(self)
        self.base_format = None
        self.current_format = None
        
    def set_base_format(self, base_format):
        self.base_format = base_format
        
    def get_format(self):
        return self.current_format
        
    def set_style(self):
        """
        Set font style with the following attributes:
        'foreground_color', 'background_color', 'italic', 'bold' and 'underline'
        """
        if self.current_format is None:
            assert self.base_format is not None
            self.current_format = QTextCharFormat(self.base_format)
        # Foreground color
        if self.foreground_color is None:
            qcolor = self.base_format.foreground()
        else:
            cstr = self.ANSI_COLORS[self.foreground_color-30][self.intensity]
            qcolor = QColor(cstr)
        self.current_format.setForeground(qcolor)        
        # Background color
        if self.background_color is None:
            qcolor = self.base_format.background()
        else:
            cstr = self.ANSI_COLORS[self.background_color-40][self.intensity]
            qcolor = QColor(cstr)
        self.current_format.setBackground(qcolor)
        
        font = self.current_format.font()
        # Italic
        if self.italic is None:
            italic = self.base_format.fontItalic()
        else:
            italic = self.italic
        font.setItalic(italic)
        # Bold
        if self.bold is None:
            bold = self.base_format.font().bold()
        else:
            bold = self.bold
        font.setBold(bold)
        # Underline
        if self.underline is None:
            underline = self.base_format.font().underline()
        else:
            underline = self.underline
        font.setUnderline(underline)
        self.current_format.setFont(font)
Ejemplo n.º 9
0
 def textFormat(self, name):
     """(Internal) Returns a QTextCharFormat setup according to the preferences.
     
     For bookmarks and the current line, FullWidthSelection is automatically enabled.
     
     """
     f = QTextCharFormat()
     f.setBackground(self._baseColors[name])
     if name in ('current', 'mark', 'error'):
         f.setProperty(QTextFormat.FullWidthSelection, True)
     return f
Ejemplo n.º 10
0
def _makeFormat(bg=None, fg=None, bold=False):
    """Make QTextCharFormat with gived parameters
    """
    format = QTextCharFormat()
    if bg is not None:
        format.setBackground(QColor(bg))
    if fg is not None:
        format.setForeground(QColor(fg))
    if bold:
        format.setFontWeight(QFont.Bold)

    return format
Ejemplo n.º 11
0
    def formatConverterFunction(format):
        if format == qutepart.syntax.TextFormat():
            return None  # Do not apply default format. Performance optimization

        qtFormat = QTextCharFormat()
        qtFormat.setForeground(QBrush(QColor(format.color)))
        qtFormat.setBackground(QBrush(QColor(format.background)))
        qtFormat.setFontItalic(format.italic)
        qtFormat.setFontWeight(QFont.Bold if format.bold else QFont.Normal)
        qtFormat.setFontUnderline(format.underline)
        qtFormat.setFontStrikeOut(format.strikeOut)

        return qtFormat
Ejemplo n.º 12
0
def getFormat(**kwargs):
	"""
	Returns a `QTextCharFormat <http://doc.qt.nokia.com/qtextcharformat.html>`_ format.
	
	:param \*\*kwargs: Format settings.
	:type \*\*kwargs: dict
	:return: Format.
	:rtype: QTextCharFormat
	"""

	settings = foundations.dataStructures.Structure(**{"format" : QTextCharFormat(),
								"backgroundColor" : None,
								"color" : None,
								"fontWeight" : None,
								"fontPointSize" : None,
								"italic" : False})
	settings.update(kwargs)

	format = QTextCharFormat(settings.format)

	settings.backgroundColor and format.setBackground(settings.backgroundColor)
	settings.color and format.setForeground(settings.color)
	settings.fontWeight and format.setFontWeight(settings.fontWeight)
	settings.fontPointSize and format.setFontPointSize(settings.fontPointSize)
	settings.italic and	format.setFontItalic(True)

	return format
Ejemplo n.º 13
0
def eltToStyle(elt):
    fmt = QTextCharFormat()
    if elt.get("bold"):
        fmt.setFontWeight(QFont.Bold if toBool(elt.get("bold")) else QFont.Normal)
    if elt.get("italic"):
        fmt.setFontItalic(toBool(elt.get("italic")))
    if elt.get("underline"):
        fmt.setFontUnderline(toBool(elt.get("underline")))
    if elt.get("textColor"):
        fmt.setForeground(QColor(elt.get("textColor")))
    if elt.get("backgroundColor"):
        fmt.setBackground(QColor(elt.get("backgroundColor")))
    if elt.get("underlineColor"):
        fmt.setUnderlineColor(QColor(elt.get("underlineColor")))

    return fmt
Ejemplo n.º 14
0
def eltToStyle(elt):
    fmt = QTextCharFormat()
    if elt.get('bold'):
        fmt.setFontWeight(QFont.Bold if toBool(elt.get('bold')) else QFont.Normal)
    if elt.get('italic'):
        fmt.setFontItalic(toBool(elt.get('italic')))
    if elt.get('underline'):
        fmt.setFontUnderline(toBool(elt.get('underline')))
    if elt.get('textColor'):
        fmt.setForeground(QColor(elt.get('textColor')))
    if elt.get('backgroundColor'):
        fmt.setBackground(QColor(elt.get('backgroundColor')))
    if elt.get('underlineColor'):
        fmt.setUnderlineColor(QColor(elt.get('underlineColor')))
        
    return fmt
Ejemplo n.º 15
0
def html_copy(cursor, scheme="editor", number_lines=False):
    """Return a new QTextDocument with highlighting set as HTML textcharformats.
    
    The cursor is a cursor of a document.Document instance. If the cursor 
    has a selection, only the selection is put in the new document.
    
    If number_lines is True, line numbers are added.
    
    """
    data = textformats.formatData(scheme)
    doc = QTextDocument()
    doc.setDefaultFont(data.font)
    doc.setPlainText(cursor.document().toPlainText())
    if metainfo.info(cursor.document()).highlighting:
        highlight(doc, mapping(data), ly.lex.state(documentinfo.mode(cursor.document())))
    if cursor.hasSelection():
        # cut out not selected text
        start, end = cursor.selectionStart(), cursor.selectionEnd()
        cur1 = QTextCursor(doc)
        cur1.setPosition(start, QTextCursor.KeepAnchor)
        cur2 = QTextCursor(doc)
        cur2.setPosition(end)
        cur2.movePosition(QTextCursor.End, QTextCursor.KeepAnchor)
        cur2.removeSelectedText()
        cur1.removeSelectedText()
    if number_lines:
        c = QTextCursor(doc)
        f = QTextCharFormat()
        f.setBackground(QColor("#eeeeee"))
        if cursor.hasSelection():
            num = cursor.document().findBlock(cursor.selectionStart()).blockNumber() + 1
            last = cursor.document().findBlock(cursor.selectionEnd())
        else:
            num = 1
            last = cursor.document().lastBlock()
        lastnum = last.blockNumber() + 1
        padding = len(format(lastnum))
        block = doc.firstBlock()
        while block.isValid():
            c.setPosition(block.position())
            c.setCharFormat(f)
            c.insertText("{0:>{1}d} ".format(num, padding))
            block = block.next()
            num += 1
    return doc
Ejemplo n.º 16
0
 def __init__(self, *args, **kwargs):
     Formatter.__init__(self)
     self.data=[]
     self.styles={}
     for token, style in self.style:
         qtf=QTextCharFormat()
         if style['color']:
             qtf.setForeground(self.hex2QColor(style['color']))
         if style['bgcolor']:
             qtf.setBackground(self.hex2QColor(style['bgcolor']))
         if style['bold']:
             qtf.setFontWeight(QFont.Bold)
         if style['italic']:
             qtf.setFontItalic(True)
         if style['underline']:
             qtf.setFontUnderline(True)
         self.styles[str(token)]=qtf
     return
Ejemplo n.º 17
0
    def test(self):
        #QTextCharFormat char_fmt;
        char_fmt = QTextCharFormat()

        char_fmt.setBackground(QColor(150, 150, 250));
        self.cursor.insertText(self.tr("Ì1\n"),char_fmt);
        #QImage img;
        img = QImage()
        ok = img.load("./123.png")
        self.doc.addResource(QTextDocument.ImageResource, QUrl("myimage"), img)

        imageFormat = QTextImageFormat()
        imageFormat.setName("myimage")
        imageFormat.setWidth(10)
        imageFormat.setHeight(10)
        self.cursor.insertImage(imageFormat)

        #self.cursor.insertImage("myimage");
        self.cursor.insertText(self.tr("Æåñòêèé äèñê\n"),char_fmt);
Ejemplo n.º 18
0
def format(color, style=''):
    """Return a QTextCharFormat with the given attributes.
    """
    _color = QColor()
    _color.setNamedColor(color)

    _format = QTextCharFormat()
    _format.setForeground(_color)
    _format.setFontPointSize(12)
    if 'bold' in style:
        _format.setFontWeight(QFont.Bold)
        _format.setFontUnderline(True)
    if 'italic' in style:
        _format.setFontItalic(True)
        bc = QColor()
        bc.setNamedColor("yellow")
        _format.setBackground(bc)
		

    return _format
Ejemplo n.º 19
0
 def highlight_background(self, marker_meta_data, color=None):
     '''
     highlights the background of the marker. Use metadata.color_name if color is None
     '''
     cursor = self.ui.plainTextEditCode.textCursor()
     cursor.setPosition(0, QTextCursor.MoveAnchor); #Moves the cursor to the beginning of the document
     #Now moves the cursor to the start_line
     cursor.movePosition(QTextCursor.Down, QTextCursor.MoveAnchor, marker_meta_data.start_block)
     #select everything till the end line
     assert(marker_meta_data.end_block >= marker_meta_data.start_block)
     move_dist = marker_meta_data.end_block - marker_meta_data.start_block
     cursor.movePosition(QTextCursor.Down, QTextCursor.KeepAnchor, move_dist)
     cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor)
     format = QTextCharFormat()
     background_color = color
     if background_color is None:
         background_color = QColor(marker_meta_data.color_name)
     background_color.setAlpha(70)
     brush = QBrush(background_color)
     format.setBackground(brush)
     cursor.mergeCharFormat(format)
Ejemplo n.º 20
0
def update_char_format(baseformat, color=None, background=None, weight=None, italic=None, underline=None, font=None):
    """
    Return a copy of `baseformat` :class:`QTextCharFormat` with
    updated color, weight, background and font properties.

    """
    charformat = QTextCharFormat(baseformat)

    if color is not None:
        charformat.setForeground(color)

    if background is not None:
        charformat.setBackground(background)

    if font is not None:
        charformat.setFont(font)
    else:
        font = update_font(baseformat.font(), weight, italic, underline)
        charformat.setFont(font)

    return charformat
def update_char_format(baseformat, color=None, background=None, weight=None,
                       italic=None, underline=None, font=None):
    """
    Return a copy of `baseformat` :class:`QTextCharFormat` with
    updated color, weight, background and font properties.

    """
    charformat = QTextCharFormat(baseformat)

    if color is not None:
        charformat.setForeground(color)

    if background is not None:
        charformat.setBackground(background)

    if font is not None:
        charformat.setFont(font)
    else:
        font = update_font(baseformat.font(), weight, italic, underline)
        charformat.setFont(font)

    return charformat
Ejemplo n.º 22
0
	def format(self, family=None, color=None, background=None, bold=None, \
		italic=None):

		"""
		Creates a QTextCharFormat with the given attributes.

		Keyword arguments:
		family		--	A font family or None. (default=None)
		color		--	A color value or None. (default=None)
		background	--	A backgrund color value or None. (default=None)
		bold		--	True, False, or None. (default=None)
		italic		--	True, False, or None. (default=None)

		Returns:
		A QTextCharFormat.
		"""

		_format = QTextCharFormat()
		if family != None:
			_format.setFontFamily(family)
		if color != None:
			_color = QColor()
			_color.setNamedColor(color)
			_format.setForeground(_color)
		if background != None:
			_background = QColor()
			_background.setNamedColor(background)
			_format.setBackground(_background)
		if bold != None:
			if bold:
				_format.setFontWeight(QFont.Bold)
			else:
				_format.setFontWeight(QFont.Normal)
		if italic != None:
			if italic:
				_format.setFontItalic(True)
			else:
				_format.setFontItalic(False)
		return _format
Ejemplo n.º 23
0
class wordHighLighter(QSyntaxHighlighter):
    def __init__(self, parent=None):
        global reWord
        super(wordHighLighter, self).__init__(parent)
        # store a local reference to the global regex
        self.wordMatch = reWord
        # Initialize text formats to apply to words from various lists.
        #  - Scanno candidates get a light lilac background.
        self.scannoFormat = QTextCharFormat()
        self.scannoFormat.setBackground(QBrush(QColor("#EBD7E6")))
        # Set the style for misspelt words. We underline in red using the
        # well-known wavy red underline, the same on all platforms.
        self.misspeltFormat = QTextCharFormat()
        self.misspeltFormat.setUnderlineStyle(QTextCharFormat.WaveUnderline)
        self.misspeltFormat.setUnderlineColor(QColor("red"))

    # The linked QPlainTextEdit calls this function for every text line in the
    # whole bloody document when highlighting is turned on via the View menu,
    # at least to judge by the hang-time. Later it only calls us to look at a
    # line as it changes in editing. Anyway it behooves us to be as quick as
    # possible. We don't actually check spelling, we just use the flag that
    # was set when the last spellcheck was done. In a new document there may
    # be no word census yet.
    # Note that either one or both of MC.scannoHiliteSwitch or IMC.spellingHiliteSwitch
    # are ON, or else we are called against an empty document -- see setHighlight below.
    def highlightBlock(self, text):
        # find each word in the text and test it against our lists
        i = self.wordMatch.indexIn(text, 0)  # first word if any
        while i >= 0:
            l = self.wordMatch.matchedLength()
            w = self.wordMatch.cap(0)  # word as qstring
            if IMC.scannoHiliteSwitch:  # we are checking for scannos:
                if IMC.scannoList.check(unicode(w)):
                    self.setFormat(i, l, self.scannoFormat)
            if IMC.spellingHiliteSwitch:  # we are checking spelling:
                if (IMC.wordCensus.getFlag(w) & IMC.WordMisspelt):
                    self.setFormat(i, l, self.misspeltFormat)
            i = self.wordMatch.indexIn(text, i + l)  # advance to next word
Ejemplo n.º 24
0
class wordHighLighter(QSyntaxHighlighter):
    def __init__(self, parent=None):
        global reWord
        super(wordHighLighter, self).__init__(parent)
        # store a local reference to the global regex
        self.wordMatch = reWord
        # Initialize text formats to apply to words from various lists.
        #  - Scanno candidates get a light lilac background.
        self.scannoFormat = QTextCharFormat()
        self.scannoFormat.setBackground(QBrush(QColor("#EBD7E6")))
        # Set the style for misspelt words. We underline in red using the
        # well-known wavy red underline, the same on all platforms.
        self.misspeltFormat = QTextCharFormat()
        self.misspeltFormat.setUnderlineStyle(QTextCharFormat.WaveUnderline)
        self.misspeltFormat.setUnderlineColor(QColor("red"))

    # The linked QPlainTextEdit calls this function for every text line in the
    # whole bloody document when highlighting is turned on via the View menu,
    # at least to judge by the hang-time. Later it only calls us to look at a
    # line as it changes in editing. Anyway it behooves us to be as quick as
    # possible. We don't actually check spelling, we just use the flag that
    # was set when the last spellcheck was done. In a new document there may
    # be no word census yet.
    # Note that either one or both of MC.scannoHiliteSwitch or IMC.spellingHiliteSwitch
    # are ON, or else we are called against an empty document -- see setHighlight below.
    def highlightBlock(self, text):
        # find each word in the text and test it against our lists
        i = self.wordMatch.indexIn(text,0) # first word if any
        while i >= 0:
            l = self.wordMatch.matchedLength()
            w = self.wordMatch.cap(0) # word as qstring
            if IMC.scannoHiliteSwitch: # we are checking for scannos:
                if IMC.scannoList.check(unicode(w)):
                    self.setFormat(i,l,self.scannoFormat)
            if IMC.spellingHiliteSwitch: # we are checking spelling:
                if (IMC.wordCensus.getFlag(w) & IMC.WordMisspelt):
                    self.setFormat(i,l,self.misspeltFormat)
            i = self.wordMatch.indexIn(text,i+l) # advance to next word
Ejemplo n.º 25
0
def mk_txt_fmt(derive=None, fg=None, bg=None, bold=False, ul=None, ul_color=None):
    text_format = QTextCharFormat(derive) if derive is not None else QTextCharFormat()

    if fg is not None:
        text_format.setForeground(QBrush(parse_qcolor(fg)))
    if bg is not None:
        text_format.setBackground(QBrush(parse_qcolor(bg)))

    if bold:
        text_format.setFontWeight(QFont.Bold)

    if ul is not None:
        if ul is True:
            text_format.setUnderlineStyle(QTextCharFormat.SingleUnderline)
        elif ul in UNDERLINE_STYLES:
            text_format.setUnderlineStyle(UNDERLINE_STYLES[ul])
        else:
            raise ValueError("Unsupported underline style: '{0}'".format(ul))

    if ul_color is not None:
        text_format.setUnderlineColor(parse_qcolor(ul_color))

    return text_format
Ejemplo n.º 26
0
    def highlightBlock(self, content):
        block_pos = self.currentBlock().position()
        content = unicode(content)

        to_word = self.to_word

        f = QTextCharFormat()
        f.setBackground(HIGHLIGHT_COLOR)

        for new_word in self.parent().new_words:
            word = new_word.word_content
            word_len = len(word)
            start = -1
            while True:
                start = content.find(word, start + 1)
                if start < 0:
                    break

                if whole_word(content, start, word_len):
                    self.setFormat(start, word_len, f)
                    to_word.append((block_pos + start, word_len, content[start : start + word_len]))

        to_word.sort()
Ejemplo n.º 27
0
 def textFormat(self):
     """Returns our settings as a QTextCharFormat object."""
     f = QTextCharFormat()
     if self._tristate:
         value = lambda checkbox: [False, None, True][checkbox.checkState()]
     else:
         value = lambda checkbox: checkbox.isChecked()
     res = value(self.bold)
     if res is not None:
         f.setFontWeight(QFont.Bold if res else QFont.Normal)
     res = value(self.italic)
     if res is not None:
         f.setFontItalic(res)
     res = value(self.underline)
     if res is not None:
         f.setFontUnderline(res)
     if self.textColor.color().isValid():
         f.setForeground(self.textColor.color())
     if self.backgroundColor.color().isValid():
         f.setBackground(self.backgroundColor.color())
     if self.underlineColor.color().isValid():
         f.setUnderlineColor(self.underlineColor.color())
     return f
Ejemplo n.º 28
0
    def __check_brackets(self):
        left, right = QTextEdit.ExtraSelection(), QTextEdit.ExtraSelection()
        cursor = self.textCursor()
        block = cursor.block()
        data = block.userData()
        previous, _next = None, None

        if data is not None:
            position = cursor.position()
            block_pos = cursor.block().position()
            paren = data.paren
            n = len(paren)

            for k in range(0, n):
                if paren[k].position == position - block_pos or paren[k].position == position - block_pos - 1:
                    previous = paren[k].position + block_pos
                    if paren[k].character == "(":
                        _next = self.__match_left(block, paren[k].character, k + 1, 0)
                    elif paren[k].character == ")":
                        _next = self.__match_right(block, paren[k].character, k, 0)

        if _next is not None and _next > 0:
            if previous is not None and previous > 0:
                _format = QTextCharFormat()

                cursor.setPosition(previous)
                cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor)

                _format.setForeground(Qt.blue)
                _format.setBackground(Qt.white)
                left.format = _format
                left.cursor = cursor

                cursor.setPosition(_next)
                cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor)

                _format.setForeground(Qt.white)
                _format.setBackground(Qt.blue)
                right.format = _format
                right.cursor = cursor

                return left, right

        elif previous is not None:
            _format = QTextCharFormat()

            cursor.setPosition(previous)
            cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor)

            _format.setForeground(Qt.white)
            _format.setBackground(Qt.red)
            left.format = _format
            left.cursor = cursor
            return (left,)
Ejemplo n.º 29
0
 def textFormat(self, name):
     f = QTextCharFormat()
     f.setBackground(self._baseColors[name])
     return f
Ejemplo n.º 30
0
class OutputWidget(QPlainTextEdit):

    """Widget to handle the output of the running process."""

    def __init__(self, parent):
        super(OutputWidget, self).__init__(parent)
        self._parent = parent
        self.setReadOnly(True)
        #traceback pattern
        self.patLink = re.compile(r'(\s)*File "(.*?)", line \d.+')
        #formats
        font = settings.FONT
        self.plain_format = QTextCharFormat()
        self.plain_format.setFont(font)
        self.plain_format.setForeground(
            QBrush(QColor(resources.CUSTOM_SCHEME.get(
                "editor-text", resources.COLOR_SCHEME["editor-text"]))))
        self.error_format = QTextCharFormat()
        self.error_format.setFont(font)
        self.error_format.setAnchor(True)
        self.error_format.setForeground(QColor(resources.CUSTOM_SCHEME.get(
            "pep8-underline", resources.COLOR_SCHEME["pep8-underline"])))
        self.error_format.setBackground(QColor(resources.CUSTOM_SCHEME.get(
            "error-underline", resources.COLOR_SCHEME["error-underline"])))
        self.error_format.setToolTip(self.tr("Click to show the source"))
        self.error_format2 = QTextCharFormat()
        self.error_format2.setAnchor(True)
        self.error_format2.setFont(font)
        self.error_format2.setForeground(
            QBrush(
                QColor(resources.CUSTOM_SCHEME.get(
                    "error-underline",
                    resources.COLOR_SCHEME["error-underline"]))))

        self.connect(self, SIGNAL("blockCountChanged(int)"),
                     lambda: self.moveCursor(QTextCursor.End))

        css = 'QPlainTextEdit {color: %s; background-color: %s;' \
            'selection-color: %s; selection-background-color: %s;}' \
            % (resources.CUSTOM_SCHEME.get('editor-text',
               resources.COLOR_SCHEME['editor-text']),
               resources.CUSTOM_SCHEME.get('editor-background',
               resources.COLOR_SCHEME['editor-background']),
               resources.CUSTOM_SCHEME.get('editor-selection-color',
               resources.COLOR_SCHEME['editor-selection-color']),
               resources.CUSTOM_SCHEME.get('editor-selection-background',
               resources.COLOR_SCHEME['editor-selection-background']))
        self.setStyleSheet(css)

    def mousePressEvent(self, event):
        """
        When the execution fail, allow to press the links in the traceback,
        to go to the line when the error occur.
        """
        QPlainTextEdit.mousePressEvent(self, event)
        self.go_to_error(event)

    def refresh_output(self):
        """Read the output buffer from the process and append the text."""
        #we should decode the bytes!
        currentProcess = self._parent.currentProcess
        text = currentProcess.readAllStandardOutput().data().decode('utf8')
        self.textCursor().insertText(text, self.plain_format)

    def refresh_error(self):
        """Read the error buffer from the process and append the text."""
        #we should decode the bytes!
        cursor = self.textCursor()
        currentProcess = self._parent.currentProcess
        text = currentProcess.readAllStandardError().data().decode('utf8')
        text_lines = text.split('\n')
        for t in text_lines:
            cursor.insertBlock()
            if self.patLink.match(t):
                cursor.insertText(t, self.error_format)
            else:
                cursor.insertText(t, self.error_format2)

    def go_to_error(self, event):
        """Resolve the link and take the user to the error line."""
        cursor = self.cursorForPosition(event.pos())
        text = cursor.block().text()
        if self.patLink.match(text):
            file_path, lineno = self._parse_traceback(text)
            main_container = IDE.get_service('main_container')
            if main_container:
                main_container.open_file(
                    file_path,
                    cursorPosition=int(lineno) - 1,
                    positionIsLineNumber=True)

    def _parse_traceback(self, text):
        """
        Parse a line of python traceback and returns
        a tuple with (file_name, lineno)
        """
        file_word_index = text.find('File')
        comma_min_index = text.find(',')
        comma_max_index = text.rfind(',')
        file_name = text[file_word_index + 6:comma_min_index - 1]
        lineno = text[comma_min_index + 7:comma_max_index]
        return (file_name, lineno)

    def contextMenuEvent(self, event):
        """Show a context menu for the Plain Text widget."""
        popup_menu = self.createStandardContextMenu()

        menuOutput = QMenu(self.tr("Output"))
        cleanAction = menuOutput.addAction(self.tr("Clean"))
        popup_menu.insertSeparator(popup_menu.actions()[0])
        popup_menu.insertMenu(popup_menu.actions()[0], menuOutput)

        # This is a hack because if we leave the widget text empty
        # it throw a violent segmentation fault in start_process
        self.connect(cleanAction, SIGNAL("triggered()"),
                     lambda: self.setPlainText('\n\n'))

        popup_menu.exec_(event.globalPos())
Ejemplo n.º 31
0
    def check_brackets(self):
        left, right = QTextEdit.ExtraSelection(),\
                      QTextEdit.ExtraSelection()

        cursor = self.textCursor()
        block = cursor.block()
        data = block.userData()
        previous, next = None, None

        if data is not None:
            position = cursor.position()
            block_position = cursor.block().position()
            braces = data.braces
            N = len(braces)

            for k in range(0, N):
                if braces[k].position == position - block_position or\
                   braces[k].position == position - block_position - 1:
                    previous = braces[k].position + block_position
                    if braces[k].character in ['{', '(', '[']:
                        next = self.match_left(block, braces[k].character,
                                               k + 1, 0)
                    elif braces[k].character in ['}', ')', ']']:
                        next = self.match_right(block, braces[k].character, k,
                                                0)
#                    if next is None:
#                        next = -1
        if (next is not None and next > 0) \
            and (previous is not None and previous > 0):

            format = QTextCharFormat()

            cursor.setPosition(previous)
            cursor.movePosition(QTextCursor.NextCharacter,
                                QTextCursor.KeepAnchor)

            format.setForeground(QColor('white'))
            format.setBackground(QColor('blue'))
            left.format = format
            left.cursor = cursor

            cursor.setPosition(next)
            cursor.movePosition(QTextCursor.NextCharacter,
                                QTextCursor.KeepAnchor)

            format.setForeground(QColor('white'))
            format.setBackground(QColor('blue'))
            right.format = format
            right.cursor = cursor

            return left, right

        elif previous is not None:
            format = QTextCharFormat()

            cursor.setPosition(previous)
            cursor.movePosition(QTextCursor.NextCharacter,
                                QTextCursor.KeepAnchor)

            format.setForeground(QColor('white'))
            format.setBackground(QColor('red'))
            left.format = format
            left.cursor = cursor
            return (left, )
        elif next is not None:
            format = QTextCharFormat()

            cursor.setPosition(next)
            cursor.movePosition(QTextCursor.NextCharacter,
                                QTextCursor.KeepAnchor)

            format.setForeground(QColor('white'))
            format.setBackground(QColor('red'))
            left.format = format
            left.cursor = cursor
            return (left, )
Ejemplo n.º 32
0
    def check_brackets(self,nada):
        left, right = QTextEdit.ExtraSelection(),\
                      QTextEdit.ExtraSelection()

        cursor = self.textCursor()
        block = cursor.block()
        data = block.userData()
        #~ print data
        previous, next = None, None

        if data is not None:
            position = cursor.position()
            block_position = cursor.block().position()
            braces = data.braces
            N = len(braces)
            #~ print N

            for k in range(0, N):
                if braces[k].position == position - block_position or\
                   braces[k].position == position - block_position - 1:
                    previous = braces[k].position + block_position
                    #~ print previous
                    if braces[k].character in ['{', '(', '[']:
                        next = self.match_left(block, braces[k].character, k + 1, 0)
                        
                        #~ print next
                    elif braces[k].character in ['}', ')', ']']:
                        next = self.match_right(block,braces[k].character,k, 0)
                        if nada == True:
							return next
                        #~ print block,braces[k].character,k,next
#                    if next is None:
#                        next = -1
		#~ print previous,next
        if (next is not None and next > 0) \
            and (previous is not None and previous > 0):

            format = QTextCharFormat()

            cursor.setPosition(previous)
            cursor.movePosition(QTextCursor.NextCharacter,
                                QTextCursor.KeepAnchor)
			
            format.setForeground(QColor('blue'))
            format.setBackground(QColor('white'))
            #~ format.setFontWeight(QFont.Bold)
            #~ self.mergeCurrentCharFormat(format)
            #~ cursor.mergeCharFormat(format)
            left.format = format
            left.cursor = cursor

            cursor.setPosition(next)
            cursor.movePosition(QTextCursor.NextCharacter,
                                QTextCursor.KeepAnchor)
            #~ cf=self.textCursor().charFormat()
            #~ cf.setFontWeight(QFont.Bold)
            #~ cursor.setCharFormat(cf);
            format.setForeground(QColor('blue'))
            format.setBackground(QColor('white'))
            #~ format.setFont(QFont("Courier"))
            #~ cursor.mergeCharFormat(format)
            right.format = format
            right.cursor = cursor

            return left, right

        elif previous is not None:
            format = QTextCharFormat()

            cursor.setPosition(previous)
            cursor.movePosition(QTextCursor.NextCharacter,
                                QTextCursor.KeepAnchor)

            format.setForeground(QColor('red'))
            format.setBackground(QColor('white'))
            left.format = format
            left.cursor = cursor
            return (left,)
        elif next is not None:
            format = QTextCharFormat()

            cursor.setPosition(next)
            cursor.movePosition(QTextCursor.NextCharacter,
                                QTextCursor.KeepAnchor)

            format.setForeground(QColor('red'))
            format.setBackground(QColor('white'))
            left.format = format
            left.cursor = cursor
            return (left,)
Ejemplo n.º 33
0
class KeywordHighlighter(QSyntaxHighlighter):
    def __init__(self, document):
        QSyntaxHighlighter.__init__(self, document)

        self.clb = ConfigurationLineBuilder(ErtKeywords())


        self.comment_format = QTextCharFormat()
        self.comment_format.setForeground(QColor(0, 128, 0))
        self.comment_format.setFontItalic(True)

        self.keyword_format = QTextCharFormat()
        self.keyword_format.setForeground(QColor(200, 100, 0))
        # self.keyword_format.setFontWeight(QFont.Bold)

        self.error_format = QTextCharFormat()
        # self.error_format.setForeground(QColor(255, 0, 0))
        self.error_format.setUnderlineStyle(QTextCharFormat.WaveUnderline)
        self.error_format.setUnderlineColor(QColor(255, 0, 0))

        self.search_format = QTextCharFormat()
        self.search_format.setBackground(QColor(220, 220, 220))

        self.builtin_format = QTextCharFormat()
        self.builtin_format.setForeground(QColor(0, 170, 227))

        self.search_string = ""


    def formatKeyword(self, keyword, validation_status):
        assert isinstance(keyword, Keyword)
        if keyword.hasKeywordDefinition():
            keyword_format = QTextCharFormat(self.keyword_format)

            if not validation_status:
                keyword_format.merge(self.error_format)

            self.formatToken(keyword, keyword_format)
        else:
            self.formatToken(keyword, self.error_format)


    def highlightBlock(self, complete_block):
        try:
            block = unicode(complete_block)
        except NameError:
            block = complete_block

        self.clb.processLine(block)


        if self.clb.hasComment():
            self.setFormat(self.clb.commentIndex(), len(block) - self.clb.commentIndex(), self.comment_format)

        if not self.clb.hasConfigurationLine():
            count = len(block)

            if self.clb.hasComment():
                count = self.clb.commentIndex()

            self.setFormat(0, count, self.error_format)


        if self.clb.hasConfigurationLine():
            cl = self.clb.configurationLine()
            self.setCurrentBlockUserData(ConfigurationLineUserData(cl))

            self.formatKeyword(cl.keyword(), cl.validationStatusForToken(cl.keyword()))

            arguments = cl.arguments()

            for argument in arguments:
                if not argument.hasArgumentDefinition():
                    pass

                elif argument.argumentDefinition().isBuiltIn():
                    self.formatToken(argument, self.builtin_format)

                if not cl.validationStatusForToken(argument):
                    self.formatToken(argument, self.error_format)


        if self.search_string != "":
            for match in re.finditer("(%s)" % self.search_string, complete_block):
                self.setFormat(match.start(1), match.end(1) - match.start(1), self.search_format)


    def setSearchString(self, string):
        try:
            if self.search_string != unicode(string):
                self.search_string = unicode(string)
                self.rehighlight()
        except NameError:
            if self.search_string != string:
                self.search_string = string
                self.rehighlight()


    def formatToken(self, token, highlight_format):
        self.setFormat(token.fromIndex(), token.count(), highlight_format)
class DiffsMenu(QtGui.QDialog):
    def __init__(self, parent=None):
        super(DiffsMenu, self).__init__(parent)

        self.ui = Ui_Diffs()
        self.ui.setupUi(self)

        self.ui.actionCopyPath = QtGui.QAction("Copy path",
                                               None,
                                               triggered=self.copyPath)
        self.ui.treeResults.addAction(self.ui.actionCopyPath)

        self.folder1 = None
        self.folder2 = None
        self.files = None
        self.files_nodupes = None
        self.files_missing = None

        self.saved_diffs = {}

        self.format1 = QTextCharFormat()
        self.format1.setBackground(QColor(255, 224, 224))

        self.format2 = QTextCharFormat()
        self.format2.setBackground(QColor(224, 240, 255))

        self.menu_name = "Diffs"

        self.format_plain = QTextCharFormat()

    ##############################################################################
    ### @fn   copyPath()
    ### @desc Copies the path of the selected node to the clipboard.
    ##############################################################################
    def copyPath(self):
        node = self.ui.treeResults.currentItem()

        if not node == None:
            text = "{%s}" % tree.tree_item_to_path(node)

            clipboard = QApplication.clipboard()
            clipboard.setText(text)

    ##############################################################################
    ### @fn   set_folders()
    ### @desc Set the two folders to be compared.
    ##############################################################################
    def set_folders(self, folder1, folder2, files=None):
        if files == None:
            files1 = list_all_files(folder1)
            files2 = list_all_files(folder2)

            files = set()

            # Get rid of our folder paths so we're working on generic filenames
            # that can be used with either folder.
            files.update([
                file[len(folder1) + 1:] for file in files1
                if file[-4:] == ".txt"
            ])
            files.update([
                file[len(folder2) + 1:] for file in files2
                if file[-4:] == ".txt"
            ])

        self.ui.lblDir1.setText(folder1)
        self.ui.lblDir2.setText(folder2)

        self.folder1 = folder1
        self.folder2 = folder2
        self.files = set(files)
        self.files_nodupes = None
        self.files_missing = None

        self.saved_diffs = {}

        self.show_files()

    ##############################################################################
    ### @fn   show_files()
    ### @desc Shows the list of files.
    ##############################################################################
    def show_files(self):

        # If we're not showing identical files, then go through the list
        # and find and remove everything we don't want to see.
        if not self.ui.chkShowSame.isChecked():
            if self.files_nodupes == None:
                self.files_nodupes = set()

                for file in self.files:
                    file1 = os.path.join(self.folder1, file)
                    file2 = os.path.join(self.folder2, file)

                    if not os.path.isfile(file1) or not os.path.isfile(file2):
                        self.files_nodupes.add(file)
                        continue

                    text1 = text_files.load_text(file1)
                    text2 = text_files.load_text(file2)

                    if not text1 == text2:
                        self.files_nodupes.add(file)

            files = self.files_nodupes

        else:
            files = self.files

        # If we're not showing files not present in both directories,
        # go through the list and strip them out.
        if not self.ui.chkNotBoth.isChecked():
            if self.files_missing == None:
                self.files_missing = set()

                for file in self.files:
                    file1 = os.path.join(self.folder1, file)
                    file2 = os.path.join(self.folder2, file)

                    if not os.path.isfile(file1) or not os.path.isfile(file2):
                        self.files_missing.add(file)

            files = files - self.files_missing

        self.ui.treeResults.clear()
        self.ui.treeResults.setHeaderLabel("Results (%d)" % len(files))

        if len(files) > 0:
            tree_items = []

            for file in files:
                file = os.path.normpath(file)
                file = tree.consolidate_path(file)
                tree_item = tree.path_to_tree(file)
                tree_items.append(tree_item)

            tree_items = tree.consolidate_tree_items(tree_items)

            for item in tree_items:
                self.ui.treeResults.addTopLevelItem(item)

            self.ui.treeResults.expandAll()

    ##############################################################################
    ### @fn   changedSelection()
    ### @desc Triggered when the user selects something in the tree.
    ##############################################################################
    def changedSelection(self, current, prev):
        if current == None or current.childCount() != 0:
            return

        file = common.qt_to_unicode(current.text(0))
        path = tree.tree_item_to_path(current.parent())
        self.setWindowTitle("%s - %s" %
                            (self.menu_name, os.path.join(path, file)))
        path = dir_tools.expand_dir(path)

        file = os.path.join(path, file)
        file1 = os.path.join(self.folder1, file)
        file2 = os.path.join(self.folder2, file)

        if not os.path.isfile(file1):
            script1 = ScriptFile()
        else:
            script1 = ScriptFile(file1)

        if not os.path.isfile(file2):
            script2 = ScriptFile()
        else:
            script2 = ScriptFile(file2)

        # So we can loop this shit.
        to_diff = [
            # Text 1              Text 2              Text Box 1              Text Box 2
            (script1.translated, script2.translated, self.ui.txtTranslated1,
             self.ui.txtTranslated2),
            (script1.original, script2.original, self.ui.txtOriginal1,
             self.ui.txtOriginal2),
            (script1.comments, script2.comments, self.ui.txtComments1,
             self.ui.txtComments2),
        ]

        # Save us a little bit of time recalculating.
        if file in self.saved_diffs:
            diffs = self.saved_diffs[file]
        else:
            diffs = [None] * len(to_diff)

        for i, (text1, text2, box1, box2) in enumerate(to_diff):

            if diffs[i] == None:
                diffs[i] = DIFFER.diff_main(text1, text2)
                DIFFER.diff_cleanupSemantic(diffs[i])

            box1.setPlainText(text1)
            box2.setPlainText(text2)

            highlight1, highlight2 = parse_diffs(diffs[i])

            cursor1 = box1.textCursor()
            cursor2 = box2.textCursor()

            cursor1.select(QTextCursor.Document)
            cursor2.select(QTextCursor.Document)
            cursor1.setCharFormat(self.format_plain)
            cursor2.setCharFormat(self.format_plain)

            cursor1.movePosition(QTextCursor.Start)
            cursor2.movePosition(QTextCursor.Start)

            for pos, length in highlight1:
                cursor1.setPosition(pos, QTextCursor.MoveAnchor)
                cursor1.setPosition(pos + length, QTextCursor.KeepAnchor)
                cursor1.setCharFormat(self.format1)

            cursor1.movePosition(QTextCursor.Start)

            for pos, length in highlight2:
                cursor2.setPosition(pos, QTextCursor.MoveAnchor)
                cursor2.setPosition(pos + length, QTextCursor.KeepAnchor)
                cursor2.setCharFormat(self.format2)

            cursor2.movePosition(QTextCursor.Start)

            box1.setTextCursor(cursor1)
            box2.setTextCursor(cursor2)

        # for i, (text1, text2, box1, box2) in enumerate(to_diff):
        self.saved_diffs[file] = diffs
Ejemplo n.º 35
0
class Qutepart(QPlainTextEdit):
    '''Qutepart is based on QPlainTextEdit, and you can use QPlainTextEdit methods,
    if you don't see some functionality here.

    **Text**

    ``text`` attribute holds current text. It may be read and written.::

        qpart.text = readFile()
        saveFile(qpart.text)

    This attribute always returns text, separated with ``\\n``. Use ``textForSaving()`` for get original text.

    It is recommended to use ``lines`` attribute whenever possible,
    because access to ``text`` might require long time on big files.
    Attribute is cached, only first read access after text has been changed in slow.

    **Selected text**

    ``selectedText`` attribute holds selected text. It may be read and written.
    Write operation replaces selection with new text. If nothing is selected - just inserts text::

        print qpart.selectedText  # print selection
        qpart.selectedText = 'new text'  # replace selection

    **Text lines**

    ``lines`` attribute, which represents text as list-of-strings like object
    and allows to modify it. Examples::

        qpart.lines[0]  # get the first line of the text
        qpart.lines[-1]  # get the last line of the text
        qpart.lines[2] = 'new text'  # replace 3rd line value with 'new text'
        qpart.lines[1:4]  # get 3 lines of text starting from the second line as list of strings
        qpart.lines[1:4] = ['new line 2', 'new line3', 'new line 4']  # replace value of 3 lines
        del qpart.lines[3]  # delete 4th line
        del qpart.lines[3:5]  # delete lines 4, 5, 6

        len(qpart.lines)  # get line count

        qpart.lines.append('new line')  # append new line to the end
        qpart.lines.insert(1, 'new line')  # insert new line before line 1

        print qpart.lines  # print all text as list of strings

        # iterate over lines.
        for lineText in qpart.lines:
            doSomething(lineText)

        qpart.lines = ['one', 'thow', 'three']  # replace whole text

    **Position and selection**

    * ``cursorPosition`` - cursor position as ``(line, column)``. Lines are numerated from zero. If column is set to ``None`` - cursor will be placed before first non-whitespace character. If line or column is bigger, than actual file, cursor will be placed to the last line, to the last column
    * ``absCursorPosition`` - cursor position as offset from the beginning of text.
    * ``selectedPosition`` - selection coordinates as ``((startLine, startCol), (cursorLine, cursorCol))``.
    * ``absSelectedPosition`` - selection coordinates as ``(startPosition, cursorPosition)`` where position is offset from the beginning of text.
    Rectangular selection is not available via API currently.

    **EOL, indentation, edge**

    * ``eol`` - End Of Line character. Supported values are ``\\n``, ``\\r``, ``\\r\\n``. See comments for ``textForSaving()``
    * ``indentWidth`` - Width of ``Tab`` character, and width of one indentation level. Default is ``4``.
    * ``indentUseTabs`` - If True, ``Tab`` character inserts ``\\t``, otherwise - spaces. Default is ``False``.
    * ``lineLengthEdge`` - If not ``None`` - maximal allowed line width (i.e. 80 chars). Longer lines are marked with red (see ``lineLengthEdgeColor``) line. Default is ``None``.
    * ``lineLengthEdgeColor`` - Color of line length edge line. Default is red.

    **Visible white spaces**

    * ``drawIncorrectIndentation`` - Draw trailing whitespaces, tabs if text is indented with spaces, spaces if text is indented with tabs. Default is ``True``. Doesn't have any effect if ``drawAnyWhitespace`` is ``True``.
    * ``drawAnyWhitespace`` - Draw trailing and other whitespaces, used as indentation. Default is ``False``.

    **Autocompletion**

    Qutepart supports autocompletion, based on document contents.
    It is enabled, if ``completionEnabled`` is ``True``.
    ``completionThreshold`` is count of typed symbols, after which completion is shown.

    ** Linters support **
    * ``lintMarks`` Linter messages as {lineNumber: (type, text)} dictionary. Cleared on any edit operation. Type is one of `Qutepart.LINT_ERROR, Qutepart.LINT_WARNING, Qutepart.LINT_NOTE)

    **Actions**

    Component contains list of actions (QAction instances).
    Actions can be insered to some menu, a shortcut and an icon can be configured.

    Bookmarks:

    * ``toggleBookmarkAction`` - Set/Clear bookmark on current block
    * ``nextBookmarkAction`` - Jump to next bookmark
    * ``prevBookmarkAction`` - Jump to previous bookmark

    Scroll:

    * ``scrollUpAction`` - Scroll viewport Up
    * ``scrollDownAction`` - Scroll viewport Down
    * ``selectAndScrollUpAction`` - Select 1 line Up and scroll
    * ``selectAndScrollDownAction`` - Select 1 line Down and scroll

    Indentation:

    * ``increaseIndentAction`` - Increase indentation by 1 level
    * ``decreaseIndentAction`` - Decrease indentation by 1 level
    * ``autoIndentLineAction`` - Autoindent line
    * ``indentWithSpaceAction`` - Indent all selected lines by 1 space symbol
    * ``unIndentWithSpaceAction`` - Unindent all selected lines by 1 space symbol

    Lines:

    * ``moveLineUpAction`` - Move line Up
    * ``moveLineDownAction`` - Move line Down
    * ``deleteLineAction`` - Delete line
    * ``copyLineAction`` - Copy line
    * ``pasteLineAction`` - Paste line
    * ``cutLineAction`` - Cut line
    * ``duplicateLineAction`` - Duplicate line

    Other:
    * ``undoAction`` - Undo
    * ``redoAction`` - Redo
    * ``invokeCompletionAction`` - Invoke completion
    * ``printAction`` - Print file

    **Text modification and Undo/Redo**

    Sometimes, it is required to make few text modifications, which are Undo-Redoble as atomic operation.
    i.e. you want to indent (insert indentation) few lines of text, but user shall be able to
    Undo it in one step. In this case, you can use Qutepart as a context manager.::

        with qpart:
            qpart.modifySomeText()
            qpart.modifyOtherText()

    Nested atomic operations are joined in one operation

    **Signals**

    * ``userWarning(text)``` Warning, which shall be shown to the user on status bar. I.e. 'Rectangular selection area is too big'
    * ``languageChanged(langName)``` Language has changed. See also ``language()``
    * ``indentWidthChanged(int)`` Indentation width changed. See also ``indentWidth``
    * ``indentUseTabsChanged(bool)`` Indentation uses tab property changed. See also ``indentUseTabs``
    * ``eolChanged(eol)`` EOL mode changed. See also ``eol``.

    **Public methods**
    '''

    userWarning = pyqtSignal(unicode)
    languageChanged = pyqtSignal(unicode)
    indentWidthChanged = pyqtSignal(int)
    indentUseTabsChanged = pyqtSignal(bool)
    eolChanged = pyqtSignal(unicode)

    LINT_ERROR = 'e'
    LINT_WARNING = 'w'
    LINT_NOTE = 'n'

    _DEFAULT_EOL = '\n'

    _DEFAULT_COMPLETION_THRESHOLD = 3
    _DEFAULT_COMPLETION_ENABLED = True

    _globalSyntaxManager = SyntaxManager()

    def __init__(self, *args):
        QPlainTextEdit.__init__(self, *args)

        # toPlainText() takes a lot of time on long texts, therefore it is cached
        self._cachedText = None

        self._eol = self._DEFAULT_EOL
        self._indenter = Indenter(self)
        self.lineLengthEdge = None
        self.lineLengthEdgeColor = Qt.red
        self._atomicModificationDepth = 0

        self.drawIncorrectIndentation = True
        self.drawAnyWhitespace = False

        self._rectangularSelection = RectangularSelection(self)
        """Sometimes color themes will be supported.
        Now black on white is hardcoded in the highlighters.
        Hardcode same palette for not highlighted text
        """
        palette = self.palette()
        palette.setColor(QPalette.Base, QColor('#ffffff'))
        palette.setColor(QPalette.Text, QColor('#000000'))
        self.setPalette(palette)

        self._highlighter = None
        self._bracketHighlighter = BracketHighlighter()

        self._lines = Lines(self)

        self.completionThreshold = self._DEFAULT_COMPLETION_THRESHOLD
        self.completionEnabled = self._DEFAULT_COMPLETION_ENABLED
        self._completer = Completer(self)

        self._initActions()

        self._lineNumberArea = qutepart.sideareas.LineNumberArea(self)
        self._countCache = (-1, -1)
        self._markArea = qutepart.sideareas.MarkArea(self)

        self._bookmarks = qutepart.bookmarks.Bookmarks(self, self._markArea)

        self._userExtraSelections = [
        ]  # we draw bracket highlighting, current line and extra selections by user
        self._userExtraSelectionFormat = QTextCharFormat()
        self._userExtraSelectionFormat.setBackground(QBrush(QColor('#ffee00')))

        self._lintMarks = {}

        self.blockCountChanged.connect(self._updateLineNumberAreaWidth)
        self.updateRequest.connect(self._updateSideAreas)
        self.cursorPositionChanged.connect(self._updateExtraSelections)
        self.textChanged.connect(self._dropUserExtraSelections)
        self.textChanged.connect(self._resetCachedText)
        self.textChanged.connect(self._clearLintMarks)

        fontFamilies = {'Windows': 'Courier New', 'Darwin': 'Menlo'}
        fontFamily = fontFamilies.get(platform.system(), 'Monospace')
        self.setFont(QFont(fontFamily))

        self._updateLineNumberAreaWidth(0)
        self._updateExtraSelections()

    def _initActions(self):
        """Init shortcuts for text editing
        """
        def createAction(text, shortcut, slot, iconFileName=None):
            """Create QAction with given parameters and add to the widget
            """
            action = QAction(text, self)
            if iconFileName is not None:
                action.setIcon(QIcon(getIconPath(iconFileName)))

            keySeq = shortcut if isinstance(
                shortcut, QKeySequence) else QKeySequence(shortcut)
            action.setShortcut(keySeq)
            action.setShortcutContext(Qt.WidgetShortcut)
            action.triggered.connect(slot)

            self.addAction(action)

            return action

        # scrolling
        self.scrollUpAction = createAction(
            'Scroll up', 'Ctrl+Up', lambda: self._onShortcutScroll(down=False),
            'up.png')
        self.scrollDownAction = createAction(
            'Scroll down', 'Ctrl+Down',
            lambda: self._onShortcutScroll(down=True), 'down.png')
        self.selectAndScrollUpAction = createAction(
            'Select and scroll Up', 'Ctrl+Shift+Up',
            lambda: self._onShortcutSelectAndScroll(down=False))
        self.selectAndScrollDownAction = createAction(
            'Select and scroll Down', 'Ctrl+Shift+Down',
            lambda: self._onShortcutSelectAndScroll(down=True))

        # indentation
        self.increaseIndentAction = createAction('Increase indentation', 'Tab',
                                                 self._onShortcutIndent,
                                                 'indent.png')
        self.decreaseIndentAction = createAction(
            'Decrease indentation', 'Shift+Tab', lambda: self._indenter.
            onChangeSelectedBlocksIndent(increase=False), 'unindent.png')
        self.autoIndentLineAction = createAction(
            'Autoindent line', 'Ctrl+I', self._indenter.onAutoIndentTriggered)
        self.indentWithSpaceAction = createAction(
            'Indent with 1 space', 'Shift+Space', lambda: self._indenter.
            onChangeSelectedBlocksIndent(increase=True, withSpace=True))
        self.unIndentWithSpaceAction = createAction(
            'Unindent with 1 space', 'Shift+Backspace', lambda: self._indenter.
            onChangeSelectedBlocksIndent(increase=False, withSpace=True))

        # editing
        self.undoAction = createAction('Undo', QKeySequence.Undo, self.undo,
                                       'undo.png')
        self.redoAction = createAction('Redo', QKeySequence.Redo, self.redo,
                                       'redo.png')

        self.moveLineUpAction = createAction(
            'Move line up', 'Alt+Up',
            lambda: self._onShortcutMoveLine(down=False), 'up.png')
        self.moveLineDownAction = createAction(
            'Move line down', 'Alt+Down',
            lambda: self._onShortcutMoveLine(down=True), 'down.png')
        self.deleteLineAction = createAction('Delete line', 'Alt+Del',
                                             self._onShortcutDeleteLine,
                                             'deleted.png')
        self.copyLineAction = createAction('Copy line', 'Alt+C',
                                           self._onShortcutCopyLine,
                                           'copy.png')
        self.pasteLineAction = createAction('Paste line', 'Alt+V',
                                            self._onShortcutPasteLine,
                                            'paste.png')
        self.cutLineAction = createAction('Cut line', 'Alt+X',
                                          self._onShortcutCutLine, 'cut.png')
        self.duplicateLineAction = createAction('Duplicate line', 'Alt+D',
                                                self._onShortcutDuplicateLine)
        self.invokeCompletionAction = createAction(
            'Invoke completion', 'Ctrl+Space',
            self._completer.invokeCompletion)

        # other
        self.printAction = createAction('Print', 'Ctrl+P',
                                        self._onShortcutPrint, 'print.png')

    def __enter__(self):
        """Context management method.
        Begin atomic modification
        """
        self._atomicModificationDepth = self._atomicModificationDepth + 1
        if self._atomicModificationDepth == 1:
            self.textCursor().beginEditBlock()

    def __exit__(self, exc_type, exc_value, traceback):
        """Context management method.
        End atomic modification
        """
        self._atomicModificationDepth = self._atomicModificationDepth - 1
        if self._atomicModificationDepth == 0:
            self.textCursor().endEditBlock()

        if exc_type is not None:
            return False

    def setFont(self, font):
        pass  # suppress dockstring for non-public method
        """Set font and update tab stop width
        """
        QPlainTextEdit.setFont(self, font)
        self._updateTabStopWidth()

        # text on line numbers may overlap, if font is bigger, than code font
        self._lineNumberArea.setFont(font)

    def _updateTabStopWidth(self):
        """Update tabstop width after font or indentation changed
        """
        self.setTabStopWidth(self.fontMetrics().width(' ' *
                                                      self._indenter.width))

    @property
    def lines(self):
        return self._lines

    @lines.setter
    def lines(self, value):
        if not isinstance(value, (list, tuple)) or \
           not all([isinstance(item, basestring) for item in value]):
            raise TypeError('Invalid new value of "lines" attribute')
        self.setPlainText('\n'.join(value))

    def _resetCachedText(self):
        """Reset toPlainText() result cache
        """
        self._cachedText = None

    @property
    def text(self):
        if self._cachedText is None:
            self._cachedText = self.toPlainText()

        return self._cachedText

    @text.setter
    def text(self, text):
        self.setPlainText(text)

    def textForSaving(self):
        """Get text with correct EOL symbols. Use this method for saving a file to storage
        """
        return self.eol.join(self.text.splitlines())

    @property
    def selectedText(self):
        text = self.textCursor().selectedText()

        # replace unicode paragraph separator with habitual \n
        text = text.replace(u'\u2029', '\n')

        return text

    @selectedText.setter
    def selectedText(self, text):
        self.textCursor().insertText(text)

    @property
    def cursorPosition(self):
        cursor = self.textCursor()
        return cursor.block().blockNumber(), cursor.positionInBlock()

    @cursorPosition.setter
    def cursorPosition(self, pos):
        line, col = pos

        line = min(line, len(self.lines) - 1)
        lineText = self.lines[line]

        if col is not None:
            col = min(col, len(lineText))
        else:
            col = len(lineText) - len(lineText.lstrip())

        cursor = QTextCursor(self.document().findBlockByNumber(line))
        setPositionInBlock(cursor, col)
        self.setTextCursor(cursor)

    @property
    def absCursorPosition(self):
        return self.textCursor().position()

    @absCursorPosition.setter
    def absCursorPosition(self, pos):
        cursor = self.textCursor()
        cursor.setPosition(pos)
        self.setTextCursor(cursor)

    @property
    def selectedPosition(self):
        cursor = self.textCursor()
        cursorLine, cursorCol = cursor.blockNumber(), cursor.positionInBlock()

        cursor.setPosition(cursor.anchor())
        startLine, startCol = cursor.blockNumber(), cursor.positionInBlock()

        return ((startLine, startCol), (cursorLine, cursorCol))

    @selectedPosition.setter
    def selectedPosition(self, pos):
        anchorPos, cursorPos = pos
        anchorLine, anchorCol = anchorPos
        cursorLine, cursorCol = cursorPos

        anchorCursor = QTextCursor(
            self.document().findBlockByNumber(anchorLine))
        setPositionInBlock(anchorCursor, anchorCol)

        # just get absolute position
        cursor = QTextCursor(self.document().findBlockByNumber(cursorLine))
        setPositionInBlock(cursor, cursorCol)

        anchorCursor.setPosition(cursor.position(), QTextCursor.KeepAnchor)
        self.setTextCursor(anchorCursor)

    @property
    def absSelectedPosition(self):
        cursor = self.textCursor()
        return cursor.anchor(), cursor.position()

    @absSelectedPosition.setter
    def absSelectedPosition(self, pos):
        anchorPos, cursorPos = pos
        cursor = self.textCursor()
        cursor.setPosition(anchorPos)
        cursor.setPosition(cursorPos, QTextCursor.KeepAnchor)
        self.setTextCursor(cursor)

    def resetSelection(self):
        """Reset selection. Nothing will be selected.
        """
        cursor = self.textCursor()
        cursor.setPosition(cursor.position())
        self.setTextCursor(cursor)

    @property
    def eol(self):
        return self._eol

    @eol.setter
    def eol(self, eol):
        if not eol in ('\r', '\n', '\r\n'):
            raise ValueError("Invalid EOL value")
        if eol != self._eol:
            self._eol = eol
            self.eolChanged.emit(self._eol)

    @property
    def indentWidth(self):
        return self._indenter.width

    @indentWidth.setter
    def indentWidth(self, width):
        if self._indenter.width != width:
            self._indenter.width = width
            self._updateTabStopWidth()
            self.indentWidthChanged.emit(width)

    @property
    def indentUseTabs(self):
        return self._indenter.useTabs

    @indentUseTabs.setter
    def indentUseTabs(self, use):
        if use != self._indenter.useTabs:
            self._indenter.useTabs = use
            self.indentUseTabsChanged.emit(use)

    @property
    def lintMarks(self):
        return self._lintMarks

    @lintMarks.setter
    def lintMarks(self, marks):
        if self._lintMarks != marks:
            self._lintMarks = marks
            self.update()

    def _clearLintMarks(self):
        if self._lintMarks != {}:
            self._lintMarks = {}
            self.update()

    def replaceText(self, pos, length, text):
        """Replace length symbols from ``pos`` with new text.

        If ``pos`` is an integer, it is interpreted as absolute position, if a tuple - as ``(line, column)``
        """
        if isinstance(pos, tuple):
            pos = self.mapToAbsPosition(*pos)

        endPos = pos + length

        if not self.document().findBlock(pos).isValid():
            raise IndexError('Invalid start position %d' % pos)

        if not self.document().findBlock(endPos).isValid():
            raise IndexError('Invalid end position %d' % endPos)

        cursor = QTextCursor(self.document())
        cursor.setPosition(pos)
        cursor.setPosition(endPos, QTextCursor.KeepAnchor)

        cursor.insertText(text)

    def insertText(self, pos, text):
        """Insert text at position

        If ``pos`` is an integer, it is interpreted as absolute position, if a tuple - as ``(line, column)``
        """
        return self.replaceText(pos, 0, text)

    def detectSyntax(self,
                     xmlFileName=None,
                     mimeType=None,
                     language=None,
                     sourceFilePath=None,
                     firstLine=None):
        """Get syntax by next parameters (fill as many, as known):

            * name of XML file with syntax definition
            * MIME type of source file
            * Programming language name
            * Source file path
            * First line of source file

        First parameter in the list has the hightest priority.
        Old syntax is always cleared, even if failed to detect new.

        Method returns ``True``, if syntax is detected, and ``False`` otherwise
        """
        oldLanguage = self.language()

        self.clearSyntax()

        syntax = self._globalSyntaxManager.getSyntax(
            SyntaxHighlighter.formatConverterFunction,
            xmlFileName=xmlFileName,
            mimeType=mimeType,
            languageName=language,
            sourceFilePath=sourceFilePath,
            firstLine=firstLine)

        if syntax is not None:
            self._highlighter = SyntaxHighlighter(syntax, self)
            self._indenter.setSyntax(syntax)

        newLanguage = self.language()
        if oldLanguage != newLanguage:
            self.languageChanged.emit(newLanguage)

        return syntax is not None

    def clearSyntax(self):
        """Clear syntax. Disables syntax highlighting

        This method might take long time, if document is big. Don't call it if you don't have to (i.e. in destructor)
        """
        if self._highlighter is not None:
            self._highlighter.del_()
            self._highlighter = None
            self.languageChanged.emit(None)

    def language(self):
        """Get current language name.
        Return ``None`` for plain text
        """
        if self._highlighter is None:
            return None
        else:
            return self._highlighter.syntax().name

    def isHighlightingInProgress(self):
        """Check if text highlighting is still in progress
        """
        return self._highlighter is not None and \
               self._highlighter.isInProgress()

    def isCode(self, blockOrBlockNumber, column):
        """Check if text at given position is a code.

        If language is not known, or text is not parsed yet, ``True`` is returned
        """
        if isinstance(blockOrBlockNumber, QTextBlock):
            block = blockOrBlockNumber
        else:
            block = self.document().findBlockByNumber(blockOrBlockNumber)

        return self._highlighter is None or \
               self._highlighter.isCode(block, column)

    def isComment(self, line, column):
        """Check if text at given position is a comment. Including block comments and here documents.

        If language is not known, or text is not parsed yet, ``False`` is returned
        """
        return self._highlighter is not None and \
               self._highlighter.isComment(self.document().findBlockByNumber(line), column)

    def isBlockComment(self, line, column):
        """Check if text at given position is a block comment.

        If language is not known, or text is not parsed yet, ``False`` is returned
        """
        return self._highlighter is not None and \
               self._highlighter.isBlockComment(self.document().findBlockByNumber(line), column)

    def isHereDoc(self, line, column):
        """Check if text at given position is a here document.

        If language is not known, or text is not parsed yet, ``False`` is returned
        """
        return self._highlighter is not None and \
               self._highlighter.isHereDoc(self.document().findBlockByNumber(line), column)

    def _dropUserExtraSelections(self):
        if self._userExtraSelections:
            self.setExtraSelections([])

    def setExtraSelections(self, selections):
        """Set list of extra selections.
        Selections are list of tuples ``(startAbsolutePosition, length)``.
        Extra selections are reset on any text modification.

        This is reimplemented method of QPlainTextEdit, it has different signature. Do not use QPlainTextEdit method
        """
        def _makeQtExtraSelection(startAbsolutePosition, length):
            selection = QTextEdit.ExtraSelection()
            cursor = QTextCursor(self.document())
            cursor.setPosition(startAbsolutePosition)
            cursor.setPosition(startAbsolutePosition + length,
                               QTextCursor.KeepAnchor)
            selection.cursor = cursor
            selection.format = self._userExtraSelectionFormat
            return selection

        self._userExtraSelections = [
            _makeQtExtraSelection(*item) for item in selections
        ]
        self._updateExtraSelections()

    def mapToAbsPosition(self, line, column):
        """Convert line and column number to absolute position
        """
        block = self.document().findBlockByNumber(line)
        if not block.isValid():
            raise IndexError("Invalid line index %d" % line)
        if column >= block.length():
            raise IndexError("Invalid column index %d" % column)
        return block.position() + column

    def mapToLineCol(self, absPosition):
        """Convert absolute position to ``(line, column)``
        """
        block = self.document().findBlock(absPosition)
        if not block.isValid():
            raise IndexError("Invalid absolute position %d" % absPosition)

        return (block.blockNumber(), absPosition - block.position())

    def _updateLineNumberAreaWidth(self, newBlockCount):
        """Set line number are width according to current lines count
        """
        self.setViewportMargins(
            self._lineNumberArea.width() + self._markArea.width(), 0, 0, 0)

    def _updateSideAreas(self, rect, dy):
        """Repaint line number area if necessary
        """
        # _countCache magic taken from Qt docs Code Editor Example
        if dy:
            self._lineNumberArea.scroll(0, dy)
            self._markArea.scroll(0, dy)
        elif self._countCache[0] != self.blockCount() or \
             self._countCache[1] != self.textCursor().block().lineCount():

            # if block height not added to rect, last line number sometimes is not drawn
            blockHeight = self.blockBoundingRect(
                self.firstVisibleBlock()).height()

            self._lineNumberArea.update(0, rect.y(),
                                        self._lineNumberArea.width(),
                                        rect.height() + blockHeight)
            self._lineNumberArea.update(0, rect.y(), self._markArea.width(),
                                        rect.height() + blockHeight)
        self._countCache = (self.blockCount(),
                            self.textCursor().block().lineCount())

        if rect.contains(self.viewport().rect()):
            self._updateLineNumberAreaWidth(0)

    def resizeEvent(self, event):
        pass  # suppress dockstring for non-public method
        """QWidget.resizeEvent() implementation.
        Adjust line number area
        """
        QPlainTextEdit.resizeEvent(self, event)

        cr = self.contentsRect()
        self._lineNumberArea.setGeometry(
            QRect(cr.left(), cr.top(), self._lineNumberArea.width(),
                  cr.height()))

        self._markArea.setGeometry(
            QRect(cr.left() + self._lineNumberArea.width(), cr.top(),
                  self._markArea.width(), cr.height()))

    def _insertNewBlock(self):
        """Enter pressed.
        Insert properly indented block
        """
        cursor = self.textCursor()
        with self:
            cursor.insertBlock()
            self._indenter.autoIndentBlock(cursor.block())
        self.ensureCursorVisible()

    def textBeforeCursor(self):
        pass  # suppress docstring for non-API method, used by internal classes
        """Text in current block from start to cursor position
        """
        cursor = self.textCursor()
        return cursor.block().text()[:cursor.positionInBlock()]

    def keyPressEvent(self, event):
        pass  # suppress dockstring for non-public method
        """QPlainTextEdit.keyPressEvent() implementation.
        Catch events, which may not be catched with QShortcut and call slots
        """
        cursor = self.textCursor()

        def shouldUnindentWithBackspace():
            text = cursor.block().text()
            spaceAtStartLen = len(text) - len(text.lstrip())

            return self.textBeforeCursor().endswith(self._indenter.text()) and \
                   not cursor.hasSelection() and \
                   cursor.positionInBlock() == spaceAtStartLen

        def shouldAutoIndent(event):
            atEnd = cursor.positionInBlock() == cursor.block().length() - 1
            return atEnd and \
                   event.text() and \
                   event.text() in self._indenter.triggerCharacters()

        def backspaceOverwrite():
            with self:
                cursor.deletePreviousChar()
                cursor.insertText(' ')
                setPositionInBlock(cursor, cursor.positionInBlock() - 1)
                self.setTextCursor(cursor)

        def typeOverwrite(text):
            """QPlainTextEdit records text input in replace mode as 2 actions:
            delete char, and type char. Actions are undone separately. This is
            workaround for the Qt bug"""
            with self:
                cursor.deleteChar()
                cursor.insertText(text)

        if event.matches(QKeySequence.InsertParagraphSeparator):
            self._insertNewBlock()
        elif event.matches(
                QKeySequence.Copy) and self._rectangularSelection.isActive():
            self._rectangularSelection.copy()
        elif event.matches(
                QKeySequence.Cut) and self._rectangularSelection.isActive():
            self._rectangularSelection.cut()
        elif self._rectangularSelection.isDeleteKeyEvent(event):
            self._rectangularSelection.delete()
        elif event.key() == Qt.Key_Insert and event.modifiers(
        ) == Qt.NoModifier:
            self.setOverwriteMode(not self.overwriteMode())
        elif event.key() == Qt.Key_Backspace and \
             shouldUnindentWithBackspace():
            self._indenter.onShortcutUnindentWithBackspace()
        elif event.key() == Qt.Key_Backspace and \
             not cursor.hasSelection() and \
             self.overwriteMode() and \
             cursor.positionInBlock() > 0:
            backspaceOverwrite()
        elif self.overwriteMode() and \
            event.text() and \
            event.text().isalnum() and \
            not cursor.hasSelection() and \
            cursor.positionInBlock() < cursor.block().length():
            typeOverwrite(event.text())
        elif event.matches(QKeySequence.MoveToStartOfLine):
            self._onShortcutHome(select=False)
        elif event.matches(QKeySequence.SelectStartOfLine):
            self._onShortcutHome(select=True)
        elif self._rectangularSelection.isExpandKeyEvent(event):
            self._rectangularSelection.onExpandKeyEvent(event)
        elif shouldAutoIndent(event):
            with self:
                super(Qutepart, self).keyPressEvent(event)
                self._indenter.autoIndentBlock(cursor.block(), event.text())
        else:
            # make action shortcuts override keyboard events (non-default Qt behaviour)
            for action in self.actions():
                seq = action.shortcut()
                if seq.count() == 1 and seq[0] == event.key() | int(
                        event.modifiers()):
                    action.trigger()
                    break
            else:
                super(Qutepart, self).keyPressEvent(event)

    def mousePressEvent(self, mouseEvent):
        pass  # suppress docstring for non-public method
        if mouseEvent.modifiers() in RectangularSelection.MOUSE_MODIFIERS and \
           mouseEvent.button() == Qt.LeftButton:
            self._rectangularSelection.mousePressEvent(mouseEvent)
        else:
            super(Qutepart, self).mousePressEvent(mouseEvent)

    def mouseMoveEvent(self, mouseEvent):
        pass  # suppress docstring for non-public method
        if mouseEvent.modifiers() in RectangularSelection.MOUSE_MODIFIERS and \
           mouseEvent.buttons() == Qt.LeftButton:
            self._rectangularSelection.mouseMoveEvent(mouseEvent)
        else:
            super(Qutepart, self).mouseMoveEvent(mouseEvent)

    def _chooseVisibleWhitespace(self, text):
        result = [False for _ in range(len(text))]

        lastNonSpaceColumn = len(text.rstrip()) - 1

        # Draw not trailing whitespace
        if self.drawAnyWhitespace:
            # Any
            for column, char in enumerate(text[:lastNonSpaceColumn]):
                if char.isspace() and \
                   (char == '\t' or \
                    column == 0 or \
                    text[column - 1].isspace() or \
                    ((column + 1) < lastNonSpaceColumn and \
                     text[column + 1].isspace())):
                    result[column] = True
        elif self.drawIncorrectIndentation:
            # Only incorrect
            if self.indentUseTabs:
                # Find big space groups
                bigSpaceGroup = ' ' * self.indentWidth
                column = 0
                while column != -1:
                    column = text.find(bigSpaceGroup, column,
                                       lastNonSpaceColumn)
                    if column != -1:
                        for index in range(column, column + self.indentWidth):
                            result[index] = True
                        while index < lastNonSpaceColumn and \
                              text[index] == ' ':
                            result[index] = True
                            index += 1
                        column = index
            else:
                # Find tabs:
                column = 0
                while column != -1:
                    column = text.find('\t', column, lastNonSpaceColumn)
                    if column != -1:
                        result[column] = True
                        column += 1

        # Draw trailing whitespace
        if self.drawIncorrectIndentation or self.drawAnyWhitespace:
            for column in range(lastNonSpaceColumn + 1, len(text)):
                result[column] = True

        return result

    def _drawIndentMarkersAndEdge(self, paintEventRect):
        """Draw indentation markers
        """
        painter = QPainter(self.viewport())

        def cursorRect(block, column, offset):
            cursor = QTextCursor(block)
            setPositionInBlock(cursor, column)
            return self.cursorRect(cursor).translated(offset, 0)

        def drawWhiteSpace(block, column, char):
            leftCursorRect = cursorRect(block, column, 0)
            rightCursorRect = cursorRect(block, column + 1, 0)
            if leftCursorRect.top() == rightCursorRect.top(
            ):  # if on the same visual line
                middleHeight = (leftCursorRect.top() +
                                leftCursorRect.bottom()) / 2
                if char == ' ':
                    painter.setPen(Qt.transparent)
                    painter.setBrush(QBrush(Qt.gray))
                    xPos = (leftCursorRect.x() + rightCursorRect.x()) / 2
                    painter.drawRect(QRect(xPos, middleHeight, 2, 2))
                else:
                    painter.setPen(QColor(Qt.gray).lighter(factor=120))
                    painter.drawLine(leftCursorRect.x() + 3, middleHeight,
                                     rightCursorRect.x() - 3, middleHeight)

        def effectiveEdgePos(text):
            """Position of edge in a block.
            Defined by self.lineLengthEdge, but visible width of \t is more than 1,
            therefore effective position depends on count and position of \t symbols
            Return -1 if line is too short to have edge
            """
            if self.lineLengthEdge is None:
                return -1

            tabExtraWidth = self.indentWidth - 1
            fullWidth = len(text) + (text.count('\t') * tabExtraWidth)
            if fullWidth <= self.lineLengthEdge:
                return -1

            currentWidth = 0
            for pos, char in enumerate(text):
                if char == '\t':
                    # Qt indents up to indentation level, so visible \t width depends on position
                    currentWidth += (self.indentWidth -
                                     (currentWidth % self.indentWidth))
                else:
                    currentWidth += 1
                if currentWidth > self.lineLengthEdge:
                    return pos
            else:  # line too narrow, probably visible \t width is small
                return -1

        def drawEdgeLine(block, edgePos):
            painter.setPen(QPen(QBrush(self.lineLengthEdgeColor), 0))
            rect = cursorRect(block, edgePos, 0)
            painter.drawLine(rect.topLeft(), rect.bottomLeft())

        def drawIndentMarker(block, column):
            painter.setPen(QColor(Qt.blue).lighter())
            rect = cursorRect(block, column, offset=0)
            painter.drawLine(rect.topLeft(), rect.bottomLeft())

        indentWidthChars = len(self._indenter.text())
        cursorPos = self.cursorPosition

        for block in iterateBlocksFrom(self.firstVisibleBlock()):
            blockGeometry = self.blockBoundingGeometry(block).translated(
                self.contentOffset())
            if blockGeometry.top() > paintEventRect.bottom():
                break

            if block.isVisible() and blockGeometry.toRect().intersects(
                    paintEventRect):

                # Draw indent markers, if good indentation is not drawn
                text = block.text()
                if not self.drawAnyWhitespace:
                    column = indentWidthChars
                    while text.startswith(self._indenter.text()) and \
                          len(text) > indentWidthChars and \
                          text[indentWidthChars].isspace():

                        if column != self.lineLengthEdge and \
                           (block.blockNumber(), column) != cursorPos:  # looks ugly, if both drawn
                            """on some fonts line is drawn below the cursor, if offset is 1
                            Looks like Qt bug"""
                            drawIndentMarker(block, column)

                        text = text[indentWidthChars:]
                        column += indentWidthChars

                # Draw edge, but not over a cursor
                edgePos = effectiveEdgePos(block.text())
                if edgePos != -1 and edgePos != cursorPos[1]:
                    drawEdgeLine(block, edgePos)

                if self.drawAnyWhitespace or \
                   self.drawIncorrectIndentation:
                    text = block.text()
                    for column, draw in enumerate(
                            self._chooseVisibleWhitespace(text)):
                        if draw:
                            drawWhiteSpace(block, column, text[column])

    def paintEvent(self, event):
        pass  # suppress dockstring for non-public method
        """Paint event
        Draw indentation markers after main contents is drawn
        """
        super(Qutepart, self).paintEvent(event)
        self._drawIndentMarkersAndEdge(event.rect())

    def _currentLineExtraSelections(self):
        """QTextEdit.ExtraSelection, which highlightes current line
        """
        lineColor = QColor('#ffff99')

        def makeSelection(cursor):
            selection = QTextEdit.ExtraSelection()
            selection.format.setBackground(lineColor)
            selection.format.setProperty(QTextFormat.FullWidthSelection, True)
            cursor.clearSelection()
            selection.cursor = cursor
            return selection

        rectangularSelectionCursors = self._rectangularSelection.cursors()
        if rectangularSelectionCursors:
            return [makeSelection(cursor) \
                        for cursor in rectangularSelectionCursors]
        else:
            return [makeSelection(self.textCursor())]

    def _updateExtraSelections(self):
        """Highlight current line
        """
        cursorColumnIndex = self.textCursor().positionInBlock()

        bracketSelections = self._bracketHighlighter.extraSelections(
            self,
            self.textCursor().block(), cursorColumnIndex)

        allSelections = self._currentLineExtraSelections() + \
                        self._rectangularSelection.selections() + \
                        bracketSelections + \
                        self._userExtraSelections

        QPlainTextEdit.setExtraSelections(self, allSelections)

    def _onShortcutIndent(self):
        if self.textCursor().hasSelection():
            self._indenter.onChangeSelectedBlocksIndent(increase=True)
        else:
            self._indenter.onShortcutIndentAfterCursor()

    def _onShortcutScroll(self, down):
        """Ctrl+Up/Down pressed, scroll viewport
        """
        value = self.verticalScrollBar().value()
        if down:
            value += 1
        else:
            value -= 1
        self.verticalScrollBar().setValue(value)

    def _onShortcutSelectAndScroll(self, down):
        """Ctrl+Shift+Up/Down pressed.
        Select line and scroll viewport
        """
        cursor = self.textCursor()
        cursor.movePosition(QTextCursor.Down if down else QTextCursor.Up,
                            QTextCursor.KeepAnchor)
        self.setTextCursor(cursor)
        self._onShortcutScroll(down)

    def _onShortcutHome(self, select):
        """Home pressed, move cursor to the line start or to the text start
        """
        cursor = self.textCursor()
        anchor = QTextCursor.KeepAnchor if select else QTextCursor.MoveAnchor
        text = cursor.block().text()
        spaceAtStartLen = len(text) - len(text.lstrip())
        if cursor.positionInBlock() == spaceAtStartLen:  # if at start of text
            setPositionInBlock(cursor, 0, anchor)
        else:
            setPositionInBlock(cursor, spaceAtStartLen, anchor)
        self.setTextCursor(cursor)

    def _selectLines(self, startBlockNumber, endBlockNumber):
        """Select whole lines
        """
        startBlock = self.document().findBlockByNumber(startBlockNumber)
        endBlock = self.document().findBlockByNumber(endBlockNumber)
        cursor = QTextCursor(startBlock)
        cursor.setPosition(endBlock.position(), QTextCursor.KeepAnchor)
        cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor)
        self.setTextCursor(cursor)

    def _selectedBlocks(self):
        """Return selected blocks and tuple (startBlock, endBlock)
        """
        cursor = self.textCursor()
        return self.document().findBlock(cursor.selectionStart()), \
               self.document().findBlock(cursor.selectionEnd())

    def _selectedBlockNumbers(self):
        """Return selected block numbers and tuple (startBlockNumber, endBlockNumber)
        """
        startBlock, endBlock = self._selectedBlocks()
        return startBlock.blockNumber(), endBlock.blockNumber()

    def _onShortcutMoveLine(self, down):
        """Move line up or down
        Actually, not a selected text, but next or previous block is moved
        TODO keep bookmarks when moving
        """
        startBlock, endBlock = self._selectedBlocks()

        startBlockNumber = startBlock.blockNumber()
        endBlockNumber = endBlock.blockNumber()

        def _moveBlock(block, newNumber):
            text = block.text()
            with self:
                del self.lines[block.blockNumber()]
                self.lines.insert(newNumber, text)

        if down:  # move next block up
            blockToMove = endBlock.next()
            if not blockToMove.isValid():
                return

            # if operaiton is UnDone, marks are located incorrectly
            self._bookmarks.clear(startBlock, endBlock.next())

            _moveBlock(blockToMove, startBlockNumber)

            self._selectLines(startBlockNumber + 1, endBlockNumber + 1)
        else:  # move previous block down
            blockToMove = startBlock.previous()
            if not blockToMove.isValid():
                return

            # if operaiton is UnDone, marks are located incorrectly
            self._bookmarks.clear(startBlock.previous(), endBlock)

            _moveBlock(blockToMove, endBlockNumber)

            self._selectLines(startBlockNumber - 1, endBlockNumber - 1)

        self._markArea.update()

    def _selectedLinesSlice(self):
        """Get slice of selected lines
        """
        startBlockNumber, endBlockNumber = self._selectedBlockNumbers()
        return slice(startBlockNumber, endBlockNumber + 1, 1)

    def _onShortcutDeleteLine(self):
        """Delete line(s) under cursor
        """
        del self.lines[self._selectedLinesSlice()]

    def _onShortcutCopyLine(self):
        """Copy selected lines to the clipboard
        """
        lines = self.lines[self._selectedLinesSlice()]
        text = self._eol.join(lines)
        QApplication.clipboard().setText(text)

    def _onShortcutPasteLine(self):
        """Paste lines from the clipboard
        """
        lines = self.lines[self._selectedLinesSlice()]
        text = QApplication.clipboard().text()
        if text:
            with self:
                if self.textCursor().hasSelection():
                    startBlockNumber, endBlockNumber = self._selectedBlockNumbers(
                    )
                    del self.lines[self._selectedLinesSlice()]
                    self.lines.insert(startBlockNumber, text)
                else:
                    line, col = self.cursorPosition
                    if col > 0:
                        line = line + 1
                    self.lines.insert(line, text)

    def _onShortcutCutLine(self):
        """Cut selected lines to the clipboard
        """
        lines = self.lines[self._selectedLinesSlice()]

        self._onShortcutCopyLine()
        self._onShortcutDeleteLine()

    def _onShortcutDuplicateLine(self):
        """Duplicate selected text or current line
        """
        cursor = self.textCursor()
        if cursor.hasSelection():  # duplicate selection
            text = cursor.selectedText()
            selectionStart, selectionEnd = cursor.selectionStart(
            ), cursor.selectionEnd()
            cursor.setPosition(selectionEnd)
            cursor.insertText(text)
            # restore selection
            cursor.setPosition(selectionStart)
            cursor.setPosition(selectionEnd, QTextCursor.KeepAnchor)
            self.setTextCursor(cursor)
        else:
            line = cursor.blockNumber()
            self.lines.insert(line + 1, self.lines[line])
            self.ensureCursorVisible()

        self._updateExtraSelections(
        )  # newly inserted text might be highlighted as braces

    def _onShortcutPrint(self):
        """Ctrl+P handler.
        Show dialog, print file
        """
        dialog = QPrintDialog(self)
        if dialog.exec_() == QDialog.Accepted:
            printer = dialog.printer()
            self.print_(printer)

    def insertFromMimeData(self, source):
        pass  # suppress docstring for non-public method
        if source.hasFormat(self._rectangularSelection.MIME_TYPE):
            self._rectangularSelection.paste(source)
        else:
            super(Qutepart, self).insertFromMimeData(source)

    def del_(self):
        self._completer.del_()
class DiffsMenu(QtGui.QDialog):
  def __init__(self, parent = None):
    super(DiffsMenu, self).__init__(parent)
    
    self.ui = Ui_Diffs()
    self.ui.setupUi(self)
    
    self.ui.actionCopyPath = QtGui.QAction("Copy path", None, triggered = self.copyPath)
    self.ui.treeResults.addAction(self.ui.actionCopyPath)
    
    self.folder1        = None
    self.folder2        = None
    self.files          = None
    self.files_nodupes  = None
    self.files_missing  = None
    
    self.saved_diffs    = {}
    
    self.format1 = QTextCharFormat()
    self.format1.setBackground(QColor(255, 224, 224))
    
    self.format2 = QTextCharFormat()
    self.format2.setBackground(QColor(224, 240, 255))
    
    self.menu_name = "Diffs"
    
    self.format_plain = QTextCharFormat()
  
  ##############################################################################
  ### @fn   copyPath()
  ### @desc Copies the path of the selected node to the clipboard.
  ##############################################################################
  def copyPath(self):
    node = self.ui.treeResults.currentItem()
    
    if not node == None:
      text = "{%s}" % tree.tree_item_to_path(node)
      
      clipboard = QApplication.clipboard()
      clipboard.setText(text)
  
  ##############################################################################
  ### @fn   set_folders()
  ### @desc Set the two folders to be compared.
  ##############################################################################
  def set_folders(self, folder1, folder2, files = None):
    if files == None:
      files1 = list_all_files(folder1)
      files2 = list_all_files(folder2)
      
      files = set()
      
      # Get rid of our folder paths so we're working on generic filenames
      # that can be used with either folder.
      files.update([file[len(folder1) + 1:] for file in files1 if file[-4:] == ".txt"])
      files.update([file[len(folder2) + 1:] for file in files2 if file[-4:] == ".txt"])
    
    self.ui.lblDir1.setText(folder1)
    self.ui.lblDir2.setText(folder2)
    
    self.folder1        = folder1
    self.folder2        = folder2
    self.files          = set(files)
    self.files_nodupes  = None
    self.files_missing  = None
    
    self.saved_diffs    = {}
    
    self.show_files()
  
  ##############################################################################
  ### @fn   show_files()
  ### @desc Shows the list of files.
  ##############################################################################
  def show_files(self):
  
    # If we're not showing identical files, then go through the list
    # and find and remove everything we don't want to see.
    if not self.ui.chkShowSame.isChecked():
      if self.files_nodupes == None:
        self.files_nodupes = set()
      
        for file in self.files:
          file1 = os.path.join(self.folder1, file)
          file2 = os.path.join(self.folder2, file)
          
          if not os.path.isfile(file1) or not os.path.isfile(file2):
            self.files_nodupes.add(file)
            continue
          
          text1 = text_files.load_text(file1)
          text2 = text_files.load_text(file2)
          
          if not text1 == text2:
            self.files_nodupes.add(file)
            
      files = self.files_nodupes
      
    else:
      files = self.files
      
    # If we're not showing files not present in both directories,
    # go through the list and strip them out.
    if not self.ui.chkNotBoth.isChecked():
      if self.files_missing == None:
        self.files_missing = set()
      
        for file in self.files:
          file1 = os.path.join(self.folder1, file)
          file2 = os.path.join(self.folder2, file)
          
          if not os.path.isfile(file1) or not os.path.isfile(file2):
            self.files_missing.add(file)
            
      files = files - self.files_missing
      
    self.ui.treeResults.clear()
    self.ui.treeResults.setHeaderLabel("Results (%d)" % len(files))
    
    if len(files) > 0:
      tree_items = []
      
      for file in files:
        file = os.path.normpath(file)
        file = dir_tools.consolidate_dir(file)
        tree_item = tree.path_to_tree(file)
        tree_items.append(tree_item)
      
      tree_items = tree.consolidate_tree_items(tree_items)
      
      for item in tree_items:
        self.ui.treeResults.addTopLevelItem(item)
      
      self.ui.treeResults.expandAll()
  
  ##############################################################################
  ### @fn   changedSelection()
  ### @desc Triggered when the user selects something in the tree.
  ##############################################################################
  def changedSelection(self, current, prev):
    if current == None or current.childCount() != 0:
      return
    
    file = common.qt_to_unicode(current.text(0))
    path = tree.tree_item_to_path(current.parent())
    self.setWindowTitle("%s - %s" % (self.menu_name, os.path.join(path, file)))
    path = dir_tools.expand_dir(path)
    
    file = os.path.join(path, file)
    file1 = os.path.join(self.folder1, file)
    file2 = os.path.join(self.folder2, file)
    
    if not os.path.isfile(file1):
      script1 = ScriptFile()
    else:
      script1 = ScriptFile(file1)
    
    if not os.path.isfile(file2):
      script2 = ScriptFile()
    else:
      script2 = ScriptFile(file2)
    
    # So we can loop this shit.
    to_diff = [
      # Text 1              Text 2              Text Box 1              Text Box 2
      (script1[common.editor_config.lang_trans],  script2[common.editor_config.lang_trans], self.ui.txtTranslated1, self.ui.txtTranslated2),
      (script1[common.editor_config.lang_orig],    script2[common.editor_config.lang_orig],   self.ui.txtOriginal1,   self.ui.txtOriginal2),
      (script1.comments,    script2.comments,   self.ui.txtComments1,   self.ui.txtComments2),
    ]
    
    # Save us a little bit of time recalculating.
    if file in self.saved_diffs:
      diffs = self.saved_diffs[file]
    else:
      diffs = [None] * len(to_diff)
    
    for i, (text1, text2, box1, box2) in enumerate(to_diff):
    
      if diffs[i] == None:
        diffs[i] = DIFFER.diff_main(text1, text2)
        DIFFER.diff_cleanupSemantic(diffs[i])
      
      box1.setPlainText(text1)
      box2.setPlainText(text2)
      
      highlight1, highlight2 = parse_diffs(diffs[i])
      
      cursor1 = box1.textCursor()
      cursor2 = box2.textCursor()
      
      cursor1.select(QTextCursor.Document)
      cursor2.select(QTextCursor.Document)
      cursor1.setCharFormat(self.format_plain)
      cursor2.setCharFormat(self.format_plain)
      
      cursor1.movePosition(QTextCursor.Start)
      cursor2.movePosition(QTextCursor.Start)
      
      for pos, length in highlight1:
        cursor1.setPosition(pos, QTextCursor.MoveAnchor)
        cursor1.setPosition(pos + length, QTextCursor.KeepAnchor)
        cursor1.setCharFormat(self.format1)
      
      cursor1.movePosition(QTextCursor.Start)
      
      for pos, length in highlight2:
        cursor2.setPosition(pos, QTextCursor.MoveAnchor)
        cursor2.setPosition(pos + length, QTextCursor.KeepAnchor)
        cursor2.setCharFormat(self.format2)
      
      cursor2.movePosition(QTextCursor.Start)
      
      box1.setTextCursor(cursor1)
      box2.setTextCursor(cursor2)
    
    # for i, (text1, text2, box1, box2) in enumerate(to_diff):
    self.saved_diffs[file] = diffs
Ejemplo n.º 37
0
class ViewHighlighter(widgets.arbitraryhighlighter.ArbitraryHighlighter,
                      plugin.Plugin):
    def __init__(self, view):
        super(ViewHighlighter, self).__init__(view)
        self._cursorFormat = QTextCharFormat()
        self._cursorFormat.setProperty(QTextFormat.FullWidthSelection, True)
        app.settingsChanged.connect(self.readSettings)
        self.readSettings()
        bookmarks.bookmarks(view.document()).marksChanged.connect(
            self.updateMarkedLines)
        self.updateMarkedLines()
        view.cursorPositionChanged.connect(self.updateCursor)
        view.installEventFilter(self)

    def updateMarkedLines(self):
        """Called when something changes in the bookmarks."""
        for type, marks in bookmarks.bookmarks(
                self.parent().document()).marks().items():
            self.highlight(type, marks, -1)

    def eventFilter(self, view, ev):
        if ev.type() in (QEvent.FocusIn, QEvent.FocusOut):
            self.updateCursor(view)
        return False

    def updateCursor(self, view=None):
        """Called when the textCursor has moved. Highlights the current line.

        If view is None (the default), our parent() is assumed to be the
        view. The eventFilter() method calls us with the view, this is
        done because the event filter is sometimes called very late in
        the destructor phase, when our parent is possibly not valid
        anymore.

        """
        if view is None:
            view = self.parent()
        # sometimes in the destruction phase, view is a generic QWidget...
        try:
            cursor = view.textCursor()
        except AttributeError:
            return
        # highlight current line
        cursor.clearSelection()
        color = QColor(self._baseColors['current'])
        color.setAlpha(200 if view.hasFocus() else 100)
        self._cursorFormat.setBackground(color)
        self.highlight(self._cursorFormat, [cursor], 0)

    def readSettings(self):
        data = textformats.formatData('editor')
        self._baseColors = data.baseColors
        self.updateCursor()
        self.reload()

    def textFormat(self, name):
        """(Internal) Returns a QTextCharFormat setup according to the preferences.

        For bookmarks and the current line, FullWidthSelection is automatically enabled.

        """
        f = QTextCharFormat()
        f.setBackground(self._baseColors[name])
        if name in ('current', 'mark', 'error'):
            f.setProperty(QTextFormat.FullWidthSelection, True)
        return f
Ejemplo n.º 38
0
    def check_brackets(self):
        left, right = QTextEdit.ExtraSelection(),\
                      QTextEdit.ExtraSelection()

        cursor = self.textCursor()
        block = cursor.block()
        data = block.userData()
        previous, next = None, None

        if data is not None:
            position = cursor.position()
            block_position = cursor.block().position()
            braces = data.braces
            N = len(braces)

            for k in range(0, N):
                if braces[k].position == position - block_position or\
                   braces[k].position == position - block_position - 1:
                    previous = braces[k].position + block_position
                    if braces[k].character in ['{', '(', '[']:
                        next = self.match_left(block,
                                               braces[k].character,
                                               k + 1, 0)
                    elif braces[k].character in ['}', ')', ']']:
                        next = self.match_right(block,
                                                braces[k].character,
                                                k, 0)
#                    if next is None:
#                        next = -1
        if (next is not None and next > 0) \
            and (previous is not None and previous > 0):

            format = QTextCharFormat()

            cursor.setPosition(previous)
            cursor.movePosition(QTextCursor.NextCharacter,
                                QTextCursor.KeepAnchor)

            format.setForeground(QColor('white'))
            format.setBackground(QColor('blue'))
            left.format = format
            left.cursor = cursor

            cursor.setPosition(next)
            cursor.movePosition(QTextCursor.NextCharacter,
                                QTextCursor.KeepAnchor)

            format.setForeground(QColor('white'))
            format.setBackground(QColor('blue'))
            right.format = format
            right.cursor = cursor

            return left, right

        elif previous is not None:
            format = QTextCharFormat()

            cursor.setPosition(previous)
            cursor.movePosition(QTextCursor.NextCharacter,
                                QTextCursor.KeepAnchor)

            format.setForeground(QColor('white'))
            format.setBackground(QColor('red'))
            left.format = format
            left.cursor = cursor
            return (left,)
        elif next is not None:
            format = QTextCharFormat()

            cursor.setPosition(next)
            cursor.movePosition(QTextCursor.NextCharacter,
                                QTextCursor.KeepAnchor)

            format.setForeground(QColor('white'))
            format.setBackground(QColor('red'))
            left.format = format
            left.cursor = cursor
            return (left,)
Ejemplo n.º 39
0
class KeywordHighlighter(QSyntaxHighlighter):
    def __init__(self, document):
        QSyntaxHighlighter.__init__(self, document)

        self.clb = ConfigurationLineBuilder(ErtKeywords())

        self.comment_format = QTextCharFormat()
        self.comment_format.setForeground(QColor(0, 128, 0))
        self.comment_format.setFontItalic(True)

        self.keyword_format = QTextCharFormat()
        self.keyword_format.setForeground(QColor(200, 100, 0))
        # self.keyword_format.setFontWeight(QFont.Bold)

        self.error_format = QTextCharFormat()
        # self.error_format.setForeground(QColor(255, 0, 0))
        self.error_format.setUnderlineStyle(QTextCharFormat.WaveUnderline)
        self.error_format.setUnderlineColor(QColor(255, 0, 0))

        self.search_format = QTextCharFormat()
        self.search_format.setBackground(QColor(220, 220, 220))

        self.builtin_format = QTextCharFormat()
        self.builtin_format.setForeground(QColor(0, 170, 227))

        self.search_string = ""

    def formatKeyword(self, keyword, validation_status):
        assert isinstance(keyword, Keyword)
        if keyword.hasKeywordDefinition():
            keyword_format = QTextCharFormat(self.keyword_format)

            if not validation_status:
                keyword_format.merge(self.error_format)

            self.formatToken(keyword, keyword_format)
        else:
            self.formatToken(keyword, self.error_format)

    def highlightBlock(self, complete_block):
        try:
            block = unicode(complete_block)
        except NameError:
            block = complete_block

        self.clb.processLine(block)

        if self.clb.hasComment():
            self.setFormat(self.clb.commentIndex(),
                           len(block) - self.clb.commentIndex(),
                           self.comment_format)

        if not self.clb.hasConfigurationLine():
            count = len(block)

            if self.clb.hasComment():
                count = self.clb.commentIndex()

            self.setFormat(0, count, self.error_format)

        if self.clb.hasConfigurationLine():
            cl = self.clb.configurationLine()
            self.setCurrentBlockUserData(ConfigurationLineUserData(cl))

            self.formatKeyword(cl.keyword(),
                               cl.validationStatusForToken(cl.keyword()))

            arguments = cl.arguments()

            for argument in arguments:
                if not argument.hasArgumentDefinition():
                    pass

                elif argument.argumentDefinition().isBuiltIn():
                    self.formatToken(argument, self.builtin_format)

                if not cl.validationStatusForToken(argument):
                    self.formatToken(argument, self.error_format)

        if self.search_string != "":
            for match in re.finditer("(%s)" % self.search_string,
                                     complete_block):
                self.setFormat(match.start(1),
                               match.end(1) - match.start(1),
                               self.search_format)

    def setSearchString(self, string):
        try:
            if self.search_string != unicode(string):
                self.search_string = unicode(string)
                self.rehighlight()
        except NameError:
            if self.search_string != string:
                self.search_string = string
                self.rehighlight()

    def formatToken(self, token, highlight_format):
        self.setFormat(token.fromIndex(), token.count(), highlight_format)
Ejemplo n.º 40
0
class MusicView(QWidget):
    """Widget containing the qpopplerview.View."""

    zoomChanged = pyqtSignal(int, float)  # mode, scale

    def __init__(self, dockwidget):
        """Creates the Music View for the dockwidget."""
        super(MusicView, self).__init__(dockwidget)

        self._positions = weakref.WeakKeyDictionary()
        self._currentDocument = None
        self._links = None
        self._clicking_link = False

        self._highlightFormat = QTextCharFormat()
        self._highlightMusicFormat = Highlighter()
        self._highlightRange = None
        self._highlightTimer = QTimer(singleShot=True,
                                      interval=250,
                                      timeout=self.updateHighlighting)
        self._highlightRemoveTimer = QTimer(singleShot=True,
                                            timeout=self.clearHighlighting)

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        self.view = popplerview.View(self)
        layout.addWidget(self.view)
        app.settingsChanged.connect(self.readSettings)
        self.readSettings()
        self.view.setViewMode(qpopplerview.FitWidth)
        self.view.surface().setPageLayout(qpopplerview.RowLayout())
        self.view.surface().linkClicked.connect(self.slotLinkClicked)
        self.view.surface().linkHovered.connect(self.slotLinkHovered)
        self.view.surface().linkLeft.connect(self.slotLinkLeft)
        self.view.surface().setShowUrlTips(False)
        self.view.surface().linkHelpRequested.connect(
            self.slotLinkHelpRequested)

        self.view.viewModeChanged.connect(self.updateZoomInfo)
        self.view.surface().pageLayout().scaleChanged.connect(
            self.updateZoomInfo)
        self.view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.view.customContextMenuRequested.connect(self.showContextMenu)

        # react if cursor of current text document moves
        dockwidget.mainwindow().currentViewChanged.connect(
            self.slotCurrentViewChanged)
        view = dockwidget.mainwindow().currentView()
        if view:
            self.slotCurrentViewChanged(view)

    def sizeHint(self):
        """Returns the initial size the PDF (Music) View prefers."""
        return self.parent().mainwindow().size() / 2

    def updateZoomInfo(self):
        """Called when zoom and viewmode of the qpopplerview change, emit zoomChanged."""
        self.zoomChanged.emit(self.view.viewMode(),
                              self.view.surface().pageLayout().scale())

    def openDocument(self, doc):
        """Opens a documents.Document instance."""
        self.clear()
        self._currentDocument = doc
        document = doc.document()
        if document:
            self._links = pointandclick.links(document)
            self.view.load(document)
            position = self._positions.get(doc, (0, 0, 0))
            self.view.setPosition(position, True)

    def clear(self):
        """Empties the view."""
        cur = self._currentDocument
        if cur:
            self._positions[cur] = self.view.position()
        self._currentDocument = None
        self._links = None
        self._highlightRange = None
        self._highlightTimer.stop()
        self.view.clear()

    def readSettings(self):
        """Reads the settings from the user's preferences."""
        # background and highlight colors of music view
        colors = textformats.formatData('editor').baseColors
        self._highlightMusicFormat.setColor(colors['musichighlight'])
        color = colors['selectionbackground']
        color.setAlpha(128)
        self._highlightFormat.setBackground(color)

    def slotLinkClicked(self, ev, page, link):
        """Called when the use clicks a link.
        
        If the links is a textedit link, opens the document and puts the cursor there.
        Otherwise, call the helpers module to open the destination.
        
        """
        if ev.button() == Qt.RightButton:
            return
        cursor = self._links.cursor(link, True)
        if cursor:
            if ev.modifiers() & Qt.ShiftModifier:
                from . import editinplace
                editinplace.edit(self, cursor, ev.globalPos())
            else:
                mainwindow = self.parent().mainwindow()
                self._clicking_link = True
                mainwindow.setTextCursor(cursor, findOpenView=True)
                self._clicking_link = False
                import widgets.blink
                widgets.blink.Blinker.blink_cursor(mainwindow.currentView())
                mainwindow.activateWindow()
                mainwindow.currentView().setFocus()
        elif (isinstance(link, popplerqt4.Poppler.LinkBrowse)
              and not link.url().startswith('textedit:')):
            helpers.openUrl(QUrl(link.url()))

    def slotLinkHovered(self, page, link):
        """Called when the mouse hovers a link.
        
        If the links points to the current editor document, the token(s) it points
        at are highlighted using a transparent selection color.
        
        The highlight shows for a few seconds but disappears when the mouse moves
        off the link or when the link is clicked.
        
        """
        self.view.surface().highlight(self._highlightMusicFormat,
                                      [(page, link.linkArea().normalized())],
                                      2000)
        self._highlightRange = None
        cursor = self._links.cursor(link)
        if not cursor or cursor.document() != self.parent().mainwindow(
        ).currentDocument():
            return

        # highlight token(s) at this cursor
        cursors = pointandclick.positions(cursor)
        if cursors:
            view = self.parent().mainwindow().currentView()
            viewhighlighter.highlighter(view).highlight(
                self._highlightFormat, cursors, 2, 5000)

    def slotLinkLeft(self):
        """Called when the mouse moves off a previously highlighted link."""
        self.clearHighlighting()
        view = self.parent().mainwindow().currentView()
        viewhighlighter.highlighter(view).clear(self._highlightFormat)

    def slotLinkHelpRequested(self, pos, page, link):
        """Called when a ToolTip wants to appear above the hovered link."""
        if isinstance(link, popplerqt4.Poppler.LinkBrowse):
            cursor = self._links.cursor(link)
            if cursor:
                from . import tooltip
                text = tooltip.text(cursor)
            elif link.url():
                l = textedit.link(link.url())
                if l:
                    text = "{0} ({1}:{2})".format(os.path.basename(l.filename),
                                                  l.line, l.column)
                else:
                    text = link.url()
            QToolTip.showText(pos, text, self.view.surface(),
                              page.linkRect(link.linkArea()))

    def slotCurrentViewChanged(self, view, old=None):
        self.view.surface().clearHighlight(self._highlightMusicFormat)
        if old:
            old.cursorPositionChanged.disconnect(
                self.slotCursorPositionChanged)
        view.cursorPositionChanged.connect(self.slotCursorPositionChanged)

    def slotCursorPositionChanged(self):
        """Called when the user moves the text cursor."""
        if not self.isVisible() or not self._links:
            return  # not visible of no PDF in the viewer

        view = self.parent().mainwindow().currentView()
        links = self._links.boundLinks(view.document())
        if not links:
            return  # the PDF contains no references to the current text document

        s = links.indices(view.textCursor())
        if s is False:
            self.clearHighlighting()
        elif s:
            # move if sync is enabled and the cursor did not move as a result of
            # clicking a link
            if (not self._clicking_link and self.parent().actionCollection.
                    music_sync_cursor.isChecked()):
                rect = self.destinationsRect(links.destinations()[s])
                center = rect.center()
                self.view.ensureVisible(center.x(), center.y(),
                                        50 + rect.width() // 2,
                                        50 + rect.height() // 2)

            # perform highlighting after move has been started. This is to ensure that if kinetic scrolling is
            # is enabled its speed is already set so that we can adjust the highlight timer.
            self.highlight(links.destinations(), s)

    def highlight(self, destinations, slice, msec=None):
        """(Internal) Highlights the from the specified destinations the specified slice."""
        count = slice.stop - slice.start
        if msec is None:
            # RC: increased timer to give some time to the kinetic scrolling to complete.
            kineticTimeLeft = 0
            if self.view.kineticScrollingEnabled():
                kineticTimeLeft = 20 * self.view.kineticTicksLeft()
            msec = 5000 if count > 1 else 2000  # show selections longer
            msec += kineticTimeLeft
        self._highlightRemoveTimer.start(msec)
        if self._highlightRange == slice:
            return  # don't redraw if same
        self._highlightRange = slice
        self._destinations = destinations[slice]
        if count > 100:
            self._highlightTimer.start()
        else:
            self._highlightTimer.stop()
            self.updateHighlighting()

    def updateHighlighting(self):
        """Really orders the view's surface to draw the highlighting."""
        layout = self.view.surface().pageLayout()
        areas = [(layout[pageNum], rect) for dest in self._destinations
                 for pageNum, rect in dest]
        self.view.surface().highlight(self._highlightMusicFormat, areas)

    def clearHighlighting(self):
        """Called on timeout of the _highlightRemoveTimer."""
        self._highlightRange = None
        self.view.surface().clearHighlight(self._highlightMusicFormat)

    def showCurrentLinks(self):
        """Scrolls the view if necessary to show objects at current text cursor."""
        if not self._links:
            return  # no PDF in viewer

        view = self.parent().mainwindow().currentView()
        links = self._links.boundLinks(view.document())
        if not links:
            return  # the PDF contains no references to the current text document

        s = links.indices(view.textCursor())
        if not s:
            return
        self.view.center(
            self.destinationsRect(links.destinations()[s]).center())
        self.highlight(links.destinations(), s, 10000)

    def destinationsRect(self, destinations):
        """Return the rectangle containing all destinations."""
        layout = self.view.surface().pageLayout()
        rect = QRect()
        for dest in destinations:
            for pageNum, r in dest:
                rect = rect.united(layout[pageNum].linkRect(r.normalized()))
        # not larger than viewport
        rect.setSize(rect.size().boundedTo(self.view.viewport().size()))
        return rect

    def showContextMenu(self):
        """Called when the user right-clicks or presses the context menu key."""
        pos = self.view.mapToGlobal(QPoint(0, 0))
        link, cursor = None, None
        # mouse inside view?
        if self.view.mapFromGlobal(
                QCursor.pos()) in self.view.viewport().rect():
            pos = QCursor.pos()
            pos_in_surface = self.view.surface().mapFromGlobal(pos)
            page, link = self.view.surface().pageLayout().linkAt(
                pos_in_surface)
            if link:
                cursor = self._links.cursor(link, True)
        from . import contextmenu
        contextmenu.show(pos, self.parent(), link, cursor)
Ejemplo n.º 41
0
class QtANSIEscapeCodeHandler(ANSIEscapeCodeHandler):
    def __init__(self):
        ANSIEscapeCodeHandler.__init__(self)
        self.base_format = None
        self.current_format = None

    def set_light_background(self, state):
        if state:
            self.default_foreground_color = 30
            self.default_background_color = 47
        else:
            self.default_foreground_color = 37
            self.default_background_color = 40

    def set_base_format(self, base_format):
        self.base_format = base_format

    def get_format(self):
        return self.current_format

    def set_style(self):
        """
        Set font style with the following attributes:
        'foreground_color', 'background_color', 'italic',
        'bold' and 'underline'
        """
        if self.current_format is None:
            assert self.base_format is not None
            self.current_format = QTextCharFormat(self.base_format)
        # Foreground color
        if self.foreground_color is None:
            qcolor = self.base_format.foreground()
        else:
            cstr = self.ANSI_COLORS[self.foreground_color - 30][self.intensity]
            qcolor = QColor(cstr)
        self.current_format.setForeground(qcolor)
        # Background color
        if self.background_color is None:
            qcolor = self.base_format.background()
        else:
            cstr = self.ANSI_COLORS[self.background_color - 40][self.intensity]
            qcolor = QColor(cstr)
        self.current_format.setBackground(qcolor)

        font = self.current_format.font()
        # Italic
        if self.italic is None:
            italic = self.base_format.fontItalic()
        else:
            italic = self.italic
        font.setItalic(italic)
        # Bold
        if self.bold is None:
            bold = self.base_format.font().bold()
        else:
            bold = self.bold
        font.setBold(bold)
        # Underline
        if self.underline is None:
            underline = self.base_format.font().underline()
        else:
            underline = self.underline
        font.setUnderline(underline)
        self.current_format.setFont(font)