9 |
9 |
10 from __future__ import unicode_literals |
10 from __future__ import unicode_literals |
11 |
11 |
12 from PyQt5.QtCore import pyqtSignal, Qt, QUrl, QEvent |
12 from PyQt5.QtCore import pyqtSignal, Qt, QUrl, QEvent |
13 from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QLineEdit, QMenu, \ |
13 from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QLineEdit, QMenu, \ |
14 QDialog |
14 QDialog, QApplication |
15 |
15 |
16 |
16 |
17 class HelpIndexWidget(QWidget): |
17 class HelpIndexWidget(QWidget): |
18 """ |
18 """ |
19 Class implementing a window for showing the QtHelp index. |
19 Class implementing a window for showing the QtHelp index. |
20 |
20 |
21 @signal linkActivated(QUrl) emitted when an index entry is activated |
|
22 @signal linksActivated(links, keyword) emitted when an index entry |
|
23 referencing multiple targets is activated |
|
24 @signal escapePressed() emitted when the ESC key was pressed |
21 @signal escapePressed() emitted when the ESC key was pressed |
|
22 @signal openUrl(QUrl, str) emitted to open an entry in the current tab |
|
23 @signal newTab(QUrl, str) emitted to open an entry in a new tab |
|
24 @signal newBackgroundTab(QUrl, str) emitted to open an entry in a |
|
25 new background tab |
|
26 @signal newWindow(QUrl, str) emitted to open an entry in a new window |
25 """ |
27 """ |
26 linkActivated = pyqtSignal(QUrl) |
|
27 linksActivated = pyqtSignal(dict, str) |
|
28 escapePressed = pyqtSignal() |
28 escapePressed = pyqtSignal() |
29 |
29 openUrl = pyqtSignal(QUrl) |
30 def __init__(self, engine, mainWindow, parent=None): |
30 newTab = pyqtSignal(QUrl) |
|
31 newBackgroundTab = pyqtSignal(QUrl) |
|
32 newWindow = pyqtSignal(QUrl) |
|
33 |
|
34 def __init__(self, engine, parent=None): |
31 """ |
35 """ |
32 Constructor |
36 Constructor |
33 |
37 |
34 @param engine reference to the help engine (QHelpEngine) |
38 @param engine reference to the help engine (QHelpEngine) |
35 @param mainWindow reference to the main window object (QMainWindow) |
|
36 @param parent reference to the parent widget (QWidget) |
39 @param parent reference to the parent widget (QWidget) |
37 """ |
40 """ |
38 super(HelpIndexWidget, self).__init__(parent) |
41 super(HelpIndexWidget, self).__init__(parent) |
39 |
42 |
40 self.__engine = engine |
43 self.__engine = engine |
41 self.__mw = mainWindow |
|
42 |
44 |
43 self.__searchEdit = None |
45 self.__searchEdit = None |
44 self.__index = None |
46 self.__index = None |
45 |
47 |
46 self.__layout = QVBoxLayout(self) |
48 self.__layout = QVBoxLayout(self) |
53 self.__searchEdit.installEventFilter(self) |
55 self.__searchEdit.installEventFilter(self) |
54 self.__layout.addWidget(self.__searchEdit) |
56 self.__layout.addWidget(self.__searchEdit) |
55 |
57 |
56 self.__index = self.__engine.indexWidget() |
58 self.__index = self.__engine.indexWidget() |
57 self.__index.installEventFilter(self) |
59 self.__index.installEventFilter(self) |
|
60 self.__index.setContextMenuPolicy(Qt.CustomContextMenu) |
|
61 |
58 self.__engine.indexModel().indexCreationStarted.connect( |
62 self.__engine.indexModel().indexCreationStarted.connect( |
59 self.__disableSearchEdit) |
63 self.__disableSearchEdit) |
60 self.__engine.indexModel().indexCreated.connect( |
64 self.__engine.indexModel().indexCreated.connect( |
61 self.__enableSearchEdit) |
65 self.__enableSearchEdit) |
62 self.__index.activated.connect(self.__activated) |
66 self.__index.activated.connect(self.__activated) |
|
67 self.__index.customContextMenuRequested.connect( |
|
68 self.__showContextMenu) |
63 self.__searchEdit.returnPressed.connect( |
69 self.__searchEdit.returnPressed.connect( |
64 self.__index.activateCurrentItem) |
70 self.__index.activateCurrentItem) |
65 self.__layout.addWidget(self.__index) |
71 self.__layout.addWidget(self.__index) |
66 |
72 |
67 self.__index.viewport().installEventFilter(self) |
73 self.__index.viewport().installEventFilter(self) |
68 |
74 |
69 def __activated(self, idx): |
75 def __activated(self, idx, midButton=False): |
70 """ |
76 """ |
71 Private slot to handle the activation of a keyword entry. |
77 Private slot to handle the activation of a keyword entry. |
72 |
78 |
73 @param idx index of the activated entry (QModelIndex) |
79 @param idx index of the activated entry |
|
80 @type QModelIndex |
|
81 @param midButton flag indicating a middle mouse button release |
|
82 @type bool |
74 """ |
83 """ |
75 model = self.__index.model() |
84 model = self.__index.model() |
76 if model is not None: |
85 if model is not None: |
|
86 modifiers = QApplication.keyboardModifiers() |
77 keyword = model.data(idx, Qt.DisplayRole) |
87 keyword = model.data(idx, Qt.DisplayRole) |
78 links = model.linksForKeyword(keyword) |
88 links = model.linksForKeyword(keyword) |
79 if len(links) == 1: |
89 if len(links) == 1: |
80 self.linkActivated.emit(QUrl(links[list(links.keys())[0]])) |
90 link = QUrl(links[list(links.keys())[0]]) |
81 else: |
91 else: |
82 self.linksActivated.emit(links, keyword) |
92 link = self.__selectLink(links, keyword) |
|
93 if not link.isEmpty() and link.isValid(): |
|
94 if modifiers & Qt.ControlModifier or midButton: |
|
95 self.newTab.emit(link) |
|
96 elif modifiers & Qt.ShiftModifier: |
|
97 self.newWindow.emit(link) |
|
98 else: |
|
99 self.openUrl.emit(link) |
|
100 |
|
101 def __selectLink(self, links, keyword): |
|
102 """ |
|
103 Private method to give the user a chance to select among the |
|
104 returned links. |
|
105 |
|
106 @param links dictionary of document title and URL to select from |
|
107 @type dictionary of str (key) and QUrl (value) |
|
108 @param keyword keyword for the link set |
|
109 @type str |
|
110 @return selected link |
|
111 @rtype QUrl |
|
112 """ |
|
113 link = QUrl() |
|
114 from .HelpTopicDialog import HelpTopicDialog |
|
115 dlg = HelpTopicDialog(self, keyword, links) |
|
116 if dlg.exec_() == QDialog.Accepted: |
|
117 link = dlg.link() |
|
118 return link |
83 |
119 |
84 def __filterIndices(self, filter): |
120 def __filterIndices(self, filter): |
85 """ |
121 """ |
86 Private slot to filter the indices according to the given filter. |
122 Private slot to filter the indices according to the given filter. |
87 |
123 |
136 idx.row() + 1, idx.column(), idx.parent()) |
172 idx.row() + 1, idx.column(), idx.parent()) |
137 if idx.isValid(): |
173 if idx.isValid(): |
138 self.__index.setCurrentIndex(idx) |
174 self.__index.setCurrentIndex(idx) |
139 elif event.key() == Qt.Key_Escape: |
175 elif event.key() == Qt.Key_Escape: |
140 self.escapePressed.emit() |
176 self.escapePressed.emit() |
141 elif self.__index and watched == self.__index and \ |
|
142 event.type() == QEvent.ContextMenu: |
|
143 idx = self.__index.indexAt(event.pos()) |
|
144 if idx.isValid(): |
|
145 menu = QMenu() |
|
146 curTab = menu.addAction(self.tr("Open Link")) |
|
147 newTab = menu.addAction(self.tr("Open Link in New Tab")) |
|
148 menu.move(self.__index.mapToGlobal(event.pos())) |
|
149 |
|
150 act = menu.exec_() |
|
151 if act == curTab: |
|
152 self.__activated(idx) |
|
153 elif act == newTab: |
|
154 model = self.__index.model() |
|
155 if model is not None: |
|
156 keyword = model.data(idx, Qt.DisplayRole) |
|
157 links = model.linksForKeyword(keyword) |
|
158 if len(links) == 1: |
|
159 self.__mw.newTab(list(links.values())[0]) |
|
160 elif len(links) > 1: |
|
161 from .HelpTopicDialog import HelpTopicDialog |
|
162 dlg = HelpTopicDialog(self, keyword, links) |
|
163 if dlg.exec_() == QDialog.Accepted: |
|
164 self.__mw.newTab(dlg.link()) |
|
165 elif self.__index and watched == self.__index.viewport() and \ |
177 elif self.__index and watched == self.__index.viewport() and \ |
166 event.type() == QEvent.MouseButtonRelease: |
178 event.type() == QEvent.MouseButtonRelease: |
167 idx = self.__index.indexAt(event.pos()) |
179 idx = self.__index.indexAt(event.pos()) |
168 if idx.isValid() and event.button() == Qt.MidButton: |
180 if idx.isValid(): |
169 model = self.__index.model() |
181 self.__activated(idx, midButton=event.button() == Qt.MidButton) |
170 if model is not None: |
|
171 keyword = model.data(idx, Qt.DisplayRole) |
|
172 links = model.linksForKeyword(keyword) |
|
173 if len(links) == 1: |
|
174 self.__mw.newTab(list(links.values())[0]) |
|
175 elif len(links) > 1: |
|
176 from .HelpTopicDialog import HelpTopicDialog |
|
177 dlg = HelpTopicDialog(self, keyword, links) |
|
178 if dlg.exec_() == QDialog.Accepted: |
|
179 self.__mw.newTab(dlg.link()) |
|
180 |
182 |
181 return QWidget.eventFilter(self, watched, event) |
183 return QWidget.eventFilter(self, watched, event) |
|
184 |
|
185 def __showContextMenu(self, pos): |
|
186 """ |
|
187 Private slot showing the context menu. |
|
188 |
|
189 @param pos position to show the menu at (QPoint) |
|
190 """ |
|
191 idx = self.__index.indexAt(pos) |
|
192 if idx.isValid(): |
|
193 menu = QMenu() |
|
194 curTab = menu.addAction(self.tr("Open Link")) |
|
195 newTab = menu.addAction(self.tr("Open Link in New Tab")) |
|
196 newBackgroundTab = menu.addAction( |
|
197 self.tr("Open Link in Background Tab")) |
|
198 newWindow = menu.addAction(self.tr("Open Link in New Window")) |
|
199 menu.move(self.__index.mapToGlobal(pos)) |
|
200 |
|
201 act = menu.exec_() |
|
202 model = self.__index.model() |
|
203 if model is not None: |
|
204 keyword = model.data(idx, Qt.DisplayRole) |
|
205 links = model.linksForKeyword(keyword) |
|
206 if len(links) == 1: |
|
207 link = QUrl(links[list(links.keys())[0]]) |
|
208 else: |
|
209 link = self.__selectLink(links, keyword) |
|
210 |
|
211 if act == curTab: |
|
212 self.openUrl.emit(link) |
|
213 elif act == newTab: |
|
214 self.newTab.emit(link) |
|
215 elif act == newBackgroundTab: |
|
216 self.newBackgroundTab.emit(link) |
|
217 elif act == newWindow: |
|
218 self.newWindow.emit(link) |