Example #1
0
    def activate(self, ideSettings, ideGlobalData):
        """Activates the plugin.

        The plugin may override the method to do specific
        plugin activation handling.

        ideSettings - reference to the IDE Settings singleton
                      see codimension/src/utils/settings.py
        ideGlobalData - reference to the IDE global settings
                        see codimension/src/utils/globals.py

        Note: if overriden do not forget to call the
              base class activate()
        """
        WizardInterface.activate(self, ideSettings, ideGlobalData)

        self.__resultViewer = PylintResultViewer(self.ide, PLUGIN_HOME_DIR)
        self.ide.sideBars['bottom'].addTab(
            self.__resultViewer, QIcon(PLUGIN_HOME_DIR + 'pylint.png'),
            'Pylint', 'pylint', 2)
        self.ide.sideBars['bottom'].tabButton('pylint',
                                              QTabBar.RightSide).resize(0, 0)

        # The clear call must be here, not in the results viewer __init__()
        # This is because the viewer has not been inserted into the side bar at
        # the time of __init__() so the tooltip setting does not work
        self.__resultViewer.clear()

        self.__pylintDriver = PylintDriver(self.ide)
        self.__pylintDriver.sigFinished.connect(self.__pylintFinished)

        if self.__globalShortcut is None:
            self.__globalShortcut = QShortcut(QKeySequence('Ctrl+L'),
                                              self.ide.mainWindow, self.__run)
        else:
            self.__globalShortcut.setKey('Ctrl+L')

        # Add buttons
        for _, _, tabWidget in self.ide.editorsManager.getTextEditors():
            self.__addButton(tabWidget)

        # File type changed & new tab
        self.ide.editorsManager.sigTextEditorTabAdded.connect(
            self.__textEditorTabAdded)
        self.ide.editorsManager.sigFileTypeChanged.connect(
            self.__fileTypeChanged)

        # Add main menu
        self.__mainMenu = QMenu('Pylint', self.ide.mainWindow)
        self.__mainMenu.setIcon(QIcon(PLUGIN_HOME_DIR + 'pylint.png'))
        self.__mainRunAction = self.__mainMenu.addAction(
            QIcon(PLUGIN_HOME_DIR + 'pylint.png'), 'Run pylint\t(Ctrl+L)',
            self.__run)
        self.__mainGenerateAction = self.__mainMenu.addAction(
            QIcon(PLUGIN_HOME_DIR + 'generate.png'),
            'Generate/open pylintrc file', self.__generate)
        toolsMenu = self.ide.mainWindow.menuBar().findChild(QMenu, 'tools')
        self.__mainMenuSeparator = toolsMenu.addSeparator()
        toolsMenu.addMenu(self.__mainMenu)
        self.__mainMenu.aboutToShow.connect(self.__mainMenuAboutToShow)
Example #2
0
 def __createPopupMenus(self):
     """Generate the popup menu"""
     self.menu = QMenu()
     self.__editAct = self.menu.addAction(getIcon('bpprops.png'), "Edit...",
                                          self.__editBreak)
     self.__jumpToCodeAct = self.menu.addAction(getIcon('gotoline.png'),
                                                "Jump to code",
                                                self.__showSource)
     self.menu.addSeparator()
     self.__enableAct = self.menu.addAction(getIcon('bpenable.png'),
                                            "Enable", self.enableBreak)
     self.__enableAllAct = self.menu.addAction(getIcon('bpenableall.png'),
                                               "Enable all",
                                               self.enableAllBreaks)
     self.menu.addSeparator()
     self.__disableAct = self.menu.addAction(getIcon('bpdisable.png'),
                                             "Disable", self.disableBreak)
     self.__disableAllAct = self.menu.addAction(getIcon('bpdisableall.png'),
                                                "Disable all",
                                                self.disableAllBreaks)
     self.menu.addSeparator()
     self.__delAct = self.menu.addAction(getIcon('bpdel.png'), "Delete",
                                         self.deleteBreak)
     self.__delAllAct = self.menu.addAction(getIcon('bpdelall.png'),
                                            "Delete all",
                                            self.deleteAllBreaks)
 def __createPopupMenu(self):
     """Creates the popup menu"""
     self.__excptMenu = QMenu()
     self.__addToIgnoreMenuItem = self.__excptMenu.addAction(
         "Add to ignore list", self.__onAddToIgnore)
     self.__jumpToCodeMenuItem = self.__excptMenu.addAction(
         "Jump to code", self.__onJumpToCode)
Example #4
0
 def __initToolsMenu(self):
     """Creates the tools menu"""
     self.toolsMenu = QMenu('Python too&ls')
     self.runAct = self.toolsMenu.addAction(getIcon('run.png'),
                                            'Run script',
                                            self._parent.onRunScript)
     self.runParamAct = self.toolsMenu.addAction(
         getIcon('paramsmenu.png'), 'Set parameters and run',
         self._parent.onRunScriptDlg)
     self.toolsMenu.addSeparator()
     self.profileAct = self.toolsMenu.addAction(
         getIcon('profile.png'), 'Profile script',
         self._parent.onProfileScript)
     self.profileParamAct = self.toolsMenu.addAction(
         getIcon('paramsmenu.png'), 'Set parameters and profile',
         self._parent.onProfileScriptDlg)
     self.toolsMenu.addSeparator()
     self.disasmMenu = QMenu('Disassembly')
     self.disasmMenu.setIcon(getIcon('disassembly.png'))
     self.disasmAct0 = self.disasmMenu.addAction(
         getIcon(''), 'Disassembly (no optimization)', self._onDisasm0)
     self.disasmAct1 = self.disasmMenu.addAction(
         getIcon(''), 'Disassembly (optimization level 1)', self._onDisasm1)
     self.disasmAct2 = self.disasmMenu.addAction(
         getIcon(''), 'Disassembly (optimization level 2)', self._onDisasm2)
     self.toolsMenu.addMenu(self.disasmMenu)
     return self.toolsMenu
    def _initContextMenu(self):
        """Called to initialize a context menu"""
        self._menu = QMenu(self)
        self.__menuUndo = self._menu.addAction(getIcon('undo.png'), '&Undo',
                                               self.onUndo, "Ctrl+Z")
        self.__menuRedo = self._menu.addAction(getIcon('redo.png'), '&Redo',
                                               self.onRedo, "Ctrl+Y")
        self._menu.addSeparator()
        self.__menuCut = self._menu.addAction(getIcon('cutmenu.png'), 'Cu&t',
                                              self.onShiftDel, "Ctrl+X")
        self.__menuCopy = self._menu.addAction(getIcon('copymenu.png'),
                                               '&Copy', self.onCtrlC, "Ctrl+C")
        self.__menucopyTimestamp = self._menu.addAction(
            getIcon('copymenu.png'), '&Copy all with timestamps',
            self.onCtrlShiftC, "Ctrl+Shift+C")
        self.__menuPaste = self._menu.addAction(getIcon('pastemenu.png'),
                                                '&Paste', self.onPasteText,
                                                "Ctrl+V")
        self.__menuSelectAll = self._menu.addAction(
            getIcon('selectallmenu.png'), 'Select &all', self.selectAll,
            "Ctrl+A")
        self._menu.addSeparator()
        self.__menuOpenAsFile = self._menu.addAction(getIcon('filemenu.png'),
                                                     'O&pen as file',
                                                     self.openAsFile)
        self.__menuDownloadAndShow = self._menu.addAction(
            getIcon('filemenu.png'), 'Do&wnload and show',
            self.downloadAndShow)
        self.__menuOpenInBrowser = self._menu.addAction(
            getIcon('homepagemenu.png'), 'Open in browser', self.openInBrowser)
        self._menu.addSeparator()

        self._menu.aboutToShow.connect(self._contextMenuAboutToShow)
        self._menu.aboutToHide.connect(self._contextMenuAboutToHide)
Example #6
0
 def __createPopupMenu(self):
     """Creates the popup menu"""
     self.__framesMenu = QMenu()
     self.__setCurrentMenuItem = self.__framesMenu.addAction(
         "Set current (single click)", self.__onSetCurrent)
     self.__jumpMenuItem = self.__framesMenu.addAction(
         "Set current and jump to the source (double click)",
         self.__onSetCurrentAndJump)
Example #7
0
 def __initDiagramsMenu(self):
     """Creates the diagrams menu"""
     self.diagramsMenu = QMenu("&Diagrams")
     self.importsDgmAct = self.diagramsMenu.addAction(
         getIcon('importsdiagram.png'), 'Imports diagram',
         self._parent.onImportDgm)
     self.importsDgmParamAct = self.diagramsMenu.addAction(
         getIcon('paramsmenu.png'), 'Fine tuned imports diagram',
         self._parent.onImportDgmTuned)
     return self.diagramsMenu
Example #8
0
    def __init__(self):
        self.menu = None
        self.individualMenus = {}

        # Scene menu preparation
        self.sceneMenu = QMenu()
        self.sceneMenu.addAction(getIcon('filesvg.png'), 'Save as SVG...',
                                 self.parent().onSaveAsSVG)
        self.sceneMenu.addAction(getIcon('filepdf.png'), 'Save as PDF...',
                                 self.parent().onSaveAsPDF)
        self.sceneMenu.addAction(getIcon('filepixmap.png'), 'Save as PNG...',
                                 self.parent().onSaveAsPNG)
        self.sceneMenu.addSeparator()
        self.sceneMenu.addAction(getIcon('copymenu.png'),
                                 'Copy image to clipboard',
                                 self.parent().copyToClipboard)

        # Common menu for all the individually selected items
        self.commonMenu = QMenu()
        self.__ccSubmenuAction = self.commonMenu.addMenu(
            self.__initCustomColorsContextMenu())
        self.__rtSubmenuAction = self.commonMenu.addMenu(
            self.__initReplaceTextContextMenu())
        self.__docSubmenuAction = self.commonMenu.addMenu(
            self.__initDocContextMenu())
        self.__groupAction = self.commonMenu.addAction(
            getIcon("cfgroup.png"), "Group...", self.onGroup)

        #self.commonMenu.addSeparator()
        #self.__cutAction = self.commonMenu.addAction(
        #    getIcon("cutmenu.png"), "Cut (specific for graphics pane)",
        #    self.onCut)
        #self.__copyAction = self.commonMenu.addAction(
        #    getIcon("copymenu.png"), "Copy (specific for graphics pane)",
        #    self.onCopy)
        #self.commonMenu.addSeparator()
        #self.commonMenu.addAction(
        #    getIcon("trash.png"), "Delete", self.onDelete)

        # Individual items specific menu: begin
        ifContextMenu = QMenu()
        ifContextMenu.addAction(
            getIcon("switchbranches.png"), "Switch branch layout",
            self.onSwitchIfBranch)

        self.individualMenus[IfCell] = ifContextMenu
        self.individualMenus[OpenedGroupBegin] = self.__initOpenGroupContextMenu()
        self.individualMenus[CollapsedGroup] = self.__initCloseGroupContextMenu()
        self.individualMenus[EmptyGroup] = self.__initEmptyGroupContextMenu()

        # Individual items specific menu: end

        # Menu for a group of selected items
        self.groupMenu = QMenu()
Example #9
0
    def __buildIndividualMenu(self, item):
        """Builds a context menu for the given item"""
        self.menu = QMenu()
        if type(item) in self.individualMenus:
            individualPart = self.individualMenus[type(item)]
            self.menu.addActions(individualPart.actions())
            self.menu.addSeparator()
        self.menu.addActions(self.commonMenu.actions())

        # Note: if certain items need to be disabled then it should be done
        #       here
        self.__disableMenuItems()
Example #10
0
 def __initReplaceTextContextMenu(self):
     """Create the Replace text submenu"""
     self.__replaceTextSubmenu = QMenu('Replace text')
     self.__replaceTextSubmenu.setIcon(getIcon('replacetextmenu.png'))
     self.__rtAction = self.__replaceTextSubmenu.addAction(
         getIcon("replacetitle.png"), "Replace text...",
         self.onReplaceText)
     self.__replaceTextSubmenu.addSeparator()
     self.__removeRTAction = self.__replaceTextSubmenu.addAction(
         getIcon('trash.png'), 'Remove replacement text',
         self.onRemoveReplacementText)
     return self.__replaceTextSubmenu
Example #11
0
 def __initCustomColorsContextMenu(self):
     """Create the custom colors submenu"""
     self.__customColorsSubmenu = QMenu('Custom colors')
     self.__customColorsSubmenu.setIcon(getIcon('customcolorsmenu.png'))
     self.__ccAction = self.__customColorsSubmenu.addAction(
         getIcon("customcolors.png"), "Custom colors...",
         self.onCustomColors)
     self.__customColorsSubmenu.addSeparator()
     self.__removeCCAction = self.__customColorsSubmenu.addAction(
         getIcon('trash.png'), 'Remove custom colors',
         self.onRemoveCustomColors)
     return self.__customColorsSubmenu
Example #12
0
    def __init__(self):
        self.menu = None
        self.individualMenus = {}

        # Scene menu preparation
        self.sceneMenu = QMenu()
        self.sceneMenu.addAction(getIcon('filesvg.png'), 'Save as SVG...',
                                 self.parent().onSaveAsSVG)
        self.sceneMenu.addAction(getIcon('filepdf.png'), 'Save as PDF...',
                                 self.parent().onSaveAsPDF)
        self.sceneMenu.addAction(getIcon('filepixmap.png'), 'Save as PNG...',
                                 self.parent().onSaveAsPNG)
        self.sceneMenu.addSeparator()
        self.sceneMenu.addAction(getIcon('copymenu.png'), 'Copy to clipboard',
                                 self.parent().copyToClipboard)

        # Common menu for all the individually selected items
        self.commonMenu = QMenu()
        self.__ccAction = self.commonMenu.addAction(
            getIcon("customcolors.png"), "Custom colors...",
            self.onCustomColors)
        self.__rtAction = self.commonMenu.addAction(
            getIcon("replacetitle.png"), "Replace text...", self.onReplaceText)
        self.commonMenu.addSeparator()
        self.__removeCCAction = self.commonMenu.addAction(
            getIcon('trash.png'), 'Remove custom colors',
            self.onRemoveCustomColors)
        self.__removeRTAction = self.commonMenu.addAction(
            getIcon('trash.png'), 'Remove replacement text',
            self.onRemoveReplacementText)
        self.commonMenu.addSeparator()
        self.__cutAction = self.commonMenu.addAction(getIcon("cutmenu.png"),
                                                     "Cut", self.onCut)
        self.__copyAction = self.commonMenu.addAction(getIcon("copymenu.png"),
                                                      "Copy", self.onCopy)
        self.commonMenu.addSeparator()
        self.commonMenu.addAction(getIcon("trash.png"), "Delete",
                                  self.onDelete)

        # Individual items specific menu: begin
        ifContextMenu = QMenu()
        ifContextMenu.addAction(getIcon("switchbranches.png"),
                                "Switch branch layout", self.onSwitchIfBranch)

        self.individualMenus[IfCell] = ifContextMenu
        # Individual items specific menu: end

        # Menu for a group of selected items
        self.groupMenu = QMenu()
        self.__groupAction = self.groupMenu.addAction(getIcon("cfgroup.png"),
                                                      "Group...", self.onGroup)
Example #13
0
 def __initOpenGroupContextMenu(self):
     """Creates the open group context menu"""
     ogMenu = QMenu()
     ogMenu.addAction(getIcon("collapse.png"), "Collapse", self.onGroupCollapse)
     ogMenu.addAction(getIcon("replacetitle.png"), "Edit title...", self.onGroupEditTitle)
     ogMenu.addAction(getIcon("ungroup.png"), "Ungroup", self.onGroupUngroup)
     return ogMenu
Example #14
0
 def __initCloseGroupContextMenu(self):
     """Creates the closed group context menu"""
     cgMenu = QMenu()
     cgMenu.addAction(getIcon("expand.png"), "Expand", self.onGroupExpand)
     cgMenu.addAction(getIcon("replacetitle.png"), "Edit title...", self.onGroupEditTitle)
     cgMenu.addAction(getIcon("ungroup.png"), "Ungroup", self.onGroupUngroup)
     return cgMenu
Example #15
0
 def __initDocContextMenu(self):
     """Create the Documentation submenu"""
     self.__docSubmenu = QMenu('Documentation')
     self.__docSubmenu.setIcon(getIcon('markdown.png'))
     self.__editDocAction = self.__docSubmenu.addAction(
         getIcon('replacetitle.png'), 'Add/edit doc link/anchor...',
         self.onEditDoc)
     self.__autoDocActon = self.__docSubmenu.addAction(
         getIcon('createdoc.png'),
         'Create doc file, add link and open for editing',
         self.onAutoAddDoc)
     self.__docSubmenu.addSeparator()
     self.__removeDocAction = self.__docSubmenu.addAction(
         getIcon('trash.png'), 'Remove doc link/anchor',
         self.onRemoveDoc)
     return self.__docSubmenu
Example #16
0
    def __buildGroupMenu(self, items):
        """Builds a context menu for the group of items"""
        self.menu = QMenu()
        if type(items[0]) in self.individualMenus:
            if self.areSelectedOfTypes([[items[0].kind, items[0].subKind]]):
                individualPart = self.individualMenus[type(items[0])]
                self.menu.addActions(individualPart.actions())
                self.menu.addSeparator()
        self.menu.addActions(self.commonMenu.actions())
        if not self.groupMenu.isEmpty():
            self.menu.addSeparator()
            self.menu.addActions(self.groupMenu.actions())

        # Note: if certain items need to be disabled then it should be done
        #       here
        self.__disableMenuItems()
Example #17
0
    def __createContextMenu(self):
        """Creates context menu for the table raws"""
        self.__contextMenu = QMenu(self)
        self.__callersMenu = QMenu("Callers", self)
        self.__outsideCallersMenu = QMenu("Outside callers", self)
        self.__calleesMenu = QMenu("Callees", self)
        self.__outsideCalleesMenu = QMenu("Outside callees", self)
        self.__contextMenu.addMenu(self.__callersMenu)
        self.__contextMenu.addMenu(self.__outsideCallersMenu)
        self.__contextMenu.addSeparator()
        self.__contextMenu.addMenu(self.__calleesMenu)
        self.__contextMenu.addMenu(self.__outsideCalleesMenu)

        self.__callersMenu.triggered.connect(self.__onCallContextMenu)
        self.__outsideCallersMenu.triggered.connect(self.__onCallContextMenu)
        self.__calleesMenu.triggered.connect(self.__onCallContextMenu)
        self.__outsideCalleesMenu.triggered.connect(self.__onCallContextMenu)

        self.__table.setContextMenuPolicy(Qt.CustomContextMenu)
        self.__table.customContextMenuRequested.connect(self.__showContextMenu)
Example #18
0
    def __createPopupMenus(self):
        """Generates the popup menus"""
        self.menu = QMenu()
        self.menu.addAction("Add", self.__addWatchPoint)
        self.menu.addAction("Edit...", self.__editWatchPoint)
        self.menu.addSeparator()
        self.menu.addAction("Enable", self.__enableWatchPoint)
        self.menu.addAction("Enable all", self.__enableAllWatchPoints)
        self.menu.addSeparator()
        self.menu.addAction("Disable", self.__disableWatchPoint)
        self.menu.addAction("Disable all", self.__disableAllWatchPoints)
        self.menu.addSeparator()
        self.menu.addAction("Delete", self.__deleteWatchPoint)
        self.menu.addAction("Delete all", self.__deleteAllWatchPoints)

        self.backMenuActions = {}
        self.backMenu = QMenu()
        self.backMenu.addAction("Add", self.__addWatchPoint)
        self.backMenuActions["EnableAll"] = \
            self.backMenu.addAction("Enable all", self.__enableAllWatchPoints)
        self.backMenuActions["DisableAll"] = \
            self.backMenu.addAction("Disable all",
                                    self.__disableAllWatchPoints)
        self.backMenuActions["DeleteAll"] = \
            self.backMenu.addAction("Delete all",
                                    self.__deleteAllWatchPoints)
        self.backMenu.aboutToShow.connect(self.__showBackMenu)

        self.multiMenu = QMenu()
        self.multiMenu.addAction("Add", self.__addWatchPoint)
        self.multiMenu.addSeparator()
        self.multiMenu.addAction("Enable selected",
                                 self.__enableSelectedWatchPoints)
        self.multiMenu.addAction("Enable all", self.__enableAllWatchPoints)
        self.multiMenu.addSeparator()
        self.multiMenu.addAction("Disable selected",
                                 self.__disableSelectedWatchPoints)
        self.multiMenu.addAction("Disable all", self.__disableAllWatchPoints)
        self.multiMenu.addSeparator()
        self.multiMenu.addAction("Delete selected",
                                 self.__deleteSelectedWatchPoints)
        self.multiMenu.addAction("Delete all", self.__deleteAllWatchPoints)
    def __createLayout(self):
        """Creates the widget layout"""
        verticalLayout = QVBoxLayout(self)
        verticalLayout.setContentsMargins(0, 0, 0, 0)
        verticalLayout.setSpacing(0)

        self.__headerLabel = HeaderFitLabel(self)
        self.__headerLabel.setText('Variables')
        self.__headerLabel.setSizePolicy(QSizePolicy.Expanding,
                                         QSizePolicy.Fixed)
        self.__headerLabel.setMinimumWidth(10)

        self.__filterMenu = QMenu(self)
        self.__showAllAct = self.__filterMenu.addAction('Show all variables')
        self.__showAllAct.setData('showall')
        self.__filterMenu.addSeparator()
        self.__filters = []
        for title, settingName, _ in VARIABLE_FILTERS:
            action = self.__filterMenu.addAction(title)
            action.setCheckable(True)
            action.setData(settingName)
            self.__filters.append(action)
        self.__filterMenu.aboutToShow.connect(self.__filterMenuAboutToShow)
        self.__filterMenu.triggered.connect(self.__filterMenuTriggered)

        self.__filterButton = QToolButton(self)
        self.__filterButton.setIcon(getIcon('dbgvarflt.png'))
        self.__filterButton.setToolTip('Variable filter')
        self.__filterButton.setPopupMode(QToolButton.InstantPopup)
        self.__filterButton.setMenu(self.__filterMenu)
        self.__filterButton.setFocusPolicy(Qt.NoFocus)
        self.__filterButton.setFixedSize(self.__headerLabel.height(),
                                         self.__headerLabel.height())

        self.__execStatement = CDMComboBox(True)
        self.__execStatement.setSizePolicy(QSizePolicy.Expanding,
                                           QSizePolicy.Expanding)
        self.__execStatement.lineEdit().setToolTip("Execute statement")
        self.__execStatement.setFixedHeight(26)
        self.__execStatement.editTextChanged.connect(
            self.__execStatementChanged)
        self.__execStatement.enterClicked.connect(self.__onEnterInExec)
        self.__execButton = QPushButton("Exec")
        self.__execButton.setEnabled(False)
        self.__execButton.setFixedHeight(26)
        self.__execButton.clicked.connect(self.__onExec)

        self.headerToolbar = QToolBar(self)
        self.headerToolbar.setIconSize(QSize(18, 18))
        self.headerToolbar.setContentsMargins(1, 1, 1, 1)
        self.headerToolbar.addWidget(self.__headerLabel)
        self.headerToolbar.addWidget(self.__filterButton)

        execLayout = QGridLayout()
        execLayout.setContentsMargins(1, 1, 1, 1)
        execLayout.setSpacing(1)
        execLayout.addWidget(self.__execStatement, 0, 0)
        execLayout.addWidget(self.__execButton, 0, 1)

        verticalLayout.addWidget(self.headerToolbar)
        verticalLayout.addWidget(self.__browser)
        verticalLayout.addLayout(execLayout)
class VariablesViewer(QWidget):
    """Implements the variables viewer for a debugger"""

    # First group of filters
    FilterGlobalAndLocal = 0
    FilterGlobalOnly = 1
    FilterLocalOnly = 2

    def __init__(self, debugger, parent=None):
        QWidget.__init__(self, parent)

        self.__debugger = debugger
        self.__browser = VariablesBrowser(debugger, self)
        self.__createLayout()

        self.setTabOrder(self.__browser, self.__execStatement)
        self.setTabOrder(self.__execStatement, self.__execButton)

        self.__updateFilter()

    def __createLayout(self):
        """Creates the widget layout"""
        verticalLayout = QVBoxLayout(self)
        verticalLayout.setContentsMargins(0, 0, 0, 0)
        verticalLayout.setSpacing(0)

        self.__headerLabel = HeaderFitLabel(self)
        self.__headerLabel.setText('Variables')
        self.__headerLabel.setSizePolicy(QSizePolicy.Expanding,
                                         QSizePolicy.Fixed)
        self.__headerLabel.setMinimumWidth(10)

        self.__filterMenu = QMenu(self)
        self.__showAllAct = self.__filterMenu.addAction('Show all variables')
        self.__showAllAct.setData('showall')
        self.__filterMenu.addSeparator()
        self.__filters = []
        for title, settingName, _ in VARIABLE_FILTERS:
            action = self.__filterMenu.addAction(title)
            action.setCheckable(True)
            action.setData(settingName)
            self.__filters.append(action)
        self.__filterMenu.aboutToShow.connect(self.__filterMenuAboutToShow)
        self.__filterMenu.triggered.connect(self.__filterMenuTriggered)

        self.__filterButton = QToolButton(self)
        self.__filterButton.setIcon(getIcon('dbgvarflt.png'))
        self.__filterButton.setToolTip('Variable filter')
        self.__filterButton.setPopupMode(QToolButton.InstantPopup)
        self.__filterButton.setMenu(self.__filterMenu)
        self.__filterButton.setFocusPolicy(Qt.NoFocus)
        self.__filterButton.setFixedSize(self.__headerLabel.height(),
                                         self.__headerLabel.height())

        self.__execStatement = CDMComboBox(True)
        self.__execStatement.setSizePolicy(QSizePolicy.Expanding,
                                           QSizePolicy.Expanding)
        self.__execStatement.lineEdit().setToolTip("Execute statement")
        self.__execStatement.setFixedHeight(26)
        self.__execStatement.editTextChanged.connect(
            self.__execStatementChanged)
        self.__execStatement.enterClicked.connect(self.__onEnterInExec)
        self.__execButton = QPushButton("Exec")
        self.__execButton.setEnabled(False)
        self.__execButton.setFixedHeight(26)
        self.__execButton.clicked.connect(self.__onExec)

        self.headerToolbar = QToolBar(self)
        self.headerToolbar.setIconSize(QSize(18, 18))
        self.headerToolbar.setContentsMargins(1, 1, 1, 1)
        self.headerToolbar.addWidget(self.__headerLabel)
        self.headerToolbar.addWidget(self.__filterButton)

        execLayout = QGridLayout()
        execLayout.setContentsMargins(1, 1, 1, 1)
        execLayout.setSpacing(1)
        execLayout.addWidget(self.__execStatement, 0, 0)
        execLayout.addWidget(self.__execButton, 0, 1)

        verticalLayout.addWidget(self.headerToolbar)
        verticalLayout.addWidget(self.__browser)
        verticalLayout.addLayout(execLayout)

    def __filterMenuAboutToShow(self):
        """Debug variable filter menu is about to show"""
        for flt in self.__filters:
            flt.setChecked(Settings()[flt.data()])

    def __filterMenuTriggered(self, act):
        """A filter has been changed"""
        name = act.data()
        if name == 'showall':
            for _, settingName, _ in VARIABLE_FILTERS:
                Settings()[settingName] = True
        else:
            Settings()[name] = not Settings()[name]
        self.__updateFilter()

    def updateVariables(self, areGlobals, frameNumber, variables):
        """Triggered when a new set of variables is received"""
        self.__browser.showVariables(areGlobals, variables, frameNumber)
        self.__updateHeaderLabel()

    def updateVariable(self, areGlobals, variables):
        """Triggered when a new variable has been received"""
        self.__browser.showVariable(areGlobals, variables)
        self.__updateHeaderLabel()

    def __updateHeaderLabel(self):
        """Updates the header text"""
        shown, total = self.__browser.getShownAndTotalCounts()
        if shown == 0 and total == 0:
            self.__headerLabel.setText("Variables")
        else:
            self.__headerLabel.setText("Variables (" + str(shown) + " of " +
                                       str(total) + ")")

    def __updateFilter(self):
        """Updates the current filter"""
        self.__browser.filterChanged()
        self.__updateHeaderLabel()

    def clear(self):
        """Clears the content"""
        self.__browser.clear()
        self.__updateHeaderLabel()

    def clearAll(self):
        """Clears everything including the history"""
        self.clear()
        self.__execStatement.lineEdit().setText("")
        self.__execStatement.clear()

    def __execStatementChanged(self, text):
        """Triggered when a exec statement is changed"""
        text = str(text).strip()
        self.__execButton.setEnabled(text != "")

    def __onEnterInExec(self):
        """Enter/return clicked in exec"""
        self.__onExec()

    def __onExec(self):
        """Triggered when the Exec button is clicked"""
        text = self.__execStatement.currentText().strip()
        if text != "":
            currentFrame = GlobalData().mainWindow.getCurrentFrameNumber()
            self.__debugger.remoteExecuteStatement(text, currentFrame)
            self.__debugger.remoteClientVariables(1, currentFrame)  # globals
            self.__debugger.remoteClientVariables(0, currentFrame)  # locals

    def switchControl(self, isInIDE):
        """Switches the UI depending where the control flow is"""
        self.__browser.setEnabled(isInIDE)
        self.__filterButton.setEnabled(isInIDE)

        self.__execStatement.setEnabled(isInIDE)
        if isInIDE:
            text = self.__execStatement.currentText().strip()
            self.__execButton.setEnabled(text != "")
        else:
            self.__execButton.setEnabled(False)
Example #21
0
    def __createToolbar(self):
        """Creates the toolbar"""
        self.__toolbar = QToolBar(self)
        self.__toolbar.setOrientation(Qt.Vertical)
        self.__toolbar.setMovable(False)
        self.__toolbar.setAllowedAreas(Qt.RightToolBarArea)
        self.__toolbar.setIconSize(QSize(16, 16))
        self.__toolbar.setFixedWidth(30)
        self.__toolbar.setContentsMargins(0, 0, 0, 0)

        # Buttons
        saveAsMenu = QMenu(self)
        saveAsSVGAct = saveAsMenu.addAction(getIcon('filesvg.png'),
                                            'Save as SVG...')
        saveAsSVGAct.triggered.connect(self.onSaveAsSVG)

        saveAsPDFAct = saveAsMenu.addAction(getIcon('filepdf.png'),
                                            'Save as PDF...')
        saveAsPDFAct.triggered.connect(self.onSaveAsPDF)
        saveAsPNGAct = saveAsMenu.addAction(getIcon('filepixmap.png'),
                                            'Save as PNG...')
        saveAsPNGAct.triggered.connect(self.onSaveAsPNG)
        saveAsMenu.addSeparator()
        saveAsCopyToClipboardAct = saveAsMenu.addAction(
            getIcon('copymenu.png'), 'Copy to clipboard')
        saveAsCopyToClipboardAct.triggered.connect(self.copyToClipboard)

        self.__saveAsButton = QToolButton(self)
        self.__saveAsButton.setIcon(getIcon('saveasmenu.png'))
        self.__saveAsButton.setToolTip('Save as')
        self.__saveAsButton.setPopupMode(QToolButton.InstantPopup)
        self.__saveAsButton.setMenu(saveAsMenu)
        self.__saveAsButton.setFocusPolicy(Qt.NoFocus)

        self.__levelUpButton = QToolButton(self)
        self.__levelUpButton.setFocusPolicy(Qt.NoFocus)
        self.__levelUpButton.setIcon(getIcon('levelup.png'))
        self.__levelUpButton.setToolTip('Smart zoom level up (Shift+wheel)')
        self.__levelUpButton.clicked.connect(self.onSmartZoomLevelUp)
        self.__levelIndicator = QLabel('<b>0</b>', self)
        self.__levelIndicator.setAlignment(Qt.AlignCenter)
        self.__levelDownButton = QToolButton(self)
        self.__levelDownButton.setFocusPolicy(Qt.NoFocus)
        self.__levelDownButton.setIcon(getIcon('leveldown.png'))
        self.__levelDownButton.setToolTip('Smart zoom level down (Shift+wheel)')
        self.__levelDownButton.clicked.connect(self.onSmartZoomLevelDown)

        fixedSpacer = QWidget()
        fixedSpacer.setFixedHeight(10)

        self.__hideDocstrings = QToolButton(self)
        self.__hideDocstrings.setCheckable(True)
        self.__hideDocstrings.setIcon(getIcon('hidedocstrings.png'))
        self.__hideDocstrings.setToolTip('Show/hide docstrings')
        self.__hideDocstrings.setFocusPolicy(Qt.NoFocus)
        self.__hideDocstrings.setChecked(Settings()['hidedocstrings'])
        self.__hideDocstrings.clicked.connect(self.__onHideDocstrings)
        self.__hideComments = QToolButton(self)
        self.__hideComments.setCheckable(True)
        self.__hideComments.setIcon(getIcon('hidecomments.png'))
        self.__hideComments.setToolTip('Show/hide comments')
        self.__hideComments.setFocusPolicy(Qt.NoFocus)
        self.__hideComments.setChecked(Settings()['hidecomments'])
        self.__hideComments.clicked.connect(self.__onHideComments)
        self.__hideExcepts = QToolButton(self)
        self.__hideExcepts.setCheckable(True)
        self.__hideExcepts.setIcon(getIcon('hideexcepts.png'))
        self.__hideExcepts.setToolTip('Show/hide except blocks')
        self.__hideExcepts.setFocusPolicy(Qt.NoFocus)
        self.__hideExcepts.setChecked(Settings()['hideexcepts'])
        self.__hideExcepts.clicked.connect(self.__onHideExcepts)

        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        self.__toolbar.addWidget(self.__saveAsButton)
        self.__toolbar.addWidget(spacer)
        self.__toolbar.addWidget(self.__levelUpButton)
        self.__toolbar.addWidget(self.__levelIndicator)
        self.__toolbar.addWidget(self.__levelDownButton)
        self.__toolbar.addWidget(fixedSpacer)
        self.__toolbar.addWidget(self.__hideDocstrings)
        self.__toolbar.addWidget(self.__hideComments)
        self.__toolbar.addWidget(self.__hideExcepts)
        return self.__toolbar
Example #22
0
    def __createLayout(self):
        """Creates the toolbar and layout"""
        # Buttons
        printButton = QAction(getIcon('printer.png'), 'Print (Ctrl+P)', self)
        printButton.triggered.connect(self.__onPrint)

        printPreviewButton = QAction(getIcon('printpreview.png'),
                                     'Print preview', self)
        printPreviewButton.triggered.connect(self.__onPrintPreview)
        printPreviewButton.setEnabled(False)
        printPreviewButton.setVisible(False)

        printSpacer = QWidget()
        printSpacer.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        printSpacer.setFixedHeight(8)

        # Imports diagram and its menu
        importsMenu = QMenu(self)
        importsDlgAct = importsMenu.addAction(getIcon('detailsdlg.png'),
                                              'Fine tuned imports diagram')
        importsDlgAct.triggered.connect(self.onImportDgmTuned)
        self.importsDiagramButton = QToolButton(self)
        self.importsDiagramButton.setIcon(getIcon('importsdiagram.png'))
        self.importsDiagramButton.setToolTip('Generate imports diagram')
        self.importsDiagramButton.setPopupMode(QToolButton.DelayedPopup)
        self.importsDiagramButton.setMenu(importsMenu)
        self.importsDiagramButton.setFocusPolicy(Qt.NoFocus)
        self.importsDiagramButton.clicked.connect(self.onImportDgm)
        self.importsDiagramButton.setEnabled(False)

        # Run script and its menu
        runScriptMenu = QMenu(self)
        runScriptDlgAct = runScriptMenu.addAction(getIcon('detailsdlg.png'),
                                                  'Set run/debug parameters')
        runScriptDlgAct.triggered.connect(self.onRunScriptDlg)
        self.runScriptButton = QToolButton(self)
        self.runScriptButton.setIcon(getIcon('run.png'))
        self.runScriptButton.setToolTip('Run script')
        self.runScriptButton.setPopupMode(QToolButton.DelayedPopup)
        self.runScriptButton.setMenu(runScriptMenu)
        self.runScriptButton.setFocusPolicy(Qt.NoFocus)
        self.runScriptButton.clicked.connect(self.onRunScript)
        self.runScriptButton.setEnabled(False)

        # Profile script and its menu
        profileScriptMenu = QMenu(self)
        profileScriptDlgAct = profileScriptMenu.addAction(
            getIcon('detailsdlg.png'), 'Set profile parameters')
        profileScriptDlgAct.triggered.connect(self.onProfileScriptDlg)
        self.profileScriptButton = QToolButton(self)
        self.profileScriptButton.setIcon(getIcon('profile.png'))
        self.profileScriptButton.setToolTip('Profile script')
        self.profileScriptButton.setPopupMode(QToolButton.DelayedPopup)
        self.profileScriptButton.setMenu(profileScriptMenu)
        self.profileScriptButton.setFocusPolicy(Qt.NoFocus)
        self.profileScriptButton.clicked.connect(self.onProfileScript)
        self.profileScriptButton.setEnabled(False)

        # Debug script and its menu
        debugScriptMenu = QMenu(self)
        debugScriptDlgAct = debugScriptMenu.addAction(
            getIcon('detailsdlg.png'), 'Set run/debug parameters')
        debugScriptDlgAct.triggered.connect(self.onDebugScriptDlg)
        self.debugScriptButton = QToolButton(self)
        self.debugScriptButton.setIcon(getIcon('debugger.png'))
        self.debugScriptButton.setToolTip('Debug script')
        self.debugScriptButton.setPopupMode(QToolButton.DelayedPopup)
        self.debugScriptButton.setMenu(debugScriptMenu)
        self.debugScriptButton.setFocusPolicy(Qt.NoFocus)
        self.debugScriptButton.clicked.connect(self.onDebugScript)
        self.debugScriptButton.setEnabled(False)

        # Disassembling
        disasmScriptMenu = QMenu(self)
        disasmScriptMenu.addAction(getIcon(''),
                                   'Disassembly (no optimization)',
                                   self.__editor._onDisasm0)
        disasmScriptMenu.addAction(getIcon(''),
                                   'Disassembly (optimization level 1)',
                                   self.__editor._onDisasm1)
        disasmScriptMenu.addAction(getIcon(''),
                                   'Disassembly (optimization level 2)',
                                   self.__editor._onDisasm2)
        self.disasmScriptButton = QToolButton(self)
        self.disasmScriptButton.setIcon(getIcon('disassembly.png'))
        self.disasmScriptButton.setToolTip('Disassembly script')
        self.disasmScriptButton.setPopupMode(QToolButton.DelayedPopup)
        self.disasmScriptButton.setMenu(disasmScriptMenu)
        self.disasmScriptButton.setFocusPolicy(Qt.NoFocus)
        self.disasmScriptButton.clicked.connect(self.__editor._onDisasm0)
        self.disasmScriptButton.setEnabled(False)

        # Dead code
        self.deadCodeScriptButton = QAction(getIcon('deadcode.png'),
                                            'Find dead code', self)
        self.deadCodeScriptButton.triggered.connect(self.__onDeadCode)
        self.deadCodeScriptButton.setEnabled(False)

        undoSpacer = QWidget()
        undoSpacer.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        undoSpacer.setFixedHeight(8)

        self.__undoButton = QAction(getIcon('undo.png'), 'Undo (Ctrl+Z)', self)
        self.__undoButton.setShortcut('Ctrl+Z')
        self.__undoButton.triggered.connect(self.__editor.onUndo)
        self.__undoButton.setEnabled(False)

        self.__redoButton = QAction(getIcon('redo.png'), 'Redo (Ctrl+Y)', self)
        self.__redoButton.setShortcut('Ctrl+Y')
        self.__redoButton.triggered.connect(self.__editor.onRedo)
        self.__redoButton.setEnabled(False)

        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        self.removeTrailingSpacesButton = QAction(getIcon('trailingws.png'),
                                                  'Remove trailing spaces',
                                                  self)
        self.removeTrailingSpacesButton.triggered.connect(
            self.onRemoveTrailingWS)
        self.expandTabsButton = QAction(getIcon('expandtabs.png'),
                                        'Expand tabs (4 spaces)', self)
        self.expandTabsButton.triggered.connect(self.onExpandTabs)

        # The toolbar
        toolbar = QToolBar(self)
        toolbar.setOrientation(Qt.Vertical)
        toolbar.setMovable(False)
        toolbar.setAllowedAreas(Qt.RightToolBarArea)
        toolbar.setIconSize(QSize(16, 16))
        toolbar.setFixedWidth(30)
        toolbar.setContentsMargins(0, 0, 0, 0)

        toolbar.addAction(printPreviewButton)
        toolbar.addAction(printButton)
        toolbar.addWidget(printSpacer)
        toolbar.addWidget(self.importsDiagramButton)
        toolbar.addWidget(self.runScriptButton)
        toolbar.addWidget(self.profileScriptButton)
        toolbar.addWidget(self.debugScriptButton)
        toolbar.addWidget(self.disasmScriptButton)
        toolbar.addAction(self.deadCodeScriptButton)
        toolbar.addWidget(undoSpacer)
        toolbar.addAction(self.__undoButton)
        toolbar.addAction(self.__redoButton)
        toolbar.addWidget(spacer)
        toolbar.addAction(self.removeTrailingSpacesButton)
        toolbar.addAction(self.expandTabsButton)

        self.importsBar = ImportListWidget(self.__editor)
        self.importsBar.hide()

        self.__outsideChangesBar = OutsideChangeWidget(self.__editor)
        self.__outsideChangesBar.sigReloadRequest.connect(self.__onReload)
        self.__outsideChangesBar.reloadAllNonModifiedRequest.connect(
            self.reloadAllNonModified)
        self.__outsideChangesBar.hide()

        hLayout = QHBoxLayout()
        hLayout.setContentsMargins(0, 0, 0, 0)
        hLayout.setSpacing(0)

        vLayout = QVBoxLayout()
        vLayout.setContentsMargins(0, 0, 0, 0)
        vLayout.setSpacing(0)

        self.__navigationBar = NavigationBar(self.__editor, self)
        vLayout.addWidget(self.__navigationBar)
        vLayout.addWidget(self.__editor)

        hLayout.addLayout(vLayout)
        hLayout.addWidget(toolbar)
        widget = QWidget()
        widget.setLayout(hLayout)

        self.__splitter = QSplitter(Qt.Horizontal, self)

        self.__flowUI = FlowUIWidget(self.__editor, self)
        self.__mdView = MDWidget(self.__editor, self)

        self.__renderLayout = QVBoxLayout()
        self.__renderLayout.setContentsMargins(0, 0, 0, 0)
        self.__renderLayout.setSpacing(0)
        self.__renderLayout.addWidget(self.__flowUI)
        self.__renderLayout.addWidget(self.__mdView)
        self.__renderWidget = QWidget()
        self.__renderWidget.setLayout(self.__renderLayout)

        self.__splitter.addWidget(widget)
        self.__splitter.addWidget(self.__renderWidget)

        containerLayout = QHBoxLayout()
        containerLayout.setContentsMargins(0, 0, 0, 0)
        containerLayout.setSpacing(0)
        containerLayout.addWidget(self.__splitter)
        self.setLayout(containerLayout)

        self.__renderWidget.setVisible(False)

        self.__splitter.setSizes(Settings()['flowSplitterSizes'])
        self.__splitter.splitterMoved.connect(self.flowSplitterMoved)
        Settings().sigFlowSplitterChanged.connect(self.otherFlowSplitterMoved)
Example #23
0
class ClientExceptionsViewer(QWidget):

    """Implements the client exceptions viewer for a debugger"""

    sigClientExceptionsCleared = pyqtSignal()

    def __init__(self, parent, ignoredExceptionsViewer):
        QWidget.__init__(self, parent)

        self.__ignoredExceptionsViewer = ignoredExceptionsViewer
        self.__currentItem = None

        self.__createPopupMenu()
        self.__createLayout()

        GlobalData().project.sigProjectChanged.connect(self.__onProjectChanged)

    def setFocus(self):
        """Sets the widget focus"""
        self.exceptionsList.setFocus()

    def __createPopupMenu(self):
        """Creates the popup menu"""
        self.__excptMenu = QMenu()
        self.__addToIgnoreMenuItem = self.__excptMenu.addAction(
            "Add to ignore list", self.__onAddToIgnore)
        self.__jumpToCodeMenuItem = self.__excptMenu.addAction(
            "Jump to code", self.__onJumpToCode)

    def __createLayout(self):
        """Creates the widget layout"""
        verticalLayout = QVBoxLayout(self)
        verticalLayout.setContentsMargins(0, 0, 0, 0)
        verticalLayout.setSpacing(0)

        self.__excptLabel = QLabel("Exceptions", self)

        self.headerFrame = QFrame()
        self.headerFrame.setObjectName('excpt')
        self.headerFrame.setStyleSheet('QFrame#excpt {' +
                                       getLabelStyle(self.__excptLabel) + '}')
        self.headerFrame.setFixedHeight(HEADER_HEIGHT)

        headerLayout = QHBoxLayout()
        headerLayout.setContentsMargins(0, 0, 0, 0)
        headerLayout.addSpacing(3)
        headerLayout.addWidget(self.__excptLabel)
        self.headerFrame.setLayout(headerLayout)

        self.exceptionsList = QTreeWidget(self)
        self.exceptionsList.setSortingEnabled(False)
        self.exceptionsList.setAlternatingRowColors(True)
        self.exceptionsList.setRootIsDecorated(True)
        self.exceptionsList.setItemsExpandable(True)
        self.exceptionsList.setUniformRowHeights(True)
        self.exceptionsList.setSelectionMode(QAbstractItemView.SingleSelection)
        self.exceptionsList.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.exceptionsList.setItemDelegate(NoOutlineHeightDelegate(4))
        self.exceptionsList.setContextMenuPolicy(Qt.CustomContextMenu)

        self.__addToIgnoreButton = QAction(
            getIcon('add.png'), "Add exception to the list of ignored", self)
        self.__addToIgnoreButton.triggered.connect(self.__onAddToIgnore)
        self.__addToIgnoreButton.setEnabled(False)

        expandingSpacer = QWidget()
        expandingSpacer.setSizePolicy(QSizePolicy.Expanding,
                                      QSizePolicy.Expanding)

        self.__jumpToCodeButton = QAction(
            getIcon('gotoline.png'), "Jump to the code", self)
        self.__jumpToCodeButton.triggered.connect(self.__onJumpToCode)
        self.__jumpToCodeButton.setEnabled(False)

        self.__delAllButton = QAction(
            getIcon('trash.png'), "Delete all the client exceptions", self)
        self.__delAllButton.triggered.connect(self.__onDelAll)
        self.__delAllButton.setEnabled(False)

        self.toolbar = QToolBar()
        self.toolbar.setOrientation(Qt.Horizontal)
        self.toolbar.setMovable(False)
        self.toolbar.setAllowedAreas(Qt.TopToolBarArea)
        self.toolbar.setIconSize(QSize(16, 16))
        self.toolbar.setFixedHeight(28)
        self.toolbar.setContentsMargins(0, 0, 0, 0)
        self.toolbar.addAction(self.__addToIgnoreButton)
        self.toolbar.addAction(self.__jumpToCodeButton)
        self.toolbar.addWidget(expandingSpacer)
        self.toolbar.addAction(self.__delAllButton)

        self.exceptionsList.itemDoubleClicked.connect(
            self.__onExceptionDoubleClicked)
        self.exceptionsList.customContextMenuRequested.connect(
            self.__showContextMenu)
        self.exceptionsList.itemSelectionChanged.connect(
            self.__onSelectionChanged)

        self.exceptionsList.setHeaderLabels(["Exception",
                                             "Function", "Arguments"])

        verticalLayout.addWidget(self.headerFrame)
        verticalLayout.addWidget(self.toolbar)
        verticalLayout.addWidget(self.exceptionsList)

    def clear(self):
        """Clears the content"""
        self.exceptionsList.clear()
        self.__updateExceptionsLabel()
        self.__addToIgnoreButton.setEnabled(False)
        self.__jumpToCodeButton.setEnabled(False)
        self.__delAllButton.setEnabled(False)
        self.__currentItem = None
        self.sigClientExceptionsCleared.emit()

    def __onExceptionDoubleClicked(self, item, column):
        """Triggered when an exception is double clicked"""
        del item    # unused argument
        del column  # unused argument
        if self.__currentItem is not None:
            if self.__currentItem.getType() == STACK_FRAME_ITEM:
                self.__onJumpToCode()
                return

            # This is an exception item itself.
            # Open a separate dialog window with th detailed info.

    def __showContextMenu(self, coord):
        """Shows the frames list context menu"""
        self.__currentItem = self.exceptionsList.itemAt(coord)

        self.__addToIgnoreMenuItem.setEnabled(
            self.__addToIgnoreButton.isEnabled())
        self.__jumpToCodeMenuItem.setEnabled(
            self.__jumpToCodeButton.isEnabled())

        if self.__currentItem is not None:
            self.__excptMenu.popup(QCursor.pos())

    def __onAddToIgnore(self):
        """Adds an exception into the ignore list"""
        if self.__currentItem is not None:
            self.__ignoredExceptionsViewer.addExceptionFilter(
                str(self.__currentItem.getExceptionType()))
            self.__addToIgnoreButton.setEnabled(False)

    def __onJumpToCode(self):
        """Jumps to the corresponding source code line"""
        if self.__currentItem is not None:
            if self.__currentItem.getType() == STACK_FRAME_ITEM:
                fileName = self.__currentItem.getFileName()
                if '<' not in fileName and '>' not in fileName:
                    lineNumber = self.__currentItem.getLineNumber()

                    editorsManager = GlobalData().mainWindow.editorsManager()
                    editorsManager.openFile(fileName, lineNumber)
                    editor = editorsManager.currentWidget().getEditor()
                    editor.gotoLine(lineNumber)
                    editorsManager.currentWidget().setFocus()

    def __onDelAll(self):
        """Triggered when all the exceptions should be deleted"""
        self.clear()

    def addException(self, exceptionType, exceptionMessage, stackTrace):
        """Adds the exception to the view"""
        for index in range(self.exceptionsList.topLevelItemCount()):
            item = self.exceptionsList.topLevelItem(index)
            if item.equal(exceptionType, exceptionMessage, stackTrace):
                item.incrementCounter()
                self.exceptionsList.clearSelection()
                self.exceptionsList.setCurrentItem(item)
                self.__updateExceptionsLabel()
                return

        item = ExceptionItem(self.exceptionsList, exceptionType,
                             exceptionMessage, stackTrace)
        self.exceptionsList.clearSelection()
        self.exceptionsList.setCurrentItem(item)
        self.__updateExceptionsLabel()
        self.__delAllButton.setEnabled(True)

    def __updateExceptionsLabel(self):
        """Updates the exceptions header label"""
        total = self.getTotalCount()
        if total > 0:
            self.__excptLabel.setText("Exceptions (total: " + str(total) + ")")
        else:
            self.__excptLabel.setText("Exceptions")

    def getTotalCount(self):
        """Provides the total number of exceptions"""
        count = 0
        for index in range(self.exceptionsList.topLevelItemCount()):
            count += self.exceptionsList.topLevelItem(index).getCount()
        return count

    def __onProjectChanged(self, what):
        """Triggered when a project is changed"""
        if what == CodimensionProject.CompleteProject:
            self.clear()

    def __onSelectionChanged(self):
        """Triggered when the current item is changed"""
        selected = list(self.exceptionsList.selectedItems())
        if selected:
            self.__currentItem = selected[0]
            if self.__currentItem.getType() == STACK_FRAME_ITEM:
                fileName = self.__currentItem.getFileName()
                if '<' in fileName or '>' in fileName:
                    self.__jumpToCodeButton.setEnabled(False)
                else:
                    self.__jumpToCodeButton.setEnabled(True)
                self.__addToIgnoreButton.setEnabled(False)
            else:
                self.__jumpToCodeButton.setEnabled(False)
                excType = str(self.__currentItem.getExceptionType())
                if self.__ignoredExceptionsViewer.isIgnored(excType) or \
                   " " in excType or excType.startswith("unhandled"):
                    self.__addToIgnoreButton.setEnabled(False)
                else:
                    self.__addToIgnoreButton.setEnabled(True)
        else:
            self.__currentItem = None
            self.__addToIgnoreButton.setEnabled(False)
            self.__jumpToCodeButton.setEnabled(False)
Example #24
0
    def __init__(self):
        self.encodingReloadMenu = QMenu("Set &encoding and reload")
        self.encodingReloadActGrp = QActionGroup(self)
        self.encodingWriteMenu = QMenu("Set encodin&g")
        self.encodingWriteActGrp = QActionGroup(self)

        mainWindow = GlobalData().mainWindow
        editorsManager = mainWindow.editorsManagerWidget.editorsManager

        self._menu = QMenu(self)
        self.__menuUndo = self._menu.addAction(getIcon('undo.png'), '&Undo',
                                               self.onUndo, "Ctrl+Z")
        self.__menuRedo = self._menu.addAction(getIcon('redo.png'), '&Redo',
                                               self.onRedo, "Ctrl+Y")
        self._menu.addSeparator()

        self.__menuCut = self._menu.addAction(getIcon('cutmenu.png'), 'Cu&t',
                                              self.onShiftDel, "Ctrl+X")
        self.__menuCopy = self._menu.addAction(getIcon('copymenu.png'),
                                               '&Copy', self.onCtrlC, "Ctrl+C")
        self.__menuPaste = self._menu.addAction(getIcon('pastemenu.png'),
                                                '&Paste', self.paste, "Ctrl+V")
        self.__menuSelectAll = self._menu.addAction(
            getIcon('selectallmenu.png'), 'Select &all', self.selectAll,
            "Ctrl+A")
        self._menu.addSeparator()

        self.__initReloadEncodingMenu()
        self.encodingReloadMenu.setIcon(getIcon('textencoding.png'))
        self._menu.addMenu(self.encodingReloadMenu)
        self.__initWriteEncodingMenu()
        self.encodingWriteMenu.setIcon(getIcon('textencoding.png'))
        menu = self._menu.addMenu(self.encodingWriteMenu)
        self.__menuClearEncoding = self._menu.addAction(
            getIcon('clearmenu.png'), 'Clear explicit encoding',
            self.__onClearEncoding)
        self._menu.addSeparator()

        menu = self._menu.addMenu(self.__initToolsMenu())
        menu.setIcon(getIcon('toolsmenu.png'))
        self._menu.addSeparator()

        menu = self._menu.addMenu(self.__initDiagramsMenu())
        menu.setIcon(getIcon('diagramsmenu.png'))
        self._menu.addSeparator()

        self.__menuOpenAsFile = self._menu.addAction(getIcon('filemenu.png'),
                                                     'O&pen as file',
                                                     self.openAsFile)
        self.__menuDownloadAndShow = self._menu.addAction(
            getIcon('filemenu.png'), 'Do&wnload and show',
            self.downloadAndShow)
        self.__menuOpenInBrowser = self._menu.addAction(
            getIcon('homepagemenu.png'), 'Open in browser', self.openInBrowser)
        self._menu.addSeparator()

        self.__menuHighlightInPrj = self._menu.addAction(
            getIcon("highlightmenu.png"), "&Highlight in project browser",
            editorsManager.onHighlightInPrj)
        self.__menuHighlightInFS = self._menu.addAction(
            getIcon("highlightmenu.png"), "H&ighlight in file system browser",
            editorsManager.onHighlightInFS)
        self._menuHighlightInOutline = self._menu.addAction(
            getIcon("highlightmenu.png"), "Highlight in &outline browser",
            self.highlightInOutline, 'Ctrl+B')

        # Plugins support
        self.__pluginMenuSeparator = self._menu.addSeparator()
        mainWindow = GlobalData().mainWindow
        editorsManager = mainWindow.editorsManagerWidget.editorsManager
        registeredMenus = editorsManager.getPluginMenus()
        if registeredMenus:
            for path in registeredMenus:
                self._menu.addMenu(registeredMenus[path])
        else:
            self.__pluginMenuSeparator.setVisible(False)

        editorsManager.sigPluginContextMenuAdded.connect(
            self.__onPluginMenuAdded)
        editorsManager.sigPluginContextMenuRemoved.connect(
            self.__onPluginMenuRemoved)
Example #25
0
class EditorContextMenuMixin:
    """Encapsulates the context menu handling"""
    def __init__(self):
        self.encodingReloadMenu = QMenu("Set &encoding and reload")
        self.encodingReloadActGrp = QActionGroup(self)
        self.encodingWriteMenu = QMenu("Set encodin&g")
        self.encodingWriteActGrp = QActionGroup(self)

        mainWindow = GlobalData().mainWindow
        editorsManager = mainWindow.editorsManagerWidget.editorsManager

        self._menu = QMenu(self)
        self.__menuUndo = self._menu.addAction(getIcon('undo.png'), '&Undo',
                                               self.onUndo, "Ctrl+Z")
        self.__menuRedo = self._menu.addAction(getIcon('redo.png'), '&Redo',
                                               self.onRedo, "Ctrl+Y")
        self._menu.addSeparator()

        self.__menuCut = self._menu.addAction(getIcon('cutmenu.png'), 'Cu&t',
                                              self.onShiftDel, "Ctrl+X")
        self.__menuCopy = self._menu.addAction(getIcon('copymenu.png'),
                                               '&Copy', self.onCtrlC, "Ctrl+C")
        self.__menuPaste = self._menu.addAction(getIcon('pastemenu.png'),
                                                '&Paste', self.paste, "Ctrl+V")
        self.__menuSelectAll = self._menu.addAction(
            getIcon('selectallmenu.png'), 'Select &all', self.selectAll,
            "Ctrl+A")
        self._menu.addSeparator()

        self.__initReloadEncodingMenu()
        self.encodingReloadMenu.setIcon(getIcon('textencoding.png'))
        self._menu.addMenu(self.encodingReloadMenu)
        self.__initWriteEncodingMenu()
        self.encodingWriteMenu.setIcon(getIcon('textencoding.png'))
        menu = self._menu.addMenu(self.encodingWriteMenu)
        self.__menuClearEncoding = self._menu.addAction(
            getIcon('clearmenu.png'), 'Clear explicit encoding',
            self.__onClearEncoding)
        self._menu.addSeparator()

        menu = self._menu.addMenu(self.__initToolsMenu())
        menu.setIcon(getIcon('toolsmenu.png'))
        self._menu.addSeparator()

        menu = self._menu.addMenu(self.__initDiagramsMenu())
        menu.setIcon(getIcon('diagramsmenu.png'))
        self._menu.addSeparator()

        self.__menuOpenAsFile = self._menu.addAction(getIcon('filemenu.png'),
                                                     'O&pen as file',
                                                     self.openAsFile)
        self.__menuDownloadAndShow = self._menu.addAction(
            getIcon('filemenu.png'), 'Do&wnload and show',
            self.downloadAndShow)
        self.__menuOpenInBrowser = self._menu.addAction(
            getIcon('homepagemenu.png'), 'Open in browser', self.openInBrowser)
        self._menu.addSeparator()

        self.__menuHighlightInPrj = self._menu.addAction(
            getIcon("highlightmenu.png"), "&Highlight in project browser",
            editorsManager.onHighlightInPrj)
        self.__menuHighlightInFS = self._menu.addAction(
            getIcon("highlightmenu.png"), "H&ighlight in file system browser",
            editorsManager.onHighlightInFS)
        self._menuHighlightInOutline = self._menu.addAction(
            getIcon("highlightmenu.png"), "Highlight in &outline browser",
            self.highlightInOutline, 'Ctrl+B')

        # Plugins support
        self.__pluginMenuSeparator = self._menu.addSeparator()
        mainWindow = GlobalData().mainWindow
        editorsManager = mainWindow.editorsManagerWidget.editorsManager
        registeredMenus = editorsManager.getPluginMenus()
        if registeredMenus:
            for path in registeredMenus:
                self._menu.addMenu(registeredMenus[path])
        else:
            self.__pluginMenuSeparator.setVisible(False)

        editorsManager.sigPluginContextMenuAdded.connect(
            self.__onPluginMenuAdded)
        editorsManager.sigPluginContextMenuRemoved.connect(
            self.__onPluginMenuRemoved)

    def __initReloadEncodingMenu(self):
        """Creates the encoding menu for reloading the existing file"""
        for encoding in sorted(SUPPORTED_CODECS):
            act = self.encodingReloadMenu.addAction(encoding)
            act.setCheckable(True)
            act.setData(encoding)
            self.encodingReloadActGrp.addAction(act)
        self.encodingReloadMenu.triggered.connect(self.__onReloadWithEncoding)

    def __initWriteEncodingMenu(self):
        """Creates the encoding menu for further read/write operations"""
        for encoding in sorted(SUPPORTED_CODECS):
            act = self.encodingWriteMenu.addAction(encoding)
            act.setCheckable(True)
            act.setData(encoding)
            self.encodingWriteActGrp.addAction(act)
        self.encodingWriteMenu.triggered.connect(self.__onReadWriteEncoding)

    def __initToolsMenu(self):
        """Creates the tools menu"""
        self.toolsMenu = QMenu('Python too&ls')
        self.runAct = self.toolsMenu.addAction(getIcon('run.png'),
                                               'Run script',
                                               self._parent.onRunScript)
        self.runParamAct = self.toolsMenu.addAction(
            getIcon('paramsmenu.png'), 'Set parameters and run',
            self._parent.onRunScriptDlg)
        self.toolsMenu.addSeparator()
        self.profileAct = self.toolsMenu.addAction(
            getIcon('profile.png'), 'Profile script',
            self._parent.onProfileScript)
        self.profileParamAct = self.toolsMenu.addAction(
            getIcon('paramsmenu.png'), 'Set parameters and profile',
            self._parent.onProfileScriptDlg)
        self.toolsMenu.addSeparator()
        self.disasmMenu = QMenu('Disassembly')
        self.disasmMenu.setIcon(getIcon('disassembly.png'))
        self.disasmAct0 = self.disasmMenu.addAction(
            getIcon(''), 'Disassembly (no optimization)', self._onDisasm0)
        self.disasmAct1 = self.disasmMenu.addAction(
            getIcon(''), 'Disassembly (optimization level 1)', self._onDisasm1)
        self.disasmAct2 = self.disasmMenu.addAction(
            getIcon(''), 'Disassembly (optimization level 2)', self._onDisasm2)
        self.toolsMenu.addMenu(self.disasmMenu)
        return self.toolsMenu

    def __initDiagramsMenu(self):
        """Creates the diagrams menu"""
        self.diagramsMenu = QMenu("&Diagrams")
        self.importsDgmAct = self.diagramsMenu.addAction(
            getIcon('importsdiagram.png'), 'Imports diagram',
            self._parent.onImportDgm)
        self.importsDgmParamAct = self.diagramsMenu.addAction(
            getIcon('paramsmenu.png'), 'Fine tuned imports diagram',
            self._parent.onImportDgmTuned)
        return self.diagramsMenu

    def contextMenuEvent(self, event):
        """Called just before showing a context menu"""
        # Accepting needs to suppress the native menu
        event.accept()

        isPython = self.isPythonBuffer()
        readOnly = self.isReadOnly()
        self.__menuUndo.setEnabled(self.document().isUndoAvailable())
        self.__menuRedo.setEnabled(self.document().isRedoAvailable())
        self.__menuCut.setEnabled(not readOnly)
        self.__menuPaste.setEnabled(QApplication.clipboard().text() != ""
                                    and not readOnly)

        fileName = self._parent.getFileName()
        absFileName = os.path.isabs(fileName)
        self.__menuOpenAsFile.setEnabled(self.openAsFileAvailable())
        self.__menuDownloadAndShow.setEnabled(self.downloadAndShowAvailable())
        self.__menuOpenInBrowser.setEnabled(self.downloadAndShowAvailable())
        self.__menuHighlightInPrj.setEnabled(
            absFileName and GlobalData().project.isProjectFile(fileName))
        self.__menuHighlightInFS.setEnabled(absFileName)
        self._menuHighlightInOutline.setEnabled(isPython)
        self._menuHighlightInOutline.setEnabled(isPython)

        self.toolsMenu.setEnabled(isPython)
        if isPython:
            runEnabled = self._parent.runScriptButton.isEnabled()
            self.runAct.setEnabled(runEnabled)
            self.runParamAct.setEnabled(runEnabled)
            self.profileAct.setEnabled(runEnabled)
            self.profileParamAct.setEnabled(runEnabled)

        if absFileName:
            self.__menuClearEncoding.setEnabled(
                getFileEncoding(fileName) is not None)
        else:
            self.__menuClearEncoding.setEnabled(
                self.explicitUserEncoding is not None)

        # Check the proper encoding in the menu
        encoding = 'undefined'
        if absFileName:
            enc = getFileEncoding(fileName)
            if enc:
                encoding = enc
        else:
            if self.explicitUserEncoding:
                encoding = self.explicitUserEncoding
        encoding = getNormalizedEncoding(encoding, False)
        if absFileName:
            for act in self.encodingReloadActGrp.actions():
                act.setChecked(encoding == getNormalizedEncoding(act.data()))
        else:
            self.encodingReloadMenu.setEnabled(False)
        for act in self.encodingWriteActGrp.actions():
            act.setChecked(encoding == getNormalizedEncoding(act.data()))

        # Show the menu
        self._menu.popup(event.globalPos())

    def __isSameEncodingAsCurrent(self, enc):
        """True if the same encoding has already been set"""
        fileName = self._parent.getFileName()
        if not os.path.isabs(fileName):
            # New unsaved yet file
            if not self.explicitUserEncoding:
                return False
            return getNormalizedEncoding(enc) == getNormalizedEncoding(
                self.explicitUserEncoding)

        # Existed before or just saved new file
        currentEnc = getFileEncoding(fileName)
        if not currentEnc:
            return False
        return getNormalizedEncoding(currentEnc) == getNormalizedEncoding(enc)

    def __onReloadWithEncoding(self, act):
        """Triggered when encoding is selected"""
        # The method is called only for the existing disk files
        encoding = act.data()
        if self.__isSameEncodingAsCurrent(encoding):
            return

        if self.document().isModified():
            res = QMessageBox.warning(
                self, 'Continue loosing changes',
                '<p>The buffer has unsaved changes. Are you sure to continue '
                'reloading the content using ' + encoding + ' encoding and '
                'loosing the changes?</p>',
                QMessageBox.StandardButtons(QMessageBox.Cancel
                                            | QMessageBox.Yes),
                QMessageBox.Cancel)
            if res == QMessageBox.Cancel:
                return

        # Do the reload
        fileName = self._parent.getFileName()
        setFileEncoding(fileName, encoding)
        self.__updateFilePosition()
        self.readFile(fileName)
        self.__restoreFilePosition()
        self.__updateMainWindowStatusBar()

    def __onReadWriteEncoding(self, act):
        """Sets explicit encoding for further read/write ops"""
        encoding = act.data()
        if self.__isSameEncodingAsCurrent(encoding):
            return

        # fileName = self._parent.getFileName()
        # absFileName = os.path.isabs(fileName)
        self.document().setModified(True)
        self.explicitUserEncoding = encoding
        self.__updateMainWindowStatusBar()

    def __onClearEncoding(self):
        """Clears the explicitly set encoding"""
        self.explicitUserEncoding = None
        fileName = self._parent.getFileName()
        absFileName = os.path.isabs(fileName)
        if absFileName:
            setFileEncoding(fileName, None)
            self.encoding = detectEncodingOnClearExplicit(fileName, self.text)
        self.__updateMainWindowStatusBar()

    def onUndo(self):
        """Undo implementation"""
        if self.document().isUndoAvailable():
            self.undo()
            self._parent.modificationChanged()

    def onRedo(self):
        """Redo implementation"""
        if self.document().isRedoAvailable():
            self.redo()
            self._parent.modificationChanged()

    def __onPluginMenuAdded(self, menu, count):
        """Triggered when a new menu was added"""
        del count  # unused argument
        self._menu.addMenu(menu)
        self.__pluginMenuSeparator.setVisible(True)

    def __onDisasm(self, optimization):
        """Common implementation"""
        if self.isPythonBuffer():
            if os.path.isabs(self._parent.getFileName()):
                if not self._parent.isModified():
                    GlobalData().mainWindow.showFileDisassembly(
                        self._parent.getFileName(), optimization)
                    return
            fileName = self._parent.getFileName()
            if not fileName:
                fileName = self._parent.getShortName()
            encoding = self.encoding
            if not encoding:
                encoding = detectNewFileWriteEncoding(self, fileName)
            GlobalData().mainWindow.showBufferDisassembly(
                self.text, encoding, fileName, optimization)

    def _onDisasm0(self):
        """Triggered to disassemble the buffer without optimization"""
        self.__onDisasm(OPT_NO_OPTIMIZATION)

    def _onDisasm1(self):
        """Triggered to disassemble the buffer with optimization level 1"""
        self.__onDisasm(OPT_OPTIMIZE_ASSERT)

    def _onDisasm2(self):
        """Triggered to disassemble the buffer with optimization level 2"""
        self.__onDisasm(OPT_OPTIMIZE_DOCSTRINGS)

    def __onPluginMenuRemoved(self, menu, count):
        """Triggered when a menu was deleted"""
        self._menu.removeAction(menu.menuAction())
        self.__pluginMenuSeparator.setVisible(count != 0)

    def highlightInOutline(self):
        """Triggered when highlight in outline browser is requested"""
        if self.isPythonBuffer():
            info = getBriefModuleInfoFromMemory(self.text)
            context = getContext(self, info, True, False)
            line, _ = self.cursorPosition
            GlobalData().mainWindow.highlightInOutline(context, int(line) + 1)
            self.setFocus()

    @staticmethod
    def __updateMainWindowStatusBar():
        """Updates the main window status bar"""
        mainWindow = GlobalData().mainWindow
        editorsManager = mainWindow.editorsManagerWidget.editorsManager
        editorsManager.updateStatusBar()

    @staticmethod
    def __updateFilePosition():
        """Updates the position in a file"""
        mainWindow = GlobalData().mainWindow
        editorsManager = mainWindow.editorsManagerWidget.editorsManager
        editorsManager.updateFilePosition(None)

    @staticmethod
    def __restoreFilePosition():
        """Restores the position in a file"""
        mainWindow = GlobalData().mainWindow
        editorsManager = mainWindow.editorsManagerWidget.editorsManager
        editorsManager.restoreFilePosition(None)
Example #26
0
    def __init__(self):
        self.menu = None
        self.individualMenus = {}

        # Scene menu preparation
        self.sceneMenu = QMenu()
        self.sceneMenu.addAction(getIcon('filesvg.png'), 'Save as SVG...',
                                 self.parent().onSaveAsSVG)
        self.sceneMenu.addAction(getIcon('filepdf.png'), 'Save as PDF...',
                                 self.parent().onSaveAsPDF)
        self.sceneMenu.addAction(getIcon('filepixmap.png'), 'Save as PNG...',
                                 self.parent().onSaveAsPNG)
        self.sceneMenu.addSeparator()
        self.sceneMenu.addAction(getIcon('copymenu.png'),
                                 'Copy image to clipboard',
                                 self.parent().copyToClipboard)

        # Common menu for all the individually selected items
        self.commonMenu = QMenu()
        self.__ccAction = self.commonMenu.addAction(
            getIcon("customcolors.png"), "Custom colors...",
            self.onCustomColors)
        self.__rtAction = self.commonMenu.addAction(
            getIcon("replacetitle.png"), "Replace text...", self.onReplaceText)
        self.__groupAction = self.commonMenu.addAction(getIcon("cfgroup.png"),
                                                       "Group...",
                                                       self.onGroup)
        self.commonMenu.addSeparator()
        self.__removeCCAction = self.commonMenu.addAction(
            getIcon('trash.png'), 'Remove custom colors',
            self.onRemoveCustomColors)
        self.__removeRTAction = self.commonMenu.addAction(
            getIcon('trash.png'), 'Remove replacement text',
            self.onRemoveReplacementText)
        #self.commonMenu.addSeparator()
        #self.__cutAction = self.commonMenu.addAction(
        #    getIcon("cutmenu.png"), "Cut (specific for graphics pane)",
        #    self.onCut)
        #self.__copyAction = self.commonMenu.addAction(
        #    getIcon("copymenu.png"), "Copy (specific for graphics pane)",
        #    self.onCopy)
        #self.commonMenu.addSeparator()
        #self.commonMenu.addAction(
        #    getIcon("trash.png"), "Delete", self.onDelete)

        # Individual items specific menu: begin
        ifContextMenu = QMenu()
        ifContextMenu.addAction(getIcon("switchbranches.png"),
                                "Switch branch layout", self.onSwitchIfBranch)

        openGroupContextMenu = QMenu()
        openGroupContextMenu.addAction(getIcon("collapse.png"), "Collapse",
                                       self.onGroupCollapse)
        openGroupContextMenu.addAction(getIcon("replacetitle.png"),
                                       "Edit title...", self.onGroupEditTitle)
        openGroupContextMenu.addAction(getIcon("ungroup.png"), "Ungroup",
                                       self.onGroupUngroup)

        closeGroupContextMenu = QMenu()
        closeGroupContextMenu.addAction(getIcon("expand.png"), "Expand",
                                        self.onGroupExpand)
        closeGroupContextMenu.addAction(getIcon("replacetitle.png"),
                                        "Edit title...", self.onGroupEditTitle)
        closeGroupContextMenu.addAction(getIcon("ungroup.png"), "Ungroup",
                                        self.onGroupUngroup)

        emptyGroupContextMenu = QMenu()
        emptyGroupContextMenu.addAction(getIcon("replacetitle.png"),
                                        "Edit title...", self.onGroupEditTitle)
        emptyGroupContextMenu.addAction(getIcon("ungroup.png"), "Ungroup",
                                        self.onGroupUngroup)

        self.individualMenus[IfCell] = ifContextMenu
        self.individualMenus[OpenedGroupBegin] = openGroupContextMenu
        self.individualMenus[CollapsedGroup] = closeGroupContextMenu
        self.individualMenus[EmptyGroup] = emptyGroupContextMenu
        # Individual items specific menu: end

        # Menu for a group of selected items
        self.groupMenu = QMenu()
Example #27
0
class PylintPlugin(WizardInterface):
    """Codimension pylint plugin"""
    def __init__(self):
        WizardInterface.__init__(self)
        self.__pylintDriver = None
        self.__resultViewer = None
        self.__bufferRunAction = None
        self.__bufferGenerateAction = None
        self.__globalShortcut = None

        self.__mainMenu = None
        self.__mainMenuSeparator = None
        self.__mainRunAction = None
        self.__mainGenerateAction = None

    @staticmethod
    def isIDEVersionCompatible(ideVersion):
        """Checks if the IDE version is compatible with the plugin.

        Codimension makes this call before activating a plugin.
        The passed ideVersion is a string representing
        the current IDE version.
        True should be returned if the plugin is compatible with the IDE.
        """
        return StrictVersion(ideVersion) >= StrictVersion('4.7.1')

    def activate(self, ideSettings, ideGlobalData):
        """Activates the plugin.

        The plugin may override the method to do specific
        plugin activation handling.

        ideSettings - reference to the IDE Settings singleton
                      see codimension/src/utils/settings.py
        ideGlobalData - reference to the IDE global settings
                        see codimension/src/utils/globals.py

        Note: if overriden do not forget to call the
              base class activate()
        """
        WizardInterface.activate(self, ideSettings, ideGlobalData)

        self.__resultViewer = PylintResultViewer(self.ide, PLUGIN_HOME_DIR)
        self.ide.sideBars['bottom'].addTab(
            self.__resultViewer, QIcon(PLUGIN_HOME_DIR + 'pylint.png'),
            'Pylint', 'pylint', 2)
        self.ide.sideBars['bottom'].tabButton('pylint',
                                              QTabBar.RightSide).resize(0, 0)

        # The clear call must be here, not in the results viewer __init__()
        # This is because the viewer has not been inserted into the side bar at
        # the time of __init__() so the tooltip setting does not work
        self.__resultViewer.clear()

        self.__pylintDriver = PylintDriver(self.ide)
        self.__pylintDriver.sigFinished.connect(self.__pylintFinished)

        if self.__globalShortcut is None:
            self.__globalShortcut = QShortcut(QKeySequence('Ctrl+L'),
                                              self.ide.mainWindow, self.__run)
        else:
            self.__globalShortcut.setKey('Ctrl+L')

        # Add buttons
        for _, _, tabWidget in self.ide.editorsManager.getTextEditors():
            self.__addButton(tabWidget)

        # File type changed & new tab
        self.ide.editorsManager.sigTextEditorTabAdded.connect(
            self.__textEditorTabAdded)
        self.ide.editorsManager.sigFileTypeChanged.connect(
            self.__fileTypeChanged)

        # Add main menu
        self.__mainMenu = QMenu('Pylint', self.ide.mainWindow)
        self.__mainMenu.setIcon(QIcon(PLUGIN_HOME_DIR + 'pylint.png'))
        self.__mainRunAction = self.__mainMenu.addAction(
            QIcon(PLUGIN_HOME_DIR + 'pylint.png'), 'Run pylint\t(Ctrl+L)',
            self.__run)
        self.__mainGenerateAction = self.__mainMenu.addAction(
            QIcon(PLUGIN_HOME_DIR + 'generate.png'),
            'Generate/open pylintrc file', self.__generate)
        toolsMenu = self.ide.mainWindow.menuBar().findChild(QMenu, 'tools')
        self.__mainMenuSeparator = toolsMenu.addSeparator()
        toolsMenu.addMenu(self.__mainMenu)
        self.__mainMenu.aboutToShow.connect(self.__mainMenuAboutToShow)

    def deactivate(self):
        """Deactivates the plugin.

        The plugin may override the method to do specific
        plugin deactivation handling.
        Note: if overriden do not forget to call the
              base class deactivate()
        """
        self.__globalShortcut.setKey(0)

        self.__resultViewer = None
        self.ide.sideBars['bottom'].removeTab('pylint')
        self.__pylintDriver = None

        # Remove buttons
        for _, _, tabWidget in self.ide.editorsManager.getTextEditors():
            pylintAction = tabWidget.toolbar.findChild(QAction, 'pylint')
            tabWidget.toolbar.removeAction(pylintAction)

            # deleteLater() is essential. Otherwise the button is not removed
            # really from the list of children
            pylintAction.deleteLater()

            tabWidget.getEditor().modificationChanged.disconnect(
                self.__modificationChanged)

        self.ide.editorsManager.sigTextEditorTabAdded.disconnect(
            self.__textEditorTabAdded)
        self.ide.editorsManager.sigFileTypeChanged.disconnect(
            self.__fileTypeChanged)

        # Remove main menu items
        self.__mainRunAction.deleteLater()
        self.__mainRunAction = None
        self.__mainGenerateAction.deleteLater()
        self.__mainGenerateAction = None
        self.__mainMenu.deleteLater()
        self.__mainMenu = None
        self.__mainMenuSeparator.deleteLater()
        self.__mainMenuSeparator = None

        WizardInterface.deactivate(self)

    def getConfigFunction(self):
        """Provides a plugun configuration function.

        The plugin can provide a function which will be called when the
        user requests plugin configuring.
        If a plugin does not require any config parameters then None
        should be returned.
        By default no configuring is required.
        """
        return self.configure

    def populateMainMenu(self, parentMenu):
        """Populates the main menu.

        The main menu looks as follows:
        Plugins
            - Plugin manager (fixed item)
            - Separator (fixed item)
            - <Plugin #1 name> (this is the parentMenu passed)
            ...
        If no items were populated by the plugin then there will be no
        <Plugin #N name> menu item shown.
        It is suggested to insert plugin configuration item here if so.
        """
        del parentMenu  # unused argument

    def populateFileContextMenu(self, parentMenu):
        """Populates the file context menu.

        The file context menu shown in the project viewer window will have
        an item with a plugin name and subitems which are populated here.
        If no items were populated then the plugin menu item will not be
        shown.

        When a callback is called the corresponding menu item will have
        attached data with an absolute path to the item.
        """
        del parentMenu  # unused argument

    def populateDirectoryContextMenu(self, parentMenu):
        """Populates the directory context menu.

        The directory context menu shown in the project viewer window will
        have an item with a plugin name and subitems which are populated
        here. If no items were populated then the plugin menu item will not
        be shown.

        When a callback is called the corresponding menu item will have
        attached data with an absolute path to the directory.
        """
        del parentMenu  # unused argument

    def populateBufferContextMenu(self, parentMenu):
        """Populates the editing buffer context menu.

        The buffer context menu shown for the current edited/viewed file
        will have an item with a plugin name and subitems which are
        populated here. If no items were populated then the plugin menu
        item will not be shown.

        Note: when a buffer context menu is selected by the user it always
              refers to the current widget. To get access to the current
              editing widget the plugin can use: self.ide.currentEditorWidget
              The widget could be of different types and some circumstances
              should be considered, e.g.:
              - it could be a new file which has not been saved yet
              - it could be modified
              - it could be that the disk file has already been deleted
              - etc.
              Having the current widget reference the plugin is able to
              retrieve the information it needs.
        """
        parentMenu.setIcon(QIcon(PLUGIN_HOME_DIR + 'pylint.png'))
        self.__bufferRunAction = parentMenu.addAction(
            QIcon(PLUGIN_HOME_DIR + 'pylint.png'), 'Run pylint\t(Ctrl+L)',
            self.__run)
        self.__bufferGenerateAction = parentMenu.addAction(
            QIcon(PLUGIN_HOME_DIR + 'generate.png'),
            'Generate/open pylintrc file', self.__generate)
        parentMenu.aboutToShow.connect(self.__bufferMenuAboutToShow)

    def configure(self):
        """Configure dialog"""
        PylintPluginConfigDialog(PLUGIN_HOME_DIR, self.ide.mainWindow).exec_()

    def __canRun(self, editorWidget):
        """Tells if pylint can be run for the given editor widget"""
        if self.__pylintDriver.isInProcess():
            return False, None
        if editorWidget.getType() != MainWindowTabWidgetBase.PlainTextEditor:
            return False, None
        if not isPythonMime(editorWidget.getMime()):
            return False, None
        if editorWidget.isModified():
            return False, 'Save changes before running pylint'
        if not os.path.isabs(editorWidget.getFileName()):
            return False, 'The new file has never been saved yet. ' \
                          'Save it before running pylint'
        return True, None

    def __run(self):
        """Runs the pylint analysis"""
        editorWidget = self.ide.currentEditorWidget
        canRun, message = self.__canRun(editorWidget)
        if not canRun:
            if message:
                self.ide.showStatusBarMessage(message)
            return

        enc = editorWidget.getEncoding()
        message = self.__pylintDriver.start(editorWidget.getFileName(), enc)
        if message is None:
            self.__switchToRunning()
        else:
            logging.error(message)

    def __generate(self):
        """[Generates and] opens the pylintrc file"""
        editorWidget = self.ide.currentEditorWidget
        fileName = editorWidget.getFileName()
        if not os.path.isabs(fileName):
            fileName = None
        rcfile = self.__pylintDriver.getPylintrc(self.ide, fileName)
        if not rcfile:
            if fileName is None and not self.ide.project.isLoaded():
                logging.error('Cannot generate pylintrc. '
                              'The current buffer file has not been saved yet '
                              'and there is no project')
                return

            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
            rcfile = self.__pylintDriver.generateRCFile(self.ide, fileName)
            QApplication.restoreOverrideCursor()

        if rcfile:
            if os.path.exists(rcfile):
                self.ide.mainWindow.openFile(rcfile, 0)
                return

        # It really could be only the rc generating error
        logging.error('Error generating pylintrc file ' + str(rcfile))

    def __pylintFinished(self, results):
        """Pylint has finished"""
        self.__switchToIdle()
        error = results.get('ProcessError', None)
        if error:
            logging.error(error)
        else:
            self.__resultViewer.showResults(results)
            self.ide.mainWindow.activateBottomTab('pylint')

    def __switchToRunning(self):
        """Switching to the running mode"""
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        # disable buttons
        for _, _, tabWidget in self.ide.editorsManager.getTextEditors():
            pylintAction = tabWidget.toolbar.findChild(QAction, 'pylint')
            if pylintAction is not None:
                pylintAction.setEnabled(False)
        # disable menu

    def __switchToIdle(self):
        """Switching to the idle mode"""
        QApplication.restoreOverrideCursor()
        # enable buttons
        for _, _, tabWidget in self.ide.editorsManager.getTextEditors():
            pylintAction = tabWidget.toolbar.findChild(QAction, 'pylint')
            if pylintAction is not None:
                pylintAction.setEnabled(self.__canRun(tabWidget)[0])
        # enable menu

    def __addButton(self, tabWidget):
        """Adds a button to the editor toolbar"""
        pylintButton = QAction(QIcon(PLUGIN_HOME_DIR + 'pylint.png'),
                               'Run pylint (Ctrl+L)', tabWidget.toolbar)
        pylintButton.setEnabled(self.__canRun(tabWidget)[0])
        pylintButton.triggered.connect(self.__run)
        pylintButton.setObjectName('pylint')

        beforeWidget = tabWidget.toolbar.findChild(QAction,
                                                   'deadCodeScriptButton')
        tabWidget.toolbar.insertAction(beforeWidget, pylintButton)
        tabWidget.getEditor().modificationChanged.connect(
            self.__modificationChanged)

    def __modificationChanged(self):
        """Triggered when one of the text editors changed their mod state"""
        pylintAction = self.ide.currentEditorWidget.toolbar.findChild(
            QAction, 'pylint')
        if pylintAction is not None:
            pylintAction.setEnabled(
                self.__canRun(self.ide.currentEditorWidget)[0])

    def __textEditorTabAdded(self, tabIndex):
        """Triggered when a new tab is added"""
        del tabIndex  #unused argument

        self.__addButton(self.ide.currentEditorWidget)

    def __fileTypeChanged(self, shortFileName, uuid, mime):
        """Triggered when a file changed its type"""
        del shortFileName  # unused argument
        del uuid  # unused argument
        del mime  # unused argument

        # Supposedly it can happened only on the current tab
        pylintAction = self.ide.currentEditorWidget.toolbar.findChild(
            QAction, 'pylint')
        if pylintAction is not None:
            pylintAction.setEnabled(
                self.__canRun(self.ide.currentEditorWidget)[0])

    def __bufferMenuAboutToShow(self):
        """The buffer context menu is about to show"""
        runEnable, generateState = self.__calcRunGenerateState()
        self.__bufferRunAction.setEnabled(runEnable)
        self.__bufferGenerateAction.setEnabled(generateState[0])
        self.__bufferGenerateAction.setText(generateState[1])

    def __mainMenuAboutToShow(self):
        """The main menu is about to show"""
        runEnable, generateState = self.__calcRunGenerateState()
        self.__mainRunAction.setEnabled(runEnable)
        self.__mainGenerateAction.setEnabled(generateState[0])
        self.__mainGenerateAction.setText(generateState[1])

    def __calcRunGenerateState(self):
        """Calculates the enable/disable state of the run/generate menu items"""
        editorWidget = self.ide.currentEditorWidget
        defaultGenerateText = 'Open pylintrc file'
        if editorWidget.getType() != MainWindowTabWidgetBase.PlainTextEditor:
            return False, (False, defaultGenerateText)
        if not isPythonMime(editorWidget.getMime()):
            return False, (False, defaultGenerateText)
        if self.__pylintDriver.isInProcess():
            return False, (False, defaultGenerateText)

        fileName = editorWidget.getFileName()
        if not os.path.isabs(fileName):
            fileName = None
        rcfile = self.__pylintDriver.getPylintrc(self.ide, fileName)

        if editorWidget.isModified():
            if rcfile:
                return False, (True, defaultGenerateText)
            return False, (True, 'Generate and open pylintrc file')

        # Saved python file and no pylint running
        if rcfile:
            return fileName is not None, (True, defaultGenerateText)
        return fileName is not None, (True, 'Generate and open pylintrc file')
Example #28
0
 def __initEmptyGroupContextMenu(self):
     """Creates the empty group context menu"""
     egMenu = QMenu()
     egMenu.addAction(getIcon("replacetitle.png"), "Edit title...", self.onGroupEditTitle)
     egMenu.addAction(getIcon("ungroup.png"), "Ungroup", self.onGroupUngroup)
     return egMenu
Example #29
0
class BreakPointView(QTreeView):
    """Breakpoint viewer widget"""

    sigSelectionChanged = pyqtSignal(QModelIndex)

    def __init__(self, parent, bpointsModel):
        QTreeView.__init__(self, parent)

        self.__model = None
        self.setModel(bpointsModel)

        self.setItemsExpandable(False)
        self.setRootIsDecorated(False)
        self.setAlternatingRowColors(True)
        self.setUniformRowHeights(True)
        self.setSelectionMode(QAbstractItemView.SingleSelection)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.setItemDelegate(NoOutlineHeightDelegate(4))

        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.__showContextMenu)
        self.doubleClicked.connect(self.__doubleClicked)

        self.__createPopupMenus()

    def setModel(self, model):
        """Sets the breakpoint model"""
        self.__model = model

        self.sortingModel = QSortFilterProxyModel()
        self.sortingModel.setSourceModel(self.__model)
        QTreeView.setModel(self, self.sortingModel)

        header = self.header()
        header.setSortIndicator(COLUMN_LOCATION, Qt.AscendingOrder)
        header.setSortIndicatorShown(True)
        header.setSectionsClickable(True)

        self.setSortingEnabled(True)
        self.layoutDisplay()

    def layoutDisplay(self):
        """Performs the layout operation"""
        self.__resizeColumns()
        self.__resort()

    def __resizeColumns(self):
        """Resizes the view when items get added, edited or deleted"""
        self.header().setStretchLastSection(True)
        self.header().resizeSections(QHeaderView.ResizeToContents)
        self.header().resizeSection(COLUMN_TEMPORARY, 22)
        self.header().resizeSection(COLUMN_ENABLED, 22)

    def __resort(self):
        """Resorts the tree"""
        self.model().sort(self.header().sortIndicatorSection(),
                          self.header().sortIndicatorOrder())

    def toSourceIndex(self, index):
        """Converts an index to a source index"""
        return self.sortingModel.mapToSource(index)

    def __fromSourceIndex(self, sindex):
        """Convert a source index to an index"""
        return self.sortingModel.mapFromSource(sindex)

    def __setRowSelected(self, index, selected=True):
        """Selects a row"""
        if not index.isValid():
            return

        if selected:
            flags = QItemSelectionModel.SelectionFlags(
                QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
        else:
            flags = QItemSelectionModel.SelectionFlags(
                QItemSelectionModel.Deselect | QItemSelectionModel.Rows)
        self.selectionModel().select(index, flags)

    def __createPopupMenus(self):
        """Generate the popup menu"""
        self.menu = QMenu()
        self.__editAct = self.menu.addAction(getIcon('bpprops.png'), "Edit...",
                                             self.__editBreak)
        self.__jumpToCodeAct = self.menu.addAction(getIcon('gotoline.png'),
                                                   "Jump to code",
                                                   self.__showSource)
        self.menu.addSeparator()
        self.__enableAct = self.menu.addAction(getIcon('bpenable.png'),
                                               "Enable", self.enableBreak)
        self.__enableAllAct = self.menu.addAction(getIcon('bpenableall.png'),
                                                  "Enable all",
                                                  self.enableAllBreaks)
        self.menu.addSeparator()
        self.__disableAct = self.menu.addAction(getIcon('bpdisable.png'),
                                                "Disable", self.disableBreak)
        self.__disableAllAct = self.menu.addAction(getIcon('bpdisableall.png'),
                                                   "Disable all",
                                                   self.disableAllBreaks)
        self.menu.addSeparator()
        self.__delAct = self.menu.addAction(getIcon('bpdel.png'), "Delete",
                                            self.deleteBreak)
        self.__delAllAct = self.menu.addAction(getIcon('bpdelall.png'),
                                               "Delete all",
                                               self.deleteAllBreaks)

    def __showContextMenu(self, _):
        """Shows the context menu"""
        index = self.currentIndex()
        if not index.isValid():
            return
        sindex = self.toSourceIndex(index)
        if not sindex.isValid():
            return
        bpoint = self.__model.getBreakPointByIndex(sindex)
        if not bpoint:
            return

        enableCount, disableCount = self.__model.getCounts()

        self.__editAct.setEnabled(True)
        self.__enableAct.setEnabled(not bpoint.isEnabled())
        self.__disableAct.setEnabled(bpoint.isEnabled())
        self.__jumpToCodeAct.setEnabled(True)
        self.__delAct.setEnabled(True)
        self.__enableAllAct.setEnabled(disableCount > 0)
        self.__disableAllAct.setEnabled(enableCount > 0)
        self.__delAllAct.setEnabled(enableCount + disableCount > 0)

        self.menu.popup(QCursor.pos())

    def __doubleClicked(self, index):
        """Handles the double clicked signal"""
        if not index.isValid():
            return

        sindex = self.toSourceIndex(index)
        if not sindex.isValid():
            return

        # Jump to the code
        bpoint = self.__model.getBreakPointByIndex(sindex)
        fileName = bpoint.getAbsoluteFileName()
        line = bpoint.getLineNumber()
        self.jumpToCode(fileName, line)

    @staticmethod
    def jumpToCode(fileName, line):
        """Jumps to the source code"""
        editorsManager = GlobalData().mainWindow.editorsManager()
        editorsManager.openFile(fileName, line)
        editor = editorsManager.currentWidget().getEditor()
        editor.gotoLine(line)
        editorsManager.currentWidget().setFocus()

    def __editBreak(self):
        """Handle the edit breakpoint context menu entry"""
        index = self.currentIndex()
        if index.isValid():
            self.__editBreakpoint(index)

    def __editBreakpoint(self, index):
        """Edits a breakpoint"""
        sindex = self.toSourceIndex(index)
        if sindex.isValid():
            bpoint = self.__model.getBreakPointByIndex(sindex)
            if not bpoint:
                return

            dlg = BreakpointEditDialog(bpoint)
            if dlg.exec_() == QDialog.Accepted:
                newBpoint = dlg.getData()
                if newBpoint == bpoint:
                    return
                self.__model.setBreakPointByIndex(sindex, newBpoint)
                self.layoutDisplay()

    def __setBpEnabled(self, index, enabled):
        """Sets the enabled status of a breakpoint"""
        sindex = self.toSourceIndex(index)
        if sindex.isValid():
            self.__model.setBreakPointEnabledByIndex(sindex, enabled)

    def enableBreak(self):
        """Handles the enable breakpoint context menu entry"""
        index = self.currentIndex()
        self.__setBpEnabled(index, True)
        self.__resizeColumns()
        self.__resort()

    def enableAllBreaks(self):
        """Handles the enable all breakpoints context menu entry"""
        index = self.model().index(0, 0)
        while index.isValid():
            self.__setBpEnabled(index, True)
            index = self.indexBelow(index)
        self.__resizeColumns()
        self.__resort()

    def disableBreak(self):
        """Handles the disable breakpoint context menu entry"""
        index = self.currentIndex()
        self.__setBpEnabled(index, False)
        self.__resizeColumns()
        self.__resort()

    def disableAllBreaks(self):
        """Handles the disable all breakpoints context menu entry"""
        index = self.model().index(0, 0)
        while index.isValid():
            self.__setBpEnabled(index, False)
            index = self.indexBelow(index)
        self.__resizeColumns()
        self.__resort()

    def deleteBreak(self):
        """Handles the delete breakpoint context menu entry"""
        index = self.currentIndex()
        sindex = self.toSourceIndex(index)
        if sindex.isValid():
            self.__model.deleteBreakPointByIndex(sindex)

    def deleteAllBreaks(self):
        """Handles the delete all breakpoints context menu entry"""
        self.__model.deleteAll()

    def __showSource(self):
        """Handles the goto context menu entry"""
        index = self.currentIndex()
        self.__doubleClicked(index)

    def highlightBreakpoint(self, fname, lineno):
        """Handles the clientLine signal"""
        sindex = self.__model.getBreakPointIndex(fname, lineno)
        if sindex.isValid():
            return

        index = self.__fromSourceIndex(sindex)
        if index.isValid():
            self.__clearSelection()
            self.__setRowSelected(index, True)

    def __getSelectedItemsCount(self):
        """Provides the count of items selected"""
        count = len(self.selectedIndexes()) / (self.__model.columnCount() - 1)
        # column count is 1 greater than selectable
        return count

    def selectionChanged(self, selected, deselected):
        """The slot is called when the selection has changed"""
        if selected.indexes():
            self.sigSelectionChanged.emit(selected.indexes()[0])
        else:
            self.sigSelectionChanged.emit(QModelIndex())
        QTreeView.selectionChanged(self, selected, deselected)
Example #30
0
class CFSceneContextMenuMixin:

    """Encapsulates the context menu handling"""

    def __init__(self):
        self.menu = None
        self.individualMenus = {}

        # Scene menu preparation
        self.sceneMenu = QMenu()
        self.sceneMenu.addAction(getIcon('filesvg.png'), 'Save as SVG...',
                                 self.parent().onSaveAsSVG)
        self.sceneMenu.addAction(getIcon('filepdf.png'), 'Save as PDF...',
                                 self.parent().onSaveAsPDF)
        self.sceneMenu.addAction(getIcon('filepixmap.png'), 'Save as PNG...',
                                 self.parent().onSaveAsPNG)
        self.sceneMenu.addSeparator()
        self.sceneMenu.addAction(getIcon('copymenu.png'),
                                 'Copy image to clipboard',
                                 self.parent().copyToClipboard)

        # Common menu for all the individually selected items
        self.commonMenu = QMenu()
        self.__ccSubmenuAction = self.commonMenu.addMenu(
            self.__initCustomColorsContextMenu())
        self.__rtSubmenuAction = self.commonMenu.addMenu(
            self.__initReplaceTextContextMenu())
        self.__docSubmenuAction = self.commonMenu.addMenu(
            self.__initDocContextMenu())
        self.__groupAction = self.commonMenu.addAction(
            getIcon("cfgroup.png"), "Group...", self.onGroup)

        #self.commonMenu.addSeparator()
        #self.__cutAction = self.commonMenu.addAction(
        #    getIcon("cutmenu.png"), "Cut (specific for graphics pane)",
        #    self.onCut)
        #self.__copyAction = self.commonMenu.addAction(
        #    getIcon("copymenu.png"), "Copy (specific for graphics pane)",
        #    self.onCopy)
        #self.commonMenu.addSeparator()
        #self.commonMenu.addAction(
        #    getIcon("trash.png"), "Delete", self.onDelete)

        # Individual items specific menu: begin
        ifContextMenu = QMenu()
        ifContextMenu.addAction(
            getIcon("switchbranches.png"), "Switch branch layout",
            self.onSwitchIfBranch)

        self.individualMenus[IfCell] = ifContextMenu
        self.individualMenus[OpenedGroupBegin] = self.__initOpenGroupContextMenu()
        self.individualMenus[CollapsedGroup] = self.__initCloseGroupContextMenu()
        self.individualMenus[EmptyGroup] = self.__initEmptyGroupContextMenu()

        # Individual items specific menu: end

        # Menu for a group of selected items
        self.groupMenu = QMenu()

    def __initOpenGroupContextMenu(self):
        """Creates the open group context menu"""
        ogMenu = QMenu()
        ogMenu.addAction(getIcon("collapse.png"), "Collapse", self.onGroupCollapse)
        ogMenu.addAction(getIcon("replacetitle.png"), "Edit title...", self.onGroupEditTitle)
        ogMenu.addAction(getIcon("ungroup.png"), "Ungroup", self.onGroupUngroup)
        return ogMenu

    def __initCloseGroupContextMenu(self):
        """Creates the closed group context menu"""
        cgMenu = QMenu()
        cgMenu.addAction(getIcon("expand.png"), "Expand", self.onGroupExpand)
        cgMenu.addAction(getIcon("replacetitle.png"), "Edit title...", self.onGroupEditTitle)
        cgMenu.addAction(getIcon("ungroup.png"), "Ungroup", self.onGroupUngroup)
        return cgMenu

    def __initEmptyGroupContextMenu(self):
        """Creates the empty group context menu"""
        egMenu = QMenu()
        egMenu.addAction(getIcon("replacetitle.png"), "Edit title...", self.onGroupEditTitle)
        egMenu.addAction(getIcon("ungroup.png"), "Ungroup", self.onGroupUngroup)
        return egMenu

    def __initCustomColorsContextMenu(self):
        """Create the custom colors submenu"""
        self.__customColorsSubmenu = QMenu('Custom colors')
        self.__customColorsSubmenu.setIcon(getIcon('customcolorsmenu.png'))
        self.__ccAction = self.__customColorsSubmenu.addAction(
            getIcon("customcolors.png"), "Custom colors...",
            self.onCustomColors)
        self.__customColorsSubmenu.addSeparator()
        self.__removeCCAction = self.__customColorsSubmenu.addAction(
            getIcon('trash.png'), 'Remove custom colors',
            self.onRemoveCustomColors)
        return self.__customColorsSubmenu

    def __initReplaceTextContextMenu(self):
        """Create the Replace text submenu"""
        self.__replaceTextSubmenu = QMenu('Replace text')
        self.__replaceTextSubmenu.setIcon(getIcon('replacetextmenu.png'))
        self.__rtAction = self.__replaceTextSubmenu.addAction(
            getIcon("replacetitle.png"), "Replace text...",
            self.onReplaceText)
        self.__replaceTextSubmenu.addSeparator()
        self.__removeRTAction = self.__replaceTextSubmenu.addAction(
            getIcon('trash.png'), 'Remove replacement text',
            self.onRemoveReplacementText)
        return self.__replaceTextSubmenu

    def __initDocContextMenu(self):
        """Create the Documentation submenu"""
        self.__docSubmenu = QMenu('Documentation')
        self.__docSubmenu.setIcon(getIcon('markdown.png'))
        self.__editDocAction = self.__docSubmenu.addAction(
            getIcon('replacetitle.png'), 'Add/edit doc link/anchor...',
            self.onEditDoc)
        self.__autoDocActon = self.__docSubmenu.addAction(
            getIcon('createdoc.png'),
            'Create doc file, add link and open for editing',
            self.onAutoAddDoc)
        self.__docSubmenu.addSeparator()
        self.__removeDocAction = self.__docSubmenu.addAction(
            getIcon('trash.png'), 'Remove doc link/anchor',
            self.onRemoveDoc)
        return self.__docSubmenu

    def onContextMenu(self, event):
        """Triggered when a context menu should be shown"""
        selectedItems = self.selectedItems()
        selectionCount = len(selectedItems)
        if selectionCount == 0:
            self.sceneMenu.popup(event.screenPos())
            return

        if selectionCount == 1:
            self.__buildIndividualMenu(selectedItems[0])
        else:
            self.__buildGroupMenu(selectedItems)
        self.menu.popup(event.screenPos())

    def __buildIndividualMenu(self, item):
        """Builds a context menu for the given item"""
        self.menu = QMenu()
        if type(item) in self.individualMenus:
            individualPart = self.individualMenus[type(item)]
            self.menu.addActions(individualPart.actions())
            self.menu.addSeparator()
        self.menu.addActions(self.commonMenu.actions())

        # Note: if certain items need to be disabled then it should be done
        #       here
        self.__disableMenuItems()

    def __buildGroupMenu(self, items):
        """Builds a context menu for the group of items"""
        self.menu = QMenu()
        if type(items[0]) in self.individualMenus:
            if self.areSelectedOfTypes([[items[0].kind, items[0].subKind]]):
                individualPart = self.individualMenus[type(items[0])]
                self.menu.addActions(individualPart.actions())
                self.menu.addSeparator()
        self.menu.addActions(self.commonMenu.actions())
        if not self.groupMenu.isEmpty():
            self.menu.addSeparator()
            self.menu.addActions(self.groupMenu.actions())

        # Note: if certain items need to be disabled then it should be done
        #       here
        self.__disableMenuItems()

    def __disableMenuItems(self):
        """Disables the common menu items as needed"""
        totalComments = self.countComments()
        hasComment = totalComments > 0
        hasDocstring = self.isDocstringInSelection()
        hasMinimizedExcepts = self.isInSelected([(CellElement.EXCEPT_MINIMIZED,
                                                  None)])
        # Doc links are considered comments as well
        totalDocLinks = self.countInSelected([(CellElement.INDEPENDENT_DOC, None),
                                              (CellElement.LEADING_DOC, None),
                                              (CellElement.ABOVE_DOC, None)])
        totalNonDocComments = totalComments - totalDocLinks

        totalGroups = sum(self.countGroups())
        count = len(self.selectedItems())
        totalCCGroups = sum(self.countGroupsWithCustomColors())
        totalCCDocs = self.countDocWithCustomColors()

        self.__ccAction.setEnabled(totalNonDocComments == 0 and
                                   not hasMinimizedExcepts)
        self.__removeCCAction.setEnabled(
            self.countItemsWithCML(CMLcc) + totalCCGroups + totalCCDocs == count)
        self.__customColorsSubmenu.setEnabled(self.__ccAction.isEnabled() or
                                              self.__removeCCAction.isEnabled())

        self.__rtAction.setEnabled(not hasComment and
                                   not hasDocstring and
                                   not hasMinimizedExcepts and
                                   totalDocLinks == 0 and
                                   totalGroups == 0)
        self.__removeRTAction.setEnabled(
            self.countItemsWithCML(CMLrt) == count)
        self.__replaceTextSubmenu.setEnabled(self.__rtAction.isEnabled() or
                                             self.__removeRTAction.isEnabled())

        self.__groupAction.setEnabled(self.__canBeGrouped())

        itemsWithDocCML = self.countItemsWithCML(CMLdoc)
        self.__removeDocAction.setEnabled(totalDocLinks + itemsWithDocCML == count)
        if count != 1 or totalNonDocComments != 0 or hasDocstring or totalGroups != 0:
            self.__editDocAction.setEnabled(False)
            self.__autoDocActon.setEnabled(False)
        else:
            self.__editDocAction.setEnabled(True)
            fileName = None
            editor = self.selectedItems()[0].getEditor()
            if editor:
                fileName = editor._parent.getFileName()
            self.__autoDocActon.setEnabled(
                    fileName and totalDocLinks + itemsWithDocCML == 0)
        self.__docSubmenu.setEnabled(self.__removeDocAction.isEnabled() or
                                     self.__editDocAction.isEnabled() or
                                     self.__autoDocActon.isEnabled())

        #self.__cutAction.setEnabled(count == 1)
        #self.__copyAction.setEnabled(count == 1)

    def __actionPrerequisites(self):
        """True if an editor related action can be done"""
        selectedItems = self.selectedItems()
        if not selectedItems:
            return False
        editor = selectedItems[0].getEditor()
        if editor is None:
            return False
        return True

    def onSwitchIfBranch(self):
        """If primitive should switch the branches"""
        if not self.__actionPrerequisites():
            return

        # Memorize the current selection
        selection = self.serializeSelection()

        # The selected items need to be sorted in the reverse line no oreder
        editor = self.selectedItems()[0].getEditor()
        with editor:
            for item in self.sortSelectedReverse():
                if item.kind == CellElement.IF:
                    cmlComment = CMLVersion.find(item.ref.leadingCMLComments,
                                                 CMLsw)
                    if cmlComment is None:
                        # Did not exist, so needs to be generated
                        line = CMLsw.generate(item.ref.body.beginPos)
                        lineNo = item.getFirstLine()
                        editor.insertLines(line, lineNo)
                    else:
                        # Existed, so it just needs to be deleted
                        cmlComment.removeFromText(editor)
        QApplication.processEvents()
        self.parent().redrawNow()
        self.restoreSelectionByID(selection)

    def onCustomColors(self):
        """Custom background and foreground colors"""
        if not self.__actionPrerequisites():
            return

        # Memorize the current selection
        selection = self.serializeSelection()

        bgcolor, fgcolor, bordercolor = self.selectedItems()[0].getColors()
        hasDocstring = self.isDocstringInSelection()
        dlg = CustomColorsDialog(bgcolor, fgcolor,
                                 None if hasDocstring else bordercolor,
                                 self.parent())
        if dlg.exec_():
            bgcolor = dlg.backgroundColor()
            fgcolor = dlg.foregroundColor()
            bordercolor = dlg.borderColor()

            editor = self.selectedItems()[0].getEditor()
            with editor:
                # Add colors is done via delete/insert for the Doc and group
                # items. So it is safer to do first because the cc comment may be
                # in a set of selected which is inserted before the doc cml and
                # thus breaks the line numbering
                for item in self.selectedItems():
                    if item.isCMLDoc():
                        # The doc always exists so just add/change the colors
                        item.cmlRef.updateCustomColors(editor, bgcolor,
                                                       fgcolor, bordercolor)
                        continue
                    if item.isGroupItem():
                        # The group always exists so just add/change the colors
                        item.groupBeginCMLRef.updateCustomColors(editor,
                                                                 bgcolor,
                                                                 fgcolor,
                                                                 bordercolor)

                for item in self.sortSelectedReverse():
                    if item.isCMLDoc() or item.isGroupItem():
                        continue
                    if item.isDocstring():
                        cmlComment = CMLVersion.find(
                            item.ref.docstring.leadingCMLComments,
                            CMLcc)
                    else:
                        cmlComment = CMLVersion.find(
                            item.ref.leadingCMLComments, CMLcc)
                    if cmlComment is not None:
                        # Existed, so remove the old one first
                        lineNo = cmlComment.ref.beginLine
                        cmlComment.removeFromText(editor)
                    else:
                        lineNo = item.getFirstLine()

                    pos = item.ref.body.beginPos
                    if item.isDocstring():
                        pos = item.ref.docstring.beginPos
                    line = CMLcc.generate(bgcolor, fgcolor, bordercolor, pos)
                    editor.insertLines(line, lineNo)
            QApplication.processEvents()
            self.parent().redrawNow()
            self.restoreSelectionByID(selection)

    def onReplaceText(self):
        """Replace the code with a title"""
        if not self.__actionPrerequisites():
            return

        # Memorize the current selection
        selection = self.serializeSelection()

        dlg = ReplaceTextDialog('Replace text', 'Item caption:', self.parent())

        # If it was one item selection and there was a previous text then
        # set it for editing
        if len(self.selectedItems()) == 1:
            cmlComment = CMLVersion.find(
                self.selectedItems()[0].ref.leadingCMLComments, CMLrt)
            if cmlComment is not None:
                dlg.setText(cmlComment.getText())

        if dlg.exec_():
            replacementText = dlg.text()
            editor = self.selectedItems()[0].getEditor()
            with editor:
                for item in self.sortSelectedReverse():
                    cmlComment = CMLVersion.find(
                        item.ref.leadingCMLComments, CMLrt)
                    if cmlComment is not None:
                        # Existed, so remove the old one first
                        lineNo = cmlComment.ref.beginLine
                        cmlComment.removeFromText(editor)
                    else:
                        lineNo = item.getFirstLine()

                    line = CMLrt.generate(replacementText,
                                          item.ref.body.beginPos)
                    editor.insertLines(line, lineNo)
            QApplication.processEvents()
            self.parent().redrawNow()
            self.restoreSelectionByID(selection)

    def onGroupCollapse(self):
        """Collapses the selected group"""
        if not self.__actionPrerequisites():
            return

        # The selected items need to be sorted in the reverse line no oreder
        editor = self.selectedItems()[0].getEditor()
        with editor:
            for item in self.sortSelectedReverse():
                if item.kind == CellElement.OPENED_GROUP_BEGIN:
                    fileName = editor._parent.getFileName()
                    if not fileName:
                        fileName = editor._parent.getShortName()
                    addCollapsedGroup(fileName, item.getGroupId())

        QApplication.processEvents()
        self.parent().redrawNow()

    def onGroupExpand(self):
        """Expands the selected group"""
        if not self.__actionPrerequisites():
            return

        # The selected items need to be sorted in the reverse line no oreder
        editor = self.selectedItems()[0].getEditor()
        with editor:
            for item in self.sortSelectedReverse():
                if item.kind == CellElement.COLLAPSED_GROUP:
                    fileName = editor._parent.getFileName()
                    if not fileName:
                        fileName = editor._parent.getShortName()
                    removeCollapsedGroup(fileName, item.getGroupId())

        QApplication.processEvents()
        self.parent().redrawNow()

    def onGroupEditTitle(self):
        """Edit (or view) the group title"""
        if not self.__actionPrerequisites():
            return

        # Memorize the current selection
        selection = self.serializeSelection()

        dlg = ReplaceTextDialog('Group title', 'Group title:', self.parent())

        # If it was one item selection and there was a previous text then
        # set it for editing
        if len(self.selectedItems()) == 1:
            title = self.selectedItems()[0].getTitle()
            if title:
                dlg.setText(title)

        if dlg.exec_():
            newTitle = dlg.text()
            editor = self.selectedItems()[0].getEditor()
            with editor:
                for item in self.sortSelectedReverse():
                    item.groupBeginCMLRef.updateTitle(editor, newTitle)
            QApplication.processEvents()
            self.parent().redrawNow()
            self.restoreSelectionByID(selection)

    def onGroupUngroup(self):
        """Ungroups the items"""
        if not self.__actionPrerequisites():
            return

        # Memorize the current selection
        selection = self.serializeSelection()

        # The selected items need to be sorted in the reverse line no oreder
        editor = self.selectedItems()[0].getEditor()
        with editor:
            for item in self.sortSelectedReverse():
                item.groupEndCMLRef.removeFromText(editor)
                item.groupBeginCMLRef.removeFromText(editor)
        QApplication.processEvents()
        self.parent().redrawNow()
        self.restoreSelectionByTooltip(selection)

    def onDelete(self):
        """Delete the item"""
        print("Delete")

    def onGroup(self):
        """Groups items into a single one"""
        dlg = ReplaceTextDialog('Group title', 'Group title:', self.parent())

        if dlg.exec_():
            groupTitle = dlg.text()
            selected = self.__extendSelectionForGrouping()
            selected = self.sortSelected(selected)
            editor = selected[0].getEditor()

            firstLine, lastLine, pos = self.__getLineRange(selected)

            groupid = self.parent().generateNewGroupId()
            beginComment = CMLgb.generate(groupid, groupTitle,
                                          None, None, None, pos)
            endComment = CMLge.generate(groupid, pos)

            with editor:
                editor.insertLines(endComment, lastLine + 1)
                editor.insertLines(beginComment, firstLine)

            # Redraw the group collapsed
            fileName = editor._parent.getFileName()
            if not fileName:
                fileName = editor._parent.getShortName()
            addCollapsedGroup(fileName, groupid)

            QApplication.processEvents()
            self.parent().redrawNow()

    def onCopy(self):
        """Copying..."""
        selectedItems = self.selectedItems()
        if selectedItems:
            if len(selectedItems) > 1:
                print('Copying multiple items has not been implemented yet')
                return
            selectedItems[0].copyToClipboard()

    def onCut(self):
        """Cutting..."""
        print("Cut")

    def onRemoveCustomColors(self):
        """Removing the previously set custom colors"""
        if not self.__actionPrerequisites():
            return

        # Memorize the current selection
        selection = self.serializeSelection()

        editor = self.selectedItems()[0].getEditor()
        with editor:
            # Remove colors is done via delete/insert for the Doc and group
            # items. So it is safer to do first because the cc comment may be
            # in a set of selected which is inserted before the doc cml and
            # thus breaks the line numbering
            for item in self.selectedItems():
                # The doc always exists
                if item.isCMLDoc():
                    item.cmlRef.removeCustomColors(editor)
                    continue
                # The group always exists
                if item.isGroupItem():
                    item.groupBeginCMLRef.removeCustomColors(editor)

            # Now handle the rest of items
            for item in self.sortSelectedReverse():
                if item.isCMLDoc() or item.isGroupItem():
                    continue
                if item.isDocstring():
                    cmlComment = CMLVersion.find(
                        item.ref.docstring.leadingCMLComments, CMLcc)
                else:
                    cmlComment = CMLVersion.find(
                        item.ref.leadingCMLComments, CMLcc)
                if cmlComment is not None:
                    cmlComment.removeFromText(editor)
        QApplication.processEvents()
        self.parent().redrawNow()
        self.restoreSelectionByID(selection)

    def onRemoveReplacementText(self):
        """Removing replacement text"""
        if self.__actionPrerequisites():
            # Memorize the current selection
            selection = self.serializeSelection()

            editor = self.selectedItems()[0].getEditor()
            with editor:
                for item in self.sortSelectedReverse():
                    cmlComment = CMLVersion.find(item.ref.leadingCMLComments,
                                                 CMLrt)
                    if cmlComment is not None:
                        cmlComment.removeFromText(editor)
            QApplication.processEvents()
            self.parent().redrawNow()
            self.restoreSelectionByID(selection)

    def areSelectedOfTypes(self, matchList):
        """Checks if the selected items belong to the match"""
        # match is a list of pairs [kind, subKind]
        #   None would mean 'match any'
        selectedItems = self.selectedItems()
        if selectedItems:
            for selectedItem in selectedItems:
                for kind, subKind in matchList:
                    match = True
                    if kind is not None:
                        if kind != selectedItem.kind:
                            match = False
                    if subKind is not None:
                        if subKind != selectedItem.subKind:
                            match = False
                    if match:
                        break
                else:
                    return False
            return True
        return False

    def __createDocFile(self, link, fromFile):
        """Creates the doc file if needed"""
        fName, _, errMsg = preResolveLinkPath(link, fromFile, True)
        if errMsg:
            logging.error(errMsg)
            return None

        if os.path.exists(fName):
            return fName

        try:
            os.makedirs(os.path.dirname(fName), exist_ok=True)
            with open(fName, 'w') as f:
                pass
        except Exception as exc:
            logging.error('Error creating the documentation file ' +
                          fName + ': ' + str(exc))
            return None
        return fName

    def onEditDoc(self):
        """Editing the CML doc comment"""
        if not self.__actionPrerequisites():
            return

        selectedItem = self.selectedItems()[0]  # Exactly one is selected
        editor = selectedItem.getEditor()
        fileName = editor._parent.getFileName()
        if not fileName:
            fileName = editor._parent.getShortName()

        # It could be a CML doc or an item which has a CML doc
        if selectedItem.isComment():
            cmlRef = selectedItem.cmlRef
        else:
            # If not found then it means the doc link needs to be created
            cmlRef = self.__findCMLinItem(selectedItem, CMLdoc)

        dlg = DocLinkAnchorDialog('Add' if cmlRef is None else 'Edit',
                                  cmlRef, fileName, self.parent())
        if dlg.exec_():
            link = dlg.linkEdit.text().strip()
            anchor = dlg.anchorEdit.text().strip()
            title = dlg.title()
            needToCreate = dlg.needToCreate()

            # First create a file if asked
            if needToCreate:
                docFileName = self.__createDocFile(link, fileName)
                if not docFileName:
                    return

            selection = self.serializeSelection()
            with editor:
                # Now insert a new cml comment or update existing
                if cmlRef:
                    # It is editing, the comment exists
                    lineNo = cmlRef.ref.beginLine
                    pos = cmlRef.ref.beginPos
                    cmlRef.removeFromText(editor)
                    bgColor = cmlRef.bgColor
                    fgColor = cmlRef.fgColor
                    border = cmlRef.border
                else:
                    # It is a new doc link
                    lineNo = selectedItem.getFirstLine()
                    pos = selectedItem.ref.body.beginPos
                    bgColor = None
                    fgColor = None
                    border = None

                line = CMLdoc.generate(link, anchor, title,
                                       bgColor, fgColor, border, pos)
                editor.insertLines(line, lineNo)

            QApplication.processEvents()
            self.parent().redrawNow()
            self.restoreSelectionByID(selection)

    @staticmethod
    def __getAutoDocFileName(fileName):
        """Forms the auto doc file name"""
        # Markdown is used as a default documentation format
        fBaseName = os.path.basename(fileName)
        if '.' in fBaseName:
            fileExtension = fBaseName.split('.')[-1]
            fBaseName = fBaseName[:-len(fileExtension)] + 'md'
        else:
            fBaseName += '.md'

        project = GlobalData().project
        if project.isProjectFile(fileName):
            projectDir = project.getProjectDir()
            relativePath = fileName[len(projectDir):]
            projectName = project.getProjectName()
            if relativePath.startswith(projectName):
                relativePath = relativePath.replace(projectName, '', 1)
            return os.path.normpath(
                os.path.sep.join([projectDir + 'doc',
                                  os.path.dirname(relativePath),
                                  fBaseName]))
        return os.path.normpath(
            os.path.sep.join([os.path.dirname(fileName),
                              'doc',
                               fBaseName]))

    def onAutoAddDoc(self):
        """Create a doc file, add a link and open for editing"""
        if not self.__actionPrerequisites():
            return

        selectedItem = self.selectedItems()[0]  # Exactly one is selected
        editor = selectedItem.getEditor()
        fileName = editor._parent.getFileName()
        if not fileName:
            logging.error('Save file before invoking auto doc')
            return

        needContent = False
        newAnchor = 'doc' + str(uuid.uuid4().fields[-1])[-6:]

        docFileName = self.__getAutoDocFileName(fileName)
        if not os.path.exists(docFileName):
            # Create file and populate with the default content
            try:
                os.makedirs(os.path.dirname(docFileName), exist_ok=True)
                with open(docFileName, 'w') as f:
                    pass
            except Exception as exc:
                logging.error('Error creating the documentation file ' +
                              docFileName + ': ' + str(exc))
                return
            needContent = True

        project = GlobalData().project
        if project.isProjectFile(docFileName):
            link = project.getRelativePath(docFileName)
        else:
            link = os.path.relpath(docFileName, fileName)

        # Insert a doc link
        with editor:
            lineNo = selectedItem.getFirstLine()
            line = CMLdoc.generate(link, newAnchor, 'See documentation',
                                   None, None, None,
                                   selectedItem.ref.body.beginPos)
            editor.insertLines(line, lineNo)

            QApplication.processEvents()
            self.parent().redrawNow()

        # Open the file
        if GlobalData().mainWindow.openFile(docFileName, -1):
            if needContent:
                widget = GlobalData().mainWindow.em.getWidgetForFileName(docFileName)
                editor = widget.getEditor()
                editor.text = getDefaultFileDoc(fileName, newAnchor)
                editor.document().setModified(False)

    def onRemoveDoc(self):
        """Removing the CML doc comment"""
        if not self.__actionPrerequisites():
            return

        editor = self.selectedItems()[0].getEditor()
        with editor:
            for item in self.sortSelectedReverse():
                cmlComment = CMLVersion.find(item.ref.leadingCMLComments,
                                             CMLdoc)
                if cmlComment is not None:
                    cmlComment.removeFromText(editor)
        QApplication.processEvents()
        self.parent().redrawNow()

    def countInSelected(self, matchList):
        """Counts the number of matching items in selection"""
        # match is a list of pairs [kind, subKind]
        #   None would mean 'match any'
        count = 0
        for selectedItem in self.selectedItems():
            for kind, subKind in matchList:
                match = True
                if kind is not None:
                    if kind != selectedItem.kind:
                        match = False
                if subKind is not None:
                    if subKind != selectedItem.subKind:
                        match = False
                if match:
                    count += 1
        return count

    def isInSelected(self, matchList):
        """Checks if any if the match list items is in the selection"""
        return self.countInSelected(matchList) > 0

    def isDocstringInSelection(self):
        """True if a docstring item in the selection"""
        for item in self.selectedItems():
            if item.isDocstring():
                return True
        return False

    def countComments(self):
        """Count comments in selection"""
        count = 0
        for item in self.selectedItems():
            if item.isComment():
                count += 1
        return count

    def isCommentInSelection(self):
        """True if a comment item in the selection"""
        return self.countComments() > 0

    def countItemsWithCML(self, cmlType):
        """Counts items with have a certain type of a CML comment"""
        count = 0
        for item in self.selectedItems():
            if self.__findCMLinItem(item, cmlType) is not None:
                count += 1
        return count

    def __findCMLinItem(self, item, cmlType):
        """Finds a related CML item"""
        if item.isComment():
            # Doc links are comments so they are skipped here
            return None
        if item.isDocstring():
            # Side comments for docstrings? Nonesense! So they are ignored
            # even if they are collected
            cml = CMLVersion.find(item.ref.docstring.leadingCMLComments, cmlType)
            if cml is not None:
                return cml

        if hasattr(item.ref, 'leadingCMLComments'):
            cml = CMLVersion.find(item.ref.leadingCMLComments, cmlType)
            if cml is not None:
                return cml
        if hasattr(item.ref, 'sideCMLComments'):
            cml = CMLVersion.find(item.ref.sideCMLComments, cmlType)
            if cml is not None:
                return cml
        return None

    def countGroups(self):
        """Counts empty, close and open groups"""
        emptyCount = 0
        closeCount = 0
        openCount = 0
        for item in self.selectedItems():
            if item.kind == CellElement.EMPTY_GROUP:
                emptyCount += 1
            elif item.kind == CellElement.COLLAPSED_GROUP:
                closeCount += 1
            elif item.kind == CellElement.OPENED_GROUP_BEGIN:
                openCount += 1
        return emptyCount, closeCount, openCount

    def countGroupsWithCustomColors(self):
        """Counts the groups with any color defined"""
        emptyCount = 0
        closeCount = 0
        openCount = 0
        for item in self.selectedItems():
            if item.kind in [CellElement.EMPTY_GROUP,
                             CellElement.COLLAPSED_GROUP,
                             CellElement.OPENED_GROUP_BEGIN]:
                if item.groupBeginCMLRef.bgColor is not None or \
                   item.groupBeginCMLRef.fgColor is not None or \
                   item.groupBeginCMLRef.border is not None:
                    if item.kind == CellElement.EMPTY_GROUP:
                        emptyCount += 1
                    elif item.kind == CellElement.COLLAPSED_GROUP:
                        closeCount += 1
                    else:
                        openCount += 1
        return emptyCount, closeCount, openCount

    def countDocWithCustomColors(self):
        count = 0
        for item in self.selectedItems():
            if item.isCMLDoc():
                if item.cmlRef.bgColor is not None or \
                   item.cmlRef.fgColor is not None or \
                   item.cmlRef.border is not None:
                    count += 1
        return count

    def sortSelectedReverse(self):
        """Sorts the selected items in reverse order"""
        result = []
        for item in self.selectedItems():
            itemBegin = item.getAbsPosRange()[0]
            for index in range(len(result)):
                if itemBegin > result[index].getAbsPosRange()[0]:
                    result.insert(index, item)
                    break
            else:
                result.append(item)
        return result

    def sortSelected(self, selected):
        """Sorts the selected items in direct order"""
        result = []
        for item in selected:
            itemBegin = item.getAbsPosRange()[0]
            for index in range(len(result)):
                if itemBegin < result[index].getAbsPosRange()[0]:
                    result.insert(index, item)
                    break
            else:
                result.append(item)
        return result

    def __canBeGrouped(self):
        """True if the selected items can be grouped"""
        # Cannot import it at the top...
        from .flowuiwidget import SMART_ZOOM_ALL, SMART_ZOOM_NO_CONTENT

        if Settings()['smartZoom'] not in [SMART_ZOOM_ALL,
                                           SMART_ZOOM_NO_CONTENT]:
            return False
        if self.__areAllSelectedComments():
            return False
        if self.__areScopeDocstringOrCommentSelected():
            return False
        if self.__isModuleSelected():
            return False

        # Extend the selection with all the selected items comments
        selected = self.__extendSelectionForGrouping()

        if self.__areLoneCommentsSelected(selected):
            return False

        if self.__areIncompleteScopeSelected(selected):
            return False

        scopeCoveredRegions = self.__getSelectedScopeRegions(selected)

        # The __areIfFullySelected() also updates the regions with
        # fully selected if regions
        if not self.__areIfFullySelected(selected, scopeCoveredRegions):
            return False

        selected = self.sortSelected(selected)
        begin = selected[0].getAbsPosRange()[0]
        end = selected[-1].getAbsPosRange()[1]

        if not self.__isSelectionContinuous(selected, scopeCoveredRegions,
                                            begin, end):
            return False

        if self.__moreThanOneIfBranchSelected(selected, scopeCoveredRegions):
            return False
        return True

    def __areAllSelectedComments(self):
        """True if all selected items are comments"""
        for item in self.selectedItems():
            if not item.isComment():
                return False
        return True

    def __areScopeDocstringOrCommentSelected(self):
        for item in self.selectedItems():
            if item.scopedItem():
                if item.subKind in [ScopeCellElement.SIDE_COMMENT,
                                    ScopeCellElement.DOCSTRING]:
                    return True
        return False

    def __isModuleSelected(self):
        """True if the whole module is selected"""
        for item in self.selectedItems():
            if item.kind == CellElement.FILE_SCOPE:
                return True
        return False

    def __areIncompleteScopeSelected(self, selected):
        """True if an incomplete scope selected"""
        for item in selected:
            if item.kind in [CellElement.FOR_SCOPE,
                             CellElement.WHILE_SCOPE]:
                if item.ref.elsePart:
                    for relatedItem in self.findItemsForRef(item.ref.elsePart):
                        if relatedItem not in selected:
                            return True
            elif item.kind in [CellElement.TRY_SCOPE]:
                # It could be that the exception blocks are hidden, so there
                # will be exactly one more item instead of many and that item
                # will have a ref which matches the try scope.
                exceptPartCount = 0
                for exceptPart in item.ref.exceptParts:
                    for relatedItem in self.findItemsForRef(exceptPart):
                        exceptPartCount += 1
                        if relatedItem not in selected:
                            return True
                if exceptPartCount == 0:
                    # here: no except blocks on the diagram, they are collapsed
                    tryItems = self.findItemsForRef(item.ref)
                    for tryItem in tryItems:
                        if tryItem.kind == CellElement.EXCEPT_MINIMIZED:
                            if not tryItem.isSelected():
                                return True
                            break
                    else:
                        # The minimized except is not selected
                        return True
                if item.ref.elsePart:
                    for relatedItem in self.findItemsForRef(item.ref.elsePart):
                        if relatedItem not in selected:
                            return True
                if item.ref.finallyPart:
                    for relatedItem in self.findItemsForRef(item.ref.finallyPart):
                        if relatedItem not in selected:
                            return True
            elif item.kind in [CellElement.ELSE_SCOPE,
                               CellElement.EXCEPT_SCOPE,
                               CellElement.FINALLY_SCOPE]:
                for relatedItem in self.findItemsForRef(item.leaderRef):
                    if relatedItem not in selected:
                        return True
            elif item.kind == CellElement.EXCEPT_MINIMIZED:
                # here: no except blocks on the diagram, they are collapsed
                tryItems = self.findItemsForRef(item.ref)
                for tryItem in tryItems:
                    if tryItem.kind == CellElement.TRY_SCOPE:
                        if tryItem.subKind == ScopeCellElement.TOP_LEFT:
                            if not tryItem.isSelected():
                                return True
                            break
                else:
                    # The try is not selected
                    return True
        return False

    def __extendSelectionForGrouping(self):
        """Extends the selection with the leading and side comments"""
        boundComments = []
        selected = self.selectedItems()
        for item in selected:
            if not item.isComment() and not self.isOpenGroupItem(item):
                for relatedItem in self.findItemsForRef(item.ref):
                    if relatedItem not in selected:
                        boundComments.append(relatedItem)
        return selected + boundComments

    def __areLoneCommentsSelected(self, selected):
        """True if there are comments selected which have no main item selected"""
        for item in selected:
            if item.isComment():
                if item.kind in [CellElement.SIDE_COMMENT,
                                 CellElement.LEADING_COMMENT,
                                 CellElement.ABOVE_COMMENT]:
                    for relatedItem in self.findItemsForRef(item.ref):
                        if relatedItem not in selected:
                            return True
        return False

    def __getLineRange(self, selected):
        first = selected[0]
        last = selected[-1]

        if first.kind == CellElement.OPENED_GROUP_BEGIN:
            firstLine = first.groupBeginCMLRef.ref.parts[0].beginLine
            pos = first.groupBeginCMLRef.ref.parts[0].beginPos
        else:
            firstLine = first.getLineRange()[0]
            pos = first.ref.beginPos

        if last.scopedItem():
            lastLine = last.ref.endLine
        elif last.kind == CellElement.OPENED_GROUP_BEGIN:
            lastLine = last.groupEndCMLRef.ref.parts[-1].endLine
        else:
            lastLine = last.getLineRange()[1]
        return firstLine, lastLine, pos

    def __getSelectedScopeRegions(self, selected):
        """Provides the regions of the selected scope items"""
        coveredRegions = []
        for item in selected:
            if item.scopedItem():
                if item.subKind in [ScopeCellElement.TOP_LEFT]:
                    if item.ref.leadingComment:
                        coveredRegions.append((item.ref.leadingComment.begin,
                                               item.ref.end))
                    else:
                        coveredRegions.append((item.ref.begin, item.ref.end))
            elif item.kind == CellElement.OPENED_GROUP_BEGIN:
                coveredRegions.append(item.getAbsPosRange())
        return coveredRegions

    def __areIfFullySelected(self, selected, regions):
        """Checks if selected IFs are fully selected"""
        for item in selected:
            if item.kind == CellElement.IF:
                ifBegin = item.ref.begin
                ifEnd = item.ref.end
                for item in self.items():
                    if item.isProxyItem():
                        continue
                    if item.scopedItem():
                        if item.subKind not in [ScopeCellElement.TOP_LEFT,
                                                ScopeCellElement.DOCSTRING,
                                                ScopeCellElement.SIDE_COMMENT]:
                            continue
                    if item in selected:
                        continue
                    itemRange = item.getAbsPosRange()
                    if self.isInRegion(itemRange[0], itemRange[1], regions):
                        continue
                    if itemRange[0] > ifBegin and itemRange[0] < ifEnd:
                        return False
                    if itemRange[1] > ifBegin and itemRange[1] < ifEnd:
                        return False
                regions.append([ifBegin, ifEnd])
        return True

    @staticmethod
    def isInRegion(start, finish, regions):
        for region in regions:
            if start >= region[0] and finish <= region[1]:
                return True
        return False

    def __isSelectionContinuous(self, selected, regions, begin, end):
        """Checks if the selection is continuous"""
        for item in self.items():
            if item.isProxyItem():
                continue
            if item.scopedItem():
                if item.subKind not in [ScopeCellElement.TOP_LEFT,
                                        ScopeCellElement.DOCSTRING,
                                        ScopeCellElement.SIDE_COMMENT]:
                    continue
            if item in selected:
                continue
            itemRange = item.getAbsPosRange()
            if self.isInRegion(itemRange[0], itemRange[1], regions):
                continue

            # It is important to keep < and > instead of <= and >=
            # This is because the scopes start with the first statement
            if itemRange[0] > begin and itemRange[0] < end:
                return False
            if itemRange[1] > begin and itemRange[1] < end:
                return False
        return True

    def __moreThanOneIfBranchSelected(self, selected, regions):
        """Checks that the continuous selected items belong to more than one
        not selected IF statements
        """
        ifRef = None
        for item in selected:
            if item.kind != CellElement.IF:
                itemRange = item.getAbsPosRange()
                if item.kind != CellElement.OPENED_GROUP_BEGIN:
                    if self.isInRegion(itemRange[0], itemRange[1], regions):
                        # Here: an item is in a selected scope item, in a selected
                        #       open group or in a fully selected if
                        continue
                # Test if an item belongs to an if statement branch
                if item.kind in [CellElement.OPENED_GROUP_BEGIN,
                                 CellElement.EMPTY_GROUP,
                                 CellElement.COLLAPSED_GROUP]:
                    branchId = item.groupBeginCMLRef.ref.getParentIfID()
                elif item.kind in [CellElement.INDEPENDENT_DOC,
                                   CellElement.LEADING_DOC,
                                   CellElement.ABOVE_DOC]:
                    branchId = item.cmlRef.ref.getParentIfID()
                else:
                    branchId = item.ref.getParentIfID()
                if branchId is not None:
                    if ifRef is None:
                        ifRef = branchId
                    else:
                        if branchId != ifRef:
                            # Selected items belong to more than one branch
                            return True
        return False