symbian-qemu-0.9.1-12/python-2.6.1/Lib/idlelib/MultiCall.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     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 string
       
    34 import re
       
    35 import Tkinter
       
    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 sys.platform == "darwin" and sys.executable.count(".app"):
       
    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 = [reduce(lambda x, y: x + y,
       
   110                        [_modifiers[i][0]+'-' for i in range(len(_modifiers))
       
   111                         if (1 << i) & s],
       
   112                        "")
       
   113                 for s in _states]
       
   114 _state_subsets = map(lambda i: filter(lambda j: not (j & (~i)), _states),
       
   115                       _states)
       
   116 for l in _state_subsets:
       
   117     l.sort(lambda a, b, nummod = lambda x: len(filter(lambda i: (1<<i) & x,
       
   118                                                       range(len(_modifiers)))):
       
   119            nummod(b) - nummod(a))
       
   120 # _state_codes gives for each state, the portable code to be passed as mc_state
       
   121 _state_codes = [reduce(lambda x, y: x | y,
       
   122                        [_modifier_masks[i] for i in range(len(_modifiers))
       
   123                         if (1 << i) & s],
       
   124                        0)
       
   125                 for s in _states]
       
   126 
       
   127 class _ComplexBinder:
       
   128     # This class binds many functions, and only unbinds them when it is deleted.
       
   129     # self.handlerids is the list of seqs and ids of binded handler functions.
       
   130     # The binded functions sit in a dictionary of lists of lists, which maps
       
   131     # a detail (or None) and a state into a list of functions.
       
   132     # When a new detail is discovered, handlers for all the possible states
       
   133     # are binded.
       
   134 
       
   135     def __create_handler(self, lists, mc_type, mc_state):
       
   136         def handler(event, lists = lists,
       
   137                     mc_type = mc_type, mc_state = mc_state,
       
   138                     ishandlerrunning = self.ishandlerrunning,
       
   139                     doafterhandler = self.doafterhandler):
       
   140             ishandlerrunning[:] = [True]
       
   141             event.mc_type = mc_type
       
   142             event.mc_state = mc_state
       
   143             wascalled = {}
       
   144             r = None
       
   145             for l in lists:
       
   146                 for i in range(len(l)-1, -1, -1):
       
   147                     func = l[i]
       
   148                     if func not in wascalled:
       
   149                         wascalled[func] = True
       
   150                         r = l[i](event)
       
   151                         if r:
       
   152                             break
       
   153                 if r:
       
   154                     break
       
   155             ishandlerrunning[:] = []
       
   156             # Call all functions in doafterhandler and remove them from list
       
   157             while doafterhandler:
       
   158                 doafterhandler.pop()()
       
   159             if r:
       
   160                 return r
       
   161         return handler
       
   162 
       
   163     def __init__(self, type, widget, widgetinst):
       
   164         self.type = type
       
   165         self.typename = _types[type][0]
       
   166         self.widget = widget
       
   167         self.widgetinst = widgetinst
       
   168         self.bindedfuncs = {None: [[] for s in _states]}
       
   169         self.handlerids = []
       
   170         # we don't want to change the lists of functions while a handler is
       
   171         # running - it will mess up the loop and anyway, we usually want the
       
   172         # change to happen from the next event. So we have a list of functions
       
   173         # for the handler to run after it finishes calling the binded functions.
       
   174         # It calls them only once.
       
   175         # ishandlerrunning is a list. An empty one means no, otherwise - yes.
       
   176         # this is done so that it would be mutable.
       
   177         self.ishandlerrunning = []
       
   178         self.doafterhandler = []
       
   179         for s in _states:
       
   180             lists = [self.bindedfuncs[None][i] for i in _state_subsets[s]]
       
   181             handler = self.__create_handler(lists, type, _state_codes[s])
       
   182             seq = '<'+_state_names[s]+self.typename+'>'
       
   183             self.handlerids.append((seq, self.widget.bind(self.widgetinst,
       
   184                                                           seq, handler)))
       
   185 
       
   186     def bind(self, triplet, func):
       
   187         if not self.bindedfuncs.has_key(triplet[2]):
       
   188             self.bindedfuncs[triplet[2]] = [[] for s in _states]
       
   189             for s in _states:
       
   190                 lists = [ self.bindedfuncs[detail][i]
       
   191                           for detail in (triplet[2], None)
       
   192                           for i in _state_subsets[s]       ]
       
   193                 handler = self.__create_handler(lists, self.type,
       
   194                                                 _state_codes[s])
       
   195                 seq = "<%s%s-%s>"% (_state_names[s], self.typename, triplet[2])
       
   196                 self.handlerids.append((seq, self.widget.bind(self.widgetinst,
       
   197                                                               seq, handler)))
       
   198         doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].append(func)
       
   199         if not self.ishandlerrunning:
       
   200             doit()
       
   201         else:
       
   202             self.doafterhandler.append(doit)
       
   203 
       
   204     def unbind(self, triplet, func):
       
   205         doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].remove(func)
       
   206         if not self.ishandlerrunning:
       
   207             doit()
       
   208         else:
       
   209             self.doafterhandler.append(doit)
       
   210 
       
   211     def __del__(self):
       
   212         for seq, id in self.handlerids:
       
   213             self.widget.unbind(self.widgetinst, seq, id)
       
   214 
       
   215 # define the list of event types to be handled by MultiEvent. the order is
       
   216 # compatible with the definition of event type constants.
       
   217 _types = (
       
   218     ("KeyPress", "Key"), ("KeyRelease",), ("ButtonPress", "Button"),
       
   219     ("ButtonRelease",), ("Activate",), ("Circulate",), ("Colormap",),
       
   220     ("Configure",), ("Deactivate",), ("Destroy",), ("Enter",), ("Expose",),
       
   221     ("FocusIn",), ("FocusOut",), ("Gravity",), ("Leave",), ("Map",),
       
   222     ("Motion",), ("MouseWheel",), ("Property",), ("Reparent",), ("Unmap",),
       
   223     ("Visibility",),
       
   224 )
       
   225 
       
   226 # which binder should be used for every event type?
       
   227 _binder_classes = (_ComplexBinder,) * 4 + (_SimpleBinder,) * (len(_types)-4)
       
   228 
       
   229 # A dictionary to map a type name into its number
       
   230 _type_names = dict([(name, number)
       
   231                      for number in range(len(_types))
       
   232                      for name in _types[number]])
       
   233 
       
   234 _keysym_re = re.compile(r"^\w+$")
       
   235 _button_re = re.compile(r"^[1-5]$")
       
   236 def _parse_sequence(sequence):
       
   237     """Get a string which should describe an event sequence. If it is
       
   238     successfully parsed as one, return a tuple containing the state (as an int),
       
   239     the event type (as an index of _types), and the detail - None if none, or a
       
   240     string if there is one. If the parsing is unsuccessful, return None.
       
   241     """
       
   242     if not sequence or sequence[0] != '<' or sequence[-1] != '>':
       
   243         return None
       
   244     words = string.split(sequence[1:-1], '-')
       
   245 
       
   246     modifiers = 0
       
   247     while words and words[0] in _modifier_names:
       
   248         modifiers |= 1 << _modifier_names[words[0]]
       
   249         del words[0]
       
   250 
       
   251     if words and words[0] in _type_names:
       
   252         type = _type_names[words[0]]
       
   253         del words[0]
       
   254     else:
       
   255         return None
       
   256 
       
   257     if _binder_classes[type] is _SimpleBinder:
       
   258         if modifiers or words:
       
   259             return None
       
   260         else:
       
   261             detail = None
       
   262     else:
       
   263         # _ComplexBinder
       
   264         if type in [_type_names[s] for s in ("KeyPress", "KeyRelease")]:
       
   265             type_re = _keysym_re
       
   266         else:
       
   267             type_re = _button_re
       
   268 
       
   269         if not words:
       
   270             detail = None
       
   271         elif len(words) == 1 and type_re.match(words[0]):
       
   272             detail = words[0]
       
   273         else:
       
   274             return None
       
   275 
       
   276     return modifiers, type, detail
       
   277 
       
   278 def _triplet_to_sequence(triplet):
       
   279     if triplet[2]:
       
   280         return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'-'+ \
       
   281                triplet[2]+'>'
       
   282     else:
       
   283         return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'>'
       
   284 
       
   285 _multicall_dict = {}
       
   286 def MultiCallCreator(widget):
       
   287     """Return a MultiCall class which inherits its methods from the
       
   288     given widget class (for example, Tkinter.Text). This is used
       
   289     instead of a templating mechanism.
       
   290     """
       
   291     if widget in _multicall_dict:
       
   292         return _multicall_dict[widget]
       
   293 
       
   294     class MultiCall (widget):
       
   295         assert issubclass(widget, Tkinter.Misc)
       
   296 
       
   297         def __init__(self, *args, **kwargs):
       
   298             apply(widget.__init__, (self,)+args, kwargs)
       
   299             # a dictionary which maps a virtual event to a tuple with:
       
   300             #  0. the function binded
       
   301             #  1. a list of triplets - the sequences it is binded to
       
   302             self.__eventinfo = {}
       
   303             self.__binders = [_binder_classes[i](i, widget, self)
       
   304                               for i in range(len(_types))]
       
   305 
       
   306         def bind(self, sequence=None, func=None, add=None):
       
   307             #print "bind(%s, %s, %s) called." % (sequence, func, add)
       
   308             if type(sequence) is str and len(sequence) > 2 and \
       
   309                sequence[:2] == "<<" and sequence[-2:] == ">>":
       
   310                 if sequence in self.__eventinfo:
       
   311                     ei = self.__eventinfo[sequence]
       
   312                     if ei[0] is not None:
       
   313                         for triplet in ei[1]:
       
   314                             self.__binders[triplet[1]].unbind(triplet, ei[0])
       
   315                     ei[0] = func
       
   316                     if ei[0] is not None:
       
   317                         for triplet in ei[1]:
       
   318                             self.__binders[triplet[1]].bind(triplet, func)
       
   319                 else:
       
   320                     self.__eventinfo[sequence] = [func, []]
       
   321             return widget.bind(self, sequence, func, add)
       
   322 
       
   323         def unbind(self, sequence, funcid=None):
       
   324             if type(sequence) is str and len(sequence) > 2 and \
       
   325                sequence[:2] == "<<" and sequence[-2:] == ">>" and \
       
   326                sequence in self.__eventinfo:
       
   327                 func, triplets = self.__eventinfo[sequence]
       
   328                 if func is not None:
       
   329                     for triplet in triplets:
       
   330                         self.__binders[triplet[1]].unbind(triplet, func)
       
   331                     self.__eventinfo[sequence][0] = None
       
   332             return widget.unbind(self, sequence, funcid)
       
   333 
       
   334         def event_add(self, virtual, *sequences):
       
   335             #print "event_add(%s,%s) was called"%(repr(virtual),repr(sequences))
       
   336             if virtual not in self.__eventinfo:
       
   337                 self.__eventinfo[virtual] = [None, []]
       
   338 
       
   339             func, triplets = self.__eventinfo[virtual]
       
   340             for seq in sequences:
       
   341                 triplet = _parse_sequence(seq)
       
   342                 if triplet is None:
       
   343                     #print >> sys.stderr, "Seq. %s was added by Tkinter."%seq
       
   344                     widget.event_add(self, virtual, seq)
       
   345                 else:
       
   346                     if func is not None:
       
   347                         self.__binders[triplet[1]].bind(triplet, func)
       
   348                     triplets.append(triplet)
       
   349 
       
   350         def event_delete(self, virtual, *sequences):
       
   351             if virtual not in self.__eventinfo:
       
   352                 return
       
   353             func, triplets = self.__eventinfo[virtual]
       
   354             for seq in sequences:
       
   355                 triplet = _parse_sequence(seq)
       
   356                 if triplet is None:
       
   357                     #print >> sys.stderr, "Seq. %s was deleted by Tkinter."%seq
       
   358                     widget.event_delete(self, virtual, seq)
       
   359                 else:
       
   360                     if func is not None:
       
   361                         self.__binders[triplet[1]].unbind(triplet, func)
       
   362                     triplets.remove(triplet)
       
   363 
       
   364         def event_info(self, virtual=None):
       
   365             if virtual is None or virtual not in self.__eventinfo:
       
   366                 return widget.event_info(self, virtual)
       
   367             else:
       
   368                 return tuple(map(_triplet_to_sequence,
       
   369                                  self.__eventinfo[virtual][1])) + \
       
   370                        widget.event_info(self, virtual)
       
   371 
       
   372         def __del__(self):
       
   373             for virtual in self.__eventinfo:
       
   374                 func, triplets = self.__eventinfo[virtual]
       
   375                 if func:
       
   376                     for triplet in triplets:
       
   377                         self.__binders[triplet[1]].unbind(triplet, func)
       
   378 
       
   379 
       
   380     _multicall_dict[widget] = MultiCall
       
   381     return MultiCall
       
   382 
       
   383 if __name__ == "__main__":
       
   384     # Test
       
   385     root = Tkinter.Tk()
       
   386     text = MultiCallCreator(Tkinter.Text)(root)
       
   387     text.pack()
       
   388     def bindseq(seq, n=[0]):
       
   389         def handler(event):
       
   390             print seq
       
   391         text.bind("<<handler%d>>"%n[0], handler)
       
   392         text.event_add("<<handler%d>>"%n[0], seq)
       
   393         n[0] += 1
       
   394     bindseq("<Key>")
       
   395     bindseq("<Control-Key>")
       
   396     bindseq("<Alt-Key-a>")
       
   397     bindseq("<Control-Key-a>")
       
   398     bindseq("<Alt-Control-Key-a>")
       
   399     bindseq("<Key-b>")
       
   400     bindseq("<Control-Button-1>")
       
   401     bindseq("<Alt-Button-1>")
       
   402     bindseq("<FocusOut>")
       
   403     bindseq("<Enter>")
       
   404     bindseq("<Leave>")
       
   405     root.mainloop()