9 |
9 |
10 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl, QEvent |
10 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl, QEvent |
11 from PyQt6.QtGui import QGuiApplication, QClipboard |
11 from PyQt6.QtGui import QGuiApplication, QClipboard |
12 from PyQt6.QtHelp import QHelpLink |
12 from PyQt6.QtHelp import QHelpLink |
13 from PyQt6.QtWidgets import ( |
13 from PyQt6.QtWidgets import ( |
14 QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QMenu, QDialog, |
14 QWidget, |
15 QApplication |
15 QVBoxLayout, |
|
16 QHBoxLayout, |
|
17 QLabel, |
|
18 QLineEdit, |
|
19 QMenu, |
|
20 QDialog, |
|
21 QApplication, |
16 ) |
22 ) |
17 |
23 |
18 |
24 |
19 class HelpIndexWidget(QWidget): |
25 class HelpIndexWidget(QWidget): |
20 """ |
26 """ |
21 Class implementing a window for showing the QtHelp index. |
27 Class implementing a window for showing the QtHelp index. |
22 |
28 |
23 @signal escapePressed() emitted when the ESC key was pressed |
29 @signal escapePressed() emitted when the ESC key was pressed |
24 @signal openUrl(QUrl, str) emitted to open an entry in the current tab |
30 @signal openUrl(QUrl, str) emitted to open an entry in the current tab |
25 @signal newTab(QUrl, str) emitted to open an entry in a new tab |
31 @signal newTab(QUrl, str) emitted to open an entry in a new tab |
26 @signal newBackgroundTab(QUrl, str) emitted to open an entry in a |
32 @signal newBackgroundTab(QUrl, str) emitted to open an entry in a |
27 new background tab |
33 new background tab |
28 @signal newWindow(QUrl, str) emitted to open an entry in a new window |
34 @signal newWindow(QUrl, str) emitted to open an entry in a new window |
29 """ |
35 """ |
|
36 |
30 escapePressed = pyqtSignal() |
37 escapePressed = pyqtSignal() |
31 openUrl = pyqtSignal(QUrl) |
38 openUrl = pyqtSignal(QUrl) |
32 newTab = pyqtSignal(QUrl) |
39 newTab = pyqtSignal(QUrl) |
33 newBackgroundTab = pyqtSignal(QUrl) |
40 newBackgroundTab = pyqtSignal(QUrl) |
34 newWindow = pyqtSignal(QUrl) |
41 newWindow = pyqtSignal(QUrl) |
35 |
42 |
36 def __init__(self, engine, internal=False, parent=None): |
43 def __init__(self, engine, internal=False, parent=None): |
37 """ |
44 """ |
38 Constructor |
45 Constructor |
39 |
46 |
40 @param engine reference to the help engine |
47 @param engine reference to the help engine |
41 @type QHelpEngine |
48 @type QHelpEngine |
42 @param internal flag indicating the internal help viewer |
49 @param internal flag indicating the internal help viewer |
43 @type bool |
50 @type bool |
44 @param parent reference to the parent widget |
51 @param parent reference to the parent widget |
45 @type QWidget |
52 @type QWidget |
46 """ |
53 """ |
47 super().__init__(parent) |
54 super().__init__(parent) |
48 |
55 |
49 self.__engine = engine |
56 self.__engine = engine |
50 self.__internal = internal |
57 self.__internal = internal |
51 |
58 |
52 self.__searchEdit = None |
59 self.__searchEdit = None |
53 self.__index = None |
60 self.__index = None |
54 |
61 |
55 self.__layout = QVBoxLayout(self) |
62 self.__layout = QVBoxLayout(self) |
56 if internal: |
63 if internal: |
57 # no margins for the internal variant |
64 # no margins for the internal variant |
58 self.__layout.setContentsMargins(0, 0, 0, 0) |
65 self.__layout.setContentsMargins(0, 0, 0, 0) |
59 |
66 |
60 self.__searchEditLayout = QHBoxLayout() |
67 self.__searchEditLayout = QHBoxLayout() |
61 label = QLabel(self.tr("&Look for:")) |
68 label = QLabel(self.tr("&Look for:")) |
62 self.__searchEditLayout.addWidget(label) |
69 self.__searchEditLayout.addWidget(label) |
63 |
70 |
64 self.__searchEdit = QLineEdit() |
71 self.__searchEdit = QLineEdit() |
65 self.__searchEdit.setClearButtonEnabled(True) |
72 self.__searchEdit.setClearButtonEnabled(True) |
66 label.setBuddy(self.__searchEdit) |
73 label.setBuddy(self.__searchEdit) |
67 self.__searchEdit.textChanged.connect(self.__filterIndices) |
74 self.__searchEdit.textChanged.connect(self.__filterIndices) |
68 self.__searchEdit.installEventFilter(self) |
75 self.__searchEdit.installEventFilter(self) |
69 self.__searchEditLayout.addWidget(self.__searchEdit) |
76 self.__searchEditLayout.addWidget(self.__searchEdit) |
70 self.__layout.addLayout(self.__searchEditLayout) |
77 self.__layout.addLayout(self.__searchEditLayout) |
71 |
78 |
72 self.__index = self.__engine.indexWidget() |
79 self.__index = self.__engine.indexWidget() |
73 self.__index.setContextMenuPolicy( |
80 self.__index.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) |
74 Qt.ContextMenuPolicy.CustomContextMenu) |
81 |
75 |
|
76 self.__engine.indexModel().indexCreationStarted.connect( |
82 self.__engine.indexModel().indexCreationStarted.connect( |
77 self.__disableSearchEdit) |
83 self.__disableSearchEdit |
78 self.__engine.indexModel().indexCreated.connect( |
84 ) |
79 self.__enableSearchEdit) |
85 self.__engine.indexModel().indexCreated.connect(self.__enableSearchEdit) |
80 self.__index.documentActivated.connect(self.__documentActivated) |
86 self.__index.documentActivated.connect(self.__documentActivated) |
81 self.__index.documentsActivated.connect(self.__documentsActivated) |
87 self.__index.documentsActivated.connect(self.__documentsActivated) |
82 self.__index.customContextMenuRequested.connect( |
88 self.__index.customContextMenuRequested.connect(self.__showContextMenu) |
83 self.__showContextMenu) |
89 self.__searchEdit.returnPressed.connect(self.__index.activateCurrentItem) |
84 self.__searchEdit.returnPressed.connect( |
|
85 self.__index.activateCurrentItem) |
|
86 self.__layout.addWidget(self.__index) |
90 self.__layout.addWidget(self.__index) |
87 |
91 |
88 @pyqtSlot(QHelpLink, str) |
92 @pyqtSlot(QHelpLink, str) |
89 def __documentActivated(self, document, keyword, modifiers=None): |
93 def __documentActivated(self, document, keyword, modifiers=None): |
90 """ |
94 """ |
91 Private slot to handle the activation of a keyword entry. |
95 Private slot to handle the activation of a keyword entry. |
92 |
96 |
93 @param document reference to a data structure containing the |
97 @param document reference to a data structure containing the |
94 document info |
98 document info |
95 @type QHelpLink |
99 @type QHelpLink |
96 @param keyword keyword for the URL |
100 @param keyword keyword for the URL |
97 @type str |
101 @type str |
100 """ |
104 """ |
101 if modifiers is None: |
105 if modifiers is None: |
102 modifiers = QApplication.keyboardModifiers() |
106 modifiers = QApplication.keyboardModifiers() |
103 if not document.url.isEmpty() and document.url.isValid(): |
107 if not document.url.isEmpty() and document.url.isValid(): |
104 if modifiers & ( |
108 if modifiers & ( |
105 Qt.KeyboardModifier.ControlModifier | |
|
106 Qt.KeyboardModifier.ControlModifier |
109 Qt.KeyboardModifier.ControlModifier |
|
110 | Qt.KeyboardModifier.ControlModifier |
107 ): |
111 ): |
108 self.newBackgroundTab.emit(document.url) |
112 self.newBackgroundTab.emit(document.url) |
109 elif modifiers & Qt.KeyboardModifier.ControlModifier: |
113 elif modifiers & Qt.KeyboardModifier.ControlModifier: |
110 self.newTab.emit(document.url) |
114 self.newTab.emit(document.url) |
111 elif ( |
115 elif modifiers & Qt.KeyboardModifier.ShiftModifier and not self.__internal: |
112 modifiers & Qt.KeyboardModifier.ShiftModifier and |
|
113 not self.__internal |
|
114 ): |
|
115 self.newWindow.emit(document.url) |
116 self.newWindow.emit(document.url) |
116 else: |
117 else: |
117 self.openUrl.emit(document.url) |
118 self.openUrl.emit(document.url) |
118 |
119 |
119 def __documentsActivated(self, documents, helpKeyword): |
120 def __documentsActivated(self, documents, helpKeyword): |
120 """ |
121 """ |
121 Private slot to handle the activation of an entry with multiple help |
122 Private slot to handle the activation of an entry with multiple help |
122 documents. |
123 documents. |
123 |
124 |
124 @param documents list of help document link data structures |
125 @param documents list of help document link data structures |
125 @type list of QHelpLink |
126 @type list of QHelpLink |
126 @param helpKeyword keyword for the entry |
127 @param helpKeyword keyword for the entry |
127 @type str |
128 @type str |
128 """ |
129 """ |
129 modifiers = QApplication.keyboardModifiers() |
130 modifiers = QApplication.keyboardModifiers() |
130 document = ( |
131 document = ( |
131 documents[0] |
132 documents[0] |
132 if len(documents) == 1 else |
133 if len(documents) == 1 |
133 self.__selectDocument(documents, helpKeyword) |
134 else self.__selectDocument(documents, helpKeyword) |
134 ) |
135 ) |
135 self.__documentActivated(document, helpKeyword, modifiers) |
136 self.__documentActivated(document, helpKeyword, modifiers) |
136 |
137 |
137 def __selectDocument(self, documents, helpKeyword): |
138 def __selectDocument(self, documents, helpKeyword): |
138 """ |
139 """ |
139 Private method to give the user a chance to select among the |
140 Private method to give the user a chance to select among the |
140 given documents. |
141 given documents. |
141 |
142 |
142 @param documents list of help document link data structures |
143 @param documents list of help document link data structures |
143 @type list of QHelpLink |
144 @type list of QHelpLink |
144 @param helpKeyword keyword for the documents |
145 @param helpKeyword keyword for the documents |
145 @type str |
146 @type str |
146 @return selected document |
147 @return selected document |
147 @rtype QHelpLink |
148 @rtype QHelpLink |
148 """ |
149 """ |
149 document = QHelpLink() |
150 document = QHelpLink() |
150 |
151 |
151 from .HelpTopicDialog import HelpTopicDialog |
152 from .HelpTopicDialog import HelpTopicDialog |
|
153 |
152 dlg = HelpTopicDialog(self, helpKeyword, documents) |
154 dlg = HelpTopicDialog(self, helpKeyword, documents) |
153 if dlg.exec() == QDialog.DialogCode.Accepted: |
155 if dlg.exec() == QDialog.DialogCode.Accepted: |
154 document = dlg.document() |
156 document = dlg.document() |
155 |
157 |
156 return document |
158 return document |
157 |
159 |
158 def __filterIndices(self, indexFilter): |
160 def __filterIndices(self, indexFilter): |
159 """ |
161 """ |
160 Private slot to filter the indexes according to the given filter. |
162 Private slot to filter the indexes according to the given filter. |
161 |
163 |
162 @param indexFilter filter to be used |
164 @param indexFilter filter to be used |
163 @type str |
165 @type str |
164 """ |
166 """ |
165 if '*' in indexFilter: |
167 if "*" in indexFilter: |
166 self.__index.filterIndices(indexFilter, indexFilter) |
168 self.__index.filterIndices(indexFilter, indexFilter) |
167 else: |
169 else: |
168 self.__index.filterIndices(indexFilter) |
170 self.__index.filterIndices(indexFilter) |
169 |
171 |
170 def __enableSearchEdit(self): |
172 def __enableSearchEdit(self): |
171 """ |
173 """ |
172 Private slot to enable the search edit. |
174 Private slot to enable the search edit. |
173 """ |
175 """ |
174 self.__searchEdit.setEnabled(True) |
176 self.__searchEdit.setEnabled(True) |
175 self.__filterIndices(self.__searchEdit.text()) |
177 self.__filterIndices(self.__searchEdit.text()) |
176 |
178 |
177 def __disableSearchEdit(self): |
179 def __disableSearchEdit(self): |
178 """ |
180 """ |
179 Private slot to enable the search edit. |
181 Private slot to enable the search edit. |
180 """ |
182 """ |
181 self.__searchEdit.setEnabled(False) |
183 self.__searchEdit.setEnabled(False) |
182 |
184 |
183 def focusInEvent(self, evt): |
185 def focusInEvent(self, evt): |
184 """ |
186 """ |
185 Protected method handling focus in events. |
187 Protected method handling focus in events. |
186 |
188 |
187 @param evt reference to the focus event object |
189 @param evt reference to the focus event object |
188 @type QFocusEvent |
190 @type QFocusEvent |
189 """ |
191 """ |
190 if evt.reason() != Qt.FocusReason.MouseFocusReason: |
192 if evt.reason() != Qt.FocusReason.MouseFocusReason: |
191 self.__searchEdit.selectAll() |
193 self.__searchEdit.selectAll() |
192 self.__searchEdit.setFocus() |
194 self.__searchEdit.setFocus() |
193 |
195 |
194 def eventFilter(self, watched, event): |
196 def eventFilter(self, watched, event): |
195 """ |
197 """ |
196 Public method called to filter the event queue. |
198 Public method called to filter the event queue. |
197 |
199 |
198 @param watched the QObject being watched |
200 @param watched the QObject being watched |
199 @type QObject |
201 @type QObject |
200 @param event the event that occurred |
202 @param event the event that occurred |
201 @type QEvent |
203 @type QEvent |
202 @return flag indicating whether the event was handled |
204 @return flag indicating whether the event was handled |
203 @rtype bool |
205 @rtype bool |
204 """ |
206 """ |
205 if ( |
207 if ( |
206 self.__searchEdit and watched == self.__searchEdit and |
208 self.__searchEdit |
207 event.type() == QEvent.Type.KeyPress |
209 and watched == self.__searchEdit |
|
210 and event.type() == QEvent.Type.KeyPress |
208 ): |
211 ): |
209 idx = self.__index.currentIndex() |
212 idx = self.__index.currentIndex() |
210 if event.key() == Qt.Key.Key_Up: |
213 if event.key() == Qt.Key.Key_Up: |
211 idx = self.__index.model().index( |
214 idx = self.__index.model().index( |
212 idx.row() - 1, idx.column(), idx.parent()) |
215 idx.row() - 1, idx.column(), idx.parent() |
|
216 ) |
213 if idx.isValid(): |
217 if idx.isValid(): |
214 self.__index.setCurrentIndex(idx) |
218 self.__index.setCurrentIndex(idx) |
215 elif event.key() == Qt.Key.Key_Down: |
219 elif event.key() == Qt.Key.Key_Down: |
216 idx = self.__index.model().index( |
220 idx = self.__index.model().index( |
217 idx.row() + 1, idx.column(), idx.parent()) |
221 idx.row() + 1, idx.column(), idx.parent() |
|
222 ) |
218 if idx.isValid(): |
223 if idx.isValid(): |
219 self.__index.setCurrentIndex(idx) |
224 self.__index.setCurrentIndex(idx) |
220 elif event.key() == Qt.Key.Key_Escape: |
225 elif event.key() == Qt.Key.Key_Escape: |
221 self.escapePressed.emit() |
226 self.escapePressed.emit() |
222 |
227 |
223 return QWidget.eventFilter(self, watched, event) |
228 return QWidget.eventFilter(self, watched, event) |
224 |
229 |
225 def __showContextMenu(self, pos): |
230 def __showContextMenu(self, pos): |
226 """ |
231 """ |
227 Private slot showing the context menu. |
232 Private slot showing the context menu. |
228 |
233 |
229 @param pos position to show the menu at |
234 @param pos position to show the menu at |
230 @type QPoint |
235 @type QPoint |
231 """ |
236 """ |
232 idx = self.__index.indexAt(pos) |
237 idx = self.__index.indexAt(pos) |
233 if idx.isValid(): |
238 if idx.isValid(): |
234 menu = QMenu() |
239 menu = QMenu() |
235 curTab = menu.addAction(self.tr("Open Link")) |
240 curTab = menu.addAction(self.tr("Open Link")) |
236 if self.__internal: |
241 if self.__internal: |
237 newTab = menu.addAction(self.tr("Open Link in New Page")) |
242 newTab = menu.addAction(self.tr("Open Link in New Page")) |
238 newBackgroundTab = menu.addAction( |
243 newBackgroundTab = menu.addAction( |
239 self.tr("Open Link in Background Page")) |
244 self.tr("Open Link in Background Page") |
|
245 ) |
240 else: |
246 else: |
241 newTab = menu.addAction(self.tr("Open Link in New Tab")) |
247 newTab = menu.addAction(self.tr("Open Link in New Tab")) |
242 newBackgroundTab = menu.addAction( |
248 newBackgroundTab = menu.addAction( |
243 self.tr("Open Link in Background Tab")) |
249 self.tr("Open Link in Background Tab") |
|
250 ) |
244 newWindow = menu.addAction(self.tr("Open Link in New Window")) |
251 newWindow = menu.addAction(self.tr("Open Link in New Window")) |
245 menu.addSeparator() |
252 menu.addSeparator() |
246 copyLink = menu.addAction(self.tr("Copy URL to Clipboard")) |
253 copyLink = menu.addAction(self.tr("Copy URL to Clipboard")) |
247 menu.move(self.__index.mapToGlobal(pos)) |
254 menu.move(self.__index.mapToGlobal(pos)) |
248 |
255 |
249 act = menu.exec() |
256 act = menu.exec() |
250 model = self.__index.model() |
257 model = self.__index.model() |
251 if model is not None: |
258 if model is not None: |
252 helpKeyword = model.data(idx, Qt.ItemDataRole.DisplayRole) |
259 helpKeyword = model.data(idx, Qt.ItemDataRole.DisplayRole) |
253 helpLinks = self.__engine.documentsForKeyword(helpKeyword, "") |
260 helpLinks = self.__engine.documentsForKeyword(helpKeyword, "") |
254 if len(helpLinks) == 1: |
261 if len(helpLinks) == 1: |
255 link = helpLinks[0].url |
262 link = helpLinks[0].url |
256 else: |
263 else: |
257 link = self.__selectDocument(helpLinks, helpKeyword).url |
264 link = self.__selectDocument(helpLinks, helpKeyword).url |
258 |
265 |
259 if not link.isEmpty() and link.isValid(): |
266 if not link.isEmpty() and link.isValid(): |
260 if act == curTab: |
267 if act == curTab: |
261 self.openUrl.emit(link) |
268 self.openUrl.emit(link) |
262 elif act == newTab: |
269 elif act == newTab: |
263 self.newTab.emit(link) |
270 self.newTab.emit(link) |