blob: 8c9def73c6007da5f50095976311604a60af5ebc [file] [log] [blame]
Jack Jansen40775ba1995-07-17 11:42:23 +00001"""Tools for use in AppleEvent clients and servers:
2conversion between AE types and python types
3
4pack(x) converts a Python object to an AEDesc object
5unpack(desc) does the reverse
6coerce(x, wanted_sample) coerces a python object to another python object
7"""
8
9#
10# This code was originally written by Guido, and modified/extended by Jack
11# to include the various types that were missing. The reference used is
12# Apple Event Registry, chapter 9.
13#
14
15import struct
16import string
17import types
18from string import strip
19from types import *
20import AE
21from AppleEvents import *
22from AERegistry import *
23from AEObjects import *
24import MacOS
25import macfs
26import StringIO
27import aetypes
28from aetypes import mkenum, mktype
29
30# These ones seem to be missing from AppleEvents
31# (they're in AERegistry.h)
32
33#typeColorTable = 'clrt'
34#typeDrawingArea = 'cdrw'
35#typePixelMap = 'cpix'
36#typePixelMapMinus = 'tpmm'
37#typeRotation = 'trot'
38#typeTextStyles = 'tsty'
39#typeStyledText = 'STXT'
40#typeAEText = 'tTXT'
41#typeEnumeration = 'enum'
42
43#
44# Some AE types are immedeately coerced into something
45# we like better (and which is equivalent)
46#
47unpacker_coercions = {
48 typeComp : typeExtended,
49 typeColorTable : typeAEList,
50 typeDrawingArea : typeAERecord,
51 typeFixed : typeExtended,
52 typeFloat : typeExtended,
53 typePixelMap : typeAERecord,
54 typeRotation : typeAERecord,
55 typeStyledText : typeAERecord,
56 typeTextStyles : typeAERecord,
57};
58
59#
60# Some python types we need in the packer:
61#
62AEDescType = type(AE.AECreateDesc('TEXT', ''))
63_sample_fss = macfs.FSSpec(':')
64_sample_alias = _sample_fss.NewAliasMinimal()
65FSSType = type(_sample_fss)
66AliasType = type(_sample_alias)
67
68def pack(x, forcetype = None):
69 """Pack a python object into an AE descriptor"""
70
71 if forcetype:
72 if type(x) is StringType:
73 return AE.AECreateDesc(forcetype, x)
74 else:
75 return pack(x).AECoerceDesc(forcetype)
76
77 if x == None:
78 return AE.AECreateDesc('null', '')
79
80 t = type(x)
81 if t == AEDescType:
82 return x
83 if t == FSSType:
84 return AE.AECreateDesc('fss ', x.data)
85 if t == AliasType:
86 return AE.AECreateDesc('alis', x.data)
87 if t == IntType:
88 return AE.AECreateDesc('long', struct.pack('l', x))
89 if t == FloatType:
90 #
91 # XXXX (note by Guido) Weird thing -- Think C's "double" is 10 bytes, but
92 # struct.pack('d') return 12 bytes (and struct.unpack requires
93 # them, too). The first 2 bytes seem to be repeated...
94 # Probably an alignment problem
95 # XXXX (note by Jack) haven't checked this under MW
96 #
97 return AE.AECreateDesc('exte', struct.pack('d', x)[2:])
98 if t == StringType:
99 return AE.AECreateDesc('TEXT', x)
100 if t == ListType:
101 list = AE.AECreateList('', 0)
102 for item in x:
103 list.AEPutDesc(0, pack(item))
104 return list
105 if t == DictionaryType:
106 record = AE.AECreateList('', 1)
107 for key, value in x.items():
108 record.AEPutParamDesc(key, pack(value))
109 return record
110 if t == InstanceType and hasattr(x, '__aepack__'):
111 return x.__aepack__()
112 return AE.AECreateDesc('TEXT', repr(x)) # Copout
113
114def unpack(desc):
115 """Unpack an AE descriptor to a python object"""
116 t = desc.type
117
118 if unpacker_coercions.has_key(t):
119 desc = desc.AECoerceDesc(unpacker_coercions[t])
120
121 if t == typeAEList:
122 l = []
123 for i in range(desc.AECountItems()):
124 keyword, item = desc.AEGetNthDesc(i+1, '****')
125 l.append(unpack(item))
126 return l
127 if t == typeAERecord:
128 d = {}
129 for i in range(desc.AECountItems()):
130 keyword, item = desc.AEGetNthDesc(i+1, '****')
131 d[keyword] = unpack(item)
132 return d
133 if t == typeAEText:
134 record = desc.AECoerceDesc('reco')
135 return mkaetext(unpack(record))
136 if t == typeAlias:
137 return macfs.RawAlias(desc.data)
138 # typeAppleEvent returned as unknown
139 if t == typeBoolean:
140 return struct.unpack('b', desc.data)[0]
141 if t == typeChar:
142 return desc.data
143 # typeColorTable coerced to typeAEList
144 # typeComp coerced to extended
145 # typeData returned as unknown
146 # typeDrawingArea coerced to typeAERecord
147 if t == typeEnumeration:
148 return mkenum(desc.data)
149 # typeEPS returned as unknown
150 if t == typeExtended:
151 data = desc.data
152 # XXX See corresponding note for pack()
153 return struct.unpack('d', data[:2] + data)[0]
154 if t == typeFalse:
155 return 0
156 # typeFixed coerced to extended
157 # typeFloat coerced to extended
158 if t == typeFSS:
159 return macfs.RawFSSpec(desc.data)
160 if t == typeInsertionLoc:
161 record = desc.AECoerceDesc('reco')
162 return mkinsertionloc(unpack(record))
163 # typeInteger equal to typeLongInteger
164 if t == typeIntlText:
165 script, language = struct.unpack('hh', desc.data[:4])
166 return aetypes.IntlText(script, language, desc.data[4:])
167 if t == typeIntlWritingCode:
168 script, language = struct.unpack('hh', desc.data)
169 return aetypes.IntlWritingCode(script, language)
170 if t == typeKeyword:
171 return mkkeyword(desc.data)
172 # typeLongFloat is equal to typeFloat
173 if t == typeLongInteger:
174 return struct.unpack('l', desc.data)[0]
175 if t == typeNull:
176 return None
177 if t == typeMagnitude:
178 v = struct.unpack('l', desc.data)
179 if v < 0:
180 v = 0x100000000L + v
181 return v
182 if t == typeObjectSpecifier:
183 record = desc.AECoerceDesc('reco')
184 return mkobject(unpack(record))
185 # typePict returned as unknown
186 # typePixelMap coerced to typeAERecord
187 # typePixelMapMinus returned as unknown
188 # typeProcessSerialNumber returned as unknown
189 if t == typeQDPoint:
190 v, h = struct.unpack('hh', desc.data)
191 return aetypes.QDPoint(v, h)
192 if t == typeQDRectangle:
193 v0, h0, v1, h1 = struct.unpack('hhhh', desc.data)
194 return aetypes.QDRectangle(v0, h0, v1, h1)
195 if t == typeRGBColor:
196 r, g, b = struct.unpack('hhh', desc.data)
197 return aetypes.RGBColor(r, g, b)
198 # typeRotation coerced to typeAERecord
199 # typeScrapStyles returned as unknown
200 # typeSessionID returned as unknown
201 if t == typeShortFloat:
202 return struct.unpack('f', desc.data)[0]
203 if t == typeShortInteger:
204 return struct.unpack('h', desc.data)[0]
205 # typeSMFloat identical to typeShortFloat
206 # typeSMInt indetical to typeShortInt
207 # typeStyledText coerced to typeAERecord
208 if t == typeTargetID:
209 return mktargetid(desc.data)
210 # typeTextStyles coerced to typeAERecord
211 # typeTIFF returned as unknown
212 if t == typeTrue:
213 return 1
214 if t == typeType:
215 return mktype(desc.data)
216 #
217 # The following are special
218 #
219 if t == 'rang':
220 record = desc.AECoerceDesc('reco')
221 return mkrange(unpack(record))
222 if t == 'cmpd':
223 record = desc.AECoerceDesc('reco')
224 return mkcomparison(unpack(record))
225 if t == 'logi':
226 record = desc.AECoerceDesc('reco')
227 return mklogical(unpack(record))
228 return mkunknown(desc.type, desc.data)
229
230def coerce(data, egdata):
231 """Coerce a python object to another type using the AE coercers"""
232 pdata = pack(data)
233 pegdata = pack(egdata)
234 pdata = pdata.AECoerceDesc(pegdata.type)
235 return unpack(pdata)
236
237#
238# Helper routines for unpack
239#
240def mktargetid(data):
241 sessionID = getlong(data[:4])
242 name = mkppcportrec(data[4:4+72])
243 location = mklocationnamerec(data[76:76+36])
244 rcvrName = mkppcportrec(data[112:112+72])
245 return sessionID, name, location, rcvrName
246
247def mkppcportrec(rec):
248 namescript = getword(rec[:2])
249 name = getpstr(rec[2:2+33])
250 portkind = getword(rec[36:38])
251 if portkind == 1:
252 ctor = rec[38:42]
253 type = rec[42:46]
254 identity = (ctor, type)
255 else:
256 identity = getpstr(rec[38:38+33])
257 return namescript, name, portkind, identity
258
259def mklocationnamerec(rec):
260 kind = getword(rec[:2])
261 stuff = rec[2:]
262 if kind == 0: stuff = None
263 if kind == 2: stuff = getpstr(stuff)
264 return kind, stuff
265
266def mkunknown(type, data):
267 return aetypes.Unknown(type, data)
268
269def getpstr(s):
270 return s[1:1+ord(s[0])]
271
272def getlong(s):
273 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
274
275def getword(s):
276 return (ord(s[0])<<8) | (ord(s[1])<<0)
277
278def mkkeyword(keyword):
279 return aetypes.Keyword(keyword)
280
281def mkrange(dict):
282 return aetypes.Range(dict['star'], dict['stop'])
283
284def mkcomparison(dict):
285 return aetypes.Comparison(dict['obj1'], dict['relo'].enum, dict['obj2'])
286
287def mklogical(dict):
288 return aetypes.Logical(dict['logc'], dict['term'])
289
290def mkstyledtext(dict):
291 return aetypes.StyledText(dict['ksty'], dict['ktxt'])
292
293def mkaetext(dict):
294 return aetypes.AEText(dict[keyAEScriptTag], dict[keyAEStyles], dict[keyAEText])
295
296def mkinsertionloc(dict):
297 return aetypes.InsertionLoc(dict[keyAEObject], dict[keyAEPosition])
298
299def mkobject(dict):
300 want = dict['want'].type
301 form = dict['form'].enum
302 seld = dict['seld']
303 fr = dict['from']
304 if form in ('name', 'indx', 'rang', 'test'):
305 if want == 'text': return aetypes.Text(seld, fr)
306 if want == 'cha ': return aetypes.Character(seld, fr)
307 if want == 'cwor': return aetypes.Word(seld, fr)
308 if want == 'clin': return aetypes.Line(seld, fr)
309 if want == 'cpar': return aetypes.Paragraph(seld, fr)
310 if want == 'cwin': return aetypes.Window(seld, fr)
311 if want == 'docu': return aetypes.Document(seld, fr)
312 if want == 'file': return aetypes.File(seld, fr)
313 if want == 'cins': return aetypes.InsertionPoint(seld, fr)
314 if want == 'prop' and form == 'prop' and aetypes.IsType(seld):
315 return aetypes.Property(seld.type, fr)
316 return aetypes.ObjectSpecifier(want, form, seld, fr)
317
318def _test():
319 """Test program. Pack and unpack various things"""
320 objs = [
321 'a string',
322 12,
323 12.0,
324 None,
325 ['a', 'list', 'of', 'strings'],
326 {'key1': 'value1', 'key2':'value2'},
327 macfs.FSSpec(':'),
328 macfs.FSSpec(':').NewAliasMinimal(),
329 aetypes.Enum('enum'),
330 aetypes.Type('type'),
331 aetypes.Keyword('kwrd'),
332 aetypes.Range(1, 10),
333 aetypes.Comparison(1, '< ', 10),
334 aetypes.Logical('not ', 1),
335 # Cannot do StyledText
336 # Cannot do AEText
337 aetypes.IntlText(0, 0, 'international text'),
338 aetypes.IntlWritingCode(0,0),
339 aetypes.QDPoint(50,100),
340 aetypes.QDRectangle(50,100,150,200),
341 aetypes.RGBColor(0x7000, 0x6000, 0x5000),
342 aetypes.Unknown('xxxx', 'unknown type data'),
343 aetypes.Character(1),
344 aetypes.Character(2, aetypes.Line(2)),
345 ]
346 for o in objs:
347 print 'BEFORE', o, `o`
348 packed = pack(o)
349 unpacked = unpack(packed)
350 print 'AFTER ', unpacked, `unpacked`
351 import sys
352 sys.exit(1)
353
354if __name__ == '__main__':
355 _test()
356