import Wbaseimport Wimport Wkeysimport Scrapimport stringimport Evtimport Eventsimport Qdimport Winimport Fmimport Numericimport operatorimport stringfrom types import *# List Manager selection constants...# c.f. http://developer.apple.com/techpubs/mac/MoreToolbox/MoreToolbox-214.htmllOnlyOne = 128lExtendDrag = 64lNoDisjoint = 32lNoExtend = 16lNoRect = 8lUseSense = 4lNoNilHilite = 2# NewHandle function... this is missing from the Python 1.5.2b1 libs,# so we hack it as follows:def NewHandle(size=0):	import Res	return Res.Resource('.'*size)# little sentry class to make it easy to pick our fontcellfont = Fm.GetFNum("Profont")if not cellfont: cellfont = Fm.GetFNum("Monaco")class SetCellFont:	def __init__(self):		# later: save old font here!				# set preferred font		Qd.TextFont(cellfont)		Qd.TextSize(9)class ArrayEditor:		def __init__(self, data):		self.window = W.Window((500, 250), "Array Editor", minsize = (250, 150), tabbable=0)		self.window.list = self.list = List2D((-1, 20, 1, 1))		self.list.setdata( data )		self.list.ared = self		self.window.editfld = self.editfld = EntryFld( (50,-1,200,22), "" )		self.editfld.ared = self		self.window.open()				def close(self):		self.list.ared = None		self.list = None		self.editfld.ared = None		self.editfld = None		self.window.close()		self.window = None	def setvalue(self, val, drow=0, dcol=0):		"Set the value in the current cell, and move the selection by drow, dcol."		if type(val) == StringType:			try:				if '.' in val or 'e' in val or 'E' in val:					val = string.atof(val)				else:					val = string.atoi(val)			except:				val = 0		self.editfld.select(0)		self.list.select(1)		sel = self.list.getselection()		if sel:			sel = sel[0]			self.list[sel] = val		else:			sel = (0,0)		self.list.setselection( [(sel[0]+drow, sel[1]+dcol)] )EntryFldParent = W.EditTextclass EntryFld(EntryFldParent):	"""A customized EditText field, which connects to an Array Editor."""		def key(self, char, event):		# check for special keys...		(what, message, when, where, modifiers) = event		if modifiers & Events.cmdKey:			if char == '.':				# cancel entry				self.Reset()				elif char == Wkeys.returnkey:			# accept entry, move down			self.ared.setvalue(self.get(), drow=1)					elif char == Wkeys.tabkey:			print "Got tab key!"			# accept entry, move right			self.ared.setvalue(self.get(), dcol=1)		elif char == Wkeys.enterkey:			# accept entry, move right			self.ared.setvalue(self.get())					else:			EntryFldParent.key(self, char, event)		def set(self, data):		self.origData = data		EntryFldParent.set(self,data)			def Reset(self):		"Reset to original data."		self.set(self.origData)		self.selectall()	class List2D(Wbase.SelectableWidget):		"""Two-dimensional list widget, like a spreadsheet."""		LDEF_ID = 0		def __init__(self, possize, data = None, callback = None, 				flags = lExtendDrag + lNoDisjoint):		if data is None:			data = Numeric.zeros((0,0))		self.data = data		Wbase.SelectableWidget.__init__(self, possize)		self._selected = 0		self._enabled = 1		self._list = None		self._cols = self.data.shape[1]		self._callback = callback		self._flags = flags		self.lasttyping = ""		self.lasttime = Evt.TickCount()		self.timelimit = 30		self.setdata(data)		self.drawingmode = 0		self.ared = None		# ArrayEditor object			def open(self):		self.setdrawingmode(0)		self.createlist()		self.setdrawingmode(1)		def createlist(self):		import List		self._calcbounds()		self.SetPort()		fontsentry = SetCellFont()		# create rectangle for drawing the list in -- leave room for borders & scroll bar		viewrect = self._bounds		viewrect = viewrect[0]+1, viewrect[1]+1, viewrect[2]-16, viewrect[3]-16		# create rect describing the extend of the data		datarect = (0,0, self._cols,0)		# cell size		ascent, descent, width, leading = Qd.GetFontInfo()		cSize = (width*11, ascent+descent+leading)		# misc. boolean options		drawIt = 0		hasGrow = 1		scrollHoriz = 1		scrollVert = 1		self._list = List.LNew(viewrect,datarect, cSize, self.LDEF_ID, self._parentwindow.wid,					drawIt, hasGrow, scrollHoriz, scrollVert)		if self.drawingmode:			self._list.LSetDrawingMode(0)		self._list.selFlags = self._flags		self.setdata(self.data)		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]:			# set visRgn to empty, to prevent nasty drawing side effect of LSize()			Qd.RectRgn(self._parentwindow.wid.GetWindowPort().visRgn, (0, 0, 0, 0))			# adjust viewing rect			# list still has the same upper/left coordinates, use LSize			l, t, r, b = self._bounds			width = r - l - 17		# leave room for scroll bars!			height = b - t - 17			self._list.LSize(width, height)			## leave the cell size alone!!!			#l, t, r, b = self._list.LRect((0,0))			#cellheight = b - t			#self._list.LCellSize((width, cellheight))			# reset visRgn			self._parentwindow.wid.CalcVis()		else:			# oh well, since 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):		Wbase.SelectableWidget.close(self)		self._list = None		self._callback = None		self.data = None		def setdata(self, data):		# for now, we'll assume 2D data!		rows = data.shape[0]		cols = data.shape[1]		self.data = data		the_list = self._list		if not self._parent or not self._list:			return		# turn off drawing, and remember our scroll position		self.setdrawingmode(0)		topcell = self.gettopcell()		# delete old data, if any		the_list.LDelRow(0, 1)		# I *think* this deletes all rows, but I'm not sure!		the_list.LDelColumn(0, 1)		# add new rows and columns		the_list.LAddRow(rows, 0)		the_list.LAddColumn(cols-1,0)		# stuff the data into the cells		self_itemrepr = self.itemrepr		set_cell = the_list.LSetCell		for row in range(rows):			for col in range(cols):				set_cell(self_itemrepr(data[row,col]), (col,row))		# restore scroll position and drawing mode		self.settopcell(topcell)		self.setdrawingmode(1)		def click(self, point, modifiers):		if not self._enabled: return		# let the List Manager handle it first		isdoubleclick = self._list.LClick(point, modifiers)		# then, update our entry field...		window = self.ared.window		sel = self.getselection()		if len(sel) == 1:			self.ared.editfld.set( str(self.data[sel[0][0],sel[0][1]]) )		else:			self.ared.editfld.set('')				# finally, do the callback if any		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()		selrows = map(lambda x:x[0],sel)		selcols = map(lambda x:x[1],sel)		maxrows, maxcols = self.data.shape		newselection = []		if char in Wkeys.arrowkeys:			if char == Wkeys.uparrowkey:				# simulate click above top-left cell				target = [min(selrows)-1, min(selcols)]				if target[0] < 0: target[0] = 0			elif char == Wkeys.downarrowkey:				target = [max(selrows)+1, max(selcols)]				if target[0] >= maxrows: target[0] = maxrows			elif char == Wkeys.leftarrowkey:				target = [min(selrows), min(selcols)-1]				if target[1] < 0: target[1] = 0			elif char == Wkeys.rightarrowkey:				target = [max(selrows), max(selcols)+1]				if target[1] >= maxcols: target[1] = maxcols			r = self._list.LRect((target[1],target[0]))			self.click( ((r[0]+r[2])/2, (r[1]+r[3])/2), modifiers )			return				elif char in (Wkeys.deletekey, Wkeys.backspacekey, '\033'):			self.domenu_clear()			return					elif self.ared:			self.ared.editfld.selectall()			self.ared.editfld.select(1)			self.ared.editfld.key(char,event)			return					if modifiers & Events.shiftKey:			sel = [newselection] + sel		else:			sel = [newselection]		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 = []		maxrow = max(map(lambda x:x[0],sel))		minrow = min(map(lambda x:x[0],sel))		maxcol = max(map(lambda x:x[1],sel))		mincol = min(map(lambda x:x[1],sel))		selitems = []		for i in range(minrow,maxrow+1):			selitems.append( ['']*(maxcol-mincol+1) )		for i in sel:			selitems[i[0]-minrow][i[1]-mincol] = str(self.data[i[0],i[1]])		text = string.join(map(lambda x:string.join(x,'\t'),selitems), '\r')		Scrap.ZeroScrap()		Scrap.PutScrap('TEXT', text)		def domenu_clear(self, *args):		sel = self.getselection()		for i in sel:			self.data[i[0],i[1]] = 0			self._list.LSetCell(self.itemrepr(0), (i[1],i[0]))			def can_copy(self, *args):		return len(self.getselection()) <> 0		def can_paste(self, *args):		length, offset = Scrap.GetScrap(NewHandle(), 'TEXT')		return length > 0	def domenu_paste(self, *args):		h = NewHandle()		length, offset = Scrap.GetScrap(h, 'TEXT')		if not length: return		sel = self.getselection()		if not sel: row,col = (0,0)		else: row,col = sel[0]		newsel = []		lines = string.split(h.data, '\r')		for line in lines:			items = string.split(line, '\t')			for i in range(len(items)):				try:					self.data[row,col+i] = eval(items[i])					self._list.LSetCell(self.itemrepr(self.data[row,col+i]), (col+i,row))					newsel.append( (row,col+i) )				except:					pass			row = row+1		self.setselection(newsel)	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 = []		# list of selected items		rows,cols = self.data.shape		point = (0,0)		while 1:			ok, point = self._list.LGetSelect(1, point)			if not ok:				break			# store selection in (row,col) order -- opposite ListMgr's order!			items.append((point[1],point[0]))			point = point[0]+1, point[1]			if point[0] >= cols:				point = 0,point[1]+1		return items		def setselection(self, selection):		# update the edit field		print selection		if len(selection) == 1:			self.ared.editfld.set( str(self.data[selection[0][0],selection[0][1]]) )		else:			self.ared.editfld.set('')		# then do usual List handling stuff		if not self._parent or not self._list:			self._sel = selection			return		print 2		set_sel = self._list.LSetSelect		rows,cols = self.data.shape		for row in range(rows):			for col in range(cols):				if (row,col) in selection:					set_sel(1, (col,row))					print "setting:", (row,col)				else:					set_sel(0, (col,row))		print 3		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 not self._list: return		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 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 "%10s" % str(item)		def __getitem__(self, pos):		return operator.__getitem__(self.data,pos)		def __setitem__(self, pos, item):		if self._parent and self._list:			self._list.LSetCell(self.itemrepr(item), (pos[1],pos[0]))		operator.__setitem__(self.data,pos,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((self.data.shape[1]-1, self.data.shape[0]-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testared = Nonedef test():	global testared	try:		testared.close()		del testared	except: pass		data = Numeric.reshape(Numeric.arange(200),(20,10))	testared = ArrayEditor(data)#test()