--- a/UI/UserInterface.py Sat Dec 03 18:20:38 2016 +0100 +++ b/UI/UserInterface.py Sun Dec 04 18:40:41 2016 +0100 @@ -267,6 +267,7 @@ self.findFilesDialog = None self.replaceFilesDialog = None self.__notification = None + self.__readingSession = False # now setup the connections splash.showMessage(self.tr("Setting up connections...")) @@ -370,9 +371,15 @@ self.project.projectClosed.connect(self.__projectClosed) self.project.projectClosed.connect( self.backgroundService.preferencesOrProjectChanged) + self.project.projectOpened.connect(self.__writeCrashSession) + self.project.projectClosed.connect(self.__writeCrashSession) self.multiProject.multiProjectOpened.connect( self.__activateMultiProjectBrowser) + self.multiProject.multiProjectOpened.connect( + self.__writeCrashSession) + self.multiProject.multiProjectClosed.connect( + self.__writeCrashSession) self.debuggerUI.resetUI.connect(self.viewmanager.handleResetUI) self.debuggerUI.resetUI.connect(self.debugViewer.handleResetUI) @@ -450,6 +457,10 @@ self.projectBrowser.handleEditorLineChanged) self.viewmanager.checkActions.connect( self.cooperation.checkEditorActions) + self.viewmanager.editorOpened.connect(self.__writeCrashSession) + self.viewmanager.editorClosed.connect(self.__writeCrashSession) + self.viewmanager.editorRenamed.connect(self.__writeCrashSession) + self.viewmanager.editorChanged.connect(self.__writeCrashSession) self.shell.zoomValueChanged.connect(self.viewmanager.zoomValueChanged) @@ -536,7 +547,7 @@ self.currentProfile = None self.shutdownCalled = False - self.inCloseEevent = False + self.inCloseEvent = False # now redirect stdout and stderr # TODO: release - reenable redirection @@ -1088,6 +1099,10 @@ .e4p, then it is opened as a project file. If it ends in .e4m or .e5m, it is opened as a multiproject. """ + # check and optionally read a crash session and ignore any arguments + if self.__readCrashSession(): + return + # no args, return if args is None: if not self.__noOpenAtStartup: @@ -1257,6 +1272,32 @@ self.exitAct.setMenuRole(QAction.QuitRole) self.actions.append(self.exitAct) + self.saveSessionAct = E5Action( + self.tr('Save session'), + self.tr('Save session...'), + 0, 0, self, 'save_session_to_file') + self.saveSessionAct.setStatusTip(self.tr('Save session')) + self.saveSessionAct.setWhatsThis(self.tr( + """<b>Save session...</b>""" + """<p>This saves the current session to disk. A dialog is""" + """ opened to select the file name.</p>""" + )) + self.saveSessionAct.triggered.connect(self.__saveSessionToFile) + self.actions.append(self.saveSessionAct) + + self.loadSessionAct = E5Action( + self.tr('Load session'), + self.tr('Load session...'), + 0, 0, self, 'load_session_from_file') + self.loadSessionAct.setStatusTip(self.tr('Load session')) + self.loadSessionAct.setWhatsThis(self.tr( + """<b>Load session...</b>""" + """<p>This loads a session saved to disk previously. A dialog is""" + """ opened to select the file name.</p>""" + )) + self.loadSessionAct.triggered.connect(self.__loadSessionFromFile) + self.actions.append(self.loadSessionAct) + self.newWindowAct = E5Action( self.tr('New Window'), UI.PixmapCache.getIcon("newWindow.png"), @@ -2465,6 +2506,9 @@ self.__menus["file"] = self.viewmanager.initFileMenu() mb.addMenu(self.__menus["file"]) self.__menus["file"].addSeparator() + self.__menus["file"].addAction(self.saveSessionAct) + self.__menus["file"].addAction(self.loadSessionAct) + self.__menus["file"].addSeparator() self.__menus["file"].addAction(self.exitAct) act = self.__menus["file"].actions()[0] sep = self.__menus["file"].insertSeparator(act) @@ -5781,54 +5825,168 @@ "<p>The tasks file <b>{0}</b> could not be read.</p>") .format(fn)) - def __writeSession(self): + def __writeSession(self, filename="", crashSession=False): """ Private slot to write the session data to an XML file (.e5s). - """ - fn = os.path.join(Utilities.getConfigDir(), "eric6session.e5s") + + @param filename name of a session file to write + @type str + @param crashSession flag indicating to write a crash session file + @type bool + @return flag indicating success + @rtype bool + """ + res = False + if filename: + fn = filename + elif crashSession: + fn = os.path.join(Utilities.getConfigDir(), + "eric6_crash_session.e5s") + else: + fn = os.path.join(Utilities.getConfigDir(), + "eric6session.e5s") f = QFile(fn) if f.open(QIODevice.WriteOnly): from E5XML.SessionWriter import SessionWriter SessionWriter(f, None).writeXML() f.close() + res = True else: E5MessageBox.critical( self, self.tr("Save session"), - self.tr( - "<p>The session file <b>{0}</b> could not be written.</p>") + self.tr("<p>The session file <b>{0}</b> could not be" + " written.</p>") .format(fn)) - - def __readSession(self): + return res + + def __readSession(self, filename=""): """ Private slot to read in the session file (.e5s or .e4s). - """ - fn = os.path.join(Utilities.getConfigDir(), "eric6session.e5s") - if not os.path.exists(fn): - fn = os.path.join(Utilities.getConfigDir(), "eric6session.e4s") + + @param filename name of a session file to read + @type str + @return flag indicating success + @rtype bool + """ + if filename: + fn = filename + else: + fn = os.path.join(Utilities.getConfigDir(), + "eric6session.e5s") if not os.path.exists(fn): + fn = os.path.join(Utilities.getConfigDir(), + "eric6session.e4s") + if not os.path.exists(fn): + E5MessageBox.critical( + self, + self.tr("Read session"), + self.tr("<p>The session file <b>{0}</b> could not" + " be read.</p>") + .format(fn)) + fn = "" + + res = False + if fn: + f = QFile(fn) + if f.open(QIODevice.ReadOnly): + from E5XML.SessionReader import SessionReader + self.__readingSession = True + reader = SessionReader(f, True) + reader.readXML() + self.__readingSession = False + f.close() + res = True + else: E5MessageBox.critical( self, self.tr("Read session"), - self.tr( - "<p>The session file <b>{0}</b> could not" - " be read.</p>") + self.tr("<p>The session file <b>{0}</b> could not be" + " read.</p>") .format(fn)) - return - - f = QFile(fn) - if f.open(QIODevice.ReadOnly): - from E5XML.SessionReader import SessionReader - reader = SessionReader(f, True) - reader.readXML() - f.close() - else: - E5MessageBox.critical( + + # Write a crash session after a session was read. + self.__writeCrashSession() + + return res + + def __saveSessionToFile(self): + """ + Private slot to save a session to disk. + """ + sessionFile, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( + self, + self.tr("Save session"), + Utilities.getHomeDir(), + self.tr("eric6 Session Files (*.e5s)"), + "") + + if not sessionFile: + return + + ext = QFileInfo(sessionFile).suffix() + if not ext: + ex = selectedFilter.split("(*")[1].split(")")[0] + if ex: + sessionFile += ex + + self.__writeSession(filename=sessionFile) + + def __loadSessionFromFile(self): + """ + Private slot to load a session from disk. + """ + sessionFile = E5FileDialog.getOpenFileName( + self, + self.tr("Load session"), + Utilities.getHomeDir(), + self.tr("eric6 Session Files (*.e5s)")) + + if not sessionFile: + return + + self.__readSession(filename=sessionFile) + + def __deleteCrashSession(self): + """ + Private slot to delete the crash session file. + """ + fn = os.path.join(Utilities.getConfigDir(), + "eric6_crash_session.e5s") + if os.path.exists(fn): + try: + os.remove(fn) + except OSError: + # ignore it silently + pass + + def __writeCrashSession(self): + """ + Private slot to write a crash session file. + """ + if not self.__readingSession: + self.__writeSession(crashSession=True) + + def __readCrashSession(self): + """ + Private method to check for and read a crash session. + + @return flag indicating a crash session file was found and read + @rtype bool + """ + res = False + fn = os.path.join(Utilities.getConfigDir(), + "eric6_crash_session.e5s") + if os.path.exists(fn): + yes = E5MessageBox.yesNo( self, - self.tr("Read session"), - self.tr( - "<p>The session file <b>{0}</b> could not be read.</p>") - .format(fn)) + self.tr("Crash Session found!"), + self.tr("""A session file of a crashed session was found.""" + """ Shall this session be restored?""")) + if yes: + res = self.__readSession(filename=fn) + + return res def showFindFileByNameDialog(self): """ @@ -6034,8 +6192,8 @@ """ if self.__shutdown(): event.accept() - if not self.inCloseEevent: - self.inCloseEevent = True + if not self.inCloseEvent: + self.inCloseEvent = True QTimer.singleShot(0, e5App().closeAllWindows) else: event.ignore() @@ -6056,7 +6214,7 @@ if not self.irc.shutdown(): return False - self.__writeSession() + sessionCreated = self.__writeSession() if not self.project.closeProject(): return False @@ -6067,6 +6225,9 @@ if not self.viewmanager.closeViewManager(): return False + if sessionCreated: + self.__deleteCrashSession() + self.__previewer.shutdown() self.shell.closeShell()