[Python-checkins] python/dist/src/Lib/plat-mac Audio_mac.py,NONE,1.1 EasyDialogs.py,NONE,1.1 FrameWork.py,NONE,1.1 MiniAEFrame.py,NONE,1.1 PixMapWrapper.py,NONE,1.1 WASTEconst.py,NONE,1.1 aepack.py,NONE,1.1 aetools.py,NONE,1.1 aetypes.py,NONE,1.1 applesingle.py,NONE,1.1 appletrawmain.py,NONE,1.1 appletrunner.py,NONE,1.1 argvemulator.py,NONE,1.1 bgenlocations.py,NONE,1.1 buildtools.py,NONE,1.1 bundlebuilder.py,NONE,1.1 cfmfile.py,NONE,1.1 dialogs.rsrc,NONE,1.1 errors.rsrc,NONE,1.1 findertools.py,NONE,1.1 ic.py,NONE,1.1 icopen.py,NONE,1.1 macerrors.py,NONE,1.1 macfs.py,NONE,1.1 macostools.py,NONE,1.1 macresource.py,NONE,1.1 plistlib.py,NONE,1.1 videoreader.py,NONE,1.1

jackjansen@users.sourceforge.net jackjansen@users.sourceforge.net
Mon, 30 Dec 2002 14:04:23 -0800


Update of /cvsroot/python/python/dist/src/Lib/plat-mac
In directory sc8-pr-cvs1:/tmp/cvs-serv29062/Lib/plat-mac

Added Files:
	Audio_mac.py EasyDialogs.py FrameWork.py MiniAEFrame.py 
	PixMapWrapper.py WASTEconst.py aepack.py aetools.py aetypes.py 
	applesingle.py appletrawmain.py appletrunner.py 
	argvemulator.py bgenlocations.py buildtools.py 
	bundlebuilder.py cfmfile.py dialogs.rsrc errors.rsrc 
	findertools.py ic.py icopen.py macerrors.py macfs.py 
	macostools.py macresource.py plistlib.py videoreader.py 
Log Message:
Moved most of Mac/Lib hierarchy to Lib/plat-mac: it can be used both
in MacPython-OS9 and MacPython-OSX (or the equivalent unix Python on
Mac OS X). The only items remaining in Mac/Lib are modules that are
meaningful only for MacPython-OS9 (CFM stuff, MacPython preferences
in resources, etc).


--- NEW FILE: Audio_mac.py ---
QSIZE = 100000
error='Audio_mac.error'

class Play_Audio_mac:

	def __init__(self, qsize=QSIZE):
		self._chan = None
		self._qsize = qsize
		self._outrate = 22254
		self._sampwidth = 1
		self._nchannels = 1
		self._gc = []
		self._usercallback = None

	def __del__(self):
		self.stop()
		self._usercallback = None

	def wait(self):
		import time
		while self.getfilled():
			time.sleep(0.1)
		self._chan = None
		self._gc = []

	def stop(self, quietNow = 1):
		##chan = self._chan
		self._chan = None
		##chan.SndDisposeChannel(1)
		self._gc = []

	def setoutrate(self, outrate):
		self._outrate = outrate

	def setsampwidth(self, sampwidth):
		self._sampwidth = sampwidth

	def setnchannels(self, nchannels):
		self._nchannels = nchannels

	def writeframes(self, data):
		import time
		from Carbon.Sound import bufferCmd, callBackCmd, extSH
		import struct
		import MacOS
		if not self._chan:
			from Carbon import Snd
			self._chan = Snd.SndNewChannel(5, 0, self._callback)
		nframes = len(data) / self._nchannels / self._sampwidth
		if len(data) != nframes * self._nchannels * self._sampwidth:
			raise error, 'data is not a whole number of frames'
		while self._gc and \
			  self.getfilled() + nframes > \
				self._qsize / self._nchannels / self._sampwidth:
			time.sleep(0.1)
		if self._sampwidth == 1:
			import audioop
			data = audioop.add(data, '\x80'*len(data), 1)
		h1 = struct.pack('llHhllbbl',
			id(data)+MacOS.string_id_to_buffer,
			self._nchannels,
			self._outrate, 0,
			0,
			0,
			extSH,
			60,
			nframes)
		h2 = 22*'\0'
		h3 = struct.pack('hhlll',
			self._sampwidth*8,
			0,
			0,
			0,
			0)
		header = h1+h2+h3
		self._gc.append((header, data))
		self._chan.SndDoCommand((bufferCmd, 0, header), 0)
		self._chan.SndDoCommand((callBackCmd, 0, 0), 0)

	def _callback(self, *args):
		del self._gc[0]
		if self._usercallback:
			self._usercallback()
			
	def setcallback(self, callback):
		self._usercallback = callback

	def getfilled(self):
		filled = 0
		for header, data in self._gc:
			filled = filled + len(data)
		return filled / self._nchannels / self._sampwidth

	def getfillable(self):
		return (self._qsize / self._nchannels / self._sampwidth) - self.getfilled()

	def ulaw2lin(self, data):
		import audioop
		return audioop.ulaw2lin(data, 2)

def test():
	import aifc
	import macfs
	fss, ok = macfs.PromptGetFile("Select an AIFF soundfile", "AIFF")
	if not ok: return
	fn = fss.as_pathname()
	af = aifc.open(fn, 'r')
	print af.getparams()
	p = Play_Audio_mac()
	p.setoutrate(af.getframerate())
	p.setsampwidth(af.getsampwidth())
	p.setnchannels(af.getnchannels())
	BUFSIZ = 10000
	while 1:
		data = af.readframes(BUFSIZ)
		if not data: break
		p.writeframes(data)
		print 'wrote', len(data), 'space', p.getfillable()
	p.wait()

if __name__ == '__main__':
	test()

--- NEW FILE: EasyDialogs.py ---
"""Easy to use dialogs.

Message(msg) -- display a message and an OK button.
AskString(prompt, default) -- ask for a string, display OK and Cancel buttons.
AskPassword(prompt, default) -- like AskString(), but shows text as bullets.
AskYesNoCancel(question, default) -- display a question and Yes, No and Cancel buttons.
bar = Progress(label, maxvalue) -- Display a progress bar
bar.set(value) -- Set value
bar.inc( *amount ) -- increment value by amount (default=1)
bar.label( *newlabel ) -- get or set text label. 

More documentation in each function.
This module uses DLOG resources 260 and on.
Based upon STDWIN dialogs with the same names and functions.
"""

from Carbon.Dlg import GetNewDialog, SetDialogItemText, GetDialogItemText, ModalDialog
from Carbon import Qd
from Carbon import QuickDraw
from Carbon import Dialogs
from Carbon import Windows
from Carbon import Dlg,Win,Evt,Events # sdm7g
from Carbon import Ctl
from Carbon import Controls
from Carbon import Menu
import MacOS
import string
from Carbon.ControlAccessor import *	# Also import Controls constants
import macfs
import macresource

_initialized = 0

def _initialize():
	global _initialized
	if _initialized: return
	macresource.need("DLOG", 260, "dialogs.rsrc", __name__)


def cr2lf(text):
	if '\r' in text:
		text = string.join(string.split(text, '\r'), '\n')
	return text

def lf2cr(text):
	if '\n' in text:
		text = string.join(string.split(text, '\n'), '\r')
	if len(text) > 253:
		text = text[:253] + '\311'
	return text

def Message(msg, id=260, ok=None):
	"""Display a MESSAGE string.
	
	Return when the user clicks the OK button or presses Return.
	
	The MESSAGE string can be at most 255 characters long.
	"""
	_initialize()
	d = GetNewDialog(id, -1)
	if not d:
		print "EasyDialogs: Can't get DLOG resource with id =", id, " (missing resource file?)"
		return
	h = d.GetDialogItemAsControl(2)
	SetDialogItemText(h, lf2cr(msg))
	if ok != None:
		h = d.GetDialogItemAsControl(1)
		h.SetControlTitle(ok)
	d.SetDialogDefaultItem(1)
	d.AutoSizeDialog()
	d.GetDialogWindow().ShowWindow()
	while 1:
		n = ModalDialog(None)
		if n == 1:
			return


def AskString(prompt, default = "", id=261, ok=None, cancel=None):
	"""Display a PROMPT string and a text entry field with a DEFAULT string.
	
	Return the contents of the text entry field when the user clicks the
	OK button or presses Return.
	Return None when the user clicks the Cancel button.
	
	If omitted, DEFAULT is empty.
	
	The PROMPT and DEFAULT strings, as well as the return value,
	can be at most 255 characters long.
	"""
	
	_initialize()
	d = GetNewDialog(id, -1)
	if not d:
		print "EasyDialogs: Can't get DLOG resource with id =", id, " (missing resource file?)"
		return
	h = d.GetDialogItemAsControl(3)
	SetDialogItemText(h, lf2cr(prompt))
	h = d.GetDialogItemAsControl(4)
	SetDialogItemText(h, lf2cr(default))
	d.SelectDialogItemText(4, 0, 999)
#	d.SetDialogItem(4, 0, 255)
	if ok != None:
		h = d.GetDialogItemAsControl(1)
		h.SetControlTitle(ok)
	if cancel != None:
		h = d.GetDialogItemAsControl(2)
		h.SetControlTitle(cancel)
	d.SetDialogDefaultItem(1)
	d.SetDialogCancelItem(2)
	d.AutoSizeDialog()
	d.GetDialogWindow().ShowWindow()
	while 1:
		n = ModalDialog(None)
		if n == 1:
			h = d.GetDialogItemAsControl(4)
			return cr2lf(GetDialogItemText(h))
		if n == 2: return None

def AskPassword(prompt,	 default='', id=264, ok=None, cancel=None):	
	"""Display a PROMPT string and a text entry field with a DEFAULT string.
	The string is displayed as bullets only.
	
	Return the contents of the text entry field when the user clicks the
	OK button or presses Return.
	Return None when the user clicks the Cancel button.
	
	If omitted, DEFAULT is empty.
	
	The PROMPT and DEFAULT strings, as well as the return value,
	can be at most 255 characters long.
	"""
	_initialize()
	d = GetNewDialog(id, -1)
	if not d:
		print "EasyDialogs: Can't get DLOG resource with id =", id, " (missing resource file?)"
		return
	h = d.GetDialogItemAsControl(3)
	SetDialogItemText(h, lf2cr(prompt))	
	pwd = d.GetDialogItemAsControl(4)
	bullets = '\245'*len(default)
##	SetControlData(pwd, kControlEditTextPart, kControlEditTextTextTag, bullets)
	SetControlData(pwd, kControlEditTextPart, kControlEditTextPasswordTag, default)
	d.SelectDialogItemText(4, 0, 999)
	Ctl.SetKeyboardFocus(d.GetDialogWindow(), pwd, kControlEditTextPart)
	if ok != None:
		h = d.GetDialogItemAsControl(1)
		h.SetControlTitle(ok)
	if cancel != None:
		h = d.GetDialogItemAsControl(2)
		h.SetControlTitle(cancel)
	d.SetDialogDefaultItem(Dialogs.ok)
	d.SetDialogCancelItem(Dialogs.cancel)
	d.AutoSizeDialog()
	d.GetDialogWindow().ShowWindow()
	while 1:
		n = ModalDialog(None)
		if n == 1:
			h = d.GetDialogItemAsControl(4)
			return cr2lf(GetControlData(pwd, kControlEditTextPart, kControlEditTextPasswordTag))
		if n == 2: return None

def AskYesNoCancel(question, default = 0, yes=None, no=None, cancel=None, id=262):
	"""Display a QUESTION string which can be answered with Yes or No.
	
	Return 1 when the user clicks the Yes button.
	Return 0 when the user clicks the No button.
	Return -1 when the user clicks the Cancel button.
	
	When the user presses Return, the DEFAULT value is returned.
	If omitted, this is 0 (No).
	
	The QUESTION string can be at most 255 characters.
	"""
	
	_initialize()
	d = GetNewDialog(id, -1)
	if not d:
		print "EasyDialogs: Can't get DLOG resource with id =", id, " (missing resource file?)"
		return
	# Button assignments:
	# 1 = default (invisible)
	# 2 = Yes
	# 3 = No
	# 4 = Cancel
	# The question string is item 5
	h = d.GetDialogItemAsControl(5)
	SetDialogItemText(h, lf2cr(question))
	if yes != None:
		if yes == '':
			d.HideDialogItem(2)
		else:
			h = d.GetDialogItemAsControl(2)
			h.SetControlTitle(yes)
	if no != None:
		if no == '':
			d.HideDialogItem(3)
		else:
			h = d.GetDialogItemAsControl(3)
			h.SetControlTitle(no)
	if cancel != None:
		if cancel == '':
			d.HideDialogItem(4)
		else:
			h = d.GetDialogItemAsControl(4)
			h.SetControlTitle(cancel)
	d.SetDialogCancelItem(4)
	if default == 1:
		d.SetDialogDefaultItem(2)
	elif default == 0:
		d.SetDialogDefaultItem(3)
	elif default == -1:
		d.SetDialogDefaultItem(4)
	d.AutoSizeDialog()
	d.GetDialogWindow().ShowWindow()
	while 1:
		n = ModalDialog(None)
		if n == 1: return default
		if n == 2: return 1
		if n == 3: return 0
		if n == 4: return -1


		

screenbounds = Qd.GetQDGlobalsScreenBits().bounds
screenbounds = screenbounds[0]+4, screenbounds[1]+4, \
	screenbounds[2]-4, screenbounds[3]-4

kControlProgressBarIndeterminateTag = 'inde'	# from Controls.py


class ProgressBar:
	def __init__(self, title="Working...", maxval=0, label="", id=263):
		self.w = None
		self.d = None
		_initialize()
		self.d = GetNewDialog(id, -1)
		self.w = self.d.GetDialogWindow()
		self.label(label)
		self.title(title)
		self.set(0, maxval)
		self.d.AutoSizeDialog()
		self.w.ShowWindow()
		self.d.DrawDialog()

	def __del__( self ):
		if self.w:
			self.w.BringToFront()
			self.w.HideWindow()
		del self.w
		del self.d
		
	def title(self, newstr=""):
		"""title(text) - Set title of progress window"""
		self.w.BringToFront()
		self.w.SetWTitle(newstr)
		
	def label( self, *newstr ):
		"""label(text) - Set text in progress box"""
		self.w.BringToFront()
		if newstr:
			self._label = lf2cr(newstr[0])
		text_h = self.d.GetDialogItemAsControl(2)
		SetDialogItemText(text_h, self._label)

	def _update(self, value):
		maxval = self.maxval
		if maxval == 0:		# an indeterminate bar
			Ctl.IdleControls(self.w)	# spin the barber pole
		else:				# a determinate bar
			if maxval > 32767:
				value = int(value/(maxval/32767.0))
				maxval = 32767
			progbar = self.d.GetDialogItemAsControl(3)
			progbar.SetControlMaximum(maxval)
			progbar.SetControlValue(value)	# set the bar length

		# Test for cancel button
		ready, ev = Evt.WaitNextEvent( Events.mDownMask, 1  )
		if ready :
			what,msg,when,where,mod = ev
			part = Win.FindWindow(where)[0]
			if Dlg.IsDialogEvent(ev):
				ds = Dlg.DialogSelect(ev)
				if ds[0] and ds[1] == self.d and ds[-1] == 1:
					self.w.HideWindow()
					self.w = None
					self.d = None
					raise KeyboardInterrupt, ev
			else:
				if part == 4:	# inDrag 
					self.w.DragWindow(where, screenbounds)
				else:
					MacOS.HandleEvent(ev) 
			
			
	def set(self, value, max=None):
		"""set(value) - Set progress bar position"""
		if max != None:
			self.maxval = max
			bar = self.d.GetDialogItemAsControl(3)
			if max <= 0:	# indeterminate bar
				bar.SetControlData(0,kControlProgressBarIndeterminateTag,'\x01')
			else:			# determinate bar
				bar.SetControlData(0,kControlProgressBarIndeterminateTag,'\x00')
		if value < 0:
			value = 0
		elif value > self.maxval:
			value = self.maxval
		self.curval = value
		self._update(value)

	def inc(self, n=1):
		"""inc(amt) - Increment progress bar position"""
		self.set(self.curval + n)

ARGV_ID=265
ARGV_ITEM_OK=1
ARGV_ITEM_CANCEL=2
ARGV_OPTION_GROUP=3
ARGV_OPTION_EXPLAIN=4
ARGV_OPTION_VALUE=5
ARGV_OPTION_ADD=6
ARGV_COMMAND_GROUP=7
ARGV_COMMAND_EXPLAIN=8
ARGV_COMMAND_ADD=9
ARGV_ADD_OLDFILE=10
ARGV_ADD_NEWFILE=11
ARGV_ADD_FOLDER=12
ARGV_CMDLINE_GROUP=13
ARGV_CMDLINE_DATA=14

##def _myModalDialog(d):
##	while 1:
##		ready, ev = Evt.WaitNextEvent(0xffff, -1)
##		print 'DBG: WNE', ready, ev
##		if ready : 
##			what,msg,when,where,mod = ev
##			part, window = Win.FindWindow(where)
##			if Dlg.IsDialogEvent(ev):
##				didit, dlgdone, itemdone = Dlg.DialogSelect(ev)
##				print 'DBG: DialogSelect', didit, dlgdone, itemdone, d
##				if didit and dlgdone == d:
##					return itemdone
##			elif window == d.GetDialogWindow():
##				d.GetDialogWindow().SelectWindow()
##				if part == 4:	# inDrag 
##						d.DragWindow(where, screenbounds)
##				else:
##					MacOS.HandleEvent(ev) 
##			else:
##				MacOS.HandleEvent(ev) 
##
def _setmenu(control, items):
		mhandle = control.GetControlData_Handle(Controls.kControlMenuPart,
				Controls.kControlPopupButtonMenuHandleTag)
		menu = Menu.as_Menu(mhandle)
		for item in items:
			if type(item) == type(()):
				label = item[0]
			else:
				label = item
			if label[-1] == '=' or label[-1] == ':':
				label = label[:-1]
			menu.AppendMenu(label)
##		mhandle, mid = menu.getpopupinfo()
##		control.SetControlData_Handle(Controls.kControlMenuPart,
##				Controls.kControlPopupButtonMenuHandleTag, mhandle)
		control.SetControlMinimum(1)
		control.SetControlMaximum(len(items)+1)
		
def _selectoption(d, optionlist, idx):
	if idx < 0 or idx >= len(optionlist):
		MacOS.SysBeep()
		return
	option = optionlist[idx]
	if type(option) == type(()):
		if len(option) == 4:
			help = option[2]
		elif len(option) > 1:
			help = option[-1]
		else:
			help = ''
	else:
		help = ''
	h = d.GetDialogItemAsControl(ARGV_OPTION_EXPLAIN)
	if help and len(help) > 250:
		help = help[:250] + '...'
	Dlg.SetDialogItemText(h, help)
	hasvalue = 0
	if type(option) == type(()):
		label = option[0]
	else:
		label = option
	if label[-1] == '=' or label[-1] == ':':
		hasvalue = 1
	h = d.GetDialogItemAsControl(ARGV_OPTION_VALUE)
	Dlg.SetDialogItemText(h, '')
	if hasvalue:
		d.ShowDialogItem(ARGV_OPTION_VALUE)
		d.SelectDialogItemText(ARGV_OPTION_VALUE, 0, 0)
	else:
		d.HideDialogItem(ARGV_OPTION_VALUE)


def GetArgv(optionlist=None, commandlist=None, addoldfile=1, addnewfile=1, addfolder=1, id=ARGV_ID):
	_initialize()
	d = GetNewDialog(id, -1)
	if not d:
		print "EasyDialogs: Can't get DLOG resource with id =", id, " (missing resource file?)"
		return
#	h = d.GetDialogItemAsControl(3)
#	SetDialogItemText(h, lf2cr(prompt))
#	h = d.GetDialogItemAsControl(4)
#	SetDialogItemText(h, lf2cr(default))
#	d.SelectDialogItemText(4, 0, 999)
#	d.SetDialogItem(4, 0, 255)
	if optionlist:
		_setmenu(d.GetDialogItemAsControl(ARGV_OPTION_GROUP), optionlist)
		_selectoption(d, optionlist, 0)
	else:
		d.GetDialogItemAsControl(ARGV_OPTION_GROUP).DeactivateControl()
	if commandlist:
		_setmenu(d.GetDialogItemAsControl(ARGV_COMMAND_GROUP), commandlist)
		if type(commandlist[0]) == type(()) and len(commandlist[0]) > 1:
			help = commandlist[0][-1]
			h = d.GetDialogItemAsControl(ARGV_COMMAND_EXPLAIN)
			Dlg.SetDialogItemText(h, help)
	else:
		d.GetDialogItemAsControl(ARGV_COMMAND_GROUP).DeactivateControl()
	if not addoldfile:
		d.GetDialogItemAsControl(ARGV_ADD_OLDFILE).DeactivateControl()
	if not addnewfile:
		d.GetDialogItemAsControl(ARGV_ADD_NEWFILE).DeactivateControl()
	if not addfolder:
		d.GetDialogItemAsControl(ARGV_ADD_FOLDER).DeactivateControl()
	d.SetDialogDefaultItem(ARGV_ITEM_OK)
	d.SetDialogCancelItem(ARGV_ITEM_CANCEL)
	d.GetDialogWindow().ShowWindow()
	d.DrawDialog()
	if hasattr(MacOS, 'SchedParams'):
		appsw = MacOS.SchedParams(1, 0)
	try:
		while 1:
			stringstoadd = []
			n = ModalDialog(None)
			if n == ARGV_ITEM_OK:
				break
			elif n == ARGV_ITEM_CANCEL:
				raise SystemExit
			elif n == ARGV_OPTION_GROUP:
				idx = d.GetDialogItemAsControl(ARGV_OPTION_GROUP).GetControlValue()-1
				_selectoption(d, optionlist, idx)
			elif n == ARGV_OPTION_VALUE:
				pass
			elif n == ARGV_OPTION_ADD:
				idx = d.GetDialogItemAsControl(ARGV_OPTION_GROUP).GetControlValue()-1
				if 0 <= idx < len(optionlist):
					option = optionlist[idx]
					if type(option) == type(()):
						option = option[0]
					if option[-1] == '=' or option[-1] == ':':
						option = option[:-1]
						h = d.GetDialogItemAsControl(ARGV_OPTION_VALUE)
						value = Dlg.GetDialogItemText(h)
					else:
						value = ''
					if len(option) == 1:
						stringtoadd = '-' + option
					else:
						stringtoadd = '--' + option
					stringstoadd = [stringtoadd]
					if value:
						stringstoadd.append(value)
				else:
					MacOS.SysBeep()
			elif n == ARGV_COMMAND_GROUP:
				idx = d.GetDialogItemAsControl(ARGV_COMMAND_GROUP).GetControlValue()-1
				if 0 <= idx < len(commandlist) and type(commandlist[idx]) == type(()) and \
						len(commandlist[idx]) > 1:
					help = commandlist[idx][-1]
					h = d.GetDialogItemAsControl(ARGV_COMMAND_EXPLAIN)
					Dlg.SetDialogItemText(h, help)
			elif n == ARGV_COMMAND_ADD:
				idx = d.GetDialogItemAsControl(ARGV_COMMAND_GROUP).GetControlValue()-1
				if 0 <= idx < len(commandlist):
					command = commandlist[idx]
					if type(command) == type(()):
						command = command[0]
					stringstoadd = [command]
				else:
					MacOS.SysBeep()
			elif n == ARGV_ADD_OLDFILE:
				fss, ok = macfs.StandardGetFile()
				if ok:
					stringstoadd = [fss.as_pathname()]
			elif n == ARGV_ADD_NEWFILE:
				fss, ok = macfs.StandardPutFile('')
				if ok:
					stringstoadd = [fss.as_pathname()]
			elif n == ARGV_ADD_FOLDER:
				fss, ok = macfs.GetDirectory()
				if ok:
					stringstoadd = [fss.as_pathname()]
			elif n == ARGV_CMDLINE_DATA:
				pass # Nothing to do
			else:
				raise RuntimeError, "Unknown dialog item %d"%n
			
			for stringtoadd in stringstoadd:
				if '"' in stringtoadd or "'" in stringtoadd or " " in stringtoadd:
					stringtoadd = `stringtoadd`
				h = d.GetDialogItemAsControl(ARGV_CMDLINE_DATA)
				oldstr = GetDialogItemText(h)
				if oldstr and oldstr[-1] != ' ':
					oldstr = oldstr + ' '
				oldstr = oldstr + stringtoadd
				if oldstr[-1] != ' ':
					oldstr = oldstr + ' '
				SetDialogItemText(h, oldstr)
				d.SelectDialogItemText(ARGV_CMDLINE_DATA, 0x7fff, 0x7fff)
		h = d.GetDialogItemAsControl(ARGV_CMDLINE_DATA)
		oldstr = GetDialogItemText(h)
		tmplist = string.split(oldstr)
		newlist = []
		while tmplist:
			item = tmplist[0]
			del tmplist[0]
			if item[0] == '"':
				while item[-1] != '"':
					if not tmplist:
						raise RuntimeError, "Unterminated quoted argument"
					item = item + ' ' + tmplist[0]
					del tmplist[0]
				item = item[1:-1]
			if item[0] == "'":
				while item[-1] != "'":
					if not tmplist:
						raise RuntimeError, "Unterminated quoted argument"
					item = item + ' ' + tmplist[0]
					del tmplist[0]
				item = item[1:-1]
			newlist.append(item)
		return newlist
	finally:
		if hasattr(MacOS, 'SchedParams'):
			apply(MacOS.SchedParams, appsw)
		del d
	
def test():
	import time, sys

	Message("Testing EasyDialogs.")
	optionlist = (('v', 'Verbose'), ('verbose', 'Verbose as long option'), 
				('flags=', 'Valued option'), ('f:', 'Short valued option'))
	commandlist = (('start', 'Start something'), ('stop', 'Stop something'))
	argv = GetArgv(optionlist=optionlist, commandlist=commandlist, addoldfile=0)
	for i in range(len(argv)):
		print 'arg[%d] = %s'%(i, `argv[i]`)
	print 'Type return to continue - ',
	sys.stdin.readline()
	ok = AskYesNoCancel("Do you want to proceed?")
	ok = AskYesNoCancel("Do you want to identify?", yes="Identify", no="No")
	if ok > 0:
		s = AskString("Enter your first name", "Joe")
		s2 = AskPassword("Okay %s, tell us your nickname"%s, s, cancel="None")
		if not s2:
			Message("%s has no secret nickname"%s)
		else:
			Message("Hello everybody!!\nThe secret nickname of %s is %s!!!"%(s, s2))
	text = ( "Working Hard...", "Hardly Working..." ,
			"So far, so good!", "Keep on truckin'" )
	bar = ProgressBar("Progress, progress...", 0, label="Ramping up...")
	try:
		if hasattr(MacOS, 'SchedParams'):
			appsw = MacOS.SchedParams(1, 0)
		for i in xrange(20):
			bar.inc()
			time.sleep(0.05)
		bar.set(0,100)
		for i in xrange(100):
			bar.set(i)
			time.sleep(0.05)
			if i % 10 == 0:
				bar.label(text[(i/10) % 4])
		bar.label("Done.")
		time.sleep(1.0) 	# give'em a chance to see "Done."
	finally:
		del bar
		if hasattr(MacOS, 'SchedParams'):
			apply(MacOS.SchedParams, appsw)

if __name__ == '__main__':
	try:
		test()
	except KeyboardInterrupt:
		Message("Operation Canceled.")


--- NEW FILE: FrameWork.py ---
"A sort of application framework for the Mac"

DEBUG=0

import MacOS
import traceback

from Carbon.AE import *
from Carbon.AppleEvents import *
from Carbon.Ctl import *
from Carbon.Controls import *
from Carbon.Dlg import *
from Carbon.Dialogs import *
from Carbon.Evt import *
from Carbon.Events import *
from Carbon.Help import *
from Carbon.Menu import *
from Carbon.Menus import *
from Carbon.Qd import *
[...1081 lines suppressed...]
	def enablehelp(self, *args):
		hm = self.gethelpmenu()
		self.nohelpitem = MenuItem(hm, "There isn't any", None, self.nohelp)
		
	def nohelp(self, *args):
		print "I told you there isn't any!"
		
	def debug(self, *args):
		import pdb
		pdb.set_trace()


def test():
	"Test program"
	app = TestApp()
	app.mainloop()


if __name__ == '__main__':
	test()

--- NEW FILE: MiniAEFrame.py ---
"""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
from Carbon import AE
from Carbon.AppleEvents import *
from Carbon import Evt
from Carbon.Events import *
from Carbon import Menu
from Carbon import Win
from Carbon.Windows import *
from Carbon 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':
					if hasattr(MacOS, 'OutputSeen'):
						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:
					if hasattr(MacOS, 'OutputSeen'):
						MacOS.OutputSeen()
					self.quitting = 1
				Menu.HiliteMenu(0)
				return
		# Anything not handled is passed to Python/SIOUX
		if hasattr(MacOS, 'HandleEvent'):
			MacOS.HandleEvent(event)
		else:
			print "Unhandled event:", 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()

--- NEW FILE: PixMapWrapper.py ---
"""PixMapWrapper - defines the PixMapWrapper class, which wraps an opaque
QuickDraw PixMap data structure in a handy Python class.  Also provides 
methods to convert to/from pixel data (from, e.g., the img module) or a
Python Imaging Library Image object.

J. Strout <joe@strout.net>  February 1999"""

from Carbon import Qd
from Carbon import QuickDraw
import struct
import MacOS
import img
import imgformat

# PixMap data structure element format (as used with struct)
_pmElemFormat = {
	'baseAddr':'l',		# address of pixel data
	'rowBytes':'H',		# bytes per row, plus 0x8000
	'bounds':'hhhh',	# coordinates imposed over pixel data
		'top':'h',
		'left':'h',
		'bottom':'h',
		'right':'h',
	'pmVersion':'h',	# flags for Color QuickDraw
	'packType':'h',		# format of compression algorithm
	'packSize':'l',		# size after compression
	'hRes':'l',			# horizontal pixels per inch
	'vRes':'l',			# vertical pixels per inch
	'pixelType':'h',	# pixel format
	'pixelSize':'h',	# bits per pixel
	'cmpCount':'h',		# color components per pixel
	'cmpSize':'h',		# bits per component
	'planeBytes':'l',	# offset in bytes to next plane
	'pmTable':'l',		# handle to color table
	'pmReserved':'l'	# reserved for future use
}

# PixMap data structure element offset
_pmElemOffset = {
	'baseAddr':0,
	'rowBytes':4,
	'bounds':6,
		'top':6,
		'left':8,
		'bottom':10,
		'right':12,
	'pmVersion':14,
	'packType':16,
	'packSize':18,
	'hRes':22,
	'vRes':26,
	'pixelType':30,
	'pixelSize':32,
	'cmpCount':34,
	'cmpSize':36,
	'planeBytes':38,
	'pmTable':42,
	'pmReserved':46
}

class PixMapWrapper:
	"""PixMapWrapper -- wraps the QD PixMap object in a Python class,
	with methods to easily get/set various pixmap fields.  Note: Use the
	PixMap() method when passing to QD calls."""

	def __init__(self):
		self.__dict__['data'] = ''
		self._header = struct.pack("lhhhhhhhlllhhhhlll",
			id(self.data)+MacOS.string_id_to_buffer,
			0,						# rowBytes
			0, 0, 0, 0,				# bounds
			0,						# pmVersion
			0, 0,					# packType, packSize
			72<<16, 72<<16,			# hRes, vRes
			QuickDraw.RGBDirect,	# pixelType
			16,						# pixelSize
			2, 5,					# cmpCount, cmpSize,
			0, 0, 0)				# planeBytes, pmTable, pmReserved
		self.__dict__['_pm'] = Qd.RawBitMap(self._header)
	
	def _stuff(self, element, bytes):
		offset = _pmElemOffset[element]
		fmt = _pmElemFormat[element]
		self._header = self._header[:offset] \
			+ struct.pack(fmt, bytes) \
			+ self._header[offset + struct.calcsize(fmt):]
		self.__dict__['_pm'] = None
	
	def _unstuff(self, element):
		offset = _pmElemOffset[element]
		fmt = _pmElemFormat[element]
		return struct.unpack(fmt, self._header[offset:offset+struct.calcsize(fmt)])[0]

	def __setattr__(self, attr, val):
		if attr == 'baseAddr':
			raise 'UseErr', "don't assign to .baseAddr -- assign to .data instead"
		elif attr == 'data':
			self.__dict__['data'] = val
			self._stuff('baseAddr', id(self.data) + MacOS.string_id_to_buffer)
		elif attr == 'rowBytes':
			# high bit is always set for some odd reason
			self._stuff('rowBytes', val | 0x8000)
		elif attr == 'bounds':
			# assume val is in official Left, Top, Right, Bottom order!
			self._stuff('left',val[0])
			self._stuff('top',val[1])
			self._stuff('right',val[2])
			self._stuff('bottom',val[3])
		elif attr == 'hRes' or attr == 'vRes':
			# 16.16 fixed format, so just shift 16 bits
			self._stuff(attr, int(val) << 16)
		elif attr in _pmElemFormat.keys():
			# any other pm attribute -- just stuff
			self._stuff(attr, val)
		else:
			self.__dict__[attr] = val	

	def __getattr__(self, attr):
		if attr == 'rowBytes':
			# high bit is always set for some odd reason
			return self._unstuff('rowBytes') & 0x7FFF
		elif attr == 'bounds':
			# return bounds in official Left, Top, Right, Bottom order!
			return ( \
				self._unstuff('left'),
				self._unstuff('top'),
				self._unstuff('right'),
				self._unstuff('bottom') )
		elif attr == 'hRes' or attr == 'vRes':
			# 16.16 fixed format, so just shift 16 bits
			return self._unstuff(attr) >> 16
		elif attr in _pmElemFormat.keys():
			# any other pm attribute -- just unstuff
			return self._unstuff(attr)
		else:
			return self.__dict__[attr]	

		
	def PixMap(self):
		"Return a QuickDraw PixMap corresponding to this data."
		if not self.__dict__['_pm']:
			self.__dict__['_pm'] = Qd.RawBitMap(self._header)
		return self.__dict__['_pm']

	def blit(self, x1=0,y1=0,x2=None,y2=None, port=None):
		"""Draw this pixmap into the given (default current) grafport.""" 
		src = self.bounds
		dest = [x1,y1,x2,y2]
		if x2 == None:
			dest[2] = x1 + src[2]-src[0]
		if y2 == None:
			dest[3] = y1 + src[3]-src[1]
		if not port: port = Qd.GetPort()
		Qd.CopyBits(self.PixMap(), port.GetPortBitMapForCopyBits(), src, tuple(dest),
				QuickDraw.srcCopy, None)
	
	def fromstring(self,s,width,height,format=imgformat.macrgb):
		"""Stuff this pixmap with raw pixel data from a string.
		Supply width, height, and one of the imgformat specifiers."""
		# we only support 16- and 32-bit mac rgb...
		# so convert if necessary
		if format != imgformat.macrgb and format != imgformat.macrgb16:
			# (LATER!)
			raise "NotImplementedError", "conversion to macrgb or macrgb16"
		self.data = s
		self.bounds = (0,0,width,height)
		self.cmpCount = 3
		self.pixelType = QuickDraw.RGBDirect
		if format == imgformat.macrgb:
			self.pixelSize = 32
			self.cmpSize = 8
		else:
			self.pixelSize = 16
			self.cmpSize = 5
		self.rowBytes = width*self.pixelSize/8

	def tostring(self, format=imgformat.macrgb):
		"""Return raw data as a string in the specified format."""
		# is the native format requested?  if so, just return data
		if (format == imgformat.macrgb and self.pixelSize == 32) or \
		   (format == imgformat.macrgb16 and self.pixelsize == 16):
			return self.data
		# otherwise, convert to the requested format
		# (LATER!)
			raise "NotImplementedError", "data format conversion"

	def fromImage(self,im):
		"""Initialize this PixMap from a PIL Image object."""
		# We need data in ARGB format; PIL can't currently do that,
		# but it can do RGBA, which we can use by inserting one null
		# up frontpm = 
		if im.mode != 'RGBA': im = im.convert('RGBA')
		data = chr(0) + im.tostring()
		self.fromstring(data, im.size[0], im.size[1])

	def toImage(self):
		"""Return the contents of this PixMap as a PIL Image object."""
		import Image
		# our tostring() method returns data in ARGB format,
		# whereas Image uses RGBA; a bit of slicing fixes this...
		data = self.tostring()[1:] + chr(0)
		bounds = self.bounds
		return Image.fromstring('RGBA',(bounds[2]-bounds[0],bounds[3]-bounds[1]),data)

def test():
	import MacOS
	import macfs
	import Image
	fsspec, ok = macfs.PromptGetFile("Image File:")
	if not ok: return
	path = fsspec.as_pathname()
	pm = PixMapWrapper()
	pm.fromImage( Image.open(path) )
	pm.blit(20,20)
	return pm


--- NEW FILE: WASTEconst.py ---
# Generated from 'WASTE.h'

kPascalStackBased = None # workaround for header parsing
def FOUR_CHAR_CODE(x): return x
weCantUndoErr = -10015
weEmptySelectionErr = -10013
weUnknownObjectTypeErr = -9478
weObjectNotFoundErr = -9477
weReadOnlyErr = -9476
weTextNotFoundErr = -9474
weInvalidTextEncodingErr = -9473
weDuplicateAttributeErr = -9472
weInvalidAttributeSizeErr = -9471
weReadOnlyAttributeErr = -9470
weOddByteCountErr = -9469
weHandlerNotFoundErr = -1717
weNotHandledErr = -1708
weNewerVersionErr = -1706
weCorruptDataErr = -1702
weProtocolErr = -603
weUndefinedSelectorErr = -50
weFlushLeft = -2
weFlushRight = -1
weFlushDefault = 0
weCenter = 1
weJustify = 2
weDirDefault = 1
weDirRightToLeft = -1
weDirLeftToRight = 0
weDoFont = 0x0001
weDoFace = 0x0002
weDoSize = 0x0004
weDoColor = 0x0008
weDoAll = weDoFont | weDoFace | weDoSize | weDoColor
weDoAddSize = 0x0010
weDoToggleFace = 0x0020
weDoReplaceFace = 0x0040
weDoPreserveScript = 0x0080
weDoExtractSubscript = 0x0100
weDoFaceMask = 0x0200
weDoDirection = 0x00000001
weDoAlignment = 0x00000002
weDoLeftIndent = 0x00000004
weDoRightIndent = 0x00000008
weDoFirstLineIndent = 0x00000010
weDoLineSpacing = 0x00000020
weDoSpaceBefore = 0x00000040
weDoSpaceAfter = 0x00000080
weDoBottomBorderStyle = 0x00000400
kLeadingEdge = -1
kTrailingEdge = 0
kObjectEdge = 2
weFAutoScroll = 0
weFOutlineHilite = 2
weFReadOnly = 5
weFUndo = 6
weFIntCutAndPaste = 7
weFDragAndDrop = 8
weFInhibitRecal = 9
weFUseTempMem = 10
weFDrawOffscreen = 11
weFInhibitRedraw = 12
weFMonoStyled = 13
weFMultipleUndo = 14
weFNoKeyboardSync = 29
weFInhibitICSupport = 30
weFInhibitColor = 31
weDoAutoScroll = 1 << weFAutoScroll
weDoOutlineHilite = 1 << weFOutlineHilite
weDoReadOnly = 1 << weFReadOnly
weDoUndo = 1 << weFUndo
weDoIntCutAndPaste = 1 << weFIntCutAndPaste
weDoDragAndDrop = 1 << weFDragAndDrop
weDoInhibitRecal = 1 << weFInhibitRecal
weDoUseTempMem = 1 << weFUseTempMem
weDoDrawOffscreen = 1 << weFDrawOffscreen
weDoInhibitRedraw = 1 << weFInhibitRedraw
weDoMonoStyled = 1 << weFMonoStyled
weDoMultipleUndo = 1 << weFMultipleUndo
weDoNoKeyboardSync = 1 << weFNoKeyboardSync
weDoInhibitICSupport = 1 << weFInhibitICSupport
# weDoInhibitColor = 1 << weFInhibitColor
weBitToggle = -2
weBitTest = -1
weBitClear = 0
weBitSet = 1
weLowerCase = 0
weUpperCase = 1
weFindWholeWords = 0x00000001
weFindCaseInsensitive = 0x00000002
weFindDiacriticalInsensitive = 0x00000004
wePutIntCutAndPaste = 0x00000001
wePutAddToTypingSequence = 0x00000002
wePutDetectUnicodeBOM = 0x00000200
weStreamDestinationKindMask = 0x000000FF
weStreamIncludeObjects = 0x00000100
weGetAddUnicodeBOM = 0x00000200
weGetLittleEndian = 0x00000400
weTagFontFamily = FOUR_CHAR_CODE('font')
weTagFontSize = FOUR_CHAR_CODE('ptsz')
weTagPlain = FOUR_CHAR_CODE('plan')
weTagBold = FOUR_CHAR_CODE('bold')
weTagItalic = FOUR_CHAR_CODE('ital')
weTagUnderline = FOUR_CHAR_CODE('undl')
weTagOutline = FOUR_CHAR_CODE('outl')
weTagShadow = FOUR_CHAR_CODE('shad')
weTagCondensed = FOUR_CHAR_CODE('cond')
weTagExtended = FOUR_CHAR_CODE('pexp')
weTagStrikethrough = FOUR_CHAR_CODE('strk')
weTagTextColor = FOUR_CHAR_CODE('colr')
weTagBackgroundColor = FOUR_CHAR_CODE('pbcl')
weTagTransferMode = FOUR_CHAR_CODE('pptm')
weTagVerticalShift = FOUR_CHAR_CODE('xshf')
weTagAlignment = FOUR_CHAR_CODE('pjst')
weTagDirection = FOUR_CHAR_CODE('LDIR')
weTagLineSpacing = FOUR_CHAR_CODE('ledg')
weTagLeftIndent = FOUR_CHAR_CODE('lein')
weTagRightIndent = FOUR_CHAR_CODE('riin')
weTagFirstLineIndent = FOUR_CHAR_CODE('fidt')
weTagSpaceBefore = FOUR_CHAR_CODE('spbe')
weTagSpaceAfter = FOUR_CHAR_CODE('spaf')
weTagBottomBorderStyle = FOUR_CHAR_CODE('BBRD')
weTagForceFontFamily = FOUR_CHAR_CODE('ffnt')
weTagAddFontSize = FOUR_CHAR_CODE('+siz')
weTagAddVerticalShift = FOUR_CHAR_CODE('+shf')
weTagTextEncoding = FOUR_CHAR_CODE('ptxe')
weTagQDStyles = FOUR_CHAR_CODE('qdst')
weTagTETextStyle = FOUR_CHAR_CODE('tets')
weTagAlignmentDefault = FOUR_CHAR_CODE('deft')
weTagAlignmentLeft = FOUR_CHAR_CODE('left')
weTagAlignmentCenter = FOUR_CHAR_CODE('cent')
weTagAlignmentRight = FOUR_CHAR_CODE('rght')
weTagAlignmentFull = FOUR_CHAR_CODE('full')
weTagDirectionDefault = FOUR_CHAR_CODE('deft')
weTagDirectionLeftToRight = FOUR_CHAR_CODE('L->R')
weTagDirectionRightToLeft = FOUR_CHAR_CODE('R->L')
weTagBorderStyleNone = FOUR_CHAR_CODE('NONE')
weTagBorderStyleThin = FOUR_CHAR_CODE('SLDL')
weTagBorderStyleDotted = FOUR_CHAR_CODE('DTDL')
weTagBorderStyleThick = FOUR_CHAR_CODE('THKL')
weLineSpacingSingle = 0x00000000
weLineSpacingOneAndHalf = 0x00008000
weLineSpacingDouble = 0x00010000
weCharByteHook = FOUR_CHAR_CODE('cbyt')
weCharToPixelHook = FOUR_CHAR_CODE('c2p ')
weCharTypeHook = FOUR_CHAR_CODE('ctyp')
weClickLoop = FOUR_CHAR_CODE('clik')
weCurrentDrag = FOUR_CHAR_CODE('drag')
weDrawTextHook = FOUR_CHAR_CODE('draw')
weDrawTSMHiliteHook = FOUR_CHAR_CODE('dtsm')
weEraseHook = FOUR_CHAR_CODE('eras')
weFontFamilyToNameHook = FOUR_CHAR_CODE('ff2n')
weFontNameToFamilyHook = FOUR_CHAR_CODE('fn2f')
weFluxProc = FOUR_CHAR_CODE('flux')
weHiliteDropAreaHook = FOUR_CHAR_CODE('hidr')
weLineBreakHook = FOUR_CHAR_CODE('lbrk')
wePixelToCharHook = FOUR_CHAR_CODE('p2c ')
wePort = FOUR_CHAR_CODE('port')
wePreTrackDragHook = FOUR_CHAR_CODE('ptrk')
weRefCon = FOUR_CHAR_CODE('refc')
weScrollProc = FOUR_CHAR_CODE('scrl')
weText = FOUR_CHAR_CODE('text')
weTranslateDragHook = FOUR_CHAR_CODE('xdrg')
weTranslucencyThreshold = FOUR_CHAR_CODE('tluc')
weTSMDocumentID = FOUR_CHAR_CODE('tsmd')
weTSMPreUpdate = FOUR_CHAR_CODE('pre ')
weTSMPostUpdate = FOUR_CHAR_CODE('post')
weURLHint = FOUR_CHAR_CODE('urlh')
weWordBreakHook = FOUR_CHAR_CODE('wbrk')
weNewHandler = FOUR_CHAR_CODE('new ')
weDisposeHandler = FOUR_CHAR_CODE('free')
weDrawHandler = FOUR_CHAR_CODE('draw')
weClickHandler = FOUR_CHAR_CODE('clik')
weStreamHandler = FOUR_CHAR_CODE('strm')
weHoverHandler = FOUR_CHAR_CODE('hovr')
kTypeText = FOUR_CHAR_CODE('TEXT')
kTypeStyles = FOUR_CHAR_CODE('styl')
kTypeSoup = FOUR_CHAR_CODE('SOUP')
kTypeFontTable = FOUR_CHAR_CODE('FISH')
kTypeParaFormat = FOUR_CHAR_CODE('WEpf')
kTypeRulerScrap = FOUR_CHAR_CODE('WEru')
kTypeCharFormat = FOUR_CHAR_CODE('WEcf')
kTypeStyleScrap = FOUR_CHAR_CODE('WEst')
kTypeUnicodeText = FOUR_CHAR_CODE('utxt')
kTypeUTF8Text = FOUR_CHAR_CODE('UTF8')
kTypeStyledText = FOUR_CHAR_CODE('STXT')
weAKNone = 0
weAKUnspecified = 1
weAKTyping = 2
weAKCut = 3
weAKPaste = 4
weAKClear = 5
weAKDrag = 6
weAKSetStyle = 7
weAKSetRuler = 8
weAKBackspace = 9
weAKFwdDelete = 10
weAKCaseChange = 11
weAKObjectChange = 12
weToScrap = 0
weToDrag = 1
weToSoup = 2
weMouseEnter = 0
weMouseWithin = 1
weMouseLeave = 2
kCurrentSelection = -1
kNullStyle = -2

--- NEW FILE: aepack.py ---
"""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 *
from Carbon import AE
from Carbon.AppleEvents import *
import MacOS
import macfs
import StringIO
import aetypes
from aetypes import mkenum, mktype
import os

# 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 = AE.AEDescType
FSSType = macfs.FSSpecType
AliasType = macfs.AliasType

def packkey(ae, key, value):
	if hasattr(key, 'which'):
		keystr = key.which
	elif hasattr(key, 'want'):
		keystr = key.want
	else:
		keystr = key
	ae.AEPutParamDesc(keystr, pack(value))

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 == UnicodeType:
		data = t.encode('utf16')
		if data[:2] == '\xfe\xff':
			data = data[2:]
		return AE.AECreateDesc('utxt', data)
	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():
			packkey(record, key, value)
			#record.AEPutParamDesc(key, pack(value))
		return record
	if t == InstanceType and hasattr(x, '__aepack__'):
		return x.__aepack__()
	if hasattr(x, 'which'):
		return AE.AECreateDesc('TEXT', x.which)
	if hasattr(x, 'want'):
		return AE.AECreateDesc('TEXT', x.want)
	return AE.AECreateDesc('TEXT', repr(x)) # Copout

def unpack(desc, formodulename=""):
	"""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, formodulename))
		return l
	if t == typeAERecord:
		d = {}
		for i in range(desc.AECountItems()):
			keyword, item = desc.AEGetNthDesc(i+1, '****')
			d[keyword] = unpack(item, formodulename)
		return d
	if t == typeAEText:
		record = desc.AECoerceDesc('reco')
		return mkaetext(unpack(record, formodulename))
	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
	if t == typeUnicodeText:
		return unicode(desc.data, 'utf16')
	# 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, formodulename))
	# 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')
		# If we have been told the name of the module we are unpacking aedescs for,
		# we can attempt to create the right type of python object from that module.
		if formodulename:
			return mkobjectfrommodule(unpack(record, formodulename), formodulename)
		return mkobject(unpack(record, formodulename))
	# 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, formodulename))
	if t == 'cmpd':
		record = desc.AECoerceDesc('reco')
		return mkcomparison(unpack(record, formodulename))
	if t == 'logi':
		record = desc.AECoerceDesc('reco')
		return mklogical(unpack(record, formodulename))
	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)

# Note by Jack: I'm not 100% sure of the following code. This was
# provided by Donovan Preston, but I wonder whether the assignment
# to __class__ is safe. Moreover, shouldn't there be a better
# initializer for the classes in the suites?
def mkobjectfrommodule(dict, modulename):
	want = dict['want'].type
	module = __import__(modulename)
	codenamemapper = module._classdeclarations
	classtype = codenamemapper.get(want, None)
	newobj = mkobject(dict)
	if classtype:
		newobj.__class__ = classtype
	return newobj

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()
	

--- NEW FILE: aetools.py ---
"""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 *
from Carbon import AE
from Carbon import AppleEvents
import MacOS
import sys

from aetypes import *
from aepack import packkey, 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, formodulename=""):
	parameters = {}
	try:
		dirobj = ae.AEGetParamDesc('----', '****')
	except AE.Error:
		pass
	else:
		parameters['----'] = unpack(dirobj, formodulename)
		del dirobj
	# Workaround for what I feel is a bug in OSX 10.2: 'errn' won't show up in missed...
	try:
		dirobj = ae.AEGetParamDesc('errn', '****')
	except AE.Error:
		pass
	else:
		parameters['errn'] = unpack(dirobj, formodulename)
		del dirobj
	while 1:
		key = missed(ae)
		if not key: break
		parameters[key] = unpack(ae.AEGetParamDesc(key, '****'), formodulename)
	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, formodulename)
	return parameters, attributes

def packevent(ae, parameters = {}, attributes = {}):
	for key, value in parameters.items():
		packkey(ae, key, value)
	for key, value in attributes.items():
		packkey(ae, key, 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
	_moduleName = 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 start(self):
		"""Deprecated, used _start()"""
		self._start()
			
	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, self._moduleName)
		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['----']
			if as:
				item.__class__ = as
			return item

	def _set(self, _object, _arguments = {}, _attributes = {}):
		""" _set: set data for an object
		Required argument: the object
		Keyword argument _parameters: Parameter dictionary for the set operation
		Keyword argument _attributes: AppleEvent attribute dictionary
		Returns: the data
		"""
		_code = 'core'
		_subcode = 'setd'
		
		_arguments['----'] = _object

		_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)

--- NEW FILE: aetypes.py ---
"""aetypes - Python objects representing various AE types."""

from Carbon.AppleEvents import *
import struct
from types import *
import string

#
# convoluted, since there are cyclic dependencies between this file and
# aetools_convert.
#
def pack(*args, **kwargs):
	from aepack import pack
	return apply(pack, args, kwargs)
	
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)

# Jack changed the way this is done
class InsertionLoc:
	def __init__(self, of, pos):
		self.of = of
		self.pos = pos
	
	def __repr__(self):
		return "InsertionLoc(%s, %s)" % (`self.of`, `self.pos`)
		
	def __aepack__(self):
		rec = {'kobj': self.of, 'kpos': self.pos}
		return pack(rec, forcetype='insl')
		
# Convenience functions for dsp:
def beginning(of):
	return InsertionLoc(of, Enum('bgng'))
	
def end(of):
	return InsertionLoc(of, Enum('end '))

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')


--- NEW FILE: applesingle.py ---
# applesingle - a module to decode AppleSingle files
import struct
import MacOS
import sys

Error="applesingle.Error"

verbose=0

# File header format: magic, version, unused, number of entries
AS_HEADER_FORMAT="ll16sh"
AS_HEADER_LENGTH=26
# The flag words for AppleSingle
AS_MAGIC=0x00051600
AS_VERSION=0x00020000

# Entry header format: id, offset, length
AS_ENTRY_FORMAT="lll"
AS_ENTRY_LENGTH=12

# The id values
AS_DATAFORK=1
AS_RESOURCEFORK=2
AS_IGNORE=(3,4,5,6,8,9,10,11,12,13,14,15)

def decode(input, output, resonly=0):
	if type(input) == type(''):
		input = open(input, 'rb')
	# Should we also test for FSSpecs or FSRefs?
	header = input.read(AS_HEADER_LENGTH)
	try:
		magic, version, dummy, nentry = struct.unpack(AS_HEADER_FORMAT, header)
	except ValueError, arg:
		raise Error, "Unpack header error: %s"%arg
	if verbose:
		print 'Magic:   0x%8.8x'%magic
		print 'Version: 0x%8.8x'%version
		print 'Entries: %d'%nentry
	if magic != AS_MAGIC:
		raise Error, 'Unknown AppleSingle magic number 0x%8.8x'%magic
	if version != AS_VERSION:
		raise Error, 'Unknown AppleSingle version number 0x%8.8x'%version
	if nentry <= 0:
		raise Error, "AppleSingle file contains no forks"
	headers = [input.read(AS_ENTRY_LENGTH) for i in range(nentry)]
	didwork = 0
	for hdr in headers:
		try:
			id, offset, length = struct.unpack(AS_ENTRY_FORMAT, hdr)
		except ValueError, arg:
			raise Error, "Unpack entry error: %s"%arg
		if verbose:
			print 'Fork %d, offset %d, length %d'%(id, offset, length)
		input.seek(offset)
		if length == 0:
			data = ''
		else:
			data = input.read(length)
		if len(data) != length:
			raise Error, 'Short read: expected %d bytes got %d'%(length, len(data))
		if id == AS_DATAFORK:
			if verbose:
				print '  (data fork)'
			if not resonly:
				didwork = 1
				fp = open(output, 'wb')
				fp.write(data)
				fp.close()
		elif id == AS_RESOURCEFORK:
			didwork = 1
			if verbose:
				print '  (resource fork)'
			if resonly:
				fp = open(output, 'wb')
			else:
				fp = MacOS.openrf(output, 'wb')
			fp.write(data)
			fp.close()
		elif id in AS_IGNORE:
			if verbose:
				print '  (ignored)'
		else:
			raise Error, 'Unknown fork type %d'%id
	if not didwork:
		raise Error, 'No useful forks found'

def _test():
	if len(sys.argv) < 3 or sys.argv[1] == '-r' and len(sys.argv) != 4:
		print 'Usage: applesingle.py [-r] applesinglefile decodedfile'
		sys.exit(1)
	if sys.argv[1] == '-r':
		resonly = 1
		del sys.argv[1]
	else:
		resonly = 0
	decode(sys.argv[1], sys.argv[2], resonly=resonly)
	
if __name__ == '__main__':
	_test()
	
--- NEW FILE: appletrawmain.py ---
# Emulate sys.argv and run __main__.py or __main__.pyc in an environment that
# is as close to "normal" as possible.
#
# This script is put into __rawmain__.pyc for applets that need argv
# emulation, by BuildApplet and friends.
#
import argvemulator
import os
import sys
import marshal

#
# Make sure we have an argv[0], and make _dir point to the Resources
# directory.
#
if not sys.argv or sys.argv[0][:1] == '-':
	# Insert our (guessed) name.
	_dir = os.path.split(sys.executable)[0] # removes "python"
	_dir = os.path.split(_dir)[0] # Removes "MacOS"
	_dir = os.path.join(_dir, 'Resources')
	sys.argv.insert(0, '__rawmain__')
else:
	_dir = os.path.split(sys.argv[0])[0]
#
# Add the Resources directory to the path. This is where files installed
# by BuildApplet.py with the --extra option show up, and if those files are 
# modules this sys.path modification is necessary to be able to import them.
#
sys.path.insert(0, _dir)
#
# Create sys.argv
#
argvemulator.ArgvCollector().mainloop()
#
# Find the real main program to run
#
__file__ = os.path.join(_dir, '__main__.py')
if os.path.exists(__file__):
	#
	# Setup something resembling a normal environment and go.
	#
	sys.argv[0] = __file__
	del argvemulator, os, sys, _dir
	execfile(__file__)
else:
	__file__ = os.path.join(_dir, '__main__.pyc')
	if os.path.exists(__file__):
		#
		# If we have only a .pyc file we read the code object from that
		#
		sys.argv[0] = __file__
		_fp = open(__file__, 'rb')
		_fp.read(8)
		__code__ = marshal.load(_fp)
		#
		# Again, we create an almost-normal environment (only __code__ is
		# funny) and go.
		#
		del argvemulator, os, sys, marshal, _dir, _fp
		exec __code__
	else:
		sys.stderr.write("%s: neither __main__.py nor __main__.pyc found\n"%sys.argv[0])
		sys.exit(1)

--- NEW FILE: appletrunner.py ---
#!/usr/bin/env python
# This file is meant as an executable script for running applets.
# BuildApplet will use it as the main executable in the .app bundle if
# we are not running in a framework build.

import os
import sys
for name in ["__rawmain__.py", "__rawmain__.pyc", "__main__.py", "__main__.pyc"]:
	realmain = os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])),
					  "Resources", name)
	if os.path.exists(realmain):
		break
else:
	sys.stderr.write("%s: cannot find applet main program\n" % sys.argv[0])
	sys.exit(1)
sys.argv.insert(1, realmain)
os.execve(sys.executable, sys.argv, os.environ)

--- NEW FILE: argvemulator.py ---
"""argvemulator - create sys.argv from OSA events. Used by applets that
want unix-style arguments.
"""

import sys
import traceback
from Carbon import AE
from Carbon.AppleEvents import *
from Carbon import Evt
from Carbon.Events import *
import aetools

class ArgvCollector:
	
	"""A minimal FrameWork.Application-like class"""
	
	def __init__(self):
		self.quitting = 0
		self.ae_handlers = {}
		# Remove the funny -psn_xxx_xxx argument
		if len(sys.argv) > 1 and sys.argv[1][:4] == '-psn':
			del sys.argv[1]
		self.installaehandler('aevt', 'oapp', self.open_app)
		self.installaehandler('aevt', 'odoc', self.open_file)
	
	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 mainloop(self, mask = highLevelEventMask, timeout = 1*60):
		stoptime = Evt.TickCount() + timeout
		while not self.quitting and Evt.TickCount() < stoptime:
			self.dooneevent(mask, timeout)
		self.close()
	
	def _quit(self):
		self.quitting = 1
	
	def dooneevent(self, mask = highLevelEventMask, timeout = 1*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:
			try:
				AE.AEProcessAppleEvent(event)
			except AE.Error, err:
				msg = "High Level Event: %s %s" % \
					(`hex(message)`, `hex(h | (v<<16))`)
				print 'AE error: ', err
				print 'in', msg
				traceback.print_exc()
			return
		else:
			print "Unhandled event:", event

	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 open_app(self, **args):
		self._quit()
		
	def open_file(self, _object=None, **args):
		for alias in _object:
			fss = alias.Resolve()[0]
			pathname = fss.as_pathname()
			sys.argv.append(pathname)
		self._quit()
		
	def other(self, _object=None, _class=None, _type=None, **args):
		print 'Ignore AppleEvent', (_class, _type), 'for', _object, 'Other args:', args

if __name__ == '__main__':
	ArgvCollector().mainloop()
	print "sys.argv=", sys.argv

--- NEW FILE: bgenlocations.py ---
#
# Local customizations for generating the Carbon interface modules.
# Edit this file to reflect where things should be on your system.
# Note that pathnames are unix-style for OSX MachoPython/unix-Python,
# but mac-style for MacPython, whether running on OS9 or OSX.
#

import sys, os

Error = "bgenlocations.Error"
#
# Where bgen is. For unix-Python bgen isn't installed, so you have to refer to
# the source tree here.
if sys.platform == 'mac':
	# For MacPython we know where it is
	def _pardir(p): return os.path.split(p)[0]
	BGENDIR=os.path.join(sys.prefix, "Tools", "bgen", "bgen")
else:
	# for unix-Python we don't know, please set it yourself.
	BGENDIR="/Users/jack/src/python/Tools/bgen/bgen"

#
# Where to find the Universal Header include files. If you have CodeWarrior
# installed you can use the Universal Headers from there, otherwise you can
# download them from the Apple website. Bgen can handle both unix- and mac-style
# end of lines, so don't worry about that.
#
if sys.platform == 'mac':
	_MWERKSDIR="Macintosh HD:Applications (Mac OS 9):Metrowerks CodeWarrior 7.0:Metrowerks CodeWarrior"
else:
	_MWERKSDIR="/Volumes/Moes/Applications (Mac OS 9)/Metrowerks CodeWarrior 7.0/Metrowerks CodeWarrior/"
INCLUDEDIR=os.path.join(_MWERKSDIR, "MacOS Support", "Universal", "Interfaces", "CIncludes")

#
# Where to put the python definitions files. Note that, on unix-Python,
# if you want to commit your changes to the CVS repository this should refer to
# your source directory, not your installed directory.
#
if sys.platform == 'mac':
	TOOLBOXDIR=os.path.join(sys.prefix, "Mac", "Lib", "Carbon")
else:
	TOOLBOXDIR="/Users/jack/src/python/Mac/Lib/Carbon"

# Creator for C files:
CREATOR="CWIE"

if not os.path.exists(BGENDIR):
	raise Error, "Please fix bgenlocations.py, BGENDIR does not exist: %s" % BGENDIR
if not os.path.exists(INCLUDEDIR):
	raise Error, "Please fix bgenlocations.py, INCLUDEDIR does not exist: %s" % INCLUDEDIR
if not os.path.exists(TOOLBOXDIR):
	raise Error, "Please fix bgenlocations.py, TOOLBOXDIR does not exist: %s" % TOOLBOXDIR
	
# Sigh, due to the way these are used make sure they end with : or /.
if BGENDIR[-1] != os.sep:
	BGENDIR = BGENDIR + os.sep
if INCLUDEDIR[-1] != os.sep:
	INCLUDEDIR = INCLUDEDIR + os.sep
if TOOLBOXDIR[-1] != os.sep:
	TOOLBOXDIR = TOOLBOXDIR + os.sep
	

--- NEW FILE: buildtools.py ---
"""tools for BuildApplet and BuildApplication"""

import sys
import os
import string
import imp
import marshal
import macfs
from Carbon import Res
import MACFS
import MacOS
import macostools
import macresource
import EasyDialogs
import shutil


BuildError = "BuildError"

# .pyc file (and 'PYC ' resource magic number)
MAGIC = imp.get_magic()

# Template file (searched on sys.path)
TEMPLATE = "PythonInterpreter"

# Specification of our resource
RESTYPE = 'PYC '
RESNAME = '__main__'

# A resource with this name sets the "owner" (creator) of the destination
# It should also have ID=0. Either of these alone is not enough.
OWNERNAME = "owner resource"

# Default applet creator code
DEFAULT_APPLET_CREATOR="Pyta"

# OpenResFile mode parameters
READ = 1
WRITE = 2


def findtemplate(template=None):
	"""Locate the applet template along sys.path"""
	if MacOS.runtimemodel == 'macho':
		if template:
			return template
		return findtemplate_macho()
	if not template:
		template=TEMPLATE
	for p in sys.path:
		file = os.path.join(p, template)
		try:
			file, d1, d2 = macfs.ResolveAliasFile(file)
			break
		except (macfs.error, ValueError):
			continue
	else:
		raise BuildError, "Template %s not found on sys.path" % `template`
	file = file.as_pathname()
	return file
	
def findtemplate_macho():
	execpath = sys.executable.split('/')
	if not 'Contents' in execpath:
		raise BuildError, "Not running from a .app bundle: %s" % sys.executable
	i = execpath.index('Contents')
	return '/'.join(execpath[:i])


def process(template, filename, destname, copy_codefragment, 
		rsrcname=None, others=[], raw=0, progress="default"):
	
	if progress == "default":
		progress = EasyDialogs.ProgressBar("Processing %s..."%os.path.split(filename)[1], 120)
		progress.label("Compiling...")
		progress.inc(0)
	# check for the script name being longer than 32 chars. This may trigger a bug
	# on OSX that can destroy your sourcefile.
	if '#' in os.path.split(filename)[1]:
		raise BuildError, "BuildApplet could destroy your sourcefile on OSX, please rename: %s" % filename
	# Read the source and compile it
	# (there's no point overwriting the destination if it has a syntax error)
	
	fp = open(filename, 'rU')
	text = fp.read()
	fp.close()
	try:
		code = compile(text, filename, "exec")
	except SyntaxError, arg:
		raise BuildError, "Syntax error in script %s: %s" % (filename, arg)
	except EOFError:
		raise BuildError, "End-of-file in script %s" % (filename,)
	
	# Set the destination file name. Note that basename
	# does contain the whole filepath, only a .py is stripped.
	
	if string.lower(filename[-3:]) == ".py":
		basename = filename[:-3]
		if MacOS.runtimemodel != 'macho' and not destname:
			destname = basename
	else:
		basename = filename
		
	if not destname:
		if MacOS.runtimemodel == 'macho':
			destname = basename + '.app'
		else:
			destname = basename + '.applet'
	if not rsrcname:
		rsrcname = basename + '.rsrc'
		
	# Try removing the output file. This fails in MachO, but it should
	# do any harm.
	try:
		os.remove(destname)
	except os.error:
		pass
	process_common(template, progress, code, rsrcname, destname, 0, 
		copy_codefragment, raw, others)
	

def update(template, filename, output):
	if MacOS.runtimemodel == 'macho':
		raise BuildError, "No updating yet for MachO applets"
	if progress:
		progress = EasyDialogs.ProgressBar("Updating %s..."%os.path.split(filename)[1], 120)
	else:
		progress = None
	if not output:
		output = filename + ' (updated)'
	
	# Try removing the output file
	try:
		os.remove(output)
	except os.error:
		pass
	process_common(template, progress, None, filename, output, 1, 1)


def process_common(template, progress, code, rsrcname, destname, is_update, 
		copy_codefragment, raw=0, others=[]):
	if MacOS.runtimemodel == 'macho':
		return process_common_macho(template, progress, code, rsrcname, destname,
			is_update, raw, others)
	if others:
		raise BuildError, "Extra files only allowed for MachoPython applets"
	# Create FSSpecs for the various files
	template_fss = macfs.FSSpec(template)
	template_fss, d1, d2 = macfs.ResolveAliasFile(template_fss)
	dest_fss = macfs.FSSpec(destname)
	
	# Copy data (not resources, yet) from the template
	if progress:
		progress.label("Copy data fork...")
		progress.set(10)
	
	if copy_codefragment:
		tmpl = open(template, "rb")
		dest = open(destname, "wb")
		data = tmpl.read()
		if data:
			dest.write(data)
		dest.close()
		tmpl.close()
		del dest
		del tmpl
	
	# Open the output resource fork
	
	if progress:
		progress.label("Copy resources...")
		progress.set(20)
	try:
		output = Res.FSpOpenResFile(dest_fss, WRITE)
	except MacOS.Error:
		Res.FSpCreateResFile(destname, '????', 'APPL', MACFS.smAllScripts)
		output = Res.FSpOpenResFile(dest_fss, WRITE)
	
	# Copy the resources from the target specific resource template, if any
	typesfound, ownertype = [], None
	try:
		input = Res.FSpOpenResFile(rsrcname, READ)
	except (MacOS.Error, ValueError):
		pass
		if progress:
			progress.inc(50)
	else:
		if is_update:
			skip_oldfile = ['cfrg']
		else:
			skip_oldfile = []
		typesfound, ownertype = copyres(input, output, skip_oldfile, 0, progress)
		Res.CloseResFile(input)
	
	# Check which resource-types we should not copy from the template
	skiptypes = []
	if 'vers' in typesfound: skiptypes.append('vers')
	if 'SIZE' in typesfound: skiptypes.append('SIZE')
	if 'BNDL' in typesfound: skiptypes = skiptypes + ['BNDL', 'FREF', 'icl4', 
			'icl8', 'ics4', 'ics8', 'ICN#', 'ics#']
	if not copy_codefragment:
		skiptypes.append('cfrg')
##	skipowner = (ownertype <> None)
	
	# Copy the resources from the template
	
	input = Res.FSpOpenResFile(template_fss, READ)
	dummy, tmplowner = copyres(input, output, skiptypes, 1, progress)
		
	Res.CloseResFile(input)
##	if ownertype == None:
##		raise BuildError, "No owner resource found in either resource file or template"
	# Make sure we're manipulating the output resource file now
	
	Res.UseResFile(output)

	if ownertype == None:
		# No owner resource in the template. We have skipped the
		# Python owner resource, so we have to add our own. The relevant
		# bundle stuff is already included in the interpret/applet template.
		newres = Res.Resource('\0')
		newres.AddResource(DEFAULT_APPLET_CREATOR, 0, "Owner resource")
		ownertype = DEFAULT_APPLET_CREATOR
	
	if code:
		# Delete any existing 'PYC ' resource named __main__
		
		try:
			res = Res.Get1NamedResource(RESTYPE, RESNAME)
			res.RemoveResource()
		except Res.Error:
			pass
		
		# Create the raw data for the resource from the code object
		if progress:
			progress.label("Write PYC resource...")
			progress.set(120)
		
		data = marshal.dumps(code)
		del code
		data = (MAGIC + '\0\0\0\0') + data
		
		# Create the resource and write it
		
		id = 0
		while id < 128:
			id = Res.Unique1ID(RESTYPE)
		res = Res.Resource(data)
		res.AddResource(RESTYPE, id, RESNAME)
		attrs = res.GetResAttrs()
		attrs = attrs | 0x04	# set preload
		res.SetResAttrs(attrs)
		res.WriteResource()
		res.ReleaseResource()
	
	# Close the output file
	
	Res.CloseResFile(output)
	
	# Now set the creator, type and bundle bit of the destination
	dest_finfo = dest_fss.GetFInfo()
	dest_finfo.Creator = ownertype
	dest_finfo.Type = 'APPL'
	dest_finfo.Flags = dest_finfo.Flags | MACFS.kHasBundle | MACFS.kIsShared
	dest_finfo.Flags = dest_finfo.Flags & ~MACFS.kHasBeenInited
	dest_fss.SetFInfo(dest_finfo)
	
	macostools.touched(dest_fss)
	if progress:
		progress.label("Done.")
		progress.inc(0)

def process_common_macho(template, progress, code, rsrcname, destname, is_update, raw=0, others=[]):
	# First make sure the name ends in ".app"
	if destname[-4:] != '.app':
		destname = destname + '.app'
	# Now deduce the short name
	shortname = os.path.split(destname)[1]
	if shortname[-4:] == '.app':
		# Strip the .app suffix
		shortname = shortname[:-4]
	# And deduce the .plist and .icns names
	plistname = None
	icnsname = None
	if rsrcname and rsrcname[-5:] == '.rsrc':
		tmp = rsrcname[:-5]
		plistname = tmp + '.plist'
		if os.path.exists(plistname):
			icnsname = tmp + '.icns'
			if not os.path.exists(icnsname):
				icnsname = None
		else:
			plistname = None
	# Start with copying the .app framework
	if not is_update:
		exceptlist = ["Contents/Info.plist", 
				"Contents/Resources/English.lproj/InfoPlist.strings", 
				"Contents/Resources/English.lproj/Documentation", 
				"Contents/Resources/python.rsrc",
				]
		copyapptree(template, destname, exceptlist, progress)
		# SERIOUS HACK. If we've just copied a symlink as the
		# executable we assume we're running from the MacPython addon
		# to 10.2 python. We remove the symlink again and install
		# the appletrunner script.
		executable = os.path.join(destname, "Contents/MacOS/python")
		if os.path.islink(executable):
			os.remove(executable)
			dummyfp, appletrunner, d2 = imp.find_module('appletrunner')
			del dummyfp
			shutil.copy2(appletrunner, executable)
			os.chmod(executable, 0775)
	# Now either use the .plist file or the default
	if progress:
		progress.label('Create info.plist')
		progress.inc(0)
	if plistname:
		shutil.copy2(plistname, os.path.join(destname, 'Contents', 'Info.plist'))
		if icnsname:
			icnsdest = os.path.split(icnsname)[1]
			icnsdest = os.path.join(destname, 
				os.path.join('Contents', 'Resources', icnsdest))
			shutil.copy2(icnsname, icnsdest)
		# XXXX Wrong. This should be parsed from plist file. Also a big hack:-)
		if shortname == 'PythonIDE':
			ownertype = 'Pide'
		else:
			ownertype = 'PytA'
		# XXXX Should copy .icns file
	else:
		cocoainfo = ''
		for o in others:
			if o[-4:] == '.nib':
				nibname = os.path.split(o)[1][:-4]
				cocoainfo = """
        <key>NSMainNibFile</key>
        <string>%s</string>
        <key>NSPrincipalClass</key>
        <string>NSApplication</string>""" % nibname
			elif o[-6:] == '.lproj':
				files = os.listdir(o)
				for f in files:
					if f[-4:] == '.nib':
						nibname = os.path.split(f)[1][:-4]
						cocoainfo = """
        <key>NSMainNibFile</key>
        <string>%s</string>
        <key>NSPrincipalClass</key>
        <string>NSApplication</string>""" % nibname

		plistname = os.path.join(template, 'Contents', 'Resources', 'Applet-Info.plist')
		plistdata = open(plistname).read()
		plistdata = plistdata % {'appletname':shortname, 'cocoainfo':cocoainfo}
		ofp = open(os.path.join(destname, 'Contents', 'Info.plist'), 'w')
		ofp.write(plistdata)
		ofp.close()
		ownertype = 'PytA'
	# Create the PkgInfo file
	if progress:
		progress.label('Create PkgInfo')
		progress.inc(0)
	ofp = open(os.path.join(destname, 'Contents', 'PkgInfo'), 'wb')
	ofp.write('APPL' + ownertype)
	ofp.close()
		
	
	# Copy the resources from the target specific resource template, if any
	typesfound, ownertype = [], None
	try:
		input = macresource.open_pathname(rsrcname)
	except (MacOS.Error, ValueError):
		if progress:
			progress.inc(50)
	else:
		if progress:
			progress.label("Copy resources...")
			progress.set(20)
		resfilename = 'python.rsrc'  # XXXX later: '%s.rsrc' % shortname
		try:
			output = Res.FSOpenResourceFile(
					os.path.join(destname, 'Contents', 'Resources', resfilename), 
					u'', WRITE)
		except MacOS.Error:
			fsr, dummy = Res.FSCreateResourceFile(
					os.path.join(destname, 'Contents', 'Resources'), 
					unicode(resfilename), '')
			output = Res.FSOpenResourceFile(fsr, u'', WRITE)
		
		typesfound, ownertype = copyres(input, output, [], 0, progress)
		Res.CloseResFile(input)
		Res.CloseResFile(output)

	if code:
		if raw:
			pycname = '__rawmain__.pyc'
		else:
			pycname = '__main__.pyc'
			# And we also create __rawmain__.pyc
			outputfilename = os.path.join(destname, 'Contents', 'Resources', '__rawmain__.pyc')
			if progress:
				progress.label('Creating __rawmain__.pyc')
				progress.inc(0)
			rawsourcefp, rawsourcefile, d2 = imp.find_module('appletrawmain')
			rawsource = rawsourcefp.read()
			rawcode = compile(rawsource, rawsourcefile, 'exec')
			writepycfile(rawcode, outputfilename)
			
		outputfilename = os.path.join(destname, 'Contents', 'Resources', pycname)
		if progress:
			progress.label('Creating '+pycname)
			progress.inc(0)
		writepycfile(code, outputfilename)
	# Copy other files the user asked for
	for osrc in others:
		oname = os.path.split(osrc)[1]
		odst = os.path.join(destname, 'Contents', 'Resources', oname)
		if progress: 
			progress.label('Copy ' + oname)
			progress.inc(0)
		if os.path.isdir(osrc):
			copyapptree(osrc, odst)
		else:
			shutil.copy2(osrc, odst)
	if progress: 
		progress.label('Done.')
		progress.inc(0)
	
##	macostools.touched(dest_fss)

# Copy resources between two resource file descriptors.
# skip a resource named '__main__' or (if skipowner is set) with ID zero.
# Also skip resources with a type listed in skiptypes.
#
def copyres(input, output, skiptypes, skipowner, progress=None):
	ctor = None
	alltypes = []
	Res.UseResFile(input)
	ntypes = Res.Count1Types()
	progress_type_inc = 50/ntypes
	for itype in range(1, 1+ntypes):
		type = Res.Get1IndType(itype)
		if type in skiptypes:
			continue
		alltypes.append(type)
		nresources = Res.Count1Resources(type)
		progress_cur_inc = progress_type_inc/nresources
		for ires in range(1, 1+nresources):
			res = Res.Get1IndResource(type, ires)
			id, type, name = res.GetResInfo()
			lcname = string.lower(name)

			if lcname == OWNERNAME and id == 0:
				if skipowner:
					continue # Skip this one
				else:
					ctor = type
			size = res.size
			attrs = res.GetResAttrs()
			if progress:
				progress.label("Copy %s %d %s"%(type, id, name))
				progress.inc(progress_cur_inc)
			res.LoadResource()
			res.DetachResource()
			Res.UseResFile(output)
			try:
				res2 = Res.Get1Resource(type, id)
			except MacOS.Error:
				res2 = None
			if res2:
				if progress:
					progress.label("Overwrite %s %d %s"%(type, id, name))
					progress.inc(0)
				res2.RemoveResource()
			res.AddResource(type, id, name)
			res.WriteResource()
			attrs = attrs | res.GetResAttrs()
			res.SetResAttrs(attrs)
			Res.UseResFile(input)
	return alltypes, ctor

def copyapptree(srctree, dsttree, exceptlist=[], progress=None):
	names = []
	if os.path.exists(dsttree):
		shutil.rmtree(dsttree)
	os.mkdir(dsttree)
	todo = os.listdir(srctree)
	while todo:
		this, todo = todo[0], todo[1:]
		if this in exceptlist:
			continue
		thispath = os.path.join(srctree, this)
		if os.path.isdir(thispath):
			thiscontent = os.listdir(thispath)
			for t in thiscontent:
				todo.append(os.path.join(this, t))
		names.append(this)
	for this in names:
		srcpath = os.path.join(srctree, this)
		dstpath = os.path.join(dsttree, this)
		if os.path.isdir(srcpath):
			os.mkdir(dstpath)
		elif os.path.islink(srcpath):
			endpoint = os.readlink(srcpath)
			os.symlink(endpoint, dstpath)
		else:
			if progress:
				progress.label('Copy '+this)
				progress.inc(0)
			shutil.copy2(srcpath, dstpath)
			
def writepycfile(codeobject, cfile):
	import marshal
	fc = open(cfile, 'wb')
	fc.write('\0\0\0\0') # MAGIC placeholder, written later
	fc.write('\0\0\0\0') # Timestap placeholder, not needed
	marshal.dump(codeobject, fc)
	fc.flush()
	fc.seek(0, 0)
	fc.write(MAGIC)
	fc.close()


--- NEW FILE: bundlebuilder.py ---
#! /usr/bin/env python

"""\
bundlebuilder.py -- Tools to assemble MacOS X (application) bundles.

This module contains two classes to build so called "bundles" for
MacOS X. BundleBuilder is a general tool, AppBuilder is a subclass
specialized in building application bundles.

[Bundle|App]Builder objects are instantiated with a bunch of keyword
arguments, and have a build() method that will do all the work. See
the class doc strings for a description of the constructor arguments.

The module contains a main program that can be used in two ways:

  % python bundlebuilder.py [options] build
  % python buildapp.py [options] build

Where "buildapp.py" is a user-supplied setup.py-like script following
this model:

  from bundlebuilder import buildapp
  buildapp(<lots-of-keyword-args>)

"""


__all__ = ["BundleBuilder", "BundleBuilderError", "AppBuilder", "buildapp"]


import sys
import os, errno, shutil
import imp, marshal
import re
from copy import deepcopy
import getopt
from plistlib import Plist
from types import FunctionType as function


class BundleBuilderError(Exception): pass


class Defaults:

	"""Class attributes that don't start with an underscore and are
	not functions or classmethods are (deep)copied to self.__dict__.
	This allows for mutable default values.
	"""

	def __init__(self, **kwargs):
		defaults = self._getDefaults()
		defaults.update(kwargs)
		self.__dict__.update(defaults)

	def _getDefaults(cls):
		defaults = {}
		for name, value in cls.__dict__.items():
			if name[0] != "_" and not isinstance(value,
					(function, classmethod)):
				defaults[name] = deepcopy(value)
		for base in cls.__bases__:
			if hasattr(base, "_getDefaults"):
				defaults.update(base._getDefaults())
		return defaults
	_getDefaults = classmethod(_getDefaults)


class BundleBuilder(Defaults):

	"""BundleBuilder is a barebones class for assembling bundles. It
	knows nothing about executables or icons, it only copies files
	and creates the PkgInfo and Info.plist files.
	"""

	# (Note that Defaults.__init__ (deep)copies these values to
	# instance variables. Mutable defaults are therefore safe.)

	# Name of the bundle, with or without extension.
	name = None

	# The property list ("plist")
	plist = Plist(CFBundleDevelopmentRegion = "English",
	              CFBundleInfoDictionaryVersion = "6.0")

	# The type of the bundle.
	type = "APPL"
	# The creator code of the bundle.
	creator = None

	# List of files that have to be copied to <bundle>/Contents/Resources.
	resources = []

	# List of (src, dest) tuples; dest should be a path relative to the bundle
	# (eg. "Contents/Resources/MyStuff/SomeFile.ext).
	files = []

	# Directory where the bundle will be assembled.
	builddir = "build"

	# platform, name of the subfolder of Contents that contains the executable.
	platform = "MacOS"

	# Make symlinks instead copying files. This is handy during debugging, but
	# makes the bundle non-distributable.
	symlink = 0

	# Verbosity level.
	verbosity = 1

	def setup(self):
		# XXX rethink self.name munging, this is brittle.
		self.name, ext = os.path.splitext(self.name)
		if not ext:
			ext = ".bundle"
		bundleextension = ext
		# misc (derived) attributes
		self.bundlepath = pathjoin(self.builddir, self.name + bundleextension)
		self.execdir = pathjoin("Contents", self.platform)

		plist = self.plist
		plist.CFBundleName = self.name
		plist.CFBundlePackageType = self.type
		if self.creator is None:
			if hasattr(plist, "CFBundleSignature"):
				self.creator = plist.CFBundleSignature
			else:
				self.creator = "????"
		plist.CFBundleSignature = self.creator

	def build(self):
		"""Build the bundle."""
		builddir = self.builddir
		if builddir and not os.path.exists(builddir):
			os.mkdir(builddir)
		self.message("Building %s" % repr(self.bundlepath), 1)
		if os.path.exists(self.bundlepath):
			shutil.rmtree(self.bundlepath)
		os.mkdir(self.bundlepath)
		self.preProcess()
		self._copyFiles()
		self._addMetaFiles()
		self.postProcess()
		self.message("Done.", 1)

	def preProcess(self):
		"""Hook for subclasses."""
		pass
	def postProcess(self):
		"""Hook for subclasses."""
		pass

	def _addMetaFiles(self):
		contents = pathjoin(self.bundlepath, "Contents")
		makedirs(contents)
		#
		# Write Contents/PkgInfo
		assert len(self.type) == len(self.creator) == 4, \
				"type and creator must be 4-byte strings."
		pkginfo = pathjoin(contents, "PkgInfo")
		f = open(pkginfo, "wb")
		f.write(self.type + self.creator)
		f.close()
		#
		# Write Contents/Info.plist
		infoplist = pathjoin(contents, "Info.plist")
		self.plist.write(infoplist)

	def _copyFiles(self):
		files = self.files[:]
		for path in self.resources:
			files.append((path, pathjoin("Contents", "Resources",
				os.path.basename(path))))
		if self.symlink:
			self.message("Making symbolic links", 1)
			msg = "Making symlink from"
		else:
			self.message("Copying files", 1)
			msg = "Copying"
		files.sort()
		for src, dst in files:
			if os.path.isdir(src):
				self.message("%s %s/ to %s/" % (msg, src, dst), 2)
			else:
				self.message("%s %s to %s" % (msg, src, dst), 2)
			dst = pathjoin(self.bundlepath, dst)
			if self.symlink:
				symlink(src, dst, mkdirs=1)
			else:
				copy(src, dst, mkdirs=1)

	def message(self, msg, level=0):
		if level <= self.verbosity:
			indent = ""
			if level > 1:
				indent = (level - 1) * "  "
			sys.stderr.write(indent + msg + "\n")

	def report(self):
		# XXX something decent
		pass


if __debug__:
	PYC_EXT = ".pyc"
else:
	PYC_EXT = ".pyo"

MAGIC = imp.get_magic()
USE_FROZEN = hasattr(imp, "set_frozenmodules")

# For standalone apps, we have our own minimal site.py. We don't need
# all the cruft of the real site.py.
SITE_PY = """\
import sys
del sys.path[1:]  # sys.path[0] is Contents/Resources/
"""

if USE_FROZEN:
	FROZEN_ARCHIVE = "FrozenModules.marshal"
	SITE_PY += """\
# bootstrapping
import imp, marshal
f = open(sys.path[0] + "/%s", "rb")
imp.set_frozenmodules(marshal.load(f))
f.close()
""" % FROZEN_ARCHIVE

SITE_CO = compile(SITE_PY, "<-bundlebuilder.py->", "exec")

EXT_LOADER = """\
import imp, sys, os
for p in sys.path:
	path = os.path.join(p, "%(filename)s")
	if os.path.exists(path):
		break
else:
	assert 0, "file not found: %(filename)s"
mod = imp.load_dynamic("%(name)s", path)
sys.modules["%(name)s"] = mod
"""

MAYMISS_MODULES = ['mac', 'os2', 'nt', 'ntpath', 'dos', 'dospath',
	'win32api', 'ce', '_winreg', 'nturl2path', 'sitecustomize',
	'org.python.core', 'riscos', 'riscosenviron', 'riscospath'
]

STRIP_EXEC = "/usr/bin/strip"

BOOTSTRAP_SCRIPT = """\
#!/bin/sh

execdir=$(dirname ${0})
executable=${execdir}/%(executable)s
resdir=$(dirname ${execdir})/Resources
main=${resdir}/%(mainprogram)s
PYTHONPATH=$resdir
export PYTHONPATH
exec ${executable} ${main} ${1}
"""


class AppBuilder(BundleBuilder):

	# A Python main program. If this argument is given, the main
	# executable in the bundle will be a small wrapper that invokes
	# the main program. (XXX Discuss why.)
	mainprogram = None

	# The main executable. If a Python main program is specified
	# the executable will be copied to Resources and be invoked
	# by the wrapper program mentioned above. Otherwise it will
	# simply be used as the main executable.
	executable = None

	# The name of the main nib, for Cocoa apps. *Must* be specified
	# when building a Cocoa app.
	nibname = None

	# Symlink the executable instead of copying it.
	symlink_exec = 0

	# If True, build standalone app.
	standalone = 0

	# The following attributes are only used when building a standalone app.

	# Exclude these modules.
	excludeModules = []

	# Include these modules.
	includeModules = []

	# Include these packages.
	includePackages = []

	# Strip binaries.
	strip = 0

	# Found Python modules: [(name, codeobject, ispkg), ...]
	pymodules = []

	# Modules that modulefinder couldn't find:
	missingModules = []
	maybeMissingModules = []

	# List of all binaries (executables or shared libs), for stripping purposes
	binaries = []

	def setup(self):
		if self.standalone and self.mainprogram is None:
			raise BundleBuilderError, ("must specify 'mainprogram' when "
					"building a standalone application.")
		if self.mainprogram is None and self.executable is None:
			raise BundleBuilderError, ("must specify either or both of "
					"'executable' and 'mainprogram'")

		if self.name is not None:
			pass
		elif self.mainprogram is not None:
			self.name = os.path.splitext(os.path.basename(self.mainprogram))[0]
		elif executable is not None:
			self.name = os.path.splitext(os.path.basename(self.executable))[0]
		if self.name[-4:] != ".app":
			self.name += ".app"

		if self.executable is None:
			if not self.standalone:
				self.symlink_exec = 1
			self.executable = sys.executable

		if self.nibname:
			self.plist.NSMainNibFile = self.nibname
			if not hasattr(self.plist, "NSPrincipalClass"):
				self.plist.NSPrincipalClass = "NSApplication"

		BundleBuilder.setup(self)

		self.plist.CFBundleExecutable = self.name

		if self.standalone:
			self.findDependencies()

	def preProcess(self):
		resdir = "Contents/Resources"
		if self.executable is not None:
			if self.mainprogram is None:
				execname = self.name
			else:
				execname = os.path.basename(self.executable)
			execpath = pathjoin(self.execdir, execname)
			if not self.symlink_exec:
				self.files.append((self.executable, execpath))
				self.binaries.append(execpath)
			self.execpath = execpath

		if self.mainprogram is not None:
			mainprogram = os.path.basename(self.mainprogram)
			self.files.append((self.mainprogram, pathjoin(resdir, mainprogram)))
			# Write bootstrap script
			executable = os.path.basename(self.executable)
			execdir = pathjoin(self.bundlepath, self.execdir)
			bootstrappath = pathjoin(execdir, self.name)
			makedirs(execdir)
			open(bootstrappath, "w").write(BOOTSTRAP_SCRIPT % locals())
			os.chmod(bootstrappath, 0775)

	def postProcess(self):
		if self.standalone:
			self.addPythonModules()
		if self.strip and not self.symlink:
			self.stripBinaries()

		if self.symlink_exec and self.executable:
			self.message("Symlinking executable %s to %s" % (self.executable,
					self.execpath), 2)
			dst = pathjoin(self.bundlepath, self.execpath)
			makedirs(os.path.dirname(dst))
			os.symlink(os.path.abspath(self.executable), dst)

		if self.missingModules or self.maybeMissingModules:
			self.reportMissing()

	def addPythonModules(self):
		self.message("Adding Python modules", 1)

		if USE_FROZEN:
			# This anticipates the acceptance of this patch:
			#   http://www.python.org/sf/642578
			# Create a file containing all modules, frozen.
			frozenmodules = []
			for name, code, ispkg in self.pymodules:
				if ispkg:
					self.message("Adding Python package %s" % name, 2)
				else:
					self.message("Adding Python module %s" % name, 2)
				frozenmodules.append((name, marshal.dumps(code), ispkg))
			frozenmodules = tuple(frozenmodules)
			relpath = pathjoin("Contents", "Resources", FROZEN_ARCHIVE)
			abspath = pathjoin(self.bundlepath, relpath)
			f = open(abspath, "wb")
			marshal.dump(frozenmodules, f)
			f.close()
			# add site.pyc
			sitepath = pathjoin(self.bundlepath, "Contents", "Resources",
					"site" + PYC_EXT)
			writePyc(SITE_CO, sitepath)
		else:
			# Create individual .pyc files.
			for name, code, ispkg in self.pymodules:
				if ispkg:
					name += ".__init__"
				path = name.split(".")
				path = pathjoin("Contents", "Resources", *path) + PYC_EXT

				if ispkg:
					self.message("Adding Python package %s" % path, 2)
				else:
					self.message("Adding Python module %s" % path, 2)

				abspath = pathjoin(self.bundlepath, path)
				makedirs(os.path.dirname(abspath))
				writePyc(code, abspath)

	def stripBinaries(self):
		if not os.path.exists(STRIP_EXEC):
			self.message("Error: can't strip binaries: no strip program at "
				"%s" % STRIP_EXEC, 0)
		else:
			self.message("Stripping binaries", 1)
			for relpath in self.binaries:
				self.message("Stripping %s" % relpath, 2)
				abspath = pathjoin(self.bundlepath, relpath)
				assert not os.path.islink(abspath)
				rv = os.system("%s -S \"%s\"" % (STRIP_EXEC, abspath))

	def findDependencies(self):
		self.message("Finding module dependencies", 1)
		import modulefinder
		mf = modulefinder.ModuleFinder(excludes=self.excludeModules)
		# manually add our own site.py
		site = mf.add_module("site")
		site.__code__ = SITE_CO
		mf.scan_code(SITE_CO, site)

		includeModules = self.includeModules[:]
		for name in self.includePackages:
			includeModules.extend(findPackageContents(name).keys())
		for name in includeModules:
			try:
				mf.import_hook(name)
			except ImportError:
				self.missingModules.append(name)

		mf.run_script(self.mainprogram)
		modules = mf.modules.items()
		modules.sort()
		for name, mod in modules:
			if mod.__file__ and mod.__code__ is None:
				# C extension
				path = mod.__file__
				filename = os.path.basename(path)
				if USE_FROZEN:
					# "proper" freezing, put extensions in Contents/Resources/,
					# freeze a tiny "loader" program. Due to Thomas Heller.
					dstpath = pathjoin("Contents", "Resources", filename)
					source = EXT_LOADER % {"name": name, "filename": filename}
					code = compile(source, "<dynloader for %s>" % name, "exec")
					mod.__code__ = code
				else:
					# just copy the file
					dstpath = name.split(".")[:-1] + [filename]
					dstpath = pathjoin("Contents", "Resources", *dstpath)
				self.files.append((path, dstpath))
				self.binaries.append(dstpath)
			if mod.__code__ is not None:
				ispkg = mod.__path__ is not None
				if not USE_FROZEN or name != "site":
					# Our site.py is doing the bootstrapping, so we must
					# include a real .pyc file if USE_FROZEN is True.
					self.pymodules.append((name, mod.__code__, ispkg))

		if hasattr(mf, "any_missing_maybe"):
			missing, maybe = mf.any_missing_maybe()
		else:
			missing = mf.any_missing()
			maybe = []
		self.missingModules.extend(missing)
		self.maybeMissingModules.extend(maybe)

	def reportMissing(self):
		missing = [name for name in self.missingModules
				if name not in MAYMISS_MODULES]
		if self.maybeMissingModules:
			maybe = self.maybeMissingModules
		else:
			maybe = [name for name in missing if "." in name]
			missing = [name for name in missing if "." not in name]
		missing.sort()
		maybe.sort()
		if maybe:
			self.message("Warning: couldn't find the following submodules:", 1)
			self.message("    (Note that these could be false alarms -- "
			             "it's not always", 1)
			self.message("    possible to distinguish between \"from package "
			             "import submodule\" ", 1)
			self.message("    and \"from package import name\")", 1)
			for name in maybe:
				self.message("  ? " + name, 1)
		if missing:
			self.message("Warning: couldn't find the following modules:", 1)
			for name in missing:
				self.message("  ? " + name, 1)

	def report(self):
		# XXX something decent
		import pprint
		pprint.pprint(self.__dict__)
		if self.standalone:
			self.reportMissing()

#
# Utilities.
#

SUFFIXES = [_suf for _suf, _mode, _tp in imp.get_suffixes()]
identifierRE = re.compile(r"[_a-zA-z][_a-zA-Z0-9]*$")

def findPackageContents(name, searchpath=None):
	head = name.split(".")[-1]
	if identifierRE.match(head) is None:
		return {}
	try:
		fp, path, (ext, mode, tp) = imp.find_module(head, searchpath)
	except ImportError:
		return {}
	modules = {name: None}
	if tp == imp.PKG_DIRECTORY and path:
		files = os.listdir(path)
		for sub in files:
			sub, ext = os.path.splitext(sub)
			fullname = name + "." + sub
			if sub != "__init__" and fullname not in modules:
				modules.update(findPackageContents(fullname, [path]))
	return modules

def writePyc(code, path):
	f = open(path, "wb")
	f.write("\0" * 8)  # don't bother about a time stamp
	marshal.dump(code, f)
	f.seek(0, 0)
	f.write(MAGIC)
	f.close()

def copy(src, dst, mkdirs=0):
	"""Copy a file or a directory."""
	if mkdirs:
		makedirs(os.path.dirname(dst))
	if os.path.isdir(src):
		shutil.copytree(src, dst)
	else:
		shutil.copy2(src, dst)

def copytodir(src, dstdir):
	"""Copy a file or a directory to an existing directory."""
	dst = pathjoin(dstdir, os.path.basename(src))
	copy(src, dst)

def makedirs(dir):
	"""Make all directories leading up to 'dir' including the leaf
	directory. Don't moan if any path element already exists."""
	try:
		os.makedirs(dir)
	except OSError, why:
		if why.errno != errno.EEXIST:
			raise

def symlink(src, dst, mkdirs=0):
	"""Copy a file or a directory."""
	if mkdirs:
		makedirs(os.path.dirname(dst))
	os.symlink(os.path.abspath(src), dst)

def pathjoin(*args):
	"""Safe wrapper for os.path.join: asserts that all but the first
	argument are relative paths."""
	for seg in args[1:]:
		assert seg[0] != "/"
	return os.path.join(*args)


cmdline_doc = """\
Usage:
  python bundlebuilder.py [options] command
  python mybuildscript.py [options] command

Commands:
  build      build the application
  report     print a report

Options:
  -b, --builddir=DIR     the build directory; defaults to "build"
  -n, --name=NAME        application name
  -r, --resource=FILE    extra file or folder to be copied to Resources
  -e, --executable=FILE  the executable to be used
  -m, --mainprogram=FILE the Python main program
  -p, --plist=FILE       .plist file (default: generate one)
      --nib=NAME         main nib name
  -c, --creator=CCCC     4-char creator code (default: '????')
  -l, --link             symlink files/folder instead of copying them
      --link-exec        symlink the executable instead of copying it
      --standalone       build a standalone application, which is fully
                         independent of a Python installation
  -x, --exclude=MODULE   exclude module (with --standalone)
  -i, --include=MODULE   include module (with --standalone)
      --package=PACKAGE  include a whole package (with --standalone)
      --strip            strip binaries (remove debug info)
  -v, --verbose          increase verbosity level
  -q, --quiet            decrease verbosity level
  -h, --help             print this message
"""

def usage(msg=None):
	if msg:
		print msg
	print cmdline_doc
	sys.exit(1)

def main(builder=None):
	if builder is None:
		builder = AppBuilder(verbosity=1)

	shortopts = "b:n:r:e:m:c:p:lx:i:hvq"
	longopts = ("builddir=", "name=", "resource=", "executable=",
		"mainprogram=", "creator=", "nib=", "plist=", "link",
		"link-exec", "help", "verbose", "quiet", "standalone",
		"exclude=", "include=", "package=", "strip")

	try:
		options, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
	except getopt.error:
		usage()

	for opt, arg in options:
		if opt in ('-b', '--builddir'):
			builder.builddir = arg
		elif opt in ('-n', '--name'):
			builder.name = arg
		elif opt in ('-r', '--resource'):
			builder.resources.append(arg)
		elif opt in ('-e', '--executable'):
			builder.executable = arg
		elif opt in ('-m', '--mainprogram'):
			builder.mainprogram = arg
		elif opt in ('-c', '--creator'):
			builder.creator = arg
		elif opt == "--nib":
			builder.nibname = arg
		elif opt in ('-p', '--plist'):
			builder.plist = Plist.fromFile(arg)
		elif opt in ('-l', '--link'):
			builder.symlink = 1
		elif opt == '--link-exec':
			builder.symlink_exec = 1
		elif opt in ('-h', '--help'):
			usage()
		elif opt in ('-v', '--verbose'):
			builder.verbosity += 1
		elif opt in ('-q', '--quiet'):
			builder.verbosity -= 1
		elif opt == '--standalone':
			builder.standalone = 1
		elif opt in ('-x', '--exclude'):
			builder.excludeModules.append(arg)
		elif opt in ('-i', '--include'):
			builder.includeModules.append(arg)
		elif opt == '--package':
			builder.includePackages.append(arg)
		elif opt == '--strip':
			builder.strip = 1

	if len(args) != 1:
		usage("Must specify one command ('build', 'report' or 'help')")
	command = args[0]

	if command == "build":
		builder.setup()
		builder.build()
	elif command == "report":
		builder.setup()
		builder.report()
	elif command == "help":
		usage()
	else:
		usage("Unknown command '%s'" % command)


def buildapp(**kwargs):
	builder = AppBuilder(**kwargs)
	main(builder)


if __name__ == "__main__":
	main()

--- NEW FILE: cfmfile.py ---
"""codefragments.py -- wrapper to modify code fragments."""

# (c) 1998, Just van Rossum, Letterror

__version__ = "0.8b3"
__author__ = "jvr"

import macfs
import struct
from Carbon import Res
import os
import sys

DEBUG = 0

error = "cfm.error"

BUFSIZE = 0x80000

def mergecfmfiles(srclist, dst, architecture = 'fat'):
	"""Merge all files in srclist into a new file dst. 
	
	If architecture is given, only code fragments of that type will be used:
	"pwpc" for PPC, "m68k" for cfm68k. This does not work for "classic"
	68k code, since it does not use code fragments to begin with.
	If architecture is None, all fragments will be used, enabling FAT binaries.
	"""
	
	srclist = list(srclist)
	for i in range(len(srclist)):
		if type(srclist[i]) == macfs.FSSpecType:
			srclist[i] = srclist[i].as_pathname()
	if type(dst) == macfs.FSSpecType:
		dst = dst.as_pathname()
	
	dstfile = open(dst, "wb")
	rf = Res.FSpOpenResFile(dst, 3)
	try:
		dstcfrg = CfrgResource()
		for src in srclist:
			srccfrg = CfrgResource(src)
			for frag in srccfrg.fragments:
				if frag.architecture == 'pwpc' and architecture == 'm68k':
					continue
				if frag.architecture == 'm68k' and architecture == 'pwpc':
					continue
				dstcfrg.append(frag)
				
				frag.copydata(dstfile)
				
		cfrgres = Res.Resource(dstcfrg.build())
		Res.UseResFile(rf)
		cfrgres.AddResource('cfrg', 0, "")
	finally:
		dstfile.close()
		rf = Res.CloseResFile(rf)


class CfrgResource:
	
	def __init__(self, path = None):
		self.version = 1
		self.fragments = []
		self.path = path
		if path is not None and os.path.exists(path):
			currentresref = Res.CurResFile()
			resref = Res.FSpOpenResFile(path, 1)
			Res.UseResFile(resref)
			try:
				try:
					data = Res.Get1Resource('cfrg', 0).data
				except Res.Error:
					raise Res.Error, "no 'cfrg' resource found", sys.exc_traceback
			finally:
				Res.CloseResFile(resref)
				Res.UseResFile(currentresref)
			self.parse(data)
			if self.version <> 1:
				raise error, "unknown 'cfrg' resource format"	
	
	def parse(self, data):
		(res1, res2, self.version, 
			res3, res4, res5, res6, 
			self.memberCount) = struct.unpack("8l", data[:32])
		data = data[32:]
		while data:
			frag = FragmentDescriptor(self.path, data)
			data = data[frag.memberSize:]
			self.fragments.append(frag)
	
	def build(self):
		self.memberCount = len(self.fragments)
		data = struct.pack("8l", 0, 0, self.version, 0, 0, 0, 0, self.memberCount)
		for frag in self.fragments:
			data = data + frag.build()
		return data
	
	def append(self, frag):
		self.fragments.append(frag)


class FragmentDescriptor:
	
	def __init__(self, path, data = None):
		self.path = path
		if data is not None:
			self.parse(data)
	
	def parse(self, data):
		self.architecture = data[:4]
		(	self.updatelevel, 
			self.currentVersion, 
			self.oldDefVersion, 
			self.stacksize,
			self.applibdir, 
			self.fragtype,
			self.where,
			self.offset,
			self.length,
			self.res1, self.res2,
			self.memberSize,) = struct.unpack("4lhBB4lh", data[4:42])
		pname = data[42:self.memberSize]
		self.name = pname[1:1+ord(pname[0])]
	
	def build(self):
		data = self.architecture
		data = data + struct.pack("4lhBB4l",
				self.updatelevel, 
				self.currentVersion, 
				self.oldDefVersion, 
				self.stacksize,
				self.applibdir, 
				self.fragtype,
				self.where,
				self.offset,
				self.length,
				self.res1, self.res2)
		self.memberSize = len(data) + 2 + 1 + len(self.name)
		# pad to 4 byte boundaries
		if self.memberSize % 4:
			self.memberSize = self.memberSize + 4 - (self.memberSize % 4)
		data = data + struct.pack("hb", self.memberSize, len(self.name))
		data = data + self.name
		data = data + '\000' * (self.memberSize - len(data))
		return data
	
	def getfragment(self):
		if self.where <> 1:
			raise error, "can't read fragment, unsupported location"
		f = open(self.path, "rb")
		f.seek(self.offset)
		if self.length:
			frag = f.read(self.length)
		else:
			frag = f.read()
		f.close()
		return frag
	
	def copydata(self, outfile):
		if self.where <> 1:
			raise error, "can't read fragment, unsupported location"
		infile = open(self.path, "rb")
		if self.length == 0:
			infile.seek(0, 2)
			self.length = infile.tell()
		
		# Position input file and record new offset from output file
		infile.seek(self.offset)
		
		# pad to 16 byte boundaries
		offset = outfile.tell()
		if offset % 16:
			offset = offset + 16 - (offset % 16)
		outfile.seek(offset)
		self.offset = offset
		
		l = self.length
		while l:
			if l > BUFSIZE:
				outfile.write(infile.read(BUFSIZE))
				l = l - BUFSIZE
			else:
				outfile.write(infile.read(l))
				l = 0
		infile.close()


--- NEW FILE: dialogs.rsrc ---
(This appears to be a binary file; contents omitted.)

--- NEW FILE: errors.rsrc ---
(This appears to be a binary file; contents omitted.)

--- NEW FILE: findertools.py ---
"""Utility routines depending on the finder,
a combination of code by Jack Jansen and erik@letterror.com.

Most events have been captured from
Lasso Capture AE and than translated to python code.

IMPORTANT
Note that the processes() function returns different values
depending on the OS version it is running on. On MacOS 9
the Finder returns the process *names* which can then be
used to find out more about them. On MacOS 8.6 and earlier
the Finder returns a code which does not seem to work.
So bottom line: the processes() stuff does not work on < MacOS9

Mostly written by erik@letterror.com
"""
import Finder
from Carbon import AppleEvents
import aetools
import MacOS
import sys
import macfs
import aetypes
from types import *

__version__ = '1.1'
Error = 'findertools.Error'

_finder_talker = None

def _getfinder():
	"""returns basic (recyclable) Finder AE interface object"""
	global _finder_talker
	if not _finder_talker:
		_finder_talker = Finder.Finder()
	_finder_talker.send_flags = ( _finder_talker.send_flags | 
		AppleEvents.kAECanInteract | AppleEvents.kAECanSwitchLayer)
	return _finder_talker
	
def launch(file):
	"""Open a file thru the finder. Specify file by name or fsspec"""
	finder = _getfinder()
	fss = macfs.FSSpec(file)
	return finder.open(fss)
	
def Print(file):
	"""Print a file thru the finder. Specify file by name or fsspec"""
	finder = _getfinder()
	fss = macfs.FSSpec(file)
	return finder._print(fss)
	
def copy(src, dstdir):
	"""Copy a file to a folder"""
	finder = _getfinder()
	if type(src) == type([]):
		src_fss = []
		for s in src:
			src_fss.append(macfs.FSSpec(s))
	else:
		src_fss = macfs.FSSpec(src)
	dst_fss = macfs.FSSpec(dstdir)
	return finder.duplicate(src_fss, to=dst_fss)

def move(src, dstdir):
	"""Move a file to a folder"""
	finder = _getfinder()
	if type(src) == type([]):
		src_fss = []
		for s in src:
			src_fss.append(macfs.FSSpec(s))
	else:
		src_fss = macfs.FSSpec(src)
	dst_fss = macfs.FSSpec(dstdir)
	return finder.move(src_fss, to=dst_fss)
	
def sleep():
	"""Put the mac to sleep"""
	finder = _getfinder()
	finder.sleep()
	
def shutdown():
	"""Shut the mac down"""
	finder = _getfinder()
	finder.shut_down()
	
def restart():
	"""Restart the mac"""
	finder = _getfinder()
	finder.restart()


#---------------------------------------------------
#	Additional findertools
#

def reveal(file):
	"""Reveal a file in the finder. Specify file by name or fsspec."""
	finder = _getfinder()
	fss = macfs.FSSpec(file)
	file_alias = fss.NewAlias()
	return finder.reveal(file_alias)
	
def select(file):
	"""select a file in the finder. Specify file by name or fsspec."""
	finder = _getfinder()
	fss = macfs.FSSpec(file)
	file_alias = fss.NewAlias()
	return finder.select(file_alias)
	
def update(file):
	"""Update the display of the specified object(s) to match 
	their on-disk representation. Specify file by name or fsspec."""
	finder = _getfinder()
	fss = macfs.FSSpec(file)
	file_alias = fss.NewAlias()
	return finder.update(file_alias)


#---------------------------------------------------
#	More findertools
#

def comment(object, comment=None):
	"""comment: get or set the Finder-comment of the item, displayed in the –Get Info” window."""
	object = macfs.FSSpec(object)
	fss = macfs.FSSpec(object)
	object_alias = fss.NewAlias()
	if comment == None:
		return _getcomment(object_alias)
	else:
		return _setcomment(object_alias, comment)
	
def _setcomment(object_alias, comment):
	finder = _getfinder()
	args = {}
	attrs = {}
	aeobj_00 = aetypes.ObjectSpecifier(want=aetypes.Type('cobj'), form="alis", seld=object_alias, fr=None)
	aeobj_01 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), form="prop", seld=aetypes.Type('comt'), fr=aeobj_00)
	args['----'] = aeobj_01
	args["data"] = comment
	_reply, args, attrs = finder.send("core", "setd", args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		return args['----']

def _getcomment(object_alias):
	finder = _getfinder()
	args = {}
	attrs = {}
	aeobj_00 = aetypes.ObjectSpecifier(want=aetypes.Type('cobj'), form="alis", seld=object_alias, fr=None)
	aeobj_01 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), form="prop", seld=aetypes.Type('comt'), fr=aeobj_00)
	args['----'] = aeobj_01
	_reply, args, attrs = finder.send("core", "getd", args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		return args['----']


#---------------------------------------------------
#	Get information about current processes in the Finder.

def processes():
	"""processes returns a list of all active processes running on this computer and their creators."""
	finder = _getfinder()
	args = {}
	attrs = {}
	processnames = []
	processnumbers = []
	creators = []
	partitions = []
	used = []
	## get the processnames or else the processnumbers
	args['----'] = aetypes.ObjectSpecifier(want=aetypes.Type('prcs'), form="indx", seld=aetypes.Unknown('abso', "all "), fr=None)
	_reply, args, attrs = finder.send('core', 'getd', args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	p = []
	if args.has_key('----'):
		p =  args['----']
		for proc in p:
			if hasattr(proc, 'seld'):
				# it has a real name
				processnames.append(proc.seld)
			elif hasattr(proc, 'type'):
				if proc.type == "psn ":
					# it has a process number
					processnumbers.append(proc.data)
	## get the creators
	args = {}
	attrs = {}
	aeobj_0 = aetypes.ObjectSpecifier(want=aetypes.Type('prcs'), form="indx", seld=aetypes.Unknown('abso', "all "), fr=None)
	args['----'] =  aetypes.ObjectSpecifier(want=aetypes.Type('prop'), form="prop", seld=aetypes.Type('fcrt'), fr=aeobj_0)
	_reply, args, attrs = finder.send('core', 'getd', args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(_arg)
	if args.has_key('----'):
		p =  args['----']
		creators = p[:]
	## concatenate in one dict
	result = []
	if len(processnames) > len(processnumbers):
		data = processnames
	else:
		data = processnumbers
	for i in range(len(creators)):
		result.append((data[i], creators[i]))
	return result

class _process:
	pass

def isactiveprocess(processname):
	"""Check of processname is active. MacOS9"""
	all = processes()
	ok = 0
	for n, c in all:
		if n == processname:
			return 1
	return 0
	
def processinfo(processname):
	"""Return an object with all process properties as attributes for processname. MacOS9"""
	p = _process()
	
	if processname == "Finder":
		p.partition = None
		p.used = None
	else:
		p.partition = _processproperty(processname, 'appt')
		p.used = _processproperty(processname, 'pusd')
	p.visible = _processproperty(processname, 'pvis')		#Is the process' layer visible?
	p.frontmost = _processproperty(processname, 'pisf')	#Is the process the frontmost process?
	p.file = _processproperty(processname, 'file')			#the file from which the process was launched
	p.filetype  = _processproperty(processname, 'asty')		#the OSType of the file type of the process
	p.creatortype = _processproperty(processname, 'fcrt')	#the OSType of the creator of the process (the signature)
	p.accepthighlevel = _processproperty(processname, 'revt')	#Is the process high-level event aware (accepts open application, open document, print document, and quit)?
	p.hasscripting = _processproperty(processname, 'hscr')		#Does the process have a scripting terminology, i.e., can it be scripted?
	return p
	
def _processproperty(processname, property):
	"""return the partition size and memory used for processname"""
	finder = _getfinder()
	args = {}
	attrs = {}
	aeobj_00 = aetypes.ObjectSpecifier(want=aetypes.Type('prcs'), form="name", seld=processname, fr=None)
	aeobj_01 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), form="prop", seld=aetypes.Type(property), fr=aeobj_00)
	args['----'] = aeobj_01
	_reply, args, attrs = finder.send("core", "getd", args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		return args['----']


#---------------------------------------------------
#	Mess around with Finder windows.
	
def openwindow(object):
	"""Open a Finder window for object, Specify object by name or fsspec."""
	finder = _getfinder()
	object = macfs.FSSpec(object)
	fss = macfs.FSSpec(object)
	object_alias = fss.NewAlias()
	args = {}
	attrs = {}
	_code = 'aevt'
	_subcode = 'odoc'
	aeobj_0 = aetypes.ObjectSpecifier(want=aetypes.Type('cfol'), form="alis", seld=object_alias, fr=None)
	args['----'] = aeobj_0
	_reply, args, attrs = finder.send(_code, _subcode, args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	
def closewindow(object):
	"""Close a Finder window for folder, Specify by path."""
	finder = _getfinder()
	fss = macfs.FSSpec(object)
	object_alias = fss.NewAlias()
	args = {}
	attrs = {}
	_code = 'core'
	_subcode = 'clos'
	aeobj_0 = aetypes.ObjectSpecifier(want=aetypes.Type('cfol'), form="alis", seld=object_alias, fr=None)
	args['----'] = aeobj_0
	_reply, args, attrs = finder.send(_code, _subcode, args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)

def location(object, pos=None):
	"""Set the position of a Finder window for folder to pos=(w, h). Specify file by name or fsspec.
	If pos=None, location will return the current position of the object."""
	fss = macfs.FSSpec(object)
	object_alias = fss.NewAlias()
	if not pos:
		return _getlocation(object_alias)
	return _setlocation(object_alias, pos)
	
def _setlocation(object_alias, (x, y)):
	"""_setlocation: Set the location of the icon for the object."""
	finder = _getfinder()
	args = {}
	attrs = {}
	aeobj_00 = aetypes.ObjectSpecifier(want=aetypes.Type('cfol'), form="alis", seld=object_alias, fr=None)
	aeobj_01 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), form="prop", seld=aetypes.Type('posn'), fr=aeobj_00)
	args['----'] = aeobj_01
	args["data"] = [x, y]
	_reply, args, attrs = finder.send("core", "setd", args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	return (x,y)
	
def _getlocation(object_alias):
	"""_getlocation: get the location of the icon for the object."""
	finder = _getfinder()
	args = {}
	attrs = {}
	aeobj_00 = aetypes.ObjectSpecifier(want=aetypes.Type('cfol'), form="alis", seld=object_alias, fr=None)
	aeobj_01 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), form="prop", seld=aetypes.Type('posn'), fr=aeobj_00)
	args['----'] = aeobj_01
	_reply, args, attrs = finder.send("core", "getd", args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		pos = args['----']
		return pos.h, pos.v

def label(object, index=None):
	"""label: set or get the label of the item. Specify file by name or fsspec."""
	fss = macfs.FSSpec(object)
	object_alias = fss.NewAlias()
	if index == None:
		return _getlabel(object_alias)
	if index < 0 or index > 7:
		index = 0
	return _setlabel(object_alias, index)
	
def _getlabel(object_alias):
	"""label: Get the label for the object."""
	finder = _getfinder()
	args = {}
	attrs = {}
	aeobj_00 = aetypes.ObjectSpecifier(want=aetypes.Type('cobj'), form="alis", seld=object_alias, fr=None)
	aeobj_01 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), form="prop", seld=aetypes.Type('labi'), fr=aeobj_00)
	args['----'] = aeobj_01
	_reply, args, attrs = finder.send("core", "getd", args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		return args['----']

def _setlabel(object_alias, index):
	"""label: Set the label for the object."""
	finder = _getfinder()
	args = {}
	attrs = {}
	_code = 'core'
	_subcode = 'setd'
	aeobj_0 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'),
			form="alis", seld=object_alias, fr=None)
	aeobj_1 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'),
			form="prop", seld=aetypes.Type('labi'), fr=aeobj_0)
	args['----'] = aeobj_1
	args["data"] = index
	_reply, args, attrs = finder.send(_code, _subcode, args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	return index

def windowview(folder, view=None):
	"""windowview: Set the view of the window for the folder. Specify file by name or fsspec.
	0 = by icon (default)
	1 = by name
	2 = by button
	"""
	fss = macfs.FSSpec(folder)
	folder_alias = fss.NewAlias()
	if view == None:
		return _getwindowview(folder_alias)
	return _setwindowview(folder_alias, view)
	
def _setwindowview(folder_alias, view=0):
	"""set the windowview"""
	attrs = {}
	args = {}
	if view == 1:
		_v = aetypes.Type('pnam')
	elif view == 2:
		_v = aetypes.Type('lgbu')
	else:
		_v = aetypes.Type('iimg')
	finder = _getfinder()
	aeobj_0 = aetypes.ObjectSpecifier(want = aetypes.Type('cfol'), 
			form = 'alis', seld = folder_alias, fr=None)
	aeobj_1 = aetypes.ObjectSpecifier(want = aetypes.Type('prop'), 
			form = 'prop', seld = aetypes.Type('cwnd'), fr=aeobj_0)
	aeobj_2 = aetypes.ObjectSpecifier(want = aetypes.Type('prop'), 
			form = 'prop', seld = aetypes.Type('pvew'), fr=aeobj_1)
	aeobj_3 = aetypes.ObjectSpecifier(want = aetypes.Type('prop'), 
			form = 'prop', seld = _v, fr=None)
	_code = 'core'
	_subcode = 'setd'
	args['----'] = aeobj_2
	args['data'] = aeobj_3
	_reply, args, attrs = finder.send(_code, _subcode, args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		return args['----']

def _getwindowview(folder_alias):
	"""get the windowview"""
	attrs = {}
	args = {}
	finder = _getfinder()
	args = {}
	attrs = {}
	aeobj_00 = aetypes.ObjectSpecifier(want=aetypes.Type('cfol'), form="alis", seld=folder_alias, fr=None)
	aeobj_01 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), form="prop", seld=aetypes.Type('cwnd'), fr=aeobj_00)
	aeobj_02 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), form="prop", seld=aetypes.Type('pvew'), fr=aeobj_01)
	args['----'] = aeobj_02
	_reply, args, attrs = finder.send("core", "getd", args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	views = {'iimg':0, 'pnam':1, 'lgbu':2}
	if args.has_key('----'):
		return views[args['----'].enum]

def windowsize(folder, size=None):
	"""Set the size of a Finder window for folder to size=(w, h), Specify by path.
	If size=None, windowsize will return the current size of the window.
	Specify file by name or fsspec.
	"""
	fss = macfs.FSSpec(folder)
	folder_alias = fss.NewAlias()
	openwindow(fss)
	if not size:
		return _getwindowsize(folder_alias)
	return _setwindowsize(folder_alias, size)
	
def _setwindowsize(folder_alias, (w, h)):
	"""Set the size of a Finder window for folder to (w, h)"""
	finder = _getfinder()
	args = {}
	attrs = {}
	_code = 'core'
	_subcode = 'setd'
	aevar00 = [w, h]
	aeobj_0 = aetypes.ObjectSpecifier(want=aetypes.Type('cfol'),
			form="alis", seld=folder_alias, fr=None)
	aeobj_1 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), 
			form="prop", seld=aetypes.Type('cwnd'), fr=aeobj_0)
	aeobj_2 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), 
			form="prop", seld=aetypes.Type('ptsz'), fr=aeobj_1)
	args['----'] = aeobj_2
	args["data"] = aevar00
	_reply, args, attrs = finder.send(_code, _subcode, args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	return (w, h)
		
def _getwindowsize(folder_alias):
	"""Set the size of a Finder window for folder to (w, h)"""
	finder = _getfinder()
	args = {}
	attrs = {}
	aeobj_0 = aetypes.ObjectSpecifier(want=aetypes.Type('cfol'), 
			form="alis", seld=folder_alias, fr=None)
	aeobj_1 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), 
			form="prop", seld=aetypes.Type('cwnd'), fr=aeobj_0)
	aeobj_2 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), 
			form="prop", seld=aetypes.Type('posn'), fr=aeobj_1)
	args['----'] = aeobj_2
	_reply, args, attrs = finder.send('core', 'getd', args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		return args['----']

def windowposition(folder, pos=None):
	"""Set the position of a Finder window for folder to pos=(w, h)."""
	fss = macfs.FSSpec(folder)
	folder_alias = fss.NewAlias()
	openwindow(fss)
	if not pos:
		return _getwindowposition(folder_alias)
	if type(pos) == InstanceType:
		# pos might be a QDPoint object as returned by _getwindowposition
		pos = (pos.h, pos.v)
	return _setwindowposition(folder_alias, pos)
			
def _setwindowposition(folder_alias, (x, y)):
	"""Set the size of a Finder window for folder to (w, h)."""
	finder = _getfinder()
	args = {}
	attrs = {}
	aeobj_0 = aetypes.ObjectSpecifier(want=aetypes.Type('cfol'), 
			form="alis", seld=folder_alias, fr=None)
	aeobj_1 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), 
			form="prop", seld=aetypes.Type('cwnd'), fr=aeobj_0)
	aeobj_2 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), 
			form="prop", seld=aetypes.Type('posn'), fr=aeobj_1)
	args['----'] = aeobj_2
	args["data"] = [x, y]
	_reply, args, attrs = finder.send('core', 'setd', args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		return args['----']

def _getwindowposition(folder_alias):
	"""Get the size of a Finder window for folder, Specify by path."""
	finder = _getfinder()
	args = {}
	attrs = {}
	aeobj_0 = aetypes.ObjectSpecifier(want=aetypes.Type('cfol'), 
			form="alis", seld=folder_alias, fr=None)
	aeobj_1 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), 
			form="prop", seld=aetypes.Type('cwnd'), fr=aeobj_0)
	aeobj_2 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), 
			form="prop", seld=aetypes.Type('ptsz'), fr=aeobj_1)
	args['----'] = aeobj_2
	_reply, args, attrs = finder.send('core', 'getd', args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		return args['----']

def icon(object, icondata=None):
	"""icon sets the icon of object, if no icondata is given,
	icon will return an AE object with binary data for the current icon.
	If left untouched, this data can be used to paste the icon on another file.
	Development opportunity: get and set the data as PICT."""
	fss = macfs.FSSpec(object)
	object_alias = fss.NewAlias()
	if icondata == None:
		return _geticon(object_alias)
	return _seticon(object_alias, icondata)
	
def _geticon(object_alias):
	"""get the icondata for object. Binary data of some sort."""
	finder = _getfinder()
	args = {}
	attrs = {}
	aeobj_00 = aetypes.ObjectSpecifier(want=aetypes.Type('cobj'), 
			form="alis", seld=object_alias, fr=None)
	aeobj_01 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), 
			form="prop", seld=aetypes.Type('iimg'), fr=aeobj_00)
	args['----'] = aeobj_01
	_reply, args, attrs = finder.send("core", "getd", args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		return args['----']

def _seticon(object_alias, icondata):
	"""set the icondata for object, formatted as produced by _geticon()"""
	finder = _getfinder()
	args = {}
	attrs = {}
	aeobj_00 = aetypes.ObjectSpecifier(want=aetypes.Type('cobj'), 
			form="alis", seld=object_alias, fr=None)
	aeobj_01 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), 
			form="prop", seld=aetypes.Type('iimg'), fr=aeobj_00)
	args['----'] = aeobj_01
	args["data"] = icondata
	_reply, args, attrs = finder.send("core", "setd", args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		return args['----'].data


#---------------------------------------------------
#	Volumes and servers.
	
def mountvolume(volume, server=None, username=None, password=None):
	"""mount a volume, local or on a server on AppleTalk.
	Note: mounting a ASIP server requires a different operation.
	server is the name of the server where the volume belongs
	username, password belong to a registered user of the volume."""
	finder = _getfinder()
	args = {}
	attrs = {}
	if password:
		args["PASS"] = password
	if username:
		args["USER"] = username
	if server:
		args["SRVR"] = server
	args['----'] = volume
	_reply, args, attrs = finder.send("aevt", "mvol", args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		return args['----']

def unmountvolume(volume):
	"""unmount a volume that's on the desktop"""
	putaway(volume)
	
def putaway(object):
	"""puth the object away, whereever it came from."""
	finder = _getfinder()
	args = {}
	attrs = {}
	args['----'] = aetypes.ObjectSpecifier(want=aetypes.Type('cdis'), form="name", seld=object, fr=None)
	_reply, args, attrs = talker.send("fndr", "ptwy", args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		return args['----']


#---------------------------------------------------
#	Miscellaneous functions
#

def volumelevel(level):
	"""set the audio output level, parameter between 0 (silent) and 7 (full blast)"""
	finder = _getfinder()
	args = {}
	attrs = {}
	if level < 0:
		level = 0
	elif level > 7:
		level = 7
	args['----'] = level
	_reply, args, attrs = finder.send("aevt", "stvl", args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		return args['----']

def OSversion():
	"""return the version of the system software"""
	finder = _getfinder()
	args = {}
	attrs = {}
	aeobj_00 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), form="prop", seld=aetypes.Type('ver2'), fr=None)
	args['----'] = aeobj_00
	_reply, args, attrs = finder.send("core", "getd", args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		return args['----']

def filesharing():
	"""return the current status of filesharing and whether it is starting up or not:
		-1	file sharing is off and not starting up
		0	file sharing is off and starting up
		1	file sharing is on"""
	status = -1
	finder = _getfinder()
	# see if it is on
	args = {}
	attrs = {}
	args['----'] = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), form="prop", seld=aetypes.Type('fshr'), fr=None)
	_reply, args, attrs = finder.send("core", "getd", args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		if args['----'] == 0:
			status = -1
		else:
			status = 1
	# is it starting up perchance?
	args = {}
	attrs = {}
	args['----'] = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), form="prop", seld=aetypes.Type('fsup'), fr=None)
	_reply, args, attrs = finder.send("core", "getd", args, attrs)
	if args.has_key('errn'):
		raise Error, aetools.decodeerror(args)
	if args.has_key('----'):
		if args['----'] == 1:
			status = 0
	return status
	
def movetotrash(path):
	"""move the object to the trash"""
	fss = macfs.FSSpec(path)
	trashfolder = macfs.FSSpec(macfs.FindFolder(fss.as_tuple()[0], 'trsh', 0) + ("",)).as_pathname()
	move(path, trashfolder)

def emptytrash():
	"""empty the trash"""
	finder = _getfinder()
	args = {}
	attrs = {}
	args['----'] = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), form="prop", seld=aetypes.Type('trsh'), fr=None)
	_reply, args, attrs = finder.send("fndr", "empt", args, attrs)
	if args.has_key('errn'):
		raise aetools.Error, aetools.decodeerror(args)


def _test():
	print 'Original findertools functionality test...'
	print 'Testing launch...'
	fss, ok = macfs.PromptGetFile('File to launch:')
	if ok:
		result = launch(fss)
		if result:
			print 'Result: ', result
		print 'Press return-',
		sys.stdin.readline()
	print 'Testing print...'
	fss, ok = macfs.PromptGetFile('File to print:')
	if ok:
		result = Print(fss)
		if result:
			print 'Result: ', result
		print 'Press return-',
		sys.stdin.readline()
	print 'Testing copy...'
	fss, ok = macfs.PromptGetFile('File to copy:')
	if ok:
		dfss, ok = macfs.GetDirectory()
		if ok:
			result = copy(fss, dfss)
			if result:
				print 'Result:', result
			print 'Press return-',
			sys.stdin.readline()
	print 'Testing move...'
	fss, ok = macfs.PromptGetFile('File to move:')
	if ok:
		dfss, ok = macfs.GetDirectory()
		if ok:
			result = move(fss, dfss)
			if result:
				print 'Result:', result
			print 'Press return-',
			sys.stdin.readline()
	import EasyDialogs
	print 'Testing sleep...'
	if EasyDialogs.AskYesNoCancel('Sleep?') > 0:
		result = sleep()
		if result:
			print 'Result:', result
		print 'Press return-',
		sys.stdin.readline()
	print 'Testing shutdown...'
	if EasyDialogs.AskYesNoCancel('Shut down?') > 0:
		result = shutdown()
		if result:
			print 'Result:', result
		print 'Press return-',
		sys.stdin.readline()
	print 'Testing restart...'
	if EasyDialogs.AskYesNoCancel('Restart?') > 0:
		result = restart()
		if result:
			print 'Result:', result
		print 'Press return-',
		sys.stdin.readline()

def _test2():
	print '\nmorefindertools version %s\nTests coming upƒ' %__version__
	import os
	import random

	# miscellaneous
	print '\tfilesharing on?',	filesharing()		# is file sharing on, off, starting up?
	print '\tOS version', 		OSversion()		# the version of the system software

	# set the soundvolume in a simple way
	print '\tSystem beep volume'
	for i in range(0, 7):
		volumelevel(i)		
		MacOS.SysBeep()

	# Finder's windows, file location, file attributes
	open("@findertoolstest", "w")
	f = macfs.FSSpec("@findertoolstest").as_pathname()
	reveal(f)				# reveal this file in a Finder window
	select(f)				# select this file

	base, file = os.path.split(f)
	closewindow(base)	# close the window this file is in	(opened by reveal)
	openwindow(base)		# open it again
	windowview(base, 1)	# set the view by list

	label(f, 2)				# set the label of this file to something orange
	print '\tlabel', label(f)	# get the label of this file

	# the file location only works in a window with icon view!
	print 'Random locations for an icon'
	windowview(base, 0)		# set the view by icon
	windowsize(base, (600, 600))
	for i in range(50):
		location(f, (random.randint(10, 590), random.randint(10, 590)))

	windowsize(base, (200, 400))
	windowview(base, 1)		# set the view by icon

	orgpos = windowposition(base)
	print 'Animated window location'
	for i in range(10):
		pos = (100+i*10, 100+i*10)
		windowposition(base, pos)
		print '\twindow position', pos
	windowposition(base, orgpos)	# park it where it was beforeƒ

	print 'Put a comment in file', f, ':'
	print '\t', comment(f)		# print the Finder comment this file has
	s = 'This is a comment no one reads!'
	comment(f, s)			# set the Finder comment
	
def _test3():
	print 'MacOS9 or better specific functions'
	# processes
	pr = processes()		# return a list of tuples with (active_processname, creatorcode)
	print 'Return a list of current active processes:'
	for p in pr:
		print '\t', p
	
	# get attributes of the first process in the list
	print 'Attributes of the first process in the list:'
	pinfo = processinfo(pr[0][0])
	print '\t', pr[0][0]
	print '\t\tmemory partition', pinfo.partition		# the memory allocated to this process
	print '\t\tmemory used', pinfo.used			# the memory actuall used by this process
	print '\t\tis visible', pinfo.visible			# is the process visible to the user
	print '\t\tis frontmost', pinfo.frontmost		# is the process the front most one?
	print '\t\thas scripting', pinfo.hasscripting		# is the process scriptable?
	print '\t\taccepts high level events', 	pinfo.accepthighlevel	# does the process accept high level appleevents?

if __name__ == '__main__':
	_test()
	_test2()
	_test3()
	

--- NEW FILE: ic.py ---
"""IC wrapper module, based on Internet Config 1.3"""

import icglue
import string
import sys
from Carbon import Res
import macfs
import macostools

error=icglue.error

# From ictypes.h:
icPrefNotFoundErr = -666		# preference not found (duh!)
icPermErr = -667				# cannot set preference
icPrefDataErr = -668			# problem with preference data
icInternalErr = -669			# hmm, this is not good
icTruncatedErr = -670			# more data was present than was returned
icNoMoreWritersErr = -671		# you cannot begin a write session because someone else is already doing it */
icNothingToOverrideErr = -672	# no component for the override component to capture
icNoURLErr = -673				# no URL found
icConfigNotFoundErr = -674		# no configuration was found
icConfigInappropriateErr = -675	# incorrect manufacturer code

ICattr_no_change = -1

icNoPerm = 0
icReadOnlyPerm = 1
icReadWritePerm = 2
# End of ictypes.h

class ICOpaqueData:
	"""An unparseable IC entry"""
	def __init__(self, data):
		self.data = data

	def __repr__(self):
		return "ICOpaqueData(%s)"%`self.data`

_ICOpaqueDataType=type(ICOpaqueData(''))
		
def _decode_default(data, key):
	if len(data) == 0:
		return data
	if ord(data[0]) == len(data)-1:
		# Assume Pstring
		return data[1:]
	return ICOpaqueData(data)
	
	
def _decode_multistr(data, key):
	numstr = ord(data[0]) << 8 | ord(data[1])
	rv = []
	ptr = 2
	for i in range(numstr):
		strlen = ord(data[ptr])
		str = data[ptr+1:ptr+strlen+1]
		rv.append(str)
		ptr = ptr + strlen + 1
	return rv
	
def _decode_fontrecord(data, key):
	size = ord(data[0]) << 8 | ord(data[1])
	face = ord(data[2])
	namelen = ord(data[4])
	return size, face, data[5:5+namelen]
	
def _decode_boolean(data, key):
	return ord(data[0])
	
def _decode_text(data, key):
	return data
	
def _decode_charset(data, key):
	return data[:256], data[256:]
	
def _decode_appspec(data, key):
	namelen = ord(data[4])
	return data[0:4], data[5:5+namelen]

def _code_default(data, key):
	return chr(len(data)) + data
		
def _code_multistr(data, key):
	numstr = len(data)
	rv = chr((numstr>>8) & 0xff) + chr(numstr & 0xff)
	for i in data:
		rv = rv + _code_default(i)
	return rv
	
def _code_fontrecord(data, key):
	size, face, name = data
	return chr((size>>8) & 0xff) + chr(size & 0xff) + chr(face & 0xff) + \
		chr(0) + _code_default(name)
	
def _code_boolean(data, key):
	print 'XXXX boolean:', `data`
	return chr(data)
	
def _code_text(data, key):
	return data
	
def _code_charset(data, key):
	return data[0] + data[1]
	
def _code_appspec(data, key):
	return data[0] + _code_default(data[1])
	
_decoder_table = {
	"ArchieAll" : (_decode_multistr , _code_multistr),
	"UMichAll" : (_decode_multistr , _code_multistr),
	"InfoMacAll" : (_decode_multistr , _code_multistr),
	"ListFont" : (_decode_fontrecord , _code_fontrecord),
	"ScreenFont" : (_decode_fontrecord , _code_fontrecord),
	"PrinterFont" : (_decode_fontrecord , _code_fontrecord),
#	"DownloadFolder" : (_decode_filespec , _code_filespec),
	"Signature": (_decode_text , _code_text),
	"Plan" : (_decode_text , _code_text),
	"MailHeaders" : (_decode_text , _code_text),
	"NewsHeaders" : (_decode_text , _code_text),
#	"Mapping" 
	"CharacterSet" : (_decode_charset , _code_charset),
	"Helper\245" : (_decode_appspec , _code_appspec),
#	"Services" : (_decode_services, ????),
	"NewMailFlashIcon" : (_decode_boolean , _code_boolean),
	"NewMailDialog" : (_decode_boolean , _code_boolean),
	"NewMailPlaySound" : (_decode_boolean , _code_boolean),
#	"WebBackgroundColor" : _decode_color,
	"NoProxyDomains" : (_decode_multistr , _code_multistr),
	"UseHTTPProxy" : (_decode_boolean , _code_boolean),
	"UseGopherProxy": (_decode_boolean , _code_boolean),
	"UseFTPProxy" : (_decode_boolean , _code_boolean),
	"UsePassiveFTP" : (_decode_boolean , _code_boolean),
}

def _decode(data, key):
	if '\245' in key:
		key2 = key[:string.index(key, '\245')+1]
	else:
		key2 = key
	if _decoder_table.has_key(key2):
		decoder = _decoder_table[key2][0]
	else:
		decoder = _decode_default
	return decoder(data, key)

def _code(data, key):
	if type(data) == _ICOpaqueDataType:
		return data.data
	if '\245' in key:
		key2 = key[:string.index(key, '\245')+1]
	else:
		key2 = key
	if _decoder_table.has_key(key2):
		coder = _decoder_table[key2][1]
	else:
		coder = _code_default
	return coder(data, key)
	
class IC:
	def __init__(self, signature='Pyth', ic=None):
		if ic:
			self.ic = ic
		else:
			self.ic = icglue.ICStart(signature)
			if hasattr(self.ic, 'ICFindConfigFile'):
				self.ic.ICFindConfigFile()
		self.h = Res.Resource('')
			
	def keys(self):
		rv = []
		self.ic.ICBegin(icReadOnlyPerm)
		num = self.ic.ICCountPref()
		for i in range(num):
			rv.append(self.ic.ICGetIndPref(i+1))
		self.ic.ICEnd()
		return rv
		
	def has_key(self, key):
		return self.__contains__(key)
		
	def __contains__(self, key):
		try:
			dummy = self.ic.ICFindPrefHandle(key, self.h)
		except icglue.error:
			return 0
		return 1
		
	def __getitem__(self, key):
		attr = self.ic.ICFindPrefHandle(key, self.h)
		return _decode(self.h.data, key)
		
	def __setitem__(self, key, value):
		value = _code(value, key)
		self.ic.ICSetPref(key, ICattr_no_change, value)
		
	def launchurl(self, url, hint=""):
		self.ic.ICLaunchURL(hint, url, 0, len(url))
		
	def parseurl(self, data, start=None, end=None, hint=""):
		if start == None:
			selStart = 0
			selEnd = len(data)
		else:
			selStart = selEnd = start
		if end != None:
			selEnd = end
		selStart, selEnd = self.ic.ICParseURL(hint, data, selStart, selEnd, self.h)
		return self.h.data, selStart, selEnd
		
	def mapfile(self, file):
		if type(file) != type(''):
			file = file.as_tuple()[2]
		return self.ic.ICMapFilename(file)
		
	def maptypecreator(self, type, creator, filename=""):
		return self.ic.ICMapTypeCreator(type, creator, filename)
		
	def settypecreator(self, file):
		if type(file) == type(''):
			fss = macfs.FSSpec(file)
		else:
			fss = file
		name = fss.as_tuple()[2]
		record = self.mapfile(name)
		fss.SetCreatorType(record[2], record[1])
		macostools.touched(fss)
		
# Convenience routines
_dft_ic = None

def launchurl(url, hint=""):
	global _dft_ic
	if _dft_ic == None: _dft_ic = IC()
	return _dft_ic.launchurl(url, hint)
	
def parseurl(data, start=None, end=None, hint=""):
	global _dft_ic
	if _dft_ic == None: _dft_ic = IC()
	return _dft_ic.parseurl(data, start, end, hint)
	
def mapfile(filename):
	global _dft_ic
	if _dft_ic == None: _dft_ic = IC()
	return _dft_ic.mapfile(filename)
	
def maptypecreator(type, creator, filename=""):
	global _dft_ic
	if _dft_ic == None: _dft_ic = IC()
	return _dft_ic.maptypecreator(type, creator, filename)
	
def settypecreator(file):
	global _dft_ic
	if _dft_ic == None: _dft_ic = IC()
	return _dft_ic.settypecreator(file)
		
def _test():
	ic = IC()
	for k in ic.keys():
		try:
			v = ic[k]
		except error:
			v = '????'
		print k, '\t', v
	sys.exit(1)
	
if __name__ == '__main__':
	_test()
	

--- NEW FILE: icopen.py ---
"""icopen patch

OVERVIEW

icopen patches MacOS Python to use the Internet Config file mappings to select
the type and creator for a file.

Version 1 released to the public domain 3 November 1999
by Oliver Steele (steele@cs.brandeis.edu).

DETAILS

This patch causes files created by Python's open(filename, 'w') command (and
by functions and scripts that call it) to set the type and creator of the file
to the type and creator associated with filename's extension (the
portion of the filename after the last period), according to Internet Config.
Thus, a script that creates a file foo.html will create one that opens in whatever
browser you've set to handle *.html files, and so on.

Python IDE uses its own algorithm to select the type and creator for saved
editor windows, so this patch won't effect their types.

As of System 8.6 at least, Internet Config is built into the system, and the
file mappings are accessed from the Advanced pane of the Internet control
panel.  User Mode (in the Edit menu) needs to be set to Advanced in order to
access this pane.

INSTALLATION

Put this file in your Python path, and create a file named {Python}:sitecustomize.py
that contains:
	import icopen

(If {Python}:sitecustomizer.py already exists, just add the 'import' line to it.)

The next time you launch PythonInterpreter or Python IDE, the patch will take
effect.
"""

import __builtin__

_builtin_open = globals().get('_builtin_open', __builtin__.open)

def _open_with_typer(*args):
	file = apply(_builtin_open, args)
	filename = args[0]
	mode = 'r'
	if args[1:]:
		mode = args[1]
	if mode[0] == 'w':
		from ic import error, settypecreator
		try:
			settypecreator(filename)
		except error:
			pass
	return file

__builtin__.open = _open_with_typer

"""
open('test.py')
_open_with_typer('test.py', 'w')
_open_with_typer('test.txt', 'w')
_open_with_typer('test.html', 'w')
_open_with_typer('test.foo', 'w')
"""
--- NEW FILE: macerrors.py ---
svTempDisable	=	-32768	#svTempDisable
svDisabled	=	-32640	#Reserve range -32640 to -32768 for Apple temp disables.
fontNotOutlineErr	=	-32615	#bitmap font passed to routine that does outlines only
kURL68kNotSupportedError	=	-30788	#kURL68kNotSupportedError
kURLAccessNotAvailableError	=	-30787	#kURLAccessNotAvailableError
kURLInvalidConfigurationError	=	-30786	#kURLInvalidConfigurationError
kURLExtensionFailureError	=	-30785	#kURLExtensionFailureError
kURLFileEmptyError	=	-30783	#kURLFileEmptyError
kURLInvalidCallError	=	-30781	#kURLInvalidCallError
kURLUnsettablePropertyError	=	-30780	#kURLUnsettablePropertyError
kURLPropertyBufferTooSmallError	=	-30779	#kURLPropertyBufferTooSmallError
kURLUnknownPropertyError	=	-30778	#kURLUnknownPropertyError
kURLPropertyNotYetKnownError	=	-30777	#kURLPropertyNotYetKnownError
kURLAuthenticationError	=	-30776	#kURLAuthenticationError
kURLServerBusyError	=	-30775	#kURLServerBusyError
kURLUnsupportedSchemeError	=	-30774	#kURLUnsupportedSchemeError
kURLInvalidURLError	=	-30773	#kURLInvalidURLError
kURLDestinationExistsError	=	-30772	#kURLDestinationExistsError
kURLProgressAlreadyDisplayedError	=	-30771	#kURLProgressAlreadyDisplayedError
[...1813 lines suppressed...]
ENETRESET	=	52	#Network dropped connection on reset
ECONNABORTED	=	53	#Software caused connection abort
ECONNRESET	=	54	#Connection reset by peer
ENOBUFS	=	55	#No buffer space available
EISCONN	=	56	#Socket is already connected
ENOTCONN	=	57	#Socket is not connected
ESHUTDOWN	=	58	#Can't send after socket shutdown
ETOOMANYREFS	=	59	#Too many references: can't splice
ETIMEDOUT	=	60	#Operation timed out
ECONNREFUSED	=	61	#Connection refused
ELOOP	=	62	#Too many levels of symbolic links
ENAMETOOLONG	=	63	#File name too long
EHOSTDOWN	=	64	#Host is down
EHOSTUNREACH	=	65	#No route to host
ENOTEMPTY	=	66	#Directory not empty
ELOOK	=	67	#Internal mapping for kOTLookErr, don't return to client
ENOLCK	=	77	#No locks available
ENOSYS	=	78	#Function not implemented
EILSEQ	=	88	#Wide character encoding error
EUNKNOWN	=	99	#Unknown error

--- NEW FILE: macfs.py ---
"""macfs - Pure Python module designed to be backward compatible with
macfs and MACFS.
"""
import sys
import struct
import Carbon.Res
import Carbon.File
import Nav

# First step: ensure we also emulate the MACFS module, which contained
# all the constants

sys.modules['MACFS'] = sys.modules[__name__]

# Import all those constants
from Carbon.Files import *
from Carbon.Folders import *

# For some obscure historical reason these are here too:
READ = 1
WRITE = 2
smAllScripts = -3

# The old name of the error object:
error = Carbon.File.Error

#
# The various objects macfs used to export. We override them here, because some
# of the method names are subtly different.
#
class FSSpec(Carbon.File.FSSpec):
	def as_fsref(self):
		return FSRef(self)
		
	def NewAlias(self, src=None):
		return Alias(Carbon.File.NewAlias(src, self))
		
	def GetCreatorType(self):
		finfo = self.FSpGetFInfo()
		return finfo.Creator, finfo.Type
		
	def SetCreatorType(self, ctor, tp):
		finfo = self.FSpGetFInfo()
		finfo.Creator = ctor
		finfo.Type = tp
		self.FSpSetFInfo(finfo)
		
	def GetFInfo(self):
		return self.FSpGetFInfo()
		
	def SetFInfo(self, info):
		return self.FSpSetFInfo(info)
		
	def GetDates(self):
		import os
		statb = os.stat(self.as_pathname())
		return statb.st_ctime, statb.st_mtime, 0
	
	def SetDates(self, *dates):
		print "FSSpec.SetDates no longer implemented"
	
class FSRef(Carbon.File.FSRef):
	def as_fsspec(self):
		return FSSpec(self)
	
class Alias(Carbon.File.Alias):

	def GetInfo(self, index):
		return self.GetAliasInfo(index)
		
	def Update(self, *args):
		print "Alias.Update not yet implemented"
		
	def Resolve(self, src=None):
		fss, changed = self.ResolveAlias(src)
		return FSSpec(fss), changed
		
from Carbon.File import FInfo

# Backward-compatible type names:
FSSpecType = FSSpec
FSRefType = FSRef
AliasType = Alias
FInfoType = FInfo

# Global functions:
def ResolveAliasFile(fss, chain=1):
	fss, isdir, isalias = Carbon.File.ResolveAliasFile(fss, chain)
	return FSSpec(fss), isdir, isalias
	
def RawFSSpec(data):
	return FSSpec(rawdata=data)
	
def RawAlias(data):
	return Alias(rawdata=data)
	
def FindApplication(*args):
	raise NotImplementedError, "FindApplication no longer implemented"
	
def NewAliasMinimalFromFullPath(path):
	return Alias(Carbon.File.NewAliasMinimalFromFullPath(path, '', ''))
	
# Another global function:
from Carbon.Folder import FindFolder

#
# Finally the old Standard File routine emulators.
#

_movablemodal = 0
_curfolder = None

def _mktypelist(typelist):
	# Workaround for OSX typeless files:
	if 'TEXT' in typelist and not '\0\0\0\0' in typelist:
		typelist = typelist + ('\0\0\0\0',)
	if not typelist:
		return None
	data = 'Pyth' + struct.pack("hh", 0, len(typelist))
	for type in typelist:
		data = data+type
	return Carbon.Res.Handle(data)
	
def StandardGetFile(*typelist):
	"""Ask for an input file, optionally specifying 4-char file types that are
	allowable"""
	return apply(PromptGetFile, (None,)+typelist)
	
def PromptGetFile(prompt, *typelist):
	"""Ask for an input file giving the user a prompt message. Optionally you can
	specifying 4-char file types that are allowable"""
	args = {}
	flags = 0x56
	typehandle = _mktypelist(typelist)
	if typehandle:
		args['typeList'] = typehandle
	else:
		flags = flags | 0x01
	if prompt:
		args['message'] = prompt
	args['preferenceKey'] = 'PyMC'
	if _movablemodal:
		args['eventProc'] = None
	args['dialogOptionFlags'] = flags
	_handleSetFolder(args)
	try:
		rr = Nav.NavChooseFile(args)
		good = 1
	except Nav.error, arg:
		if arg[0] != -128: # userCancelledErr
			raise Nav.error, arg
		good = 0
		fss = None
	else:
		if rr.selection:
			fss = FSSpec(rr.selection[0])
		else:
			fss = None
			good = 0
##	if typehandle:
##		typehandle.DisposeHandle()
	return fss, good

def StandardPutFile(prompt, default=None):
	"""Ask the user for an output file, with a prompt. Optionally you cn supply a
	default output filename"""
	args = {}
	flags = 0x07
	if prompt:
		args['message'] = prompt
	args['preferenceKey'] = 'PyMC'
	if _movablemodal:
		args['eventProc'] = None
	if default:
		args['savedFileName'] = default
	args['dialogOptionFlags'] = flags
	_handleSetFolder(args)
	try:
		rr = Nav.NavPutFile(args)
		good = 1
	except Nav.error, arg:
		if arg[0] != -128: # userCancelledErr
			raise Nav.error, arg
		good = 0
		fss = None
	else:
		fss = FSSpec(rr.selection[0])
	return fss, good
	
def SetFolder(folder):
	global _curfolder
	if _curfolder:
		rv = _curfolder
	else:
		rv = None
	_curfolder = FSSpec(folder)
	return rv
	
def _handleSetFolder(args):
	global _curfolder
	if not _curfolder:
		return
	import aepack
	fss = _curfolder
	aedesc = aepack.pack(fss)
	args['defaultLocation'] = aedesc
	_curfolder = None
	
def GetDirectory(prompt=None):
	"""Ask the user to select a folder. Optionally you can give a prompt."""
	args = {}
	flags = 0x17
	if prompt:
		args['message'] = prompt
	args['preferenceKey'] = 'PyMC'
	if _movablemodal:
		args['eventProc'] = None
	args['dialogOptionFlags'] = flags
	_handleSetFolder(args)
	try:
		rr = Nav.NavChooseFolder(args)
		good = 1
	except Nav.error, arg:
		if arg[0] != -128: # userCancelledErr
			raise Nav.error, arg
		good = 0
		fss = None
	else:
		fss = FSSpec(rr.selection[0])
	return fss, good
	

--- NEW FILE: macostools.py ---
"""macostools - Various utility functions for MacOS.

mkalias(src, dst) - Create a finder alias 'dst' pointing to 'src'
copy(src, dst) - Full copy of 'src' to 'dst'
"""

import macfs
from Carbon import Res
import os
from MACFS import *
import MacOS
import time
try:
	openrf = MacOS.openrf
except AttributeError:
	# Backward compatability
	openrf = open

Error = 'macostools.Error'

BUFSIZ=0x80000		# Copy in 0.5Mb chunks

#
# Not guaranteed to be correct or stay correct (Apple doesn't tell you
# how to do this), but it seems to work.
#
def mkalias(src, dst, relative=None):
	"""Create a finder alias"""
	srcfss = macfs.FSSpec(src)
	# The next line will fail under unix-Python if the destination
	# doesn't exist yet. We should change this code to be fsref-based.
	dstfss = macfs.FSSpec(dst)
	if relative:
		relativefss = macfs.FSSpec(relative)
		# ik mag er geen None in stoppen :-(
		alias = srcfss.NewAlias(relativefss)
	else:
		alias = srcfss.NewAlias()
	
	if os.path.isdir(src):
		cr, tp = 'MACS', 'fdrp'
	else:
		cr, tp = srcfss.GetCreatorType()
	
	Res.FSpCreateResFile(dstfss, cr, tp, -1)
	h = Res.FSpOpenResFile(dstfss, 3)
	resource = Res.Resource(alias.data)
	resource.AddResource('alis', 0, '')
	Res.CloseResFile(h)
	
	dstfinfo = dstfss.GetFInfo()
	dstfinfo.Flags = dstfinfo.Flags|0x8000    # Alias flag
	dstfss.SetFInfo(dstfinfo)
	
def mkdirs(dst):
	"""Make directories leading to 'dst' if they don't exist yet"""
	if dst == '' or os.path.exists(dst):
		return
	head, tail = os.path.split(dst)
	if os.sep == ':' and not ':' in head:
		head = head + ':'
	mkdirs(head)
	os.mkdir(dst, 0777)
	
def touched(dst):
	"""Tell the finder a file has changed"""
	file_fss = macfs.FSSpec(dst)
	vRefNum, dirID, name = file_fss.as_tuple()
	dir_fss = macfs.FSSpec((vRefNum, dirID, ''))
	crdate, moddate, bkdate = dir_fss.GetDates()
	now = time.time()
	if now == moddate:
		now = now + 1
	try:
		dir_fss.SetDates(crdate, now, bkdate)
	except macfs.error:
		pass
	
def touched_ae(dst):
	"""Tell the finder a file has changed"""
	import Finder
	f = Finder.Finder()
	file_fss = macfs.FSSpec(dst)
	vRefNum, dirID, name = file_fss.as_tuple()
	dir_fss = macfs.FSSpec((vRefNum, dirID, ''))
	f.update(dir_fss)
	
def copy(src, dst, createpath=0, copydates=1, forcetype=None):
	"""Copy a file, including finder info, resource fork, etc"""
	if hasattr(src, 'as_pathname'):
		src = src.as_pathname()
	if hasattr(dst, 'as_pathname'):
		dst = dst.as_pathname()
	if createpath:
		mkdirs(os.path.split(dst)[0])
	
	ifp = open(src, 'rb')
	ofp = open(dst, 'wb')
	d = ifp.read(BUFSIZ)
	while d:
		ofp.write(d)
		d = ifp.read(BUFSIZ)
	ifp.close()
	ofp.close()
	
	ifp = openrf(src, '*rb')
	ofp = openrf(dst, '*wb')
	d = ifp.read(BUFSIZ)
	while d:
		ofp.write(d)
		d = ifp.read(BUFSIZ)
	ifp.close()
	ofp.close()
	
	srcfss = macfs.FSSpec(src)
	dstfss = macfs.FSSpec(dst)
	sf = srcfss.GetFInfo()
	df = dstfss.GetFInfo()
	df.Creator, df.Type = sf.Creator, sf.Type
	if forcetype != None:
		df.Type = forcetype
	df.Flags = (sf.Flags & (kIsStationary|kNameLocked|kHasBundle|kIsInvisible|kIsAlias))
	dstfss.SetFInfo(df)
	if copydates:
		crdate, mddate, bkdate = srcfss.GetDates()
		dstfss.SetDates(crdate, mddate, bkdate)
	touched(dstfss)
	
def copytree(src, dst, copydates=1):
	"""Copy a complete file tree to a new destination"""
	if os.path.isdir(src):
		mkdirs(dst)
		files = os.listdir(src)
		for f in files:
			copytree(os.path.join(src, f), os.path.join(dst, f), copydates)
	else:
		copy(src, dst, 1, copydates)

--- NEW FILE: macresource.py ---
"""macresource - Locate and open the resources needed for a script."""

from Carbon import Res
import os
import sys

class ArgumentError(TypeError): pass
class ResourceFileNotFoundError(ImportError): pass

def need(restype, resid, filename=None, modname=None):
	"""Open a resource file, if needed. restype and resid
	are required parameters, and identify the resource for which to test. If it
	is available we are done. If it is not available we look for a file filename
	(default: modname with .rsrc appended) either in the same folder as
	where modname was loaded from, or otherwise across sys.path.
	
	Returns the refno of the resource file opened (or None)"""

	if modname is None and filename is None:
		raise ArgumentError, "Either filename or modname argument (or both) must be given"
	
	if type(resid) is type(1):
		try:
			h = Res.GetResource(restype, resid)
		except Res.Error:
			pass
		else:
			return None
	else:
		try:
			h = Res.GetNamedResource(restype, resid)
		except Res.Error:
			pass
		else:
			return None
			
	# Construct a filename if we don't have one
	if not filename:
		if '.' in modname:
			filename = modname.split('.')[-1] + '.rsrc'
		else:
			filename = modname + '.rsrc'
	
	# Now create a list of folders to search
	searchdirs = []
	if modname == '__main__':
		# If we're main we look in the current directory
		searchdirs = [os.curdir]
	if sys.modules.has_key(modname):
		mod = sys.modules[modname]
		if hasattr(mod, '__file__'):
			searchdirs = [os.path.split(mod.__file__)[0]]
	if not searchdirs:
		searchdirs = sys.path
	
	# And look for the file
	for dir in searchdirs:
		pathname = os.path.join(dir, filename)
		if os.path.exists(pathname):
			break
	else:
		raise ResourceFileNotFoundError, filename
	
	refno = open_pathname(pathname)
	
	# And check that the resource exists now
	if type(resid) is type(1):
		h = Res.GetResource(restype, resid)
	else:
		h = Res.GetNamedResource(restype, resid)
	return refno
	
def open_pathname(pathname, verbose=0):
	"""Open a resource file given by pathname, possibly decoding an
	AppleSingle file"""
	try:
		refno = Res.FSpOpenResFile(pathname, 1)
	except Res.Error, arg:
		if arg[0] in (-37, -39):
			# No resource fork. We may be on OSX, and this may be either
			# a data-fork based resource file or a AppleSingle file
			# from the CVS repository.
			try:
				refno = Res.FSOpenResourceFile(pathname, u'', 1)
			except Res.Error, arg:
				if arg[0] != -199:
					# -199 is "bad resource map"
					raise
			else:
				return refno
			# Finally try decoding an AppleSingle file
			pathname = _decode(pathname, verbose=verbose)
			refno = Res.FSOpenResourceFile(pathname, u'', 1)
		else:
			raise
	return refno
	
def open_error_resource():
	"""Open the resource file containing the error code to error message
	mapping."""
	need('Estr', 1, filename="errors.rsrc", modname=__name__)
	
def _decode(pathname, verbose=0):
	# Decode an AppleSingle resource file, return the new pathname.
	newpathname = pathname + '.df.rsrc'
	if os.path.exists(newpathname) and \
			os.stat(newpathname).st_mtime >= os.stat(pathname).st_mtime:
		return newpathname
	if verbose:
		print 'Decoding', pathname
	import applesingle
	applesingle.decode(pathname, newpathname, resonly=1)
	return newpathname
	
	
--- NEW FILE: plistlib.py ---
"""plistlib.py -- a tool to generate and parse MacOSX .plist files.

The main class in this module is Plist. It takes a set of arbitrary
keyword arguments, which will be the top level elements of the plist
dictionary. After instantiation you can add more elements by assigning
new attributes to the Plist instance.

To write out a plist file, call the write() method of the Plist
instance with a filename or a file object.

To parse a plist from a file, use the Plist.fromFile(pathOrFile)
classmethod, with a file name or a file object as the only argument.
(Warning: you need pyexpat installed for this to work, ie. it doesn't
work with a vanilla Python 2.2 as shipped with MacOS X.2.)

Values can be strings, integers, floats, booleans, tuples, lists,
dictionaries, Data or Date objects. String values (including dictionary
keys) may be unicode strings -- they will be written out as UTF-8.

For convenience, this module exports a class named Dict(), which
allows you to easily construct (nested) dicts using keyword arguments.
But regular dicts work, too.

To support Boolean values in plists with Python < 2.3, "bool", "True"
and "False" are exported. Use these symbols from this module if you
want to be compatible with Python 2.2.x (strongly recommended).

The <data> plist type is supported through the Data class. This is a
thin wrapper around a Python string.

The <date> plist data has (limited) support through the Date class.
(Warning: Dates are only supported if the PyXML package is installed.)

Generate Plist example:

	pl = Plist(
		aString="Doodah",
		aList=["A", "B", 12, 32.1, [1, 2, 3]],
		aFloat = 0.1,
		anInt = 728,
		aDict=Dict(
			anotherString="<hello & hi there!>",
			aUnicodeValue=u'M\xe4ssig, Ma\xdf',
			aTrueValue=True,
			aFalseValue=False,
		),
		someData = Data("<binary gunk>"),
		someMoreData = Data("<lots of binary gunk>" * 10),
		aDate = Date(time.mktime(time.gmtime())),
	)
	# unicode keys are possible, but a little awkward to use:
	pl[u'\xc5benraa'] = "That was a unicode key."
	pl.write(fileName)

Parse Plist example:

	pl = Plist.fromFile(pathOrFile)
	print pl.aKey


"""

# written by Just van Rossum (just@letterror.com), 2002-11-19


__all__ = ["Plist", "Data", "Date", "Dict", "False", "True", "bool"]


INDENT = "\t"


class DumbXMLWriter:

	def __init__(self, file):
		self.file = file
		self.stack = []
		self.indentLevel = 0

	def beginElement(self, element):
		self.stack.append(element)
		self.writeln("<%s>" % element)
		self.indentLevel += 1

	def endElement(self, element):
		assert self.indentLevel > 0
		assert self.stack.pop() == element
		self.indentLevel -= 1
		self.writeln("</%s>" % element)

	def simpleElement(self, element, value=None):
		if value:
			value = _encode(value)
			self.writeln("<%s>%s</%s>" % (element, value, element))
		else:
			self.writeln("<%s/>" % element)

	def writeln(self, line):
		if line:
			self.file.write(self.indentLevel * INDENT + line + "\n")
		else:
			self.file.write("\n")


def _encode(text):
	text = text.replace("&", "&amp;")
	text = text.replace("<", "&lt;")
	return text.encode("utf-8")


PLISTHEADER = """\
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
"""

class PlistWriter(DumbXMLWriter):

	def __init__(self, file):
		file.write(PLISTHEADER)
		DumbXMLWriter.__init__(self, file)

	def writeValue(self, value):
		if isinstance(value, (str, unicode)):
			self.simpleElement("string", value)
		elif isinstance(value, bool):
			# must switch for bool before int, as bool is a
			# subclass of int...
			if value:
				self.simpleElement("true")
			else:
				self.simpleElement("false")
		elif isinstance(value, int):
			self.simpleElement("integer", str(value))
		elif isinstance(value, float):
			# should perhaps use repr() for better precision?
			self.simpleElement("real", str(value))
		elif isinstance(value, (dict, Dict)):
			self.writeDict(value)
		elif isinstance(value, Data):
			self.writeData(value)
		elif isinstance(value, Date):
			self.simpleElement("date", value.toString())
		elif isinstance(value, (tuple, list)):
			self.writeArray(value)
		else:
			assert 0, "unsuported type: %s" % type(value)

	def writeData(self, data):
		self.beginElement("data")
		for line in data.asBase64().split("\n"):
			if line:
				self.writeln(line)
		self.endElement("data")

	def writeDict(self, d):
		self.beginElement("dict")
		items = d.items()
		items.sort()
		for key, value in items:
			assert isinstance(key, (str, unicode)), "keys must be strings"
			self.simpleElement("key", key)
			self.writeValue(value)
		self.endElement("dict")

	def writeArray(self, array):
		self.beginElement("array")
		for value in array:
			self.writeValue(value)
		self.endElement("array")


class Dict:

	"""Dict wrapper for convenient access of values through attributes."""

	def __init__(self, **kwargs):
		self.__dict__.update(kwargs)

	def __cmp__(self, other):
		if isinstance(other, self.__class__):
			return cmp(self.__dict__, other.__dict__)
		elif isinstance(other, dict):
			return cmp(self.__dict__, other)
		else:
			return cmp(id(self), id(other))

	def __str__(self):
		return "%s(**%s)" % (self.__class__.__name__, self.__dict__)
	__repr__ = __str__

	def copy(self):
		return self.__class__(**self.__dict__)

	def __getattr__(self, attr):
		"""Delegate everything else to the dict object."""
		return getattr(self.__dict__, attr)


class Plist(Dict):

	"""The main Plist object. Basically a dict (the toplevel object
	of a plist is a dict) with two additional methods to read from
	and write to files.
	"""

	def fromFile(cls, pathOrFile):
		didOpen = 0
		if not hasattr(pathOrFile, "write"):
			pathOrFile = open(pathOrFile)
			didOpen = 1
		p = PlistParser()
		plist = p.parse(pathOrFile)
		if didOpen:
			pathOrFile.close()
		return plist
	fromFile = classmethod(fromFile)

	def write(self, pathOrFile):
		if not hasattr(pathOrFile, "write"):
			pathOrFile = open(pathOrFile, "w")
			didOpen = 1
		else:
			didOpen = 0

		writer = PlistWriter(pathOrFile)
		writer.writeln("<plist version=\"1.0\">")
		writer.writeDict(self.__dict__)
		writer.writeln("</plist>")

		if didOpen:
			pathOrFile.close()


class Data:

	"""Wrapper for binary data."""

	def __init__(self, data):
		self.data = data

	def fromBase64(cls, data):
		import base64
		return cls(base64.decodestring(data))
	fromBase64 = classmethod(fromBase64)

	def asBase64(self):
		import base64
		return base64.encodestring(self.data)

	def __cmp__(self, other):
		if isinstance(other, self.__class__):
			return cmp(self.data, other.data)
		elif isinstance(other, str):
			return cmp(self.data, other)
		else:
			return cmp(id(self), id(other))

	def __repr__(self):
		return "%s(%s)" % (self.__class__.__name__, repr(self.data))


class Date:

	"""Primitive date wrapper, uses time floats internally, is agnostic
	about time zones.
	"""

	def __init__(self, date):
		if isinstance(date, str):
			from xml.utils.iso8601 import parse
			date = parse(date)
		self.date = date

	def toString(self):
		from xml.utils.iso8601 import tostring
		return tostring(self.date)

	def __cmp__(self, other):
		if isinstance(other, self.__class__):
			return cmp(self.date, other.date)
		elif isinstance(other, (int, float)):
			return cmp(self.date, other)
		else:
			return cmp(id(self), id(other))

	def __repr__(self):
		return "%s(%s)" % (self.__class__.__name__, repr(self.toString()))


class PlistParser:

	def __init__(self):
		self.stack = []
		self.currentKey = None
		self.root = None

	def parse(self, file):
		from xml.parsers.expat import ParserCreate
		parser = ParserCreate()
		parser.StartElementHandler = self.handleBeginElement
		parser.EndElementHandler = self.handleEndElement
		parser.CharacterDataHandler = self.handleData
		parser.ParseFile(file)
		return self.root

	def handleBeginElement(self, element, attrs):
		self.data = []
		handler = getattr(self, "begin_" + element, None)
		if handler is not None:
			handler(attrs)

	def handleEndElement(self, element):
		handler = getattr(self, "end_" + element, None)
		if handler is not None:
			handler()

	def handleData(self, data):
		self.data.append(data)

	def addObject(self, value):
		if self.currentKey is not None:
			self.stack[-1][self.currentKey] = value
			self.currentKey = None
		elif not self.stack:
			# this is the root object
			assert self.root is value
		else:
			self.stack[-1].append(value)

	def getData(self):
		data = "".join(self.data)
		try:
			data = data.encode("ascii")
		except UnicodeError:
			pass
		self.data = []
		return data

	# element handlers

	def begin_dict(self, attrs):
		if self.root is None:
			self.root = d = Plist()
		else:
			d = Dict()
		self.addObject(d)
		self.stack.append(d)
	def end_dict(self):
		self.stack.pop()

	def end_key(self):
		self.currentKey = self.getData()

	def begin_array(self, attrs):
		a = []
		self.addObject(a)
		self.stack.append(a)
	def end_array(self):
		self.stack.pop()

	def end_true(self):
		self.addObject(True)
	def end_false(self):
		self.addObject(False)
	def end_integer(self):
		self.addObject(int(self.getData()))
	def end_real(self):
		self.addObject(float(self.getData()))
	def end_string(self):
		self.addObject(self.getData())
	def end_data(self):
		self.addObject(Data.fromBase64(self.getData()))
	def end_date(self):
		self.addObject(Date(self.getData()))


# cruft to support booleans in Python <= 2.3
import sys
if sys.version_info[:2] < (2, 3):
	# Python 2.2 and earlier: no booleans
	# Python 2.2.x: booleans are ints
	class bool(int):
		"""Imitation of the Python 2.3 bool object."""
		def __new__(cls, value):
			return int.__new__(cls, not not value)
		def __repr__(self):
			if self:
				return "True"
			else:
				return "False"
	True = bool(1)
	False = bool(0)
else:
	# Bind the boolean builtins to local names
	True = True
	False = False
	bool = bool


if __name__ == "__main__":
	from StringIO import StringIO
	import time
	if len(sys.argv) == 1:
		pl = Plist(
			aString="Doodah",
			aList=["A", "B", 12, 32.1, [1, 2, 3]],
			aFloat = 0.1,
			anInt = 728,
			aDict=Dict(
				anotherString="<hello & hi there!>",
				aUnicodeValue=u'M\xe4ssig, Ma\xdf',
				aTrueValue=True,
				aFalseValue=False,
			),
			someData = Data("<binary gunk>"),
			someMoreData = Data("<lots of binary gunk>" * 10),
			aDate = Date(time.mktime(time.gmtime())),
		)
	elif len(sys.argv) == 2:
		pl = Plist.fromFile(sys.argv[1])
	else:
		print "Too many arguments: at most 1 plist file can be given."
		sys.exit(1)

	# unicode keys are possible, but a little awkward to use:
	pl[u'\xc5benraa'] = "That was a unicode key."
	f = StringIO()
	pl.write(f)
	xml = f.getvalue()
	print xml
	f.seek(0)
	pl2 = Plist.fromFile(f)
	assert pl == pl2
	f = StringIO()
	pl2.write(f)
	assert xml == f.getvalue()
	#print repr(pl2)

--- NEW FILE: videoreader.py ---
# Video file reader, using QuickTime
#
# This module was quickly ripped out of another software package, so there is a good
# chance that it does not work as-is and it needs some hacking.
#
# Jack Jansen, August 2000
#
import sys
from Carbon import Qt
from Carbon import QuickTime
from Carbon import Qd
from Carbon import Qdoffs
from Carbon import QDOffscreen
from Carbon import Res
import MediaDescr
import imgformat
import os
# import audio.format
import macfs

class VideoFormat:
	def __init__(self, name, descr, width, height, format):
		self.__name = name
		self.__descr = descr
		self.__width = width
		self.__height = height
		self.__format = format
		
	def getname(self):
		return self.__name
		
	def getdescr(self):
		return self.__descr
		
	def getsize(self):
		return self.__width, self.__height
		
	def getformat(self):
		return self.__format
		
class _Reader:
	def __init__(self, path):
		fsspec = macfs.FSSpec(path)
		fd = Qt.OpenMovieFile(fsspec, 0)
		self.movie, d1, d2 = Qt.NewMovieFromFile(fd, 0, 0)
		self.movietimescale = self.movie.GetMovieTimeScale()
		try:
			self.audiotrack = self.movie.GetMovieIndTrackType(1,
				QuickTime.AudioMediaCharacteristic, QuickTime.movieTrackCharacteristic)
			self.audiomedia = self.audiotrack.GetTrackMedia()
		except Qt.Error:
			self.audiotrack = self.audiomedia = None
			self.audiodescr = {}
		else:
			handle = Res.Handle('')
			n = self.audiomedia.GetMediaSampleDescriptionCount()
			self.audiomedia.GetMediaSampleDescription(1, handle)
			self.audiodescr = MediaDescr.SoundDescription.decode(handle.data)
			self.audiotimescale = self.audiomedia.GetMediaTimeScale()
			del handle
	
		try:	
			self.videotrack = self.movie.GetMovieIndTrackType(1,
				QuickTime.VisualMediaCharacteristic, QuickTime.movieTrackCharacteristic)
			self.videomedia = self.videotrack.GetTrackMedia()
		except Qt.Error:
			self.videotrack = self.videomedia = self.videotimescale = None
		if self.videotrack:
			self.videotimescale = self.videomedia.GetMediaTimeScale()
			x0, y0, x1, y1 = self.movie.GetMovieBox()
			self.videodescr = {'width':(x1-x0), 'height':(y1-y0)}
			self._initgworld()
		self.videocurtime = None
		self.audiocurtime = None

		
	def __del__(self):
		self.audiomedia = None
		self.audiotrack = None
		self.videomedia = None
		self.videotrack = None
		self.movie = None
		
	def _initgworld(self):
		old_port, old_dev = Qdoffs.GetGWorld()
		try:
			movie_w = self.videodescr['width']
			movie_h = self.videodescr['height']
			movie_rect = (0, 0, movie_w, movie_h)
			self.gworld = Qdoffs.NewGWorld(32,  movie_rect, None, None, QDOffscreen.keepLocal)
			self.pixmap = self.gworld.GetGWorldPixMap()
			Qdoffs.LockPixels(self.pixmap)
			Qdoffs.SetGWorld(self.gworld.as_GrafPtr(), None)
			Qd.EraseRect(movie_rect)
			self.movie.SetMovieGWorld(self.gworld.as_GrafPtr(), None)
			self.movie.SetMovieBox(movie_rect)
			self.movie.SetMovieActive(1)
			self.movie.MoviesTask(0)
			self.movie.SetMoviePlayHints(QuickTime.hintsHighQuality, QuickTime.hintsHighQuality)
			# XXXX framerate
		finally:
			Qdoffs.SetGWorld(old_port, old_dev)
		
	def _gettrackduration_ms(self, track):
		tracktime = track.GetTrackDuration()
		return self._movietime_to_ms(tracktime)
		
	def _movietime_to_ms(self, time):
		value, d1, d2 = Qt.ConvertTimeScale((time, self.movietimescale, None), 1000)
		return value
		
	def _videotime_to_ms(self, time):
		value, d1, d2 = Qt.ConvertTimeScale((time, self.videotimescale, None), 1000)
		return value
		
	def _audiotime_to_ms(self, time):
		value, d1, d2 = Qt.ConvertTimeScale((time, self.audiotimescale, None), 1000)
		return value
		
	def _videotime_to_movietime(self, time):
		value, d1, d2 = Qt.ConvertTimeScale((time, self.videotimescale, None),
				self.movietimescale)
		return value
		
	def HasAudio(self):
		return not self.audiotrack is None
		
	def HasVideo(self):
		return not self.videotrack is None
		
	def GetAudioDuration(self):
		if not self.audiotrack:
			return 0
		return self._gettrackduration_ms(self.audiotrack)

	def GetVideoDuration(self):
		if not self.videotrack:
			return 0
		return self._gettrackduration_ms(self.videotrack)
		
	def GetAudioFormat(self):
		bps = self.audiodescr['sampleSize']
		nch = self.audiodescr['numChannels']
		if nch == 1:
			channels = ['mono']
		elif nch == 2:
			channels = ['left', 'right']
		else:
			channels = map(lambda x: str(x+1), range(nch))
		if bps % 8:
			# Funny bits-per sample. We pretend not to understand
			blocksize = 0
			fpb = 0
		else:
			# QuickTime is easy (for as far as we support it): samples are always a whole
			# number of bytes, so frames are nchannels*samplesize, and there's one frame per block.
			blocksize = (bps/8)*nch
			fpb = 1
		if self.audiodescr['dataFormat'] == 'raw ':
			encoding = 'linear-excess'
		elif self.audiodescr['dataFormat'] == 'twos':
			encoding = 'linear-signed'
		else:
			encoding = 'quicktime-coding-%s'%self.audiodescr['dataFormat']
##		return audio.format.AudioFormatLinear('quicktime_audio', 'QuickTime Audio Format', 
##			channels, encoding, blocksize=blocksize, fpb=fpb, bps=bps)
		return channels, encoding, blocksize, fpb, bps
			
	def GetAudioFrameRate(self):
		return int(self.audiodescr['sampleRate'])
		
	def GetVideoFormat(self):
		width = self.videodescr['width']
		height = self.videodescr['height']
		return VideoFormat('dummy_format', 'Dummy Video Format', width, height, imgformat.macrgb)
		
	def GetVideoFrameRate(self):
		tv = self.videocurtime
		if tv == None:
			tv = 0
		flags = QuickTime.nextTimeStep|QuickTime.nextTimeEdgeOK
		tv, dur = self.videomedia.GetMediaNextInterestingTime(flags, tv, 1.0)
		dur = self._videotime_to_ms(dur)
		return int((1000.0/dur)+0.5)
		
	def ReadAudio(self, nframes, time=None):
		if not time is None:
			self.audiocurtime = time
		flags = QuickTime.nextTimeStep|QuickTime.nextTimeEdgeOK
		if self.audiocurtime == None:
			self.audiocurtime = 0
		tv = self.audiomedia.GetMediaNextInterestingTimeOnly(flags, self.audiocurtime, 1.0)
		if tv < 0 or (self.audiocurtime and tv < self.audiocurtime):
			return self._audiotime_to_ms(self.audiocurtime), None
		h = Res.Handle('')
		desc_h = Res.Handle('')
		size, actualtime, sampleduration, desc_index, actualcount, flags = \
			self.audiomedia.GetMediaSample(h, 0, tv, desc_h, nframes)
		self.audiocurtime = actualtime + actualcount*sampleduration
		return self._audiotime_to_ms(actualtime), h.data
		
	def ReadVideo(self, time=None):
		if not time is None:
			self.videocurtime = time
		flags = QuickTime.nextTimeStep
		if self.videocurtime == None:
			flags = flags | QuickTime.nextTimeEdgeOK
			self.videocurtime = 0
		tv = self.videomedia.GetMediaNextInterestingTimeOnly(flags, self.videocurtime, 1.0)
		if tv < 0 or (self.videocurtime and tv <= self.videocurtime):
			return self._videotime_to_ms(self.videocurtime), None
		self.videocurtime = tv
		moviecurtime = self._videotime_to_movietime(self.videocurtime)
		self.movie.SetMovieTimeValue(moviecurtime)
		self.movie.MoviesTask(0)
		return self._videotime_to_ms(self.videocurtime), self._getpixmapcontent()
		
	def _getpixmapcontent(self):
		"""Shuffle the offscreen PixMap data, because it may have funny stride values"""
		rowbytes = Qdoffs.GetPixRowBytes(self.pixmap)
		width = self.videodescr['width']
		height = self.videodescr['height']
		start = 0
		rv = ''
		for i in range(height):
			nextline = Qdoffs.GetPixMapBytes(self.pixmap, start, width*4)
			start = start + rowbytes
			rv = rv + nextline
		return rv

def reader(url):
	try:
		rdr = _Reader(url)
	except IOError:
		return None
	return rdr

def _test():
	import img
	import MacOS
	Qt.EnterMovies()
	fss, ok = macfs.PromptGetFile('Video to convert')
	if not ok: sys.exit(0)
	path = fss.as_pathname()
	rdr = reader(path)
	if not rdr:
		sys.exit(1)
	dstfss, ok = macfs.StandardPutFile('Name for output folder')
	if not ok: sys.exit(0)
	dstdir = dstfss.as_pathname()
	num = 0
	os.mkdir(dstdir)
	videofmt = rdr.GetVideoFormat()
	imgfmt = videofmt.getformat()
	imgw, imgh = videofmt.getsize()
	timestamp, data = rdr.ReadVideo()
	while data:
		fname = 'frame%04.4d.jpg'%num
		num = num+1
		pname = os.path.join(dstdir, fname)
		print 'Writing', fname, imgw, imgh, len(data)
		wrt = img.writer(imgfmt, pname)
		wrt.width = imgw
		wrt.height = imgh
		wrt.write(data)
		timestamp, data = rdr.ReadVideo()
		MacOS.SetCreatorAndType(pname, 'ogle', 'JPEG')
		if num > 20: 
			print 'stopping at 20 frames so your disk does not fill up:-)'
			break
	print 'Total frames:', num
		
if __name__ == '__main__':
	_test()
	sys.exit(1)