1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """A special type of C{Action}s used to create keyboard shortcuts."""
17
18 import re
19
20 from muntjac.event.action import Action
21
22
24 """Shortcuts are a special type of L{Action}s used to create keyboard
25 shortcuts.
26
27 The ShortcutAction is triggered when the user presses a given key in
28 combination with the (optional) given modifier keys.
29
30 ShortcutActions can be global (by attaching to the L{Window}), or attached
31 to different parts of the UI so that a specific shortcut is only valid in
32 part of the UI. For instance, one can attach shortcuts to a specific
33 L{Panel} - look for L{ComponentContainer}s implementing L{IHandler} or
34 L{INotifier}.
35
36 ShortcutActions have a caption that may be used to display the shortcut
37 visually. This allows the ShortcutAction to be used as a plain Action while
38 still reacting to a keyboard shortcut. Note that this functionality is not
39 very well supported yet, but it might still be a good idea to give a caption
40 to the shortcut.
41
42 @author: Vaadin Ltd.
43 @author: Richard Lincoln
44 """
45
46
47 SHORTHAND_CHAR_ALT = '&'
48
49
50 SHORTHAND_CHAR_SHIFT = '_'
51
52
53 SHORTHAND_CHAR_CTRL = '^'
54
55
56 _SHORTHAND_ALT = re.escape(SHORTHAND_CHAR_ALT)
57 _SHORTHAND_SHIFT = re.escape(SHORTHAND_CHAR_SHIFT)
58 _SHORTHAND_CTRL = re.escape(SHORTHAND_CHAR_CTRL)
59
60
61 _SHORTHAND_ESCAPE = re.compile(('(' + _SHORTHAND_ALT + '?)'
62 + _SHORTHAND_ALT + '|(' + _SHORTHAND_SHIFT + '?)'
63 + _SHORTHAND_SHIFT + '|(' + _SHORTHAND_CTRL + '?)'
64 + _SHORTHAND_CTRL))
65
66
67 _SHORTHAND_REMOVE = re.compile(('([' + _SHORTHAND_ALT + '|'
68 + _SHORTHAND_SHIFT + '|' + _SHORTHAND_CTRL + '])\\1'))
69
70
71 _SHORTHANDS = re.compile(('(' + _SHORTHAND_ALT + '|' + _SHORTHAND_SHIFT
72 + '|' + _SHORTHAND_CTRL + ')(?!\\1)(?:(' + _SHORTHAND_ALT
73 + '|' + _SHORTHAND_SHIFT + '|' + _SHORTHAND_CTRL
74 + ')(?!\\1|\\2))?(?:(' + _SHORTHAND_ALT + '|' + _SHORTHAND_SHIFT
75 + '|' + _SHORTHAND_CTRL + ')(?!\\1|\\2|\\3))?.'))
76
77
79 """Creates a shortcut either using a shorthand notation to encode the
80 keycode a in the caption or one that reacts to the given L{KeyCode} and
81 (optionally) L{ModifierKey}s.
82
83 The shortcut might be shown in the UI (e.g context menu), in which case
84 the caption will be used.
85
86 Insert one or more modifier characters before the character to use as
87 keycode. E.g C{"&Save"} will make a shortcut responding to
88 ALT-S, C{"E^xit"} will respond to CTRL-X.<br/>
89 Multiple modifiers can be used, e.g C{"&^Delete"} will respond
90 to CTRL-ALT-D (the order of the modifier characters is not important).
91
92 The modifier characters will be removed from the caption. The modifier
93 character is be escaped by itself: two consecutive characters are turned
94 into the original character w/o the special meaning. E.g
95 C{"Save&&&close"} will respond to ALT-C, and the caption will
96 say "Save&close".
97
98 @param args: tuple of the form
99 - (caption, kc, m)
100 1. used when displaying the shortcut visually
101 2. KeyCode that the shortcut reacts to
102 3. optional modifier keys
103 - (caption, icon, kc, m)
104 1. used when displaying the shortcut visually
105 2. used when displaying the shortcut visually
106 3. KeyCode that the shortcut reacts to
107 4. optional modifier keys
108 - (shorthandCaption)
109 1. the caption in modifier shorthand
110 - (shorthandCaption, modifierKeys)
111 1. the caption in modifier shorthand
112 2. modifier keys
113 """
114 self._keyCode = None
115 self._modifiers = tuple()
116
117 args = args
118 nargs = len(args)
119 if nargs == 1:
120 shorthandCaption, = args
121 ShortcutAction.__init__(self, shorthandCaption, None)
122 elif nargs == 2:
123 shorthandCaption, modifierKeys = args
124
125
126 super(ShortcutAction, self).__init__(self._SHORTHAND_ESCAPE.sub(
127 shorthandCaption, '$1$2$3'))
128
129
130
131 shorthandCaption = self._SHORTHAND_REMOVE.sub(
132 shorthandCaption, '\u001A')
133
134 m = self._SHORTHANDS.search(shorthandCaption)
135 if m is not None:
136 match = m.group()
137
138
139 self._keyCode = m.group()[len(match) - 1].upper()
140
141
142 if modifierKeys is not None:
143 self._modifiers = modifierKeys
144 else:
145
146 mod = [None] * (len(match) - 1)
147 for i in range(len(mod)):
148 kc = match[i]
149
150 if kc == self.SHORTHAND_CHAR_ALT:
151 mod[i] = self.ModifierKey.ALT
152
153 elif kc == self.SHORTHAND_CHAR_CTRL:
154 mod[i] = self.ModifierKey.CTRL
155
156 elif kc == self.SHORTHAND_CHAR_SHIFT:
157 mod[i] = self.ModifierKey.SHIFT
158
159 self._modifiers = mod
160 else:
161 self._keyCode = -1
162 self._modifiers = modifierKeys
163 elif nargs == 3:
164 caption, kc, m = args
165 super(ShortcutAction, self).__init__(caption)
166 self._keyCode = kc
167 self._modifiers = m
168 elif nargs == 4:
169 caption, icon, kc, m = args
170 super(ShortcutAction, self).__init__(caption, icon)
171 self._keyCode = kc
172 self._modifiers = m
173 else:
174 raise ValueError, 'invalid number of arguments'
175
176
178 """Get the L{KeyCode} that this shortcut reacts to (in
179 combination with the L{ModifierKey}s).
180
181 @return: keycode for this shortcut
182 """
183 return self._keyCode
184
185
187 """Get the L{ModifierKey}s required for the shortcut to react.
188
189 @return: modifier keys for this shortcut
190 """
191 return self._modifiers
192
193
260
261
263 """Modifier key constants"""
264
265 SHIFT = 16
266 CTRL = 17
267 ALT = 18
268 META = 91
269