Fix an amazing number of typos & malformed sentences reported by Detlef
[python/dscho.git] / Mac / Tools / IDE / PyBrowser.py
blob31e6c86b9540d91adaa062e3d0e4599e41ca1fc4
1 import W
2 import Wkeys
3 import struct
4 import string
5 import types
6 import regex
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)
14 arrows = (nullid, closedid, openid, closedsolidid, opensolidid)
16 has_ctlcharsRE = regex.compile('[\000-\037\177-\377]')
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
45 class BrowserWidget(W.List):
47 LDEF_ID = 471
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)
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()
70 def unpack(self, object, indent):
71 return unpack_object(object, indent)
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)
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)
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)
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')
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
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
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)
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)
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
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)
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)
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)
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
249 def listhit(self, isdbl):
250 if isdbl:
251 self.openselection()
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)
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)
277 def getmaxindent(self, max = max):
278 maxindent = 0
279 for item in self.items:
280 maxindent = max(maxindent, item[3])
281 return maxindent
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)
295 class Browser:
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)
314 def close(self):
315 if self.w.wid:
316 self.w.close()
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)
339 def update(self):
340 self.w.browser.update()
343 SIMPLE_TYPES = (
344 types.NoneType,
345 types.IntType,
346 types.LongType,
347 types.FloatType,
348 types.ComplexType,
349 types.StringType
352 INDEXING_TYPES = (
353 types.TupleType,
354 types.ListType,
355 types.DictionaryType
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)
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
381 def unpack_dict(dict, indent = 0):
382 items = dict.items()
383 return pack_items(items, indent)
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)
392 def unpack_class(clss, indent = 0):
393 items = [('__bases__', clss.__bases__), ('__name__', clss.__name__)] + clss.__dict__.items()
394 return pack_items(items, indent)
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)
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)
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"""
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)
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)