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