diff options
Diffstat (limited to 'Mac/Tools/twit/mactwit_browser.py')
-rw-r--r-- | Mac/Tools/twit/mactwit_browser.py | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/Mac/Tools/twit/mactwit_browser.py b/Mac/Tools/twit/mactwit_browser.py new file mode 100644 index 0000000..26a35b1 --- /dev/null +++ b/Mac/Tools/twit/mactwit_browser.py @@ -0,0 +1,429 @@ +"""A simple Mac-only browse utility to peek at the inner data structures of Python.""" +# Minor modifications by Jack to facilitate incorporation in twit. + +# june 1996 +# Written by Just van Rossum <just@knoware.nl>, please send comments/improvements. +# Loosely based on Jack Jansens's PICTbrowse.py, but depends on his fabulous FrameWork.py +# XXX Some parts are *very* poorly solved. Will fix. Guido has to check if all the +# XXX "python-peeking" is done correctly. I kindof reverse-engineered it ;-) + +# disclaimer: although I happen to be the brother of Python's father, programming is +# not what I've been trained to do. So don't be surprised if you find anything that's not +# as nice as it could be... + +# XXX to do: +# Arrow key support +# Copy & Paste? +# MAIN_TEXT item should not contain (type); should be below or something. +# MAIN_TEXT item should check if a string is binary or not: convert to '/000' style +# or convert newlines. + +version = "1.0" + +import FrameWork +import EasyDialogs +import Dlg +import Res +import Qd +import List +import sys +from Types import * +from QuickDraw import * +import string +import time +import os + +# The initial object to start browsing with. Can be anything, but 'sys' makes kindof sense. +start_object = sys + +# Resource definitions +ID_MAIN = 516 +NUM_LISTS = 4 # the number of lists used. could be changed, but the dlg item numbers should be consistent +MAIN_TITLE = 3 # this is only the first text item, the other three ID's should be 5, 7 and 9 +MAIN_LIST = 4 # this is only the first list, the other three ID's should be 6, 8 and 10 +MAIN_TEXT = 11 +MAIN_LEFT = 1 +MAIN_RIGHT = 2 +MAIN_RESET = 12 +MAIN_CLOSE = 13 +MAIN_LINE = 14 + +def Initialize(): + # this bit ensures that this module will also work as an applet if the resources are + # in the resource fork of the applet + # stolen from Jack, so it should work(?!;-) + try: + # if this doesn't raise an error, we are an applet containing the necessary resources + # so we don't have to bother opening the resource file + dummy = Res.GetResource('DLOG', ID_MAIN) + except Res.Error: + savewd = os.getcwd() + ourparentdir = os.path.split(openresfile.func_code.co_filename)[0] + os.chdir(ourparentdir) + try: + Res.OpenResFile("mactwit_browse.rsrc") + except Res.Error, arg: + EasyDialogs.Message("Cannot open mactwit_browse.rsrc: "+arg[1]) + sys.exit(1) + os.chdir(savewd) + +def main(): + Initialize() + PythonBrowse() + +# this is all there is to it to make an application. +class PythonBrowse(FrameWork.Application): + def __init__(self): + FrameWork.Application.__init__(self) + VarBrowser(self).open(start_object) + self.mainloop() + + def do_about(self, id, item, window, event): + EasyDialogs.Message(self.__class__.__name__ + " version " + version + "\rby Just van Rossum") + + def quit(self, *args): + raise self + +class MyList: + def __init__(self, wid, rect, itemnum): + # wid is the window (dialog) where our list is going to be in + # rect is it's item rectangle (as in dialog item) + # itemnum is the itemnumber in the dialog + self.rect = rect + rect2 = rect[0]+1, rect[1]+1, rect[2]-16, rect[3]-1 # Scroll bar space, that's 15 + 1, Jack! + self.list = List.LNew(rect2, (0, 0, 1, 0), (0,0), 0, wid, + 0, 1, 0, 1) + self.wid = wid + self.active = 0 + self.itemnum = itemnum + + def setcontent(self, content, title = ""): + # first, gather some stuff + keylist = [] + valuelist = [] + thetype = type(content) + if thetype == DictType: + keylist = content.keys() + keylist.sort() + for key in keylist: + valuelist.append(content[key]) + elif thetype == ListType: + keylist = valuelist = content + elif thetype == TupleType: + + keylist = valuelist = [] + for i in content: + keylist.append(i) + else: + # XXX help me! is all this correct? is there more I should consider??? + # XXX is this a sensible way to do it in the first place???? + # XXX I'm not familiar enough with Python's guts to be sure. GUIDOOOOO!!! + if hasattr(content, "__dict__"): + keylist = keylist + content.__dict__.keys() + if hasattr(content, "__methods__"): + keylist = keylist + content.__methods__ + if hasattr(content, "__members__"): + keylist = keylist + content.__members__ + if hasattr(content, "__class__"): + keylist.append("__class__") + if hasattr(content, "__bases__"): + keylist.append("__bases__") + if hasattr(content, "__name__"): + title = content.__name__ + if "__name__" not in keylist: + keylist.append("__name__") + keylist.sort() + for key in keylist: + valuelist.append(getattr(content, key)) + if content <> None: + title = title + "\r" + cleantype(content) + # now make that list! + tp, h, rect = self.wid.GetDialogItem(self.itemnum - 1) + Dlg.SetDialogItemText(h, title[:255]) + self.list.LDelRow(0, 1) + self.list.LSetDrawingMode(0) + self.list.LAddRow(len(keylist), 0) + for i in range(len(keylist)): + self.list.LSetCell(str(keylist[i]), (0, i)) + self.list.LSetDrawingMode(1) + self.list.LUpdate(self.wid.GetWindowPort().visRgn) + self.content = content + self.keylist = keylist + self.valuelist = valuelist + self.title = title + + # draw a frame around the list, List Manager doesn't do that + def drawframe(self): + Qd.SetPort(self.wid) + Qd.FrameRect(self.rect) + rect2 = Qd.InsetRect(self.rect, -3, -3) + save = Qd.GetPenState() + Qd.PenSize(2, 2) + if self.active: + Qd.PenPat(Qd.qd.black) + else: + Qd.PenPat(Qd.qd.white) + # draw (or erase) an extra frame to indicate this is the acive list (or not) + Qd.FrameRect(rect2) + Qd.SetPenState(save) + + + +class VarBrowser(FrameWork.DialogWindow): + def open(self, start_object, title = ""): + FrameWork.DialogWindow.open(self, ID_MAIN) + if title <> "": + windowtitle = self.wid.GetWTitle() + self.wid.SetWTitle(windowtitle + " >> " + title) + else: + if hasattr(start_object, "__name__"): + windowtitle = self.wid.GetWTitle() + self.wid.SetWTitle(windowtitle + " >> " + str(getattr(start_object, "__name__")) ) + + self.SetPort() + Qd.TextFont(3) + Qd.TextSize(9) + self.lists = [] + self.listitems = [] + for i in range(NUM_LISTS): + self.listitems.append(MAIN_LIST + 2 * i) # dlg item numbers... have to be consistent + for i in self.listitems: + tp, h, rect = self.wid.GetDialogItem(i) + list = MyList(self.wid, rect, i) + self.lists.append(list) + self.leftover = [] + self.rightover = [] + self.setup(start_object, title) + + def close(self): + self.lists = [] + self.listitems = [] + self.do_postclose() + + def setup(self, start_object, title = ""): + # here we set the starting point for our expedition + self.start = start_object + self.lists[0].setcontent(start_object, title) + for list in self.lists[1:]: + list.setcontent(None) + + def do_listhit(self, event, item): + (what, message, when, where, modifiers) = event + Qd.SetPort(self.wid) + where = Qd.GlobalToLocal(where) + for list in self.lists: + list.active = 0 + list = self.lists[self.listitems.index(item)] + list.active = 1 + for l in self.lists: + l.drawframe() + + point = (0,0) + ok, point = list.list.LGetSelect(1, point) + if ok: + oldsel = point[1] + else: + oldsel = -1 + # This should be: list.list.LClick(where, modifiers) + # Since the selFlags field of the list is not accessible from Python I have to do it like this. + # The effect is that you can't select more items by using shift or command. + list.list.LClick(where, 0) + + index = self.listitems.index(item) + 1 + point = (0,0) + ok, point = list.list.LGetSelect(1, point) + if oldsel == point[1]: + return # selection didn't change, do nothing. + if not ok: + for i in range(index, len(self.listitems)): + self.lists[i].setcontent(None) + self.rightover = [] + return + + if point[1] >= len(list.keylist): + return # XXX is this still necessary? is ok really true? + key = str(list.keylist[point[1]]) + value = list.valuelist[point[1]] + + self.settextitem("") + thetype = type(value) + if thetype == ListType or \ + thetype == TupleType or \ + thetype == DictType or \ + hasattr(value, "__dict__") or \ + hasattr(value, "__methods__") or \ + hasattr(value, "__members__"): # XXX or, or... again: did I miss something? + if index >= len(self.listitems): + # we've reached the right side of our dialog. move everything to the left + # (by pushing the rightbutton...) + self.do_rightbutton(1) + index = index - 1 + newlist = self.lists[index] + newlist.setcontent(value, key) + else: + index = index - 1 + self.settextitem( str(value) + "\r" + cleantype(value)) + for i in range(index + 1, len(self.listitems)): + self.lists[i].setcontent(None) + self.rightover = [] + + # helper to set the big text item at the bottom of the dialog. + def settextitem(self, text): + tp, h, rect = self.wid.GetDialogItem(MAIN_TEXT) + Dlg.SetDialogItemText(h, text[:255]) + + def do_rawupdate(self, window, event): + Qd.SetPort(self.wid) + iType, iHandle, iRect = window.GetDialogItem(MAIN_LINE) + Qd.FrameRect(iRect) + for list in self.lists: + Qd.FrameRect(list.rect) + if list.active: + # see MyList.drawframe + rect2 = Qd.InsetRect(list.rect, -3, -3) + save = Qd.GetPenState() + Qd.PenSize(2, 2) + Qd.FrameRect(rect2) + Qd.SetPenState(save) + for list in self.lists: + list.list.LUpdate(self.wid.GetWindowPort().visRgn) + + def do_activate(self, activate, event): + for list in self.lists: + list.list.LActivate(activate) + + # scroll everything one 'unit' to the left + # XXX I don't like the way this works. Too many 'manual' assignments + def do_rightbutton(self, force = 0): + if not force and self.rightover == []: + return + self.scroll(-1) + point = (0, 0) + ok, point = self.lists[0].list.LGetSelect(1, point) + self.leftover.append(point, self.lists[0].content, self.lists[0].title, self.lists[0].active) + for i in range(len(self.lists)-1): + point = (0, 0) + ok, point = self.lists[i+1].list.LGetSelect(1, point) + self.lists[i].setcontent(self.lists[i+1].content, self.lists[i+1].title) + self.lists[i].list.LSetSelect(ok, point) + self.lists[i].list.LAutoScroll() + self.lists[i].active = self.lists[i+1].active + self.lists[i].drawframe() + if len(self.rightover) > 0: + point, content, title, active = self.rightover[-1] + self.lists[-1].setcontent(content, title) + self.lists[-1].list.LSetSelect(1, point) + self.lists[-1].list.LAutoScroll() + self.lists[-1].active = active + self.lists[-1].drawframe() + del self.rightover[-1] + else: + self.lists[-1].setcontent(None) + self.lists[-1].active = 0 + for list in self.lists: + list.drawframe() + + # scroll everything one 'unit' to the right + def do_leftbutton(self): + if self.leftover == []: + return + self.scroll(1) + if self.lists[-1].content <> None: + point = (0, 0) + ok, point = self.lists[-1].list.LGetSelect(1, point) + self.rightover.append(point, self.lists[-1].content, self.lists[-1].title, self.lists[-1].active ) + for i in range(len(self.lists)-1, 0, -1): + point = (0, 0) + ok, point = self.lists[i-1].list.LGetSelect(1, point) + self.lists[i].setcontent(self.lists[i-1].content, self.lists[i-1].title) + self.lists[i].list.LSetSelect(ok, point) + self.lists[i].list.LAutoScroll() + self.lists[i].active = self.lists[i-1].active + self.lists[i].drawframe() + if len(self.leftover) > 0: + point, content, title, active = self.leftover[-1] + self.lists[0].setcontent(content, title) + self.lists[0].list.LSetSelect(1, point) + self.lists[0].list.LAutoScroll() + self.lists[0].active = active + self.lists[0].drawframe() + del self.leftover[-1] + else: + self.lists[0].setcontent(None) + self.lists[0].active = 0 + + # create some visual feedback when 'scrolling' the lists to the left or to the right + def scroll(self, leftright): # leftright should be 1 or -1 + # first, build a region containing all list rectangles + myregion = Qd.NewRgn() + mylastregion = Qd.NewRgn() + for list in self.lists: + AddRect2Rgn(list.rect, myregion) + AddRect2Rgn(list.rect, mylastregion) + # set the pen, but save it's state first + self.SetPort() + save = Qd.GetPenState() + Qd.PenPat(Qd.qd.gray) + Qd.PenMode(srcXor) + # how far do we have to scroll? + distance = self.lists[1].rect[0] - self.lists[0].rect[0] + step = 30 + lasttime = time.clock() # for delay + # do it + for i in range(0, distance, step): + if i <> 0: + Qd.FrameRgn(mylastregion) # erase last region + Qd.OffsetRgn(mylastregion, step * leftright, 0) + # draw gray region + Qd.FrameRgn(myregion) + Qd.OffsetRgn(myregion, step * leftright, 0) + while time.clock() - lasttime < 0.05: + pass # delay + lasttime = time.clock() + # clean up after your dog + Qd.FrameRgn(mylastregion) + Qd.SetPenState(save) + + def reset(self): + for list in self.lists: + point = (0,0) + ok, point = list.list.LGetSelect(1, point) + if ok: + sel = list.keylist[point[1]] + list.setcontent(list.content, list.title) + if ok: + list.list.LSetSelect(1, (0, list.keylist.index(sel))) + list.list.LAutoScroll() + + def do_itemhit(self, item, event): + if item in self.listitems: + self.do_listhit(event, item) + elif item == MAIN_LEFT: + self.do_leftbutton() + elif item == MAIN_RIGHT: + self.do_rightbutton() + elif item == MAIN_CLOSE: + self.close() + elif item == MAIN_RESET: + self.reset() + +# helper function that returns a short string containing the type of an arbitrary object +# eg: cleantype("wat is dit nu weer?") -> '(string)' +def cleantype(obj): + # type() typically returns something like: <type 'string'> + items = string.split(str(type(obj)), "'") + if len(items) == 3: + return '(' + items[1] + ')' + else: + # just in case, I don't know. + return str(type(obj)) + +# helper for VarBrowser.scroll +def AddRect2Rgn(theRect, theRgn): + rRgn = Qd.NewRgn() + Qd.RectRgn(rRgn, theRect) + Qd.UnionRgn(rRgn, theRgn, theRgn) + + +if __name__ == "__main__": + main() |