summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Mac/Lib/MiniAEFrame.py194
-rw-r--r--Mac/Lib/aepack.py347
-rw-r--r--Mac/Lib/aetools.py277
-rw-r--r--Mac/Lib/aetypes.py564
4 files changed, 1382 insertions, 0 deletions
diff --git a/Mac/Lib/MiniAEFrame.py b/Mac/Lib/MiniAEFrame.py
new file mode 100644
index 0000000..fbb6f1b
--- /dev/null
+++ b/Mac/Lib/MiniAEFrame.py
@@ -0,0 +1,194 @@
+"""MiniAEFrame - A minimal AppleEvent Application framework.
+
+There are two classes:
+ AEServer -- a mixin class offering nice AE handling.
+ MiniApplication -- a very minimal alternative to FrameWork.py,
+ only suitable for the simplest of AppleEvent servers.
+"""
+
+import sys
+import traceback
+import MacOS
+import AE
+from AppleEvents import *
+import Evt
+from Events import *
+import Menu
+import Win
+from Windows import *
+import Qd
+
+import aetools
+import EasyDialogs
+
+kHighLevelEvent = 23 # Not defined anywhere for Python yet?
+
+
+class MiniApplication:
+
+ """A minimal FrameWork.Application-like class"""
+
+ def __init__(self):
+ self.quitting = 0
+ # Initialize menu
+ self.appleid = 1
+ self.quitid = 2
+ Menu.ClearMenuBar()
+ self.applemenu = applemenu = Menu.NewMenu(self.appleid, "\024")
+ applemenu.AppendMenu("%s;(-" % self.getaboutmenutext())
+ if MacOS.runtimemodel == 'ppc':
+ applemenu.AppendResMenu('DRVR')
+ applemenu.InsertMenu(0)
+ self.quitmenu = Menu.NewMenu(self.quitid, "File")
+ self.quitmenu.AppendMenu("Quit")
+ self.quitmenu.SetItemCmd(1, ord("Q"))
+ self.quitmenu.InsertMenu(0)
+ Menu.DrawMenuBar()
+
+ def __del__(self):
+ self.close()
+
+ def close(self):
+ pass
+
+ def mainloop(self, mask = everyEvent, timeout = 60*60):
+ while not self.quitting:
+ self.dooneevent(mask, timeout)
+
+ def _quit(self):
+ self.quitting = 1
+
+ def dooneevent(self, mask = everyEvent, timeout = 60*60):
+ got, event = Evt.WaitNextEvent(mask, timeout)
+ if got:
+ self.lowlevelhandler(event)
+
+ def lowlevelhandler(self, event):
+ what, message, when, where, modifiers = event
+ h, v = where
+ if what == kHighLevelEvent:
+ msg = "High Level Event: %s %s" % \
+ (`code(message)`, `code(h | (v<<16))`)
+ try:
+ AE.AEProcessAppleEvent(event)
+ except AE.Error, err:
+ print 'AE error: ', err
+ print 'in', msg
+ traceback.print_exc()
+ return
+ elif what == keyDown:
+ c = chr(message & charCodeMask)
+ if modifiers & cmdKey:
+ if c == '.':
+ raise KeyboardInterrupt, "Command-period"
+ if c == 'q':
+ MacOS.OutputSeen()
+ self.quitting = 1
+ return
+ elif what == mouseDown:
+ partcode, window = Win.FindWindow(where)
+ if partcode == inMenuBar:
+ result = Menu.MenuSelect(where)
+ id = (result>>16) & 0xffff # Hi word
+ item = result & 0xffff # Lo word
+ if id == self.appleid:
+ if item == 1:
+ EasyDialogs.Message(self.getabouttext())
+ elif item > 1 and hasattr(Menu, 'OpenDeskAcc'):
+ name = self.applemenu.GetMenuItemText(item)
+ Menu.OpenDeskAcc(name)
+ elif id == self.quitid and item == 1:
+ MacOS.OutputSeen()
+ self.quitting = 1
+ Menu.HiliteMenu(0)
+ return
+ # Anything not handled is passed to Python/SIOUX
+ MacOS.HandleEvent(event)
+
+ def getabouttext(self):
+ return self.__class__.__name__
+
+ def getaboutmenutext(self):
+ return "About %s\311" % self.__class__.__name__
+
+
+class AEServer:
+
+ def __init__(self):
+ self.ae_handlers = {}
+
+ def installaehandler(self, classe, type, callback):
+ AE.AEInstallEventHandler(classe, type, self.callback_wrapper)
+ self.ae_handlers[(classe, type)] = callback
+
+ def close(self):
+ for classe, type in self.ae_handlers.keys():
+ AE.AERemoveEventHandler(classe, type)
+
+ def callback_wrapper(self, _request, _reply):
+ _parameters, _attributes = aetools.unpackevent(_request)
+ _class = _attributes['evcl'].type
+ _type = _attributes['evid'].type
+
+ if self.ae_handlers.has_key((_class, _type)):
+ _function = self.ae_handlers[(_class, _type)]
+ elif self.ae_handlers.has_key((_class, '****')):
+ _function = self.ae_handlers[(_class, '****')]
+ elif self.ae_handlers.has_key(('****', '****')):
+ _function = self.ae_handlers[('****', '****')]
+ else:
+ raise 'Cannot happen: AE callback without handler', (_class, _type)
+
+ # XXXX Do key-to-name mapping here
+
+ _parameters['_attributes'] = _attributes
+ _parameters['_class'] = _class
+ _parameters['_type'] = _type
+ if _parameters.has_key('----'):
+ _object = _parameters['----']
+ del _parameters['----']
+ # The try/except that used to be here can mask programmer errors.
+ # Let the program crash, the programmer can always add a **args
+ # to the formal parameter list.
+ rv = apply(_function, (_object,), _parameters)
+ else:
+ #Same try/except comment as above
+ rv = apply(_function, (), _parameters)
+
+ if rv == None:
+ aetools.packevent(_reply, {})
+ else:
+ aetools.packevent(_reply, {'----':rv})
+
+
+def code(x):
+ "Convert a long int to the 4-character code it really is"
+ s = ''
+ for i in range(4):
+ x, c = divmod(x, 256)
+ s = chr(c) + s
+ return s
+
+class _Test(AEServer, MiniApplication):
+ """Mini test application, handles required events"""
+
+ def __init__(self):
+ MiniApplication.__init__(self)
+ AEServer.__init__(self)
+ self.installaehandler('aevt', 'oapp', self.open_app)
+ self.installaehandler('aevt', 'quit', self.quit)
+ self.installaehandler('****', '****', self.other)
+ self.mainloop()
+
+ def quit(self, **args):
+ self._quit()
+
+ def open_app(self, **args):
+ pass
+
+ def other(self, _object=None, _class=None, _type=None, **args):
+ print 'AppleEvent', (_class, _type), 'for', _object, 'Other args:', args
+
+
+if __name__ == '__main__':
+ _Test()
diff --git a/Mac/Lib/aepack.py b/Mac/Lib/aepack.py
new file mode 100644
index 0000000..be6e645
--- /dev/null
+++ b/Mac/Lib/aepack.py
@@ -0,0 +1,347 @@
+"""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 *
+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 : typeFloat,
+ typeColorTable : typeAEList,
+ typeDrawingArea : typeAERecord,
+ typeFixed : typeFloat,
+ typeExtended : typeFloat,
+ 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:
+ return AE.AECreateDesc('doub', struct.pack('d', x))
+ 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])
+ t = desc.type # This is a guess by Jack....
+
+ 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 == typeFalse:
+ return 0
+ if t == typeFloat:
+ data = desc.data
+ return struct.unpack('d', data)[0]
+ 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)
+ if t == typeLongInteger:
+ return struct.unpack('l', desc.data)[0]
+ if t == typeLongDateTime:
+ a, b = struct.unpack('lL', desc.data)
+ return (long(a) << 32) + b
+ 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/aetools.py b/Mac/Lib/aetools.py
new file mode 100644
index 0000000..1af761e
--- /dev/null
+++ b/Mac/Lib/aetools.py
@@ -0,0 +1,277 @@
+"""Tools for use in AppleEvent clients and servers.
+
+pack(x) converts a Python object to an AEDesc object
+unpack(desc) does the reverse
+
+packevent(event, parameters, attributes) sets params and attrs in an AEAppleEvent record
+unpackevent(event) returns the parameters and attributes from an AEAppleEvent record
+
+Plus... Lots of classes and routines that help representing AE objects,
+ranges, conditionals, logicals, etc., so you can write, e.g.:
+
+ x = Character(1, Document("foobar"))
+
+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.
+
+"""
+
+
+from types import *
+import AE
+import AppleEvents
+import MacOS
+import sys
+
+from aetypes import *
+from aepack import pack, unpack, coerce, AEDescType
+
+Error = 'aetools.Error'
+
+# 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',
+ 'rtid',
+ 'evcl',
+ 'evid',
+ 'addr',
+ 'optk',
+ 'timo',
+ 'inte', # this attribute is read only - will be set in AESend
+ 'esrc', # this attribute is read only
+ 'miss', # this attribute is read only
+ 'from' # new in 1.0.1
+]
+
+def missed(ae):
+ try:
+ desc = ae.AEGetAttributeDesc('miss', 'keyw')
+ except AE.Error, msg:
+ return None
+ return desc.data
+
+def unpackevent(ae):
+ parameters = {}
+ try:
+ dirobj = ae.AEGetParamDesc('----', '****')
+ except AE.Error:
+ pass
+ else:
+ parameters['----'] = unpack(dirobj)
+ del dirobj
+ while 1:
+ key = missed(ae)
+ if not key: break
+ parameters[key] = unpack(ae.AEGetParamDesc(key, '****'))
+ attributes = {}
+ for key in aekeywords:
+ try:
+ desc = ae.AEGetAttributeDesc(key, '****')
+ except (AE.Error, MacOS.Error), msg:
+ if msg[0] != -1701 and msg[0] != -1704:
+ raise sys.exc_type, sys.exc_value
+ continue
+ attributes[key] = unpack(desc)
+ return parameters, attributes
+
+def packevent(ae, parameters = {}, attributes = {}):
+ for key, value in parameters.items():
+ ae.AEPutParamDesc(key, pack(value))
+ for key, value in attributes.items():
+ ae.AEPutAttributeDesc(key, pack(value))
+
+#
+# Support routine for automatically generated Suite interfaces
+# These routines are also useable for the reverse function.
+#
+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) or edict is None:
+ 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']
+ err_a1 = errn
+ if arguments.has_key('errs'):
+ err_a2 = arguments['errs']
+ else:
+ err_a2 = MacOS.GetErrorString(errn)
+ if arguments.has_key('erob'):
+ err_a3 = arguments['erob']
+ else:
+ err_a3 = None
+
+ return (err_a1, err_a2, err_a3)
+
+class TalkTo:
+ """An AE connection to an application"""
+ _signature = None # Can be overridden by subclasses
+
+ def __init__(self, signature=None, start=0, timeout=0):
+ """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.
+ """
+ self.target_signature = None
+ if signature is None:
+ signature = self._signature
+ 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)
+ self.target_signature = signature
+ else:
+ raise TypeError, "signature should be 4-char string or AEDesc"
+ self.send_flags = AppleEvents.kAEWaitReply
+ self.send_priority = AppleEvents.kAENormalPriority
+ if timeout:
+ self.send_timeout = timeout
+ else:
+ self.send_timeout = AppleEvents.kAEDefaultTimeout
+ if start:
+ self.start()
+
+ def start(self):
+ """Start the application, if it is not running yet"""
+ try:
+ self.send('ascr', 'noop')
+ except AE.Error:
+ _launch(self.target_signature)
+
+ 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))
+
+ #
+ # The following events are somehow "standard" and don't seem to appear in any
+ # suite...
+ #
+ def activate(self):
+ """Send 'activate' command"""
+ self.send('misc', 'actv')
+
+ def _get(self, _object, as=None, _attributes={}):
+ """_get: get data from an object
+ Required argument: the object
+ Keyword argument _attributes: AppleEvent attribute dictionary
+ Returns: the data
+ """
+ _code = 'core'
+ _subcode = 'getd'
+
+ _arguments = {'----':_object}
+ if as:
+ _arguments['rtyp'] = mktype(as)
+
+ _reply, _arguments, _attributes = self.send(_code, _subcode,
+ _arguments, _attributes)
+ if _arguments.has_key('errn'):
+ raise Error, decodeerror(_arguments)
+
+ if _arguments.has_key('----'):
+ return _arguments['----']
+
+# Tiny Finder class, for local use only
+
+class _miniFinder(TalkTo):
+ def open(self, _object, _attributes={}, **_arguments):
+ """open: Open the specified object(s)
+ Required argument: list of objects to open
+ Keyword argument _attributes: AppleEvent attribute dictionary
+ """
+ _code = 'aevt'
+ _subcode = 'odoc'
+
+ if _arguments: raise TypeError, 'No optional args expected'
+ _arguments['----'] = _object
+
+
+ _reply, _arguments, _attributes = self.send(_code, _subcode,
+ _arguments, _attributes)
+ if _arguments.has_key('errn'):
+ raise Error, decodeerror(_arguments)
+ # XXXX Optionally decode result
+ if _arguments.has_key('----'):
+ return _arguments['----']
+#pass
+
+_finder = _miniFinder('MACS')
+
+def _launch(appfile):
+ """Open a file thru the finder. Specify file by name or fsspec"""
+ _finder.open(_application_file(('ID ', appfile)))
+
+
+class _application_file(ComponentItem):
+ """application file - An application's file on disk"""
+ want = 'appf'
+
+_application_file._propdict = {
+}
+_application_file._elemdict = {
+}
+
+# Test program
+# XXXX Should test more, really...
+
+def test():
+ target = AE.AECreateDesc('sign', 'quil')
+ ae = AE.AECreateAppleEvent('aevt', 'oapp', target, -1, 0)
+ print unpackevent(ae)
+ raw_input(":")
+ ae = AE.AECreateAppleEvent('core', 'getd', target, -1, 0)
+ obj = Character(2, Word(1, Document(1)))
+ print obj
+ print repr(obj)
+ packevent(ae, {'----': obj})
+ params, attrs = unpackevent(ae)
+ print params['----']
+ raw_input(":")
+
+if __name__ == '__main__':
+ test()
+ sys.exit(1)
diff --git a/Mac/Lib/aetypes.py b/Mac/Lib/aetypes.py
new file mode 100644
index 0000000..253d1e7
--- /dev/null
+++ b/Mac/Lib/aetypes.py
@@ -0,0 +1,564 @@
+"""aetypes - Python objects representing various AE types."""
+
+from AppleEvents 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 Boolean:
+ """An AE boolean value"""
+
+ def __init__(self, bool):
+ self.bool = (not not bool)
+
+ def __repr__(self):
+ return "Boolean(%s)" % `self.bool`
+
+ def __str__(self):
+ if self.bool:
+ return "True"
+ else:
+ return "False"
+
+ def __aepack__(self):
+ return pack(struct.pack('b', self.bool), 'bool')
+
+def IsBoolean(x):
+ return IsInstance(x, Boolean)
+
+def mkboolean(bool):
+ if IsBoolean(bool): return bool
+ return Boolean(bool)
+
+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 NComparison(Comparison):
+ # The class attribute 'relo' must be set in a subclass
+
+ def __init__(self, obj1, obj2):
+ Comparison.__init__(obj1, self.relo, obj2)
+
+class Ordinal:
+ """An AE Ordinal"""
+
+ def __init__(self, abso):
+# self.obj1 = obj1
+ self.abso = "%-4.4s" % str(abso)
+
+ def __repr__(self):
+ return "Ordinal(%s)" % (`self.abso`)
+
+ def __str__(self):
+ return "%s" % (string.strip(self.abso))
+
+ def __aepack__(self):
+ return pack(self.abso, 'abso')
+
+def IsOrdinal(x):
+ return IsInstance(x, Ordinal)
+
+class NOrdinal(Ordinal):
+ # The class attribute 'abso' must be set in a subclass
+
+ def __init__(self):
+ Ordinal.__init__(self, self.abso)
+
+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 4-char class code of thing we want,
+ e.g. word, paragraph or property
+
+ 'form' enum how we specify which 'want' 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)
+
+
+# Backwards compatability, sigh...
+class Property(ObjectSpecifier):
+
+ def __init__(self, which, fr = None, want='prop'):
+ ObjectSpecifier.__init__(self, want, 'prop', mktype(which), fr)
+
+ def __repr__(self):
+ if self.fr:
+ return "Property(%s, %s)" % (`self.seld.type`, `self.fr`)
+ else:
+ return "Property(%s)" % `self.seld.type`
+
+ 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 NProperty(ObjectSpecifier):
+ # Subclasses *must* self baseclass attributes:
+ # want is the type of this property
+ # which is the property name of this property
+
+ def __init__(self, fr = None):
+ #try:
+ # dummy = self.want
+ #except:
+ # self.want = 'prop'
+ self.want = 'prop'
+ ObjectSpecifier.__init__(self, self.want, 'prop',
+ mktype(self.which), fr)
+
+ def __repr__(self):
+ rv = "Property(%s"%`self.seld.type`
+ if self.fr:
+ rv = rv + ", fr=%s" % `self.fr`
+ if self.want != 'prop':
+ rv = rv + ", want=%s" % `self.want`
+ return rv + ")"
+
+ 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'
+ elif t == TupleType:
+ # Breakout: specify both form and seld in a tuple
+ # (if you want ID or rele or somesuch)
+ form, seld = seld
+ else:
+ form = 'indx'
+ ObjectSpecifier.__init__(self, want, form, seld, fr)
+
+
+class ComponentItem(SelectableItem):
+ # Derived classes *must* set the *class attribute* 'want' to some constant
+ # Also, dictionaries _propdict and _elemdict must be set to map property
+ # and element names to the correct classes
+
+ 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
+
+ def __getattr__(self, name):
+ if self._elemdict.has_key(name):
+ cls = self._elemdict[name]
+ return DelayedComponentItem(cls, self)
+ if self._propdict.has_key(name):
+ cls = self._propdict[name]
+ return cls(self)
+ raise AttributeError, name
+
+
+class DelayedComponentItem:
+ def __init__(self, compclass, fr):
+ self.compclass = compclass
+ self.fr = fr
+
+ def __call__(self, which):
+ return self.compclass(which, self.fr)
+
+ def __repr__(self):
+ return "%s(???, %s)" % (self.__class__.__name__, `self.fr`)
+
+ def __str__(self):
+ return "selector for element %s of %s"%(self.__class__.__name__, str(self.fr))
+
+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')
+