7 from Carbon
import Qd
, Icn
, Fm
, QuickDraw
8 from Carbon
.List
import GetListPort
9 from Carbon
.QuickDraw
import hilitetransfermode
13 closedid
= struct
.pack('h', 468)
14 openid
= struct
.pack('h', 469)
15 closedsolidid
= struct
.pack('h', 470)
16 opensolidid
= struct
.pack('h', 471)
18 arrows
= (nullid
, closedid
, openid
, closedsolidid
, opensolidid
)
20 has_ctlcharsRE
= re
.compile(r
'[\000-\037\177-\377]')
21 def ctlcharsREsearch(str):
22 if has_ctlcharsRE
.search(str) is None:
26 def double_repr(key
, value
, truncvalue
= 0,
27 type = type, StringType
= types
.StringType
,
28 has_ctlchars
= ctlcharsREsearch
, _repr
= repr, str = str):
29 if type(key
) == StringType
and has_ctlchars(key
) < 0:
33 if key
== '__builtins__':
34 value
= "<" + type(value
).__name
__ + " '__builtin__'>"
35 elif key
== '__return__':
36 # bleh, when returning from a class codeblock we get infinite recursion in repr.
37 # Use safe repr instead.
39 value
= repr.repr(value
)
43 '' + value
# test to see if it is a string, in case a __repr__ method is buggy
45 value
= '\xa5\xa5\xa5 exception in repr()'
47 return key
+ '\t' + value
[:255]
48 return key
+ '\t' + value
51 def truncString(s
, maxwid
):
55 strwid
= Qd
.TextWidth(s
, 0, strlen
);
59 Qd
.TextFace(QuickDraw
.condense
)
60 strwid
= Qd
.TextWidth(s
, 0, strlen
)
61 ellipsis
= Qd
.StringWidth('\xc9')
70 mid
= int(strlen
* maxwid
/ strwid
)
75 strwid
= Qd
.TextWidth(s
, 0, mid
) + ellipsis
76 strwid2
= Qd
.TextWidth(s
, 0, mid
+ 1) + ellipsis
77 if strwid
<= maxwid
and maxwid
<= strwid2
:
86 elif strwid2
< maxwid
:
89 return 1, s
[:mid
] + '\xc9'
92 def drawTextCell(text
, cellRect
, ascent
, theList
):
95 Qd
.MoveTo(l
+ 2, t
+ ascent
)
96 condense
, text
= truncString(text
, cellwidth
- 3)
98 Qd
.TextFace(QuickDraw
.condense
)
99 Qd
.DrawText(text
, 0, len(text
))
106 class BrowserWidget(W
.CustomList
):
108 def __init__(self
, possize
, object = None, col
= 100, closechildren
= 0):
109 W
.List
.__init
__(self
, possize
, callback
= self
.listhit
)
110 self
.object = (None,)
112 self
.lastmaxindent
= 0
113 self
.closechildren
= closechildren
117 self
.bind('return', self
.openselection
)
118 self
.bind('enter', self
.openselection
)
119 if object is not None:
122 def set(self
, object):
123 if self
.object[0] is not object:
124 self
.object = object,
125 self
[:] = self
.unpack(object, 0)
126 elif self
._parentwindow
is not None and self
._parentwindow
.wid
:
129 def unpack(self
, object, indent
):
130 return unpack_object(object, indent
)
135 self
.setdrawingmode(0)
136 sel
= self
.getselectedobjects()
137 fold
= self
.getunfoldedobjects()
138 topcell
= self
.gettopcell()
139 self
[:] = self
.unpack(self
.object[0], 0)
140 self
.unfoldobjects(fold
)
141 self
.setselectedobjects(sel
)
142 self
.settopcell(topcell
)
143 self
.setdrawingmode(1)
145 def setcolumn(self
, col
):
147 self
.colstr
= struct
.pack('h', col
)
149 sel
= self
.getselection()
150 self
.setitems(self
.items
)
151 self
.setselection(sel
)
153 def key(self
, char
, event
):
154 if char
in (Wkeys
.leftarrowkey
, Wkeys
.rightarrowkey
):
155 sel
= self
.getselection()
157 self
.setdrawingmode(0)
159 self
.fold(index
, char
== Wkeys
.rightarrowkey
)
160 self
.setdrawingmode(1)
162 W
.List
.key(self
, char
, event
)
164 def rollover(self
, (x
, y
), onoff
):
166 if self
.incolumn((x
, y
)):
167 W
.SetCursor('hmover')
171 def inarrow(self
, (x
, y
)):
172 cl
, ct
, cr
, cb
= self
._list
.LRect((0, 0))
173 l
, t
, r
, b
= self
._bounds
176 index
= (y
- ct
) / cellheight
177 if index
< len(self
.items
):
181 def incolumn(self
, (x
, y
)):
182 l
, t
, r
, b
= self
._list
.LRect((0, 0))
183 abscol
= l
+ self
.col
184 return abs(abscol
- x
) < 3
186 def trackcolumn(self
, (x
, y
)):
187 from Carbon
import Qd
, QuickDraw
, Evt
189 l
, t
, r
, b
= self
._bounds
190 bounds
= l
, t
, r
, b
= l
+ 1, t
+ 1, r
- 16, b
- 1
191 abscol
= l
+ self
.col
192 mincol
= l
+ self
.mincol
195 Qd
.PenPat('\000\377\000\377\000\377\000\377')
196 Qd
.PenMode(QuickDraw
.srcXor
)
197 rect
= abscol
- 1, t
, abscol
, b
203 Evt
.WaitNextEvent(0, 1, None) # needed for OSX
204 (x
, y
) = Evt
.GetMouse()
205 if (x
, y
) <> lastpoint
:
207 newcol
= max(newcol
, mincol
)
208 newcol
= min(newcol
, maxcol
)
210 rect
= newcol
- 1, t
, newcol
, b
214 Qd
.PenPat(Qd
.qd
.black
)
216 if newcol
> 0 and newcol
<> abscol
:
217 self
.setcolumn(newcol
- l
)
219 def click(self
, point
, modifiers
):
220 if point
== (-1, -1): # gross.
221 W
.List
.click(self
, point
,modifiers
)
223 hit
, index
= self
.inarrow(point
)
225 (key
, value
, arrow
, indent
) = self
.items
[index
]
226 self
.fold(index
, arrow
== 1)
227 elif self
.incolumn(point
):
228 self
.trackcolumn(point
)
230 W
.List
.click(self
, point
, modifiers
)
233 def findmatch(self
, tag
):
239 for i
in range(len(items
)):
240 item
= lower(str(items
[i
][0]))
241 if tag
<= item
< match
:
247 return len(items
) - 1
250 if self
.closechildren
:
251 for window
in self
.children
:
256 def fold(self
, index
, onoff
):
257 (key
, value
, arrow
, indent
) = self
.items
[index
]
258 if arrow
== 0 or (onoff
and arrow
== 2) or (not onoff
and arrow
== 1):
261 topcell
= self
.gettopcell()
263 self
[index
] = (key
, value
, 4, indent
)
264 self
.setdrawingmode(0)
265 self
[index
+1:index
+1] = self
.unpack(value
, indent
+ 1)
266 self
[index
] = (key
, value
, 2, indent
)
268 self
[index
] = (key
, value
, 3, indent
)
269 self
.setdrawingmode(0)
271 for i
in range(index
+ 1, len(self
.items
)):
272 (dummy
, dummy
, dummy
, subindent
) = self
.items
[i
]
273 if subindent
<= indent
:
276 self
[index
+1:index
+1+count
] = []
277 self
[index
] = (key
, value
, 1, indent
)
278 maxindent
= self
.getmaxindent()
279 if maxindent
<> self
.lastmaxindent
:
280 newabsindent
= self
.col
+ (maxindent
- self
.lastmaxindent
) * self
.indent
281 if newabsindent
>= self
.mincol
:
282 self
.setcolumn(newabsindent
)
283 self
.lastmaxindent
= maxindent
284 self
.settopcell(topcell
)
285 self
.setdrawingmode(1)
287 def unfoldobjects(self
, objects
):
290 index
= self
.items
.index(obj
)
296 def getunfoldedobjects(self
):
299 for index
in range(len(self
.items
)):
300 (key
, value
, arrow
, indent
) = self
.items
[index
]
301 if indent
> curindent
:
302 (k
, v
, a
, i
) = self
.items
[index
- 1]
303 objects
.append((k
, v
, 1, i
))
305 elif indent
< curindent
:
309 def listhit(self
, isdbl
):
313 def openselection(self
):
315 sel
= self
.getselection()
317 (key
, value
, arrow
, indent
) = self
[index
]
319 self
.children
.append(Browser(value
))
320 elif type(value
) == types
.StringType
and '\0' not in value
:
321 editor
= self
._parentwindow
.parent
.getscript(value
)
325 elif os
.path
.exists(value
) and os
.path
.isfile(value
):
327 fss
= macfs
.FSSpec(value
)
328 if fss
.GetCreatorType()[1] == 'TEXT':
329 W
.getapplication().openscript(value
)
331 def itemrepr(self
, (key
, value
, arrow
, indent
), str = str, double_repr
= double_repr
,
332 arrows
= arrows
, pack
= struct
.pack
):
333 arrow
= arrows
[arrow
]
334 return arrow
+ pack('h', self
.indent
* indent
) + self
.colstr
+ \
335 double_repr(key
, value
, 1)
337 def getmaxindent(self
, max = max):
339 for item
in self
.items
:
340 maxindent
= max(maxindent
, item
[3])
343 def domenu_copy(self
, *args
):
344 sel
= self
.getselectedobjects()
346 for key
, value
, dummy
, dummy
in sel
:
347 selitems
.append(double_repr(key
, value
))
348 text
= string
.join(selitems
, '\r')
350 from Carbon
import Scrap
351 if hasattr(Scrap
, 'PutScrap'):
353 Scrap
.PutScrap('TEXT', text
)
355 Scrap
.ClearCurrentScrap()
356 sc
= Scrap
.GetCurrentScrap()
357 sc
.PutScrapFlavor('TEXT', 0, text
)
359 def listDefDraw(self
, selected
, cellRect
, theCell
,
360 dataOffset
, dataLen
, theList
):
361 self
.myDrawCell(0, selected
, cellRect
, theCell
,
362 dataOffset
, dataLen
, theList
)
364 def listDefHighlight(self
, selected
, cellRect
, theCell
,
365 dataOffset
, dataLen
, theList
):
366 self
.myDrawCell(1, selected
, cellRect
, theCell
,
367 dataOffset
, dataLen
, theList
)
369 def myDrawCell(self
, onlyHilite
, selected
, cellRect
, theCell
,
370 dataOffset
, dataLen
, theList
):
371 savedPort
= Qd
.GetPort()
372 Qd
.SetPort(GetListPort(theList
))
373 savedClip
= Qd
.NewRgn()
374 Qd
.GetClip(savedClip
)
375 Qd
.ClipRect(cellRect
)
376 savedPenState
= Qd
.GetPenState()
379 l
, t
, r
, b
= cellRect
382 Qd
.EraseRect(cellRect
)
384 ascent
, descent
, leading
, size
, hm
= Fm
.FontMetrics()
385 linefeed
= ascent
+ descent
+ leading
388 data
= theList
.LGetCell(dataLen
, theCell
)
389 iconId
, indent
, tab
= struct
.unpack("hhh", data
[:6])
391 key
, value
= data
[6:].split("\t", 1)
393 # bogus data, at least don't crash.
402 theIcon
= Icn
.GetCIcon(iconId
)
406 rect
= (0, 0, 16, 16)
407 rect
= Qd
.OffsetRect(rect
, l
, t
)
408 rect
= Qd
.OffsetRect(rect
, 0, (theList
.cellSize
[1] - (rect
[3] - rect
[1])) / 2)
409 Icn
.PlotCIcon(rect
, theIcon
)
412 cl
, ct
, cr
, cb
= cellRect
413 vl
, vt
, vr
, vb
= self
._viewbounds
414 cl
= vl
+ PICTWIDTH
+ indent
419 drawTextCell(key
, (cl
, ct
, cr
, cb
), ascent
, theList
)
423 drawTextCell(value
, (cl
, ct
, cr
, cb
), ascent
, theList
)
425 # drawTextCell("???", 3, cellRect, ascent, theList)
427 return # we have bogus data
429 # draw nice dotted line
430 l
, t
, r
, b
= cellRect
431 l
= self
._viewbounds
[0] + tab
433 if not (theList
.cellSize
[1] & 0x01) or (t
& 0x01):
434 myPat
= "\xff\x00\xff\x00\xff\x00\xff\x00"
436 myPat
= "\x00\xff\x00\xff\x00\xff\x00\xff"
438 Qd
.PenMode(QuickDraw
.srcCopy
)
439 Qd
.PaintRect((l
, t
, r
, b
))
442 if selected
or onlyHilite
:
443 l
, t
, r
, b
= cellRect
444 l
= self
._viewbounds
[0] + PICTWIDTH
445 r
= self
._viewbounds
[2]
446 Qd
.PenMode(hilitetransfermode
)
447 Qd
.PaintRect((l
, t
, r
, b
))
449 # restore graphics environment
450 Qd
.SetPort(savedPort
)
451 Qd
.SetClip(savedClip
)
452 Qd
.DisposeRgn(savedClip
)
453 Qd
.SetPenState(savedPenState
)
459 def __init__(self
, object = None, title
= None, closechildren
= 0):
460 if hasattr(object, '__name__'):
461 name
= object.__name
__
465 title
= 'Object browser'
467 title
= title
+ ': ' + name
468 self
.w
= w
= W
.Window((300, 400), title
, minsize
= (100, 100))
469 w
.info
= W
.TextBox((18, 8, -70, 15))
470 w
.updatebutton
= W
.BevelButton((-64, 4, 50, 16), 'Update', self
.update
)
471 w
.browser
= BrowserWidget((-1, 24, 1, -14), None)
472 w
.bind('cmdu', w
.updatebutton
.push
)
474 self
.set(object, name
)
480 def set(self
, object, name
= ''):
482 tp
= type(object).__name
__
487 if not name
and hasattr(object, '__name__'):
488 name
= object.__name
__
490 info
= name
+ ': ' + tp
495 info
= info
+ ' (%d element)' % length
497 info
= info
+ ' (%d elements)' % length
498 self
.w
.info
.set(info
)
499 self
.w
.browser
.set(object)
502 self
.w
.browser
.update()
520 def unpack_object(object, indent
= 0):
522 if tp
in SIMPLE_TYPES
and tp
is not types
.NoneType
:
523 raise TypeError, "can't browse simple type: %s" % tp
.__name
__
524 elif tp
== types
.DictionaryType
:
525 return unpack_dict(object, indent
)
526 elif tp
in (types
.TupleType
, types
.ListType
):
527 return unpack_sequence(object, indent
)
528 elif tp
== types
.InstanceType
:
529 return unpack_instance(object, indent
)
530 elif tp
== types
.ClassType
:
531 return unpack_class(object, indent
)
532 elif tp
== types
.ModuleType
:
533 return unpack_dict(object.__dict
__, indent
)
535 return unpack_other(object, indent
)
537 def unpack_sequence(seq
, indent
= 0):
538 items
= map(None, range(len(seq
)), seq
)
539 items
= map(lambda (k
, v
), type = type, simp
= SIMPLE_TYPES
, indent
= indent
:
540 (k
, v
, not type(v
) in simp
, indent
), items
)
543 def unpack_dict(dict, indent
= 0):
545 return pack_items(items
, indent
)
547 def unpack_instance(inst
, indent
= 0):
548 if hasattr(inst
, '__pybrowse_unpack__'):
549 return unpack_object(inst
.__pybrowse
_unpack
__(), indent
)
551 items
= [('__class__', inst
.__class
__)] + inst
.__dict
__.items()
552 return pack_items(items
, indent
)
554 def unpack_class(clss
, indent
= 0):
555 items
= [('__bases__', clss
.__bases
__), ('__name__', clss
.__name
__)] + clss
.__dict
__.items()
556 return pack_items(items
, indent
)
558 def unpack_other(object, indent
= 0):
560 if hasattr(object, '__members__'):
561 attrs
= attrs
+ object.__members
__
562 if hasattr(object, '__methods__'):
563 attrs
= attrs
+ object.__methods
__
564 if hasattr(object, '__dict__'):
565 attrs
= attrs
+ object.__dict
__.keys()
566 if hasattr(object, '__slots__'):
568 attrs
= attrs
+ object.__slots
__
569 if hasattr(object, "__class__") and "__class__" not in attrs
:
570 attrs
.append("__class__")
571 if hasattr(object, "__doc__") and "__doc__" not in attrs
:
572 attrs
.append("__doc__")
575 items
.append((attr
, getattr(object, attr
)))
576 return pack_items(items
, indent
)
578 def pack_items(items
, indent
= 0):
579 items
= map(lambda (k
, v
), type = type, simp
= SIMPLE_TYPES
, indent
= indent
:
580 (k
, v
, not type(v
) in simp
, indent
),
582 return tuple_caselesssort(items
)
584 def caselesssort(alist
):
585 """Return a sorted copy of a list. If there are only strings in the list,
586 it will not consider case"""
589 # turn ['FOO', 'aaBc', 'ABcD'] into [('foo', 'FOO'), ('aabc', 'aaBc'), ('abcd', 'ABcD')], if possible
590 tupledlist
= map(lambda item
, lower
= string
.lower
: (lower(item
), item
), alist
)
592 # at least one element in alist is not a string, proceed the normal way...
598 # turn [('aabc', 'aaBc'), ('abcd', 'ABcD'), ('foo', 'FOO')] into ['aaBc', 'ABcD', 'FOO']
599 return map(lambda x
: x
[1], tupledlist
)
601 def tuple_caselesssort(items
):
603 tupledlist
= map(lambda tuple, lower
= string
.lower
: (lower(tuple[0]), tuple), items
)
604 except (AttributeError, TypeError):
610 return map(lambda (low
, tuple): tuple, tupledlist
)