From 40775bafabe8b32d3a0ff2276430ad75b139dde9 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Mon, 17 Jul 1995 11:42:23 +0000 Subject: [PATCH] Split aepack and aetypes off from aetools (it was getting too big) Added support for all basic types mentioned in Apple Event Registry Added support for automatically-generated suites. --- Mac/Lib/toolbox/aepack.py | 356 ++++++++++++++++++++++++ Mac/Lib/toolbox/aetools.py | 551 ++++++------------------------------- Mac/Lib/toolbox/aetypes.py | 452 ++++++++++++++++++++++++++++++ 3 files changed, 897 insertions(+), 462 deletions(-) create mode 100644 Mac/Lib/toolbox/aepack.py create mode 100644 Mac/Lib/toolbox/aetypes.py diff --git a/Mac/Lib/toolbox/aepack.py b/Mac/Lib/toolbox/aepack.py new file mode 100644 index 00000000000..8c9def73c60 --- /dev/null +++ b/Mac/Lib/toolbox/aepack.py @@ -0,0 +1,356 @@ +"""Tools for use in AppleEvent clients and servers: +conversion between AE types and python types + +pack(x) converts a Python object to an AEDesc object +unpack(desc) does the reverse +coerce(x, wanted_sample) coerces a python object to another python object +""" + +# +# This code was originally written by Guido, and modified/extended by Jack +# to include the various types that were missing. The reference used is +# Apple Event Registry, chapter 9. +# + +import struct +import string +import types +from string import strip +from types import * +import AE +from AppleEvents import * +from AERegistry import * +from AEObjects import * +import MacOS +import macfs +import StringIO +import aetypes +from aetypes import mkenum, mktype + +# These ones seem to be missing from AppleEvents +# (they're in AERegistry.h) + +#typeColorTable = 'clrt' +#typeDrawingArea = 'cdrw' +#typePixelMap = 'cpix' +#typePixelMapMinus = 'tpmm' +#typeRotation = 'trot' +#typeTextStyles = 'tsty' +#typeStyledText = 'STXT' +#typeAEText = 'tTXT' +#typeEnumeration = 'enum' + +# +# Some AE types are immedeately coerced into something +# we like better (and which is equivalent) +# +unpacker_coercions = { + typeComp : typeExtended, + typeColorTable : typeAEList, + typeDrawingArea : typeAERecord, + typeFixed : typeExtended, + typeFloat : typeExtended, + typePixelMap : typeAERecord, + typeRotation : typeAERecord, + typeStyledText : typeAERecord, + typeTextStyles : typeAERecord, +}; + +# +# Some python types we need in the packer: +# +AEDescType = type(AE.AECreateDesc('TEXT', '')) +_sample_fss = macfs.FSSpec(':') +_sample_alias = _sample_fss.NewAliasMinimal() +FSSType = type(_sample_fss) +AliasType = type(_sample_alias) + +def pack(x, forcetype = None): + """Pack a python object into an AE descriptor""" + + if forcetype: + if type(x) is StringType: + return AE.AECreateDesc(forcetype, x) + else: + return pack(x).AECoerceDesc(forcetype) + + if x == None: + return AE.AECreateDesc('null', '') + + t = type(x) + if t == AEDescType: + return x + if t == FSSType: + return AE.AECreateDesc('fss ', x.data) + if t == AliasType: + return AE.AECreateDesc('alis', x.data) + if t == IntType: + return AE.AECreateDesc('long', struct.pack('l', x)) + if t == FloatType: + # + # XXXX (note by Guido) Weird thing -- Think C's "double" is 10 bytes, but + # struct.pack('d') return 12 bytes (and struct.unpack requires + # them, too). The first 2 bytes seem to be repeated... + # Probably an alignment problem + # XXXX (note by Jack) haven't checked this under MW + # + return AE.AECreateDesc('exte', struct.pack('d', x)[2:]) + if t == StringType: + return AE.AECreateDesc('TEXT', x) + if t == ListType: + list = AE.AECreateList('', 0) + for item in x: + list.AEPutDesc(0, pack(item)) + return list + if t == DictionaryType: + record = AE.AECreateList('', 1) + for key, value in x.items(): + record.AEPutParamDesc(key, pack(value)) + return record + if t == InstanceType and hasattr(x, '__aepack__'): + return x.__aepack__() + return AE.AECreateDesc('TEXT', repr(x)) # Copout + +def unpack(desc): + """Unpack an AE descriptor to a python object""" + t = desc.type + + if unpacker_coercions.has_key(t): + desc = desc.AECoerceDesc(unpacker_coercions[t]) + + if t == typeAEList: + l = [] + for i in range(desc.AECountItems()): + keyword, item = desc.AEGetNthDesc(i+1, '****') + l.append(unpack(item)) + return l + if t == typeAERecord: + d = {} + for i in range(desc.AECountItems()): + keyword, item = desc.AEGetNthDesc(i+1, '****') + d[keyword] = unpack(item) + return d + if t == typeAEText: + record = desc.AECoerceDesc('reco') + return mkaetext(unpack(record)) + if t == typeAlias: + return macfs.RawAlias(desc.data) + # typeAppleEvent returned as unknown + if t == typeBoolean: + return struct.unpack('b', desc.data)[0] + if t == typeChar: + return desc.data + # typeColorTable coerced to typeAEList + # typeComp coerced to extended + # typeData returned as unknown + # typeDrawingArea coerced to typeAERecord + if t == typeEnumeration: + return mkenum(desc.data) + # typeEPS returned as unknown + if t == typeExtended: + data = desc.data + # XXX See corresponding note for pack() + return struct.unpack('d', data[:2] + data)[0] + if t == typeFalse: + return 0 + # typeFixed coerced to extended + # typeFloat coerced to extended + if t == typeFSS: + return macfs.RawFSSpec(desc.data) + if t == typeInsertionLoc: + record = desc.AECoerceDesc('reco') + return mkinsertionloc(unpack(record)) + # typeInteger equal to typeLongInteger + if t == typeIntlText: + script, language = struct.unpack('hh', desc.data[:4]) + return aetypes.IntlText(script, language, desc.data[4:]) + if t == typeIntlWritingCode: + script, language = struct.unpack('hh', desc.data) + return aetypes.IntlWritingCode(script, language) + if t == typeKeyword: + return mkkeyword(desc.data) + # typeLongFloat is equal to typeFloat + if t == typeLongInteger: + return struct.unpack('l', desc.data)[0] + if t == typeNull: + return None + if t == typeMagnitude: + v = struct.unpack('l', desc.data) + if v < 0: + v = 0x100000000L + v + return v + if t == typeObjectSpecifier: + record = desc.AECoerceDesc('reco') + return mkobject(unpack(record)) + # typePict returned as unknown + # typePixelMap coerced to typeAERecord + # typePixelMapMinus returned as unknown + # typeProcessSerialNumber returned as unknown + if t == typeQDPoint: + v, h = struct.unpack('hh', desc.data) + return aetypes.QDPoint(v, h) + if t == typeQDRectangle: + v0, h0, v1, h1 = struct.unpack('hhhh', desc.data) + return aetypes.QDRectangle(v0, h0, v1, h1) + if t == typeRGBColor: + r, g, b = struct.unpack('hhh', desc.data) + return aetypes.RGBColor(r, g, b) + # typeRotation coerced to typeAERecord + # typeScrapStyles returned as unknown + # typeSessionID returned as unknown + if t == typeShortFloat: + return struct.unpack('f', desc.data)[0] + if t == typeShortInteger: + return struct.unpack('h', desc.data)[0] + # typeSMFloat identical to typeShortFloat + # typeSMInt indetical to typeShortInt + # typeStyledText coerced to typeAERecord + if t == typeTargetID: + return mktargetid(desc.data) + # typeTextStyles coerced to typeAERecord + # typeTIFF returned as unknown + if t == typeTrue: + return 1 + if t == typeType: + return mktype(desc.data) + # + # The following are special + # + if t == 'rang': + record = desc.AECoerceDesc('reco') + return mkrange(unpack(record)) + if t == 'cmpd': + record = desc.AECoerceDesc('reco') + return mkcomparison(unpack(record)) + if t == 'logi': + record = desc.AECoerceDesc('reco') + return mklogical(unpack(record)) + return mkunknown(desc.type, desc.data) + +def coerce(data, egdata): + """Coerce a python object to another type using the AE coercers""" + pdata = pack(data) + pegdata = pack(egdata) + pdata = pdata.AECoerceDesc(pegdata.type) + return unpack(pdata) + +# +# Helper routines for unpack +# +def mktargetid(data): + sessionID = getlong(data[:4]) + name = mkppcportrec(data[4:4+72]) + location = mklocationnamerec(data[76:76+36]) + rcvrName = mkppcportrec(data[112:112+72]) + return sessionID, name, location, rcvrName + +def mkppcportrec(rec): + namescript = getword(rec[:2]) + name = getpstr(rec[2:2+33]) + portkind = getword(rec[36:38]) + if portkind == 1: + ctor = rec[38:42] + type = rec[42:46] + identity = (ctor, type) + else: + identity = getpstr(rec[38:38+33]) + return namescript, name, portkind, identity + +def mklocationnamerec(rec): + kind = getword(rec[:2]) + stuff = rec[2:] + if kind == 0: stuff = None + if kind == 2: stuff = getpstr(stuff) + return kind, stuff + +def mkunknown(type, data): + return aetypes.Unknown(type, data) + +def getpstr(s): + return s[1:1+ord(s[0])] + +def getlong(s): + return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3]) + +def getword(s): + return (ord(s[0])<<8) | (ord(s[1])<<0) + +def mkkeyword(keyword): + return aetypes.Keyword(keyword) + +def mkrange(dict): + return aetypes.Range(dict['star'], dict['stop']) + +def mkcomparison(dict): + return aetypes.Comparison(dict['obj1'], dict['relo'].enum, dict['obj2']) + +def mklogical(dict): + return aetypes.Logical(dict['logc'], dict['term']) + +def mkstyledtext(dict): + return aetypes.StyledText(dict['ksty'], dict['ktxt']) + +def mkaetext(dict): + return aetypes.AEText(dict[keyAEScriptTag], dict[keyAEStyles], dict[keyAEText]) + +def mkinsertionloc(dict): + return aetypes.InsertionLoc(dict[keyAEObject], dict[keyAEPosition]) + +def mkobject(dict): + want = dict['want'].type + form = dict['form'].enum + seld = dict['seld'] + fr = dict['from'] + if form in ('name', 'indx', 'rang', 'test'): + if want == 'text': return aetypes.Text(seld, fr) + if want == 'cha ': return aetypes.Character(seld, fr) + if want == 'cwor': return aetypes.Word(seld, fr) + if want == 'clin': return aetypes.Line(seld, fr) + if want == 'cpar': return aetypes.Paragraph(seld, fr) + if want == 'cwin': return aetypes.Window(seld, fr) + if want == 'docu': return aetypes.Document(seld, fr) + if want == 'file': return aetypes.File(seld, fr) + if want == 'cins': return aetypes.InsertionPoint(seld, fr) + if want == 'prop' and form == 'prop' and aetypes.IsType(seld): + return aetypes.Property(seld.type, fr) + return aetypes.ObjectSpecifier(want, form, seld, fr) + +def _test(): + """Test program. Pack and unpack various things""" + objs = [ + 'a string', + 12, + 12.0, + None, + ['a', 'list', 'of', 'strings'], + {'key1': 'value1', 'key2':'value2'}, + macfs.FSSpec(':'), + macfs.FSSpec(':').NewAliasMinimal(), + aetypes.Enum('enum'), + aetypes.Type('type'), + aetypes.Keyword('kwrd'), + aetypes.Range(1, 10), + aetypes.Comparison(1, '< ', 10), + aetypes.Logical('not ', 1), + # Cannot do StyledText + # Cannot do AEText + aetypes.IntlText(0, 0, 'international text'), + aetypes.IntlWritingCode(0,0), + aetypes.QDPoint(50,100), + aetypes.QDRectangle(50,100,150,200), + aetypes.RGBColor(0x7000, 0x6000, 0x5000), + aetypes.Unknown('xxxx', 'unknown type data'), + aetypes.Character(1), + aetypes.Character(2, aetypes.Line(2)), + ] + for o in objs: + print 'BEFORE', o, `o` + packed = pack(o) + unpacked = unpack(packed) + print 'AFTER ', unpacked, `unpacked` + import sys + sys.exit(1) + +if __name__ == '__main__': + _test() + diff --git a/Mac/Lib/toolbox/aetools.py b/Mac/Lib/toolbox/aetools.py index bb0d20f2eb1..565cdec89b5 100644 --- a/Mac/Lib/toolbox/aetools.py +++ b/Mac/Lib/toolbox/aetools.py @@ -15,477 +15,22 @@ and pack(x) will create an AE object reference equivalent to AppleScript's character 1 of document "foobar" +Some of the stuff that appears to be exported from this module comes from other +files: the pack stuff from aepack, the objects from aetypes. + """ -import struct -import string -from string import strip from types import * import AE +import AppleEvents import MacOS -import macfs -import StringIO - - -AEDescType = type(AE.AECreateDesc('TEXT', '')) - -FSSType = type(macfs.FSSpec(':')) - - -def pack(x, forcetype = None): - if forcetype: - if type(x) is StringType: - return AE.AECreateDesc(forcetype, x) - else: - return pack(x).AECoerceDesc(forcetype) - if x == None: - return AE.AECreateDesc('null', '') - t = type(x) - if t == AEDescType: - return x - if t == FSSType: - vol, dir, filename = x.as_tuple() - fnlen = len(filename) - header = struct.pack('hlb', vol, dir, fnlen) - padding = '\0'*(63-fnlen) - return AE.AECreateDesc('fss ', header + filename + padding) - if t == IntType: - return AE.AECreateDesc('long', struct.pack('l', x)) - if t == FloatType: - # XXX Weird thing -- Think C's "double" is 10 bytes, but - # struct.pack('d') return 12 bytes (and struct.unpack requires - # them, too). The first 2 bytes seem to be repeated... - # Probably an alignment problem - return AE.AECreateDesc('exte', struct.pack('d', x)[2:]) - if t == StringType: - return AE.AECreateDesc('TEXT', x) - if t == ListType: - list = AE.AECreateList('', 0) - for item in x: - list.AEPutDesc(0, pack(item)) - return list - if t == DictionaryType: - record = AE.AECreateList('', 1) - for key, value in x.items(): - record.AEPutParamDesc(key, pack(value)) - return record - if t == InstanceType and hasattr(x, '__aepack__'): - return x.__aepack__() - return AE.AECreateDesc('TEXT', repr(x)) # Copout - - -def unpack(desc): - t = desc.type - if t == 'TEXT': - return desc.data - if t == 'fals': - return 0 - if t == 'true': - return 1 - if t == 'enum': - return mkenum(desc.data) - if t == 'type': - return mktype(desc.data) - if t == 'long': - return struct.unpack('l', desc.data)[0] - if t == 'shor': - return struct.unpack('h', desc.data)[0] - if t == 'sing': - return struct.unpack('f', desc.data)[0] - if t == 'exte': - data = desc.data - # XXX See corresponding note for pack() - return struct.unpack('d', data[:2] + data)[0] - if t in ('doub', 'comp', 'magn'): - return unpack(desc.AECoerceDesc('exte')) - if t == 'null': - return None - if t == 'list': - l = [] - for i in range(desc.AECountItems()): - keyword, item = desc.AEGetNthDesc(i+1, '****') - l.append(unpack(item)) - return l - if t == 'reco': - d = {} - for i in range(desc.AECountItems()): - keyword, item = desc.AEGetNthDesc(i+1, '****') - d[keyword] = unpack(item) - return d - if t == 'obj ': - record = desc.AECoerceDesc('reco') - return mkobject(unpack(record)) - if t == 'rang': - record = desc.AECoerceDesc('reco') - return mkrange(unpack(record)) - if t == 'cmpd': - record = desc.AECoerceDesc('reco') - return mkcomparison(unpack(record)) - if t == 'logi': - record = desc.AECoerceDesc('reco') - return mklogical(unpack(record)) - if t == 'targ': - return mktargetid(desc.data) - if t == 'alis': - # XXX Can't handle alias records yet, so coerce to FS spec... - return unpack(desc.AECoerceDesc('fss ')) - if t == 'fss ': - return mkfss(desc.data) - return mkunknown(desc.type, desc.data) - - -def mkfss(data): - print "mkfss data =", `data` - vol, dir, fnlen = struct.unpack('hlb', data[:7]) - filename = data[7:7+fnlen] - print (vol, dir, fnlen, filename) - return macfs.FSSpec((vol, dir, filename)) - - -def mktargetid(data): - sessionID = getlong(data[:4]) - name = mkppcportrec(data[4:4+72]) - print len(name), `name` - location = mklocationnamerec(data[76:76+36]) - rcvrName = mkppcportrec(data[112:112+72]) - return sessionID, name, location, rcvrName - -def mkppcportrec(rec): - namescript = getword(rec[:2]) - name = getpstr(rec[2:2+33]) - portkind = getword(rec[36:38]) - if portkind == 1: - ctor = rec[38:42] - type = rec[42:46] - identity = (ctor, type) - else: - identity = getpstr(rec[38:38+33]) - return namescript, name, portkind, identity - -def mklocationnamerec(rec): - kind = getword(rec[:2]) - stuff = rec[2:] - if kind == 0: stuff = None - if kind == 2: stuff = getpstr(stuff) - return kind, stuff - -def getpstr(s): - return s[1:1+ord(s[0])] - -def getlong(s): - return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3]) - -def getword(s): - return (ord(s[0])<<8) | (ord(s[1])<<0) - - -def mkunknown(type, data): - return Unknown(type, data) - -class Unknown: - - def __init__(self, type, data): - self.type = type - self.data = data - - def __repr__(self): - return "Unknown(%s, %s)" % (`self.type`, `self.data`) - - def __aepack__(self): - return pack(self.data, self.type) - - -def IsSubclass(cls, base): - """Test whether CLASS1 is the same as or a subclass of CLASS2""" - # Loop to optimize for single inheritance - while 1: - if cls is base: return 1 - if len(cls.__bases__) <> 1: break - cls = cls.__bases__[0] - # Recurse to cope with multiple inheritance - for c in cls.__bases__: - if IsSubclass(c, base): return 1 - return 0 - -def IsInstance(x, cls): - """Test whether OBJECT is an instance of (a subclass of) CLASS""" - return type(x) is InstanceType and IsSubclass(x.__class__, cls) - - -def nice(s): - if type(s) is StringType: return repr(s) - else: return str(s) - - -def mkenum(enum): - if IsEnum(enum): return enum - return Enum(enum) - -class Enum: - - def __init__(self, enum): - self.enum = "%-4.4s" % str(enum) - - def __repr__(self): - return "Enum(%s)" % `self.enum` - - def __str__(self): - return strip(self.enum) - - def __aepack__(self): - return pack(self.enum, 'enum') - -def IsEnum(x): - return IsInstance(x, Enum) - - -def mktype(type): - if IsType(type): return type - return Type(type) - -class Type: - - def __init__(self, type): - self.type = "%-4.4s" % str(type) - - def __repr__(self): - return "Type(%s)" % `self.type` - - def __str__(self): - return strip(self.type) - - def __aepack__(self): - return pack(self.type, 'type') - -def IsType(x): - return IsInstance(x, Type) - - -def mkrange(dict): - return Range(dict['star'], dict['stop']) - -class Range: - - def __init__(self, start, stop): - self.start = start - self.stop = stop - - def __repr__(self): - return "Range(%s, %s)" % (`self.start`, `self.stop`) - - def __str__(self): - return "%s thru %s" % (nice(self.start), nice(self.stop)) - - def __aepack__(self): - return pack({'star': self.start, 'stop': self.stop}, 'rang') - -def IsRange(x): - return IsInstance(x, Range) - - -def mkcomparison(dict): - return Comparison(dict['obj1'], dict['relo'].enum, dict['obj2']) - -class Comparison: - - def __init__(self, obj1, relo, obj2): - self.obj1 = obj1 - self.relo = "%-4.4s" % str(relo) - self.obj2 = obj2 - - def __repr__(self): - return "Comparison(%s, %s, %s)" % (`self.obj1`, `self.relo`, `self.obj2`) - - def __str__(self): - return "%s %s %s" % (nice(self.obj1), strip(self.relo), nice(self.obj2)) - - def __aepack__(self): - return pack({'obj1': self.obj1, - 'relo': mkenum(self.relo), - 'obj2': self.obj2}, - 'cmpd') - -def IsComparison(x): - return IsInstance(x, Comparison) - - -def mklogical(dict): - return Logical(dict['logc'], dict['term']) - -class Logical: - - def __init__(self, logc, term): - self.logc = "%-4.4s" % str(logc) - self.term = term - - def __repr__(self): - return "Logical(%s, %s)" % (`self.logc`, `self.term`) - - def __str__(self): - if type(self.term) == ListType and len(self.term) == 2: - return "%s %s %s" % (nice(self.term[0]), - strip(self.logc), - nice(self.term[1])) - else: - return "%s(%s)" % (strip(self.logc), nice(self.term)) - - def __aepack__(self): - return pack({'logc': mkenum(self.logc), 'term': self.term}, 'logi') - -def IsLogical(x): - return IsInstance(x, Logical) - - -class ObjectSpecifier: - - """A class for constructing and manipulation AE object specifiers in python. - - An object specifier is actually a record with four fields: - - key type description - --- ---- ----------- - - 'want' type what kind of thing we want, - e.g. word, paragraph or property - - 'form' enum how we specify the thing(s) we want, - e.g. by index, by range, by name, or by property specifier - - 'seld' any which thing(s) we want, - e.g. its index, its name, or its property specifier - - 'from' object the object in which it is contained, - or null, meaning look for it in the application - - Note that we don't call this class plain "Object", since that name - is likely to be used by the application. - """ - - def __init__(self, want, form, seld, fr = None): - self.want = want - self.form = form - self.seld = seld - self.fr = fr - - def __repr__(self): - s = "ObjectSpecifier(%s, %s, %s" % (`self.want`, `self.form`, `self.seld`) - if self.fr: - s = s + ", %s)" % `self.fr` - else: - s = s + ")" - return s - - def __aepack__(self): - return pack({'want': mktype(self.want), - 'form': mkenum(self.form), - 'seld': self.seld, - 'from': self.fr}, - 'obj ') - - -def IsObjectSpecifier(x): - return IsInstance(x, ObjectSpecifier) - - -class Property(ObjectSpecifier): - - def __init__(self, which, fr = None): - ObjectSpecifier.__init__(self, 'prop', 'prop', mkenum(which), fr) - - def __repr__(self): - if self.fr: - return "Property(%s, %s)" % (`self.seld.enum`, `self.fr`) - else: - return "Property(%s)" % `self.seld.enum` - - def __str__(self): - if self.fr: - return "Property %s of %s" % (str(self.seld), str(self.fr)) - else: - return "Property %s" % str(self.seld) - - -class SelectableItem(ObjectSpecifier): - - def __init__(self, want, seld, fr = None): - t = type(seld) - if t == StringType: - form = 'name' - elif IsRange(seld): - form = 'rang' - elif IsComparison(seld) or IsLogical(seld): - form = 'test' - else: - form = 'indx' - ObjectSpecifier.__init__(self, want, form, seld, fr) - - -class ComponentItem(SelectableItem): - # Derived classes *must* set the *class attribute* 'want' to some constant - - def __init__(self, which, fr = None): - SelectableItem.__init__(self, self.want, which, fr) - - def __repr__(self): - if not self.fr: - return "%s(%s)" % (self.__class__.__name__, `self.seld`) - return "%s(%s, %s)" % (self.__class__.__name__, `self.seld`, `self.fr`) - - def __str__(self): - seld = self.seld - if type(seld) == StringType: - ss = repr(seld) - elif IsRange(seld): - start, stop = seld.start, seld.stop - if type(start) == InstanceType == type(stop) and \ - start.__class__ == self.__class__ == stop.__class__: - ss = str(start.seld) + " thru " + str(stop.seld) - else: - ss = str(seld) - else: - ss = str(seld) - s = "%s %s" % (self.__class__.__name__, ss) - if self.fr: s = s + " of %s" % str(self.fr) - return s - - -template = """ -class %s(ComponentItem): want = '%s' -""" - -exec template % ("Text", 'text') -exec template % ("Character", 'cha ') -exec template % ("Word", 'cwor') -exec template % ("Line", 'clin') -exec template % ("Paragraph", 'cpar') -exec template % ("Window", 'cwin') -exec template % ("Document", 'docu') -exec template % ("File", 'file') -exec template % ("InsertionPoint", 'cins') - - -def mkobject(dict): - want = dict['want'].type - form = dict['form'].enum - seld = dict['seld'] - fr = dict['from'] - if form in ('name', 'indx', 'rang', 'test'): - if want == 'text': return Text(seld, fr) - if want == 'cha ': return Character(seld, fr) - if want == 'cwor': return Word(seld, fr) - if want == 'clin': return Line(seld, fr) - if want == 'cpar': return Paragraph(seld, fr) - if want == 'cwin': return Window(seld, fr) - if want == 'docu': return Document(seld, fr) - if want == 'file': return File(seld, fr) - if want == 'cins': return InsertionPoint(seld, fr) - if want == 'prop' and form == 'prop' and IsType(seld): - return Property(seld.type, fr) - return ObjectSpecifier(want, form, seld, fr) +from aetypes import * +from aepack import pack, unpack, coerce, AEDescType # Special code to unpack an AppleEvent (which is *not* a disguised record!) +# Note by Jack: No??!? If I read the docs correctly it *is*.... aekeywords = [ 'tran', @@ -531,8 +76,90 @@ def packevent(ae, parameters = {}, attributes = {}): for key, value in attributes.items(): ae.AEPutAttributeDesc(key, pack(value)) +# +# Support routine for automatically generated Suite interfaces +# +def keysubst(arguments, keydict): + """Replace long name keys by their 4-char counterparts, and check""" + ok = keydict.values() + for k in arguments.keys(): + if keydict.has_key(k): + v = arguments[k] + del arguments[k] + arguments[keydict[k]] = v + elif k != '----' and k not in ok: + raise TypeError, 'Unknown keyword argument: %s'%k + +def enumsubst(arguments, key, edict): + """Substitute a single enum keyword argument, if it occurs""" + if not arguments.has_key(key): + return + v = arguments[key] + ok = edict.values() + if edict.has_key(v): + arguments[key] = edict[v] + elif not v in ok: + raise TypeError, 'Unknown enumerator: %s'%v + +def decodeerror(arguments): + """Create the 'best' argument for a raise MacOS.Error""" + errn = arguments['errn'] + errarg = (errn, MacOS.GetErrorString(errn)) + if arguments.has_key('errs'): + errarg = errarg + (arguments['errs'],) + if arguments.has_key('erob'): + errarg = errarg + (arguments['erob'],) + return errarg +class TalkTo: + """An AE connection to an application""" + + def __init__(self, signature): + """Create a communication channel with a particular application. + + Addressing the application is done by specifying either a + 4-byte signature, an AEDesc or an object that will __aepack__ + to an AEDesc. + """ + if type(signature) == AEDescType: + self.target = signature + elif type(signature) == InstanceType and hasattr(signature, '__aepack__'): + self.target = signature.__aepack__() + elif type(signature) == StringType and len(signature) != 4: + self.target = AE.AECreateDesc(AppleEvents.typeApplSignature, signature) + else: + raise TypeError, "signature should be 4-char string or AEDesc" + self.send_flags = AppleEvents.kAEWaitReply + self.send_priority = AppleEvents.kAENormalPriority + self.send_timeout = AppleEvents.kAEDefaultTimeout + + def newevent(self, code, subcode, parameters = {}, attributes = {}): + """Create a complete structure for an apple event""" + + event = AE.AECreateAppleEvent(code, subcode, self.target, + AppleEvents.kAutoGenerateReturnID, AppleEvents.kAnyTransactionID) + packevent(event, parameters, attributes) + return event + + def sendevent(self, event): + """Send a pre-created appleevent, await the reply and unpack it""" + + reply = event.AESend(self.send_flags, self.send_priority, + self.send_timeout) + parameters, attributes = unpackevent(reply) + return reply, parameters, attributes + + def send(self, code, subcode, parameters = {}, attributes = {}): + """Send an appleevent given code/subcode/pars/attrs and unpack the reply""" + return self.sendevent(self.newevent(code, subcode, parameters, attributes)) + + def activate(self): + """Send 'activate' command""" + self.send('misc', 'actv') + + # Test program +# XXXX Should test more, really... def test(): target = AE.AECreateDesc('sign', 'KAHL') diff --git a/Mac/Lib/toolbox/aetypes.py b/Mac/Lib/toolbox/aetypes.py new file mode 100644 index 00000000000..e0a466f781e --- /dev/null +++ b/Mac/Lib/toolbox/aetypes.py @@ -0,0 +1,452 @@ +"""aetypes - Python objects representing various AE types.""" + +from AppleEvents import * +from AERegistry import * +from AEObjects import * +import struct +from types import * +import string + +# +# convoluted, since there are cyclic dependencies between this file and +# aetools_convert. +# +def pack(*args): + from aepack import pack + return apply(pack, args) + +def IsSubclass(cls, base): + """Test whether CLASS1 is the same as or a subclass of CLASS2""" + # Loop to optimize for single inheritance + while 1: + if cls is base: return 1 + if len(cls.__bases__) <> 1: break + cls = cls.__bases__[0] + # Recurse to cope with multiple inheritance + for c in cls.__bases__: + if IsSubclass(c, base): return 1 + return 0 + +def IsInstance(x, cls): + """Test whether OBJECT is an instance of (a subclass of) CLASS""" + return type(x) is InstanceType and IsSubclass(x.__class__, cls) + +def nice(s): + """'nice' representation of an object""" + if type(s) is StringType: return repr(s) + else: return str(s) + +class Unknown: + """An uninterpreted AE object""" + + def __init__(self, type, data): + self.type = type + self.data = data + + def __repr__(self): + return "Unknown(%s, %s)" % (`self.type`, `self.data`) + + def __aepack__(self): + return pack(self.data, self.type) + +class Enum: + """An AE enumeration value""" + + def __init__(self, enum): + self.enum = "%-4.4s" % str(enum) + + def __repr__(self): + return "Enum(%s)" % `self.enum` + + def __str__(self): + return string.strip(self.enum) + + def __aepack__(self): + return pack(self.enum, typeEnumeration) + +def IsEnum(x): + return IsInstance(x, Enum) + +def mkenum(enum): + if IsEnum(enum): return enum + return Enum(enum) + +class Type: + """An AE 4-char typename object""" + + def __init__(self, type): + self.type = "%-4.4s" % str(type) + + def __repr__(self): + return "Type(%s)" % `self.type` + + def __str__(self): + return string.strip(self.type) + + def __aepack__(self): + return pack(self.type, typeType) + +def IsType(x): + return IsInstance(x, Type) + +def mktype(type): + if IsType(type): return type + return Type(type) + + +class Keyword: + """An AE 4-char keyword object""" + + def __init__(self, keyword): + self.keyword = "%-4.4s" % str(keyword) + + def __repr__(self): + return "Keyword(%s)" % `self.keyword` + + def __str__(self): + return string.strip(self.keyword) + + def __aepack__(self): + return pack(self.keyword, typeKeyword) + +def IsKeyword(x): + return IsInstance(x, Keyword) + +class Range: + """An AE range object""" + + def __init__(self, start, stop): + self.start = start + self.stop = stop + + def __repr__(self): + return "Range(%s, %s)" % (`self.start`, `self.stop`) + + def __str__(self): + return "%s thru %s" % (nice(self.start), nice(self.stop)) + + def __aepack__(self): + return pack({'star': self.start, 'stop': self.stop}, 'rang') + +def IsRange(x): + return IsInstance(x, Range) + +class Comparison: + """An AE Comparison""" + + def __init__(self, obj1, relo, obj2): + self.obj1 = obj1 + self.relo = "%-4.4s" % str(relo) + self.obj2 = obj2 + + def __repr__(self): + return "Comparison(%s, %s, %s)" % (`self.obj1`, `self.relo`, `self.obj2`) + + def __str__(self): + return "%s %s %s" % (nice(self.obj1), string.strip(self.relo), nice(self.obj2)) + + def __aepack__(self): + return pack({'obj1': self.obj1, + 'relo': mkenum(self.relo), + 'obj2': self.obj2}, + 'cmpd') + +def IsComparison(x): + return IsInstance(x, Comparison) + +class Logical: + """An AE logical expression object""" + + def __init__(self, logc, term): + self.logc = "%-4.4s" % str(logc) + self.term = term + + def __repr__(self): + return "Logical(%s, %s)" % (`self.logc`, `self.term`) + + def __str__(self): + if type(self.term) == ListType and len(self.term) == 2: + return "%s %s %s" % (nice(self.term[0]), + string.strip(self.logc), + nice(self.term[1])) + else: + return "%s(%s)" % (string.strip(self.logc), nice(self.term)) + + def __aepack__(self): + return pack({'logc': mkenum(self.logc), 'term': self.term}, 'logi') + +def IsLogical(x): + return IsInstance(x, Logical) + +class StyledText: + """An AE object respresenting text in a certain style""" + + def __init__(self, style, text): + self.style = style + self.text = text + + def __repr__(self): + return "StyledText(%s, %s)" % (`self.style`, `self.text`) + + def __str__(self): + return self.text + + def __aepack__(self): + return pack({'ksty': self.style, 'ktxt': self.text}, 'STXT') + +def IsStyledText(x): + return IsInstance(x, StyledText) + +class AEText: + """An AE text object with style, script and language specified""" + + def __init__(self, script, style, text): + self.script = script + self.style = style + self.text = text + + def __repr__(self): + return "AEText(%s, %s, %s)" % (`self.script`, `self.style`, `self.text`) + + def __str__(self): + return self.text + + def __aepack__(self): + return pack({keyAEScriptTag: self.script, keyAEStyles: self.style, + keyAEText: self.text}, typeAEText) + +def IsAEText(x): + return IsInstance(x, AEText) + +class IntlText: + """A text object with script and language specified""" + + def __init__(self, script, language, text): + self.script = script + self.language = language + self.text = text + + def __repr__(self): + return "IntlText(%s, %s, %s)" % (`self.script`, `self.language`, `self.text`) + + def __str__(self): + return self.text + + def __aepack__(self): + return pack(struct.pack('hh', self.script, self.language)+self.text, + typeIntlText) + +def IsIntlText(x): + return IsInstance(x, IntlText) + +class IntlWritingCode: + """An object representing script and language""" + + def __init__(self, script, language): + self.script = script + self.language = language + + def __repr__(self): + return "IntlWritingCode(%s, %s)" % (`self.script`, `self.language`) + + def __str__(self): + return "script system %d, language %d"%(self.script, self.language) + + def __aepack__(self): + return pack(struct.pack('hh', self.script, self.language), + typeIntlWritingCode) + +def IsIntlWritingCode(x): + return IsInstance(x, IntlWritingCode) + +class QDPoint: + """A point""" + + def __init__(self, v, h): + self.v = v + self.h = h + + def __repr__(self): + return "QDPoint(%s, %s)" % (`self.v`, `self.h`) + + def __str__(self): + return "(%d, %d)"%(self.v, self.h) + + def __aepack__(self): + return pack(struct.pack('hh', self.v, self.h), + typeQDPoint) + +def IsQDPoint(x): + return IsInstance(x, QDPoint) + +class QDRectangle: + """A rectangle""" + + def __init__(self, v0, h0, v1, h1): + self.v0 = v0 + self.h0 = h0 + self.v1 = v1 + self.h1 = h1 + + def __repr__(self): + return "QDRectangle(%s, %s, %s, %s)" % (`self.v0`, `self.h0`, + `self.v1`, `self.h1`) + + def __str__(self): + return "(%d, %d)-(%d, %d)"%(self.v0, self.h0, self.v1, self.h1) + + def __aepack__(self): + return pack(struct.pack('hhhh', self.v0, self.h0, self.v1, self.h1), + typeQDRectangle) + +def IsQDRectangle(x): + return IsInstance(x, QDRectangle) + +class RGBColor: + """An RGB color""" + + def __init__(self, r, g, b): + self.r = r + self.g = g + self.b = b + + def __repr__(self): + return "RGBColor(%s, %s, %s)" % (`self.r`, `self.g`, `self.b`) + + def __str__(self): + return "0x%x red, 0x%x green, 0x%x blue"% (self.r, self.g, self.b) + + def __aepack__(self): + return pack(struct.pack('hhh', self.r, self.g, self.b), + typeRGBColor) + +def IsRGBColor(x): + return IsInstance(x, RGBColor) + +class ObjectSpecifier: + + """A class for constructing and manipulation AE object specifiers in python. + + An object specifier is actually a record with four fields: + + key type description + --- ---- ----------- + + 'want' type what kind of thing we want, + e.g. word, paragraph or property + + 'form' enum how we specify the thing(s) we want, + e.g. by index, by range, by name, or by property specifier + + 'seld' any which thing(s) we want, + e.g. its index, its name, or its property specifier + + 'from' object the object in which it is contained, + or null, meaning look for it in the application + + Note that we don't call this class plain "Object", since that name + is likely to be used by the application. + """ + + def __init__(self, want, form, seld, fr = None): + self.want = want + self.form = form + self.seld = seld + self.fr = fr + + def __repr__(self): + s = "ObjectSpecifier(%s, %s, %s" % (`self.want`, `self.form`, `self.seld`) + if self.fr: + s = s + ", %s)" % `self.fr` + else: + s = s + ")" + return s + + def __aepack__(self): + return pack({'want': mktype(self.want), + 'form': mkenum(self.form), + 'seld': self.seld, + 'from': self.fr}, + 'obj ') + + +def IsObjectSpecifier(x): + return IsInstance(x, ObjectSpecifier) + + +class Property(ObjectSpecifier): + + def __init__(self, which, fr = None): + ObjectSpecifier.__init__(self, 'prop', 'prop', mkenum(which), fr) + + def __repr__(self): + if self.fr: + return "Property(%s, %s)" % (`self.seld.enum`, `self.fr`) + else: + return "Property(%s)" % `self.seld.enum` + + def __str__(self): + if self.fr: + return "Property %s of %s" % (str(self.seld), str(self.fr)) + else: + return "Property %s" % str(self.seld) + + +class SelectableItem(ObjectSpecifier): + + def __init__(self, want, seld, fr = None): + t = type(seld) + if t == StringType: + form = 'name' + elif IsRange(seld): + form = 'rang' + elif IsComparison(seld) or IsLogical(seld): + form = 'test' + else: + form = 'indx' + ObjectSpecifier.__init__(self, want, form, seld, fr) + + +class ComponentItem(SelectableItem): + # Derived classes *must* set the *class attribute* 'want' to some constant + + def __init__(self, which, fr = None): + SelectableItem.__init__(self, self.want, which, fr) + + def __repr__(self): + if not self.fr: + return "%s(%s)" % (self.__class__.__name__, `self.seld`) + return "%s(%s, %s)" % (self.__class__.__name__, `self.seld`, `self.fr`) + + def __str__(self): + seld = self.seld + if type(seld) == StringType: + ss = repr(seld) + elif IsRange(seld): + start, stop = seld.start, seld.stop + if type(start) == InstanceType == type(stop) and \ + start.__class__ == self.__class__ == stop.__class__: + ss = str(start.seld) + " thru " + str(stop.seld) + else: + ss = str(seld) + else: + ss = str(seld) + s = "%s %s" % (self.__class__.__name__, ss) + if self.fr: s = s + " of %s" % str(self.fr) + return s + + +template = """ +class %s(ComponentItem): want = '%s' +""" + +exec template % ("Text", 'text') +exec template % ("Character", 'cha ') +exec template % ("Word", 'cwor') +exec template % ("Line", 'clin') +exec template % ("Paragraph", 'cpar') +exec template % ("Window", 'cwin') +exec template % ("Document", 'docu') +exec template % ("File", 'file') +exec template % ("InsertionPoint", 'cins') +