First Checked In.
diff --git a/Mac/Tools/IDE/Wbase.py b/Mac/Tools/IDE/Wbase.py
new file mode 100644
index 0000000..46a8c96
--- /dev/null
+++ b/Mac/Tools/IDE/Wbase.py
@@ -0,0 +1,720 @@
+import Qd
+import Win
+import QuickDraw
+import Evt
+import string
+from types import *
+import sys
+
+WidgetsError = "WidgetsError"
+
+DEBUG = 0
+
+class Widget:
+	
+	"""Base class for all widgets."""
+	
+	_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._visible = onoff
+		for w in self._widgets:
+			w.show(onoff)
+		if self._parentwindow is not None and self._parentwindow.wid is not None:
+			self.SetPort()
+			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):
+		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):
+		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):
+		# calculate absolute bounds relative to the window origin from our
+		# abstract _possize attribute, which is either a 4-tuple or a callable object
+		oldbounds = self._bounds
+		pl, pt, pr, pb = self._parent._bounds
+		if callable(self._possize):
+			# _possize is callable, let it figure it out by itself: it should return 
+			# the bounds relative to our parent widget.
+			width = pr - pl
+			height = pb - pt
+			self._bounds = Qd.OffsetRect(self._possize(width, height), pl, pt)
+		else:
+			# _possize must be a 4-tuple. This is where the algorithm by Peter Kriens and
+			# Petr van Blokland kicks in. (*** Parts of this algorithm are applied for 
+			# patents by Ericsson, Sweden ***)
+			l, t, r, b = self._possize
+			# depending on the values of l(eft), t(op), r(right) and b(ottom), 
+			# they mean different things:
+			if l < -1:
+				# l is less than -1, this mean it measures from the *right* of it's parent
+				l = pr + l
+			else:
+				# l is -1 or greater, this mean it measures from the *left* of it's parent
+				l = pl + l
+			if t < -1:
+				# t is less than -1, this mean it measures from the *bottom* of it's parent
+				t = pb + t
+			else:
+				# t is -1 or greater, this mean it measures from the *top* of it's parent
+				t = pt + t
+			if r > 1:
+				# r is greater than 1, this means r is the *width* of the widget
+				r = l + r
+			else:
+				# r is less than 1, this means it measures from the *right* of it's parent
+				r = pr + r
+			if b > 1:
+				# b is greater than 1, this means b is the *height* of the widget
+				b = t + b
+			else:
+				# b is less than 1, this means it measures from the *bottom* of it's parent
+				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 isinstance(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):
+	
+	"""Base class for clickable widgets. (note: self._enabled must be true to receive click events.)"""
+	
+	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):
+
+	"""Base class for selectable widgets."""
+	
+	_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.SetPort()
+		self.draw()
+	
+	def setpattern(self, pattern):
+		self._framepattern = pattern
+		self.SetPort()
+		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)
+
+def _darkencolor((r, g, b)):
+	return 0.75 * r, 0.75 * g, 0.75 * b
+
+class BevelBox(Widget):
+	
+	"""'Platinum' beveled rectangle."""
+	
+	def __init__(self, possize, color = (0xe000, 0xe000, 0xe000)):
+		Widget.__init__(self, possize)
+		self._color = color
+		self._darkercolor = _darkencolor(color)
+	
+	def setcolor(self, color):
+		self._color = color
+		self.SetPort()
+		self.draw()
+	
+	def draw(self, visRgn = None):
+		if self._visible:
+			l, t, r, b = Qd.InsetRect(self._bounds, 1, 1)
+			Qd.RGBForeColor(self._color)
+			Qd.PaintRect((l, t, r, b))
+			Qd.RGBForeColor(self._darkercolor)
+			Qd.MoveTo(l, b)
+			Qd.LineTo(r, b)
+			Qd.LineTo(r, t)
+			Qd.RGBForeColor((0, 0, 0))
+
+
+class Group(Widget):
+	
+	"""A container for subwidgets"""
+
+
+class HorizontalPanes(Widget):
+	
+	"""Panes, a.k.a. frames. Works a bit like a group. Devides the widget area into "panes",
+	which can be resized by the user by clicking and dragging between the subwidgets."""
+	
+	_direction = 1
+	
+	def __init__(self, possize, panesizes = None, gutter = 8):
+		"""panesizes should be a tuple of numbers. The length of the tuple is the number of panes, 
+		the items in the tuple are the relative sizes of these panes; these numbers should add up 
+		to 1 (the total size of all panes)."""
+		ClickableWidget.__init__(self, possize)
+		self._panesizes = panesizes
+		self._gutter = gutter
+		self._enabled = 1
+		self.setuppanes()
+	
+	#def open(self):
+	#	self.installbounds()
+	#	ClickableWidget.open(self)
+	
+	def _calcbounds(self):
+		# hmmm. It should not neccesary be override _calcbounds :-(
+		self.installbounds()
+		Widget._calcbounds(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:
+			# XXX does not work!
+			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):
+	"""see HorizontalPanes"""
+	_direction = 0
+	boundstemplate = "lambda width, height: (width * %s + %d, 0, width * %s + %d, height)"
+
+
+class ColorPicker(ClickableWidget):
+	
+	"""Color picker widget. Allows the user to choose a color."""
+	
+	def __init__(self, possize, color = (0, 0, 0), callback = None):
+		ClickableWidget.__init__(self, possize)
+		self._color = color
+		self._callback = callback
+		self._enabled = 1
+	
+	def click(self, point, modifiers):
+		if not self._enabled:
+			return
+		import ColorPicker
+		newcolor, ok = ColorPicker.GetColor("", self._color)
+		if ok:
+			self._color = newcolor
+			self.SetPort()
+			self.draw()
+			if self._callback:
+				return CallbackCall(self._callback, 0, self._color)
+	
+	def set(self, color):
+		self._color = color
+		self.SetPort()
+		self.draw()
+	
+	def get(self):
+		return self._color
+	
+	def draw(self, visRgn=None):
+		if self._visible:
+			if not visRgn:
+				visRgn = self._parentwindow.wid.GetWindowPort().visRgn
+			Qd.PenPat(Qd.qd.gray)
+			rect = self._bounds
+			Qd.FrameRect(rect)
+			rect = Qd.InsetRect(rect, 3, 3)
+			Qd.PenNormal()
+			Qd.RGBForeColor(self._color)
+			Qd.PaintRect(rect)
+			Qd.RGBForeColor((0, 0, 0))
+			
+
+# misc utils
+
+def CallbackCall(callback, mustfit, *args):
+	"""internal helper routine for W"""
+	# XXX this function should die.
+	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,
+	"zoomin"	: Qd.GetCursor(472).data,
+	"zoomout"	: Qd.GetCursor(473).data,
+	"zoom"	: Qd.GetCursor(474).data,
+}
+
+def SetCursor(what):
+	"""Set the cursorshape to any of these: 'arrow', 'cross', 'fist', 'hand', 'hmover', 'iBeam', 
+	'plus', 'vmover', 'watch', 'zoom', 'zoomin', 'zoomout'."""
+	Qd.SetCursor(_cursors[what])