1"""MiniAEFrame - A minimal AppleEvent Application framework.
2
3There are two classes:
4    AEServer -- a mixin class offering nice AE handling.
5    MiniApplication -- a very minimal alternative to FrameWork.py,
6        only suitable for the simplest of AppleEvent servers.
7"""
8
9from warnings import warnpy3k
10warnpy3k("In 3.x, the MiniAEFrame module is removed.", stacklevel=2)
11
12import traceback
13import MacOS
14from Carbon import AE
15from Carbon.AppleEvents import *
16from Carbon import Evt
17from Carbon.Events import *
18from Carbon import Menu
19from Carbon import Win
20from Carbon.Windows import *
21from Carbon import Qd
22
23import aetools
24import EasyDialogs
25
26kHighLevelEvent = 23                # Not defined anywhere for Python yet?
27
28
29class MiniApplication:
30
31    """A minimal FrameWork.Application-like class"""
32
33    def __init__(self):
34        self.quitting = 0
35        # Initialize menu
36        self.appleid = 1
37        self.quitid = 2
38        Menu.ClearMenuBar()
39        self.applemenu = applemenu = Menu.NewMenu(self.appleid, "\024")
40        applemenu.AppendMenu("%s;(-" % self.getaboutmenutext())
41        if MacOS.runtimemodel == 'ppc':
42            applemenu.AppendResMenu('DRVR')
43        applemenu.InsertMenu(0)
44        self.quitmenu = Menu.NewMenu(self.quitid, "File")
45        self.quitmenu.AppendMenu("Quit")
46        self.quitmenu.SetItemCmd(1, ord("Q"))
47        self.quitmenu.InsertMenu(0)
48        Menu.DrawMenuBar()
49
50    def __del__(self):
51        self.close()
52
53    def close(self):
54        pass
55
56    def mainloop(self, mask = everyEvent, timeout = 60*60):
57        while not self.quitting:
58            self.dooneevent(mask, timeout)
59
60    def _quit(self):
61        self.quitting = 1
62
63    def dooneevent(self, mask = everyEvent, timeout = 60*60):
64        got, event = Evt.WaitNextEvent(mask, timeout)
65        if got:
66            self.lowlevelhandler(event)
67
68    def lowlevelhandler(self, event):
69        what, message, when, where, modifiers = event
70        h, v = where
71        if what == kHighLevelEvent:
72            msg = "High Level Event: %r %r" % (code(message), code(h | (v<<16)))
73            try:
74                AE.AEProcessAppleEvent(event)
75            except AE.Error, err:
76                print 'AE error: ', err
77                print 'in', msg
78                traceback.print_exc()
79            return
80        elif what == keyDown:
81            c = chr(message & charCodeMask)
82            if modifiers & cmdKey:
83                if c == '.':
84                    raise KeyboardInterrupt, "Command-period"
85                if c == 'q':
86                    if hasattr(MacOS, 'OutputSeen'):
87                        MacOS.OutputSeen()
88                    self.quitting = 1
89                    return
90        elif what == mouseDown:
91            partcode, window = Win.FindWindow(where)
92            if partcode == inMenuBar:
93                result = Menu.MenuSelect(where)
94                id = (result>>16) & 0xffff      # Hi word
95                item = result & 0xffff      # Lo word
96                if id == self.appleid:
97                    if item == 1:
98                        EasyDialogs.Message(self.getabouttext())
99                    elif item > 1 and hasattr(Menu, 'OpenDeskAcc'):
100                        name = self.applemenu.GetMenuItemText(item)
101                        Menu.OpenDeskAcc(name)
102                elif id == self.quitid and item == 1:
103                    if hasattr(MacOS, 'OutputSeen'):
104                        MacOS.OutputSeen()
105                    self.quitting = 1
106                Menu.HiliteMenu(0)
107                return
108        # Anything not handled is passed to Python/SIOUX
109        if hasattr(MacOS, 'HandleEvent'):
110            MacOS.HandleEvent(event)
111        else:
112            print "Unhandled event:", event
113
114    def getabouttext(self):
115        return self.__class__.__name__
116
117    def getaboutmenutext(self):
118        return "About %s\311" % self.__class__.__name__
119
120
121class AEServer:
122
123    def __init__(self):
124        self.ae_handlers = {}
125
126    def installaehandler(self, classe, type, callback):
127        AE.AEInstallEventHandler(classe, type, self.callback_wrapper)
128        self.ae_handlers[(classe, type)] = callback
129
130    def close(self):
131        for classe, type in self.ae_handlers.keys():
132            AE.AERemoveEventHandler(classe, type)
133
134    def callback_wrapper(self, _request, _reply):
135        _parameters, _attributes = aetools.unpackevent(_request)
136        _class = _attributes['evcl'].type
137        _type = _attributes['evid'].type
138
139        if (_class, _type) in self.ae_handlers:
140            _function = self.ae_handlers[(_class, _type)]
141        elif (_class, '****') in self.ae_handlers:
142            _function = self.ae_handlers[(_class, '****')]
143        elif ('****', '****') in self.ae_handlers:
144            _function = self.ae_handlers[('****', '****')]
145        else:
146            raise 'Cannot happen: AE callback without handler', (_class, _type)
147
148        # XXXX Do key-to-name mapping here
149
150        _parameters['_attributes'] = _attributes
151        _parameters['_class'] = _class
152        _parameters['_type'] = _type
153        if '----' in _parameters:
154            _object = _parameters['----']
155            del _parameters['----']
156            # The try/except that used to be here can mask programmer errors.
157            # Let the program crash, the programmer can always add a **args
158            # to the formal parameter list.
159            rv = _function(_object, **_parameters)
160        else:
161            #Same try/except comment as above
162            rv = _function(**_parameters)
163
164        if rv is None:
165            aetools.packevent(_reply, {})
166        else:
167            aetools.packevent(_reply, {'----':rv})
168
169
170def code(x):
171    "Convert a long int to the 4-character code it really is"
172    s = ''
173    for i in range(4):
174        x, c = divmod(x, 256)
175        s = chr(c) + s
176    return s
177
178class _Test(AEServer, MiniApplication):
179    """Mini test application, handles required events"""
180
181    def __init__(self):
182        MiniApplication.__init__(self)
183        AEServer.__init__(self)
184        self.installaehandler('aevt', 'oapp', self.open_app)
185        self.installaehandler('aevt', 'quit', self.quit)
186        self.installaehandler('****', '****', self.other)
187        self.mainloop()
188
189    def quit(self, **args):
190        self._quit()
191
192    def open_app(self, **args):
193        pass
194
195    def other(self, _object=None, _class=None, _type=None, **args):
196        print 'AppleEvent', (_class, _type), 'for', _object, 'Other args:', args
197
198
199if __name__ == '__main__':
200    _Test()
201