eric7/EricWidgets/EricToolBarDialog.py

branch
eric7
changeset 8358
144a6b854f70
parent 8356
68ec9c3d4de5
child 8366
2a9f5153c438
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric7/EricWidgets/EricToolBarDialog.py	Sat May 22 19:58:24 2021 +0200
@@ -0,0 +1,525 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2008 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a toolbar configuration dialog.
+"""
+
+from PyQt6.QtCore import pyqtSlot, Qt
+from PyQt6.QtGui import QColor
+from PyQt6.QtWidgets import (
+    QDialog, QDialogButtonBox, QTreeWidgetItem, QInputDialog, QLineEdit,
+    QListWidgetItem, QAbstractButton
+)
+
+from EricWidgets import EricMessageBox
+
+from .Ui_EricToolBarDialog import Ui_EricToolBarDialog
+
+import UI.PixmapCache
+
+
+class EricToolBarItem:
+    """
+    Class storing data belonging to a toolbar entry of the toolbar dialog.
+    """
+    def __init__(self, toolBarId, actionIDs, default):
+        """
+        Constructor
+        
+        @param toolBarId id of the toolbar object (integer)
+        @param actionIDs list of action IDs belonging to the toolbar
+            (list of integer)
+        @param default flag indicating a default toolbar (boolean)
+        """
+        self.toolBarId = toolBarId
+        self.actionIDs = actionIDs[:]
+        self.isDefault = default
+        self.title = ""
+        self.isChanged = False
+    
+
+class EricToolBarDialog(QDialog, Ui_EricToolBarDialog):
+    """
+    Class implementing a toolbar configuration dialog.
+    """
+    ActionIdRole = Qt.ItemDataRole.UserRole
+    WidgetActionRole = Qt.ItemDataRole.UserRole + 1
+    
+    def __init__(self, toolBarManager, parent=None):
+        """
+        Constructor
+        
+        @param toolBarManager reference to a toolbar manager object
+            (EricToolBarManager)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super().__init__(parent)
+        self.setupUi(self)
+        
+        self.__manager = toolBarManager
+        self.__toolbarItems = {}
+        # maps toolbar item IDs to toolbar items
+        self.__currentToolBarItem = None
+        self.__removedToolBarIDs = []
+        # remember custom toolbars to be deleted
+        
+        self.__widgetActionToToolBarItemID = {}
+        # maps widget action IDs to toolbar item IDs
+        self.__toolBarItemToWidgetActionID = {}
+        # maps toolbar item IDs to widget action IDs
+        
+        self.upButton.setIcon(UI.PixmapCache.getIcon("1uparrow"))
+        self.downButton.setIcon(UI.PixmapCache.getIcon("1downarrow"))
+        self.leftButton.setIcon(UI.PixmapCache.getIcon("1leftarrow"))
+        self.rightButton.setIcon(UI.PixmapCache.getIcon("1rightarrow"))
+        
+        self.__restoreDefaultsButton = self.buttonBox.button(
+            QDialogButtonBox.StandardButton.RestoreDefaults)
+        self.__resetButton = self.buttonBox.button(
+            QDialogButtonBox.StandardButton.Reset)
+        
+        self.actionsTree.header().hide()
+        self.__separatorText = self.tr("--Separator--")
+        itm = QTreeWidgetItem(self.actionsTree, [self.__separatorText])
+        self.actionsTree.setCurrentItem(itm)
+        
+        for category in sorted(self.__manager.categories()):
+            categoryItem = QTreeWidgetItem(self.actionsTree, [category])
+            for action in self.__manager.categoryActions(category):
+                item = QTreeWidgetItem(categoryItem)
+                item.setText(0, action.text())
+                item.setIcon(0, action.icon())
+                item.setTextAlignment(
+                    0,
+                    Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter
+                )
+                item.setData(0, EricToolBarDialog.ActionIdRole, int(id(action)))
+                item.setData(0, EricToolBarDialog.WidgetActionRole, False)
+                if self.__manager.isWidgetAction(action):
+                    item.setData(0, EricToolBarDialog.WidgetActionRole, True)
+                    item.setData(0, Qt.ItemDataRole.ForegroundRole,
+                                 QColor(Qt.GlobalColor.blue))
+                    self.__widgetActionToToolBarItemID[id(action)] = None
+            categoryItem.setExpanded(True)
+        
+        for tbID, actions in list(self.__manager.toolBarsActions().items()):
+            tb = self.__manager.toolBarById(tbID)
+            default = self.__manager.isDefaultToolBar(tb)
+            tbItem = EricToolBarItem(tbID, [], default)
+            self.__toolbarItems[id(tbItem)] = tbItem
+            self.__toolBarItemToWidgetActionID[id(tbItem)] = []
+            actionIDs = []
+            for action in actions:
+                if action is None:
+                    actionIDs.append(None)
+                else:
+                    aID = id(action)
+                    actionIDs.append(aID)
+                    if aID in self.__widgetActionToToolBarItemID:
+                        self.__widgetActionToToolBarItemID[aID] = id(tbItem)
+                        self.__toolBarItemToWidgetActionID[id(tbItem)].append(
+                            aID)
+            tbItem.actionIDs = actionIDs
+            self.toolbarComboBox.addItem(tb.windowTitle(), int(id(tbItem)))
+            if default:
+                self.toolbarComboBox.setItemData(
+                    self.toolbarComboBox.count() - 1,
+                    QColor(Qt.GlobalColor.darkGreen),
+                    Qt.ItemDataRole.ForegroundRole)
+        self.toolbarComboBox.model().sort(0)
+        
+        self.toolbarComboBox.currentIndexChanged[int].connect(
+            self.__toolbarComboBox_currentIndexChanged)
+        self.toolbarComboBox.setCurrentIndex(0)
+    
+    @pyqtSlot()
+    def on_newButton_clicked(self):
+        """
+        Private slot to create a new toolbar.
+        """
+        name, ok = QInputDialog.getText(
+            self,
+            self.tr("New Toolbar"),
+            self.tr("Toolbar Name:"),
+            QLineEdit.EchoMode.Normal)
+        if ok and name:
+            if self.toolbarComboBox.findText(name) != -1:
+                # toolbar with this name already exists
+                EricMessageBox.critical(
+                    self,
+                    self.tr("New Toolbar"),
+                    self.tr(
+                        """A toolbar with the name <b>{0}</b> already"""
+                        """ exists.""")
+                    .format(name))
+                return
+            
+            tbItem = EricToolBarItem(None, [], False)
+            tbItem.title = name
+            tbItem.isChanged = True
+            self.__toolbarItems[id(tbItem)] = tbItem
+            self.__toolBarItemToWidgetActionID[id(tbItem)] = []
+            self.toolbarComboBox.addItem(name, int(id(tbItem)))
+            self.toolbarComboBox.model().sort(0)
+            self.toolbarComboBox.setCurrentIndex(
+                self.toolbarComboBox.findText(name))
+    
+    @pyqtSlot()
+    def on_removeButton_clicked(self):
+        """
+        Private slot to remove a custom toolbar.
+        """
+        name = self.toolbarComboBox.currentText()
+        res = EricMessageBox.yesNo(
+            self,
+            self.tr("Remove Toolbar"),
+            self.tr(
+                """Should the toolbar <b>{0}</b> really be removed?""")
+            .format(name))
+        if res:
+            index = self.toolbarComboBox.currentIndex()
+            tbItemID = self.toolbarComboBox.itemData(index)
+            tbItem = self.__toolbarItems[tbItemID]
+            if (
+                tbItem.toolBarId is not None and
+                tbItem.toolBarId not in self.__removedToolBarIDs
+            ):
+                self.__removedToolBarIDs.append(tbItem.toolBarId)
+            del self.__toolbarItems[tbItemID]
+            for widgetActionID in self.__toolBarItemToWidgetActionID[tbItemID]:
+                self.__widgetActionToToolBarItemID[widgetActionID] = None
+            del self.__toolBarItemToWidgetActionID[tbItemID]
+            self.toolbarComboBox.removeItem(index)
+    
+    @pyqtSlot()
+    def on_renameButton_clicked(self):
+        """
+        Private slot to rename a custom toolbar.
+        """
+        oldName = self.toolbarComboBox.currentText()
+        newName, ok = QInputDialog.getText(
+            self,
+            self.tr("Rename Toolbar"),
+            self.tr("New Toolbar Name:"),
+            QLineEdit.EchoMode.Normal,
+            oldName)
+        if ok and newName:
+            if oldName == newName:
+                return
+            if self.toolbarComboBox.findText(newName) != -1:
+                # toolbar with this name already exists
+                EricMessageBox.critical(
+                    self,
+                    self.tr("Rename Toolbar"),
+                    self.tr(
+                        """A toolbar with the name <b>{0}</b> already"""
+                        """ exists.""")
+                    .format(newName))
+                return
+            index = self.toolbarComboBox.currentIndex()
+            self.toolbarComboBox.setItemText(index, newName)
+            tbItem = self.__toolbarItems[self.toolbarComboBox.itemData(index)]
+            tbItem.title = newName
+            tbItem.isChanged = True
+    
+    def __setupButtons(self):
+        """
+        Private slot to set the buttons state.
+        """
+        index = self.toolbarComboBox.currentIndex()
+        if index > -1:
+            itemID = self.toolbarComboBox.itemData(index)
+            self.__currentToolBarItem = self.__toolbarItems[itemID]
+            self.renameButton.setEnabled(
+                not self.__currentToolBarItem.isDefault)
+            self.removeButton.setEnabled(
+                not self.__currentToolBarItem.isDefault)
+            self.__restoreDefaultsButton.setEnabled(
+                self.__currentToolBarItem.isDefault)
+            self.__resetButton.setEnabled(
+                self.__currentToolBarItem.toolBarId is not None)
+        
+        row = self.toolbarActionsList.currentRow()
+        self.upButton.setEnabled(row > 0)
+        self.downButton.setEnabled(row < self.toolbarActionsList.count() - 1)
+        self.leftButton.setEnabled(self.toolbarActionsList.count() > 0)
+        rightEnable = (
+            self.actionsTree.currentItem().parent() is not None or
+            self.actionsTree.currentItem().text(0) == self.__separatorText
+        )
+        self.rightButton.setEnabled(rightEnable)
+    
+    @pyqtSlot(int)
+    def __toolbarComboBox_currentIndexChanged(self, index):
+        """
+        Private slot called upon a selection of the current toolbar.
+        
+        @param index index of the new current toolbar (integer)
+        """
+        itemID = self.toolbarComboBox.itemData(index)
+        self.__currentToolBarItem = self.__toolbarItems[itemID]
+        self.toolbarActionsList.clear()
+        for actionID in self.__currentToolBarItem.actionIDs:
+            item = QListWidgetItem(self.toolbarActionsList)
+            if actionID is None:
+                item.setText(self.__separatorText)
+            else:
+                action = self.__manager.actionById(actionID)
+                item.setText(action.text())
+                item.setIcon(action.icon())
+                item.setTextAlignment(Qt.AlignmentFlag.AlignLeft |
+                                      Qt.AlignmentFlag.AlignVCenter)
+                item.setData(EricToolBarDialog.ActionIdRole, int(id(action)))
+                item.setData(EricToolBarDialog.WidgetActionRole, False)
+                if self.__manager.isWidgetAction(action):
+                    item.setData(EricToolBarDialog.WidgetActionRole, True)
+                    item.setData(Qt.ItemDataRole.ForegroundRole,
+                                 QColor(Qt.GlobalColor.blue))
+        self.toolbarActionsList.setCurrentRow(0)
+        
+        self.__setupButtons()
+    
+    @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
+    def on_actionsTree_currentItemChanged(self, current, previous):
+        """
+        Private slot called, when the currently selected action changes.
+        
+        @param current reference to the current item (QTreeWidgetItem)
+        @param previous reference to the previous current item
+            (QTreeWidgetItem)
+        """
+        self.__setupButtons()
+    
+    @pyqtSlot(QListWidgetItem, QListWidgetItem)
+    def on_toolbarActionsList_currentItemChanged(self, current, previous):
+        """
+        Private slot to handle a change of the current item.
+        
+        @param current reference to the current item (QListWidgetItem)
+        @param previous reference to the previous current item
+            (QListWidgetItem)
+        """
+        self.__setupButtons()
+    
+    @pyqtSlot()
+    def on_upButton_clicked(self):
+        """
+        Private slot used to move an action up in the list.
+        """
+        row = self.toolbarActionsList.currentRow()
+        if row == 0:
+            # we're already at the top
+            return
+        
+        actionID = self.__currentToolBarItem.actionIDs.pop(row)
+        self.__currentToolBarItem.actionIDs.insert(row - 1, actionID)
+        self.__currentToolBarItem.isChanged = True
+        itm = self.toolbarActionsList.takeItem(row)
+        self.toolbarActionsList.insertItem(row - 1, itm)
+        self.toolbarActionsList.setCurrentItem(itm)
+        self.__setupButtons()
+    
+    @pyqtSlot()
+    def on_downButton_clicked(self):
+        """
+        Private slot used to move an action down in the list.
+        """
+        row = self.toolbarActionsList.currentRow()
+        if row == self.toolbarActionsList.count() - 1:
+            # we're already at the end
+            return
+        
+        actionID = self.__currentToolBarItem.actionIDs.pop(row)
+        self.__currentToolBarItem.actionIDs.insert(row + 1, actionID)
+        self.__currentToolBarItem.isChanged = True
+        itm = self.toolbarActionsList.takeItem(row)
+        self.toolbarActionsList.insertItem(row + 1, itm)
+        self.toolbarActionsList.setCurrentItem(itm)
+        self.__setupButtons()
+    
+    @pyqtSlot()
+    def on_leftButton_clicked(self):
+        """
+        Private slot to delete an action from the list.
+        """
+        row = self.toolbarActionsList.currentRow()
+        actionID = self.__currentToolBarItem.actionIDs.pop(row)
+        self.__currentToolBarItem.isChanged = True
+        if actionID in self.__widgetActionToToolBarItemID:
+            self.__widgetActionToToolBarItemID[actionID] = None
+            self.__toolBarItemToWidgetActionID[
+                id(self.__currentToolBarItem)].remove(actionID)
+        itm = self.toolbarActionsList.takeItem(row)
+        del itm
+        self.toolbarActionsList.setCurrentRow(row)
+        self.__setupButtons()
+    
+    @pyqtSlot()
+    def on_rightButton_clicked(self):
+        """
+        Private slot to add an action to the list.
+        """
+        row = self.toolbarActionsList.currentRow() + 1
+            
+        item = QListWidgetItem()
+        if self.actionsTree.currentItem().text(0) == self.__separatorText:
+            item.setText(self.__separatorText)
+            actionID = None
+        else:
+            actionID = self.actionsTree.currentItem().data(
+                0, EricToolBarDialog.ActionIdRole)
+            action = self.__manager.actionById(actionID)
+            item.setText(action.text())
+            item.setIcon(action.icon())
+            item.setTextAlignment(Qt.AlignmentFlag.AlignLeft |
+                                  Qt.AlignmentFlag.AlignVCenter)
+            item.setData(EricToolBarDialog.ActionIdRole, int(id(action)))
+            item.setData(EricToolBarDialog.WidgetActionRole, False)
+            if self.__manager.isWidgetAction(action):
+                item.setData(EricToolBarDialog.WidgetActionRole, True)
+                item.setData(Qt.ItemDataRole.ForegroundRole,
+                             QColor(Qt.GlobalColor.blue))
+                oldTbItemID = self.__widgetActionToToolBarItemID[actionID]
+                if oldTbItemID is not None:
+                    self.__toolbarItems[oldTbItemID].actionIDs.remove(actionID)
+                    self.__toolbarItems[oldTbItemID].isChanged = True
+                    self.__toolBarItemToWidgetActionID[oldTbItemID].remove(
+                        actionID)
+                self.__widgetActionToToolBarItemID[actionID] = id(
+                    self.__currentToolBarItem)
+                self.__toolBarItemToWidgetActionID[
+                    id(self.__currentToolBarItem)].append(actionID)
+        self.toolbarActionsList.insertItem(row, item)
+        self.__currentToolBarItem.actionIDs.insert(row, actionID)
+        self.__currentToolBarItem.isChanged = True
+        self.toolbarActionsList.setCurrentRow(row)
+        self.__setupButtons()
+    
+    @pyqtSlot(QAbstractButton)
+    def on_buttonBox_clicked(self, button):
+        """
+        Private slot called, when a button of the button box was clicked.
+        
+        @param button reference to the button clicked (QAbstractButton)
+        """
+        if button == self.buttonBox.button(
+            QDialogButtonBox.StandardButton.Cancel
+        ):
+            self.reject()
+        elif button == self.buttonBox.button(
+            QDialogButtonBox.StandardButton.Apply
+        ):
+            self.__saveToolBars()
+            self.__setupButtons()
+        elif button == self.buttonBox.button(
+            QDialogButtonBox.StandardButton.Ok
+        ):
+            self.__saveToolBars()
+            self.accept()
+        elif button == self.buttonBox.button(
+            QDialogButtonBox.StandardButton.Reset
+        ):
+            self.__resetCurrentToolbar()
+            self.__setupButtons()
+        elif button == self.buttonBox.button(
+            QDialogButtonBox.StandardButton.RestoreDefaults
+        ):
+            self.__restoreCurrentToolbarToDefault()
+            self.__setupButtons()
+    
+    def __saveToolBars(self):
+        """
+        Private method to save the configured toolbars.
+        
+        @exception RuntimeError raised to indicate an invalid action
+        """
+        # step 1: remove toolbars marked for deletion
+        for tbID in self.__removedToolBarIDs:
+            tb = self.__manager.toolBarById(tbID)
+            self.__manager.deleteToolBar(tb)
+        self.__removedToolBarIDs = []
+        
+        # step 2: save configured toolbars
+        for tbItem in list(self.__toolbarItems.values()):
+            if not tbItem.isChanged:
+                continue
+            
+            if tbItem.toolBarId is None:
+                # new custom toolbar
+                tb = self.__manager.createToolBar(tbItem.title)
+                tbItem.toolBarId = id(tb)
+            else:
+                tb = self.__manager.toolBarById(tbItem.toolBarId)
+                if not tbItem.isDefault and tbItem.title:
+                    self.__manager.renameToolBar(tb, tbItem.title)
+            
+            actions = []
+            for actionID in tbItem.actionIDs:
+                if actionID is None:
+                    actions.append(None)
+                else:
+                    action = self.__manager.actionById(actionID)
+                    if action is None:
+                        raise RuntimeError(
+                            "No such action, id: 0x{0:x}".format(actionID))
+                    actions.append(action)
+            self.__manager.setToolBar(tb, actions)
+            tbItem.isChanged = False
+    
+    def __restoreCurrentToolbar(self, actions):
+        """
+        Private methdo to restore the current toolbar to the given list of
+        actions.
+        
+        @param actions list of actions to set for the current toolbar
+        (list of QAction)
+        """
+        tbItemID = id(self.__currentToolBarItem)
+        for widgetActionID in self.__toolBarItemToWidgetActionID[tbItemID]:
+            self.__widgetActionToToolBarItemID[widgetActionID] = None
+        self.__toolBarItemToWidgetActionID[tbItemID] = []
+        self.__currentToolBarItem.actionIDs = []
+        
+        for action in actions:
+            if action is None:
+                self.__currentToolBarItem.actionIDs.append(None)
+            else:
+                actionID = id(action)
+                self.__currentToolBarItem.actionIDs.append(actionID)
+                if actionID in self.__widgetActionToToolBarItemID:
+                    oldTbItemID = self.__widgetActionToToolBarItemID[actionID]
+                    if oldTbItemID is not None:
+                        self.__toolbarItems[oldTbItemID].actionIDs.remove(
+                            actionID)
+                        self.__toolbarItems[oldTbItemID].isChanged = True
+                        self.__toolBarItemToWidgetActionID[oldTbItemID].remove(
+                            actionID)
+                    self.__widgetActionToToolBarItemID[actionID] = tbItemID
+                    self.__toolBarItemToWidgetActionID[tbItemID].append(
+                        actionID)
+        self.__toolbarComboBox_currentIndexChanged(
+            self.toolbarComboBox.currentIndex())
+    
+    def __resetCurrentToolbar(self):
+        """
+        Private method to revert all changes made to the current toolbar.
+        """
+        tbID = self.__currentToolBarItem.toolBarId
+        actions = self.__manager.toolBarActions(tbID)
+        self.__restoreCurrentToolbar(actions)
+        self.__currentToolBarItem.isChanged = False
+    
+    def __restoreCurrentToolbarToDefault(self):
+        """
+        Private method to set the current toolbar to its default configuration.
+        """
+        if not self.__currentToolBarItem.isDefault:
+            return
+        
+        tbID = self.__currentToolBarItem.toolBarId
+        actions = self.__manager.defaultToolBarActions(tbID)
+        self.__restoreCurrentToolbar(actions)
+        self.__currentToolBarItem.isChanged = True

eric ide

mercurial