~ubuntu-branches/ubuntu/utopic/python-traitsui/utopic

« back to all changes in this revision

Viewing changes to traitsui/key_bindings.py

  • Committer: Bazaar Package Importer
  • Author(s): Varun Hiremath
  • Date: 2011-07-09 13:57:39 UTC
  • Revision ID: james.westby@ubuntu.com-20110709135739-x5u20q86huissmn1
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#-------------------------------------------------------------------------------
 
2
#
 
3
#  Copyright (c) 2005, Enthought, Inc.
 
4
#  All rights reserved.
 
5
#
 
6
#  This software is provided without warranty under the terms of the BSD
 
7
#  license included in enthought/LICENSE.txt and may be redistributed only
 
8
#  under the conditions described in the aforementioned license.  The license
 
9
#  is also available online at http://www.enthought.com/licenses/BSD.txt
 
10
#
 
11
#  Thanks for using Enthought open source!
 
12
#
 
13
#  Author: David C. Morrill
 
14
#  Date:   05/20/2005
 
15
#
 
16
#-------------------------------------------------------------------------------
 
17
 
 
18
""" Defines KeyBinding and KeyBindings classes, which manage the mapping of
 
19
    keystroke events into method calls on controller objects that are supplied
 
20
    by the application.
 
21
"""
 
22
 
 
23
#-------------------------------------------------------------------------------
 
24
#  Imports:
 
25
#-------------------------------------------------------------------------------
 
26
 
 
27
from __future__ import absolute_import
 
28
 
 
29
from traits.api import (Any, Event, HasPrivateTraits, HasStrictTraits, Instance, List,
 
30
    Property, Str, cached_property, on_trait_change)
 
31
 
 
32
from .api import HGroup, Item, KeyBindingEditor, ListEditor, View, toolkit
 
33
 
 
34
from traits.trait_base import SequenceTypes
 
35
 
 
36
#-------------------------------------------------------------------------------
 
37
#  Key binding trait definition:
 
38
#-------------------------------------------------------------------------------
 
39
 
 
40
# Trait definition for key bindings
 
41
Binding = Str( event = 'binding', editor = KeyBindingEditor() )
 
42
 
 
43
#-------------------------------------------------------------------------------
 
44
#  'KeyBinding' class:
 
45
#-------------------------------------------------------------------------------
 
46
 
 
47
class KeyBinding ( HasStrictTraits ):
 
48
    """ Binds one or two keystrokes to a method.
 
49
    """
 
50
 
 
51
    #---------------------------------------------------------------------------
 
52
    #  Trait definitions:
 
53
    #---------------------------------------------------------------------------
 
54
 
 
55
    # First key binding
 
56
    binding1 = Binding
 
57
 
 
58
    # Second key binding
 
59
    binding2 = Binding
 
60
 
 
61
    # Description of what application function the method performs
 
62
    description = Str
 
63
 
 
64
    # Name of controller method the key is bound to
 
65
    method_name = Str
 
66
 
 
67
    # KeyBindings object that "owns" the KeyBinding
 
68
    owner = Instance( 'KeyBindings' )
 
69
 
 
70
    #---------------------------------------------------------------------------
 
71
    #  Traits view definitions:
 
72
    #---------------------------------------------------------------------------
 
73
 
 
74
    traits_view = View(
 
75
        HGroup(
 
76
            Item( 'binding1' ),
 
77
            Item( 'binding2' ),
 
78
            Item( 'description', style = 'readonly' ),
 
79
            show_labels = False
 
80
        )
 
81
    )
 
82
 
 
83
    #---------------------------------------------------------------------------
 
84
    #  Handles a binding trait being changed:
 
85
    #---------------------------------------------------------------------------
 
86
 
 
87
    def _binding_changed ( self ):
 
88
        if self.owner is not None:
 
89
            self.owner.binding_modified = self
 
90
 
 
91
#-------------------------------------------------------------------------------
 
92
#  'KeyBindings' class:
 
93
#-------------------------------------------------------------------------------
 
94
 
 
95
class KeyBindings ( HasPrivateTraits ):
 
96
    """ A set of key bindings.
 
97
    """
 
98
 
 
99
    #---------------------------------------------------------------------------
 
100
    #  Trait definitions:
 
101
    #---------------------------------------------------------------------------
 
102
 
 
103
    # Set of defined key bindings (redefined dynamically)
 
104
    bindings = List( KeyBinding )
 
105
 
 
106
    # Optional prefix to add to each method name
 
107
    prefix = Str
 
108
 
 
109
    # Optional suffix to add to each method name
 
110
    suffix = Str
 
111
 
 
112
    #-- Private Traits ---------------------------------------------------------
 
113
 
 
114
    # The (optional) list of controllers associated with this KeyBindings
 
115
    # object. The controllers may also be provided with the 'do' method:
 
116
    controllers = List( transient = True )
 
117
 
 
118
    # The 'parent' KeyBindings object of this one (if any):
 
119
    parent = Instance( 'KeyBindings', transient = True )
 
120
 
 
121
    # The root of the KeyBindings tree this object is part of:
 
122
    root = Property( depends_on = 'parent' )
 
123
 
 
124
    # The child KeyBindings of this object (if any):
 
125
    children = List( transient = True )
 
126
 
 
127
    # Event fired when one of the contained KeyBinding objects is changed
 
128
    binding_modified = Event( KeyBinding )
 
129
 
 
130
    # Control that currently has the focus (if any)
 
131
    focus_owner = Any( transient = True )
 
132
 
 
133
    #---------------------------------------------------------------------------
 
134
    #  Traits view definitions:
 
135
    #---------------------------------------------------------------------------
 
136
 
 
137
    traits_view = View( [ Item( 'bindings',
 
138
                                style      = 'custom',
 
139
                                show_label = False,
 
140
                                editor     = ListEditor( style = 'custom' ) ),
 
141
                          '|{Click on an entry field, then press the key to '
 
142
                          'assign. Double-click a field to clear it.}<>' ],
 
143
                        title     = 'Update Key Bindings',
 
144
                        kind      = 'livemodal',
 
145
                        resizable = True,
 
146
                        width     = 0.4,
 
147
                        height    = 0.4 )
 
148
 
 
149
    #---------------------------------------------------------------------------
 
150
    #  Initializes the object:
 
151
    #---------------------------------------------------------------------------
 
152
 
 
153
    def __init__ ( self, *bindings, **traits ):
 
154
        super( KeyBindings, self ).__init__( **traits )
 
155
 
 
156
        if (len( bindings ) == 1) and isinstance( bindings[0], SequenceTypes ):
 
157
            bindings = bindings[0]
 
158
 
 
159
        n = len( bindings )
 
160
        self.add_trait( 'bindings', List( KeyBinding, minlen = n,
 
161
                                                      maxlen = n,
 
162
                                                      mode   = 'list' ) )
 
163
        self.bindings = [ binding.set( owner = self ) for binding in bindings ]
 
164
 
 
165
    #---------------------------------------------------------------------------
 
166
    #  Processes a keyboard event:
 
167
    #---------------------------------------------------------------------------
 
168
 
 
169
    def do ( self, event, controllers = [], *args, **kw ):
 
170
        """ Processes a keyboard event.
 
171
        """
 
172
        if isinstance( controllers, dict ):
 
173
            controllers = controllers.values()
 
174
        elif not isinstance( controllers, SequenceTypes ):
 
175
            controllers = [ controllers ]
 
176
        else:
 
177
            controllers = list( controllers )
 
178
 
 
179
        return self._do( toolkit().key_event_to_name( event ),
 
180
                         controllers, args, kw.get( 'recursive', False ) )
 
181
 
 
182
    #---------------------------------------------------------------------------
 
183
    #  Merges another set of key bindings into this set:
 
184
    #---------------------------------------------------------------------------
 
185
 
 
186
    def merge ( self, key_bindings ):
 
187
        """ Merges another set of key bindings into this set.
 
188
        """
 
189
        binding_dic = {}
 
190
        for binding in self.bindings:
 
191
            binding_dic[ binding.method_name ] = binding
 
192
 
 
193
        for binding in key_bindings.bindings:
 
194
            binding2 = binding_dic.get( binding.method_name )
 
195
            if binding2 is not None:
 
196
                binding2.binding1 = binding.binding1
 
197
                binding2.binding2 = binding.binding2
 
198
 
 
199
    #---------------------------------------------------------------------------
 
200
    #  Returns a clone of the KeyBindings object:
 
201
    #---------------------------------------------------------------------------
 
202
 
 
203
    def clone ( self, **traits ):
 
204
        """ Returns a clone of the KeyBindings object.
 
205
        """
 
206
        return self.__class__( *self.bindings, **traits ).set(
 
207
                               **self.get( 'prefix', 'suffix' ) )
 
208
 
 
209
    #---------------------------------------------------------------------------
 
210
    #  Dispose of the object:
 
211
    #---------------------------------------------------------------------------
 
212
 
 
213
    def dispose ( self ):
 
214
        """ Dispose of the object.
 
215
        """
 
216
        if self.parent is not None:
 
217
            self.parent.children.remove( self )
 
218
 
 
219
        del self.controllers
 
220
        del self.children
 
221
        del self.bindings
 
222
 
 
223
        self.parent = self._root = self.focus_owner = None
 
224
 
 
225
    #---------------------------------------------------------------------------
 
226
    #  Edits a possibly hierarchical set of KeyBindings:
 
227
    #---------------------------------------------------------------------------
 
228
 
 
229
    def edit ( self ):
 
230
        """ Edits a possibly hierarchical set of KeyBindings.
 
231
        """
 
232
        bindings = list( set( self.root._get_bindings( [] ) ) )
 
233
        bindings.sort( lambda l, r:
 
234
            cmp( '%s%02d' % ( l.binding1[-1:], len( l.binding1 ) ),
 
235
                 '%s%02d' % ( r.binding1[-1:], len( r.binding1 ) ) ) )
 
236
        KeyBindings( bindings ).edit_traits()
 
237
 
 
238
    #---------------------------------------------------------------------------
 
239
    #  Returns the current binding for a specified key (if any):
 
240
    #---------------------------------------------------------------------------
 
241
 
 
242
    def key_binding_for ( self, binding, key_name ):
 
243
        """ Returns the current binding for a specified key (if any).
 
244
        """
 
245
        if key_name != '':
 
246
            for a_binding in self.bindings:
 
247
                if ((a_binding is not binding) and
 
248
                    ((key_name == a_binding.binding1) or
 
249
                     (key_name == a_binding.binding2))):
 
250
                    return a_binding
 
251
 
 
252
        return None
 
253
 
 
254
    #-- Property Implementations -----------------------------------------------
 
255
 
 
256
    @cached_property
 
257
    def _get_root ( self ):
 
258
        root = self
 
259
        while root.parent is not None:
 
260
            root = root.parent
 
261
 
 
262
        return root
 
263
 
 
264
    #-- Event Handlers ---------------------------------------------------------
 
265
 
 
266
    def _binding_modified_changed ( self, binding ):
 
267
        """ Handles a binding being changed.
 
268
        """
 
269
        binding1 = binding.binding1
 
270
        binding2 = binding.binding2
 
271
        for a_binding in self.bindings:
 
272
            if binding is not a_binding:
 
273
                if binding1 == a_binding.binding1:
 
274
                    a_binding.binding1 = ''
 
275
                if binding1 == a_binding.binding2:
 
276
                    a_binding.binding2 = ''
 
277
                if binding2 == a_binding.binding1:
 
278
                    a_binding.binding1 = ''
 
279
                if binding2 == a_binding.binding2:
 
280
                    a_binding.binding2 = ''
 
281
 
 
282
    def _focus_owner_changed ( self, old, new ):
 
283
        """ Handles the focus owner being changed.
 
284
        """
 
285
        if old is not None:
 
286
            old.border_size = 0
 
287
 
 
288
    @on_trait_change( 'children[]' )
 
289
    def _children_modified ( self, removed, added ):
 
290
        """ Handles child KeyBindings being added to the object.
 
291
        """
 
292
        for item in added:
 
293
            item.parent = self
 
294
 
 
295
    #-- object Method Overrides ------------------------------------------------
 
296
 
 
297
    #---------------------------------------------------------------------------
 
298
    #  Restores the state of a previously pickled object:
 
299
    #---------------------------------------------------------------------------
 
300
 
 
301
    def __setstate__ ( self, state ):
 
302
        """ Restores the state of a previously pickled object.
 
303
        """
 
304
        n = len( state[ 'bindings' ] )
 
305
        self.add_trait( 'bindings', List( KeyBinding, minlen = n, maxlen = n ) )
 
306
        self.__dict__.update( state )
 
307
        self.bindings = self.bindings[:]
 
308
 
 
309
    #-- Private Methods --------------------------------------------------------
 
310
 
 
311
    def _get_bindings ( self, bindings ):
 
312
        """ Returns all of the bindings of this object and all of its children.
 
313
        """
 
314
        bindings.extend( self.bindings )
 
315
        for child in self.children:
 
316
            child._get_bindings( bindings )
 
317
 
 
318
        return bindings
 
319
 
 
320
    def _do ( self, key_name, controllers, args, recursive ):
 
321
        """ Process the specified key for the specified set of controllers for
 
322
            this KeyBindings object and all of its children.
 
323
        """
 
324
        # Search through our own bindings for a match:
 
325
        for binding in self.bindings:
 
326
            if (key_name == binding.binding1) or (key_name == binding.binding2):
 
327
                method_name = '%s%s%s' % (
 
328
                              self.prefix, binding.method_name, self.suffix )
 
329
                for controller in (controllers + self.controllers):
 
330
                    method = getattr( controller, method_name, None )
 
331
                    if method is not None:
 
332
                        result = method( *args )
 
333
                        if result is not False:
 
334
                            return True
 
335
 
 
336
                if binding.method_name == 'edit_bindings':
 
337
                    self.edit()
 
338
                    return True
 
339
 
 
340
        # If recursive, continue searching through a children's bindings:
 
341
        if recursive:
 
342
            for child in self.children:
 
343
                if child._do( key_name, controllers, args, recursive ):
 
344
                    return True
 
345
 
 
346
        # Indicate no one processed the key:
 
347
        return False
 
348