|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2008 - 2009 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 |
|
7 """ |
|
8 Module implementing the helpbrowser using QWebView. |
|
9 """ |
|
10 |
|
11 import os |
|
12 |
|
13 from PyQt4.QtCore import * |
|
14 from PyQt4.QtGui import * |
|
15 from PyQt4.QtWebKit import QWebView, QWebPage, QWebSettings |
|
16 from PyQt4.QtNetwork import QNetworkProxy, QNetworkAccessManager, QNetworkReply, \ |
|
17 QNetworkRequest |
|
18 |
|
19 import Preferences |
|
20 import Utilities |
|
21 import UI.PixmapCache |
|
22 |
|
23 from DownloadDialog import DownloadDialog |
|
24 from HelpWebSearchWidget import HelpWebSearchWidget |
|
25 from Bookmarks.AddBookmarkDialog import AddBookmarkDialog |
|
26 from JavaScriptResources import fetchLinks_js |
|
27 from HTMLResources import notFoundPage_html |
|
28 import Helpviewer.HelpWindow |
|
29 |
|
30 from Network.NetworkAccessManagerProxy import NetworkAccessManagerProxy |
|
31 |
|
32 from OpenSearch.OpenSearchEngineAction import OpenSearchEngineAction |
|
33 |
|
34 ########################################################################################## |
|
35 |
|
36 class JavaScriptExternalObject(QObject): |
|
37 """ |
|
38 Class implementing an external javascript object to add search providers. |
|
39 """ |
|
40 @pyqtSlot(str) |
|
41 def AddSearchProvider(self, url): |
|
42 """ |
|
43 Public slot to add a search provider. |
|
44 |
|
45 @param url url of the XML file defining the search provider (string) |
|
46 """ |
|
47 HelpWebSearchWidget.openSearchManager().addEngine(QUrl(url)); |
|
48 |
|
49 class LinkedResource(object): |
|
50 """ |
|
51 Class defining a data structure for linked resources. |
|
52 """ |
|
53 def __init__(self): |
|
54 """ |
|
55 Constructor |
|
56 """ |
|
57 self.rel = "" |
|
58 self.type_ = "" |
|
59 self.href = "" |
|
60 self.title = "" |
|
61 |
|
62 ########################################################################################## |
|
63 |
|
64 class JavaScriptEricObject(QObject): |
|
65 """ |
|
66 Class implementing an external javascript object to search via the startpage. |
|
67 """ |
|
68 # these must be in line with the strings used by the javascript part of the start page |
|
69 translations = [ |
|
70 QT_TRANSLATE_NOOP("JavaScriptEricObject", "Welcome to Eric Web Browser!"), |
|
71 QT_TRANSLATE_NOOP("JavaScriptEricObject", "Eric Web Browser"), |
|
72 QT_TRANSLATE_NOOP("JavaScriptEricObject", "Search!"), |
|
73 QT_TRANSLATE_NOOP("JavaScriptEricObject", "About Eric"), |
|
74 ] |
|
75 |
|
76 @pyqtSlot(str, result = str) |
|
77 def translate(self, trans): |
|
78 """ |
|
79 Public method to translate the given string. |
|
80 |
|
81 @param trans string to be translated (string) |
|
82 @return translation (string) |
|
83 """ |
|
84 if trans == "QT_LAYOUT_DIRECTION": |
|
85 # special handling to detect layout direction |
|
86 if qApp.isLeftToRight(): |
|
87 return "LTR" |
|
88 else: |
|
89 return "RTL" |
|
90 |
|
91 return self.trUtf8(trans) |
|
92 |
|
93 @pyqtSlot(result = str) |
|
94 def providerString(self): |
|
95 """ |
|
96 Public method to get a string for the search provider. |
|
97 |
|
98 @return string for the search provider (string) |
|
99 """ |
|
100 return self.trUtf8("Search results provided by {0}")\ |
|
101 .format(HelpWebSearchWidget.openSearchManager().currentEngineName()) |
|
102 |
|
103 @pyqtSlot(str, result = str) |
|
104 def searchUrl(self, searchStr): |
|
105 """ |
|
106 Public method to get the search URL for the given search term. |
|
107 |
|
108 @param searchStr search term (string) |
|
109 @return search URL (string) |
|
110 """ |
|
111 return unicode( |
|
112 HelpWebSearchWidget.openSearchManager().currentEngine()\ |
|
113 .searchUrl(searchStr).toEncoded()) |
|
114 |
|
115 ########################################################################################## |
|
116 |
|
117 class HelpWebPage(QWebPage): |
|
118 """ |
|
119 Class implementing an enhanced web page. |
|
120 """ |
|
121 def __init__(self, parent = None): |
|
122 """ |
|
123 Constructor |
|
124 |
|
125 @param parent parent widget of this window (QWidget) |
|
126 """ |
|
127 QWebPage.__init__(self, parent) |
|
128 |
|
129 self.__lastRequest = None |
|
130 self.__lastRequestType = QWebPage.NavigationTypeOther |
|
131 |
|
132 self.__proxy = NetworkAccessManagerProxy(self) |
|
133 self.__proxy.setWebPage(self) |
|
134 self.__proxy.setPrimaryNetworkAccessManager( |
|
135 Helpviewer.HelpWindow.HelpWindow.networkAccessManager()) |
|
136 self.setNetworkAccessManager(self.__proxy) |
|
137 |
|
138 def acceptNavigationRequest(self, frame, request, type_): |
|
139 """ |
|
140 Protected method to determine, if a request may be accepted. |
|
141 |
|
142 @param frame reference to the frame sending the request (QWebFrame) |
|
143 @param request reference to the request object (QNetworkRequest) |
|
144 @param type_ type of the navigation request (QWebPage.NavigationType) |
|
145 @return flag indicating acceptance (boolean) |
|
146 """ |
|
147 self.__lastRequest = request |
|
148 self.__lastRequestType = type_ |
|
149 |
|
150 return QWebPage.acceptNavigationRequest(self, frame, request, type_) |
|
151 |
|
152 def populateNetworkRequest(self, request): |
|
153 """ |
|
154 Public method to add data to a network request. |
|
155 |
|
156 @param request reference to the network request object (QNetworkRequest) |
|
157 """ |
|
158 request.setAttribute(QNetworkRequest.User + 100, QVariant(self)) |
|
159 request.setAttribute(QNetworkRequest.User + 101, |
|
160 QVariant(self.__lastRequestType)) |
|
161 |
|
162 def pageAttributeId(self): |
|
163 """ |
|
164 Public method to get the attribute id of the page attribute. |
|
165 |
|
166 @return attribute id of the page attribute (integer) |
|
167 """ |
|
168 return QNetworkRequest.User + 100 |
|
169 |
|
170 ########################################################################################## |
|
171 |
|
172 class HelpBrowser(QWebView): |
|
173 """ |
|
174 Class implementing the helpbrowser widget. |
|
175 |
|
176 This is a subclass of the Qt QWebView to implement an |
|
177 interface compatible with the QTextBrowser based variant. |
|
178 |
|
179 @signal sourceChanged(const QUrl &) emitted after the current URL has changed |
|
180 @signal forwardAvailable(bool) emitted after the current URL has changed |
|
181 @signal backwardAvailable(bool) emitted after the current URL has changed |
|
182 @signal highlighted(const QString&) emitted, when the mouse hovers over a link |
|
183 @signal search(const QUrl &) emitted, when a search is requested |
|
184 """ |
|
185 def __init__(self, parent = None, name = ""): |
|
186 """ |
|
187 Constructor |
|
188 |
|
189 @param parent parent widget of this window (QWidget) |
|
190 @param name name of this window (string) |
|
191 """ |
|
192 QWebView.__init__(self, parent) |
|
193 self.setObjectName(name) |
|
194 self.setWhatsThis(self.trUtf8( |
|
195 """<b>Help Window</b>""" |
|
196 """<p>This window displays the selected help information.</p>""" |
|
197 )) |
|
198 |
|
199 self.__page = HelpWebPage(self) |
|
200 self.setPage(self.__page) |
|
201 |
|
202 self.mw = parent |
|
203 self.ctrlPressed = False |
|
204 self.__downloadWindows = [] |
|
205 self.__isLoading = False |
|
206 |
|
207 self.__currentZoom = 100 |
|
208 self.__zoomLevels = [ |
|
209 30, 50, 67, 80, 90, |
|
210 100, |
|
211 110, 120, 133, 150, 170, 200, 240, 300, |
|
212 ] |
|
213 |
|
214 self.__javaScriptBinding = None |
|
215 self.__javaScriptEricObject = None |
|
216 |
|
217 self.connect(self.mw, SIGNAL("zoomTextOnlyChanged(bool)"), self.__applyZoom) |
|
218 |
|
219 self.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) |
|
220 self.connect(self, SIGNAL('linkClicked(const QUrl &)'), self.setSource) |
|
221 self.connect(self, SIGNAL('iconChanged()'), self.__iconChanged) |
|
222 |
|
223 self.connect(self, SIGNAL('urlChanged(const QUrl &)'), self.__urlChanged) |
|
224 self.connect(self, SIGNAL('statusBarMessage(const QString &)'), |
|
225 self.__statusBarMessage) |
|
226 self.connect(self.page(), |
|
227 SIGNAL('linkHovered(const QString &, const QString &, const QString &)'), |
|
228 self.__linkHovered) |
|
229 |
|
230 self.connect(self, SIGNAL('loadStarted()'), self.__loadStarted) |
|
231 self.connect(self, SIGNAL('loadProgress(int)'), self.__loadProgress) |
|
232 self.connect(self, SIGNAL('loadFinished(bool)'), self.__loadFinished) |
|
233 |
|
234 self.page().setForwardUnsupportedContent(True) |
|
235 self.connect(self.page(), SIGNAL('unsupportedContent(QNetworkReply *)'), |
|
236 self.__unsupportedContent) |
|
237 |
|
238 self.connect(self.page(), SIGNAL('downloadRequested(const QNetworkRequest &)'), |
|
239 self.__downloadRequested) |
|
240 self.connect(self.page(), SIGNAL("frameCreated(QWebFrame *)"), |
|
241 self.__addExternalBinding) |
|
242 self.__addExternalBinding(self.page().mainFrame()) |
|
243 |
|
244 self.connect(HelpWebSearchWidget.openSearchManager(), |
|
245 SIGNAL("currentEngineChanged()"), |
|
246 self.__currentEngineChanged) |
|
247 |
|
248 def __addExternalBinding(self, frame = None): |
|
249 """ |
|
250 Private slot to add javascript bindings for adding search providers. |
|
251 |
|
252 @param frame reference to the web frame (QWebFrame) |
|
253 """ |
|
254 self.page().settings().setAttribute(QWebSettings.JavascriptEnabled, True) |
|
255 if self.__javaScriptBinding is None: |
|
256 self.__javaScriptBinding = JavaScriptExternalObject(self) |
|
257 |
|
258 if frame is None: |
|
259 # called from QWebFrame.javaScriptWindowObjectCleared |
|
260 frame = self.sender() |
|
261 if frame.url().scheme() == "pyrc" and frame.url().path() == "home": |
|
262 if self.__javaScriptEricObject is None: |
|
263 self.__javaScriptEricObject = JavaScriptEricObject(self) |
|
264 frame.addToJavaScriptWindowObject("eric", self.__javaScriptEricObject) |
|
265 else: |
|
266 # called from QWebPage.frameCreated |
|
267 self.connect(frame, SIGNAL("javaScriptWindowObjectCleared()"), |
|
268 self.__addExternalBinding) |
|
269 frame.addToJavaScriptWindowObject("external", self.__javaScriptBinding) |
|
270 |
|
271 def linkedResources(self, relation = ""): |
|
272 """ |
|
273 Public method to extract linked resources. |
|
274 |
|
275 @param relation relation to extract (string) |
|
276 @return list of linked resources (list of LinkedResource) |
|
277 """ |
|
278 resources = [] |
|
279 |
|
280 lst = self.page().mainFrame().evaluateJavaScript(fetchLinks_js).toList() |
|
281 for variant in lst: |
|
282 m = variant.toMap() |
|
283 rel = m["rel"].toString() |
|
284 type_ = m["type"].toString() |
|
285 href = m["href"].toString() |
|
286 title = m["title"].toString() |
|
287 |
|
288 if href == "" or type_ == "": |
|
289 continue |
|
290 if relation and rel != relation: |
|
291 continue |
|
292 |
|
293 resource = LinkedResource() |
|
294 resource.rel = rel |
|
295 resource.type_ = type_ |
|
296 resource.href = href |
|
297 resource.title = title |
|
298 |
|
299 resources.append(resource) |
|
300 |
|
301 return resources |
|
302 |
|
303 def __currentEngineChanged(self): |
|
304 """ |
|
305 Private slot to track a change of the current search engine. |
|
306 """ |
|
307 if self.url().toString() == "pyrc:home": |
|
308 self.reload() |
|
309 |
|
310 def setSource(self, name): |
|
311 """ |
|
312 Public method used to set the source to be displayed. |
|
313 |
|
314 @param name filename to be shown (QUrl) |
|
315 """ |
|
316 if name is None or not name.isValid(): |
|
317 return |
|
318 |
|
319 if self.ctrlPressed: |
|
320 # open in a new window |
|
321 self.mw.newTab(name) |
|
322 self.ctrlPressed = False |
|
323 return |
|
324 |
|
325 if not name.scheme(): |
|
326 name.setUrl(Preferences.getHelp("DefaultScheme") + name.toString()) |
|
327 |
|
328 if len(name.scheme()) == 1 or \ |
|
329 name.scheme() == "file": |
|
330 # name is a local file |
|
331 if name.scheme() and len(name.scheme()) == 1: |
|
332 # it is a local path on win os |
|
333 name = QUrl.fromLocalFile(name.toString()) |
|
334 |
|
335 if not QFileInfo(name.toLocalFile()).exists(): |
|
336 QMessageBox.critical(None, |
|
337 self.trUtf8("Web Browser"), |
|
338 self.trUtf8("""<p>The file <b>{0}</b> does not exist.</p>""")\ |
|
339 .format(name.toLocalFile()), |
|
340 QMessageBox.StandardButtons(\ |
|
341 QMessageBox.Ok)) |
|
342 return |
|
343 |
|
344 if name.toLocalFile().endswith(".pdf") or \ |
|
345 name.toLocalFile().endswith(".PDF") or \ |
|
346 name.toLocalFile().endswith(".chm") or \ |
|
347 name.toLocalFile().endswith(".CHM"): |
|
348 started = QDesktopServices.openUrl(name) |
|
349 if not started: |
|
350 QMessageBox.critical(self, |
|
351 self.trUtf8("Web Browser"), |
|
352 self.trUtf8("""<p>Could not start a viewer""" |
|
353 """ for file <b>{0}</b>.</p>""").format(name.path())) |
|
354 return |
|
355 elif name.scheme() in ["mailto", "ftp"]: |
|
356 started = QDesktopServices.openUrl(name) |
|
357 if not started: |
|
358 QMessageBox.critical(self, |
|
359 self.trUtf8("Web Browser"), |
|
360 self.trUtf8("""<p>Could not start an application""" |
|
361 """ for URL <b>{0}</b>.</p>""").format(name.toString())) |
|
362 return |
|
363 elif name.scheme() == "javascript": |
|
364 scriptSource = name.toString()[11:] |
|
365 res = self.page().mainFrame().evaluateJavaScript(scriptSource).toString() |
|
366 if res: |
|
367 self.setHtml(res) |
|
368 return |
|
369 else: |
|
370 if name.toString().endswith(".pdf") or \ |
|
371 name.toString().endswith(".PDF") or \ |
|
372 name.toString().endswith(".chm") or \ |
|
373 name.toString().endswith(".CHM"): |
|
374 started = QDesktopServices.openUrl(name) |
|
375 if not started: |
|
376 QMessageBox.critical(self, |
|
377 self.trUtf8("Web Browser"), |
|
378 self.trUtf8("""<p>Could not start a viewer""" |
|
379 """ for file <b>{0}</b>.</p>""").format(name.path())) |
|
380 return |
|
381 |
|
382 self.load(name) |
|
383 |
|
384 def source(self): |
|
385 """ |
|
386 Public method to return the URL of the loaded page. |
|
387 |
|
388 @return URL loaded in the help browser (QUrl) |
|
389 """ |
|
390 return self.url() |
|
391 |
|
392 def documentTitle(self): |
|
393 """ |
|
394 Public method to return the title of the loaded page. |
|
395 |
|
396 @return title (string) |
|
397 """ |
|
398 return self.title() |
|
399 |
|
400 def backward(self): |
|
401 """ |
|
402 Public slot to move backwards in history. |
|
403 """ |
|
404 self.triggerPageAction(QWebPage.Back) |
|
405 self.__urlChanged(self.history().currentItem().url()) |
|
406 |
|
407 def forward(self): |
|
408 """ |
|
409 Public slot to move forward in history. |
|
410 """ |
|
411 self.triggerPageAction(QWebPage.Forward) |
|
412 self.__urlChanged(self.history().currentItem().url()) |
|
413 |
|
414 def home(self): |
|
415 """ |
|
416 Public slot to move to the first page loaded. |
|
417 """ |
|
418 homeUrl = QUrl(Preferences.getHelp("HomePage")) |
|
419 self.setSource(homeUrl) |
|
420 self.__urlChanged(self.history().currentItem().url()) |
|
421 |
|
422 def reload(self): |
|
423 """ |
|
424 Public slot to reload the current page. |
|
425 """ |
|
426 self.triggerPageAction(QWebPage.Reload) |
|
427 |
|
428 def copy(self): |
|
429 """ |
|
430 Public slot to copy the selected text. |
|
431 """ |
|
432 self.triggerPageAction(QWebPage.Copy) |
|
433 |
|
434 def isForwardAvailable(self): |
|
435 """ |
|
436 Public method to determine, if a forward move in history is possible. |
|
437 |
|
438 @return flag indicating move forward is possible (boolean) |
|
439 """ |
|
440 return self.history().canGoForward() |
|
441 |
|
442 def isBackwardAvailable(self): |
|
443 """ |
|
444 Public method to determine, if a backwards move in history is possible. |
|
445 |
|
446 @return flag indicating move backwards is possible (boolean) |
|
447 """ |
|
448 return self.history().canGoBack() |
|
449 |
|
450 def __levelForZoom(self, zoom): |
|
451 """ |
|
452 Private method determining the zoom level index given a zoom factor. |
|
453 |
|
454 @param zoom zoom factor (integer) |
|
455 @return index of zoom factor (integer) |
|
456 """ |
|
457 try: |
|
458 index = self.__zoomLevels.index(zoom) |
|
459 except ValueError: |
|
460 for index in range(len(self.__zoomLevels)): |
|
461 if zoom <= self.__zoomLevels[index]: |
|
462 break |
|
463 return index |
|
464 |
|
465 def __applyZoom(self): |
|
466 """ |
|
467 Private slot to apply the current zoom factor. |
|
468 """ |
|
469 try: |
|
470 self.setZoomFactor(self.__currentZoom / 100.0) |
|
471 except AttributeError: |
|
472 self.setTextSizeMultiplier(self.__currentZoom / 100.0) |
|
473 |
|
474 def zoomIn(self): |
|
475 """ |
|
476 Public slot to zoom into the page. |
|
477 """ |
|
478 index = self.__levelForZoom(self.__currentZoom) |
|
479 if index < len(self.__zoomLevels) - 1: |
|
480 self.__currentZoom = self.__zoomLevels[index + 1] |
|
481 self.__applyZoom() |
|
482 |
|
483 def zoomOut(self): |
|
484 """ |
|
485 Public slot to zoom out of the page. |
|
486 """ |
|
487 index = self.__levelForZoom(self.__currentZoom) |
|
488 if index > 0: |
|
489 self.__currentZoom = self.__zoomLevels[index - 1] |
|
490 self.__applyZoom() |
|
491 |
|
492 def zoomReset(self): |
|
493 """ |
|
494 Public method to reset the zoom factor. |
|
495 """ |
|
496 self.__currentZoom = 100 |
|
497 self.__applyZoom() |
|
498 |
|
499 def wheelEvent(self, evt): |
|
500 """ |
|
501 Protected method to handle wheel events. |
|
502 |
|
503 @param evt reference to the wheel event (QWheelEvent) |
|
504 """ |
|
505 if evt.modifiers() & Qt.ControlModifier: |
|
506 degrees = evt.delta() / 8 |
|
507 steps = degrees / 15 |
|
508 self.__currentZoom += steps * 10 |
|
509 self.__applyZoom() |
|
510 evt.accept() |
|
511 return |
|
512 |
|
513 QWebView.wheelEvent(self, evt) |
|
514 |
|
515 def hasSelection(self): |
|
516 """ |
|
517 Public method to determine, if there is some text selected. |
|
518 |
|
519 @return flag indicating text has been selected (boolean) |
|
520 """ |
|
521 return self.selectedText() != "" |
|
522 |
|
523 def findNextPrev(self, txt, case, backwards, wrap): |
|
524 """ |
|
525 Public slot to find the next occurrence of a text. |
|
526 |
|
527 @param txt text to search for (string) |
|
528 @param case flag indicating a case sensitive search (boolean) |
|
529 @param backwards flag indicating a backwards search (boolean) |
|
530 @param wrap flag indicating to wrap around (boolean) |
|
531 """ |
|
532 findFlags = QWebPage.FindFlags() |
|
533 if case: |
|
534 findFlags |= QWebPage.FindCaseSensitively |
|
535 if backwards: |
|
536 findFlags |= QWebPage.FindBackward |
|
537 if wrap: |
|
538 findFlags |= QWebPage.FindWrapsAroundDocument |
|
539 |
|
540 return self.findText(txt, findFlags) |
|
541 |
|
542 def contextMenuEvent(self, evt): |
|
543 """ |
|
544 Protected method called to create a context menu. |
|
545 |
|
546 This method is overridden from QWebView. |
|
547 |
|
548 @param evt reference to the context menu event object (QContextMenuEvent) |
|
549 """ |
|
550 pos = evt.pos() |
|
551 menu = QMenu(self) |
|
552 |
|
553 hit = self.page().mainFrame().hitTestContent(evt.pos()) |
|
554 if not hit.linkUrl().isEmpty(): |
|
555 act = menu.addAction(self.trUtf8("Open Link in New Tab\tCtrl+LMB"), |
|
556 self.__openLinkInNewTab) |
|
557 act.setData(QVariant(hit.linkUrl())) |
|
558 menu.addSeparator() |
|
559 menu.addAction(self.trUtf8("Save Lin&k"), self.__downloadLink) |
|
560 act = menu.addAction(self.trUtf8("Bookmark this Link"), self.__bookmarkLink) |
|
561 act.setData(QVariant(hit.linkUrl())) |
|
562 menu.addSeparator() |
|
563 menu.addAction(self.trUtf8("Copy Link to Clipboard"), self.__copyLink) |
|
564 |
|
565 if not hit.imageUrl().isEmpty(): |
|
566 if not menu.isEmpty(): |
|
567 menu.addSeparator() |
|
568 act = menu.addAction(self.trUtf8("Open Image in New Tab"), |
|
569 self.__openLinkInNewTab) |
|
570 act.setData(QVariant(hit.imageUrl())) |
|
571 menu.addSeparator() |
|
572 menu.addAction(self.trUtf8("Save Image"), self.__downloadImage) |
|
573 menu.addAction(self.trUtf8("Copy Image to Clipboard"), self.__copyImage) |
|
574 act = menu.addAction(self.trUtf8("Copy Image Location to Clipboard"), |
|
575 self.__copyImageLocation) |
|
576 act.setData(QVariant(hit.imageUrl().toString())) |
|
577 menu.addSeparator() |
|
578 act = menu.addAction(self.trUtf8("Block Image"), self.__blockImage) |
|
579 act.setData(QVariant(hit.imageUrl().toString())) |
|
580 |
|
581 if not menu.isEmpty(): |
|
582 menu.addSeparator() |
|
583 menu.addAction(self.mw.newTabAct) |
|
584 menu.addAction(self.mw.newAct) |
|
585 menu.addSeparator() |
|
586 menu.addAction(self.mw.saveAsAct) |
|
587 menu.addSeparator() |
|
588 menu.addAction(self.trUtf8("Bookmark this Page"), self.__addBookmark) |
|
589 menu.addSeparator() |
|
590 menu.addAction(self.mw.backAct) |
|
591 menu.addAction(self.mw.forwardAct) |
|
592 menu.addAction(self.mw.homeAct) |
|
593 menu.addSeparator() |
|
594 menu.addAction(self.mw.zoomInAct) |
|
595 menu.addAction(self.mw.zoomOutAct) |
|
596 menu.addSeparator() |
|
597 if self.selectedText(): |
|
598 menu.addAction(self.mw.copyAct) |
|
599 menu.addAction(self.mw.findAct) |
|
600 menu.addSeparator() |
|
601 if self.selectedText(): |
|
602 self.__searchMenu = menu.addMenu(self.trUtf8("Search with...")) |
|
603 |
|
604 engineNames = HelpWebSearchWidget.openSearchManager().allEnginesNames() |
|
605 for engineName in engineNames: |
|
606 engine = HelpWebSearchWidget.openSearchManager().engine(engineName) |
|
607 act = OpenSearchEngineAction(engine, self.__searchMenu) |
|
608 self.__searchMenu.addAction(act) |
|
609 act.setData(QVariant(engineName)) |
|
610 self.connect(self.__searchMenu, SIGNAL("triggered(QAction *)"), |
|
611 self.__searchRequested) |
|
612 |
|
613 menu.addSeparator() |
|
614 menu.addAction(self.trUtf8("Web Inspector..."), self.__webInspector) |
|
615 |
|
616 menu.exec_(evt.globalPos()) |
|
617 |
|
618 def __openLinkInNewTab(self): |
|
619 """ |
|
620 Private method called by the context menu to open a link in a new window. |
|
621 """ |
|
622 act = self.sender() |
|
623 url = act.data().toUrl() |
|
624 if url.isEmpty(): |
|
625 return |
|
626 |
|
627 oldCtrlPressed = self.ctrlPressed |
|
628 self.ctrlPressed = True |
|
629 self.setSource(url) |
|
630 self.ctrlPressed = oldCtrlPressed |
|
631 |
|
632 def __bookmarkLink(self): |
|
633 """ |
|
634 Private slot to bookmark a link via the context menu. |
|
635 """ |
|
636 act = self.sender() |
|
637 url = act.data().toUrl() |
|
638 if url.isEmpty(): |
|
639 return |
|
640 |
|
641 dlg = AddBookmarkDialog() |
|
642 dlg.setUrl(unicode(url.toEncoded())) |
|
643 dlg.exec_() |
|
644 |
|
645 def __downloadLink(self): |
|
646 """ |
|
647 Private slot to download a link and save it to disk. |
|
648 """ |
|
649 self.pageAction(QWebPage.DownloadLinkToDisk).trigger() |
|
650 |
|
651 def __copyLink(self): |
|
652 """ |
|
653 Private slot to copy a link to the clipboard. |
|
654 """ |
|
655 self.pageAction(QWebPage.CopyLinkToClipboard).trigger() |
|
656 |
|
657 def __downloadImage(self): |
|
658 """ |
|
659 Private slot to download an image and save it to disk. |
|
660 """ |
|
661 self.pageAction(QWebPage.DownloadImageToDisk).trigger() |
|
662 |
|
663 def __copyImage(self): |
|
664 """ |
|
665 Private slot to copy an image to the clipboard. |
|
666 """ |
|
667 self.pageAction(QWebPage.CopyImageToClipboard).trigger() |
|
668 |
|
669 def __copyImageLocation(self): |
|
670 """ |
|
671 Private slot to copy an image location to the clipboard. |
|
672 """ |
|
673 act = self.sender() |
|
674 url = act.data().toString() |
|
675 QApplication.clipboard().setText(url) |
|
676 |
|
677 def __blockImage(self): |
|
678 """ |
|
679 Private slot to add a block rule for an image URL. |
|
680 """ |
|
681 act = self.sender() |
|
682 url = act.data().toString() |
|
683 dlg = Helpviewer.HelpWindow.HelpWindow.adblockManager().showDialog() |
|
684 dlg.addCustomRule(url) |
|
685 |
|
686 def __searchRequested(self, act): |
|
687 """ |
|
688 Private slot to search for some text with a selected search engine. |
|
689 |
|
690 @param act reference to the action that triggered this slot (QAction) |
|
691 """ |
|
692 searchText = self.selectedText() |
|
693 |
|
694 if not searchText: |
|
695 return |
|
696 |
|
697 engineName = act.data().toString() |
|
698 if engineName: |
|
699 engine = HelpWebSearchWidget.openSearchManager().engine(engineName) |
|
700 self.emit(SIGNAL("search(const QUrl &)"), engine.searchUrl(searchText)) |
|
701 |
|
702 def __webInspector(self): |
|
703 """ |
|
704 Private slot to show the web inspector window. |
|
705 """ |
|
706 self.triggerPageAction(QWebPage.InspectElement) |
|
707 |
|
708 def __addBookmark(self): |
|
709 """ |
|
710 Private slot to bookmark the current link. |
|
711 """ |
|
712 dlg = AddBookmarkDialog() |
|
713 dlg.setUrl(unicode(self.url().toEncoded())) |
|
714 dlg.setTitle(self.title()) |
|
715 dlg.exec_() |
|
716 |
|
717 def keyPressEvent(self, evt): |
|
718 """ |
|
719 Protected method called by a key press. |
|
720 |
|
721 This method is overridden from QTextBrowser. |
|
722 |
|
723 @param evt the key event (QKeyEvent) |
|
724 """ |
|
725 self.ctrlPressed = (evt.key() == Qt.Key_Control) |
|
726 QWebView.keyPressEvent(self, evt) |
|
727 |
|
728 def keyReleaseEvent(self, evt): |
|
729 """ |
|
730 Protected method called by a key release. |
|
731 |
|
732 This method is overridden from QTextBrowser. |
|
733 |
|
734 @param evt the key event (QKeyEvent) |
|
735 """ |
|
736 self.ctrlPressed = False |
|
737 QWebView.keyReleaseEvent(self, evt) |
|
738 |
|
739 def clearHistory(self): |
|
740 """ |
|
741 Public slot to clear the history. |
|
742 """ |
|
743 self.history().clear() |
|
744 self.__urlChanged(self.history().currentItem().url()) |
|
745 |
|
746 ############################################################################ |
|
747 ## Signal converters below |
|
748 ############################################################################ |
|
749 |
|
750 def __urlChanged(self, url): |
|
751 """ |
|
752 Private slot to handle the urlChanged signal. |
|
753 |
|
754 @param url the new url (QUrl) |
|
755 """ |
|
756 self.emit(SIGNAL('sourceChanged(const QUrl &)'), url) |
|
757 |
|
758 self.emit(SIGNAL('forwardAvailable(bool)'), self.isForwardAvailable()) |
|
759 self.emit(SIGNAL('backwardAvailable(bool)'), self.isBackwardAvailable()) |
|
760 |
|
761 def __statusBarMessage(self, text): |
|
762 """ |
|
763 Private slot to handle the statusBarMessage signal. |
|
764 |
|
765 @param text text to be shown in the status bar (string) |
|
766 """ |
|
767 self.mw.statusBar().showMessage(text) |
|
768 |
|
769 def __linkHovered(self, link, title, textContent): |
|
770 """ |
|
771 Private slot to handle the linkHovered signal. |
|
772 |
|
773 @param link the URL of the link (string) |
|
774 @param title the link title (string) |
|
775 @param textContent text content of the link (string) |
|
776 """ |
|
777 self.emit(SIGNAL('highlighted(const QString&)'), link) |
|
778 |
|
779 ############################################################################ |
|
780 ## Signal handlers below |
|
781 ############################################################################ |
|
782 |
|
783 def __loadStarted(self): |
|
784 """ |
|
785 Private method to handle the loadStarted signal. |
|
786 """ |
|
787 self.__isLoading = True |
|
788 self.mw.setLoading(self) |
|
789 self.mw.progressBar().show() |
|
790 |
|
791 def __loadProgress(self, progress): |
|
792 """ |
|
793 Private method to handle the loadProgress signal. |
|
794 |
|
795 @param progress progress value (integer) |
|
796 """ |
|
797 self.mw.progressBar().setValue(progress) |
|
798 |
|
799 def __loadFinished(self, ok): |
|
800 """ |
|
801 Private method to handle the loadFinished signal. |
|
802 |
|
803 @param ok flag indicating the result (boolean) |
|
804 """ |
|
805 self.__isLoading = False |
|
806 self.mw.progressBar().hide() |
|
807 self.mw.resetLoading(self) |
|
808 |
|
809 self.__iconChanged() |
|
810 |
|
811 self.mw.passwordManager().fill(self.page()) |
|
812 |
|
813 def isLoading(self): |
|
814 """ |
|
815 Public method to get the loading state. |
|
816 |
|
817 @return flag indicating the loading state (boolean) |
|
818 """ |
|
819 return self.__isLoading |
|
820 |
|
821 def saveAs(self): |
|
822 """ |
|
823 Public method to save the current page to a file. |
|
824 """ |
|
825 url = self.url() |
|
826 if url.isEmpty(): |
|
827 return |
|
828 |
|
829 req = QNetworkRequest(url) |
|
830 reply = self.mw.networkAccessManager().get(req) |
|
831 self.__unsupportedContent(reply, True) |
|
832 |
|
833 def __unsupportedContent(self, reply, requestFilename = None, download = False): |
|
834 """ |
|
835 Private slot to handle the unsupportedContent signal. |
|
836 |
|
837 @param reply reference to the reply object (QNetworkReply) |
|
838 @keyparam requestFilename indicating to ask for a filename |
|
839 (boolean or None). If it is None, the behavior is determined |
|
840 by a configuration option. |
|
841 @keyparam download flag indicating a download operation (boolean) |
|
842 """ |
|
843 if reply is None: |
|
844 return |
|
845 |
|
846 replyUrl = reply.url() |
|
847 |
|
848 if replyUrl.scheme() == "abp": |
|
849 return |
|
850 |
|
851 if reply.error() == QNetworkReply.NoError: |
|
852 if reply.url().isEmpty(): |
|
853 return |
|
854 header = reply.header(QNetworkRequest.ContentLengthHeader) |
|
855 size, ok = header.toInt() |
|
856 if ok and size == 0: |
|
857 return |
|
858 |
|
859 if requestFilename is None: |
|
860 requestFilename = Preferences.getUI("RequestDownloadFilename") |
|
861 dlg = DownloadDialog(reply, requestFilename, self.page(), download) |
|
862 self.connect(dlg, SIGNAL("done()"), self.__downloadDone) |
|
863 self.__downloadWindows.append(dlg) |
|
864 dlg.show() |
|
865 else: |
|
866 replyUrl = reply.url() |
|
867 if replyUrl.isEmpty(): |
|
868 return |
|
869 |
|
870 html = notFoundPage_html |
|
871 urlString = unicode(replyUrl.toEncoded()) |
|
872 title = self.trUtf8("Error loading page: {0}").format(urlString) |
|
873 pixmap = qApp.style()\ |
|
874 .standardIcon(QStyle.SP_MessageBoxWarning, None, self)\ |
|
875 .pixmap(32, 32) |
|
876 imageBuffer = QBuffer() |
|
877 imageBuffer.open(QIODevice.ReadWrite) |
|
878 if pixmap.save(imageBuffer, "PNG"): |
|
879 html.replace("IMAGE_BINARY_DATA_HERE", |
|
880 unicode(imageBuffer.buffer().toBase64())) |
|
881 html = html.format( |
|
882 title, |
|
883 reply.errorString(), |
|
884 self.trUtf8("When connecting to: {0}.").format(urlString), |
|
885 self.trUtf8("Check the address for errors such as <b>ww</b>.example.org " |
|
886 "instead of <b>www</b>.example.org"), |
|
887 self.trUtf8("If the address is correct, try checking the network " |
|
888 "connection."), |
|
889 self.trUtf8("If your computer or network is protected by a firewall or " |
|
890 "proxy, make sure that the browser is permitted to access " |
|
891 "the network.") |
|
892 ) |
|
893 self.setHtml(html, replyUrl) |
|
894 self.mw.historyManager().removeHistoryEntry(replyUrl, self.title()) |
|
895 self.emit(SIGNAL('loadFinished(bool)'), False) |
|
896 |
|
897 def __downloadDone(self): |
|
898 """ |
|
899 Private slot to handle the done signal of the download dialogs. |
|
900 """ |
|
901 dlg = self.sender() |
|
902 if dlg in self.__downloadWindows: |
|
903 self.disconnect(dlg, SIGNAL("done()"), self.__downloadDone) |
|
904 self.__downloadWindows.remove(dlg) |
|
905 dlg.deleteLater() |
|
906 |
|
907 def __downloadRequested(self, request): |
|
908 """ |
|
909 Private slot to handle a download request. |
|
910 |
|
911 @param request reference to the request object (QNetworkRequest) |
|
912 """ |
|
913 if request.url().isEmpty(): |
|
914 return |
|
915 mgr = self.page().networkAccessManager() |
|
916 self.__unsupportedContent(mgr.get(request), download = True) |
|
917 |
|
918 def __iconChanged(self): |
|
919 """ |
|
920 Private slot to handle the icon change. |
|
921 """ |
|
922 self.mw.iconChanged(self.icon()) |
|
923 |
|
924 ############################################################################ |
|
925 ## Miscellaneous methods below |
|
926 ############################################################################ |
|
927 |
|
928 def createWindow(self, windowType): |
|
929 """ |
|
930 Protected method called, when a new window should be created. |
|
931 |
|
932 @param windowType type of the requested window (QWebPage.WebWindowType) |
|
933 """ |
|
934 self.mw.newTab() |
|
935 return self.mw.currentBrowser() |
|
936 |
|
937 def preferencesChanged(self): |
|
938 """ |
|
939 Public method to indicate a change of the settings. |
|
940 """ |
|
941 self.reload() |