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