eric6/Project/Project.py

branch
maintenance
changeset 8142
43248bafe9b2
parent 8043
0acf98cd089a
parent 8140
61113590d4ab
child 8176
31965986ecd1
--- a/eric6/Project/Project.py	Mon Feb 01 10:38:43 2021 +0100
+++ b/eric6/Project/Project.py	Tue Mar 02 17:12:08 2021 +0100
@@ -41,6 +41,14 @@
 import Preferences
 import Utilities
 
+from .ProjectFile import ProjectFile
+from .UserProjectFile import UserProjectFile
+from .DebuggerPropertiesFile import DebuggerPropertiesFile
+
+from Sessions.SessionFile import SessionFile
+
+from Tasks.TasksFile import TasksFile
+
 
 class Project(QObject):
     """
@@ -184,6 +192,12 @@
         
         self.__initData()
         
+        self.__projectFile = ProjectFile(self)
+        self.__userProjectFile = UserProjectFile(self)
+        self.__debuggerPropertiesFile = DebuggerPropertiesFile(self)
+        self.__sessionFile = SessionFile(False)
+        self.__tasksFile = TasksFile(False)
+        
         self.recent = []
         self.__loadRecent()
         
@@ -582,6 +596,7 @@
             "README": "OTHERS",
             "README.*": "OTHERS",
             "*.e4p": "OTHERS",
+            "*.epj": "OTHERS",
             "GNUmakefile": "OTHERS",
             "makefile": "OTHERS",
             "Makefile": "OTHERS",
@@ -787,34 +802,50 @@
         
     def __readProject(self, fn):
         """
-        Private method to read in a project (.e4p) file.
+        Private method to read in a project (.epj or .e4p) file.
         
         @param fn filename of the project file to be read (string)
         @return flag indicating success
         """
-        f = QFile(fn)
-        if f.open(QIODevice.ReadOnly):
-            from E5XML.ProjectReader import ProjectReader
-            reader = ProjectReader(f, self)
-            reader.readXML()
-            res = not reader.hasError()
-            f.close()
+        if os.path.splitext(fn)[1] == ".epj":
+            # new JSON based format
+            with E5OverrideCursor():
+                res = self.__projectFile.readFile(fn)
         else:
-            E5MessageBox.critical(
-                self.ui,
-                self.tr("Read project file"),
-                self.tr(
-                    "<p>The project file <b>{0}</b> could not be read.</p>")
-                .format(fn))
-            return False
-        
-        self.pfile = os.path.abspath(fn)
-        self.ppath = os.path.abspath(os.path.dirname(fn))
-        
-        # insert filename into list of recently opened projects
-        self.__syncRecent()
+            # old XML based format
+            f = QFile(fn)
+            if f.open(QIODevice.ReadOnly):
+                from E5XML.ProjectReader import ProjectReader
+                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.Sha1).toHex(),
+                        encoding="utf-8")
+                    self.pdata["HASH"] = hashStr
+                    self.setDirty(True)
+            else:
+                E5MessageBox.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
         
         if res:
+            self.pfile = os.path.abspath(fn)
+            self.ppath = os.path.abspath(os.path.dirname(fn))
+            
+            # insert filename into list of recently opened projects
+            self.__syncRecent()
+            
             if self.pdata["TRANSLATIONPATTERN"]:
                 self.translationsRoot = self.pdata["TRANSLATIONPATTERN"].split(
                     "%language%")[0]
@@ -859,15 +890,6 @@
                 if dn not in self.otherssubdirs:
                     self.otherssubdirs.append(dn)
             
-            # 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.Sha1).toHex(),
-                    encoding="utf-8")
-                self.pdata["HASH"] = hashStr
-                self.setDirty(True)
-            
         return res
 
     def __writeProject(self, fn=None):
@@ -896,20 +918,26 @@
         if fn is None:
             fn = self.pfile
         
-        f = QFile(fn)
-        if f.open(QIODevice.WriteOnly):
-            from E5XML.ProjectWriter import ProjectWriter
-            ProjectWriter(f, os.path.splitext(
-                os.path.basename(fn))[0]).writeXML()
-            res = True
+        if os.path.splitext(fn)[1] == ".epj":
+            # new JSON based format
+            with E5OverrideCursor():
+                res = self.__projectFile.writeFile(fn)
         else:
-            E5MessageBox.critical(
-                self.ui,
-                self.tr("Save project file"),
-                self.tr(
-                    "<p>The project file <b>{0}</b> could not be"
-                    " written.</p>").format(fn))
-            res = False
+            # old XML based format
+            f = QFile(fn)
+            if f.open(QIODevice.WriteOnly):
+                from E5XML.ProjectWriter import ProjectWriter
+                ProjectWriter(f, os.path.splitext(
+                    os.path.basename(fn))[0]).writeXML()
+                res = True
+            else:
+                E5MessageBox.critical(
+                    self.ui,
+                    self.tr("Save Project File"),
+                    self.tr(
+                        "<p>The project file <b>{0}</b> could not be"
+                        " written.</p>").format(fn))
+                res = False
         
         if res:
             self.pfile = os.path.abspath(fn)
@@ -924,52 +952,50 @@
         
     def __readUserProperties(self):
         """
-        Private method to read in the user specific project file (.e4q).
+        Private method to read in the user specific project file (.eqj or
+        .e4q).
+        """
+        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)
+        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.ReadOnly):
+                    from E5XML.UserProjectReader import UserProjectReader
+                    reader = UserProjectReader(f, self)
+                    reader.readXML()
+                    f.close()
+                else:
+                    E5MessageBox.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))
+        
+    def __writeUserProperties(self):
+        """
+        Private method to write the user specific project data to a JSON file.
         """
         if self.pfile is None:
             return
         
         fn, ext = os.path.splitext(os.path.basename(self.pfile))
-        fn = os.path.join(self.getProjectManagementDir(), '{0}.e4q'.format(fn))
-        if os.path.exists(fn):
-            f = QFile(fn)
-            if f.open(QIODevice.ReadOnly):
-                from E5XML.UserProjectReader import UserProjectReader
-                reader = UserProjectReader(f, self)
-                reader.readXML()
-                f.close()
-            else:
-                E5MessageBox.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))
-        
-    def __writeUserProperties(self):
-        """
-        Private method to write the project data to an XML file.
-        """
-        if self.pfile is None:
-            return
-        
-        fn, ext = os.path.splitext(os.path.basename(self.pfile))
-        fn = os.path.join(self.getProjectManagementDir(), '{0}.e4q'.format(fn))
-        
-        f = QFile(fn)
-        if f.open(QIODevice.WriteOnly):
-            from E5XML.UserProjectWriter import UserProjectWriter
-            UserProjectWriter(
-                f, os.path.splitext(os.path.basename(fn))[0]).writeXML()
-            f.close()
-        else:
-            E5MessageBox.critical(
-                self.ui,
-                self.tr("Save user project properties"),
-                self.tr(
-                    "<p>The user specific project properties file <b>{0}</b>"
-                    " could not be written.</p>").format(fn))
-        
+        fn = os.path.join(self.getProjectManagementDir(), '{0}.eqj'.format(fn))
+        
+        with E5OverrideCursor():
+            self.__userProjectFile.writeFile(fn)
+    
     def __showContextMenuSession(self):
         """
         Private slot called before the Session menu is shown.
@@ -980,9 +1006,9 @@
         else:
             fn, ext = os.path.splitext(os.path.basename(self.pfile))
             fn_new = os.path.join(self.getProjectManagementDir(),
-                                  '{0}.e5s'.format(fn))
+                                  '{0}.esj'.format(fn))
             fn_old = os.path.join(self.getProjectManagementDir(),
-                                  '{0}.e4s'.format(fn))
+                                  '{0}.e5s'.format(fn))
             enable = os.path.exists(fn_new) or os.path.exists(fn_old)
         self.sessActGrp.findChild(
             QAction, "project_load_session").setEnabled(enable)
@@ -992,46 +1018,7 @@
     @pyqtSlot()
     def __readSession(self, quiet=False, indicator=""):
         """
-        Private method to read in the project session file (.e5s or .e4s).
-        
-        @param quiet flag indicating quiet operations.
-                If this flag is true, no errors are reported.
-        @param indicator indicator string (string)
-        """
-        if self.pfile is None:
-            if not quiet:
-                E5MessageBox.critical(
-                    self.ui,
-                    self.tr("Read project session"),
-                    self.tr("Please save the project first."))
-            return
-            
-        fn1, ext = os.path.splitext(os.path.basename(self.pfile))
-        fn = os.path.join(self.getProjectManagementDir(),
-                          '{0}{1}.e5s'.format(fn1, indicator))
-        if not os.path.exists(fn):
-            fn = os.path.join(self.getProjectManagementDir(),
-                              '{0}{1}.e4s'.format(fn1, indicator))
-        
-        f = QFile(fn)
-        if f.open(QIODevice.ReadOnly):
-            from E5XML.SessionReader import SessionReader
-            reader = SessionReader(f, False)
-            reader.readXML(quiet=quiet)
-            f.close()
-        else:
-            if not quiet:
-                E5MessageBox.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))
-        
-    @pyqtSlot()
-    def __writeSession(self, quiet=False, indicator=""):
-        """
-        Private method to write the session data to an XML file (.e5s).
+        Private method to read in the project session file (.esj or .e5s).
         
         @param quiet flag indicating quiet operations.
                 If this flag is true, no errors are reported.
@@ -1041,29 +1028,59 @@
             if not quiet:
                 E5MessageBox.critical(
                     self.ui,
-                    self.tr("Save project session"),
+                    self.tr("Read Project Session"),
+                    self.tr("Please save the project first."))
+            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 os.path.exists(fn):
+                f = QFile(fn)
+                if f.open(QIODevice.ReadOnly):
+                    from E5XML.SessionReader import SessionReader
+                    reader = SessionReader(f, False)
+                    reader.readXML(quiet=quiet)
+                    f.close()
+                else:
+                    if not quiet:
+                        E5MessageBox.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))
+        
+    @pyqtSlot()
+    def __writeSession(self, quiet=False, indicator=""):
+        """
+        Private method to write the session data to an XML file (.esj).
+        
+        @param quiet flag indicating quiet operations.
+            If this flag is true, no errors are reported.
+        @param indicator indicator string (string)
+        """
+        if self.pfile is None:
+            if not quiet:
+                E5MessageBox.critical(
+                    self.ui,
+                    self.tr("Save Project Session"),
                     self.tr("Please save the project first."))
             return
         
         fn, ext = os.path.splitext(os.path.basename(self.pfile))
         fn = os.path.join(self.getProjectManagementDir(),
-                          '{0}{1}.e5s'.format(fn, indicator))
-        
-        f = QFile(fn)
-        if f.open(QIODevice.WriteOnly):
-            from E5XML.SessionWriter import SessionWriter
-            SessionWriter(
-                f, os.path.splitext(os.path.basename(fn))[0]).writeXML()
-            f.close()
-        else:
-            if not quiet:
-                E5MessageBox.critical(
-                    self.ui,
-                    self.tr("Save project session"),
-                    self.tr(
-                        "<p>The project session file <b>{0}</b> could not be"
-                        " written.</p>").format(fn))
-        
+                          '{0}{1}.esj'.format(fn, indicator))
+        
+        self.__sessionFile.writeFile(fn)
+    
     def __deleteSession(self):
         """
         Private method to delete the session file.
@@ -1071,88 +1088,76 @@
         if self.pfile is None:
             E5MessageBox.critical(
                 self.ui,
-                self.tr("Delete project session"),
+                self.tr("Delete Project Session"),
                 self.tr("Please save the project first."))
             return
             
         fname, ext = os.path.splitext(os.path.basename(self.pfile))
         
-        for fn in [
-            os.path.join(
-                self.getProjectManagementDir(), "{0}.e5s".format(fname)),
-            os.path.join(
-                self.getProjectManagementDir(), "{0}.e4s".format(fname))]:
+        for ext in (".esj", ".e5s", ".e4s"):
+            fn = os.path.join(
+                self.getProjectManagementDir(), "{0}{1}".format(fname, ext)),
             if os.path.exists(fn):
                 try:
                     os.remove(fn)
                 except OSError:
                     E5MessageBox.critical(
                         self.ui,
-                        self.tr("Delete project session"),
+                        self.tr("Delete Project Session"),
                         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 (.e6t).
+        Private method to read in the project tasks file (.etj or .e6t).
         """
         if self.pfile is None:
             E5MessageBox.critical(
                 self.ui,
-                self.tr("Read tasks"),
+                self.tr("Read Tasks"),
                 self.tr("Please save the project first."))
             return
-            
+        
         base, ext = os.path.splitext(os.path.basename(self.pfile))
         fn = os.path.join(self.getProjectManagementDir(),
-                          '{0}.e6t'.format(base))
-        if not os.path.exists(fn):
-            # try again with the old extension
+                          '{0}.etj'.format(base))
+        if os.path.exists(fn):
+            # try new style JSON file first
+            self.__tasksFile.readFile(fn)
+        else:
+            # try old style XML file second
             fn = os.path.join(self.getProjectManagementDir(),
-                              '{0}.e4t'.format(base))
-            if not os.path.exists(fn):
-                return
-        f = QFile(fn)
-        if f.open(QIODevice.ReadOnly):
-            from E5XML.TasksReader import TasksReader
-            reader = TasksReader(f, True)
-            reader.readXML()
-            f.close()
-        else:
-            E5MessageBox.critical(
-                self.ui,
-                self.tr("Read tasks"),
-                self.tr(
-                    "<p>The tasks file <b>{0}</b> could not be read.</p>")
-                .format(fn))
+                              '{0}.e6t'.format(base))
+            if os.path.exists(fn):
+                f = QFile(fn)
+                if f.open(QIODevice.ReadOnly):
+                    from E5XML.TasksReader import TasksReader
+                    reader = TasksReader(f, True)
+                    reader.readXML()
+                    f.close()
+                else:
+                    E5MessageBox.critical(
+                        self.ui,
+                        self.tr("Read Tasks"),
+                        self.tr(
+                            "<p>The tasks file <b>{0}</b> could not be read."
+                            "</p>")
+                        .format(fn))
         
     def writeTasks(self):
         """
-        Public method to write the tasks data to an XML file (.e6t).
+        Public method to write the tasks data to a JSON file (.etj).
         """
         if self.pfile is None:
             return
             
         fn, ext = os.path.splitext(os.path.basename(self.pfile))
         
-        fn = os.path.join(self.getProjectManagementDir(), '{0}.e6t'.format(fn))
-        f = QFile(fn)
-        ok = f.open(QIODevice.WriteOnly)
-        if not ok:
-            E5MessageBox.critical(
-                self.ui,
-                self.tr("Save tasks"),
-                self.tr(
-                    "<p>The tasks file <b>{0}</b> could not be written.</p>")
-                .format(fn))
-            return
-        
-        from E5XML.TasksWriter import TasksWriter
-        TasksWriter(
-            f, True, os.path.splitext(os.path.basename(fn))[0]).writeXML()
-        f.close()
-        
+        fn = os.path.join(self.getProjectManagementDir(),
+                          '{0}.etj'.format(fn))
+        self.__tasksFile.writeFile(fn)
+    
     def __showContextMenuDebugger(self):
         """
         Private slot called before the Debugger menu is shown.
@@ -1162,8 +1167,13 @@
             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}.e4d'.format(fn))
+                              '{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)
         self.dbgActGrp.findChild(
             QAction, "project_debugger_properties_load").setEnabled(enable)
@@ -1173,7 +1183,54 @@
     @pyqtSlot()
     def __readDebugProperties(self, quiet=False):
         """
-        Private method to read in the project debugger properties file (.e4d).
+        Private method to read in the project debugger properties file
+        (.edj or .e4d).
+        
+        @param quiet flag indicating quiet operations.
+            If this flag is true, no errors are reported.
+        """
+        if self.pfile is None:
+            if not quiet:
+                E5MessageBox.critical(
+                    self.ui,
+                    self.tr("Read Debugger Properties"),
+                    self.tr("Please save the project first."))
+            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
+            self.__debuggerPropertiesFile.readFile(fn)
+        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.ReadOnly):
+                from E5XML.DebuggerPropertiesReader import (
+                    DebuggerPropertiesReader
+                )
+                reader = DebuggerPropertiesReader(f, self)
+                reader.readXML(quiet=quiet)
+                f.close()
+                self.debugPropertiesLoaded = True
+                self.debugPropertiesChanged = False
+            else:
+                if not quiet:
+                    E5MessageBox.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))
+    
+    @pyqtSlot()
+    def __writeDebugProperties(self, quiet=False):
+        """
+        Private method to write the project debugger properties file (.edj).
         
         @param quiet flag indicating quiet operations.
                 If this flag is true, no errors are reported.
@@ -1182,87 +1239,40 @@
             if not quiet:
                 E5MessageBox.critical(
                     self.ui,
-                    self.tr("Read debugger properties"),
+                    self.tr("Save Debugger Properties"),
                     self.tr("Please save the project first."))
             return
-            
+        
         fn, ext = os.path.splitext(os.path.basename(self.pfile))
-        fn = os.path.join(self.getProjectManagementDir(), '{0}.e4d'.format(fn))
-        
-        f = QFile(fn)
-        if f.open(QIODevice.ReadOnly):
-            from E5XML.DebuggerPropertiesReader import DebuggerPropertiesReader
-            reader = DebuggerPropertiesReader(f, self)
-            reader.readXML(quiet=quiet)
-            f.close()
-            self.debugPropertiesLoaded = True
-            self.debugPropertiesChanged = False
-        else:
-            if not quiet:
-                E5MessageBox.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))
-        
-    @pyqtSlot()
-    def __writeDebugProperties(self, quiet=False):
-        """
-        Private method to write the project debugger properties file (.e4d).
-        
-        @param quiet flag indicating quiet operations.
-                If this flag is true, no errors are reported.
-        """
-        if self.pfile is None:
-            if not quiet:
-                E5MessageBox.critical(
-                    self.ui,
-                    self.tr("Save debugger properties"),
-                    self.tr("Please save the project first."))
-            return
-            
-        fn, ext = os.path.splitext(os.path.basename(self.pfile))
-        fn = os.path.join(self.getProjectManagementDir(), '{0}.e4d'.format(fn))
-        
-        f = QFile(fn)
-        if f.open(QIODevice.WriteOnly):
-            from E5XML.DebuggerPropertiesWriter import DebuggerPropertiesWriter
-            DebuggerPropertiesWriter(
-                f, os.path.splitext(os.path.basename(fn))[0]).writeXML()
-            f.close()
-            self.debugPropertiesChanged = False
-        else:
-            if not quiet:
-                E5MessageBox.critical(
-                    self.ui,
-                    self.tr("Save debugger properties"),
-                    self.tr(
-                        "<p>The project debugger properties file <b>{0}</b>"
-                        " could not be written.</p>").format(fn))
-        
+        fn = os.path.join(self.getProjectManagementDir(), '{0}.edj'.format(fn))
+        
+        with E5OverrideCursor():
+            self.__debuggerPropertiesFile.writeFile(fn)
+    
     def __deleteDebugProperties(self):
         """
-        Private method to delete the project debugger properties file (.e4d).
+        Private method to delete the project debugger properties file
+        (.edj or .e4d).
         """
         if self.pfile is None:
             E5MessageBox.critical(
                 self.ui,
-                self.tr("Delete debugger properties"),
+                self.tr("Delete Debugger Properties"),
                 self.tr("Please save the project first."))
             return
             
         fname, ext = os.path.splitext(os.path.basename(self.pfile))
         
-        for fn in [os.path.join(self.getProjectManagementDir(),
-                                "{0}.e4d".format(fname))]:
+        for ext in (".edj", ".e4d"):
+            fn = os.path.join(self.getProjectManagementDir(),
+                              "{0}{1}".format(fname, ext))
             if os.path.exists(fn):
                 try:
                     os.remove(fn)
                 except OSError:
                     E5MessageBox.critical(
                         self.ui,
-                        self.tr("Delete debugger properties"),
+                        self.tr("Delete Debugger Properties"),
                         self.tr(
                             "<p>The project debugger properties file"
                             " <b>{0}</b> could not be deleted.</p>")
@@ -2187,7 +2197,7 @@
         
     def removeDirectory(self, dn):
         """
-        Public slot to remove a directory from the project.
+        Public method to remove a directory from the project.
         
         The directory is not deleted from the project directory.
         
@@ -2216,7 +2226,7 @@
         
     def deleteFile(self, fn):
         """
-        Public slot to delete a file from the project directory.
+        Public method to delete a file from the project directory.
         
         @param fn filename to be deleted from the project
         @return flag indicating success (boolean)
@@ -2260,7 +2270,7 @@
         
     def deleteDirectory(self, dn):
         """
-        Public slot to delete a directory from the project directory.
+        Public method to delete a directory from the project directory.
         
         @param dn directory name to be removed from the project
         @return flag indicating success (boolean)
@@ -2396,7 +2406,7 @@
                             self.ppath, self.pdata["MAINSCRIPT"])
                     else:
                         ms = self.pdata["MAINSCRIPT"]
-                    os.makedirs(os.path.dirname(ms))
+                    os.makedirs(os.path.dirname(ms), exist_ok=True)
                     with open(ms, "w"):
                         pass
                     self.appendFile(ms, True)
@@ -2408,7 +2418,7 @@
                             mf = os.path.join(self.ppath, mf)
                     else:
                         mf = os.path.join(self.ppath, Project.DefaultMakefile)
-                    os.makedirs(os.path.dirname(mf))
+                    os.makedirs(os.path.dirname(mf), exist_ok=True)
                     with open(mf, "w"):
                         pass
                     self.appendFile(mf)
@@ -2417,12 +2427,12 @@
                 if not self.translationsRoot.endswith(os.sep):
                     tpd = os.path.dirname(tpd)
                 if not os.path.isdir(tpd):
-                    os.makedirs(tpd)
+                    os.makedirs(tpd, exist_ok=True)
                 if self.pdata["TRANSLATIONSBINPATH"]:
                     tpd = os.path.join(
                         self.ppath, self.pdata["TRANSLATIONSBINPATH"])
                     if not os.path.isdir(tpd):
-                        os.makedirs(tpd)
+                        os.makedirs(tpd, exist_ok=True)
                 
                 # create management directory if not present
                 self.createProjectManagementDir()
@@ -2940,7 +2950,7 @@
                 self.tr("Open project"),
                 Preferences.getMultiProject("Workspace") or
                 Utilities.getHomeDir(),
-                self.tr("Project Files (*.e4p)"))
+                self.tr("Project Files (*.epj);;XML Project Files (*.e4p)"))
         
         QApplication.processEvents()
         
@@ -3110,6 +3120,9 @@
         """
         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()
@@ -3125,7 +3138,7 @@
         
         @return flag indicating success (boolean)
         """
-        defaultFilter = self.tr("Project Files (*.e4p)")
+        defaultFilter = self.tr("Project Files (*.epj)")
         if self.ppath:
             defaultPath = self.ppath
         else:
@@ -3137,7 +3150,8 @@
             self.parent(),
             self.tr("Save project as"),
             defaultPath,
-            self.tr("Project Files (*.e4p)"),
+            self.tr("Project Files (*.epj);;"
+                    "XML Project Files (*.e4p)"),
             defaultFilter,
             E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
         
@@ -4031,9 +4045,9 @@
         self.filetypesAct = E5Action(
             self.tr('Filetype Associations'),
             self.tr('Filetype Associations...'), 0, 0,
-            self, 'project_filetype_associatios')
+            self, 'project_filetype_associations')
         self.filetypesAct.setStatusTip(
-            self.tr('Show the project filetype associations'))
+            self.tr('Show the project file type associations'))
         self.filetypesAct.setWhatsThis(self.tr(
             """<b>Filetype Associations...</b>"""
             """<p>This shows a dialog to edit the file type associations of"""
@@ -4049,7 +4063,7 @@
         self.lexersAct = E5Action(
             self.tr('Lexer Associations'),
             self.tr('Lexer Associations...'), 0, 0,
-            self, 'project_lexer_associatios')
+            self, 'project_lexer_associations')
         self.lexersAct.setStatusTip(self.tr(
             'Show the project lexer associations (overriding defaults)'))
         self.lexersAct.setWhatsThis(self.tr(

eric ide

mercurial