src/eric7/Project/Project.py

branch
server
changeset 10596
ea35c92a3c7c
parent 10593
b7ebfc2e47d4
child 10597
fbe93720ee9f
diff -r 6156d9675f62 -r ea35c92a3c7c src/eric7/Project/Project.py
--- a/src/eric7/Project/Project.py	Mon Feb 19 19:37:00 2024 +0100
+++ b/src/eric7/Project/Project.py	Thu Feb 22 16:26:46 2024 +0100
@@ -23,8 +23,6 @@
 from PyQt6.QtCore import (
     QByteArray,
     QCryptographicHash,
-    QFile,
-    QIODevice,
     QObject,
     QProcess,
     pyqtSignal,
@@ -45,12 +43,8 @@
 from eric7.EricWidgets.EricApplication import ericApp
 from eric7.EricWidgets.EricListSelectionDialog import EricListSelectionDialog
 from eric7.EricWidgets.EricProgressDialog import EricProgressDialog
-from eric7.EricXML.DebuggerPropertiesReader import DebuggerPropertiesReader
-from eric7.EricXML.ProjectReader import ProjectReader
-from eric7.EricXML.SessionReader import SessionReader
-from eric7.EricXML.TasksReader import TasksReader
-from eric7.EricXML.UserProjectReader import UserProjectReader
 from eric7.Globals import recentNameProject
+from eric7.RemoteServerInterface import EricServerFileDialog
 from eric7.Sessions.SessionFile import SessionFile
 from eric7.SystemUtilities import (
     FileSystemUtilities,
@@ -165,18 +159,21 @@
     DefaultMake = "make"
     DefaultMakefile = "makefile"
 
-    def __init__(self, parent=None, filename=None):
+    def __init__(self, parent=None, filename=None, remoteServer=None):
         """
         Constructor
 
         @param parent parent widget (usually the ui object)
         @type QWidget
-        @param filename optional filename of a project file to open
-        @type str
+        @param filename optional filename of a project file to open (defaults to None)
+        @type str (optional)
+        @param remoteServer reference to the 'eric-ide' server interface object
+        @type EricServerInterface
         """
         super().__init__(parent)
 
         self.ui = parent
+        self.__remotefsInterface = remoteServer.getServiceInterface("FileSystem")
 
         self.__progLanguages = [
             "Python3",
@@ -217,7 +214,7 @@
         else:
             self.vcs = self.initVCS()
 
-        self.__model = ProjectBrowserModel(self)
+        self.__model = ProjectBrowserModel(self, fsInterface=self.__remotefsInterface)
 
         self.codemetrics = None
         self.codecoverage = None
@@ -1106,10 +1103,18 @@
         """
         removed = False
         removelist = []
-        for file in self.__pdata[index]:
-            if not os.path.exists(os.path.join(self.ppath, file)):
-                removelist.append(file)
-                removed = True
+        if FileSystemUtilities.isRemoteFileName(self.ppath):
+            for file in self.__pdata[index]:
+                if not self.__remotefsInterface.exists(
+                    self.__remotefsInterface.join(self.ppath, file)
+                ):
+                    removelist.append(file)
+                    removed = True
+        else:
+            for file in self.__pdata[index]:
+                if not os.path.exists(os.path.join(self.ppath, file)):
+                    removelist.append(file)
+                    removed = True
 
         if removed:
             for file in removelist:
@@ -1118,50 +1123,27 @@
 
     def __readProject(self, fn):
         """
-        Private method to read in a project (.epj or .e4p) file.
+        Private method to read in a project file (.epj).
 
         @param fn filename of the project file to be read
         @type str
         @return flag indicating success
         @rtype bool
         """
-        if os.path.splitext(fn)[1] == ".epj":
-            # new JSON based format
-            with EricOverrideCursor():
-                res = self.__projectFile.readFile(fn)
-        else:
-            # old XML based format
-            f = QFile(fn)
-            if f.open(QIODevice.OpenModeFlag.ReadOnly):
-                reader = ProjectReader(f, self)
-                reader.readXML()
-                res = not reader.hasError()
-                f.close()
-
-                # create hash value, if it doesn't have one
-                if reader.version.startswith("5.") and not self.__pdata["HASH"]:
-                    hashStr = str(
-                        QCryptographicHash.hash(
-                            QByteArray(self.ppath.encode("utf-8")),
-                            QCryptographicHash.Algorithm.Sha1,
-                        ).toHex(),
-                        encoding="utf-8",
-                    )
-                    self.__pdata["HASH"] = hashStr
-                    self.setDirty(True)
-            else:
-                EricMessageBox.critical(
-                    self.ui,
-                    self.tr("Read Project File"),
-                    self.tr(
-                        "<p>The project file <b>{0}</b> could not be read.</p>"
-                    ).format(fn),
-                )
-                res = False
+        with EricOverrideCursor():
+            res = self.__projectFile.readFile(fn)
 
         if res:
-            self.pfile = os.path.abspath(fn)
-            self.ppath = os.path.abspath(os.path.dirname(fn))
+            if FileSystemUtilities.isRemoteFileName(fn):
+                self.pfile = fn
+                self.ppath = self.__remotefsInterface.dirname(fn)
+                self.name = self.__remotefsInterface.splitext(
+                    self.__remotefsInterface.basename(fn)
+                )[0]
+            else:
+                self.pfile = os.path.abspath(fn)
+                self.ppath = os.path.abspath(os.path.dirname(fn))
+                self.name = os.path.splitext(os.path.basename(fn))[0]
 
             # insert filename into list of recently opened projects
             self.__syncRecent()
@@ -1172,15 +1154,22 @@
                 )[0]
             elif self.__pdata["MAINSCRIPT"]:
                 self.translationsRoot = os.path.splitext(self.__pdata["MAINSCRIPT"])[0]
-            if os.path.isdir(os.path.join(self.ppath, self.translationsRoot)):
-                dn = self.translationsRoot
+
+            if FileSystemUtilities.isRemoteFileName(self.ppath):
+                if self.__remotefsInterface.isdir(
+                    self.__remotefsInterface.join(self.ppath, self.translationsRoot)
+                ):
+                    dn = self.translationsRoot
+                else:
+                    dn = self.__remotefsInterface.dirname(self.translationsRoot)
             else:
-                dn = os.path.dirname(self.translationsRoot)
-            if dn not in self.subdirs:
+                if os.path.isdir(os.path.join(self.ppath, self.translationsRoot)):
+                    dn = self.translationsRoot
+                else:
+                    dn = os.path.dirname(self.translationsRoot)
+            if dn and dn not in self.subdirs:
                 self.subdirs.append(dn)
 
-            self.name = os.path.splitext(os.path.basename(fn))[0]
-
             # check, if the files of the project still exist in the
             # project directory
             for fileCategory in self.getFileCategories():
@@ -1189,14 +1178,22 @@
             # get the names of subdirectories the files are stored in
             for fileCategory in [c for c in self.getFileCategories() if c != "OTHERS"]:
                 for fn in self.__pdata[fileCategory]:
-                    dn = os.path.dirname(fn)
-                    if dn not in self.subdirs:
+                    dn = (
+                        self.__remotefsInterface.dirname(fn)
+                        if FileSystemUtilities.isRemoteFileName(fn)
+                        else os.path.dirname(fn)
+                    )
+                    if dn and dn not in self.subdirs:
                         self.subdirs.append(dn)
 
             # get the names of other subdirectories
             for fn in self.__pdata["OTHERS"]:
-                dn = os.path.dirname(fn)
-                if dn not in self.otherssubdirs:
+                dn = (
+                    self.__remotefsInterface.dirname(fn)
+                    if FileSystemUtilities.isRemoteFileName(fn)
+                    else os.path.dirname(fn)
+                )
+                if dn and dn not in self.otherssubdirs:
                     self.otherssubdirs.append(dn)
 
         return res
@@ -1235,9 +1232,16 @@
             res = self.__projectFile.writeFile(fn)
 
         if res:
-            self.pfile = os.path.abspath(fn)
-            self.ppath = os.path.abspath(os.path.dirname(fn))
-            self.name = os.path.splitext(os.path.basename(fn))[0]
+            if FileSystemUtilities.isRemoteFileName(fn):
+                self.pfile = fn
+                self.ppath = self.__remotefsInterface.dirname(fn)
+                self.name = self.__remotefsInterface.splitext(
+                    self.__remotefsInterface.basename(fn)
+                )[0]
+            else:
+                self.pfile = os.path.abspath(fn)
+                self.ppath = os.path.abspath(os.path.dirname(fn))
+                self.name = os.path.splitext(os.path.basename(fn))[0]
             self.setDirty(False)
 
             # insert filename into list of recently opened projects
@@ -1247,35 +1251,27 @@
 
     def __readUserProperties(self):
         """
-        Private method to read in the user specific project file (.eqj or
-        .e4q).
+        Private method to read in the user specific project file (.eqj).
         """
         if self.pfile is None:
             return
 
-        fn1, ext = os.path.splitext(os.path.basename(self.pfile))
-        fn = os.path.join(self.getProjectManagementDir(), "{0}.eqj".format(fn1))
-        if os.path.exists(fn):
-            # try the new JSON based format first
-            self.__userProjectFile.readFile(fn)
+        if FileSystemUtilities.isRemoteFileName(self.pfile):
+            fn1, _ext = self.__remotefsInterface.splitext(
+                self.__remotefsInterface.basename(self.pfile)
+            )
+            fn = self.__remotefsInterface.join(
+                self.getProjectManagementDir(), f"{fn1}.eqj"
+            )
+            if not self.__remotefsInterface.exists(fn):
+                return
         else:
-            # try the old XML based format second
-            fn = os.path.join(self.getProjectManagementDir(), "{0}.e4q".format(fn1))
-            if os.path.exists(fn):
-                f = QFile(fn)
-                if f.open(QIODevice.OpenModeFlag.ReadOnly):
-                    reader = UserProjectReader(f, self)
-                    reader.readXML()
-                    f.close()
-                else:
-                    EricMessageBox.critical(
-                        self.ui,
-                        self.tr("Read User Project Properties"),
-                        self.tr(
-                            "<p>The user specific project properties file"
-                            " <b>{0}</b> could not be read.</p>"
-                        ).format(fn),
-                    )
+            fn1, _ext = os.path.splitext(os.path.basename(self.pfile))
+            fn = os.path.join(self.getProjectManagementDir(), f"{fn1}.eqj")
+            if not os.path.exists(fn):
+                return
+
+        self.__userProjectFile.readFile(fn)
 
     def __writeUserProperties(self):
         """
@@ -1284,8 +1280,16 @@
         if self.pfile is None:
             return
 
-        fn, ext = os.path.splitext(os.path.basename(self.pfile))
-        fn = os.path.join(self.getProjectManagementDir(), "{0}.eqj".format(fn))
+        if FileSystemUtilities.isRemoteFileName(self.pfile):
+            fn1, _ext = self.__remotefsInterface.splitext(
+                self.__remotefsInterface.basename(self.pfile)
+            )
+            fn = self.__remotefsInterface.join(
+                self.getProjectManagementDir(), f"{fn1}.eqj"
+            )
+        else:
+            fn1, _ext = os.path.splitext(os.path.basename(self.pfile))
+            fn = os.path.join(self.getProjectManagementDir(), f"{fn1}.eqj")
 
         with EricOverrideCursor():
             self.__userProjectFile.writeFile(fn)
@@ -1298,17 +1302,25 @@
         if self.pfile is None:
             enable = False
         else:
-            fn, ext = os.path.splitext(os.path.basename(self.pfile))
-            fn_new = os.path.join(self.getProjectManagementDir(), "{0}.esj".format(fn))
-            fn_old = os.path.join(self.getProjectManagementDir(), "{0}.e5s".format(fn))
-            enable = os.path.exists(fn_new) or os.path.exists(fn_old)
+            if FileSystemUtilities.isRemoteFileName(self.pfile):
+                fn, _ext = self.__remotefsInterface.splitext(
+                    self.__remotefsInterface.basename(self.pfile)
+                )
+                fn_sess = self.__remotefsInterface.join(
+                    self.getProjectManagementDir(), f"{fn}.esj"
+                )
+                enable = self.__remotefsInterface.exists(fn)
+            else:
+                fn, _ext = os.path.splitext(os.path.basename(self.pfile))
+                fn_sess = os.path.join(self.getProjectManagementDir(), f"{fn}.esj")
+                enable = os.path.exists(fn_sess)
         self.sessActGrp.findChild(QAction, "project_load_session").setEnabled(enable)
         self.sessActGrp.findChild(QAction, "project_delete_session").setEnabled(enable)
 
     @pyqtSlot()
     def __readSession(self, quiet=False, indicator=""):
         """
-        Private method to read in the project session file (.esj or .e5s).
+        Private method to read in the project session file (.esj).
 
         @param quiet flag indicating quiet operations.
                 If this flag is true, no errors are reported.
@@ -1325,34 +1337,24 @@
                 )
             return
 
-        fn1, ext = os.path.splitext(os.path.basename(self.pfile))
-        fn = os.path.join(
-            self.getProjectManagementDir(), "{0}{1}.esj".format(fn1, indicator)
-        )
-        if os.path.exists(fn):
-            # try the new JSON based format first
-            self.__sessionFile.readFile(fn)
-        else:
-            # try the old XML based format second
-            fn = os.path.join(
-                self.getProjectManagementDir(), "{0}{1}.e5s".format(fn1, indicator)
+        if FileSystemUtilities.isRemoteFileName(self.pfile):
+            fn1, _ext = self.__remotefsInterface.splitext(
+                self.__remotefsInterface.basename(self.pfile)
+            )
+            fn = self.__remotefsInterface.join(
+                self.getProjectManagementDir(), f"{fn1}{indicator}.esj"
             )
-            if os.path.exists(fn):
-                f = QFile(fn)
-                if f.open(QIODevice.OpenModeFlag.ReadOnly):
-                    reader = SessionReader(f, False)
-                    reader.readXML(quiet=quiet)
-                    f.close()
-                else:
-                    if not quiet:
-                        EricMessageBox.critical(
-                            self.ui,
-                            self.tr("Read project session"),
-                            self.tr(
-                                "<p>The project session file <b>{0}</b> could"
-                                " not be read.</p>"
-                            ).format(fn),
-                        )
+            if not self.__remotefsInterface.exists(fn):
+                return
+        else:
+            fn1, _ext = os.path.splitext(os.path.basename(self.pfile))
+            fn = os.path.join(
+                self.getProjectManagementDir(), f"{fn1}{indicator}.esj"
+            )
+            if not os.path.exists(fn):
+                return
+
+        self.__sessionFile.readFile(fn)
 
     @pyqtSlot()
     def __writeSession(self, quiet=False, indicator=""):
@@ -1374,10 +1376,18 @@
                 )
             return
 
-        fn, ext = os.path.splitext(os.path.basename(self.pfile))
-        fn = os.path.join(
-            self.getProjectManagementDir(), "{0}{1}.esj".format(fn, indicator)
-        )
+        if FileSystemUtilities.isRemoteFileName(self.pfile):
+            fn1, _ext = self.__remotefsInterface.splitext(
+                self.__remotefsInterface.basename(self.pfile)
+            )
+            fn = self.__remotefsInterface.join(
+                self.getProjectManagementDir(), f"{fn1}{indicator}.esj"
+            )
+        else:
+            fn1, _ext = os.path.splitext(os.path.basename(self.pfile))
+            fn = os.path.join(
+                self.getProjectManagementDir(), f"{fn1}{indicator}.esj"
+            )
 
         self.__sessionFile.writeFile(fn)
 
@@ -1393,28 +1403,36 @@
             )
             return
 
-        fname, ext = os.path.splitext(os.path.basename(self.pfile))
-
-        for ext in (".esj", ".e5s", ".e4s"):
-            fn = os.path.join(
-                self.getProjectManagementDir(), "{0}{1}".format(fname, ext)
-            )
-            if os.path.exists(fn):
-                try:
+        try:
+            if FileSystemUtilities.isRemoteFileName(self.pfile):
+                title = self.tr("Delete Remote Project Session")
+                fname, _ext = self.__remotefsInterface.splitext(
+                    self.__remotefsInterface.basename(self.pfile)
+                )
+                fn = self.__remotefsInterface.join(
+                    self.getProjectManagementDir(), f"{fname}.esj"
+                )
+                if self.__remotefsInterface.exists(fn):
+                    self.__remotefsInterface.remove(fn)
+            else:
+                title = self.tr("Delete Project Session")
+                fname, _ext = os.path.splitext(os.path.basename(self.pfile))
+                fn = os.path.join(self.getProjectManagementDir(), f"{fname}.esj")
+                if os.path.exists(fn):
                     os.remove(fn)
-                except OSError:
-                    EricMessageBox.critical(
-                        self.ui,
-                        self.tr("Delete Project Session"),
-                        self.tr(
-                            "<p>The project session file <b>{0}</b> could"
-                            " not be deleted.</p>"
-                        ).format(fn),
-                    )
+        except OSError:
+            EricMessageBox.critical(
+                self.ui,
+                title,
+                self.tr(
+                    "<p>The project session file <b>{0}</b> could"
+                    " not be deleted.</p>"
+                ).format(fn),
+            )
 
     def __readTasks(self):
         """
-        Private method to read in the project tasks file (.etj or .e6t).
+        Private method to read in the project tasks file (.etj).
         """
         if self.pfile is None:
             EricMessageBox.critical(
@@ -1424,28 +1442,22 @@
             )
             return
 
-        base, ext = os.path.splitext(os.path.basename(self.pfile))
-        fn = os.path.join(self.getProjectManagementDir(), "{0}.etj".format(base))
-        if os.path.exists(fn):
-            # try new style JSON file first
-            self.__tasksFile.readFile(fn)
+        if FileSystemUtilities.isRemoteFileName(self.pfile):
+            base, _ext = self.__remotefsInterface.splitext(
+                self.__remotefsInterface.basename(self.pfile)
+            )
+            fn = self.__remotefsInterface.join(
+                self.getProjectManagementDir(), f"{base}.etj"
+            )
+            if not self.__remotefsInterface.exists(fn):
+                return
         else:
-            # try old style XML file second
-            fn = os.path.join(self.getProjectManagementDir(), "{0}.e6t".format(base))
-            if os.path.exists(fn):
-                f = QFile(fn)
-                if f.open(QIODevice.OpenModeFlag.ReadOnly):
-                    reader = TasksReader(f, True)
-                    reader.readXML()
-                    f.close()
-                else:
-                    EricMessageBox.critical(
-                        self.ui,
-                        self.tr("Read Tasks"),
-                        self.tr(
-                            "<p>The tasks file <b>{0}</b> could not be read.</p>"
-                        ).format(fn),
-                    )
+            base, ext = os.path.splitext(os.path.basename(self.pfile))
+            fn = os.path.join(self.getProjectManagementDir(), f"{base}.etj")
+            if not os.path.exists(fn):
+                return
+
+        self.__tasksFile.readFile(fn)
 
     def writeTasks(self):
         """
@@ -1454,9 +1466,17 @@
         if self.pfile is None:
             return
 
-        fn, ext = os.path.splitext(os.path.basename(self.pfile))
-
-        fn = os.path.join(self.getProjectManagementDir(), "{0}.etj".format(fn))
+        if FileSystemUtilities.isRemoteFileName(self.pfile):
+            base, _ext = self.__remotefsInterface.splitext(
+                self.__remotefsInterface.basename(self.pfile)
+            )
+            fn = self.__remotefsInterface.join(
+                self.getProjectManagementDir(), f"{base}.etj"
+            )
+        else:
+            base, ext = os.path.splitext(os.path.basename(self.pfile))
+            fn = os.path.join(self.getProjectManagementDir(), f"{base}.etj")
+
         self.__tasksFile.writeFile(fn)
 
     def __showContextMenuDebugger(self):
@@ -1467,13 +1487,18 @@
         if self.pfile is None:
             enable = False
         else:
-            fn, ext = os.path.splitext(os.path.basename(self.pfile))
-            # try new style file first
-            fn = os.path.join(self.getProjectManagementDir(), "{0}.edj".format(fn))
-            if not os.path.exists(fn):
-                # try old style file second
-                fn = os.path.join(self.getProjectManagementDir(), "{0}.e4d".format(fn))
-            enable = os.path.exists(fn)
+            if FileSystemUtilities.isRemoteFileName(self.pfile):
+                fn1, _ext = self.__remotefsInterface.splitext(
+                    self.__remotefsInterface.basename(self.pfile)
+                )
+                fn = self.__remotefsInterface.join(
+                    self.getProjectManagementDir(), f"{fn1}.edj"
+                )
+                enable = self.__remotefsInterface.exists(fn)
+            else:
+                fn1, _ext = os.path.splitext(os.path.basename(self.pfile))
+                fn = os.path.join(self.getProjectManagementDir(), f"{fn1}.edj")
+                enable = os.path.exists(fn)
         self.dbgActGrp.findChild(
             QAction, "project_debugger_properties_load"
         ).setEnabled(enable)
@@ -1500,34 +1525,24 @@
                 )
             return
 
-        fn1, ext = os.path.splitext(os.path.basename(self.pfile))
-        fn = os.path.join(self.getProjectManagementDir(), "{0}.edj".format(fn1))
-        if os.path.exists(fn):
-            # try the new JSON based format first
-            if self.__debuggerPropertiesFile.readFile(fn):
-                self.debugPropertiesLoaded = True
-                self.debugPropertiesChanged = False
+        if FileSystemUtilities.isRemoteFileName(self.pfile):
+            fn1, _ext = self.__remotefsInterface.splitext(
+                self.__remotefsInterface.basename(self.pfile)
+            )
+            fn = self.__remotefsInterface.join(
+                self.getProjectManagementDir(), f"{fn1}.edj"
+            )
+            if not self.__remotefsInterface.exists(fn):
+                return
         else:
-            # try the old XML based format second
-            fn = os.path.join(self.getProjectManagementDir(), "{0}.e4d".format(fn1))
-
-            f = QFile(fn)
-            if f.open(QIODevice.OpenModeFlag.ReadOnly):
-                reader = DebuggerPropertiesReader(f, self)
-                reader.readXML(quiet=quiet)
-                f.close()
-                self.debugPropertiesLoaded = True
-                self.debugPropertiesChanged = False
-            else:
-                if not quiet:
-                    EricMessageBox.critical(
-                        self.ui,
-                        self.tr("Read Debugger Properties"),
-                        self.tr(
-                            "<p>The project debugger properties file"
-                            " <b>{0}</b> could not be read.</p>"
-                        ).format(fn),
-                    )
+            fn1, _ext = os.path.splitext(os.path.basename(self.pfile))
+            fn = os.path.join(self.getProjectManagementDir(), f"{fn1}.edj")
+        if not os.path.exists(fn):
+            return
+
+        if self.__debuggerPropertiesFile.readFile(fn):
+            self.debugPropertiesLoaded = True
+            self.debugPropertiesChanged = False
 
     @pyqtSlot()
     def __writeDebugProperties(self, quiet=False):
@@ -1547,8 +1562,16 @@
                 )
             return
 
-        fn, ext = os.path.splitext(os.path.basename(self.pfile))
-        fn = os.path.join(self.getProjectManagementDir(), "{0}.edj".format(fn))
+        if FileSystemUtilities.isRemoteFileName(self.pfile):
+            fn1, _ext = self.__remotefsInterface.splitext(
+                self.__remotefsInterface.basename(self.pfile)
+            )
+            fn = self.__remotefsInterface.join(
+                self.getProjectManagementDir(), f"{fn1}.edj"
+            )
+        else:
+            fn1, _ext = os.path.splitext(os.path.basename(self.pfile))
+            fn = os.path.join(self.getProjectManagementDir(), f"{fn1}.edj")
 
         with EricOverrideCursor():
             self.__debuggerPropertiesFile.writeFile(fn)
@@ -1566,24 +1589,32 @@
             )
             return
 
-        fname, ext = os.path.splitext(os.path.basename(self.pfile))
-
-        for ext in (".edj", ".e4d"):
-            fn = os.path.join(
-                self.getProjectManagementDir(), "{0}{1}".format(fname, ext)
-            )
-            if os.path.exists(fn):
-                try:
+        try:
+            if FileSystemUtilities.isRemoteFileName(self.pfile):
+                title = self.tr("Delete Remote Debugger Properties")
+                fname, _ext = self.__remotefsInterface.splitext(
+                    self.__remotefsInterface.basename(self.pfile)
+                )
+                fn = self.__remotefsInterface.join(
+                    self.getProjectManagementDir(), f"{fname}.edj"
+                )
+                if self.__remotefsInterface.exists(fn):
+                    self.__remotefsInterface.remove(fn)
+            else:
+                title = self.tr("Delete Debugger Properties")
+                fname, _ext = os.path.splitext(os.path.basename(self.pfile))
+                fn = os.path.join(self.getProjectManagementDir(), f"{fname}.edj")
+                if os.path.exists(fn):
                     os.remove(fn)
-                except OSError:
-                    EricMessageBox.critical(
-                        self.ui,
-                        self.tr("Delete Debugger Properties"),
-                        self.tr(
-                            "<p>The project debugger properties file"
-                            " <b>{0}</b> could not be deleted.</p>"
-                        ).format(fn),
-                    )
+        except OSError:
+            EricMessageBox.critical(
+                self.ui,
+                title,
+                self.tr(
+                    "<p>The project debugger properties file"
+                    " <b>{0}</b> could not be deleted.</p>"
+                ).format(fn),
+            )
 
     def __initDebugProperties(self):
         """
@@ -1804,16 +1835,37 @@
         for langFile in self.__pdata["TRANSLATIONS"][:]:
             qmFile = self.__binaryTranslationFile(langFile)
             if qmFile:
-                if qmFile not in self.__pdata["TRANSLATIONS"] and os.path.exists(
-                    os.path.join(self.ppath, qmFile)
-                ):
-                    self.appendFile(qmFile)
-                if tbPath:
-                    qmFile = os.path.join(tbPath, os.path.basename(qmFile))
+                if FileSystemUtilities.isRemoteFileName(self.ppath):
+                    if (
+                        qmFile not in self.__pdata["TRANSLATIONS"]
+                        and self.__remotefsInterface.exists(
+                            self.__remotefsInterface.join(self.ppath, qmFile)
+                        )
+                    ):
+                        self.appendFile(qmFile)
+                    if tbPath:
+                        qmFile = self.__remotefsInterface.join(
+                            tbPath, self.__remotefsInterface.basename(qmFile)
+                        )
+                        if (
+                            qmFile not in self.__pdata["TRANSLATIONS"]
+                            and self.__remotefsInterface.exists(
+                                self.__remotefsInterface.join(self.ppath, qmFile)
+                            )
+                        ):
+                            self.appendFile(qmFile)
+                else:
                     if qmFile not in self.__pdata["TRANSLATIONS"] and os.path.exists(
                         os.path.join(self.ppath, qmFile)
                     ):
                         self.appendFile(qmFile)
+                    if tbPath:
+                        qmFile = os.path.join(tbPath, os.path.basename(qmFile))
+                        if (
+                            qmFile not in self.__pdata["TRANSLATIONS"]
+                            and os.path.exists(os.path.join(self.ppath, qmFile))
+                        ):
+                            self.appendFile(qmFile)
 
     def removeLanguageFile(self, langFile):
         """
@@ -1832,12 +1884,18 @@
         if qmFile:
             with contextlib.suppress(ValueError):
                 if self.__pdata["TRANSLATIONSBINPATH"]:
-                    qmFile = self.getRelativePath(
-                        os.path.join(
+                    if FileSystemUtilities.isRemoteFileName(self.ppath):
+                        qmFile = self.__remotefsInterface.join(
                             self.__pdata["TRANSLATIONSBINPATH"],
-                            os.path.basename(qmFile),
+                            self.__remotefsInterface.basename(qmFile),
                         )
-                    )
+                    else:
+                        qmFile = self.getRelativePath(
+                            os.path.join(
+                                self.__pdata["TRANSLATIONSBINPATH"],
+                                os.path.basename(qmFile),
+                            )
+                        )
                 self.__model.removeItem(qmFile)
                 self.__pdata["TRANSLATIONS"].remove(qmFile)
         self.setDirty(True)
@@ -1853,13 +1911,18 @@
         qmFile = self.__binaryTranslationFile(langFile)
 
         try:
-            fn = os.path.join(self.ppath, langFile)
-            if os.path.exists(fn):
-                os.remove(fn)
+            if FileSystemUtilities.isRemoteFileName(self.ppath):
+                fn = self.__remotefsInterface.join(self.ppath, langFile)
+                if self.__remotefsInterface.exists(fn):
+                    self.__remotefsInterface.remove(fn)
+            else:
+                fn = os.path.join(self.ppath, langFile)
+                if os.path.exists(fn):
+                    os.remove(fn)
         except OSError as err:
             EricMessageBox.critical(
                 self.ui,
-                self.tr("Delete translation"),
+                self.tr("Delete Translation"),
                 self.tr(
                     "<p>The selected translation file <b>{0}</b> could not be"
                     " deleted.</p><p>Reason: {1}</p>"
@@ -1910,14 +1973,22 @@
 
         # make it relative to the project root, if it starts with that path
         # assume relative paths are relative to the project root
-        newfn = self.getRelativePath(fn) if os.path.isabs(fn) else fn
-        newdir = os.path.dirname(newfn)
+        if FileSystemUtilities.isRemoteFileName(self.ppath):
+            newfn = self.getRelativePath(fn) if fn.startswith(self.ppath) else fn
+            newdir = self.__remotefsInterface.dirname(newfn)
+        else:
+            newfn = self.getRelativePath(fn) if os.path.isabs(fn) else fn
+            newdir = os.path.dirname(newfn)
 
         if isSourceFile:
             filetype = "SOURCES"
         else:
             filetype = "OTHERS"
-            bfn = os.path.basename(newfn)
+            bfn = (
+                self.__remotefsInterface.basename(newfn)
+                if FileSystemUtilities.isRemoteFileName(self.ppath)
+                else os.path.basename(newfn)
+            )
             if fnmatch.fnmatch(bfn, "*.ts") or fnmatch.fnmatch(bfn, "*.qm"):
                 filetype = "TRANSLATIONS"
             else:
@@ -1974,6 +2045,7 @@
         @param startdir start directory for the selection dialog
         @type str
         """
+        # TODO: adapt to remote server
         from .AddFileDialog import AddFileDialog
 
         if not startdir:
@@ -2038,6 +2110,7 @@
         @param quiet flag indicating quiet operations
         @type bool
         """
+        # TODO: adapt to remote server
         # get all relevant filename patterns
         patterns = []
         ignorePatterns = []
@@ -2121,6 +2194,7 @@
         @param target target directory
         @type str
         """
+        # TODO: adapt to remote server
         # first perform the addition of source
         self.__addSingleDirectory(filetype, source, target, True)
 
@@ -2159,6 +2233,7 @@
         @param startdir start directory for the selection dialog
         @type str
         """
+        # TODO: adapt to remote server
         from .AddDirectoryDialog import AddDirectoryDialog
 
         if not startdir:
@@ -2199,6 +2274,7 @@
         @param fn file name or directory name to add
         @type str
         """
+        # TODO: adapt to remote server
         if fn:
             # if it is below the project directory, make it relative to that
             fn = self.getRelativePath(fn)
@@ -2244,6 +2320,7 @@
         @return flag indicating success
         @rtype bool
         """
+        # TODO: adapt to remote server
         fn = self.getRelativePath(oldfn)
         isSourceFile = fn in self.__pdata["SOURCES"]
 
@@ -2302,6 +2379,7 @@
                 even if it doesn't have the source extension
         @type bool
         """
+        # TODO: adapt to remote server
         fn = self.getRelativePath(oldname)
         if os.path.dirname(oldname) == os.path.dirname(newname):
             if self.__isInPdata(oldname):
@@ -2324,6 +2402,7 @@
         @return list of files starting with a common prefix
         @rtype list of str
         """
+        # TODO: adapt to remote server
         filelist = []
         start = self.getRelativePath(start)
         for fileCategory in [
@@ -3423,9 +3502,6 @@
         """
         if self.isDirty():
             if len(self.pfile) > 0:
-                if self.pfile.endswith(".e4p"):
-                    self.pfile = self.pfile.replace(".e4p", ".epj")
-                    self.__syncRecent()
                 ok = self.__writeProject()
             else:
                 ok = self.saveProjectAs()
@@ -3927,8 +4003,7 @@
         @rtype str
         """
         if self.pfile:
-            name = os.path.splitext(self.pfile)[0]
-            return os.path.basename(name)
+            return self.name
         else:
             return ""
 
@@ -3939,7 +4014,10 @@
         @return path of the management directory
         @rtype str
         """
-        return os.path.join(self.ppath, ".eric7project")
+        if FileSystemUtilities.isRemoteFileName(self.ppath):
+            return self.__remotefsInterface.join(self.ppath, ".eric7project")
+        else:
+            return os.path.join(self.ppath, ".eric7project")
 
     def createProjectManagementDir(self):
         """
@@ -3949,8 +4027,10 @@
         """
         # create management directory if not present
         mgmtDir = self.getProjectManagementDir()
-        if not os.path.exists(mgmtDir):
-            os.makedirs(mgmtDir)
+        if FileSystemUtilities.isRemoteFileName(mgmtDir):
+            self.__remotefsInterface.makedirs(mgmtDir, exist_ok=True)
+        else:
+            os.makedirs(mgmtDir, exist_ok=True)
 
     def getHash(self):
         """
@@ -3976,7 +4056,13 @@
             return ""
 
         try:
-            return str(pathlib.Path(path).relative_to(self.ppath))
+            if FileSystemUtilities.isRemoteFileName(self.ppath):
+                if self.__remotefsInterface.separator() == "\\":
+                    return str(pathlib.PureWindowsPath(path).relative_to(self.ppath))
+                else:
+                    return str(pathlib.PurePosixPath(path).relative_to(self.ppath))
+            else:
+                return str(pathlib.PurePath(path).relative_to(self.ppath))
         except ValueError:
             return path
 
@@ -3991,7 +4077,12 @@
             belong to the project
         @rtype str
         """
-        return FileSystemUtilities.fromNativeSeparators(self.getRelativePath(path))
+        if FileSystemUtilities.isRemoteFileName(self.ppath):
+            return self.__remotefsInterface.fromNativeSeparators(
+                self.getRelativePath(path)
+            )
+        else:
+            return FileSystemUtilities.fromNativeSeparators(self.getRelativePath(path))
 
     def getAbsolutePath(self, fn):
         """
@@ -4003,8 +4094,11 @@
         @return absolute path
         @rtype str
         """
-        if not os.path.isabs(fn):
-            fn = os.path.join(self.ppath, fn)
+        if not fn.startswith(self.ppath):
+            if FileSystemUtilities.isRemoteFileName(self.ppath):
+                fn = self.__remotefsInterface.join(self.ppath, fn)
+            else:
+                fn = os.path.join(self.ppath, fn)
         return fn
 
     def getAbsoluteUniversalPath(self, fn):
@@ -4017,8 +4111,15 @@
         @return absolute path
         @rtype str
         """
-        if not os.path.isabs(fn):
-            fn = os.path.join(self.ppath, FileSystemUtilities.toNativeSeparators(fn))
+        if not fn.startswith(self.ppath):
+            if FileSystemUtilities.isRemoteFileName(self.ppath):
+                fn = self.__remotefsInterface.join(
+                    self.ppath, self.__remotefsInterface.fromNativeSeparators(fn)
+                )
+            else:
+                fn = os.path.join(
+                    self.ppath, FileSystemUtilities.fromNativeSeparators(fn)
+                )
         return fn
 
     def getEolString(self):
@@ -4241,7 +4342,11 @@
         @return flag indicating membership
         @rtype bool
         """
-        newfn = os.path.abspath(fn)
+        newfn = (
+            fn
+            if FileSystemUtilities.isRemoteFileName(fn)
+            else os.path.abspath(fn)
+        )
         newfn = self.getRelativePath(newfn)
         if newfn in self.__pdata[group] or (
             group == "OTHERS"
@@ -4327,6 +4432,25 @@
         act.triggered.connect(self.openProject)
         self.actions.append(act)
 
+        self.openRemoteAct = EricAction(
+            self.tr("Open remote project"),
+            EricPixmapCache.getIcon("projectOpen-remote"),
+            self.tr("Open (Remote)..."),
+            0,
+            0,
+            self.actGrp1,
+            "project_open_remote",
+        )
+        self.openRemoteAct.setStatusTip(self.tr("Open an existing remote project"))
+        self.openRemoteAct.setWhatsThis(
+            self.tr(
+                "<b>Open (Remote)...</b><p>This opens an existing remote project.</p>"
+            )
+        )
+        self.openRemoteAct.triggered.connect(self.__openRemoteProject)
+        self.actions.append(self.openRemoteAct)
+        self.openRemoteAct.setEnabled(False)  # server is not connected initially
+
         self.reloadAct = EricAction(
             self.tr("Reload project"),
             EricPixmapCache.getIcon("projectReload"),
@@ -7294,6 +7418,34 @@
             # the configuration file does not exist or is invalid JSON
             self.__initVenvConfiguration()
 
+    #############################################################################
+    ## Below are methods implementing the support for 'eric-ide server projects
+    #############################################################################
+
+    @pyqtSlot(bool)
+    def remoteConnectionChanged(self, connected):
+        """
+        Public slot to handle a change of the 'eric-ide' server connection state.
+
+        @param connected flag indicating the connection state
+        @type bool
+        """
+        self.openRemoteAct.setEnabled(connected)
+
+    @pyqtSlot()
+    def __openRemoteProject(self):
+        """
+        Private slot to open a project of an 'eric-ide' server.
+        """
+        # TODO: not implemented yet
+        fn = EricServerFileDialog.getOpenFileName(
+            self.parent(),
+            self.tr("Open project"),
+            "",
+            self.tr("Project Files (*.epj)"),
+        )
+        if fn:
+            self.openProject(fn=fn)
 
 #
 # eflag: noqa = M601

eric ide

mercurial