summaryrefslogtreecommitdiffstats
path: root/Mac/Tools/IDE/PyBrowser.py
diff options
context:
space:
mode:
Diffstat (limited to 'Mac/Tools/IDE/PyBrowser.py')
-rw-r--r--Mac/Tools/IDE/PyBrowser.py440
1 files changed, 440 insertions, 0 deletions
diff --git a/Mac/Tools/IDE/PyBrowser.py b/Mac/Tools/IDE/PyBrowser.py
new file mode 100644
index 0000000..d62d256
--- /dev/null
+++ b/Mac/Tools/IDE/PyBrowser.py
@@ -0,0 +1,440 @@
+import W
+import Wkeys
+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] is not 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 (Wkeys.leftarrowkey, Wkeys.rightarrowkey):
+ sel = self.getselection()
+ sel.reverse()
+ self.setdrawingmode(0)
+ for index in sel:
+ self.fold(index, char == Wkeys.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)
+