47 |
61 |
48 |
62 |
49 class SessionManager(QObject): |
63 class SessionManager(QObject): |
50 """ |
64 """ |
51 Class implementing the session manager. |
65 Class implementing the session manager. |
52 |
66 |
53 @signal sessionsMetaDataChanged() emitted to indicate a change of the |
67 @signal sessionsMetaDataChanged() emitted to indicate a change of the |
54 list of session meta data |
68 list of session meta data |
55 """ |
69 """ |
|
70 |
56 sessionsMetaDataChanged = pyqtSignal() |
71 sessionsMetaDataChanged = pyqtSignal() |
57 |
72 |
58 SwitchSession = 1 |
73 SwitchSession = 1 |
59 CloneSession = 2 |
74 CloneSession = 2 |
60 ReplaceSession = SwitchSession | 4 |
75 ReplaceSession = SwitchSession | 4 |
61 RestoreSession = 8 |
76 RestoreSession = 8 |
62 |
77 |
63 def __init__(self, parent=None): |
78 def __init__(self, parent=None): |
64 """ |
79 """ |
65 Constructor |
80 Constructor |
66 |
81 |
67 @param parent reference to the parent object |
82 @param parent reference to the parent object |
68 @type QObject |
83 @type QObject |
69 """ |
84 """ |
70 super().__init__(parent) |
85 super().__init__(parent) |
71 |
86 |
72 sessionsDirName = self.getSessionsDirectory() |
87 sessionsDirName = self.getSessionsDirectory() |
73 sessionsDir = QDir(sessionsDirName) |
88 sessionsDir = QDir(sessionsDirName) |
74 if not sessionsDir.exists(): |
89 if not sessionsDir.exists(): |
75 sessionsDir.mkpath(sessionsDirName) |
90 sessionsDir.mkpath(sessionsDirName) |
76 |
91 |
77 self.__sessionMetaData = [] |
92 self.__sessionMetaData = [] |
78 # list containing meta data about saved sessions |
93 # list containing meta data about saved sessions |
79 |
94 |
80 self.__sessionDefault = os.path.join(sessionsDirName, "session.json") |
95 self.__sessionDefault = os.path.join(sessionsDirName, "session.json") |
81 self.__sessionBackup1 = os.path.join(sessionsDirName, |
96 self.__sessionBackup1 = os.path.join(sessionsDirName, "session.json.old") |
82 "session.json.old") |
97 self.__sessionBackup2 = os.path.join(sessionsDirName, "session.json.old1") |
83 self.__sessionBackup2 = os.path.join(sessionsDirName, |
98 |
84 "session.json.old1") |
99 self.__lastActiveSession = Preferences.getWebBrowser("SessionLastActivePath") |
85 |
|
86 self.__lastActiveSession = Preferences.getWebBrowser( |
|
87 "SessionLastActivePath") |
|
88 if not os.path.exists(self.__lastActiveSession): |
100 if not os.path.exists(self.__lastActiveSession): |
89 self.__lastActiveSession = self.__sessionDefault |
101 self.__lastActiveSession = self.__sessionDefault |
90 |
102 |
91 self.__sessionsDirectoryWatcher = QFileSystemWatcher( |
103 self.__sessionsDirectoryWatcher = QFileSystemWatcher( |
92 [self.getSessionsDirectory()], self) |
104 [self.getSessionsDirectory()], self |
|
105 ) |
93 self.__sessionsDirectoryWatcher.directoryChanged.connect( |
106 self.__sessionsDirectoryWatcher.directoryChanged.connect( |
94 self.__sessionDirectoryChanged) |
107 self.__sessionDirectoryChanged |
95 |
108 ) |
|
109 |
96 self.__backupSavedSession() |
110 self.__backupSavedSession() |
97 |
111 |
98 self.__autoSaveTimer = None |
112 self.__autoSaveTimer = None |
99 self.__shutdown = False |
113 self.__shutdown = False |
100 |
114 |
101 def activateTimer(self): |
115 def activateTimer(self): |
102 """ |
116 """ |
103 Public method to activate the session save timer. |
117 Public method to activate the session save timer. |
104 """ |
118 """ |
105 if self.__autoSaveTimer is None: |
119 if self.__autoSaveTimer is None: |
106 self.__autoSaveTimer = QTimer() |
120 self.__autoSaveTimer = QTimer() |
107 self.__autoSaveTimer.setSingleShot(True) |
121 self.__autoSaveTimer.setSingleShot(True) |
108 self.__autoSaveTimer.timeout.connect(self.__autoSaveSession) |
122 self.__autoSaveTimer.timeout.connect(self.__autoSaveSession) |
109 self.__initSessionSaveTimer() |
123 self.__initSessionSaveTimer() |
110 |
124 |
111 def preferencesChanged(self): |
125 def preferencesChanged(self): |
112 """ |
126 """ |
113 Public slot to react upon changes of the settings. |
127 Public slot to react upon changes of the settings. |
114 """ |
128 """ |
115 self.__initSessionSaveTimer() |
129 self.__initSessionSaveTimer() |
116 |
130 |
117 def getSessionsDirectory(self): |
131 def getSessionsDirectory(self): |
118 """ |
132 """ |
119 Public method to get the directory sessions are stored in. |
133 Public method to get the directory sessions are stored in. |
120 |
134 |
121 @return name of the sessions directory |
135 @return name of the sessions directory |
122 @rtype str |
136 @rtype str |
123 """ |
137 """ |
124 return os.path.join(Utilities.getConfigDir(), |
138 return os.path.join(Utilities.getConfigDir(), "web_browser", "sessions") |
125 "web_browser", "sessions") |
139 |
126 |
|
127 def defaultSessionFile(self): |
140 def defaultSessionFile(self): |
128 """ |
141 """ |
129 Public method to get the name of the default session file. |
142 Public method to get the name of the default session file. |
130 |
143 |
131 @return name of the default session file |
144 @return name of the default session file |
132 @rtype str |
145 @rtype str |
133 """ |
146 """ |
134 return self.__sessionDefault |
147 return self.__sessionDefault |
135 |
148 |
136 def lastActiveSessionFile(self): |
149 def lastActiveSessionFile(self): |
137 """ |
150 """ |
138 Public method to get the name of the last active session file. |
151 Public method to get the name of the last active session file. |
139 |
152 |
140 @return name of the last active session file |
153 @return name of the last active session file |
141 @rtype str |
154 @rtype str |
142 """ |
155 """ |
143 return self.__lastActiveSession |
156 return self.__lastActiveSession |
144 |
157 |
145 def shutdown(self): |
158 def shutdown(self): |
146 """ |
159 """ |
147 Public method to perform any shutdown actions. |
160 Public method to perform any shutdown actions. |
148 """ |
161 """ |
149 self.__autoSaveTimer.stop() |
162 self.__autoSaveTimer.stop() |
150 if not self.__shutdown: |
163 if not self.__shutdown: |
151 self.__autoSaveSession(startTimer=False) |
164 self.__autoSaveSession(startTimer=False) |
152 self.__shutdown = True |
165 self.__shutdown = True |
153 |
166 |
154 def autoSaveSession(self): |
167 def autoSaveSession(self): |
155 """ |
168 """ |
156 Public method to save the current session state. |
169 Public method to save the current session state. |
157 """ |
170 """ |
158 self.__autoSaveSession(startTimer=False) |
171 self.__autoSaveSession(startTimer=False) |
159 |
172 |
160 def __initSessionSaveTimer(self): |
173 def __initSessionSaveTimer(self): |
161 """ |
174 """ |
162 Private slot to initialize the auto save timer. |
175 Private slot to initialize the auto save timer. |
163 """ |
176 """ |
164 self.__autoSaveInterval = Preferences.getWebBrowser( |
177 self.__autoSaveInterval = ( |
165 "SessionAutoSaveInterval") * 1000 |
178 Preferences.getWebBrowser("SessionAutoSaveInterval") * 1000 |
166 |
179 ) |
|
180 |
167 if Preferences.getWebBrowser("SessionAutoSave"): |
181 if Preferences.getWebBrowser("SessionAutoSave"): |
168 if not self.__autoSaveTimer.isActive(): |
182 if not self.__autoSaveTimer.isActive(): |
169 self.__autoSaveTimer.start(self.__autoSaveInterval) |
183 self.__autoSaveTimer.start(self.__autoSaveInterval) |
170 else: |
184 else: |
171 self.__autoSaveTimer.stop() |
185 self.__autoSaveTimer.stop() |
172 |
186 |
173 @pyqtSlot() |
187 @pyqtSlot() |
174 def __autoSaveSession(self, startTimer=True): |
188 def __autoSaveSession(self, startTimer=True): |
175 """ |
189 """ |
176 Private slot to save the current session state. |
190 Private slot to save the current session state. |
177 |
191 |
178 @param startTimer flag indicating to restart the timer |
192 @param startTimer flag indicating to restart the timer |
179 @type bool |
193 @type bool |
180 """ |
194 """ |
181 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
195 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
182 |
196 |
183 if not WebBrowserWindow.isPrivate(): |
197 if not WebBrowserWindow.isPrivate(): |
184 Preferences.setWebBrowser("SessionLastActivePath", |
198 Preferences.setWebBrowser("SessionLastActivePath", self.__lastActiveSession) |
185 self.__lastActiveSession) |
|
186 self.writeCurrentSession(self.__lastActiveSession) |
199 self.writeCurrentSession(self.__lastActiveSession) |
187 |
200 |
188 if startTimer: |
201 if startTimer: |
189 self.__autoSaveTimer.start(self.__autoSaveInterval) |
202 self.__autoSaveTimer.start(self.__autoSaveInterval) |
190 |
203 |
191 def writeCurrentSession(self, sessionFileName): |
204 def writeCurrentSession(self, sessionFileName): |
192 """ |
205 """ |
193 Public method to write the current session to the given file name. |
206 Public method to write the current session to the given file name. |
194 |
207 |
195 @param sessionFileName file name of the session |
208 @param sessionFileName file name of the session |
196 @type str |
209 @type str |
197 """ |
210 """ |
198 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
211 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
199 |
212 |
200 sessionData = {"Windows": []} |
213 sessionData = {"Windows": []} |
201 |
214 |
202 activeWindow = WebBrowserWindow.getWindow() |
215 activeWindow = WebBrowserWindow.getWindow() |
203 for window in WebBrowserWindow.mainWindows(): |
216 for window in WebBrowserWindow.mainWindows(): |
204 data = window.tabWidget().getSessionData() |
217 data = window.tabWidget().getSessionData() |
205 |
218 |
206 # add window geometry |
219 # add window geometry |
207 geometry = window.saveGeometry() |
220 geometry = window.saveGeometry() |
208 data["WindowGeometry"] = bytes(geometry.toBase64()).decode("ascii") |
221 data["WindowGeometry"] = bytes(geometry.toBase64()).decode("ascii") |
209 |
222 |
210 sessionData["Windows"].append(data) |
223 sessionData["Windows"].append(data) |
211 |
224 |
212 if window is activeWindow: |
225 if window is activeWindow: |
213 sessionData["CurrentWindowIndex"] = ( |
226 sessionData["CurrentWindowIndex"] = len(sessionData["Windows"]) - 1 |
214 len(sessionData["Windows"]) - 1 |
227 |
215 ) |
|
216 |
|
217 if sessionData["Windows"]: |
228 if sessionData["Windows"]: |
218 with open(sessionFileName, "w") as sessionFile: |
229 with open(sessionFileName, "w") as sessionFile: |
219 json.dump(sessionData, sessionFile, indent=2) |
230 json.dump(sessionData, sessionFile, indent=2) |
220 |
231 |
221 @classmethod |
232 @classmethod |
222 def readSessionFromFile(cls, sessionFileName): |
233 def readSessionFromFile(cls, sessionFileName): |
223 """ |
234 """ |
224 Class method to read the session data from a file. |
235 Class method to read the session data from a file. |
225 |
236 |
226 @param sessionFileName file name of the session file |
237 @param sessionFileName file name of the session file |
227 @type str |
238 @type str |
228 @return dictionary containing the session data |
239 @return dictionary containing the session data |
229 @rtype dict |
240 @rtype dict |
230 """ |
241 """ |
233 sessionData = json.load(sessionFile) |
244 sessionData = json.load(sessionFile) |
234 if not cls.isValidSession(sessionData): |
245 if not cls.isValidSession(sessionData): |
235 sessionData = {} |
246 sessionData = {} |
236 except OSError: |
247 except OSError: |
237 sessionData = {} |
248 sessionData = {} |
238 |
249 |
239 return sessionData |
250 return sessionData |
240 |
251 |
241 @classmethod |
252 @classmethod |
242 def isValidSession(cls, session): |
253 def isValidSession(cls, session): |
243 """ |
254 """ |
244 Class method to check the validity of a session. |
255 Class method to check the validity of a session. |
245 |
256 |
246 @param session dictionary containing the session data |
257 @param session dictionary containing the session data |
247 @type dict |
258 @type dict |
248 @return flag indicating validity |
259 @return flag indicating validity |
249 @rtype bool |
260 @rtype bool |
250 """ |
261 """ |
251 if not session: |
262 if not session: |
252 return False |
263 return False |
253 |
264 |
254 if "Windows" not in session: |
265 if "Windows" not in session: |
255 return False |
266 return False |
256 |
267 |
257 if not session["Windows"]: |
268 if not session["Windows"]: |
258 return False |
269 return False |
259 |
270 |
260 return True |
271 return True |
261 |
272 |
262 def __backupSavedSession(self): |
273 def __backupSavedSession(self): |
263 """ |
274 """ |
264 Private method to backup the most recently saved session. |
275 Private method to backup the most recently saved session. |
265 """ |
276 """ |
266 if os.path.exists(self.__lastActiveSession): |
277 if os.path.exists(self.__lastActiveSession): |
267 |
278 |
268 if os.path.exists(self.__sessionBackup1): |
279 if os.path.exists(self.__sessionBackup1): |
269 os.unlink(self.__sessionBackup2) |
280 os.unlink(self.__sessionBackup2) |
270 shutil.copy(self.__sessionBackup1, self.__sessionBackup2) |
281 shutil.copy(self.__sessionBackup1, self.__sessionBackup2) |
271 |
282 |
272 os.unlink(self.__sessionBackup1) |
283 os.unlink(self.__sessionBackup1) |
273 shutil.copy(self.__lastActiveSession, self.__sessionBackup1) |
284 shutil.copy(self.__lastActiveSession, self.__sessionBackup1) |
274 |
285 |
275 def sessionMetaData(self, includeBackups=False): |
286 def sessionMetaData(self, includeBackups=False): |
276 """ |
287 """ |
277 Public method to get the sessions meta data. |
288 Public method to get the sessions meta data. |
278 |
289 |
279 @param includeBackups flag indicating to include backup sessions |
290 @param includeBackups flag indicating to include backup sessions |
280 @type bool |
291 @type bool |
281 @return list of session meta data |
292 @return list of session meta data |
282 @rtype list of SessionMetaData |
293 @rtype list of SessionMetaData |
283 """ |
294 """ |
284 self.__fillMetaDataList() |
295 self.__fillMetaDataList() |
285 |
296 |
286 metaDataList = self.__sessionMetaData[:] |
297 metaDataList = self.__sessionMetaData[:] |
287 |
298 |
288 if includeBackups and os.path.exists(self.__sessionBackup1): |
299 if includeBackups and os.path.exists(self.__sessionBackup1): |
289 data = SessionMetaData() |
300 data = SessionMetaData() |
290 data.name = self.tr("Backup 1") |
301 data.name = self.tr("Backup 1") |
291 data.filePath = self.__sessionBackup1 |
302 data.filePath = self.__sessionBackup1 |
292 data.isBackup = True |
303 data.isBackup = True |
293 metaDataList.append(data) |
304 metaDataList.append(data) |
294 |
305 |
295 if includeBackups and os.path.exists(self.__sessionBackup2): |
306 if includeBackups and os.path.exists(self.__sessionBackup2): |
296 data = SessionMetaData() |
307 data = SessionMetaData() |
297 data.name = self.tr("Backup 2") |
308 data.name = self.tr("Backup 2") |
298 data.filePath = self.__sessionBackup2 |
309 data.filePath = self.__sessionBackup2 |
299 data.isBackup = True |
310 data.isBackup = True |
300 metaDataList.append(data) |
311 metaDataList.append(data) |
301 |
312 |
302 return metaDataList |
313 return metaDataList |
303 |
314 |
304 def __fillMetaDataList(self): |
315 def __fillMetaDataList(self): |
305 """ |
316 """ |
306 Private method to fill the sessions meta data list. |
317 Private method to fill the sessions meta data list. |
307 |
318 |
308 The sessions meta data list is only populated, if the variable holding |
319 The sessions meta data list is only populated, if the variable holding |
309 it is empty (i.e. it is populated on demand). |
320 it is empty (i.e. it is populated on demand). |
310 """ |
321 """ |
311 if self.__sessionMetaData: |
322 if self.__sessionMetaData: |
312 return |
323 return |
313 |
324 |
314 sessionFiles = pathlib.Path(self.getSessionsDirectory()).glob("*.json") |
325 sessionFiles = pathlib.Path(self.getSessionsDirectory()).glob("*.json") |
315 |
326 |
316 for sessionFile in sessionFiles: |
327 for sessionFile in sessionFiles: |
317 sessionData = self.readSessionFromFile(sessionFile.resolve()) |
328 sessionData = self.readSessionFromFile(sessionFile.resolve()) |
318 if not sessionData or not sessionData["Windows"]: |
329 if not sessionData or not sessionData["Windows"]: |
319 continue |
330 continue |
320 |
331 |
321 data = SessionMetaData() |
332 data = SessionMetaData() |
322 data.name = sessionFile.stem |
333 data.name = sessionFile.stem |
323 data.filePath = sessionFile.resolve() |
334 data.filePath = sessionFile.resolve() |
324 |
335 |
325 if sessionFile == pathlib.Path(self.defaultSessionFile()): |
336 if sessionFile == pathlib.Path(self.defaultSessionFile()): |
326 data.name = self.tr("Default Session") |
337 data.name = self.tr("Default Session") |
327 data.isDefault = True |
338 data.isDefault = True |
328 |
339 |
329 if self.__isActive(sessionFile): |
340 if self.__isActive(sessionFile): |
330 data.isActive = True |
341 data.isActive = True |
331 |
342 |
332 if data.isDefault: |
343 if data.isDefault: |
333 # default session is always first |
344 # default session is always first |
334 self.__sessionMetaData.insert(0, data) |
345 self.__sessionMetaData.insert(0, data) |
335 else: |
346 else: |
336 self.__sessionMetaData.append(data) |
347 self.__sessionMetaData.append(data) |
337 |
348 |
338 def __isActive(self, filePath): |
349 def __isActive(self, filePath): |
339 """ |
350 """ |
340 Private method to check, if a given file is the active one. |
351 Private method to check, if a given file is the active one. |
341 |
352 |
342 @param filePath path of the session file to be checked |
353 @param filePath path of the session file to be checked |
343 @type str or pathlib.Path |
354 @type str or pathlib.Path |
344 @return flag indicating the active file |
355 @return flag indicating the active file |
345 @rtype bool |
356 @rtype bool |
346 """ |
357 """ |
347 return pathlib.Path(filePath) == pathlib.Path(self.__lastActiveSession) |
358 return pathlib.Path(filePath) == pathlib.Path(self.__lastActiveSession) |
348 |
359 |
349 @pyqtSlot() |
360 @pyqtSlot() |
350 def __sessionDirectoryChanged(self): |
361 def __sessionDirectoryChanged(self): |
351 """ |
362 """ |
352 Private slot handling changes of the sessions directory. |
363 Private slot handling changes of the sessions directory. |
353 """ |
364 """ |
354 self.__sessionMetaData = [] |
365 self.__sessionMetaData = [] |
355 |
366 |
356 self.sessionsMetaDataChanged.emit() |
367 self.sessionsMetaDataChanged.emit() |
357 |
368 |
358 @pyqtSlot() |
369 @pyqtSlot() |
359 def aboutToShowSessionsMenu(self, menu): |
370 def aboutToShowSessionsMenu(self, menu): |
360 """ |
371 """ |
361 Public slot to populate the sessions selection menu. |
372 Public slot to populate the sessions selection menu. |
362 |
373 |
363 @param menu reference to the menu about to be shown |
374 @param menu reference to the menu about to be shown |
364 @type QMenu |
375 @type QMenu |
365 """ |
376 """ |
366 menu.clear() |
377 menu.clear() |
367 |
378 |
368 actionGroup = QActionGroup(menu) |
379 actionGroup = QActionGroup(menu) |
369 sessions = self.sessionMetaData(includeBackups=False) |
380 sessions = self.sessionMetaData(includeBackups=False) |
370 for session in sessions: |
381 for session in sessions: |
371 act = menu.addAction(session.name) |
382 act = menu.addAction(session.name) |
372 act.setCheckable(True) |
383 act.setCheckable(True) |
373 act.setChecked(session.isActive) |
384 act.setChecked(session.isActive) |
374 act.setData(session.filePath) |
385 act.setData(session.filePath) |
375 actionGroup.addAction(act) |
386 actionGroup.addAction(act) |
376 act.triggered.connect( |
387 act.triggered.connect(functools.partial(self.__sessionActTriggered, act)) |
377 functools.partial(self.__sessionActTriggered, act)) |
388 |
378 |
|
379 @pyqtSlot() |
389 @pyqtSlot() |
380 def __sessionActTriggered(self, act): |
390 def __sessionActTriggered(self, act): |
381 """ |
391 """ |
382 Private slot to handle the menu selection of a session. |
392 Private slot to handle the menu selection of a session. |
383 |
393 |
384 @param act reference to the action that triggered |
394 @param act reference to the action that triggered |
385 @type QAction |
395 @type QAction |
386 """ |
396 """ |
387 path = act.data() |
397 path = act.data() |
388 self.switchToSession(path) |
398 self.switchToSession(path) |
389 |
399 |
390 def openSession(self, sessionFilePath, flags=0): |
400 def openSession(self, sessionFilePath, flags=0): |
391 """ |
401 """ |
392 Public method to open a session from a given session file. |
402 Public method to open a session from a given session file. |
393 |
403 |
394 @param sessionFilePath name of the session file to get session from |
404 @param sessionFilePath name of the session file to get session from |
395 @type str |
405 @type str |
396 @param flags flags determining the open mode |
406 @param flags flags determining the open mode |
397 @type int |
407 @type int |
398 """ |
408 """ |
399 if self.__isActive(sessionFilePath): |
409 if self.__isActive(sessionFilePath): |
400 return |
410 return |
401 |
411 |
402 sessionData = self.readSessionFromFile(sessionFilePath) |
412 sessionData = self.readSessionFromFile(sessionFilePath) |
403 if not sessionData or not sessionData["Windows"]: |
413 if not sessionData or not sessionData["Windows"]: |
404 return |
414 return |
405 |
415 |
406 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
416 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
|
417 |
407 window = WebBrowserWindow.mainWindow() |
418 window = WebBrowserWindow.mainWindow() |
408 |
419 |
409 if ((flags & SessionManager.SwitchSession) == |
420 if (flags & SessionManager.SwitchSession) == SessionManager.SwitchSession: |
410 SessionManager.SwitchSession): |
|
411 # save the current session |
421 # save the current session |
412 self.writeCurrentSession(self.__lastActiveSession) |
422 self.writeCurrentSession(self.__lastActiveSession) |
413 |
423 |
414 # create new window for the new session |
424 # create new window for the new session |
415 window = window.newWindow(restoreSession=True) |
425 window = window.newWindow(restoreSession=True) |
416 |
426 |
417 # close all existing windows |
427 # close all existing windows |
418 for win in WebBrowserWindow.mainWindows()[:]: |
428 for win in WebBrowserWindow.mainWindows()[:]: |
419 if win is not window: |
429 if win is not window: |
420 win.forceClose() |
430 win.forceClose() |
421 |
431 |
422 if ( |
432 if (flags & SessionManager.ReplaceSession) != SessionManager.ReplaceSession: |
423 (flags & SessionManager.ReplaceSession) != |
433 self.__lastActiveSession = pathlib.Path(sessionFilePath).resolve() |
424 SessionManager.ReplaceSession |
|
425 ): |
|
426 self.__lastActiveSession = ( |
|
427 pathlib.Path(sessionFilePath).resolve() |
|
428 ) |
|
429 self.__sessionMetaData = [] |
434 self.__sessionMetaData = [] |
430 |
435 |
431 self.restoreSessionFromData(window, sessionData) |
436 self.restoreSessionFromData(window, sessionData) |
432 |
437 |
433 @classmethod |
438 @classmethod |
434 def restoreSessionFromData(cls, window=None, sessionData=None): |
439 def restoreSessionFromData(cls, window=None, sessionData=None): |
435 """ |
440 """ |
436 Class method to restore a session from a session data dictionary. |
441 Class method to restore a session from a session data dictionary. |
437 |
442 |
438 @param window reference to main window to restore to |
443 @param window reference to main window to restore to |
439 @type WebBrowserWindow |
444 @type WebBrowserWindow |
440 @param sessionData dictionary containing the session data |
445 @param sessionData dictionary containing the session data |
441 """ |
446 """ |
442 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
447 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
|
448 |
443 if window is None: |
449 if window is None: |
444 window = WebBrowserWindow.mainWindow() |
450 window = WebBrowserWindow.mainWindow() |
445 |
451 |
446 with EricOverrideCursor(): |
452 with EricOverrideCursor(): |
447 # restore session for first window |
453 # restore session for first window |
448 data = sessionData["Windows"].pop(0) |
454 data = sessionData["Windows"].pop(0) |
449 window.tabWidget().loadFromSessionData(data) |
455 window.tabWidget().loadFromSessionData(data) |
450 if "WindowGeometry" in data: |
456 if "WindowGeometry" in data: |
451 geometry = QByteArray.fromBase64( |
457 geometry = QByteArray.fromBase64(data["WindowGeometry"].encode("ascii")) |
452 data["WindowGeometry"].encode("ascii")) |
|
453 window.restoreGeometry(geometry) |
458 window.restoreGeometry(geometry) |
454 QApplication.processEvents() |
459 QApplication.processEvents() |
455 |
460 |
456 # restore additional windows |
461 # restore additional windows |
457 for data in sessionData["Windows"]: |
462 for data in sessionData["Windows"]: |
458 window = ( |
463 window = WebBrowserWindow.mainWindow().newWindow(restoreSession=True) |
459 WebBrowserWindow.mainWindow().newWindow( |
|
460 restoreSession=True) |
|
461 ) |
|
462 window.tabWidget().loadFromSessionData(data) |
464 window.tabWidget().loadFromSessionData(data) |
463 if "WindowGeometry" in data: |
465 if "WindowGeometry" in data: |
464 geometry = QByteArray.fromBase64( |
466 geometry = QByteArray.fromBase64( |
465 data["WindowGeometry"].encode("ascii")) |
467 data["WindowGeometry"].encode("ascii") |
|
468 ) |
466 window.restoreGeometry(geometry) |
469 window.restoreGeometry(geometry) |
467 QApplication.processEvents() |
470 QApplication.processEvents() |
468 |
471 |
469 if "CurrentWindowIndex" in sessionData: |
472 if "CurrentWindowIndex" in sessionData: |
470 currentWindowIndex = sessionData["CurrentWindowIndex"] |
473 currentWindowIndex = sessionData["CurrentWindowIndex"] |
471 with contextlib.suppress(IndexError): |
474 with contextlib.suppress(IndexError): |
472 currentWindow = ( |
475 currentWindow = WebBrowserWindow.mainWindows()[currentWindowIndex] |
473 WebBrowserWindow.mainWindows()[currentWindowIndex] |
|
474 ) |
|
475 QTimer.singleShot(0, lambda: currentWindow.raise_()) |
476 QTimer.singleShot(0, lambda: currentWindow.raise_()) |
476 |
477 |
477 def renameSession(self, sessionFilePath, flags=0): |
478 def renameSession(self, sessionFilePath, flags=0): |
478 """ |
479 """ |
479 Public method to rename or clone a session. |
480 Public method to rename or clone a session. |
480 |
481 |
481 @param sessionFilePath name of the session file |
482 @param sessionFilePath name of the session file |
482 @type str |
483 @type str |
483 @param flags flags determining a rename or clone operation |
484 @param flags flags determining a rename or clone operation |
484 @type int |
485 @type int |
485 """ |
486 """ |
486 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
487 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
487 |
488 |
488 suggestedName = pathlib.Path(sessionFilePath).stem |
489 suggestedName = pathlib.Path(sessionFilePath).stem |
489 if flags & SessionManager.CloneSession: |
490 if flags & SessionManager.CloneSession: |
490 suggestedName += "_cloned" |
491 suggestedName += "_cloned" |
491 title = self.tr("Clone Session") |
492 title = self.tr("Clone Session") |
492 else: |
493 else: |
495 newName, ok = QInputDialog.getText( |
496 newName, ok = QInputDialog.getText( |
496 WebBrowserWindow.getWindow(), |
497 WebBrowserWindow.getWindow(), |
497 title, |
498 title, |
498 self.tr("Please enter a new name:"), |
499 self.tr("Please enter a new name:"), |
499 QLineEdit.EchoMode.Normal, |
500 QLineEdit.EchoMode.Normal, |
500 suggestedName) |
501 suggestedName, |
501 |
502 ) |
|
503 |
502 if not ok: |
504 if not ok: |
503 return |
505 return |
504 |
506 |
505 if not newName.endswith(".json"): |
507 if not newName.endswith(".json"): |
506 newName += ".json" |
508 newName += ".json" |
507 |
509 |
508 newSessionPath = os.path.join(self.getSessionsDirectory(), newName) |
510 newSessionPath = os.path.join(self.getSessionsDirectory(), newName) |
509 if os.path.exists(newSessionPath): |
511 if os.path.exists(newSessionPath): |
510 EricMessageBox.information( |
512 EricMessageBox.information( |
511 WebBrowserWindow.getWindow(), |
513 WebBrowserWindow.getWindow(), |
512 title, |
514 title, |
513 self.tr("""The session file "{0}" exists already. Please""" |
515 self.tr( |
514 """ enter another name.""").format(newName)) |
516 """The session file "{0}" exists already. Please""" |
|
517 """ enter another name.""" |
|
518 ).format(newName), |
|
519 ) |
515 self.renameSession(sessionFilePath, flags) |
520 self.renameSession(sessionFilePath, flags) |
516 return |
521 return |
517 |
522 |
518 if flags & SessionManager.CloneSession: |
523 if flags & SessionManager.CloneSession: |
519 if not shutil.copy(sessionFilePath, newSessionPath): |
524 if not shutil.copy(sessionFilePath, newSessionPath): |
520 EricMessageBox.critical( |
525 EricMessageBox.critical( |
521 WebBrowserWindow.getWindow(), |
526 WebBrowserWindow.getWindow(), |
522 title, |
527 title, |
523 self.tr("""An error occurred while cloning the session""" |
528 self.tr( |
524 """ file.""")) |
529 """An error occurred while cloning the session""" """ file.""" |
|
530 ), |
|
531 ) |
525 return |
532 return |
526 else: |
533 else: |
527 try: |
534 try: |
528 os.rename(sessionFilePath, newSessionPath) |
535 os.rename(sessionFilePath, newSessionPath) |
529 except OSError: |
536 except OSError: |
530 EricMessageBox.critical( |
537 EricMessageBox.critical( |
531 WebBrowserWindow.getWindow(), |
538 WebBrowserWindow.getWindow(), |
532 title, |
539 title, |
533 self.tr("""An error occurred while renaming the session""" |
540 self.tr( |
534 """ file.""")) |
541 """An error occurred while renaming the session""" """ file.""" |
|
542 ), |
|
543 ) |
535 return |
544 return |
536 if self.__isActive(sessionFilePath): |
545 if self.__isActive(sessionFilePath): |
537 self.__lastActiveSession = newSessionPath |
546 self.__lastActiveSession = newSessionPath |
538 self.__sessionMetaData = [] |
547 self.__sessionMetaData = [] |
539 |
548 |
540 def saveSession(self): |
549 def saveSession(self): |
541 """ |
550 """ |
542 Public method to save the current session. |
551 Public method to save the current session. |
543 """ |
552 """ |
544 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
553 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
|
554 |
545 newName, ok = QInputDialog.getText( |
555 newName, ok = QInputDialog.getText( |
546 WebBrowserWindow.getWindow(), |
556 WebBrowserWindow.getWindow(), |
547 self.tr("Save Session"), |
557 self.tr("Save Session"), |
548 self.tr("Please enter a name for the session:"), |
558 self.tr("Please enter a name for the session:"), |
549 QLineEdit.EchoMode.Normal, |
559 QLineEdit.EchoMode.Normal, |
550 self.tr("Saved Session ({0})").format( |
560 self.tr("Saved Session ({0})").format( |
551 QDateTime.currentDateTime().toString("yyyy-MM-dd HH-mm-ss"))) |
561 QDateTime.currentDateTime().toString("yyyy-MM-dd HH-mm-ss") |
552 |
562 ), |
|
563 ) |
|
564 |
553 if not ok: |
565 if not ok: |
554 return |
566 return |
555 |
567 |
556 if not newName.endswith(".json"): |
568 if not newName.endswith(".json"): |
557 newName += ".json" |
569 newName += ".json" |
558 |
570 |
559 newSessionPath = os.path.join(self.getSessionsDirectory(), newName) |
571 newSessionPath = os.path.join(self.getSessionsDirectory(), newName) |
560 if os.path.exists(newSessionPath): |
572 if os.path.exists(newSessionPath): |
561 EricMessageBox.information( |
573 EricMessageBox.information( |
562 WebBrowserWindow.getWindow(), |
574 WebBrowserWindow.getWindow(), |
563 self.tr("Save Session"), |
575 self.tr("Save Session"), |
564 self.tr("""The session file "{0}" exists already. Please""" |
576 self.tr( |
565 """ enter another name.""").format(newName)) |
577 """The session file "{0}" exists already. Please""" |
|
578 """ enter another name.""" |
|
579 ).format(newName), |
|
580 ) |
566 self.saveSession() |
581 self.saveSession() |
567 return |
582 return |
568 |
583 |
569 self.writeCurrentSession(newSessionPath) |
584 self.writeCurrentSession(newSessionPath) |
570 |
585 |
571 def replaceSession(self, sessionFilePath): |
586 def replaceSession(self, sessionFilePath): |
572 """ |
587 """ |
573 Public method to replace the current session with the given one. |
588 Public method to replace the current session with the given one. |
574 |
589 |
575 @param sessionFilePath file name of the session file to replace with |
590 @param sessionFilePath file name of the session file to replace with |
576 @type str |
591 @type str |
577 @return flag indicating success |
592 @return flag indicating success |
578 @rtype bool |
593 @rtype bool |
579 """ |
594 """ |
580 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
595 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
|
596 |
581 res = EricMessageBox.yesNo( |
597 res = EricMessageBox.yesNo( |
582 WebBrowserWindow.getWindow(), |
598 WebBrowserWindow.getWindow(), |
583 self.tr("Restore Backup"), |
599 self.tr("Restore Backup"), |
584 self.tr("""Are you sure you want to replace the current""" |
600 self.tr("""Are you sure you want to replace the current""" """ session?"""), |
585 """ session?""")) |
601 ) |
586 if res: |
602 if res: |
587 self.openSession(sessionFilePath, SessionManager.ReplaceSession) |
603 self.openSession(sessionFilePath, SessionManager.ReplaceSession) |
588 return True |
604 return True |
589 else: |
605 else: |
590 return False |
606 return False |
591 |
607 |
592 def switchToSession(self, sessionFilePath): |
608 def switchToSession(self, sessionFilePath): |
593 """ |
609 """ |
594 Public method to switch the current session to the given one. |
610 Public method to switch the current session to the given one. |
595 |
611 |
596 @param sessionFilePath file name of the session file to switch to |
612 @param sessionFilePath file name of the session file to switch to |
597 @type str |
613 @type str |
598 @return flag indicating success |
614 @return flag indicating success |
599 @rtype bool |
615 @rtype bool |
600 """ |
616 """ |
601 self.openSession(sessionFilePath, SessionManager.SwitchSession) |
617 self.openSession(sessionFilePath, SessionManager.SwitchSession) |
602 return True |
618 return True |
603 |
619 |
604 def cloneSession(self, sessionFilePath): |
620 def cloneSession(self, sessionFilePath): |
605 """ |
621 """ |
606 Public method to clone a session. |
622 Public method to clone a session. |
607 |
623 |
608 @param sessionFilePath file name of the session file to be cloned |
624 @param sessionFilePath file name of the session file to be cloned |
609 @type str |
625 @type str |
610 """ |
626 """ |
611 self.renameSession(sessionFilePath, SessionManager.CloneSession) |
627 self.renameSession(sessionFilePath, SessionManager.CloneSession) |
612 |
628 |
613 def deleteSession(self, sessionFilePath): |
629 def deleteSession(self, sessionFilePath): |
614 """ |
630 """ |
615 Public method to delete a session. |
631 Public method to delete a session. |
616 |
632 |
617 @param sessionFilePath file name of the session file to be deleted |
633 @param sessionFilePath file name of the session file to be deleted |
618 @type str |
634 @type str |
619 """ |
635 """ |
620 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
636 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
|
637 |
621 sfp = pathlib.Path(sessionFilePath) |
638 sfp = pathlib.Path(sessionFilePath) |
622 res = EricMessageBox.yesNo( |
639 res = EricMessageBox.yesNo( |
623 WebBrowserWindow.getWindow(), |
640 WebBrowserWindow.getWindow(), |
624 self.tr("Delete Session"), |
641 self.tr("Delete Session"), |
625 self.tr("""Are you sure you want to delete session "{0}"?""") |
642 self.tr("""Are you sure you want to delete session "{0}"?""").format( |
626 .format(sfp.stem)) |
643 sfp.stem |
|
644 ), |
|
645 ) |
627 if res: |
646 if res: |
628 sfp.unlink() |
647 sfp.unlink() |
629 |
648 |
630 def newSession(self): |
649 def newSession(self): |
631 """ |
650 """ |
632 Public method to start a new session. |
651 Public method to start a new session. |
633 """ |
652 """ |
634 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
653 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
|
654 |
635 newName, ok = QInputDialog.getText( |
655 newName, ok = QInputDialog.getText( |
636 WebBrowserWindow.getWindow(), |
656 WebBrowserWindow.getWindow(), |
637 self.tr("New Session"), |
657 self.tr("New Session"), |
638 self.tr("Please enter a name for the new session:"), |
658 self.tr("Please enter a name for the new session:"), |
639 QLineEdit.EchoMode.Normal, |
659 QLineEdit.EchoMode.Normal, |
640 self.tr("New Session ({0})").format( |
660 self.tr("New Session ({0})").format( |
641 QDateTime.currentDateTime().toString("yyyy-MM-dd HH-mm-ss"))) |
661 QDateTime.currentDateTime().toString("yyyy-MM-dd HH-mm-ss") |
642 |
662 ), |
|
663 ) |
|
664 |
643 if not ok: |
665 if not ok: |
644 return |
666 return |
645 |
667 |
646 if not newName.endswith(".json"): |
668 if not newName.endswith(".json"): |
647 newName += ".json" |
669 newName += ".json" |
648 |
670 |
649 newSessionPath = os.path.join(self.getSessionsDirectory(), newName) |
671 newSessionPath = os.path.join(self.getSessionsDirectory(), newName) |
650 if os.path.exists(newSessionPath): |
672 if os.path.exists(newSessionPath): |
651 EricMessageBox.information( |
673 EricMessageBox.information( |
652 WebBrowserWindow.getWindow(), |
674 WebBrowserWindow.getWindow(), |
653 self.tr("New Session"), |
675 self.tr("New Session"), |
654 self.tr("""The session file "{0}" exists already. Please""" |
676 self.tr( |
655 """ enter another name.""").format(newName)) |
677 """The session file "{0}" exists already. Please""" |
|
678 """ enter another name.""" |
|
679 ).format(newName), |
|
680 ) |
656 self.newSession() |
681 self.newSession() |
657 return |
682 return |
658 |
683 |
659 self.writeCurrentSession(self.__lastActiveSession) |
684 self.writeCurrentSession(self.__lastActiveSession) |
660 |
685 |
661 # create new window for the new session and close all existing windows |
686 # create new window for the new session and close all existing windows |
662 window = WebBrowserWindow.mainWindow().newWindow() |
687 window = WebBrowserWindow.mainWindow().newWindow() |
663 for win in WebBrowserWindow.mainWindows(): |
688 for win in WebBrowserWindow.mainWindows(): |
664 if win is not window: |
689 if win is not window: |
665 win.forceClose() |
690 win.forceClose() |
666 |
691 |
667 self.__lastActiveSession = newSessionPath |
692 self.__lastActiveSession = newSessionPath |
668 self.__autoSaveSession() |
693 self.__autoSaveSession() |
669 |
694 |
670 def showSessionManagerDialog(self): |
695 def showSessionManagerDialog(self): |
671 """ |
696 """ |
672 Public method to show the session manager dialog. |
697 Public method to show the session manager dialog. |
673 """ |
698 """ |
674 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
699 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
675 from .SessionManagerDialog import SessionManagerDialog |
700 from .SessionManagerDialog import SessionManagerDialog |
676 |
701 |
677 dlg = SessionManagerDialog(WebBrowserWindow.getWindow()) |
702 dlg = SessionManagerDialog(WebBrowserWindow.getWindow()) |
678 dlg.open() |
703 dlg.open() |
679 |
704 |
680 def selectSession(self): |
705 def selectSession(self): |
681 """ |
706 """ |
682 Public method to select a session to be restored. |
707 Public method to select a session to be restored. |
683 |
708 |
684 @return name of the session file to be restored |
709 @return name of the session file to be restored |
685 @rtype str |
710 @rtype str |
686 """ |
711 """ |
687 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
712 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
688 |
713 |
689 self.__fillMetaDataList() |
714 self.__fillMetaDataList() |
690 |
715 |
691 if self.__sessionMetaData: |
716 if self.__sessionMetaData: |
692 # skip, if no session file available |
717 # skip, if no session file available |
693 dlg = QDialog(WebBrowserWindow.getWindow(), |
718 dlg = QDialog( |
694 Qt.WindowType.WindowStaysOnTopHint) |
719 WebBrowserWindow.getWindow(), Qt.WindowType.WindowStaysOnTopHint |
|
720 ) |
695 lbl = QLabel(self.tr("Please select the startup session:")) |
721 lbl = QLabel(self.tr("Please select the startup session:")) |
696 combo = QComboBox(dlg) |
722 combo = QComboBox(dlg) |
697 buttonBox = QDialogButtonBox( |
723 buttonBox = QDialogButtonBox( |
698 QDialogButtonBox.StandardButton.Ok | |
724 QDialogButtonBox.StandardButton.Ok |
699 QDialogButtonBox.StandardButton.Cancel, |
725 | QDialogButtonBox.StandardButton.Cancel, |
700 dlg) |
726 dlg, |
|
727 ) |
701 buttonBox.accepted.connect(dlg.accept) |
728 buttonBox.accepted.connect(dlg.accept) |
702 buttonBox.rejected.connect(dlg.reject) |
729 buttonBox.rejected.connect(dlg.reject) |
703 |
730 |
704 layout = QVBoxLayout() |
731 layout = QVBoxLayout() |
705 layout.addWidget(lbl) |
732 layout.addWidget(lbl) |
706 layout.addWidget(combo) |
733 layout.addWidget(combo) |
707 layout.addWidget(buttonBox) |
734 layout.addWidget(buttonBox) |
708 dlg.setLayout(layout) |
735 dlg.setLayout(layout) |
709 |
736 |
710 lastActiveSessionFilePath = pathlib.Path(self.__lastActiveSession) |
737 lastActiveSessionFilePath = pathlib.Path(self.__lastActiveSession) |
711 |
738 |
712 for metaData in self.__sessionMetaData: |
739 for metaData in self.__sessionMetaData: |
713 if ( |
740 if pathlib.Path(metaData.filePath) != lastActiveSessionFilePath: |
714 pathlib.Path(metaData.filePath) != |
|
715 lastActiveSessionFilePath |
|
716 ): |
|
717 combo.addItem(metaData.name, metaData.filePath) |
741 combo.addItem(metaData.name, metaData.filePath) |
718 else: |
742 else: |
719 combo.insertItem( |
743 combo.insertItem( |
720 0, |
744 0, |
721 self.tr("{0} (last session)").format(metaData.name), |
745 self.tr("{0} (last session)").format(metaData.name), |
722 metaData.filePath |
746 metaData.filePath, |
723 ) |
747 ) |
724 combo.setCurrentIndex(0) |
748 combo.setCurrentIndex(0) |
725 |
749 |
726 if dlg.exec() == QDialog.DialogCode.Accepted: |
750 if dlg.exec() == QDialog.DialogCode.Accepted: |
727 session = combo.currentData() |
751 session = combo.currentData() |
728 if session is None: |
752 if session is None: |
729 self.__lastActiveSession = self.__sessionDefault |
753 self.__lastActiveSession = self.__sessionDefault |
730 else: |
754 else: |
731 self.__lastActiveSession = session |
755 self.__lastActiveSession = session |
732 |
756 |
733 return self.__lastActiveSession |
757 return self.__lastActiveSession |