Initial revision
diff --git a/Mac/Contrib/PyIDE-src/IDELib/PythonIDEMain.py b/Mac/Contrib/PyIDE-src/IDELib/PythonIDEMain.py
new file mode 100644
index 0000000..4b14bcd
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/PythonIDEMain.py
@@ -0,0 +1,224 @@
+# copyright 1997 Just van Rossum, Letterror. just@knoware.nl
+
+import Splash
+
+import FrameWork
+import WFrameWorkPatch
+import Win
+import W
+import os
+import macfs
+
+class PythonIDE(WFrameWorkPatch.Application):
+	
+	def __init__(self):
+		self.preffilepath = ":Python:PythonIDE preferences"
+		WFrameWorkPatch.Application.__init__(self, 'Pyth')
+		import AE, AppleEvents
+		
+		AE.AEInstallEventHandler(AppleEvents.kCoreEventClass, AppleEvents.kAEOpenApplication, 
+				self.ignoreevent)
+		AE.AEInstallEventHandler(AppleEvents.kCoreEventClass, AppleEvents.kAEPrintDocuments, 
+				self.ignoreevent)
+		AE.AEInstallEventHandler(AppleEvents.kCoreEventClass, AppleEvents.kAEOpenDocuments, 
+				self.opendocsevent)
+		AE.AEInstallEventHandler(AppleEvents.kCoreEventClass, AppleEvents.kAEQuitApplication, 
+				self.quitevent)
+		import PyConsole, PyEdit
+		Splash.wait()
+		PyConsole.installconsole()
+		PyConsole.installoutput()
+		import sys
+		for path in sys.argv[1:]:
+			self.opendoc(path)
+		self.mainloop()
+	
+	def makeusermenus(self):
+		m = WFrameWorkPatch.Menu(self.menubar, "File")
+		newitem = FrameWork.MenuItem(m, "New", "N", 'new')
+		openitem = FrameWork.MenuItem(m, "OpenŠ", "O", 'open')
+		FrameWork.Separator(m)
+		closeitem = FrameWork.MenuItem(m, "Close", "W", 'close')
+		saveitem = FrameWork.MenuItem(m, "Save", "S", 'save')
+		saveasitem = FrameWork.MenuItem(m, "Save asŠ", None, 'save_as')
+		FrameWork.Separator(m)
+		quititem = FrameWork.MenuItem(m, "Quit", "Q", 'quit')
+		
+		m = WFrameWorkPatch.Menu(self.menubar, "Edit")
+		undoitem = FrameWork.MenuItem(m, "Undo", 'Z', "undo")
+		FrameWork.Separator(m)
+		cutitem = FrameWork.MenuItem(m, "Cut", 'X', "cut")
+		copyitem = FrameWork.MenuItem(m, "Copy", "C", "copy")
+		pasteitem = FrameWork.MenuItem(m, "Paste", "V", "paste")
+		FrameWork.MenuItem(m, "Clear", None,  "clear")
+		FrameWork.Separator(m)
+		selallitem = FrameWork.MenuItem(m, "Select all", "A", "selectall")
+		sellineitem = FrameWork.MenuItem(m, "Select line", "L", "selectline")
+		FrameWork.Separator(m)
+		finditem = FrameWork.MenuItem(m, "FindŠ", "F", "find")
+		findagainitem = FrameWork.MenuItem(m, "Find again", 'G', "findnext")
+		enterselitem = FrameWork.MenuItem(m, "Enter search string", "E", "entersearchstring")
+		replaceitem = FrameWork.MenuItem(m, "Replace", None, "replace")
+		replacefinditem = FrameWork.MenuItem(m, "Replace & find again", 'T', "replacefind")
+		FrameWork.Separator(m)
+		shiftleftitem = FrameWork.MenuItem(m, "Shift left", "[", "shiftleft")
+		shiftrightitem = FrameWork.MenuItem(m, "Shift right", "]", "shiftright")
+		
+		m = WFrameWorkPatch.Menu(self.menubar, "Python")
+		runitem = FrameWork.MenuItem(m, "Run window", "R", 'run')
+		runselitem = FrameWork.MenuItem(m, "Run selection", None, 'runselection')
+		FrameWork.Separator(m)
+		moditem = FrameWork.MenuItem(m, "Module browserŠ", "M", self.domenu_modulebrowser)
+		FrameWork.Separator(m)
+		mm = FrameWork.SubMenu(m, "Preferences")
+		FrameWork.MenuItem(mm, "Set Scripts folderŠ", None, self.do_setscriptsfolder)
+		
+		self.openwindowsmenu = WFrameWorkPatch.Menu(self.menubar, 'Windows')
+		self.makeopenwindowsmenu()
+		self._menustocheck = [closeitem, saveitem, saveasitem,
+				undoitem, cutitem, copyitem, pasteitem, 
+				selallitem, sellineitem, 
+				finditem, findagainitem, enterselitem, replaceitem, replacefinditem,
+				shiftleftitem, shiftrightitem, 
+				runitem, runselitem]
+		
+		prefs = self.getprefs()
+		if prefs.scriptsfolder:
+			fss, fss_changed = macfs.RawAlias(prefs.scriptsfolder).Resolve()
+			self.scriptsfolder = fss.NewAlias()
+			prefs.scriptsfolder = self.scriptsfolder.data
+			self.scriptsfoldermodtime = fss.GetDates()[1]
+		else:
+			path = os.path.join(os.getcwd(), 'Scripts')
+			if not os.path.exists(path):
+				os.mkdir(path)
+			fss = macfs.FSSpec(path)
+			self.scriptsfolder = fss.NewAlias()
+			self.scriptsfoldermodtime = fss.GetDates()[1]
+		self._scripts = {}
+		self.scriptsmenu = None
+		self.makescriptsmenu()
+	
+	def quitevent(self, theAppleEvent, theReply):
+		import AE
+		AE.AEInteractWithUser(50000000)
+		self._quit()
+	
+	def suspendresume(self, onoff):
+		if onoff:
+			fss, fss_changed = self.scriptsfolder.Resolve()
+			modtime = fss.GetDates()[1]
+			if self.scriptsfoldermodtime <> modtime or fss_changed:
+				self.scriptsfoldermodtime = modtime
+				W.SetCursor('watch')
+				self.makescriptsmenu()
+	
+	def ignoreevent(self, theAppleEvent, theReply):
+		pass
+	
+	def opendocsevent(self, theAppleEvent, theReply):
+		W.SetCursor('watch')
+		import aetools
+		parameters, args = aetools.unpackevent(theAppleEvent)
+		docs = parameters['----']
+		if type(docs) <> type([]):
+			docs = [docs]
+		for doc in docs:
+			fss, a = doc.Resolve()
+			path = fss.as_pathname()
+			self.opendoc(path)
+	
+	def opendoc(self, path):
+		fcreator, ftype = macfs.FSSpec(path).GetCreatorType()
+		if ftype == 'TEXT':
+			self.openscript(path)
+		else:
+			W.Message("Can¹t open file of type '%s'." % ftype)
+	
+	def getabouttext(self):
+		return "About Python IDEŠ"
+	
+	def do_about(self, id, item, window, event):
+		Splash.about()
+	
+	def do_setscriptsfolder(self, *args):
+		fss, ok = macfs.GetDirectory("Select Scripts Folder")
+		if ok:
+			prefs = self.getprefs()
+			alis = fss.NewAlias()
+			prefs.scriptsfolder = alis.data
+			self.scriptsfolder = alis
+			self.makescriptsmenu()
+			prefs.save()
+	
+	def domenu_modulebrowser(self, *args):
+		W.SetCursor('watch')
+		import ModuleBrowser
+		ModuleBrowser.ModuleBrowser()
+	
+	def domenu_open(self, *args):
+		fss, ok = macfs.StandardGetFile("TEXT")
+		if ok:
+			self.openscript(fss.as_pathname())
+	
+	def domenu_new(self, *args):
+		W.SetCursor('watch')
+		import PyEdit
+		return PyEdit.Editor()
+	
+	def makescriptsmenu(self):
+		W.SetCursor('watch')
+		if self._scripts:
+			for id, item in self._scripts.keys():
+				if self.menubar.menus.has_key(id):
+					m = self.menubar.menus[id]
+					m.delete()
+			self._scripts = {}
+		if self.scriptsmenu:
+			if hasattr(self.scriptsmenu, 'id') and self.menubar.menus.has_key(self.scriptsmenu.id):
+				self.scriptsmenu.delete()
+		self.scriptsmenu = FrameWork.Menu(self.menubar, "Scripts")
+		#FrameWork.MenuItem(self.scriptsmenu, "New script", None, self.domenu_new)
+		#self.scriptsmenu.addseparator()
+		fss, fss_changed = self.scriptsfolder.Resolve()
+		self.scriptswalk(fss.as_pathname(), self.scriptsmenu)
+	
+	def makeopenwindowsmenu(self):
+		for i in range(len(self.openwindowsmenu.items)):
+			self.openwindowsmenu.menu.DeleteMenuItem(1)
+			self.openwindowsmenu.items = []
+		windows = []
+		self._openwindows = {}
+		for window in self._windows.keys():
+			title = window.GetWTitle()
+			if not title:
+				title = "<no title>"
+			windows.append(title, window)
+		windows.sort()
+		for title, window in windows:
+			item = FrameWork.MenuItem(self.openwindowsmenu, title, callback = self.domenu_openwindows)
+			self._openwindows[item.item] = window
+		self._openwindowscheckmark = 0
+		self.checkopenwindowsmenu()
+		
+	def domenu_openwindows(self, id, item, window, event):
+		w = self._openwindows[item]
+		w.ShowWindow()
+		w.SelectWindow()
+	
+	def domenu_quit(self):
+		self._quit()
+	
+	def domenu_save(self, *args):
+		print "Save"
+	
+	def _quit(self):
+		import PyConsole, PyEdit
+		PyConsole.console.writeprefs()
+		PyEdit.searchengine.writeprefs()
+		for window in self._windows.values():
+			if window.close() > 0:
+				return
+		self.quitting = 1
+
+PythonIDE()
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Resources/Widgets.rsrc.hqx b/Mac/Contrib/PyIDE-src/IDELib/Resources/Widgets.rsrc.hqx
new file mode 100644
index 0000000..beb56e6
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Resources/Widgets.rsrc.hqx
@@ -0,0 +1,89 @@
+(This file must be converted with BinHex 4.0)
+
+:$&GTC'GPG(-ZFR0bB`"bFh*M8P0&4!%!N!F5HcQ*!*!%!3!!!"%3!!!3%!!!!@X
+*!!!!E'PZC@CTC@aNF`N!!!"6Bh*[E'aLBA*c"`!!!!aAD@4RCA4c,R*cFQ0bEfa
+c)&"bC@Cc1'XJ-LiiBMGi!!"bFh*M8P0&4!%!rj!%!*!5X6"P#!#3"K*lEhKc#!!
+!!'PZCQpdCAKdF`B!!!"#GA4dEfjc!`!!!(*eER-*!!!!FR9ZBR9dG'pZF``!!!"
+bG@jcC@aPBh4TEfjc$!!!!(*eER0PE'*eG(4[ER-%!!!!BQPZC(-%!!!!F(9cD(-
+0!!!!E@9ZG9pREh4[E'PZCA-*!!!!FfKTCR4XC3!!!-3!a!#3"3X!#`!4![m-!2r
+q!!!!5!!!!%J!N!F,!!X!N!8"!!S!N!8,!!X!N!#!"!#3"3X!#`#3#8J!!!")!*!
+&!J!"!!)!N!e(IP3!!!!#!!$rN!B!!Cb3"2rr!!)!N!X,!!X!N!8,!!X!!!#!!!!
+!S!!!!*J!!!#'!!!!NB!!!)4J!!#4J!!!KJ!!!*J!!!#J!!!!J!!!!2m!!!$%!-3
+!N!8,!!X!%3,r$!$rrJ!!!%J!!!")!*!(#`!,!*!&!3!+!*!&#`!,!*!!J!3!N!8
+,!!X!N!P)!!!!5!#3"3)!!3!#!*!04hq$!!!!!J!!rj!'!!'FN!6rr`!#!*!,#`!
+,!*!&#`!,!*!1UUUS!#4%B!!*%B!!!NB!!!#B!!!!)!#3#rm!!!",!%X!N!8,!!X
+4!3%!#J#3"3X!#j!!!!)!N!8,!!X!N!8,!!X!N!8,!!X!!!J!$!!1!!m!$i!2`!q
+!$`!1!!`!#!$r!!!!5`",!*!&#`!,%3%"!!S!N!8,!!Z3!!!#!*!&#`!,!*!&#`!
+,!*!&#`!,!*!)rq"r`$q!(`!1!!3!N!Ar!!!!53"*!*!&#J!+%3%"!!S!N!8+!!U
+3!!!#!*!&#J!+!*!&#J!+!*!&#J!+!!!3!"J!(!!H!"m!(`!H!"`!'!!3!2m!!!"
+*!%N!N!8+!!S4!3%!#J#3"3S!#T!!!!)!N!8+!!S!N!8+!!S!N!8+!!S!N!Mr`(q
+!2`!H!!`!N!Ar!!!#bQ!1!!"-4%9'!)!!N!C"q[rZ6R&1F@!!!!*19J!!,`XQEJ!
+)-#i!'QF1DejA3'G5DPK53'SXB!S[#dkk!'*BMf"),`Xr,J!-2bi!$LmZ!"![,J!
+8(bi!'%kk!&T2l`!5B#J[#cmZ!!`r,J!1,bi!%#mZ!"3I,J!B6VS"d%r[!"*J##m
+,6VS#,&L2*Pp1AL"I6qm!&%l3K'eKD@i!!!"19J!!6Pj1GBT0H8a%48C*EQPd!!!
+!6PEr[NMR(c!q,J!5*Qi!#L4Z!"C#E[r-I!"#E[r+5'lrr+Kd)&)[+!!)U(0#TkM
+B+Km["DKk,`ZSHdKZrqUSQ+LH,`ZSSdKZrpDS05JZrpC`%1#N)#lrfR)3iU!Y32r
+1)LlrhR33j+%Y3Ir5d)63J5e!rmBJ5U!T5Qi!&'F!!,Jf"f!5)&)JD!"3)&!--!!
+0-!"R$&*$-#i!&0"(X%0Zj$!$N!"(28$rc$`Z!"6F4ja$8dCb!G*$28(rbR3%e'X
+!!Mm#0"284$m#U*-J8L"S!&![%$m(2blrc+L&5NC[*(!%d'X!!Mm!-"2340"ZrmJ
+r!+L6)&)JD!"3,a!r,[r+2`DSK8KZrlj)HJ"mU'C)E[qqU*dr+`!#-#lrb0"!d&0
+53$m!U*-r+`!'-#lrb0"!d&053$m!U*%J5U!U5Li!#'FD,`Sr,J!82`F[,J!1,`X
+I,J!)6VS!3Nr[!")[,[rmU(-["DKj,`@Sf8KZrqUSQ8cI$2K1ANjeLNej6%4&4N4
+bBAF!!")3-C!3!%j@rqS[#dKZrrbSG#"Z!"BJ8#mS!!LSFd+RU0JQAbm,U(S[,J!
++U(Y)E[rUU*LSRMmm!$+SR#mZ!!USSLmZrrbSFbm,U(N[#kMC5'lrkULC*Pp1ANj
+eMdej6%4&4NKTCfKXD@GSG!!!6PB!!%jH6R@,6AP-4%9'3fa[Ff8!N!9%!B!DF#C
+)*NS564**D!QB!BJ#3!)J!L!%%!3)#!3)"!J"J"[`2rJrqKrr(rp[rj!%rRrq2ri
+rr"rm$rJ(q!Ii!!N!#!!!!%3!N!30J"*`%N`55LJ++!*)!N!#)!)J""!%#!J%#!3
+)!*!%$B!Im"rm(rirrMrqIrjrrMrq2r`Ir!ri"rJ(q!!*!!J!!!"%!!!"J!'!!B!
+"J"')-BarrM'-%BJ"J!'!!B!"J!#3"!2!!m!$`!2!'pJlh(rqrrprrM[F'pJ$`!2
+!!m!$`!!!!!F!"`!!!2)!N!5!"!#3"4!!%!#3#8J!!!")!*!&!J!"!!)!N"%#!*!
+&%!!3!*!&!J#3"4!!%!#3#!)!!`!$J!2!!q!$m!2J!m!$J!-!!J#3#`)!!`!#J!*
+!!L!#%!)J!N!#J!-!!J#3$J)!!2q3"J!"QC!%rrm!!`#3$``!!!!2!!!!$F!!!!a
+`!!!0(!!!$%F!!!dF!!!-F!!!$F!!!!m!!!!-!*!4mJ#3")!%!*!&%!!3!*!*5!!
+!!%J!N!8#!!%!!J#3%3)!N!83!"!!N!8#!*!&%!!3!*!12rJIm!rJ"m!$J!%!N"8
+rq"!3##!%3!+!!3#3%J)!!2q3"J!"QC!%rrm!!`#3'Jrrrm!$4%F!!0%F!!!dF!!
+!$F!!!!-!N"R+!*!%J!)!N!83!"!!N!P)!!!!5!#3"3%!!3!"!*!4!J#3"4!!%!#
+3"3)!N!83!"!!N!J#!!-!!i!$`!2J!r!$i!2!!i!$!!)!N!X#!!-!!i!$`!2J!r!
+$i!2!!i!$!!)!N!i"!!$rN!B!!3#3#J)!!`!$J!2!!q!$m!2J!m!$J!-!!J#3#XS
+!N!5!!J#3"4!!%!#3#8J!!!")!*!&!3!"!!%!N"%#!*!&%!!3!*!&!J#3"4!!%!#
+3$Mri(r!2i!I!!i!"!*!92rJIm!rJ"m!$J!%!N")"!!$rN!B!!3#3%$ri(r!2i!I
+!!i!"!*!0"FjJ#J!!6%4&4J(A!!"1qJ9'6R919[r'51FI-#4Z!!Jk,J!-1#i!$LC
+Z!"!-4!!"E!K#8h!"B!!"L%KZrrbSG#"Zrr`@+!"'98m[#N*R2`@SKM!I2!"+4QF
+%[%4Z#$D&F!"J!!&FF!!3!`"!!#!r!+L)98m[#N*R2`@SKM!I2!#m4'i)0S9`!@!
+!!6B-4J!"E!K#8h!"B!!"+$e'rmC)E[r'5'lraMmm)!kTkdKZrmC)E[r'2c`3%+R
+V286rd%KZrp")E[r32c`J$URV5'lrd%KZrp!r2"!3UHXp4IrD5'lrfNKZrpSr2#!
+1UHY)E[rD5'lrfMmm%"#TkdKZrpT)E[rN2c`3$URV5'lrd%KZrq3r2"!%UHY)E[r
+'5'lrj$mm%!DTkdKZrq4)E[rZ2c`3%+RV5'lrlNKZrr)r2"!1UHY)E[rb2c`!&UR
+V5'lrmNKZrr)r2#J3UHXf,[rd98p"qJ"f5&#SM$!I2J"+3fi%GJ"J5P92,`T#Ccm
+$U)B`(p"(2!"96bm+3QF`!e*!2`#SKM!Id%Fk!,a%EJbi4@i)Z%9Q'P*$B"Dm4'm
+#8d0+3fi%GJ"J#,T%E,*53f#Z0S0`!8cI$2K1ANjeLh4bG@jMFh4bD@jR!!)"b8j
+@rrj)ja``*Qi!#$BZ!!`NEJ!11#S!"TKU!!)JEJ!8)&!JD!!)'LJ!4M!U!!*83$m
+!-",3EJ!52`#SNdKZrri`"&G!2`!r!bm,6VVpm%S!6qm!$'FJ,`Y#CcmZrrkSKEC
+Zrrj["MmmrmQSJh!!%!8r!+L)B!J[#d*R2`1SK8cI$$K1ANjeM%4bBAG8CAKd3f9
+XE!!!!%j@rkj)jam`*Qi!$$JZ!"BU,J!B5'lraUKd)%8J8#mS!!LSFeP2U0JJAbe
+)rqi[,[rZU(S[#kKl5'lrY+LBU*j+,J!)CJ!#(Lm,U+0)E[rDU$8J,[rDFK$LS#e
+!rr)Q,[rHiU-X,[rLiUBZ!0k$hSB-4!!'E3!"EL"&)&!YD!"3rp3JE[r8S#NNE[r
+8*&,8lJ!829,rdMeU!!,rXMeU!!6rf&b+A83Y5[qZ2!3Z#N*ZrrC+E[r5CeTC6cm
+Zrp+U(L"I*JK+JfG+)%-J8#eS!!Erq#eS!!Vrr%KZrrJr+`!#2a1SU%KZrrK#Cc!
+Zrrb3!'lrq#"&)&!b+!!3NN!`!H")lNM33H*!2`#SU%KZrrJ[!kSIGJ"J(J`b!!N
+`!'B82!053b"+d--Z#$!%N!"$28$rpQ!'8N1f4'hH5NCY!!#J)%8J8(!3d'J!!Y"
+Zrl)h3!!#)%8J8$!S!!,3E[rB0d!!"L"&)&!`+`!'X'J!"Qm+)%8J8$GS!!B!"M!
+V!!+`D`!'E"B["6mZrr3[#cm',blrVNkkrGK2l`!3)%8J8$!S!!,3E[rB0d!!!L"
+&)&!hD!!'!!B`+`!#X'X!"Q`@,`8r,[rd,`Xr,[rf,`G1Z[fH6qm!%#"&)&!hD!!
+'!!BJE[r8S#TJ(NT%CaS["6mZrr3[#cmm!!0"qJ$+5&"1Z[eZ6qm!%#e6rrJYD`!
+%rr`J45"3-#J!!Y"ZrpK63$e!rrT53$e!rriJ45"3##J!!!!4C`J),J!!rrPR$NK
+ZrmT"qJ#'5&#SCQ!-5'lrbN(k!)T)8+KQ5'lrbULG3QHSR%KZrrLSSULH5Li!#QB
+'5Li!#'FL)%8J8(!3d'J!!MG!!!)J45"30fJ!"J!'2c`!-ULF,`ZSSLmZrmDSFbm
+ZrqkSH5mZrqkSf8KZrl5SQ8cI$2K1ANjeLNej6%4&4N4bBAF!!#Jr2cm!%'CQ-$"
+QCM!`CQB`-'CQ-$!!%$!`CQB`-'CQ-$"QCM!`CQB!6PB!!%MR($!D,J!B*Qi!&$J
+Z!!if,J!-*'i!#$!Z!"TR3P0!C`T63'FJ8d"R0Q!d,`Sr!cm%,bi!%#m,(`9#*dk
+kr0C2l`!8B"S[#Mm$2`3[,J!3,`XI"4mm!!&1Z[bk6qm!&%cI$$K1AL"I6qm!&%l
+3K'eKD@i!N!C%!!!"!!1!"m!"!!%!!3"rr(rm!3!"!!%!"m!$J!%!!!!"!!1!"m!
+2i!rJ!i$rr[rqrrlrrJ1!$q!2i!I!!i!"!!!(!!F!!!%!!!!4%!!!%"!!!!&V"cG
+,$#RH!!!!(!$q!!035808!!8!)Na%48B!!3"U3e958`!$!)*MD@0Z!!-!XJ(8!!`
+!N!J"e3!B!!!!b!#3"!(@!#)!!!'3!!#3"!(A!$-!!!(I!*!%!GJ!3J!!!Li!N!3
+"f3"0!!!#H`#3"!(8!!!J!!,)!*!%!GF!B3!!#IB!N!3"e!"A)!!&PJ#3"!(9!&`
+J!!AH!*!%!GErr`!!"LB!N!3"errr!!!2b!#3"!(8rrm!!!CZ!*!%!GArr`!!"f3
+!N!3"e[rr!!!)@J#3"!(Arrm!!!NS!*!%#e4hEdaTEQ9-4%9'#f0XEh0PC'&bFQp
+h#@p`C@jKFR*[Ga"ME'pcC@4KFR*[Gh0[E'PN$Qp`C@jKFR*[Gh0[E'PN#Q&bFQp
+hFQPRD(3*BA*bEhGNEhGZ"%KKEQ3%4QPcG!Y#FQphFf9b6%4&4V4I:
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Splash.py b/Mac/Contrib/PyIDE-src/IDELib/Splash.py
new file mode 100644
index 0000000..99a36fa
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Splash.py
@@ -0,0 +1,162 @@
+import Dlg
+import Res
+
+splash = Dlg.GetNewDialog(468, -1)
+splash.DrawDialog()
+
+import Qd, TE, Fm, sys
+
+_real__import__ = None
+
+def install_importhook():
+	global _real__import__
+	import __builtin__
+	if _real__import__ is None:
+		_real__import__ = __builtin__.__import__
+		__builtin__.__import__ = my__import__
+
+def uninstall_importhook():
+	global _real__import__
+	if _real__import__ is not None:
+		import __builtin__
+		__builtin__.__import__ = _real__import__
+		_real__import__ = None
+
+_progress = 0
+
+def importing(module):
+	global _progress
+	Qd.SetPort(splash)
+	fontID = Fm.GetFNum("Python-Sans")
+	if not fontID:
+		fontID = geneva
+	Qd.TextFont(fontID)
+	Qd.TextSize(9)
+	rect = (35, 260, 365, 276)
+	if module:
+		TE.TETextBox('Importing: ' + module, rect, 0)
+		if not _progress:
+			Qd.FrameRect((35, 276, 365, 284))
+		pos = min(36 + 330 * _progress / 44, 364)
+		Qd.PaintRect((36, 277, pos, 283))
+		_progress = _progress + 1
+	else:
+		Qd.EraseRect(rect)
+		Qd.PaintRect((36, 277, pos, 283))
+
+def my__import__(name, globals=None, locals=None, fromlist=None):
+	try:
+		return sys.modules[name]
+	except KeyError:
+		try:
+			importing(name)
+		except:
+			try:
+				rv = _real__import__(name)
+			finally:
+				uninstall_importhook()
+			return rv
+		return _real__import__(name)
+
+install_importhook()
+
+kHighLevelEvent = 23
+import Win
+from Fonts import *
+from QuickDraw import *
+from TextEdit import *
+import string
+import sys
+import random
+
+_keepsplashscreenopen = 0
+
+abouttext1 = """The Python Integrated Developement Environment for the Macintosh
+Version: %s
+Copyright 1997 Just van Rossum, Letterror. <just@knoware.nl>
+
+Python %s
+%s
+Written by Guido van Rossum with Jack Jansen (and others)
+
+See: <http://www.python.org/> for information and documentation."""
+
+flauwekul = [	'Goodday, Bruce.', 
+			'What¹s new?', 
+			'Nudge, nudge, say no more!', 
+			'No, no sir, it¹s not dead. It¹s resting.',
+			'Albatros!',
+			'It¹s . . .',
+			'Is your name not Bruce, then?',
+			"""But Mr F.G. Superman has a secret identity . . . 
+when trouble strikes at any time . . . 
+at any place . . . he is ready to become . . . 
+Bicycle Repair Man!"""
+			]
+
+def nl2return(text):
+	return string.join(string.split(text, '\n'), '\r')
+
+def UpdateSplash(drawdialog = 0, what = 0):
+	if drawdialog:
+		splash.DrawDialog()
+	drawtext(what)
+	Win.ValidRect(splash.GetWindowPort().portRect)
+
+def drawtext(what = 0):
+	Qd.SetPort(splash)
+	fontID = Fm.GetFNum("Python-Sans")
+	if not fontID:
+		fontID = geneva
+	Qd.TextFont(fontID)
+	Qd.TextSize(9)
+	rect = (10, 125, 390, 260)
+	if not what:
+		import __main__
+		abouttxt = nl2return(abouttext1  \
+			% (__main__.__version__, sys.version, sys.copyright))
+	else:
+		abouttxt = nl2return(random.choice(flauwekul))
+	TE.TETextBox(abouttxt, rect, teJustCenter)
+
+UpdateSplash(1)
+
+def wait():
+	import Evt
+	from Events import *
+	global splash
+	try:
+		splash
+	except NameError:
+		return
+	Qd.InitCursor()
+	time = Evt.TickCount()
+	whattext = 0
+	while _keepsplashscreenopen:
+		ok, event = Evt.EventAvail(highLevelEventMask)
+		if ok:
+			# got apple event, back to mainloop
+			break
+		ok, event = Evt.EventAvail(mDownMask | keyDownMask | updateMask)
+		if ok:
+			ok, event = Evt.WaitNextEvent(mDownMask | keyDownMask | updateMask, 30)
+			if ok:
+				(what, message, when, where, modifiers) = event
+				if what == updateEvt:
+					if Win.WhichWindow(message) == splash:
+						UpdateSplash(1, whattext)
+				else:
+					break
+		if Evt.TickCount() - time > 360:
+			whattext = not whattext
+			drawtext(whattext)
+			time = Evt.TickCount()
+	del splash
+	#Res.CloseResFile(splashresfile)
+
+def about():
+	global splash, splashresfile, _keepsplashscreenopen
+	_keepsplashscreenopen = 1
+	splash = Dlg.GetNewDialog(468, -1)
+	splash.DrawDialog()
+	wait()
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/FontSettings.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/FontSettings.py
new file mode 100644
index 0000000..0a331df
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/FontSettings.py
@@ -0,0 +1,139 @@
+"""usage: 
+newsettings = print FontDialog(('Chicago', 0, 12, (0, 0, 0)))	# font name or id, style flags, size, color (color is ignored)
+if newsettings:
+	font, style, size, color = newsettings	# 'font' is always the font name, not the id number
+	# do something
+"""
+
+import W
+import PyEdit
+import TextEdit
+import string
+import types
+
+_stylenames = ["Plain", "Bold", "Italic", "Underline", "Outline", "Shadow", "Condensed", "Extended"]
+
+
+class _FontDialog:
+	
+	def __init__(self, fontsettings):
+		leftmargin = 46
+		self.w = W.ModalDialog((440, 180), 'Font settings')
+		self.w.fonttitle = W.TextBox((10, 12, 30, 14), "Font:", TextEdit.teJustRight)
+		self.w.pop = W.FontMenu((leftmargin, 10, 16, 16), self.setfont)
+		self.w.fontname = W.TextBox((leftmargin + 20, 12, 150, 14))
+		self.w.sizetitle = W.TextBox((10, 38, 30, 14), "Size:", TextEdit.teJustRight)
+		self.w.sizeedit = W.EditText((leftmargin, 35, 40, 20), "", self.checksize)
+		styletop = 64
+		self.w.styletitle = W.TextBox((10, styletop + 2, 30, 14), "Style:", TextEdit.teJustRight)
+		for i in range(len(_stylenames)):
+			top = styletop + (i % 4) * 20
+			left = leftmargin + 80 * (i > 3) - 2
+			if i:
+				self.w[i] = W.CheckBox((left, top, 76, 16), _stylenames[i], self.dostyle)
+			else:
+				self.w[i] = W.CheckBox((left, top, 70, 16), _stylenames[i], self.doplain)
+		self.w.cancelbutton = W.Button((-180, -26, 80, 16), "Cancel", self.cancel)
+		self.w.donebutton = W.Button((-90, -26, 80, 16), "Done", self.done)
+		
+		sampletext = "Sample text."
+		self.w.sample = W.EditText((230, 10, -10, 130), sampletext, fontsettings = fontsettings)
+		
+		self.w.setdefaultbutton(self.w.donebutton)
+		self.w.bind('cmd.', self.w.cancelbutton.push)
+		self.w.bind('cmdw', self.w.donebutton.push)
+		self.lastsize = fontsettings[2]
+		self._rv = None
+		self.set(fontsettings)
+		self.w.open()
+	
+	def set(self, fontsettings):
+		font, style, size, color = fontsettings
+		if type(font) <> types.StringType:
+			import Res
+			res = Res.GetResource('FOND', font)
+			font = res.GetResInfo()[2]
+		self.w.fontname.set(font)
+		self.w.sizeedit.set(str(size))
+		if style:
+			for i in range(1, len(_stylenames)):
+				self.w[i].set(style & 0x01)
+				style = style >> 1
+		else:
+			self.w[0].set(1)
+	
+	def get(self):
+		font = self.w.fontname.get()
+		style = 0
+		if not self.w[0].get():
+			flag = 0x01
+			for i in range(1, len(_stylenames)):
+				if self.w[i].get():
+					style = style | flag
+				flag = flag << 1
+		size = self.lastsize
+		return (font, style, size, (0, 0, 0))
+	
+	def doit(self):
+		if self.w[0].get():
+			style = 0
+		else:
+			style = 0
+			for i in range(1, len(_stylenames)):
+				if self.w[i].get():
+					style = style | 2 ** (i - 1)
+		#self.w.sample.set(`style`)
+		self.w.sample.setfontsettings(self.get())
+	
+	def checksize(self):
+		size = self.w.sizeedit.get()
+		if not size:
+			return
+		try:
+			size = string.atoi(size)
+		except (ValueError, OverflowError):
+			good = 0
+		else:
+			good = 1 <= size <= 500
+		if good:
+			if self.lastsize <> size:
+				self.lastsize = size
+				self.doit()
+		else:
+			# beep!
+			self.w.sizeedit.set(`self.lastsize`)
+			self.w.sizeedit.selectall()
+	
+	def doplain(self):
+		for i in range(1, len(_stylenames)):
+			self.w[i].set(0)
+		self.w[0].set(1)
+		self.doit()
+	
+	def dostyle(self):
+		for i in range(1, len(_stylenames)):
+			if self.w[i].get():
+				self.w[0].set(0)
+				break
+		else:
+			self.w[0].set(1)
+		self.doit()
+	
+	def cancel(self):
+		self.w.close()
+	
+	def done(self):
+		self._rv = self.get()
+		self.w.close()
+	
+	def setfont(self, fontname):
+		self.w.fontname.set(fontname)
+		self.doit()
+	
+
+def FontDialog(fontsettings):
+	fd = _FontDialog(fontsettings)
+	return fd._rv
+
+def test():
+	print FontDialog(('Zapata-Light', 0, 25, (0, 0, 0)))
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/MacPrefs.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/MacPrefs.py
new file mode 100644
index 0000000..caf71b8
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/MacPrefs.py
@@ -0,0 +1,109 @@
+import macfs
+import marshal
+import os
+import macostools
+import types
+
+kOnSystemDisk = 0x8000
+
+
+class PrefObject:
+	
+	def __init__(self, dict = None):
+		if dict == None:
+			self._prefsdict = {}
+		else:
+			self._prefsdict = dict
+	
+	def __len__(self):
+		return len(self._prefsdict)
+	
+	def __delattr__(self, attr):
+		if self._prefsdict.has_key(attr):
+			del self._prefsdict[attr]
+		else:
+			raise AttributeError, 'delete non-existing instance attribute'
+	
+	def __getattr__(self, attr):
+		if attr == '__members__':
+			keys = self._prefsdict.keys()
+			keys.sort()
+			return keys
+		try:
+			return self._prefsdict[attr]
+		except KeyError:
+			raise AttributeError, attr
+	
+	def __setattr__(self, attr, value):
+		if attr[0] <> '_':
+			self._prefsdict[attr] = value
+		else:
+			self.__dict__[attr] = value
+	
+	def getprefsdict(self):
+		return self._prefsdict
+
+
+class PrefFile(PrefObject):
+	
+	def __init__(self, path, creator = 'Pyth'):
+		# Find the preferences folder and our prefs file, create if needed.
+		self.__path = path
+		self.__creator = creator
+		self._prefsdict = {}
+		try:
+			prefdict = marshal.load(open(self.__path, 'rb'))
+		except IOError:
+			pass
+		else:
+			for key, value in prefdict.items():
+				if type(value) == types.DictType:
+					self._prefsdict[key] = PrefObject(value)
+				else:
+					self._prefsdict[key] = value
+	
+	def save(self):
+		prefdict = {}
+		for key, value in self._prefsdict.items():
+			if type(value) == types.InstanceType:
+				prefdict[key] = value.getprefsdict()
+				if not prefdict[key]:
+					del prefdict[key]
+			else:
+				prefdict[key] = value
+		marshal.dump(prefdict, open(self.__path, 'wb'))
+		fss = macfs.FSSpec(self.__path)
+		fss.SetCreatorType(self.__creator, 'pref')
+	
+	def __getattr__(self, attr):
+		if attr == '__members__':
+			keys = self._prefsdict.keys()
+			keys.sort()
+			return keys
+		try:
+			return self._prefsdict[attr]
+		except KeyError:
+			if attr[0] <> '_':
+				self._prefsdict[attr] = PrefObject()
+				return self._prefsdict[attr]
+			else:
+				raise AttributeError, attr
+
+
+_prefscache = {}
+
+def GetPrefs(prefname, creator = 'Pyth'):
+	if _prefscache.has_key(prefname):
+		return _prefscache[prefname]
+	# Find the preferences folder and our prefs file, create if needed.
+	vrefnum, dirid = macfs.FindFolder(kOnSystemDisk, 'pref', 0)
+	prefsfolder_fss = macfs.FSSpec((vrefnum, dirid, ''))
+	prefsfolder = prefsfolder_fss.as_pathname()
+	path = os.path.join(prefsfolder, prefname)
+	head, tail = os.path.split(path)
+	# make sure the folder(s) exist
+	macostools.mkdirs(head)
+	
+	preffile = PrefFile(path, creator)
+	_prefscache[prefname] = preffile
+	return preffile
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/ModuleBrowser.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/ModuleBrowser.py
new file mode 100644
index 0000000..67685b8
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/ModuleBrowser.py
@@ -0,0 +1,164 @@
+import W
+import sys
+
+__version__ = "0.2"
+__author__ = "jvr"
+
+class _modulebrowser:
+	
+	def __init__(self):
+		self.editmodules = []
+		self.modules = []
+		self.window = W.Window((194, 1000), "Module Browser", minsize = (194, 160), maxsize = (340, 20000))
+		
+		self.window.openbutton = W.Button((10, 8, 80, 16), "Open", self.openbuttonhit)
+		self.window.browsebutton = W.Button((100, 8, 80, 16), "BrowseŠ", self.browsebuttonhit)
+		self.window.reloadbutton = W.Button((10, 32, 80, 16), "Reload", self.reloadbuttonhit)
+		self.window.openotherbutton = W.Button((100, 32, 80, 16), "Open otherŠ", self.openother)
+		
+		self.window.openbutton.enable(0)
+		self.window.reloadbutton.enable(0)
+		self.window.browsebutton.enable(0)
+		self.window.setdefaultbutton(self.window.browsebutton)
+		
+		self.window.bind("cmdr", self.window.reloadbutton.push)
+		self.window.bind("cmdb", self.window.browsebutton.push)
+	
+		self.window.bind("<activate>", self.activate)
+		self.window.bind("<close>", self.close)
+		
+		self.window.list = W.List((-1, 56, 1, -14), [], self.listhit)
+		
+		self.window.open()
+		self.checkbuttons()
+	
+	def close(self):
+		global _browser
+		_browser = None
+	
+	def activate(self, onoff):
+		if onoff:
+			self.makelist()
+		
+	def listhit(self, isdbl):
+		self.checkbuttons()
+		if isdbl:
+			if self.window._defaultbutton:
+				self.window._defaultbutton.push()
+	
+	def checkbuttons(self):
+		sel = self.window.list.getselection()
+		if sel:
+			for i in sel:
+				if self.editmodules[i]:
+					self.window.openbutton.enable(1)
+					self.window.reloadbutton.enable(1)
+					self.window.setdefaultbutton(self.window.openbutton)
+					break
+			else:
+				self.window.openbutton.enable(0)
+				self.window.reloadbutton.enable(0)
+				self.window.setdefaultbutton(self.window.browsebutton)
+			self.window.browsebutton.enable(1)
+		else:
+			#self.window.setdefaultbutton(self.window.browsebutton)
+			self.window.openbutton.enable(0)
+			self.window.reloadbutton.enable(0)
+			self.window.browsebutton.enable(0)
+	
+	def openbuttonhit(self):
+		import imp
+		sel = self.window.list.getselection()
+		W.SetCursor("watch")
+		for i in sel:
+			modname = self.window.list[i]
+			try:
+				self.openscript(sys.modules[modname].__file__, modname)
+			except IOError:
+				try:
+					file, path, description = imp.find_module(modname)
+				except ImportError:
+					W.SetCursor("arrow")
+					W.Message("Can¹t find file for module ³%s²." 
+							% modname)
+				else:
+					self.openscript(path, modname)					
+	
+	def openscript(self, path, modname):
+		if path[-3:] == '.py':
+			W.getapplication().openscript(path)
+		else:
+			W.Message("Can¹t edit ³%s²; it might be a shared library or a .pyc file." 
+					% modname)
+	
+	def openother(self):
+		import imp
+		import EasyDialogs
+		
+		modname = EasyDialogs.AskString("Open module:")
+		if modname:
+			try:
+				file, path, description = imp.find_module(modname)
+			except ImportError:
+				if modname in sys.builtin_module_names:
+					alerttext = "³%s² is a builtin module, which you can¹t edit." % modname
+				else:
+					alerttext = "No module named ³%s²." % modname
+				raise W.AlertError, alerttext
+			self.openscript(path, modname)
+	
+	def reloadbuttonhit(self):
+		sel = self.window.list.getselection()
+		W.SetCursor("watch")
+		for i in sel:
+			m = sys.modules[self.window.list[i]]
+			reload(m)
+	
+	def browsebuttonhit(self):
+		sel = self.window.list.getselection()
+		if not sel:
+			return
+		import PyBrowser
+		for i in sel:
+			PyBrowser.Browser(sys.modules[self.window.list[i]])
+	
+	def makelist(self):
+		editmodules, modules = getmoduleslist()
+		if modules == self.modules:
+			return
+		self.editmodules, self.modules = editmodules, modules
+		self.window.list.setdrawingmode(0)
+		sel = self.window.list.getselectedobjects()
+		self.window.list.set(self.modules)
+		self.window.list.setselectedobjects(sel)
+		self.window.list.setdrawingmode(1)
+
+
+def getmoduleslist():
+	import PyBrowser	# for caselesssort function
+	modules = sys.modules.keys()
+	modules = PyBrowser.caselesssort(modules)
+	editmodules = []
+	sysmodules = sys.modules
+	modulesappend = editmodules.append
+	for m in modules:
+		module = sysmodules[m]
+		try:
+			if sysmodules[m].__file__[-3:] == '.py':
+				modulesappend(1)
+			else:
+				modulesappend(0)
+		except AttributeError:
+			modulesappend(0)
+	return editmodules, modules
+	
+	
+
+_browser = None
+
+def ModuleBrowser():
+	global _browser
+	if _browser is not None:
+		_browser.window.select()
+	else:
+		_browser = _modulebrowser()
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/ProfileBrowser.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/ProfileBrowser.py
new file mode 100644
index 0000000..710481c
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/ProfileBrowser.py
@@ -0,0 +1,82 @@
+import W
+import Evt
+
+import sys
+import StringIO
+import string
+
+def timer(TickCount = Evt.TickCount):
+	return TickCount() / 60.0
+
+class ProfileBrowser:
+	
+	def __init__(self, stats = None):
+		self.sortkeys = ('calls',)
+		self.setupwidgets()
+		self.setstats(stats)
+	
+	def setupwidgets(self): 
+		self.w = W.Window((580, 400), "Profile Statistics", minsize = (200, 100), tabbable = 0)
+		self.w.divline = W.HorizontalLine((0, 20, 0, 0))
+		self.w.titlebar = W.TextBox((4, 4, 40, 12), 'Sort by:')
+		self.buttons = []
+		self.w.button_calls = W.RadioButton((54, 4, 45, 12), 'calls', self.buttons, self.setsort)
+		self.w.button_time = W.RadioButton((104, 4, 40, 12), 'time', self.buttons, self.setsort)
+		self.w.button_cumulative = W.RadioButton((154, 4, 75, 12), 'cumulative', self.buttons, self.setsort)
+		self.w.button_stdname = W.RadioButton((234, 4, 60, 12), 'stdname', self.buttons, self.setsort)
+		self.w.button_calls.set(1)
+		self.w.button_file = W.RadioButton((304, 4, 40, 12), 'file', self.buttons, self.setsort)
+		self.w.button_line = W.RadioButton((354, 4, 50, 12), 'line', self.buttons, self.setsort)
+		self.w.button_module = W.RadioButton((404, 4, 55, 12), 'module', self.buttons, self.setsort)
+		self.w.button_name = W.RadioButton((464, 4, 50, 12), 'name', self.buttons, self.setsort)
+##		self.w.button_nfl = W.RadioButton((4, 4, 12, 12), 'nfl', self.buttons, self.setsort)
+##		self.w.button_pcalls = W.RadioButton((4, 4, 12, 12), 'pcalls', self.buttons, self.setsort)
+		self.w.text = W.TextEditor((0, 21, -15, -15), inset = (6, 5), 
+				readonly = 1, wrap = 0, fontsettings = ('Monaco', 0, 9, (0, 0, 0)))
+		self.w._bary = W.Scrollbar((-15, 20, 16, -14), self.w.text.vscroll, max = 32767)
+		self.w._barx = W.Scrollbar((-1, -15, -14, 16), self.w.text.hscroll, max = 32767)
+		self.w.open()
+	
+	def setstats(self, stats):
+		self.stats = stats
+		self.stats.strip_dirs()
+		self.displaystats()
+	
+	def setsort(self):
+		# Grmpf. The callback doesn't give us the button:-(
+		for b in self.buttons:
+			if b.get():
+				if b._title == self.sortkeys[0]:
+					return
+				self.sortkeys = (b._title,) + self.sortkeys[:3]
+				break
+		self.displaystats()
+	
+	def displaystats(self):
+		W.SetCursor('watch')
+		apply(self.stats.sort_stats, self.sortkeys)
+		saveout = sys.stdout
+		try:
+			s = sys.stdout = StringIO.StringIO()
+			self.stats.print_stats()
+		finally:
+			sys.stdout = saveout
+		text = string.join(string.split(s.getvalue(), '\n'), '\r')
+		self.w.text.set(text)
+
+
+def main():
+	import pstats
+	args = sys.argv[1:]
+	for i in args:
+		stats = pstats.Stats(i)
+		browser = ProfileBrowser(stats)
+	else:
+		import macfs
+		fss, ok = macfs.PromptGetFile('Profiler data')
+		if not ok: sys.exit(0)
+		stats = pstats.Stats(fss.as_pathname())
+		browser = ProfileBrowser(stats)
+
+if __name__ == '__main__':
+	main()
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyBrowser.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyBrowser.py
new file mode 100644
index 0000000..8d5f46c
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyBrowser.py
@@ -0,0 +1,439 @@
+import W, SpecialKeys
+import struct
+import string
+import types
+import regex
+
+nullid = '\0\0'
+closedid = struct.pack('h', 468)
+openid = struct.pack('h', 469)
+closedsolidid = struct.pack('h', 470)
+opensolidid = struct.pack('h', 471)
+
+arrows = (nullid, closedid, openid, closedsolidid, opensolidid)
+
+has_ctlcharsRE = regex.compile('[\000-\037\177-\377]')
+
+def double_repr(key, value, truncvalue = 0, 
+			type = type, StringType = types.StringType,
+			has_ctlchars = has_ctlcharsRE.search, _repr = repr, str = str):
+	if type(key) == StringType and has_ctlchars(key) < 0:
+		key = str(key)
+	else:
+		key = _repr(key)
+	if type(value) == StringType and has_ctlchars(value) < 0:
+		value = str(value)
+	elif key == '__builtins__':
+		value = "<" + type(value).__name__ + " '__builtin__'>"
+	elif key == '__return__':
+		# bleh, when returning from a class codeblock we get infinite recursion in repr. 
+		# Use safe repr instead.
+		import repr
+		value = repr.repr(value)
+	else:
+		try:
+			value = _repr(value)
+			'' + value	# test to see if it is a string, in case a __repr__ method is buggy
+		except:
+			value = '€€€ exception in repr()'
+	if truncvalue:
+		return key + '\t' + value[:255]
+	return key + '\t' + value
+
+
+class BrowserWidget(W.List):
+	
+	LDEF_ID = 471
+	
+	def __init__(self, possize, object = None, col = 100, closechildren = 0):
+		W.List.__init__(self, possize, callback = self.listhit)
+		self.object = None,
+		self.indent = 16
+		self.lastmaxindent = 0
+		self.closechildren = closechildren
+		self.children = []
+		self.mincol = 64
+		self.setcolumn(col)
+		self.bind('return', self.openselection)
+		self.bind('enter', self.openselection)
+		if object is not None:
+			self.set(object)
+	
+	def set(self, object):
+		if self.object[0] <> object:
+			self.object = object,
+			self[:] = self.unpack(object, 0)
+		elif self._parentwindow is not None and self._parentwindow.wid:
+			self.update()
+	
+	def unpack(self, object, indent):
+		return unpack_object(object, indent)
+	
+	def update(self):
+		# for now...
+		W.SetCursor('watch')
+		self.setdrawingmode(0)
+		sel = self.getselectedobjects()
+		fold = self.getunfoldedobjects()
+		topcell = self.gettopcell()
+		self[:] = self.unpack(self.object[0], 0)
+		self.unfoldobjects(fold)
+		self.setselectedobjects(sel)
+		self.settopcell(topcell)
+		self.setdrawingmode(1)
+	
+	def setcolumn(self, col):
+		self.col = col
+		self.colstr = struct.pack('h', col)
+		if self._list:
+			sel = self.getselection()
+			self.setitems(self.items)
+			self.setselection(sel)
+	
+	def key(self, char, event):
+		if char in (SpecialKeys.leftarrowkey, SpecialKeys.rightarrowkey):
+			sel = self.getselection()
+			sel.reverse()
+			self.setdrawingmode(0)
+			for index in sel:
+				self.fold(index, char == SpecialKeys.rightarrowkey)
+			self.setdrawingmode(1)
+		else:
+			W.List.key(self, char, event)
+	
+	def rollover(self, (x, y), onoff):
+		if onoff:
+			if self.incolumn((x, y)):
+				W.SetCursor('hmover')
+			else:
+				W.SetCursor('arrow')
+	
+	def inarrow(self, (x, y)):
+		cl, ct, cr, cb = self._list.LRect((0, 0))
+		l, t, r, b = self._bounds
+		if (x - cl) < 16:
+			cellheight = cb - ct
+			index = (y - ct) / cellheight
+			if index < len(self.items):
+				return 1, index
+		return None, None
+	
+	def incolumn(self, (x, y)):
+		l, t, r, b = self._list.LRect((0, 0))
+		abscol = l + self.col
+		return abs(abscol - x) < 3
+	
+	def trackcolumn(self, (x, y)):
+		import Qd, QuickDraw, Evt
+		self.SetPort()
+		l, t, r, b = self._bounds
+		bounds = l, t, r, b = l + 1, t + 1, r - 16, b - 1
+		abscol = l + self.col
+		mincol = l + self.mincol
+		maxcol = r - 10
+		diff = abscol - x
+		Qd.PenPat('\000\377\000\377\000\377\000\377')
+		Qd.PenMode(QuickDraw.srcXor)
+		rect = abscol - 1, t, abscol, b
+		Qd.PaintRect(rect)
+		lastpoint = (x, y)
+		newcol = -1
+		#W.SetCursor('fist')
+		while Evt.Button():
+			(x, y) = Evt.GetMouse()
+			if (x, y) <> lastpoint:
+				newcol = x + diff
+				newcol = max(newcol, mincol)
+				newcol = min(newcol, maxcol)
+				Qd.PaintRect(rect)
+				rect = newcol - 1, t, newcol, b
+				Qd.PaintRect(rect)
+				lastpoint = (x, y)
+		Qd.PaintRect(rect)
+		Qd.PenPat(Qd.qd.black)
+		Qd.PenNormal()
+		if newcol > 0 and newcol <> abscol:
+			self.setcolumn(newcol - l)
+	
+	def click(self, point, modifiers):
+		if point == (-1, -1):	# gross.
+			W.List.click(self, point ,modifiers)
+			return
+		hit, index = self.inarrow(point)
+		if hit:
+			(key, value, arrow, indent) = self.items[index]
+			self.fold(index, arrow == 1)
+		elif self.incolumn(point):
+			self.trackcolumn(point)
+		else:
+			W.List.click(self, point, modifiers)
+	
+	# for W.List.key
+	def findmatch(self, tag):
+		lower = string.lower
+		items = self.items
+		taglen = len(tag)
+		match = '\377' * 100
+		match_i = -1
+		for i in range(len(items)):
+			item = lower(str(items[i][0]))
+			if tag <= item < match:
+				match = item
+				match_i = i
+		if match_i >= 0:
+			return match_i
+		else:
+			return len(items) - 1
+	
+	def close(self):
+		if self.closechildren:
+			for window in self.children:
+				window.close()
+		self.children = []
+		W.List.close(self)
+	
+	def fold(self, index, onoff):
+		(key, value, arrow, indent) = self.items[index]
+		if arrow == 0 or (onoff and arrow == 2) or (not onoff and arrow == 1):
+			return
+		W.SetCursor('watch')
+		topcell = self.gettopcell()
+		if onoff:
+			self[index] = (key, value, 4, indent)
+			self.setdrawingmode(0)
+			self[index+1:index+1] = self.unpack(value, indent + 1)
+			self[index] = (key, value, 2, indent)
+		else:
+			self[index] = (key, value, 3, indent)
+			self.setdrawingmode(0)
+			count = 0
+			for i in range(index + 1, len(self.items)):
+				(dummy, dummy, dummy, subindent) = self.items[i]
+				if subindent <= indent:
+					break
+				count = count + 1
+			self[index+1:index+1+count] = []
+			self[index] = (key, value, 1, indent)
+		maxindent = self.getmaxindent()
+		if maxindent <> self.lastmaxindent:
+			newabsindent = self.col + (maxindent - self.lastmaxindent) * self.indent
+			if newabsindent >= self.mincol:
+				self.setcolumn(newabsindent)
+			self.lastmaxindent = maxindent
+		self.settopcell(topcell)
+		self.setdrawingmode(1)
+	
+	def unfoldobjects(self, objects):
+		for obj in objects:
+			try:
+				index = self.items.index(obj)
+			except ValueError:
+				pass
+			else:
+				self.fold(index, 1)
+	
+	def getunfoldedobjects(self):
+		curindent = 0
+		objects = []
+		for index in range(len(self.items)):
+			(key, value, arrow, indent) = self.items[index]
+			if indent > curindent:
+				(k, v, a, i) = self.items[index - 1]
+				objects.append((k, v, 1, i))
+				curindent = indent
+			elif indent < curindent:
+				curindent = indent
+		return objects
+	
+	def listhit(self, isdbl):
+		if isdbl:
+			self.openselection()
+	
+	def openselection(self):
+		import os
+		sel = self.getselection()
+		for index in sel:
+			(key, value, arrow, indent) = self[index]
+			if arrow:
+				self.children.append(Browser(value))
+			elif type(value) == types.StringType and '\0' not in value:
+				editor = self._parentwindow.parent.getscript(value)
+				if editor:
+					editor.select()
+					return
+				elif os.path.exists(value) and os.path.isfile(value):
+					import macfs
+					fss = macfs.FSSpec(value)
+					if fss.GetCreatorType()[1] == 'TEXT':
+						W.getapplication().openscript(value)
+	
+	def itemrepr(self, (key, value, arrow, indent), str = str, double_repr = double_repr, 
+			arrows = arrows, pack = struct.pack):
+		arrow = arrows[arrow]
+		return arrow + pack('h', self.indent * indent) + self.colstr + \
+				double_repr(key, value, 1)
+	
+	def getmaxindent(self, max = max):
+		maxindent = 0
+		for item in self.items:
+			maxindent = max(maxindent, item[3])
+		return maxindent
+	
+	def domenu_copy(self, *args):
+		sel = self.getselectedobjects()
+		selitems = []
+		for key, value, dummy, dummy in sel:
+			selitems.append(double_repr(key, value))
+		text = string.join(selitems, '\r')
+		if text:
+			import Scrap
+			Scrap.ZeroScrap()
+			Scrap.PutScrap('TEXT', text)
+
+
+class Browser:
+	
+	def __init__(self, object = None, title = None, closechildren = 0):
+		if hasattr(object, '__name__'):
+			name = object.__name__
+		else:
+			name = ''
+		if title is None:
+			title = 'Object browser'
+			if name:
+				title = title + ': ' + name
+		self.w = w = W.Window((300, 400), title, minsize = (100, 100))
+		w.info = W.TextBox((18, 8, -70, 15))
+		w.updatebutton = W.Button((-64, 4, 50, 16), 'Update', self.update)
+		w.browser = BrowserWidget((-1, 24, 1, -14), None)
+		w.bind('cmdu', w.updatebutton.push)
+		w.open()
+		self.set(object, name)
+	
+	def close(self):
+		if self.w.wid:
+			self.w.close()
+	
+	def set(self, object, name = ''):
+		W.SetCursor('watch')
+		tp = type(object).__name__
+		try:
+			length = len(object)
+		except:
+			length = -1
+		if not name and hasattr(object, '__name__'):
+			name = object.__name__
+		if name:
+			info = name + ': ' + tp
+		else:
+			info = tp
+		if length >= 0:
+			if length == 1:
+				info = info + ' (%d element)' % length
+			else:
+				info = info + ' (%d elements)' % length
+		self.w.info.set(info)
+		self.w.browser.set(object)
+	
+	def update(self):
+		self.w.browser.update()
+
+
+SIMPLE_TYPES = (
+	types.NoneType,
+	types.IntType,
+	types.LongType,
+	types.FloatType,
+	types.ComplexType,
+	types.StringType
+)
+
+INDEXING_TYPES = (
+	types.TupleType,
+	types.ListType,
+	types.DictionaryType
+)
+
+def unpack_object(object, indent = 0):
+	tp = type(object)
+	if tp in SIMPLE_TYPES and tp is not types.NoneType:
+		raise TypeError, 'can¹t browse simple type: %s' % tp.__name__
+	elif tp == types.DictionaryType:
+		return unpack_dict(object, indent)
+	elif tp in (types.TupleType, types.ListType):
+		return unpack_sequence(object, indent)
+	elif tp == types.InstanceType:
+		return unpack_instance(object, indent)
+	elif tp == types.ClassType:
+		return unpack_class(object, indent)
+	elif tp == types.ModuleType:
+		return unpack_dict(object.__dict__, indent)
+	else:
+		return unpack_other(object, indent)
+
+def unpack_sequence(seq, indent = 0):
+	items = map(None, range(len(seq)), seq)
+	items = map(lambda (k, v), type = type, simp = SIMPLE_TYPES, indent = indent: 
+				(k, v, not type(v) in simp, indent), items)
+	return items
+
+def unpack_dict(dict, indent = 0):
+	items = dict.items()
+	return pack_items(items, indent)
+
+def unpack_instance(inst, indent = 0):
+	if hasattr(inst, '__pybrowse_unpack__'):
+		return unpack_object(inst.__pybrowse_unpack__(), indent)
+	else:
+		items = [('__class__', inst.__class__)] + inst.__dict__.items()
+		return pack_items(items, indent)
+
+def unpack_class(clss, indent = 0):
+	items = [('__bases__', clss.__bases__), ('__name__', clss.__name__)] + clss.__dict__.items()
+	return pack_items(items, indent)
+
+def unpack_other(object, indent = 0):
+	attrs = []
+	if hasattr(object, '__members__'):
+		attrs = attrs + object.__members__
+	if hasattr(object, '__methods__'):
+		attrs = attrs + object.__methods__
+	items = []
+	for attr in attrs:
+		items.append((attr, getattr(object, attr)))
+	return pack_items(items, indent)
+
+def pack_items(items, indent = 0):
+	items = map(lambda (k, v), type = type, simp = SIMPLE_TYPES, indent = indent: 
+				(k, v, not type(v) in simp, indent), 
+			items)
+	return tuple_caselesssort(items)
+
+def caselesssort(alist):
+	"""Return a sorted copy of a list. If there are only strings in the list, 
+	it will not consider case"""
+	
+	try:
+		# turn ['FOO',  'aaBc', 'ABcD'] into [('foo', 'FOO'), ('aabc', 'aaBc'), ('abcd', 'ABcD')], if possible
+		tupledlist = map(lambda item, lower = string.lower: (lower(item), item), alist)
+	except TypeError:
+		# at least one element in alist is not a string, proceed the normal way...
+		alist = alist[:]
+		alist.sort()
+		return alist
+	else:
+		tupledlist.sort()
+		# turn [('aabc', 'aaBc'), ('abcd', 'ABcD'), ('foo', 'FOO')] into ['aaBc', 'ABcD', 'FOO']
+		return map(lambda x: x[1], tupledlist)
+
+def tuple_caselesssort(items):
+	try:
+		tupledlist = map(lambda tuple, lower = string.lower: (lower(tuple[0]), tuple), items)
+	except TypeError:
+		items = items[:]
+		items.sort()
+		return items
+	else:
+		tupledlist.sort()
+		return map(lambda (low, tuple): tuple, tupledlist)
+
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyConsole.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyConsole.py
new file mode 100644
index 0000000..8cb2e4b
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyConsole.py
@@ -0,0 +1,293 @@
+import W
+import Fm
+from WASTEconst import *
+from SpecialKeys import *
+from types import *
+import Events
+import string
+import sys,os
+import traceback
+import MacOS
+import MacPrefs
+import PyInteractive
+
+if not hasattr(sys, 'ps1'):
+	sys.ps1 = '>>> '
+if not hasattr(sys, 'ps2'):
+	sys.ps2 = '... '
+
+
+class ConsoleTextWidget(W.EditText):
+	
+	def __init__(self, *args, **kwargs):
+		apply(W.EditText.__init__, (self,) + args, kwargs)
+		self._inputstart = 0
+		self._buf = ''
+		self.pyinteractive = PyInteractive.PyInteractive()
+	
+		import __main__
+		self._namespace = __main__.__dict__
+	
+	def insert(self, text):
+		self.checkselection()
+		self.ted.WEInsert(text, None, None)
+		self.changed = 1
+		self.selchanged = 1
+	
+	def set_namespace(self, dict):
+		if type(dict) <> DictionaryType:
+			raise TypeError, "The namespace needs to be a dictionary"
+		self._namespace = dict
+	
+	def open(self):
+		W.EditText.open(self)
+		self.write('Python ' + sys.version + '\n' + sys.copyright + '\n')
+		self.write(sys.ps1)
+		self.flush()
+	
+	def key(self, char, event):
+		(what, message, when, where, modifiers) = event
+		if self._enabled and not modifiers & Events.cmdKey or char in arrowkeys:
+			if char not in navigationkeys:
+				self.checkselection()
+			if char == enterkey:
+				char = returnkey
+			selstart, selend = self.getselection()
+			if char == backspacekey:
+				if selstart <= (self._inputstart - (selstart <> selend)):
+					return
+			self.ted.WEKey(ord(char), modifiers)
+			if char not in navigationkeys:
+				self.changed = 1
+			if char not in scrollkeys:
+				self.selchanged = 1
+			self.updatescrollbars()
+			if char == returnkey:
+				text = self.get()[self._inputstart:selstart]
+				saveyield = MacOS.EnableAppswitch(0)
+				self.pyinteractive.executeline(text, self, self._namespace)
+				MacOS.EnableAppswitch(saveyield)
+				selstart, selend = self.getselection()
+				self._inputstart = selstart
+	
+	def domenu_save_as(self, *args):
+		import macfs
+		fss, ok = macfs.StandardPutFile('Save console text as:', 'console.txt')
+		if not ok:
+			return
+		f = open(fss.as_pathname(), 'wb')
+		f.write(self.get())
+		f.close()
+		fss.SetCreatorType(W._signature, 'TEXT')
+	
+	def write(self, text):
+		self._buf = self._buf + text
+		if '\n' in self._buf:
+			self.flush()
+	
+	def flush(self):
+		stuff = string.split(self._buf, '\n')
+		stuff = string.join(stuff, '\r')
+		self.setselection_at_end()
+		self.ted.WEInsert(stuff, None, None)
+		selstart, selend = self.getselection()
+		self._inputstart = selstart
+		self._buf = ""
+		self.ted.WEClearUndo()
+		self.updatescrollbars()
+	
+	def selection_ok(self):
+		selstart, selend = self.getselection()
+		return not (selstart < self._inputstart or selend < self._inputstart)
+	
+	def checkselection(self):
+		if not self.selection_ok():
+			self.setselection_at_end()
+	
+	def setselection_at_end(self):
+		end = self.ted.WEGetTextLength()
+		self.setselection(end, end)
+		self.updatescrollbars()
+		
+	def domenu_cut(self, *args):
+		if not self.selection_ok():
+			return
+		W.EditText.domenu_cut(self)
+	
+	def domenu_paste(self, *args):
+		if not self.selection_ok():
+			self.setselection_at_end()
+		W.EditText.domenu_paste(self)
+	
+	def domenu_clear(self, *args):
+		if not self.selection_ok():
+			return
+		W.EditText.domenu_clear(self)
+
+
+class PyConsole(W.Window):
+	
+	def __init__(self, bounds, show = 1, fontsettings = ("Monaco", 0, 9, (0, 0, 0)), unclosable = 0):
+		W.Window.__init__(self,
+					bounds, 
+					"Python Interactive", 
+					minsize = (200, 100), 
+					tabbable = 0, 
+					show = show)
+		
+		self._unclosable = unclosable
+		consoletext = ConsoleTextWidget((-1, -1, -14, 1), inset = (6, 5), fontsettings = fontsettings)
+		self._bary = W.Scrollbar((-15, 14, 16, -14), consoletext.vscroll, max = 32767)
+		self.consoletext = consoletext
+		self.namespacemenu = W.PopupMenu((-15, -1, 16, 16), [], self.consoletext.set_namespace)
+		self.namespacemenu.bind('<click>', self.makenamespacemenu)
+		self.open()
+	
+	def makenamespacemenu(self, *args):
+		W.SetCursor('watch')
+		namespacelist = self.getnamespacelist()
+		self.namespacemenu.set([("Font settingsŠ", self.dofontsettings), 
+				["Namespace"] + namespacelist, ("Clear buffer", self.clearbuffer), ("Browse namespaceŠ", self.browsenamespace)])
+		currentname = self.consoletext._namespace["__name__"]
+		for i in range(len(namespacelist)):
+			if namespacelist[i][0] == currentname:
+				break
+		else:
+			return
+		# XXX this functionality should be generally available in Wmenus
+		submenuid = self.namespacemenu.menu.menu.GetItemMark(2)
+		menu = self.namespacemenu.menu.bar.menus[submenuid]
+		menu.menu.CheckItem(i + 1, 1)
+	
+	def browsenamespace(self):
+		import PyBrowser, W
+		W.SetCursor('watch')
+		PyBrowser.Browser(self.consoletext._namespace, self.consoletext._namespace["__name__"])
+	
+	def clearbuffer(self):
+		import Res
+		self.consoletext.ted.WEUseText(Res.Resource(''))
+		self.consoletext.write(sys.ps1)
+		self.consoletext.flush()
+	
+	def getnamespacelist(self):
+		import __main__
+		editors = filter(lambda x: x.__class__.__name__ == "Editor", self.parent._windows.values())
+		
+		namespaces = [ ("__main__",__main__.__dict__) ]
+		for ed in editors:
+			modname = os.path.splitext(ed.title)[0]
+			if sys.modules.has_key(modname):
+				module = sys.modules[modname] 
+				namespaces.append((modname, module.__dict__))
+			else:
+				if ed.title[-3:] == '.py':
+					modname = ed.title[:-3]
+				else:
+					modname = ed.title
+				ed.globals["__name__"] = modname
+				namespaces.append((modname, ed.globals))
+		return namespaces
+	
+	def dofontsettings(self):
+		import FontSettings
+		fontsettings = FontSettings.FontDialog(self.consoletext.getfontsettings())
+		if fontsettings:
+			self.consoletext.setfontsettings(fontsettings)
+	
+	def show(self, onoff = 1):
+		W.Window.show(self, onoff)
+		if onoff:
+			self.select()
+	
+	def close(self):
+		if self._unclosable:
+			self.show(0)
+			return -1
+		W.Window.close(self)
+	
+	def writeprefs(self):
+		prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
+		prefs.console.show = self.isvisible()
+		prefs.console.windowbounds = self.getbounds()
+		prefs.console.fontsettings = self.consoletext.getfontsettings()
+		prefs.save()
+
+
+class OutputTextWidget(W.EditText):
+	
+	def _getflags(self):
+		import WASTEconst
+		return WASTEconst.weDoAutoScroll | WASTEconst.weDoMonoStyled | \
+				WASTEconst.weDoUndo
+
+class PyOutput:
+	
+	def __init__(self):
+		self.w = None
+		self.closed = 1
+		self._buf = ''
+		# should be able to set this
+		self.savestdout, self.savestderr = sys.stdout, sys.stderr
+		sys.stderr = sys.stdout = self
+	
+	def write(self, text):
+		self._buf = self._buf + text
+		if '\n' in self._buf:
+			self.flush()
+	
+	def flush(self):
+		self.show()
+		stuff = string.split(self._buf, '\n')
+		stuff = string.join(stuff, '\r')
+		end = self.w.outputtext.ted.WEGetTextLength()
+		self.w.outputtext.setselection(end, end)
+		self.w.outputtext.ted.WEInsert(stuff, None, None)
+		self._buf = ""
+		self.w.outputtext.ted.WEClearUndo()
+		self.w.outputtext.updatescrollbars()
+	
+	def show(self):
+		if self.closed:
+			if not self.w:
+				self.setupwidgets()
+				self.w.open()
+				self.closed = 0
+			else:
+				self.w.show(1)
+				self.closed = 0
+				self.w.select()
+	
+	def setupwidgets(self): 
+		self.w = W.Window((450, 200), "Output", minsize = (200, 100), tabbable = 0)
+		self.w.outputtext = OutputTextWidget((-1, -1, -14, 1), inset = (6, 5))
+		self.w._bary = W.Scrollbar((-15, -1, 16, -14), self.w.outputtext.vscroll, max = 32767)
+		self.w.bind("<close>", self.close)
+		self.w.bind("<activate>", self.activate)
+	
+	def activate(self, onoff):
+		if onoff:
+			self.closed = 0
+	
+	def close(self):
+		self.w.show(0)
+		self.closed = 1
+		return -1
+
+
+def installconsole(defaultshow = 1):
+	global console
+	prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
+	if not prefs.console or not hasattr(prefs.console, 'show'):
+		prefs.console.show = defaultshow
+	if not hasattr(prefs.console, "windowbounds"):
+		prefs.console.windowbounds = (450, 250)
+	if not hasattr(prefs.console, "fontsettings"):
+		prefs.console.fontsettings = ("Monaco", 0, 9, (0, 0, 0))
+	console = PyConsole(prefs.console.windowbounds, prefs.console.show, prefs.console.fontsettings, 1)
+
+def installoutput():
+	global output
+	output = PyOutput()
+
+
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyDebugger.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyDebugger.py
new file mode 100644
index 0000000..c1f5ce0
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyDebugger.py
@@ -0,0 +1,880 @@
+import sys
+import bdb
+import types
+import os
+
+import W
+import WASTEconst
+import PyBrowser
+import Qd
+import Evt
+import Lists
+import MacOS
+_filenames = {}
+
+SIMPLE_TYPES = (
+	types.NoneType,
+	types.IntType,
+	types.LongType,
+	types.FloatType,
+	types.ComplexType,
+	types.StringType
+)
+
+
+class Debugger(bdb.Bdb):
+	
+	def __init__(self, title = 'Debugger'):
+		bdb.Bdb.__init__(self)
+		self.closed = 1
+		self.title = title
+		self.breaksviewer = None
+		self.reset()
+		self.tracing = 0
+		self.tracingmonitortime = Evt.TickCount()
+		
+		prefs = W.getapplication().getprefs()
+		if prefs.debugger:
+			self.breaks = prefs.debugger.breaks
+			self.bounds, self.horpanes, self.verpanes = prefs.debugger.windowsettings
+			self.tracemagic = prefs.debugger.tracemagic
+			for key in self.breaks.keys():
+				if key[:1] == '<' and key[-1:] == '>':
+					del self.breaks[key]
+		else:
+			self.breaks = {}
+			self.horpanes = (0.4, 0.6)
+			self.verpanes = (0.3, 0.35, 0.35)
+			self.bounds = (600, 400)
+			self.tracemagic = 0
+		self.laststacksel = None
+		self.editors = {}
+	
+	def reset(self):
+		self.currentframe = None
+		self.file = None
+		self.laststack = None
+		self.reason = 'Not running'
+		self.continuewithoutdebugger = 0
+		bdb.Bdb.reset(self)
+		self.forget()
+	
+	def start(self, bottomframe = None, running = 0):
+		W.getapplication().DebuggerQuit = bdb.BdbQuit
+		import Menu
+		Menu.HiliteMenu(0)
+		if self.closed:
+			self.setupwidgets(self.title)
+			self.closed = 0
+		if not self.w.parent.debugger_quitting:
+			self.w.select()
+			raise W.AlertError, 'There is another debugger session busy.'
+		self.reset()
+		self.botframe = bottomframe
+		if running:
+			self.set_continue()
+			self.reason = 'RunningŠ'
+			self.setstate('running')
+		else:
+			self.set_step()
+			self.reason = 'stopped'
+			self.setstate('stopped')
+		sys.settrace(self.trace_dispatch)
+	
+	def stop(self):
+		self.set_quit()
+		if self.w.parent:
+			self.exit_mainloop()
+			self.resetwidgets()
+	
+	def set_continue_without_debugger(self):
+		sys.settrace(None)
+		self.set_quit()
+		self.clear_tracefuncs()
+		self.continuewithoutdebugger = 1
+		if self.w.parent:
+			self.exit_mainloop()
+			self.resetwidgets()
+	
+	def clear_tracefuncs(self):
+		try:
+			raise 'spam'
+		except:
+			pass
+		frame = sys.exc_traceback.tb_frame
+		while frame is not None:
+			del frame.f_trace
+			frame = frame.f_back
+	
+	def postmortem(self, exc_type, exc_value, traceback):
+		if self.closed:
+			self.setupwidgets(self.title)
+			self.closed = 0
+		if not self.w.parent.debugger_quitting:
+			raise W.AlertError, 'There is another debugger session busy.'
+		self.reset()
+		if traceback:
+			self.botframe = traceback.tb_frame
+			while traceback.tb_next <> None:
+				traceback = traceback.tb_next
+			frame = traceback.tb_frame
+		else:
+			self.botframe = None
+			frame = None
+		self.w.panes.bottom.buttons.killbutton.enable(1)
+		self.reason = '(dead) ' + self.formatexception(exc_type, exc_value)
+		self.w.select()
+		self.setup(frame, traceback)
+		self.setstate('dead')
+		self.showstack(self.curindex)
+		self.showframe(self.curindex)
+	
+	def setupwidgets(self, title):
+		self.w = w = W.Window(self.bounds, title, minsize = (500, 300))
+		
+		w.panes = W.HorizontalPanes((8, 4, -8, -8), self.horpanes)
+		
+		w.panes.browserpanes = browserpanes = W.VerticalPanes(None, self.verpanes)
+		
+		browserpanes.stacklist = W.Group(None)
+		browserpanes.stacklist.title = W.TextBox((4, 0, 0, 12), 'Stack')
+		browserpanes.stacklist.stack = W.List((0, 16, 0, 0), callback = self.do_stack, flags = Lists.lOnlyOne)
+		
+		browserpanes.locals = W.Group(None)
+		browserpanes.locals.title = W.TextBox((4, 0, 0, 12), 'Local variables')
+		browserpanes.locals.browser = PyBrowser.BrowserWidget((0, 16, 0, 0))
+		
+		browserpanes.globals = W.Group(None)
+		browserpanes.globals.title = W.TextBox((4, 0, 0, 12), 'Global variables')
+		browserpanes.globals.browser = PyBrowser.BrowserWidget((0, 16, 0, 0))
+		
+		w.panes.bottom = bottom = W.Group(None)
+		bottom.src = src = W.Group((0, 52, 0, 0))
+		source = SourceViewer((1, 1, -15, -15), readonly = 1, debugger = self)
+		src.optionsmenu = W.PopupMenu((-16, 0, 16, 16), [])
+		src.optionsmenu.bind('<click>', self.makeoptionsmenu)
+		
+		src._barx = W.Scrollbar((0, -16, -15, 16), source.hscroll, max = 32767)
+		src._bary = W.Scrollbar((-16, 15, 16, -15), source.vscroll, max = 32767)
+		src.source = source
+		src.frame = W.Frame((0, 0, -15, -15))
+		
+		bottom.tracingmonitor = TracingMonitor((0, 23, 6, 6))
+		bottom.state = W.TextBox((12, 20, 0, 16), self.reason)
+		
+		bottom.srctitle = W.TextBox((12, 36, 0, 14))
+		bottom.buttons = buttons = W.Group((12, 0, 0, 16))
+		
+		buttons.runbutton = W.Button((0, 0, 50, 16), "Run", self.do_run)
+		buttons.stopbutton = W.Button((58, 0, 50, 16), "Stop", self.do_stop)
+		buttons.killbutton = W.Button((116, 0, 50, 16), "Kill", self.do_kill)
+		buttons.line = W.VerticalLine((173, 0, 0, 0))
+		buttons.stepbutton = W.Button((181, 0, 50, 16), "Step", self.do_step)
+		buttons.stepinbutton = W.Button((239, 0, 50, 16), "Step in", self.do_stepin)
+		buttons.stepoutbutton = W.Button((297, 0, 50, 16), "Step out", self.do_stepout)
+		
+		w.bind('cmdr', buttons.runbutton.push)
+		w.bind('cmd.', buttons.stopbutton.push)
+		w.bind('cmdk', buttons.killbutton.push)
+		w.bind('cmds', buttons.stepbutton.push)
+		w.bind('cmdt', buttons.stepinbutton.push)
+		w.bind('cmdu', buttons.stepoutbutton.push)
+		
+		w.bind('<close>', self.close)
+		
+		w.open()
+		w.xxx___select(w.panes.bottom.src.source)
+	
+	def makeoptionsmenu(self):
+		options = [('Clear breakpoints', self.w.panes.bottom.src.source.clearbreakpoints), 
+				('Clear all breakpoints', self.clear_all_breaks),
+				('Edit breakpointsŠ', self.edit_breaks), '-',
+				(self.tracemagic and 
+					'Disable __magic__ tracing' or 'Enable __magic__ tracing', self.togglemagic)]
+		self.w.panes.bottom.src.optionsmenu.set(options)
+	
+	def edit_breaks(self):
+		if self.breaksviewer:
+			self.breaksviewer.select()
+		else:
+			self.breaksviewer = BreakpointsViewer(self)
+	
+	def togglemagic(self):
+		self.tracemagic = not self.tracemagic
+	
+	def setstate(self, state):
+		self.w.panes.bottom.tracingmonitor.reset()
+		self.w.panes.bottom.state.set(self.reason)
+		buttons = self.w.panes.bottom.buttons
+		if state == 'stopped':
+			buttons.runbutton.enable(1)
+			buttons.stopbutton.enable(0)
+			buttons.killbutton.enable(1)
+			buttons.stepbutton.enable(1)
+			buttons.stepinbutton.enable(1)
+			buttons.stepoutbutton.enable(1)
+		elif state == 'running':
+			buttons.runbutton.enable(0)
+			buttons.stopbutton.enable(1)
+			buttons.killbutton.enable(1)
+			buttons.stepbutton.enable(0)
+			buttons.stepinbutton.enable(0)
+			buttons.stepoutbutton.enable(0)
+		elif state == 'idle':
+			buttons.runbutton.enable(0)
+			buttons.stopbutton.enable(0)
+			buttons.killbutton.enable(0)
+			buttons.stepbutton.enable(0)
+			buttons.stepinbutton.enable(0)
+			buttons.stepoutbutton.enable(0)
+		elif state == 'dead':
+			buttons.runbutton.enable(0)
+			buttons.stopbutton.enable(0)
+			buttons.killbutton.enable(1)
+			buttons.stepbutton.enable(0)
+			buttons.stepinbutton.enable(0)
+			buttons.stepoutbutton.enable(0)
+		else:
+			print 'unknown state:', state
+	
+	def resetwidgets(self):
+		self.reason = ''
+		self.w.panes.bottom.srctitle.set('')
+		self.w.panes.bottom.src.source.set('')
+		self.w.panes.browserpanes.stacklist.stack.set([])
+		self.w.panes.browserpanes.locals.browser.set({})
+		self.w.panes.browserpanes.globals.browser.set({})
+		self.setstate('idle')
+	
+	# W callbacks
+	
+	def close(self):
+		self.set_quit()
+		self.exit_mainloop()
+		self.closed = 1
+		
+		self.unregister_editor(self.w.panes.bottom.src.source, 
+				self.w.panes.bottom.src.source.file)
+		self.horpanes = self.w.panes.getpanesizes()
+		self.verpanes = self.w.panes.browserpanes.getpanesizes()
+		self.bounds = self.w.getbounds()
+		prefs = W.getapplication().getprefs()
+		prefs.debugger.breaks = self.breaks
+		prefs.debugger.windowsettings = self.bounds, self.horpanes, self.verpanes
+		prefs.debugger.tracemagic = self.tracemagic
+		prefs.save()
+	
+	# stack list callback
+	
+	def do_stack(self, isdbl):
+		sel = self.w.panes.browserpanes.stacklist.stack.getselection()
+		if isdbl:
+			if sel:
+				frame, lineno = self.stack[sel[0] + 1]
+				filename = frame.f_code.co_filename
+				editor = self.w._parentwindow.parent.openscript(filename, lineno)
+				if self.breaks.has_key(filename):
+					editor.showbreakpoints(1)
+		else:
+			if sel and sel <> self.laststacksel:
+				self.showframe(sel[0] + 1)
+			self.laststacksel = sel
+	
+	def geteditor(self, filename):
+		if filename[:1] == '<' and filename[-1:] == '>':
+			editor = W.getapplication().getscript(filename[1:-1])
+		else:
+			editor = W.getapplication().getscript(filename)
+		return editor
+	
+	# button callbacks
+	
+	def do_run(self):
+		self.running()
+		self.set_continue()
+		self.exit_mainloop()
+	
+	def do_stop(self):
+		self.set_step()
+	
+	def do_kill(self):
+		self.set_quit()
+		self.exit_mainloop()
+		self.resetwidgets()
+	
+	def do_step(self):
+		self.running()
+		self.set_next(self.curframe)
+		self.exit_mainloop()
+	
+	def do_stepin(self):
+		self.running()
+		self.set_step()
+		self.exit_mainloop()
+	
+	def do_stepout(self):
+		self.running()
+		self.set_return(self.curframe)
+		self.exit_mainloop()
+	
+	def running(self):
+		W.SetCursor('watch')
+		self.reason = 'RunningŠ'
+		self.setstate('running')
+		#self.w.panes.bottom.src.source.set('')
+		#self.w.panes.browserpanes.stacklist.stack.set([])
+		#self.w.panes.browserpanes.locals.browser.set({})
+		#self.w.panes.browserpanes.globals.browser.set({})
+	
+	def exit_mainloop(self):
+		self.w.parent.debugger_quitting = 1
+	
+	#
+	
+	def showframe(self, stackindex):
+		(frame, lineno) = self.stack[stackindex]
+		W.SetCursor('watch')
+		filename = frame.f_code.co_filename
+		if filename <> self.file:
+			editor = self.geteditor(filename)
+			if editor:
+				self.w.panes.bottom.src.source.set(editor.get(), filename)
+			else:
+				try:
+					f = open(filename, 'rb')
+					data = f.read()
+					f.close()
+				except IOError:
+					if filename[-3:] == '.py':
+						import imp
+						modname = os.path.basename(filename)[:-3]
+						try:
+							f, filename, (suff, mode, dummy) = imp.find_module(modname)
+						except ImportError:
+							self.w.panes.bottom.src.source.set('can¹t find file')
+						else:
+							f.close()
+							if suff == '.py':
+								f = open(filename, 'rb')
+								data = f.read()
+								f.close()
+								self.w.panes.bottom.src.source.set(data, filename)
+							else:
+								self.w.panes.bottom.src.source.set('can¹t find file')
+					else:
+						self.w.panes.bottom.src.source.set('can¹t find file')
+				else:
+					self.w.panes.bottom.src.source.set(data, filename)
+			self.file = filename
+		self.w.panes.bottom.srctitle.set('Source: ' + filename + ((lineno > 0) and (' (line %d)' % lineno) or ' '))
+		self.goto_line(lineno)
+		self.lineno = lineno
+		self.showvars((frame, lineno))
+	
+	def showvars(self, (frame, lineno)):
+		if frame.f_locals is not frame.f_globals:
+			locals = frame.f_locals
+		else:
+			locals = {'Same as Globals':''}
+		filteredlocals = {}
+		for key, value in locals.items():
+			# empty key is magic for Python 1.4; '.' is magic for 1.5...
+			if not key or key[0] <> '.':
+				filteredlocals[key] = value
+		self.w.panes.browserpanes.locals.browser.set(filteredlocals)
+		self.w.panes.browserpanes.globals.browser.set(frame.f_globals)
+	
+	def showstack(self, stackindex):
+		stack = []
+		for frame, lineno in self.stack[1:]:
+			filename = frame.f_code.co_filename
+			try:
+				filename = _filenames[filename]
+			except KeyError:
+				if filename[:1] + filename[-1:] <> '<>':
+					filename = os.path.basename(filename)
+				_filenames[frame.f_code.co_filename] = filename
+			funcname = frame.f_code.co_name
+			if funcname == '?':
+				funcname = '<toplevel>'
+			stack.append(filename + ': ' + funcname)
+		if stack <> self.laststack:
+			self.w.panes.browserpanes.stacklist.stack.set(stack)
+			self.laststack = stack
+		sel = [stackindex - 1]
+		self.w.panes.browserpanes.stacklist.stack.setselection(sel)
+		self.laststacksel = sel
+	
+	def goto_line(self, lineno):
+		if lineno > 0:
+			self.w.panes.bottom.src.source.selectline(lineno - 1)
+		else:
+			self.w.panes.bottom.src.source.setselection(0, 0)
+	
+	# bdb entry points
+	
+#	def user_call(self, frame, argument_list):
+#		self.reason = 'Calling'
+#		self.interaction(frame, None)
+	
+	def user_line(self, frame):
+		# This function is called when we stop or break at this line
+		self.reason = 'Stopped'
+		self.interaction(frame, None)
+	
+	def user_return(self, frame, return_value):
+		# This function is called when a return trap is set here
+		fname = frame.f_code.co_name
+		if fname <> '?':
+			self.reason = 'Returning from %s()' % frame.f_code.co_name
+			frame.f_locals['__return__'] = return_value
+		elif frame.f_back is self.botframe:
+			self.reason = 'Done'
+		else:
+			self.reason = 'Returning'
+		self.interaction(frame, None, 1)
+	
+	def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
+		# This function is called when we stop or break at this line
+		self.reason = self.formatexception(exc_type, exc_value)
+		self.interaction(frame, exc_traceback)
+	
+	def formatexception(self, exc_type, exc_value):
+		if exc_type == SyntaxError:
+			try:
+				value, (filename, lineno, charno, line) = exc_value
+			except:
+				pass
+			else:
+				return str(exc_type) + ': ' + str(value)
+		if type(exc_type) == types.ClassType:
+			nice = exc_type.__name__
+		else:
+			nice = str(exc_type)
+		value = str(exc_value)
+		if exc_value and value:
+			nice = nice + ": " + value
+		return nice
+	
+	def forget(self):
+		self.stack = []
+		self.curindex = 0
+		self.curframe = None
+	
+	def setup(self, f, t, isreturning = 0):
+		self.forget()
+		self.stack, self.curindex = self.get_stack(f, t)
+		self.curframe = self.stack[self.curindex - isreturning][0]
+	
+	def interaction(self, frame, traceback, isreturning = 0):
+		saveport = Qd.GetPort()
+		self.w.select()
+		try:
+			self.setup(frame, traceback, isreturning)
+			self.setstate('stopped')
+			stackindex = self.curindex
+			if isreturning:
+				if frame.f_back is not self.botframe:
+					stackindex = stackindex - 1
+			self.showstack(stackindex)
+			self.showframe(stackindex)
+			self.w.parent.debugger_mainloop()
+			self.forget()
+		finally:
+			Qd.SetPort(saveport)
+	
+	# bdb customization
+	
+	def trace_dispatch(self, frame, event, arg, TickCount = Evt.TickCount):
+		if TickCount() - self.tracingmonitortime > 15:
+			self.tracingmonitortime = TickCount()
+			self.w.panes.bottom.tracingmonitor.toggle()
+		try:
+			try:
+				MacOS.EnableAppswitch(0)
+				if self.quitting:
+					# returning None is not enough, a former BdbQuit exception
+					# might have been eaten by the print statement
+					raise bdb.BdbQuit
+				if event == 'line':
+					return self.dispatch_line(frame)
+				if event == 'call':
+					return self.dispatch_call(frame, arg)
+				if event == 'return':
+					return self.dispatch_return(frame, arg)
+				if event == 'exception':
+					return self.dispatch_exception(frame, arg)
+				print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
+				return self.trace_dispatch
+			finally:
+				MacOS.EnableAppswitch(-1)
+		except KeyboardInterrupt:
+			self.set_step()
+			return self.trace_dispatch
+		except bdb.BdbQuit:
+			if self.continuewithoutdebugger:
+				self.clear_tracefuncs()
+				return
+			else:
+				raise bdb.BdbQuit
+		except:
+			print 'XXX Exception during debugger interaction.', \
+					self.formatexception(sys.exc_type, sys.exc_value)
+			return self.trace_dispatch
+	
+	def dispatch_call(self, frame, arg):
+		if not self.tracemagic and \
+				frame.f_code.co_name[:2] == '__' == frame.f_code.co_name[-2:] and \
+				frame.f_code.co_name <> '__init__':
+			return
+		if self.botframe is None:
+			# First call of dispatch since reset()
+			self.botframe = frame.f_back	# xxx !!! added f_back
+			return self.trace_dispatch
+		if not (self.stop_here(frame) or self.break_anywhere(frame)):
+			# No need to trace this function
+			return # None
+		self.user_call(frame, arg)
+		if self.quitting:
+			raise bdb.BdbQuit
+		return self.trace_dispatch
+	
+	def set_continue(self):
+		# Don't stop except at breakpoints or when finished
+		self.stopframe = self.botframe
+		self.returnframe = None
+		self.quitting = 0
+		# unlike in bdb/pdb, there's a chance that breakpoints change 
+		# *while* a program (this program ;-) is running. It's actually quite likely.
+		# So we don't delete frame.f_trace until the bottom frame if there are no breakpoints.
+	
+	def set_break(self, filename, lineno):
+		if not self.breaks.has_key(filename):
+			self.breaks[filename] = []
+		list = self.breaks[filename]
+		if lineno in list:
+			return 'There is already a breakpoint there!'
+		list.append(lineno)
+		list.sort()	# I want to keep them neatly sorted; easier for drawing
+		self.update_breaks(filename)
+	
+	def clear_break(self, filename, lineno):
+		bdb.Bdb.clear_break(self, filename, lineno)
+		self.update_breaks(filename)
+	
+	def clear_all_file_breaks(self, filename):
+		bdb.Bdb.clear_all_file_breaks(self, filename)
+		self.update_breaks(filename)
+	
+	def clear_all_breaks(self):
+		bdb.Bdb.clear_all_breaks(self)
+		for editors in self.editors.values():
+			for editor in editors:
+				editor.drawbreakpoints()
+	
+	# special
+	
+	def toggle_break(self, filename, lineno):
+		if self.get_break(filename, lineno):
+			self.clear_break(filename, lineno)
+		else:
+			self.set_break(filename, lineno)
+	
+	def clear_breaks_above(self, filename, above):
+		if not self.breaks.has_key(filename):
+			return 'There are no breakpoints in that file!'
+		for lineno in self.breaks[filename][:]:
+			if lineno > above:
+				self.breaks[filename].remove(lineno)
+		if not self.breaks[filename]:
+			del self.breaks[filename]
+	
+	# editor stuff
+	
+	def update_breaks(self, filename):
+		if self.breaksviewer:
+			self.breaksviewer.update()
+		if self.editors.has_key(filename):
+			for editor in self.editors[filename]:
+				if editor._debugger:	# XXX
+					editor.drawbreakpoints()
+				else:
+					print 'xxx dead editor!'
+	
+	def update_allbreaks(self):
+		if self.breaksviewer:
+			self.breaksviewer.update()
+		for filename in self.breaks.keys():
+			if self.editors.has_key(filename):
+				for editor in self.editors[filename]:
+					if editor._debugger:	# XXX
+						editor.drawbreakpoints()
+					else:
+						print 'xxx dead editor!'
+	
+	def register_editor(self, editor, filename):
+		if not filename:
+			return
+		if not self.editors.has_key(filename):
+			self.editors[filename] = [editor]
+		elif editor not in self.editors[filename]:
+			self.editors[filename].append(editor)
+	
+	def unregister_editor(self, editor, filename):
+		if not filename:
+			return
+		try:
+			self.editors[filename].remove(editor)
+			if not self.editors[filename]:
+				del self.editors[filename]
+				# if this was an untitled window, clear the breaks.
+				if filename[:1] == '<' and filename[-1:] == '>' and \
+						self.breaks.has_key(filename):
+					self.clear_all_file_breaks(filename)
+		except (KeyError, ValueError):
+			pass
+		
+
+class SourceViewer(W.PyEditor):
+	
+	def __init__(self, *args, **kwargs):
+		apply(W.PyEditor.__init__, (self,) + args, kwargs)
+		self.bind('<click>', self.clickintercept)
+	
+	def clickintercept(self, point, modifiers):
+		if self._parentwindow._currentwidget <> self and not self.pt_in_breaks(point):
+			self._parentwindow.xxx___select(self)
+			return 1
+	
+	def _getviewrect(self):
+		l, t, r, b = self._bounds
+		if self._debugger:
+			return (l + 12, t + 2, r - 1, b - 2)
+		else:
+			return (l + 5, t + 2, r - 1, b - 2)
+	
+	def select(self, onoff, isclick = 0):
+		if W.SelectableWidget.select(self, onoff):
+			return
+		self.SetPort()
+		#if onoff:
+		#	self.ted.WEActivate()
+		#else:
+		#	self.ted.WEDeactivate()
+		self.drawselframe(onoff)
+	
+	def drawselframe(self, onoff):
+		pass
+
+
+class BreakpointsViewer:
+	
+	def __init__(self, debugger):
+		self.debugger = debugger
+		import Lists
+		self.w = W.Window((300, 250), 'Breakpoints', minsize = (200, 200))
+		self.w.panes = W.HorizontalPanes((8, 8, -8, -32), (0.3, 0.7))
+		self.w.panes.files = W.List(None, callback = self.filehit)		#, flags = Lists.lOnlyOne)
+		self.w.panes.gr = W.Group(None)
+		self.w.panes.gr.breaks = W.List((0, 0, -130, 0), callback = self.linehit)	#, flags = Lists.lOnlyOne)
+		self.w.panes.gr.openbutton = W.Button((-80, 4, 0, 16), 'ViewŠ', self.openbuttonhit)
+		self.w.panes.gr.deletebutton = W.Button((-80, 28, 0, 16), 'Delete', self.deletebuttonhit)
+		
+		self.w.bind('<close>', self.close)
+		self.w.bind('backspace', self.w.panes.gr.deletebutton.push)
+		
+		self.setup()
+		self.w.open()
+		self.w.panes.gr.openbutton.enable(0)
+		self.w.panes.gr.deletebutton.enable(0)
+		self.curfile = None
+	
+	def deletebuttonhit(self):
+		if self.w._currentwidget == self.w.panes.files:
+			self.del_filename()
+		else:
+			self.del_number()
+		self.checkbuttons()
+	
+	def del_number(self):
+		if self.curfile is None:
+			return
+		sel = self.w.panes.gr.breaks.getselectedobjects()
+		for lineno in sel:
+			self.debugger.clear_break(self.curfile, lineno)
+	
+	def del_filename(self):
+		sel = self.w.panes.files.getselectedobjects()
+		for filename in sel:
+			self.debugger.clear_all_file_breaks(filename)
+		self.debugger.update_allbreaks()
+	
+	def setup(self):
+		files = self.debugger.breaks.keys()
+		files.sort()
+		self.w.panes.files.set(files)
+	
+	def close(self):
+		self.debugger.breaksviewer = None
+		self.debugger = None
+	
+	def update(self):
+		sel = self.w.panes.files.getselectedobjects()
+		self.setup()
+		self.w.panes.files.setselectedobjects(sel)
+		sel = self.w.panes.files.getselection()
+		if len(sel) == 0 and self.curfile:
+			self.w.panes.files.setselectedobjects([self.curfile])
+		self.filehit(0)
+	
+	def select(self):
+		self.w.select()
+	
+	def selectfile(self, file):
+		self.w.panes.files.setselectedobjects([file])
+		self.filehit(0)			
+	
+	def openbuttonhit(self):
+		self.filehit(1)
+	
+	def filehit(self, isdbl):
+		sel = self.w.panes.files.getselectedobjects()
+		if isdbl:
+			for filename in sel:
+				lineno = None
+				if filename == self.curfile:
+					linesel = self.w.panes.gr.breaks.getselectedobjects()
+					if linesel:
+						lineno = linesel[-1]
+					elif self.w.panes.gr.breaks:
+						lineno = self.w.panes.gr.breaks[0]
+				editor = self.w._parentwindow.parent.openscript(filename, lineno)
+				editor.showbreakpoints(1)
+			return
+		if len(sel) == 1:
+			file = sel[0]
+			filebreaks = self.debugger.breaks[file][:]
+			if self.curfile == file:
+				linesel = self.w.panes.gr.breaks.getselectedobjects()
+			self.w.panes.gr.breaks.set(filebreaks)
+			if self.curfile == file:
+				self.w.panes.gr.breaks.setselectedobjects(linesel)
+			self.curfile = file
+		else:
+			if len(sel) <> 0:
+				self.curfile = None
+			self.w.panes.gr.breaks.set([])
+		self.checkbuttons()
+	
+	def linehit(self, isdbl):
+		if isdbl:
+			files = self.w.panes.files.getselectedobjects()
+			if len(files) <> 1:
+				return
+			filename = files[0]
+			linenos = self.w.panes.gr.breaks.getselectedobjects()
+			if not linenos:
+				return
+			lineno = linenos[-1]
+			editor = self.w._parentwindow.parent.openscript(filename, lineno)
+			editor.showbreakpoints(1)
+		self.checkbuttons()
+	
+	def checkbuttons(self):
+		if self.w.panes.files.getselection():
+			self.w.panes.gr.openbutton.enable(1)
+			self.w._parentwindow.setdefaultbutton(self.w.panes.gr.openbutton)
+			if self.w._currentwidget == self.w.panes.files:
+				if self.w.panes.files.getselection():
+					self.w.panes.gr.deletebutton.enable(1)
+				else:
+					self.w.panes.gr.deletebutton.enable(0)
+			else:
+				if self.w.panes.gr.breaks.getselection():
+					self.w.panes.gr.deletebutton.enable(1)
+				else:
+					self.w.panes.gr.deletebutton.enable(0)
+		else:
+			self.w.panes.gr.openbutton.enable(0)
+			self.w.panes.gr.deletebutton.enable(0)
+
+
+class TracingMonitor(W.Widget):
+	
+	def __init__(self, *args, **kwargs):
+		apply(W.Widget.__init__, (self,) + args, kwargs)
+		self.state = 0
+	
+	def toggle(self):
+		if hasattr(self, "_parentwindow") and self._parentwindow is not None:
+			self.state = self.state % 2 + 1
+			port = Qd.GetPort()
+			self.SetPort()
+			self.draw()
+			Qd.SetPort(port)
+	
+	def reset(self):
+		if self._parentwindow:
+			self.state = 0
+			port = Qd.GetPort()
+			self.SetPort()
+			self.draw()
+			Qd.SetPort(port)
+	
+	def draw(self, visRgn = None):
+		if self.state == 2:
+			Qd.PaintOval(self._bounds)
+		else:
+			Qd.EraseOval(self._bounds)
+
+
+# convenience funcs
+
+def postmortem(exc_type, exc_value, tb):
+	d = getdebugger()
+	d.postmortem(exc_type, exc_value, tb)
+
+def start(bottomframe = None):
+	d = getdebugger()
+	d.start(bottomframe)
+
+def startfromhere():
+	d = getdebugger()
+	try:
+		raise 'spam'
+	except:
+		frame = sys.exc_traceback.tb_frame.f_back
+	d.start(frame)
+
+def startfrombottom():
+	d = getdebugger()
+	d.start(_getbottomframe(), 1)
+
+def stop():
+	d = getdebugger()
+	d.stop()
+
+def cont():
+	sys.settrace(None)
+	d = getdebugger()
+	d.set_continue_without_debugger()
+
+def _getbottomframe():
+	try:
+		raise 'spam'
+	except:
+		pass
+	frame = sys.exc_traceback.tb_frame
+	while 1:
+		if frame.f_code.co_name == 'mainloop' or frame.f_back is None:
+			break
+		frame = frame.f_back
+	return frame
+
+_debugger = None
+
+def getdebugger():
+	global _debugger
+	if _debugger is None:
+		_debugger = Debugger()
+	return _debugger
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyEdit.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyEdit.py
new file mode 100644
index 0000000..10f92ff
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyEdit.py
@@ -0,0 +1,963 @@
+"""a simple python editor"""
+
+import W
+import Wtraceback
+from SpecialKeys import *
+
+import macfs
+import MacOS
+import Win
+import Res
+import Evt
+import os
+import imp
+import sys
+import string
+import marshal
+import regex
+
+_scriptuntitledcounter = 1
+_wordchars = string.letters + string.digits + "_"
+
+
+class Editor(W.Window):
+	
+	def __init__(self, path = "", title = ""):
+		global _scriptuntitledcounter
+		if not path:
+			if title:
+				self.title = title
+			else:
+				self.title = "Untitled Script " + `_scriptuntitledcounter`
+				_scriptuntitledcounter = _scriptuntitledcounter + 1
+			text = ""
+			self._creator = W._signature
+		elif os.path.exists(path):
+			self.title = os.path.basename(path)
+			f = open(path, "rb")
+			text = f.read()
+			f.close()
+			fss = macfs.FSSpec(path)
+			self._creator, filetype = fss.GetCreatorType()
+		else:
+			raise IOError, "file '%s' does not exist" % path
+		self.path = path
+		
+		self.settings = {}
+		if self.path:
+			self.readwindowsettings()
+		if self.settings.has_key("windowbounds"):
+			bounds = self.settings["windowbounds"]
+		else:
+			bounds = (500, 250)
+		if self.settings.has_key("fontsettings"):
+			self.fontsettings = self.settings["fontsettings"]
+		else:
+			self.fontsettings = ("Python-Sans", 0, 9, (0, 0, 0))
+		W.Window.__init__(self, bounds, self.title, minsize = (330, 120), tabbable = 0)
+		
+		self.setupwidgets(text)
+		if self.settings.has_key("selection"):
+			selstart, selend = self.settings["selection"]
+			self.setselection(selstart, selend)
+		self.open()
+		self.setinfotext()
+		self.globals = {}
+		self._buf = ""  # for write method
+		self.debugging = 0
+		self.profiling = 0
+		if self.settings.has_key("run_as_main"):
+			self.run_as_main = self.settings["run_as_main"]
+		else:
+			self.run_as_main = 0
+	
+	def readwindowsettings(self):
+		try:
+			resref = Res.OpenResFile(self.path)
+		except Res.Error:
+			return
+		try:
+			Res.UseResFile(resref)
+			data = Res.Get1Resource('PyWS', 128)
+			self.settings = marshal.loads(data.data)
+		except:
+			pass
+		Res.CloseResFile(resref)
+		
+	def writewindowsettings(self):
+		try:
+			resref = Res.OpenResFile(self.path)
+		except Res.Error:
+			Res.CreateResFile(self.path)
+			resref = Res.OpenResFile(self.path)
+		try:
+			data = Res.Resource(marshal.dumps(self.settings))
+			Res.UseResFile(resref)
+			try:
+				temp = Res.Get1Resource('PyWS', 128)
+				temp.RemoveResource()
+			except Res.Error:
+				pass
+			data.AddResource('PyWS', 128, "window settings")
+		finally:
+			Res.UpdateResFile(resref)
+			Res.CloseResFile(resref)
+	
+	def getsettings(self):
+		self.settings = {}
+		self.settings["windowbounds"] = self.getbounds()
+		self.settings["selection"] = self.getselection()
+		self.settings["fontsettings"] = self.editgroup.editor.getfontsettings()
+		self.settings["run_as_main"] = self.run_as_main
+	
+	def get(self):
+		return self.editgroup.editor.get()
+	
+	def getselection(self):
+		return self.editgroup.editor.ted.WEGetSelection()
+	
+	def setselection(self, selstart, selend):
+		self.editgroup.editor.setselection(selstart, selend)
+	
+	def getfilename(self):
+		if self.path:
+			return self.path
+		return '<%s>' % self.title
+	
+	def setupwidgets(self, text):
+		topbarheight = 24
+		popfieldwidth = 80
+		self.lastlineno = None
+		
+		# make an editor
+		self.editgroup = W.Group((0, topbarheight + 1, 0, 0))
+		editor = W.PyEditor((0, 0, -15,-15), text, fontsettings = self.fontsettings, 
+				file = self.getfilename())
+		
+		# make the widgets
+		self.popfield = ClassFinder((popfieldwidth - 17, -15, 16, 16), [], self.popselectline)
+		self.linefield = W.EditText((-1, -15, popfieldwidth - 15, 16), inset = (6, 2))
+		self.editgroup._barx = W.Scrollbar((popfieldwidth - 2, -15, -14, 16), editor.hscroll, max = 32767)
+		self.editgroup._bary = W.Scrollbar((-15, 14, 16, -14), editor.vscroll, max = 32767)
+		self.editgroup.editor = editor	# add editor *after* scrollbars
+		
+		self.editgroup.optionsmenu = W.PopupMenu((-15, -1, 16, 16), [])
+		self.editgroup.optionsmenu.bind('<click>', self.makeoptionsmenu)
+		self.hline = W.HorizontalLine((0, topbarheight, 0, 0))
+		self.infotext = W.TextBox((175, 6, -4, 14))
+		self.runbutton = W.Button((5, 4, 80, 16), "Run all", self.run)
+		self.runselbutton = W.Button((90, 4, 80, 16), "Run selection", self.runselection)
+		
+		# bind some keys
+		editor.bind("cmdr", self.runbutton.push)
+		editor.bind("enter", self.runselbutton.push)
+		editor.bind("cmdj", self.domenu_gotoline)
+		editor.bind("cmdd", self.domenu_toggledebugger)
+		editor.bind("<idle>", self.updateselection)
+		
+		editor.bind("cmde", searchengine.setfindstring)
+		editor.bind("cmdf", searchengine.show)
+		editor.bind("cmdg", searchengine.findnext)
+		editor.bind("cmdshiftr", searchengine.replace)
+		editor.bind("cmdt", searchengine.replacefind)
+		
+		self.linefield.bind("return", self.dolinefield)
+		self.linefield.bind("enter", self.dolinefield)
+		self.linefield.bind("tab", self.dolinefield)
+		
+		# intercept clicks
+		editor.bind("<click>", self.clickeditor)
+		self.linefield.bind("<click>", self.clicklinefield)
+	
+	def makeoptionsmenu(self):
+		menuitems = [('Font settingsŠ', self.domenu_fontsettings), 
+				('\0' + chr(self.run_as_main) + 'Run as __main__', self.domenu_toggle_run_as_main), 
+				('Modularize', self.domenu_modularize),
+				('Browse namespaceŠ', self.domenu_browsenamespace), 
+				'-']
+		if self.editgroup.editor._debugger:
+			menuitems = menuitems + [('Disable debugger', self.domenu_toggledebugger),
+				('Clear breakpoints', self.domenu_clearbreakpoints),
+				('Edit breakpointsŠ', self.domenu_editbreakpoints)]
+		else:
+			menuitems = menuitems + [('Enable debugger', self.domenu_toggledebugger)]
+		if self.profiling:
+			menuitems = menuitems + [('Disable profiler', self.domenu_toggleprofiler)]
+		else:
+			menuitems = menuitems + [('Enable profiler', self.domenu_toggleprofiler)]
+		self.editgroup.optionsmenu.set(menuitems)
+	
+	def domenu_toggle_run_as_main(self):
+		self.run_as_main = not self.run_as_main
+		self.editgroup.editor.selchanged = 1
+	
+	def showbreakpoints(self, onoff):
+		self.editgroup.editor.showbreakpoints(onoff)
+		self.debugging = onoff
+	
+	def domenu_clearbreakpoints(self, *args):
+		self.editgroup.editor.clearbreakpoints()
+	
+	def domenu_editbreakpoints(self, *args):
+		self.editgroup.editor.editbreakpoints()
+	
+	def domenu_toggledebugger(self, *args):
+		if not self.debugging:
+			W.SetCursor('watch')
+		self.debugging = not self.debugging
+		self.editgroup.editor.togglebreakpoints()
+		
+	def domenu_toggleprofiler(self, *args):
+		self.profiling = not self.profiling
+	
+	def domenu_browsenamespace(self, *args):
+		import PyBrowser, W
+		W.SetCursor('watch')
+		globals, file = self.getenvironment()
+		modname = _filename_as_modname(self.title)
+		if not modname:
+			modname = self.title
+		PyBrowser.Browser(globals, "Object browser: " + modname)
+	
+	def domenu_modularize(self, *args):
+		modname = _filename_as_modname(self.title)
+		if not modname:
+			raise W.AlertError, 'Can¹t modularize ³%s²' % self.title
+		self.run()	
+		if self.path:
+			file = self.path
+		else:
+			file = self.title
+		
+		if self.globals and not sys.modules.has_key(modname):
+			module = imp.new_module(modname)
+			for attr in self.globals.keys():
+				setattr(module,attr,self.globals[attr])
+			sys.modules[modname] = module
+			self.globals = {}
+	
+	def domenu_fontsettings(self, *args):
+		import FontSettings
+		fontsettings = FontSettings.FontDialog(self.editgroup.editor.getfontsettings())
+		if fontsettings:
+			self.editgroup.editor.setfontsettings(fontsettings)
+	
+	def clicklinefield(self):
+		if self._currentwidget <> self.linefield:
+			self.linefield.select(1)
+			self.linefield.selectall()
+			return 1
+	
+	def clickeditor(self):
+		if self._currentwidget <> self.editgroup.editor:
+			self.dolinefield()
+			return 1
+	
+	def updateselection(self, force = 0):
+		sel = min(self.editgroup.editor.getselection())
+		lineno = self.editgroup.editor.offsettoline(sel)
+		if lineno <> self.lastlineno or force:
+			self.lastlineno = lineno
+			self.linefield.set(str(lineno + 1))
+			self.linefield.selview()
+	
+	def dolinefield(self):
+		try:
+			lineno = string.atoi(self.linefield.get()) - 1
+			if lineno <> self.lastlineno:
+				self.editgroup.editor.selectline(lineno)
+				self.updateselection(1)
+		except:
+			self.updateselection(1)
+		self.editgroup.editor.select(1)
+	
+	def setinfotext(self):
+		if not hasattr(self, 'infotext'):
+			return
+		if self.path:
+			self.infotext.set(self.path)
+		else:
+			self.infotext.set("")
+	
+	def close(self):
+		if self.editgroup.editor.changed:
+			import EasyDialogs
+			import Qd
+			Qd.InitCursor() # XXX should be done by dialog
+			save = EasyDialogs.AskYesNoCancel('Save window ³%s² before closing?' % self.title, 1)
+			if save > 0:
+				if self.domenu_save():
+					return 1
+			elif save < 0:
+				return 1
+		self.globals = None	     # XXX doesn't help... all globals leak :-(
+		W.Window.close(self)
+	
+	def domenu_close(self, *args):
+		return self.close()
+	
+	def domenu_save(self, *args):
+		if not self.path:
+			# Will call us recursively
+			return self.domenu_save_as()
+		data = self.editgroup.editor.get()
+		fp = open(self.path, 'wb')  # open file in binary mode, data has '\r' line-endings
+		fp.write(data)
+		fp.close()
+		fss = macfs.FSSpec(self.path)
+		fss.SetCreatorType(self._creator, 'TEXT')
+		self.getsettings()
+		self.writewindowsettings()
+		self.editgroup.editor.changed = 0
+		self.editgroup.editor.selchanged = 0
+		import linecache
+		if linecache.cache.has_key(self.path):
+			del linecache.cache[self.path]
+		import macostools
+		macostools.touched(self.path)
+	
+	def can_save(self, menuitem):
+		return self.editgroup.editor.changed or self.editgroup.editor.selchanged
+	
+	def domenu_save_as(self, *args):
+		fss, ok = macfs.StandardPutFile('Save as:', self.title)
+		if not ok: 
+			return 1
+		self.showbreakpoints(0)
+		self.path = fss.as_pathname()
+		self.setinfotext()
+		self.title = os.path.split(self.path)[-1]
+		self.wid.SetWTitle(self.title)
+		self.domenu_save()
+		self.editgroup.editor.setfile(self.getfilename())
+		app = W.getapplication()
+		app.makeopenwindowsmenu()
+		if hasattr(app, 'makescriptsmenu'):
+			app = W.getapplication()
+			fss, fss_changed = app.scriptsfolder.Resolve()
+			path = fss.as_pathname()
+			if path == self.path[:len(path)]:
+				W.getapplication().makescriptsmenu()
+	
+	def domenu_gotoline(self, *args):
+		self.linefield.selectall()
+		self.linefield.select(1)
+		self.linefield.selectall()
+	
+	def domenu_selectline(self, *args):
+		self.editgroup.editor.expandselection()
+	
+	def domenu_shiftleft(self, *args):
+		self.editgroup.editor.shiftleft()
+	
+	def domenu_shiftright(self, *args):
+		self.editgroup.editor.shiftright()
+	
+	def domenu_find(self, *args):
+		searchengine.show()
+	
+	def domenu_entersearchstring(self, *args):
+		searchengine.setfindstring()
+	
+	def domenu_replace(self, *args):
+		searchengine.replace()
+	
+	def domenu_findnext(self, *args):
+		searchengine.findnext()
+	
+	def domenu_replacefind(self, *args):
+		searchengine.replacefind()
+	
+	def domenu_run(self, *args):
+		self.runbutton.push()
+	
+	def domenu_runselection(self, *args):
+		self.runselbutton.push()
+	
+	def run(self):
+		pytext = self.editgroup.editor.get()
+		globals, file = self.getenvironment()
+		if self.path:
+			cwd = os.getcwd()
+			os.chdir(os.path.dirname(self.path) + ':')
+		else:
+			cwd = None
+		execstring(pytext, globals, globals, file, self.debugging, self.run_as_main, self.profiling)
+		if cwd:
+			os.chdir(cwd)
+	
+	def runselection(self):
+		self._runselection()
+	
+	def _runselection(self):
+		globals, file = self.getenvironment()
+		locals = globals
+		# select whole lines
+		self.editgroup.editor.expandselection()
+		
+		# get lineno of first selected line
+		selstart, selend = self.editgroup.editor.getselection()
+		selstart, selend = min(selstart, selend), max(selstart, selend)
+		selfirstline = self.editgroup.editor.offsettoline(selstart)
+		alltext = self.editgroup.editor.get()
+		pytext = alltext[selstart:selend]
+		lines = string.split(pytext, '\r')
+		indent = getminindent(lines)
+		if indent == 1:
+			classname = ''
+			alllines = string.split(alltext, '\r')
+			identifieRE_match = _identifieRE.match
+			for i in range(selfirstline - 1, -1, -1):
+				line = alllines[i]
+				if line[:6] == 'class ':
+					classname = string.split(string.strip(line[6:]))[0]
+					classend = identifieRE_match(classname)
+					if classend < 1:
+						raise W.AlertError, 'Can¹t find a class.'
+					classname = classname[:classend]
+					break
+				elif line and line[0] not in '\t#':
+					raise W.AlertError, 'Can¹t find a class.'
+			else:
+				raise W.AlertError, 'Can¹t find a class.'
+			if globals.has_key(classname):
+				locals = globals[classname].__dict__
+			else:
+				raise W.AlertError, 'Can¹t find class ³%s².' % classname
+			for i in range(len(lines)):
+				lines[i] = lines[i][1:]
+			pytext = string.join(lines, '\r')
+		elif indent > 0:
+			raise W.AlertError, 'Can¹t run indented code.'
+		
+		# add newlines to fool compile/exec: a traceback will give the right line number
+		pytext = selfirstline * '\r' + pytext
+		
+		if self.path:
+			cwd = os.getcwd()
+			os.chdir(os.path.dirname(self.path) + ':')
+		else:
+			cwd = None
+		execstring(pytext, globals, locals, file, self.debugging, self.run_as_main, self.profiling)
+		if cwd:
+			os.chdir(cwd)
+	
+	def getenvironment(self):
+		if self.path:
+			file = self.path
+			modname = _filename_as_modname(self.title)
+			if sys.modules.has_key(modname):
+				globals = sys.modules[modname].__dict__
+				self.globals = {}
+			else:
+				globals = self.globals
+		else:
+			file = '<%s>' % self.title
+			globals = self.globals
+		return globals, file
+	
+	def write(self, stuff):
+		"""for use as stdout"""
+		self._buf = self._buf + stuff
+		if '\n' in self._buf:
+			self.flush()
+	
+	def flush(self):
+		stuff = string.split(self._buf, '\n')
+		stuff = string.join(stuff, '\r')
+		end = self.editgroup.editor.ted.WEGetTextLength()
+		self.editgroup.editor.ted.WESetSelection(end, end)
+		self.editgroup.editor.ted.WEInsert(stuff, None, None)
+		self.editgroup.editor.updatescrollbars()
+		self._buf = ""
+		# ? optional:
+		#self.wid.SelectWindow()
+	
+	def getclasslist(self):
+		from string import find, strip
+		editor = self.editgroup.editor
+		text = editor.get()
+		list = []
+		append = list.append
+		functag = "func"
+		classtag = "class"
+		methodtag = "method"
+		pos = -1
+		if text[:4] == 'def ':
+			append((pos + 4, functag))
+			pos = 4
+		while 1:
+			pos = find(text, '\rdef ', pos + 1)
+			if pos < 0:
+				break
+			append((pos + 5, functag))
+		pos = -1
+		if text[:6] == 'class ':
+			append((pos + 6, classtag))
+			pos = 6
+		while 1:
+			pos = find(text, '\rclass ', pos + 1)
+			if pos < 0:
+				break
+			append((pos + 7, classtag))
+		pos = 0
+		while 1:
+			pos = find(text, '\r\tdef ', pos + 1)
+			if pos < 0:
+				break
+			append((pos + 6, methodtag))
+		list.sort()
+		classlist = []
+		methodlistappend = None
+		offsetToLine = editor.ted.WEOffsetToLine
+		getLineRange = editor.ted.WEGetLineRange
+		append = classlist.append
+		identifieRE_match = _identifieRE.match
+		for pos, tag in list:
+			lineno = offsetToLine(pos)
+			lineStart, lineEnd = getLineRange(lineno)
+			line = strip(text[pos:lineEnd])
+			line = line[:identifieRE_match(line)]
+			if tag is functag:
+				append(("def " + line, lineno + 1))
+				methodlistappend = None
+			elif tag is classtag:
+				append(["class " + line])
+				methodlistappend = classlist[-1].append
+			elif methodlistappend and tag is methodtag:
+				methodlistappend(("def " + line, lineno + 1))
+		return classlist
+	
+	def popselectline(self, lineno):
+		self.editgroup.editor.selectline(lineno - 1)
+	
+	def selectline(self, lineno, charoffset = 0):
+		self.editgroup.editor.selectline(lineno - 1, charoffset)
+
+
+class Reporter(Editor):
+	
+	def close(self):
+		self.globals = None
+		W.Window.close(self)
+	
+	def domenu_run(self, *args):
+		self.run()
+	
+	def domenu_runselection(self, *args):
+		self.runselection()
+	
+	def setupwidgets(self, text):
+		topbarheight = -1
+		popfieldwidth = 80
+		self.lastlineno = None
+		
+		# make an editor
+		self.editgroup = W.Group((0, topbarheight + 1, 0, 0))
+		self.editgroup.editor = W.PyEditor((0, 0, -15,-15), text)
+		
+		# make the widgets
+		self.editgroup._barx = W.Scrollbar((popfieldwidth-2, -15, -14, 16), self.editgroup.editor.hscroll, max = 32767)
+		self.editgroup._bary = W.Scrollbar((-15, -1, 16, -14), self.editgroup.editor.vscroll, max = 32767)
+		self.hline = W.HorizontalLine((0, -15, 0, 0))
+		
+		# bind some keys
+		self.editgroup.editor.bind("cmdr", self.run)
+		self.editgroup.editor.bind("enter", self.runselection)
+		
+		self.editgroup.editor.bind("cmde", searchengine.setfindstring)
+		self.editgroup.editor.bind("cmdf", searchengine.show)
+		self.editgroup.editor.bind("cmdg", searchengine.findnext)
+		self.editgroup.editor.bind("cmdshiftr", searchengine.replace)
+		self.editgroup.editor.bind("cmdt", searchengine.replacefind)
+
+
+def _escape(where, what) : 
+	return string.join(string.split(where, what), '\\' + what)
+
+def _makewholewordpattern(word):
+	# first, escape special regex chars
+	for esc in "\\[].*^+$?":
+		word = _escape(word, esc)
+	import regex
+	notwordcharspat = '[^' + _wordchars + ']'
+	pattern = '\(' + word + '\)'
+	if word[0] in _wordchars:
+		pattern = notwordcharspat + pattern
+	if word[-1] in _wordchars:
+		pattern = pattern + notwordcharspat
+	return regex.compile(pattern)
+
+class SearchEngine:
+	
+	def __init__(self):
+		self.visible = 0
+		self.w = None
+		self.parms = {  "find": "",
+					"replace": "",
+					"wrap": 1,
+					"casesens": 1,
+					"wholeword": 1
+				}
+		import MacPrefs
+		prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
+		if prefs.searchengine:
+			self.parms["casesens"] = prefs.searchengine.casesens
+			self.parms["wrap"] = prefs.searchengine.wrap
+			self.parms["wholeword"] = prefs.searchengine.wholeword
+	
+	def show(self):
+		self.visible = 1
+		if self.w:
+			self.w.wid.ShowWindow()
+			self.w.wid.SelectWindow()
+			self.w.find.edit.select(1)
+			self.w.find.edit.selectall()
+			return
+		self.w = W.Dialog((420, 150), "Find")
+		
+		self.w.find = TitledEditText((10, 4, 300, 36), "Search for:")
+		self.w.replace = TitledEditText((10, 100, 300, 36), "Replace with:")
+		
+		self.w.boxes = W.Group((10, 50, 300, 40))
+		self.w.boxes.casesens = W.CheckBox((0, 0, 100, 16), "Case sensitive")
+		self.w.boxes.wholeword = W.CheckBox((0, 20, 100, 16), "Whole word")
+		self.w.boxes.wrap = W.CheckBox((110, 0, 100, 16), "Wrap around")
+		
+		self.buttons = [	("Find",		"cmdf",	 self.find), 
+					("Replace",	     "cmdr",	 self.replace), 
+					("Replace all",	 None,   self.replaceall), 
+					("Don¹t find",  "cmdd",	 self.dont), 
+					("Cancel",	      "cmd.",	 self.cancel)
+				]
+		for i in range(len(self.buttons)):
+			bounds = -90, 22 + i * 24, 80, 16
+			title, shortcut, callback = self.buttons[i]
+			self.w[title] = W.Button(bounds, title, callback)
+			if shortcut:
+				self.w.bind(shortcut, self.w[title].push)
+		self.w.setdefaultbutton(self.w["Don¹t find"])
+		self.w.find.edit.bind("<key>", self.key)
+		self.w.bind("<activate>", self.activate)
+		self.w.open()
+		self.setparms()
+		self.w.find.edit.select(1)
+		self.w.find.edit.selectall()
+		self.checkbuttons()
+	
+	def key(self, char, modifiers):
+		self.w.find.edit.key(char, modifiers)
+		self.checkbuttons()
+		return 1
+	
+	def activate(self, onoff):
+		if onoff:
+			self.checkbuttons()
+	
+	def checkbuttons(self):
+		editor = findeditor(self)
+		if editor:
+			if self.w.find.get():
+				for title, cmd, call in self.buttons[:-2]:
+					self.w[title].enable(1)
+				self.w.setdefaultbutton(self.w["Find"])
+			else:
+				for title, cmd, call in self.buttons[:-2]:
+					self.w[title].enable(0)
+				self.w.setdefaultbutton(self.w["Don¹t find"])
+		else:
+			for title, cmd, call in self.buttons[:-2]:
+				self.w[title].enable(0)
+			self.w.setdefaultbutton(self.w["Don¹t find"])
+	
+	def find(self):
+		self.getparmsfromwindow()
+		if self.findnext():
+			self.hide()
+	
+	def replace(self):
+		editor = findeditor(self)
+		if not editor:
+			return
+		if self.visible:
+			self.getparmsfromwindow()
+		text = editor.getselectedtext()
+		find = self.parms["find"]
+		if not self.parms["casesens"]:
+			find = string.lower(find)
+			text = string.lower(text)
+		if text == find:
+			self.hide()
+			editor.insert(self.parms["replace"])
+	
+	def replaceall(self):
+		editor = findeditor(self)
+		if not editor:
+			return
+		if self.visible:
+			self.getparmsfromwindow()
+		W.SetCursor("watch")
+		find = self.parms["find"]
+		if not find:
+			return
+		findlen = len(find)
+		replace = self.parms["replace"]
+		replacelen = len(replace)
+		Text = editor.get()
+		if not self.parms["casesens"]:
+			find = string.lower(find)
+			text = string.lower(Text)
+		else:
+			text = Text
+		newtext = ""
+		pos = 0
+		counter = 0
+		while 1:
+			if self.parms["wholeword"]:
+				wholewordRE = _makewholewordpattern(find)
+				wholewordRE.search(text, pos)
+				if wholewordRE.regs:
+					pos = wholewordRE.regs[1][0]
+				else:
+					pos = -1
+			else:
+				pos = string.find(text, find, pos)
+			if pos < 0:
+				break
+			counter = counter + 1
+			text = text[:pos] + replace + text[pos + findlen:]
+			Text = Text[:pos] + replace + Text[pos + findlen:]
+			pos = pos + replacelen
+		W.SetCursor("arrow")
+		if counter:
+			self.hide()
+			import EasyDialogs
+			import Res
+			editor.changed = 1
+			editor.selchanged = 1
+			editor.ted.WEUseText(Res.Resource(Text))
+			editor.ted.WECalText()
+			editor.SetPort()
+			Win.InvalRect(editor._bounds)
+			#editor.ted.WEUpdate(self.w.wid.GetWindowPort().visRgn)
+			EasyDialogs.Message("Replaced %d occurrences" % counter)
+	
+	def dont(self):
+		self.getparmsfromwindow()
+		self.hide()
+	
+	def replacefind(self):
+		self.replace()
+		self.findnext()
+	
+	def setfindstring(self):
+		editor = findeditor(self)
+		if not editor:
+			return
+		find = editor.getselectedtext()
+		if not find:
+			return
+		self.parms["find"] = find
+		if self.w:
+			self.w.find.edit.set(self.parms["find"])
+			self.w.find.edit.selectall()
+	
+	def findnext(self):
+		editor = findeditor(self)
+		if not editor:
+			return
+		find = self.parms["find"]
+		if not find:
+			return
+		text = editor.get()
+		if not self.parms["casesens"]:
+			find = string.lower(find)
+			text = string.lower(text)
+		selstart, selend = editor.getselection()
+		selstart, selend = min(selstart, selend), max(selstart, selend)
+		if self.parms["wholeword"]:
+			wholewordRE = _makewholewordpattern(find)
+			wholewordRE.search(text, selend)
+			if wholewordRE.regs:
+				pos = wholewordRE.regs[1][0]
+			else:
+				pos = -1
+		else:
+			pos = string.find(text, find, selend)
+		if pos >= 0:
+			editor.setselection(pos, pos + len(find))
+			return 1
+		elif self.parms["wrap"]:
+			if self.parms["wholeword"]:
+				wholewordRE.search(text, 0)
+				if wholewordRE.regs:
+					pos = wholewordRE.regs[1][0]
+				else:
+					pos = -1
+			else:
+				pos = string.find(text, find)
+			if selstart > pos >= 0:
+				editor.setselection(pos, pos + len(find))
+				return 1
+	
+	def setparms(self):
+		for key, value in self.parms.items():
+			try:
+				self.w[key].set(value)
+			except KeyError:
+				self.w.boxes[key].set(value)
+	
+	def getparmsfromwindow(self):
+		if not self.w:
+			return
+		for key, value in self.parms.items():
+			try:
+				value = self.w[key].get()
+			except KeyError:
+				value = self.w.boxes[key].get()
+			self.parms[key] = value
+	
+	def cancel(self):
+		self.hide()
+		self.setparms()
+	
+	def hide(self):
+		if self.w:
+			self.w.wid.HideWindow()
+			self.visible = 0
+	
+	def writeprefs(self):
+		import MacPrefs
+		self.getparmsfromwindow()
+		prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
+		prefs.searchengine.casesens = self.parms["casesens"]
+		prefs.searchengine.wrap = self.parms["wrap"]
+		prefs.searchengine.wholeword = self.parms["wholeword"]
+		prefs.save()
+	
+
+class TitledEditText(W.Group):
+	
+	def __init__(self, possize, title, text = ""):
+		W.Group.__init__(self, possize)
+		self.title = W.TextBox((0, 0, 0, 16), title)
+		self.edit = W.EditText((0, 16, 0, 0), text)
+	
+	def set(self, value):
+		self.edit.set(value)
+	
+	def get(self):
+		return self.edit.get()
+
+
+class ClassFinder(W.PopupWidget):
+	
+	def click(self, point, modifiers):
+		W.SetCursor("watch")
+		self.set(self._parentwindow.getclasslist())
+		W.PopupWidget.click(self, point, modifiers)
+
+
+def getminindent(lines):
+	indent = -1
+	for line in lines:
+		stripped = string.strip(line)
+		if not stripped or stripped[0] == '#':
+			continue
+		if indent < 0 or line[:indent] <> indent * '\t':
+			indent = 0
+			for c in line:
+				if c <> '\t':
+					break
+				indent = indent + 1
+	return indent
+
+
+def getoptionkey():
+	return not not ord(Evt.GetKeys()[7]) & 0x04
+
+
+def execstring(pytext, globals, locals, filename = "<string>", debugging = 0, 
+			run_as_main = 0, profiling = 0):
+	if debugging:
+		import PyDebugger, bdb
+		BdbQuit = bdb.BdbQuit
+	else:
+		BdbQuit = 'BdbQuitDummyException'
+	pytext = string.split(pytext, '\r')
+	pytext = string.join(pytext, '\n') + '\n'
+	W.SetCursor("watch")
+	modname = os.path.basename(filename)
+	if modname[-3:] == '.py':
+		modname = modname[:-3]
+	if run_as_main:
+		globals['__name__'] = '__main__'
+	else:
+		globals['__name__'] = modname
+	globals['__file__'] = filename
+	sys.argv = [filename]
+	try:
+		code = compile(pytext, filename, "exec")
+	except:
+		tracebackwindow.traceback(1, filename)
+		return
+	try:
+		if debugging:
+			PyDebugger.startfromhere()
+		else:
+			MacOS.EnableAppswitch(0)
+		try:
+			if profiling:
+				import profile, ProfileBrowser
+				p = profile.Profile(ProfileBrowser.timer)
+				p.set_cmd(filename)
+				try:
+					p.runctx(code, globals, locals)
+				finally:
+					import pstats
+					
+					stats = pstats.Stats(p)
+					ProfileBrowser.ProfileBrowser(stats)
+			else:
+				exec code in globals, locals
+		finally:
+			MacOS.EnableAppswitch(-1)
+	except W.AlertError, detail:
+		raise W.AlertError, detail
+	except (KeyboardInterrupt, BdbQuit):
+		pass
+	except:
+		if debugging:
+			sys.settrace(None)
+			PyDebugger.postmortem(sys.exc_type, sys.exc_value, sys.exc_traceback)
+			return
+		else:
+			tracebackwindow.traceback(1, filename)
+	if debugging:
+		sys.settrace(None)
+		PyDebugger.stop()
+
+
+_identifieRE = regex.compile("[A-Za-z_][A-Za-z_0-9]*")
+
+def _filename_as_modname(fname):
+	if fname[-3:] == '.py':
+		mname = fname[:-3]
+		if _identifieRE.match(mname) == len(mname):
+			return mname
+
+def findeditor(topwindow, fromtop = 0):
+	wid = Win.FrontWindow()
+	if not fromtop:
+		if topwindow.w and wid == topwindow.w.wid:
+			wid = topwindow.w.wid.GetNextWindow()
+	if not wid:
+		return
+	window = W.getapplication()._windows[wid]
+	if not W.HasBaseClass(window, Editor):
+		return
+	return window.editgroup.editor
+
+
+searchengine = SearchEngine()
+tracebackwindow = Wtraceback.TraceBack()
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyFontify.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyFontify.py
new file mode 100644
index 0000000..6503636
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyFontify.py
@@ -0,0 +1,153 @@
+"""Module to analyze Python source code; for syntax coloring tools.
+
+Interface:
+	tags = fontify(pytext, searchfrom, searchto)
+
+The 'pytext' argument is a string containing Python source code.
+The (optional) arguments 'searchfrom' and 'searchto' may contain a slice in pytext. 
+The returned value is a lists of tuples, formatted like this:
+	[('keyword', 0, 6, None), ('keyword', 11, 17, None), ('comment', 23, 53, None), etc. ]
+The tuple contents are always like this:
+	(tag, startindex, endindex, sublist)
+tag is one of 'keyword', 'string', 'comment' or 'identifier'
+sublist is not used, hence always None. 
+"""
+
+# Based on FontText.py by Mitchell S. Chapman,
+# which was modified by Zachary Roadhouse,
+# then un-Tk'd by Just van Rossum.
+# Many thanks for regular expression debugging & authoring are due to:
+#	Tim (the-incredib-ly y'rs) Peters and Cristian Tismer
+# So, who owns the copyright? ;-) How about this:
+# Copyright 1996-1997: 
+#	Mitchell S. Chapman,
+#	Zachary Roadhouse,
+#	Tim Peters,
+#	Just van Rossum
+
+__version__ = "0.3.1"
+
+import string, regex
+
+# First a little helper, since I don't like to repeat things. (Tismer speaking)
+import string
+def replace(where, what, with):
+	return string.join(string.split(where, what), with)
+
+# This list of keywords is taken from ref/node13.html of the
+# Python 1.3 HTML documentation. ("access" is intentionally omitted.)
+keywordsList = [
+	"del", "from", "lambda", "return",
+	"and", "elif", "global", "not", "try",
+	"break", "else", "if", "or", "while",
+	"class", "except", "import", "pass",
+	"continue", "finally", "in", "print",
+	"def", "for", "is", "raise"]
+
+# Build up a regular expression which will match anything
+# interesting, including multi-line triple-quoted strings.
+commentPat = "#.*"
+
+pat = "q[^\q\n]*\(\\\\[\000-\377][^\q\n]*\)*q"
+quotePat = replace(pat, "q", "'") + "\|" + replace(pat, 'q', '"')
+
+# Way to go, Tim!
+pat = """
+	qqq
+	[^\\q]*
+	\(
+		\(	\\\\[\000-\377]
+		\|	q
+			\(	\\\\[\000-\377]
+			\|	[^\\q]
+			\|	q
+				\(	\\\\[\000-\377]
+				\|	[^\\q]
+				\)
+			\)
+		\)
+		[^\\q]*
+	\)*
+	qqq
+"""
+pat = string.join(string.split(pat), '')	# get rid of whitespace
+tripleQuotePat = replace(pat, "q", "'") + "\|" + replace(pat, 'q', '"')
+
+# Build up a regular expression which matches all and only
+# Python keywords. This will let us skip the uninteresting
+# identifier references.
+# nonKeyPat identifies characters which may legally precede
+# a keyword pattern.
+nonKeyPat = "\(^\|[^a-zA-Z0-9_.\"']\)"
+
+keyPat = nonKeyPat + "\("
+for keyword in keywordsList:
+	keyPat = keyPat + keyword + "\|"
+keyPat = keyPat[:-2] + "\)" + nonKeyPat
+
+matchPat = keyPat + "\|" + commentPat + "\|" + tripleQuotePat + "\|" + quotePat
+matchRE = regex.compile(matchPat)
+
+idKeyPat = "[ \t]*[A-Za-z_][A-Za-z_0-9.]*"	# Ident w. leading whitespace.
+idRE = regex.compile(idKeyPat)
+
+
+def fontify(pytext, searchfrom = 0, searchto = None):
+	if searchto is None:
+		searchto = len(pytext)
+	# Cache a few attributes for quicker reference.
+	search = matchRE.search
+	group = matchRE.group
+	idSearch = idRE.search
+	idGroup = idRE.group
+	
+	tags = []
+	tags_append = tags.append
+	commentTag = 'comment'
+	stringTag = 'string'
+	keywordTag = 'keyword'
+	identifierTag = 'identifier'
+	
+	start = 0
+	end = searchfrom
+	while 1:
+		start = search(pytext, end)
+		if start < 0 or start >= searchto:
+			break	# EXIT LOOP
+		match = group(0)
+		end = start + len(match)
+		c = match[0]
+		if c not in "#'\"":
+			# Must have matched a keyword.
+			if start <> searchfrom:
+				# there's still a redundant char before and after it, strip!
+				match = match[1:-1]
+				start = start + 1
+			else:
+				# this is the first keyword in the text.
+				# Only a space at the end.
+				match = match[:-1]
+			end = end - 1
+			tags_append((keywordTag, start, end, None))
+			# If this was a defining keyword, look ahead to the
+			# following identifier.
+			if match in ["def", "class"]:
+				start = idSearch(pytext, end)
+				if start == end:
+					match = idGroup(0)
+					end = start + len(match)
+					tags_append((identifierTag, start, end, None))
+		elif c == "#":
+			tags_append((commentTag, start, end, None))
+		else:
+			tags_append((stringTag, start, end, None))
+	return tags
+
+
+def test(path):
+	f = open(path)
+	text = f.read()
+	f.close()
+	tags = fontify(text)
+	for tag, start, end, sublist in tags:
+		print tag, `text[start:end]`
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyInteractive.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyInteractive.py
new file mode 100644
index 0000000..dc75e4b
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyInteractive.py
@@ -0,0 +1,115 @@
+import string
+import sys
+import traceback
+
+
+try:
+	sys.ps1
+except AttributeError:
+	sys.ps1 = ">>> "
+try:
+	sys.ps2
+except AttributeError:
+	sys.ps2 = "... "
+
+
+def print_exc(limit=None, file=None):
+	if not file:
+		file = sys.stderr
+	# we're going to skip the outermost traceback object, we don't
+	# want people to see the line which excecuted their code.
+	tb = sys.exc_traceback
+	if tb:
+		tb = tb.tb_next
+	try:
+		sys.last_type = sys.exc_type
+		sys.last_value = sys.exc_value
+		sys.last_traceback = tb
+		traceback.print_exception(sys.last_type, sys.last_value, 
+					sys.last_traceback, limit, file)
+	except:
+		print '--- hola! ---'
+		traceback.print_exception(sys.exc_type, sys.exc_value, 
+					sys.exc_traceback, limit, file)
+
+
+class PyInteractive:
+	
+	def __init__(self):
+		self._pybuf = ""
+	
+	def executeline(self, stuff, out = None, env = None):
+		if env is None:
+			import __main__
+			env = __main__.__dict__
+		if out:
+			saveerr, saveout = sys.stderr, sys.stdout
+			sys.stderr = sys.stdout = out
+		try:
+			if self._pybuf:
+				self._pybuf = self._pybuf + '\n' + stuff
+			else:
+				self._pybuf = stuff
+			
+			# Compile three times: as is, with \n, and with \n\n appended.
+			# If it compiles as is, it's complete.  If it compiles with
+			# one \n appended, we expect more.  If it doesn't compile
+			# either way, we compare the error we get when compiling with
+			# \n or \n\n appended.  If the errors are the same, the code
+			# is broken.  But if the errors are different, we expect more.
+			# Not intuitive; not even guaranteed to hold in future
+			# releases; but this matches the compiler's behavior in Python
+			# 1.4 and 1.5.
+			err = err1 = err2 = None
+			code = code1 = code2 = None
+			
+			# quickly get out of here when the line is 'empty' or is a comment
+			stripped = string.strip(self._pybuf)
+			if not stripped or stripped[0] == '#':
+				self._pybuf = ''
+				sys.stdout.write(sys.ps1)
+				sys.stdout.flush()
+				return
+			
+			try:
+				code = compile(self._pybuf, "<input>", "single")
+			except SyntaxError, err:
+				pass
+			except:
+				# OverflowError. More?
+				print_exc()
+				self._pybuf = ""
+				sys.stdout.write(sys.ps1)
+				sys.stdout.flush()
+				return
+			
+			try:
+				code1 = compile(self._pybuf + "\n", "<input>", "single")
+			except SyntaxError, err1:
+				pass
+			
+			try:
+				code2 = compile(self._pybuf + "\n\n", "<input>", "single")
+			except SyntaxError, err2:
+				pass
+			
+			if code:
+				try:
+					exec code in env
+				except:
+					print_exc()
+				self._pybuf = ""
+			elif code1:
+				pass
+			elif err1 == err2 or (not stuff and self._pybuf):
+				print_exc()
+				self._pybuf = ""
+			if self._pybuf:
+				sys.stdout.write(sys.ps2)
+				sys.stdout.flush()
+			else:
+				sys.stdout.write(sys.ps1)
+				sys.stdout.flush()
+		finally:
+			if out:
+				sys.stderr, sys.stdout = saveerr, saveout
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/SpecialKeys.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/SpecialKeys.py
new file mode 100644
index 0000000..a7ee5d8
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/SpecialKeys.py
@@ -0,0 +1,44 @@
+spacekey		= ' '
+returnkey 		= '\r'
+tabkey 		= '\t'
+enterkey 		= '\003'
+backspacekey 	= '\010'
+deletekey 		= '\177'
+
+helpkey 		= '\005'
+
+leftarrowkey 	= '\034'
+rightarrowkey 	= '\035'
+uparrowkey 		= '\036'
+downarrowkey 	= '\037'
+arrowkeys 		= [leftarrowkey, rightarrowkey, uparrowkey, downarrowkey]
+
+topkey 		= '\001'
+bottomkey 		= '\004'
+pageupkey 		= '\013'
+pagedownkey 	= '\014'
+scrollkeys 		= [topkey, bottomkey, pageupkey, pagedownkey]
+
+navigationkeys = arrowkeys + scrollkeys
+
+keycodes = {
+	"space"		: ' ',
+	"return"		: '\r',
+	"tab" 			: '\t',
+	"enter" 		: '\003',
+	"backspace"	: '\010',
+	"delete"		: '\177',
+	"help" 		: '\005',
+	"leftarrow"		: '\034',
+	"rightarrow" 	: '\035',
+	"uparrow" 		: '\036',
+	"downarrow"	: '\037',
+	"top" 			: '\001',
+	"bottom" 		: '\004',
+	"pageup" 		: '\013',
+	"pagedown" 	: '\014'
+}
+
+keynames = {}
+for k, v in keycodes.items():
+	keynames[v] = k
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/W.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/W.py
new file mode 100644
index 0000000..161ddd6
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/W.py
@@ -0,0 +1,33 @@
+"""Widgets for the Macintosh. Built on top of FrameWork"""
+
+__version__ = "0.1"
+
+from Wbase import *
+from Wcontrols import *
+from Wtext import *
+from Wlist import *
+from Wwindows import *
+from Wmenus import *
+
+_application = None
+_signature = None
+
+AlertError = 'AlertError'
+
+def setapplication(app, sig):
+	global _application, _signature
+	_application = app
+	_signature = sig
+
+def getapplication():
+	if _application is None:
+		raise WidgetsError, 'W not properly initialized: unknown Application'
+	return _application
+
+def Message(text):
+	import EasyDialogs, Qd
+	Qd.InitCursor()
+	if text:
+		EasyDialogs.Message(text)
+	else:
+		EasyDialogs.Message('<Alert text not specified>')
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/WFrameWorkPatch.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/WFrameWorkPatch.py
new file mode 100644
index 0000000..267d8ac
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/WFrameWorkPatch.py
@@ -0,0 +1,344 @@
+import FrameWork
+import Win
+import Qd
+import MacOS
+import Events
+import traceback
+from types import *
+
+import Menu; MenuToolbox = Menu; del Menu
+
+
+class Application(FrameWork.Application):
+	
+	def __init__(self, signature = 'Pyth'):
+		import W
+		W.setapplication(self, signature)
+		FrameWork.Application.__init__(self)
+		self._suspended = 0
+		self.quitting = 0
+		self.debugger_quitting = 1
+		self.DebuggerQuit = 'DebuggerQuitDummyException'
+	
+	def mainloop(self, mask = FrameWork.everyEvent, wait = 0):
+		import W
+		self.quitting = 0
+		saveyield = MacOS.EnableAppswitch(-1)
+		try:
+			while not self.quitting:
+				try:
+					self.do1event(mask, wait)
+				except W.AlertError, detail:
+					MacOS.EnableAppswitch(-1)
+					W.Message(detail)
+				except self.DebuggerQuit:
+					MacOS.EnableAppswitch(-1)
+				except:
+					MacOS.EnableAppswitch(-1)
+					import PyEdit
+					PyEdit.tracebackwindow.traceback()
+		finally:
+			MacOS.EnableAppswitch(1)
+	
+	def debugger_mainloop(self, mask = FrameWork.everyEvent, wait = 0):
+		import W
+		self.debugger_quitting = 0
+		saveyield = MacOS.EnableAppswitch(-1)
+		try:
+			while not self.quitting and not self.debugger_quitting:
+				try:
+					self.do1event(mask, wait)
+				except W.AlertError, detail:
+					W.Message(detail)
+				except:
+					import PyEdit
+					PyEdit.tracebackwindow.traceback()
+		finally:
+			MacOS.EnableAppswitch(saveyield)
+	
+	def idle(self, event):
+		if not self._suspended:
+			if not self.do_frontWindowMethod("idle", event):
+				Qd.InitCursor()
+	
+	def do_frontWindowMethod(self, attr, *args):
+		wid = Win.FrontWindow()
+		if wid and self._windows.has_key(wid):
+			window = self._windows[wid]
+			if hasattr(window, attr):
+				handler = getattr(window, attr)
+				apply(handler, args)
+				return 1
+	
+	def appendwindow(self, wid, window):
+		self._windows[wid] = window
+		self.makeopenwindowsmenu()
+		
+	def removewindow(self, wid):
+		del self._windows[wid]
+		self.makeopenwindowsmenu()
+	
+	def do_key(self, event):
+		(what, message, when, where, modifiers) = event
+		ch = chr(message & FrameWork.charCodeMask)
+		rest = message & ~FrameWork.charCodeMask
+		wid = Win.FrontWindow()
+		if modifiers & FrameWork.cmdKey:
+			if wid and self._windows.has_key(wid):
+				self.checkmenus(self._windows[wid])
+			else:
+				self.checkmenus(None)
+			event = (what, ord(ch) | rest, when, where, modifiers)
+			result = MenuToolbox.MenuKey(ord(ch))
+			id = (result>>16) & 0xffff	# Hi word
+			item = result & 0xffff		# Lo word
+			if id:
+				self.do_rawmenu(id, item, None, event)
+				return	# here! we had a menukey! 
+			#else:
+			#	print "XXX Command-" +`ch`
+		# See whether the front window wants it
+		if wid and self._windows.has_key(wid):
+			window = self._windows[wid]
+			try:
+				do_char = window.do_char
+			except AttributeError:
+				do_char = self.do_char
+			do_char(ch, event)
+		# else it wasn't for us, sigh...
+	
+	def do_inMenuBar(self, partcode, window, event):
+		Qd.InitCursor()
+		(what, message, when, where, modifiers) = event
+		self.checkopenwindowsmenu()
+		wid = Win.FrontWindow()
+		if wid and self._windows.has_key(wid):
+			self.checkmenus(self._windows[wid])
+		else:
+			self.checkmenus(None)
+		result = MenuToolbox.MenuSelect(where)
+		id = (result>>16) & 0xffff	# Hi word
+		item = result & 0xffff		# Lo word
+		self.do_rawmenu(id, item, window, event)
+	
+	def do_updateEvt(self, event):
+		(what, message, when, where, modifiers) = event
+		wid = Win.WhichWindow(message)
+		if wid and self._windows.has_key(wid):
+			window = self._windows[wid]
+			window.do_rawupdate(wid, event)
+		else:
+			if wid:
+				wid.HideWindow()
+				import sys
+				sys.stderr.write("XXX killed unknown (crashed?) Python window.\n")
+			else:
+				MacOS.HandleEvent(event)
+	
+	def suspendresume(self, onoff):
+		pass
+	
+	def do_suspendresume(self, event):
+		# Is this a good idea???
+		(what, message, when, where, modifiers) = event
+		self._suspended = not message & 1
+		self.suspendresume(message & 1)
+		w = Win.FrontWindow()
+		if w:
+			# XXXX Incorrect, should stuff windowptr into message field
+			nev = (Events.activateEvt, w, when, where, message&1)
+			self.do_activateEvt(nev)
+	
+	def checkopenwindowsmenu(self):
+		if self._openwindowscheckmark:
+			self.openwindowsmenu.menu.CheckItem(self._openwindowscheckmark, 0)
+		window = Win.FrontWindow()
+		if window:
+			for item, wid in self._openwindows.items():
+				if wid == window:
+					#self.pythonwindowsmenuitem.check(1)
+					self.openwindowsmenu.menu.CheckItem(item, 1)
+					self._openwindowscheckmark = item
+					break
+		else:
+			self._openwindowscheckmark = 0
+		#if self._openwindows:
+		#	self.pythonwindowsmenuitem.enable(1)
+		#else:
+		#	self.pythonwindowsmenuitem.enable(0)
+	
+	def checkmenus(self, window):
+		for item in self._menustocheck:
+			callback = item.menu.items[item.item-1][2]
+			if type(callback) <> StringType:
+				item.enable(1)
+			elif hasattr(window, "domenu_" + callback):
+				if hasattr(window, "can_" + callback):
+					canhandler = getattr(window, "can_" + callback)
+					if canhandler(item):
+						item.enable(1)
+					else:
+						item.enable(0)
+				else:
+					item.enable(1)
+			else:
+				item.enable(0)
+	
+	def makemenubar(self):
+		self.menubar = MenuBar(self)
+		FrameWork.AppleMenu(self.menubar, self.getabouttext(), self.do_about)
+		self.makeusermenus()
+
+	def scriptswalk(self, top, menu):
+		import os, macfs, string
+		try:
+			names = os.listdir(top)
+		except os.error:
+			FrameWork.MenuItem(menu, '(Scripts Folder not found)', None, None)
+			return
+		for name in names:
+			path = os.path.join(top, name)
+			name = string.strip(name)
+			if name[-3:] == '---':
+				menu.addseparator()
+			elif os.path.isdir(path):
+				submenu = FrameWork.SubMenu(menu, name)
+				self.scriptswalk(path, submenu)
+			else:
+				fss = macfs.FSSpec(path)
+				creator, type = fss.GetCreatorType()
+				if type == 'TEXT':
+					if name[-3:] == '.py':
+						name = name[:-3]
+					item = FrameWork.MenuItem(menu, name, None, self.domenu_script)
+					self._scripts[(menu.id, item.item)] = path
+	
+	def domenu_script(self, id, item, window, event):
+		(what, message, when, where, modifiers) = event
+		path = self._scripts[(id, item)]
+		import os
+		if not os.path.exists(path):
+			self.makescriptsmenu()
+			import W
+			raise W.AlertError, "File not found."
+		if modifiers & FrameWork.optionKey:
+			self.openscript(path)
+		else:
+			import W, MacOS, sys
+			W.SetCursor("watch")
+			sys.argv = [path]
+			#cwd = os.getcwd()
+			#os.chdir(os.path.dirname(path) + ':')
+			try:
+				# xxx if there is a script window for this file,
+				# exec in that window's namespace.
+				# xxx what to do when it's not saved???
+				# promt to save?
+				MacOS.EnableAppswitch(0)
+				execfile(path, {'__name__': '__main__', '__file__': path})
+			except W.AlertError, detail:
+				MacOS.EnableAppswitch(-1)
+				raise W.AlertError, detail
+			except KeyboardInterrupt:
+				MacOS.EnableAppswitch(-1)
+			except:
+				MacOS.EnableAppswitch(-1)
+				import PyEdit
+				PyEdit.tracebackwindow.traceback(1)
+			else:
+				MacOS.EnableAppswitch(-1)
+			#os.chdir(cwd)
+	
+	def openscript(self, filename, lineno = None, charoffset = 0):
+		import os, PyEdit, W
+		editor = self.getscript(filename)
+		if editor:
+			editor.select()
+		elif os.path.exists(filename):
+			editor = PyEdit.Editor(filename)
+		elif filename[-3:] == '.py':
+			import imp
+			modname = os.path.basename(filename)[:-3]
+			try:
+				f, filename, (suff, mode, dummy) = imp.find_module(modname)
+			except ImportError:
+				raise W.AlertError, "Can¹t find file for ³%s²" % modname
+			else:
+				f.close()
+			if suff == '.py':
+				self.openscript(filename, lineno, charoffset)
+				return
+			else:
+				raise W.AlertError, "Can¹t find file for ³%s²" % modname
+		else:
+			raise W.AlertError, "Can¹t find file Œ%s¹" % filename
+		if lineno is not None:
+			editor.selectline(lineno, charoffset)
+		return editor
+	
+	def getscript(self, filename):
+		if filename[:1] == '<' and filename[-1:] == '>':
+			filename = filename[1:-1]
+		import string
+		lowpath = string.lower(filename)
+		for wid, window in self._windows.items():
+			if hasattr(window, "path") and lowpath == string.lower(window.path):
+				return window
+			elif hasattr(window, "path") and filename == wid.GetWTitle():
+				return window
+	
+	def getprefs(self):
+		import MacPrefs
+		return MacPrefs.GetPrefs(self.preffilepath)
+
+
+
+class MenuBar(FrameWork.MenuBar):
+	
+	possibleIDs = range(10, 256)
+	
+	def getnextid(self):
+		id = self.possibleIDs[0]
+		del self.possibleIDs[0]
+		return id
+	
+	def __init__(self, parent = None):
+		self.bar = MenuToolbox.GetMenuBar()
+		MenuToolbox.ClearMenuBar()
+		self.menus = {}
+		self.parent = parent
+	
+	def dispatch(self, id, item, window, event):
+		if self.menus.has_key(id):
+			self.menus[id].dispatch(id, item, window, event)
+	
+	def delmenu(self, id):
+		MenuToolbox.DeleteMenu(id)
+		if id in self.possibleIDs:
+			print "XXX duplicate menu ID!", id
+		self.possibleIDs.append(id)
+	
+
+class Menu(FrameWork.Menu):
+	
+	def dispatch(self, id, item, window, event):
+		title, shortcut, callback, kind = self.items[item-1]
+		if type(callback) == StringType:
+			callback = self._getmenuhandler(callback)
+		if callback:
+			import W
+			W.CallbackCall(callback, 0, id, item, window, event)
+	
+	def _getmenuhandler(self, callback):
+		menuhandler = None
+		wid = Win.FrontWindow()
+		if wid and self.bar.parent._windows.has_key(wid):
+			window = self.bar.parent._windows[wid]
+			if hasattr(window, "domenu_" + callback):
+				menuhandler = getattr(window, "domenu_" + callback)
+			elif hasattr(self.bar.parent, "domenu_" + callback):
+				menuhandler = getattr(self.bar.parent, "domenu_" + callback)
+		elif hasattr(self.bar.parent, "domenu_" + callback):
+			menuhandler = getattr(self.bar.parent, "domenu_" + callback)
+		return menuhandler
+
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wbase.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wbase.py
new file mode 100644
index 0000000..bcd7789
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wbase.py
@@ -0,0 +1,612 @@
+import Qd
+import Win
+import QuickDraw
+import Evt
+import string
+from types import *
+from SpecialKeys import *
+import sys
+
+WidgetsError = "WidgetsError"
+
+DEBUG = 0
+
+class Widget:
+	
+	_selectable = 0
+	
+	def __init__(self, possize):
+		self._widgets = []
+		self._widgetsdict = {}
+		self._possize = possize
+		self._bounds = None
+		self._visible = 1
+		self._enabled = 0
+		self._selected = 0
+		self._activated = 0
+		self._callback = None
+		self._parent = None
+		self._parentwindow = None
+		self._bindings = {}
+		self._backcolor = None
+	
+	def show(self, onoff):
+		self.SetPort()
+		self._visible = onoff
+		print 'Background'
+		if self._visible and self._backcolor:
+			penstate = Qd.GetPenState()
+			Qd.RGBForeColor(self._backcolor)
+			Qd.FrameRect(self._bounds)
+			Qd.RGBForeColor((0, 0, 0))
+			Qd.SetPenState(penstate)
+			
+		for w in self._widgets:
+			w.show(onoff)
+		if onoff:
+			self.draw()
+		else:
+			Qd.EraseRect(self._bounds)
+	
+	def draw(self, visRgn = None):
+		if self._visible:
+			# draw your stuff here
+			pass
+	
+	def getpossize(self):
+		return self._possize
+	
+	def getbounds(self):
+		return self._bounds
+	
+	def move(self, x, y = None):
+		"""absolute move"""
+		if y == None:
+			x, y = x
+		if type(self._possize) <> TupleType:
+			raise WidgetsError, "can't move widget with bounds function"
+		l, t, r, b = self._possize
+		self.resize(x, y, r, b)
+	
+	def rmove(self, x, y = None):
+		"""relative move"""
+		if y == None:
+			x, y = x
+		if type(self._possize) <> TupleType:
+			raise WidgetsError, "can't move widget with bounds function"
+		l, t, r, b = self._possize
+		self.resize(l + x, t + y, r, b)
+		
+	def resize(self, *args):
+		#print "yep.", args
+		if len(args) == 1:
+			if type(args[0]) == FunctionType or type(args[0]) == MethodType:
+				self._possize = args[0]
+			else:
+				apply(self.resize, args[0])
+		elif len(args) == 2:
+			self._possize = (0, 0) + args
+		elif len(args) == 4:
+			self._possize = args
+		else:
+			raise TypeError, "wrong number of arguments"
+		self._calcbounds()
+	
+	def open(self):
+		self._calcbounds()
+	
+	def close(self):
+		#print "xxx Closing Widget"
+		del self._callback
+		del self._possize
+		del self._bindings
+		del self._parent
+		del self._parentwindow
+	
+	def bind(self, key, callback):
+		"""bind a key or an 'event' to a callback"""
+		if callback:
+			self._bindings[key] = callback
+		elif self._bindings.has_key(key):
+			del self._bindings[key]
+	
+	def adjust(self, oldbounds):
+		self.SetPort()
+		Win.InvalRect(oldbounds)
+		Win.InvalRect(self._bounds)
+	
+	def _calcbounds(self):
+		oldbounds = self._bounds
+		pl, pt, pr, pb = self._parent._bounds
+		if callable(self._possize):
+			width = pr - pl
+			height = pb - pt
+			self._bounds = Qd.OffsetRect(self._possize(width, height), pl, pt)
+		else:
+			l, t, r, b = self._possize
+			if l < -1:
+				l = pr + l
+			else:
+				l = pl + l
+			if t < -1:
+				t = pb + t
+			else:
+				t = pt + t
+			if r > 1:
+				r = l + r
+			else:
+				r = pr + r
+			if b > 1:
+				b = t + b
+			else:
+				b = pb + b
+			self._bounds = (l, t, r, b)
+		if oldbounds and oldbounds <> self._bounds:
+			self.adjust(oldbounds)
+		for w in self._widgets:
+			w._calcbounds()
+	
+	def test(self, point):
+		if Qd.PtInRect(point, self._bounds):
+			return 1
+	
+	def click(self, point, modifiers):
+		pass
+	
+	def findwidget(self, point, onlyenabled = 1):
+		if self.test(point):
+			for w in self._widgets:
+				widget = w.findwidget(point)
+				if widget is not None:
+					return widget
+			if self._enabled or not onlyenabled:
+				return self
+	
+	def forall(self, methodname, *args):
+		for w in self._widgets:
+			rv = apply(w.forall, (methodname,) + args)
+			if rv: 
+				return rv
+		if self._bindings.has_key("<" + methodname + ">"):
+			callback = self._bindings["<" + methodname + ">"]
+			rv = apply(callback, args)
+			if rv: 
+				return rv
+		if hasattr(self, methodname):
+			method = getattr(self, methodname)
+			return apply(method, args)
+	
+	def forall_butself(self, methodname, *args):
+		for w in self._widgets:
+			rv = apply(w.forall, (methodname,) + args)
+			if rv: 
+				return rv
+	
+	def forall_frombottom(self, methodname, *args):
+		if self._bindings.has_key("<" + methodname + ">"):
+			callback = self._bindings["<" + methodname + ">"]
+			rv = apply(callback, args)
+			if rv: 
+				return rv
+		if hasattr(self, methodname):
+			method = getattr(self, methodname)
+			rv = apply(method, args)
+			if rv: 
+				return rv
+		for w in self._widgets:
+			rv = apply(w.forall_frombottom, (methodname,) + args)
+			if rv: 
+				return rv
+	
+	def _addwidget(self, key, widget):
+		if widget in self._widgets:
+			raise ValueError, "duplicate widget"
+		if self._widgetsdict.has_key(key):
+			self._removewidget(key)
+		self._widgets.append(widget)
+		self._widgetsdict[key] = widget
+		widget._parent = self
+		self._setparentwindow(widget)
+		if self._parentwindow and self._parentwindow.wid:
+			widget.forall_frombottom("open")
+			Win.InvalRect(widget._bounds)
+	
+	def _setparentwindow(self, widget):
+		widget._parentwindow = self._parentwindow
+		for w in widget._widgets:
+			self._setparentwindow(w)
+	
+	def _removewidget(self, key):
+		if not self._widgetsdict.has_key(key):
+			raise KeyError, "no widget with key " + `key`
+		widget = self._widgetsdict[key]
+		for k in widget._widgetsdict.keys():
+			widget._removewidget(k)
+		if self._parentwindow._currentwidget == widget:
+			widget.select(0)
+			self._parentwindow._currentwidget = None
+		self.SetPort()
+		Win.InvalRect(widget._bounds)
+		widget.close()
+		del self._widgetsdict[key]
+		self._widgets.remove(widget)
+	
+	def __setattr__(self, attr, value):
+		if type(value) == InstanceType and HasBaseClass(value, Widget) and	\
+				attr not in ("_currentwidget", "_lastrollover", 
+					"_parent", "_parentwindow", "_defaultbutton"):
+			if hasattr(self, attr):
+				raise ValueError, "Can't replace existing attribute: " + attr
+			self._addwidget(attr, value)
+		self.__dict__[attr] = value
+	
+	def __delattr__(self, attr):
+		if attr == "_widgetsdict":
+			raise AttributeError, "cannot delete attribute _widgetsdict"
+		if self._widgetsdict.has_key(attr):
+			self._removewidget(attr)
+			if self.__dict__.has_key(attr):
+				del self.__dict__[attr]
+		elif self.__dict__.has_key(attr):
+			del self.__dict__[attr]
+		else:
+			raise AttributeError, attr
+	
+	def __setitem__(self, key, value):
+		self._addwidget(key, value)
+	
+	def __getitem__(self, key):
+		if not self._widgetsdict.has_key(key):
+			raise KeyError, key
+		return self._widgetsdict[key]
+	
+	def __delitem__(self, key):
+		self._removewidget(key)
+	
+	def SetPort(self):
+		self._parentwindow.SetPort()
+	
+	def __del__(self):
+		if DEBUG:
+			print "%s instance deleted" % self.__class__.__name__
+	
+	def _drawbounds(self):
+		Qd.FrameRect(self._bounds)
+
+
+class ClickableWidget(Widget):
+
+	def click(self, point, modifiers):
+		pass
+	
+	def enable(self, onoff):
+		self._enabled = onoff
+		self.SetPort()
+		self.draw()
+	
+	def callback(self):
+		if self._callback:
+			return CallbackCall(self._callback, 1)
+	
+
+class SelectableWidget(ClickableWidget):
+
+	_selectable = 1
+	
+	def select(self, onoff, isclick = 0):
+		if onoff == self._selected:
+			return 1
+		if self._bindings.has_key("<select>"):
+			callback = self._bindings["<select>"]
+			if callback(onoff):
+				return 1
+		self._selected = onoff
+		if onoff:
+			if self._parentwindow._currentwidget is not None:
+				self._parentwindow._currentwidget.select(0)
+			self._parentwindow._currentwidget = self
+		else:
+			self._parentwindow._currentwidget = None
+	
+	def key(self, char, event):
+		pass
+	
+	def drawselframe(self, onoff):
+		if not self._parentwindow._hasselframes:
+			return
+		thickrect = Qd.InsetRect(self._bounds, -3, -3)
+		state = Qd.GetPenState()
+		Qd.PenSize(2, 2)
+		if onoff:
+			Qd.PenPat(Qd.qd.black)
+		else:
+			Qd.PenPat(Qd.qd.white)
+		Qd.FrameRect(thickrect)
+		Qd.SetPenState(state)
+	
+	def adjust(self, oldbounds):
+		self.SetPort()
+		if self._selected:
+			Win.InvalRect(Qd.InsetRect(oldbounds, -3, -3))
+			Win.InvalRect(Qd.InsetRect(self._bounds, -3, -3))
+		else:
+			Win.InvalRect(oldbounds)
+			Win.InvalRect(self._bounds)
+
+
+class _Line(Widget):
+	
+	def __init__(self, possize, thickness = 1):
+		Widget.__init__(self, possize)
+		self._thickness = thickness
+	
+	def open(self):
+		self._calcbounds()
+		self.SetPort()
+		self.draw()
+	
+	def draw(self, visRgn = None):
+		if self._visible:
+			Qd.PaintRect(self._bounds)
+	
+	def _drawbounds(self):
+		pass
+
+class HorizontalLine(_Line):
+	
+	def _calcbounds(self):
+		Widget._calcbounds(self)
+		l, t, r, b = self._bounds
+		self._bounds = l, t, r, t + self._thickness
+
+class VerticalLine(_Line):
+	
+	def _calcbounds(self):
+		Widget._calcbounds(self)
+		l, t, r, b = self._bounds
+		self._bounds = l, t, l + self._thickness, b
+
+
+class Frame(Widget):
+	
+	def __init__(self, possize, pattern = Qd.qd.black, color = (0, 0, 0)):
+		Widget.__init__(self, possize)
+		self._framepattern = pattern
+		self._framecolor = color
+	
+	def setcolor(self, color):
+		self._framecolor = color
+		self.draw()
+	
+	def setpattern(self, pattern):
+		self._framepattern = pattern
+		self.draw()
+		
+	def draw(self, visRgn = None):
+		if self._visible:
+			penstate = Qd.GetPenState()
+			Qd.PenPat(self._framepattern)
+			Qd.RGBForeColor(self._framecolor)
+			Qd.FrameRect(self._bounds)
+			Qd.RGBForeColor((0, 0, 0))
+			Qd.SetPenState(penstate)
+
+
+class Group(Widget): pass
+	
+
+class HorizontalPanes(Widget):
+	
+	_direction = 1
+	
+	def __init__(self, possize, panesizes = None, gutter = 8):
+		ClickableWidget.__init__(self, possize)
+		self._panesizes = panesizes
+		self._gutter = gutter
+		self._enabled = 1
+		self.setuppanes()
+	
+	def open(self):
+		self.installbounds()
+		ClickableWidget.open(self)
+	
+	def setuppanes(self):
+		panesizes = self._panesizes
+		total = 0
+		if panesizes is not None:
+			#if len(self._widgets) <> len(panesizes):
+			#	raise TypeError, 'number of widgets does not match number of panes'
+			for panesize in panesizes:
+				if not 0 < panesize < 1:
+					raise TypeError, 'pane sizes must be between 0 and 1, not including.'
+				total = total + panesize
+			if round(total, 4) <> 1.0:
+				raise TypeError, 'pane sizes must add up to 1'
+		else:
+			step = 1.0 / len(self._widgets)
+			panesizes = []
+			for i in range(len(self._widgets)):
+				panesizes.append(step)
+		current = 0
+		self._panesizes = []
+		self._gutters = []
+		for panesize in panesizes:
+			if current:
+				self._gutters.append(current)
+			self._panesizes.append(current, current + panesize)
+			current = current + panesize
+		self.makepanebounds()
+	
+	def getpanesizes(self):
+		return map(lambda (fr, to): to-fr,  self._panesizes)
+	
+	boundstemplate = "lambda width, height: (0, height * %s + %d, width, height * %s + %d)"
+	
+	def makepanebounds(self):
+		halfgutter = self._gutter / 2
+		self._panebounds = []
+		for i in range(len(self._panesizes)):
+			panestart, paneend = self._panesizes[i]
+			boundsstring = self.boundstemplate % (`panestart`, panestart and halfgutter, 
+							`paneend`, (paneend <> 1.0) and -halfgutter)
+			self._panebounds.append(eval(boundsstring))
+	
+	def installbounds(self):
+		#self.setuppanes()
+		for i in range(len(self._widgets)):
+			w = self._widgets[i]
+			w._possize = self._panebounds[i]
+			#if hasattr(w, "setuppanes"):
+			#	w.setuppanes()
+			if hasattr(w, "installbounds"):
+				w.installbounds()
+	
+	def rollover(self, point, onoff):
+		if onoff:
+			orgmouse = point[self._direction]
+			halfgutter = self._gutter / 2
+			l, t, r, b = self._bounds
+			if self._direction:
+				begin, end = t, b
+			else:
+				begin, end = l, r
+			
+			i = self.findgutter(orgmouse, begin, end)
+			if i is None:
+				SetCursor("arrow")
+			else:
+				SetCursor(self._direction and 'vmover' or 'hmover')
+	
+	def findgutter(self, orgmouse, begin, end):
+		tolerance = max(4, self._gutter) / 2
+		for i in range(len(self._gutters)):
+			pos = begin + (end - begin) * self._gutters[i]
+			if abs(orgmouse - pos) <= tolerance:
+				break
+		else:
+			return
+		return i
+	
+	def click(self, point, modifiers):
+		# what a mess...
+		orgmouse = point[self._direction]
+		halfgutter = self._gutter / 2
+		l, t, r, b = self._bounds
+		if self._direction:
+			begin, end = t, b
+		else:
+			begin, end = l, r
+		
+		i = self.findgutter(orgmouse, begin, end)
+		if i is None:
+			return
+		
+		pos = orgpos = begin + (end - begin) * self._gutters[i]	# init pos too, for fast click on border, bug done by Petr
+		
+		minpos = self._panesizes[i][0]
+		maxpos = self._panesizes[i+1][1]
+		minpos = begin + (end - begin) * minpos + 64
+		maxpos = begin + (end - begin) * maxpos - 64
+		if minpos > orgpos and maxpos < orgpos:
+			return
+		
+		#SetCursor("fist")
+		self.SetPort()
+		if self._direction:
+			rect = l, orgpos - 1, r, orgpos
+		else:
+			rect = orgpos - 1, t, orgpos, b
+		
+		# track mouse --- XXX  move to separate method?
+		Qd.PenMode(QuickDraw.srcXor)
+		Qd.PenPat(Qd.qd.gray)
+		Qd.PaintRect(rect)
+		lastpos = None
+		while Evt.Button():
+			pos = orgpos - orgmouse + Evt.GetMouse()[self._direction]
+			pos = max(pos, minpos)
+			pos = min(pos, maxpos)
+			if pos == lastpos:
+				continue
+			Qd.PenPat(Qd.qd.gray)
+			Qd.PaintRect(rect)
+			if self._direction:
+				rect = l, pos - 1, r, pos
+			else:
+				rect = pos - 1, t, pos, b
+			Qd.PenPat(Qd.qd.gray)
+			Qd.PaintRect(rect)
+			lastpos = pos
+		Qd.PaintRect(rect)
+		Qd.PenNormal()
+		SetCursor("watch")
+		
+		newpos = (pos - begin) / float(end - begin)
+		self._gutters[i] = newpos
+		self._panesizes[i] = self._panesizes[i][0], newpos
+		self._panesizes[i+1] = newpos, self._panesizes[i+1][1]
+		self.makepanebounds()
+		self.installbounds()
+		self._calcbounds()
+	
+
+class VerticalPanes(HorizontalPanes):
+
+	_direction = 0
+	boundstemplate = "lambda width, height: (width * %s + %d, 0, width * %s + %d, height)"
+
+
+# misc utils
+
+def CallbackCall(callback, mustfit, *args):
+	if type(callback) == FunctionType:
+		func = callback
+		maxargs = func.func_code.co_argcount
+	elif type(callback) == MethodType:
+		func = callback.im_func
+		maxargs = func.func_code.co_argcount - 1
+	else:
+		if callable(callback):
+			return apply(callback, args)
+		else:
+			raise TypeError, "uncallable callback object"
+	
+	if func.func_defaults:
+		minargs = maxargs - len(func.func_defaults)
+	else:
+		minargs = maxargs
+	if minargs <= len(args) <= maxargs:
+		return apply(callback, args)
+	elif not mustfit and minargs == 0:
+		return callback()
+	else:
+		if mustfit:
+			raise TypeError, "callback accepts wrong number of arguments: " + `len(args)`
+		else:
+			raise TypeError, "callback accepts wrong number of arguments: 0 or " + `len(args)`
+
+
+def HasBaseClass(obj, class_):
+	try:
+		raise obj
+	except class_:
+		return 1
+	except:
+		pass
+	return 0
+
+
+_cursors = {
+	"watch"	: Qd.GetCursor(QuickDraw.watchCursor).data,
+	"arrow"	: Qd.qd.arrow,
+	"iBeam"	: Qd.GetCursor(QuickDraw.iBeamCursor).data,
+	"cross"	: Qd.GetCursor(QuickDraw.crossCursor).data,
+	"plus"		: Qd.GetCursor(QuickDraw.plusCursor).data,
+	"hand"	: Qd.GetCursor(468).data,
+	"fist"		: Qd.GetCursor(469).data,
+	"hmover"	: Qd.GetCursor(470).data,
+	"vmover"	: Qd.GetCursor(471).data
+}
+
+def SetCursor(what):
+	Qd.SetCursor(_cursors[what])
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wcontrols.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wcontrols.py
new file mode 100644
index 0000000..8c3c395
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wcontrols.py
@@ -0,0 +1,376 @@
+import Ctl
+import Controls
+import Win
+import Wbase
+import Qd
+import Evt
+
+class ControlWidget(Wbase.ClickableWidget):
+
+	def __init__(self, possize, title = "Control", procID = 0, callback = None, value = 0, min = 0, max = 1):
+		Wbase.ClickableWidget.__init__(self, possize)
+		self._control = None
+		self._title = title
+		self._callback = callback
+		self._procID = procID
+		self._value = value
+		self._min = min
+		self._max = max
+		self._enabled = 1
+	
+	def open(self):
+		self._calcbounds()
+		self._control = Ctl.NewControl(self._parentwindow.wid, 
+						self._bounds, 
+						self._title, 
+						1, 
+						self._value, 
+						self._min, 
+						self._max, 
+						self._procID, 
+						0)
+		self.SetPort()
+		Win.ValidRect(self._bounds)
+		self.enable(self._enabled)
+	
+	def adjust(self, oldbounds):
+		self.SetPort()
+		self._control.HideControl()
+		self._control.MoveControl(self._bounds[0], self._bounds[1])
+		self._control.SizeControl(self._bounds[2] - self._bounds[0], self._bounds[3] - self._bounds[1])
+		if self._visible:
+			Qd.EraseRect(self._bounds)
+			self._control.ShowControl()
+			Win.ValidRect(self._bounds)
+	
+	def close(self):
+		self._control.HideControl()
+		self._control = None
+		Wbase.ClickableWidget.close(self)
+	
+	def enable(self, onoff):
+		if self._control and self._enabled <> onoff:
+			self._control.HiliteControl((not onoff) and 255)
+			self._enabled = onoff
+	
+	def show(self, onoff):
+		self._visible = onoff
+		for w in self._widgets:
+			w.show(onoff)
+		if onoff:
+			self._control.ShowControl()
+		else:
+			self._control.HideControl()
+	
+	def activate(self, onoff):
+		self._activated = onoff
+		if self._enabled:
+			self._control.HiliteControl((not onoff) and 255)
+	
+	def draw(self, visRgn = None):
+		if self._visible:
+			self._control.Draw1Control()
+	
+	def test(self, point):
+		ctltype, control = Ctl.FindControl(point, self._parentwindow.wid)
+		if self._enabled and control == self._control:
+			return 1
+	
+	def click(self, point, modifiers):
+		if not self._enabled:
+			return
+		part = self._control.TrackControl(point)
+		if part:
+			if self._callback:
+				Wbase.CallbackCall(self._callback, 0)
+	
+	def settitle(self, title):
+		if self._control:
+			self._control.SetControlTitle(title)
+		self._title = title
+	
+	def gettitle(self):
+		return self._title
+
+class Button(ControlWidget):
+	
+	def __init__(self, possize, title = "Button", callback = None):
+		procID = Controls.pushButProc | Controls.useWFont
+		ControlWidget.__init__(self, possize, title, procID, callback, 0, 0, 1)
+		self._isdefault = 0
+	
+	def push(self):
+		if not self._enabled:
+			return
+		import time
+		self._control.HiliteControl(1)
+		time.sleep(0.1)
+		self._control.HiliteControl(0)
+		if self._callback:
+			Wbase.CallbackCall(self._callback, 0)
+	
+	def enable(self, onoff):
+		if self._control and self._enabled <> onoff:
+			self._control.HiliteControl((not onoff) and 255)
+			self._enabled = onoff
+			if self._isdefault and self._visible:
+				self.SetPort()
+				self.drawfatframe(onoff)
+	
+	def activate(self, onoff):
+		self._activated = onoff
+		if self._enabled:
+			self._control.HiliteControl((not onoff) and 255)
+			if self._isdefault and self._visible:
+				self.SetPort()
+				self.drawfatframe(onoff)
+	
+	def show(self, onoff):
+		ControlWidget.show(self, onoff)
+		if self._isdefault:
+			self.drawfatframe(onoff and self._enabled)
+	
+	def draw(self, visRgn = None):
+		if self._visible:
+			self._control.Draw1Control()
+			if self._isdefault and self._activated:
+				self.drawfatframe(self._enabled)
+	
+	def drawfatframe(self, onoff):
+		state = Qd.GetPenState()
+		if onoff:
+			Qd.PenPat(Qd.qd.black)
+		else:
+			Qd.PenPat(Qd.qd.white)
+		fatrect = Qd.InsetRect(self._bounds, -4, -4)
+		Qd.PenSize(3, 3)
+		Qd.FrameRoundRect(fatrect, 16, 16)
+		Qd.SetPenState(state)
+	
+	def _setdefault(self, onoff):
+		self._isdefault = onoff
+		if self._control:
+			self.SetPort()
+			self.drawfatframe(onoff)
+	
+	def adjust(self, oldbounds):
+		if self._isdefault:
+			old = Qd.InsetRect(oldbounds, -4, -4)
+			new = Qd.InsetRect(self._bounds, -4, -4)
+			Qd.EraseRect(old)
+			Win.InvalRect(old)
+			Win.InvalRect(new)
+		ControlWidget.adjust(self, oldbounds)
+
+
+class CheckBox(ControlWidget):
+	
+	def __init__(self, possize, title = "Checkbox", callback = None, value = 0):
+		procID = Controls.checkBoxProc | Controls.useWFont
+		ControlWidget.__init__(self, possize, title, procID, callback, value, 0, 1)
+	
+	def click(self, point, modifiers):
+		if not self._enabled:
+			return
+		part = self._control.TrackControl(point)
+		if part:
+			self.toggle()
+			if self._callback:
+				Wbase.CallbackCall(self._callback, 0, self.get())
+	
+	def push(self):
+		if not self._enabled:
+			return
+		self.toggle()
+		if self._callback:
+			Wbase.CallbackCall(self._callback, 0, self.get())
+	
+	def toggle(self):
+		self.set(not self.get())
+	
+	def set(self, value):
+		if self._control:
+			self._control.SetControlValue(value)
+		else:
+			self._value = value
+	
+	def get(self):
+		if self._control:
+			return self._control.GetControlValue()
+		else:
+			return self._value
+	
+
+class RadioButton(ControlWidget):
+	
+	def __init__(self, possize, title = "Radiobutton", thebuttons, callback = None, value = 0):
+		procID = Controls.radioButProc | Controls.useWFont
+		ControlWidget.__init__(self, possize, title, procID, callback, value, 0, 1)
+		self.thebuttons = thebuttons
+		thebuttons.append(self)
+	
+	def close(self):
+		self.thebuttons = None
+		ControlWidget.close(self)
+	
+	def click(self, point, modifiers):
+		if not self._enabled:
+			return
+		part = self._control.TrackControl(point)
+		if part:
+			self.set(1)
+			if self._callback:
+				Wbase.CallbackCall(self._callback, 0, 1)
+	
+	def push(self):
+		if not self._enabled:
+			return
+		self.set(1)
+		if self._callback:
+			Wbase.CallbackCall(self._callback, 0, 1)
+	
+	def set(self, value):
+		for button in self.thebuttons:
+			if button._control:
+				button._control.SetControlValue(button == self)
+			else:
+				button._value = (button == self)
+	
+	def get(self):
+		if self._control:
+			return self._control.GetControlValue()
+		else:
+			return self._value
+	
+
+class Scrollbar(ControlWidget):
+	
+	def __init__(self, possize, callback = None, value = 0, min = 0, max = 0):
+		procID = Controls.scrollBarProc
+		ControlWidget.__init__(self, possize, "", procID, callback, value, min, max)
+	
+	# interface
+	def set(self, value):
+		if self._callback:
+			Wbase.CallbackCall(self._callback, 1, value)
+	
+	def up(self):
+		if self._callback:
+			Wbase.CallbackCall(self._callback, 1, '+')
+	
+	def down(self):
+		if self._callback:
+			Wbase.CallbackCall(self._callback, 1, '-')
+	
+	def pageup(self):
+		if self._callback:
+			Wbase.CallbackCall(self._callback, 1, '++')
+	
+	def pagedown(self):
+		if self._callback:
+			Wbase.CallbackCall(self._callback, 1, '--')
+	
+	def setmin(self, min):
+		self._control.SetControlMinimum(min)
+	
+	def setmax(self, min):
+		self._control.SetControlMinimum(max)
+	
+	def getmin(self):
+		return self._control.GetControlMinimum()
+	
+	def getmax(self):
+		return self._control.GetControlMinimum()
+	
+	# internals
+	def click(self, point, modifiers):
+		if not self._enabled:
+			return
+		# custom TrackControl. A mousedown in a scrollbar arrow or page area should
+		# generate _control hits as long as the mouse is a) down, b) still in the same part
+		part = self._control.TestControl(point)
+		if Controls.inUpButton <= part <= Controls.inPageDown:	
+			self._control.HiliteControl(part)
+			self._hit(part)
+			oldpart = part
+			while Evt.StillDown():
+				part = self._control.TestControl(point)
+				if part == oldpart:
+					self._control.HiliteControl(part)
+					self._hit(part)
+				else:
+					self._control.HiliteControl(0)
+				self.SetPort()
+				point = Evt.GetMouse()
+			self._control.HiliteControl(0)
+		elif part == Controls.inThumb:
+			part = self._control.TrackControl(point)
+			if part:
+				self._hit(part)
+	
+	def _hit(self, part):
+		if part == Controls.inThumb:
+			value = self._control.GetControlValue()
+		elif part == Controls.inUpButton:
+			value = "+"
+		elif part == Controls.inDownButton:
+			value = "-"
+		elif part == Controls.inPageUp:
+			value = "++"
+		elif part == Controls.inPageDown:
+			value = "--"
+		if self._callback:
+			Wbase.CallbackCall(self._callback, 1, value)
+	
+	def draw(self, visRgn = None):
+		if self._visible:
+			self._control.Draw1Control()
+			Qd.FrameRect(self._bounds)
+	
+	def adjust(self, oldbounds):
+		self.SetPort()
+		Win.InvalRect(oldbounds)
+		self._control.HideControl()
+		self._control.MoveControl(self._bounds[0], self._bounds[1])
+		self._control.SizeControl(self._bounds[2] - self._bounds[0], self._bounds[3] - self._bounds[1])
+		if self._visible:
+			Qd.EraseRect(self._bounds)
+			if self._activated:
+				self._control.ShowControl()
+			else:
+				Qd.FrameRect(self._bounds)
+			Win.ValidRect(self._bounds)
+	
+	def activate(self, onoff):
+		self._activated = onoff
+		if self._visible:
+			if onoff:
+				self._control.ShowControl()
+			else:
+				self._control.HideControl()
+				self.draw(None)
+				Win.ValidRect(self._bounds)
+		
+	def set(self, value):
+		if self._control:
+			self._control.SetControlValue(value)
+		else:
+			self._value = value
+	
+	def get(self):
+		if self._control:
+			return self._control.GetControlValue()
+		else:
+			return self._value
+	
+
+def _scalebarvalue(absmin, absmax, curmin, curmax):
+	if curmin <= absmin and curmax >= absmax:
+		return None
+	if curmin <= absmin:
+		return 0
+	if curmax >= absmax:
+		return 32767
+	perc = float(curmin-absmin) / float((absmax - absmin) - (curmax - curmin))
+	return int(perc*32767)
+
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wdialogs.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wdialogs.py
new file mode 100644
index 0000000..b954255
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wdialogs.py
@@ -0,0 +1,183 @@
+import W
+from types import *
+import string
+
+"""
+import Wdialogs
+testDict1 = {1:1, 2:2, 3:3}
+testDict2 = {3:3,4:4, 'testDict1':testDict1, 6:6, 7:7}
+testDict3 = {3:3,4:4, 'testDict2':testDict2, 'testDict1':testDict1, 6:6, 7:7}
+Wdialogs.EditDictionary(testDict3)
+
+import Wdialogs
+a = Wdialogs.Ask('xxx', 'default text', ['font', 'typografie', 'lettertonwerpen', 'huisstijl'])
+"""
+
+def Message(text, button = "OK"):
+	w = W.ModalDialog((300, 100))
+	w.button = W.Button((-90, -30, 80, 16), button, w.close)
+	w.message = W.TextBox((10, 10, -10, -40), text)
+	w.setdefaultbutton(w.button)
+	w.open()
+
+
+def Ask(question, defaulttext = "", selections = []):
+	d = _Ask(question, defaulttext, selections)
+	return d.rv
+
+
+class _Ask:
+	#	selections is a list of possible for selections
+	
+	def __init__(self, question, defaulttext, selections):
+		self.selections = []
+		for s in selections:
+			self.selections.append(string.lower(s))
+		self.selections.sort()
+		self.w = W.ModalDialog((300, 120))
+		self.w.button1 = W.Button((-90, -30, 80, 16), "OK", self.button1hit)
+		self.w.button2 = W.Button((-180, -30, 80, 16), "Cancel", self.button2hit)
+		self.w.question = W.TextBox((10, 10, -10, 30), question)
+		self.w.input = W.EditText((10, 40, -10, 20), defaulttext, self.processInput)
+		self.rv = None
+		self.w.setdefaultbutton(self.w.button1)
+		
+		self.w.bind("cmd.", self.w.button2.push)
+		self.w.open()
+	
+	def processInput(self, key, modifiers):	# Process user input to match a selection
+		pos = self.w.input.getselection()
+		input = string.lower(self.w.input.get()[0:pos[1]])
+		if len(input):
+			for t in self.selections:
+				if input == t[0:pos[0]]:
+					self.w.input.set(t)
+					self.w.input.setselection(pos[0], pos[1])
+					return
+		self.w.input.set(input)
+		self.w.input.setselection(pos[1], pos[1])
+		
+	def button1hit(self):
+		self.rv = self.w.input.get()
+		self.w.close()
+		
+	def button2hit(self):
+		self.w.close()
+
+class _AskYesNo:
+	
+	def __init__(self, question, cancelFlag= 0):
+		if cancelFlag:
+			size = 190, 80
+		else:	size = 150, 80
+		self.w = W.ModalDialog(size)
+		self.w.yes = W.Button((10, -36, 50, 24), 'Yes', self.yes)
+		if cancelFlag:
+			self.w.cancel = W.Button((70, -36, -70, 24), "Cancel", self.cancel)	
+		self.w.no = W.Button((-60, -36, -10, 24), 'No', self.no)
+		self.w.question = W.TextBox((10, 10, -10, 30), question)
+		self.rv = None
+		self.w.setdefaultbutton(self.w.yes)
+		if cancelFlag:
+			self.w.bind("cmd.", self.w.cancel)
+		else:	self.w.bind("cmd.", self.w.no)
+		self.w.open()
+	
+	def yes(self):
+		self.rv = 1
+		self.w.close()
+		
+	def no(self):
+		self.rv = 0
+		self.w.close()
+
+	def cancel(self):
+		self.rv = -1
+		self.w.close()
+
+def AskYesNo(question):
+	d = _AskYesNo(question, 0)
+	return d.rv
+	
+def AskYesCancelNo(question):
+	d = _AskYesNo(question, 1)
+	return d.rv
+	
+class CallBackButton(W.Button):
+	def click(self, point, modifiers):
+		if not self._enabled:
+			return
+		part = self._control.TrackControl(point)
+		if part:
+			if self._callback:
+				self._callback(self.dict)
+	
+	def push(self):
+		if not self._enabled:
+			return
+		import time
+		self._control.HiliteControl(1)
+		time.sleep(0.1)
+		self._control.HiliteControl(0)
+		if self._callback:
+			self._callback(self.dict)
+		
+class EditDictionary:			# Auto layout editor of dictionary
+	def __init__(self, dictionary, title = 'Dictionary Editor'):
+		self.leading = 20
+		self.d = dictionary
+		keys = self.d.keys()
+		windowSize = 400, len(keys) * self.leading + 100
+		self.w = w = W.ModalDialog(windowSize)
+		y = 2 * self.leading
+		theFont = fontsettings = ('Geneva', 0, 10, (0,0,0))
+		keys.sort()
+		for key in keys:
+			if type(key) == StringType:
+				label = key
+			else:	label = `key`
+			if type(self.d[key]) == StringType:
+				value = self.d[key]
+			else:
+				value = `self.d[key]`		# Just show the value
+			
+			if type(self.d[key]) == DictType:	# Make a button
+				button = w[label] = CallBackButton((110, y, 50, 18), label, self.pushDict)
+				button.dict = self.d[key]
+			else:
+				w['k_' + label] = W.TextBox((10, y, 200, 18), label, fontsettings = theFont)
+				w[label] = W.EditText((110, y, -10, 18), value, fontsettings = theFont)
+			y = y + self.leading
+		
+		w._name = W.TextBox((10, 4, 100, 10), title)
+		w._ok = W.Button((-160, -36, 60, 24), "OK", self.ok)
+		w._cancel = W.Button((-80, -36, 60, 24), "Cancel", self.cancel)
+		w.setdefaultbutton(self.w._ok)
+
+		self.rv = None	# Return value
+		w.open()
+	
+	def pushDict(self, dict):
+		EditDictionary(dict)
+		
+	def popDict(self):
+		self.w.close()
+		
+	def ok(self):
+		self.rv = 1
+		for key in self.d.keys():
+			if type(key) == StringType:
+				label = key
+			else:	label = `key`
+			if type(self.d[key]) == StringType or self.d[key] == None:
+				self.d[key] = self.w[label].get()
+			else:	
+				try:
+					self.d[key] = eval(self.w[label].get())
+				except:
+					pass
+		self.popDict()
+		
+	def cancel(self):
+		self.rv = 0
+		self.popDict()
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wgrid.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wgrid.py
new file mode 100644
index 0000000..efd7cb9
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wgrid.py
@@ -0,0 +1,33 @@
+"""grid utility for widgets"""
+
+class Grid:
+	
+	def __init__(self, 	ncol = None, 
+				minncol = None,
+				maxncol = None,
+				colwidth = None, 
+				mincolwidth = None, 
+				maxcolwidth = None, 
+				width = None, 
+				minwidth = None, 
+				maxwidth = None, 
+				vgrid = 8, 
+				gutter = 10,
+				leftmargin = None,
+				rightmargin = None,
+				topmargin = None,
+				bottommargin = None
+				):
+		if leftmargin == None:
+			leftmargin = gutter
+		if rightmargin == None:
+			rightmargin = gutter
+		if topmargin == None:
+			topmargin = vgrid
+		if bottommargin == None:
+			bottommargin = vgrid
+	
+	def getbounds(self, width, height, bounds):
+		xxx
+
+
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wlist.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wlist.py
new file mode 100644
index 0000000..028e878
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wlist.py
@@ -0,0 +1,425 @@
+import Wbase
+import Scrap
+from SpecialKeys import *
+import string
+import Evt
+import Events
+import Qd
+import Win
+
+
+class List(Wbase.SelectableWidget):
+	
+	LDEF_ID = 0
+	
+	def __init__(self, possize, items = None, callback = None, flags = 0, cols = 1):
+		if items is None:
+			items = []
+		self.items = items
+		Wbase.SelectableWidget.__init__(self, possize)
+		self._selected = 0
+		self._enabled = 1
+		self._list = None
+		self._cols = cols
+		self._callback = callback
+		self._flags = flags
+		self.lasttyping = ""
+		self.lasttime = Evt.TickCount()
+		self.timelimit = 30
+		self.setitems(items)
+		self.drawingmode = 0
+	
+	def open(self):
+		self.setdrawingmode(0)
+		self.createlist()
+		self.setdrawingmode(1)
+	
+	def createlist(self):
+		import List
+		self._calcbounds()
+		self.SetPort()
+		rect = self._bounds
+		rect = rect[0]+1, rect[1]+1, rect[2]-16, rect[3]-1
+		self._list = List.LNew(rect, (0, 0, self._cols, 0), (0, 0), self.LDEF_ID, self._parentwindow.wid,
+					0, 1, 0, 1)
+		if self.drawingmode:
+			self._list.LSetDrawingMode(0)
+		self._list.selFlags = self._flags
+		self.setitems(self.items)
+		if hasattr(self, "_sel"):
+			self.setselection(self._sel)
+			del self._sel
+	
+	def adjust(self, oldbounds):
+		self.SetPort()
+		if self._selected:
+			Win.InvalRect(Qd.InsetRect(oldbounds, -3, -3))
+			Win.InvalRect(Qd.InsetRect(self._bounds, -3, -3))
+		else:
+			Win.InvalRect(oldbounds)
+			Win.InvalRect(self._bounds)
+		if oldbounds[:2] == self._bounds[:2]:
+			# list still has the same upper/left coordinates, use LSize
+			l, t, r, b = self._bounds
+			width = r - l - 17
+			height = b - t - 2
+			self._list.LSize(width, height)
+			# now *why* deosn't the list manager recalc the cellrect???
+			l, t, r, b = self._list.LRect((0,0))
+			cellheight = b - t
+			self._list.LCellSize((width, cellheight))
+		else:
+			# oh well, sice the list manager doesn't have a LMove call,
+			# we have to make the list all over again...
+			sel = self.getselection()
+			topcell = self.gettopcell()
+			self._list = None
+			self.setdrawingmode(0)
+			self.createlist()
+			self.setselection(sel)
+			self.settopcell(topcell)
+			self.setdrawingmode(1)
+	
+	def close(self):
+		self._list = None
+		self._callback = None
+		self.items[:] = []
+		Wbase.SelectableWidget.close(self)
+	
+	def set(self, items):
+		self.setitems(items)
+	
+	def setitems(self, items):
+		self.items = items
+		the_list = self._list
+		if not self._parent or not self._list:
+			return
+		self.setdrawingmode(0)
+		topcell = self.gettopcell()
+		the_list.LDelRow(0, 1)
+		the_list.LAddRow(len(self.items), 0)
+		self_itemrepr = self.itemrepr
+		set_cell = the_list.LSetCell
+		for i in range(len(items)):
+			set_cell(self_itemrepr(items[i]), (0, i))
+		self.settopcell(topcell)
+		self.setdrawingmode(1)
+	
+	def click(self, point, modifiers):
+		if not self._enabled:
+			return
+		isdoubleclick = self._list.LClick(point, modifiers)
+		if self._callback:
+			Wbase.CallbackCall(self._callback, 0, isdoubleclick)
+		return 1
+	
+	def key(self, char, event):
+		(what, message, when, where, modifiers) = event
+		sel = self.getselection()
+		newselection = []
+		if char == uparrowkey:
+			if len(sel) >= 1 and min(sel) > 0:
+				newselection = [min(sel) - 1]
+			else:
+				newselection = [0]
+		elif char == downarrowkey:
+			if len(sel) >= 1 and max(sel) < (len(self.items) - 1):
+					newselection = [max(sel) + 1]
+			else:
+				newselection = [len(self.items) - 1]
+		else:
+			modifiers = 0
+			if (self.lasttime + self.timelimit) < Evt.TickCount():
+				self.lasttyping = ""
+			self.lasttyping = self.lasttyping + string.lower(char)
+			self.lasttime = Evt.TickCount()
+			i = self.findmatch(self.lasttyping)
+			newselection = [i]
+		if modifiers & Events.shiftKey:
+			newselection = newselection + sel
+		self.setselection(newselection)
+		self._list.LAutoScroll()
+		self.click((-1, -1), 0)
+	
+	def findmatch(self, tag):
+		lower = string.lower
+		items = self.items
+		taglen = len(tag)
+		match = '\377' * 100
+		match_i = -1
+		for i in range(len(items)):
+			item = lower(str(items[i]))
+			if tag <= item < match:
+				match = item
+				match_i = i
+		if match_i >= 0:
+			return match_i
+		else:
+			return len(items) - 1
+	
+	def domenu_copy(self, *args):
+		sel = self.getselection()
+		selitems = []
+		for i in sel:
+			selitems.append(str(self.items[i]))
+		text = string.join(selitems, '\r')
+		if text:
+			Scrap.ZeroScrap()
+			Scrap.PutScrap('TEXT', text)
+	
+	def can_copy(self, *args):
+		return len(self.getselection()) <> 0
+	
+	def domenu_selectall(self, *args):
+		self.selectall()
+	
+	def selectall(self):
+		self.setselection(range(len(self.items)))
+		self._list.LAutoScroll()
+		self.click((-1, -1), 0)
+	
+	def getselection(self):
+		if not self._parent or not self._list:
+			if hasattr(self, "_sel"):
+				return self._sel
+			return []
+		items = []
+		point = (0,0)
+		while 1:
+			ok, point = self._list.LGetSelect(1, point)
+			if not ok:
+				break
+			items.append(point[1])
+			point = point[0], point[1]+1
+		return items
+	
+	def setselection(self, selection):
+		if not self._parent or not self._list:
+			self._sel = selection
+			return
+		set_sel = self._list.LSetSelect
+		for i in range(len(self.items)):
+			if i in selection:
+				set_sel(1, (0, i))
+			else:
+				set_sel(0, (0, i))
+		self._list.LAutoScroll()
+	
+	def getselectedobjects(self):
+		sel = self.getselection()
+		objects = []
+		for i in sel:
+			objects.append(self.items[i])
+		return objects
+	
+	def setselectedobjects(self, objects):
+		sel = []
+		for o in objects:
+			try:
+				sel.append(self.items.index(o))
+			except:
+				pass
+		self.setselection(sel)
+	
+	def gettopcell(self):
+		l, t, r, b = self._bounds
+		t = t + 1
+		cl, ct, cr, cb = self._list.LRect((0, 0))
+		cellheight = cb - ct
+		return (t - ct) / cellheight
+	
+	def settopcell(self, topcell):
+		top = self.gettopcell()
+		diff = topcell - top
+		self._list.LScroll(0, diff)
+	
+	def draw(self, visRgn = None):
+		if self._visible:
+			if not visRgn:
+				visRgn = self._parentwindow.wid.GetWindowPort().visRgn
+			self._list.LUpdate(visRgn)
+			Qd.FrameRect(self._bounds)
+			if self._selected and self._activated:
+				self.drawselframe(1)
+	
+	def adjust(self, oldbounds):
+		self.SetPort()
+		if self._selected:
+			Win.InvalRect(Qd.InsetRect(oldbounds, -3, -3))
+			Win.InvalRect(Qd.InsetRect(self._bounds, -3, -3))
+		else:
+			Win.InvalRect(oldbounds)
+			Win.InvalRect(self._bounds)
+		if oldbounds[:2] == self._bounds[:2]:
+			# list still has the same upper/left coordinates, use LSize
+			l, t, r, b = self._bounds
+			width = r - l - 17
+			height = b - t - 2
+			self._list.LSize(width, height)
+			# now *why* deosn't the list manager recalc the cellrect???
+			l, t, r, b = self._list.LRect((0,0))
+			cellheight = b - t
+			self._list.LCellSize((width, cellheight))
+		else:
+			# oh well, sice the list manager doesn't have a LMove call,
+			# we have to make the list all over again...
+			sel = self.getselection()
+			topcell = self.gettopcell()
+			self._list = None
+			self.setdrawingmode(0)
+			self.createlist()
+			self.setselection(sel)
+			self.settopcell(topcell)
+			self.setdrawingmode(1)
+	
+	def select(self, onoff, isclick = 0):
+		if Wbase.SelectableWidget.select(self, onoff):
+			return
+		self.SetPort()
+		self.drawselframe(onoff)
+	
+	def activate(self, onoff):
+		self._activated = onoff
+		if self._visible:
+			self._list.LActivate(onoff)
+			if self._selected:
+				self.drawselframe(onoff)
+	
+	def get(self):
+		return self.items
+	
+	def itemrepr(self, item):
+		return str(item)[:255]
+	
+	def __getitem__(self, index):
+		return self.items[index]
+	
+	def __setitem__(self, index, item):
+		if self._parent and self._list:
+			self._list.LSetCell(self.itemrepr(item), (0, index))
+		self.items[index] = item
+	
+	def __delitem__(self, index):
+		if self._parent and self._list:
+			self._list.LDelRow(1, index)
+		del self.items[index]
+	
+	def __getslice__(self, a, b):
+		return self.items[a:b]
+	
+	def __delslice__(self, a, b):
+		if b-a:
+			if self._parent and self._list:
+				self._list.LDelRow(b-a, a)
+			del self.items[a:b]
+	
+	def __setslice__(self, a, b, items):
+		if self._parent and self._list:
+			l = len(items)
+			the_list = self._list
+			self.setdrawingmode(0)
+			if b-a:
+				if b > len(self.items):
+					# fix for new 1.5 "feature" where b is sys.maxint instead of len(self)...
+					# LDelRow doesn't like maxint.
+					b = len(self.items)
+				the_list.LDelRow(b-a, a)
+			the_list.LAddRow(l, a)
+			self_itemrepr = self.itemrepr
+			set_cell = the_list.LSetCell
+			for i in range(len(items)):
+				set_cell(self_itemrepr(items[i]), (0, i + a))
+			self.items[a:b] = items
+			self.setdrawingmode(1)
+		else:
+			self.items[a:b] = items
+	
+	def __len__(self):
+		return len(self.items)
+	
+	def append(self, item):
+		if self._parent and self._list:
+			index = len(self.items)
+			self._list.LAddRow(1, index)
+			self._list.LSetCell(self.itemrepr(item), (0, index))
+		self.items.append(item)
+	
+	def remove(self, item):
+		index = self.items.index(item)
+		self.__delitem__(index)
+	
+	def index(self, item):
+		return self.items.index(item)
+	
+	def insert(self, index, item):
+		if index < 0:
+			index = 0
+		if self._parent and self._list:
+			self._list.LAddRow(1, index)
+			self._list.LSetCell(self.itemrepr(item), (0, index))
+		self.items.insert(index, item)
+	
+	def setdrawingmode(self, onoff):
+		if onoff:
+			self.drawingmode = self.drawingmode - 1
+			if self.drawingmode == 0 and self._list is not None:
+				self._list.LSetDrawingMode(1)
+				if self._visible:
+					bounds = l, t, r, b = Qd.InsetRect(self._bounds, 1, 1)
+					cl, ct, cr, cb = self._list.LRect((0, len(self.items)-1))
+					if cb < b:
+						self.SetPort()
+						Qd.EraseRect((l, cb, cr, b))
+					self._list.LUpdate(self._parentwindow.wid.GetWindowPort().visRgn)
+					Win.ValidRect(bounds)
+		else:
+			if self.drawingmode == 0 and self._list is not None:
+				self._list.LSetDrawingMode(0)
+			self.drawingmode = self.drawingmode + 1
+
+
+class MultiList(List):
+	
+	def setitems(self, items):
+		self.items = items
+		if not self._parent or not self._list:
+			return
+		self._list.LDelRow(0, 1)
+		self.setdrawingmode(0)
+		self._list.LAddRow(len(self.items), 0)
+		self_itemrepr = self.itemrepr
+		set_cell = self._list.LSetCell
+		for i in range(len(items)):
+			row = items[i]
+			for j in range(len(row)):
+				item = row[j]
+				set_cell(self_itemrepr(item), (j, i))
+		self.setdrawingmode(1)
+	
+	def getselection(self):
+		if not self._parent or not self._list:
+			if hasattr(self, "_sel"):
+				return self._sel
+			return []
+		items = []
+		point = (0,0)
+		while 1:
+			ok, point = self._list.LGetSelect(1, point)
+			if not ok:
+				break
+			items.append(point[1])
+			point = point[0], point[1]+1
+		return items
+	
+	def setselection(self, selection):
+		if not self._parent or not self._list:
+			self._sel = selection
+			return
+		set_sel = self._list.LSetSelect
+		for i in range(len(self.items)):
+			if i in selection:
+				set_sel(1, (0, i))
+			else:
+				set_sel(0, (0, i))
+		#self._list.LAutoScroll()
+
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wmenus.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wmenus.py
new file mode 100644
index 0000000..350e036
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wmenus.py
@@ -0,0 +1,182 @@
+import FrameWork
+import Qd
+import Wbase
+from types import *
+import WFrameWorkPatch
+
+_arrowright = Qd.GetPicture(472)
+_arrowdown = Qd.GetPicture(473)
+	
+
+
+class PopupWidget(Wbase.ClickableWidget):
+	
+	def __init__(self, possize, items = [], callback = None):
+		Wbase.Widget.__init__(self, possize)
+		self._items = items
+		self._itemsdict = {}
+		self._callback = callback
+		self._enabled = 1
+	
+	def close(self):
+		Wbase.Widget.close(self)
+		self._items = None
+		self._itemsdict = {}
+	
+	def draw(self, visRgn = None):
+		if self._visible:
+			Qd.FrameRect(self._bounds)
+			l, t, r, b = self._bounds
+			l = l + 2
+			t = t + 3
+			pictframe = (l, t, l + 10, t + 10)
+			Qd.DrawPicture(_arrowright, pictframe)
+	
+	def click(self, point, modifiers):
+		if not self._enabled:
+			return
+		self.menu = FrameWork.Menu(self._parentwindow.parent.menubar, 'Foo', -1)
+		self._additems(self._items, self.menu)
+		
+		self.SetPort()
+		l, t, r, b = self._bounds
+		l, t = Qd.LocalToGlobal((l+1, t+1))
+		Wbase.SetCursor("arrow")
+		reply = self.menu.menu.PopUpMenuSelect(t, l, 1)
+		if reply:
+			id = (reply & 0xffff0000) >> 16
+			item = reply & 0xffff
+			self._menu_callback(id, item)
+		self._emptymenu()
+	
+	def set(self, items):
+		self._items = items
+	
+	def _additems(self, items, menu):
+		from FrameWork import SubMenu, MenuItem
+		menu_id = menu.id
+		for item in items:
+			if item == "-":
+				menu.addseparator()
+				continue
+			elif type(item) == ListType:
+				submenu = SubMenu(menu, item[0])
+				self._additems(item[1:], submenu)
+				continue
+			elif type(item) == StringType:
+				menuitemtext = object = item
+			elif type(item) == TupleType and len(item) == 2:
+				menuitemtext, object = item
+			else:
+				raise WidgetsError, "illegal itemlist for popup menu"
+			
+			if menuitemtext[:1] == '\0':
+				check = ord(menuitemtext[1])
+				menuitemtext = menuitemtext[2:]
+			else:
+				check = 0
+			menuitem = MenuItem(menu, menuitemtext, None, None)
+			if check:
+				menuitem.check(1)
+			self._itemsdict[(menu_id, menuitem.item)] = object
+	
+	def _emptymenu(self):
+		menus = self._parentwindow.parent.menubar.menus
+		for id, item in self._itemsdict.keys():
+			if menus.has_key(id):
+				self.menu = menus[id]
+				self.menu.delete()
+		self._itemsdict = {}
+	
+	def _menu_callback(self, id, item):
+		thing = self._itemsdict[(id, item)]
+		if callable(thing):
+			thing()
+		elif self._callback:
+			Wbase.CallbackCall(self._callback, 0, thing)
+
+
+class PopupMenu(PopupWidget):
+	
+	def open(self):
+		self._calcbounds()
+		self.menu = WFrameWorkPatch.Menu(self._parentwindow.parent.menubar, 'Foo', -1)
+		self._additems(self._items, self.menu)
+	
+	def close(self):
+		self._emptymenu()
+		Wbase.Widget.close(self)
+		self._items = None
+		self._itemsdict = {}
+		self.menu = None
+	
+	def set(self, items):
+		if self._itemsdict:
+			self._emptymenu()
+		self.menu = WFrameWorkPatch.Menu(self._parentwindow.parent.menubar, 'Foo', -1)
+		self._items = items
+		self._additems(self._items, self.menu)
+	
+	def click(self, point, modifiers):
+		if not self._enabled:
+			return
+		self.SetPort()
+		l, t, r, b = self._bounds
+		l, t = Qd.LocalToGlobal((l+1, t+1))
+		Wbase.SetCursor("arrow")
+		reply = self.menu.menu.PopUpMenuSelect(t, l, 1)
+		if reply:
+			id = (reply & 0xffff0000) >> 16
+			item = reply & 0xffff
+			self._menu_callback(id, item)
+
+
+class FontMenu(PopupMenu):
+	
+	menu = None
+	
+	def __init__(self, possize, callback):
+		PopupMenu.__init__(self, possize)
+		makefontmenu()
+		self._callback = callback
+		self._enabled = 1
+	
+	def open(self):
+		self._calcbounds()
+	
+	def close(self):
+		pass
+	
+	def set(self):
+		raise Wbase.WidgetsError, "can't change font menu widget"
+	
+	def _menu_callback(self, id, item):
+		fontname = self.menu.menu.GetMenuItemText(item)
+		if self._callback:
+			Wbase.CallbackCall(self._callback, 0, fontname)
+
+	def click(self, point, modifiers):
+		if not self._enabled:
+			return
+		makefontmenu()
+		return PopupMenu.click(self, point, modifiers)
+	
+
+def makefontmenu():
+	if FontMenu.menu is not None:
+		return
+	import W
+	FontMenu.menu = WFrameWorkPatch.Menu(W.getapplication().menubar, 'Foo', -1)
+	W.SetCursor('watch')
+	for i in range(FontMenu.menu.menu.CountMItems(), 0, -1):
+		FontMenu.menu.menu.DeleteMenuItem(i)
+	FontMenu.menu.menu.AppendResMenu('FOND')
+
+
+def _getfontlist():
+	import Res
+	fontnames = []
+	for i in range(1, Res.CountResources('FOND') + 1):
+		r = Res.GetIndResource('FOND', i)
+		fontnames.append(r.GetResInfo()[2])
+	return fontnames
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wquicktime.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wquicktime.py
new file mode 100644
index 0000000..adfdadb
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wquicktime.py
@@ -0,0 +1,119 @@
+import os
+import Qd
+import Win
+import Qt, QuickTime
+import W
+import macfs
+import Evt, Events
+
+_moviesinitialized = 0
+
+def EnterMovies():
+	global _moviesinitialized
+	if not _moviesinitialized:
+		Qt.EnterMovies()
+		_moviesinitialized = 1
+
+class Movie(W.Widget):
+	
+	def __init__(self, possize):
+		EnterMovies()
+		self.movie = None
+		self.running = 0
+		W.Widget.__init__(self, possize)
+	
+	def adjust(self, oldbounds):
+		self.SetPort()
+		Win.InvalRect(oldbounds)
+		Win.InvalRect(self._bounds)
+		self.calcmoviebox()
+	
+	def set(self, path_or_fss, start = 0):
+		self.SetPort()
+		if self.movie:
+			#Win.InvalRect(self.movie.GetMovieBox())
+			Qd.PaintRect(self.movie.GetMovieBox())
+		if type(path_or_fss) == type(''):
+			path = path_or_fss
+			fss = macfs.FSSpec(path)
+		else:
+			path = path_or_fss.as_pathname()
+			fss = path_or_fss
+		self.movietitle = os.path.basename(path)
+		movieResRef = Qt.OpenMovieFile(fss, 1)
+		self.movie, dummy = Qt.NewMovieFromFile(movieResRef, QuickTime.newMovieActive)
+		self.moviebox = self.movie.GetMovieBox()
+		self.calcmoviebox()
+		Qd.ObscureCursor()	# XXX does this work at all?
+		self.movie.GoToBeginningOfMovie()
+		if start:
+			self.movie.StartMovie()
+			self.running = 1
+		else:
+			self.running = 0
+			self.movie.MoviesTask(0)
+	
+	def get(self):
+		return self.movie
+	
+	def getmovietitle(self):
+		return self.movietitle
+	
+	def start(self):
+		if self.movie:
+			Qd.ObscureCursor()
+			self.movie.StartMovie()
+			self.running = 1
+	
+	def stop(self):
+		if self.movie:
+			self.movie.StopMovie()
+			self.running = 0
+	
+	def rewind(self):
+		if self.movie:
+			self.movie.GoToBeginningOfMovie()
+	
+	def calcmoviebox(self):
+		if not self.movie:
+			return
+		ml, mt, mr, mb = self.moviebox
+		wl, wt, wr, wb = widgetbox = self._bounds
+		mheight = mb - mt
+		mwidth = mr - ml
+		wheight = wb - wt
+		wwidth = wr - wl
+		if (mheight * 2 < wheight) and (mwidth * 2 < wwidth):
+			scale = 2
+		elif mheight > wheight or mwidth > wwidth:
+			scale = min(float(wheight) / mheight, float(wwidth) / mwidth)
+		else:
+			scale = 1
+		mwidth, mheight = mwidth * scale, mheight * scale
+		ml, mt = wl + (wwidth - mwidth) / 2, wt + (wheight - mheight) / 2
+		mr, mb = ml + mwidth, mt + mheight
+		self.movie.SetMovieBox((ml, mt, mr, mb))
+	
+	def idle(self, *args):
+		if self.movie:
+			if not self.movie.IsMovieDone() and self.running:
+				Qd.ObscureCursor()
+				while 1:
+					self.movie.MoviesTask(0)
+					gotone, event = Evt.EventAvail(Events.everyEvent)
+					if gotone or self.movie.IsMovieDone():
+						break
+			elif self.running:
+				box = self.movie.GetMovieBox()
+				self.SetPort()
+				Win.InvalRect(box)
+				self.movie = None
+				self.running = 0
+	
+	def draw(self, visRgn = None):
+		if self._visible:
+			Qd.PaintRect(self._bounds)
+			if self.movie:
+				self.movie.UpdateMovie()
+				self.movie.MoviesTask(0)
+
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wtext.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wtext.py
new file mode 100644
index 0000000..2654cc3
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wtext.py
@@ -0,0 +1,882 @@
+import Qd
+import TE
+import Fm
+import waste
+import WASTEconst
+import Res
+import Evt
+import Events
+import Scrap
+import string
+
+import Win
+import Wbase
+import Wcontrols
+from SpecialKeys import *
+import PyFontify
+from types import *
+import Fonts
+import TextEdit
+
+
+
+class TextBox(Wbase.Widget):
+	
+	def __init__(self, possize, text = "", align = TextEdit.teJustLeft, 
+				fontsettings = ("Monaco", 0, 9, (0, 0, 0)),
+				backcolor = (0xffff, 0xffff, 0xffff)
+				):
+	
+		Wbase.Widget.__init__(self, possize)
+		self.fontsettings = fontsettings
+		self.text = text
+		self.align = align
+		self.backcolor = backcolor	# Settings of editor added by Petr 9/7/97
+	
+	def draw(self, visRgn = None):
+		if self._visible:
+			(font, style, size, color) = self.fontsettings
+			fontid = GetFNum(font)
+			savestate = Qd.GetPenState()
+			Qd.TextFont(fontid)
+			Qd.TextFace(style)
+			Qd.TextSize(size)
+			Qd.RGBForeColor(color)
+			Qd.RGBBackColor(self.backcolor)		# Added by Petr 9/7/97
+			TE.TETextBox(self.text, self._bounds, self.align)
+			Qd.RGBBackColor((0xffff, 0xffff, 0xffff))	# Reset color Added by Petr 9/7/97
+			Qd.SetPenState(savestate)
+	
+	def get(self):
+		return self.text
+	
+	def set(self, text):
+		self.text = text
+		if self._parentwindow and self._parentwindow.wid:
+			self.SetPort()
+			self.draw()
+
+
+class ScrollWidget:
+	
+	# to be overridden
+	def getscrollbarvalues(self):
+		return None, None
+	
+	# internal method
+	def updatescrollbars(self):
+		vx, vy = self.getscrollbarvalues()
+		if self._parent._barx:
+			if vx <> None:
+				self._parent._barx.enable(1)
+				self._parent._barx.set(vx)
+			else:
+				self._parent._barx.enable(0)
+		if self._parent._bary:
+			if vy <> None:
+				self._parent._bary.enable(1)
+				self._parent._bary.set(vy)
+			else:
+				self._parent._bary.enable(0)
+	
+
+UNDOLABELS = [	# Indexed by WEGetUndoInfo() value
+	None, "", "typing", "Cut", "Paste", "Clear", "Drag", "Style"]
+	
+class EditText(Wbase.SelectableWidget, ScrollWidget):
+	
+	def __init__(self, possize, text = "", 
+				callback = None, inset = (3, 3), 
+				fontsettings = ("Python-Sans", 0, 9, (0, 0, 0)),
+				readonly = 0):
+				
+		Wbase.SelectableWidget.__init__(self, possize)
+		self.temptext = text
+		self.ted = None
+		self.selection = None
+		self._callback = callback
+		self.changed = 0
+		self.selchanged = 0
+		self._selected = 0
+		self._enabled = 1
+		self.wrap = 1
+		self.readonly = readonly
+		self.fontsettings = fontsettings
+		if type(inset) <> TupleType:
+			self.inset = (inset, inset)
+		else:
+			self.inset = inset
+	
+	def open(self):
+		if not hasattr(self._parent, "_barx"):
+			self._parent._barx = None
+		if not hasattr(self._parent, "_bary"):
+			self._parent._bary = None
+		self._calcbounds()
+		self.SetPort()
+		viewrect, destrect = self._calctextbounds()
+		flags = self._getflags()
+		self.ted = waste.WENew(destrect, viewrect, flags)
+		self.ted.WEInstallTabHooks()
+		self.ted.WESetAlignment(WASTEconst.weFlushLeft)
+		self.setfontsettings(self.fontsettings)
+		self.ted.WEUseText(Res.Resource(self.temptext))
+		self.ted.WECalText()
+		if self.selection:
+			self.setselection(self.selection[0], self.selection[1])
+			self.selection = None
+		else:
+			self.selview()
+		self.temptext = None
+		self.updatescrollbars()
+		self.bind("pageup", self.scrollpageup)
+		self.bind("pagedown", self.scrollpagedown)
+		self.bind("top", self.scrolltop)
+		self.bind("bottom", self.scrollbottom)
+		self.selchanged = 0
+	
+	def close(self):
+		self._parent._barx = None
+		self._parent._bary = None
+		self.ted = None
+		self.temptext = None
+		Wbase.SelectableWidget.close(self)
+	
+	def getfontsettings(self):
+		import Res
+		(font, style, size, color) = self.ted.WEGetRunInfo(0)[4]
+		font = GetFName(font)
+		return (font, style, size, color)
+	
+	def setfontsettings(self, (font, style, size, color)):
+		self.SetPort()
+		if type(font) <> StringType:
+			font = GetFName(font)
+		self.fontsettings = (font, style, size, color)
+		fontid = GetFNum(font)
+		readonly = self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, -1)
+		if readonly:
+			self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 0)
+		self.ted.WEFeatureFlag(WASTEconst.weFInhibitRecal, 1)
+		selstart, selend = self.ted.WEGetSelection()
+		self.ted.WESetSelection(0, self.ted.WEGetTextLength())
+		self.ted.WESetStyle(WASTEconst.weDoFace, (0, 0, 0, (0, 0, 0)))
+		self.ted.WESetStyle(WASTEconst.weDoFace | 
+					WASTEconst.weDoColor | 
+					WASTEconst.weDoFont | 
+					WASTEconst.weDoSize, 
+					(fontid, style, size, color))
+		self.ted.WEFeatureFlag(WASTEconst.weFInhibitRecal, 0)
+		self.ted.WECalText()
+		self.ted.WESetSelection(selstart, selend)
+		if readonly:
+			self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 1)
+		viewrect = self.ted.WEGetViewRect()
+		Qd.EraseRect(viewrect)
+		self.ted.WEUpdate(self._parentwindow.wid.GetWindowPort().visRgn)
+		self.selchanged = 1
+		self.updatescrollbars()
+	
+	def adjust(self, oldbounds):
+		self.SetPort()
+		if self._selected and self._parentwindow._hasselframes:
+			Win.InvalRect(Qd.InsetRect(oldbounds, -3, -3))
+			Win.InvalRect(Qd.InsetRect(self._bounds, -3, -3))
+		else:
+			Win.InvalRect(oldbounds)
+			Win.InvalRect(self._bounds)
+		viewrect, destrect = self._calctextbounds()
+		self.ted.WESetViewRect(viewrect)
+		self.ted.WESetDestRect(destrect)
+		if self.wrap:
+			self.ted.WECalText()
+		if self.ted.WEGetDestRect()[3] < viewrect[1]:
+			self.selview()
+		self.updatescrollbars()
+	
+	# interface -----------------------
+	# selection stuff
+	def selview(self):
+		self.ted.WESelView()
+	
+	def selectall(self):
+		self.ted.WESetSelection(0, self.ted.WEGetTextLength())
+		self.selchanged = 1
+		self.updatescrollbars()
+	
+	def selectline(self, lineno, charoffset = 0):
+		newselstart, newselend = self.ted.WEGetLineRange(lineno)
+		self.ted.WESetSelection(newselstart + charoffset, newselend)
+		self.selchanged = 1
+		self.updatescrollbars()
+	
+	def getselection(self):
+		if self.ted:
+			return self.ted.WEGetSelection()
+		else:
+			return self.selection
+	
+	def setselection(self, selstart, selend):
+		self.selchanged = 1
+		if self.ted:
+			self.ted.WESetSelection(selstart, selend)
+			self.ted.WESelView()
+			self.updatescrollbars()
+		else:
+			self.selection = selstart, selend
+	
+	def offsettoline(self, offset):
+		return self.ted.WEOffsetToLine(offset)
+	
+	def countlines(self):
+		return self.ted.WECountLines()
+	
+	def getselectedtext(self):
+		selstart, selend = self.ted.WEGetSelection()
+		return self.ted.WEGetText().data[selstart:selend]
+	
+	def expandselection(self):
+		oldselstart, oldselend = self.ted.WEGetSelection()
+		selstart, selend = min(oldselstart, oldselend), max(oldselstart, oldselend)
+		if selstart <> selend and chr(self.ted.WEGetChar(selend-1)) == '\r':
+			selend = selend - 1
+		newselstart, dummy = self.ted.WEFindLine(selstart, 0)
+		dummy, newselend = self.ted.WEFindLine(selend, 0)
+		if oldselstart <> newselstart or  oldselend <> newselend:
+			self.ted.WESetSelection(newselstart, newselend)
+			self.updatescrollbars()
+		self.selchanged = 1
+	
+	def insert(self, text):
+		self.ted.WEInsert(text, None, None)
+		self.changed = 1
+		self.selchanged = 1
+	
+	def shiftleft(self):
+		self.expandselection()
+		selstart, selend = self.ted.WEGetSelection()
+		selstart, selend = min(selstart, selend), max(selstart, selend)
+		snippet = self.getselectedtext()
+		lines = string.split(snippet, '\r')
+		for i in range(len(lines)):
+			if lines[i][:1] == '\t':
+				lines[i] = lines[i][1:]
+		snippet = string.join(lines, '\r')
+		self.insert(snippet)
+		self.ted.WESetSelection(selstart, selstart + len(snippet))
+	
+	def shiftright(self):
+		self.expandselection()
+		selstart, selend = self.ted.WEGetSelection()
+		selstart, selend = min(selstart, selend), max(selstart, selend)
+		snippet = self.getselectedtext()
+		lines = string.split(snippet, '\r')
+		for i in range(len(lines) - 1):
+			lines[i] = '\t' + lines[i]
+		snippet = string.join(lines, '\r')
+		self.insert(snippet)
+		self.ted.WESetSelection(selstart, selstart + len(snippet))
+	
+	# text
+	def set(self, text):
+		if not self.ted:
+			self.temptext = text
+		else:
+			self.ted.WEUseText(Res.Resource(text))
+			self.ted.WECalText()
+			self.SetPort()
+			viewrect, destrect = self._calctextbounds()
+			self.ted.WESetViewRect(viewrect)
+			self.ted.WESetDestRect(destrect)
+			rgn = Qd.NewRgn()
+			Qd.RectRgn(rgn, viewrect)
+			Qd.EraseRect(viewrect)
+			self.draw(rgn)
+			#Win.InvalRect(self.ted.WEGetViewRect())
+			self.updatescrollbars()
+	
+	def get(self):
+		if not self._parent:
+			return self.temptext
+		else:
+			return self.ted.WEGetText().data
+	
+	# events
+	def key(self, char, event):
+		(what, message, when, where, modifiers) = event
+		if self._enabled and not modifiers & Events.cmdKey or char in arrowkeys:
+			self.ted.WEKey(ord(char), modifiers)
+			if char not in navigationkeys:
+				self.changed = 1
+			if char not in scrollkeys:
+				self.selchanged = 1
+			self.updatescrollbars()
+			if self._callback:
+				Wbase.CallbackCall(self._callback, 0, char, modifiers)
+	
+	def click(self, point, modifiers):
+		if not self._enabled:
+			return
+		self.ted.WEClick(point, modifiers, Evt.TickCount())
+		self.selchanged = 1
+		self.updatescrollbars()
+		return 1
+	
+	def idle(self):
+		self.SetPort()
+		self.ted.WEIdle()
+	
+	def rollover(self, point, onoff):
+		if onoff:
+			Wbase.SetCursor("iBeam")
+	
+	def activate(self, onoff):
+		self._activated = onoff
+		if self._selected and self._visible:
+			if onoff:
+				self.ted.WEActivate()
+			else:
+				self.ted.WEDeactivate()
+			if self._selected:
+				self.drawselframe(onoff)
+	
+	def select(self, onoff, isclick = 0):
+		if Wbase.SelectableWidget.select(self, onoff):
+			return
+		self.SetPort()
+		if onoff:
+			self.ted.WEActivate()
+			if self._parentwindow._tabbable and not isclick:
+				self.selectall()
+		else:
+			self.ted.WEDeactivate()
+		self.drawselframe(onoff)
+	
+	def draw(self, visRgn = None):
+		if self._visible:
+			if not visRgn:
+				visRgn = self._parentwindow.wid.GetWindowPort().visRgn
+			self.ted.WEUpdate(visRgn)
+			if self._selected and self._activated:
+				self.drawselframe(1)
+			Qd.FrameRect(self._bounds)
+	
+	# scrolling
+	def scrollpageup(self):
+		if self._parent._bary and self._parent._bary._enabled:
+			self.vscroll("++")
+	
+	def scrollpagedown(self):
+		if self._parent._bary and self._parent._bary._enabled:
+			self.vscroll("--")
+	
+	def scrolltop(self):
+		if self._parent._bary and self._parent._bary._enabled:
+			self.vscroll(0)
+		if self._parent._barx and self._parent._barx._enabled:
+			self.hscroll(0)
+	
+	def scrollbottom(self):
+		if self._parent._bary and self._parent._bary._enabled:
+			self.vscroll(32767)
+	
+	# menu handlers
+	def domenu_copy(self, *args):
+		selbegin, selend = self.ted.WEGetSelection()
+		if selbegin == selend:
+			return
+		Scrap.ZeroScrap()
+		self.ted.WECopy()
+		self.updatescrollbars()
+	
+	def domenu_cut(self, *args):
+		selbegin, selend = self.ted.WEGetSelection()
+		if selbegin == selend:
+			return
+		Scrap.ZeroScrap()
+		self.ted.WECut()
+		self.updatescrollbars()
+		self.selview()
+		self.changed = 1
+		self.selchanged = 1
+		if self._callback:
+			Wbase.CallbackCall(self._callback, 0, "", None)
+	
+	def domenu_paste(self, *args):
+		if not self.ted.WECanPaste():
+			return
+		self.selview()
+		self.ted.WEPaste()
+		self.updatescrollbars()
+		self.changed = 1
+		self.selchanged = 1
+		if self._callback:
+			Wbase.CallbackCall(self._callback, 0, "", None)
+	
+	def domenu_clear(self, *args):
+		self.ted.WEDelete()
+		self.selview()
+		self.updatescrollbars()
+		self.changed = 1
+		self.selchanged = 1
+		if self._callback:
+			Wbase.CallbackCall(self._callback, 0, "", None)
+	
+	def domenu_undo(self, *args):
+		which, redo = self.ted.WEGetUndoInfo()
+		if not which: 
+			return
+		self.ted.WEUndo()
+		self.updatescrollbars()
+		self.changed = 1
+		self.selchanged = 1
+		if self._callback:
+			Wbase.CallbackCall(self._callback, 0, "", None)
+	
+	def can_undo(self, menuitem):
+		which, redo = self.ted.WEGetUndoInfo()
+		which = UNDOLABELS[which]
+		if which == None: 
+			return None
+		if redo:
+			which = "Redo "+which
+		else:
+			which = "Undo "+which
+		menuitem.settext(which)
+		return 1
+	
+	def domenu_selectall(self, *args):
+		self.selectall()
+	
+	# private
+	def getscrollbarvalues(self):
+		dr = self.ted.WEGetDestRect()
+		vr = self.ted.WEGetViewRect()
+		vx = Wcontrols._scalebarvalue(dr[0], dr[2], vr[0], vr[2])
+		vy = Wcontrols._scalebarvalue(dr[1], dr[3], vr[1], vr[3])
+		return vx, vy
+	
+	def vscroll(self, value):
+		lineheight = self.ted.WEGetHeight(0, 1)
+		dr = self.ted.WEGetDestRect()
+		vr = self.ted.WEGetViewRect()
+		destheight = dr[3] - dr[1]
+		viewheight = vr[3] - vr[1]
+		viewoffset = maxdelta = vr[1] - dr[1]
+		mindelta = vr[3] - dr[3]
+		if value == "+":
+			delta = lineheight
+		elif value == "-":
+			delta = - lineheight
+		elif value == "++":
+			delta = viewheight - lineheight
+		elif value == "--":
+			delta = lineheight - viewheight
+		else:	# in thumb
+			cur = (32767 * viewoffset) / (destheight - viewheight)
+			delta = (cur-value)*(destheight - viewheight)/32767
+			if abs(delta - viewoffset) <=2:
+				# compensate for irritating rounding error
+				delta = viewoffset
+		delta = min(maxdelta, delta)
+		delta = max(mindelta, delta)
+		self.ted.WEScroll(0, delta)
+		self.updatescrollbars()
+	
+	def hscroll(self, value):
+		dr = self.ted.WEGetDestRect()
+		vr = self.ted.WEGetViewRect()
+		destwidth = dr[2] - dr[0]
+		viewwidth = vr[2] - vr[0]
+		viewoffset = maxdelta = vr[0] - dr[0]
+		mindelta = vr[2] - dr[2]
+		if value == "+":
+			delta = 32
+		elif value == "-":
+			delta = - 32
+		elif value == "++":
+			delta = 0.5 * (vr[2] - vr[0])
+		elif value == "--":
+			delta = 0.5 * (vr[0] - vr[2])
+		else:	# in thumb
+			cur = (32767 * viewoffset) / (destwidth - viewwidth)
+			delta = (cur-value)*(destwidth - viewwidth)/32767
+			if abs(delta - viewoffset) <=2:
+				# compensate for irritating rounding error
+				delta = viewoffset
+		delta = min(maxdelta, delta)
+		delta = max(mindelta, delta)
+		self.ted.WEScroll(delta, 0)
+		self.updatescrollbars()
+	
+	# some internals
+	def _getflags(self):
+		flags = WASTEconst.weDoAutoScroll | WASTEconst.weDoMonoStyled | \
+				WASTEconst.weDoUndo
+		if self.readonly:
+			flags = flags | WASTEconst.weDoReadOnly
+		return flags
+	
+	def _getviewrect(self):
+		return Qd.InsetRect(self._bounds, self.inset[0], self.inset[1])
+	
+	def _calctextbounds(self):
+		viewrect = l, t, r, b = self._getviewrect()
+		if self.ted:
+			dl, dt, dr, db = self.ted.WEGetDestRect()
+			vl, vt, vr, vb = self.ted.WEGetViewRect()
+			yshift = t - vt
+			if (db - dt) < (b - t):
+				destrect = viewrect
+			else:
+				destrect = l, dt + yshift, r, db + yshift
+		else:
+			destrect = viewrect
+		return viewrect, destrect
+		
+
+class TextEditor(EditText):
+	
+	def __init__(self, possize, text = "", callback = None, wrap = 1, inset = (4, 4),
+				fontsettings = ("Python-Sans", 0, 9, (0, 0, 0)),
+				readonly = 0):
+		EditText.__init__(self, possize, text, callback, inset, fontsettings, readonly)
+		self.wrap = wrap
+	
+	def _getflags(self):
+		flags = WASTEconst.weDoAutoScroll | WASTEconst.weDoMonoStyled | \
+				WASTEconst.weDoOutlineHilite
+		if self.readonly:
+			flags = flags | WASTEconst.weDoReadOnly
+		else:
+			flags = flags | WASTEconst.weDoUndo
+		return flags
+	
+	def _getviewrect(self):
+		l, t, r, b = self._bounds
+		return (l + 5, t + 2, r, b - 2)
+	
+	def _calctextbounds(self):
+		if self.wrap:
+			return EditText._calctextbounds(self)
+		else:
+			viewrect = l, t, r, b = self._getviewrect()
+			if self.ted:
+				dl, dt, dr, db = self.ted.WEGetDestRect()
+				vl, vt, vr, vb = self.ted.WEGetViewRect()
+				xshift = l - vl
+				yshift = t - vt
+				if (db - dt) < (b - t):
+					yshift = t - dt
+				destrect = (dl + xshift, dt + yshift, dr + xshift, db + yshift)
+			else:
+				destrect = (l, t, r + 5000, b)
+			return viewrect, destrect
+	
+	def draw(self, visRgn = None):
+		if self._visible:
+			if not visRgn:
+				visRgn = self._parentwindow.wid.GetWindowPort().visRgn
+			self.ted.WEUpdate(visRgn)
+			if self._selected and self._activated:
+				self.drawselframe(1)
+
+
+class PyEditor(TextEditor):
+	
+	def __init__(self, possize, text = "", callback = None, inset = (4, 4),
+				fontsettings = ("Python-Sans", 0, 9, (0, 0, 0)),
+				readonly = 0,
+				debugger = None,
+				file = ''):
+		TextEditor.__init__(self, possize, text, callback, 0, inset, fontsettings, readonly)
+		self.bind("cmd[", self.shiftleft)
+		self.bind("cmd]", self.shiftright)
+		self.file = file	# only for debugger reference
+		self._debugger = debugger
+		if debugger:
+			debugger.register_editor(self, self.file)
+	
+	def setfile(self, file):
+		self.file = file
+	
+	def set(self, text, file = ''):
+		oldfile = self.file
+		self.file = file
+		if self._debugger:
+			self._debugger.unregister_editor(self, oldfile)
+			self._debugger.register_editor(self, file)
+		TextEditor.set(self, text)
+	
+	def close(self):
+		if self._debugger:
+			self._debugger.unregister_editor(self, self.file)
+			self._debugger = None
+		TextEditor.close(self)		
+	
+	def click(self, point, modifiers):
+		if not self._enabled:
+			return
+		if self._debugger and self.pt_in_breaks(point):
+			self.breakhit(point, modifiers)
+		elif self._debugger:
+			bl, bt, br, bb = self._getbreakrect()
+			Qd.EraseRect((bl, bt, br-1, bb))
+			TextEditor.click(self, point, modifiers)
+			self.drawbreakpoints()
+		else:
+			TextEditor.click(self, point, modifiers)
+			if self.ted.WEGetClickCount() >= 3:
+				# select block with our indent
+				lines = string.split(self.get(), '\r')
+				selstart, selend = self.ted.WEGetSelection()
+				lineno = self.ted.WEOffsetToLine(selstart)
+				tabs = 0
+				line = lines[lineno]
+				while line[tabs:] and line[tabs] == '\t':
+					tabs = tabs + 1
+				tabstag = '\t' * tabs
+				fromline = 0
+				toline = len(lines)
+				if tabs:
+					for i in range(lineno - 1, -1, -1):
+						line = lines[i]
+						if line[:tabs] <> tabstag:
+							fromline = i + 1
+							break
+					for i in range(lineno + 1, toline):
+						line = lines[i]
+						if line[:tabs] <> tabstag:
+							toline = i - 1
+							break
+				selstart, dummy = self.ted.WEGetLineRange(fromline)
+				dummy, selend = self.ted.WEGetLineRange(toline)
+				self.ted.WESetSelection(selstart, selend)
+	
+	def breakhit(self, point, modifiers):
+		if not self.file:
+			return
+		offset, edge = self.ted.WEGetOffset(point)
+		lineno = self.ted.WEOffsetToLine(offset) + 1
+		if edge < 0:
+			self._debugger.clear_breaks_above(self.file, lineno)
+		else:
+			self._debugger.clear_breaks_above(self.file, self.countlines())
+			self._debugger.toggle_break(self.file, lineno)
+	
+	def key(self, char, event):
+		(what, message, when, where, modifiers) = event
+		if modifiers & Events.cmdKey and not char in arrowkeys:
+			return
+		if char == '\r':
+			selstart, selend = self.ted.WEGetSelection()
+			selstart, selend = min(selstart, selend), max(selstart, selend)
+			lastchar = chr(self.ted.WEGetChar(selstart-1))
+			if lastchar <> '\r' and selstart:
+				pos, dummy = self.ted.WEFindLine(selstart, 0)
+				lineres = Res.Resource('')
+				self.ted.WECopyRange(pos, selstart, lineres, None, None)
+				line = lineres.data + '\n'
+				tabcount = self.extratabs(line)
+				self.ted.WEKey(ord('\r'), 0)
+				for i in range(tabcount):
+					self.ted.WEKey(ord('\t'), 0)
+			else:
+				self.ted.WEKey(ord('\r'), 0)
+		elif char in ')]}':
+			self.ted.WEKey(ord(char), modifiers)
+			self.balanceparens(char)
+		else:
+			self.ted.WEKey(ord(char), modifiers)
+		if char not in navigationkeys:
+			self.changed = 1
+		self.selchanged = 1
+		self.updatescrollbars()
+	
+	def balanceparens(self, char):
+		if char == ')':
+			target = '('
+		elif char == ']':
+			target = '['
+		elif char == '}':
+			target = '{'
+		recursionlevel = 1
+		selstart, selend = self.ted.WEGetSelection()
+		count = min(selstart, selend) - 2
+		mincount = max(0, count - 2048)
+		lastquote = None
+		while count > mincount:
+			testchar = chr(self.ted.WEGetChar(count))
+			if testchar in "\"'" and chr(self.ted.WEGetChar(count - 1)) <> '\\':
+				if lastquote == testchar:
+					recursionlevel = recursionlevel - 1
+					lastquote = None
+				elif not lastquote:
+					recursionlevel = recursionlevel + 1
+					lastquote = testchar
+			elif not lastquote and testchar == char:
+				recursionlevel = recursionlevel + 1
+			elif not lastquote and testchar == target:
+				recursionlevel = recursionlevel - 1
+				if recursionlevel == 0:
+					import time
+					autoscroll = self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, -1)
+					if autoscroll:
+						self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 0)
+					self.ted.WESetSelection(count, count + 1)
+					time.sleep(0.2)
+					self.ted.WESetSelection(selstart, selend)
+					if autoscroll:
+						self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 1)
+					break
+			count = count - 1
+	
+	def extratabs(self, line):
+		tabcount = 0
+		for c in line:
+			if c <> '\t':
+				break
+			tabcount = tabcount + 1
+		last = 0
+		cleanline = ''
+		tags = PyFontify.fontify(line)
+		# strip comments and strings
+		for tag, start, end, sublist in tags:
+			if tag in ('string', 'comment'):
+				cleanline = cleanline + line[last:start]
+				last = end
+		cleanline = cleanline + line[last:]
+		cleanline = string.strip(cleanline)
+		if cleanline and cleanline[-1] == ':':
+			tabcount = tabcount + 1
+		else:
+			for open, close in (('(', ')'), ('[', ']'), ('{', '}')):
+				count = string.count(cleanline, open)
+				if count and count <> string.count(cleanline, close):
+					tabcount = tabcount + 2
+					break
+		return tabcount
+	
+	def rollover(self, point, onoff):
+		if onoff:
+			if self._debugger and self.pt_in_breaks(point):
+				Wbase.SetCursor("arrow")
+			else:
+				Wbase.SetCursor("iBeam")
+	
+	def draw(self, visRgn = None):
+		TextEditor.draw(self, visRgn)
+		if self._debugger:
+			self.drawbreakpoints()
+	
+	def showbreakpoints(self, onoff):
+		if (not not self._debugger) <> onoff:
+			if onoff:
+				import PyDebugger
+				self._debugger = PyDebugger.getdebugger()
+				self._debugger.register_editor(self, self.file)
+			elif self._debugger:
+				self._debugger.unregister_editor(self, self.file)
+				self._debugger = None
+			self.adjust(self._bounds)
+	
+	def togglebreakpoints(self):
+		self.showbreakpoints(not self._debugger)
+	
+	def clearbreakpoints(self):
+		if self.file:
+			self._debugger.clear_all_file_breaks(self.file)
+	
+	def editbreakpoints(self):
+		if self._debugger:
+			self._debugger.edit_breaks()
+			self._debugger.breaksviewer.selectfile(self.file)
+	
+	def drawbreakpoints(self, eraseall = 0):
+		breakrect = bl, bt, br, bb = self._getbreakrect()
+		br = br - 1
+		self.SetPort()
+		Qd.PenPat(Qd.qd.gray)
+		Qd.PaintRect((br, bt, br + 1, bb))
+		Qd.PenNormal()
+		self._parentwindow.tempcliprect(breakrect)
+		Qd.RGBForeColor((0xffff, 0, 0))
+		try:
+			lasttop = bt
+			self_ted = self.ted
+			Qd_PaintOval = Qd.PaintOval
+			Qd_EraseRect = Qd.EraseRect
+			for lineno in self._debugger.get_file_breaks(self.file):
+				start, end = self_ted.WEGetLineRange(lineno - 1)
+				if lineno <> self_ted.WEOffsetToLine(start) + 1:
+					# breakpoints beyond our text: erase rest, and back out
+					Qd_EraseRect((bl, lasttop, br, bb))
+					break
+				(x, y), h = self_ted.WEGetPoint(start, 0)
+				bottom = y + h
+				#print y, (lasttop, bottom)
+				if bottom > lasttop:
+					Qd_EraseRect((bl, lasttop, br, y + h * eraseall))
+					lasttop = bottom
+				redbullet = bl + 2, y + 3, bl + 8, y + 9
+				Qd_PaintOval(redbullet)
+			else:
+				Qd_EraseRect((bl, lasttop, br, bb))
+			Qd.RGBForeColor((0, 0, 0))
+		finally:
+			self._parentwindow.restoreclip()
+	
+	def updatescrollbars(self):
+		if self._debugger:
+			self.drawbreakpoints(1)
+		TextEditor.updatescrollbars(self)
+	
+	def pt_in_breaks(self, point):
+		return Qd.PtInRect(point, self._getbreakrect())
+	
+	def _getbreakrect(self):
+		if self._debugger:
+			l, t, r, b = self._bounds
+			return (l+1, t+1, l + 12, b-1)
+		else:
+			return (0, 0, 0, 0)
+	
+	def _getviewrect(self):
+		l, t, r, b = self._bounds
+		if self._debugger:
+			return (l + 17, t + 2, r, b - 2)
+		else:
+			return (l + 5, t + 2, r, b - 2)
+	
+	def _calctextbounds(self):
+		viewrect = l, t, r, b = self._getviewrect()
+		if self.ted:
+			dl, dt, dr, db = self.ted.WEGetDestRect()
+			vl, vt, vr, vb = self.ted.WEGetViewRect()
+			xshift = l - vl
+			yshift = t - vt
+			if (db - dt) < (b - t):
+				yshift = t - dt
+			destrect = (dl + xshift, dt + yshift, dr + xshift, db + yshift)
+		else:
+			destrect = (l, t, r + 5000, b)
+		return viewrect, destrect
+
+
+def GetFNum(font):
+	if font <> 'Chicago':
+		fontid = Fm.GetFNum(font)
+		if fontid == 0:
+			fontid = Fonts.monaco
+	else:
+		fontid = 0
+	return fontid
+
+def GetFName(fontid):
+	try:
+		res = Res.GetResource('FOND', fontid)
+	except Res.Error:
+		fontname = 'Monaco'
+	else:
+		fontname = res.GetResInfo()[2]
+	return fontname
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wtraceback.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wtraceback.py
new file mode 100644
index 0000000..5804cd7
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wtraceback.py
@@ -0,0 +1,196 @@
+import traceback
+import sys
+import W
+import os
+import types
+
+class TraceList(W.List):
+	
+	LDEF_ID = 468
+	
+	def createlist(self):
+		import List
+		self._calcbounds()
+		self.SetPort()
+		rect = self._bounds
+		rect = rect[0]+1, rect[1]+1, rect[2]-16, rect[3]-1
+		self._list = List.LNew(rect, (0, 0, 1, 0), (0, 28), self.LDEF_ID, self._parentwindow.wid,
+					0, 1, 0, 1)
+		self.set(self.items)
+	
+
+class TraceBack:
+	
+	def __init__(self, title = "Traceback"):
+		app = W.getapplication()  # checks if W is properly initialized
+		self.title = title
+		self.w = None
+		self.closed = 1
+		self.start = 0
+		self.lastwindowtitle = ""
+		self.bounds = (360, 298)
+	
+	def traceback(self, start = 0, lastwindowtitle = ""):
+		try:
+			self.lastwindowtitle = lastwindowtitle
+			self.start = start
+			self.type, self.value, self.tb = sys.exc_type, sys.exc_value, sys.exc_traceback
+			if self.type is not SyntaxError:
+				self.show()
+				if type(self.type) == types.ClassType:
+					errortext = self.type.__name__
+				else:
+					errortext = str(self.type)
+				value = str(self.value)
+				if self.value and value:
+					errortext = errortext + ": " + value
+				self.w.text.set(errortext)
+				self.buildtblist()
+				self.w.list.set(self.textlist)
+				self.w.list.setselection([len(self.textlist) - 1])
+				self.w.wid.SelectWindow()
+				self.closed = 0
+			else:
+				self.syntaxerror()
+		except:
+			traceback.print_exc()
+	
+	def syntaxerror(self):
+		try:
+			value, (filename, lineno, charno, line) = self.value
+		except:
+			filename = ""
+			lineno = None
+			value = self.value
+		if not filename and self.lastwindowtitle:
+			filename = self.lastwindowtitle
+		elif not filename:
+			filename = "<unknown>"
+		if filename and os.path.exists(filename):
+			filename = os.path.split(filename)[1]
+		if lineno:
+			charno = charno - 1
+			text = value + '\rFile: "' + str(filename) + '", line ' + str(lineno) + '\r\r' + line[:charno] + "€" + line[charno:-1]
+		else:
+			text = value + '\rFile: "' + str(filename) + '"'
+		self.syntaxdialog = W.ModalDialog((360, 120), "Syntax Error")
+		self.syntaxdialog.text = W.TextBox((10, 10, -10, -40), text)
+		self.syntaxdialog.cancel = W.Button((-190, -32, 80, 16), "Cancel", self.syntaxclose)
+		self.syntaxdialog.edit = W.Button((-100, -32, 80, 16), "Edit", self.syntaxedit)
+		self.syntaxdialog.setdefaultbutton(self.syntaxdialog.edit)
+		self.syntaxdialog.bind("cmd.", self.syntaxdialog.cancel.push)
+		self.syntaxdialog.open()
+	
+	def syntaxclose(self):
+		self.syntaxdialog.close()
+		del self.syntaxdialog
+	
+	def syntaxedit(self):
+		try:
+			value, (filename, lineno, charno, line) = self.value
+		except:
+			filename = ""
+			lineno = None
+		if not filename and self.lastwindowtitle:
+			filename = self.lastwindowtitle
+		elif not filename:
+			filename = "<unknown>"
+		self.syntaxclose()
+		if lineno:
+			W.getapplication().openscript(filename, lineno, charno - 1)
+		else:
+			W.getapplication().openscript(filename)
+	
+	def show(self):
+		if self.closed:
+			self.setupwidgets()
+			self.w.open()
+		else:
+			self.w.wid.ShowWindow()
+			self.w.wid.SelectWindow()
+	
+	def close(self):
+		self.bounds = self.w.getbounds()
+		self.closed = 1
+		self.type, self.value, self.tb = None, None, None
+		self.tblist = None
+	
+	def activate(self, onoff):
+		if onoff:
+			if self.closed:
+				self.traceback()
+			self.closed = 0
+			self.checkbuttons()
+	
+	def setupwidgets(self):
+		self.w = W.Window(self.bounds, self.title, minsize = (316, 168))
+		self.w.text = W.TextBox((10, 10, -10, 30))
+		self.w.tbtitle = W.TextBox((10, 40, -10, 10), "Traceback (innermost last):")
+		self.w.list = TraceList((10, 60, -10, -40), callback = self.listhit)
+		
+		self.w.editbutton = W.Button((10, -30, 60, 16), "Edit", self.edit)
+		self.w.editbutton.enable(0)
+		
+		self.w.browselocalsbutton = W.Button((80, -30, 100, 16), "Browse localsŠ", self.browselocals)
+		self.w.browselocalsbutton.enable(0)
+		
+		self.w.postmortembutton = W.Button((190, -30, 100, 16), "Post mortemŠ", self.postmortem)
+		
+		self.w.setdefaultbutton(self.w.editbutton)
+		self.w.bind("cmdb", self.w.browselocalsbutton.push)
+		self.w.bind("<close>", self.close)
+		self.w.bind("<activate>", self.activate)
+	
+	def buildtblist(self):
+		tb = self.tb
+		for i in range(self.start):
+			if tb.tb_next is None:
+				break
+			tb = tb.tb_next
+		self.tblist = traceback.extract_tb(tb)
+		self.textlist = []
+		for filename, lineno, func, line in self.tblist:
+			tbline = ""
+			if os.path.exists(filename):
+				filename = os.path.split(filename)[1]
+				tbline = 'File "' + filename + '", line ' + `lineno` + ', in ' + func
+			else:
+				tbline = 'File "' + filename + '", line ' + `lineno` + ', in ' + func
+			if line:
+				tbline = tbline + '\r      ' + line
+			self.textlist.append(tbline[:255])
+	
+	def edit(self):
+		sel = self.w.list.getselection()
+		for i in sel:
+			filename, lineno, func, line = self.tblist[i]
+			W.getapplication().openscript(filename, lineno)
+	
+	def browselocals(self):
+		sel = self.w.list.getselection()
+		for i in sel:
+			tb = self.tb
+			for j in range(i + self.start):
+				tb = tb.tb_next
+			self.browse(tb.tb_frame.f_locals)
+	
+	def browse(self, object):
+		import PyBrowser
+		PyBrowser.Browser(object)
+	
+	def postmortem(self):
+		import PyDebugger
+		PyDebugger.postmortem(self.type, self.value, self.tb)
+	
+	def listhit(self, isdbl):
+		if isdbl:
+			self.w.editbutton.push()
+		else:
+			self.checkbuttons()
+	
+	def checkbuttons(self):
+		havefile = len(self.w.list.getselection()) > 0
+		self.w.editbutton.enable(havefile)
+		self.w.browselocalsbutton.enable(havefile)
+		self.w.setdefaultbutton(havefile and self.w.editbutton or self.w.postmortembutton)
+
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wwindows.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wwindows.py
new file mode 100644
index 0000000..c488810
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/Wwindows.py
@@ -0,0 +1,578 @@
+import Qd
+import Win
+import Evt
+import Fm
+import FrameWork
+import Windows
+import Events
+import Wbase
+import Wlist
+import Dlg
+import MacOS
+import Menu
+import struct
+
+from types import *
+from SpecialKeys import *
+
+
+class Window(FrameWork.Window, Wbase.SelectableWidget):
+	
+	windowkind = Windows.documentProc
+	
+	def __init__(self, possize, title = "", minsize = None, maxsize = None, tabbable = 1, show = 1):
+		Wbase.SelectableWidget.__init__(self, possize)
+		self._globalbounds = l, t, r, b = self.getwindowbounds(possize, minsize)
+		self._bounds = (0, 0, r - l, b - t)
+		self._tabchain = []
+		self._currentwidget = None
+		self.title = title
+		self._parentwindow = self
+		self._tabbable = tabbable
+		self._defaultbutton = None
+		self._drawwidgetbounds = 0
+		self._show = show
+		self._lastrollover = None
+		if minsize:
+			self._hasgrowbox = 1
+			self.windowkind = self.windowkind | 8
+			l, t = minsize
+			if maxsize:
+				r, b = maxsize[0] + 1, maxsize[1] + 1
+			else:
+				r, b = 32000, 32000
+			self.growlimit = (l, t, r, b)
+		else:
+			self._hasgrowbox = 0
+			if self.windowkind == 0 or self.windowkind >=8:
+				self.windowkind = self.windowkind | 4
+		import W
+		if not W._application:
+			raise W.WidgetsError, 'W not properly initialized: unknown Application'
+		FrameWork.Window.__init__(self, W._application)
+	
+	def gettitle(self):
+		return self.title
+	
+	def settitle(self, title):
+		self.title = title
+		if self.wid:
+			self.wid.SetWTitle(title)
+	
+	def getwindowbounds(self, size, minsize = None):
+		return windowbounds(size, minsize)	
+	
+	def getcurrentwidget(self):
+		return self._currentwidget
+	
+	def show(self, onoff):
+		if onoff:
+			self.wid.ShowWindow()
+		else:
+			self.wid.HideWindow()
+	
+	def isvisible(self):
+		return self.wid.IsWindowVisible()
+	
+	def getbounds(self):
+		if 0:	#self.isvisible():
+			self.wid.GetWindowContentRgn(scratchRegion)
+			self._globalbounds = GetRgnBounds(scratchRegion)
+		return self._globalbounds
+		
+	def select(self):
+		self.wid.SelectWindow()
+	
+	def open(self):
+		self.wid = Win.NewCWindow(self._globalbounds, self.title, self._show,
+			self.windowkind, -1, 1, 0)
+		self.SetPort()
+		fnum = Fm.GetFNum("Python-Sans")
+		if fnum == 0:
+			fnum = Fm.GetFNum("Geneva")
+		Qd.TextFont(fnum)	# XXX font&size from a function?
+		Qd.TextSize(9)	# XXX font&size from a function?
+		if self._bindings.has_key("<open>"):
+			callback = self._bindings["<open>"]
+			callback()
+		for w in self._widgets:
+			w.forall_frombottom("open")
+		self._maketabchain()
+		if self._tabchain:
+			self._tabchain[0].select(1)
+		if self._tabbable:
+			self.bind('tab', self.nextwidget)
+			self.bind('shifttab', self.previouswidget)
+		self.do_postopen()
+	
+	def close(self):
+		if not self.wid:
+			return	# we are already closed
+		if self._bindings.has_key("<close>"):
+			callback = self._bindings["<close>"]
+			rv = callback()
+			if rv:
+				return rv
+		#for key in self._widgetsdict.keys():
+		#	self._removewidget(key)
+		self.forall_butself("close")
+		Wbase.SelectableWidget.close(self)
+		self._tabchain = []
+		self._currentwidget = None
+		self.wid.HideWindow()
+		self.do_postclose()
+	
+	def domenu_close(self, *args):
+		self.close()
+	
+	def move(self, x, y = None):
+		"""absolute move"""
+		if y == None:
+			x, y = x
+		self.wid.MoveWindow(x, y, 0)
+	
+	def resize(self, x, y = None):
+		if y == None:
+			x, y = x
+		if self._hasgrowbox:
+			self.SetPort()
+			Win.InvalRect(self.getgrowrect())
+		self.wid.SizeWindow(x, y, 1)
+		self._calcbounds()
+	
+	def test(self, point):
+		return 1
+	
+	def draw(self, visRgn = None):
+		if self._hasgrowbox:
+			self.tempcliprect(self.getgrowrect())
+			self.wid.DrawGrowIcon()
+			self.restoreclip()
+	
+	def idle(self, *args):
+		self.SetPort()
+		point = Evt.GetMouse()
+		widget = self.findwidget(point, 0)
+		if widget is not None and hasattr(widget, "rollover"):
+			if 1:	#self._lastrollover <> widget:
+				if self._lastrollover:
+					self._lastrollover.rollover(point, 0)
+				self._lastrollover = widget
+				self._lastrollover.rollover(point, 1)
+		else:
+			if self._lastrollover:
+				self._lastrollover.rollover(point, 0)
+			self._lastrollover = None
+			Wbase.SetCursor("arrow")
+		if self._bindings.has_key("<idle>"):
+			callback = self._bindings["<idle>"]
+			if callback():
+				return
+		if self._currentwidget is not None and hasattr(self._currentwidget, "idle"):
+			if self._currentwidget._bindings.has_key("<idle>"):
+				callback = self._currentwidget._bindings["<idle>"]
+				if callback():
+					return
+			self._currentwidget.idle()
+
+	def xxx___select(self, widget):
+		if self._currentwidget == widget:
+			return
+		if self._bindings.has_key("<select>"):
+			callback = self._bindings["<select>"]
+			if callback(widget):
+				return
+		if widget is None:
+			if self._currentwidget is not None:
+				self._currentwidget.select(0)
+		elif type(widget) == InstanceType and widget._selectable:
+			widget.select(1)
+		elif widget == -1 or widget == 1:
+			if len(self._tabchain) <= 1:
+				return
+			temp = self._tabchain[(self._tabchain.index(self._currentwidget) + widget) % len(self._tabchain)]
+			temp.select(1)
+		else:
+			raise TypeError, "Widget is not selectable"
+	
+	def setdefaultbutton(self, newdefaultbutton = None, *keys):
+		if newdefaultbutton == self._defaultbutton:
+			return
+		if self._defaultbutton:
+			self._defaultbutton._setdefault(0)
+		if not newdefaultbutton:
+			self.bind("return", None)
+			self.bind("enter", None)
+			return
+		import Wcontrols
+		if not Wbase.HasBaseClass(newdefaultbutton, Wcontrols.Button):
+			raise TypeError, "widget is not a button"
+		self._defaultbutton = newdefaultbutton
+		self._defaultbutton._setdefault(1)
+		if not keys:
+			self.bind("return", self._defaultbutton.push)
+			self.bind("enter", self._defaultbutton.push)
+		else:
+			for key in keys:
+				self.bind(key, self._defaultbutton.push)
+	
+	def nextwidget(self):
+		self.xxx___select(1)
+	
+	def previouswidget(self):
+		self.xxx___select(-1)
+	
+	def drawwidgetbounds(self, onoff):
+		self._drawwidgetbounds = onoff
+		self.SetPort()
+		Win.InvalRect(self._bounds)
+	
+	def _drawbounds(self):
+		pass
+
+	def _maketabchain(self):
+		# XXX This has to change, it's no good when we are adding or deleting widgets.
+		# XXX Perhaps we shouldn't keep a "tabchain" at all.
+		self._hasselframes = 0
+		self._collectselectablewidgets(self._widgets)
+		if self._hasselframes and len(self._tabchain) > 1:
+			self._hasselframes = 1
+		else:
+			self._hasselframes = 0
+	
+	def _collectselectablewidgets(self, widgets):
+		for w in widgets:
+			if w._selectable:
+				self._tabchain.append(w)
+				if Wbase.HasBaseClass(w, Wlist.List):
+					self._hasselframes = 1
+			self._collectselectablewidgets(w._widgets)
+	
+	def _calcbounds(self):
+		self._possize = self.wid.GetWindowPort().portRect[2:]
+		w, h = self._possize
+		self._bounds = (0, 0, w, h)
+		self.wid.GetWindowContentRgn(scratchRegion)
+		l, t, r, b = GetRgnBounds(scratchRegion)
+		self._globalbounds = l, t, l + w, t + h
+		for w in self._widgets:
+			w._calcbounds()
+	
+	# FrameWork override methods
+	def do_inDrag(self, partcode, window, event):
+		where = event[3]
+		self.wid.GetWindowContentRgn(scratchRegion)
+		was_l, was_t, r, b = GetRgnBounds(scratchRegion)
+		window.DragWindow(where, self.draglimit)
+		self.wid.GetWindowContentRgn(scratchRegion)
+		is_l, is_t, r, b = GetRgnBounds(scratchRegion)
+		self._globalbounds = Qd.OffsetRect(self._globalbounds, 
+					is_l - was_l, is_t - was_t)
+	
+	def do_char(self, char, event):
+		(what, message, when, where, modifiers) = event
+		key = char
+		if keynames.has_key(key):
+			key = keynames[char]
+		if modifiers & Events.shiftKey:
+			key = 'shift' + key
+		if modifiers & Events.cmdKey:
+			key = 'cmd' + key
+		if modifiers & Events.controlKey:
+			key = 'control' + key
+		if self._bindings.has_key("<key>"):
+			callback = self._bindings["<key>"]
+			if Wbase.CallbackCall(callback, 0, char, event):
+				return
+		if self._bindings.has_key(key):
+			callback = self._bindings[key]
+			Wbase.CallbackCall(callback, 0, char, event)
+		elif self._currentwidget is not None:
+			if self._currentwidget._bindings.has_key(key):
+				callback = self._currentwidget._bindings[key]
+				Wbase.CallbackCall(callback, 0, char, event)
+			else:
+				if self._currentwidget._bindings.has_key("<key>"):
+					callback = self._currentwidget._bindings["<key>"]
+					if Wbase.CallbackCall(callback, 0, char, event):
+						return
+				self._currentwidget.key(char, event)
+	
+	def do_contentclick(self, point, modifiers, event):
+		widget = self.findwidget(point)
+		if widget is not None:
+			if self._bindings.has_key("<click>"):
+				callback = self._bindings["<click>"]
+				if Wbase.CallbackCall(callback, 0, point, modifiers):
+					return
+			if widget._bindings.has_key("<click>"):
+				callback = widget._bindings["<click>"]
+				if Wbase.CallbackCall(callback, 0, point, modifiers):
+					return
+			if widget._selectable:
+				widget.select(1, 1)
+			widget.click(point, modifiers)
+	
+	def do_update(self, window, event):
+		Qd.EraseRgn(window.GetWindowPort().visRgn)
+		self.forall("draw", window.GetWindowPort().visRgn)
+		if self._drawwidgetbounds:
+			self.forall("_drawbounds")
+	
+	def do_activate(self, onoff, event):
+		if not onoff:
+			if self._lastrollover:
+				self._lastrollover.rollover((0, 0), 0)
+				self._lastrollover = None
+		self.SetPort()
+		self.forall("activate", onoff)
+		self.draw()
+	
+	def do_postresize(self, width, height, window):
+		Win.InvalRect(self.getgrowrect())
+		self._calcbounds()
+	
+	# utilities
+	def tempcliprect(self, tempcliprect):
+		tempclip = Qd.NewRgn()
+		Qd.RectRgn(tempclip, tempcliprect)
+		self.tempclip(tempclip)
+	
+	def tempclip(self, tempclip):
+		if hasattr(self, "saveclip"):
+			raise Wbase.WidgetsError, "already have saveclip"
+		self.saveclip = Qd.NewRgn()
+		Qd.GetClip(self.saveclip)
+		Qd.SectRgn(self.wid.GetWindowPort().visRgn, tempclip, tempclip)
+		Qd.SetClip(tempclip)
+	
+	def restoreclip(self):
+		Qd.SetClip(self.saveclip)
+		del self.saveclip
+	
+	def getgrowrect(self):
+		l, t, r, b = self.wid.GetWindowPort().portRect
+		return (r - 15, b - 15, r, b)
+	
+	def has_key(self, key):
+		return self._widgetsdict.has_key(key)
+	
+	def __getattr__(self, attr):
+		global _successcount, _failcount, _magiccount
+		if self._widgetsdict.has_key(attr):
+			_successcount = _successcount + 1
+			return self._widgetsdict[attr]
+		if self._currentwidget is None or (attr[:7] <> 'domenu_' and 
+				attr[:4] <> 'can_' and attr <> 'insert'):
+			_failcount = _failcount + 1
+			raise AttributeError, attr
+		# special case: if a domenu_xxx, can_xxx or insert method is asked for, 
+		# see if the active widget supports it
+		_magiccount = _magiccount + 1
+		return getattr(self._currentwidget, attr)
+
+_successcount = 0
+_failcount = 0
+_magiccount = 0
+
+class Dialog(Window):
+	
+	windowkind = Windows.movableDBoxProc
+	
+	# this __init__ seems redundant, but it's not: it has less args
+	def __init__(self, possize, title = ""):
+		Window.__init__(self, possize, title)
+	
+	def can_close(self, *args):
+		return 0
+	
+	def getwindowbounds(self, size, minsize = None):
+		screenbounds = sl, st, sr, sb = Qd.qd.screenBits.bounds
+		w, h = size
+		l = sl + (sr - sl - w) / 2
+		t = st + (sb - st - h) / 3
+		return l, t, l + w, t + h
+	
+class ModalDialog(Dialog):
+	
+	def __init__(self, possize, title = ""):
+		Dialog.__init__(self, possize, title)
+		if title:
+			self.windowkind = Windows.movableDBoxProc
+		else:
+			self.windowkind = Windows.dBoxProc
+	
+	def open(self):
+		import W
+		if not W._application:
+			raise W.WidgetsError, 'W not properly initialized: unknown Application'
+		Dialog.open(self)
+		self.app = W._application
+		self.done = 0
+		Menu.HiliteMenu(0)
+		self.mainloop()
+	
+	def close(self):
+		if not self.wid:
+			return	# we are already closed
+		self.done = 1
+		del self.app
+		Dialog.close(self)
+	
+	def mainloop(self):
+		saveyield = MacOS.EnableAppswitch(-1)
+		while not self.done:
+			#self.do1event()
+			self.do1event(	Events.keyDownMask + 
+						Events.autoKeyMask + 
+						Events.activMask + 
+						Events.updateMask + 
+						Events.mDownMask +
+						Events.mUpMask, 
+						10)
+		MacOS.EnableAppswitch(saveyield)
+	
+	def do1event(self, mask = Events.everyEvent, wait = 0):
+		ok, event = self.app.getevent(mask, wait)
+		if Dlg.IsDialogEvent(event):
+			if self.app.do_dialogevent(event):
+				return
+		if ok:
+			self.dispatch(event)
+		else:
+			self.app.idle(event)
+	
+	def do_keyDown(self, event):
+		self.do_key(event)
+	
+	def do_autoKey(self, event):
+		if not event[-1] & Events.cmdKey:
+			self.do_key(event)
+	
+	def do_key(self, event):
+		(what, message, when, where, modifiers) = event
+		w = Win.FrontWindow()
+		if w <> self.wid:
+			return
+		c = chr(message & Events.charCodeMask)
+		if modifiers & Events.cmdKey:
+			self.app.checkmenus(self)
+			result = Menu.MenuKey(ord(c))
+			id = (result>>16) & 0xffff	# Hi word
+			item = result & 0xffff		# Lo word
+			if id:
+				self.app.do_rawmenu(id, item, None, event)
+				return
+		self.do_char(c, event)
+	
+	def do_mouseDown(self, event):
+		(what, message, when, where, modifiers) = event
+		partcode, wid = Win.FindWindow(where)
+		#
+		# Find the correct name.
+		#
+		if FrameWork.partname.has_key(partcode):
+			name = "do_" + FrameWork.partname[partcode]
+		else:
+			name = "do_%d" % partcode
+		
+		if name == "do_inDesk":
+			MacOS.HandleEvent(event)
+			return
+		if wid == self.wid:
+			try:
+				handler = getattr(self, name)
+			except AttributeError:
+				handler = self.app.do_unknownpartcode
+		else:
+			#MacOS.HandleEvent(event)
+			return		
+		handler(partcode, wid, event)
+	
+	def dispatch(self, event):
+		(what, message, when, where, modifiers) = event
+		if FrameWork.eventname.has_key(what):
+			name = "do_" + FrameWork.eventname[what]
+		else:
+			name = "do_%d" % what
+		try:
+			handler = getattr(self, name)
+		except AttributeError:
+			try:
+				handler = getattr(self.app, name)
+			except AttributeError:
+				handler = self.app.do_unknownevent
+		handler(event)
+	
+
+def FrontWindowInsert(stuff):
+	if not stuff:
+		return
+	if type(stuff) <> StringType:
+		raise TypeError, 'string expected'
+	import W
+	app = W.getapplication()
+	wid = Win.FrontWindow()
+	if wid and app._windows.has_key(wid):
+		window = app._windows[wid]
+		if hasattr(window, "insert"):
+			try:
+				window.insert(stuff)
+				return
+			except:
+				pass
+	import EasyDialogs
+	if EasyDialogs.AskYesNoCancel(
+			"Can¹t find window or widget to insert text into; copy to clipboard instead?", 
+			1) == 1:
+		import Scrap
+		Scrap.ZeroScrap()
+		Scrap.PutScrap('TEXT', stuff)
+
+
+# not quite based on the same function in FrameWork	
+_windowcounter = 0
+
+def getnextwindowpos():
+	global _windowcounter
+	rows = 8
+	l = 4 * (rows + 1 - (_windowcounter % rows) + _windowcounter / rows)
+	t = 44 + 20 * (_windowcounter % rows)
+	_windowcounter = _windowcounter + 1
+	return l, t
+
+def windowbounds(preferredsize, minsize = None):
+	"Return sensible window bounds"
+	
+	global _windowcounter
+	if len(preferredsize) == 4:
+		bounds = l, t, r, b = preferredsize
+		union = Qd.UnionRect(bounds, Qd.qd.screenBits.bounds)
+		if union == Qd.qd.screenBits.bounds:
+			return bounds
+		else:
+			preferredsize = r - l, b - t
+	if not minsize:
+		minsize = preferredsize
+	minwidth, minheight = minsize
+	width, height = preferredsize
+	
+	sl, st, sr, sb = screenbounds = Qd.InsetRect(Qd.qd.screenBits.bounds, 4, 4)
+	l, t = getnextwindowpos()
+	if (l + width) > sr:
+		_windowcounter = 0
+		l, t = getnextwindowpos()
+	r = l + width
+	b = t + height
+	if (t + height) > sb:
+		b = sb
+		if (b - t) < minheight:
+			b = t + minheight
+	return l, t, r, b
+
+scratchRegion = Qd.NewRgn()
+
+# util -- move somewhere convenient???
+def GetRgnBounds(the_Rgn):
+	(t, l, b, r) = struct.unpack("hhhh", the_Rgn.data[2:10])
+	return (l, t, r, b)
diff --git a/Mac/Contrib/PyIDE-src/PythonIDE.py b/Mac/Contrib/PyIDE-src/PythonIDE.py
new file mode 100644
index 0000000..e1a94a6
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/PythonIDE.py
@@ -0,0 +1,57 @@
+# copyright 1997 Just van Rossum, Letterror. just@knoware.nl
+
+# keep this (__main__) as clean as possible, since we are using 
+# it like the "normal" interpreter.
+
+__version__ = '0.9b1'
+
+def init():
+	import sys
+	import MacOS
+	
+	if sys.version[:5] == '1.5a3':
+		def MyEnableAppswitch(yield, 
+				table = {-1:0, 0:-1, 1:1}, 
+				EnableAppswitch = MacOS.EnableAppswitch):
+			return table[EnableAppswitch(table[yield])]
+		MacOS.EnableAppswitch = MyEnableAppswitch
+	
+	MacOS.EnableAppswitch(-1)
+	
+	import Qd, QuickDraw
+	Qd.SetCursor(Qd.GetCursor(QuickDraw.watchCursor).data)
+	
+	import os
+	
+	# kludge to keep stdwin's TextEdit.py out the door...
+	import string
+	for i in range(len(sys.path)):
+		path = sys.path[i]
+		if string.find(path, 'stdwin') > 0:
+			del sys.path[i]
+			break
+	
+	try:
+		import SpecialKeys	# if this succeeds, we should have everything we need inside the applet.
+		del SpecialKeys
+	except ImportError:
+		# personal hack for me
+		wherearewe = os.getcwd()
+		import Res, macfs
+		if os.path.exists(os.path.join(wherearewe, 'IDELib')):
+			sys.path.append(os.path.join(wherearewe, ':IDELib'))
+			sys.path.append(os.path.join(wherearewe, ':IDELib:Widgets'))
+			Res.FSpOpenResFile(macfs.FSSpec(os.path.join(wherearewe, ':IDELib:Resources:Widgets.rsrc')), 1)
+			Res.FSpOpenResFile(macfs.FSSpec(os.path.join(wherearewe, 'PythonIDE.rsrc')), 1)
+		else:
+			oneback = os.path.split(wherearewe)[0]
+			sys.path.append(os.path.join(oneback, ':Fog:Widgets'))
+			Res.FSpOpenResFile(macfs.FSSpec(os.path.join(oneback, ':Fog:Resources:Widgets.rsrc')), 1)
+			Res.FSpOpenResFile(macfs.FSSpec(os.path.join(wherearewe, 'PythonIDE.rsrc')), 1)
+	
+init()
+del init
+
+##import trace
+##trace.set_trace()
+import PythonIDEMain
diff --git a/Mac/Contrib/PyIDE-src/PythonIDE.rsrc.hqx b/Mac/Contrib/PyIDE-src/PythonIDE.rsrc.hqx
new file mode 100644
index 0000000..5ce5e60
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/PythonIDE.rsrc.hqx
@@ -0,0 +1,221 @@
+(This file must be converted with BinHex 4.0)
+
+:$P"jG'K[ENP%45jbFh*M!(*cFQ058d9%!3#3"b`Nd"S!N!3"!!!!+ci!!#Sq!!!
+!j[HEcGlIHUcGhQHEhHhhR0lYji[YlIqEhrlr!1rpr`2f$P"jG'K[ENP%45jbFh*
+ME(-J8(*PCR-i+5!b,MKL0hJ!!(*cFQ058d9%!3!!!!%"!*!2!H5KX6"P#!#3"L`
+N!$,Vra0TQplGjTV1hGpj[HhYHmhYhhUqh[Ir"1rrHElHp2m&D*hIpjhIqrm"qFq
+cr`(`!#[Vra2fQmlGlRQphGjTR0lGk+hHhrLphrAr!QLph[6r"@LXlQLYhk[r!I!
+2*ZVr$RV-lGhiUqlYpT[GhZqXhr(r!fLYlHrer`4iVGriV`!!+2)SmJ#3"@J"C!!
+4![m-!2rq!!!!5!!!!%J!N!GS!@3!N!8"!!S!N!9S!@3!Q)&N!*!&D!&N!*!*5!!
+!!%J!N!8)!!%!#!#3$bJ!!!$r!!$rN!B!!Ik3"J!#rC!'!!2mN!B!"2Z3"J!&qT!
+'!!EjN!B!"rL3"J!)pj!'!!RfN!B!#[@3"J!,p*!'!!ccN!B!$I+3"J!1mC!'!!r
+`N!B!%1q3"J!4lT!'!",YN!B!%qb3"J!8kj!'!"AUN!B!&ZQ3"J!Ak*!'!"MRN!B
+!'HD3"J!DjC!'!"[NN!B!(113"J!GiT!'!"lKN!B!(q#3"J!Jhj!'!#(HN!B!)Yf
+3"J!Mh*!'!#6EN!B!*GU3"J!QfC!'!#IBN!B!+0H3"J!TeT!'!#V9N!B!+p53"J!
+Xdj!'!#h5N!B!,Y'3"J![d*!'!$$2N!B!-Fk3"J!bcC!'!$2-N!B!0-Z3"J!ebT!
+'!$E*N!B!0mL3"J!iaj!'!$R'N!B!1X@3"J!la*!'!$c$N!B!2F+3"J!q`C!'!$r
+!N!B!3,q3"J""[T!'!%+pN!B!3lb3"J"%Zj!'!%@kN!B!4VQ3"J"(Z*!'!%LhN!B
+!5ED3"J"+YC!'!%ZdN!B!6,13"J"0XT!'!%kaN!B!6l#3"J"3Vj!'!&'ZN!B!8Uf
+3"J"6V*!'!&5VN!B!9DU3"J"@UC!'!&HSN!B!@+H3"J"CTT!'!&UPN!B!@k53"J"
+FSj!'!&fLN!B!AU'3"J"IS*!'!'#IN!B!BCk3"J"LRC!'!'1FN!B!C*Z3"J"PQT!
+'!'DCN!B!CjL3"J"SPj!'!'Q@N!B!DT@3"J"VP*!'!'b6N!B!EC+3"J"ZNC!'!'q
+3!*!!N!#3!*!!N!!!F)q3"J"aMT!'!(+0N!B!Fib3"J"dLj!'!(@+N!B!GSQ3"J"
+hL*!'!(L(N!B!HBD3"J"kKC!'!(Z%N!B!I)13"J"pJT!'!(k"N!B!Ii#3"J#!Ij!
+'!)&qN!B!JRf3"J#$I*!'!)4lN!B!KAU3"J#'HC!'!)GiN!B!L(H3"J#*GT!'!)T
+eN!B!Lh53"J#-Fj!'!)ebN!B!MR'3"J#2F*!'!*!!Ej!'!*&ZN!B!NQf3"J#6E*!
+'!*4VN!B!P@U3"J#@DC!'!*GSN!B!Q'H3"J#CCT!'!*TPN!B!Qf53"J#FBj!'!*e
+LN!B!RQ'3"J#IB*!'!+"IN!B!S9k3"J#LAC!'!+0FN!B!T&Z3"J#P@T!'!+CCN!B
+!TeL3"J#S9j!'!+P@N!B!UP@3"J#V9*!'!+a6N!B!V9+3"J#Z8C!'!+p3N!B!X%q
+3"J#a6T!'!,*0N!B!Xdb3"J#d5j!'!,9+N!B!YNQ3"J#h5*!'!,K(N!B!Z8D3"J#
+k4C!'!,Y%N!B![%13"J#p3T!'!,j"N!B![d#3"J$!2j!'!-%qN!B!`Mf3"J$$2*!
+'!-3lN!B!a6U3"J$'1C!'!-FiN!B!b$H3"J$*0T!'!-SeN!B!bc53"J$--j!'!-d
+bN!B!cM'3"J$2-*!'!0![N!B!d5k3"J$5,C!'!0-XN!B!e#Z3"J$9+T!'!0BTN!B
+!ebL3"J$B*j!'!0NQN!B!fL@3"J$E**!'!0`MN!B!h5+3"J$H)C!'!0mJN!B!i"q
+3"J$K(T!'!1)GN!B!iab3"J$N'j!'!18DN!B!jKQ3"J$R'*!'!1JAN!B!k4D3"J$
+U&C!'!1X8N!B!l"13"J$Y%T!'!1i4N!B!la#3"J$`$j!'!2%1N!B!mJf3"J$c$*!
+'!23,N!B!p3U3"J$f#C!'!2F)N!B!q!H3"J$j"T!'!2S&N!B!q`53"J$m!j!'!2d
+#N!B!rJ'3"J$r!*!,D!&N!*!&D!&N!!!!"S%!J3#G!!!'J3#"!*d!!!D"!)%!R3!
+!"S%!J3#G!!!LJJ!!1[mkq`$q1[X!rMVT!2ikq`!#(6Skq`!#(6Sk`J#G!!!hK3!
+$#Thrrrlr!iX!#S[mr`1,#JU,r2m"L`V[!!%+Lrcr!iX+!(Vmr`5G#J"km2hr!Cd
++a3#G!!!YKJ!"1Z2qrrhr!GAMq[rrirVr!H-Gm3!"(GAkr`(MeIVr!I$9q[m"m$V
+'!*d!!"H'!!$9rIrUr`#qm3!![ZEr!H-+a`#G!!!@K`!!F[crkIm!Ar-!!&rNr`"
+ba`#G!!!@K`!![[crkIm!RI-!!)[Nr`#qa`#G!!!@K`!!m2crkIm!eI-!!,lNr`$
+`a`#G!!!8K`!!m2crkIm!ir-!!0AMrmF!R3!!&SF!!,lmrqRr!+hc!!#Gj2m![XF
+!R3!!+SF!!)[mrrRr!Z1qir6r"()!(6Skp`!!F[6r!Z1qeI6r")X!(6Skb`#G!!!
+mK`!"#[$prrcr!["b#[i!!3Tbprm#ii[`r[m#m)X+qJ!"#Z2hr`&k#[d!!A,`q2m
+#m)[`rIm"R3V1!*d!!#b'!!"brIrpr`(9(IS!!6VMmIm"iahk!!"0qIm"m$Vk!!%
+GeI(r!I!kc`#G!!!RKJ!!eIhrrIm!1[J!!$V`r`#qqJ!!VIRr!%hi!!!km2m"i`V
+3!*d!!#D(!!"br2rqr`#,pJ!!RI$r!&rm!!"IqIm!VIB!!)[`r`"kd!#G!!!QK`!
+![[crr[m!1[B!!&r`r`#Yr!!!RIRr!(,f!!!km2m![Y!!R3!!*)F!!2$mrrlr!!V
+f!!!km2m!ir`!!0Ajr`!kpJ!!#Zrrd!#G!!!FK`$lrrlrp3!!#Zrrr!!!irRr!"h
+e!1rrd!#G!!!QK`!!eIcrr[m!1[B!!$V`r`#qr!!!VIRr!%hf!!!km2m!ip!!R3!
+!QIF!!3SGqJ!"#Khk!!%+(IS!!3SkqJ!"#MVk!!%+1[S!!3SkqJ!"#MVj!!!km!!
+!1[N!!$Vj!!-k!!#Gr2rqr`"bpJ!!H[$r!)[m!!"kqIm!LrB!!&r`r`1Y!!!kq3!
+"1JVa!!%k#[S!!6S+qJ!"1JVk!!%k#[S!!4d+qJ!"(3Vk!!%G#[S!!4d+qJ!"(3V
+k!!%G#[S!rahP!!%-qJ!,(Ch`rrr`V6S!#ThMr[m&[MS!#ThMr[m&[MS!#ThMr[m
+&[MS!#ThMr[m&[MS!#S[Mr[m&[MS!#S[Mr[m&[Nd!#S[Mr[m&[Nd!#S[Mr[m"e8h
+h!!)+HZ2qr`@q63!!HYAqr`@qFJ!!HYAqr`,9Lr$prrlr!H-+q!!"#[$ar`(`#[`
+!!3VMq[m"m"hi!!%+ir$r!AUqr[m&iiX+!%fqr[m#ihS+p`!"6GAqr`AML`S!6El
+qr`AML`S!1Vlqr`AMR3S!1Vlqr`AMR3S!1Vlqr`AMR3S!1VlqrbrMR3S!1Vl`rrr
+MR3S!1Uh`rrrMV4d!1Uh`rrr`V4d!1Uh`rrr`V4d!1Uh`rrr`V4hS!!#Lq`!"6I$
+kr`',m2Vr!Ch`q[m"RI$kr`'Gm2Vr!ChMq[m"RH2kr`'YirVr!DhMq[m!VIN!!6V
+Mq[m"VGAkr`'qeIArrIm!RIJ!!+h`r`"IqJ!!6IRr!+hi!!#,k2m"ikhkr`(`1[N
+!!+hkr`(MRIVr!H1Gr2rrr`(`RIVr!I#Gq[m"m*hkr`(`RIVr!I#,q[m"m*hkr`(
+`RIVr!I#GqIm!AqN!!$Rm!!%km,$r!)[l!!%+eH,rrIm!1[J!!%h`r`#qqJ!!VIR
+r!(,i!!!kh[m"e3Vl!!"kkIqer`!kkJ!!0[`!!+fZr`!Gr!!!FZ(rr[m!RIB!!+h
+`r`"Ir!!!6IRr!,lf!!#Gh[m!H[`!!"hSrlAr!0AU!!!fr3!!#Uhr!(,m!!$9iIr
+qr`"0pJ!!F[$r!*hm!!#GqIm!H[B!!%hHr`$9r!!!FZMrY2m!(HX!!$,p!!!kVIm
+![[`!i2rqr`!kpJ!!1[$r!2$m!!#qqIm!1[B!!"hGrr`!!+hSrl6r!$VV!!!`r3!
+!1Uhr!,lm!1$rr[m!#[B!!"h`r`$`r!!!eIRr!$Ve!0hrr!!!VHMrY2m!AqX!!$E
+p!!!+VIm!H[`!!0AKrrlr!$Vf!!"0m2m![[`!!*hjr`"IpJ!!1Ylr!12m!!"kk2q
+dr`!kk`!!Hr`!!12cr`$MqIm!irRr!12jr`$Mjrm!eI2r"Nd!!$S!!*hcr`$Mm2m
+$irrrH[B!!hVrrq2cr`Ck!!!k!!"bqIm!LrB!!(,fr`$9qIm!ir2r"Ud!!"d!!$V
+cr`$MqIm"ir$kr`(Mm1Mr!H2`mIrrm2Vrrr$crrm!r`VZ!!$Hr!!!ArIr)["I(3!
+!#Nh9rr"b1J!!#Nh9rr"b1J!!#MUqrr"b1J!!#MUqlIm"HMVq!!%k[[Ir!Y9keIl
+r!Ejkp[m"HMVq!!%kVIEr!SXk!2m!!MU,#[J!!Kek1[i!!6U,prm#m&qqr[m#eAV
+Mq[m"m"hi!!%+irRr!DdkrJ!+1RVrrkdk#J!!1RVfr`Gb[[$rrp@,eIIr$Eik#J!
+!1R,`rlik#J!!#cTbm2r963S!!"ebm1lr"p90#J!!(9r`prm3iddG!!!GAr$ridd
+G!!!G6I$hr`H,VH2rrq1Y(I%!!+2l!!#YqIm"[JVk!!&b(IS!!A)GqJ!"FKhk!!"
+bm2m"e6Vk!!"bjrm"icVk!!&0m2Vr!I!krJ$p!!!+q!!!#[S!!6V`qIm!m2Vr!2$
+jr`#Yq!!!LrVr!I"0qJ!"1Nhk!!%keIRr!2$kr`$`q2m!F[S!!4eIr!$r!!%GF[S
+!!4h9m2m!H[S!!3UYqIm"R3Vl!!)+FJVl!!%+VIMr!2$lr`(`Ar)!!&cm!!%Gm2V
+r!I!+h3!!Lr,r!I!Gq!!!HZMr!$Vi!!"bq[m!1[d!kJ!!6HMr!(,i!!!kq[m!FZm
+!!$VSr`#,mJ$e!!%+m2,r!+hi!!%+ir[r!,l[!!%+ir$r!$Vc!!"Cr!!![[Rr!&r
+F!!%+ir2r!(,f!!$Mk[m!H[B!!,lmr`#Gr!$T!!#Yk[m![[B!!*hmr`#ql3!!HZV
+r!H-+mJ$d!!"Imrm"m!Vh!!!kqrm!(Hi!!$V`r`$9m`!!82`!q2rD!!#Gmrm!(IB
+!!)[Ur`!kpJ!!H[cr!$Vm!1N!!&rUr`"bpJ!!1[cr!(VY!!!kk[m!Lr%!p!!!#[2
+r!,le!2cr!,lX!!$`m2m!(I3!!&,p!!!kqIm![YS!!(,dr`$`p3!!AqVrp3!!1[c
+r!!Vm!1N!!$VUr`!kpJ!!#[cr!$VX!1Vr!&ra!2-!!12dr`"kp3!![[hr!)[X!!#
+qm2m!6I3!!&$p!!!kqIm![YS!!%hdr`$9p3!!1ZVrp3!!1[crq`$T!!!+k[m!(I8
+!r2m!1Z`!!12Vr`!km3$c!!#qp2m!H[8!!,lpr`"kl!!!RI$r!(,d!!"8r3!!(IR
+r!12D!!"kmrre!!"bk[m!(IB!!%hmr`!kr!$T!!!kk[m!6IB!!$Vmr`"Il3!!#ZV
+r!(Va!2-!!2$dr`#,p3!!irhr!*hX!!#qm2m!1[3!!'Vm!!$`qIm!#YX!!llrrq2
+fr`!kpJ!!RHVr!%hf!!"kr2m!Ar`!k3!!FZVr!(Vf!!CIrrr`rrq,l3!$1[rrm2$
+r!q2rrkha!23!!ahrrq2fr`#qpJ!'#[rrm2rreH`!rrrrm22rr`$r#[F!!)Rm!!"
+kqIm!Lp`!"aeb1J!!#MUqqIm!VIF!!4h`k[m!eIF!!3V`r2m!eI`!kJ!"#Z2Ur`(
+`#[F!"hSk#J!!1RS+lJ!'HMS+!!!kH[Er"lik#J!!1RSkmJ$d!!GI63S!!$Tbm2R
+r!$Vh!2p0"4d!!$TI1Zi!"ep0(3!!(9r`prm(RDhMrrrMV4hk!!"Eq`!!eIRr!$V
+9!!"kqIm!6IJ!!,lSr`"kq!!!VIVr!(Vp!1S!!)[Sr`#Gm!!!#Zm!!!Vk!!%keIR
+r!(,U!1d!!4h9q[m"i`Vi!!!+j`!!#[X!!3UYmIm"m%hl!!#Dr!!"#[$kr`(`#[J
+!!$Vl!!)k[R,l!!)k[R,l!!!Gp`!!RIVr!$Vi!!#,k2m!1[J!!(Vjr`1,#J!!rJ$
+r#[J!!4d+r!!"#UhRr`"bl`!"#Khl!!*k[Khl!!!Gq!!!1[Vr!)[h!!!kq`!#FY8
+kr3$r!!*b[MVl!!!Gq!!"(I$lr`#qlJ!!1[X!!Nh91[X!!"hi!!%+ir$r!$Vm!!#
+er!!!RIRr!&rh!!BGeAT01R+Gr[m&eAT01R+Gr[m'eAT01R+GH[F!!3V`r2m!H[F
+!!3VMk[m!RIB!!12jr`,MLem$1Pq,RIB!"Tf,AcTILq2Rr`$9l3$rL`jb1Nekirr
+rm*eb1Nek[JVh!!#,r2m"i`Vh!!CkRA)k6AV9r[m$RA)k63&keIlr"Teb1Nek[JV
+h!!"bqrm!(Hi!"PqYHMSkHVlqr`DYHMSkHVikp`!!1[$r!,lm!!"Jr!$ir`!+pJ!
+![ZVr!$Vf!!#Yr2m!1[B!!*hUr`!kpJ!!H[Err[m!ArB!!(VKr`"kl3!!6I2r!)[
+f!!!kr2m!RIB!!$VdrrIr!,lf!!!+r2m![Z`!mrm!ir8!lrm!#[d!!&lp!!!kqIm
+!ir8!!)[Urr8!!(Vmrr8!!(,Ur`!GpJ!!6IErr[m!1[B!!$VKr`!kl3!!(I2r!&r
+e!2cr!(,e!26rprm!H[8!!2$pr`#,l!!!eI6r!+he!!#qm2m!1[d!!&lp!!!kqIm
+![[8!!(VVr`$9p3!!6Ihr!2$e!!!kk[re!!!kp[rqrr8!!$VKr`!kl!$cr`!kp3$
+mr`!kp3!!irArprm!H[8!!,lpr`"kl!!![[6r!(Ve!!#Ym2m!F[d!!',p!!!GqIm
+!m28!!*hUrr8!!(Vmr`!+pJ!!HZVr!$Vf!!"Ip[rqr`!kpJ!!6H(r!%hY!!!kmrm
+!F[B!!!Vmr`"kp3$drrIr!)[e!!$`rIm!RH`!!12dr`#qp3!!eI$r!$Vp!!"Lr!!
+!m2Rr!"hf!!$9k[m!1[B!!,lmr`!kpJ!!VHVr!%hf!!#,p[rqr`"bpJ!!HZ(r!(V
+Y!!"Imrm!RIB!!$Vmr`#YpJ!!1[6rprm![[B!!"hmr`$9l3!!#[2r!2$e!1rrr!!
+!PI`!!(Vjr`#,p`!C1SXk(3!kFZ2rrje0(3!kFZ2rrje0(3!kFS[h!!!kqrm!VIF
+!'4f,64d!1Pr9rrqY64d!1Pr9rrqY64d!1Pq,p`!"#[$frrlr!0Ah!!%+iq(r!I!
++lJ!!eI2r!I!Gp`!![[cr!I!Gp`!!VI6rp[m!1[F!!)[lr`!klJ!!H[,r!%hh!!"
+bm2m!Lr`!!)(l!!$9qIm!1[J!!!Vl!!)+HMVl!!)+HMVl!!!+p`!!eIVr!%hi!!!
++qJ!"HMVk!!&k6IS!!!Vi!!#YpIrpr`"kq!!!Lprr!+h[!!"bmIm![[J!!(,kr`#
+qq!!!6I2rp[m"i`Vj!!!kq[m"i`V`!!!kmIm"m!Vj!!%Gm2(r!H-+r!!!Er`!!3V
+`q[m"m"hG!!!Gq`!"(Elir`"bq`!"(3VG!!"kpIrpr`"Iq!!!FYrr!(Vi!2m+q`!
+!H[$r!)[i!!!kq[m!RIJ!!$VcrrEr!,li!!%Gm2[r!,lh!!!Gq`!"6I$ar`(9#[N
+!!3VMmIm"m"hm!!"cr!!!RIRr!&rF!!F+VASk1Pq,m2Er"Y9k66TILh,E!!$9p[r
+qr`#YpJ!![Z(r!0Af!!DGLemk6AVMm2m"i`Vh!!"kr2m"i`Vh!!"kp2rhr`(`#[F
+!!(,mr`(`(IF!"PqGFMSkHVl[r`!kp`!!1[$r!0Am!!"3r!$irpS!!+hUr`!kf`!
+!H[Err[m!6IB!!(,Kr`"kpJ!!1ZVr!)[f!!!kr2m!LrB!!$VdrrIr!+hf!!!+r2m
+![[8!k[m!eI8!!2$`r`!Gr3!!82d!!$Vjr`$9fJ!!HZVrfJ!!1[Err[m!(IB!!$V
+Kr`!kpJ!!#ZVr!%he!2cr!&re!26rprm!H[8!!12pr`"kp3!!eH[r!*he!!#qm2m
+!6Id!!&$p!!!kqIm![YS!!$VVr`$9fJ!!1[Err[rd!1(r!"he!1Vr!$Ve!!$MrIm
+!1[8!!,lerrIr!%he!!#qrIm!H[8!!,lVr`"kp3!!H[$r!(Vp!!"3r3!!1[Rr!12
+D!!"kk[rD!!"0p[rqr`!kpJ!!1Z(r!$Vf!!!Gk[m!Ar8!r2m!F[8!p2rhr`"kp3!
+!irhr!)[e!!$9krm!VI8!!,l`r`!kr3!!8[`!q2m!#YX!!+hUr`!kf`!!H[Err[m
+!ArB!!(VKr`"kpJ!!6HVr!)[f!!!kr2m!RIB!!$VdrrIr!,lf!!!Gr2m![[B!!!V
+Ur`$Mp3$[r`!+r3!!C[`!!(Vjr`"kh!!"1[$Ur`#Yh!!"#Z2frrlr!0Ah!!%+ir$
+r"VjI1J!kAlljr`(M#[F!!,lUr`(`#[F!!+hmr`(`(IF!!*hdrrEr!$Vh!!"kqrm
+!1[F!!(VTr`"0p`!!F[$r!+hm!!"Nq`!!eIRr!$VG!!$9k2m!6Gd!!+herrhr!(V
+i!!#,m2m!H[S!!A,`q[m!RIJ!!(,Sr`#qq!!!6IVr!,li!!"0mrrfr`(9#[N!!$V
+kr`(M#[N!!$VSr`(`#[N!!4h`mIm"m!Vm!!#'r!!"#Z2kr`(`(IJ!!"hl!!)GVA,
+l!!)GVA,l!!%G[ZIr!I"kq`!##Uekq`!##Uekq`$r#[J!!)[errhr!(,i!!"kmIm
+!H[J!!(,kr`#,q!!!FZMr!+hi!!"0q[m!VIJ!!$VcrrEr!,li!!!kq[m!eIJ!!4h
+`kIm"i`Vj!!%+m2(r!I!+r!!!N[`!!)[jr`"bp`!C1Vjk1MTILr$rrljk1MTILr$
+rrljk1MTILr$NraM9HMSkAi[`rrr9HMSk6AV`rrrMHNdk6AUGp`!"#Z2frrlr!,l
+f!!$9mrm!eIB!!,lmr`$MpJ!![ZVr!I!+p`!!RIcr!I!+p`!!Lr6rp[m!(IF!!(V
+lr`!kp`!!FZRr!$Vh!!"0m2m![[`!!&cm!!$`qIm!#[B!!,kdr`"0pJ!!H[Err[m
+!ArB!!(Vcr`"kpJ!!Arcr!(Vf!!"Ik[m!RIB!!$Vmr`#YpJ!!1[6rprm![[B!!"h
+mr`#qpJ!!#ZVr!12e!1rr!!Vp!!"Fr3!!(IRr!12e!!#,Y2m!(IB!!%hfrrlr!$V
+f!!!kmrm!1[B!!$Vmr`"0pJ!!1ZVr!(,f!!!+r2m!H[8!p2rhr`#,p3!!m2hr!)[
+e!!$Mkrm!VI8!!,l`r`!kr3!!92d!!$Vjr`#qp3!!HV6rp3!!1[Err[re!!!Gmrm
+!(I8!r2m!1[8!k[m!1[8!r2m!1[8!!12errIr!(Ve!!#qrIm!H[8!!,lVr`"kp3!
+!RI$r!(Vp!!"Fr3!!(IRr!12e!!#,Y2m!(IB!!&rfrrlr!$Vf!!!kmrm!1[B!!$V
+mr`"0pJ!!1ZVr!(,f!!!+r2m!H[8!p2rhr`#,p3!!m2hr!*he!!$Mkrm![[8!!,l
+`r`!kr3!!A2`!!2$jr`!+pJ!![V6r!%hf!!"kp[rqr`"IpJ!!H[2r!(Vf!!"br2m
+!H[B!!&rUr`#GpJ!!1[cr!+hf!!!kp2rhr`#qpJ!!(Icr!0Af!!!+k[m!m28!lrm
+!#[d!!*Rm!!#,qIm!H[F!!$V,raLYAcS!1R,MrrqYAcS!1R,MrrqqAcS!1R+,p`!
+"#Z2frrlr!,lf!!qGFMS!1R,9rrr9FMS!1R+,pJ!![[cr!H-+p`!![ZVr!I!+p`!
+!VIcr!I!+p`$rHJXk#MTIVIrrm(Sk#MS,Ajhrrr"k1JSG6Cdkp`!!H[[r!$Vh!!"
+bkIm!1[F!!%h`r`#Yr!!!K2`!!3VMqIm!(IJ!!0A0r`(`6IX!!JU,F[X!!JU,F[X
+!!!Vh!!#,pIrpr`"bq!!!(IS!rhVk!!!Gq!!!F[Vr!)[i!!"bk2m!VIJ!!%hkr`#
+Yq!$r#[X!!NfG#[d!r`!#1Td+q`!!#[J!!$Vkr`$9q!!"(I$Tr`(M#[N!!3V`mIm
+"m!Vm!!"Rr!!"#Z2jr`!kq!!!eFhr!%hH!!"kp2rpr`(`F[S!!!V[!!!GqJ!"F[$
+kr`#,q!!!FZMr!+hi!!"0qIm"L`Vl!!!GmJ$d!!!+q`!"1Z2kr`$9q!!"(I$Tr`(
+M#[N!!3V`mIm"m!Vm!!"`r!!!LrRr!(,h!!!kc[m!Lq8!"`U,AcS!1R,9mrrlr`D
+qAcS!1Pq,l3!'R9mk!$TI[[Rr!H-+p`!![ZVr!I!+p`!!VIRr"q0b1J!G6BXGmJ$
+d!!CbHMS+(8fGq2m!1[F!!(,Tr`!kp`!!6I$r!+hm!!"+r!$ir`!+pJ!![Xrr!$V
+N!!"klIrer`"Il3!!Ar2r!(Vf!!"0k[m!LrB!!$Vcr`#Gm3$d!!!Gmrm![[8!k[m
+!ir8!!2$`r`!Gr3!!5[d!!$Vjr`$Mp3!!Lmrri`!!6HhrpIm!1Zd!!$Vcr`!kpJ!
+!(HVr!&re!22r!(,a!2-!!2$dr`#,p3!!eH[r!+he!!#qm2m!6Id!!%Mp!!!kqIm
+![[8!!(,3r`$9i`!!1ZhrpIrV!22r!"he!1Vr!$Ve!!$Mp2m!1[%!m`!![[6r!(V
+e!!#qkrm!H[8!!(V`r`"kr3!!5[d!!$Vjr`$9p3!!HXrri`!!6HhrpIm!1Zd!!$V
+cr`!kpJ!!#ZVr!&re!22r!(,a!2-!!12dr`"kp3!!eH[r!*he!!#qm2m!F[d!!%V
+p!!!+q2re!!#qcrm!1Z3!!(VYrrAr!&rY!!"Imrm!H[B!!%hUr`#,pJ!!1[2r!*h
+a!23!!!Vcr`#qp3$Ur`$9p3!!m2$r!"hp!!"6r!!!RIRr!(,h!!%Gm-rr!)[P!!%
++iqhrpIm![Zd!!,lcr`$9pJ!!VHVr!H-+p`!!Lr2r!I!+mJ$d!!"bmrm"m"hh!!"
+bkIm!1[F!!$V`r`#qr!!!9I`!!3V`q[m"m"hi!!#qcIm!1ZB!!)[Xrr6r!(,[!!"
+bmIm!H[J!!%hSr`#,q!!!1[(r!+hb!28!!6V`m[m!eIJ!!4h`kIm"i`Vj!!%+m2(
+r!I!kr!!!LrX!!0Ajr`(91[X!!3UYlrm!m2[r!["b[[[r!["b[[[r!2$ir`(`6IS
+!!AT0qJ!"HNhk!!"kkrrdr`(`F[S!rh,k!!&bm2$r!(Vk!!&0m1Ir!AS+q`!"6I$
+`r`',#[X!!MU,#[d!r`!#1SX+q`!"1Z2`r`'Y#[X!!6V9jrm"[Khl!!%GeI$r!H-
++r!!!U2`!!(Vfr`D,64d!1R,`l[mCe9qYirqqLad!!&qYirqqLad!!&qYirqqLh,
+fraLY64d!1Pr9rrqY64d!1Pr9rrqY64d!1Pr9k[rbr`qqAcS!1PqqrrqqAcS!1Pq
+qlIm'e9mk!"e0VH6r"Z0b1J!G6ChYr`hMFMS!(8fGrrrMFMS!(3T0RIrrih)k!!T
+0Lqhr"["b1J!+1S[Nr`I`LcS+#MU,m1lr!)[m!!!8r!!!m0[r!$VU!!#qa[q"rk,
+rr!!!&[d!!"hDr`"bkJ!!m-ErJIqLr`!kr3!!&[d!!$VDr`#,k`!!#XArJIqLr`"
+br3!!&2d!!$VDr`"kkJ$&ri(rS[m!6Id!!"6m!0Vr!&rU!!$Ma[q"rk,r!$Vp!!!
+Ar!!!RGcr!I!+kJ!!HXErJIqMr`#qr!!!'2`!!4h`hIm!AqN!!3V9arq"rk2r!$V
+m!!!qq`!"#YAlr`$`jIm!1ZJ!!3UYq[m!m06r!2$qrrhr!BZYq[m!m-,r!2$kr`$
+`err(r`$`qrm!m1Er!H-+r!!!6IS!"`TbVI$r[SZGjIm!eHB!"PqGirr9RAV6r`2
+`AkhM$Ir9V6S!!%fYirrMVA,``Im(FTh9rq1YFZ2ArmIrriX&[[r`[RUqjIm!RI`
+!!#cc!!$`j2m!1Z%!!,l5r`*k!!$e!!#,`2m!VI`!!(,@rmErr!!!(H2r!!Vp!!!
+Xp!!!(H2r!(VK!0(r!Ud!!28!!,l!r`$`r!!!RGEra[m!1[d!!%hMr`!kr3!!+[3
+!!$VMr`#YiJ!!1Y(r!Y8!!28![[rm!!#qe[r'r`"0r3!!HZ2r!(Vp!!!Up!!!1Z2
+r!(VK!0(r!Vi!!28!!11rrr`!!+h@rmEr!$Vp!!"Iirm!6Id!!#Vc!12r!%hK!!$
+9d[m#HJ!!p3!!VF$r!,lm!!"ke[r'rr`!!$VMr`!Gr3!!,r-!!*hPr`(`#Z%!!(V
+5r`)k!!$e!!"0`2m!F[`!!"h@rmIr!,ll!!$9jIm!eI`!!$$c!!%+m1Er!%hI!!$
+9e2m!Lri!p!!!VF,r!0Ak!!"kerr)r`(`(IX!!$VPr`!kr!!!X2)!!6VMq[m"Lq2
+kr`',irVr!(VH!!%keIVr!Ch9q[m"VGAkr`'YeIVr!Dfqq[m"V3Vq!23!!3Uqq[r
+r[[Vr!EkYq[m"[Uhkr`'qVIVr!EkYq[m"eDhkr`(91[N!!)[kr`(9RIVr!G@Gq[m
+"ijhkr`(MLrcrrrm"ii[kr`(MLrVr!H1,q[m"m)[kr`(`LrVr![#,m2[r!I"0q3!
+"Ar$lr`,`Lr$lr`,`RI$lr`(`ArX!!4,a!"N+LpArrr#Y1J!+LpArrr#Y1J!+LpA
+rrr#q1YX!!AV9r[m&[MS!!(V9r[m&[Nd!!(V9r[m&[Nd!!(V9r[m&[Nd!!(+qr[m
+"[Nhm!2)!!A+qr[m&[R)!!(+qr[m&[R)!!%fqr[m&[R)!!%fqr[m&eA)!!%fqr[m
+&eAS!!%fqr[m&eAS!!%fqr[m"eAVf!#Jk[[$rrp9k!!!k[[$rrp@,!!!k[[$rrp@
+,#J!k[[$rrp@,#J!k[[$rrcM9L`S!1Uh`rrrML`S!1Uh`rrrML`S!1Uh`rrrMR3S
+!1Uh`rrrMR3S!1Uh`rrrMR3S!(DhMrrrMR3Vh!"NGVH2rrq1Y(3!GVH2rrq1Y(3!
+GVH2rrq1Y(IS!!(hY!!!Gq3!!(IN!!"h9!!!kq3!!1[N!!$Vj!!!Gq3!!(IN!l`!
+!(IN!!"hj!!!kq3!!1[N!!$Vj!!!kq3!!1[!!!"hj!!!Gq3!!(IN!!"hj!!%G#[S
+!!4d+qJ!"(3Vk!!%G#[S!!4d+qJ$r#[S!r`Va!2m+qJ$r#[S!r`Vh!!!'J3#"!*d
+!!!D"!)%!R3!!"S%!J3#G!!!'J3#"!*d!!!$r!!!!'!"A!(B"J`)'!!%"!*!(!G3
+!!$!+!!!!%J#3"`B!&3"Z!AR!!J(c!!!!H`!(!6S1*#K"8&"-580"9%P26LN0*#K
+3@94)6diT1NaTBK%N+&"C9%K26LNk8'aeCdPZFa%N+&"C9%K26LNk6@&M1NaTBKN
+N+&"C9%K26LNk6@&M1NaTBMTdEfpXBQpi'b3S8&P85%p1+6T0B@-k6'PL1R0MFQP
+`G'PZC`!!!""849K8G(4iG!#S-$%i-3#3"3T3i!!fV!!!"k%J!!!!#P$J!*`m!!!
+qI!#3"!T3i!#F2!!!"k%J!!!!0!#4B!!!!!8`,MPL-5F`,MPL-5#T-6Nj0b"+GA0
+d)(CKEL"5Eh0cG@dJ+%aPG(4PFR*[FLN!!!!G!*&J!!!!#8aPG(4PFR*[FJaLH5"
+-CA4dCA*bEh)!!!%!!!!V2J!!+Mi!!!$Q!b-Rq#Q!!!!!(!$1!!C35808!!!!1N4
+-6dF!!!"'4%P86!!!!&*69&)M!!!!ANG9YdN!!!"U8dPD43!#!(CfCA*c!!%!QJ(
+c!!`!N!J"e!!'!!!SpJ#3"!(8!*!%+4)!N!@"rrm!!#NS!b-RX#J"rrm!!#QR!*!
+%rj!%!!!TZ`-M*R3!!Irr!!!Tb3-M*k3!!2rr!!!Te`-M+A!!!Irr!!!Tj3-M*kJ
+!![rr!!!U(3-M*eJ&3@*[GA3&3@*[GA3,F(PdD'pZ)'a[Cfq"43:
diff --git "a/Mac/Contrib/PyIDE-src/Scripts/  Hold option to open a script\302\212" "b/Mac/Contrib/PyIDE-src/Scripts/  Hold option to open a script\302\212"
new file mode 100644
index 0000000..062c64c
--- /dev/null
+++ "b/Mac/Contrib/PyIDE-src/Scripts/  Hold option to open a script\302\212"
@@ -0,0 +1,4 @@
+"Hold the option key to open a script instead of running it."
+
+import W
+W.Message(__doc__)
diff --git a/Mac/Contrib/PyIDE-src/Scripts/  separator --- b/Mac/Contrib/PyIDE-src/Scripts/  separator ---
new file mode 100644
index 0000000..a24dcd7
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/Scripts/  separator ---
@@ -0,0 +1 @@
+A separator ends with '---'
diff --git "a/Mac/Contrib/PyIDE-src/Scripts/Hack/Remove .pyc files\302\212" "b/Mac/Contrib/PyIDE-src/Scripts/Hack/Remove .pyc files\302\212"
new file mode 100644
index 0000000..80f52d5
--- /dev/null
+++ "b/Mac/Contrib/PyIDE-src/Scripts/Hack/Remove .pyc files\302\212"
@@ -0,0 +1,20 @@
+import sys
+import os
+import macfs
+
+def walk(top):
+	names = os.listdir(top)
+	for name in names:
+		path = os.path.join(top, name)
+		if os.path.isdir(path):
+			walk(path)
+		else:
+			if path[-4:] == '.pyc' and os.path.exists(path[:-1]):
+				print "deleting:", path
+				os.remove(path)
+			elif path[-4:] == '.pyc':
+				print "!!! ------ .pyc file without .py file:", path
+
+fss, ok = macfs.GetDirectory('Select the starting folder:')
+if ok:
+	walk(fss.as_pathname())
diff --git "a/Mac/Contrib/PyIDE-src/Scripts/Hack/Toolbox Assistant\302\212" "b/Mac/Contrib/PyIDE-src/Scripts/Hack/Toolbox Assistant\302\212"
new file mode 100644
index 0000000..e55c00a
--- /dev/null
+++ "b/Mac/Contrib/PyIDE-src/Scripts/Hack/Toolbox Assistant\302\212"
@@ -0,0 +1,53 @@
+import aetools
+import Standard_Suite
+import Required_Suite
+import MacOS
+import W
+
+
+class Toolbox(aetools.TalkTo, Standard_Suite.Standard_Suite):
+	
+	def LookupTopic(self, _object, _attributes={}, **_arguments):
+		_code = 'DanR'
+		_subcode = 'REF '
+
+		#aetools.keysubst(_arguments, self._argmap_OpenURL)
+		_arguments['----'] = _object
+
+
+		_reply, _arguments, _attributes = self.send(_code, _subcode,
+				_arguments, _attributes)
+		if _arguments.has_key('errn'):
+			raise MacOS.Error, aetools.decodeerror(_arguments)
+		# XXXX Optionally decode result
+		if _arguments.has_key('----'):
+			return _arguments['----']
+	
+
+class ToolboxAssi:
+	
+	def __init__(self):
+		self.talker = Toolbox('ALTV')
+		self.w = W.Window((200, 100), "Toolbox Assistant")
+		self.w.button = W.Button((-94, -32, 80, 16), "Lookup", self.lookup)
+		self.w.prompt = W.TextBox((10, 8, -10, 15), "Enter topic:")
+		self.w.edit = W.EditText((10, 24, -10, 20))
+		self.w.setdefaultbutton(self.w.button)
+		self.w.open()
+	
+	def lookup(self):
+		lookup = self.w.edit.get()
+		try:
+			self.talker.LookupTopic(lookup)
+		except MacOS.Error, detail:
+			if type(detail) == type(()):
+				if detail[0] == -609:
+					self.talker.start()
+					try:
+						self.talker.LookupTopic(lookup)
+						return
+					except MacOS.Error:
+						pass
+			W.Message("Requested topic not found.")
+
+ToolboxAssi()
diff --git a/Mac/Contrib/PyIDE-src/Scripts/Insert b/Mac/Contrib/PyIDE-src/Scripts/Insert
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/Scripts/Insert
diff --git "a/Mac/Contrib/PyIDE-src/Scripts/Insert file name\302\212" "b/Mac/Contrib/PyIDE-src/Scripts/Insert file name\302\212"
new file mode 100644
index 0000000..7632f8e
--- /dev/null
+++ "b/Mac/Contrib/PyIDE-src/Scripts/Insert file name\302\212"
@@ -0,0 +1,6 @@
+import macfs
+
+fss, ok = macfs.StandardGetFile()
+if ok:
+	import W
+	W.FrontWindowInsert('"%s"' % fss.as_pathname())
diff --git "a/Mac/Contrib/PyIDE-src/Scripts/Insert folder name\302\212" "b/Mac/Contrib/PyIDE-src/Scripts/Insert folder name\302\212"
new file mode 100644
index 0000000..1a9e06b
--- /dev/null
+++ "b/Mac/Contrib/PyIDE-src/Scripts/Insert folder name\302\212"
@@ -0,0 +1,6 @@
+import macfs
+
+fss, ok = macfs.GetDirectory()
+if ok:
+	import W
+	W.FrontWindowInsert('"%s"' % fss.as_pathname())
diff --git a/Mac/Contrib/PyIDE-src/trace.py b/Mac/Contrib/PyIDE-src/trace.py
new file mode 100644
index 0000000..c45c686
--- /dev/null
+++ b/Mac/Contrib/PyIDE-src/trace.py
@@ -0,0 +1,119 @@
+__version__ = "$Id$"
+
+import sys
+from time import time
+
+class Trace:
+	def __init__(self):
+		self.dispatch = {
+			'call': self.trace_dispatch_call,
+			'return': self.trace_dispatch_return,
+			'exception': self.trace_dispatch_exception,
+			}
+		self.curframe = None
+		self.depth = -1
+		self.stdout = sys.stdout
+
+	def run(self, cmd, globals = None, locals = None):
+		if globals is None:
+			import __main__
+			globals = __main__.__dict__
+		if locals is None:
+			locals = globals
+		sys.setprofile(self.trace_dispatch)
+		try:
+			exec cmd in globals, locals
+		finally:
+			sys.setprofile(None)
+
+	def runcall(self, func, *args):
+		sys.setprofile(self.trace_dispatch)
+		try:
+			apply(func, args)
+		finally:
+			sys.setprofile(None)
+
+	def trace_dispatch(self, frame, event, arg):
+		curstdout = sys.stdout
+		sys.stdout = self.stdout
+		self.dispatch[event](frame, arg)
+		sys.stdout = curstdout
+
+	def trace_dispatch_call(self, frame, arg):
+		self.depth = self.depth + 1
+		self.curframe = frame
+		code = frame.f_code
+		funcname = code.co_name
+		if not funcname:
+			funcname = '<lambda>'
+		filename = code.co_filename
+		lineno = frame.f_lineno
+		if lineno == -1:
+			code = code.co_code
+			if ord(code[0]) == 127:	# SET_LINENO
+				lineno = ord(code[1]) | ord(code[2]) << 8
+		pframe = frame.f_back
+		if pframe:
+			plineno = ' (%d)' % pframe.f_lineno
+		else:
+			plineno = ''
+		print '%s> %s:%d %s%s' % (' '*self.depth,filename,lineno,funcname,plineno)
+		frame.f_locals['__start_time'] = time()
+
+	def trace_dispatch_return(self, frame, arg):
+		try:
+			t = frame.f_locals['__start_time']
+		except KeyError:
+			t = ''
+		else:
+			t = ' [%.4f]' % (time() - t)
+		funcname = frame.f_code.co_name
+		self.curframe = frame.f_back
+		if not funcname:
+			funcname = '<lambda>'
+		filename = frame.f_code.co_filename
+		print '%s< %s:%d %s%s' % (' '*self.depth,filename,frame.f_lineno,funcname,t)
+		self.depth = self.depth - 1
+
+	def trace_dispatch_exception(self, frame, arg):
+		t = ''
+		if frame is not self.curframe:
+			try:
+				t = frame.f_locals['__start_time']
+			except KeyError:
+				pass
+			else:
+				t = ' [%.4f]' % (time() - t)
+			self.depth = self.depth - 1
+			self.curframe = frame
+		funcname = frame.f_code.co_name
+		if not funcname:
+			funcname = '<lambda>'
+		filename = frame.f_code.co_filename
+		print '%sE %s:%d %s%s' % (' '*(self.depth+1),filename,frame.f_lineno,funcname,t)
+
+	def set_trace(self):
+		try:
+			raise 'xyzzy'
+		except:
+			frame = sys.exc_traceback.tb_frame
+		while frame.f_code.co_name != 'set_trace':
+			frame = frame.f_back
+		d = 0
+		while frame:
+			d = d + 1
+			frame = frame.f_back
+		self.depth = d
+		sys.setprofile(self.trace_dispatch)
+
+def run(cmd, globals = None, locals = None):
+	Trace().run(cmd, globals, locals)
+
+def runcall(*func_args):
+	apply(Trace().runcall, funcargs)
+
+def set_trace():
+	Trace().set_trace()
+
+def unset_trace():
+	sys.setprofile(None)