Continued implementing session support for the new web browser.

Fri, 30 Jun 2017 19:58:09 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Fri, 30 Jun 2017 19:58:09 +0200
changeset 5779
b53fabc86f3c
parent 5778
1ee0b025691e
child 5780
79d06c98c5c9

Continued implementing session support for the new web browser.

Preferences/__init__.py file | annotate | diff | comparison | revisions
WebBrowser/Session/SessionManager.py file | annotate | diff | comparison | revisions
WebBrowser/WebBrowserWindow.py file | annotate | diff | comparison | revisions
--- a/Preferences/__init__.py	Fri Jun 30 19:48:32 2017 +0200
+++ b/Preferences/__init__.py	Fri Jun 30 19:58:09 2017 +0200
@@ -1106,6 +1106,7 @@
         # Sessions
         "SessionAutoSave": True,
         "SessionAutoSaveInterval": 15,  # interval in seconds
+        "SessionLastActivePath": "",
     }
     if QWebEngineSettings:
         webBrowserDefaults["HelpViewerType"] = 1      # eric browser
--- a/WebBrowser/Session/SessionManager.py	Fri Jun 30 19:48:32 2017 +0200
+++ b/WebBrowser/Session/SessionManager.py	Fri Jun 30 19:58:09 2017 +0200
@@ -12,16 +12,41 @@
 import os
 import json
 
-from PyQt5.QtCore import pyqtSlot, QObject, QTimer, QDir
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QTimer, QDir, QFile, \
+    QFileInfo, QFileSystemWatcher
 
 import Utilities
 import Preferences
 
 
+class SessionMetaData(object):
+    """
+    Class implementing a data structure to store meta data for a session.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        self.name = ""
+        self.filePath = ""
+        self.isActive = False
+        self.isDefault = False
+        self.isBackup = False
+
+
 class SessionManager(QObject):
     """
     Class implementing the session manager.
+    
+    @signal sessionsMetaDataChanged() emitted to indicate a change of the
+        list of session meta data
     """
+    sessionsMetaDataChanged = pyqtSignal()
+    
+    SwitchSession = 1
+    CloneSession = 2
+    ReplaceSession = SwitchSession | 4
+    
     def __init__(self, parent=None):
         """
         Constructor
@@ -31,9 +56,31 @@
         """
         super(SessionManager, self).__init__(parent)
         
-        sessionsDir = QDir(self.getSessionsDirectory())
+        sessionsDirName = self.getSessionsDirectory()
+        sessionsDir = QDir(sessionsDirName)
         if not sessionsDir.exists():
-            sessionsDir.mkpath(self.getSessionsDirectory())
+            sessionsDir.mkpath(sessionsDirName)
+        
+        self.__sessionMetaData = []
+        # list containing meta data about saved sessions
+        
+        self.__sessionDefault = os.path.join(sessionsDirName, "session.json")
+        self.__sessionBackup1 = os.path.join(sessionsDirName,
+                                             "session.json.old")
+        self.__sessionBackup2 = os.path.join(sessionsDirName,
+                                             "session.json.old1")
+        
+        self.__lastActiveSession = Preferences.getWebBrowser(
+            "SessionLastActivePath")
+        if not QFile.exists(self.__lastActiveSession):
+            self.__lastActiveSession = self.__sessionDefault
+        
+        self.__sessionsDirectoryWatcher = \
+            QFileSystemWatcher([self.getSessionsDirectory()], self)
+        self.__sessionsDirectoryWatcher.directoryChanged.connect(
+            self.__sessionDirectoryChanged)
+        
+        self.__backupSavedSession()
         
         self.__autoSaveTimer = QTimer()
         self.__autoSaveTimer.setSingleShot(True)
@@ -64,7 +111,23 @@
         @return name of the default session file
         @rtype str
         """
-        return os.path.join(self.getSessionsDirectory(), "session.json")
+        return self.__sessionDefault
+    
+    def lastActiveSessionFile(self):
+        """
+        Public method to get the name of the last active session file.
+        
+        @return name of the last active session file
+        @rtype str
+        """
+        return self.__lastActiveSession
+    
+    def shutdown(self):
+        """
+        Public method to perform any shutdown actions.
+        """
+        self.__autoSaveTimer.stop()
+        self.__autoSaveSession(startTimer=False)
     
     def __initSessionSaveTimer(self):
         """
@@ -80,16 +143,22 @@
             self.__autoSaveTimer.stop()
     
     @pyqtSlot()
-    def __autoSaveSession(self):
+    def __autoSaveSession(self, startTimer=True):
         """
         Private slot to save the current session state.
+        
+        @param startTimer flag indicating to restart the timer
+        @type bool
         """
         from WebBrowser.WebBrowserWindow import WebBrowserWindow
         
         if not WebBrowserWindow.isPrivate():
-            self.writeCurrentSession(self.defaultSessionFile())
+            Preferences.setWebBrowser("SessionLastActivePath",
+                                      self.__lastActiveSession)
+            self.writeCurrentSession(self.__lastActiveSession)
         
-        self.__autoSaveTimer.start(self.__autoSaveInterval)
+        if startTimer:
+            self.__autoSaveTimer.start(self.__autoSaveInterval)
     
     def writeCurrentSession(self, sessionFileName):
         """
@@ -110,7 +179,133 @@
             data["WindowGeometry"] = bytes(geometry.toBase64()).decode("ascii")
             
             sessionData["Windows"].append(data)
+        else:
+            return
         
         sessionFile = open(sessionFileName, "w")
         json.dump(sessionData, sessionFile, indent=2)
         sessionFile.close()
+    
+    def __backupSavedSession(self):
+        """
+        Private method to backup the most recently saved session.
+        """
+        if QFile.exists(self.__lastActiveSession):
+            
+            if QFile.exists(self.__sessionBackup1):
+                QFile.remove(self.__sessionBackup2)
+                QFile.copy(self.__sessionBackup1, self.__sessionBackup2)
+            
+            QFile.remove(self.__sessionBackup1)
+            QFile.copy(self.__lastActiveSession, self.__sessionBackup1)
+    
+    def sessionMetaData(self, includeBackups=False):
+        """
+        Public method to get the sessions meta data.
+        
+        @param includeBackups flag indicating to include backup sessions
+        @type bool
+        @return list of session meta data
+        @rtype list of SessionMetaData
+        """
+        self.__fillMetaDataList()
+        
+        metaDataList = self.__sessionMetaData[:]
+        
+        if includeBackups and QFile.exists(self.__sessionBackup1):
+            data = SessionMetaData()
+            data.name = self.tr("Backup 1")
+            data.filePath = self.__sessionBackup1
+            data.isBackup = True
+            metaDataList.append(data)
+        
+        if includeBackups and QFile.exists(self.__sessionBackup2):
+            data = SessionMetaData()
+            data.name = self.tr("Backup 2")
+            data.filePath = self.__sessionBackup2
+            data.isBackup = True
+            metaDataList.append(data)
+        
+        return metaDataList
+    
+    def __fillMetaDataList(self):
+        """
+        Private method to fill the sessions meta data list.
+        
+        The sessions meta data list is only populated, if the variable holding
+        it is empty (i.e. it is populated on demand).
+        """
+        if self.__sessionMetaData:
+            return
+        
+        sessionFilesInfoList = QDir(self.getSessionsDirectory()).entryInfoList(
+            QDir.Files, QDir.Time)
+        for sessionFileInfo in sessionFilesInfoList:
+            data = SessionMetaData()
+            data.name = sessionFileInfo.baseName()
+            data.filePath = sessionFileInfo.canonicalFilePath()
+            
+            if sessionFileInfo == QFileInfo(self.defaultSessionFile()):
+                data.name = self.tr("Default Session")
+                data.isDefault = True
+            
+            if self.__isActive(sessionFileInfo):
+                data.isActive = True
+            
+            self.__sessionMetaData.append(data)
+    
+    def __isActive(self, filePath):
+        """
+        Private method to check, if a given file is the active one.
+        
+        @param filePath path of the session file to be checked
+        @type str or QFileInfo
+        @return flag indicating the active file
+        @rtype bool
+        """
+        return QFileInfo(filePath) == QFileInfo(self.__lastActiveSession)
+    
+    @pyqtSlot()
+    def __sessionDirectoryChanged(self):
+        """
+        Private slot handling changes of the sessions directory.
+        """
+        self.__sessionMetaData = []
+        
+        self.sessionsMetaDataChanged.emit()
+    
+    def openSession(self, sessionFilePath="", flags=None):
+        # TODO: implement this
+        pass
+    
+    def renameSession(self, sessionFilePath="", flags=None):
+        # TODO: implement this
+        pass
+    
+    def saveSession(self):
+        # TODO: implement this
+        pass
+    
+    def __replaceSession(self, sessionFilePath):
+        # TODO: implement this
+        pass
+    
+    def __switchToSession(self, sessionFilePath):
+        # TODO: implement this
+        pass
+    
+    def __cloneSession(self, sessionFilePath):
+        # TODO: implement this
+        pass
+    
+    def __deleteSession(self, sessionFilePath):
+        # TODO: implement this
+        pass
+    
+    def __newSession(self):
+        # TODO: implement this
+        pass
+    
+    def openSessionManagerDialog(self):
+        # TODO: implement this
+        pass
--- a/WebBrowser/WebBrowserWindow.py	Fri Jun 30 19:48:32 2017 +0200
+++ b/WebBrowser/WebBrowserWindow.py	Fri Jun 30 19:58:09 2017 +0200
@@ -109,6 +109,8 @@
     _tabManager = None
     _sessionManager = None
     
+    _performingShutdown = False
+    
     def __init__(self, home, path, parent, name, fromEric=False,
                  initShortcutsOnly=False, searchWord=None,
                  private=False, qthelp=False, settingsDir=""):
@@ -410,8 +412,6 @@
             self.__virusTotal.fileScanReport.connect(
                 self.__virusTotalFileScanReport)
             
-            self.__shutdownCalled = False
-            
             self.flashCookieManager()
             
             if WebBrowserWindow._useQtHelp:
@@ -889,7 +889,7 @@
             if self.__fromEric:
                 self.exitAct.triggered.connect(self.close)
             else:
-                self.exitAct.triggered.connect(self.__closeAllWindows)
+                self.exitAct.triggered.connect(self.shutdown)
         self.__actions.append(self.exitAct)
         
         self.backAct = E5Action(
@@ -2688,15 +2688,6 @@
         """
         self.__searchWidget.showFind()
         
-    def __closeAllWindows(self):
-        """
-        Private slot to close all windows.
-        """
-        for browser in WebBrowserWindow.BrowserWindows:
-            if browser != self:
-                browser.close()
-        self.close()
-        
     def closeEvent(self, e):
         """
         Protected event handler for the close event.
@@ -2705,62 +2696,30 @@
             <br />This event is simply accepted after the history has been
             saved and all window references have been deleted.
         """
-        if not self.__shutdownCalled:
-            res = self.shutdown()
-            
-            if res:
-                e.accept()
-                self.webBrowserWindowClosed.emit(self)
-            else:
-                e.ignore()
+        res = self.__shutdownWindow()
+        
+        if res:
+            e.accept()
+            self.webBrowserWindowClosed.emit(self)
         else:
-            e.accept()
+            e.ignore()
     
-    def shutdown(self):
-        """
-        Public method to shut down the web browser.
+    def __shutdownWindow(self):
+        """
+        Private method to shut down a web browser window.
         
         @return flag indicating successful shutdown (boolean)
         """
-        if not self.__tabWidget.shallShutDown():
-            return False
-        
-        if not self.downloadManager().allowQuit():
-            return False
-        
-        self.downloadManager().shutdown()
-        
-        self.cookieJar().close()
+        if not WebBrowserWindow._performingShutdown:
+            if not self.__tabWidget.shallShutDown():
+                return False
         
         self.__bookmarksToolBar.setModel(None)
-        self.bookmarksManager().close()
-        
-        self.historyManager().close()
-        
-        self.passwordManager().close()
-        
-        self.adBlockManager().close()
-        
-        self.userAgentsManager().close()
-        
-        self.speedDial().close()
-        
-        self.syncManager().close()
-        
-        ZoomManager.instance().close()
-        
-        WebIconProvider.instance().close()
         
         self.__virusTotal.close()
         
-        self.flashCookieManager().shutdown()
-        
         self.__navigationBar.searchEdit().openSearchManager().close()
         
-        if len(WebBrowserWindow.BrowserWindows) == 1:
-            # it is the last window
-            self.tabManager().close()
-        
         if WebBrowserWindow._useQtHelp:
             self.__searchEngine.cancelIndexing()
             self.__searchEngine.cancelSearching()
@@ -2789,12 +2748,105 @@
         except ValueError:
             pass
         
-        self.networkManager().shutdown()
-        
         if not self.__fromEric:
             Preferences.syncPreferences()
-        
-        self.__shutdownCalled = True
+            if not WebBrowserWindow._performingShutdown and \
+                    len(WebBrowserWindow.BrowserWindows) == 0:
+                # shut down the browser in case the last window was
+                # simply closed
+                self.shutdown()
+        
+        return True
+    
+    def __shallShutDown(self):
+        """
+        Private method to check, if the application should be shut down.
+        
+        @return flag indicating a shut down
+        @rtype bool
+        """
+        if Preferences.getWebBrowser("WarnOnMultipleClose"):
+            windowCount = len(WebBrowserWindow.BrowserWindows)
+            tabCount = 0
+            for browser in WebBrowserWindow.BrowserWindows:
+                tabCount += browser.tabWidget().count()
+            
+            if windowCount > 1 or tabCount > 1:
+                mb = E5MessageBox.E5MessageBox(
+                    E5MessageBox.Information,
+                    self.tr("Are you sure you want to close the web browser?"),
+                    self.tr("""Are you sure you want to close the web"""
+                            """ browser?\n"""
+                            """You have {0} windows with {1} tabs open.""")
+                    .format(windowCount, tabCount),
+                    modal=True,
+                    parent=self)
+                if self.fromEric:
+                    quitButton = mb.addButton(
+                        self.tr("&Close"), E5MessageBox.AcceptRole)
+                    quitButton.setIcon(UI.PixmapCache.getIcon("close.png"))
+                else:
+                    quitButton = mb.addButton(
+                        self.tr("&Quit"), E5MessageBox.AcceptRole)
+                    quitButton.setIcon(UI.PixmapCache.getIcon("exit.png"))
+                mb.addButton(E5MessageBox.Cancel)
+                mb.exec_()
+                return mb.clickedButton() == quitButton
+        
+        return True
+    
+    def shutdown(self):
+        """
+        Public method to shut down the web browser.
+        
+        @return flag indicating successful shutdown (boolean)
+        """
+        if not self.__shallShutDown():
+            return False
+        
+        if not self.downloadManager().allowQuit():
+            return False
+        
+        WebBrowserWindow._performingShutdown = True
+        
+        self.sessionManager().shutdown()
+        
+        self.downloadManager().shutdown()
+        
+        self.cookieJar().close()
+        
+        self.bookmarksManager().close()
+        
+        self.historyManager().close()
+        
+        self.passwordManager().close()
+        
+        self.adBlockManager().close()
+        
+        self.userAgentsManager().close()
+        
+        self.speedDial().close()
+        
+        self.syncManager().close()
+        
+        ZoomManager.instance().close()
+        
+        WebIconProvider.instance().close()
+        
+        self.flashCookieManager().shutdown()
+        
+        if len(WebBrowserWindow.BrowserWindows) == 1:
+            # it is the last window
+            self.tabManager().close()
+        
+        self.networkManager().shutdown()
+        
+        
+        for browser in WebBrowserWindow.BrowserWindows:
+            if browser != self:
+                browser.close()
+        self.close()
+        
         return True
 
     def __backward(self):

eric ide

mercurial