Merge lp:~googol-deactivatedaccount/openlp/bug-768495 into lp:openlp

Proposed by Andreas Preikschat
Status: Merged
Approved by: Raoul Snyman
Approved revision: 1826
Merged at revision: 1840
Proposed branch: lp:~googol-deactivatedaccount/openlp/bug-768495
Merge into: lp:openlp
Diff against target: 173 lines (+102/-7)
2 files modified
openlp/core/ui/shortcutlistform.py (+5/-2)
openlp/core/utils/actions.py (+97/-5)
To merge this branch: bzr merge lp:~googol-deactivatedaccount/openlp/bug-768495
Reviewer Review Type Date Requested Status
Raoul Snyman Approve
Tim Bentley Approve
Review via email: mp+85891@code.launchpad.net

This proposal supersedes a proposal from 2011-12-14.

Commit message

bzr commit -m "fixed bug #768495 (Shortcut can be assigned twice in certain circumstances)" --fixes lp:768495

Description of the change

Hello,

Fixed bug 768495 (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.

Possible space for improvements:
- notify the user that a shortcut has been disabled
- make sure that shortcuts with lower priority are disabled if there should be a conflict (currently we remove the shortcut from the last actions)

How to test this?
1) Assign "R" to the "Re-index tool" action
2) Disable the songs plugin
3) Assign "R" to another action
4) Enable the songs plugin

To post a comment you must log in.
Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

Resolved conflict

Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

- Fixed an i18n bug
- Remove shortcuts from the map if removed from the action list

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

shortcuts = map(unicode, map(QtGui.QKeySequence.toString, shortcuts))

I think you have two spaces in the line above... ^^

review: Needs Fixing
Revision history for this message
Tim Bentley (trb143) :
review: Approve
Revision history for this message
Raoul Snyman (raoul-snyman) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'openlp/core/ui/shortcutlistform.py'
--- openlp/core/ui/shortcutlistform.py 2011-07-07 21:22:41 +0000
+++ openlp/core/ui/shortcutlistform.py 2011-12-15 15:40:29 +0000
@@ -344,8 +344,11 @@
344 if category.name is None:344 if category.name is None:
345 continue345 continue
346 for action in category.actions:346 for action in category.actions:
347 if self.changedActions .has_key(action):347 if action in self.changedActions:
348 old_shortcuts = map(unicode,
349 map(QtGui.QKeySequence.toString, action.shortcuts()))
348 action.setShortcuts(self.changedActions[action])350 action.setShortcuts(self.changedActions[action])
351 self.action_list.update_shortcut_map(action, old_shortcuts)
349 settings.setValue(352 settings.setValue(
350 action.objectName(), QtCore.QVariant(action.shortcuts()))353 action.objectName(), QtCore.QVariant(action.shortcuts()))
351 settings.endGroup()354 settings.endGroup()
@@ -452,7 +455,7 @@
452 those shortcuts which are not saved yet but already assigned (as changes455 those shortcuts which are not saved yet but already assigned (as changes
453 are applied when closing the dialog).456 are applied when closing the dialog).
454 """457 """
455 if self.changedActions.has_key(action):458 if action in self.changedActions:
456 return self.changedActions[action]459 return self.changedActions[action]
457 return action.shortcuts()460 return action.shortcuts()
458461
459462
=== modified file 'openlp/core/utils/actions.py'
--- openlp/core/utils/actions.py 2011-12-10 20:31:16 +0000
+++ openlp/core/utils/actions.py 2011-12-15 15:40:29 +0000
@@ -188,6 +188,7 @@
188 actions or categories.188 actions or categories.
189 """189 """
190 instance = None190 instance = None
191 shortcut_map = {}
191192
192 def __init__(self):193 def __init__(self):
193 self.categories = CategoryList()194 self.categories = CategoryList()
@@ -224,17 +225,45 @@
224 self.categories[category].actions.append(action)225 self.categories[category].actions.append(action)
225 else:226 else:
226 self.categories[category].actions.add(action, weight)227 self.categories[category].actions.add(action, weight)
227 if not category:
228 # Stop here, as this action is not configurable.
229 return
230 # Load the shortcut from the config.228 # Load the shortcut from the config.
231 settings = QtCore.QSettings()229 settings = QtCore.QSettings()
232 settings.beginGroup(u'shortcuts')230 settings.beginGroup(u'shortcuts')
233 shortcuts = settings.value(action.objectName(),231 shortcuts = settings.value(action.objectName(),
234 QtCore.QVariant(action.shortcuts())).toStringList()232 QtCore.QVariant(action.shortcuts())).toStringList()
233 settings.endGroup()
234 if not shortcuts:
235 action.setShortcuts([])
236 return
237 # We have to do this to ensure that the loaded shortcut list e. g.
238 # STRG+O (German) is converted to CTRL+O, which is only done when we
239 # convert the strings in this way (QKeySequence -> QString -> unicode).
240 shortcuts = map(QtGui.QKeySequence, shortcuts)
241 shortcuts = map(unicode, map(QtGui.QKeySequence.toString, shortcuts))
242 # Check the alternate shortcut first, to avoid problems when the
243 # alternate shortcut becomes the primary shortcut after removing the
244 # (initial) primary shortcut due to conflicts.
245 if len(shortcuts) == 2:
246 existing_actions = ActionList.shortcut_map.get(shortcuts[1], [])
247 # Check for conflicts with other actions considering the shortcut
248 # context.
249 if self._is_shortcut_available(existing_actions, action):
250 actions = ActionList.shortcut_map.get(shortcuts[1], [])
251 actions.append(action)
252 ActionList.shortcut_map[shortcuts[1]] = actions
253 else:
254 shortcuts.remove(shortcuts[1])
255 # Check the primary shortcut.
256 existing_actions = ActionList.shortcut_map.get(shortcuts[0], [])
257 # Check for conflicts with other actions considering the shortcut
258 # context.
259 if self._is_shortcut_available(existing_actions, action):
260 actions = ActionList.shortcut_map.get(shortcuts[0], [])
261 actions.append(action)
262 ActionList.shortcut_map[shortcuts[0]] = actions
263 else:
264 shortcuts.remove(shortcuts[0])
235 action.setShortcuts(265 action.setShortcuts(
236 [QtGui.QKeySequence(shortcut) for shortcut in shortcuts])266 [QtGui.QKeySequence(shortcut) for shortcut in shortcuts])
237 settings.endGroup()
238267
239 def remove_action(self, action, category=None):268 def remove_action(self, action, category=None):
240 """269 """
@@ -242,7 +271,7 @@
242 automatically removed.271 automatically removed.
243272
244 ``action``273 ``action``
245 The QAction object to be removed.274 The ``QAction`` object to be removed.
246275
247 ``category``276 ``category``
248 The name (unicode string) of the category, which contains the277 The name (unicode string) of the category, which contains the
@@ -254,6 +283,15 @@
254 # Remove empty categories.283 # Remove empty categories.
255 if len(self.categories[category].actions) == 0:284 if len(self.categories[category].actions) == 0:
256 self.categories.remove(category)285 self.categories.remove(category)
286 shortcuts = map(unicode,
287 map(QtGui.QKeySequence.toString, action.shortcuts()))
288 for shortcut in shortcuts:
289 # Remove action from the list of actions which are using this
290 # shortcut.
291 ActionList.shortcut_map[shortcut].remove(action)
292 # Remove empty entries.
293 if not ActionList.shortcut_map[shortcut]:
294 del ActionList.shortcut_map[shortcut]
257295
258 def add_category(self, name, weight):296 def add_category(self, name, weight):
259 """297 """
@@ -275,6 +313,60 @@
275 return313 return
276 self.categories.add(name, weight)314 self.categories.add(name, weight)
277315
316 def update_shortcut_map(self, action, old_shortcuts):
317 """
318 Remove the action for the given ``old_shortcuts`` from the
319 ``shortcut_map`` to ensure its up-to-dateness.
320
321 **Note**: The new action's shortcuts **must** be assigned to the given
322 ``action`` **before** calling this method.
323
324 ``action``
325 The action whose shortcuts are supposed to be updated in the
326 ``shortcut_map``.
327
328 ``old_shortcuts``
329 A list of unicode keysequences.
330 """
331 for old_shortcut in old_shortcuts:
332 # Remove action from the list of actions which are using this
333 # shortcut.
334 ActionList.shortcut_map[old_shortcut].remove(action)
335 # Remove empty entries.
336 if not ActionList.shortcut_map[old_shortcut]:
337 del ActionList.shortcut_map[old_shortcut]
338 new_shortcuts = map(unicode,
339 map(QtGui.QKeySequence.toString, action.shortcuts()))
340 # Add the new shortcuts to the map.
341 for new_shortcut in new_shortcuts:
342 existing_actions = ActionList.shortcut_map.get(new_shortcut, [])
343 existing_actions.append(action)
344 ActionList.shortcut_map[new_shortcut] = existing_actions
345
346 def _is_shortcut_available(self, existing_actions, action):
347 """
348 Checks if the given ``action`` may use its assigned shortcut(s) or not.
349 Returns ``True`` or ``False.
350
351 ``existing_actions``
352 A list of actions which already use a particular shortcut.
353
354 ``action``
355 The action which wants to use a particular shortcut.
356 """
357 for existing_action in existing_actions:
358 if action is existing_action:
359 continue
360 if existing_action.parent() is action.parent():
361 return False
362 if existing_action.shortcutContext() in [QtCore.Qt.WindowShortcut,
363 QtCore.Qt.ApplicationShortcut]:
364 return False
365 if action.shortcutContext() in [QtCore.Qt.WindowShortcut,
366 QtCore.Qt.ApplicationShortcut]:
367 return False
368 return True
369
278370
279class CategoryOrder(object):371class CategoryOrder(object):
280 """372 """