blob: 5495dfafb6a9dba0afbda4e9a1c4335352b04177 [file] [log] [blame]
Jack Jansend0fc42f2001-08-19 22:05:06 +00001"""Tools for use in AppleEvent clients and servers.
2
3pack(x) converts a Python object to an AEDesc object
4unpack(desc) does the reverse
5
6packevent(event, parameters, attributes) sets params and attrs in an AEAppleEvent record
7unpackevent(event) returns the parameters and attributes from an AEAppleEvent record
8
9Plus... Lots of classes and routines that help representing AE objects,
10ranges, conditionals, logicals, etc., so you can write, e.g.:
11
12 x = Character(1, Document("foobar"))
13
14and pack(x) will create an AE object reference equivalent to AppleScript's
15
16 character 1 of document "foobar"
17
18Some of the stuff that appears to be exported from this module comes from other
19files: the pack stuff from aepack, the objects from aetypes.
20
21"""
22
23
24from types import *
Jack Jansen5a6fdcd2001-08-25 12:15:04 +000025from Carbon import AE
26from Carbon import AppleEvents
Jack Jansend0fc42f2001-08-19 22:05:06 +000027import MacOS
28import sys
29
30from aetypes import *
Jack Jansen8b777672002-08-07 14:49:00 +000031from aepack import packkey, pack, unpack, coerce, AEDescType
Jack Jansend0fc42f2001-08-19 22:05:06 +000032
33Error = 'aetools.Error'
34
35# Special code to unpack an AppleEvent (which is *not* a disguised record!)
36# Note by Jack: No??!? If I read the docs correctly it *is*....
37
38aekeywords = [
39 'tran',
40 'rtid',
41 'evcl',
42 'evid',
43 'addr',
44 'optk',
45 'timo',
46 'inte', # this attribute is read only - will be set in AESend
47 'esrc', # this attribute is read only
48 'miss', # this attribute is read only
49 'from' # new in 1.0.1
50]
51
52def missed(ae):
53 try:
54 desc = ae.AEGetAttributeDesc('miss', 'keyw')
55 except AE.Error, msg:
56 return None
57 return desc.data
58
Jack Jansen8b777672002-08-07 14:49:00 +000059def unpackevent(ae, formodulename=""):
Jack Jansend0fc42f2001-08-19 22:05:06 +000060 parameters = {}
61 try:
62 dirobj = ae.AEGetParamDesc('----', '****')
63 except AE.Error:
64 pass
65 else:
Jack Jansen8b777672002-08-07 14:49:00 +000066 parameters['----'] = unpack(dirobj, formodulename)
Jack Jansend0fc42f2001-08-19 22:05:06 +000067 del dirobj
68 while 1:
69 key = missed(ae)
70 if not key: break
Jack Jansen8b777672002-08-07 14:49:00 +000071 parameters[key] = unpack(ae.AEGetParamDesc(key, '****'), formodulename)
Jack Jansend0fc42f2001-08-19 22:05:06 +000072 attributes = {}
73 for key in aekeywords:
74 try:
75 desc = ae.AEGetAttributeDesc(key, '****')
76 except (AE.Error, MacOS.Error), msg:
77 if msg[0] != -1701 and msg[0] != -1704:
78 raise sys.exc_type, sys.exc_value
79 continue
Jack Jansen8b777672002-08-07 14:49:00 +000080 attributes[key] = unpack(desc, formodulename)
Jack Jansend0fc42f2001-08-19 22:05:06 +000081 return parameters, attributes
82
83def packevent(ae, parameters = {}, attributes = {}):
84 for key, value in parameters.items():
Jack Jansen8b777672002-08-07 14:49:00 +000085 packkey(ae, key, value)
Jack Jansend0fc42f2001-08-19 22:05:06 +000086 for key, value in attributes.items():
Jack Jansen8b777672002-08-07 14:49:00 +000087 packkey(ae, key, value)
Jack Jansend0fc42f2001-08-19 22:05:06 +000088
89#
90# Support routine for automatically generated Suite interfaces
91# These routines are also useable for the reverse function.
92#
93def keysubst(arguments, keydict):
94 """Replace long name keys by their 4-char counterparts, and check"""
95 ok = keydict.values()
96 for k in arguments.keys():
97 if keydict.has_key(k):
98 v = arguments[k]
99 del arguments[k]
100 arguments[keydict[k]] = v
101 elif k != '----' and k not in ok:
102 raise TypeError, 'Unknown keyword argument: %s'%k
103
104def enumsubst(arguments, key, edict):
105 """Substitute a single enum keyword argument, if it occurs"""
106 if not arguments.has_key(key) or edict is None:
107 return
108 v = arguments[key]
109 ok = edict.values()
110 if edict.has_key(v):
111 arguments[key] = edict[v]
112 elif not v in ok:
113 raise TypeError, 'Unknown enumerator: %s'%v
114
115def decodeerror(arguments):
116 """Create the 'best' argument for a raise MacOS.Error"""
117 errn = arguments['errn']
118 err_a1 = errn
119 if arguments.has_key('errs'):
120 err_a2 = arguments['errs']
121 else:
122 err_a2 = MacOS.GetErrorString(errn)
123 if arguments.has_key('erob'):
124 err_a3 = arguments['erob']
125 else:
126 err_a3 = None
127
128 return (err_a1, err_a2, err_a3)
129
130class TalkTo:
131 """An AE connection to an application"""
132 _signature = None # Can be overridden by subclasses
Jack Jansen8b777672002-08-07 14:49:00 +0000133 _moduleName = None # Can be overridden by subclasses
Jack Jansend0fc42f2001-08-19 22:05:06 +0000134
135 def __init__(self, signature=None, start=0, timeout=0):
136 """Create a communication channel with a particular application.
137
138 Addressing the application is done by specifying either a
139 4-byte signature, an AEDesc or an object that will __aepack__
140 to an AEDesc.
141 """
142 self.target_signature = None
143 if signature is None:
144 signature = self._signature
145 if type(signature) == AEDescType:
146 self.target = signature
147 elif type(signature) == InstanceType and hasattr(signature, '__aepack__'):
148 self.target = signature.__aepack__()
149 elif type(signature) == StringType and len(signature) == 4:
150 self.target = AE.AECreateDesc(AppleEvents.typeApplSignature, signature)
151 self.target_signature = signature
152 else:
153 raise TypeError, "signature should be 4-char string or AEDesc"
154 self.send_flags = AppleEvents.kAEWaitReply
155 self.send_priority = AppleEvents.kAENormalPriority
156 if timeout:
157 self.send_timeout = timeout
158 else:
159 self.send_timeout = AppleEvents.kAEDefaultTimeout
160 if start:
Jack Jansenc8febec2002-01-23 22:46:30 +0000161 self._start()
Jack Jansend0fc42f2001-08-19 22:05:06 +0000162
Jack Jansenc8febec2002-01-23 22:46:30 +0000163 def _start(self):
Jack Jansend0fc42f2001-08-19 22:05:06 +0000164 """Start the application, if it is not running yet"""
165 try:
166 self.send('ascr', 'noop')
167 except AE.Error:
168 _launch(self.target_signature)
169
Jack Jansenc8febec2002-01-23 22:46:30 +0000170 def start(self):
171 """Deprecated, used _start()"""
172 self._start()
173
Jack Jansend0fc42f2001-08-19 22:05:06 +0000174 def newevent(self, code, subcode, parameters = {}, attributes = {}):
175 """Create a complete structure for an apple event"""
176
177 event = AE.AECreateAppleEvent(code, subcode, self.target,
178 AppleEvents.kAutoGenerateReturnID, AppleEvents.kAnyTransactionID)
179 packevent(event, parameters, attributes)
180 return event
181
182 def sendevent(self, event):
183 """Send a pre-created appleevent, await the reply and unpack it"""
184
185 reply = event.AESend(self.send_flags, self.send_priority,
186 self.send_timeout)
Jack Jansen8b777672002-08-07 14:49:00 +0000187 parameters, attributes = unpackevent(reply, self._moduleName)
Jack Jansend0fc42f2001-08-19 22:05:06 +0000188 return reply, parameters, attributes
189
190 def send(self, code, subcode, parameters = {}, attributes = {}):
191 """Send an appleevent given code/subcode/pars/attrs and unpack the reply"""
192 return self.sendevent(self.newevent(code, subcode, parameters, attributes))
193
194 #
195 # The following events are somehow "standard" and don't seem to appear in any
196 # suite...
197 #
198 def activate(self):
199 """Send 'activate' command"""
200 self.send('misc', 'actv')
201
202 def _get(self, _object, as=None, _attributes={}):
203 """_get: get data from an object
204 Required argument: the object
205 Keyword argument _attributes: AppleEvent attribute dictionary
206 Returns: the data
207 """
208 _code = 'core'
209 _subcode = 'getd'
210
211 _arguments = {'----':_object}
212 if as:
213 _arguments['rtyp'] = mktype(as)
214
215 _reply, _arguments, _attributes = self.send(_code, _subcode,
216 _arguments, _attributes)
217 if _arguments.has_key('errn'):
218 raise Error, decodeerror(_arguments)
219
220 if _arguments.has_key('----'):
221 return _arguments['----']
Jack Jansen8b777672002-08-07 14:49:00 +0000222 if as:
223 item.__class__ = as
224 return item
225
226 def _set(self, _object, _arguments = {}, _attributes = {}):
227 """ _set: set data for an object
228 Required argument: the object
229 Keyword argument _parameters: Parameter dictionary for the set operation
230 Keyword argument _attributes: AppleEvent attribute dictionary
231 Returns: the data
232 """
233 _code = 'core'
234 _subcode = 'setd'
235
236 _arguments['----'] = _object
237
238 _reply, _arguments, _attributes = self.send(_code, _subcode,
239 _arguments, _attributes)
240 if _arguments.has_key('errn'):
241 raise Error, decodeerror(_arguments)
242
243 if _arguments.has_key('----'):
244 return _arguments['----']
Jack Jansend0fc42f2001-08-19 22:05:06 +0000245
246# Tiny Finder class, for local use only
247
248class _miniFinder(TalkTo):
249 def open(self, _object, _attributes={}, **_arguments):
250 """open: Open the specified object(s)
251 Required argument: list of objects to open
252 Keyword argument _attributes: AppleEvent attribute dictionary
253 """
254 _code = 'aevt'
255 _subcode = 'odoc'
256
257 if _arguments: raise TypeError, 'No optional args expected'
258 _arguments['----'] = _object
259
260
261 _reply, _arguments, _attributes = self.send(_code, _subcode,
262 _arguments, _attributes)
263 if _arguments.has_key('errn'):
264 raise Error, decodeerror(_arguments)
265 # XXXX Optionally decode result
266 if _arguments.has_key('----'):
267 return _arguments['----']
268#pass
269
270_finder = _miniFinder('MACS')
271
272def _launch(appfile):
273 """Open a file thru the finder. Specify file by name or fsspec"""
274 _finder.open(_application_file(('ID ', appfile)))
275
276
277class _application_file(ComponentItem):
278 """application file - An application's file on disk"""
279 want = 'appf'
280
281_application_file._propdict = {
282}
283_application_file._elemdict = {
284}
285
286# Test program
287# XXXX Should test more, really...
288
289def test():
290 target = AE.AECreateDesc('sign', 'quil')
291 ae = AE.AECreateAppleEvent('aevt', 'oapp', target, -1, 0)
292 print unpackevent(ae)
293 raw_input(":")
294 ae = AE.AECreateAppleEvent('core', 'getd', target, -1, 0)
295 obj = Character(2, Word(1, Document(1)))
296 print obj
297 print repr(obj)
298 packevent(ae, {'----': obj})
299 params, attrs = unpackevent(ae)
300 print params['----']
301 raw_input(":")
302
303if __name__ == '__main__':
304 test()
305 sys.exit(1)