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