eric7/HelpViewer/HelpBookmarksWidget.py

branch
eric7
changeset 8900
9c153ce17d74
child 8902
ba9b8c6e4928
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric7/HelpViewer/HelpBookmarksWidget.py	Tue Jan 04 17:13:35 2022 +0100
@@ -0,0 +1,374 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+import contextlib
+import json
+
+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
+)
+
+import Preferences
+
+from .HelpBookmarkPropertiesDialog import HelpBookmarkPropertiesDialog
+
+class HelpBookmarksWidget(QListWidget):
+    """
+    Class implementing a widget showing the list of bookmarks.
+    
+    @signal escapePressed() emitted when the ESC key was pressed
+    @signal openUrl(QUrl, str) emitted to open an entry in the current tab
+    @signal newTab(QUrl, str) emitted to open an entry in a new tab
+    @signal newBackgroundTab(QUrl, str) emitted to open an entry in a
+        new background tab
+    """
+    escapePressed = pyqtSignal()
+    openUrl = pyqtSignal(QUrl)
+    newTab = pyqtSignal(QUrl)
+    newBackgroundTab = pyqtSignal(QUrl)
+    
+    UrlRole = Qt.ItemDataRole.UserRole + 1
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (defaults to None)
+        @type QWidget (optional)
+        """
+        super().__init__(parent)
+        self.setObjectName("HelpBookmarksWidget")
+        
+        self.__helpViewer = parent
+        
+        self.setAlternatingRowColors(True)
+        self.setSelectionMode(
+            QAbstractItemView.SelectionMode.ExtendedSelection)
+        self.setSortingEnabled(True)
+        
+        self.setContextMenuPolicy(
+            Qt.ContextMenuPolicy.CustomContextMenu)
+        self.customContextMenuRequested.connect(
+            self.__showContextMenu)
+        
+        self.__bookmarks = []
+        self.__loadBookmarks()
+        
+        self.itemDoubleClicked.connect(self.__bookmarkActivated)
+    
+    @pyqtSlot(QPoint)
+    def __showContextMenu(self, point):
+        """
+        Private slot to handle the customContextMenuRequested signal of
+        the viewlist.
+        
+        @param point position to open the menu at
+        @type QPoint
+        """
+        selectedItemsCount = len(self.selectedItems())
+        if selectedItemsCount == 0:
+            # background menu
+            self.__showBackgroundMenu(point)
+        elif selectedItemsCount == 1:
+            # single bookmark menu
+            self.__showBookmarkContextMenu(point)
+        else:
+            # multiple selected bookmarks
+            self.__showBookmarksContextMenu(point)
+    
+    @pyqtSlot(QPoint)
+    def __showBackgroundMenu(self, point):
+        """
+        Private slot to show the background menu (i.e. no selection).
+        
+        @param point position to open the menu at
+        @type QPoint
+        """
+        menu = QMenu()
+        openBookmarks = menu.addAction(self.tr("Open All Bookmarks"))
+        menu.addSeparator()
+        newBookmark = menu.addAction(self.tr("New Bookmark"))
+        addBookmark = menu.addAction(self.tr("Bookmark Page"))
+        menu.addSeparator()
+        deleteBookmarks = menu.addAction(self.tr("Delete All Bookmark"))
+        
+        act = menu.exec(self.mapToGlobal(point))
+        if act == openBookmarks:
+            self.__openBookmarks(selected=False)
+        elif act == newBookmark:
+            self.__newBookmark()
+        elif act == addBookmark:
+            self.__bookmarkCurrentPage()
+        elif act == deleteBookmarks:
+            self.__deleteBookmarks([
+                self.item(row) for row in range(self.count())
+            ])
+    
+    @pyqtSlot(QPoint)
+    def __showBookmarkContextMenu(self, point):
+        """
+        Private slot to show the context menu for a bookmark.
+        
+        @param point position to open the menu at
+        @type QPoint
+        """
+        itm = self.selectedItems()[0]
+        url = itm.data(self.UrlRole)
+        validUrl = (
+            url is not None and not url.isEmpty() and url.isValid()
+        )
+        
+        menu = QMenu()
+        curPage = menu.addAction(self.tr("Open Link"))
+        curPage.setEnabled(validUrl)
+        newPage = menu.addAction(self.tr("Open Link in New Page"))
+        newPage.setEnabled(validUrl)
+        newBackgroundPage = menu.addAction(
+            self.tr("Open Link in Background Page"))
+        newBackgroundPage.setEnabled(validUrl)
+        menu.addSeparator()
+        copyUrl = menu.addAction(self.tr("Copy URL to Clipboard"))
+        copyUrl.setEnabled(validUrl)
+        menu.addSeparator()
+        newBookmark = menu.addAction(self.tr("New Bookmark"))
+        addBookmark = menu.addAction(self.tr("Bookmark Page"))
+        menu.addSeparator()
+        editBookmark = menu.addAction(self.tr("Edit Bookmark"))
+        menu.addSeparator()
+        deleteBookmark = menu.addAction(self.tr("Delete Bookmark"))
+        
+        act = menu.exec(self.mapToGlobal(point))
+        if act == curPage:
+            self.openUrl.emit(url)
+        elif act == newPage:
+            self.newTab.emit(url)
+        elif act == newBackgroundPage:
+            self.newBackgroundTab.emit(url)
+        elif act == copyUrl:
+            # copy the URL to both clipboard areas
+            QGuiApplication.clipboard().setText(
+                url.toString(), QClipboard.Mode.Clipboard)
+            QGuiApplication.clipboard().setText(
+                url.toString(), QClipboard.Mode.Selection)
+        elif act == newBookmark:
+            self.__newBookmark()
+        elif act == addBookmark:
+            self.__bookmarkCurrentPage()
+        elif act == editBookmark:
+            self.__editBookmark(itm)
+        elif act == deleteBookmark:
+            self.__deleteBookmarks([itm])
+    
+    @pyqtSlot(QPoint)
+    def __showBookmarksContextMenu(self, point):
+        """
+        Private slot to show the context menu for multiple bookmark.
+        
+        @param point position to open the menu at
+        @type QPoint
+        """
+        menu = QMenu()
+        openBookmarks = menu.addAction(self.tr("Open Selected Bookmarks"))
+        menu.addSeparator()
+        deleteBookmarks = menu.addAction(self.tr("Delete Selected Bookmarks"))
+        
+        act = menu.exec(self.mapToGlobal(point))
+        if act == openBookmarks:
+            self.__openBookmarks(selected=True)
+        elif act == deleteBookmarks:
+            self.__deleteBookmarks(self.selectedItems())
+    
+    @pyqtSlot(str, str)
+    def __addBookmark(self, title, url):
+        """
+        Private slot to add a bookmark entry.
+        
+        @param title title for the bookmark
+        @type str
+        @param url URL for the bookmark
+        @type str
+        """
+        url = url.strip()
+        
+        itm = QListWidgetItem(title, self)
+        itm.setData(self.UrlRole, QUrl(url))
+        itm.setToolTip(url)
+    
+    @pyqtSlot(str, QUrl)
+    def addBookmark(self, title, url):
+        """
+        Public slot to add a bookmark with given data.
+        
+        @param title title for the bookmark
+        @type str
+        @param url URL for the bookmark
+        @type QUrl
+        """
+        dlg = HelpBookmarkPropertiesDialog(title, url.toString(), self)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            title, url = dlg.getData()
+            self.__addBookmark(title, url)
+            self.sortItems()
+            self.__saveBookmarks()
+    
+    @pyqtSlot()
+    def __bookmarkCurrentPage(self):
+        """
+        Private slot to bookmark the current page.
+        """
+        currentViewer = self.__helpViewer.currentViewer()
+        title = currentViewer.pageTitle()
+        url = currentViewer.link()
+        self.addBookmark(title, url)
+    
+    @pyqtSlot()
+    def __newBookmark(self):
+        """
+        Private slot to create a new bookmark.
+        """
+        dlg = HelpBookmarkPropertiesDialog(parent=self)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            title, url = dlg.getData()
+            self.__addBookmark(title, url)
+            self.sortItems()
+            self.__saveBookmarks()
+    
+    @pyqtSlot()
+    def __editBookmark(self, itm):
+        """
+        Private slot to edit a bookmark.
+        
+        @param itm reference to the bookmark item to be edited
+        @type QListWidgetItem
+        """
+        dlg = HelpBookmarkPropertiesDialog(
+            itm.text(), itm.data(self.UrlRole).toString(), self)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            title, url = dlg.getData()
+            itm.setText(title)
+            itm.setData(self.UrlRole, QUrl(url))
+            itm.setToolTip(url)
+            self.sortItems()
+            self.__saveBookmarks()
+    
+    @pyqtSlot(QListWidgetItem)
+    def __bookmarkActivated(self, itm):
+        """
+        Private slot handling the activation of a bookmark.
+        
+        @param itm reference to the activated item
+        @type QListWidgetItem
+        """
+        url = itm.data(self.UrlRole)
+        if url and not url.isEmpty() and url.isValid():
+            buttons = QApplication.mouseButtons()
+            modifiers = QApplication.keyboardModifiers()
+            
+            if buttons & Qt.MouseButton.MiddleButton:
+                self.newTab.emit(url)
+            else:
+                if (
+                    modifiers & (
+                        Qt.KeyboardModifier.ControlModifier |
+                        Qt.KeyboardModifier.ShiftModifier
+                    ) == (
+                        Qt.KeyboardModifier.ControlModifier |
+                        Qt.KeyboardModifier.ShiftModifier
+                    )
+                ):
+                    self.newBackgroundTab.emit(url)
+                elif modifiers & Qt.KeyboardModifier.ControlModifier:
+                    self.newTab.emit(url)
+                elif (
+                    modifiers & Qt.KeyboardModifier.ShiftModifier and
+                    not self.__internal
+                ):
+                    self.newWindow.emit(url)
+                else:
+                    self.openUrl.emit(url)
+    
+    def __openBookmarks(self, selected=False):
+        """
+        Private method to open all or selected bookmarks.
+        
+        @param selected flag indicating to open the selected bookmarks
+            (defaults to False)
+        @type bool (optional)
+        """
+        items = (
+            self.selectedItems()
+            if selected else
+            [self.item(row) for row in range(self.count())]
+        )
+        
+        for itm in items:
+            url = itm.data(self.UrlRole)
+            if url is not None and not url.isEmpty() and url.isValid():
+                self.newTab.emit(url)
+    
+    def __deleteBookmarks(self, items):
+        """
+        Private method to delete the given bookmark items.
+        
+        @param items list of bookmarks to be deleted
+        @type list of QListWidgetItem
+        """
+        from UI.DeleteFilesConfirmationDialog import (
+            DeleteFilesConfirmationDialog
+        )
+        dlg = DeleteFilesConfirmationDialog(
+            self,
+            self.tr("Delete Bookmarks"),
+            self.tr("Shall these bookmarks really be deleted?"),
+            [itm.text() for itm in items]
+        )
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            for itm in items:
+                self.takeItem(self.row(itm))
+                del itm
+            self.__saveBookmarks()
+    
+    def __loadBookmarks(self):
+        """
+        Private method to load the defined bookmarks.
+        """
+        bookmarksStr = Preferences.getHelp("Bookmarks")
+        with contextlib.suppress(ValueError):
+            bookmarks = json.loads(bookmarksStr)
+        
+        self.clear()
+        for bookmark in bookmarks:
+            self.__addBookmark(bookmark["title"], bookmark["url"])
+        self.sortItems()
+    
+    def __saveBookmarks(self):
+        """
+        Private method to save the defined bookmarks.
+        """
+        bookmarks = []
+        for row in range(self.count()):
+            itm = self.item(row)
+            bookmarks.append({
+                "title": itm.text(),
+                "url": itm.data(self.UrlRole).toString(),
+            })
+        Preferences.setHelp("Bookmarks", json.dumps(bookmarks))
+    
+    def __exportBookmarks(self):
+        """
+        Private method to export the bookmarks into a JSON file.
+        """
+        # TODO: not yet implemented
+    
+    def __importBookmarks(self):
+        """
+        Private method 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

eric ide

mercurial