1 # -*- coding: utf-8 -*- |
1 # -*- coding: utf-8 -*- |
2 |
2 |
3 # Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
3 # Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
4 # |
4 # |
5 |
5 |
|
6 """ |
|
7 Module implementing a widget showing the list of bookmarks. |
|
8 """ |
|
9 |
6 import contextlib |
10 import contextlib |
|
11 import datetime |
7 import json |
12 import json |
|
13 import os |
8 |
14 |
9 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QPoint, QUrl |
15 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QPoint, QUrl |
10 from PyQt6.QtGui import QClipboard, QGuiApplication |
16 from PyQt6.QtGui import QClipboard, QGuiApplication |
11 from PyQt6.QtWidgets import ( |
17 from PyQt6.QtWidgets import ( |
12 QAbstractItemView, QApplication, QDialog, QListWidget, QListWidgetItem, QMenu |
18 QAbstractItemView, QApplication, QDialog, QListWidget, QListWidgetItem, |
|
19 QMenu |
13 ) |
20 ) |
14 |
21 |
|
22 from EricWidgets import EricFileDialog, EricMessageBox |
|
23 |
15 import Preferences |
24 import Preferences |
16 |
25 |
17 from .HelpBookmarkPropertiesDialog import HelpBookmarkPropertiesDialog |
26 from .HelpBookmarkPropertiesDialog import HelpBookmarkPropertiesDialog |
|
27 |
18 |
28 |
19 class HelpBookmarksWidget(QListWidget): |
29 class HelpBookmarksWidget(QListWidget): |
20 """ |
30 """ |
21 Class implementing a widget showing the list of bookmarks. |
31 Class implementing a widget showing the list of bookmarks. |
22 |
32 |
92 openBookmarks = menu.addAction(self.tr("Open All Bookmarks")) |
102 openBookmarks = menu.addAction(self.tr("Open All Bookmarks")) |
93 menu.addSeparator() |
103 menu.addSeparator() |
94 newBookmark = menu.addAction(self.tr("New Bookmark")) |
104 newBookmark = menu.addAction(self.tr("New Bookmark")) |
95 addBookmark = menu.addAction(self.tr("Bookmark Page")) |
105 addBookmark = menu.addAction(self.tr("Bookmark Page")) |
96 menu.addSeparator() |
106 menu.addSeparator() |
97 deleteBookmarks = menu.addAction(self.tr("Delete All Bookmark")) |
107 deleteBookmarks = menu.addAction(self.tr("Delete All Bookmarks")) |
|
108 menu.addSeparator() |
|
109 exportBookmarks = menu.addAction(self.tr("Export All Bookmarks")) |
|
110 importBookmarks = menu.addAction(self.tr("Import Bookmarks")) |
98 |
111 |
99 act = menu.exec(self.mapToGlobal(point)) |
112 act = menu.exec(self.mapToGlobal(point)) |
100 if act == openBookmarks: |
113 if act == openBookmarks: |
101 self.__openBookmarks(selected=False) |
114 self.__openBookmarks(selected=False) |
102 elif act == newBookmark: |
115 elif act == newBookmark: |
160 self.__bookmarkCurrentPage() |
180 self.__bookmarkCurrentPage() |
161 elif act == editBookmark: |
181 elif act == editBookmark: |
162 self.__editBookmark(itm) |
182 self.__editBookmark(itm) |
163 elif act == deleteBookmark: |
183 elif act == deleteBookmark: |
164 self.__deleteBookmarks([itm]) |
184 self.__deleteBookmarks([itm]) |
|
185 elif act == exportBookmarks: |
|
186 self.__exportBookmarks(selected=False) |
|
187 elif act == importBookmarks: |
|
188 self.__importBookmarks() |
165 |
189 |
166 @pyqtSlot(QPoint) |
190 @pyqtSlot(QPoint) |
167 def __showBookmarksContextMenu(self, point): |
191 def __showBookmarksContextMenu(self, point): |
168 """ |
192 """ |
169 Private slot to show the context menu for multiple bookmark. |
193 Private slot to show the context menu for multiple bookmark. |
173 """ |
197 """ |
174 menu = QMenu() |
198 menu = QMenu() |
175 openBookmarks = menu.addAction(self.tr("Open Selected Bookmarks")) |
199 openBookmarks = menu.addAction(self.tr("Open Selected Bookmarks")) |
176 menu.addSeparator() |
200 menu.addSeparator() |
177 deleteBookmarks = menu.addAction(self.tr("Delete Selected Bookmarks")) |
201 deleteBookmarks = menu.addAction(self.tr("Delete Selected Bookmarks")) |
|
202 menu.addSeparator() |
|
203 exportBookmarks = menu.addAction(self.tr("Export Selected Bookmarks")) |
|
204 exportAllBookmarks = menu.addAction(self.tr("Export All Bookmarks")) |
|
205 importBookmarks = menu.addAction(self.tr("Import Bookmarks")) |
178 |
206 |
179 act = menu.exec(self.mapToGlobal(point)) |
207 act = menu.exec(self.mapToGlobal(point)) |
180 if act == openBookmarks: |
208 if act == openBookmarks: |
181 self.__openBookmarks(selected=True) |
209 self.__openBookmarks(selected=True) |
182 elif act == deleteBookmarks: |
210 elif act == deleteBookmarks: |
183 self.__deleteBookmarks(self.selectedItems()) |
211 self.__deleteBookmarks(self.selectedItems()) |
|
212 elif act == exportBookmarks: |
|
213 self.__exportBookmarks(selected=True) |
|
214 elif act == exportAllBookmarks: |
|
215 self.__exportBookmarks(selected=False) |
|
216 elif act == importBookmarks: |
|
217 self.__importBookmarks() |
184 |
218 |
185 @pyqtSlot(str, str) |
219 @pyqtSlot(str, str) |
186 def __addBookmark(self, title, url): |
220 def __addBookmark(self, title, url): |
187 """ |
221 """ |
188 Private slot to add a bookmark entry. |
222 Private slot to add a bookmark entry. |
356 "title": itm.text(), |
390 "title": itm.text(), |
357 "url": itm.data(self.UrlRole).toString(), |
391 "url": itm.data(self.UrlRole).toString(), |
358 }) |
392 }) |
359 Preferences.setHelp("Bookmarks", json.dumps(bookmarks)) |
393 Preferences.setHelp("Bookmarks", json.dumps(bookmarks)) |
360 |
394 |
361 def __exportBookmarks(self): |
395 @pyqtSlot() |
362 """ |
396 def __exportBookmarks(self, selected=False): |
363 Private method to export the bookmarks into a JSON file. |
397 """ |
364 """ |
398 Private slot to export the bookmarks into a JSON file. |
365 # TODO: not yet implemented |
399 |
366 |
400 @param selected flag indicating to export the selected bookmarks |
|
401 (defaults to False) |
|
402 @type bool (optional) |
|
403 """ |
|
404 filename, selectedFilter = EricFileDialog.getSaveFileNameAndFilter( |
|
405 self, |
|
406 self.tr("Export Bookmarks"), |
|
407 "", |
|
408 self.tr("eric Bookmarks Files (*.json);;All Files (*)"), |
|
409 None, |
|
410 EricFileDialog.DontConfirmOverwrite |
|
411 ) |
|
412 if filename: |
|
413 ext = os.path.splitext(filename)[1] |
|
414 if not ext: |
|
415 ex = selectedFilter.split("(*")[1].split(")")[0] |
|
416 if ex: |
|
417 filename += ex |
|
418 |
|
419 if os.path.exists(filename): |
|
420 ok = EricMessageBox.yesNo( |
|
421 self, |
|
422 self.tr("Export Bookmarks"), |
|
423 self.tr("""The file <b>{0}</b> already exists. Do you""" |
|
424 """ want to overwrite it?""").format(filename)) |
|
425 if not ok: |
|
426 return |
|
427 |
|
428 bookmarksDict = { |
|
429 "creator": "eric7", |
|
430 "version": 1, |
|
431 "created": datetime.datetime.now().isoformat( |
|
432 sep=" ", timespec="seconds"), |
|
433 "bookmarks": [] |
|
434 } |
|
435 bookmarkItems = ( |
|
436 self.selectedItems() |
|
437 if selected else |
|
438 [self.item(row) for row in range(self.count())] |
|
439 ) |
|
440 for bookmarkItem in bookmarkItems: |
|
441 bookmarksDict["bookmarks"].append({ |
|
442 "type": "url", |
|
443 "title": bookmarkItem.text(), |
|
444 "url": bookmarkItem.data(self.UrlRole).toString(), |
|
445 }) |
|
446 |
|
447 jsonStr = json.dumps(bookmarksDict, indent=2, sort_keys=True) |
|
448 try: |
|
449 with open(filename, "w") as f: |
|
450 f.write(jsonStr) |
|
451 except OSError as err: |
|
452 EricMessageBox.critical( |
|
453 self, |
|
454 self.tr("Export Bookmarks"), |
|
455 self.tr("""<p>The bookmarks could not be exported""" |
|
456 """ to <b>{0}</b>.</p><p>Reason: {1}</p>""") |
|
457 .format(filename, str(err))) |
|
458 |
|
459 @pyqtSlot() |
367 def __importBookmarks(self): |
460 def __importBookmarks(self): |
368 """ |
461 """ |
369 Private method to import bookmarks from a JSON file. |
462 Private slot to import bookmarks from a JSON file. |
370 """ |
463 """ |
371 # TODO: not yet implemented |
464 from .HelpBookmarksImportDialog import HelpBookmarksImportDialog |
372 # 1. read file |
465 |
373 # 2. check, if exported from eric and compatible format version |
466 dlg = HelpBookmarksImportDialog(self) |
374 # 3. process each entry |
467 if dlg.exec() == QDialog.DialogCode.Accepted: |
|
468 replace, filename = dlg.getData() |
|
469 |
|
470 try: |
|
471 with open(filename, "r") as f: |
|
472 jsonStr = f.read() |
|
473 bookmarks = json.loads(jsonStr) |
|
474 except (OSError, json.JSONDecodeError) as err: |
|
475 EricMessageBox.critical( |
|
476 self, |
|
477 self.tr("Import Bookmarks"), |
|
478 self.tr( |
|
479 "<p>The bookmarks file <b>{0}</b> could not be " |
|
480 "read.</p><p>Reason: {1}</p>" |
|
481 ).format(filename, str(err)) |
|
482 ) |
|
483 return |
|
484 |
|
485 if not isinstance(bookmarks, dict): |
|
486 EricMessageBox.critical( |
|
487 self, |
|
488 self.tr("Import Bookmarks"), |
|
489 self.tr( |
|
490 "The bookmarks file <b>{0}</b> has invalid contents." |
|
491 ).format(filename) |
|
492 ) |
|
493 return |
|
494 |
|
495 try: |
|
496 if bookmarks["creator"] != "eric7": |
|
497 EricMessageBox.critical( |
|
498 self, |
|
499 self.tr("Import Bookmarks"), |
|
500 self.tr( |
|
501 "The bookmarks file <b>{0}</b> was not created" |
|
502 " with 'eric7'." |
|
503 ).format(filename) |
|
504 ) |
|
505 return |
|
506 |
|
507 if bookmarks["version"] != 1: |
|
508 EricMessageBox.critical( |
|
509 self, |
|
510 self.tr("Import Bookmarks"), |
|
511 self.tr( |
|
512 "The bookmarks file <b>{0}</b> has an unsupported" |
|
513 " format version." |
|
514 ).format(filename) |
|
515 ) |
|
516 return |
|
517 |
|
518 if replace: |
|
519 self.clear() |
|
520 |
|
521 for bookmark in bookmarks["bookmarks"]: |
|
522 if bookmark["type"] == "url": |
|
523 self.__addBookmark(bookmark["title"], bookmark["url"]) |
|
524 self.sortItems() |
|
525 self.__saveBookmarks() |
|
526 |
|
527 except KeyError: |
|
528 EricMessageBox.critical( |
|
529 self, |
|
530 self.tr("Import Bookmarks"), |
|
531 self.tr( |
|
532 "The bookmarks file <b>{0}</b> has invalid contents." |
|
533 ).format(filename) |
|
534 ) |