eric7/HelpViewer/HelpBookmarksWidget.py

branch
eric7
changeset 8900
9c153ce17d74
child 8902
ba9b8c6e4928
equal deleted inserted replaced
8899:764178f2124d 8900:9c153ce17d74
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 import contextlib
7 import json
8
9 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QPoint, QUrl
10 from PyQt6.QtGui import QClipboard, QGuiApplication
11 from PyQt6.QtWidgets import (
12 QAbstractItemView, QApplication, QDialog, QListWidget, QListWidgetItem, QMenu
13 )
14
15 import Preferences
16
17 from .HelpBookmarkPropertiesDialog import HelpBookmarkPropertiesDialog
18
19 class HelpBookmarksWidget(QListWidget):
20 """
21 Class implementing a widget showing the list of bookmarks.
22
23 @signal escapePressed() emitted when the ESC key was pressed
24 @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
26 @signal newBackgroundTab(QUrl, str) emitted to open an entry in a
27 new background tab
28 """
29 escapePressed = pyqtSignal()
30 openUrl = pyqtSignal(QUrl)
31 newTab = pyqtSignal(QUrl)
32 newBackgroundTab = pyqtSignal(QUrl)
33
34 UrlRole = Qt.ItemDataRole.UserRole + 1
35
36 def __init__(self, parent=None):
37 """
38 Constructor
39
40 @param parent reference to the parent widget (defaults to None)
41 @type QWidget (optional)
42 """
43 super().__init__(parent)
44 self.setObjectName("HelpBookmarksWidget")
45
46 self.__helpViewer = parent
47
48 self.setAlternatingRowColors(True)
49 self.setSelectionMode(
50 QAbstractItemView.SelectionMode.ExtendedSelection)
51 self.setSortingEnabled(True)
52
53 self.setContextMenuPolicy(
54 Qt.ContextMenuPolicy.CustomContextMenu)
55 self.customContextMenuRequested.connect(
56 self.__showContextMenu)
57
58 self.__bookmarks = []
59 self.__loadBookmarks()
60
61 self.itemDoubleClicked.connect(self.__bookmarkActivated)
62
63 @pyqtSlot(QPoint)
64 def __showContextMenu(self, point):
65 """
66 Private slot to handle the customContextMenuRequested signal of
67 the viewlist.
68
69 @param point position to open the menu at
70 @type QPoint
71 """
72 selectedItemsCount = len(self.selectedItems())
73 if selectedItemsCount == 0:
74 # background menu
75 self.__showBackgroundMenu(point)
76 elif selectedItemsCount == 1:
77 # single bookmark menu
78 self.__showBookmarkContextMenu(point)
79 else:
80 # multiple selected bookmarks
81 self.__showBookmarksContextMenu(point)
82
83 @pyqtSlot(QPoint)
84 def __showBackgroundMenu(self, point):
85 """
86 Private slot to show the background menu (i.e. no selection).
87
88 @param point position to open the menu at
89 @type QPoint
90 """
91 menu = QMenu()
92 openBookmarks = menu.addAction(self.tr("Open All Bookmarks"))
93 menu.addSeparator()
94 newBookmark = menu.addAction(self.tr("New Bookmark"))
95 addBookmark = menu.addAction(self.tr("Bookmark Page"))
96 menu.addSeparator()
97 deleteBookmarks = menu.addAction(self.tr("Delete All Bookmark"))
98
99 act = menu.exec(self.mapToGlobal(point))
100 if act == openBookmarks:
101 self.__openBookmarks(selected=False)
102 elif act == newBookmark:
103 self.__newBookmark()
104 elif act == addBookmark:
105 self.__bookmarkCurrentPage()
106 elif act == deleteBookmarks:
107 self.__deleteBookmarks([
108 self.item(row) for row in range(self.count())
109 ])
110
111 @pyqtSlot(QPoint)
112 def __showBookmarkContextMenu(self, point):
113 """
114 Private slot to show the context menu for a bookmark.
115
116 @param point position to open the menu at
117 @type QPoint
118 """
119 itm = self.selectedItems()[0]
120 url = itm.data(self.UrlRole)
121 validUrl = (
122 url is not None and not url.isEmpty() and url.isValid()
123 )
124
125 menu = QMenu()
126 curPage = menu.addAction(self.tr("Open Link"))
127 curPage.setEnabled(validUrl)
128 newPage = menu.addAction(self.tr("Open Link in New Page"))
129 newPage.setEnabled(validUrl)
130 newBackgroundPage = menu.addAction(
131 self.tr("Open Link in Background Page"))
132 newBackgroundPage.setEnabled(validUrl)
133 menu.addSeparator()
134 copyUrl = menu.addAction(self.tr("Copy URL to Clipboard"))
135 copyUrl.setEnabled(validUrl)
136 menu.addSeparator()
137 newBookmark = menu.addAction(self.tr("New Bookmark"))
138 addBookmark = menu.addAction(self.tr("Bookmark Page"))
139 menu.addSeparator()
140 editBookmark = menu.addAction(self.tr("Edit Bookmark"))
141 menu.addSeparator()
142 deleteBookmark = menu.addAction(self.tr("Delete Bookmark"))
143
144 act = menu.exec(self.mapToGlobal(point))
145 if act == curPage:
146 self.openUrl.emit(url)
147 elif act == newPage:
148 self.newTab.emit(url)
149 elif act == newBackgroundPage:
150 self.newBackgroundTab.emit(url)
151 elif act == copyUrl:
152 # copy the URL to both clipboard areas
153 QGuiApplication.clipboard().setText(
154 url.toString(), QClipboard.Mode.Clipboard)
155 QGuiApplication.clipboard().setText(
156 url.toString(), QClipboard.Mode.Selection)
157 elif act == newBookmark:
158 self.__newBookmark()
159 elif act == addBookmark:
160 self.__bookmarkCurrentPage()
161 elif act == editBookmark:
162 self.__editBookmark(itm)
163 elif act == deleteBookmark:
164 self.__deleteBookmarks([itm])
165
166 @pyqtSlot(QPoint)
167 def __showBookmarksContextMenu(self, point):
168 """
169 Private slot to show the context menu for multiple bookmark.
170
171 @param point position to open the menu at
172 @type QPoint
173 """
174 menu = QMenu()
175 openBookmarks = menu.addAction(self.tr("Open Selected Bookmarks"))
176 menu.addSeparator()
177 deleteBookmarks = menu.addAction(self.tr("Delete Selected Bookmarks"))
178
179 act = menu.exec(self.mapToGlobal(point))
180 if act == openBookmarks:
181 self.__openBookmarks(selected=True)
182 elif act == deleteBookmarks:
183 self.__deleteBookmarks(self.selectedItems())
184
185 @pyqtSlot(str, str)
186 def __addBookmark(self, title, url):
187 """
188 Private slot to add a bookmark entry.
189
190 @param title title for the bookmark
191 @type str
192 @param url URL for the bookmark
193 @type str
194 """
195 url = url.strip()
196
197 itm = QListWidgetItem(title, self)
198 itm.setData(self.UrlRole, QUrl(url))
199 itm.setToolTip(url)
200
201 @pyqtSlot(str, QUrl)
202 def addBookmark(self, title, url):
203 """
204 Public slot to add a bookmark with given data.
205
206 @param title title for the bookmark
207 @type str
208 @param url URL for the bookmark
209 @type QUrl
210 """
211 dlg = HelpBookmarkPropertiesDialog(title, url.toString(), self)
212 if dlg.exec() == QDialog.DialogCode.Accepted:
213 title, url = dlg.getData()
214 self.__addBookmark(title, url)
215 self.sortItems()
216 self.__saveBookmarks()
217
218 @pyqtSlot()
219 def __bookmarkCurrentPage(self):
220 """
221 Private slot to bookmark the current page.
222 """
223 currentViewer = self.__helpViewer.currentViewer()
224 title = currentViewer.pageTitle()
225 url = currentViewer.link()
226 self.addBookmark(title, url)
227
228 @pyqtSlot()
229 def __newBookmark(self):
230 """
231 Private slot to create a new bookmark.
232 """
233 dlg = HelpBookmarkPropertiesDialog(parent=self)
234 if dlg.exec() == QDialog.DialogCode.Accepted:
235 title, url = dlg.getData()
236 self.__addBookmark(title, url)
237 self.sortItems()
238 self.__saveBookmarks()
239
240 @pyqtSlot()
241 def __editBookmark(self, itm):
242 """
243 Private slot to edit a bookmark.
244
245 @param itm reference to the bookmark item to be edited
246 @type QListWidgetItem
247 """
248 dlg = HelpBookmarkPropertiesDialog(
249 itm.text(), itm.data(self.UrlRole).toString(), self)
250 if dlg.exec() == QDialog.DialogCode.Accepted:
251 title, url = dlg.getData()
252 itm.setText(title)
253 itm.setData(self.UrlRole, QUrl(url))
254 itm.setToolTip(url)
255 self.sortItems()
256 self.__saveBookmarks()
257
258 @pyqtSlot(QListWidgetItem)
259 def __bookmarkActivated(self, itm):
260 """
261 Private slot handling the activation of a bookmark.
262
263 @param itm reference to the activated item
264 @type QListWidgetItem
265 """
266 url = itm.data(self.UrlRole)
267 if url and not url.isEmpty() and url.isValid():
268 buttons = QApplication.mouseButtons()
269 modifiers = QApplication.keyboardModifiers()
270
271 if buttons & Qt.MouseButton.MiddleButton:
272 self.newTab.emit(url)
273 else:
274 if (
275 modifiers & (
276 Qt.KeyboardModifier.ControlModifier |
277 Qt.KeyboardModifier.ShiftModifier
278 ) == (
279 Qt.KeyboardModifier.ControlModifier |
280 Qt.KeyboardModifier.ShiftModifier
281 )
282 ):
283 self.newBackgroundTab.emit(url)
284 elif modifiers & Qt.KeyboardModifier.ControlModifier:
285 self.newTab.emit(url)
286 elif (
287 modifiers & Qt.KeyboardModifier.ShiftModifier and
288 not self.__internal
289 ):
290 self.newWindow.emit(url)
291 else:
292 self.openUrl.emit(url)
293
294 def __openBookmarks(self, selected=False):
295 """
296 Private method to open all or selected bookmarks.
297
298 @param selected flag indicating to open the selected bookmarks
299 (defaults to False)
300 @type bool (optional)
301 """
302 items = (
303 self.selectedItems()
304 if selected else
305 [self.item(row) for row in range(self.count())]
306 )
307
308 for itm in items:
309 url = itm.data(self.UrlRole)
310 if url is not None and not url.isEmpty() and url.isValid():
311 self.newTab.emit(url)
312
313 def __deleteBookmarks(self, items):
314 """
315 Private method to delete the given bookmark items.
316
317 @param items list of bookmarks to be deleted
318 @type list of QListWidgetItem
319 """
320 from UI.DeleteFilesConfirmationDialog import (
321 DeleteFilesConfirmationDialog
322 )
323 dlg = DeleteFilesConfirmationDialog(
324 self,
325 self.tr("Delete Bookmarks"),
326 self.tr("Shall these bookmarks really be deleted?"),
327 [itm.text() for itm in items]
328 )
329 if dlg.exec() == QDialog.DialogCode.Accepted:
330 for itm in items:
331 self.takeItem(self.row(itm))
332 del itm
333 self.__saveBookmarks()
334
335 def __loadBookmarks(self):
336 """
337 Private method to load the defined bookmarks.
338 """
339 bookmarksStr = Preferences.getHelp("Bookmarks")
340 with contextlib.suppress(ValueError):
341 bookmarks = json.loads(bookmarksStr)
342
343 self.clear()
344 for bookmark in bookmarks:
345 self.__addBookmark(bookmark["title"], bookmark["url"])
346 self.sortItems()
347
348 def __saveBookmarks(self):
349 """
350 Private method to save the defined bookmarks.
351 """
352 bookmarks = []
353 for row in range(self.count()):
354 itm = self.item(row)
355 bookmarks.append({
356 "title": itm.text(),
357 "url": itm.data(self.UrlRole).toString(),
358 })
359 Preferences.setHelp("Bookmarks", json.dumps(bookmarks))
360
361 def __exportBookmarks(self):
362 """
363 Private method to export the bookmarks into a JSON file.
364 """
365 # TODO: not yet implemented
366
367 def __importBookmarks(self):
368 """
369 Private method to import bookmarks from a JSON file.
370 """
371 # TODO: not yet implemented
372 # 1. read file
373 # 2. check, if exported from eric and compatible format version
374 # 3. process each entry

eric ide

mercurial