10a8c90248264a8b26970b4473770bcc3df8515fJosh Gao"""
20a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMultiCall - a class which inherits its methods from a Tkinter widget (Text, for
30a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoexample), but enables multiple calls of functions per virtual event - all
40a8c90248264a8b26970b4473770bcc3df8515fJosh Gaomatching events will be called, not only the most specific one. This is done
50a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoby wrapping the event functions - event_add, event_delete and event_info.
60a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMultiCall recognizes only a subset of legal event sequences. Sequences which
70a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoare not recognized are treated by the original Tk handling mechanism. A
80a8c90248264a8b26970b4473770bcc3df8515fJosh Gaomore-specific event will be called before a less-specific event.
90a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
100a8c90248264a8b26970b4473770bcc3df8515fJosh GaoThe recognized sequences are complete one-event sequences (no emacs-style
110a8c90248264a8b26970b4473770bcc3df8515fJosh GaoCtrl-X Ctrl-C, no shortcuts like <3>), for all types of events.
120a8c90248264a8b26970b4473770bcc3df8515fJosh GaoKey/Button Press/Release events can have modifiers.
130a8c90248264a8b26970b4473770bcc3df8515fJosh GaoThe recognized modifiers are Shift, Control, Option and Command for Mac, and
140a8c90248264a8b26970b4473770bcc3df8515fJosh GaoControl, Alt, Shift, Meta/M for other platforms.
150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
160a8c90248264a8b26970b4473770bcc3df8515fJosh GaoFor all events which were handled by MultiCall, a new member is added to the
170a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoevent instance passed to the binded functions - mc_type. This is one of the
180a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoevent type constants defined in this module (such as MC_KEYPRESS).
190a8c90248264a8b26970b4473770bcc3df8515fJosh GaoFor Key/Button events (which are handled by MultiCall and may receive
200a8c90248264a8b26970b4473770bcc3df8515fJosh Gaomodifiers), another member is added - mc_state. This member gives the state
210a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoof the recognized modifiers, as a combination of the modifier constants
220a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoalso defined in this module (for example, MC_SHIFT).
230a8c90248264a8b26970b4473770bcc3df8515fJosh GaoUsing these members is absolutely portable.
240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
250a8c90248264a8b26970b4473770bcc3df8515fJosh GaoThe order by which events are called is defined by these rules:
260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao1. A more-specific event will be called before a less-specific event.
270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao2. A recently-binded event will be called before a previously-binded event,
280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao   unless this conflicts with the first rule.
290a8c90248264a8b26970b4473770bcc3df8515fJosh GaoEach function will be called at most once for each event.
300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao"""
310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
320a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport sys
330a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport string
340a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport re
350a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport Tkinter
360a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom idlelib import macosxSupport
370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# the event type constants, which define the meaning of mc_type
390a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3;
400a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMC_ACTIVATE=4; MC_CIRCULATE=5; MC_COLORMAP=6; MC_CONFIGURE=7;
410a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMC_DEACTIVATE=8; MC_DESTROY=9; MC_ENTER=10; MC_EXPOSE=11; MC_FOCUSIN=12;
420a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMC_FOCUSOUT=13; MC_GRAVITY=14; MC_LEAVE=15; MC_MAP=16; MC_MOTION=17;
430a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMC_MOUSEWHEEL=18; MC_PROPERTY=19; MC_REPARENT=20; MC_UNMAP=21; MC_VISIBILITY=22;
440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# the modifier state constants, which define the meaning of mc_state
450a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5
460a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMC_OPTION = 1<<6; MC_COMMAND = 1<<7
470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# define the list of modifiers, to be used in complex event types.
490a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoif macosxSupport.runningAsOSXApp():
500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    _modifiers = (("Shift",), ("Control",), ("Option",), ("Command",))
510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    _modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND)
520a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoelse:
530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    _modifiers = (("Control",), ("Alt",), ("Shift",), ("Meta", "M"))
540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    _modifier_masks = (MC_CONTROL, MC_ALT, MC_SHIFT, MC_META)
550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# a dictionary to map a modifier name into its number
570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_modifier_names = dict([(name, number)
580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                         for number in range(len(_modifiers))
590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                         for name in _modifiers[number]])
600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# A binder is a class which binds functions to one type of event. It has two
620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# methods: bind and unbind, which get a function and a parsed sequence, as
630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# returned by _parse_sequence(). There are two types of binders:
640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# _SimpleBinder handles event types with no modifiers and no detail.
650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# No Python functions are called when no events are binded.
660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# _ComplexBinder handles event types with modifiers and a detail.
670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# A Python function is called each time an event is generated.
680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
690a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass _SimpleBinder:
700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __init__(self, type, widget, widgetinst):
710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.type = type
720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.sequence = '<'+_types[type][0]+'>'
730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.widget = widget
740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.widgetinst = widgetinst
750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.bindedfuncs = []
760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.handlerid = None
770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def bind(self, triplet, func):
790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if not self.handlerid:
800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            def handler(event, l = self.bindedfuncs, mc_type = self.type):
810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                event.mc_type = mc_type
820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                wascalled = {}
830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                for i in range(len(l)-1, -1, -1):
840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    func = l[i]
850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    if func not in wascalled:
860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                        wascalled[func] = True
870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                        r = func(event)
880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                        if r:
890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                            return r
900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.handlerid = self.widget.bind(self.widgetinst,
910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                                              self.sequence, handler)
920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.bindedfuncs.append(func)
930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def unbind(self, triplet, func):
950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.bindedfuncs.remove(func)
960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if not self.bindedfuncs:
970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.widget.unbind(self.widgetinst, self.sequence, self.handlerid)
980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.handlerid = None
990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __del__(self):
1010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.handlerid:
1020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.widget.unbind(self.widgetinst, self.sequence, self.handlerid)
1030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# An int in range(1 << len(_modifiers)) represents a combination of modifiers
1050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# (if the least significent bit is on, _modifiers[0] is on, and so on).
1060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# _state_subsets gives for each combination of modifiers, or *state*,
1070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# a list of the states which are a subset of it. This list is ordered by the
1080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# number of modifiers is the state - the most specific state comes first.
1090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_states = range(1 << len(_modifiers))
1100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_state_names = [''.join(m[0]+'-'
1110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                        for i, m in enumerate(_modifiers)
1120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                        if (1 << i) & s)
1130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                for s in _states]
1140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1150a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef expand_substates(states):
1160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    '''For each item of states return a list containing all combinations of
1170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    that item with individual bits reset, sorted by the number of set bits.
1180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    '''
1190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def nbits(n):
1200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        "number of bits set in n base 2"
1210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        nb = 0
1220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        while n:
1230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            n, rem = divmod(n, 2)
1240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            nb += rem
1250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return nb
1260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    statelist = []
1270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    for state in states:
1280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        substates = list(set(state & x for x in states))
1290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        substates.sort(key=nbits, reverse=True)
1300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        statelist.append(substates)
1310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    return statelist
1320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_state_subsets = expand_substates(_states)
1340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# _state_codes gives for each state, the portable code to be passed as mc_state
1360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_state_codes = []
1370a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofor s in _states:
1380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    r = 0
1390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    for i in range(len(_modifiers)):
1400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if (1 << i) & s:
1410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            r |= _modifier_masks[i]
1420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    _state_codes.append(r)
1430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1440a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass _ComplexBinder:
1450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # This class binds many functions, and only unbinds them when it is deleted.
1460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # self.handlerids is the list of seqs and ids of binded handler functions.
1470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # The binded functions sit in a dictionary of lists of lists, which maps
1480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # a detail (or None) and a state into a list of functions.
1490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # When a new detail is discovered, handlers for all the possible states
1500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # are binded.
1510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __create_handler(self, lists, mc_type, mc_state):
1530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        def handler(event, lists = lists,
1540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    mc_type = mc_type, mc_state = mc_state,
1550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    ishandlerrunning = self.ishandlerrunning,
1560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    doafterhandler = self.doafterhandler):
1570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            ishandlerrunning[:] = [True]
1580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            event.mc_type = mc_type
1590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            event.mc_state = mc_state
1600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            wascalled = {}
1610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            r = None
1620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            for l in lists:
1630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                for i in range(len(l)-1, -1, -1):
1640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    func = l[i]
1650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    if func not in wascalled:
1660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                        wascalled[func] = True
1670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                        r = l[i](event)
1680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                        if r:
1690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                            break
1700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                if r:
1710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    break
1720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            ishandlerrunning[:] = []
1730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # Call all functions in doafterhandler and remove them from list
1740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            for f in doafterhandler:
1750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                f()
1760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            doafterhandler[:] = []
1770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if r:
1780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                return r
1790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return handler
1800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __init__(self, type, widget, widgetinst):
1820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.type = type
1830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.typename = _types[type][0]
1840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.widget = widget
1850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.widgetinst = widgetinst
1860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.bindedfuncs = {None: [[] for s in _states]}
1870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.handlerids = []
1880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # we don't want to change the lists of functions while a handler is
1890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # running - it will mess up the loop and anyway, we usually want the
1900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # change to happen from the next event. So we have a list of functions
1910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # for the handler to run after it finishes calling the binded functions.
1920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # It calls them only once.
1930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # ishandlerrunning is a list. An empty one means no, otherwise - yes.
1940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # this is done so that it would be mutable.
1950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.ishandlerrunning = []
1960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.doafterhandler = []
1970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        for s in _states:
1980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            lists = [self.bindedfuncs[None][i] for i in _state_subsets[s]]
1990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            handler = self.__create_handler(lists, type, _state_codes[s])
2000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            seq = '<'+_state_names[s]+self.typename+'>'
2010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.handlerids.append((seq, self.widget.bind(self.widgetinst,
2020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                                                          seq, handler)))
2030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def bind(self, triplet, func):
2050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if triplet[2] not in self.bindedfuncs:
2060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.bindedfuncs[triplet[2]] = [[] for s in _states]
2070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            for s in _states:
2080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                lists = [ self.bindedfuncs[detail][i]
2090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                          for detail in (triplet[2], None)
2100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                          for i in _state_subsets[s]       ]
2110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                handler = self.__create_handler(lists, self.type,
2120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                                                _state_codes[s])
2130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                seq = "<%s%s-%s>"% (_state_names[s], self.typename, triplet[2])
2140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                self.handlerids.append((seq, self.widget.bind(self.widgetinst,
2150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                                                              seq, handler)))
2160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].append(func)
2170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if not self.ishandlerrunning:
2180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            doit()
2190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
2200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.doafterhandler.append(doit)
2210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def unbind(self, triplet, func):
2230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].remove(func)
2240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if not self.ishandlerrunning:
2250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            doit()
2260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
2270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.doafterhandler.append(doit)
2280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __del__(self):
2300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        for seq, id in self.handlerids:
2310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.widget.unbind(self.widgetinst, seq, id)
2320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# define the list of event types to be handled by MultiEvent. the order is
2340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# compatible with the definition of event type constants.
2350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_types = (
2360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    ("KeyPress", "Key"), ("KeyRelease",), ("ButtonPress", "Button"),
2370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    ("ButtonRelease",), ("Activate",), ("Circulate",), ("Colormap",),
2380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    ("Configure",), ("Deactivate",), ("Destroy",), ("Enter",), ("Expose",),
2390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    ("FocusIn",), ("FocusOut",), ("Gravity",), ("Leave",), ("Map",),
2400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    ("Motion",), ("MouseWheel",), ("Property",), ("Reparent",), ("Unmap",),
2410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    ("Visibility",),
2420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao)
2430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# which binder should be used for every event type?
2450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_binder_classes = (_ComplexBinder,) * 4 + (_SimpleBinder,) * (len(_types)-4)
2460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# A dictionary to map a type name into its number
2480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_type_names = dict([(name, number)
2490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                     for number in range(len(_types))
2500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                     for name in _types[number]])
2510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_keysym_re = re.compile(r"^\w+$")
2530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_button_re = re.compile(r"^[1-5]$")
2540a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef _parse_sequence(sequence):
2550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    """Get a string which should describe an event sequence. If it is
2560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    successfully parsed as one, return a tuple containing the state (as an int),
2570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    the event type (as an index of _types), and the detail - None if none, or a
2580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    string if there is one. If the parsing is unsuccessful, return None.
2590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    """
2600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    if not sequence or sequence[0] != '<' or sequence[-1] != '>':
2610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return None
2620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    words = string.split(sequence[1:-1], '-')
2630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    modifiers = 0
2650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    while words and words[0] in _modifier_names:
2660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        modifiers |= 1 << _modifier_names[words[0]]
2670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        del words[0]
2680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    if words and words[0] in _type_names:
2700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        type = _type_names[words[0]]
2710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        del words[0]
2720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    else:
2730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return None
2740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    if _binder_classes[type] is _SimpleBinder:
2760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if modifiers or words:
2770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return None
2780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
2790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            detail = None
2800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    else:
2810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # _ComplexBinder
2820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if type in [_type_names[s] for s in ("KeyPress", "KeyRelease")]:
2830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            type_re = _keysym_re
2840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
2850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            type_re = _button_re
2860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if not words:
2880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            detail = None
2890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        elif len(words) == 1 and type_re.match(words[0]):
2900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            detail = words[0]
2910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
2920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return None
2930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    return modifiers, type, detail
2950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2960a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef _triplet_to_sequence(triplet):
2970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    if triplet[2]:
2980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'-'+ \
2990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao               triplet[2]+'>'
3000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    else:
3010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'>'
3020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_multicall_dict = {}
3040a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef MultiCallCreator(widget):
3050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    """Return a MultiCall class which inherits its methods from the
3060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    given widget class (for example, Tkinter.Text). This is used
3070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    instead of a templating mechanism.
3080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    """
3090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    if widget in _multicall_dict:
3100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return _multicall_dict[widget]
3110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    class MultiCall (widget):
3130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        assert issubclass(widget, Tkinter.Misc)
3140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        def __init__(self, *args, **kwargs):
3160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            widget.__init__(self, *args, **kwargs)
3170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # a dictionary which maps a virtual event to a tuple with:
3180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            #  0. the function binded
3190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            #  1. a list of triplets - the sequences it is binded to
3200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.__eventinfo = {}
3210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.__binders = [_binder_classes[i](i, widget, self)
3220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                              for i in range(len(_types))]
3230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        def bind(self, sequence=None, func=None, add=None):
3250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            #print "bind(%s, %s, %s) called." % (sequence, func, add)
3260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if type(sequence) is str and len(sequence) > 2 and \
3270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao               sequence[:2] == "<<" and sequence[-2:] == ">>":
3280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                if sequence in self.__eventinfo:
3290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    ei = self.__eventinfo[sequence]
3300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    if ei[0] is not None:
3310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                        for triplet in ei[1]:
3320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                            self.__binders[triplet[1]].unbind(triplet, ei[0])
3330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    ei[0] = func
3340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    if ei[0] is not None:
3350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                        for triplet in ei[1]:
3360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                            self.__binders[triplet[1]].bind(triplet, func)
3370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                else:
3380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    self.__eventinfo[sequence] = [func, []]
3390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return widget.bind(self, sequence, func, add)
3400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        def unbind(self, sequence, funcid=None):
3420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if type(sequence) is str and len(sequence) > 2 and \
3430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao               sequence[:2] == "<<" and sequence[-2:] == ">>" and \
3440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao               sequence in self.__eventinfo:
3450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                func, triplets = self.__eventinfo[sequence]
3460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                if func is not None:
3470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    for triplet in triplets:
3480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                        self.__binders[triplet[1]].unbind(triplet, func)
3490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    self.__eventinfo[sequence][0] = None
3500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return widget.unbind(self, sequence, funcid)
3510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        def event_add(self, virtual, *sequences):
3530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            #print "event_add(%s,%s) was called"%(repr(virtual),repr(sequences))
3540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if virtual not in self.__eventinfo:
3550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                self.__eventinfo[virtual] = [None, []]
3560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            func, triplets = self.__eventinfo[virtual]
3580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            for seq in sequences:
3590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                triplet = _parse_sequence(seq)
3600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                if triplet is None:
3610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    #print >> sys.stderr, "Seq. %s was added by Tkinter."%seq
3620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    widget.event_add(self, virtual, seq)
3630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                else:
3640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    if func is not None:
3650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                        self.__binders[triplet[1]].bind(triplet, func)
3660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    triplets.append(triplet)
3670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        def event_delete(self, virtual, *sequences):
3690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if virtual not in self.__eventinfo:
3700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                return
3710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            func, triplets = self.__eventinfo[virtual]
3720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            for seq in sequences:
3730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                triplet = _parse_sequence(seq)
3740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                if triplet is None:
3750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    #print >> sys.stderr, "Seq. %s was deleted by Tkinter."%seq
3760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    widget.event_delete(self, virtual, seq)
3770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                else:
3780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    if func is not None:
3790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                        self.__binders[triplet[1]].unbind(triplet, func)
3800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    triplets.remove(triplet)
3810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        def event_info(self, virtual=None):
3830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if virtual is None or virtual not in self.__eventinfo:
3840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                return widget.event_info(self, virtual)
3850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            else:
3860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                return tuple(map(_triplet_to_sequence,
3870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                                 self.__eventinfo[virtual][1])) + \
3880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                       widget.event_info(self, virtual)
3890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        def __del__(self):
3910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            for virtual in self.__eventinfo:
3920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                func, triplets = self.__eventinfo[virtual]
3930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                if func:
3940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    for triplet in triplets:
3950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                        self.__binders[triplet[1]].unbind(triplet, func)
3960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    _multicall_dict[widget] = MultiCall
3990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    return MultiCall
4000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
4010a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoif __name__ == "__main__":
4020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # Test
4030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    root = Tkinter.Tk()
4040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    text = MultiCallCreator(Tkinter.Text)(root)
4050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    text.pack()
4060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def bindseq(seq, n=[0]):
4070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        def handler(event):
4080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            print seq
4090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.bind("<<handler%d>>"%n[0], handler)
4100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.event_add("<<handler%d>>"%n[0], seq)
4110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        n[0] += 1
4120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    bindseq("<Key>")
4130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    bindseq("<Control-Key>")
4140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    bindseq("<Alt-Key-a>")
4150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    bindseq("<Control-Key-a>")
4160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    bindseq("<Alt-Control-Key-a>")
4170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    bindseq("<Key-b>")
4180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    bindseq("<Control-Button-1>")
4190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    bindseq("<Alt-Button-1>")
4200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    bindseq("<FocusOut>")
4210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    bindseq("<Enter>")
4220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    bindseq("<Leave>")
4230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    root.mainloop()
424