src/eric7/Project/ProjectSourcesBrowser.py

branch
eric7-maintenance
changeset 9264
18a7312cfdb3
parent 9192
a763d57e23bc
parent 9246
f3120bfe82fb
child 9442
906485dcd210
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/Project/ProjectSourcesBrowser.py	Sun Jul 24 11:29:56 2022 +0200
@@ -0,0 +1,1305 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2002 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class used to display the Sources part of the project.
+"""
+
+import os
+import contextlib
+
+from PyQt6.QtCore import pyqtSignal
+from PyQt6.QtWidgets import QDialog, QInputDialog, QMenu
+
+from EricWidgets import EricMessageBox
+from EricWidgets.EricApplication import ericApp
+
+from UI.BrowserModel import (
+    BrowserFileItem,
+    BrowserClassItem,
+    BrowserMethodItem,
+    BrowserClassAttributeItem,
+    BrowserImportItem,
+)
+
+from .ProjectBrowserModel import (
+    ProjectBrowserFileItem,
+    ProjectBrowserSimpleDirectoryItem,
+    ProjectBrowserDirectoryItem,
+    ProjectBrowserSourceType,
+)
+from .ProjectBaseBrowser import ProjectBaseBrowser
+
+import Utilities
+import UI.PixmapCache
+
+from CodeFormatting.BlackFormattingAction import BlackFormattingAction
+
+
+class ProjectSourcesBrowser(ProjectBaseBrowser):
+    """
+    A class used to display the Sources part of the project.
+
+    @signal showMenu(str, QMenu) emitted when a menu is about to be shown.
+        The name of the menu and a reference to the menu are given.
+    """
+
+    showMenu = pyqtSignal(str, QMenu)
+
+    def __init__(self, project, parent=None):
+        """
+        Constructor
+
+        @param project reference to the project object
+        @param parent parent widget of this browser (QWidget)
+        """
+        ProjectBaseBrowser.__init__(self, project, ProjectBrowserSourceType, parent)
+
+        self.selectedItemsFilter = [
+            ProjectBrowserFileItem,
+            ProjectBrowserSimpleDirectoryItem,
+        ]
+
+        self.setWindowTitle(self.tr("Sources"))
+
+        self.setWhatsThis(
+            self.tr(
+                """<b>Project Sources Browser</b>"""
+                """<p>This allows to easily see all sources contained in the"""
+                """ current project. Several actions can be executed via the"""
+                """ context menu.</p>"""
+            )
+        )
+
+        project.prepareRepopulateItem.connect(self._prepareRepopulateItem)
+        project.completeRepopulateItem.connect(self._completeRepopulateItem)
+
+        self.codemetrics = None
+        self.codecoverage = None
+        self.profiledata = None
+        self.classDiagram = None
+        self.importsDiagram = None
+        self.packageDiagram = None
+        self.applicationDiagram = None
+        self.loadedDiagram = None
+
+    def __closeAllWindows(self):
+        """
+        Private method to close all project related windows.
+        """
+        self.codemetrics and self.codemetrics.close()
+        self.codecoverage and self.codecoverage.close()
+        self.profiledata and self.profiledata.close()
+        self.classDiagram and self.classDiagram.close()
+        self.importsDiagram and self.importsDiagram.close()
+        self.packageDiagram and self.packageDiagram.close()
+        self.applicationDiagram and self.applicationDiagram.close()
+        self.loadedDiagram and self.loadedDiagram.close()
+
+    def _projectClosed(self):
+        """
+        Protected slot to handle the projectClosed signal.
+        """
+        self.__closeAllWindows()
+        ProjectBaseBrowser._projectClosed(self)
+
+    def _createPopupMenus(self):
+        """
+        Protected overloaded method to generate the popup menu.
+        """
+        ProjectBaseBrowser._createPopupMenus(self)
+        self.sourceMenuActions = {}
+
+        if self.project.isPythonProject():
+            self.__createPythonPopupMenus()
+        elif self.project.isRubyProject():
+            self.__createRubyPopupMenus()
+        elif self.project.isJavaScriptProject():
+            self.__createJavaScriptPopupMenus()
+        else:
+            # assign generic source menu
+            self.mainMenu = self.sourceMenu
+
+    def __createPythonPopupMenus(self):
+        """
+        Private method to generate the popup menus for a Python project.
+        """
+        self.checksMenu = QMenu(self.tr("Check"))
+        self.checksMenu.aboutToShow.connect(self.__showContextMenuCheck)
+
+        self.formattingMenu = QMenu(self.tr("Code Formatting"))
+        self.formattingMenu.addAction(
+            self.tr("Format Code"),
+            lambda: self.__performFormatWithBlack(BlackFormattingAction.Format),
+        )
+        self.formattingMenu.addAction(
+            self.tr("Check Formatting"),
+            lambda: self.__performFormatWithBlack(BlackFormattingAction.Check),
+        )
+        self.formattingMenu.addAction(
+            self.tr("Formatting Diff"),
+            lambda: self.__performFormatWithBlack(BlackFormattingAction.Diff),
+        )
+        self.formattingMenu.aboutToShow.connect(self.__showContextMenuFormatting)
+
+        self.menuShow = QMenu(self.tr("Show"))
+        self.menuShow.addAction(self.tr("Code metrics..."), self.__showCodeMetrics)
+        self.coverageMenuAction = self.menuShow.addAction(
+            self.tr("Code coverage..."), self.__showCodeCoverage
+        )
+        self.profileMenuAction = self.menuShow.addAction(
+            self.tr("Profile data..."), self.__showProfileData
+        )
+        self.menuShow.aboutToShow.connect(self.__showContextMenuShow)
+
+        self.graphicsMenu = QMenu(self.tr("Diagrams"))
+        self.classDiagramAction = self.graphicsMenu.addAction(
+            self.tr("Class Diagram..."), self.__showClassDiagram
+        )
+        self.graphicsMenu.addAction(
+            self.tr("Package Diagram..."), self.__showPackageDiagram
+        )
+        self.importsDiagramAction = self.graphicsMenu.addAction(
+            self.tr("Imports Diagram..."), self.__showImportsDiagram
+        )
+        self.graphicsMenu.addAction(
+            self.tr("Application Diagram..."), self.__showApplicationDiagram
+        )
+        self.graphicsMenu.addSeparator()
+        self.graphicsMenu.addAction(
+            UI.PixmapCache.getIcon("open"),
+            self.tr("Load Diagram..."),
+            self.__loadDiagram,
+        )
+        self.graphicsMenu.aboutToShow.connect(self.__showContextMenuGraphics)
+
+        self.__startMenu = QMenu(self.tr("Start"), self)
+        self.__startMenu.addAction(
+            UI.PixmapCache.getIcon("runScript"),
+            self.tr("Run Script..."),
+            self.__contextMenuRunScript,
+        )
+        self.__startMenu.addAction(
+            UI.PixmapCache.getIcon("debugScript"),
+            self.tr("Debug Script..."),
+            self.__contextMenuDebugScript,
+        )
+        self.__startMenu.addAction(
+            UI.PixmapCache.getIcon("profileScript"),
+            self.tr("Profile Script..."),
+            self.__contextMenuProfileScript,
+        )
+        self.__startMenu.addAction(
+            UI.PixmapCache.getIcon("coverageScript"),
+            self.tr("Coverage run of Script..."),
+            self.__contextMenuCoverageScript,
+        )
+
+        self.testingAction = self.sourceMenu.addAction(
+            self.tr("Run tests..."), self.handleTesting
+        )
+        self.sourceMenu.addSeparator()
+        act = self.sourceMenu.addAction(self.tr("Rename file"), self._renameFile)
+        self.menuActions.append(act)
+        act = self.sourceMenu.addAction(
+            self.tr("Remove from project"), self._removeFile
+        )
+        self.menuActions.append(act)
+        act = self.sourceMenu.addAction(self.tr("Delete"), self.__deleteFile)
+        self.menuActions.append(act)
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(self.tr("New package..."), self.__addNewPackage)
+        self.sourceMenu.addAction(self.tr("Add source files..."), self.__addSourceFiles)
+        self.sourceMenu.addAction(
+            self.tr("Add source directory..."), self.__addSourceDirectory
+        )
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addMenu(self.graphicsMenu)
+        self.sourceMenu.addMenu(self.checksMenu)
+        self.sourceMenu.addMenu(self.formattingMenu)
+        self.sourceMenuActions["Show"] = self.sourceMenu.addMenu(self.menuShow)
+        self.sourceMenu.addSeparator()
+        self.__startAct = self.sourceMenu.addMenu(self.__startMenu)
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(
+            self.tr("Copy Path to Clipboard"), self._copyToClipboard
+        )
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(
+            self.tr("Expand all directories"), self._expandAllDirs
+        )
+        self.sourceMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.menu.addSeparator()
+        self.menu.addAction(self.tr("New package..."), self.__addNewPackage)
+        self.menu.addAction(self.tr("Add source files..."), self.__addSourceFiles)
+        self.menu.addAction(
+            self.tr("Add source directory..."), self.__addSourceDirectory
+        )
+        self.menu.addSeparator()
+        self.menu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.menu.addAction(self.tr("Collapse all directories"), self._collapseAllDirs)
+        self.menu.addSeparator()
+        self.menu.addAction(self.tr("Configure..."), self._configure)
+
+        # create the attribute menu
+        self.gotoMenu = QMenu(self.tr("Goto"), self)
+        self.gotoMenu.aboutToShow.connect(self._showGotoMenu)
+        self.gotoMenu.triggered.connect(self._gotoAttribute)
+
+        self.attributeMenu = QMenu(self)
+        self.attributeMenu.addMenu(self.gotoMenu)
+        self.attributeMenu.addSeparator()
+        self.attributeMenu.addAction(self.tr("New package..."), self.__addNewPackage)
+        self.attributeMenu.addAction(
+            self.tr("Add source files..."), self.project.addSourceFiles
+        )
+        self.attributeMenu.addAction(
+            self.tr("Add source directory..."), self.project.addSourceDir
+        )
+        self.attributeMenu.addSeparator()
+        self.attributeMenu.addAction(
+            self.tr("Expand all directories"), self._expandAllDirs
+        )
+        self.attributeMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.attributeMenu.addSeparator()
+        self.attributeMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.backMenu = QMenu(self)
+        self.backMenu.addAction(self.tr("New package..."), self.__addNewPackage)
+        self.backMenu.addAction(
+            self.tr("Add source files..."), self.project.addSourceFiles
+        )
+        self.backMenu.addAction(
+            self.tr("Add source directory..."), self.project.addSourceDir
+        )
+        self.backMenu.addSeparator()
+        self.backMenu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.backMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.backMenu.addSeparator()
+        self.backMenu.addAction(self.tr("Configure..."), self._configure)
+        self.backMenu.setEnabled(False)
+
+        self.multiMenu.addSeparator()
+        act = self.multiMenu.addAction(self.tr("Remove from project"), self._removeFile)
+        self.multiMenuActions.append(act)
+        act = self.multiMenu.addAction(self.tr("Delete"), self.__deleteFile)
+        self.multiMenuActions.append(act)
+        self.multiMenu.addSeparator()
+        self.multiMenu.addMenu(self.checksMenu)
+        self.multiMenu.addMenu(self.formattingMenu)
+        self.multiMenu.addSeparator()
+        self.multiMenu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.multiMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.multiMenu.addSeparator()
+        self.multiMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.dirMenu = QMenu(self)
+        act = self.dirMenu.addAction(self.tr("Remove from project"), self._removeDir)
+        self.dirMenuActions.append(act)
+        act = self.dirMenu.addAction(self.tr("Delete"), self._deleteDirectory)
+        self.dirMenuActions.append(act)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr("New package..."), self.__addNewPackage)
+        self.dirMenu.addAction(self.tr("Add source files..."), self.__addSourceFiles)
+        self.dirMenu.addAction(
+            self.tr("Add source directory..."), self.__addSourceDirectory
+        )
+        self.dirMenu.addSeparator()
+        act = self.dirMenu.addMenu(self.graphicsMenu)
+        self.dirMenu.addMenu(self.checksMenu)
+        self.dirMenu.addMenu(self.formattingMenu)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr("Copy Path to Clipboard"), self._copyToClipboard)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.dirMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.dirMultiMenu = QMenu(self)
+        self.dirMultiMenu.addAction(
+            self.tr("Expand all directories"), self._expandAllDirs
+        )
+        self.dirMultiMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.dirMultiMenu.addSeparator()
+        self.dirMultiMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.sourceMenu.aboutToShow.connect(self.__showContextMenu)
+        self.multiMenu.aboutToShow.connect(self.__showContextMenuMulti)
+        self.dirMenu.aboutToShow.connect(self.__showContextMenuDir)
+        self.dirMultiMenu.aboutToShow.connect(self.__showContextMenuDirMulti)
+        self.backMenu.aboutToShow.connect(self.__showContextMenuBack)
+        self.mainMenu = self.sourceMenu
+
+    def __createRubyPopupMenus(self):
+        """
+        Private method to generate the popup menus for a Ruby project.
+        """
+        self.graphicsMenu = QMenu(self.tr("Diagrams"))
+        self.classDiagramAction = self.graphicsMenu.addAction(
+            self.tr("Class Diagram..."), self.__showClassDiagram
+        )
+        self.graphicsMenu.addAction(
+            self.tr("Package Diagram..."), self.__showPackageDiagram
+        )
+        self.graphicsMenu.addAction(
+            self.tr("Application Diagram..."), self.__showApplicationDiagram
+        )
+        self.graphicsMenu.addSeparator()
+        self.graphicsMenu.addAction(
+            UI.PixmapCache.getIcon("fileOpen"),
+            self.tr("Load Diagram..."),
+            self.__loadDiagram,
+        )
+
+        self.sourceMenu.addSeparator()
+        act = self.sourceMenu.addAction(self.tr("Rename file"), self._renameFile)
+        self.menuActions.append(act)
+        act = self.sourceMenu.addAction(
+            self.tr("Remove from project"), self._removeFile
+        )
+        self.menuActions.append(act)
+        act = self.sourceMenu.addAction(self.tr("Delete"), self.__deleteFile)
+        self.menuActions.append(act)
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(self.tr("Add source files..."), self.__addSourceFiles)
+        self.sourceMenu.addAction(
+            self.tr("Add source directory..."), self.__addSourceDirectory
+        )
+        self.sourceMenu.addSeparator()
+        act = self.sourceMenu.addMenu(self.graphicsMenu)
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(
+            self.tr("Expand all directories"), self._expandAllDirs
+        )
+        self.sourceMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.menu.addSeparator()
+        self.menu.addAction(self.tr("Add source files..."), self.__addSourceFiles)
+        self.menu.addAction(
+            self.tr("Add source directory..."), self.__addSourceDirectory
+        )
+        self.menu.addSeparator()
+        self.menu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.menu.addAction(self.tr("Collapse all directories"), self._collapseAllDirs)
+        self.menu.addSeparator()
+        self.menu.addAction(self.tr("Configure..."), self._configure)
+
+        # create the attribute menu
+        self.gotoMenu = QMenu(self.tr("Goto"), self)
+        self.gotoMenu.aboutToShow.connect(self._showGotoMenu)
+        self.gotoMenu.triggered.connect(self._gotoAttribute)
+
+        self.attributeMenu = QMenu(self)
+        self.attributeMenu.addMenu(self.gotoMenu)
+        self.attributeMenu.addSeparator()
+        self.attributeMenu.addAction(
+            self.tr("Add source files..."), self.project.addSourceFiles
+        )
+        self.attributeMenu.addAction(
+            self.tr("Add source directory..."), self.project.addSourceDir
+        )
+        self.attributeMenu.addSeparator()
+        self.attributeMenu.addAction(
+            self.tr("Expand all directories"), self._expandAllDirs
+        )
+        self.attributeMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.attributeMenu.addSeparator()
+        self.attributeMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.backMenu = QMenu(self)
+        self.backMenu.addAction(
+            self.tr("Add source files..."), self.project.addSourceFiles
+        )
+        self.backMenu.addAction(
+            self.tr("Add source directory..."), self.project.addSourceDir
+        )
+        self.backMenu.addSeparator()
+        self.backMenu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.backMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.backMenu.setEnabled(False)
+        self.backMenu.addSeparator()
+        self.backMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.multiMenu.addSeparator()
+        act = self.multiMenu.addAction(self.tr("Remove from project"), self._removeFile)
+        self.multiMenuActions.append(act)
+        act = self.multiMenu.addAction(self.tr("Delete"), self.__deleteFile)
+        self.multiMenuActions.append(act)
+        self.multiMenu.addSeparator()
+        self.multiMenu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.multiMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.multiMenu.addSeparator()
+        self.multiMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.dirMenu = QMenu(self)
+        act = self.dirMenu.addAction(self.tr("Remove from project"), self._removeDir)
+        self.dirMenuActions.append(act)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr("Add source files..."), self.__addSourceFiles)
+        self.dirMenu.addAction(
+            self.tr("Add source directory..."), self.__addSourceDirectory
+        )
+        self.dirMenu.addSeparator()
+        act = self.dirMenu.addMenu(self.graphicsMenu)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.dirMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.dirMultiMenu = QMenu(self)
+        self.dirMultiMenu.addAction(
+            self.tr("Expand all directories"), self._expandAllDirs
+        )
+        self.dirMultiMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.dirMultiMenu.addSeparator()
+        self.dirMultiMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.sourceMenu.aboutToShow.connect(self.__showContextMenu)
+        self.multiMenu.aboutToShow.connect(self.__showContextMenuMulti)
+        self.dirMenu.aboutToShow.connect(self.__showContextMenuDir)
+        self.dirMultiMenu.aboutToShow.connect(self.__showContextMenuDirMulti)
+        self.backMenu.aboutToShow.connect(self.__showContextMenuBack)
+        self.mainMenu = self.sourceMenu
+
+    def __createJavaScriptPopupMenus(self):
+        """
+        Private method to generate the popup menus for a Python project.
+        """
+        self.checksMenu = QMenu(self.tr("Check"))
+        self.checksMenu.aboutToShow.connect(self.__showContextMenuCheck)
+
+        self.sourceMenu.addSeparator()
+        act = self.sourceMenu.addAction(self.tr("Rename file"), self._renameFile)
+        self.menuActions.append(act)
+        act = self.sourceMenu.addAction(
+            self.tr("Remove from project"), self._removeFile
+        )
+        self.menuActions.append(act)
+        act = self.sourceMenu.addAction(self.tr("Delete"), self.__deleteFile)
+        self.menuActions.append(act)
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(self.tr("Add source files..."), self.__addSourceFiles)
+        self.sourceMenu.addAction(
+            self.tr("Add source directory..."), self.__addSourceDirectory
+        )
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addMenu(self.checksMenu)
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(
+            self.tr("Copy Path to Clipboard"), self._copyToClipboard
+        )
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(
+            self.tr("Expand all directories"), self._expandAllDirs
+        )
+        self.sourceMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.menu.addSeparator()
+        self.menu.addAction(self.tr("Add source files..."), self.__addSourceFiles)
+        self.menu.addAction(
+            self.tr("Add source directory..."), self.__addSourceDirectory
+        )
+        self.menu.addSeparator()
+        self.menu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.menu.addAction(self.tr("Collapse all directories"), self._collapseAllDirs)
+        self.menu.addSeparator()
+        self.menu.addAction(self.tr("Configure..."), self._configure)
+
+        # create the attribute menu
+        self.gotoMenu = QMenu(self.tr("Goto"), self)
+        self.gotoMenu.aboutToShow.connect(self._showGotoMenu)
+        self.gotoMenu.triggered.connect(self._gotoAttribute)
+
+        self.attributeMenu = QMenu(self)
+        self.attributeMenu.addMenu(self.gotoMenu)
+        self.attributeMenu.addSeparator()
+        self.attributeMenu.addAction(
+            self.tr("Add source files..."), self.project.addSourceFiles
+        )
+        self.attributeMenu.addAction(
+            self.tr("Add source directory..."), self.project.addSourceDir
+        )
+        self.attributeMenu.addSeparator()
+        self.attributeMenu.addAction(
+            self.tr("Expand all directories"), self._expandAllDirs
+        )
+        self.attributeMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.attributeMenu.addSeparator()
+        self.attributeMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.backMenu = QMenu(self)
+        self.backMenu.addAction(
+            self.tr("Add source files..."), self.project.addSourceFiles
+        )
+        self.backMenu.addAction(
+            self.tr("Add source directory..."), self.project.addSourceDir
+        )
+        self.backMenu.addSeparator()
+        self.backMenu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.backMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.backMenu.addSeparator()
+        self.backMenu.addAction(self.tr("Configure..."), self._configure)
+        self.backMenu.setEnabled(False)
+
+        self.multiMenu.addSeparator()
+        act = self.multiMenu.addAction(self.tr("Remove from project"), self._removeFile)
+        self.multiMenuActions.append(act)
+        act = self.multiMenu.addAction(self.tr("Delete"), self.__deleteFile)
+        self.multiMenuActions.append(act)
+        self.multiMenu.addSeparator()
+        self.multiMenu.addMenu(self.checksMenu)
+        self.multiMenu.addSeparator()
+        self.multiMenu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.multiMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.multiMenu.addSeparator()
+        self.multiMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.dirMenu = QMenu(self)
+        act = self.dirMenu.addAction(self.tr("Remove from project"), self._removeDir)
+        self.dirMenuActions.append(act)
+        act = self.dirMenu.addAction(self.tr("Delete"), self._deleteDirectory)
+        self.dirMenuActions.append(act)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr("Add source files..."), self.__addSourceFiles)
+        self.dirMenu.addAction(
+            self.tr("Add source directory..."), self.__addSourceDirectory
+        )
+        self.dirMenu.addSeparator()
+        self.dirMenu.addMenu(self.checksMenu)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr("Copy Path to Clipboard"), self._copyToClipboard)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.dirMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.dirMultiMenu = QMenu(self)
+        self.dirMultiMenu.addAction(
+            self.tr("Expand all directories"), self._expandAllDirs
+        )
+        self.dirMultiMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.dirMultiMenu.addSeparator()
+        self.dirMultiMenu.addAction(self.tr("Configure..."), self._configure)
+
+        self.sourceMenu.aboutToShow.connect(self.__showContextMenu)
+        self.multiMenu.aboutToShow.connect(self.__showContextMenuMulti)
+        self.dirMenu.aboutToShow.connect(self.__showContextMenuDir)
+        self.dirMultiMenu.aboutToShow.connect(self.__showContextMenuDirMulti)
+        self.backMenu.aboutToShow.connect(self.__showContextMenuBack)
+        self.mainMenu = self.sourceMenu
+
+    def _contextMenuRequested(self, coord):
+        """
+        Protected slot to show the context menu.
+
+        @param coord the position of the mouse pointer (QPoint)
+        """
+        if not self.project.isOpen():
+            return
+
+        with contextlib.suppress(Exception):
+            categories = self.getSelectedItemsCountCategorized(
+                [
+                    ProjectBrowserFileItem,
+                    BrowserClassItem,
+                    BrowserMethodItem,
+                    ProjectBrowserSimpleDirectoryItem,
+                    BrowserClassAttributeItem,
+                    BrowserImportItem,
+                ]
+            )
+            cnt = categories["sum"]
+            if cnt <= 1:
+                index = self.indexAt(coord)
+                if index.isValid():
+                    self._selectSingleItem(index)
+                    categories = self.getSelectedItemsCountCategorized(
+                        [
+                            ProjectBrowserFileItem,
+                            BrowserClassItem,
+                            BrowserMethodItem,
+                            ProjectBrowserSimpleDirectoryItem,
+                            BrowserClassAttributeItem,
+                            BrowserImportItem,
+                        ]
+                    )
+                    cnt = categories["sum"]
+
+            bfcnt = categories[str(ProjectBrowserFileItem)]
+            cmcnt = (
+                categories[str(BrowserClassItem)]
+                + categories[str(BrowserMethodItem)]
+                + categories[str(BrowserClassAttributeItem)]
+                + categories[str(BrowserImportItem)]
+            )
+            sdcnt = categories[str(ProjectBrowserSimpleDirectoryItem)]
+            if cnt > 1 and cnt == bfcnt:
+                self.multiMenu.popup(self.mapToGlobal(coord))
+            elif cnt > 1 and cnt == sdcnt:
+                self.dirMultiMenu.popup(self.mapToGlobal(coord))
+            else:
+                index = self.indexAt(coord)
+                if cnt == 1 and index.isValid():
+                    if bfcnt == 1 or cmcnt == 1:
+                        itm = self.model().item(index)
+                        if isinstance(itm, ProjectBrowserFileItem):
+                            fn = itm.fileName()
+                            if self.project.isPythonProject():
+                                if fn.endswith(".ptl"):
+                                    for act in self.sourceMenuActions.values():
+                                        act.setEnabled(False)
+                                    self.classDiagramAction.setEnabled(True)
+                                    self.importsDiagramAction.setEnabled(True)
+                                    self.testingAction.setEnabled(False)
+                                    self.checksMenu.menuAction().setEnabled(False)
+                                elif fn.endswith(".rb"):
+                                    # entry for mixed mode programs
+                                    for act in self.sourceMenuActions.values():
+                                        act.setEnabled(False)
+                                    self.classDiagramAction.setEnabled(True)
+                                    self.importsDiagramAction.setEnabled(False)
+                                    self.testingAction.setEnabled(False)
+                                    self.checksMenu.menuAction().setEnabled(False)
+                                elif fn.endswith(".js"):
+                                    # entry for mixed mode programs
+                                    for act in self.sourceMenuActions.values():
+                                        act.setEnabled(False)
+                                    self.testingAction.setEnabled(False)
+                                    self.checksMenu.menuAction().setEnabled(False)
+                                    self.graphicsMenu.menuAction().setEnabled(False)
+                                else:
+                                    # assume the source file is a Python file
+                                    for act in self.sourceMenuActions.values():
+                                        act.setEnabled(True)
+                                    self.classDiagramAction.setEnabled(True)
+                                    self.importsDiagramAction.setEnabled(True)
+                                    self.testingAction.setEnabled(True)
+                                    self.checksMenu.menuAction().setEnabled(True)
+                            self.sourceMenu.popup(self.mapToGlobal(coord))
+                        elif isinstance(
+                            itm,
+                            (BrowserClassItem, BrowserMethodItem, BrowserImportItem),
+                        ):
+                            self.menu.popup(self.mapToGlobal(coord))
+                        elif isinstance(itm, BrowserClassAttributeItem):
+                            self.attributeMenu.popup(self.mapToGlobal(coord))
+                        else:
+                            self.backMenu.popup(self.mapToGlobal(coord))
+                    elif sdcnt == 1:
+                        self.classDiagramAction.setEnabled(False)
+                        self.dirMenu.popup(self.mapToGlobal(coord))
+                    else:
+                        self.backMenu.popup(self.mapToGlobal(coord))
+                else:
+                    self.backMenu.popup(self.mapToGlobal(coord))
+
+    def __showContextMenu(self):
+        """
+        Private slot called by the sourceMenu aboutToShow signal.
+        """
+        ProjectBaseBrowser._showContextMenu(self, self.sourceMenu)
+
+        itm = self.model().item(self.currentIndex())
+        if itm:
+            try:
+                self.__startAct.setEnabled(itm.isPython3File())
+            except AttributeError:
+                self.__startAct.setEnabled(False)
+        else:
+            self.__startAct.setEnabled(False)
+
+        self.showMenu.emit("Main", self.sourceMenu)
+
+    def __showContextMenuMulti(self):
+        """
+        Private slot called by the multiMenu aboutToShow signal.
+        """
+        ProjectBaseBrowser._showContextMenuMulti(self, self.multiMenu)
+
+        self.showMenu.emit("MainMulti", self.multiMenu)
+
+    def __showContextMenuDir(self):
+        """
+        Private slot called by the dirMenu aboutToShow signal.
+        """
+        ProjectBaseBrowser._showContextMenuDir(self, self.dirMenu)
+
+        self.showMenu.emit("MainDir", self.dirMenu)
+
+    def __showContextMenuDirMulti(self):
+        """
+        Private slot called by the dirMultiMenu aboutToShow signal.
+        """
+        ProjectBaseBrowser._showContextMenuDirMulti(self, self.dirMultiMenu)
+
+        self.showMenu.emit("MainDirMulti", self.dirMultiMenu)
+
+    def __showContextMenuBack(self):
+        """
+        Private slot called by the backMenu aboutToShow signal.
+        """
+        ProjectBaseBrowser._showContextMenuBack(self, self.backMenu)
+
+        self.showMenu.emit("MainBack", self.backMenu)
+
+    def __showContextMenuShow(self):
+        """
+        Private slot called before the show menu is shown.
+        """
+        prEnable = False
+        coEnable = False
+
+        # first check if the file belongs to a project and there is
+        # a project coverage file
+        fn = self.project.getMainScript(True)
+        if fn is not None:
+            prEnable = self.project.isPy3Project() and bool(
+                Utilities.getProfileFileNames(fn)
+            )
+            coEnable = self.project.isPy3Project() and bool(
+                Utilities.getCoverageFileNames(fn)
+            )
+
+        # now check the selected item
+        itm = self.model().item(self.currentIndex())
+        fn = itm.fileName()
+        if fn is not None:
+            prEnable |= itm.isPython3File() and bool(Utilities.getProfileFileNames(fn))
+            coEnable |= itm.isPython3File() and bool(Utilities.getCoverageFileName(fn))
+
+        self.profileMenuAction.setEnabled(prEnable)
+        self.coverageMenuAction.setEnabled(coEnable)
+
+        self.showMenu.emit("Show", self.menuShow)
+
+    def _openItem(self):
+        """
+        Protected slot to handle the open popup menu entry.
+        """
+        itmList = self.getSelectedItems(
+            [
+                BrowserFileItem,
+                BrowserClassItem,
+                BrowserMethodItem,
+                BrowserClassAttributeItem,
+                BrowserImportItem,
+            ]
+        )
+
+        for itm in itmList:
+            if isinstance(itm, BrowserFileItem):
+                if itm.isPython3File():
+                    self.sourceFile[str].emit(itm.fileName())
+                elif itm.isRubyFile():
+                    self.sourceFile[str, int, str].emit(itm.fileName(), -1, "Ruby")
+                elif itm.isDFile():
+                    self.sourceFile[str, int, str].emit(itm.fileName(), -1, "D")
+                else:
+                    self.sourceFile[str].emit(itm.fileName())
+            elif isinstance(itm, BrowserClassItem):
+                self.sourceFile[str, int].emit(itm.fileName(), itm.classObject().lineno)
+            elif isinstance(itm, BrowserMethodItem):
+                self.sourceFile[str, int].emit(
+                    itm.fileName(), itm.functionObject().lineno
+                )
+            elif isinstance(itm, BrowserClassAttributeItem):
+                self.sourceFile[str, int].emit(
+                    itm.fileName(), itm.attributeObject().lineno
+                )
+            elif isinstance(itm, BrowserImportItem):
+                self.sourceFile[str, list].emit(itm.fileName(), itm.linenos())
+
+    def __addNewPackage(self):
+        """
+        Private method to add a new package to the project.
+        """
+        itm = self.model().item(self.currentIndex())
+        if isinstance(
+            itm, (ProjectBrowserFileItem, BrowserClassItem, BrowserMethodItem)
+        ):
+            dn = os.path.dirname(itm.fileName())
+        elif isinstance(
+            itm, (ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem)
+        ):
+            dn = itm.dirName()
+        else:
+            dn = ""
+
+        dn = self.project.getRelativePath(dn)
+        if dn.startswith(os.sep):
+            dn = dn[1:]
+        from .NewPythonPackageDialog import NewPythonPackageDialog
+
+        dlg = NewPythonPackageDialog(dn, self)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            packageName = dlg.getData()
+            nameParts = packageName.split(".")
+            packagePath = self.project.ppath
+            packageFile = ""
+            for name in nameParts:
+                packagePath = os.path.join(packagePath, name)
+                if not os.path.exists(packagePath):
+                    try:
+                        os.mkdir(packagePath)
+                    except OSError as err:
+                        EricMessageBox.critical(
+                            self,
+                            self.tr("Add new Python package"),
+                            self.tr(
+                                """<p>The package directory <b>{0}</b> could"""
+                                """ not be created. Aborting...</p>"""
+                                """<p>Reason: {1}</p>"""
+                            ).format(packagePath, str(err)),
+                        )
+                        return
+                packageFile = os.path.join(packagePath, "__init__.py")
+                if not os.path.exists(packageFile):
+                    try:
+                        with open(packageFile, "w", encoding="utf-8"):
+                            pass
+                    except OSError as err:
+                        EricMessageBox.critical(
+                            self,
+                            self.tr("Add new Python package"),
+                            self.tr(
+                                """<p>The package file <b>{0}</b> could"""
+                                """ not be created. Aborting...</p>"""
+                                """<p>Reason: {1}</p>"""
+                            ).format(packageFile, str(err)),
+                        )
+                        return
+                self.project.appendFile(packageFile)
+            if packageFile:
+                self.sourceFile[str].emit(packageFile)
+
+    def __addSourceFiles(self):
+        """
+        Private method to add a source file to the project.
+        """
+        itm = self.model().item(self.currentIndex())
+        if isinstance(
+            itm, (ProjectBrowserFileItem, BrowserClassItem, BrowserMethodItem)
+        ):
+            dn = os.path.dirname(itm.fileName())
+        elif isinstance(
+            itm, (ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem)
+        ):
+            dn = itm.dirName()
+        else:
+            dn = None
+        self.project.addFiles("source", dn)
+
+    def __addSourceDirectory(self):
+        """
+        Private method to add source files of a directory to the project.
+        """
+        itm = self.model().item(self.currentIndex())
+        if isinstance(
+            itm, (ProjectBrowserFileItem, BrowserClassItem, BrowserMethodItem)
+        ):
+            dn = os.path.dirname(itm.fileName())
+        elif isinstance(
+            itm, (ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem)
+        ):
+            dn = itm.dirName()
+        else:
+            dn = None
+        self.project.addDirectory("source", dn)
+
+    def __deleteFile(self):
+        """
+        Private method to delete files from the project.
+        """
+        itmList = self.getSelectedItems()
+
+        files = []
+        fullNames = []
+        for itm in itmList:
+            fn2 = itm.fileName()
+            fullNames.append(fn2)
+            fn = self.project.getRelativePath(fn2)
+            files.append(fn)
+
+        from UI.DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog
+
+        dlg = DeleteFilesConfirmationDialog(
+            self.parent(),
+            self.tr("Delete files"),
+            self.tr("Do you really want to delete these files from the project?"),
+            files,
+        )
+
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            for fn2, fn in zip(fullNames, files):
+                self.closeSourceWindow.emit(fn2)
+                self.project.deleteFile(fn)
+
+    ###########################################################################
+    ## Methods for the Checks submenu
+    ###########################################################################
+
+    def __showContextMenuCheck(self):
+        """
+        Private slot called before the checks menu is shown.
+        """
+        self.showMenu.emit("Checks", self.checksMenu)
+
+    ###########################################################################
+    ## Methods for the Show submenu
+    ###########################################################################
+
+    def __showCodeMetrics(self):
+        """
+        Private method to handle the code metrics context menu action.
+        """
+        itm = self.model().item(self.currentIndex())
+        fn = itm.fileName()
+
+        from DataViews.CodeMetricsDialog import CodeMetricsDialog
+
+        self.codemetrics = CodeMetricsDialog()
+        self.codemetrics.show()
+        self.codemetrics.start(fn)
+
+    def __showCodeCoverage(self):
+        """
+        Private method to handle the code coverage context menu action.
+        """
+        itm = self.model().item(self.currentIndex())
+        fn = itm.fileName()
+        pfn = self.project.getMainScript(True)
+
+        files = set()
+
+        if pfn is not None:
+            files |= set(Utilities.getCoverageFileNames(pfn))
+
+        if fn is not None:
+            files |= set(Utilities.getCoverageFileNames(fn))
+
+        if list(files):
+            if len(files) > 1:
+                cfn, ok = QInputDialog.getItem(
+                    None,
+                    self.tr("Code Coverage"),
+                    self.tr("Please select a coverage file"),
+                    files,
+                    0,
+                    False,
+                )
+                if not ok:
+                    return
+            else:
+                cfn = files[0]
+        else:
+            return
+
+        from DataViews.PyCoverageDialog import PyCoverageDialog
+
+        self.codecoverage = PyCoverageDialog()
+        self.codecoverage.show()
+        self.codecoverage.start(cfn, fn)
+
+    def __showProfileData(self):
+        """
+        Private method to handle the show profile data context menu action.
+        """
+        itm = self.model().item(self.currentIndex())
+        fn = itm.fileName()
+        pfn = self.project.getMainScript(True)
+
+        files = set()
+
+        if pfn is not None:
+            files |= set(Utilities.getProfileFileNames(pfn))
+
+        if fn is not None:
+            files |= set(Utilities.getProfileFileNames(fn))
+
+        if list(files):
+            if len(files) > 1:
+                pfn, ok = QInputDialog.getItem(
+                    None,
+                    self.tr("Profile Data"),
+                    self.tr("Please select a profile file"),
+                    files,
+                    0,
+                    False,
+                )
+                if not ok:
+                    return
+            else:
+                pfn = files[0]
+        else:
+            return
+
+        from DataViews.PyProfileDialog import PyProfileDialog
+
+        self.profiledata = PyProfileDialog()
+        self.profiledata.show()
+        self.profiledata.start(pfn, fn)
+
+    ###########################################################################
+    ## Methods for the Graphics submenu
+    ###########################################################################
+
+    def __showContextMenuGraphics(self):
+        """
+        Private slot called before the checks menu is shown.
+        """
+        self.showMenu.emit("Graphics", self.graphicsMenu)
+
+    def __showClassDiagram(self):
+        """
+        Private method to handle the class diagram context menu action.
+        """
+        itm = self.model().item(self.currentIndex())
+        try:
+            fn = itm.fileName()
+        except AttributeError:
+            fn = itm.dirName()
+        res = EricMessageBox.yesNo(
+            self,
+            self.tr("Class Diagram"),
+            self.tr("""Include class attributes?"""),
+            yesDefault=True,
+        )
+
+        from Graphics.UMLDialog import UMLDialog, UMLDialogType
+
+        self.classDiagram = UMLDialog(
+            UMLDialogType.CLASS_DIAGRAM, self.project, fn, self, noAttrs=not res
+        )
+        self.classDiagram.show()
+
+    def __showImportsDiagram(self):
+        """
+        Private method to handle the imports diagram context menu action.
+        """
+        itm = self.model().item(self.currentIndex())
+        try:
+            fn = itm.fileName()
+        except AttributeError:
+            fn = itm.dirName()
+        package = fn if os.path.isdir(fn) else os.path.dirname(fn)
+        res = EricMessageBox.yesNo(
+            self,
+            self.tr("Imports Diagram"),
+            self.tr("""Include imports from external modules?"""),
+        )
+
+        from Graphics.UMLDialog import UMLDialog, UMLDialogType
+
+        self.importsDiagram = UMLDialog(
+            UMLDialogType.IMPORTS_DIAGRAM,
+            self.project,
+            package,
+            self,
+            showExternalImports=res,
+        )
+        self.importsDiagram.show()
+
+    def __showPackageDiagram(self):
+        """
+        Private method to handle the package diagram context menu action.
+        """
+        itm = self.model().item(self.currentIndex())
+        try:
+            fn = itm.fileName()
+        except AttributeError:
+            fn = itm.dirName()
+        package = fn if os.path.isdir(fn) else os.path.dirname(fn)
+        res = EricMessageBox.yesNo(
+            self,
+            self.tr("Package Diagram"),
+            self.tr("""Include class attributes?"""),
+            yesDefault=True,
+        )
+
+        from Graphics.UMLDialog import UMLDialog, UMLDialogType
+
+        self.packageDiagram = UMLDialog(
+            UMLDialogType.PACKAGE_DIAGRAM, self.project, package, self, noAttrs=not res
+        )
+        self.packageDiagram.show()
+
+    def __showApplicationDiagram(self):
+        """
+        Private method to handle the application diagram context menu action.
+        """
+        res = EricMessageBox.yesNo(
+            self,
+            self.tr("Application Diagram"),
+            self.tr("""Include module names?"""),
+            yesDefault=True,
+        )
+
+        from Graphics.UMLDialog import UMLDialog, UMLDialogType
+
+        self.applicationDiagram = UMLDialog(
+            UMLDialogType.APPLICATION_DIAGRAM, self.project, self, noModules=not res
+        )
+        self.applicationDiagram.show()
+
+    def __loadDiagram(self):
+        """
+        Private slot to load a diagram from file.
+        """
+        from Graphics.UMLDialog import UMLDialog, UMLDialogType
+
+        self.loadedDiagram = None
+        loadedDiagram = UMLDialog(UMLDialogType.NO_DIAGRAM, self.project, parent=self)
+        if loadedDiagram.load():
+            self.loadedDiagram = loadedDiagram
+            self.loadedDiagram.show(fromFile=True)
+
+    ###########################################################################
+    ## Methods for the Start submenu
+    ###########################################################################
+
+    def __contextMenuRunScript(self):
+        """
+        Private method to run the editor script.
+        """
+        fn = self.model().item(self.currentIndex()).fileName()
+        ericApp().getObject("DebugUI").doRun(False, script=fn)
+
+    def __contextMenuDebugScript(self):
+        """
+        Private method to debug the editor script.
+        """
+        fn = self.model().item(self.currentIndex()).fileName()
+        ericApp().getObject("DebugUI").doDebug(False, script=fn)
+
+    def __contextMenuProfileScript(self):
+        """
+        Private method to profile the editor script.
+        """
+        fn = self.model().item(self.currentIndex()).fileName()
+        ericApp().getObject("DebugUI").doProfile(False, script=fn)
+
+    def __contextMenuCoverageScript(self):
+        """
+        Private method to run a coverage test of the editor script.
+        """
+        fn = self.model().item(self.currentIndex()).fileName()
+        ericApp().getObject("DebugUI").doCoverage(False, script=fn)
+
+    ###########################################################################
+    ## Methods for the Code Formatting submenu
+    ###########################################################################
+
+    def __showContextMenuFormatting(self):
+        """
+        Private slot called before the Code Formatting menu is shown.
+        """
+        self.showMenu.emit("Formatting", self.formattingMenu)
+
+    def __performFormatWithBlack(self, action):
+        """
+        Private method to format the selected project sources using the 'Black' tool.
+
+        Following actions are supported.
+        <ul>
+        <li>BlackFormattingAction.Format - the code reformatting is performed</li>
+        <li>BlackFormattingAction.Check - a check is performed, if code formatting
+            is necessary</li>
+        <li>BlackFormattingAction.Diff - a unified diff of potential code formatting
+            changes is generated</li>
+        </ul>
+
+        @param action formatting operation to be performed
+        @type BlackFormattingAction
+        """
+        from CodeFormatting.BlackConfigurationDialog import BlackConfigurationDialog
+        from CodeFormatting.BlackFormattingDialog import BlackFormattingDialog
+
+        files = [
+            itm.fileName()
+            for itm in self.getSelectedItems(
+                [
+                    BrowserFileItem,
+                    BrowserClassItem,
+                    BrowserMethodItem,
+                    BrowserClassAttributeItem,
+                    BrowserImportItem,
+                ]
+            )
+            if itm.isPython3File()
+        ]
+        if not files:
+            # called for a directory
+            itm = self.model().item(self.currentIndex())
+            dirName = itm.dirName()
+            files = [
+                f
+                for f in self.project.getProjectFiles("SOURCES", normalized=True)
+                if f.startswith(dirName)
+            ]
+
+        vm = ericApp().getObject("ViewManager")
+        files = [fn for fn in files if vm.checkFileDirty(fn)]
+
+        if files:
+            dlg = BlackConfigurationDialog(withProject=True)
+            if dlg.exec() == QDialog.DialogCode.Accepted:
+                config = dlg.getConfiguration()
+
+                formattingDialog = BlackFormattingDialog(
+                    config, files, project=self.project, action=action
+                )
+                formattingDialog.exec()
+        else:
+            EricMessageBox.information(
+                self,
+                self.tr("Code Formatting"),
+                self.tr("""There are no files left for reformatting."""),
+            )

eric ide

mercurial