| 
									
										
										
										
											2001-08-19 22:05:06 +00:00
										 |  |  | """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 * | 
					
						
							| 
									
										
										
										
											2001-08-25 12:15:04 +00:00
										 |  |  | from Carbon import AE | 
					
						
							|  |  |  | from Carbon import AppleEvents | 
					
						
							| 
									
										
										
										
											2001-08-19 22:05:06 +00:00
										 |  |  | 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) |