~ubuntu-branches/ubuntu/maverick/python3.1/maverick

« back to all changes in this revision

Viewing changes to Lib/idlelib/MultiCall.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2009-03-23 00:01:27 UTC
  • Revision ID: james.westby@ubuntu.com-20090323000127-5fstfxju4ufrhthq
Tags: upstream-3.1~a1+20090322
ImportĀ upstreamĀ versionĀ 3.1~a1+20090322

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
MultiCall - a class which inherits its methods from a Tkinter widget (Text, for
 
3
example), but enables multiple calls of functions per virtual event - all
 
4
matching events will be called, not only the most specific one. This is done
 
5
by wrapping the event functions - event_add, event_delete and event_info.
 
6
MultiCall recognizes only a subset of legal event sequences. Sequences which
 
7
are not recognized are treated by the original Tk handling mechanism. A
 
8
more-specific event will be called before a less-specific event.
 
9
 
 
10
The recognized sequences are complete one-event sequences (no emacs-style
 
11
Ctrl-X Ctrl-C, no shortcuts like <3>), for all types of events.
 
12
Key/Button Press/Release events can have modifiers.
 
13
The recognized modifiers are Shift, Control, Option and Command for Mac, and
 
14
Control, Alt, Shift, Meta/M for other platforms.
 
15
 
 
16
For all events which were handled by MultiCall, a new member is added to the
 
17
event instance passed to the binded functions - mc_type. This is one of the
 
18
event type constants defined in this module (such as MC_KEYPRESS).
 
19
For Key/Button events (which are handled by MultiCall and may receive
 
20
modifiers), another member is added - mc_state. This member gives the state
 
21
of the recognized modifiers, as a combination of the modifier constants
 
22
also defined in this module (for example, MC_SHIFT).
 
23
Using these members is absolutely portable.
 
24
 
 
25
The order by which events are called is defined by these rules:
 
26
1. A more-specific event will be called before a less-specific event.
 
27
2. A recently-binded event will be called before a previously-binded event,
 
28
   unless this conflicts with the first rule.
 
29
Each function will be called at most once for each event.
 
30
"""
 
31
 
 
32
import sys
 
33
import re
 
34
import tkinter
 
35
from idlelib import macosxSupport
 
36
 
 
37
# the event type constants, which define the meaning of mc_type
 
38
MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3;
 
39
MC_ACTIVATE=4; MC_CIRCULATE=5; MC_COLORMAP=6; MC_CONFIGURE=7;
 
40
MC_DEACTIVATE=8; MC_DESTROY=9; MC_ENTER=10; MC_EXPOSE=11; MC_FOCUSIN=12;
 
41
MC_FOCUSOUT=13; MC_GRAVITY=14; MC_LEAVE=15; MC_MAP=16; MC_MOTION=17;
 
42
MC_MOUSEWHEEL=18; MC_PROPERTY=19; MC_REPARENT=20; MC_UNMAP=21; MC_VISIBILITY=22;
 
43
# the modifier state constants, which define the meaning of mc_state
 
44
MC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5
 
45
MC_OPTION = 1<<6; MC_COMMAND = 1<<7
 
46
 
 
47
# define the list of modifiers, to be used in complex event types.
 
48
if macosxSupport.runningAsOSXApp():
 
49
    _modifiers = (("Shift",), ("Control",), ("Option",), ("Command",))
 
50
    _modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND)
 
51
else:
 
52
    _modifiers = (("Control",), ("Alt",), ("Shift",), ("Meta", "M"))
 
53
    _modifier_masks = (MC_CONTROL, MC_ALT, MC_SHIFT, MC_META)
 
54
 
 
55
# a dictionary to map a modifier name into its number
 
56
_modifier_names = dict([(name, number)
 
57
                         for number in range(len(_modifiers))
 
58
                         for name in _modifiers[number]])
 
59
 
 
60
# A binder is a class which binds functions to one type of event. It has two
 
61
# methods: bind and unbind, which get a function and a parsed sequence, as
 
62
# returned by _parse_sequence(). There are two types of binders:
 
63
# _SimpleBinder handles event types with no modifiers and no detail.
 
64
# No Python functions are called when no events are binded.
 
65
# _ComplexBinder handles event types with modifiers and a detail.
 
66
# A Python function is called each time an event is generated.
 
67
 
 
68
class _SimpleBinder:
 
69
    def __init__(self, type, widget, widgetinst):
 
70
        self.type = type
 
71
        self.sequence = '<'+_types[type][0]+'>'
 
72
        self.widget = widget
 
73
        self.widgetinst = widgetinst
 
74
        self.bindedfuncs = []
 
75
        self.handlerid = None
 
76
 
 
77
    def bind(self, triplet, func):
 
78
        if not self.handlerid:
 
79
            def handler(event, l = self.bindedfuncs, mc_type = self.type):
 
80
                event.mc_type = mc_type
 
81
                wascalled = {}
 
82
                for i in range(len(l)-1, -1, -1):
 
83
                    func = l[i]
 
84
                    if func not in wascalled:
 
85
                        wascalled[func] = True
 
86
                        r = func(event)
 
87
                        if r:
 
88
                            return r
 
89
            self.handlerid = self.widget.bind(self.widgetinst,
 
90
                                              self.sequence, handler)
 
91
        self.bindedfuncs.append(func)
 
92
 
 
93
    def unbind(self, triplet, func):
 
94
        self.bindedfuncs.remove(func)
 
95
        if not self.bindedfuncs:
 
96
            self.widget.unbind(self.widgetinst, self.sequence, self.handlerid)
 
97
            self.handlerid = None
 
98
 
 
99
    def __del__(self):
 
100
        if self.handlerid:
 
101
            self.widget.unbind(self.widgetinst, self.sequence, self.handlerid)
 
102
 
 
103
# An int in range(1 << len(_modifiers)) represents a combination of modifiers
 
104
# (if the least significent bit is on, _modifiers[0] is on, and so on).
 
105
# _state_subsets gives for each combination of modifiers, or *state*,
 
106
# a list of the states which are a subset of it. This list is ordered by the
 
107
# number of modifiers is the state - the most specific state comes first.
 
108
_states = range(1 << len(_modifiers))
 
109
_state_names = [''.join(m[0]+'-'
 
110
                        for i, m in enumerate(_modifiers)
 
111
                        if (1 << i) & s)
 
112
                for s in _states]
 
113
 
 
114
def expand_substates(states):
 
115
    '''For each item of states return a list containing all combinations of
 
116
    that item with individual bits reset, sorted by the number of set bits.
 
117
    '''
 
118
    def nbits(n):
 
119
        "number of bits set in n base 2"
 
120
        nb = 0
 
121
        while n:
 
122
            n, rem = divmod(n, 2)
 
123
            nb += rem
 
124
        return nb
 
125
    statelist = []
 
126
    for state in states:
 
127
        substates = list(set(state & x for x in states))
 
128
        substates.sort(key=nbits, reverse=True)
 
129
        statelist.append(substates)
 
130
    return statelist
 
131
 
 
132
_state_subsets = expand_substates(_states)
 
133
 
 
134
# _state_codes gives for each state, the portable code to be passed as mc_state
 
135
_state_codes = []
 
136
for s in _states:
 
137
    r = 0
 
138
    for i in range(len(_modifiers)):
 
139
        if (1 << i) & s:
 
140
            r |= _modifier_masks[i]
 
141
    _state_codes.append(r)
 
142
 
 
143
class _ComplexBinder:
 
144
    # This class binds many functions, and only unbinds them when it is deleted.
 
145
    # self.handlerids is the list of seqs and ids of binded handler functions.
 
146
    # The binded functions sit in a dictionary of lists of lists, which maps
 
147
    # a detail (or None) and a state into a list of functions.
 
148
    # When a new detail is discovered, handlers for all the possible states
 
149
    # are binded.
 
150
 
 
151
    def __create_handler(self, lists, mc_type, mc_state):
 
152
        def handler(event, lists = lists,
 
153
                    mc_type = mc_type, mc_state = mc_state,
 
154
                    ishandlerrunning = self.ishandlerrunning,
 
155
                    doafterhandler = self.doafterhandler):
 
156
            ishandlerrunning[:] = [True]
 
157
            event.mc_type = mc_type
 
158
            event.mc_state = mc_state
 
159
            wascalled = {}
 
160
            r = None
 
161
            for l in lists:
 
162
                for i in range(len(l)-1, -1, -1):
 
163
                    func = l[i]
 
164
                    if func not in wascalled:
 
165
                        wascalled[func] = True
 
166
                        r = l[i](event)
 
167
                        if r:
 
168
                            break
 
169
                if r:
 
170
                    break
 
171
            ishandlerrunning[:] = []
 
172
            # Call all functions in doafterhandler and remove them from list
 
173
            while doafterhandler:
 
174
                doafterhandler.pop()()
 
175
            if r:
 
176
                return r
 
177
        return handler
 
178
 
 
179
    def __init__(self, type, widget, widgetinst):
 
180
        self.type = type
 
181
        self.typename = _types[type][0]
 
182
        self.widget = widget
 
183
        self.widgetinst = widgetinst
 
184
        self.bindedfuncs = {None: [[] for s in _states]}
 
185
        self.handlerids = []
 
186
        # we don't want to change the lists of functions while a handler is
 
187
        # running - it will mess up the loop and anyway, we usually want the
 
188
        # change to happen from the next event. So we have a list of functions
 
189
        # for the handler to run after it finishes calling the binded functions.
 
190
        # It calls them only once.
 
191
        # ishandlerrunning is a list. An empty one means no, otherwise - yes.
 
192
        # this is done so that it would be mutable.
 
193
        self.ishandlerrunning = []
 
194
        self.doafterhandler = []
 
195
        for s in _states:
 
196
            lists = [self.bindedfuncs[None][i] for i in _state_subsets[s]]
 
197
            handler = self.__create_handler(lists, type, _state_codes[s])
 
198
            seq = '<'+_state_names[s]+self.typename+'>'
 
199
            self.handlerids.append((seq, self.widget.bind(self.widgetinst,
 
200
                                                          seq, handler)))
 
201
 
 
202
    def bind(self, triplet, func):
 
203
        if triplet[2] not in self.bindedfuncs:
 
204
            self.bindedfuncs[triplet[2]] = [[] for s in _states]
 
205
            for s in _states:
 
206
                lists = [ self.bindedfuncs[detail][i]
 
207
                          for detail in (triplet[2], None)
 
208
                          for i in _state_subsets[s]       ]
 
209
                handler = self.__create_handler(lists, self.type,
 
210
                                                _state_codes[s])
 
211
                seq = "<%s%s-%s>"% (_state_names[s], self.typename, triplet[2])
 
212
                self.handlerids.append((seq, self.widget.bind(self.widgetinst,
 
213
                                                              seq, handler)))
 
214
        doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].append(func)
 
215
        if not self.ishandlerrunning:
 
216
            doit()
 
217
        else:
 
218
            self.doafterhandler.append(doit)
 
219
 
 
220
    def unbind(self, triplet, func):
 
221
        doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].remove(func)
 
222
        if not self.ishandlerrunning:
 
223
            doit()
 
224
        else:
 
225
            self.doafterhandler.append(doit)
 
226
 
 
227
    def __del__(self):
 
228
        for seq, id in self.handlerids:
 
229
            self.widget.unbind(self.widgetinst, seq, id)
 
230
 
 
231
# define the list of event types to be handled by MultiEvent. the order is
 
232
# compatible with the definition of event type constants.
 
233
_types = (
 
234
    ("KeyPress", "Key"), ("KeyRelease",), ("ButtonPress", "Button"),
 
235
    ("ButtonRelease",), ("Activate",), ("Circulate",), ("Colormap",),
 
236
    ("Configure",), ("Deactivate",), ("Destroy",), ("Enter",), ("Expose",),
 
237
    ("FocusIn",), ("FocusOut",), ("Gravity",), ("Leave",), ("Map",),
 
238
    ("Motion",), ("MouseWheel",), ("Property",), ("Reparent",), ("Unmap",),
 
239
    ("Visibility",),
 
240
)
 
241
 
 
242
# which binder should be used for every event type?
 
243
_binder_classes = (_ComplexBinder,) * 4 + (_SimpleBinder,) * (len(_types)-4)
 
244
 
 
245
# A dictionary to map a type name into its number
 
246
_type_names = dict([(name, number)
 
247
                     for number in range(len(_types))
 
248
                     for name in _types[number]])
 
249
 
 
250
_keysym_re = re.compile(r"^\w+$")
 
251
_button_re = re.compile(r"^[1-5]$")
 
252
def _parse_sequence(sequence):
 
253
    """Get a string which should describe an event sequence. If it is
 
254
    successfully parsed as one, return a tuple containing the state (as an int),
 
255
    the event type (as an index of _types), and the detail - None if none, or a
 
256
    string if there is one. If the parsing is unsuccessful, return None.
 
257
    """
 
258
    if not sequence or sequence[0] != '<' or sequence[-1] != '>':
 
259
        return None
 
260
    words = sequence[1:-1].split('-')
 
261
    modifiers = 0
 
262
    while words and words[0] in _modifier_names:
 
263
        modifiers |= 1 << _modifier_names[words[0]]
 
264
        del words[0]
 
265
    if words and words[0] in _type_names:
 
266
        type = _type_names[words[0]]
 
267
        del words[0]
 
268
    else:
 
269
        return None
 
270
    if _binder_classes[type] is _SimpleBinder:
 
271
        if modifiers or words:
 
272
            return None
 
273
        else:
 
274
            detail = None
 
275
    else:
 
276
        # _ComplexBinder
 
277
        if type in [_type_names[s] for s in ("KeyPress", "KeyRelease")]:
 
278
            type_re = _keysym_re
 
279
        else:
 
280
            type_re = _button_re
 
281
 
 
282
        if not words:
 
283
            detail = None
 
284
        elif len(words) == 1 and type_re.match(words[0]):
 
285
            detail = words[0]
 
286
        else:
 
287
            return None
 
288
 
 
289
    return modifiers, type, detail
 
290
 
 
291
def _triplet_to_sequence(triplet):
 
292
    if triplet[2]:
 
293
        return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'-'+ \
 
294
               triplet[2]+'>'
 
295
    else:
 
296
        return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'>'
 
297
 
 
298
_multicall_dict = {}
 
299
def MultiCallCreator(widget):
 
300
    """Return a MultiCall class which inherits its methods from the
 
301
    given widget class (for example, Tkinter.Text). This is used
 
302
    instead of a templating mechanism.
 
303
    """
 
304
    if widget in _multicall_dict:
 
305
        return _multicall_dict[widget]
 
306
 
 
307
    class MultiCall (widget):
 
308
        assert issubclass(widget, tkinter.Misc)
 
309
 
 
310
        def __init__(self, *args, **kwargs):
 
311
            widget.__init__(self, *args, **kwargs)
 
312
            # a dictionary which maps a virtual event to a tuple with:
 
313
            #  0. the function binded
 
314
            #  1. a list of triplets - the sequences it is binded to
 
315
            self.__eventinfo = {}
 
316
            self.__binders = [_binder_classes[i](i, widget, self)
 
317
                              for i in range(len(_types))]
 
318
 
 
319
        def bind(self, sequence=None, func=None, add=None):
 
320
            #print("bind(%s, %s, %s)" % (sequence, func, add),
 
321
            #      file=sys.__stderr__)
 
322
            if type(sequence) is str and len(sequence) > 2 and \
 
323
               sequence[:2] == "<<" and sequence[-2:] == ">>":
 
324
                if sequence in self.__eventinfo:
 
325
                    ei = self.__eventinfo[sequence]
 
326
                    if ei[0] is not None:
 
327
                        for triplet in ei[1]:
 
328
                            self.__binders[triplet[1]].unbind(triplet, ei[0])
 
329
                    ei[0] = func
 
330
                    if ei[0] is not None:
 
331
                        for triplet in ei[1]:
 
332
                            self.__binders[triplet[1]].bind(triplet, func)
 
333
                else:
 
334
                    self.__eventinfo[sequence] = [func, []]
 
335
            return widget.bind(self, sequence, func, add)
 
336
 
 
337
        def unbind(self, sequence, funcid=None):
 
338
            if type(sequence) is str and len(sequence) > 2 and \
 
339
               sequence[:2] == "<<" and sequence[-2:] == ">>" and \
 
340
               sequence in self.__eventinfo:
 
341
                func, triplets = self.__eventinfo[sequence]
 
342
                if func is not None:
 
343
                    for triplet in triplets:
 
344
                        self.__binders[triplet[1]].unbind(triplet, func)
 
345
                    self.__eventinfo[sequence][0] = None
 
346
            return widget.unbind(self, sequence, funcid)
 
347
 
 
348
        def event_add(self, virtual, *sequences):
 
349
            #print("event_add(%s, %s)" % (repr(virtual), repr(sequences)),
 
350
            #      file=sys.__stderr__)
 
351
            if virtual not in self.__eventinfo:
 
352
                self.__eventinfo[virtual] = [None, []]
 
353
 
 
354
            func, triplets = self.__eventinfo[virtual]
 
355
            for seq in sequences:
 
356
                triplet = _parse_sequence(seq)
 
357
                if triplet is None:
 
358
                    #print("Tkinter event_add(%s)" % seq, file=sys.__stderr__)
 
359
                    widget.event_add(self, virtual, seq)
 
360
                else:
 
361
                    if func is not None:
 
362
                        self.__binders[triplet[1]].bind(triplet, func)
 
363
                    triplets.append(triplet)
 
364
 
 
365
        def event_delete(self, virtual, *sequences):
 
366
            if virtual not in self.__eventinfo:
 
367
                return
 
368
            func, triplets = self.__eventinfo[virtual]
 
369
            for seq in sequences:
 
370
                triplet = _parse_sequence(seq)
 
371
                if triplet is None:
 
372
                    #print("Tkinter event_delete: %s" % seq, file=sys.__stderr__)
 
373
                    widget.event_delete(self, virtual, seq)
 
374
                else:
 
375
                    if func is not None:
 
376
                        self.__binders[triplet[1]].unbind(triplet, func)
 
377
                    triplets.remove(triplet)
 
378
 
 
379
        def event_info(self, virtual=None):
 
380
            if virtual is None or virtual not in self.__eventinfo:
 
381
                return widget.event_info(self, virtual)
 
382
            else:
 
383
                return tuple(map(_triplet_to_sequence,
 
384
                                 self.__eventinfo[virtual][1])) + \
 
385
                       widget.event_info(self, virtual)
 
386
 
 
387
        def __del__(self):
 
388
            for virtual in self.__eventinfo:
 
389
                func, triplets = self.__eventinfo[virtual]
 
390
                if func:
 
391
                    for triplet in triplets:
 
392
                        self.__binders[triplet[1]].unbind(triplet, func)
 
393
 
 
394
 
 
395
    _multicall_dict[widget] = MultiCall
 
396
    return MultiCall
 
397
 
 
398
if __name__ == "__main__":
 
399
    # Test
 
400
    root = tkinter.Tk()
 
401
    text = MultiCallCreator(tkinter.Text)(root)
 
402
    text.pack()
 
403
    def bindseq(seq, n=[0]):
 
404
        def handler(event):
 
405
            print(seq)
 
406
        text.bind("<<handler%d>>"%n[0], handler)
 
407
        text.event_add("<<handler%d>>"%n[0], seq)
 
408
        n[0] += 1
 
409
    bindseq("<Key>")
 
410
    bindseq("<Control-Key>")
 
411
    bindseq("<Alt-Key-a>")
 
412
    bindseq("<Control-Key-a>")
 
413
    bindseq("<Alt-Control-Key-a>")
 
414
    bindseq("<Key-b>")
 
415
    bindseq("<Control-Button-1>")
 
416
    bindseq("<Alt-Button-1>")
 
417
    bindseq("<FocusOut>")
 
418
    bindseq("<Enter>")
 
419
    bindseq("<Leave>")
 
420
    root.mainloop()