--- a/src/eric7/WebBrowser/History/HistoryMenu.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/WebBrowser/History/HistoryMenu.py Wed Jul 13 14:55:47 2022 +0200 @@ -11,8 +11,13 @@ import functools from PyQt6.QtCore import ( - pyqtSignal, Qt, QMimeData, QUrl, QModelIndex, QSortFilterProxyModel, - QAbstractProxyModel + pyqtSignal, + Qt, + QMimeData, + QUrl, + QModelIndex, + QSortFilterProxyModel, + QAbstractProxyModel, ) from PyQt6.QtWidgets import QMenu @@ -27,129 +32,128 @@ class HistoryMenuModel(QAbstractProxyModel): """ Class implementing a model for the history menu. - + It maps the first bunch of items of the source model to the root. """ + MOVEDROWS = 15 - + def __init__(self, sourceModel, parent=None): """ Constructor - + @param sourceModel reference to the source model (QAbstractItemModel) @param parent reference to the parent object (QObject) """ super().__init__(parent) - + self.__treeModel = sourceModel - + self.setSourceModel(sourceModel) - + def bumpedRows(self): """ Public method to determine the number of rows moved to the root. - + @return number of rows moved to the root (integer) """ first = self.__treeModel.index(0, 0) if not first.isValid(): return 0 return min(self.__treeModel.rowCount(first), self.MOVEDROWS) - + def columnCount(self, parent=None): """ Public method to get the number of columns. - + @param parent index of parent (QModelIndex) @return number of columns (integer) """ if parent is None: parent = QModelIndex() - + return self.__treeModel.columnCount(self.mapToSource(parent)) - + def rowCount(self, parent=None): """ Public method to determine the number of rows. - + @param parent index of parent (QModelIndex) @return number of rows (integer) """ if parent is None: parent = QModelIndex() - + if parent.column() > 0: return 0 - + if not parent.isValid(): folders = self.sourceModel().rowCount() bumpedItems = self.bumpedRows() if ( - bumpedItems <= self.MOVEDROWS and - bumpedItems == self.sourceModel().rowCount( - self.sourceModel().index(0, 0)) + bumpedItems <= self.MOVEDROWS + and bumpedItems + == self.sourceModel().rowCount(self.sourceModel().index(0, 0)) ): folders -= 1 return bumpedItems + folders - - if ( - parent.internalId() == sys.maxsize and - parent.row() < self.bumpedRows() - ): + + if parent.internalId() == sys.maxsize and parent.row() < self.bumpedRows(): return 0 - + idx = self.mapToSource(parent) defaultCount = self.sourceModel().rowCount(idx) if idx == self.sourceModel().index(0, 0): return defaultCount - self.bumpedRows() - + return defaultCount - + def mapFromSource(self, sourceIndex): """ Public method to map an index to the proxy model index. - + @param sourceIndex reference to a source model index (QModelIndex) @return proxy model index (QModelIndex) """ sourceRow = self.__treeModel.mapToSource(sourceIndex).row() - return self.createIndex( - sourceIndex.row(), sourceIndex.column(), sourceRow) - + return self.createIndex(sourceIndex.row(), sourceIndex.column(), sourceRow) + def mapToSource(self, proxyIndex): """ Public method to map an index to the source model index. - + @param proxyIndex reference to a proxy model index (QModelIndex) @return source model index (QModelIndex) """ if not proxyIndex.isValid(): return QModelIndex() - + if proxyIndex.internalId() == sys.maxsize: bumpedItems = self.bumpedRows() if proxyIndex.row() < bumpedItems: return self.__treeModel.index( - proxyIndex.row(), proxyIndex.column(), - self.__treeModel.index(0, 0)) + proxyIndex.row(), proxyIndex.column(), self.__treeModel.index(0, 0) + ) if ( - bumpedItems <= self.MOVEDROWS and - bumpedItems == self.sourceModel().rowCount( - self.__treeModel.index(0, 0)) + bumpedItems <= self.MOVEDROWS + and bumpedItems + == self.sourceModel().rowCount(self.__treeModel.index(0, 0)) ): bumpedItems -= 1 - return self.__treeModel.index(proxyIndex.row() - bumpedItems, - proxyIndex.column()) - + return self.__treeModel.index( + proxyIndex.row() - bumpedItems, proxyIndex.column() + ) + historyIndex = self.__treeModel.sourceModel().index( - proxyIndex.internalId(), proxyIndex.column()) + proxyIndex.internalId(), proxyIndex.column() + ) treeIndex = self.__treeModel.mapFromSource(historyIndex) return treeIndex - + def index(self, row, column, parent=None): """ Public method to create an index. - + @param row row number for the index (integer) @param column column number for the index (integer) @param parent index of the parent item (QModelIndex) @@ -157,25 +161,24 @@ """ if parent is None: parent = QModelIndex() - + if ( - row < 0 or - column < 0 or - column >= self.columnCount(parent) or - parent.column() > 0 + row < 0 + or column < 0 + or column >= self.columnCount(parent) + or parent.column() > 0 ): return QModelIndex() - + if not parent.isValid(): return self.createIndex(row, column, sys.maxsize) - + treeIndexParent = self.mapToSource(parent) - + bumpedItems = 0 if treeIndexParent == self.sourceModel().index(0, 0): bumpedItems = self.bumpedRows() - treeIndex = self.__treeModel.index( - row + bumpedItems, column, treeIndexParent) + treeIndex = self.__treeModel.index(row + bumpedItems, column, treeIndexParent) historyIndex = self.__treeModel.mapToSource(treeIndex) historyRow = historyIndex.row() if historyRow == -1: @@ -185,36 +188,33 @@ def parent(self, index): """ Public method to get the parent index. - + @param index index of item to get parent (QModelIndex) @return index of parent (QModelIndex) """ offset = index.internalId() if offset == sys.maxsize or not index.isValid(): return QModelIndex() - - historyIndex = self.__treeModel.sourceModel().index( - index.internalId(), 0) + + historyIndex = self.__treeModel.sourceModel().index(index.internalId(), 0) treeIndex = self.__treeModel.mapFromSource(historyIndex) treeIndexParent = treeIndex.parent() - + sourceRow = self.sourceModel().mapToSource(treeIndexParent).row() bumpedItems = self.bumpedRows() - if ( - bumpedItems <= self.MOVEDROWS and - bumpedItems == self.sourceModel().rowCount( - self.sourceModel().index(0, 0)) + if bumpedItems <= self.MOVEDROWS and bumpedItems == self.sourceModel().rowCount( + self.sourceModel().index(0, 0) ): bumpedItems -= 1 - - return self.createIndex(bumpedItems + treeIndexParent.row(), - treeIndexParent.column(), - sourceRow) - + + return self.createIndex( + bumpedItems + treeIndexParent.row(), treeIndexParent.column(), sourceRow + ) + def mimeData(self, indexes): """ Public method to return the mime data. - + @param indexes list of indexes (QModelIndexList) @return mime data (QMimeData) """ @@ -222,7 +222,7 @@ for index in indexes: url = index.data(HistoryModel.UrlRole) urls.append(url) - + mdata = QMimeData() mdata.setUrls(urls) return mdata @@ -232,48 +232,46 @@ """ Class implementing a model to show the most visited history entries. """ + def __init__(self, sourceModel, parent=None): """ Constructor - + @param sourceModel reference to the source model (QAbstractItemModel) @param parent reference to the parent object (QObject) """ super().__init__(parent) - + self.setDynamicSortFilter(True) self.setSourceModel(sourceModel) - + def lessThan(self, left, right): """ Public method used to sort the displayed items. - + @param left index of left item (QModelIndex) @param right index of right item (QModelIndex) @return true, if left is less than right (boolean) """ from .HistoryFilterModel import HistoryFilterModel - frequency_L = self.sourceModel().data( - left, HistoryFilterModel.FrequencyRole) - dateTime_L = self.sourceModel().data( - left, HistoryModel.DateTimeRole) - frequency_R = self.sourceModel().data( - right, HistoryFilterModel.FrequencyRole) - dateTime_R = self.sourceModel().data( - right, HistoryModel.DateTimeRole) - + + frequency_L = self.sourceModel().data(left, HistoryFilterModel.FrequencyRole) + dateTime_L = self.sourceModel().data(left, HistoryModel.DateTimeRole) + frequency_R = self.sourceModel().data(right, HistoryFilterModel.FrequencyRole) + dateTime_R = self.sourceModel().data(right, HistoryModel.DateTimeRole) + # Sort results in descending frequency-derived score. If frequencies # are equal, sort on most recently viewed if frequency_R == frequency_L: return dateTime_R < dateTime_L - + return frequency_R < frequency_L class HistoryMenu(EricModelMenu): """ Class implementing the history menu. - + @signal openUrl(QUrl, str) emitted to open a URL in the current tab @signal newTab(QUrl, str) emitted to open a URL in a new tab @signal newBackgroundTab(QUrl, str) emitted to open a URL in a new @@ -282,128 +280,132 @@ @signal newPrivateWindow(QUrl, str) emitted to open a URL in a new private window """ + openUrl = pyqtSignal(QUrl, str) newTab = pyqtSignal(QUrl, str) newBackgroundTab = pyqtSignal(QUrl, str) newWindow = pyqtSignal(QUrl, str) newPrivateWindow = pyqtSignal(QUrl, str) - + def __init__(self, parent=None, tabWidget=None): """ Constructor - + @param parent reference to the parent widget (QWidget) @param tabWidget reference to the tab widget managing the browser tabs (HelpTabWidget """ EricModelMenu.__init__(self, parent) - + self.__tabWidget = tabWidget self.__mw = parent - + self.__historyManager = None self.__historyMenuModel = None self.__initialActions = [] self.__mostVisitedMenu = None - + self.__closedTabsMenu = QMenu(self.tr("Closed Tabs")) - self.__closedTabsMenu.aboutToShow.connect( - self.__aboutToShowClosedTabsMenu) + self.__closedTabsMenu.aboutToShow.connect(self.__aboutToShowClosedTabsMenu) self.__tabWidget.closedTabsManager().closedTabAvailable.connect( - self.__closedTabAvailable) - + self.__closedTabAvailable + ) + self.setMaxRows(7) - + self.activated.connect(self.__activated) self.setStatusBarTextRole(HistoryModel.UrlStringRole) - + def __activated(self, idx): """ Private slot handling the activated signal. - + @param idx index of the activated item (QModelIndex) """ if self._keyboardModifiers & Qt.KeyboardModifier.ControlModifier: self.newTab.emit( - idx.data(HistoryModel.UrlRole), - idx.data(HistoryModel.TitleRole)) + idx.data(HistoryModel.UrlRole), idx.data(HistoryModel.TitleRole) + ) elif self._keyboardModifiers & Qt.KeyboardModifier.ShiftModifier: self.newWindow.emit( - idx.data(HistoryModel.UrlRole), - idx.data(HistoryModel.TitleRole)) + idx.data(HistoryModel.UrlRole), idx.data(HistoryModel.TitleRole) + ) else: self.openUrl.emit( - idx.data(HistoryModel.UrlRole), - idx.data(HistoryModel.TitleRole)) - + idx.data(HistoryModel.UrlRole), idx.data(HistoryModel.TitleRole) + ) + def prePopulated(self): """ Public method to add any actions before the tree. - + @return flag indicating if any actions were added (boolean) """ if self.__historyManager is None: from WebBrowser.WebBrowserWindow import WebBrowserWindow + self.__historyManager = WebBrowserWindow.historyManager() self.__historyMenuModel = HistoryMenuModel( - self.__historyManager.historyTreeModel(), self) + self.__historyManager.historyTreeModel(), self + ) self.setModel(self.__historyMenuModel) - + # initial actions for act in self.__initialActions: self.addAction(act) if len(self.__initialActions) != 0: self.addSeparator() self.setFirstSeparator(self.__historyMenuModel.bumpedRows()) - + return False - + def postPopulated(self): """ Public method to add any actions after the tree. """ if len(self.__historyManager.history()) > 0: self.addSeparator() - + if self.__mostVisitedMenu is None: self.__mostVisitedMenu = HistoryMostVisitedMenu(10, self) self.__mostVisitedMenu.setTitle(self.tr("Most Visited")) self.__mostVisitedMenu.openUrl.connect(self.openUrl) self.__mostVisitedMenu.newTab.connect(self.newTab) - self.__mostVisitedMenu.newBackgroundTab.connect( - self.newBackgroundTab) + self.__mostVisitedMenu.newBackgroundTab.connect(self.newBackgroundTab) self.__mostVisitedMenu.newWindow.connect(self.newWindow) - self.__mostVisitedMenu.newPrivateWindow.connect( - self.newPrivateWindow) + self.__mostVisitedMenu.newPrivateWindow.connect(self.newPrivateWindow) self.addMenu(self.__mostVisitedMenu) act = self.addMenu(self.__closedTabsMenu) act.setIcon(UI.PixmapCache.getIcon("trash")) act.setEnabled(self.__tabWidget.canRestoreClosedTab()) self.addSeparator() - - act = self.addAction(UI.PixmapCache.getIcon("history"), - self.tr("Show All History...")) + + act = self.addAction( + UI.PixmapCache.getIcon("history"), self.tr("Show All History...") + ) act.triggered.connect(self.showHistoryDialog) - act = self.addAction(UI.PixmapCache.getIcon("historyClear"), - self.tr("Clear History...")) + act = self.addAction( + UI.PixmapCache.getIcon("historyClear"), self.tr("Clear History...") + ) act.triggered.connect(self.__clearHistoryDialog) - + def setInitialActions(self, actions): """ Public method to set the list of actions that should appear first in the menu. - + @param actions list of initial actions (list of QAction) """ self.__initialActions = actions[:] for act in self.__initialActions: self.addAction(act) - + def showHistoryDialog(self): """ Public slot to show the history dialog. """ from .HistoryDialog import HistoryDialog + dlg = HistoryDialog(self.__mw) dlg.openUrl.connect(self.openUrl) dlg.newTab.connect(self.newTab) @@ -411,53 +413,55 @@ dlg.newWindow.connect(self.newWindow) dlg.newPrivateWindow.connect(self.newPrivateWindow) dlg.show() - + def __clearHistoryDialog(self): """ Private slot to clear the history. """ if self.__historyManager is not None and EricMessageBox.yesNo( - self, - self.tr("Clear History"), - self.tr("""Do you want to clear the history?""")): + self, + self.tr("Clear History"), + self.tr("""Do you want to clear the history?"""), + ): self.__historyManager.clear() self.__tabWidget.clearClosedTabsList() - + def __aboutToShowClosedTabsMenu(self): """ Private slot to populate the closed tabs menu. """ fm = self.__closedTabsMenu.fontMetrics() try: - maxWidth = fm.horizontalAdvance('m') * 40 + maxWidth = fm.horizontalAdvance("m") * 40 except AttributeError: - maxWidth = fm.width('m') * 40 - + maxWidth = fm.width("m") * 40 + import WebBrowser.WebBrowserWindow + self.__closedTabsMenu.clear() for index, tab in enumerate( self.__tabWidget.closedTabsManager().allClosedTabs() ): - title = fm.elidedText(tab.title, Qt.TextElideMode.ElideRight, - maxWidth) + title = fm.elidedText(tab.title, Qt.TextElideMode.ElideRight, maxWidth) act = self.__closedTabsMenu.addAction( - WebBrowser.WebBrowserWindow.WebBrowserWindow.icon(tab.url), - title) + WebBrowser.WebBrowserWindow.WebBrowserWindow.icon(tab.url), title + ) act.setData(index) act.triggered.connect( - functools.partial(self.__tabWidget.restoreClosedTab, act)) + functools.partial(self.__tabWidget.restoreClosedTab, act) + ) self.__closedTabsMenu.addSeparator() self.__closedTabsMenu.addAction( - self.tr("Restore All Closed Tabs"), - self.__tabWidget.restoreAllClosedTabs) + self.tr("Restore All Closed Tabs"), self.__tabWidget.restoreAllClosedTabs + ) self.__closedTabsMenu.addAction( - self.tr("Clear List"), - self.__tabWidget.clearClosedTabsList) - + self.tr("Clear List"), self.__tabWidget.clearClosedTabsList + ) + def __closedTabAvailable(self, avail): """ Private slot to handle changes of the availability of closed tabs. - + @param avail flag indicating the availability of closed tabs (boolean) """ self.__closedTabsMenu.setEnabled(avail) @@ -466,7 +470,7 @@ class HistoryMostVisitedMenu(EricModelMenu): """ Class implementing the most visited history menu. - + @signal openUrl(QUrl, str) emitted to open a URL in the current tab @signal newTab(QUrl, str) emitted to open a URL in a new tab @signal newBackgroundTab(QUrl, str) emitted to open a URL in a new @@ -475,58 +479,61 @@ @signal newPrivateWindow(QUrl, str) emitted to open a URL in a new private window """ + openUrl = pyqtSignal(QUrl, str) newTab = pyqtSignal(QUrl, str) newBackgroundTab = pyqtSignal(QUrl, str) newWindow = pyqtSignal(QUrl, str) newPrivateWindow = pyqtSignal(QUrl, str) - + def __init__(self, count, parent=None): """ Constructor - + @param count maximum number of entries to be shown (integer) @param parent reference to the parent widget (QWidget) """ EricModelMenu.__init__(self, parent) - + self.__historyMenuModel = None - + self.setMaxRows(count + 1) - + self.setStatusBarTextRole(HistoryModel.UrlStringRole) - + def __activated(self, idx): """ Private slot handling the activated signal. - + @param idx index of the activated item (QModelIndex) """ if self._keyboardModifiers & Qt.KeyboardModifier.ControlModifier: self.newTab.emit( - idx.data(HistoryModel.UrlRole), - idx.data(HistoryModel.TitleRole)) + idx.data(HistoryModel.UrlRole), idx.data(HistoryModel.TitleRole) + ) elif self._keyboardModifiers & Qt.KeyboardModifier.ShiftModifier: self.newWindow.emit( - idx.data(HistoryModel.UrlRole), - idx.data(HistoryModel.TitleRole)) + idx.data(HistoryModel.UrlRole), idx.data(HistoryModel.TitleRole) + ) else: self.openUrl.emit( - idx.data(HistoryModel.UrlRole), - idx.data(HistoryModel.TitleRole)) - + idx.data(HistoryModel.UrlRole), idx.data(HistoryModel.TitleRole) + ) + def prePopulated(self): """ Public method to add any actions before the tree. - + @return flag indicating if any actions were added (boolean) """ if self.__historyMenuModel is None: from WebBrowser.WebBrowserWindow import WebBrowserWindow + historyManager = WebBrowserWindow.historyManager() self.__historyMenuModel = HistoryMostVisitedMenuModel( - historyManager.historyFilterModel(), self) + historyManager.historyFilterModel(), self + ) self.setModel(self.__historyMenuModel) self.__historyMenuModel.sort(0) - + return False