home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / MacHacksBug / Python 1.5.2c1 / Mac / Tools / IDE / PyBrowser.py < prev    next >
Encoding:
Python Source  |  2000-06-23  |  11.6 KB  |  441 lines

  1. import W
  2. import Wkeys
  3. import struct
  4. import string
  5. import types
  6. import regex
  7.  
  8. nullid = '\0\0'
  9. closedid = struct.pack('h', 468)
  10. openid = struct.pack('h', 469)
  11. closedsolidid = struct.pack('h', 470)
  12. opensolidid = struct.pack('h', 471)
  13.  
  14. arrows = (nullid, closedid, openid, closedsolidid, opensolidid)
  15.  
  16. has_ctlcharsRE = regex.compile('[\000-\037\177-\377]')
  17.  
  18. def double_repr(key, value, truncvalue = 0, 
  19.             type = type, StringType = types.StringType,
  20.             has_ctlchars = has_ctlcharsRE.search, _repr = repr, str = str):
  21.     if type(key) == StringType and has_ctlchars(key) < 0:
  22.         key = str(key)
  23.     else:
  24.         key = _repr(key)
  25.     if type(value) == StringType and has_ctlchars(value) < 0:
  26.         value = str(value)
  27.     elif key == '__builtins__':
  28.         value = "<" + type(value).__name__ + " '__builtin__'>"
  29.     elif key == '__return__':
  30.         # bleh, when returning from a class codeblock we get infinite recursion in repr. 
  31.         # Use safe repr instead.
  32.         import repr
  33.         value = repr.repr(value)
  34.     else:
  35.         try:
  36.             value = _repr(value)
  37.             '' + value    # test to see if it is a string, in case a __repr__ method is buggy
  38.         except:
  39.             value = '••• exception in repr()'
  40.     if truncvalue:
  41.         return key + '\t' + value[:255]
  42.     return key + '\t' + value
  43.  
  44.  
  45. class BrowserWidget(W.List):
  46.     
  47.     LDEF_ID = 471
  48.     
  49.     def __init__(self, possize, object = None, col = 100, closechildren = 0):
  50.         W.List.__init__(self, possize, callback = self.listhit)
  51.         self.object = (None,)
  52.         self.indent = 16
  53.         self.lastmaxindent = 0
  54.         self.closechildren = closechildren
  55.         self.children = []
  56.         self.mincol = 64
  57.         self.setcolumn(col)
  58.         self.bind('return', self.openselection)
  59.         self.bind('enter', self.openselection)
  60.         if object is not None:
  61.             self.set(object)
  62.     
  63.     def set(self, object):
  64.         if self.object[0] is not object:
  65.             self.object = object,
  66.             self[:] = self.unpack(object, 0)
  67.         elif self._parentwindow is not None and self._parentwindow.wid:
  68.             self.update()
  69.     
  70.     def unpack(self, object, indent):
  71.         return unpack_object(object, indent)
  72.     
  73.     def update(self):
  74.         # for now...
  75.         W.SetCursor('watch')
  76.         self.setdrawingmode(0)
  77.         sel = self.getselectedobjects()
  78.         fold = self.getunfoldedobjects()
  79.         topcell = self.gettopcell()
  80.         self[:] = self.unpack(self.object[0], 0)
  81.         self.unfoldobjects(fold)
  82.         self.setselectedobjects(sel)
  83.         self.settopcell(topcell)
  84.         self.setdrawingmode(1)
  85.     
  86.     def setcolumn(self, col):
  87.         self.col = col
  88.         self.colstr = struct.pack('h', col)
  89.         if self._list:
  90.             sel = self.getselection()
  91.             self.setitems(self.items)
  92.             self.setselection(sel)
  93.     
  94.     def key(self, char, event):
  95.         if char in (Wkeys.leftarrowkey, Wkeys.rightarrowkey):
  96.             sel = self.getselection()
  97.             sel.reverse()
  98.             self.setdrawingmode(0)
  99.             for index in sel:
  100.                 self.fold(index, char == Wkeys.rightarrowkey)
  101.             self.setdrawingmode(1)
  102.         else:
  103.             W.List.key(self, char, event)
  104.     
  105.     def rollover(self, (x, y), onoff):
  106.         if onoff:
  107.             if self.incolumn((x, y)):
  108.                 W.SetCursor('hmover')
  109.             else:
  110.                 W.SetCursor('arrow')
  111.     
  112.     def inarrow(self, (x, y)):
  113.         cl, ct, cr, cb = self._list.LRect((0, 0))
  114.         l, t, r, b = self._bounds
  115.         if (x - cl) < 16:
  116.             cellheight = cb - ct
  117.             index = (y - ct) / cellheight
  118.             if index < len(self.items):
  119.                 return 1, index
  120.         return None, None
  121.     
  122.     def incolumn(self, (x, y)):
  123.         l, t, r, b = self._list.LRect((0, 0))
  124.         abscol = l + self.col
  125.         return abs(abscol - x) < 3
  126.     
  127.     def trackcolumn(self, (x, y)):
  128.         import Qd, QuickDraw, Evt
  129.         self.SetPort()
  130.         l, t, r, b = self._bounds
  131.         bounds = l, t, r, b = l + 1, t + 1, r - 16, b - 1
  132.         abscol = l + self.col
  133.         mincol = l + self.mincol
  134.         maxcol = r - 10
  135.         diff = abscol - x
  136.         Qd.PenPat('\000\377\000\377\000\377\000\377')
  137.         Qd.PenMode(QuickDraw.srcXor)
  138.         rect = abscol - 1, t, abscol, b
  139.         Qd.PaintRect(rect)
  140.         lastpoint = (x, y)
  141.         newcol = -1
  142.         #W.SetCursor('fist')
  143.         while Evt.Button():
  144.             (x, y) = Evt.GetMouse()
  145.             if (x, y) <> lastpoint:
  146.                 newcol = x + diff
  147.                 newcol = max(newcol, mincol)
  148.                 newcol = min(newcol, maxcol)
  149.                 Qd.PaintRect(rect)
  150.                 rect = newcol - 1, t, newcol, b
  151.                 Qd.PaintRect(rect)
  152.                 lastpoint = (x, y)
  153.         Qd.PaintRect(rect)
  154.         Qd.PenPat(Qd.qd.black)
  155.         Qd.PenNormal()
  156.         if newcol > 0 and newcol <> abscol:
  157.             self.setcolumn(newcol - l)
  158.     
  159.     def click(self, point, modifiers):
  160.         if point == (-1, -1):    # gross.
  161.             W.List.click(self, point ,modifiers)
  162.             return
  163.         hit, index = self.inarrow(point)
  164.         if hit:
  165.             (key, value, arrow, indent) = self.items[index]
  166.             self.fold(index, arrow == 1)
  167.         elif self.incolumn(point):
  168.             self.trackcolumn(point)
  169.         else:
  170.             W.List.click(self, point, modifiers)
  171.     
  172.     # for W.List.key
  173.     def findmatch(self, tag):
  174.         lower = string.lower
  175.         items = self.items
  176.         taglen = len(tag)
  177.         match = '\377' * 100
  178.         match_i = -1
  179.         for i in range(len(items)):
  180.             item = lower(str(items[i][0]))
  181.             if tag <= item < match:
  182.                 match = item
  183.                 match_i = i
  184.         if match_i >= 0:
  185.             return match_i
  186.         else:
  187.             return len(items) - 1
  188.     
  189.     def close(self):
  190.         if self.closechildren:
  191.             for window in self.children:
  192.                 window.close()
  193.         self.children = []
  194.         W.List.close(self)
  195.     
  196.     def fold(self, index, onoff):
  197.         (key, value, arrow, indent) = self.items[index]
  198.         if arrow == 0 or (onoff and arrow == 2) or (not onoff and arrow == 1):
  199.             return
  200.         W.SetCursor('watch')
  201.         topcell = self.gettopcell()
  202.         if onoff:
  203.             self[index] = (key, value, 4, indent)
  204.             self.setdrawingmode(0)
  205.             self[index+1:index+1] = self.unpack(value, indent + 1)
  206.             self[index] = (key, value, 2, indent)
  207.         else:
  208.             self[index] = (key, value, 3, indent)
  209.             self.setdrawingmode(0)
  210.             count = 0
  211.             for i in range(index + 1, len(self.items)):
  212.                 (dummy, dummy, dummy, subindent) = self.items[i]
  213.                 if subindent <= indent:
  214.                     break
  215.                 count = count + 1
  216.             self[index+1:index+1+count] = []
  217.             self[index] = (key, value, 1, indent)
  218.         maxindent = self.getmaxindent()
  219.         if maxindent <> self.lastmaxindent:
  220.             newabsindent = self.col + (maxindent - self.lastmaxindent) * self.indent
  221.             if newabsindent >= self.mincol:
  222.                 self.setcolumn(newabsindent)
  223.             self.lastmaxindent = maxindent
  224.         self.settopcell(topcell)
  225.         self.setdrawingmode(1)
  226.     
  227.     def unfoldobjects(self, objects):
  228.         for obj in objects:
  229.             try:
  230.                 index = self.items.index(obj)
  231.             except ValueError:
  232.                 pass
  233.             else:
  234.                 self.fold(index, 1)
  235.     
  236.     def getunfoldedobjects(self):
  237.         curindent = 0
  238.         objects = []
  239.         for index in range(len(self.items)):
  240.             (key, value, arrow, indent) = self.items[index]
  241.             if indent > curindent:
  242.                 (k, v, a, i) = self.items[index - 1]
  243.                 objects.append((k, v, 1, i))
  244.                 curindent = indent
  245.             elif indent < curindent:
  246.                 curindent = indent
  247.         return objects
  248.     
  249.     def listhit(self, isdbl):
  250.         if isdbl:
  251.             self.openselection()
  252.     
  253.     def openselection(self):
  254.         import os
  255.         sel = self.getselection()
  256.         for index in sel:
  257.             (key, value, arrow, indent) = self[index]
  258.             if arrow:
  259.                 self.children.append(Browser(value))
  260.             elif type(value) == types.StringType and '\0' not in value:
  261.                 editor = self._parentwindow.parent.getscript(value)
  262.                 if editor:
  263.                     editor.select()
  264.                     return
  265.                 elif os.path.exists(value) and os.path.isfile(value):
  266.                     import macfs
  267.                     fss = macfs.FSSpec(value)
  268.                     if fss.GetCreatorType()[1] == 'TEXT':
  269.                         W.getapplication().openscript(value)
  270.     
  271.     def itemrepr(self, (key, value, arrow, indent), str = str, double_repr = double_repr, 
  272.             arrows = arrows, pack = struct.pack):
  273.         arrow = arrows[arrow]
  274.         return arrow + pack('h', self.indent * indent) + self.colstr + \
  275.                 double_repr(key, value, 1)
  276.     
  277.     def getmaxindent(self, max = max):
  278.         maxindent = 0
  279.         for item in self.items:
  280.             maxindent = max(maxindent, item[3])
  281.         return maxindent
  282.     
  283.     def domenu_copy(self, *args):
  284.         sel = self.getselectedobjects()
  285.         selitems = []
  286.         for key, value, dummy, dummy in sel:
  287.             selitems.append(double_repr(key, value))
  288.         text = string.join(selitems, '\r')
  289.         if text:
  290.             import Scrap
  291.             Scrap.ZeroScrap()
  292.             Scrap.PutScrap('TEXT', text)
  293.  
  294.  
  295. class Browser:
  296.     
  297.     def __init__(self, object = None, title = None, closechildren = 0):
  298.         if hasattr(object, '__name__'):
  299.             name = object.__name__
  300.         else:
  301.             name = ''
  302.         if title is None:
  303.             title = 'Object browser'
  304.             if name:
  305.                 title = title + ': ' + name
  306.         self.w = w = W.Window((300, 400), title, minsize = (100, 100))
  307.         w.info = W.TextBox((18, 8, -70, 15))
  308.         w.updatebutton = W.Button((-64, 4, 50, 16), 'Update', self.update)
  309.         w.browser = BrowserWidget((-1, 24, 1, -14), None)
  310.         w.bind('cmdu', w.updatebutton.push)
  311.         w.open()
  312.         self.set(object, name)
  313.     
  314.     def close(self):
  315.         if self.w.wid:
  316.             self.w.close()
  317.     
  318.     def set(self, object, name = ''):
  319.         W.SetCursor('watch')
  320.         tp = type(object).__name__
  321.         try:
  322.             length = len(object)
  323.         except:
  324.             length = -1
  325.         if not name and hasattr(object, '__name__'):
  326.             name = object.__name__
  327.         if name:
  328.             info = name + ': ' + tp
  329.         else:
  330.             info = tp
  331.         if length >= 0:
  332.             if length == 1:
  333.                 info = info + ' (%d element)' % length
  334.             else:
  335.                 info = info + ' (%d elements)' % length
  336.         self.w.info.set(info)
  337.         self.w.browser.set(object)
  338.     
  339.     def update(self):
  340.         self.w.browser.update()
  341.  
  342.  
  343. SIMPLE_TYPES = (
  344.     types.NoneType,
  345.     types.IntType,
  346.     types.LongType,
  347.     types.FloatType,
  348.     types.ComplexType,
  349.     types.StringType
  350. )
  351.  
  352. INDEXING_TYPES = (
  353.     types.TupleType,
  354.     types.ListType,
  355.     types.DictionaryType
  356. )
  357.  
  358. def unpack_object(object, indent = 0):
  359.     tp = type(object)
  360.     if tp in SIMPLE_TYPES and tp is not types.NoneType:
  361.         raise TypeError, 'can’t browse simple type: %s' % tp.__name__
  362.     elif tp == types.DictionaryType:
  363.         return unpack_dict(object, indent)
  364.     elif tp in (types.TupleType, types.ListType):
  365.         return unpack_sequence(object, indent)
  366.     elif tp == types.InstanceType:
  367.         return unpack_instance(object, indent)
  368.     elif tp == types.ClassType:
  369.         return unpack_class(object, indent)
  370.     elif tp == types.ModuleType:
  371.         return unpack_dict(object.__dict__, indent)
  372.     else:
  373.         return unpack_other(object, indent)
  374.  
  375. def unpack_sequence(seq, indent = 0):
  376.     items = map(None, range(len(seq)), seq)
  377.     items = map(lambda (k, v), type = type, simp = SIMPLE_TYPES, indent = indent: 
  378.                 (k, v, not type(v) in simp, indent), items)
  379.     return items
  380.  
  381. def unpack_dict(dict, indent = 0):
  382.     items = dict.items()
  383.     return pack_items(items, indent)
  384.  
  385. def unpack_instance(inst, indent = 0):
  386.     if hasattr(inst, '__pybrowse_unpack__'):
  387.         return unpack_object(inst.__pybrowse_unpack__(), indent)
  388.     else:
  389.         items = [('__class__', inst.__class__)] + inst.__dict__.items()
  390.         return pack_items(items, indent)
  391.  
  392. def unpack_class(clss, indent = 0):
  393.     items = [('__bases__', clss.__bases__), ('__name__', clss.__name__)] + clss.__dict__.items()
  394.     return pack_items(items, indent)
  395.  
  396. def unpack_other(object, indent = 0):
  397.     attrs = []
  398.     if hasattr(object, '__members__'):
  399.         attrs = attrs + object.__members__
  400.     if hasattr(object, '__methods__'):
  401.         attrs = attrs + object.__methods__
  402.     items = []
  403.     for attr in attrs:
  404.         items.append((attr, getattr(object, attr)))
  405.     return pack_items(items, indent)
  406.  
  407. def pack_items(items, indent = 0):
  408.     items = map(lambda (k, v), type = type, simp = SIMPLE_TYPES, indent = indent: 
  409.                 (k, v, not type(v) in simp, indent), 
  410.             items)
  411.     return tuple_caselesssort(items)
  412.  
  413. def caselesssort(alist):
  414.     """Return a sorted copy of a list. If there are only strings in the list, 
  415.     it will not consider case"""
  416.     
  417.     try:
  418.         # turn ['FOO',  'aaBc', 'ABcD'] into [('foo', 'FOO'), ('aabc', 'aaBc'), ('abcd', 'ABcD')], if possible
  419.         tupledlist = map(lambda item, lower = string.lower: (lower(item), item), alist)
  420.     except TypeError:
  421.         # at least one element in alist is not a string, proceed the normal way...
  422.         alist = alist[:]
  423.         alist.sort()
  424.         return alist
  425.     else:
  426.         tupledlist.sort()
  427.         # turn [('aabc', 'aaBc'), ('abcd', 'ABcD'), ('foo', 'FOO')] into ['aaBc', 'ABcD', 'FOO']
  428.         return map(lambda x: x[1], tupledlist)
  429.  
  430. def tuple_caselesssort(items):
  431.     try:
  432.         tupledlist = map(lambda tuple, lower = string.lower: (lower(tuple[0]), tuple), items)
  433.     except TypeError:
  434.         items = items[:]
  435.         items.sort()
  436.         return items
  437.     else:
  438.         tupledlist.sort()
  439.         return map(lambda (low, tuple): tuple, tupledlist)
  440.  
  441.