diff options
Diffstat (limited to 'Mac/Lib/toolbox/aetools.py')
-rw-r--r-- | Mac/Lib/toolbox/aetools.py | 553 |
1 files changed, 90 insertions, 463 deletions
diff --git a/Mac/Lib/toolbox/aetools.py b/Mac/Lib/toolbox/aetools.py index bb0d20f2eb..565cdec89b 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') |