|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2010 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the central widget showing the web pages. |
|
8 """ |
|
9 |
|
10 import os |
|
11 |
|
12 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl, QMarginsF |
|
13 from PyQt6.QtGui import QIcon, QPixmap, QPainter, QPageLayout |
|
14 from PyQt6.QtWidgets import ( |
|
15 QWidget, QHBoxLayout, QMenu, QToolButton, QDialog |
|
16 ) |
|
17 from PyQt6.QtPrintSupport import QPrinter, QPrintDialog, QAbstractPrintDialog |
|
18 |
|
19 from EricWidgets.EricTabWidget import EricTabWidget |
|
20 from EricWidgets import EricMessageBox |
|
21 from EricWidgets.EricApplication import ericApp |
|
22 from EricGui.EricOverrideCursor import EricOverrideCursor |
|
23 |
|
24 from .WebBrowserView import WebBrowserView |
|
25 from .WebBrowserPage import WebBrowserPage |
|
26 from .Tools import WebBrowserTools |
|
27 from . import WebInspector |
|
28 |
|
29 import UI.PixmapCache |
|
30 |
|
31 import Utilities |
|
32 import Preferences |
|
33 import Globals |
|
34 |
|
35 |
|
36 def isCupsAvailable(): |
|
37 """ |
|
38 Static method to test the availability of CUPS. |
|
39 |
|
40 @return flag indicating the availability of CUPS |
|
41 @rtype bool |
|
42 """ |
|
43 if Globals.isMacPlatform(): |
|
44 # OS X/MacOS always have CUPS |
|
45 return True |
|
46 elif Globals.isLinuxPlatform(): |
|
47 testPrinter = QPrinter() |
|
48 return testPrinter.supportsMultipleCopies() |
|
49 else: |
|
50 return False |
|
51 |
|
52 |
|
53 class WebBrowserTabWidget(EricTabWidget): |
|
54 """ |
|
55 Class implementing the central widget showing the web pages. |
|
56 |
|
57 @signal sourceChanged(WebBrowserView, QUrl) emitted after the URL of a |
|
58 browser has changed |
|
59 @signal currentUrlChanged(QUrl) emitted after the URL of the current |
|
60 browser has changed |
|
61 @signal titleChanged(WebBrowserView, str) emitted after the title of a |
|
62 browser has changed |
|
63 @signal showMessage(str) emitted to show a message in the main window |
|
64 status bar |
|
65 @signal browserOpened(QWidget) emitted after a new browser was created |
|
66 @signal browserClosed(QWidget) emitted after a browser was closed |
|
67 @signal browserZoomValueChanged(int) emitted to signal a change of the |
|
68 current browser's zoom level |
|
69 """ |
|
70 sourceChanged = pyqtSignal(WebBrowserView, QUrl) |
|
71 currentUrlChanged = pyqtSignal(QUrl) |
|
72 titleChanged = pyqtSignal(WebBrowserView, str) |
|
73 showMessage = pyqtSignal(str) |
|
74 browserOpened = pyqtSignal(QWidget) |
|
75 browserClosed = pyqtSignal(QWidget) |
|
76 browserZoomValueChanged = pyqtSignal(int) |
|
77 |
|
78 def __init__(self, parent): |
|
79 """ |
|
80 Constructor |
|
81 |
|
82 @param parent reference to the parent widget (QWidget) |
|
83 """ |
|
84 super().__init__(parent, dnd=True) |
|
85 |
|
86 from .WebBrowserTabBar import WebBrowserTabBar |
|
87 self.__tabBar = WebBrowserTabBar(self) |
|
88 self.setCustomTabBar(True, self.__tabBar) |
|
89 |
|
90 self.__mainWindow = parent |
|
91 |
|
92 self.setUsesScrollButtons(True) |
|
93 self.setDocumentMode(True) |
|
94 self.setElideMode(Qt.TextElideMode.ElideNone) |
|
95 |
|
96 from .ClosedTabsManager import ClosedTabsManager |
|
97 self.__closedTabsManager = ClosedTabsManager(self) |
|
98 self.__closedTabsManager.closedTabAvailable.connect( |
|
99 self.__closedTabAvailable) |
|
100 |
|
101 from .UrlBar.StackedUrlBar import StackedUrlBar |
|
102 self.__stackedUrlBar = StackedUrlBar(self) |
|
103 self.__tabBar.tabMoved.connect(self.__stackedUrlBar.moveBar) |
|
104 |
|
105 self.__tabContextMenuIndex = -1 |
|
106 self.currentChanged[int].connect(self.__currentChanged) |
|
107 self.setTabContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) |
|
108 self.customTabContextMenuRequested.connect(self.__showContextMenu) |
|
109 |
|
110 self.__rightCornerWidget = QWidget(self) |
|
111 self.__rightCornerWidgetLayout = QHBoxLayout(self.__rightCornerWidget) |
|
112 self.__rightCornerWidgetLayout.setContentsMargins(0, 0, 0, 0) |
|
113 self.__rightCornerWidgetLayout.setSpacing(0) |
|
114 |
|
115 self.__navigationMenu = QMenu(self) |
|
116 self.__navigationMenu.aboutToShow.connect(self.__showNavigationMenu) |
|
117 self.__navigationMenu.triggered.connect(self.__navigationMenuTriggered) |
|
118 |
|
119 self.__navigationButton = QToolButton(self) |
|
120 self.__navigationButton.setIcon( |
|
121 UI.PixmapCache.getIcon("1downarrow")) |
|
122 self.__navigationButton.setToolTip( |
|
123 self.tr("Show a navigation menu")) |
|
124 self.__navigationButton.setPopupMode( |
|
125 QToolButton.ToolButtonPopupMode.InstantPopup) |
|
126 self.__navigationButton.setMenu(self.__navigationMenu) |
|
127 self.__navigationButton.setEnabled(False) |
|
128 self.__rightCornerWidgetLayout.addWidget(self.__navigationButton) |
|
129 |
|
130 self.__closedTabsMenu = QMenu(self) |
|
131 self.__closedTabsMenu.aboutToShow.connect( |
|
132 self.__aboutToShowClosedTabsMenu) |
|
133 |
|
134 self.__closedTabsButton = QToolButton(self) |
|
135 self.__closedTabsButton.setIcon(UI.PixmapCache.getIcon("trash")) |
|
136 self.__closedTabsButton.setToolTip( |
|
137 self.tr("Show a navigation menu for closed tabs")) |
|
138 self.__closedTabsButton.setPopupMode( |
|
139 QToolButton.ToolButtonPopupMode.InstantPopup) |
|
140 self.__closedTabsButton.setMenu(self.__closedTabsMenu) |
|
141 self.__closedTabsButton.setEnabled(False) |
|
142 self.__rightCornerWidgetLayout.addWidget(self.__closedTabsButton) |
|
143 |
|
144 self.setTabsClosable(True) |
|
145 self.tabCloseRequested.connect(self.closeBrowserAt) |
|
146 |
|
147 self.setCornerWidget( |
|
148 self.__rightCornerWidget, Qt.Corner.TopRightCorner) |
|
149 |
|
150 self.__newTabButton = QToolButton(self) |
|
151 self.__newTabButton.setIcon(UI.PixmapCache.getIcon("plus")) |
|
152 self.__newTabButton.setToolTip( |
|
153 self.tr("Open a new web browser tab")) |
|
154 self.setCornerWidget(self.__newTabButton, Qt.Corner.TopLeftCorner) |
|
155 self.__newTabButton.clicked.connect(self.__newBrowser) |
|
156 |
|
157 self.__initTabContextMenu() |
|
158 |
|
159 self.__historyCompleter = None |
|
160 |
|
161 def __initTabContextMenu(self): |
|
162 """ |
|
163 Private method to create the tab context menu. |
|
164 """ |
|
165 self.__tabContextMenu = QMenu(self) |
|
166 self.tabContextNewAct = self.__tabContextMenu.addAction( |
|
167 UI.PixmapCache.getIcon("tabNew"), |
|
168 self.tr('New Tab'), self.newBrowser) |
|
169 self.__tabContextMenu.addSeparator() |
|
170 self.leftMenuAct = self.__tabContextMenu.addAction( |
|
171 UI.PixmapCache.getIcon("1leftarrow"), |
|
172 self.tr('Move Left'), self.__tabContextMenuMoveLeft) |
|
173 self.rightMenuAct = self.__tabContextMenu.addAction( |
|
174 UI.PixmapCache.getIcon("1rightarrow"), |
|
175 self.tr('Move Right'), self.__tabContextMenuMoveRight) |
|
176 self.__tabContextMenu.addSeparator() |
|
177 self.tabContextCloneAct = self.__tabContextMenu.addAction( |
|
178 self.tr("Duplicate Page"), self.__tabContextMenuClone) |
|
179 self.__tabContextMenu.addSeparator() |
|
180 self.tabContextCloseAct = self.__tabContextMenu.addAction( |
|
181 UI.PixmapCache.getIcon("tabClose"), |
|
182 self.tr('Close'), self.__tabContextMenuClose) |
|
183 self.tabContextCloseOthersAct = self.__tabContextMenu.addAction( |
|
184 UI.PixmapCache.getIcon("tabCloseOther"), |
|
185 self.tr("Close Others"), self.__tabContextMenuCloseOthers) |
|
186 self.__tabContextMenu.addAction( |
|
187 self.tr('Close All'), self.closeAllBrowsers) |
|
188 self.__tabContextMenu.addSeparator() |
|
189 self.__tabContextMenu.addAction( |
|
190 UI.PixmapCache.getIcon("printPreview"), |
|
191 self.tr('Print Preview'), self.__tabContextMenuPrintPreview) |
|
192 self.__tabContextMenu.addAction( |
|
193 UI.PixmapCache.getIcon("print"), |
|
194 self.tr('Print'), self.__tabContextMenuPrint) |
|
195 self.__tabContextMenu.addAction( |
|
196 UI.PixmapCache.getIcon("printPdf"), |
|
197 self.tr('Print as PDF'), self.__tabContextMenuPrintPdf) |
|
198 self.__tabContextMenu.addSeparator() |
|
199 if hasattr(WebBrowserPage, "isAudioMuted"): |
|
200 self.__audioAct = self.__tabContextMenu.addAction( |
|
201 "", self.__tabContextMenuAudioMute) |
|
202 self.__tabContextMenu.addSeparator() |
|
203 else: |
|
204 self.__audioAct = None |
|
205 self.__tabContextMenu.addAction( |
|
206 UI.PixmapCache.getIcon("reload"), |
|
207 self.tr('Reload All'), self.reloadAllBrowsers) |
|
208 self.__tabContextMenu.addSeparator() |
|
209 self.__tabContextMenu.addAction( |
|
210 UI.PixmapCache.getIcon("addBookmark"), |
|
211 self.tr('Bookmark All Tabs'), self.__mainWindow.bookmarkAll) |
|
212 |
|
213 self.__tabBackContextMenu = QMenu(self) |
|
214 self.__tabBackContextMenu.addAction( |
|
215 self.tr('Close All'), self.closeAllBrowsers) |
|
216 self.__tabBackContextMenu.addAction( |
|
217 UI.PixmapCache.getIcon("reload"), |
|
218 self.tr('Reload All'), self.reloadAllBrowsers) |
|
219 self.__tabBackContextMenu.addAction( |
|
220 UI.PixmapCache.getIcon("addBookmark"), |
|
221 self.tr('Bookmark All Tabs'), self.__mainWindow.bookmarkAll) |
|
222 self.__tabBackContextMenu.addSeparator() |
|
223 self.__restoreClosedTabAct = self.__tabBackContextMenu.addAction( |
|
224 UI.PixmapCache.getIcon("trash"), |
|
225 self.tr('Restore Closed Tab')) |
|
226 self.__restoreClosedTabAct.setEnabled(False) |
|
227 self.__restoreClosedTabAct.setData(0) |
|
228 self.__restoreClosedTabAct.triggered.connect( |
|
229 lambda: self.restoreClosedTab(self.__restoreClosedTabAct)) |
|
230 |
|
231 def __showContextMenu(self, coord, index): |
|
232 """ |
|
233 Private slot to show the tab context menu. |
|
234 |
|
235 @param coord the position of the mouse pointer (QPoint) |
|
236 @param index index of the tab the menu is requested for (integer) |
|
237 """ |
|
238 coord = self.mapToGlobal(coord) |
|
239 if index == -1: |
|
240 self.__tabBackContextMenu.popup(coord) |
|
241 else: |
|
242 self.__tabContextMenuIndex = index |
|
243 self.leftMenuAct.setEnabled(index > 0) |
|
244 self.rightMenuAct.setEnabled(index < self.count() - 1) |
|
245 |
|
246 self.tabContextCloseOthersAct.setEnabled(self.count() > 1) |
|
247 |
|
248 if self.__audioAct is not None: |
|
249 if ( |
|
250 self.widget(self.__tabContextMenuIndex).page() |
|
251 .isAudioMuted() |
|
252 ): |
|
253 self.__audioAct.setText(self.tr("Unmute Tab")) |
|
254 self.__audioAct.setIcon( |
|
255 UI.PixmapCache.getIcon("audioVolumeHigh")) |
|
256 else: |
|
257 self.__audioAct.setText(self.tr("Mute Tab")) |
|
258 self.__audioAct.setIcon( |
|
259 UI.PixmapCache.getIcon("audioVolumeMuted")) |
|
260 |
|
261 self.__tabContextMenu.popup(coord) |
|
262 |
|
263 def __tabContextMenuMoveLeft(self): |
|
264 """ |
|
265 Private method to move a tab one position to the left. |
|
266 """ |
|
267 self.moveTab(self.__tabContextMenuIndex, |
|
268 self.__tabContextMenuIndex - 1) |
|
269 |
|
270 def __tabContextMenuMoveRight(self): |
|
271 """ |
|
272 Private method to move a tab one position to the right. |
|
273 """ |
|
274 self.moveTab(self.__tabContextMenuIndex, |
|
275 self.__tabContextMenuIndex + 1) |
|
276 |
|
277 def __tabContextMenuClone(self): |
|
278 """ |
|
279 Private method to clone the selected tab. |
|
280 """ |
|
281 idx = self.__tabContextMenuIndex |
|
282 if idx < 0: |
|
283 idx = self.currentIndex() |
|
284 if idx < 0 or idx > self.count(): |
|
285 return |
|
286 |
|
287 url = self.widget(idx).url() |
|
288 self.newBrowser(url) |
|
289 |
|
290 def __tabContextMenuClose(self): |
|
291 """ |
|
292 Private method to close the selected tab. |
|
293 """ |
|
294 self.closeBrowserAt(self.__tabContextMenuIndex) |
|
295 |
|
296 def __tabContextMenuCloseOthers(self): |
|
297 """ |
|
298 Private slot to close all other tabs. |
|
299 """ |
|
300 index = self.__tabContextMenuIndex |
|
301 for i in ( |
|
302 list(range(self.count() - 1, index, -1)) + |
|
303 list(range(index - 1, -1, -1)) |
|
304 ): |
|
305 self.closeBrowserAt(i) |
|
306 |
|
307 def __tabContextMenuPrint(self): |
|
308 """ |
|
309 Private method to print the selected tab. |
|
310 """ |
|
311 browser = self.widget(self.__tabContextMenuIndex) |
|
312 self.printBrowser(browser) |
|
313 |
|
314 def __tabContextMenuPrintPdf(self): |
|
315 """ |
|
316 Private method to print the selected tab as PDF. |
|
317 """ |
|
318 browser = self.widget(self.__tabContextMenuIndex) |
|
319 self.printBrowserPdf(browser) |
|
320 |
|
321 def __tabContextMenuPrintPreview(self): |
|
322 """ |
|
323 Private method to show a print preview of the selected tab. |
|
324 """ |
|
325 browser = self.widget(self.__tabContextMenuIndex) |
|
326 self.printPreviewBrowser(browser) |
|
327 |
|
328 def __tabContextMenuAudioMute(self): |
|
329 """ |
|
330 Private method to mute or unmute the selected tab. |
|
331 """ |
|
332 page = self.widget(self.__tabContextMenuIndex).page() |
|
333 muted = page.isAudioMuted() |
|
334 page.setAudioMuted(not muted) |
|
335 |
|
336 @pyqtSlot(bool) |
|
337 def __recentlyAudibleChanged(self, recentlyAudible, page): |
|
338 """ |
|
339 Private slot to react on the audible state of a page. |
|
340 |
|
341 @param recentlyAudible flag indicating the new audible state |
|
342 @type bool |
|
343 @param page reference to the web page |
|
344 @type WebBrowserPage |
|
345 """ |
|
346 browser = page.view() |
|
347 if browser is None: |
|
348 return |
|
349 |
|
350 index = self.indexOf(browser) |
|
351 icon = page.icon() |
|
352 |
|
353 if page.isAudioMuted() or ( |
|
354 not page.isAudioMuted() and recentlyAudible): |
|
355 pix = QPixmap(32, 32) |
|
356 pix.fill(Qt.GlobalColor.transparent) |
|
357 painter = QPainter(pix) |
|
358 icon.paint(painter, 0, 0, 22, 22) |
|
359 if page.isAudioMuted(): |
|
360 audioIcon = UI.PixmapCache.getIcon("audioMuted") |
|
361 else: |
|
362 audioIcon = UI.PixmapCache.getIcon("audioPlaying") |
|
363 audioIcon.paint(painter, 13, 13, 18, 18) |
|
364 painter.end() |
|
365 self.setTabIcon(index, QIcon(pix)) |
|
366 else: |
|
367 self.setTabIcon(index, icon) |
|
368 |
|
369 @pyqtSlot() |
|
370 def __newBrowser(self): |
|
371 """ |
|
372 Private slot to open a new browser tab. |
|
373 """ |
|
374 self.newBrowser() |
|
375 |
|
376 def newBrowser(self, link=None, position=-1, background=False, |
|
377 restoreSession=False): |
|
378 """ |
|
379 Public method to create a new web browser tab. |
|
380 |
|
381 @param link link to be shown |
|
382 @type str or QUrl |
|
383 @param position position to create the new tab at or -1 to add it |
|
384 to the end |
|
385 @type int |
|
386 @param background flag indicating to open the tab in the |
|
387 background |
|
388 @type bool |
|
389 @param restoreSession flag indicating a restore session action |
|
390 @type bool |
|
391 @return reference to the new browser |
|
392 @rtype WebBrowserView |
|
393 """ |
|
394 if link is None: |
|
395 linkName = "" |
|
396 elif isinstance(link, QUrl): |
|
397 linkName = link.toString() |
|
398 else: |
|
399 linkName = link |
|
400 |
|
401 from .UrlBar.UrlBar import UrlBar |
|
402 urlbar = UrlBar(self.__mainWindow, self) |
|
403 if self.__historyCompleter is None: |
|
404 import WebBrowser.WebBrowserWindow |
|
405 from .History.HistoryCompleter import ( |
|
406 HistoryCompletionModel, HistoryCompleter |
|
407 ) |
|
408 self.__historyCompletionModel = HistoryCompletionModel(self) |
|
409 self.__historyCompletionModel.setSourceModel( |
|
410 WebBrowser.WebBrowserWindow.WebBrowserWindow.historyManager() |
|
411 .historyFilterModel()) |
|
412 self.__historyCompleter = HistoryCompleter( |
|
413 self.__historyCompletionModel, self) |
|
414 self.__historyCompleter.activated[str].connect(self.__pathSelected) |
|
415 urlbar.setCompleter(self.__historyCompleter) |
|
416 urlbar.returnPressed.connect( |
|
417 lambda: self.__lineEditReturnPressed(urlbar)) |
|
418 if position == -1: |
|
419 self.__stackedUrlBar.addWidget(urlbar) |
|
420 else: |
|
421 self.__stackedUrlBar.insertWidget(position, urlbar) |
|
422 |
|
423 browser = WebBrowserView(self.__mainWindow, self) |
|
424 urlbar.setBrowser(browser) |
|
425 |
|
426 browser.sourceChanged.connect( |
|
427 lambda url: self.__sourceChanged(url, browser)) |
|
428 browser.titleChanged.connect( |
|
429 lambda title: self.__titleChanged(title, browser)) |
|
430 browser.highlighted.connect(self.showMessage) |
|
431 browser.backwardAvailable.connect( |
|
432 self.__mainWindow.setBackwardAvailable) |
|
433 browser.forwardAvailable.connect(self.__mainWindow.setForwardAvailable) |
|
434 browser.loadProgress.connect( |
|
435 lambda progress: self.__loadProgress(progress, browser)) |
|
436 browser.loadFinished.connect( |
|
437 lambda ok: self.__loadFinished(ok, browser)) |
|
438 browser.faviconChanged.connect( |
|
439 lambda: self.__iconChanged(browser)) |
|
440 browser.search.connect(self.newBrowser) |
|
441 browser.page().windowCloseRequested.connect( |
|
442 lambda: self.__windowCloseRequested(browser.page())) |
|
443 browser.zoomValueChanged.connect(self.browserZoomValueChanged) |
|
444 if hasattr(WebBrowserPage, "recentlyAudibleChanged"): |
|
445 browser.page().recentlyAudibleChanged.connect( |
|
446 lambda audible: self.__recentlyAudibleChanged( |
|
447 audible, browser.page())) |
|
448 browser.page().printRequested.connect( |
|
449 lambda: self.printBrowser(browser)) |
|
450 browser.showMessage.connect(self.showMessage) |
|
451 |
|
452 index = ( |
|
453 self.addTab(browser, self.tr("...")) |
|
454 if position == -1 else |
|
455 self.insertTab(position, browser, self.tr("...")) |
|
456 ) |
|
457 if not background: |
|
458 self.setCurrentIndex(index) |
|
459 |
|
460 self.__mainWindow.closeAct.setEnabled(True) |
|
461 self.__mainWindow.closeAllAct.setEnabled(True) |
|
462 self.__navigationButton.setEnabled(True) |
|
463 |
|
464 if not restoreSession and not linkName: |
|
465 if Preferences.getWebBrowser("NewTabBehavior") == 0: |
|
466 linkName = "about:blank" |
|
467 elif Preferences.getWebBrowser("NewTabBehavior") == 1: |
|
468 linkName = Preferences.getWebBrowser("HomePage") |
|
469 elif Preferences.getWebBrowser("NewTabBehavior") == 2: |
|
470 linkName = "eric:speeddial" |
|
471 |
|
472 if linkName == "eric:blank": |
|
473 linkName = "about:blank" |
|
474 |
|
475 if linkName: |
|
476 browser.setSource(QUrl(linkName)) |
|
477 if not browser.documentTitle(): |
|
478 self.setTabText( |
|
479 index, |
|
480 self.__elide(linkName, Qt.TextElideMode.ElideMiddle) |
|
481 ) |
|
482 self.setTabToolTip(index, linkName) |
|
483 else: |
|
484 self.setTabText( |
|
485 index, |
|
486 self.__elide(browser.documentTitle().replace("&", "&&"))) |
|
487 self.setTabToolTip(index, browser.documentTitle()) |
|
488 |
|
489 self.browserOpened.emit(browser) |
|
490 |
|
491 return browser |
|
492 |
|
493 def newBrowserAfter(self, browser, link=None, background=False): |
|
494 """ |
|
495 Public method to create a new web browser tab after a given one. |
|
496 |
|
497 @param browser reference to the browser to add after (WebBrowserView) |
|
498 @param link link to be shown (string or QUrl) |
|
499 @param background flag indicating to open the tab in the |
|
500 background (bool) |
|
501 @return reference to the new browser |
|
502 @rtype WebBrowserView |
|
503 """ |
|
504 position = self.indexOf(browser) + 1 if browser else -1 |
|
505 return self.newBrowser(link, position, background) |
|
506 |
|
507 def __showNavigationMenu(self): |
|
508 """ |
|
509 Private slot to show the navigation button menu. |
|
510 """ |
|
511 self.__navigationMenu.clear() |
|
512 for index in range(self.count()): |
|
513 act = self.__navigationMenu.addAction( |
|
514 self.tabIcon(index), self.tabText(index)) |
|
515 act.setData(index) |
|
516 |
|
517 def __navigationMenuTriggered(self, act): |
|
518 """ |
|
519 Private slot called to handle the navigation button menu selection. |
|
520 |
|
521 @param act reference to the selected action (QAction) |
|
522 """ |
|
523 index = act.data() |
|
524 if index is not None: |
|
525 self.setCurrentIndex(index) |
|
526 |
|
527 def __windowCloseRequested(self, page): |
|
528 """ |
|
529 Private slot to handle the windowCloseRequested signal of a browser. |
|
530 |
|
531 @param page reference to the web page |
|
532 @type WebBrowserPage |
|
533 """ |
|
534 browser = page.view() |
|
535 if browser is None: |
|
536 return |
|
537 |
|
538 index = self.indexOf(browser) |
|
539 self.closeBrowserAt(index) |
|
540 |
|
541 def reloadAllBrowsers(self): |
|
542 """ |
|
543 Public slot to reload all browsers. |
|
544 """ |
|
545 for index in range(self.count()): |
|
546 browser = self.widget(index) |
|
547 browser and browser.reload() |
|
548 |
|
549 @pyqtSlot() |
|
550 def closeBrowser(self): |
|
551 """ |
|
552 Public slot called to handle the close action. |
|
553 """ |
|
554 self.closeBrowserAt(self.currentIndex()) |
|
555 |
|
556 def closeAllBrowsers(self, shutdown=False): |
|
557 """ |
|
558 Public slot called to handle the close all action. |
|
559 |
|
560 @param shutdown flag indicating a shutdown action |
|
561 @type bool |
|
562 """ |
|
563 for index in range(self.count() - 1, -1, -1): |
|
564 self.closeBrowserAt(index, shutdown=shutdown) |
|
565 |
|
566 def closeBrowserView(self, browser): |
|
567 """ |
|
568 Public method to close the given browser. |
|
569 |
|
570 @param browser reference to the web browser view to be closed |
|
571 @type WebBrowserView |
|
572 """ |
|
573 index = self.indexOf(browser) |
|
574 self.closeBrowserAt(index) |
|
575 |
|
576 def closeBrowserAt(self, index, shutdown=False): |
|
577 """ |
|
578 Public slot to close a browser based on its index. |
|
579 |
|
580 @param index index of browser to close |
|
581 @type int |
|
582 @param shutdown flag indicating a shutdown action |
|
583 @type bool |
|
584 """ |
|
585 browser = self.widget(index) |
|
586 if browser is None: |
|
587 return |
|
588 |
|
589 urlbar = self.__stackedUrlBar.widget(index) |
|
590 self.__stackedUrlBar.removeWidget(urlbar) |
|
591 urlbar.deleteLater() |
|
592 del urlbar |
|
593 |
|
594 self.__closedTabsManager.recordBrowser(browser, index) |
|
595 |
|
596 browser.closeWebInspector() |
|
597 WebInspector.unregisterView(browser) |
|
598 self.removeTab(index) |
|
599 self.browserClosed.emit(browser) |
|
600 browser.deleteLater() |
|
601 del browser |
|
602 |
|
603 if self.count() == 0 and not shutdown: |
|
604 self.newBrowser() |
|
605 else: |
|
606 self.currentChanged[int].emit(self.currentIndex()) |
|
607 |
|
608 def currentBrowser(self): |
|
609 """ |
|
610 Public method to get a reference to the current browser. |
|
611 |
|
612 @return reference to the current browser (WebBrowserView) |
|
613 """ |
|
614 return self.currentWidget() |
|
615 |
|
616 def browserAt(self, index): |
|
617 """ |
|
618 Public method to get a reference to the browser with the given index. |
|
619 |
|
620 @param index index of the browser to get (integer) |
|
621 @return reference to the indexed browser (WebBrowserView) |
|
622 """ |
|
623 return self.widget(index) |
|
624 |
|
625 def browsers(self): |
|
626 """ |
|
627 Public method to get a list of references to all browsers. |
|
628 |
|
629 @return list of references to browsers (list of WebBrowserView) |
|
630 """ |
|
631 li = [] |
|
632 for index in range(self.count()): |
|
633 li.append(self.widget(index)) |
|
634 return li |
|
635 |
|
636 @pyqtSlot() |
|
637 def printBrowser(self, browser=None): |
|
638 """ |
|
639 Public slot called to print the displayed page. |
|
640 |
|
641 @param browser reference to the browser to be printed (WebBrowserView) |
|
642 """ |
|
643 if browser is None: |
|
644 browser = self.currentBrowser() |
|
645 |
|
646 printer = QPrinter(mode=QPrinter.PrinterMode.HighResolution) |
|
647 if Preferences.getPrinter("ColorMode"): |
|
648 printer.setColorMode(QPrinter.ColorMode.Color) |
|
649 else: |
|
650 printer.setColorMode(QPrinter.ColorMode.GrayScale) |
|
651 if Preferences.getPrinter("FirstPageFirst"): |
|
652 printer.setPageOrder(QPrinter.PageOrder.FirstPageFirst) |
|
653 else: |
|
654 printer.setPageOrder(QPrinter.PageOrder.LastPageFirst) |
|
655 printer.setPageMargins(QMarginsF( |
|
656 Preferences.getPrinter("LeftMargin") * 10, |
|
657 Preferences.getPrinter("TopMargin") * 10, |
|
658 Preferences.getPrinter("RightMargin") * 10, |
|
659 Preferences.getPrinter("BottomMargin") * 10), |
|
660 QPageLayout.Unit.Millimeter |
|
661 ) |
|
662 printerName = Preferences.getPrinter("PrinterName") |
|
663 if printerName: |
|
664 printer.setPrinterName(printerName) |
|
665 printer.setResolution(Preferences.getPrinter("Resolution")) |
|
666 documentName = WebBrowserTools.getFileNameFromUrl(browser.url()) |
|
667 printer.setDocName(documentName) |
|
668 |
|
669 printDialog = QPrintDialog(printer, self) |
|
670 printDialog.setOptions( |
|
671 QAbstractPrintDialog.PrintDialogOption.PrintToFile | |
|
672 QAbstractPrintDialog.PrintDialogOption.PrintShowPageSize |
|
673 ) |
|
674 if not Globals.isWindowsPlatform(): |
|
675 if isCupsAvailable(): |
|
676 printDialog.setOption( |
|
677 QAbstractPrintDialog.PrintDialogOption.PrintCollateCopies) |
|
678 printDialog.setOption( |
|
679 QAbstractPrintDialog.PrintDialogOption.PrintPageRange) |
|
680 if printDialog.exec() == QDialog.DialogCode.Accepted: |
|
681 browser.page().execPrintPage(printer, 10 * 1000) |
|
682 |
|
683 @pyqtSlot() |
|
684 def printBrowserPdf(self, browser=None): |
|
685 """ |
|
686 Public slot called to print the displayed page to PDF. |
|
687 |
|
688 @param browser reference to the browser to be printed (HelpBrowser) |
|
689 """ |
|
690 if browser is None: |
|
691 browser = self.currentBrowser() |
|
692 |
|
693 name = WebBrowserTools.getFileNameFromUrl(browser.url()) |
|
694 if name: |
|
695 name = name.rsplit('.', 1)[0] |
|
696 name += '.pdf' |
|
697 if hasattr(browser.page(), "printToPdf"): |
|
698 from .Tools.PrintToPdfDialog import PrintToPdfDialog |
|
699 if not name: |
|
700 name = "printout.pdf" |
|
701 dlg = PrintToPdfDialog(name, self) |
|
702 if dlg.exec() == QDialog.DialogCode.Accepted: |
|
703 filePath, pageLayout = dlg.getData() |
|
704 if filePath: |
|
705 if os.path.exists(filePath): |
|
706 res = EricMessageBox.warning( |
|
707 self, |
|
708 self.tr("Print to PDF"), |
|
709 self.tr("""<p>The file <b>{0}</b> exists""" |
|
710 """ already. Shall it be""" |
|
711 """ overwritten?</p>""").format(filePath), |
|
712 EricMessageBox.No | EricMessageBox.Yes, |
|
713 EricMessageBox.No) |
|
714 if res == EricMessageBox.No: |
|
715 return |
|
716 browser.page().printToPdf( |
|
717 lambda pdf: self.__pdfGeneratedForSave(filePath, pdf), |
|
718 pageLayout) |
|
719 elif Globals.isLinuxPlatform(): |
|
720 printer = QPrinter(mode=QPrinter.PrinterMode.HighResolution) |
|
721 if Preferences.getPrinter("ColorMode"): |
|
722 printer.setColorMode(QPrinter.ColorMode.Color) |
|
723 else: |
|
724 printer.setColorMode(QPrinter.ColorMode.GrayScale) |
|
725 printerName = Preferences.getPrinter("PrinterName") |
|
726 if printerName: |
|
727 printer.setPrinterName(printerName) |
|
728 printer.setOutputFormat(QPrinter.OutputFormat.PdfFormat) |
|
729 if name: |
|
730 printer.setOutputFileName(name) |
|
731 printer.setResolution(Preferences.getPrinter("Resolution")) |
|
732 |
|
733 printDialog = QPrintDialog(printer, self) |
|
734 if printDialog.exec() == QDialog.DialogCode.Accepted: |
|
735 browser.render(printer) |
|
736 |
|
737 def __pdfGeneratedForSave(self, filePath, pdfData): |
|
738 """ |
|
739 Private slot to save the generated PDF data to a file. |
|
740 |
|
741 @param filePath path to save the PDF to |
|
742 @type str |
|
743 @param pdfData generated PDF document |
|
744 @type QByteArray |
|
745 """ |
|
746 if pdfData.size() == 0: |
|
747 return |
|
748 |
|
749 try: |
|
750 with open(filePath, "wb") as f: |
|
751 f.write(pdfData) |
|
752 except OSError as err: |
|
753 EricMessageBox.critical( |
|
754 self, |
|
755 self.tr("Print to PDF"), |
|
756 self.tr("""<p>The PDF could not be written to file <b>{0}""" |
|
757 """</b>.</p><p><b>Error:</b> {1}</p>""").format( |
|
758 filePath, str(err)), |
|
759 EricMessageBox.Ok) |
|
760 |
|
761 @pyqtSlot() |
|
762 def printPreviewBrowser(self, browser=None): |
|
763 """ |
|
764 Public slot called to show a print preview of the displayed file. |
|
765 |
|
766 @param browser reference to the browser to be printed (WebBrowserView) |
|
767 """ |
|
768 from PyQt6.QtPrintSupport import QPrintPreviewDialog |
|
769 |
|
770 if browser is None: |
|
771 browser = self.currentBrowser() |
|
772 |
|
773 printer = QPrinter(mode=QPrinter.PrinterMode.HighResolution) |
|
774 if Preferences.getPrinter("ColorMode"): |
|
775 printer.setColorMode(QPrinter.ColorMode.Color) |
|
776 else: |
|
777 printer.setColorMode(QPrinter.ColorMode.GrayScale) |
|
778 if Preferences.getPrinter("FirstPageFirst"): |
|
779 printer.setPageOrder(QPrinter.PageOrder.FirstPageFirst) |
|
780 else: |
|
781 printer.setPageOrder(QPrinter.PageOrder.LastPageFirst) |
|
782 printer.setPageMargins(QMarginsF( |
|
783 Preferences.getPrinter("LeftMargin") * 10, |
|
784 Preferences.getPrinter("TopMargin") * 10, |
|
785 Preferences.getPrinter("RightMargin") * 10, |
|
786 Preferences.getPrinter("BottomMargin") * 10), |
|
787 QPageLayout.Unit.Millimeter |
|
788 ) |
|
789 printerName = Preferences.getPrinter("PrinterName") |
|
790 if printerName: |
|
791 printer.setPrinterName(printerName) |
|
792 printer.setResolution(Preferences.getPrinter("Resolution")) |
|
793 |
|
794 preview = QPrintPreviewDialog(printer, self) |
|
795 preview.resize(800, 750) |
|
796 preview.paintRequested.connect( |
|
797 lambda p: self.__printPreviewRequested(p, browser)) |
|
798 preview.exec() |
|
799 |
|
800 def __printPreviewRequested(self, printer, browser): |
|
801 """ |
|
802 Private slot to generate the print preview. |
|
803 |
|
804 @param printer reference to the printer object |
|
805 @type QPrinter |
|
806 @param browser reference to the browser to be printed |
|
807 @type WebBrowserView |
|
808 """ |
|
809 with EricOverrideCursor(): |
|
810 browser.page().execPrintPage(printer, 10 * 1000) |
|
811 |
|
812 def __sourceChanged(self, url, browser): |
|
813 """ |
|
814 Private slot to handle a change of a browsers source. |
|
815 |
|
816 @param url URL of the new site |
|
817 @type QUrl |
|
818 @param browser reference to the web browser |
|
819 @type WebBrowserView |
|
820 """ |
|
821 self.sourceChanged.emit(browser, url) |
|
822 |
|
823 if browser == self.currentBrowser(): |
|
824 self.currentUrlChanged.emit(url) |
|
825 |
|
826 def __titleChanged(self, title, browser): |
|
827 """ |
|
828 Private slot to handle a change of a browsers title. |
|
829 |
|
830 @param title new title |
|
831 @type str |
|
832 @param browser reference to the web browser |
|
833 @type WebBrowserView |
|
834 """ |
|
835 index = self.indexOf(browser) |
|
836 if title == "": |
|
837 title = browser.url().toString() |
|
838 |
|
839 self.setTabText(index, self.__elide(title.replace("&", "&&"))) |
|
840 self.setTabToolTip(index, title) |
|
841 |
|
842 self.titleChanged.emit(browser, title) |
|
843 |
|
844 def __elide(self, txt, mode=Qt.TextElideMode.ElideRight, length=40): |
|
845 """ |
|
846 Private method to elide some text. |
|
847 |
|
848 @param txt text to be elided (string) |
|
849 @param mode elide mode (Qt.TextElideMode) |
|
850 @param length amount of characters to be used (integer) |
|
851 @return the elided text (string) |
|
852 """ |
|
853 if mode == Qt.TextElideMode.ElideNone or len(txt) < length: |
|
854 return txt |
|
855 elif mode == Qt.TextElideMode.ElideLeft: |
|
856 return "...{0}".format(txt[-length:]) |
|
857 elif mode == Qt.TextElideMode.ElideMiddle: |
|
858 return "{0}...{1}".format(txt[:length // 2], txt[-(length // 2):]) |
|
859 elif mode == Qt.TextElideMode.ElideRight: |
|
860 return "{0}...".format(txt[:length]) |
|
861 else: |
|
862 # just in case |
|
863 return txt |
|
864 |
|
865 def preferencesChanged(self): |
|
866 """ |
|
867 Public slot to handle a change of preferences. |
|
868 """ |
|
869 for browser in self.browsers(): |
|
870 browser.preferencesChanged() |
|
871 |
|
872 for urlbar in self.__stackedUrlBar.urlBars(): |
|
873 urlbar.preferencesChanged() |
|
874 |
|
875 def __loadFinished(self, ok, browser): |
|
876 """ |
|
877 Private method to handle the loadFinished signal. |
|
878 |
|
879 @param ok flag indicating the result |
|
880 @type bool |
|
881 @param browser reference to the web browser |
|
882 @type WebBrowserView |
|
883 """ |
|
884 if ok: |
|
885 self.showMessage.emit(self.tr("Finished loading")) |
|
886 else: |
|
887 self.showMessage.emit(self.tr("Failed to load")) |
|
888 |
|
889 def __loadProgress(self, progress, browser): |
|
890 """ |
|
891 Private method to handle the loadProgress signal. |
|
892 |
|
893 Note: This works around wegengine not sending a loadFinished |
|
894 signal for navigation on the same page. |
|
895 |
|
896 @param progress load progress in percent |
|
897 @type int |
|
898 @param browser reference to the web browser |
|
899 @type WebBrowserView |
|
900 """ |
|
901 index = self.indexOf(browser) |
|
902 if progress == 0: |
|
903 # page loading has started |
|
904 anim = self.animationLabel(index, "loadingAnimation", 40) |
|
905 if not anim: |
|
906 self.setTabIcon(index, UI.PixmapCache.getIcon("loading")) |
|
907 else: |
|
908 self.setTabIcon(index, QIcon()) |
|
909 self.setTabText(index, self.tr("Loading...")) |
|
910 self.setTabToolTip(index, self.tr("Loading...")) |
|
911 self.showMessage.emit(self.tr("Loading...")) |
|
912 |
|
913 self.__mainWindow.setLoadingActions(True) |
|
914 elif progress == 100: |
|
915 import WebBrowser.WebBrowserWindow |
|
916 self.resetAnimation(index) |
|
917 self.setTabIcon( |
|
918 index, WebBrowser.WebBrowserWindow.WebBrowserWindow.icon( |
|
919 browser.url())) |
|
920 self.showMessage.emit(self.tr("Finished loading")) |
|
921 |
|
922 self.__mainWindow.setLoadingActions(False) |
|
923 |
|
924 def __iconChanged(self, browser): |
|
925 """ |
|
926 Private slot to handle a change of the web site icon. |
|
927 |
|
928 @param browser reference to the web browser |
|
929 @type WebBrowserView |
|
930 """ |
|
931 self.setTabIcon( |
|
932 self.indexOf(browser), |
|
933 browser.icon()) |
|
934 self.__mainWindow.bookmarksManager().faviconChanged(browser.url()) |
|
935 |
|
936 def getSourceFileList(self): |
|
937 """ |
|
938 Public method to get a list of all opened Qt help files. |
|
939 |
|
940 @return dictionary with tab id as key and host/namespace as value |
|
941 """ |
|
942 sourceList = {} |
|
943 for i in range(self.count()): |
|
944 browser = self.widget(i) |
|
945 if ( |
|
946 browser is not None and |
|
947 browser.source().isValid() |
|
948 ): |
|
949 sourceList[i] = browser.source().host() |
|
950 |
|
951 return sourceList |
|
952 |
|
953 def shallShutDown(self): |
|
954 """ |
|
955 Public method to check, if the application should be shut down. |
|
956 |
|
957 @return flag indicating a shut down (boolean) |
|
958 """ |
|
959 if self.count() > 1 and Preferences.getWebBrowser( |
|
960 "WarnOnMultipleClose"): |
|
961 mb = EricMessageBox.EricMessageBox( |
|
962 EricMessageBox.Information, |
|
963 self.tr("Are you sure you want to close the window?"), |
|
964 self.tr("""Are you sure you want to close the window?\n""" |
|
965 """You have %n tab(s) open.""", "", self.count()), |
|
966 modal=True, |
|
967 parent=self) |
|
968 quitButton = mb.addButton( |
|
969 self.tr("&Quit"), EricMessageBox.AcceptRole) |
|
970 quitButton.setIcon(UI.PixmapCache.getIcon("exit")) |
|
971 closeTabButton = mb.addButton( |
|
972 self.tr("C&lose Current Tab"), EricMessageBox.AcceptRole) |
|
973 closeTabButton.setIcon(UI.PixmapCache.getIcon("tabClose")) |
|
974 mb.addButton(EricMessageBox.Cancel) |
|
975 mb.exec() |
|
976 if mb.clickedButton() == quitButton: |
|
977 return True |
|
978 else: |
|
979 if mb.clickedButton() == closeTabButton: |
|
980 self.closeBrowser() |
|
981 return False |
|
982 |
|
983 return True |
|
984 |
|
985 def stackedUrlBar(self): |
|
986 """ |
|
987 Public method to get a reference to the stacked url bar. |
|
988 |
|
989 @return reference to the stacked url bar (StackedUrlBar) |
|
990 """ |
|
991 return self.__stackedUrlBar |
|
992 |
|
993 def currentUrlBar(self): |
|
994 """ |
|
995 Public method to get a reference to the current url bar. |
|
996 |
|
997 @return reference to the current url bar (UrlBar) |
|
998 """ |
|
999 return self.__stackedUrlBar.currentWidget() |
|
1000 |
|
1001 def urlBarForView(self, view): |
|
1002 """ |
|
1003 Public method to get a reference to the UrlBar associated with the |
|
1004 given view. |
|
1005 |
|
1006 @param view reference to the view to get the urlbar for |
|
1007 @type WebBrowserView |
|
1008 @return reference to the associated urlbar |
|
1009 @rtype UrlBar |
|
1010 """ |
|
1011 for urlbar in self.__stackedUrlBar.urlBars(): |
|
1012 if urlbar.browser() is view: |
|
1013 return urlbar |
|
1014 |
|
1015 return None |
|
1016 |
|
1017 def __lineEditReturnPressed(self, edit): |
|
1018 """ |
|
1019 Private slot to handle the entering of an URL. |
|
1020 |
|
1021 @param edit reference to the line edit |
|
1022 @type UrlBar |
|
1023 """ |
|
1024 url = self.__guessUrlFromPath(edit.text()) |
|
1025 if ericApp().keyboardModifiers() == Qt.KeyboardModifier.AltModifier: |
|
1026 self.newBrowser(url) |
|
1027 else: |
|
1028 self.currentBrowser().setSource(url) |
|
1029 self.currentBrowser().setFocus() |
|
1030 |
|
1031 def __pathSelected(self, path): |
|
1032 """ |
|
1033 Private slot called when a URL is selected from the completer. |
|
1034 |
|
1035 @param path path to be shown (string) |
|
1036 """ |
|
1037 url = self.__guessUrlFromPath(path) |
|
1038 self.currentBrowser().setSource(url) |
|
1039 |
|
1040 def __guessUrlFromPath(self, path): |
|
1041 """ |
|
1042 Private method to guess an URL given a path string. |
|
1043 |
|
1044 @param path path string to guess an URL for (string) |
|
1045 @return guessed URL (QUrl) |
|
1046 """ |
|
1047 manager = self.__mainWindow.openSearchManager() |
|
1048 path = Utilities.fromNativeSeparators(path) |
|
1049 url = manager.convertKeywordSearchToUrl(path) |
|
1050 if url.isValid(): |
|
1051 return url |
|
1052 |
|
1053 try: |
|
1054 url = QUrl.fromUserInput(path) |
|
1055 except AttributeError: |
|
1056 url = QUrl(path) |
|
1057 |
|
1058 if ( |
|
1059 url.scheme() == "about" and |
|
1060 url.path() == "home" |
|
1061 ): |
|
1062 url = QUrl("eric:home") |
|
1063 |
|
1064 if url.scheme() in ["s", "search"]: |
|
1065 url = manager.currentEngine().searchUrl(url.path().strip()) |
|
1066 |
|
1067 if ( |
|
1068 url.scheme() != "" and |
|
1069 (url.host() != "" or url.path() != "") |
|
1070 ): |
|
1071 return url |
|
1072 |
|
1073 urlString = Preferences.getWebBrowser("DefaultScheme") + path.strip() |
|
1074 url = QUrl.fromEncoded(urlString.encode("utf-8"), |
|
1075 QUrl.ParsingMode.TolerantMode) |
|
1076 |
|
1077 return url |
|
1078 |
|
1079 def __currentChanged(self, index): |
|
1080 """ |
|
1081 Private slot to handle an index change. |
|
1082 |
|
1083 @param index new index (integer) |
|
1084 """ |
|
1085 self.__stackedUrlBar.setCurrentIndex(index) |
|
1086 |
|
1087 browser = self.browserAt(index) |
|
1088 if browser is not None: |
|
1089 if browser.url() == "" and browser.hasFocus(): |
|
1090 self.__stackedUrlBar.currentWidget.setFocus() |
|
1091 elif browser.url() != "": |
|
1092 browser.setFocus() |
|
1093 |
|
1094 def restoreClosedTab(self, act): |
|
1095 """ |
|
1096 Public slot to restore the most recently closed tab. |
|
1097 |
|
1098 @param act reference to the action that triggered |
|
1099 @type QAction |
|
1100 """ |
|
1101 if not self.canRestoreClosedTab(): |
|
1102 return |
|
1103 |
|
1104 tab = self.__closedTabsManager.getClosedTabAt(act.data()) |
|
1105 |
|
1106 self.newBrowser(tab.url.toString(), position=tab.position) |
|
1107 |
|
1108 def canRestoreClosedTab(self): |
|
1109 """ |
|
1110 Public method to check, if closed tabs can be restored. |
|
1111 |
|
1112 @return flag indicating that closed tabs can be restored (boolean) |
|
1113 """ |
|
1114 return self.__closedTabsManager.isClosedTabAvailable() |
|
1115 |
|
1116 def restoreAllClosedTabs(self): |
|
1117 """ |
|
1118 Public slot to restore all closed tabs. |
|
1119 """ |
|
1120 if not self.canRestoreClosedTab(): |
|
1121 return |
|
1122 |
|
1123 for tab in self.__closedTabsManager.allClosedTabs(): |
|
1124 self.newBrowser(tab.url.toString(), position=tab.position) |
|
1125 self.__closedTabsManager.clearList() |
|
1126 |
|
1127 def clearClosedTabsList(self): |
|
1128 """ |
|
1129 Public slot to clear the list of closed tabs. |
|
1130 """ |
|
1131 self.__closedTabsManager.clearList() |
|
1132 |
|
1133 def __aboutToShowClosedTabsMenu(self): |
|
1134 """ |
|
1135 Private slot to populate the closed tabs menu. |
|
1136 """ |
|
1137 fm = self.__closedTabsMenu.fontMetrics() |
|
1138 try: |
|
1139 maxWidth = fm.horizontalAdvance('m') * 40 |
|
1140 except AttributeError: |
|
1141 maxWidth = fm.width('m') * 40 |
|
1142 |
|
1143 self.__closedTabsMenu.clear() |
|
1144 for index, tab in enumerate(self.__closedTabsManager.allClosedTabs()): |
|
1145 title = fm.elidedText(tab.title, Qt.TextElideMode.ElideRight, |
|
1146 maxWidth) |
|
1147 act = self.__closedTabsMenu.addAction( |
|
1148 self.__mainWindow.icon(tab.url), title) |
|
1149 act.setData(index) |
|
1150 act.triggered.connect(lambda: self.restoreClosedTab(act)) |
|
1151 self.__closedTabsMenu.addSeparator() |
|
1152 self.__closedTabsMenu.addAction( |
|
1153 self.tr("Restore All Closed Tabs"), self.restoreAllClosedTabs) |
|
1154 self.__closedTabsMenu.addAction( |
|
1155 self.tr("Clear List"), self.clearClosedTabsList) |
|
1156 |
|
1157 def closedTabsManager(self): |
|
1158 """ |
|
1159 Public slot to get a reference to the closed tabs manager. |
|
1160 |
|
1161 @return reference to the closed tabs manager (ClosedTabsManager) |
|
1162 """ |
|
1163 return self.__closedTabsManager |
|
1164 |
|
1165 def __closedTabAvailable(self, avail): |
|
1166 """ |
|
1167 Private slot to handle changes of the availability of closed tabs. |
|
1168 |
|
1169 @param avail flag indicating the availability of closed tabs (boolean) |
|
1170 """ |
|
1171 self.__closedTabsButton.setEnabled(avail) |
|
1172 self.__restoreClosedTabAct.setEnabled(avail) |
|
1173 |
|
1174 #################################################### |
|
1175 ## Methods below implement session related functions |
|
1176 #################################################### |
|
1177 |
|
1178 def getSessionData(self): |
|
1179 """ |
|
1180 Public method to populate the session data. |
|
1181 |
|
1182 @return dictionary containing the session data |
|
1183 @rtype dict |
|
1184 """ |
|
1185 sessionData = {} |
|
1186 |
|
1187 # 1. current index |
|
1188 sessionData["CurrentTabIndex"] = self.currentIndex() |
|
1189 |
|
1190 # 2. tab data |
|
1191 sessionData["Tabs"] = [] |
|
1192 for index in range(self.count()): |
|
1193 browser = self.widget(index) |
|
1194 data = browser.getSessionData() |
|
1195 sessionData["Tabs"].append(data) |
|
1196 |
|
1197 return sessionData |
|
1198 |
|
1199 def loadFromSessionData(self, sessionData): |
|
1200 """ |
|
1201 Public method to load the session data. |
|
1202 |
|
1203 @param sessionData dictionary containing the session data as |
|
1204 generated by getSessionData() |
|
1205 @type dict |
|
1206 """ |
|
1207 tabCount = self.count() |
|
1208 |
|
1209 # 1. load tab data |
|
1210 if "Tabs" in sessionData: |
|
1211 loadTabOnActivate = Preferences.getWebBrowser( |
|
1212 "LoadTabOnActivation") |
|
1213 for data in sessionData["Tabs"]: |
|
1214 browser = self.newBrowser(restoreSession=True) |
|
1215 if loadTabOnActivate: |
|
1216 browser.storeSessionData(data) |
|
1217 title, urlStr, icon = browser.extractSessionMetaData(data) |
|
1218 index = self.indexOf(browser) |
|
1219 self.setTabText(index, title) |
|
1220 self.setTabIcon(index, icon) |
|
1221 else: |
|
1222 browser.loadFromSessionData(data) |
|
1223 |
|
1224 # 2. set tab index |
|
1225 if ( |
|
1226 "CurrentTabIndex" in sessionData and |
|
1227 sessionData["CurrentTabIndex"] >= 0 |
|
1228 ): |
|
1229 index = tabCount + sessionData["CurrentTabIndex"] |
|
1230 self.setCurrentIndex(index) |
|
1231 self.browserAt(index).activateSession() |