diff options
Diffstat (limited to 'Mac/Tools/IDE/PyBrowser.py')
-rw-r--r-- | Mac/Tools/IDE/PyBrowser.py | 440 |
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) + |