QScintilla/Lexers/SubstyledLexer.py

Tue, 12 Mar 2019 20:00:30 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Tue, 12 Mar 2019 20:00:30 +0100
branch
sub_styles
changeset 6855
4d80c8cc99a1
parent 6846
6ca9ef2c0907
child 6856
cdf13841603c
permissions
-rw-r--r--

SubstyledLexer: continued implementing the getter and setter methods.

# -*- coding: utf-8 -*-

# Copyright (c) 2003 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the lexer mixin class.
"""

from __future__ import unicode_literals

import copy

from PyQt5.QtGui import QColor

from .Lexer import Lexer

import Preferences


class SubstyledLexer(Lexer):
    """
    Class to implement the sub-styled lexer mixin class.
    """
    def __init__(self):
        """
        Constructor
        """
        super(SubstyledLexer, self).__init__()
        
        self.baseStyles = []
        # list of style numbers, that support sub-styling
        self.defaultSubStyles = {}
        # dictionary with sub-styling data
        # main key: base style number, value : dict with
        #       key: sub-style number, value: dict with
        #           'Description': string containing a short description
        #           'Words': string of whitespace separated words to be styled
        #           'Style': dictionary with styling data (only difference to
        #                    the base style is required
        #               'fore': foreground color (int containing RGB values)
        #               'paper': background color (int containing RGB values)
        #               'eolfill': fill to eol (bool)
        #               'font_family': font family (str)
        #               'font_size: point size (int)
        #               'font_bold: bold font (bool)
        #               'font_italic: italic font (bool)
        #               'font_underline: underlined font (bool)
        
        self.__subStyles = {}
        self.__subStylesInitialized = False
    
    def loadSubstyles(self):
        """
        Public method to load the sub-styles from the settings file
        """
        settings = Preferences.Prefs.settings
        
        self.loadDefaultSubStyles()
        
        for baseStyle in self.baseStyles:
            key = "Scintilla/{0}/style{1}/SubStyleLength".format(
                self.language(), baseStyle)
            if settings.contains(key):
                subStyleLength = int(settings.value(key), 0)
                self.__subStyles[baseStyle] = {}
                if subStyleLength:
                    self.__subStyles[baseStyle] = []
                    for subStyle in range(subStyleLength):
                        baseKey = "Scintilla/{0}/style{1}/substyle{2}/".format(
                            self.language(), baseStyle, subStyle)
                        if settings.contains(baseKey):
                            subStyleData = {}
                            subStyleData["Description"] = settings.value(
                                baseKey + "Description", "")
                            subStyleData["Words"] = settings.value(
                                baseKey + "Words", "")
                            style = {}
                            
                            key = baseKey + "fore"
                            if settings.contains(key):
                                style["fore"] = int(settings.value(key))
                            key = baseKey + "paper"
                            if settings.contains(key):
                                style["paper"] = int(settings.value(key))
                            key = baseKey + "eolfill"
                            if settings.contains(key):
                                style["eolfill"] = Preferences.toBool(
                                    settings.value(key))
                            key = baseKey + "font_family"
                            if settings.contains(key):
                                style["font_family"] = settings.value(key)
                            key = baseKey + "font_size"
                            if settings.contains(key):
                                style["font_size"] = int(settings.value(key))
                            key = baseKey + "font_bold"
                            if settings.contains(key):
                                style["font_bold"] = Preferences.toBool(
                                    settings.value(key))
                            key = baseKey + "font_italic"
                            if settings.contains(key):
                                style["font_italic"] = Preferences.toBool(
                                    settings.value(key))
                            key = baseKey + "font_underline"
                            if settings.contains(key):
                                style["font_underline"] = Preferences.toBool(
                                    settings.value(key))
                            
                            subStyleData["Style"] = style
                            
                            self.__subStyles[baseStyle][subStyle] = \
                                subStyleData
        
        self.__subStylesInitialized = True
    
    def loadDefaultSubStyles(self):
        """
        Public method to load the default sub-style definitions.
        """
        self.__subStyles = copy.deepcopy(self.defaultSubStyles)
    
    def readSubstyles(self, editor):
        """
        Public method to load the sub-styles and configure the editor.
        
        @param editor reference to the editor object
        @type QsciScintilla
        """
        subStyleBasesLength = editor.SendScintilla(
            editor.SCI_GETSUBSTYLEBASES, 0, None)
        if not subStyleBasesLength:
            # lexer does not support sub-styling
            return
        
        self.loadSubstyles()
        
        # free existing sub-styles first
        editor.SendScintilla(editor.SCI_FREESUBSTYLES)
        subStyleBases = b"\00" * (subStyleBasesLength + 1)
        editor.SendScintilla(editor.SCI_GETSUBSTYLEBASES, 0, subStyleBases)
        distanceToSecondary = editor.SendScintilla(
            editor.SCI_DISTANCETOSECONDARYSTYLES)
        
        subStyleBases = [b for b in bytearray(subStyleBases[:-1])]
        if distanceToSecondary:
            subStyleBases.extend(b + distanceToSecondary
                                 for b in subStyleBases[:])
        for baseStyleNo in subStyleBases:
            if baseStyleNo in self.__subStyles:
                subStylesData = self.__subStyles[baseStyleNo]
                subStyleLength = len(subStylesData)
                subStyleStart = editor.SendScintilla(
                    editor.SCI_ALLOCATESUBSTYLES, baseStyleNo, subStyleLength)
                if subStyleStart < 0:
                    subStyleLength = 0
                for subStyleIndex in range(subStyleLength):
                    styleNo = subStyleStart + subStyleIndex
                    subStyle = subStylesData[subStyleIndex]
                    # set the words
                    editor.SendScintilla(
                        editor.SCI_SETIDENTIFIERS,
                        styleNo,
                        subStyle["Words"].encode())
                    
                    # set the style
                    style = subStyle["Style"]
                    if "fore" in style:
                        color = QColor(
                            style["fore"] >> 16 & 0xff,
                            style["fore"] >> 8 & 0xff,
                            style["fore"] & 0xff,
                        )
                    else:
                        color = self.color(baseStyleNo)
                    self.setColor(color, styleNo)
                    
                    if "paper" in style:
                        color = QColor(
                            style["paper"] >> 16 & 0xff,
                            style["paper"] >> 8 & 0xff,
                            style["paper"] & 0xff,
                        )
                    else:
                        color = self.paper(baseStyleNo)
                    self.setPaper(color, styleNo)
                    
                    if "eolfill" in style:
                        eolFill = style["eolfill"]
                    else:
                        eolFill = self.eolFill(baseStyleNo)
                    self.setEolFill(eolFill, styleNo)
                    
                    font = self.font(baseStyleNo)
                    if "font_family" in style:
                        font.setFamily(style["font_family"])
                    if "font_size" in style:
                        font.setPointSize(style["font_size"])
                    if "font_bold" in style:
                        font.setBold(style["font_bold"])
                    if "font_italic" in style:
                        font.setItalic(style["font_italic"])
                    if "font_underline" in style:
                        font.setUnderline(style["font_underline"])
                    self.setFont(font, styleNo)
    
    def writeSubstyles(self):
        """
        Public method to save the sub-styles.
        """
        if not self.__subStylesInitialized:
            # make sure, the sub-styles have been initialized
            self.loadSubstyles()
        
        settings = Preferences.Prefs.settings
        
        for baseStyle in self.baseStyles:
            key = "Scintilla/{0}/style{1}/SubStyleLength".format(
                self.language(), baseStyle)
            settings.setValue(key, len(self.__subStyles[baseStyle]))
            for subStyle in self.__subStyles[baseStyle]:
                baseKey = "Scintilla/{0}/style{1}/substyle{2}/".format(
                    self.language(), baseStyle, subStyle)
                subStyleData = self.__subStyles[baseStyle][subStyle]
                
                settings.setValue(baseKey + "Description",
                                  subStyleData["Description"])
                settings.setValue(baseKey + "Words",
                                  subStyleData["Words"])
                
                style = subStyleData["Style"]
                if "fore" in style:
                    color = style["fore"]
                else:
                    col = self.color(baseStyle)
                    color = col.red() << 16 | col.green() << 8 | col.blue()
                settings.setValue(baseKey + "fore", color)
                if "paper" in style:
                    color = style["paper"]
                else:
                    col = self.paper(baseStyle)
                    color = col.red() << 16 | col.green() << 8 | col.blue()
                settings.setValue(baseKey + "paper", color)
                if "eolfill" in style:
                    eolfill = style["eolfill"]
                else:
                    eolfill = self.eolFill(baseStyle)
                settings.setValue(baseKey + "eolfill", eolfill)
                font = self.font(baseStyle)
                if "font_family" in style:
                    family = style["font_family"]
                else:
                    family = font.family()
                settings.setValue(baseKey + "font_family", family)
                if "font_size" in style:
                    size = style["font_size"]
                else:
                    size = font.pointSize()
                settings.setValue(baseKey + "font_size", size)
                if "font_bold" in style:
                    bold = style["font_bold"]
                else:
                    bold = font.bold()
                settings.setValue(baseKey + "font_bold", bold)
                if "font_italic" in style:
                    italic = style["font_italic"]
                else:
                    italic = font.italic()
                settings.setValue(baseKey + "font_italic", italic)
                if "font_underline" in style:
                    underline = style["font_underline"]
                else:
                    underline = font.underline()
                settings.setValue(baseKey + "font_underline", underline)
    
    def hasSubStyles(self):
        """
        Public method to indicate the support of sub-styles.
        
        @return flag indicating sub-styling support
        @rtype bool
        """
        return True
    
    def getBaseStyles(self):
        """
        Public method to get the list of supported base styles.
        
        @return list of base styles
        @rtype list of int
        """
        return self.baseStyles[:]
    
    def substylesCount(self, style):
        """
        Public method to get the number of defined sub-styles.
        
        @return number of defined sub-styles
        @rtype int
        """
        if style in self.__subStyles:
            count = len(self.__subStyles[style])
        else:
            count = 0
        
        return count
    
    def setSubstyleDescription(self, description, style, substyle):
        """
        Public method to set the description for a sub-style.
        
        @param description description to be set
        @type str
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        """
        if style in self.__subStyles and substyle in self.__subStyles[style]:
            self.__subStyles[style][substyle]["Description"] = description
    
    def substyleDescription(self, style, substyle):
        """
        Public method to get the description of a sub-style.
        
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        @return sub-style description
        @rtype str
        """
        if style in self.__subStyles and substyle in self.__subStyles[style]:
            desc = self.__subStyles[style][substyle]["Description"]
        else:
            desc = ""
        
        return desc
    
    def setSubstyleWords(self, words, style, substyle):
        """
        Public method to set the words for a sub-style.
        
        @param words words to be set separated by white-space
        @type str
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        """
        if style in self.__subStyles and substyle in self.__subStyles[style]:
            self.__subStyles[style][substyle]["Words"] = words
    
    def substyleWords(self, style, substyle):
        """
        Public method to get the words of a sub-style.
        
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        @return white-space separated word list
        @rtype str
        """
        if style in self.__subStyles and substyle in self.__subStyles[style]:
            words = self.__subStyles[style][substyle]["Words"]
        else:
            words = ""
        
        return words
    
    def setSubstyleColor(self, color, style, substyle):
        """
        Public method to set the foreground color of a sub-style.
        
        @param color foreground color to be set
        @type QColor
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        """
        if style in self.__subStyles and substyle in self.__subStyles[style]:
            self.__subStyles[style][substyle]["Style"]["fore"] = \
                color.red() << 16 | color.green() << 8 | color.blue()
    
    def substyleColor(self, style, substyle):
        """
        Public method to get the sub-style foreground color.
        
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        @return foreground color
        @rtype QColor
        """
        color = self.color(style)
        
        if style in self.__subStyles and substyle in self.__subStyles[style]:
            styleData = self.__subStyles[style][substyle]["Style"]
            if "fore" in styleData:
                color = QColor(
                    styleData["fore"] >> 16 & 0xff,
                    styleData["fore"] >> 8 & 0xff,
                    styleData["fore"] & 0xff,
                )
        
        return color
    
    def setSubstylePaper(self, color, style, substyle):
        """
        Public method to set the background color of a sub-style.
        
        @param color background color to be set
        @type QColor
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        """
        if style in self.__subStyles and substyle in self.__subStyles[style]:
            self.__subStyles[style][substyle]["Style"]["paper"] = \
                color.red() << 16 | color.green() << 8 | color.blue()
    
    def substylePaper(self, style, substyle):
        """
        Public method to get the sub-style background color.
        
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        @return background color
        @rtype QColor
        """
        color = self.paper(style)
        
        if style in self.__subStyles and substyle in self.__subStyles[style]:
            styleData = self.__subStyles[style][substyle]["Style"]
            if "paper" in styleData:
                color = QColor(
                    styleData["paper"] >> 16 & 0xff,
                    styleData["paper"] >> 8 & 0xff,
                    styleData["paper"] & 0xff,
                )
        
        return color
    
    def setSubstyleEolFill(self, eolFill, style, substyle):
        """
        Public method to set the eolfill flag of a sub-style.
        
        @param eolFill eolfill flag to be set
        @type bool
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        """
        if style in self.__subStyles and substyle in self.__subStyles[style]:
            self.__subStyles[style][substyle]["Style"]["eolfill"] = \
                eolFill
    
    def substyleEolFill(self, style, substyle):
        """
        Public method to get the eolfill flag.
        
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        @return eolfill flag
        @rtype bool
        """
        eolFill = self.eolFill(style)
        
        if style in self.__subStyles and substyle in self.__subStyles[style]:
            styleData = self.__subStyles[style][substyle]["Style"]
            if "eolfill" in styleData:
                eolFill = styleData["eolfill"]
        
        return eolFill
    
    def setSubstyleFont(self, font, style, substyle):
        """
        Public method to set the font of a sub-style.
        
        @param font font to be set
        @type QFont
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        """
        if style in self.__subStyles and substyle in self.__subStyles[style]:
            self.__subStyles[style][substyle]["Style"]["font_family"] = \
                font.family()
            self.__subStyles[style][substyle]["Style"]["font_size"] = \
                font.pointSize()
            self.__subStyles[style][substyle]["Style"]["font_bold"] = \
                font.bold()
            self.__subStyles[style][substyle]["Style"]["font_italic"] = \
                font.italic()
            self.__subStyles[style][substyle]["Style"]["font_underline"] = \
                font.underline()
    
    def substyleFont(self, style, substyle):
        """
        Public method to get the sub-style font.
        
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        @return font
        @rtype QFont
        """
        font = self.font(style)
        
        if style in self.__subStyles and substyle in self.__subStyles[style]:
            styleData = self.__subStyles[style][substyle]["Style"]
            if "font_family" in styleData:
                font.setFamily(styleData["font_family"])
            if "font_size" in styleData:
                font.setPointSize(styleData["font_size"])
            if "font_bold" in styleData:
                font.setBold(styleData["font_bold"])
            if "font_italic" in styleData:
                font.setItalic(styleData["font_italic"])
            if "font_underline" in styleData:
                font.setUnderline(styleData["font_underline"])
        
        return font
    
    def substyleDefaultDescription(self, style, substyle):
        """
        Public method to get the default description of a sub-style.
        
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        @return sub-style default description
        @rtype str
        """
        description = ""
        
        if style in self.defaultSubStyles and \
           substyle in self.defaultSubStyles[style]:
            styleData = self.defaultSubStyles[style][substyle]["Style"]
            description = styleData["Description"]
        
        return description
    
    def substyleDefaultWords(self, style, substyle):
        """
        Public method to get the default words of a sub-style.
        
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        @return white-space separated default word list
        @rtype str
        """
        words = ""
        
        if style in self.defaultSubStyles and \
           substyle in self.defaultSubStyles[style]:
            styleData = self.defaultSubStyles[style][substyle]["Style"]
            words = styleData["Words"]
        
        return words
    
    def substyleDefaultColor(self, style, substyle):
        """
        Public method to get the sub-style default foreground color.
        
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        @return default foreground color
        @rtype QColor
        """
        color = self.defaultColor(style)
        
        if style in self.defaultSubStyles and \
           substyle in self.defaultSubStyles[style]:
            styleData = self.defaultSubStyles[style][substyle]["Style"]
            if "fore" in styleData:
                color = QColor(
                    styleData["fore"] >> 16 & 0xff,
                    styleData["fore"] >> 8 & 0xff,
                    styleData["fore"] & 0xff,
                )
        
        return color
    
    def substyleDefaultPaper(self, style, substyle):
        """
        Public method to get the sub-style default background color.
        
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        @return default background color
        @rtype QColor
        """
        color = self.defaultPaper(style)
        
        if style in self.defaultSubStyles and \
           substyle in self.defaultSubStyles[style]:
            styleData = self.defaultSubStyles[style][substyle]["Style"]
            if "paper" in styleData:
                color = QColor(
                    styleData["paper"] >> 16 & 0xff,
                    styleData["paper"] >> 8 & 0xff,
                    styleData["paper"] & 0xff,
                )
        
        return color
    
    def substyleDefaultEolFill(self, style, substyle):
        """
        Public method to get the default eolfill flag.
        
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        @return default eolfill flag
        @rtype bool
        """
        eolFill = self.defaultEolFill(style)
        
        if style in self.defaultSubStyles and \
           substyle in self.defaultSubStyles[style]:
            styleData = self.defaultSubStyles[style][substyle]["Style"]
            if "eolfill" in styleData:
                eolFill = styleData["eolfill"]
        
        return eolFill
    
    def substyleDefaultFont(self, style, substyle):
        """
        Public method to get the default sub-style font.
        
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        @return default font
        @rtype QFont
        """
        font = self.defaultFont(style)
        
        if style in self.defaultSubStyles and \
           substyle in self.defaultSubStyles[style]:
            styleData = self.defaultSubStyles[style][substyle]["Style"]
            if "font_family" in styleData:
                font.setFamily(styleData["font_family"])
            if "font_size" in styleData:
                font.setPointSize(styleData["font_size"])
            if "font_bold" in styleData:
                font.setBold(styleData["font_bold"])
            if "font_italic" in styleData:
                font.setItalic(styleData["font_italic"])
            if "font_underline" in styleData:
                font.setUnderline(styleData["font_underline"])
        
        return font
    
    def addSubstyle(self, style):
        """
        Public method to add a sub-style to a given base style.
        
        @param style base style number
        @type int
        @return allocated sub-style number
        @rtype int
        """
        # TODO: implement this
        # add empty sub-style definition
    
    def delSubstyle(self, style, substyle):
        """
        Public method to delet a given sub-style definition.
        
        @param style base style number
        @type int
        @param substyle sub-style number
        @type int
        @return flag indicating successful deletion
        @rtype bool
        """
        ok = False
        
        if style in self.__subStyles and substyle in self.__subStyles[style]:
            del self.__subStyles[style][substyle]
            ok = True
        
        return ok

eric ide

mercurial