WebBrowser/Session/SessionManager.py

Sat, 01 Jul 2017 19:14:01 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 01 Jul 2017 19:14:01 +0200
changeset 5780
79d06c98c5c9
parent 5779
b53fabc86f3c
child 5782
60874802161b
permissions
-rw-r--r--

Continued implementing session support for the new web browser.

# -*- coding: utf-8 -*-

# Copyright (c) 2017 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the session manager.
"""

from __future__ import unicode_literals

import os
import json

from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QObject, QTimer, QDir, \
    QFile, QFileInfo, QFileSystemWatcher, QByteArray
from PyQt5.QtWidgets import QMenu, QAction, QActionGroup, QApplication

from E5Gui import E5MessageBox

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
    RestoreSession = 8
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object
        @type QObject
        """
        super(SessionManager, self).__init__(parent)
        
        sessionsDirName = self.getSessionsDirectory()
        sessionsDir = QDir(sessionsDirName)
        if not sessionsDir.exists():
            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)
        self.__autoSaveTimer.timeout.connect(self.__autoSaveSession)
        self.__initSessionSaveTimer()
    
    def preferencesChanged(self):
        """
        Public slot to react upon changes of the settings.
        """
        self.__initSessionSaveTimer()
        # TODO: implement this
    
    def getSessionsDirectory(self):
        """
        Public method to get the directory sessions are stored in.
        
        @return name of the sessions directory
        @rtype str
        """
        return os.path.join(Utilities.getConfigDir(),
                            "web_browser", "sessions")
    
    def defaultSessionFile(self):
        """
        Public method to get the name of the default session file.
        
        @return name of the default session file
        @rtype str
        """
        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):
        """
        Private slot to initialize the auto save timer.
        """
        self.__autoSaveInterval = Preferences.getWebBrowser(
            "SessionAutoSaveInterval") * 1000
        
        if Preferences.getWebBrowser("SessionAutoSave"):
            if not self.__autoSaveTimer.isActive():
                self.__autoSaveTimer.start(self.__autoSaveInterval)
        else:
            self.__autoSaveTimer.stop()
    
    @pyqtSlot()
    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():
            Preferences.setWebBrowser("SessionLastActivePath",
                                      self.__lastActiveSession)
            self.writeCurrentSession(self.__lastActiveSession)
        
        if startTimer:
            self.__autoSaveTimer.start(self.__autoSaveInterval)
    
    def writeCurrentSession(self, sessionFileName):
        """
        Public method to write the current session to the given file name.
        
        @param sessionFileName file name of the session
        @type str
        """
        from WebBrowser.WebBrowserWindow import WebBrowserWindow
        
        sessionData = {"Windows": []}
        
        for window in WebBrowserWindow.mainWindows():
            data = window.tabWidget().getSessionData()
            
            # add window geometry
            geometry = window.saveGeometry()
            data["WindowGeometry"] = bytes(geometry.toBase64()).decode("ascii")
            
            sessionData["Windows"].append(data)
        
        if sessionData["Windows"]:
            sessionFile = open(sessionFileName, "w")
            json.dump(sessionData, sessionFile, indent=2)
            sessionFile.close()
    
    def __readSessionFromFile(self, sessionFileName):
        """
        Private method to read the session data from a file.
        
        @param sessionFileName file name of the session file
        @type str
        @return dictionary containing the session data
        @rtype dict
        """
        try:
            sessionFile = open(sessionFileName, "r")
            sessionData = json.load(sessionFile)
            sessionFile.close()
        except (IOError, OSError):
            sessionData = {}
        
        return sessionData
    
    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(
            ["*.json"], 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()
    
    @pyqtSlot()
    def aboutToShowSessionsMenu(self):
        """
        Public slot to populate the sessions selection menu.
        """
        menu = self.sender()
        if isinstance(menu, QMenu):
            menu.clear()
            
            actionGroup = QActionGroup(menu)
            sessions = self.sessionMetaData(includeBackups=False)
            for session in sessions:
                act = menu.addAction(session.name)
                act.setCheckable(True)
                act.setChecked(session.isActive)
                act.setData(session.filePath)
                actionGroup.addAction(act)
                act.triggered.connect(self.__sessionActTriggered)
    
    @pyqtSlot()
    def __sessionActTriggered(self):
        """
        Private slot to handle the menu selection of a session.
        """
        act = self.sender()
        if isinstance(act, QAction):
            path = act.data()
            self.switchToSession(path)
    
    def __openSession(self, sessionFilePath, flags=None):
        """
        Private method to open a session from a given session file.
        """
        if self.__isActive(sessionFilePath):
            return
        
        sessionData = self.__readSessionFromFile(sessionFilePath)
        if not sessionData or not sessionData["Windows"]:
            return
        
        from WebBrowser.WebBrowserWindow import WebBrowserWindow
        window = WebBrowserWindow.mainWindow()
        if flags & SessionManager.SwitchSession:
            # save the current session
            self.writeCurrentSession(self.__lastActiveSession)
            
            # create new window for the new session
            window = window.newWindow(restoreSession=True)
            
            # close all existing windows
            for win in WebBrowserWindow.mainWindows():
                if win is not window:
                    win.forceClose()
            
            if not (flags & SessionManager.ReplaceSession):
                self.__lastActiveSession = \
                    QFileInfo(sessionFilePath).canonicalFilePath()
                self.__sessionMetaData = []
        
        QApplication.setOverrideCursor(Qt.WaitCursor)
        # restore session for first window
        data = sessionData["Windows"].pop(0)
        window.tabWidget().loadFromSessionData(data)
        if "WindowGeometry" in data:
            geometry = QByteArray.fromBase64(
                data["WindowGeometry"].encode("ascii"))
            window.restoreGeometry(geometry)
        QApplication.processEvents()
        
        # restore additional windows
        for data in sessionData["Windows"]:
            window = WebBrowserWindow.mainWindow()\
                .newWindow(restoreSession=True)
            window.tabWidget().loadFromSessionData(data)
            if "WindowGeometry" in data:
                geometry = QByteArray.fromBase64(
                    data["WindowGeometry"].encode("ascii"))
                window.restoreGeometry(geometry)
            QApplication.processEvents()
        QApplication.restoreOverrideCursor()
    
    def renameSession(self, sessionFilePath="", flags=None):
        # TODO: implement this
        pass
    
    def saveSession(self):
        # TODO: implement this
        pass
    
    def replaceSession(self, sessionFilePath):
        """
        Public method to replace the current session with the given one.
        
        @param sessionFilePath file name of the session file to replace with
        @type str
        """
        from WebBrowser.WebBrowserWindow import WebBrowserWindow
        res = E5MessageBox.yesNo(
            WebBrowserWindow.mainWindow(),
            self.tr("Restore Backup"),
            self.tr("""Are you sure you want to replace current session?"""))
        if res:
            self.__openSession(sessionFilePath, SessionManager.ReplaceSession)
    
    def switchToSession(self, sessionFilePath):
        """
        Public method to switch the current session to the given one.
        
        @param sessionFilePath file name of the session file to switch to
        @type str
        """
        self.__openSession(sessionFilePath, SessionManager.SwitchSession)
    
    def cloneSession(self, sessionFilePath):
        # TODO: implement this
        pass
    
    def deleteSession(self, sessionFilePath):
        # TODO: implement this
        pass
    
    def newSession(self):
        # TODO: implement this
        pass
    
    def showSessionManagerDialog(self):
        # TODO: implement this
        pass
        print("showSessionManagerDialog()")

eric ide

mercurial