eric6/QScintilla/Editor.py

Sun, 04 Oct 2020 15:25:17 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 04 Oct 2020 15:25:17 +0200
changeset 7749
285855999b4e
parent 7746
12d5e1e6abd1
child 7759
51aa6c6b66f7
permissions
-rw-r--r--

Configuration Dialog: optimized the display of the various configuration pages.

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

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

"""
Module implementing the editor component of the eric6 IDE.
"""


import os
import re
import difflib

from PyQt5.QtCore import (
    QDir, QTimer, QModelIndex, QFileInfo, pyqtSignal, pyqtSlot,
    QCryptographicHash, QEvent, QDateTime, QRegExp, Qt, QPoint
)
from PyQt5.QtGui import QCursor, QPalette, QFont, QPixmap, QPainter
from PyQt5.QtWidgets import (
    QLineEdit, QActionGroup, QDialog, QInputDialog, QApplication, QMenu
)
from PyQt5.QtPrintSupport import QPrinter, QPrintDialog, QAbstractPrintDialog
from PyQt5.Qsci import QsciScintilla, QsciMacro, QsciStyledText

from E5Gui.E5Application import e5App
from E5Gui import E5FileDialog, E5MessageBox
from E5Utilities.E5Cache import E5Cache

from .QsciScintillaCompat import QsciScintillaCompat
from .EditorMarkerMap import EditorMarkerMap
from .SpellChecker import SpellChecker

import Preferences
import Utilities
from Utilities import MouseUtilities

import UI.PixmapCache

from ThirdParty.EditorConfig import editorconfig

EditorAutoCompletionListID = 1
TemplateCompletionListID = 2


class Editor(QsciScintillaCompat):
    """
    Class implementing the editor component of the eric6 IDE.
    
    @signal modificationStatusChanged(bool, QsciScintillaCompat) emitted when
        the modification status has changed
    @signal undoAvailable(bool) emitted to signal the undo availability
    @signal redoAvailable(bool) emitted to signal the redo availability
    @signal cursorChanged(str, int, int) emitted when the cursor position
        was changed
    @signal cursorLineChanged(int) emitted when the cursor line was changed
    @signal editorAboutToBeSaved(str) emitted before the editor is saved
    @signal editorSaved(str) emitted after the editor has been saved
    @signal editorRenamed(str) emitted after the editor got a new name
        (i.e. after a 'Save As')
    @signal captionChanged(str, QsciScintillaCompat) emitted when the caption
        is updated. Typically due to a readOnly attribute change.
    @signal breakpointToggled(QsciScintillaCompat) emitted when a breakpoint
        is toggled
    @signal bookmarkToggled(QsciScintillaCompat) emitted when a bookmark is
        toggled
    @signal syntaxerrorToggled(QsciScintillaCompat) emitted when a syntax error
        was discovered
    @signal autoCompletionAPIsAvailable(bool) emitted after the autocompletion
        function has been configured
    @signal coverageMarkersShown(bool) emitted after the coverage markers have
        been shown or cleared
    @signal taskMarkersUpdated(QsciScintillaCompat) emitted when the task
        markers were updated
    @signal changeMarkersUpdated(QsciScintillaCompat) emitted when the change
        markers were updated
    @signal showMenu(str, QMenu, QsciScintillaCompat) emitted when a menu is
        about to be shown. The name of the menu, a reference to the menu and
        a reference to the editor are given.
    @signal languageChanged(str) emitted when the editors language was set. The
        language is passed as a parameter.
    @signal eolChanged(str) emitted when the editors eol type was set. The eol
        string is passed as a parameter.
    @signal encodingChanged(str) emitted when the editors encoding was set. The
            encoding name is passed as a parameter.
    @signal spellLanguageChanged(str) emitted when the editor spell check
            language was set. The language is passed as a parameter.
    @signal lastEditPositionAvailable() emitted when a last edit position is
        available
    @signal refreshed() emitted to signal a refresh of the editor contents
    @signal settingsRead() emitted to signal, that the settings have been read
        and set
    @signal mouseDoubleClick(position, buttons) emitted to signal a mouse
        double click somewhere in the editor area
    """
    modificationStatusChanged = pyqtSignal(bool, QsciScintillaCompat)
    undoAvailable = pyqtSignal(bool)
    redoAvailable = pyqtSignal(bool)
    cursorChanged = pyqtSignal(str, int, int)
    cursorLineChanged = pyqtSignal(int)
    editorAboutToBeSaved = pyqtSignal(str)
    editorSaved = pyqtSignal(str)
    editorRenamed = pyqtSignal(str)
    captionChanged = pyqtSignal(str, QsciScintillaCompat)
    breakpointToggled = pyqtSignal(QsciScintillaCompat)
    bookmarkToggled = pyqtSignal(QsciScintillaCompat)
    syntaxerrorToggled = pyqtSignal(QsciScintillaCompat)
    autoCompletionAPIsAvailable = pyqtSignal(bool)
    coverageMarkersShown = pyqtSignal(bool)
    taskMarkersUpdated = pyqtSignal(QsciScintillaCompat)
    changeMarkersUpdated = pyqtSignal(QsciScintillaCompat)
    showMenu = pyqtSignal(str, QMenu, QsciScintillaCompat)
    languageChanged = pyqtSignal(str)
    eolChanged = pyqtSignal(str)
    encodingChanged = pyqtSignal(str)
    spellLanguageChanged = pyqtSignal(str)
    lastEditPositionAvailable = pyqtSignal()
    refreshed = pyqtSignal()
    settingsRead = pyqtSignal()
    mouseDoubleClick = pyqtSignal(QPoint, int)
    
    WarningCode = 1
    WarningStyle = 2
    
    # Autocompletion icon definitions
    ClassID = 1
    ClassProtectedID = 2
    ClassPrivateID = 3
    MethodID = 4
    MethodProtectedID = 5
    MethodPrivateID = 6
    AttributeID = 7
    AttributeProtectedID = 8
    AttributePrivateID = 9
    EnumID = 10
    KeywordsID = 11
    ModuleID = 12
    
    FromDocumentID = 99
    
    TemplateImageID = 100
    
    # Cooperation related definitions
    Separator = "@@@"
    
    StartEditToken = "START_EDIT"
    EndEditToken = "END_EDIT"
    CancelEditToken = "CANCEL_EDIT"
    RequestSyncToken = "REQUEST_SYNC"
    SyncToken = "SYNC"
    
    VcsConflictMarkerLineRegExpList = (
        r"""^<<<<<<< .*?$""",
        r"""^\|\|\|\|\|\|\| .*?$""",
        r"""^=======.*?$""",
        r"""^>>>>>>> .*?$""",
    )
    
    def __init__(self, dbs, fn="", vm=None,
                 filetype="", editor=None, tv=None):
        """
        Constructor
        
        @param dbs reference to the debug server object
        @param fn name of the file to be opened (string). If it is None,
                a new (empty) editor is opened
        @param vm reference to the view manager object
            (ViewManager.ViewManager)
        @param filetype type of the source file (string)
        @param editor reference to an Editor object, if this is a cloned view
        @param tv reference to the task viewer object
        @exception IOError raised to indicate an issue accessing the file
        """
        super(Editor, self).__init__()
        self.setAttribute(Qt.WA_KeyCompression)
        self.setUtf8(True)
        
        self.dbs = dbs
        self.taskViewer = tv
        self.__setFileName(fn)
        self.vm = vm
        self.filetype = filetype
        self.filetypeByFlag = False
        self.noName = ""
        self.project = e5App().getObject("Project")
        
        # clear some variables
        self.lastHighlight = None   # remember the last highlighted line
        self.lastErrorMarker = None   # remember the last error line
        self.lastCurrMarker = None   # remember the last current line
        
        self.breaks = {}
        # key:   marker handle,
        # value: (lineno, condition, temporary,
        #         enabled, ignorecount)
        self.bookmarks = []
        # bookmarks are just a list of handles to the
        # bookmark markers
        self.syntaxerrors = {}
        # key:   marker handle
        # value: list of (error message, error index)
        self.warnings = {}
        # key:   marker handle
        # value: list of (warning message, warning type)
        self.notcoveredMarkers = []  # just a list of marker handles
        self.showingNotcoveredMarkers = False
        
        self.lexer_ = None
        self.apiLanguage = ''
        
        self.__loadEditorConfig()
        
        self.condHistory = []
        self.__lexerReset = False
        self.completer = None
        self.encoding = self.__getEditorConfig("DefaultEncoding")
        self.lastModified = 0
        self.line = -1
        self.inReopenPrompt = False
        # true if the prompt to reload a changed source is present
        self.inFileRenamed = False
        # true if we are propagating a rename action
        self.inLanguageChanged = False
        # true if we are propagating a language change
        self.inEolChanged = False
        # true if we are propagating an eol change
        self.inEncodingChanged = False
        # true if we are propagating an encoding change
        self.inDragDrop = False
        # true if we are in drop mode
        self.inLinesChanged = False
        # true if we are propagating a lines changed event
        self.__hasTaskMarkers = False
        # no task markers present
            
        self.macros = {}    # list of defined macros
        self.curMacro = None
        self.recording = False
        
        self.acAPI = False
        
        self.__lastEditPosition = None
        self.__annotationLines = 0
        
        # list of clones
        self.__clones = []
        
        # clear QScintilla defined keyboard commands
        # we do our own handling through the view manager
        self.clearAlternateKeys()
        self.clearKeys()
        
        self.__markerMap = EditorMarkerMap(self)
        
        # initialize the mark occurrences timer
        self.__markOccurrencesTimer = QTimer(self)
        self.__markOccurrencesTimer.setSingleShot(True)
        self.__markOccurrencesTimer.setInterval(
            Preferences.getEditor("MarkOccurrencesTimeout"))
        self.__markOccurrencesTimer.timeout.connect(self.__markOccurrences)
        self.__markedText = ""
        self.__searchIndicatorLines = []
        
        # initialize some spellchecking stuff
        self.spell = None
        self.lastLine = 0
        self.lastIndex = 0
        self.__inSpellLanguageChanged = False
        
        # initialize some cooperation stuff
        self.__isSyncing = False
        self.__receivedWhileSyncing = []
        self.__savedText = ""
        self.__inSharedEdit = False
        self.__isShared = False
        self.__inRemoteSharedEdit = False
        
        # connect signals before loading the text
        self.modificationChanged.connect(self.__modificationChanged)
        self.cursorPositionChanged.connect(self.__cursorPositionChanged)
        self.modificationAttempted.connect(self.__modificationReadOnly)
        
        # define the margins markers
        self.__changeMarkerSaved = self.markerDefine(
            self.__createChangeMarkerPixmap(
                "OnlineChangeTraceMarkerSaved"))
        self.__changeMarkerUnsaved = self.markerDefine(
            self.__createChangeMarkerPixmap(
                "OnlineChangeTraceMarkerUnsaved"))
        self.breakpoint = self.markerDefine(
            UI.PixmapCache.getPixmap("break"))
        self.cbreakpoint = self.markerDefine(
            UI.PixmapCache.getPixmap("cBreak"))
        self.tbreakpoint = self.markerDefine(
            UI.PixmapCache.getPixmap("tBreak"))
        self.tcbreakpoint = self.markerDefine(
            UI.PixmapCache.getPixmap("tCBreak"))
        self.dbreakpoint = self.markerDefine(
            UI.PixmapCache.getPixmap("breakDisabled"))
        self.bookmark = self.markerDefine(
            UI.PixmapCache.getPixmap("bookmark16"))
        self.syntaxerror = self.markerDefine(
            UI.PixmapCache.getPixmap("syntaxError"))
        self.notcovered = self.markerDefine(
            UI.PixmapCache.getPixmap("notcovered"))
        self.taskmarker = self.markerDefine(
            UI.PixmapCache.getPixmap("task"))
        self.warning = self.markerDefine(
            UI.PixmapCache.getPixmap("warning"))
        
        # define the line markers
        if Preferences.getEditor("LineMarkersBackground"):
            self.currentline = self.markerDefine(QsciScintilla.Background)
            self.errorline = self.markerDefine(QsciScintilla.Background)
            self.__setLineMarkerColours()
        else:
            self.currentline = self.markerDefine(
                UI.PixmapCache.getPixmap("currentLineMarker"))
            self.errorline = self.markerDefine(
                UI.PixmapCache.getPixmap("errorLineMarker"))
        
        self.breakpointMask = (
            (1 << self.breakpoint) |
            (1 << self.cbreakpoint) |
            (1 << self.tbreakpoint) |
            (1 << self.tcbreakpoint) |
            (1 << self.dbreakpoint)
        )
        
        self.changeMarkersMask = (
            (1 << self.__changeMarkerSaved) |
            (1 << self.__changeMarkerUnsaved)
        )
        
        # configure the margins
        self.__setMarginsDisplay()
        self.linesChanged.connect(self.__resizeLinenoMargin)
        
        self.marginClicked.connect(self.__marginClicked)
        
        # set the eol mode
        self.__setEolMode()
        
        # set the text display
        self.__setTextDisplay()
        
        # initialize the online syntax check timer
        try:
            self.syntaxCheckService = e5App().getObject('SyntaxCheckService')
            self.syntaxCheckService.syntaxChecked.connect(
                self.__processSyntaxCheckResult)
            self.syntaxCheckService.error.connect(
                self.__processSyntaxCheckError)
            self.__initOnlineSyntaxCheck()
        except KeyError:
            self.syntaxCheckService = None
        
        self.isResourcesFile = False
        if editor is None:
            if self.fileName:
                if (
                    (QFileInfo(self.fileName).size() // 1024) >
                        Preferences.getEditor("WarnFilesize")
                ):
                    res = E5MessageBox.yesNo(
                        self,
                        self.tr("Open File"),
                        self.tr("""<p>The size of the file <b>{0}</b>"""
                                """ is <b>{1} KB</b>."""
                                """ Do you really want to load it?</p>""")
                        .format(self.fileName,
                                QFileInfo(self.fileName).size() // 1024),
                        icon=E5MessageBox.Warning)
                    if not res:
                        raise IOError()
                self.readFile(self.fileName, True)
                self.__bindLexer(self.fileName)
                self.__bindCompleter(self.fileName)
                self.checkSyntax()
                self.isResourcesFile = self.fileName.endswith(".qrc")
                
                self.__convertTabs()
                
                self.recolor()
        else:
            # clone the given editor
            self.setDocument(editor.document())
            self.breaks = editor.breaks
            self.bookmarks = editor.bookmarks
            self.syntaxerrors = editor.syntaxerrors
            self.notcoveredMarkers = editor.notcoveredMarkers
            self.showingNotcoveredMarkers = editor.showingNotcoveredMarkers
            self.isResourcesFile = editor.isResourcesFile
            self.lastModified = editor.lastModified
            
            self.addClone(editor)
            editor.addClone(self)
        
        self.gotoLine(1)
        
        # set the text display again
        self.__setTextDisplay()
        
        # set the auto-completion function
        self.__acContext = True
        self.__acText = ""
        self.__acCompletions = set()
        self.__acCompletionsFinished = 0
        self.__acCache = E5Cache(
            size=Preferences.getEditor("AutoCompletionCacheSize"))
        self.__acCache.setMaximumCacheTime(
            Preferences.getEditor("AutoCompletionCacheTime"))
        self.__acCacheEnabled = Preferences.getEditor(
            "AutoCompletionCacheEnabled")
        self.__acTimer = QTimer(self)
        self.__acTimer.setSingleShot(True)
        self.__acTimer.setInterval(
            Preferences.getEditor("AutoCompletionTimeout"))
        self.__acTimer.timeout.connect(self.__autoComplete)
        
        self.__acWatchdog = QTimer(self)
        self.__acWatchdog.setSingleShot(True)
        self.__acWatchdog.setInterval(
            Preferences.getEditor("AutoCompletionWatchdogTime"))
        self.__acWatchdog.timeout.connect(self.autoCompleteQScintilla)
        
        self.userListActivated.connect(self.__completionListSelected)
        self.SCN_CHARADDED.connect(self.__charAdded)
        self.SCN_AUTOCCANCELLED.connect(self.__autocompletionCancelled)
        
        self.__completionListHookFunctions = {}
        self.__completionListAsyncHookFunctions = {}
        self.__setAutoCompletion()
        
        # set the call-tips function
        self.__ctHookFunctions = {}
        self.__setCallTips()
        
        # set the mouse click handlers (fired on mouse release)
        self.__mouseClickHandlers = {}
        # dictionary with tuple of keyboard modifier and mouse button as key
        # and tuple of plug-in name and function as value
        
        sh = self.sizeHint()
        if sh.height() < 300:
            sh.setHeight(300)
        self.resize(sh)
        
        # Make sure tabbing through a QWorkspace works.
        self.setFocusPolicy(Qt.StrongFocus)
        
        self.__updateReadOnly(True)
        
        self.setWhatsThis(self.tr(
            """<b>A Source Editor Window</b>"""
            """<p>This window is used to display and edit a source file."""
            """  You can open as many of these as you like. The name of the"""
            """ file is displayed in the window's titlebar.</p>"""
            """<p>In order to set breakpoints just click in the space"""
            """ between the line numbers and the fold markers. Via the"""
            """ context menu of the margins they may be edited.</p>"""
            """<p>In order to set bookmarks just Shift click in the space"""
            """ between the line numbers and the fold markers.</p>"""
            """<p>These actions can be reversed via the context menu.</p>"""
            """<p>Ctrl clicking on a syntax error marker shows some info"""
            """ about this error.</p>"""
        ))
        
        # Set the editors size, if it is too big for the view manager.
        if self.vm is not None:
            req = self.size()
            bnd = req.boundedTo(self.vm.size())
            
            if bnd.width() < req.width() or bnd.height() < req.height():
                self.resize(bnd)
            
        # set the autosave flag
        self.autosaveEnabled = Preferences.getEditor("AutosaveInterval") > 0
        self.autosaveManuallyDisabled = False
        
        self.__initContextMenu()
        self.__initContextMenuMargins()
        
        self.__checkEol()
        if editor is None:
            self.__checkLanguage()
            self.__checkEncoding()
            self.__checkSpellLanguage()
        else:
            # it's a clone
            self.__languageChanged(editor.apiLanguage, propagate=False)
            self.__encodingChanged(editor.encoding, propagate=False)
            self.__spellLanguageChanged(editor.getSpellingLanguage(),
                                        propagate=False)
        
        self.setAcceptDrops(True)
        
        # breakpoint handling
        self.breakpointModel = self.dbs.getBreakPointModel()
        self.__restoreBreakpoints()
        self.breakpointModel.rowsAboutToBeRemoved.connect(
            self.__deleteBreakPoints)
        self.breakpointModel.dataAboutToBeChanged.connect(
            self.__breakPointDataAboutToBeChanged)
        self.breakpointModel.dataChanged.connect(
            self.__changeBreakPoints)
        self.breakpointModel.rowsInserted.connect(
            self.__addBreakPoints)
        self.SCN_MODIFIED.connect(self.__modified)
        
        # establish connection to some ViewManager action groups
        self.addActions(self.vm.editorActGrp.actions())
        self.addActions(self.vm.editActGrp.actions())
        self.addActions(self.vm.copyActGrp.actions())
        self.addActions(self.vm.viewActGrp.actions())
        
        # register images to be shown in autocompletion lists
        self.__registerImages()
        
        # connect signals after loading the text
        self.textChanged.connect(self.__textChanged)
        
        # initialize the online change trace timer
        self.__initOnlineChangeTrace()
        
        if (
            self.fileName and
            self.project.isOpen() and
            self.project.isProjectSource(self.fileName)
        ):
            self.project.projectPropertiesChanged.connect(
                self.__projectPropertiesChanged)
        
        self.grabGesture(Qt.PinchGesture)
        
        self.SCN_ZOOM.connect(self.__markerMap.update)
        self.__markerMap.update()
    
    def __setFileName(self, name):
        """
        Private method to set the file name of the current file.
        
        @param name name of the current file
        @type str
        """
        self.fileName = name
        
        if self.fileName:
            self.__fileNameExtension = (
                os.path.splitext(self.fileName)[1][1:].lower()
            )
        else:
            self.__fileNameExtension = ""
    
    def __registerImages(self):
        """
        Private method to register images for autocompletion lists.
        """
        self.registerImage(self.ClassID,
                           UI.PixmapCache.getPixmap("class"))
        self.registerImage(self.ClassProtectedID,
                           UI.PixmapCache.getPixmap("class_protected"))
        self.registerImage(self.ClassPrivateID,
                           UI.PixmapCache.getPixmap("class_private"))
        self.registerImage(self.MethodID,
                           UI.PixmapCache.getPixmap("method"))
        self.registerImage(self.MethodProtectedID,
                           UI.PixmapCache.getPixmap("method_protected"))
        self.registerImage(self.MethodPrivateID,
                           UI.PixmapCache.getPixmap("method_private"))
        self.registerImage(self.AttributeID,
                           UI.PixmapCache.getPixmap("attribute"))
        self.registerImage(self.AttributeProtectedID,
                           UI.PixmapCache.getPixmap("attribute_protected"))
        self.registerImage(self.AttributePrivateID,
                           UI.PixmapCache.getPixmap("attribute_private"))
        self.registerImage(self.EnumID,
                           UI.PixmapCache.getPixmap("enum"))
        self.registerImage(self.KeywordsID,
                           UI.PixmapCache.getPixmap("keywords"))
        self.registerImage(self.ModuleID,
                           UI.PixmapCache.getPixmap("module"))
        
        self.registerImage(self.FromDocumentID,
                           UI.PixmapCache.getPixmap("editor"))
        
        self.registerImage(self.TemplateImageID,
                           UI.PixmapCache.getPixmap("templateViewer"))
    
    def addClone(self, editor):
        """
        Public method to add a clone to our list.
        
        @param editor reference to the cloned editor
        @type Editor
        """
        self.__clones.append(editor)
        
        editor.editorRenamed.connect(self.fileRenamed)
        editor.languageChanged.connect(self.languageChanged)
        editor.eolChanged.connect(self.__eolChanged)
        editor.encodingChanged.connect(self.__encodingChanged)
        editor.spellLanguageChanged.connect(self.__spellLanguageChanged)
        
    def removeClone(self, editor):
        """
        Public method to remove a clone from our list.
        
        @param editor reference to the cloned editor
        @type Editor
        """
        if editor in self.__clones:
            editor.editorRenamed.disconnect(self.fileRenamed)
            editor.languageChanged.disconnect(self.languageChanged)
            editor.eolChanged.disconnect(self.__eolChanged)
            editor.encodingChanged.disconnect(self.__encodingChanged)
            editor.spellLanguageChanged.disconnect(self.__spellLanguageChanged)
            self.__clones.remove(editor)
        
    def isClone(self, editor):
        """
        Public method to test, if the given editor is a clone.
        
        @param editor reference to the cloned editor
        @type Editor
        @return flag indicating a clone
        @rtype bool
        """
        return editor in self.__clones
        
    def __bindName(self, line0):
        """
        Private method to generate a dummy filename for binding a lexer.
        
        @param line0 first line of text to use in the generation process
            (string)
        @return dummy file name to be used for binding a lexer (string)
        """
        bindName = ""
        line0 = line0.lower()
        
        # check first line if it does not start with #!
        if line0.startswith(("<html", "<!doctype html", "<?php")):
            bindName = "dummy.html"
        elif line0.startswith(("<?xml", "<!doctype")):
            bindName = "dummy.xml"
        elif line0.startswith("index: "):
            bindName = "dummy.diff"
        elif line0.startswith("\\documentclass"):
            bindName = "dummy.tex"
        
        if not bindName and self.filetype:
            # check filetype
            from . import Lexers
            supportedLanguages = Lexers.getSupportedLanguages()
            if self.filetype in supportedLanguages:
                bindName = supportedLanguages[self.filetype][1]
            elif self.filetype in ["Python", "Python3", "MicroPython"]:
                bindName = "dummy.py"
        
        if not bindName and line0.startswith("#!"):
            # #! marker detection
            if "python3" in line0:
                bindName = "dummy.py"
                self.filetype = "Python3"
            elif "python" in line0:
                bindName = "dummy.py"
                self.filetype = "Python3"
            elif "pypy3" in line0:
                bindName = "dummy.py"
                self.filetype = "Python3"
            elif "pypy" in line0:
                bindName = "dummy.py"
                self.filetype = "Python3"
            elif ("/bash" in line0 or "/sh" in line0):
                bindName = "dummy.sh"
            elif "ruby" in line0:
                bindName = "dummy.rb"
                self.filetype = "Ruby"
            elif "perl" in line0:
                bindName = "dummy.pl"
            elif "lua" in line0:
                bindName = "dummy.lua"
            elif "dmd" in line0:
                bindName = "dummy.d"
                self.filetype = "D"
        
        if not bindName:
            # mode line detection: -*- mode: python -*-
            match = re.search(r"mode[:=]\s*([-\w_.]+)", line0)
            if match:
                mode = match.group(1).lower()
                if mode in ["python3", "pypy3"]:
                    bindName = "dummy.py"
                    self.filetype = "Python3"
                elif mode == "ruby":
                    bindName = "dummy.rb"
                    self.filetype = "Ruby"
                elif mode == "perl":
                    bindName = "dummy.pl"
                elif mode == "lua":
                    bindName = "dummy.lua"
                elif mode in ["dmd", "d"]:
                    bindName = "dummy.d"
                    self.filetype = "D"
        
        if not bindName:
            bindName = self.fileName
        
        return bindName
        
    def getMenu(self, menuName):
        """
        Public method to get a reference to the main context menu or a submenu.
        
        @param menuName name of the menu (string)
        @return reference to the requested menu (QMenu) or None
        """
        try:
            return self.__menus[menuName]
        except KeyError:
            return None
        
    def hasMiniMenu(self):
        """
        Public method to check the miniMenu flag.
        
        @return flag indicating a minimized context menu (boolean)
        """
        return self.miniMenu
        
    def __initContextMenu(self):
        """
        Private method used to setup the context menu.
        """
        self.miniMenu = Preferences.getEditor("MiniContextMenu")
        
        self.menuActs = {}
        self.menu = QMenu()
        self.__menus = {
            "Main": self.menu,
        }
        
        self.languagesMenu = self.__initContextMenuLanguages()
        self.__menus["Languages"] = self.languagesMenu
        if self.isResourcesFile:
            self.resourcesMenu = self.__initContextMenuResources()
            self.__menus["Resources"] = self.resourcesMenu
        else:
            self.checksMenu = self.__initContextMenuChecks()
            self.menuShow = self.__initContextMenuShow()
            self.graphicsMenu = self.__initContextMenuGraphics()
            self.autocompletionMenu = self.__initContextMenuAutocompletion()
            self.__menus["Checks"] = self.checksMenu
            self.__menus["Show"] = self.menuShow
            self.__menus["Graphics"] = self.graphicsMenu
            self.__menus["Autocompletion"] = self.autocompletionMenu
        self.toolsMenu = self.__initContextMenuTools()
        self.__menus["Tools"] = self.toolsMenu
        self.exportersMenu = self.__initContextMenuExporters()
        self.__menus["Exporters"] = self.exportersMenu
        self.eolMenu = self.__initContextMenuEol()
        self.__menus["Eol"] = self.eolMenu
        self.encodingsMenu = self.__initContextMenuEncodings()
        self.__menus["Encodings"] = self.encodingsMenu
        self.spellLanguagesMenu = self.__initContextMenuSpellLanguages()
        self.__menus["SpellLanguages"] = self.spellLanguagesMenu
        
        self.menuActs["Undo"] = self.menu.addAction(
            UI.PixmapCache.getIcon("editUndo"),
            self.tr('Undo'), self.undo)
        self.menuActs["Redo"] = self.menu.addAction(
            UI.PixmapCache.getIcon("editRedo"),
            self.tr('Redo'), self.redo)
        self.menuActs["Revert"] = self.menu.addAction(
            self.tr("Revert to last saved state"),
            self.revertToUnmodified)
        self.menu.addSeparator()
        self.menuActs["Cut"] = self.menu.addAction(
            UI.PixmapCache.getIcon("editCut"),
            self.tr('Cut'), self.cut)
        self.menuActs["Copy"] = self.menu.addAction(
            UI.PixmapCache.getIcon("editCopy"),
            self.tr('Copy'), self.copy)
        self.menuActs["Paste"] = self.menu.addAction(
            UI.PixmapCache.getIcon("editPaste"),
            self.tr('Paste'), self.paste)
        if not self.miniMenu:
            self.menuActs["ExecuteSelection"] = self.menu.addAction(
                self.tr("Execute Selection In Console"),
                self.__executeSelection)
            self.menu.addSeparator()
            self.menu.addAction(
                UI.PixmapCache.getIcon("editIndent"),
                self.tr('Indent'), self.indentLineOrSelection)
            self.menu.addAction(
                UI.PixmapCache.getIcon("editUnindent"),
                self.tr('Unindent'), self.unindentLineOrSelection)
            self.menuActs["Comment"] = self.menu.addAction(
                UI.PixmapCache.getIcon("editComment"),
                self.tr('Comment'), self.commentLineOrSelection)
            self.menuActs["Uncomment"] = self.menu.addAction(
                UI.PixmapCache.getIcon("editUncomment"),
                self.tr('Uncomment'), self.uncommentLineOrSelection)
            self.menuActs["StreamComment"] = self.menu.addAction(
                self.tr('Stream Comment'),
                self.streamCommentLineOrSelection)
            self.menuActs["BoxComment"] = self.menu.addAction(
                self.tr('Box Comment'),
                self.boxCommentLineOrSelection)
            self.menu.addSeparator()
            self.menu.addAction(
                self.tr('Select to brace'), self.selectToMatchingBrace)
            self.menu.addAction(self.tr('Select all'), self.__selectAll)
            self.menu.addAction(
                self.tr('Deselect all'), self.__deselectAll)
        else:
            self.menuActs["ExecuteSelection"] = None
        self.menu.addSeparator()
        self.menuActs["SpellCheck"] = self.menu.addAction(
            UI.PixmapCache.getIcon("spellchecking"),
            self.tr('Check spelling...'), self.checkSpelling)
        self.menuActs["SpellCheckSelection"] = self.menu.addAction(
            UI.PixmapCache.getIcon("spellchecking"),
            self.tr('Check spelling of selection...'),
            self.__checkSpellingSelection)
        self.menuActs["SpellCheckRemove"] = self.menu.addAction(
            self.tr("Remove from dictionary"),
            self.__removeFromSpellingDictionary)
        self.menuActs["SpellCheckLanguages"] = self.menu.addMenu(
            self.spellLanguagesMenu)
        self.menu.addSeparator()
        self.menu.addAction(
            self.tr('Shorten empty lines'), self.shortenEmptyLines)
        self.menu.addSeparator()
        self.menuActs["Languages"] = self.menu.addMenu(self.languagesMenu)
        self.menuActs["Encodings"] = self.menu.addMenu(self.encodingsMenu)
        self.menuActs["Eol"] = self.menu.addMenu(self.eolMenu)
        self.menu.addSeparator()
        self.menuActs["MonospacedFont"] = self.menu.addAction(
            self.tr("Use Monospaced Font"),
            self.handleMonospacedEnable)
        self.menuActs["MonospacedFont"].setCheckable(True)
        self.menuActs["MonospacedFont"].setChecked(self.useMonospaced)
        self.menuActs["AutosaveEnable"] = self.menu.addAction(
            self.tr("Autosave enabled"), self.__autosaveEnable)
        self.menuActs["AutosaveEnable"].setCheckable(True)
        self.menuActs["AutosaveEnable"].setChecked(self.autosaveEnabled)
        self.menuActs["TypingAidsEnabled"] = self.menu.addAction(
            self.tr("Typing aids enabled"), self.__toggleTypingAids)
        self.menuActs["TypingAidsEnabled"].setCheckable(True)
        self.menuActs["TypingAidsEnabled"].setEnabled(
            self.completer is not None)
        self.menuActs["TypingAidsEnabled"].setChecked(
            self.completer is not None and self.completer.isEnabled())
        self.menuActs["AutoCompletionEnable"] = self.menu.addAction(
            self.tr("Automatic Completion enabled"),
            self.__toggleAutoCompletionEnable)
        self.menuActs["AutoCompletionEnable"].setCheckable(True)
        self.menuActs["AutoCompletionEnable"].setChecked(
            self.autoCompletionThreshold() != -1)
        if not self.isResourcesFile:
            self.menu.addMenu(self.autocompletionMenu)
            self.menuActs["calltip"] = self.menu.addAction(
                self.tr('Calltip'), self.callTip)
            self.menuActs["codeInfo"] = self.menu.addAction(
                self.tr('Code Info'), self.__showCodeInfo)
        self.menu.addSeparator()
        if self.isResourcesFile:
            self.menu.addMenu(self.resourcesMenu)
        else:
            self.menuActs["Check"] = self.menu.addMenu(self.checksMenu)
            self.menu.addSeparator()
            self.menuActs["Show"] = self.menu.addMenu(self.menuShow)
            self.menu.addSeparator()
            self.menuActs["Diagrams"] = self.menu.addMenu(self.graphicsMenu)
        self.menu.addSeparator()
        self.menuActs["Tools"] = self.menu.addMenu(self.toolsMenu)
        self.menu.addSeparator()
        self.menu.addAction(
            UI.PixmapCache.getIcon("documentNewView"),
            self.tr('New Document View'), self.__newView)
        self.menuActs["NewSplit"] = self.menu.addAction(
            UI.PixmapCache.getIcon("splitVertical"),
            self.tr('New Document View (with new split)'),
            self.__newViewNewSplit)
        self.menuActs["NewSplit"].setEnabled(self.vm.canSplit())
        self.menu.addAction(
            UI.PixmapCache.getIcon("close"),
            self.tr('Close'), self.__contextClose)
        self.menu.addSeparator()
        self.reopenEncodingMenu = self.__initContextMenuReopenWithEncoding()
        self.menuActs["Reopen"] = self.menu.addMenu(self.reopenEncodingMenu)
        self.menuActs["Save"] = self.menu.addAction(
            UI.PixmapCache.getIcon("fileSave"),
            self.tr('Save'), self.__contextSave)
        self.menu.addAction(
            UI.PixmapCache.getIcon("fileSaveAs"),
            self.tr('Save As...'), self.__contextSaveAs)
        self.menu.addAction(
            UI.PixmapCache.getIcon("fileSaveCopy"),
            self.tr('Save Copy...'), self.__contextSaveCopy)
        if not self.miniMenu:
            self.menu.addMenu(self.exportersMenu)
            self.menu.addSeparator()
            self.menuActs["OpenRejections"] = self.menu.addAction(
                self.tr("Open 'rejection' file"),
                self.__contextOpenRejections)
            self.menu.addSeparator()
            self.menu.addAction(
                UI.PixmapCache.getIcon("printPreview"),
                self.tr("Print Preview"), self.printPreviewFile)
            self.menu.addAction(
                UI.PixmapCache.getIcon("print"),
                self.tr('Print'), self.printFile)
        else:
            self.menuActs["OpenRejections"] = None
        
        self.menu.aboutToShow.connect(self.__showContextMenu)
        
        self.spellingMenu = QMenu()
        self.__menus["Spelling"] = self.spellingMenu
        
        self.spellingMenu.aboutToShow.connect(self.__showContextMenuSpelling)
        self.spellingMenu.triggered.connect(
            self.__contextMenuSpellingTriggered)

    def __initContextMenuAutocompletion(self):
        """
        Private method used to setup the Checks context sub menu.
        
        @return reference to the generated menu (QMenu)
        """
        menu = QMenu(self.tr('Complete'))
        
        self.menuActs["acDynamic"] = menu.addAction(
            self.tr('Complete'), self.autoComplete)
        menu.addSeparator()
        self.menuActs["acClearCache"] = menu.addAction(
            self.tr("Clear Completions Cache"), self.__clearCompletionsCache)
        menu.addSeparator()
        menu.addAction(
            self.tr('Complete from Document'), self.autoCompleteFromDocument)
        self.menuActs["acAPI"] = menu.addAction(
            self.tr('Complete from APIs'), self.autoCompleteFromAPIs)
        self.menuActs["acAPIDocument"] = menu.addAction(
            self.tr('Complete from Document and APIs'),
            self.autoCompleteFromAll)
        
        menu.aboutToShow.connect(self.__showContextMenuAutocompletion)
        
        return menu

    def __initContextMenuChecks(self):
        """
        Private method used to setup the Checks context sub menu.
        
        @return reference to the generated menu (QMenu)
        """
        menu = QMenu(self.tr('Check'))
        menu.aboutToShow.connect(self.__showContextMenuChecks)
        return menu

    def __initContextMenuTools(self):
        """
        Private method used to setup the Tools context sub menu.
        
        @return reference to the generated menu (QMenu)
        """
        menu = QMenu(self.tr('Tools'))
        menu.aboutToShow.connect(self.__showContextMenuTools)
        return menu

    def __initContextMenuShow(self):
        """
        Private method used to setup the Show context sub menu.
        
        @return reference to the generated menu (QMenu)
        """
        menu = QMenu(self.tr('Show'))
        
        menu.addAction(self.tr('Code metrics...'), self.__showCodeMetrics)
        self.coverageMenuAct = menu.addAction(
            self.tr('Code coverage...'), self.__showCodeCoverage)
        self.coverageShowAnnotationMenuAct = menu.addAction(
            self.tr('Show code coverage annotations'),
            self.codeCoverageShowAnnotations)
        self.coverageHideAnnotationMenuAct = menu.addAction(
            self.tr('Hide code coverage annotations'),
            self.__codeCoverageHideAnnotations)
        self.profileMenuAct = menu.addAction(
            self.tr('Profile data...'), self.__showProfileData)
        
        menu.aboutToShow.connect(self.__showContextMenuShow)
        
        return menu
        
    def __initContextMenuGraphics(self):
        """
        Private method used to setup the diagrams context sub menu.
        
        @return reference to the generated menu (QMenu)
        """
        menu = QMenu(self.tr('Diagrams'))
        
        menu.addAction(
            self.tr('Class Diagram...'), self.__showClassDiagram)
        menu.addAction(
            self.tr('Package Diagram...'), self.__showPackageDiagram)
        menu.addAction(
            self.tr('Imports Diagram...'), self.__showImportsDiagram)
        self.applicationDiagramMenuAct = menu.addAction(
            self.tr('Application Diagram...'),
            self.__showApplicationDiagram)
        menu.addSeparator()
        menu.addAction(
            UI.PixmapCache.getIcon("open"),
            self.tr("Load Diagram..."), self.__loadDiagram)
        
        menu.aboutToShow.connect(self.__showContextMenuGraphics)
        
        return menu

    def __initContextMenuLanguages(self):
        """
        Private method used to setup the Languages context sub menu.
        
        @return reference to the generated menu (QMenu)
        """
        menu = QMenu(self.tr("Languages"))
        
        self.languagesActGrp = QActionGroup(self)
        self.noLanguageAct = menu.addAction(
            UI.PixmapCache.getIcon("fileText"),
            self.tr("No Language"))
        self.noLanguageAct.setCheckable(True)
        self.noLanguageAct.setData("None")
        self.languagesActGrp.addAction(self.noLanguageAct)
        menu.addSeparator()
        
        from . import Lexers
        self.supportedLanguages = {}
        supportedLanguages = Lexers.getSupportedLanguages()
        languages = sorted(list(supportedLanguages.keys()))
        for language in languages:
            if language != "Guessed":
                self.supportedLanguages[language] = (
                    supportedLanguages[language][:2]
                )
                act = menu.addAction(
                    UI.PixmapCache.getIcon(supportedLanguages[language][2]),
                    self.supportedLanguages[language][0])
                act.setCheckable(True)
                act.setData(language)
                self.supportedLanguages[language].append(act)
                self.languagesActGrp.addAction(act)
        
        menu.addSeparator()
        self.pygmentsAct = menu.addAction(self.tr("Guessed"))
        self.pygmentsAct.setCheckable(True)
        self.pygmentsAct.setData("Guessed")
        self.languagesActGrp.addAction(self.pygmentsAct)
        self.pygmentsSelAct = menu.addAction(self.tr("Alternatives"))
        self.pygmentsSelAct.setData("Alternatives")
        
        menu.triggered.connect(self.__languageMenuTriggered)
        menu.aboutToShow.connect(self.__showContextMenuLanguages)
        
        return menu
        
    def __initContextMenuEncodings(self):
        """
        Private method used to setup the Encodings context sub menu.
        
        @return reference to the generated menu (QMenu)
        """
        self.supportedEncodings = {}
        
        menu = QMenu(self.tr("Encodings"))
        
        self.encodingsActGrp = QActionGroup(self)
        
        for encoding in sorted(Utilities.supportedCodecs):
            act = menu.addAction(encoding)
            act.setCheckable(True)
            act.setData(encoding)
            self.supportedEncodings[encoding] = act
            self.encodingsActGrp.addAction(act)
        
        menu.triggered.connect(self.__encodingsMenuTriggered)
        menu.aboutToShow.connect(self.__showContextMenuEncodings)
        
        return menu
        
    def __initContextMenuReopenWithEncoding(self):
        """
        Private method used to setup the Reopen With Encoding context sub menu.
        
        @return reference to the generated menu (QMenu)
        """
        menu = QMenu(self.tr("Re-Open With Encoding"))
        menu.setIcon(UI.PixmapCache.getIcon("open"))
        
        for encoding in sorted(Utilities.supportedCodecs):
            act = menu.addAction(encoding)
            act.setData(encoding)
        
        menu.triggered.connect(self.__reopenWithEncodingMenuTriggered)
        
        return menu
        
    def __initContextMenuEol(self):
        """
        Private method to setup the eol context sub menu.
        
        @return reference to the generated menu (QMenu)
        """
        self.supportedEols = {}
        
        menu = QMenu(self.tr("End-of-Line Type"))
        
        self.eolActGrp = QActionGroup(self)
        
        act = menu.addAction(UI.PixmapCache.getIcon("eolLinux"),
                             self.tr("Unix"))
        act.setCheckable(True)
        act.setData('\n')
        self.supportedEols['\n'] = act
        self.eolActGrp.addAction(act)
        
        act = menu.addAction(UI.PixmapCache.getIcon("eolWindows"),
                             self.tr("Windows"))
        act.setCheckable(True)
        act.setData('\r\n')
        self.supportedEols['\r\n'] = act
        self.eolActGrp.addAction(act)
        
        act = menu.addAction(UI.PixmapCache.getIcon("eolMac"),
                             self.tr("Macintosh"))
        act.setCheckable(True)
        act.setData('\r')
        self.supportedEols['\r'] = act
        self.eolActGrp.addAction(act)
        
        menu.triggered.connect(self.__eolMenuTriggered)
        menu.aboutToShow.connect(self.__showContextMenuEol)
        
        return menu
    
    def __initContextMenuSpellLanguages(self):
        """
        Private method to setup the spell checking languages context sub menu.
        
        @return reference to the generated menu
        @rtype QMenu
        """
        self.supportedSpellLanguages = {}
        
        menu = QMenu(self.tr("Spell Check Languages"))
        
        self.spellLanguagesActGrp = QActionGroup(self)
        
        self.noSpellLanguageAct = menu.addAction(
            self.tr("No Language"))
        self.noSpellLanguageAct.setCheckable(True)
        self.noSpellLanguageAct.setData("")
        self.spellLanguagesActGrp.addAction(self.noSpellLanguageAct)
        menu.addSeparator()
        
        for language in sorted(SpellChecker.getAvailableLanguages()):
            act = menu.addAction(language)
            act.setCheckable(True)
            act.setData(language)
            self.supportedSpellLanguages[language] = act
            self.spellLanguagesActGrp.addAction(act)
        
        menu.triggered.connect(self.__spellLanguagesMenuTriggered)
        menu.aboutToShow.connect(self.__showContextMenuSpellLanguages)
        
        return menu
    
    def __initContextMenuExporters(self):
        """
        Private method used to setup the Exporters context sub menu.
        
        @return reference to the generated menu (QMenu)
        """
        menu = QMenu(self.tr("Export as"))
        
        from . import Exporters
        supportedExporters = Exporters.getSupportedFormats()
        exporters = sorted(list(supportedExporters.keys()))
        for exporter in exporters:
            act = menu.addAction(supportedExporters[exporter])
            act.setData(exporter)
        
        menu.triggered.connect(self.__exportMenuTriggered)
        
        return menu
        
    def __initContextMenuMargins(self):
        """
        Private method used to setup the context menu for the margins.
        """
        self.marginMenuActs = {}
        
        # bookmark margin
        self.bmMarginMenu = QMenu()
        
        self.bmMarginMenu.addAction(
            self.tr('Toggle bookmark'), self.menuToggleBookmark)
        self.marginMenuActs["NextBookmark"] = self.bmMarginMenu.addAction(
            self.tr('Next bookmark'), self.nextBookmark)
        self.marginMenuActs["PreviousBookmark"] = self.bmMarginMenu.addAction(
            self.tr('Previous bookmark'), self.previousBookmark)
        self.marginMenuActs["ClearBookmark"] = self.bmMarginMenu.addAction(
            self.tr('Clear all bookmarks'), self.clearBookmarks)
        
        self.bmMarginMenu.aboutToShow.connect(
            lambda: self.__showContextMenuMargin(self.bmMarginMenu))
        
        # breakpoint margin
        self.bpMarginMenu = QMenu()
        
        self.marginMenuActs["Breakpoint"] = self.bpMarginMenu.addAction(
            self.tr('Toggle breakpoint'), self.menuToggleBreakpoint)
        self.marginMenuActs["TempBreakpoint"] = self.bpMarginMenu.addAction(
            self.tr('Toggle temporary breakpoint'),
            self.__menuToggleTemporaryBreakpoint)
        self.marginMenuActs["EditBreakpoint"] = self.bpMarginMenu.addAction(
            self.tr('Edit breakpoint...'), self.menuEditBreakpoint)
        self.marginMenuActs["EnableBreakpoint"] = self.bpMarginMenu.addAction(
            self.tr('Enable breakpoint'),
            self.__menuToggleBreakpointEnabled)
        self.marginMenuActs["NextBreakpoint"] = self.bpMarginMenu.addAction(
            self.tr('Next breakpoint'), self.menuNextBreakpoint)
        self.marginMenuActs["PreviousBreakpoint"] = (
            self.bpMarginMenu.addAction(
                self.tr('Previous breakpoint'),
                self.menuPreviousBreakpoint)
        )
        self.marginMenuActs["ClearBreakpoint"] = self.bpMarginMenu.addAction(
            self.tr('Clear all breakpoints'), self.__menuClearBreakpoints)
        
        self.bpMarginMenu.aboutToShow.connect(
            lambda: self.__showContextMenuMargin(self.bpMarginMenu))
        
        # fold margin
        self.foldMarginMenu = QMenu()
        
        self.marginMenuActs["ToggleAllFolds"] = (
            self.foldMarginMenu.addAction(
                self.tr("Toggle all folds"),
                self.foldAll)
        )
        self.marginMenuActs["ToggleAllFoldsAndChildren"] = (
            self.foldMarginMenu.addAction(
                self.tr("Toggle all folds (including children)"),
                lambda: self.foldAll(True))
        )
        self.marginMenuActs["ToggleCurrentFold"] = (
            self.foldMarginMenu.addAction(
                self.tr("Toggle current fold"),
                self.toggleCurrentFold)
        )
        self.foldMarginMenu.addSeparator()
        self.marginMenuActs["ExpandChildren"] = (
            self.foldMarginMenu.addAction(
                self.tr("Expand (including children)"),
                self.__contextMenuExpandFoldWithChildren)
        )
        self.marginMenuActs["CollapseChildren"] = (
            self.foldMarginMenu.addAction(
                self.tr("Collapse (including children)"),
                self.__contextMenuCollapseFoldWithChildren)
        )
        self.foldMarginMenu.addSeparator()
        self.marginMenuActs["ClearAllFolds"] = (
            self.foldMarginMenu.addAction(
                self.tr("Clear all folds"),
                self.clearFolds)
        )
        
        self.foldMarginMenu.aboutToShow.connect(
            lambda: self.__showContextMenuMargin(self.foldMarginMenu))
        
        # indicator margin
        self.indicMarginMenu = QMenu()
        
        self.marginMenuActs["GotoSyntaxError"] = (
            self.indicMarginMenu.addAction(
                self.tr('Goto syntax error'), self.gotoSyntaxError)
        )
        self.marginMenuActs["ShowSyntaxError"] = (
            self.indicMarginMenu.addAction(
                self.tr('Show syntax error message'),
                self.__showSyntaxError)
        )
        self.marginMenuActs["ClearSyntaxError"] = (
            self.indicMarginMenu.addAction(
                self.tr('Clear syntax error'), self.clearSyntaxError)
        )
        self.indicMarginMenu.addSeparator()
        self.marginMenuActs["NextWarningMarker"] = (
            self.indicMarginMenu.addAction(
                self.tr("Next warning"), self.nextWarning)
        )
        self.marginMenuActs["PreviousWarningMarker"] = (
            self.indicMarginMenu.addAction(
                self.tr("Previous warning"), self.previousWarning)
        )
        self.marginMenuActs["ShowWarning"] = (
            self.indicMarginMenu.addAction(
                self.tr('Show warning message'), self.__showWarning)
        )
        self.marginMenuActs["ClearWarnings"] = (
            self.indicMarginMenu.addAction(
                self.tr('Clear warnings'), self.clearWarnings)
        )
        self.indicMarginMenu.addSeparator()
        self.marginMenuActs["NextCoverageMarker"] = (
            self.indicMarginMenu.addAction(
                self.tr('Next uncovered line'), self.nextUncovered)
        )
        self.marginMenuActs["PreviousCoverageMarker"] = (
            self.indicMarginMenu.addAction(
                self.tr('Previous uncovered line'), self.previousUncovered)
        )
        self.indicMarginMenu.addSeparator()
        self.marginMenuActs["NextTaskMarker"] = (
            self.indicMarginMenu.addAction(
                self.tr('Next task'), self.nextTask)
        )
        self.marginMenuActs["PreviousTaskMarker"] = (
            self.indicMarginMenu.addAction(
                self.tr('Previous task'), self.previousTask)
        )
        self.indicMarginMenu.addSeparator()
        self.marginMenuActs["NextChangeMarker"] = (
            self.indicMarginMenu.addAction(
                self.tr('Next change'), self.nextChange)
        )
        self.marginMenuActs["PreviousChangeMarker"] = (
            self.indicMarginMenu.addAction(
                self.tr('Previous change'), self.previousChange)
        )
        self.marginMenuActs["ClearChangeMarkers"] = (
            self.indicMarginMenu.addAction(
                self.tr('Clear changes'), self.__reinitOnlineChangeTrace)
        )
        
        self.indicMarginMenu.aboutToShow.connect(
            lambda: self.__showContextMenuMargin(self.indicMarginMenu))
        
    def __exportMenuTriggered(self, act):
        """
        Private method to handle the selection of an export format.
        
        @param act reference to the action that was triggered (QAction)
        """
        exporterFormat = act.data()
        self.exportFile(exporterFormat)
        
    def exportFile(self, exporterFormat):
        """
        Public method to export the file.
        
        @param exporterFormat format the file should be exported into (string)
        """
        if exporterFormat:
            from . import Exporters
            exporter = Exporters.getExporter(exporterFormat, self)
            if exporter:
                exporter.exportSource()
            else:
                E5MessageBox.critical(
                    self,
                    self.tr("Export source"),
                    self.tr(
                        """<p>No exporter available for the """
                        """export format <b>{0}</b>. Aborting...</p>""")
                    .format(exporterFormat))
        else:
            E5MessageBox.critical(
                self,
                self.tr("Export source"),
                self.tr("""No export format given. Aborting..."""))
        
    def __showContextMenuLanguages(self):
        """
        Private slot handling the aboutToShow signal of the languages context
        menu.
        """
        if self.apiLanguage.startswith("Pygments|"):
            self.pygmentsSelAct.setText(
                self.tr("Alternatives ({0})").format(
                    self.getLanguage(normalized=False)))
        else:
            self.pygmentsSelAct.setText(self.tr("Alternatives"))
        self.showMenu.emit("Languages", self.languagesMenu, self)
        
    def __selectPygmentsLexer(self):
        """
        Private method to select a specific pygments lexer.
        
        @return name of the selected pygments lexer (string)
        """
        from pygments.lexers import get_all_lexers
        lexerList = sorted(lex[0] for lex in get_all_lexers())
        try:
            lexerSel = lexerList.index(
                self.getLanguage(normalized=False, forPygments=True))
        except ValueError:
            lexerSel = 0
        lexerName, ok = QInputDialog.getItem(
            self,
            self.tr("Pygments Lexer"),
            self.tr("Select the Pygments lexer to apply."),
            lexerList,
            lexerSel,
            False)
        if ok and lexerName:
            return lexerName
        else:
            return ""
        
    def __languageMenuTriggered(self, act):
        """
        Private method to handle the selection of a lexer language.
        
        @param act reference to the action that was triggered (QAction)
        """
        if act == self.noLanguageAct:
            self.__resetLanguage()
        elif act == self.pygmentsAct:
            self.setLanguage("dummy.pygments")
        elif act == self.pygmentsSelAct:
            language = self.__selectPygmentsLexer()
            if language:
                self.setLanguage("dummy.pygments", pyname=language)
        else:
            language = act.data()
            if language:
                self.filetype = language
                self.setLanguage(self.supportedLanguages[language][1])
                self.checkSyntax()
        
    def __languageChanged(self, language, propagate=True):
        """
        Private slot handling a change of a connected editor's language.
        
        @param language language to be set (string)
        @keyparam propagate flag indicating to propagate the change (boolean)
        """
        if language == '':
            self.__resetLanguage(propagate=propagate)
        elif language == "Guessed":
            self.setLanguage("dummy.pygments",
                             propagate=propagate)
        elif language.startswith("Pygments|"):
            pyname = language.split("|", 1)[1]
            self.setLanguage("dummy.pygments", pyname=pyname,
                             propagate=propagate)
        else:
            self.filetype = language
            self.setLanguage(self.supportedLanguages[language][1],
                             propagate=propagate)
            self.checkSyntax()
        
    def __resetLanguage(self, propagate=True):
        """
        Private method used to reset the language selection.
        
        @keyparam propagate flag indicating to propagate the change (boolean)
        """
        if (
            self.lexer_ is not None and
            (self.lexer_.lexer() == "container" or
             self.lexer_.lexer() is None)
        ):
            self.SCN_STYLENEEDED.disconnect(self.__styleNeeded)
        
        self.apiLanguage = ""
        self.lexer_ = None
        self.__lexerReset = True
        self.setLexer()
        if self.completer is not None:
            self.completer.setEnabled(False)
            self.completer = None
        useMonospaced = self.useMonospaced
        self.__setTextDisplay()
        self.__setMarginsDisplay()
        self.setMonospaced(useMonospaced)
        self.menuActs["MonospacedFont"].setChecked(self.useMonospaced)
        
        if not self.inLanguageChanged and propagate:
            self.inLanguageChanged = True
            self.languageChanged.emit(self.apiLanguage)
            self.inLanguageChanged = False
        
    def setLanguage(self, filename, initTextDisplay=True, propagate=True,
                    pyname=""):
        """
        Public method to set a lexer language.
        
        @param filename filename used to determine the associated lexer
            language (string)
        @param initTextDisplay flag indicating an initialization of the text
            display is required as well (boolean)
        @keyparam propagate flag indicating to propagate the change (boolean)
        @keyparam pyname name of the pygments lexer to use (string)
        """
        # clear all warning and syntax error markers
        self.clearSyntaxError()
        self.clearWarnings()
        
        self.menuActs["MonospacedFont"].setChecked(False)
        
        self.__lexerReset = False
        self.__bindLexer(filename, pyname=pyname)
        self.__bindCompleter(filename)
        self.recolor()
        self.__checkLanguage()
        
        # set the text display
        if initTextDisplay:
            self.__setTextDisplay()
        
        # set the auto-completion and call-tips function
        self.__setAutoCompletion()
        self.__setCallTips()
        
        if not self.inLanguageChanged and propagate:
            self.inLanguageChanged = True
            self.languageChanged.emit(self.apiLanguage)
            self.inLanguageChanged = False
    
    def __checkLanguage(self):
        """
        Private method to check the selected language of the language submenu.
        """
        if self.apiLanguage == "":
            self.noLanguageAct.setChecked(True)
        elif self.apiLanguage == "Guessed":
            self.pygmentsAct.setChecked(True)
        elif self.apiLanguage.startswith("Pygments|"):
            act = self.languagesActGrp.checkedAction()
            if act:
                act.setChecked(False)
        else:
            self.supportedLanguages[self.apiLanguage][2].setChecked(True)
    
    def projectLexerAssociationsChanged(self):
        """
        Public slot to handle changes of the project lexer associations.
        """
        self.setLanguage(self.fileName)
    
    def __showContextMenuEncodings(self):
        """
        Private slot handling the aboutToShow signal of the encodings context
        menu.
        """
        self.showMenu.emit("Encodings", self.encodingsMenu, self)
        
    def __encodingsMenuTriggered(self, act):
        """
        Private method to handle the selection of an encoding.
        
        @param act reference to the action that was triggered (QAction)
        """
        encoding = act.data()
        self.setModified(True)
        self.__encodingChanged("{0}-selected".format(encoding))
        
    def __checkEncoding(self):
        """
        Private method to check the selected encoding of the encodings submenu.
        """
        try:
            (self.supportedEncodings[self.__normalizedEncoding()]
             .setChecked(True))
        except (AttributeError, KeyError):
            pass
        
    def __encodingChanged(self, encoding, propagate=True):
        """
        Private slot to handle a change of the encoding.
        
        @param encoding changed encoding (string)
        @keyparam propagate flag indicating to propagate the change (boolean)
        """
        self.encoding = encoding
        self.__checkEncoding()
        
        if not self.inEncodingChanged and propagate:
            self.inEncodingChanged = True
            self.encodingChanged.emit(self.encoding)
            self.inEncodingChanged = False
        
    def __normalizedEncoding(self, encoding=""):
        """
        Private method to calculate the normalized encoding string.
        
        @param encoding encoding to be normalized (string)
        @return normalized encoding (string)
        """
        if not encoding:
            encoding = self.encoding
        return (
            encoding
            .replace("-default", "")
            .replace("-guessed", "")
            .replace("-selected", "")
        )
        
    def __showContextMenuEol(self):
        """
        Private slot handling the aboutToShow signal of the eol context menu.
        """
        self.showMenu.emit("Eol", self.eolMenu, self)
        
    def __eolMenuTriggered(self, act):
        """
        Private method to handle the selection of an eol type.
        
        @param act reference to the action that was triggered (QAction)
        """
        eol = act.data()
        self.setEolModeByEolString(eol)
        self.convertEols(self.eolMode())
        
    def __checkEol(self):
        """
        Private method to check the selected eol type of the eol submenu.
        """
        try:
            self.supportedEols[self.getLineSeparator()].setChecked(True)
        except (AttributeError, TypeError):
            pass
        
    def __eolChanged(self):
        """
        Private slot to handle a change of the eol mode.
        """
        self.__checkEol()
        
        if not self.inEolChanged:
            self.inEolChanged = True
            eol = self.getLineSeparator()
            self.eolChanged.emit(eol)
            self.inEolChanged = False
    
    def __showContextMenuSpellLanguages(self):
        """
        Private slot handling the aboutToShow signal of the spell check
        languages context menu.
        """
        self.showMenu.emit("SpellLanguage", self.spellLanguagesMenu, self)
    
    def __spellLanguagesMenuTriggered(self, act):
        """
        Private method to handle the selection of a spell check language.
        
        @param act reference to the action that was triggered
        @type QAction
        """
        language = act.data()
        self.__setSpellingLanguage(language)
        self.spellLanguageChanged.emit(language)
    
    def __checkSpellLanguage(self):
        """
        Private slot to check the selected spell check language action.
        """
        language = self.getSpellingLanguage()
        try:
            self.supportedSpellLanguages[language].setChecked(True)
        except (AttributeError, KeyError):
            pass
    
    def __spellLanguageChanged(self, language, propagate=True):
        """
        Private slot to handle a change of the spell check language.
        
        @param language new spell check language
        @type str
        @param propagate flag indicating to propagate the change
        @type bool
        """
        self.__setSpellingLanguage(language)
        self.__checkSpellLanguage()
        
        if not self.__inSpellLanguageChanged and propagate:
            self.__inSpellLanguageChanged = True
            self.spellLanguageChanged.emit(language)
            self.__inSpellLanguageChanged = False
    
    def __bindLexer(self, filename, pyname=""):
        """
        Private slot to set the correct lexer depending on language.
        
        @param filename filename used to determine the associated lexer
            language (string)
        @keyparam pyname name of the pygments lexer to use (string)
        """
        if (
            self.lexer_ is not None and
            (self.lexer_.lexer() == "container" or
             self.lexer_.lexer() is None)
        ):
            self.SCN_STYLENEEDED.disconnect(self.__styleNeeded)
        
        language = ""
        if not self.filetype:
            if filename:
                basename = os.path.basename(filename)
                if (
                    self.project.isOpen() and
                    self.project.isProjectFile(filename)
                ):
                    language = self.project.getEditorLexerAssoc(basename)
                if not language:
                    language = Preferences.getEditorLexerAssoc(basename)
            if not language:
                bindName = self.__bindName(self.text(0))
                if bindName:
                    language = Preferences.getEditorLexerAssoc(bindName)
            if language == "Python":
                # correction for Python
                pyVer = Utilities.determinePythonVersion(
                    filename, self.text(0), self)
                language = "Python{0}".format(pyVer)
            if language in ['Python3', 'MicroPython', 'Ruby',
                            'JavaScript', 'YAML', 'JSON']:
                self.filetype = language
            else:
                self.filetype = ""
        else:
            language = self.filetype
        
        if language.startswith("Pygments|"):
            pyname = language
            self.filetype = language.split("|")[-1]
            language = ""
        
        from . import Lexers
        self.lexer_ = Lexers.getLexer(language, self, pyname=pyname)
        if self.lexer_ is None:
            self.setLexer()
            self.apiLanguage = ""
            return
        
        if pyname:
            if pyname.startswith("Pygments|"):
                self.apiLanguage = pyname
            else:
                self.apiLanguage = "Pygments|{0}".format(pyname)
        else:
            if language == "Protocol":
                self.apiLanguage = language
            else:
                # Change API language for lexer where QScintilla reports
                # an abbreviated name.
                self.apiLanguage = self.lexer_.language()
                if self.apiLanguage == "POV":
                    self.apiLanguage = "Povray"
                elif self.apiLanguage == "PO":
                    self.apiLanguage = "Gettext"
        self.setLexer(self.lexer_)
        self.__setMarginsDisplay()
        if self.lexer_.lexer() == "container" or self.lexer_.lexer() is None:
            self.SCN_STYLENEEDED.connect(self.__styleNeeded)
        
        # get the font for style 0 and set it as the default font
        key = 'Scintilla/{0}/style0/font'.format(self.lexer_.language())
        fdesc = Preferences.Prefs.settings.value(key)
        if fdesc is not None:
            font = QFont(fdesc[0], int(fdesc[1]))
            self.lexer_.setDefaultFont(font)
        self.lexer_.readSettings(Preferences.Prefs.settings, "Scintilla")
        if self.lexer_.hasSubstyles():
            self.lexer_.readSubstyles(self)
        
        # now set the lexer properties
        self.lexer_.initProperties()
        
        # initialize the lexer APIs settings
        if self.project.isOpen() and self.project.isProjectFile(filename):
            projectType = self.project.getProjectType()
        else:
            projectType = ""
        api = self.vm.getAPIsManager().getAPIs(self.apiLanguage,
                                               projectType=projectType)
        if api is not None and not api.isEmpty():
            self.lexer_.setAPIs(api.getQsciAPIs())
            self.acAPI = True
        else:
            self.acAPI = False
        self.autoCompletionAPIsAvailable.emit(self.acAPI)
        
        self.__setAnnotationStyles()
        
        self.lexer_.setDefaultColor(self.lexer_.color(0))
        self.lexer_.setDefaultPaper(self.lexer_.paper(0))
        
    def __styleNeeded(self, position):
        """
        Private slot to handle the need for more styling.
        
        @param position end position, that needs styling (integer)
        """
        self.lexer_.styleText(self.getEndStyled(), position)
        
    def getLexer(self):
        """
        Public method to retrieve a reference to the lexer object.
        
        @return the lexer object (Lexer)
        """
        return self.lexer_
        
    def getLanguage(self, normalized=True, forPygments=False):
        """
        Public method to retrieve the language of the editor.
        
        @keyparam normalized flag indicating to normalize some Pygments
            lexer names (boolean)
        @keyparam forPygments flag indicating to normalize some lexer
            names for Pygments (boolean)
        @return language of the editor (string)
        """
        if (
            self.apiLanguage == "Guessed" or
            self.apiLanguage.startswith("Pygments|")
        ):
            lang = self.lexer_.name()
            if normalized:
                # adjust some Pygments lexer names
                if lang in ("Python 2.x", "Python"):
                    lang = "Python3"
                elif lang == "Protocol Buffer":
                    lang = "Protocol"
                    
        else:
            lang = self.apiLanguage
            if forPygments:
                # adjust some names to Pygments lexer names
                if lang == "Python3":
                    lang = "Python"
                elif lang == "Protocol":
                    lang = "Protocol Buffer"
        return lang
    
    def getApiLanguage(self):
        """
        Public method to get the API language of the editor.
        
        @return API language
        @rtype str
        """
        return self.apiLanguage
        
    def __bindCompleter(self, filename):
        """
        Private slot to set the correct typing completer depending on language.
        
        @param filename filename used to determine the associated typing
            completer language (string)
        """
        if self.completer is not None:
            self.completer.setEnabled(False)
            self.completer = None
        
        filename = os.path.basename(filename)
        apiLanguage = Preferences.getEditorLexerAssoc(filename)
        if apiLanguage == "":
            pyVer = self.__getPyVersion()
            if pyVer:
                apiLanguage = "Python{0}".format(pyVer)
            elif self.isRubyFile():
                apiLanguage = "Ruby"
        
        from . import TypingCompleters
        self.completer = TypingCompleters.getCompleter(apiLanguage, self)
        
    def getCompleter(self):
        """
        Public method to retrieve a reference to the completer object.
        
        @return the completer object (CompleterBase)
        """
        return self.completer
        
    def __modificationChanged(self, m):
        """
        Private slot to handle the modificationChanged signal.
        
        It emits the signal modificationStatusChanged with parameters
        m and self.
        
        @param m modification status
        """
        if not m and bool(self.fileName):
            self.lastModified = QFileInfo(self.fileName).lastModified()
        self.modificationStatusChanged.emit(m, self)
        self.undoAvailable.emit(self.isUndoAvailable())
        self.redoAvailable.emit(self.isRedoAvailable())
        
    def __cursorPositionChanged(self, line, index):
        """
        Private slot to handle the cursorPositionChanged signal.
        
        It emits the signal cursorChanged with parameters fileName,
        line and pos.
        
        @param line line number of the cursor
        @param index position in line of the cursor
        """
        self.cursorChanged.emit(self.fileName, line + 1, index)
        
        if Preferences.getEditor("MarkOccurrencesEnabled"):
            self.__markOccurrencesTimer.stop()
            self.__markOccurrencesTimer.start()
        
        if self.lastLine != line:
            self.cursorLineChanged.emit(line)
        
        if self.spell is not None:
            # do spell checking
            doSpelling = True
            if self.lastLine == line:
                start, end = self.getWordBoundaries(
                    line, index, useWordChars=False)
                if start <= self.lastIndex and self.lastIndex <= end:
                    doSpelling = False
            if doSpelling:
                pos = self.positionFromLineIndex(self.lastLine, self.lastIndex)
                self.spell.checkWord(pos)
        
        if self.lastLine != line:
            self.__markerMap.update()
        
        self.lastLine = line
        self.lastIndex = index
        
    def __modificationReadOnly(self):
        """
        Private slot to handle the modificationAttempted signal.
        """
        E5MessageBox.warning(
            self,
            self.tr("Modification of Read Only file"),
            self.tr("""You are attempting to change a read only file. """
                    """Please save to a different file first."""))
        
    def setNoName(self, noName):
        """
        Public method to set the display string for an unnamed editor.
        
        @param noName display string for this unnamed editor (string)
        """
        self.noName = noName
        
    def getNoName(self):
        """
        Public method to get the display string for an unnamed editor.
        
        @return display string for this unnamed editor (string)
        """
        return self.noName
        
    def getFileName(self):
        """
        Public method to return the name of the file being displayed.
        
        @return filename of the displayed file (string)
        """
        return self.fileName
        
    def getFileType(self):
        """
        Public method to return the type of the file being displayed.
        
        @return type of the displayed file (string)
        """
        return self.filetype
        
    def getFileTypeByFlag(self):
        """
        Public method to return the type of the file, if it was set by an
        eflag: marker.
        
        @return type of the displayed file, if set by an eflag: marker or an
            empty string (string)
        """
        if self.filetypeByFlag:
            return self.filetype
        else:
            return ""
        
    def determineFileType(self):
        """
        Public method to determine the file type using various tests.
        
        @return type of the displayed file or an empty string (string)
        """
        ftype = self.filetype
        if not ftype:
            pyVer = self.__getPyVersion()
            if pyVer:
                ftype = "Python{0}".format(pyVer)
            elif self.isRubyFile():
                ftype = "Ruby"
            else:
                ftype = ""
        
        return ftype
        
    def getEncoding(self):
        """
        Public method to return the current encoding.
        
        @return current encoding (string)
        """
        return self.encoding
    
    def __getPyVersion(self):
        """
        Private method to return the Python main version or 0 if it's
        not a Python file at all.
        
        @return Python version or 0 if it's not a Python file (int)
        """
        return Utilities.determinePythonVersion(
            self.fileName, self.text(0), self)
    
    def isPyFile(self):
        """
        Public method to return a flag indicating a Python (2 or 3) file.
        
        @return flag indicating a Python3 file (boolean)
        """
        return self.__getPyVersion() == 3
    
    def isPy2File(self):
        """
        Public method to return a flag indicating a Python2 file.
        
        @return flag reporting always False
        @rtype bool
        """
        # kept to keep the API compatible for plugins
        return False

    def isPy3File(self):
        """
        Public method to return a flag indicating a Python3 file.
        
        @return flag indicating a Python3 file (boolean)
        """
        return self.__getPyVersion() == 3
    
    def isMicroPythonFile(self):
        """
        Public method to return a flag indicating a MicroPython file.
        
        @return flag indicating a MicroPython file
        @rtype bool
        """
        if self.filetype == "MicroPython":
            return True
        
        return False

    def isRubyFile(self):
        """
        Public method to return a flag indicating a Ruby file.
        
        @return flag indicating a Ruby file (boolean)
        """
        if self.filetype == "Ruby":
            return True
        
        if self.filetype == "":
            line0 = self.text(0)
            if (
                line0.startswith("#!") and
                "ruby" in line0
            ):
                self.filetype = "Ruby"
                return True
            
            if (
                bool(self.fileName) and
                os.path.splitext(self.fileName)[1] in
                    self.dbs.getExtensions('Ruby')
            ):
                self.filetype = "Ruby"
                return True
        
        return False

    def isJavascriptFile(self):
        """
        Public method to return a flag indicating a Javascript file.
        
        @return flag indicating a Javascript file (boolean)
        """
        if self.filetype == "JavaScript":
            return True
        
        if self.filetype == "":
            if (
                self.fileName and
                os.path.splitext(self.fileName)[1] == ".js"
            ):
                self.filetype = "JavaScript"
                return True
        
        return False
    
    def highlightVisible(self):
        """
        Public method to make sure that the highlight is visible.
        """
        if self.lastHighlight is not None:
            lineno = self.markerLine(self.lastHighlight)
            self.ensureVisible(lineno + 1)
        
    def highlight(self, line=None, error=False, syntaxError=False):
        """
        Public method to highlight [or de-highlight] a particular line.
        
        @param line line number to highlight (integer)
        @param error flag indicating whether the error highlight should be
            used (boolean)
        @param syntaxError flag indicating a syntax error (boolean)
        """
        if line is None:
            self.lastHighlight = None
            if self.lastErrorMarker is not None:
                self.markerDeleteHandle(self.lastErrorMarker)
            self.lastErrorMarker = None
            if self.lastCurrMarker is not None:
                self.markerDeleteHandle(self.lastCurrMarker)
            self.lastCurrMarker = None
        else:
            if error:
                if self.lastErrorMarker is not None:
                    self.markerDeleteHandle(self.lastErrorMarker)
                self.lastErrorMarker = self.markerAdd(line - 1, self.errorline)
                self.lastHighlight = self.lastErrorMarker
            else:
                if self.lastCurrMarker is not None:
                    self.markerDeleteHandle(self.lastCurrMarker)
                self.lastCurrMarker = self.markerAdd(line - 1,
                                                     self.currentline)
                self.lastHighlight = self.lastCurrMarker
            self.setCursorPosition(line - 1, 0)
        
    def getHighlightPosition(self):
        """
        Public method to return the position of the highlight bar.
        
        @return line number of the highlight bar (integer)
        """
        if self.lastHighlight is not None:
            return self.markerLine(self.lastHighlight)
        else:
            return 1
    
    ###########################################################################
    ## Breakpoint handling methods below
    ###########################################################################

    def __modified(self, pos, mtype, text, length, linesAdded, line, foldNow,
                   foldPrev, token, annotationLinesAdded):
        """
        Private method to handle changes of the number of lines.
        
        @param pos start position of change (integer)
        @param mtype flags identifying the change (integer)
        @param text text that is given to the Undo system (string)
        @param length length of the change (integer)
        @param linesAdded number of added/deleted lines (integer)
        @param line line number of a fold level or marker change (integer)
        @param foldNow new fold level (integer)
        @param foldPrev previous fold level (integer)
        @param token ???
        @param annotationLinesAdded number of added/deleted annotation lines
            (integer)
        """
        if (
            mtype & (self.SC_MOD_INSERTTEXT | self.SC_MOD_DELETETEXT) and
            linesAdded != 0
        ):
            if self.breaks:
                bps = []    # list of breakpoints
                for handle, (ln, cond, temp, enabled, ignorecount) in (
                    self.breaks.items()
                ):
                    line = self.markerLine(handle) + 1
                    if ln != line:
                        bps.append((ln, line))
                        self.breaks[handle] = (line, cond, temp, enabled,
                                               ignorecount)
                self.inLinesChanged = True
                for ln, line in sorted(bps, reverse=linesAdded > 0):
                    index1 = self.breakpointModel.getBreakPointIndex(
                        self.fileName, ln)
                    index2 = self.breakpointModel.index(index1.row(), 1)
                    self.breakpointModel.setData(index2, line)
                self.inLinesChanged = False
        
    def __restoreBreakpoints(self):
        """
        Private method to restore the breakpoints.
        """
        for handle in list(self.breaks.keys()):
            self.markerDeleteHandle(handle)
        self.__addBreakPoints(
            QModelIndex(), 0, self.breakpointModel.rowCount() - 1)
        self.__markerMap.update()
        
    def __deleteBreakPoints(self, parentIndex, start, end):
        """
        Private slot to delete breakpoints.
        
        @param parentIndex index of parent item (QModelIndex)
        @param start start row (integer)
        @param end end row (integer)
        """
        for row in range(start, end + 1):
            index = self.breakpointModel.index(row, 0, parentIndex)
            fn, lineno = self.breakpointModel.getBreakPointByIndex(index)[0:2]
            if fn == self.fileName:
                self.clearBreakpoint(lineno)
        
    def __changeBreakPoints(self, startIndex, endIndex):
        """
        Private slot to set changed breakpoints.
        
        @param startIndex start index of the breakpoints being changed
            (QModelIndex)
        @param endIndex end index of the breakpoints being changed
            (QModelIndex)
        """
        if not self.inLinesChanged:
            self.__addBreakPoints(QModelIndex(), startIndex.row(),
                                  endIndex.row())
        
    def __breakPointDataAboutToBeChanged(self, startIndex, endIndex):
        """
        Private slot to handle the dataAboutToBeChanged signal of the
        breakpoint model.
        
        @param startIndex start index of the rows to be changed (QModelIndex)
        @param endIndex end index of the rows to be changed (QModelIndex)
        """
        self.__deleteBreakPoints(QModelIndex(), startIndex.row(),
                                 endIndex.row())
        
    def __addBreakPoints(self, parentIndex, start, end):
        """
        Private slot to add breakpoints.
        
        @param parentIndex index of parent item (QModelIndex)
        @param start start row (integer)
        @param end end row (integer)
        """
        for row in range(start, end + 1):
            index = self.breakpointModel.index(row, 0, parentIndex)
            fn, line, cond, temp, enabled, ignorecount = (
                self.breakpointModel.getBreakPointByIndex(index)[:6]
            )
            if fn == self.fileName:
                self.newBreakpointWithProperties(
                    line, (cond, temp, enabled, ignorecount))
        
    def clearBreakpoint(self, line):
        """
        Public method to clear a breakpoint.
        
        Note: This doesn't clear the breakpoint in the debugger,
        it just deletes it from the editor internal list of breakpoints.
        
        @param line line number of the breakpoint (integer)
        """
        if self.inLinesChanged:
            return
        
        for handle in self.breaks:
            if self.markerLine(handle) == line - 1:
                break
        else:
            # not found, simply ignore it
            return
        
        del self.breaks[handle]
        self.markerDeleteHandle(handle)
        self.__markerMap.update()
        
    def newBreakpointWithProperties(self, line, properties):
        """
        Public method to set a new breakpoint and its properties.
        
        @param line line number of the breakpoint (integer)
        @param properties properties for the breakpoint (tuple)
                (condition, temporary flag, enabled flag, ignore count)
        """
        if not properties[2]:
            marker = self.dbreakpoint
        elif properties[0]:
            marker = properties[1] and self.tcbreakpoint or self.cbreakpoint
        else:
            marker = properties[1] and self.tbreakpoint or self.breakpoint
            
        if self.markersAtLine(line - 1) & self.breakpointMask == 0:
            handle = self.markerAdd(line - 1, marker)
            self.breaks[handle] = (line,) + properties
            self.breakpointToggled.emit(self)
            self.__markerMap.update()
        
    def __toggleBreakpoint(self, line, temporary=False):
        """
        Private method to toggle a breakpoint.
        
        @param line line number of the breakpoint (integer)
        @param temporary flag indicating a temporary breakpoint (boolean)
        """
        for handle in self.breaks:
            if self.markerLine(handle) == line - 1:
                # delete breakpoint or toggle it to the next state
                index = self.breakpointModel.getBreakPointIndex(
                    self.fileName, line)
                if (
                    Preferences.getDebugger("ThreeStateBreakPoints") and
                    not self.breakpointModel.isBreakPointTemporaryByIndex(
                        index)
                ):
                    self.breakpointModel.deleteBreakPointByIndex(index)
                    self.__addBreakPoint(line, True)
                else:
                    self.breakpointModel.deleteBreakPointByIndex(index)
                    self.breakpointToggled.emit(self)
                break
        else:
            self.__addBreakPoint(line, temporary)

    def __addBreakPoint(self, line, temporary):
        """
        Private method to add a new breakpoint.
        
        @param line line number of the breakpoint (integer)
        @param temporary flag indicating a temporary breakpoint (boolean)
        """
        if self.fileName and self.isPyFile():
            self.breakpointModel.addBreakPoint(
                self.fileName, line, ('', temporary, True, 0))
            self.breakpointToggled.emit(self)
        
    def __toggleBreakpointEnabled(self, line):
        """
        Private method to toggle a breakpoints enabled status.
        
        @param line line number of the breakpoint (integer)
        """
        for handle in self.breaks:
            if self.markerLine(handle) == line - 1:
                index = self.breakpointModel.getBreakPointIndex(
                    self.fileName, line)
                self.breakpointModel.setBreakPointEnabledByIndex(
                    index, not self.breaks[handle][3])
                break
        
    def curLineHasBreakpoint(self):
        """
        Public method to check for the presence of a breakpoint at the current
        line.
        
        @return flag indicating the presence of a breakpoint (boolean)
        """
        line, _ = self.getCursorPosition()
        return self.markersAtLine(line) & self.breakpointMask != 0
        
    def getBreakpointLines(self):
        """
        Public method to get the lines containing a breakpoint.
        
        @return list of lines containing a breakpoint (list of integer)
        """
        lines = []
        line = -1
        while True:
            line = self.markerFindNext(line + 1, self.breakpointMask)
            if line < 0:
                break
            else:
                lines.append(line)
        return lines
        
    def hasBreakpoints(self):
        """
        Public method to check for the presence of breakpoints.
        
        @return flag indicating the presence of breakpoints (boolean)
        """
        return len(self.breaks) > 0
        
    def __menuToggleTemporaryBreakpoint(self):
        """
        Private slot to handle the 'Toggle temporary breakpoint' context menu
        action.
        """
        if self.line < 0:
            self.line, index = self.getCursorPosition()
        self.line += 1
        self.__toggleBreakpoint(self.line, 1)
        self.line = -1
        
    def menuToggleBreakpoint(self):
        """
        Public slot to handle the 'Toggle breakpoint' context menu action.
        """
        if self.line < 0:
            self.line, index = self.getCursorPosition()
        self.line += 1
        self.__toggleBreakpoint(self.line)
        self.line = -1
        
    def __menuToggleBreakpointEnabled(self):
        """
        Private slot to handle the 'Enable/Disable breakpoint' context menu
        action.
        """
        if self.line < 0:
            self.line, index = self.getCursorPosition()
        self.line += 1
        self.__toggleBreakpointEnabled(self.line)
        self.line = -1
        
    def menuEditBreakpoint(self, line=None):
        """
        Public slot to handle the 'Edit breakpoint' context menu action.
        
        @param line linenumber of the breakpoint to edit
        """
        if line is not None:
            self.line = line - 1
        if self.line < 0:
            self.line, index = self.getCursorPosition()
        
        for handle in self.breaks:
            if self.markerLine(handle) == self.line:
                ln, cond, temp, enabled, ignorecount = self.breaks[handle]
                index = self.breakpointModel.getBreakPointIndex(self.fileName,
                                                                ln)
                if not index.isValid():
                    return
                
                from Debugger.EditBreakpointDialog import EditBreakpointDialog
                dlg = EditBreakpointDialog(
                    (self.fileName, ln),
                    (cond, temp, enabled, ignorecount),
                    self.condHistory, self, modal=True)
                if dlg.exec_() == QDialog.Accepted:
                    cond, temp, enabled, ignorecount = dlg.getData()
                    self.breakpointModel.setBreakPointByIndex(
                        index, self.fileName, ln,
                        (cond, temp, enabled, ignorecount))
                    break
        
        self.line = -1
        
    def menuNextBreakpoint(self):
        """
        Public slot to handle the 'Next breakpoint' context menu action.
        """
        line, index = self.getCursorPosition()
        if line == self.lines() - 1:
            line = 0
        else:
            line += 1
        bpline = self.markerFindNext(line, self.breakpointMask)
        if bpline < 0:
            # wrap around
            bpline = self.markerFindNext(0, self.breakpointMask)
        if bpline >= 0:
            self.setCursorPosition(bpline, 0)
            self.ensureLineVisible(bpline)
        
    def menuPreviousBreakpoint(self):
        """
        Public slot to handle the 'Previous breakpoint' context menu action.
        """
        line, index = self.getCursorPosition()
        if line == 0:
            line = self.lines() - 1
        else:
            line -= 1
        bpline = self.markerFindPrevious(line, self.breakpointMask)
        if bpline < 0:
            # wrap around
            bpline = self.markerFindPrevious(
                self.lines() - 1, self.breakpointMask)
        if bpline >= 0:
            self.setCursorPosition(bpline, 0)
            self.ensureLineVisible(bpline)
        
    def __menuClearBreakpoints(self):
        """
        Private slot to handle the 'Clear all breakpoints' context menu action.
        """
        self.__clearBreakpoints(self.fileName)
        
    def __clearBreakpoints(self, fileName):
        """
        Private slot to clear all breakpoints.
        
        @param fileName name of the file (string)
        """
        idxList = []
        for (ln, _, _, _, _) in self.breaks.values():
            index = self.breakpointModel.getBreakPointIndex(fileName, ln)
            if index.isValid():
                idxList.append(index)
        if idxList:
            self.breakpointModel.deleteBreakPoints(idxList)
    
    ###########################################################################
    ## Bookmark handling methods below
    ###########################################################################
    
    def toggleBookmark(self, line):
        """
        Public method to toggle a bookmark.
        
        @param line line number of the bookmark (integer)
        """
        for handle in self.bookmarks:
            if self.markerLine(handle) == line - 1:
                self.bookmarks.remove(handle)
                self.markerDeleteHandle(handle)
                break
        else:
            # set a new bookmark
            handle = self.markerAdd(line - 1, self.bookmark)
            self.bookmarks.append(handle)
        self.bookmarkToggled.emit(self)
        self.__markerMap.update()
        
    def getBookmarks(self):
        """
        Public method to retrieve the bookmarks.
        
        @return sorted list of all lines containing a bookmark
            (list of integer)
        """
        bmlist = []
        for handle in self.bookmarks:
            bmlist.append(self.markerLine(handle) + 1)
        
        bmlist.sort()
        return bmlist
        
    def getBookmarkLines(self):
        """
        Public method to get the lines containing a bookmark.
        
        @return list of lines containing a bookmark (list of integer)
        """
        lines = []
        line = -1
        while True:
            line = self.markerFindNext(line + 1, 1 << self.bookmark)
            if line < 0:
                break
            else:
                lines.append(line)
        return lines
        
    def hasBookmarks(self):
        """
        Public method to check for the presence of bookmarks.
        
        @return flag indicating the presence of bookmarks (boolean)
        """
        return len(self.bookmarks) > 0
    
    def menuToggleBookmark(self):
        """
        Public slot to handle the 'Toggle bookmark' context menu action.
        """
        if self.line < 0:
            self.line, index = self.getCursorPosition()
        self.line += 1
        self.toggleBookmark(self.line)
        self.line = -1
        
    def nextBookmark(self):
        """
        Public slot to handle the 'Next bookmark' context menu action.
        """
        line, index = self.getCursorPosition()
        if line == self.lines() - 1:
            line = 0
        else:
            line += 1
        bmline = self.markerFindNext(line, 1 << self.bookmark)
        if bmline < 0:
            # wrap around
            bmline = self.markerFindNext(0, 1 << self.bookmark)
        if bmline >= 0:
            self.setCursorPosition(bmline, 0)
            self.ensureLineVisible(bmline)
        
    def previousBookmark(self):
        """
        Public slot to handle the 'Previous bookmark' context menu action.
        """
        line, index = self.getCursorPosition()
        if line == 0:
            line = self.lines() - 1
        else:
            line -= 1
        bmline = self.markerFindPrevious(line, 1 << self.bookmark)
        if bmline < 0:
            # wrap around
            bmline = self.markerFindPrevious(
                self.lines() - 1, 1 << self.bookmark)
        if bmline >= 0:
            self.setCursorPosition(bmline, 0)
            self.ensureLineVisible(bmline)
        
    def clearBookmarks(self):
        """
        Public slot to handle the 'Clear all bookmarks' context menu action.
        """
        for handle in self.bookmarks:
            self.markerDeleteHandle(handle)
        self.bookmarks = []
        self.bookmarkToggled.emit(self)
        self.__markerMap.update()
    
    ###########################################################################
    ## Printing methods below
    ###########################################################################

    def printFile(self):
        """
        Public slot to print the text.
        """
        from .Printer import Printer
        printer = Printer(mode=QPrinter.HighResolution)
        sb = e5App().getObject("UserInterface").statusBar()
        printDialog = QPrintDialog(printer, self)
        if self.hasSelectedText():
            printDialog.setOption(QAbstractPrintDialog.PrintSelection, True)
        if printDialog.exec_() == QDialog.Accepted:
            sb.showMessage(self.tr('Printing...'))
            QApplication.processEvents()
            fn = self.getFileName()
            if fn is not None:
                printer.setDocName(os.path.basename(fn))
            else:
                printer.setDocName(self.noName)
            if printDialog.printRange() == QAbstractPrintDialog.Selection:
                # get the selection
                fromLine, fromIndex, toLine, toIndex = self.getSelection()
                if toIndex == 0:
                    toLine -= 1
                # Qscintilla seems to print one line more than told
                res = printer.printRange(self, fromLine, toLine - 1)
            else:
                res = printer.printRange(self)
            if res:
                sb.showMessage(self.tr('Printing completed'), 2000)
            else:
                sb.showMessage(self.tr('Error while printing'), 2000)
            QApplication.processEvents()
        else:
            sb.showMessage(self.tr('Printing aborted'), 2000)
            QApplication.processEvents()

    def printPreviewFile(self):
        """
        Public slot to show a print preview of the text.
        """
        from PyQt5.QtPrintSupport import QPrintPreviewDialog
        from .Printer import Printer
        
        printer = Printer(mode=QPrinter.HighResolution)
        fn = self.getFileName()
        if fn is not None:
            printer.setDocName(os.path.basename(fn))
        else:
            printer.setDocName(self.noName)
        preview = QPrintPreviewDialog(printer, self)
        preview.paintRequested.connect(self.__printPreview)
        preview.exec_()
    
    def __printPreview(self, printer):
        """
        Private slot to generate a print preview.
        
        @param printer reference to the printer object
            (QScintilla.Printer.Printer)
        """
        printer.printRange(self)
    
    ###########################################################################
    ## Task handling methods below
    ###########################################################################
    
    def getTaskLines(self):
        """
        Public method to get the lines containing a task.
        
        @return list of lines containing a task (list of integer)
        """
        lines = []
        line = -1
        while True:
            line = self.markerFindNext(line + 1, 1 << self.taskmarker)
            if line < 0:
                break
            else:
                lines.append(line)
        return lines
        
    def hasTaskMarkers(self):
        """
        Public method to determine, if this editor contains any task markers.
        
        @return flag indicating the presence of task markers (boolean)
        """
        return self.__hasTaskMarkers
        
    def nextTask(self):
        """
        Public slot to handle the 'Next task' context menu action.
        """
        line, index = self.getCursorPosition()
        if line == self.lines() - 1:
            line = 0
        else:
            line += 1
        taskline = self.markerFindNext(line, 1 << self.taskmarker)
        if taskline < 0:
            # wrap around
            taskline = self.markerFindNext(0, 1 << self.taskmarker)
        if taskline >= 0:
            self.setCursorPosition(taskline, 0)
            self.ensureLineVisible(taskline)
        
    def previousTask(self):
        """
        Public slot to handle the 'Previous task' context menu action.
        """
        line, index = self.getCursorPosition()
        if line == 0:
            line = self.lines() - 1
        else:
            line -= 1
        taskline = self.markerFindPrevious(line, 1 << self.taskmarker)
        if taskline < 0:
            # wrap around
            taskline = self.markerFindPrevious(
                self.lines() - 1, 1 << self.taskmarker)
        if taskline >= 0:
            self.setCursorPosition(taskline, 0)
            self.ensureLineVisible(taskline)
        
    def extractTasks(self):
        """
        Public slot to extract all tasks.
        """
        from Tasks.Task import Task
        markers = {
            taskType: Preferences.getTasks(markersName).split()
            for taskType, markersName in Task.TaskType2MarkersName.items()
        }
        txtList = self.text().split(self.getLineSeparator())
        
        # clear all task markers and tasks
        self.markerDeleteAll(self.taskmarker)
        self.taskViewer.clearFileTasks(self.fileName)
        self.__hasTaskMarkers = False
        
        # now search tasks and record them
        lineIndex = -1
        for line in txtList:
            lineIndex += 1
            shouldBreak = False
            
            if line.endswith("__NO-TASK__"):
                # ignore potential task marker
                continue
            
            for taskType, taskMarkers in markers.items():
                for taskMarker in taskMarkers:
                    index = line.find(taskMarker)
                    if index > -1:
                        task = line[index:]
                        self.markerAdd(lineIndex, self.taskmarker)
                        self.taskViewer.addFileTask(
                            task, self.fileName, lineIndex + 1, taskType)
                        self.__hasTaskMarkers = True
                        shouldBreak = True
                        break
                if shouldBreak:
                    break
        self.taskMarkersUpdated.emit(self)
        self.__markerMap.update()
    
    ###########################################################################
    ## Change tracing methods below
    ###########################################################################
    
    def __createChangeMarkerPixmap(self, key, size=16, width=4):
        """
        Private method to create a pixmap for the change markers.
        
        @param key key of the color to use (string)
        @param size size of the pixmap (integer)
        @param width width of the marker line (integer)
        @return create pixmap (QPixmap)
        """
        pixmap = QPixmap(size, size)
        pixmap.fill(Qt.transparent)
        painter = QPainter(pixmap)
        painter.fillRect(size - 4, 0, 4, size,
                         Preferences.getEditorColour(key))
        painter.end()
        return pixmap
        
    def __initOnlineChangeTrace(self):
        """
        Private slot to initialize the online change trace.
        """
        self.__hasChangeMarkers = False
        self.__oldText = self.text()
        self.__lastSavedText = self.text()
        self.__onlineChangeTraceTimer = QTimer(self)
        self.__onlineChangeTraceTimer.setSingleShot(True)
        self.__onlineChangeTraceTimer.setInterval(
            Preferences.getEditor("OnlineChangeTraceInterval"))
        self.__onlineChangeTraceTimer.timeout.connect(
            self.__onlineChangeTraceTimerTimeout)
        self.textChanged.connect(self.__resetOnlineChangeTraceTimer)
        
    def __reinitOnlineChangeTrace(self):
        """
        Private slot to re-initialize the online change trace.
        """
        self.__oldText = self.text()
        self.__lastSavedText = self.text()
        self.__deleteAllChangeMarkers()
        
    def __resetOnlineChangeTraceTimer(self):
        """
        Private method to reset the online syntax check timer.
        """
        if Preferences.getEditor("OnlineChangeTrace"):
            self.__onlineChangeTraceTimer.stop()
            self.__onlineChangeTraceTimer.start()
        
    def __onlineChangeTraceTimerTimeout(self):
        """
        Private slot to mark added and changed lines.
        """
        self.__deleteAllChangeMarkers()
        
        # step 1: mark saved changes
        oldL = self.__oldText.splitlines()
        newL = self.__lastSavedText.splitlines()
        matcher = difflib.SequenceMatcher(None, oldL, newL)
        
        for token, _, _, j1, j2 in matcher.get_opcodes():
            if token in ["insert", "replace"]:
                for lineNo in range(j1, j2):
                    self.markerAdd(lineNo, self.__changeMarkerSaved)
                    self.__hasChangeMarkers = True
        
        # step 2: mark unsaved changes
        oldL = self.__lastSavedText.splitlines()
        newL = self.text().splitlines()
        matcher = difflib.SequenceMatcher(None, oldL, newL)
        
        for token, _, _, j1, j2 in matcher.get_opcodes():
            if token in ["insert", "replace"]:
                for lineNo in range(j1, j2):
                    self.markerAdd(lineNo, self.__changeMarkerUnsaved)
                    self.__hasChangeMarkers = True
        
        if self.__hasChangeMarkers:
            self.changeMarkersUpdated.emit(self)
            self.__markerMap.update()
        
    def __resetOnlineChangeTraceInfo(self):
        """
        Private slot to reset the online change trace info.
        """
        self.__lastSavedText = self.text()
        self.__deleteAllChangeMarkers()
        
        # mark saved changes
        oldL = self.__oldText.splitlines()
        newL = self.__lastSavedText.splitlines()
        matcher = difflib.SequenceMatcher(None, oldL, newL)
        
        for token, _, _, j1, j2 in matcher.get_opcodes():
            if token in ["insert", "replace"]:
                for lineNo in range(j1, j2):
                    self.markerAdd(lineNo, self.__changeMarkerSaved)
                    self.__hasChangeMarkers = True
        
        if self.__hasChangeMarkers:
            self.changeMarkersUpdated.emit(self)
            self.__markerMap.update()
        
    def __deleteAllChangeMarkers(self):
        """
        Private slot to delete all change markers.
        """
        self.markerDeleteAll(self.__changeMarkerUnsaved)
        self.markerDeleteAll(self.__changeMarkerSaved)
        self.__hasChangeMarkers = False
        self.changeMarkersUpdated.emit(self)
        self.__markerMap.update()
        
    def getChangeLines(self):
        """
        Public method to get the lines containing a change.
        
        @return list of lines containing a change (list of integer)
        """
        lines = []
        line = -1
        while True:
            line = self.markerFindNext(line + 1, self.changeMarkersMask)
            if line < 0:
                break
            else:
                lines.append(line)
        return lines
        
    def hasChangeMarkers(self):
        """
        Public method to determine, if this editor contains any change markers.
        
        @return flag indicating the presence of change markers (boolean)
        """
        return self.__hasChangeMarkers
        
    def nextChange(self):
        """
        Public slot to handle the 'Next change' context menu action.
        """
        line, index = self.getCursorPosition()
        if line == self.lines() - 1:
            line = 0
        else:
            line += 1
        changeline = self.markerFindNext(line, self.changeMarkersMask)
        if changeline < 0:
            # wrap around
            changeline = self.markerFindNext(0, self.changeMarkersMask)
        if changeline >= 0:
            self.setCursorPosition(changeline, 0)
            self.ensureLineVisible(changeline)
        
    def previousChange(self):
        """
        Public slot to handle the 'Previous change' context menu action.
        """
        line, index = self.getCursorPosition()
        if line == 0:
            line = self.lines() - 1
        else:
            line -= 1
        changeline = self.markerFindPrevious(line, self.changeMarkersMask)
        if changeline < 0:
            # wrap around
            changeline = self.markerFindPrevious(
                self.lines() - 1, self.changeMarkersMask)
        if changeline >= 0:
            self.setCursorPosition(changeline, 0)
            self.ensureLineVisible(changeline)
    
    ###########################################################################
    ## Flags handling methods below
    ###########################################################################
    
    def __processFlags(self):
        """
        Private method to extract flags and process them.
        
        @return list of change flags (list of string)
        """
        txt = self.text()
        flags = Utilities.extractFlags(txt)
        
        changedFlags = []
        
        # Flag 1: FileType
        if "FileType" in flags:
            oldFiletype = self.filetype
            if isinstance(flags["FileType"], str):
                self.filetype = flags["FileType"]
                self.filetypeByFlag = True
                if oldFiletype != self.filetype:
                    changedFlags.append("FileType")
        else:
            if self.filetype != "" and self.filetypeByFlag:
                self.filetype = ""
                self.filetypeByFlag = False
                self.__bindName(txt.splitlines()[0])
                changedFlags.append("FileType")
        
        return changedFlags
    
    ###########################################################################
    ## File handling methods below
    ###########################################################################
    
    def checkDirty(self):
        """
        Public method to check dirty status and open a message window.
        
        @return flag indicating successful reset of the dirty flag (boolean)
        """
        if self.isModified():
            fn = self.fileName
            if fn is None:
                fn = self.noName
            res = E5MessageBox.okToClearData(
                self,
                self.tr("File Modified"),
                self.tr("<p>The file <b>{0}</b> has unsaved changes.</p>")
                .format(fn),
                self.saveFile)
            if res:
                self.vm.setEditorName(self, self.fileName)
            return res
        
        return True
        
    def revertToUnmodified(self):
        """
        Public method to revert back to the last saved state.
        """
        undo_ = True
        while self.isModified():
            if undo_:
                # try undo first
                if self.isUndoAvailable():
                    self.undo()
                else:
                    undo_ = False
            else:
                # try redo next
                if self.isRedoAvailable():
                    self.redo()
                else:
                    break
                    # Couldn't find the unmodified state
    
    def readFile(self, fn, createIt=False, encoding=""):
        """
        Public slot to read the text from a file.
        
        @param fn filename to read from (string)
        @keyparam createIt flag indicating the creation of a new file, if the
            given one doesn't exist (boolean)
        @keyparam encoding encoding to be used to read the file (string)
            (Note: this parameter overrides encoding detection)
        """
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        
        self.__loadEditorConfig(fileName=fn)
        
        try:
            if createIt and not os.path.exists(fn):
                f = open(fn, "w")
                f.close()
            if encoding == "":
                encoding = self.__getEditorConfig("DefaultEncoding",
                                                  nodefault=True)
            if encoding:
                txt, self.encoding = Utilities.readEncodedFileWithEncoding(
                    fn, encoding)
            else:
                txt, self.encoding = Utilities.readEncodedFile(fn)
        except (UnicodeDecodeError, IOError) as why:
            QApplication.restoreOverrideCursor()
            E5MessageBox.critical(
                self.vm,
                self.tr('Open File'),
                self.tr('<p>The file <b>{0}</b> could not be opened.</p>'
                        '<p>Reason: {1}</p>')
                    .format(fn, str(why)))
            QApplication.restoreOverrideCursor()
            raise
        
        modified = False
        
        self.setText(txt)
        
        # get eric specific flags
        self.__processFlags()
        
        # perform automatic EOL conversion
        if (
            self.__getEditorConfig("EOLMode", nodefault=True) or
            Preferences.getEditor("AutomaticEOLConversion")
        ):
            self.convertEols(self.eolMode())
        else:
            fileEol = self.detectEolString(txt)
            self.setEolModeByEolString(fileEol)
        
        self.extractTasks()
        
        QApplication.restoreOverrideCursor()
        
        self.setModified(modified)
        self.lastModified = QFileInfo(self.fileName).lastModified()
    
    def __convertTabs(self):
        """
        Private slot to convert tabulators to spaces.
        """
        if (
            (not self.__getEditorConfig("TabForIndentation")) and
            Preferences.getEditor("ConvertTabsOnLoad") and
            not (self.lexer_ and
                 self.lexer_.alwaysKeepTabs())
        ):
            txt = self.text()
            txtExpanded = txt.expandtabs(self.__getEditorConfig("TabWidth"))
            if txtExpanded != txt:
                self.beginUndoAction()
                self.setText(txt)
                self.endUndoAction()
                
                self.setModified(True)
    
    def __removeTrailingWhitespace(self):
        """
        Private method to remove trailing whitespace.
        """
        searchRE = r"[ \t]+$"    # whitespace at the end of a line
        
        ok = self.findFirstTarget(searchRE, True, False, False, 0, 0)
        self.beginUndoAction()
        while ok:
            self.replaceTarget("")
            ok = self.findNextTarget()
        self.endUndoAction()
        
    def writeFile(self, fn, backup=True):
        """
        Public slot to write the text to a file.
        
        @param fn filename to write to (string)
        @param backup flag indicating to save a backup (boolean)
        @return flag indicating success (boolean)
        """
        config = self.__loadEditorConfigObject(fn)
        
        eol = self.__getEditorConfig("EOLMode", nodefault=True, config=config)
        if eol is not None:
            self.convertEols(eol)
        
        if self.__getEditorConfig("StripTrailingWhitespace", config=config):
            self.__removeTrailingWhitespace()
        
        txt = self.text()
        
        if self.__getEditorConfig("InsertFinalNewline", config=config):
            eol = self.getLineSeparator()
            if eol:
                if len(txt) >= len(eol):
                    if txt[-len(eol):] != eol:
                        txt += eol
                else:
                    txt += eol
        
        # create a backup file, if the option is set
        createBackup = backup and Preferences.getEditor("CreateBackupFile")
        if createBackup:
            if os.path.islink(fn):
                fn = os.path.realpath(fn)
            bfn = '{0}~'.format(fn)
            try:
                permissions = os.stat(fn).st_mode
                perms_valid = True
            except EnvironmentError:
                # if there was an error, ignore it
                perms_valid = False
            try:
                os.remove(bfn)
            except EnvironmentError:
                # if there was an error, ignore it
                pass
            try:
                os.rename(fn, bfn)
            except EnvironmentError:
                # if there was an error, ignore it
                pass
        
        # now write text to the file fn
        try:
            editorConfigEncoding = self.__getEditorConfig(
                "DefaultEncoding", nodefault=True, config=config)
            self.encoding = Utilities.writeEncodedFile(
                fn, txt, self.encoding, forcedEncoding=editorConfigEncoding)
            if createBackup and perms_valid:
                os.chmod(fn, permissions)
            return True
        except (IOError, Utilities.CodingError, UnicodeError) as why:
            E5MessageBox.critical(
                self,
                self.tr('Save File'),
                self.tr('<p>The file <b>{0}</b> could not be saved.<br/>'
                        'Reason: {1}</p>')
                .format(fn, str(why)))
            return False
        
    def __getSaveFileName(self, path=None):
        """
        Private method to get the name of the file to be saved.
        
        @param path directory to save the file in (string)
        @return file name (string)
        """
        # save to project, if a project is loaded
        if self.project.isOpen():
            if (
                self.fileName and
                self.project.startswithProjectPath(self.fileName)
            ):
                path = os.path.dirname(self.fileName)
            else:
                path = self.project.getProjectPath()
        
        if not path and self.fileName:
            path = os.path.dirname(self.fileName)
        if not path:
            path = (
                Preferences.getMultiProject("Workspace") or
                Utilities.getHomeDir()
            )
        
        from . import Lexers
        if self.fileName:
            filterPattern = "(*{0})".format(
                os.path.splitext(self.fileName)[1])
            for fileFilter in Lexers.getSaveFileFiltersList(True):
                if filterPattern in fileFilter:
                    defaultFilter = fileFilter
                    break
            else:
                defaultFilter = Preferences.getEditor("DefaultSaveFilter")
        else:
            defaultFilter = Preferences.getEditor("DefaultSaveFilter")
        fn, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
            self,
            self.tr("Save File"),
            path,
            Lexers.getSaveFileFiltersList(True, True),
            defaultFilter,
            E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
        
        if fn:
            if fn.endswith("."):
                fn = fn[:-1]
            
            ext = QFileInfo(fn).suffix()
            if not ext:
                ex = selectedFilter.split("(*")[1].split(")")[0]
                if ex:
                    fn += ex
            if QFileInfo(fn).exists():
                res = E5MessageBox.yesNo(
                    self,
                    self.tr("Save File"),
                    self.tr("<p>The file <b>{0}</b> already exists."
                            " Overwrite it?</p>").format(fn),
                    icon=E5MessageBox.Warning)
                if not res:
                    return ""
            fn = Utilities.toNativeSeparators(fn)
        
        return fn
        
    def saveFileCopy(self, path=None):
        """
        Public method to save a copy of the file.
        
        @param path directory to save the file in (string)
        @return flag indicating success (boolean)
        """
        fn = self.__getSaveFileName(path)
        if not fn:
            return False
        
        res = self.writeFile(fn)
        if res:
            # save to project, if a project is loaded
            if (
                self.project.isOpen() and
                self.project.startswithProjectPath(fn)
            ):
                self.project.appendFile(fn)
        
        return res
        
    def saveFile(self, saveas=False, path=None):
        """
        Public method to save the text to a file.
        
        @param saveas flag indicating a 'save as' action (boolean)
        @param path directory to save the file in (string)
        @return flag indicating success (boolean)
        """
        if not saveas and not self.isModified():
            return False      # do nothing if text wasn't changed
            
        newName = None
        if saveas or self.fileName == "":
            saveas = True
            
            fn = self.__getSaveFileName(path)
            if not fn:
                return False
            
            newName = fn
            
            # save to project, if a project is loaded
            if (
                self.project.isOpen() and
                self.project.startswithProjectPath(fn)
            ):
                editorConfigEol = self.__getEditorConfig(
                    "EOLMode", nodefault=True,
                    config=self.__loadEditorConfigObject(fn))
                if editorConfigEol is not None:
                    self.setEolMode(editorConfigEol)
                else:
                    self.setEolModeByEolString(self.project.getEolString())
                self.convertEols(self.eolMode())
        else:
            fn = self.fileName
        
        self.__loadEditorConfig(fn)
        self.editorAboutToBeSaved.emit(self.fileName)
        if self.writeFile(fn):
            if saveas:
                self.__clearBreakpoints(self.fileName)
            self.__setFileName(fn)
            self.setModified(False)
            self.setReadOnly(False)
            self.setWindowTitle(self.fileName)
            # get eric specific flags
            changedFlags = self.__processFlags()
            if not self.__lexerReset and "FileType" in changedFlags:
                self.setLanguage(self.fileName)
            
            if saveas:
                self.isResourcesFile = self.fileName.endswith(".qrc")
                self.__initContextMenu()
                self.editorRenamed.emit(self.fileName)
                
                # save to project, if a project is loaded
                if (
                    self.project.isOpen() and
                    self.project.startswithProjectPath(fn)
                ):
                    self.project.appendFile(fn)
                    self.addedToProject()
                
                self.setLanguage(self.fileName)
            
            self.lastModified = QFileInfo(self.fileName).lastModified()
            if newName is not None:
                self.vm.addToRecentList(newName)
            self.editorSaved.emit(self.fileName)
            self.checkSyntax()
            self.extractTasks()
            self.__resetOnlineChangeTraceInfo()
            self.__checkEncoding()
            return True
        else:
            self.lastModified = QFileInfo(fn).lastModified()
            return False
        
    def saveFileAs(self, path=None, toProject=False):
        """
        Public slot to save a file with a new name.
        
        @param path directory to save the file in (string)
        @keyparam toProject flag indicating a save to project operation
            (boolean)
        @return tuple of two values (boolean, string) giving a success
            indicator and the name of the saved file
        """
        return self.saveFile(True, path)
        
    def handleRenamed(self, fn):
        """
        Public slot to handle the editorRenamed signal.
        
        @param fn filename to be set for the editor (string).
        """
        self.__clearBreakpoints(fn)
        
        self.__setFileName(fn)
        self.setWindowTitle(self.fileName)
        
        self.__loadEditorConfig()
        
        if self.lexer_ is None:
            self.setLanguage(self.fileName)
        
        self.lastModified = QFileInfo(self.fileName).lastModified()
        self.vm.setEditorName(self, self.fileName)
        self.__updateReadOnly(True)
        
    def fileRenamed(self, fn):
        """
        Public slot to handle the editorRenamed signal.
        
        @param fn filename to be set for the editor (string).
        """
        self.handleRenamed(fn)
        if not self.inFileRenamed:
            self.inFileRenamed = True
            self.editorRenamed.emit(self.fileName)
            self.inFileRenamed = False
    
    ###########################################################################
    ## Utility methods below
    ###########################################################################

    def ensureVisible(self, line, expand=False):
        """
        Public slot to ensure, that the specified line is visible.
        
        @param line line number to make visible
        @type int
        @keyparam expand flag indicating to expand all folds
        @type bool
        """
        self.ensureLineVisible(line - 1)
        if expand:
            self.SendScintilla(QsciScintilla.SCI_FOLDCHILDREN, line - 1,
                               QsciScintilla.SC_FOLDACTION_EXPAND)
        
    def ensureVisibleTop(self, line, expand=False):
        """
        Public slot to ensure, that the specified line is visible at the top
        of the editor.
        
        @param line line number to make visible
        @type int
        @keyparam expand flag indicating to expand all folds
        @type bool
        """
        self.ensureVisible(line)
        self.setFirstVisibleLine(line - 1)
        self.ensureCursorVisible()
        if expand:
            self.SendScintilla(QsciScintilla.SCI_FOLDCHILDREN, line - 1,
                               QsciScintilla.SC_FOLDACTION_EXPAND)
        
    def __marginClicked(self, margin, line, modifiers):
        """
        Private slot to handle the marginClicked signal.
        
        @param margin id of the clicked margin (integer)
        @param line line number of the click (integer)
        @param modifiers keyboard modifiers (Qt.KeyboardModifiers)
        """
        if margin == self.__bmMargin:
            self.toggleBookmark(line + 1)
        elif margin == self.__bpMargin:
            self.__toggleBreakpoint(line + 1)
        elif margin == self.__indicMargin:
            if self.markersAtLine(line) & (1 << self.syntaxerror):
                self.__showSyntaxError(line)
            elif self.markersAtLine(line) & (1 << self.warning):
                self.__showWarning(line)
        
    def handleMonospacedEnable(self):
        """
        Public slot to handle the Use Monospaced Font context menu entry.
        """
        if self.menuActs["MonospacedFont"].isChecked():
            if not self.lexer_:
                self.setMonospaced(True)
        else:
            if self.lexer_:
                self.lexer_.readSettings(
                    Preferences.Prefs.settings, "Scintilla")
                if self.lexer_.hasSubstyles():
                    self.lexer_.readSubstyles(self)
                self.lexer_.initProperties()
            self.setMonospaced(False)
            self.__setMarginsDisplay()
        
    def getWordBoundaries(self, line, index, useWordChars=True):
        """
        Public method to get the word boundaries at a position.
        
        @param line number of line to look at (int)
        @param index position to look at (int)
        @keyparam useWordChars flag indicating to use the wordCharacters
            method (boolean)
        @return tuple with start and end indexes of the word at the position
            (integer, integer)
        """
        text = self.text(line)
        if self.caseSensitive():
            cs = Qt.CaseSensitive
        else:
            cs = Qt.CaseInsensitive
        wc = self.wordCharacters()
        if wc is None or not useWordChars:
            regExp = QRegExp(r'[^\w_]', cs)
        else:
            wc = re.sub(r'\w', "", wc)
            regExp = QRegExp(r'[^\w{0}]'.format(re.escape(wc)), cs)
        start = regExp.lastIndexIn(text, index) + 1
        end = regExp.indexIn(text, index)
        if start == end + 1 and index > 0:
            # we are on a word boundary, try again
            start = regExp.lastIndexIn(text, index - 1) + 1
        if start == -1:
            start = 0
        if end == -1:
            end = len(text)
        
        return (start, end)
        
    def getWord(self, line, index, direction=0, useWordChars=True):
        """
        Public method to get the word at a position.
        
        @param line number of line to look at (int)
        @param index position to look at (int)
        @param direction direction to look in (0 = whole word, 1 = left,
            2 = right)
        @keyparam useWordChars flag indicating to use the wordCharacters
            method (boolean)
        @return the word at that position (string)
        """
        start, end = self.getWordBoundaries(line, index, useWordChars)
        if direction == 1:
            end = index
        elif direction == 2:
            start = index
        if end > start:
            text = self.text(line)
            word = text[start:end]
        else:
            word = ''
        return word
        
    def getWordLeft(self, line, index):
        """
        Public method to get the word to the left of a position.
        
        @param line number of line to look at (int)
        @param index position to look at (int)
        @return the word to the left of that position (string)
        """
        return self.getWord(line, index, 1)
        
    def getWordRight(self, line, index):
        """
        Public method to get the word to the right of a position.
        
        @param line number of line to look at (int)
        @param index position to look at (int)
        @return the word to the right of that position (string)
        """
        return self.getWord(line, index, 2)
        
    def getCurrentWord(self):
        """
        Public method to get the word at the current position.
        
        @return the word at that current position (string)
        """
        line, index = self.getCursorPosition()
        return self.getWord(line, index)
        
    def getCurrentWordBoundaries(self):
        """
        Public method to get the word boundaries at the current position.
        
        @return tuple with start and end indexes of the current word
            (integer, integer)
        """
        line, index = self.getCursorPosition()
        return self.getWordBoundaries(line, index)
        
    def selectWord(self, line, index):
        """
        Public method to select the word at a position.
        
        @param line number of line to look at (int)
        @param index position to look at (int)
        """
        start, end = self.getWordBoundaries(line, index)
        self.setSelection(line, start, line, end)
        
    def selectCurrentWord(self):
        """
        Public method to select the current word.
        """
        line, index = self.getCursorPosition()
        self.selectWord(line, index)
        
    def __getCharacter(self, pos):
        """
        Private method to get the character to the left of the current position
        in the current line.
        
        @param pos position to get character at (integer)
        @return requested character or "", if there are no more (string) and
            the next position (i.e. pos - 1)
        """
        if pos <= 0:
            return "", pos
        
        pos = self.positionBefore(pos)
        ch = self.charAt(pos)
        
        # Don't go past the end of the previous line
        if ch == '\n' or ch == '\r':
            return "", pos
        
        return ch, pos
    
    def getSearchText(self, selectionOnly=False):
        """
        Public method to determine the selection or the current word for the
        next search operation.
        
        @param selectionOnly flag indicating that only selected text should be
            returned (boolean)
        @return selection or current word (string)
        """
        if self.hasSelectedText():
            text = self.selectedText()
            if '\r' in text or '\n' in text:
                # the selection contains at least a newline, it is
                # unlikely to be the expression to search for
                return ''
            
            return text
        
        if not selectionOnly:
            # no selected text, determine the word at the current position
            return self.getCurrentWord()
        
        return ''
        
    def setSearchIndicator(self, startPos, indicLength):
        """
        Public method to set a search indicator for the given range.
        
        @param startPos start position of the indicator (integer)
        @param indicLength length of the indicator (integer)
        """
        self.setIndicatorRange(self.searchIndicator, startPos, indicLength)
        line = self.lineIndexFromPosition(startPos)[0]
        if line not in self.__searchIndicatorLines:
            self.__searchIndicatorLines.append(line)
        
    def clearSearchIndicators(self):
        """
        Public method to clear all search indicators.
        """
        self.clearAllIndicators(self.searchIndicator)
        self.__markedText = ""
        self.__searchIndicatorLines = []
        self.__markerMap.update()
        
    def __markOccurrences(self):
        """
        Private method to mark all occurrences of the current word.
        """
        word = self.getCurrentWord()
        if not word:
            self.clearSearchIndicators()
            return
        
        if self.__markedText == word:
            return
        
        self.clearSearchIndicators()
        ok = self.findFirstTarget(word, False, self.caseSensitive(), True,
                                  0, 0)
        while ok:
            tgtPos, tgtLen = self.getFoundTarget()
            self.setSearchIndicator(tgtPos, tgtLen)
            ok = self.findNextTarget()
        self.__markedText = word
        self.__markerMap.update()
    
    def getSearchIndicatorLines(self):
        """
        Public method to get the lines containing a search indicator.
        
        @return list of lines containing a search indicator (list of integer)
        """
        return self.__searchIndicatorLines[:]
    
    def updateMarkerMap(self):
        """
        Public method to initiate an update of the marker map.
        """
        self.__markerMap.update()
    
    ###########################################################################
    ## Highlighting marker handling methods below
    ###########################################################################
    
    def setHighlight(self, startLine, startIndex, endLine, endIndex):
        """
        Public method to set a text highlight.
        
        @param startLine line of the highlight start
        @type int
        @param startIndex index of the highlight start
        @type int
        @param endLine line of the highlight end
        @type int
        @param endIndex index of the highlight end
        @type int
        """
        self.setIndicator(self.highlightIndicator, startLine, startIndex,
                          endLine, endIndex)
    
    def clearAllHighlights(self):
        """
        Public method to clear all highlights.
        """
        self.clearAllIndicators(self.highlightIndicator)
    
    def clearHighlight(self, startLine, startIndex, endLine, endIndex):
        """
        Public method to clear a text highlight.
        
        @param startLine line of the highlight start
        @type int
        @param startIndex index of the highlight start
        @type int
        @param endLine line of the highlight end
        @type int
        @param endIndex index of the highlight end
        @type int
        """
        self.clearIndicator(self.highlightIndicator, startLine, startIndex,
                            endLine, endIndex)
    
    ###########################################################################
    ## Comment handling methods below
    ###########################################################################
    
    def __isCommentedLine(self, line, commentStr):
        """
        Private method to check, if the given line is a comment line as
        produced by the configured comment rules.
        
        @param line text of the line to check (string)
        @param commentStr comment string to check against (string)
        @return flag indicating a commented line (boolean)
        """
        if Preferences.getEditor("CommentColumn0"):
            return line.startswith(commentStr)
        else:
            return line.strip().startswith(commentStr)
    
    def toggleCommentBlock(self):
        """
        Public slot to toggle the comment of a block.
        
        If the line of the cursor or the selection is not commented, it will
        be commented. If it is commented, the comment block will be removed.
        The later works independent of the current selection.
        """
        if self.lexer_ is None or not self.lexer_.canBlockComment():
            return
        
        commentStr = self.lexer_.commentStr()
        line, index = self.getCursorPosition()
        
        # check if line starts with our comment string (i.e. was commented
        # by our comment...() slots
        if (
            self.hasSelectedText() and
            self.__isCommentedLine(self.text(self.getSelection()[0]),
                                   commentStr)
        ):
            self.uncommentLineOrSelection()
        elif not self.__isCommentedLine(self.text(line), commentStr):
            # it doesn't, so comment the line or selection
            self.commentLineOrSelection()
        else:
            # determine the start of the comment block
            begline = line
            while (
                begline > 0 and
                self.__isCommentedLine(self.text(begline - 1), commentStr)
            ):
                begline -= 1
            # determine the end of the comment block
            endline = line
            lines = self.lines()
            while (
                endline < lines and
                self.__isCommentedLine(self.text(endline + 1), commentStr)
            ):
                endline += 1
            
            self.setSelection(begline, 0, endline, self.lineLength(endline))
            self.uncommentLineOrSelection()
            
            # reset the cursor
            self.setCursorPosition(line, index - len(commentStr))
        
    def commentLine(self):
        """
        Public slot to comment the current line.
        """
        if self.lexer_ is None or not self.lexer_.canBlockComment():
            return
        
        line, index = self.getCursorPosition()
        self.beginUndoAction()
        if Preferences.getEditor("CommentColumn0"):
            self.insertAt(self.lexer_.commentStr(), line, 0)
        else:
            self.insertAt(self.lexer_.commentStr(), line,
                          self.indentation(line))
        self.endUndoAction()
        
    def uncommentLine(self):
        """
        Public slot to uncomment the current line.
        """
        if self.lexer_ is None or not self.lexer_.canBlockComment():
            return
        
        commentStr = self.lexer_.commentStr()
        line, index = self.getCursorPosition()
        
        # check if line starts with our comment string (i.e. was commented
        # by our comment...() slots
        if not self.__isCommentedLine(self.text(line), commentStr):
            return
        
        # now remove the comment string
        self.beginUndoAction()
        if Preferences.getEditor("CommentColumn0"):
            self.setSelection(line, 0, line, len(commentStr))
        else:
            self.setSelection(line, self.indentation(line),
                              line, self.indentation(line) + len(commentStr))
        self.removeSelectedText()
        self.endUndoAction()
        
    def commentSelection(self):
        """
        Public slot to comment the current selection.
        """
        if self.lexer_ is None or not self.lexer_.canBlockComment():
            return
        
        if not self.hasSelectedText():
            return
        
        commentStr = self.lexer_.commentStr()
        
        # get the selection boundaries
        lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
        if indexTo == 0:
            endLine = lineTo - 1
        else:
            endLine = lineTo
        
        self.beginUndoAction()
        # iterate over the lines
        for line in range(lineFrom, endLine + 1):
            if Preferences.getEditor("CommentColumn0"):
                self.insertAt(commentStr, line, 0)
            else:
                self.insertAt(commentStr, line, self.indentation(line))
        
        # change the selection accordingly
        self.setSelection(lineFrom, 0, endLine + 1, 0)
        self.endUndoAction()
        
    def uncommentSelection(self):
        """
        Public slot to uncomment the current selection.
        """
        if self.lexer_ is None or not self.lexer_.canBlockComment():
            return
        
        if not self.hasSelectedText():
            return
        
        commentStr = self.lexer_.commentStr()
        
        # get the selection boundaries
        lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
        if indexTo == 0:
            endLine = lineTo - 1
        else:
            endLine = lineTo
        
        self.beginUndoAction()
        # iterate over the lines
        for line in range(lineFrom, endLine + 1):
            # check if line starts with our comment string (i.e. was commented
            # by our comment...() slots
            if not self.__isCommentedLine(self.text(line), commentStr):
                continue
            
            if Preferences.getEditor("CommentColumn0"):
                self.setSelection(line, 0, line, len(commentStr))
            else:
                self.setSelection(line,
                                  self.indentation(line),
                                  line,
                                  self.indentation(line) + len(commentStr))
            self.removeSelectedText()
            
            # adjust selection start
            if line == lineFrom:
                indexFrom -= len(commentStr)
                if indexFrom < 0:
                    indexFrom = 0
            
            # adjust selection end
            if line == lineTo:
                indexTo -= len(commentStr)
                if indexTo < 0:
                    indexTo = 0
            
        # change the selection accordingly
        self.setSelection(lineFrom, indexFrom, lineTo, indexTo)
        self.endUndoAction()
        
    def commentLineOrSelection(self):
        """
        Public slot to comment the current line or current selection.
        """
        if self.hasSelectedText():
            self.commentSelection()
        else:
            self.commentLine()

    def uncommentLineOrSelection(self):
        """
        Public slot to uncomment the current line or current selection.
        """
        if self.hasSelectedText():
            self.uncommentSelection()
        else:
            self.uncommentLine()

    def streamCommentLine(self):
        """
        Public slot to stream comment the current line.
        """
        if self.lexer_ is None or not self.lexer_.canStreamComment():
            return
        
        commentStr = self.lexer_.streamCommentStr()
        line, index = self.getCursorPosition()
        
        self.beginUndoAction()
        self.insertAt(commentStr['end'], line, self.lineLength(line))
        self.insertAt(commentStr['start'], line, 0)
        self.endUndoAction()
        
    def streamCommentSelection(self):
        """
        Public slot to comment the current selection.
        """
        if self.lexer_ is None or not self.lexer_.canStreamComment():
            return
        
        if not self.hasSelectedText():
            return
        
        commentStr = self.lexer_.streamCommentStr()
        
        # get the selection boundaries
        lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
        if indexTo == 0:
            endLine = lineTo - 1
            endIndex = self.lineLength(endLine)
        else:
            endLine = lineTo
            endIndex = indexTo
        
        self.beginUndoAction()
        self.insertAt(commentStr['end'], endLine, endIndex)
        self.insertAt(commentStr['start'], lineFrom, indexFrom)
        
        # change the selection accordingly
        if indexTo > 0:
            indexTo += len(commentStr['end'])
            if lineFrom == endLine:
                indexTo += len(commentStr['start'])
        self.setSelection(lineFrom, indexFrom, lineTo, indexTo)
        self.endUndoAction()
        
    def streamCommentLineOrSelection(self):
        """
        Public slot to stream comment the current line or current selection.
        """
        if self.hasSelectedText():
            self.streamCommentSelection()
        else:
            self.streamCommentLine()
        
    def boxCommentLine(self):
        """
        Public slot to box comment the current line.
        """
        if self.lexer_ is None or not self.lexer_.canBoxComment():
            return
        
        commentStr = self.lexer_.boxCommentStr()
        line, index = self.getCursorPosition()
        
        eol = self.getLineSeparator()
        self.beginUndoAction()
        self.insertAt(eol, line, self.lineLength(line))
        self.insertAt(commentStr['end'], line + 1, 0)
        self.insertAt(commentStr['middle'], line, 0)
        self.insertAt(eol, line, 0)
        self.insertAt(commentStr['start'], line, 0)
        self.endUndoAction()
        
    def boxCommentSelection(self):
        """
        Public slot to box comment the current selection.
        """
        if self.lexer_ is None or not self.lexer_.canBoxComment():
            return
        
        if not self.hasSelectedText():
            return
            
        commentStr = self.lexer_.boxCommentStr()
        
        # get the selection boundaries
        lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
        if indexTo == 0:
            endLine = lineTo - 1
        else:
            endLine = lineTo
        
        self.beginUndoAction()
        # iterate over the lines
        for line in range(lineFrom, endLine + 1):
            self.insertAt(commentStr['middle'], line, 0)
        
        # now do the comments before and after the selection
        eol = self.getLineSeparator()
        self.insertAt(eol, endLine, self.lineLength(endLine))
        self.insertAt(commentStr['end'], endLine + 1, 0)
        self.insertAt(eol, lineFrom, 0)
        self.insertAt(commentStr['start'], lineFrom, 0)
        
        # change the selection accordingly
        self.setSelection(lineFrom, 0, endLine + 3, 0)
        self.endUndoAction()
        
    def boxCommentLineOrSelection(self):
        """
        Public slot to box comment the current line or current selection.
        """
        if self.hasSelectedText():
            self.boxCommentSelection()
        else:
            self.boxCommentLine()
    
    ###########################################################################
    ## Indentation handling methods below
    ###########################################################################

    def __indentLine(self, indent=True):
        """
        Private method to indent or unindent the current line.
        
        @param indent flag indicating an indent operation (boolean)
                <br />If the flag is true, an indent operation is performed.
                Otherwise the current line is unindented.
        """
        line, index = self.getCursorPosition()
        self.beginUndoAction()
        if indent:
            self.indent(line)
        else:
            self.unindent(line)
        self.endUndoAction()
        if indent:
            self.setCursorPosition(line, index + self.indentationWidth())
        else:
            self.setCursorPosition(line, index - self.indentationWidth())
        
    def __indentSelection(self, indent=True):
        """
        Private method to indent or unindent the current selection.
        
        @param indent flag indicating an indent operation (boolean)
                <br />If the flag is true, an indent operation is performed.
                Otherwise the current line is unindented.
        """
        if not self.hasSelectedText():
            return
        
        # get the selection
        lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
        
        if indexTo == 0:
            endLine = lineTo - 1
        else:
            endLine = lineTo
        
        self.beginUndoAction()
        # iterate over the lines
        for line in range(lineFrom, endLine + 1):
            if indent:
                self.indent(line)
            else:
                self.unindent(line)
        self.endUndoAction()
        if indent:
            if indexTo == 0:
                self.setSelection(
                    lineFrom, indexFrom + self.indentationWidth(),
                    lineTo, 0)
            else:
                self.setSelection(
                    lineFrom, indexFrom + self.indentationWidth(),
                    lineTo, indexTo + self.indentationWidth())
        else:
            indexStart = indexFrom - self.indentationWidth()
            if indexStart < 0:
                indexStart = 0
            indexEnd = indexTo - self.indentationWidth()
            if indexEnd < 0:
                indexEnd = 0
            self.setSelection(lineFrom, indexStart, lineTo, indexEnd)
        
    def indentLineOrSelection(self):
        """
        Public slot to indent the current line or current selection.
        """
        if self.hasSelectedText():
            self.__indentSelection(True)
        else:
            self.__indentLine(True)
        
    def unindentLineOrSelection(self):
        """
        Public slot to unindent the current line or current selection.
        """
        if self.hasSelectedText():
            self.__indentSelection(False)
        else:
            self.__indentLine(False)
        
    def smartIndentLineOrSelection(self):
        """
        Public slot to indent current line smartly.
        """
        if self.hasSelectedText():
            if self.lexer_ and self.lexer_.hasSmartIndent():
                self.lexer_.smartIndentSelection(self)
            else:
                self.__indentSelection(True)
        else:
            if self.lexer_ and self.lexer_.hasSmartIndent():
                self.lexer_.smartIndentLine(self)
            else:
                self.__indentLine(True)
        
    def gotoLine(self, line, pos=1, firstVisible=False, expand=False):
        """
        Public slot to jump to the beginning of a line.
        
        @param line line number to go to
        @type int
        @keyparam pos position in line to go to
        @type int
        @keyparam firstVisible flag indicating to make the line the first
            visible line
        @type bool
        @keyparam expand flag indicating to expand all folds
        @type bool
        """
        self.setCursorPosition(line - 1, pos - 1)
        if firstVisible:
            self.ensureVisibleTop(line, expand)
        else:
            self.ensureVisible(line, expand)
    
    def __textChanged(self):
        """
        Private slot to handle a change of the editor text.
        
        This slot defers the handling to the next time the event loop
        is run in order to ensure, that cursor position has been updated
        by the underlying Scintilla editor.
        """
        QTimer.singleShot(0, self.__saveLastEditPosition)
    
    def __saveLastEditPosition(self):
        """
        Private slot to record the last edit position.
        """
        self.__lastEditPosition = self.getCursorPosition()
        self.lastEditPositionAvailable.emit()
    
    def isLastEditPositionAvailable(self):
        """
        Public method to check, if a last edit position is available.
        
        @return flag indicating availability (boolean)
        """
        return self.__lastEditPosition is not None
    
    def gotoLastEditPosition(self):
        """
        Public method to move the cursor to the last edit position.
        """
        self.setCursorPosition(*self.__lastEditPosition)
        self.ensureVisible(self.__lastEditPosition[0])
    
    def gotoMethodClass(self, goUp=False):
        """
        Public method to go to the next Python method or class definition.
        
        @param goUp flag indicating the move direction (boolean)
        """
        if self.isPyFile() or self.isRubyFile():
            lineNo = self.getCursorPosition()[0]
            line = self.text(lineNo)
            if line.strip().startswith(("class ", "def ", "module ")):
                if goUp:
                    lineNo -= 1
                else:
                    lineNo += 1
            while True:
                if goUp and lineNo < 0:
                    self.setCursorPosition(0, 0)
                    self.ensureVisible(0)
                    return
                elif not goUp and lineNo == self.lines():
                    lineNo = self.lines() - 1
                    self.setCursorPosition(lineNo, self.lineLength(lineNo))
                    self.ensureVisible(lineNo)
                    return
                
                line = self.text(lineNo)
                if line.strip().startswith(("class ", "def ", "module ")):
                    # try 'def ' first because it occurs more often
                    first = line.find("def ")
                    if first > -1:
                        first += 4
                    else:
                        first = line.find("class ")
                        if first > -1:
                            first += 6
                        else:
                            first = line.find("module ") + 7
                    match = re.search("[:(]", line)
                    if match:
                        end = match.start()
                    else:
                        end = self.lineLength(lineNo) - 1
                    self.setSelection(lineNo, first, lineNo, end)
                    self.ensureVisible(lineNo)
                    return
                
                if goUp:
                    lineNo -= 1
                else:
                    lineNo += 1
    
    ###########################################################################
    ## Setup methods below
    ###########################################################################

    def readSettings(self):
        """
        Public slot to read the settings into our lexer.
        """
        # read the lexer settings and reinit the properties
        if self.lexer_ is not None:
            self.lexer_.readSettings(Preferences.Prefs.settings, "Scintilla")
            if self.lexer_.hasSubstyles():
                self.lexer_.readSubstyles(self)
            self.lexer_.initProperties()
            
            self.lexer_.setDefaultColor(self.lexer_.color(0))
            self.lexer_.setDefaultPaper(self.lexer_.paper(0))
        
        self.__bindLexer(self.fileName)
        self.recolor()
        
        # read the typing completer settings
        if self.completer is not None:
            self.completer.readSettings()
        
        # set the line marker colours or pixmap
        if Preferences.getEditor("LineMarkersBackground"):
            self.markerDefine(QsciScintilla.Background, self.currentline)
            self.markerDefine(QsciScintilla.Background, self.errorline)
            self.__setLineMarkerColours()
        else:
            self.markerDefine(
                UI.PixmapCache.getPixmap("currentLineMarker"),
                self.currentline)
            self.markerDefine(
                UI.PixmapCache.getPixmap("errorLineMarker"),
                self.errorline)
        
        # set the text display
        self.__setTextDisplay()
        
        # set margin 0 and 2 configuration
        self.__setMarginsDisplay()
        
        # set the auto-completion function
        self.__acCache.setSize(
            Preferences.getEditor("AutoCompletionCacheSize"))
        self.__acCache.setMaximumCacheTime(
            Preferences.getEditor("AutoCompletionCacheTime"))
        self.__acCacheEnabled = Preferences.getEditor(
            "AutoCompletionCacheEnabled")
        acTimeout = Preferences.getEditor("AutoCompletionTimeout")
        if acTimeout != self.__acTimer.interval:
            self.__acTimer.setInterval(acTimeout)
        self.__setAutoCompletion()
        
        # set the calltips function
        self.__setCallTips()
        
        # set the autosave flags
        self.autosaveEnabled = Preferences.getEditor("AutosaveInterval") > 0
        
        if Preferences.getEditor("MiniContextMenu") != self.miniMenu:
            # regenerate context menu
            self.__initContextMenu()
        else:
            # set checked context menu items
            self.menuActs["AutoCompletionEnable"].setChecked(
                self.autoCompletionThreshold() != -1)
            self.menuActs["MonospacedFont"].setChecked(
                self.useMonospaced)
            self.menuActs["AutosaveEnable"].setChecked(
                self.autosaveEnabled and not self.autosaveManuallyDisabled)
        
        # regenerate the margins context menu(s)
        self.__initContextMenuMargins()
        
        if Preferences.getEditor("MarkOccurrencesEnabled"):
            self.__markOccurrencesTimer.setInterval(
                Preferences.getEditor("MarkOccurrencesTimeout"))
        else:
            self.__markOccurrencesTimer.stop()
            self.clearSearchIndicators()
        
        if Preferences.getEditor("OnlineSyntaxCheck"):
            self.__onlineSyntaxCheckTimer.setInterval(
                Preferences.getEditor("OnlineSyntaxCheckInterval") * 1000)
        else:
            self.__onlineSyntaxCheckTimer.stop()
        
        if Preferences.getEditor("OnlineChangeTrace"):
            self.__onlineChangeTraceTimer.setInterval(
                Preferences.getEditor("OnlineChangeTraceInterval"))
        else:
            self.__onlineChangeTraceTimer.stop()
            self.__deleteAllChangeMarkers()
        self.markerDefine(self.__createChangeMarkerPixmap(
            "OnlineChangeTraceMarkerUnsaved"), self.__changeMarkerUnsaved)
        self.markerDefine(self.__createChangeMarkerPixmap(
            "OnlineChangeTraceMarkerSaved"), self.__changeMarkerSaved)
        
        # refresh the annotations display
        self.__refreshAnnotations()
        
        self.__markerMap.setMapPosition(
            Preferences.getEditor("ShowMarkerMapOnRight"))
        self.__markerMap.initColors()
        
        self.settingsRead.emit()
    
    def __setLineMarkerColours(self):
        """
        Private method to set the line marker colours.
        """
        self.setMarkerForegroundColor(
            Preferences.getEditorColour("CurrentMarker"), self.currentline)
        self.setMarkerBackgroundColor(
            Preferences.getEditorColour("CurrentMarker"), self.currentline)
        self.setMarkerForegroundColor(
            Preferences.getEditorColour("ErrorMarker"), self.errorline)
        self.setMarkerBackgroundColor(
            Preferences.getEditorColour("ErrorMarker"), self.errorline)
        
    def __setMarginsDisplay(self):
        """
        Private method to configure margins 0 and 2.
        """
        # set the settings for all margins
        self.setMarginsFont(Preferences.getEditorOtherFonts("MarginsFont"))
        self.setMarginsForegroundColor(
            Preferences.getEditorColour("MarginsForeground"))
        self.setMarginsBackgroundColor(
            Preferences.getEditorColour("MarginsBackground"))
        
        # reset standard margins settings
        for margin in range(5):
            self.setMarginLineNumbers(margin, False)
            self.setMarginMarkerMask(margin, 0)
            self.setMarginWidth(margin, 0)
            self.setMarginSensitivity(margin, False)
        
        # set marker margin(s) settings
        self.__bmMargin = 0
        self.__linenoMargin = 1
        self.__bpMargin = 2
        self.__foldMargin = 3
        self.__indicMargin = 4
        
        marginBmMask = (1 << self.bookmark)
        self.setMarginWidth(self.__bmMargin, 16)
        self.setMarginSensitivity(self.__bmMargin, True)
        self.setMarginMarkerMask(self.__bmMargin, marginBmMask)
        
        marginBpMask = (
            (1 << self.breakpoint) |
            (1 << self.cbreakpoint) |
            (1 << self.tbreakpoint) |
            (1 << self.tcbreakpoint) |
            (1 << self.dbreakpoint)
        )
        self.setMarginWidth(self.__bpMargin, 16)
        self.setMarginSensitivity(self.__bpMargin, True)
        self.setMarginMarkerMask(self.__bpMargin, marginBpMask)
        
        marginIndicMask = (
            (1 << self.syntaxerror) |
            (1 << self.notcovered) |
            (1 << self.taskmarker) |
            (1 << self.warning) |
            (1 << self.__changeMarkerUnsaved) |
            (1 << self.__changeMarkerSaved) |
            (1 << self.currentline) |
            (1 << self.errorline)
        )
        self.setMarginWidth(self.__indicMargin, 16)
        self.setMarginSensitivity(self.__indicMargin, True)
        self.setMarginMarkerMask(self.__indicMargin, marginIndicMask)
        
        # set linenumber margin settings
        linenoMargin = Preferences.getEditor("LinenoMargin")
        self.setMarginLineNumbers(self.__linenoMargin, linenoMargin)
        if linenoMargin:
            self.__resizeLinenoMargin()
        else:
            self.setMarginWidth(self.__linenoMargin, 0)
        
        # set folding margin settings
        if Preferences.getEditor("FoldingMargin"):
            self.setMarginWidth(self.__foldMargin, 16)
            folding = Preferences.getEditor("FoldingStyle")
            try:
                folding = QsciScintilla.FoldStyle(folding)
            except AttributeError:
                pass
            self.setFolding(folding, self.__foldMargin)
            self.setFoldMarginColors(
                Preferences.getEditorColour("FoldmarginBackground"),
                Preferences.getEditorColour("FoldmarginBackground"))
            self.setFoldMarkersColors(
                Preferences.getEditorColour("FoldMarkersForeground"),
                Preferences.getEditorColour("FoldMarkersBackground"))
        else:
            self.setMarginWidth(self.__foldMargin, 0)
            self.setFolding(QsciScintilla.NoFoldStyle, self.__foldMargin)
    
    def __resizeLinenoMargin(self):
        """
        Private slot to resize the line numbers margin.
        """
        linenoMargin = Preferences.getEditor("LinenoMargin")
        if linenoMargin:
            self.setMarginWidth(
                self.__linenoMargin, '8' * (len(str(self.lines())) + 1))
    
    def __setTabAndIndent(self):
        """
        Private method to set indentation size and style and tab width.
        """
        self.setTabWidth(self.__getEditorConfig("TabWidth"))
        self.setIndentationWidth(self.__getEditorConfig("IndentWidth"))
        if self.lexer_ and self.lexer_.alwaysKeepTabs():
            self.setIndentationsUseTabs(True)
        else:
            self.setIndentationsUseTabs(
                self.__getEditorConfig("TabForIndentation"))
    
    def __setTextDisplay(self):
        """
        Private method to configure the text display.
        """
        self.__setTabAndIndent()
        
        self.setTabIndents(Preferences.getEditor("TabIndents"))
        self.setBackspaceUnindents(Preferences.getEditor("TabIndents"))
        self.setIndentationGuides(Preferences.getEditor("IndentationGuides"))
        self.setIndentationGuidesBackgroundColor(
            Preferences.getEditorColour("IndentationGuidesBackground"))
        self.setIndentationGuidesForegroundColor(
            Preferences.getEditorColour("IndentationGuidesForeground"))
        if Preferences.getEditor("ShowWhitespace"):
            self.setWhitespaceVisibility(QsciScintilla.WsVisible)
            try:
                self.setWhitespaceForegroundColor(
                    Preferences.getEditorColour("WhitespaceForeground"))
                self.setWhitespaceBackgroundColor(
                    Preferences.getEditorColour("WhitespaceBackground"))
                self.setWhitespaceSize(
                    Preferences.getEditor("WhitespaceSize"))
            except AttributeError:
                # QScintilla before 2.5 doesn't support this
                pass
        else:
            self.setWhitespaceVisibility(QsciScintilla.WsInvisible)
        self.setEolVisibility(Preferences.getEditor("ShowEOL"))
        self.setAutoIndent(Preferences.getEditor("AutoIndentation"))
        if Preferences.getEditor("BraceHighlighting"):
            self.setBraceMatching(QsciScintilla.SloppyBraceMatch)
        else:
            self.setBraceMatching(QsciScintilla.NoBraceMatch)
        self.setMatchedBraceForegroundColor(
            Preferences.getEditorColour("MatchingBrace"))
        self.setMatchedBraceBackgroundColor(
            Preferences.getEditorColour("MatchingBraceBack"))
        self.setUnmatchedBraceForegroundColor(
            Preferences.getEditorColour("NonmatchingBrace"))
        self.setUnmatchedBraceBackgroundColor(
            Preferences.getEditorColour("NonmatchingBraceBack"))
        if Preferences.getEditor("CustomSelectionColours"):
            self.setSelectionBackgroundColor(
                Preferences.getEditorColour("SelectionBackground"))
        else:
            self.setSelectionBackgroundColor(
                QApplication.palette().color(QPalette.Highlight))
        if Preferences.getEditor("ColourizeSelText"):
            self.resetSelectionForegroundColor()
        elif Preferences.getEditor("CustomSelectionColours"):
            self.setSelectionForegroundColor(
                Preferences.getEditorColour("SelectionForeground"))
        else:
            self.setSelectionForegroundColor(
                QApplication.palette().color(QPalette.HighlightedText))
        self.setSelectionToEol(Preferences.getEditor("ExtendSelectionToEol"))
        self.setCaretForegroundColor(
            Preferences.getEditorColour("CaretForeground"))
        self.setCaretLineBackgroundColor(
            Preferences.getEditorColour("CaretLineBackground"))
        self.setCaretLineVisible(Preferences.getEditor("CaretLineVisible"))
        self.setCaretLineAlwaysVisible(
            Preferences.getEditor("CaretLineAlwaysVisible"))
        self.caretWidth = Preferences.getEditor("CaretWidth")
        self.setCaretWidth(self.caretWidth)
        self.caretLineFrameWidth = Preferences.getEditor("CaretLineFrameWidth")
        self.setCaretLineFrameWidth(self.caretLineFrameWidth)
        self.useMonospaced = Preferences.getEditor("UseMonospacedFont")
        self.setMonospaced(self.useMonospaced)
        edgeMode = Preferences.getEditor("EdgeMode")
        edge = QsciScintilla.EdgeMode(edgeMode)
        self.setEdgeMode(edge)
        if edgeMode:
            self.setEdgeColumn(Preferences.getEditor("EdgeColumn"))
            self.setEdgeColor(Preferences.getEditorColour("Edge"))
        
        wrapVisualFlag = Preferences.getEditor("WrapVisualFlag")
        self.setWrapMode(Preferences.getEditor("WrapLongLinesMode"))
        self.setWrapVisualFlags(wrapVisualFlag, wrapVisualFlag)
        self.setWrapIndentMode(Preferences.getEditor("WrapIndentMode"))
        self.setWrapStartIndent(Preferences.getEditor("WrapStartIndent"))
        
        self.zoomTo(Preferences.getEditor("ZoomFactor"))
        
        self.searchIndicator = QsciScintilla.INDIC_CONTAINER
        self.indicatorDefine(
            self.searchIndicator, QsciScintilla.INDIC_BOX,
            Preferences.getEditorColour("SearchMarkers"))
        if (
            not Preferences.getEditor("SearchMarkersEnabled") and
            not Preferences.getEditor("QuickSearchMarkersEnabled") and
            not Preferences.getEditor("MarkOccurrencesEnabled")
        ):
            self.clearAllIndicators(self.searchIndicator)
        
        self.spellingIndicator = QsciScintilla.INDIC_CONTAINER + 1
        self.indicatorDefine(
            self.spellingIndicator, QsciScintilla.INDIC_SQUIGGLE,
            Preferences.getEditorColour("SpellingMarkers"))
        self.__setSpelling()
        
        self.highlightIndicator = QsciScintilla.INDIC_CONTAINER + 2
        self.indicatorDefine(
            self.highlightIndicator, QsciScintilla.INDIC_FULLBOX,
            Preferences.getEditorColour("HighlightMarker"))
        
        self.setCursorFlashTime(QApplication.cursorFlashTime())
        
        try:
            if Preferences.getEditor("AnnotationsEnabled"):
                self.setAnnotationDisplay(QsciScintilla.AnnotationBoxed)
            else:
                self.setAnnotationDisplay(QsciScintilla.AnnotationHidden)
        except AttributeError:
            pass
        self.__setAnnotationStyles()
        
        if Preferences.getEditor("OverrideEditAreaColours"):
            self.setColor(Preferences.getEditorColour("EditAreaForeground"))
            self.setPaper(Preferences.getEditorColour("EditAreaBackground"))
        
        self.setVirtualSpaceOptions(
            Preferences.getEditor("VirtualSpaceOptions"))
        
        self.__markerMap.setEnabled(True)
    
    def __setEolMode(self):
        """
        Private method to configure the eol mode of the editor.
        """
        if (
            self.fileName and
            self.project.isOpen() and
            self.project.isProjectFile(self.fileName)
        ):
            eolMode = self.__getEditorConfig("EOLMode", nodefault=True)
            if eolMode is None:
                eolStr = self.project.getEolString()
                self.setEolModeByEolString(eolStr)
            else:
                self.setEolMode(eolMode)
        else:
            eolMode = self.__getEditorConfig("EOLMode")
            eolMode = QsciScintilla.EolMode(eolMode)
            self.setEolMode(eolMode)
        self.__eolChanged()
        
    def __setAutoCompletion(self):
        """
        Private method to configure the autocompletion function.
        """
        if self.lexer_:
            self.setAutoCompletionFillupsEnabled(
                Preferences.getEditor("AutoCompletionFillups"))
        self.setAutoCompletionCaseSensitivity(
            Preferences.getEditor("AutoCompletionCaseSensitivity"))
        self.setAutoCompletionReplaceWord(
            Preferences.getEditor("AutoCompletionReplaceWord"))
        self.setAutoCompletionThreshold(0)
        try:
            self.setAutoCompletionUseSingle(
                Preferences.getEditor("AutoCompletionShowSingle"))
        except AttributeError:
            self.setAutoCompletionShowSingle(
                Preferences.getEditor("AutoCompletionShowSingle"))
        autoCompletionSource = Preferences.getEditor("AutoCompletionSource")
        if autoCompletionSource == QsciScintilla.AcsDocument:
            self.setAutoCompletionSource(QsciScintilla.AcsDocument)
        elif autoCompletionSource == QsciScintilla.AcsAPIs:
            self.setAutoCompletionSource(QsciScintilla.AcsAPIs)
        else:
            self.setAutoCompletionSource(QsciScintilla.AcsAll)
        
        self.maxLines = Preferences.getEditor("AutoCompletionMaxLines")
        self.maxChars = Preferences.getEditor("AutoCompletionMaxChars")
        
    def __setCallTips(self):
        """
        Private method to configure the calltips function.
        """
        self.setCallTipsBackgroundColor(
            Preferences.getEditorColour("CallTipsBackground"))
        self.setCallTipsForegroundColor(
            Preferences.getEditorColour("CallTipsForeground"))
        self.setCallTipsHighlightColor(
            Preferences.getEditorColour("CallTipsHighlight"))
        self.setCallTipsVisible(Preferences.getEditor("CallTipsVisible"))
        calltipsStyle = Preferences.getEditor("CallTipsStyle")
        try:
            self.setCallTipsPosition(
                Preferences.getEditor("CallTipsPosition"))
        except AttributeError:
            pass
        
        if Preferences.getEditor("CallTipsEnabled"):
            if calltipsStyle == QsciScintilla.CallTipsNoContext:
                self.setCallTipsStyle(QsciScintilla.CallTipsNoContext)
            elif (
                calltipsStyle == QsciScintilla.CallTipsNoAutoCompletionContext
            ):
                self.setCallTipsStyle(
                    QsciScintilla.CallTipsNoAutoCompletionContext)
            else:
                self.setCallTipsStyle(QsciScintilla.CallTipsContext)
        else:
            self.setCallTipsStyle(QsciScintilla.CallTipsNone)

    ###########################################################################
    ## Autocompletion handling methods below
    ###########################################################################

    def canAutoCompleteFromAPIs(self):
        """
        Public method to check for API availablity.
        
        @return flag indicating autocompletion from APIs is available (boolean)
        """
        return self.acAPI
        
    def autoCompleteQScintilla(self):
        """
        Public method to perform an autocompletion using QScintilla methods.
        """
        self.__acText = ' '  # Prevent long running ACs to add results
        self.__acWatchdog.stop()
        if self.__acCompletions:
            return
        
        acs = Preferences.getEditor("AutoCompletionSource")
        if acs == QsciScintilla.AcsDocument:
            self.autoCompleteFromDocument()
        elif acs == QsciScintilla.AcsAPIs:
            self.autoCompleteFromAPIs()
        elif acs == QsciScintilla.AcsAll:
            self.autoCompleteFromAll()
        else:
            E5MessageBox.information(
                self,
                self.tr("Autocompletion"),
                self.tr(
                    """Autocompletion is not available because"""
                    """ there is no autocompletion source set."""))
        
    def setAutoCompletionEnabled(self, enable):
        """
        Public method to enable/disable autocompletion.
        
        @param enable flag indicating the desired autocompletion status
            (boolean)
        """
        if enable:
            autoCompletionSource = Preferences.getEditor(
                "AutoCompletionSource")
            if autoCompletionSource == QsciScintilla.AcsDocument:
                self.setAutoCompletionSource(QsciScintilla.AcsDocument)
            elif autoCompletionSource == QsciScintilla.AcsAPIs:
                self.setAutoCompletionSource(QsciScintilla.AcsAPIs)
            else:
                self.setAutoCompletionSource(QsciScintilla.AcsAll)
    
    def __toggleAutoCompletionEnable(self):
        """
        Private slot to handle the Enable Autocompletion context menu entry.
        """
        if self.menuActs["AutoCompletionEnable"].isChecked():
            self.setAutoCompletionEnabled(True)
        else:
            self.setAutoCompletionEnabled(False)
    
    #################################################################
    ## Support for autocompletion hook methods
    #################################################################
    
    def __charAdded(self, charNumber):
        """
        Private slot called to handle the user entering a character.
        
        @param charNumber value of the character entered (integer)
        """
        char = chr(charNumber)
        # update code documentation viewer
        if (
            char == "(" and
            Preferences.getDocuViewer("ShowInfoOnOpenParenthesis")
        ):
            self.vm.showEditorInfo(self)
            
        if self.isListActive():
            if self.__isStartChar(char):
                self.cancelList()
                self.autoComplete(auto=True, context=True)
                return
            elif char == '(':
                self.cancelList()
            else:
                self.__acTimer.stop()
        
        if (
            self.callTipsStyle() != QsciScintilla.CallTipsNone and
            self.lexer_ is not None and chr(charNumber) in '()'
        ):
            self.callTip()
        
        if not self.isCallTipActive():
            char = chr(charNumber)
            if self.__isStartChar(char):
                self.autoComplete(auto=True, context=True)
                return
            
            line, col = self.getCursorPosition()
            txt = self.getWordLeft(line, col)
            if len(txt) >= Preferences.getEditor("AutoCompletionThreshold"):
                self.autoComplete(auto=True, context=False)
                return
    
    def __isStartChar(self, ch):
        """
        Private method to check, if a character is an autocompletion start
        character.
        
        @param ch character to be checked (one character string)
        @return flag indicating the result (boolean)
        """
        if self.lexer_ is None:
            return False
        
        wseps = self.lexer_.autoCompletionWordSeparators()
        for wsep in wseps:
            if wsep.endswith(ch):
                return True
        
        return False
    
    def __autocompletionCancelled(self):
        """
        Private slot to handle the cancellation of an auto-completion list.
        """
        self.__acWatchdog.stop()
        
        self.__acText = ""
    
    #################################################################
    ## auto-completion hook interfaces
    #################################################################
    
    def addCompletionListHook(self, key, func, asynchroneous=False):
        """
        Public method to set an auto-completion list provider.
        
        @param key name of the provider
        @type str
        @param func function providing completion list. func
            should be a function taking a reference to the editor and
            a boolean indicating to complete a context. It should return
            the possible completions as a list of strings.
        @type function(editor, bool) -> list of str in case async is False
            and function(editor, bool, str) returning nothing in case async
            is True
        @param asynchroneous flag indicating an asynchroneous function
        @type bool
        """
        if (
            key in self.__completionListHookFunctions or
            key in self.__completionListAsyncHookFunctions
        ):
            # it was already registered
            E5MessageBox.warning(
                self,
                self.tr("Auto-Completion Provider"),
                self.tr("""The completion list provider '{0}' was already"""
                        """ registered. Ignoring duplicate request.""")
                .format(key))
            return
        
        if asynchroneous:
            self.__completionListAsyncHookFunctions[key] = func
        else:
            self.__completionListHookFunctions[key] = func
    
    def removeCompletionListHook(self, key):
        """
        Public method to remove a previously registered completion list
        provider.
        
        @param key name of the provider
        @type str
        """
        if key in self.__completionListHookFunctions:
            del self.__completionListHookFunctions[key]
        elif key in self.__completionListAsyncHookFunctions:
            del self.__completionListAsyncHookFunctions[key]
    
    def getCompletionListHook(self, key):
        """
        Public method to get the registered completion list provider.
        
        @param key name of the provider
        @type str
        @return function providing completion list
        @rtype function or None
        """
        return (self.__completionListHookFunctions.get(key) or
                self.__completionListAsyncHookFunctions.get(key))
    
    def autoComplete(self, auto=False, context=True):
        """
        Public method to start auto-completion.
        
        @keyparam auto flag indicating a call from the __charAdded method
            (boolean)
        @keyparam context flag indicating to complete a context (boolean)
        """
        if auto and not Preferences.getEditor("AutoCompletionEnabled"):
            # auto-completion is disabled
            return
        
        if self.isListActive():
            self.cancelList()
        
        if (
            self.__completionListHookFunctions or
            self.__completionListAsyncHookFunctions
        ):
            # Avoid delayed auto-completion after cursor repositioning
            self.__acText = self.__getAcText()
            if auto and Preferences.getEditor("AutoCompletionTimeout"):
                self.__acTimer.stop()
                self.__acContext = context
                self.__acTimer.start()
            else:
                self.__autoComplete(auto, context)
        elif not auto:
            self.autoCompleteQScintilla()
        elif self.autoCompletionSource() != QsciScintilla.AcsNone:
            self.autoCompleteQScintilla()
    
    def __getAcText(self):
        """
        Private method to get the text from cursor position for autocompleting.
        
        @return text left of cursor position
        @rtype str
        """
        line, col = self.getCursorPosition()
        text = self.text(line)
        try:
            if self.__isStartChar(text[col - 1]):
                acText = self.getWordLeft(line, col - 1) + text[col - 1]
            else:
                acText = self.getWordLeft(line, col)
        except IndexError:
            acText = ""
        
        return acText
    
    def __autoComplete(self, auto=True, context=None):
        """
        Private method to start auto-completion via plug-ins.
        
        @keyparam auto flag indicating a call from the __charAdded method
            (boolean)
        @keyparam context flag indicating to complete a context
        @type bool or None
        """
        self.__acCompletions.clear()
        self.__acCompletionsFinished = 0
        
        # Suppress empty completions
        if auto and self.__acText == '':
            return
        
        if self.__acCacheEnabled:
            completions = self.__acCache.get(self.__acText)
        else:
            completions = None
        if completions is not None:
            # show list with cached entries
            if self.isListActive():
                self.cancelList()
            
            self.__showCompletionsList(completions)
        else:
            if context is None:
                context = self.__acContext
            
            for key in self.__completionListAsyncHookFunctions:
                self.__completionListAsyncHookFunctions[key](
                    self, context, self.__acText)
            
            for key in self.__completionListHookFunctions:
                completions = self.__completionListHookFunctions[key](
                    self, context)
                self.completionsListReady(completions, self.__acText)
            
            if Preferences.getEditor("AutoCompletionScintillaOnFail"):
                self.__acWatchdog.start()
    
    def completionsListReady(self, completions, acText):
        """
        Public method to show the completions determined by a completions
        provider.
        
        @param completions list of possible completions
        @type list of str or set of str
        @param acText text to be completed
        @type str
        """
        currentWord = self.__getAcText() or ' '
        # process the list only, if not already obsolete ...
        if acText != self.__acText or not self.__acText.endswith(currentWord):
            # Suppress auto-completion done by QScintilla as fallback
            self.__acWatchdog.stop()
            return
        
        self.__acCompletions.update(set(completions))
        
        self.__acCompletionsFinished += 1
        # Got all results from auto completer?
        if self.__acCompletionsFinished >= (
            len(self.__completionListAsyncHookFunctions) +
            len(self.__completionListHookFunctions)
        ):
            self.__acWatchdog.stop()
            
            # Autocomplete with QScintilla if no results present
            if (
                Preferences.getEditor("AutoCompletionScintillaOnFail") and
                not self.__acCompletions
            ):
                self.autoCompleteQScintilla()
                return
        
        # ... or completions are not empty
        if not bool(completions):
            return
        
        if self.isListActive():
            self.cancelList()
        
        if self.__acCompletions:
            if self.__acCacheEnabled:
                self.__acCache.add(acText, set(self.__acCompletions))
            self.__showCompletionsList(self.__acCompletions)
    
    def __showCompletionsList(self, completions):
        """
        Private method to show the completions list.
        
        @param completions completions to be shown
        @type list of str or set of str
        """
        if Preferences.getEditor("AutoCompletionReversedList"):
            acCompletions = sorted(
                list(completions),
                key=self.__replaceLeadingUnderscores)
        else:
            acCompletions = sorted(list(completions))
        self.showUserList(EditorAutoCompletionListID, acCompletions)
    
    def __replaceLeadingUnderscores(self, txt):
        """
        Private method to replace the first two underlines for invers sorting.
        
        @param txt completion text
        @type str
        @return modified completion text
        @rtype str
        """
        if txt.startswith('_'):
            return txt[:2].replace('_', '~') + txt[2:]
        else:
            return txt
    
    def __clearCompletionsCache(self):
        """
        Private method to clear the auto-completions cache.
        """
        self.__acCache.clear()
    
    def __completionListSelected(self, listId, txt):
        """
        Private slot to handle the selection from the completion list.
        
        @param listId the ID of the user list (should be 1 or 2) (integer)
        @param txt the selected text (string)
        """
        if listId == EditorAutoCompletionListID:
            lst = txt.split()
            if len(lst) > 1:
                txt = lst[0]
            
            self.beginUndoAction()
            if Preferences.getEditor("AutoCompletionReplaceWord"):
                self.selectCurrentWord()
                self.removeSelectedText()
                line, col = self.getCursorPosition()
            else:
                line, col = self.getCursorPosition()
                wLeft = self.getWordLeft(line, col)
                if not txt.startswith(wLeft):
                    self.selectCurrentWord()
                    self.removeSelectedText()
                    line, col = self.getCursorPosition()
                elif wLeft:
                    txt = txt[len(wLeft):]
                
                if txt and txt[0] in "'\"":
                    # New in jedi 0.16: AC of dict keys
                    txt = txt[1:]
            self.insert(txt)
            self.endUndoAction()
            self.setCursorPosition(line, col + len(txt))
        elif listId == TemplateCompletionListID:
            self.__applyTemplate(txt, self.getLanguage())
    
    def canProvideDynamicAutoCompletion(self):
        """
        Public method to test the dynamic auto-completion availability.
        
        @return flag indicating the availability of dynamic auto-completion
            (boolean)
        """
        return (self.acAPI or
                bool(self.__completionListHookFunctions) or
                bool(self.__completionListAsyncHookFunctions))
    
    #################################################################
    ## call-tip hook interfaces
    #################################################################
    
    def addCallTipHook(self, key, func):
        """
        Public method to set a calltip provider.
        
        @param key name of the provider
        @type str
        @param func function providing calltips. func
            should be a function taking a reference to the editor,
            a position into the text and the amount of commas to the
            left of the cursor. It should return the possible
            calltips as a list of strings.
        @type function(editor, int, int) -> list of str
        """
        if key in self.__ctHookFunctions:
            # it was already registered
            E5MessageBox.warning(
                self,
                self.tr("Call-Tips Provider"),
                self.tr("""The call-tips provider '{0}' was already"""
                        """ registered. Ignoring duplicate request.""")
                .format(key))
            return
        
        self.__ctHookFunctions[key] = func
    
    def removeCallTipHook(self, key):
        """
        Public method to remove a previously registered calltip provider.
        
        @param key name of the provider
        @type str
        """
        if key in self.__ctHookFunctions:
            del self.__ctHookFunctions[key]
    
    def getCallTipHook(self, key):
        """
        Public method to get the registered calltip provider.
        
        @param key name of the provider
        @type str
        @return function providing calltips
        @rtype function or None
        """
        if key in self.__ctHookFunctions:
            return self.__ctHookFunctions[key]
        else:
            return None
    
    def canProvideCallTipps(self):
        """
        Public method to test the calltips availability.
        
        @return flag indicating the availability of calltips (boolean)
        """
        return (self.acAPI or
                bool(self.__ctHookFunctions))
    
    def callTip(self):
        """
        Public method to show calltips.
        """
        if bool(self.__ctHookFunctions):
            self.__callTip()
        else:
            super(Editor, self).callTip()
    
    def __callTip(self):
        """
        Private method to show call tips provided by a plugin.
        """
        pos = self.currentPosition()
        
        # move backward to the start of the current calltip working out
        # which argument to highlight
        commas = 0
        found = False
        ch, pos = self.__getCharacter(pos)
        while ch:
            if ch == ',':
                commas += 1
            elif ch == ')':
                depth = 1
                
                # ignore everything back to the start of the corresponding
                # parenthesis
                ch, pos = self.__getCharacter(pos)
                while ch:
                    if ch == ')':
                        depth += 1
                    elif ch == '(':
                        depth -= 1
                        if depth == 0:
                            break
                    ch, pos = self.__getCharacter(pos)
            elif ch == '(':
                found = True
                break
            
            ch, pos = self.__getCharacter(pos)
        
        self.SendScintilla(QsciScintilla.SCI_CALLTIPCANCEL)
        
        if not found:
            return
        
        if self.__ctHookFunctions:
            callTips = []
            for key in self.__ctHookFunctions:
                callTips.extend(self.__ctHookFunctions[key](self, pos, commas))
            callTips = list(set(callTips))
            callTips.sort()
        if len(callTips) == 0:
            if Preferences.getEditor("CallTipsScintillaOnFail"):
                # try QScintilla calltips
                super(Editor, self).callTip()
            return
        
        ctshift = 0
        for ct in callTips:
            shift = ct.index("(")
            if ctshift < shift:
                ctshift = shift
        
        cv = self.callTipsVisible()
        if cv > 0:
            # this is just a safe guard
            ct = self._encodeString("\n".join(callTips[:cv]))
        else:
            # until here and unindent below
            ct = self._encodeString("\n".join(callTips))
        
        self.SendScintilla(QsciScintilla.SCI_CALLTIPSHOW,
                           self.__adjustedCallTipPosition(ctshift, pos), ct)
        if b'\n' in ct:
            return
        
        # Highlight the current argument
        if commas == 0:
            astart = ct.find(b'(')
        else:
            astart = ct.find(b',')
            commas -= 1
            while astart != -1 and commas > 0:
                astart = ct.find(b',', astart + 1)
                commas -= 1
        
        if astart == -1:
            return
        
        depth = 0
        for aend in range(astart + 1, len(ct)):
            ch = ct[aend:aend + 1]
            
            if ch == b',' and depth == 0:
                break
            elif ch == b'(':
                depth += 1
            elif ch == b')':
                if depth == 0:
                    break
                
                depth -= 1
        
        if astart != aend:
            self.SendScintilla(QsciScintilla.SCI_CALLTIPSETHLT,
                               astart + 1, aend)
    
    def __adjustedCallTipPosition(self, ctshift, pos):
        """
        Private method to calculate an adjusted position for showing calltips.
        
        @param ctshift amount the calltip shall be shifted (integer)
        @param pos position into the text (integer)
        @return new position for the calltip (integer)
        """
        ct = pos
        if ctshift:
            ctmin = self.SendScintilla(
                QsciScintilla.SCI_POSITIONFROMLINE,
                self.SendScintilla(QsciScintilla.SCI_LINEFROMPOSITION, ct))
            if ct - ctshift < ctmin:
                ct = ctmin
            else:
                ct = ct - ctshift
        return ct
    
    #################################################################
    ## Methods needed by the code documentation viewer
    #################################################################
    
    def __showCodeInfo(self):
        """
        Private slot to handle the context menu action to show code info.
        """
        self.vm.showEditorInfo(self)
    
    #################################################################
    ## Methods needed by the context menu
    #################################################################
    
    def __marginNumber(self, xPos):
        """
        Private method to calculate the margin number based on a x position.
        
        @param xPos x position (integer)
        @return margin number (integer, -1 for no margin)
        """
        width = 0
        for margin in range(5):
            width += self.marginWidth(margin)
            if xPos <= width:
                return margin
        return -1
        
    def contextMenuEvent(self, evt):
        """
        Protected method implementing the context menu event.
        
        @param evt the context menu event (QContextMenuEvent)
        """
        evt.accept()
        if self.__marginNumber(evt.x()) == -1:
            self.spellingMenuPos = self.positionFromPoint(evt.pos())
            if (
                self.spellingMenuPos >= 0 and
                self.spell is not None and
                self.hasIndicator(self.spellingIndicator,
                                  self.spellingMenuPos)
            ):
                self.spellingMenu.popup(evt.globalPos())
            else:
                self.menu.popup(evt.globalPos())
        else:
            self.line = self.lineAt(evt.pos())
            if self.__marginNumber(evt.x()) in [self.__bmMargin,
                                                self.__linenoMargin]:
                self.bmMarginMenu.popup(evt.globalPos())
            elif self.__marginNumber(evt.x()) == self.__bpMargin:
                self.bpMarginMenu.popup(evt.globalPos())
            elif self.__marginNumber(evt.x()) == self.__indicMargin:
                self.indicMarginMenu.popup(evt.globalPos())
            elif self.__marginNumber(evt.x()) == self.__foldMargin:
                self.foldMarginMenu.popup(evt.globalPos())
        
    def __showContextMenu(self):
        """
        Private slot handling the aboutToShow signal of the context menu.
        """
        self.menuActs["Reopen"].setEnabled(
            not self.isModified() and bool(self.fileName))
        self.menuActs["Save"].setEnabled(self.isModified())
        self.menuActs["Undo"].setEnabled(self.isUndoAvailable())
        self.menuActs["Redo"].setEnabled(self.isRedoAvailable())
        self.menuActs["Revert"].setEnabled(self.isModified())
        self.menuActs["Cut"].setEnabled(self.hasSelectedText())
        self.menuActs["Copy"].setEnabled(self.hasSelectedText())
        if self.menuActs["ExecuteSelection"] is not None:
            self.menuActs["ExecuteSelection"].setEnabled(
                self.hasSelectedText())
        self.menuActs["Paste"].setEnabled(self.canPaste())
        if not self.isResourcesFile:
            if self.fileName and self.isPyFile():
                self.menuActs["Show"].setEnabled(True)
            else:
                self.menuActs["Show"].setEnabled(False)
            if (
                self.fileName and
                (self.isPyFile() or self.isRubyFile())
            ):
                self.menuActs["Diagrams"].setEnabled(True)
            else:
                self.menuActs["Diagrams"].setEnabled(False)
        if not self.miniMenu:
            if self.lexer_ is not None:
                self.menuActs["Comment"].setEnabled(
                    self.lexer_.canBlockComment())
                self.menuActs["Uncomment"].setEnabled(
                    self.lexer_.canBlockComment())
                self.menuActs["StreamComment"].setEnabled(
                    self.lexer_.canStreamComment())
                self.menuActs["BoxComment"].setEnabled(
                    self.lexer_.canBoxComment())
            else:
                self.menuActs["Comment"].setEnabled(False)
                self.menuActs["Uncomment"].setEnabled(False)
                self.menuActs["StreamComment"].setEnabled(False)
                self.menuActs["BoxComment"].setEnabled(False)
        
        self.menuActs["TypingAidsEnabled"].setEnabled(
            self.completer is not None)
        self.menuActs["TypingAidsEnabled"].setChecked(
            self.completer is not None and self.completer.isEnabled())
        
        if not self.isResourcesFile:
            self.menuActs["calltip"].setEnabled(self.canProvideCallTipps())
            self.menuActs["codeInfo"].setEnabled(
                self.vm.isEditorInfoSupported(self.getLanguage()))
        
        spellingAvailable = SpellChecker.isAvailable()
        self.menuActs["SpellCheck"].setEnabled(spellingAvailable)
        self.menuActs["SpellCheckSelection"].setEnabled(
            spellingAvailable and self.hasSelectedText())
        self.menuActs["SpellCheckRemove"].setEnabled(
            spellingAvailable and self.spellingMenuPos >= 0)
        self.menuActs["SpellCheckLanguages"].setEnabled(spellingAvailable)
        
        if self.menuActs["OpenRejections"]:
            if self.fileName:
                rej = "{0}.rej".format(self.fileName)
                self.menuActs["OpenRejections"].setEnabled(os.path.exists(rej))
            else:
                self.menuActs["OpenRejections"].setEnabled(False)
        
        self.menuActs["MonospacedFont"].setEnabled(self.lexer_ is None)
        
        splitOrientation = self.vm.getSplitOrientation()
        if splitOrientation == Qt.Horizontal:
            self.menuActs["NewSplit"].setIcon(
                UI.PixmapCache.getIcon("splitHorizontal"))
        else:
            self.menuActs["NewSplit"].setIcon(
                UI.PixmapCache.getIcon("splitVertical"))
        
        self.menuActs["Tools"].setEnabled(not self.toolsMenu.isEmpty())
        
        self.showMenu.emit("Main", self.menu, self)
        
    def __showContextMenuAutocompletion(self):
        """
        Private slot called before the autocompletion menu is shown.
        """
        self.menuActs["acDynamic"].setEnabled(
            self.canProvideDynamicAutoCompletion())
        self.menuActs["acClearCache"].setEnabled(
            self.canProvideDynamicAutoCompletion())
        self.menuActs["acAPI"].setEnabled(self.acAPI)
        self.menuActs["acAPIDocument"].setEnabled(self.acAPI)
        
        self.showMenu.emit("Autocompletion", self.autocompletionMenu, self)
        
    def __showContextMenuShow(self):
        """
        Private slot called before the show menu is shown.
        """
        prEnable = False
        coEnable = False
        
        # first check if the file belongs to a project
        if (
            self.project.isOpen() and
            self.project.isProjectSource(self.fileName)
        ):
            fn = self.project.getMainScript(True)
            if fn is not None:
                tfn = Utilities.getTestFileName(fn)
                basename = os.path.splitext(fn)[0]
                tbasename = os.path.splitext(tfn)[0]
                prEnable = (
                    prEnable or
                    os.path.isfile("{0}.profile".format(basename)) or
                    os.path.isfile("{0}.profile".format(tbasename))
                )
                coEnable = (
                    (coEnable or
                     os.path.isfile("{0}.coverage".format(basename)) or
                     os.path.isfile("{0}.coverage".format(tbasename))) and
                    self.project.isPy3Project()
                )
        
        # now check ourselves
        fn = self.getFileName()
        if fn is not None:
            tfn = Utilities.getTestFileName(fn)
            basename = os.path.splitext(fn)[0]
            tbasename = os.path.splitext(tfn)[0]
            prEnable = (
                prEnable or
                os.path.isfile("{0}.profile".format(basename)) or
                os.path.isfile("{0}.profile".format(tbasename))
            )
            coEnable = (
                (coEnable or
                 os.path.isfile("{0}.coverage".format(basename)) or
                 os.path.isfile("{0}.coverage".format(tbasename))) and
                self.isPyFile()
            )
        
        # now check for syntax errors
        if self.hasSyntaxErrors():
            coEnable = False
        
        self.profileMenuAct.setEnabled(prEnable)
        self.coverageMenuAct.setEnabled(coEnable)
        self.coverageShowAnnotationMenuAct.setEnabled(
            coEnable and len(self.notcoveredMarkers) == 0)
        self.coverageHideAnnotationMenuAct.setEnabled(
            len(self.notcoveredMarkers) > 0)
        
        self.showMenu.emit("Show", self.menuShow, self)
        
    def __showContextMenuGraphics(self):
        """
        Private slot handling the aboutToShow signal of the diagrams context
        menu.
        """
        if (
            self.project.isOpen() and
            self.project.isProjectSource(self.fileName)
        ):
            self.applicationDiagramMenuAct.setEnabled(True)
        else:
            self.applicationDiagramMenuAct.setEnabled(False)
        
        self.showMenu.emit("Graphics", self.graphicsMenu, self)
        
    def __showContextMenuMargin(self, menu):
        """
        Private slot handling the aboutToShow signal of the margins context
        menu.
        
        @param menu reference to the menu to be shown
        @type QMenu
        """
        if menu is self.bpMarginMenu:
            supportsDebugger = bool(self.fileName and self.isPyFile())
            hasBreakpoints = bool(self.breaks)
            hasBreakpoint = bool(
                self.markersAtLine(self.line) & self.breakpointMask)
            
            self.marginMenuActs["Breakpoint"].setEnabled(supportsDebugger)
            self.marginMenuActs["TempBreakpoint"].setEnabled(supportsDebugger)
            self.marginMenuActs["NextBreakpoint"].setEnabled(
                supportsDebugger and hasBreakpoints)
            self.marginMenuActs["PreviousBreakpoint"].setEnabled(
                supportsDebugger and hasBreakpoints)
            self.marginMenuActs["ClearBreakpoint"].setEnabled(
                supportsDebugger and hasBreakpoints)