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