Shortcut can be assigned twice in certain circumstances)

Description of the fix:
1) Create a dict with the shortcuts as keys and a list of actions using this shortcut as value.
2) When we add a new action/shortcut we check if the shortcut is valid. If this is the case we add it to the dict and if not we remove the shortcut.

bzr-revno: 1840
This commit is contained in:
Andreas Preikschat 2011-12-16 15:04:51 +00:00 committed by Tim Bentley
commit 36d6a9003d
2 changed files with 102 additions and 7 deletions

View File

@ -344,8 +344,11 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog):
if category.name is None: if category.name is None:
continue continue
for action in category.actions: for action in category.actions:
if self.changedActions .has_key(action): if action in self.changedActions:
old_shortcuts = map(unicode,
map(QtGui.QKeySequence.toString, action.shortcuts()))
action.setShortcuts(self.changedActions[action]) action.setShortcuts(self.changedActions[action])
self.action_list.update_shortcut_map(action, old_shortcuts)
settings.setValue( settings.setValue(
action.objectName(), QtCore.QVariant(action.shortcuts())) action.objectName(), QtCore.QVariant(action.shortcuts()))
settings.endGroup() settings.endGroup()
@ -452,7 +455,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog):
those shortcuts which are not saved yet but already assigned (as changes those shortcuts which are not saved yet but already assigned (as changes
are applied when closing the dialog). are applied when closing the dialog).
""" """
if self.changedActions.has_key(action): if action in self.changedActions:
return self.changedActions[action] return self.changedActions[action]
return action.shortcuts() return action.shortcuts()

View File

@ -188,6 +188,7 @@ class ActionList(object):
actions or categories. actions or categories.
""" """
instance = None instance = None
shortcut_map = {}
def __init__(self): def __init__(self):
self.categories = CategoryList() self.categories = CategoryList()
@ -224,17 +225,45 @@ class ActionList(object):
self.categories[category].actions.append(action) self.categories[category].actions.append(action)
else: else:
self.categories[category].actions.add(action, weight) self.categories[category].actions.add(action, weight)
if not category:
# Stop here, as this action is not configurable.
return
# Load the shortcut from the config. # Load the shortcut from the config.
settings = QtCore.QSettings() settings = QtCore.QSettings()
settings.beginGroup(u'shortcuts') settings.beginGroup(u'shortcuts')
shortcuts = settings.value(action.objectName(), shortcuts = settings.value(action.objectName(),
QtCore.QVariant(action.shortcuts())).toStringList() QtCore.QVariant(action.shortcuts())).toStringList()
settings.endGroup()
if not shortcuts:
action.setShortcuts([])
return
# We have to do this to ensure that the loaded shortcut list e. g.
# STRG+O (German) is converted to CTRL+O, which is only done when we
# convert the strings in this way (QKeySequence -> QString -> unicode).
shortcuts = map(QtGui.QKeySequence, shortcuts)
shortcuts = map(unicode, map(QtGui.QKeySequence.toString, shortcuts))
# Check the alternate shortcut first, to avoid problems when the
# alternate shortcut becomes the primary shortcut after removing the
# (initial) primary shortcut due to conflicts.
if len(shortcuts) == 2:
existing_actions = ActionList.shortcut_map.get(shortcuts[1], [])
# Check for conflicts with other actions considering the shortcut
# context.
if self._is_shortcut_available(existing_actions, action):
actions = ActionList.shortcut_map.get(shortcuts[1], [])
actions.append(action)
ActionList.shortcut_map[shortcuts[1]] = actions
else:
shortcuts.remove(shortcuts[1])
# Check the primary shortcut.
existing_actions = ActionList.shortcut_map.get(shortcuts[0], [])
# Check for conflicts with other actions considering the shortcut
# context.
if self._is_shortcut_available(existing_actions, action):
actions = ActionList.shortcut_map.get(shortcuts[0], [])
actions.append(action)
ActionList.shortcut_map[shortcuts[0]] = actions
else:
shortcuts.remove(shortcuts[0])
action.setShortcuts( action.setShortcuts(
[QtGui.QKeySequence(shortcut) for shortcut in shortcuts]) [QtGui.QKeySequence(shortcut) for shortcut in shortcuts])
settings.endGroup()
def remove_action(self, action, category=None): def remove_action(self, action, category=None):
""" """
@ -242,7 +271,7 @@ class ActionList(object):
automatically removed. automatically removed.
``action`` ``action``
The QAction object to be removed. The ``QAction`` object to be removed.
``category`` ``category``
The name (unicode string) of the category, which contains the The name (unicode string) of the category, which contains the
@ -254,6 +283,15 @@ class ActionList(object):
# Remove empty categories. # Remove empty categories.
if len(self.categories[category].actions) == 0: if len(self.categories[category].actions) == 0:
self.categories.remove(category) self.categories.remove(category)
shortcuts = map(unicode,
map(QtGui.QKeySequence.toString, action.shortcuts()))
for shortcut in shortcuts:
# Remove action from the list of actions which are using this
# shortcut.
ActionList.shortcut_map[shortcut].remove(action)
# Remove empty entries.
if not ActionList.shortcut_map[shortcut]:
del ActionList.shortcut_map[shortcut]
def add_category(self, name, weight): def add_category(self, name, weight):
""" """
@ -275,6 +313,60 @@ class ActionList(object):
return return
self.categories.add(name, weight) self.categories.add(name, weight)
def update_shortcut_map(self, action, old_shortcuts):
"""
Remove the action for the given ``old_shortcuts`` from the
``shortcut_map`` to ensure its up-to-dateness.
**Note**: The new action's shortcuts **must** be assigned to the given
``action`` **before** calling this method.
``action``
The action whose shortcuts are supposed to be updated in the
``shortcut_map``.
``old_shortcuts``
A list of unicode keysequences.
"""
for old_shortcut in old_shortcuts:
# Remove action from the list of actions which are using this
# shortcut.
ActionList.shortcut_map[old_shortcut].remove(action)
# Remove empty entries.
if not ActionList.shortcut_map[old_shortcut]:
del ActionList.shortcut_map[old_shortcut]
new_shortcuts = map(unicode,
map(QtGui.QKeySequence.toString, action.shortcuts()))
# Add the new shortcuts to the map.
for new_shortcut in new_shortcuts:
existing_actions = ActionList.shortcut_map.get(new_shortcut, [])
existing_actions.append(action)
ActionList.shortcut_map[new_shortcut] = existing_actions
def _is_shortcut_available(self, existing_actions, action):
"""
Checks if the given ``action`` may use its assigned shortcut(s) or not.
Returns ``True`` or ``False.
``existing_actions``
A list of actions which already use a particular shortcut.
``action``
The action which wants to use a particular shortcut.
"""
for existing_action in existing_actions:
if action is existing_action:
continue
if existing_action.parent() is action.parent():
return False
if existing_action.shortcutContext() in [QtCore.Qt.WindowShortcut,
QtCore.Qt.ApplicationShortcut]:
return False
if action.shortcutContext() in [QtCore.Qt.WindowShortcut,
QtCore.Qt.ApplicationShortcut]:
return False
return True
class CategoryOrder(object): class CategoryOrder(object):
""" """