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()
        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()

    def terminateMenus(self):
        """Called when the tab is closed"""
        self.encodingReloadMenu.triggered.disconnect(
            self.__onReloadWithEncoding)
        self.encodingReloadMenu.deleteLater()
        self.encodingReloadActGrp.deleteLater()
        self.encodingWriteMenu.triggered.disconnect(self.__onReadWriteEncoding)
        self.encodingWriteMenu.deleteLater()
        self.encodingWriteActGrp.deleteLater()
        self.toolsMenu.deleteLater()
        self.disasmMenu.deleteLater()
        self.diagramsMenu.deleteLater()
        self._menu.deleteLater()

        mainWindow = GlobalData().mainWindow
        editorsManager = mainWindow.editorsManagerWidget.editorsManager
        editorsManager.sigPluginContextMenuAdded.disconnect(
            self._onPluginMenuAdded)
        editorsManager.sigPluginContextMenuRemoved.disconnect(
            self._onPluginMenuRemoved)

    @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)
Exemplo n.º 2
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')