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)