--- a/eric7/HelpViewer/HelpBookmarksWidget.py Wed Jan 05 11:53:28 2022 +0100 +++ b/eric7/HelpViewer/HelpBookmarksWidget.py Wed Jan 05 14:26:11 2022 +0100 @@ -3,19 +3,29 @@ # Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de> # +""" +Module implementing a widget showing the list of bookmarks. +""" + import contextlib +import datetime import json +import os from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QPoint, QUrl from PyQt6.QtGui import QClipboard, QGuiApplication from PyQt6.QtWidgets import ( - QAbstractItemView, QApplication, QDialog, QListWidget, QListWidgetItem, QMenu + QAbstractItemView, QApplication, QDialog, QListWidget, QListWidgetItem, + QMenu ) +from EricWidgets import EricFileDialog, EricMessageBox + import Preferences from .HelpBookmarkPropertiesDialog import HelpBookmarkPropertiesDialog + class HelpBookmarksWidget(QListWidget): """ Class implementing a widget showing the list of bookmarks. @@ -94,7 +104,10 @@ newBookmark = menu.addAction(self.tr("New Bookmark")) addBookmark = menu.addAction(self.tr("Bookmark Page")) menu.addSeparator() - deleteBookmarks = menu.addAction(self.tr("Delete All Bookmark")) + deleteBookmarks = menu.addAction(self.tr("Delete All Bookmarks")) + menu.addSeparator() + exportBookmarks = menu.addAction(self.tr("Export All Bookmarks")) + importBookmarks = menu.addAction(self.tr("Import Bookmarks")) act = menu.exec(self.mapToGlobal(point)) if act == openBookmarks: @@ -107,6 +120,10 @@ self.__deleteBookmarks([ self.item(row) for row in range(self.count()) ]) + elif act == exportBookmarks: + self.__exportBookmarks(selected=False) + elif act == importBookmarks: + self.__importBookmarks() @pyqtSlot(QPoint) def __showBookmarkContextMenu(self, point): @@ -140,6 +157,9 @@ editBookmark = menu.addAction(self.tr("Edit Bookmark")) menu.addSeparator() deleteBookmark = menu.addAction(self.tr("Delete Bookmark")) + menu.addSeparator() + exportBookmarks = menu.addAction(self.tr("Export All Bookmarks")) + importBookmarks = menu.addAction(self.tr("Import Bookmarks")) act = menu.exec(self.mapToGlobal(point)) if act == curPage: @@ -162,6 +182,10 @@ self.__editBookmark(itm) elif act == deleteBookmark: self.__deleteBookmarks([itm]) + elif act == exportBookmarks: + self.__exportBookmarks(selected=False) + elif act == importBookmarks: + self.__importBookmarks() @pyqtSlot(QPoint) def __showBookmarksContextMenu(self, point): @@ -175,12 +199,22 @@ openBookmarks = menu.addAction(self.tr("Open Selected Bookmarks")) menu.addSeparator() deleteBookmarks = menu.addAction(self.tr("Delete Selected Bookmarks")) + menu.addSeparator() + exportBookmarks = menu.addAction(self.tr("Export Selected Bookmarks")) + exportAllBookmarks = menu.addAction(self.tr("Export All Bookmarks")) + importBookmarks = menu.addAction(self.tr("Import Bookmarks")) act = menu.exec(self.mapToGlobal(point)) if act == openBookmarks: self.__openBookmarks(selected=True) elif act == deleteBookmarks: self.__deleteBookmarks(self.selectedItems()) + elif act == exportBookmarks: + self.__exportBookmarks(selected=True) + elif act == exportAllBookmarks: + self.__exportBookmarks(selected=False) + elif act == importBookmarks: + self.__importBookmarks() @pyqtSlot(str, str) def __addBookmark(self, title, url): @@ -358,17 +392,143 @@ }) Preferences.setHelp("Bookmarks", json.dumps(bookmarks)) - def __exportBookmarks(self): + @pyqtSlot() + def __exportBookmarks(self, selected=False): + """ + Private slot to export the bookmarks into a JSON file. + + @param selected flag indicating to export the selected bookmarks + (defaults to False) + @type bool (optional) """ - Private method to export the bookmarks into a JSON file. - """ - # TODO: not yet implemented + filename, selectedFilter = EricFileDialog.getSaveFileNameAndFilter( + self, + self.tr("Export Bookmarks"), + "", + self.tr("eric Bookmarks Files (*.json);;All Files (*)"), + None, + EricFileDialog.DontConfirmOverwrite + ) + if filename: + ext = os.path.splitext(filename)[1] + if not ext: + ex = selectedFilter.split("(*")[1].split(")")[0] + if ex: + filename += ex + + if os.path.exists(filename): + ok = EricMessageBox.yesNo( + self, + self.tr("Export Bookmarks"), + self.tr("""The file <b>{0}</b> already exists. Do you""" + """ want to overwrite it?""").format(filename)) + if not ok: + return + + bookmarksDict = { + "creator": "eric7", + "version": 1, + "created": datetime.datetime.now().isoformat( + sep=" ", timespec="seconds"), + "bookmarks": [] + } + bookmarkItems = ( + self.selectedItems() + if selected else + [self.item(row) for row in range(self.count())] + ) + for bookmarkItem in bookmarkItems: + bookmarksDict["bookmarks"].append({ + "type": "url", + "title": bookmarkItem.text(), + "url": bookmarkItem.data(self.UrlRole).toString(), + }) + + jsonStr = json.dumps(bookmarksDict, indent=2, sort_keys=True) + try: + with open(filename, "w") as f: + f.write(jsonStr) + except OSError as err: + EricMessageBox.critical( + self, + self.tr("Export Bookmarks"), + self.tr("""<p>The bookmarks could not be exported""" + """ to <b>{0}</b>.</p><p>Reason: {1}</p>""") + .format(filename, str(err))) + @pyqtSlot() def __importBookmarks(self): """ - Private method to import bookmarks from a JSON file. + Private slot to import bookmarks from a JSON file. """ - # TODO: not yet implemented - # 1. read file - # 2. check, if exported from eric and compatible format version - # 3. process each entry + from .HelpBookmarksImportDialog import HelpBookmarksImportDialog + + dlg = HelpBookmarksImportDialog(self) + if dlg.exec() == QDialog.DialogCode.Accepted: + replace, filename = dlg.getData() + + try: + with open(filename, "r") as f: + jsonStr = f.read() + bookmarks = json.loads(jsonStr) + except (OSError, json.JSONDecodeError) as err: + EricMessageBox.critical( + self, + self.tr("Import Bookmarks"), + self.tr( + "<p>The bookmarks file <b>{0}</b> could not be " + "read.</p><p>Reason: {1}</p>" + ).format(filename, str(err)) + ) + return + + if not isinstance(bookmarks, dict): + EricMessageBox.critical( + self, + self.tr("Import Bookmarks"), + self.tr( + "The bookmarks file <b>{0}</b> has invalid contents." + ).format(filename) + ) + return + + try: + if bookmarks["creator"] != "eric7": + EricMessageBox.critical( + self, + self.tr("Import Bookmarks"), + self.tr( + "The bookmarks file <b>{0}</b> was not created" + " with 'eric7'." + ).format(filename) + ) + return + + if bookmarks["version"] != 1: + EricMessageBox.critical( + self, + self.tr("Import Bookmarks"), + self.tr( + "The bookmarks file <b>{0}</b> has an unsupported" + " format version." + ).format(filename) + ) + return + + if replace: + self.clear() + + for bookmark in bookmarks["bookmarks"]: + if bookmark["type"] == "url": + self.__addBookmark(bookmark["title"], bookmark["url"]) + self.sortItems() + self.__saveBookmarks() + + except KeyError: + EricMessageBox.critical( + self, + self.tr("Import Bookmarks"), + self.tr( + "The bookmarks file <b>{0}</b> has invalid contents." + ).format(filename) + )