src/eric7/WebBrowser/Session/SessionManager.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9280
94f4e2751790
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
13 import contextlib 13 import contextlib
14 import pathlib 14 import pathlib
15 import shutil 15 import shutil
16 16
17 from PyQt6.QtCore import ( 17 from PyQt6.QtCore import (
18 pyqtSlot, pyqtSignal, Qt, QObject, QTimer, QDir, QFileSystemWatcher, 18 pyqtSlot,
19 QByteArray, QDateTime 19 pyqtSignal,
20 Qt,
21 QObject,
22 QTimer,
23 QDir,
24 QFileSystemWatcher,
25 QByteArray,
26 QDateTime,
20 ) 27 )
21 from PyQt6.QtGui import QActionGroup 28 from PyQt6.QtGui import QActionGroup
22 from PyQt6.QtWidgets import ( 29 from PyQt6.QtWidgets import (
23 QApplication, QInputDialog, QLineEdit, QDialog, QDialogButtonBox, QLabel, 30 QApplication,
24 QComboBox, QVBoxLayout 31 QInputDialog,
32 QLineEdit,
33 QDialog,
34 QDialogButtonBox,
35 QLabel,
36 QComboBox,
37 QVBoxLayout,
25 ) 38 )
26 39
27 from EricWidgets import EricMessageBox 40 from EricWidgets import EricMessageBox
28 from EricGui.EricOverrideCursor import EricOverrideCursor 41 from EricGui.EricOverrideCursor import EricOverrideCursor
29 42
33 46
34 class SessionMetaData: 47 class SessionMetaData:
35 """ 48 """
36 Class implementing a data structure to store meta data for a session. 49 Class implementing a data structure to store meta data for a session.
37 """ 50 """
51
38 def __init__(self): 52 def __init__(self):
39 """ 53 """
40 Constructor 54 Constructor
41 """ 55 """
42 self.name = "" 56 self.name = ""
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

eric ide

mercurial