]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
First Checked In.
authorJust van Rossum <just@lettererror.com>
Sat, 30 Jan 1999 22:39:17 +0000 (22:39 +0000)
committerJust van Rossum <just@lettererror.com>
Sat, 30 Jan 1999 22:39:17 +0000 (22:39 +0000)
28 files changed:
Mac/Tools/IDE/BuildIDE.py [new file with mode: 0644]
Mac/Tools/IDE/FontSettings.py [new file with mode: 0644]
Mac/Tools/IDE/MacPrefs.py [new file with mode: 0644]
Mac/Tools/IDE/ModuleBrowser.py [new file with mode: 0644]
Mac/Tools/IDE/ProfileBrowser.py [new file with mode: 0644]
Mac/Tools/IDE/PyBrowser.py [new file with mode: 0644]
Mac/Tools/IDE/PyConsole.py [new file with mode: 0644]
Mac/Tools/IDE/PyDebugger.py [new file with mode: 0644]
Mac/Tools/IDE/PyDocSearch.py [new file with mode: 0644]
Mac/Tools/IDE/PyEdit.py [new file with mode: 0644]
Mac/Tools/IDE/PyFontify.py [new file with mode: 0644]
Mac/Tools/IDE/PyInteractive.py [new file with mode: 0644]
Mac/Tools/IDE/PythonIDE.py [new file with mode: 0644]
Mac/Tools/IDE/PythonIDE.rsrc [new file with mode: 0644]
Mac/Tools/IDE/PythonIDEMain.py [new file with mode: 0644]
Mac/Tools/IDE/Splash.py [new file with mode: 0644]
Mac/Tools/IDE/W.py [new file with mode: 0644]
Mac/Tools/IDE/Wapplication.py [new file with mode: 0644]
Mac/Tools/IDE/Wbase.py [new file with mode: 0644]
Mac/Tools/IDE/Wcontrols.py [new file with mode: 0644]
Mac/Tools/IDE/Widgets.rsrc [new file with mode: 0644]
Mac/Tools/IDE/Wkeys.py [new file with mode: 0644]
Mac/Tools/IDE/Wlists.py [new file with mode: 0644]
Mac/Tools/IDE/Wmenus.py [new file with mode: 0644]
Mac/Tools/IDE/Wquicktime.py [new file with mode: 0644]
Mac/Tools/IDE/Wtext.py [new file with mode: 0644]
Mac/Tools/IDE/Wtraceback.py [new file with mode: 0644]
Mac/Tools/IDE/Wwindows.py [new file with mode: 0644]

diff --git a/Mac/Tools/IDE/BuildIDE.py b/Mac/Tools/IDE/BuildIDE.py
new file mode 100644 (file)
index 0000000..ad6a4ea
--- /dev/null
@@ -0,0 +1,32 @@
+import sys
+import os
+import buildtools
+import Res
+import py_resource
+
+buildtools.DEBUG=1
+
+template = buildtools.findtemplate()
+
+ide_home = os.path.join(sys.exec_prefix, ":Mac:Tools:IDE")
+
+mainfilename = os.path.join(ide_home, "PythonIDE.py")
+dstfilename = os.path.join(sys.exec_prefix, "Python IDE")
+
+buildtools.process(template, mainfilename, dstfilename, 1)
+
+targetref = Res.OpenResFile(dstfilename)
+Res.UseResFile(targetref)
+
+files = os.listdir(ide_home)
+
+files = filter(lambda x: x[-3:] == '.py' and x not in ("BuildIDE.py", "PythonIDE.py"), files)
+
+for name in files:
+       print "adding", name
+       fullpath = os.path.join(ide_home, name)
+       id, name = py_resource.frompyfile(fullpath, name[:-3], preload=1,
+               ispackage=0)
+
+wresref = Res.OpenResFile(os.path.join(ide_home, "Widgets.rsrc"))
+buildtools.copyres(wresref, targetref, [], 0)
diff --git a/Mac/Tools/IDE/FontSettings.py b/Mac/Tools/IDE/FontSettings.py
new file mode 100644 (file)
index 0000000..09886ae
--- /dev/null
@@ -0,0 +1,223 @@
+"""usage: 
+newsettings = FontDialog(('Chicago', 0, 12, (0, 0, 0)))        # font name or id, style flags, size, color (color is ignored)
+if newsettings:
+       fontsettings, tabsettings = newsettings
+       font, style, size, color = fontsettings # 'font' is always the font name, not the id number
+       # do something
+"""
+
+import W
+import PyEdit
+import TextEdit
+import Qd
+import string
+import types
+import sys
+import MacOS
+if hasattr(MacOS, "SysBeep"):
+       SysBeep = MacOS.SysBeep
+else:
+       def SysBeep(*args):
+               pass
+
+_stylenames = ["Plain", "Bold", "Italic", "Underline", "Outline", "Shadow", "Condensed", "Extended"]
+
+
+class _FontDialog:
+       
+       #def __del__(self):
+       #       print "doei!"
+       
+       def __init__(self, fontsettings, tabsettings):
+               leftmargin = 60
+               leftmargin2 = leftmargin - 16
+               self.w = W.ModalDialog((440, 180), 'Font settings')
+               self.w.fonttitle = W.TextBox((10, 12, leftmargin2, 14), "Font:", TextEdit.teJustRight)
+               self.w.pop = W.FontMenu((leftmargin, 10, 16, 16), self.setfont)
+               self.w.fontname = W.TextBox((leftmargin + 20, 12, 150, 14))
+               self.w.sizetitle = W.TextBox((10, 38, leftmargin2, 14), "Size:", TextEdit.teJustRight)
+               self.w.sizeedit = W.EditText((leftmargin, 35, 40, 20), "", self.checksize)
+               styletop = 64
+               self.w.styletitle = W.TextBox((10, styletop + 2, leftmargin2, 14), "Style:", TextEdit.teJustRight)
+               for i in range(len(_stylenames)):
+                       top = styletop + (i % 4) * 20
+                       left = leftmargin + 80 * (i > 3) - 2
+                       if i:
+                               self.w[i] = W.CheckBox((left, top, 76, 16), _stylenames[i], self.dostyle)
+                       else:
+                               self.w[i] = W.CheckBox((left, top, 70, 16), _stylenames[i], self.doplain)
+               
+               if tabsettings:
+                       self.lasttab, self.tabmode = tabsettings
+                       self.w.tabsizetitle = W.TextBox((10, -26, leftmargin2, 14), "Tabsize:", TextEdit.teJustRight)
+                       self.w.tabsizeedit = W.EditText((leftmargin, -29, 40, 20), "", self.checktab)
+                       self.w.tabsizeedit.set(`self.lasttab`)
+                       radiobuttons = []
+                       self.w.tabsizechars = W.RadioButton((leftmargin + 48, -26, 55, 14), "Spaces", 
+                                       radiobuttons, self.toggletabmode)
+                       self.w.tabsizepixels = W.RadioButton((leftmargin + 110, -26, 55, 14), "Pixels", 
+                                       radiobuttons, self.toggletabmode)
+                       if self.tabmode:
+                               self.w.tabsizechars.set(1)
+                       else:
+                               self.w.tabsizepixels.set(1)
+               else:
+                       self.tabmode = None
+               
+               self.w.cancelbutton = W.Button((-180, -26, 80, 16), "Cancel", self.cancel)
+               self.w.donebutton = W.Button((-90, -26, 80, 16), "Done", self.done)
+               
+               sampletext = "Sample text."
+               self.w.sample = W.EditText((230, 10, -10, 130), sampletext, 
+                               fontsettings = fontsettings, tabsettings = tabsettings)
+               
+               self.w.setdefaultbutton(self.w.donebutton)
+               self.w.bind('cmd.', self.w.cancelbutton.push)
+               self.w.bind('cmdw', self.w.donebutton.push)
+               self.lastsize = fontsettings[2]
+               self._rv = None
+               self.set(fontsettings)
+               self.w.open()
+       
+       def toggletabmode(self, onoff):
+               if self.w.tabsizechars.get():
+                       tabmode = 1
+               else:
+                       tabmode = 0
+               if self.tabmode <> tabmode:
+                       port = self.w.wid.GetWindowPort()
+                       (font, style, size, color), (tabsize, dummy) = self.get()
+                       savesettings = W.GetPortFontSettings(port)
+                       W.SetPortFontSettings(port, (font, style, size))
+                       spacewidth = Qd.StringWidth(' ')
+                       W.SetPortFontSettings(port, savesettings)
+                       if tabmode:
+                               # convert pixels to spaces
+                               self.lasttab = int(round(float(tabsize) / spacewidth))
+                       else:
+                               # convert spaces to pixels
+                               self.lasttab = spacewidth * tabsize
+                       self.w.tabsizeedit.set(`self.lasttab`)
+                       self.tabmode = tabmode
+                       self.doit()
+       
+       def set(self, fontsettings):
+               font, style, size, color = fontsettings
+               if type(font) <> types.StringType:
+                       import Res
+                       res = Res.GetResource('FOND', font)
+                       font = res.GetResInfo()[2]
+               self.w.fontname.set(font)
+               self.w.sizeedit.set(str(size))
+               if style:
+                       for i in range(1, len(_stylenames)):
+                               self.w[i].set(style & 0x01)
+                               style = style >> 1
+               else:
+                       self.w[0].set(1)
+       
+       def get(self):
+               font = self.w.fontname.get()
+               style = 0
+               if not self.w[0].get():
+                       flag = 0x01
+                       for i in range(1, len(_stylenames)):
+                               if self.w[i].get():
+                                       style = style | flag
+                               flag = flag << 1
+               size = self.lastsize
+               if self.tabmode is None:
+                       return (font, style, size, (0, 0, 0)), (32, 0)
+               else:
+                       return (font, style, size, (0, 0, 0)), (self.lasttab, self.tabmode)
+       
+       def doit(self):
+               if self.w[0].get():
+                       style = 0
+               else:
+                       style = 0
+                       for i in range(1, len(_stylenames)):
+                               if self.w[i].get():
+                                       style = style | 2 ** (i - 1)
+               #self.w.sample.set(`style`)
+               fontsettings, tabsettings = self.get()
+               self.w.sample.setfontsettings(fontsettings)
+               self.w.sample.settabsettings(tabsettings)
+       
+       def checktab(self):
+               tabsize = self.w.tabsizeedit.get()
+               if not tabsize:
+                       return
+               try:
+                       tabsize = string.atoi(tabsize)
+               except (ValueError, OverflowError):
+                       good = 0
+                       sys.exc_traceback = None
+               else:
+                       good = 1 <= tabsize <= 500
+               if good:
+                       if self.lasttab <> tabsize:
+                               self.lasttab = tabsize
+                               self.doit()
+               else:
+                       SysBeep(0)
+                       self.w.tabsizeedit.set(`self.lasttab`)
+                       self.w.tabsizeedit.selectall()
+       
+       def checksize(self):
+               size = self.w.sizeedit.get()
+               if not size:
+                       return
+               try:
+                       size = string.atoi(size)
+               except (ValueError, OverflowError):
+                       good = 0
+                       sys.exc_traceback = None
+               else:
+                       good = 1 <= size <= 500
+               if good:
+                       if self.lastsize <> size:
+                               self.lastsize = size
+                               self.doit()
+               else:
+                       SysBeep(0)
+                       self.w.sizeedit.set(`self.lastsize`)
+                       self.w.sizeedit.selectall()
+       
+       def doplain(self):
+               for i in range(1, len(_stylenames)):
+                       self.w[i].set(0)
+               self.w[0].set(1)
+               self.doit()
+       
+       def dostyle(self):
+               for i in range(1, len(_stylenames)):
+                       if self.w[i].get():
+                               self.w[0].set(0)
+                               break
+               else:
+                       self.w[0].set(1)
+               self.doit()
+       
+       def close(self):
+               self.w.close()
+               del self.w
+       
+       def cancel(self):
+               self.close()
+       
+       def done(self):
+               self._rv = self.get()
+               self.close()
+       
+       def setfont(self, fontname):
+               self.w.fontname.set(fontname)
+               self.doit()
+       
+
+def FontDialog(fontsettings, tabsettings = (32, 0)):
+       fd = _FontDialog(fontsettings, tabsettings)
+       return fd._rv
+
+def test():
+       print FontDialog(('Zapata-Light', 0, 25, (0, 0, 0)))
diff --git a/Mac/Tools/IDE/MacPrefs.py b/Mac/Tools/IDE/MacPrefs.py
new file mode 100644 (file)
index 0000000..8a442ea
--- /dev/null
@@ -0,0 +1,108 @@
+import macfs
+import marshal
+import types
+
+kOnSystemDisk = 0x8000
+
+
+class PrefObject:
+       
+       def __init__(self, dict = None):
+               if dict == None:
+                       self._prefsdict = {}
+               else:
+                       self._prefsdict = dict
+       
+       def __len__(self):
+               return len(self._prefsdict)
+       
+       def __delattr__(self, attr):
+               if self._prefsdict.has_key(attr):
+                       del self._prefsdict[attr]
+               else:
+                       raise AttributeError, 'delete non-existing instance attribute'
+       
+       def __getattr__(self, attr):
+               if attr == '__members__':
+                       keys = self._prefsdict.keys()
+                       keys.sort()
+                       return keys
+               try:
+                       return self._prefsdict[attr]
+               except KeyError:
+                       raise AttributeError, attr
+       
+       def __setattr__(self, attr, value):
+               if attr[0] <> '_':
+                       self._prefsdict[attr] = value
+               else:
+                       self.__dict__[attr] = value
+       
+       def getprefsdict(self):
+               return self._prefsdict
+
+
+class PrefFile(PrefObject):
+       
+       def __init__(self, path, creator = 'Pyth'):
+               # Find the preferences folder and our prefs file, create if needed.
+               self.__path = path
+               self.__creator = creator
+               self._prefsdict = {}
+               try:
+                       prefdict = marshal.load(open(self.__path, 'rb'))
+               except IOError:
+                       pass
+               else:
+                       for key, value in prefdict.items():
+                               if type(value) == types.DictType:
+                                       self._prefsdict[key] = PrefObject(value)
+                               else:
+                                       self._prefsdict[key] = value
+       
+       def save(self):
+               prefdict = {}
+               for key, value in self._prefsdict.items():
+                       if type(value) == types.InstanceType:
+                               prefdict[key] = value.getprefsdict()
+                               if not prefdict[key]:
+                                       del prefdict[key]
+                       else:
+                               prefdict[key] = value
+               marshal.dump(prefdict, open(self.__path, 'wb'))
+               fss = macfs.FSSpec(self.__path)
+               fss.SetCreatorType(self.__creator, 'pref')
+       
+       def __getattr__(self, attr):
+               if attr == '__members__':
+                       keys = self._prefsdict.keys()
+                       keys.sort()
+                       return keys
+               try:
+                       return self._prefsdict[attr]
+               except KeyError:
+                       if attr[0] <> '_':
+                               self._prefsdict[attr] = PrefObject()
+                               return self._prefsdict[attr]
+                       else:
+                               raise AttributeError, attr
+
+
+_prefscache = {}
+
+def GetPrefs(prefname, creator = 'Pyth'):
+       import macostools, os
+       if _prefscache.has_key(prefname):
+               return _prefscache[prefname]
+       # Find the preferences folder and our prefs file, create if needed.
+       vrefnum, dirid = macfs.FindFolder(kOnSystemDisk, 'pref', 0)
+       prefsfolder_fss = macfs.FSSpec((vrefnum, dirid, ''))
+       prefsfolder = prefsfolder_fss.as_pathname()
+       path = os.path.join(prefsfolder, prefname)
+       head, tail = os.path.split(path)
+       # make sure the folder(s) exist
+       macostools.mkdirs(head)
+       
+       preffile = PrefFile(path, creator)
+       _prefscache[prefname] = preffile
+       return preffile
diff --git a/Mac/Tools/IDE/ModuleBrowser.py b/Mac/Tools/IDE/ModuleBrowser.py
new file mode 100644 (file)
index 0000000..d515901
--- /dev/null
@@ -0,0 +1,178 @@
+import W
+import sys
+import Qd
+
+__version__ = "0.2"
+__author__ = "jvr"
+
+class _modulebrowser:
+       
+       def __init__(self):
+               self.editmodules = []
+               self.modules = []
+               self.window = W.Window((194, 1000), "Module Browser", minsize = (194, 160), maxsize = (340, 20000))
+               
+               #self.window.bevelbox = W.BevelBox((0, 0, 0, 56))
+               self.window.openbutton = W.Button((10, 8, 80, 16), "Open", self.openbuttonhit)
+               self.window.browsebutton = W.Button((100, 8, 80, 16), "BrowseÉ", self.browsebuttonhit)
+               self.window.reloadbutton = W.Button((10, 32, 80, 16), "Reload", self.reloadbuttonhit)
+               self.window.openotherbutton = W.Button((100, 32, 80, 16), "Open otherÉ", self.openother)
+               
+               self.window.openbutton.enable(0)
+               self.window.reloadbutton.enable(0)
+               self.window.browsebutton.enable(0)
+               self.window.setdefaultbutton(self.window.browsebutton)
+               
+               self.window.bind("cmdr", self.window.reloadbutton.push)
+               self.window.bind("cmdb", self.window.browsebutton.push)
+       
+               self.window.bind("<activate>", self.activate)
+               self.window.bind("<close>", self.close)
+               
+               self.window.list = W.List((-1, 56, 1, -14), [], self.listhit)
+               
+               self.window.open()
+               self.checkbuttons()
+       
+       def close(self):
+               global _browser
+               _browser = None
+       
+       def activate(self, onoff):
+               if onoff:
+                       self.makelist()
+               
+       def listhit(self, isdbl):
+               self.checkbuttons()
+               if isdbl:
+                       if self.window._defaultbutton:
+                               self.window._defaultbutton.push()
+       
+       def checkbuttons(self):
+               sel = self.window.list.getselection()
+               if sel:
+                       for i in sel:
+                               if self.editmodules[i]:
+                                       self.window.openbutton.enable(1)
+                                       self.window.reloadbutton.enable(1)
+                                       self.window.setdefaultbutton(self.window.openbutton)
+                                       break
+                       else:
+                               self.window.openbutton.enable(0)
+                               self.window.reloadbutton.enable(0)
+                               self.window.setdefaultbutton(self.window.browsebutton)
+                       self.window.browsebutton.enable(1)
+               else:
+                       #self.window.setdefaultbutton(self.window.browsebutton)
+                       self.window.openbutton.enable(0)
+                       self.window.reloadbutton.enable(0)
+                       self.window.browsebutton.enable(0)
+       
+       def openbuttonhit(self):
+               import imp
+               sel = self.window.list.getselection()
+               W.SetCursor("watch")
+               for i in sel:
+                       modname = self.window.list[i]
+                       try:
+                               self.openscript(sys.modules[modname].__file__, modname)
+                       except IOError:
+                               try:
+                                       file, path, description = imp.find_module(modname)
+                               except ImportError:
+                                       W.SetCursor("arrow")
+                                       W.Message("CanĂ•t find file for module Ă’%sĂ“." 
+                                                       % modname)
+                               else:
+                                       self.openscript(path, modname)                                  
+       
+       def openscript(self, path, modname):
+               import os
+               if path[-3:] == '.py':
+                       W.getapplication().openscript(path, modname=modname)
+               elif path[-4:] in ['.pyc', '.pyo']:
+                       W.getapplication().openscript(path[:-1], modname=modname)
+               else:
+                       W.Message("CanĂ•t edit Ă’%sĂ“; it might be a shared library or a .pyc file." 
+                                       % modname)
+       
+       def openother(self):
+               import imp
+               import EasyDialogs
+               
+               modname = EasyDialogs.AskString("Open module:")
+               if modname:
+                       try:
+                               file, path, description = imp.find_module(modname)
+                       except ImportError:
+                               if modname in sys.builtin_module_names:
+                                       alerttext = "Ă’%sĂ“ is a builtin module, which you canĂ•t edit." % modname
+                               else:
+                                       alerttext = "No module named Ă’%sĂ“." % modname
+                               raise W.AlertError, alerttext
+                       self.openscript(path, modname)
+       
+       def reloadbuttonhit(self):
+               sel = self.window.list.getselection()
+               W.SetCursor("watch")
+               for i in sel:
+                       mname = self.window.list[i]
+                       m = sys.modules[mname]
+                       # Set the __name__ attribute of the module to its real name.
+                       # reload() complains if it's __main__, which is true
+                       # when it recently has been run as a script with "Run as __main__" 
+                       # enabled.
+                       m.__name__ = mname
+                       reload(m)
+       
+       def browsebuttonhit(self):
+               sel = self.window.list.getselection()
+               if not sel:
+                       return
+               import PyBrowser
+               for i in sel:
+                       PyBrowser.Browser(sys.modules[self.window.list[i]])
+       
+       def makelist(self):
+               editmodules, modules = getmoduleslist()
+               if modules == self.modules:
+                       return
+               self.editmodules, self.modules = editmodules, modules
+               self.window.list.setdrawingmode(0)
+               sel = self.window.list.getselectedobjects()
+               self.window.list.set(self.modules)
+               self.window.list.setselectedobjects(sel)
+               self.window.list.setdrawingmode(1)
+
+
+def getmoduleslist():
+       import PyBrowser        # for caselesssort function
+       moduleitems = sys.modules.items()
+       moduleitems = filter(lambda (name, module): module is not None, moduleitems)
+       modules = map(lambda (name, module): name, moduleitems)
+       modules = PyBrowser.caselesssort(modules)
+       editmodules = []
+       sysmodules = sys.modules
+       modulesappend = editmodules.append
+       for m in modules:
+               module = sysmodules[m]
+               try:
+                       if sysmodules[m].__file__[-3:] == '.py' or \
+                                       sysmodules[m].__file__[-4:] in ['.pyc', '.pyo']:
+                               modulesappend(1)
+                       else:
+                               modulesappend(0)
+               except AttributeError:
+                       modulesappend(0)
+       return editmodules, modules
+       
+       
+
+_browser = None
+
+def ModuleBrowser():
+       global _browser
+       if _browser is not None:
+               _browser.window.select()
+       else:
+               _browser = _modulebrowser()
diff --git a/Mac/Tools/IDE/ProfileBrowser.py b/Mac/Tools/IDE/ProfileBrowser.py
new file mode 100644 (file)
index 0000000..f5b4a38
--- /dev/null
@@ -0,0 +1,91 @@
+import W
+import Evt
+
+import sys
+import StringIO
+import string
+import pstats, fpformat
+
+# increase precision
+def f8(x):
+       return string.rjust(fpformat.fix(x, 4), 8)
+pstats.f8 = f8
+
+# hacking around a hack
+if sys.version[:3] > '1.4':
+       timer = Evt.TickCount
+else:
+       def timer(TickCount = Evt.TickCount):
+               return TickCount() / 60.0
+
+class ProfileBrowser:
+       
+       def __init__(self, stats = None):
+               self.sortkeys = ('calls',)
+               self.setupwidgets()
+               self.setstats(stats)
+       
+       def setupwidgets(self): 
+               self.w = W.Window((580, 400), "Profile Statistics", minsize = (200, 100), tabbable = 0)
+               self.w.divline = W.HorizontalLine((0, 20, 0, 0))
+               self.w.titlebar = W.TextBox((4, 4, 40, 12), 'Sort by:')
+               self.buttons = []
+               self.w.button_calls = W.RadioButton((54, 4, 45, 12), 'calls', self.buttons, self.setsort)
+               self.w.button_time = W.RadioButton((104, 4, 40, 12), 'time', self.buttons, self.setsort)
+               self.w.button_cumulative = W.RadioButton((154, 4, 75, 12), 'cumulative', self.buttons, self.setsort)
+               self.w.button_stdname = W.RadioButton((234, 4, 60, 12), 'stdname', self.buttons, self.setsort)
+               self.w.button_calls.set(1)
+               self.w.button_file = W.RadioButton((304, 4, 40, 12), 'file', self.buttons, self.setsort)
+               self.w.button_line = W.RadioButton((354, 4, 50, 12), 'line', self.buttons, self.setsort)
+               self.w.button_name = W.RadioButton((404, 4, 50, 12), 'name', self.buttons, self.setsort)
+##             self.w.button_nfl = W.RadioButton((4, 4, 12, 12), 'nfl', self.buttons, self.setsort)
+##             self.w.button_pcalls = W.RadioButton((4, 4, 12, 12), 'pcalls', self.buttons, self.setsort)
+               self.w.text = W.TextEditor((0, 21, -15, -15), inset = (6, 5), 
+                               readonly = 1, wrap = 0, fontsettings = ('Monaco', 0, 9, (0, 0, 0)))
+               self.w._bary = W.Scrollbar((-15, 20, 16, -14), self.w.text.vscroll, max = 32767)
+               self.w._barx = W.Scrollbar((-1, -15, -14, 16), self.w.text.hscroll, max = 32767)
+               self.w.open()
+       
+       def setstats(self, stats):
+               self.stats = stats
+               self.stats.strip_dirs()
+               self.displaystats()
+       
+       def setsort(self):
+               # Grmpf. The callback doesn't give us the button:-(
+               for b in self.buttons:
+                       if b.get():
+                               if b._title == self.sortkeys[0]:
+                                       return
+                               self.sortkeys = (b._title,) + self.sortkeys[:3]
+                               break
+               self.displaystats()
+       
+       def displaystats(self):
+               W.SetCursor('watch')
+               apply(self.stats.sort_stats, self.sortkeys)
+               saveout = sys.stdout
+               try:
+                       s = sys.stdout = StringIO.StringIO()
+                       self.stats.print_stats()
+               finally:
+                       sys.stdout = saveout
+               text = string.join(string.split(s.getvalue(), '\n'), '\r')
+               self.w.text.set(text)
+
+
+def main():
+       import pstats
+       args = sys.argv[1:]
+       for i in args:
+               stats = pstats.Stats(i)
+               browser = ProfileBrowser(stats)
+       else:
+               import macfs
+               fss, ok = macfs.PromptGetFile('Profiler data')
+               if not ok: sys.exit(0)
+               stats = pstats.Stats(fss.as_pathname())
+               browser = ProfileBrowser(stats)
+
+if __name__ == '__main__':
+       main()
diff --git a/Mac/Tools/IDE/PyBrowser.py b/Mac/Tools/IDE/PyBrowser.py
new file mode 100644 (file)
index 0000000..d62d256
--- /dev/null
@@ -0,0 +1,440 @@
+import W
+import Wkeys
+import struct
+import string
+import types
+import regex
+
+nullid = '\0\0'
+closedid = struct.pack('h', 468)
+openid = struct.pack('h', 469)
+closedsolidid = struct.pack('h', 470)
+opensolidid = struct.pack('h', 471)
+
+arrows = (nullid, closedid, openid, closedsolidid, opensolidid)
+
+has_ctlcharsRE = regex.compile('[\000-\037\177-\377]')
+
+def double_repr(key, value, truncvalue = 0, 
+                       type = type, StringType = types.StringType,
+                       has_ctlchars = has_ctlcharsRE.search, _repr = repr, str = str):
+       if type(key) == StringType and has_ctlchars(key) < 0:
+               key = str(key)
+       else:
+               key = _repr(key)
+       if type(value) == StringType and has_ctlchars(value) < 0:
+               value = str(value)
+       elif key == '__builtins__':
+               value = "<" + type(value).__name__ + " '__builtin__'>"
+       elif key == '__return__':
+               # bleh, when returning from a class codeblock we get infinite recursion in repr. 
+               # Use safe repr instead.
+               import repr
+               value = repr.repr(value)
+       else:
+               try:
+                       value = _repr(value)
+                       '' + value      # test to see if it is a string, in case a __repr__ method is buggy
+               except:
+                       value = '¥¥¥ exception in repr()'
+       if truncvalue:
+               return key + '\t' + value[:255]
+       return key + '\t' + value
+
+
+class BrowserWidget(W.List):
+       
+       LDEF_ID = 471
+       
+       def __init__(self, possize, object = None, col = 100, closechildren = 0):
+               W.List.__init__(self, possize, callback = self.listhit)
+               self.object = (None,)
+               self.indent = 16
+               self.lastmaxindent = 0
+               self.closechildren = closechildren
+               self.children = []
+               self.mincol = 64
+               self.setcolumn(col)
+               self.bind('return', self.openselection)
+               self.bind('enter', self.openselection)
+               if object is not None:
+                       self.set(object)
+       
+       def set(self, object):
+               if self.object[0] is not object:
+                       self.object = object,
+                       self[:] = self.unpack(object, 0)
+               elif self._parentwindow is not None and self._parentwindow.wid:
+                       self.update()
+       
+       def unpack(self, object, indent):
+               return unpack_object(object, indent)
+       
+       def update(self):
+               # for now...
+               W.SetCursor('watch')
+               self.setdrawingmode(0)
+               sel = self.getselectedobjects()
+               fold = self.getunfoldedobjects()
+               topcell = self.gettopcell()
+               self[:] = self.unpack(self.object[0], 0)
+               self.unfoldobjects(fold)
+               self.setselectedobjects(sel)
+               self.settopcell(topcell)
+               self.setdrawingmode(1)
+       
+       def setcolumn(self, col):
+               self.col = col
+               self.colstr = struct.pack('h', col)
+               if self._list:
+                       sel = self.getselection()
+                       self.setitems(self.items)
+                       self.setselection(sel)
+       
+       def key(self, char, event):
+               if char in (Wkeys.leftarrowkey, Wkeys.rightarrowkey):
+                       sel = self.getselection()
+                       sel.reverse()
+                       self.setdrawingmode(0)
+                       for index in sel:
+                               self.fold(index, char == Wkeys.rightarrowkey)
+                       self.setdrawingmode(1)
+               else:
+                       W.List.key(self, char, event)
+       
+       def rollover(self, (x, y), onoff):
+               if onoff:
+                       if self.incolumn((x, y)):
+                               W.SetCursor('hmover')
+                       else:
+                               W.SetCursor('arrow')
+       
+       def inarrow(self, (x, y)):
+               cl, ct, cr, cb = self._list.LRect((0, 0))
+               l, t, r, b = self._bounds
+               if (x - cl) < 16:
+                       cellheight = cb - ct
+                       index = (y - ct) / cellheight
+                       if index < len(self.items):
+                               return 1, index
+               return None, None
+       
+       def incolumn(self, (x, y)):
+               l, t, r, b = self._list.LRect((0, 0))
+               abscol = l + self.col
+               return abs(abscol - x) < 3
+       
+       def trackcolumn(self, (x, y)):
+               import Qd, QuickDraw, Evt
+               self.SetPort()
+               l, t, r, b = self._bounds
+               bounds = l, t, r, b = l + 1, t + 1, r - 16, b - 1
+               abscol = l + self.col
+               mincol = l + self.mincol
+               maxcol = r - 10
+               diff = abscol - x
+               Qd.PenPat('\000\377\000\377\000\377\000\377')
+               Qd.PenMode(QuickDraw.srcXor)
+               rect = abscol - 1, t, abscol, b
+               Qd.PaintRect(rect)
+               lastpoint = (x, y)
+               newcol = -1
+               #W.SetCursor('fist')
+               while Evt.Button():
+                       (x, y) = Evt.GetMouse()
+                       if (x, y) <> lastpoint:
+                               newcol = x + diff
+                               newcol = max(newcol, mincol)
+                               newcol = min(newcol, maxcol)
+                               Qd.PaintRect(rect)
+                               rect = newcol - 1, t, newcol, b
+                               Qd.PaintRect(rect)
+                               lastpoint = (x, y)
+               Qd.PaintRect(rect)
+               Qd.PenPat(Qd.qd.black)
+               Qd.PenNormal()
+               if newcol > 0 and newcol <> abscol:
+                       self.setcolumn(newcol - l)
+       
+       def click(self, point, modifiers):
+               if point == (-1, -1):   # gross.
+                       W.List.click(self, point ,modifiers)
+                       return
+               hit, index = self.inarrow(point)
+               if hit:
+                       (key, value, arrow, indent) = self.items[index]
+                       self.fold(index, arrow == 1)
+               elif self.incolumn(point):
+                       self.trackcolumn(point)
+               else:
+                       W.List.click(self, point, modifiers)
+       
+       # for W.List.key
+       def findmatch(self, tag):
+               lower = string.lower
+               items = self.items
+               taglen = len(tag)
+               match = '\377' * 100
+               match_i = -1
+               for i in range(len(items)):
+                       item = lower(str(items[i][0]))
+                       if tag <= item < match:
+                               match = item
+                               match_i = i
+               if match_i >= 0:
+                       return match_i
+               else:
+                       return len(items) - 1
+       
+       def close(self):
+               if self.closechildren:
+                       for window in self.children:
+                               window.close()
+               self.children = []
+               W.List.close(self)
+       
+       def fold(self, index, onoff):
+               (key, value, arrow, indent) = self.items[index]
+               if arrow == 0 or (onoff and arrow == 2) or (not onoff and arrow == 1):
+                       return
+               W.SetCursor('watch')
+               topcell = self.gettopcell()
+               if onoff:
+                       self[index] = (key, value, 4, indent)
+                       self.setdrawingmode(0)
+                       self[index+1:index+1] = self.unpack(value, indent + 1)
+                       self[index] = (key, value, 2, indent)
+               else:
+                       self[index] = (key, value, 3, indent)
+                       self.setdrawingmode(0)
+                       count = 0
+                       for i in range(index + 1, len(self.items)):
+                               (dummy, dummy, dummy, subindent) = self.items[i]
+                               if subindent <= indent:
+                                       break
+                               count = count + 1
+                       self[index+1:index+1+count] = []
+                       self[index] = (key, value, 1, indent)
+               maxindent = self.getmaxindent()
+               if maxindent <> self.lastmaxindent:
+                       newabsindent = self.col + (maxindent - self.lastmaxindent) * self.indent
+                       if newabsindent >= self.mincol:
+                               self.setcolumn(newabsindent)
+                       self.lastmaxindent = maxindent
+               self.settopcell(topcell)
+               self.setdrawingmode(1)
+       
+       def unfoldobjects(self, objects):
+               for obj in objects:
+                       try:
+                               index = self.items.index(obj)
+                       except ValueError:
+                               pass
+                       else:
+                               self.fold(index, 1)
+       
+       def getunfoldedobjects(self):
+               curindent = 0
+               objects = []
+               for index in range(len(self.items)):
+                       (key, value, arrow, indent) = self.items[index]
+                       if indent > curindent:
+                               (k, v, a, i) = self.items[index - 1]
+                               objects.append((k, v, 1, i))
+                               curindent = indent
+                       elif indent < curindent:
+                               curindent = indent
+               return objects
+       
+       def listhit(self, isdbl):
+               if isdbl:
+                       self.openselection()
+       
+       def openselection(self):
+               import os
+               sel = self.getselection()
+               for index in sel:
+                       (key, value, arrow, indent) = self[index]
+                       if arrow:
+                               self.children.append(Browser(value))
+                       elif type(value) == types.StringType and '\0' not in value:
+                               editor = self._parentwindow.parent.getscript(value)
+                               if editor:
+                                       editor.select()
+                                       return
+                               elif os.path.exists(value) and os.path.isfile(value):
+                                       import macfs
+                                       fss = macfs.FSSpec(value)
+                                       if fss.GetCreatorType()[1] == 'TEXT':
+                                               W.getapplication().openscript(value)
+       
+       def itemrepr(self, (key, value, arrow, indent), str = str, double_repr = double_repr, 
+                       arrows = arrows, pack = struct.pack):
+               arrow = arrows[arrow]
+               return arrow + pack('h', self.indent * indent) + self.colstr + \
+                               double_repr(key, value, 1)
+       
+       def getmaxindent(self, max = max):
+               maxindent = 0
+               for item in self.items:
+                       maxindent = max(maxindent, item[3])
+               return maxindent
+       
+       def domenu_copy(self, *args):
+               sel = self.getselectedobjects()
+               selitems = []
+               for key, value, dummy, dummy in sel:
+                       selitems.append(double_repr(key, value))
+               text = string.join(selitems, '\r')
+               if text:
+                       import Scrap
+                       Scrap.ZeroScrap()
+                       Scrap.PutScrap('TEXT', text)
+
+
+class Browser:
+       
+       def __init__(self, object = None, title = None, closechildren = 0):
+               if hasattr(object, '__name__'):
+                       name = object.__name__
+               else:
+                       name = ''
+               if title is None:
+                       title = 'Object browser'
+                       if name:
+                               title = title + ': ' + name
+               self.w = w = W.Window((300, 400), title, minsize = (100, 100))
+               w.info = W.TextBox((18, 8, -70, 15))
+               w.updatebutton = W.Button((-64, 4, 50, 16), 'Update', self.update)
+               w.browser = BrowserWidget((-1, 24, 1, -14), None)
+               w.bind('cmdu', w.updatebutton.push)
+               w.open()
+               self.set(object, name)
+       
+       def close(self):
+               if self.w.wid:
+                       self.w.close()
+       
+       def set(self, object, name = ''):
+               W.SetCursor('watch')
+               tp = type(object).__name__
+               try:
+                       length = len(object)
+               except:
+                       length = -1
+               if not name and hasattr(object, '__name__'):
+                       name = object.__name__
+               if name:
+                       info = name + ': ' + tp
+               else:
+                       info = tp
+               if length >= 0:
+                       if length == 1:
+                               info = info + ' (%d element)' % length
+                       else:
+                               info = info + ' (%d elements)' % length
+               self.w.info.set(info)
+               self.w.browser.set(object)
+       
+       def update(self):
+               self.w.browser.update()
+
+
+SIMPLE_TYPES = (
+       types.NoneType,
+       types.IntType,
+       types.LongType,
+       types.FloatType,
+       types.ComplexType,
+       types.StringType
+)
+
+INDEXING_TYPES = (
+       types.TupleType,
+       types.ListType,
+       types.DictionaryType
+)
+
+def unpack_object(object, indent = 0):
+       tp = type(object)
+       if tp in SIMPLE_TYPES and tp is not types.NoneType:
+               raise TypeError, 'canĂ•t browse simple type: %s' % tp.__name__
+       elif tp == types.DictionaryType:
+               return unpack_dict(object, indent)
+       elif tp in (types.TupleType, types.ListType):
+               return unpack_sequence(object, indent)
+       elif tp == types.InstanceType:
+               return unpack_instance(object, indent)
+       elif tp == types.ClassType:
+               return unpack_class(object, indent)
+       elif tp == types.ModuleType:
+               return unpack_dict(object.__dict__, indent)
+       else:
+               return unpack_other(object, indent)
+
+def unpack_sequence(seq, indent = 0):
+       items = map(None, range(len(seq)), seq)
+       items = map(lambda (k, v), type = type, simp = SIMPLE_TYPES, indent = indent: 
+                               (k, v, not type(v) in simp, indent), items)
+       return items
+
+def unpack_dict(dict, indent = 0):
+       items = dict.items()
+       return pack_items(items, indent)
+
+def unpack_instance(inst, indent = 0):
+       if hasattr(inst, '__pybrowse_unpack__'):
+               return unpack_object(inst.__pybrowse_unpack__(), indent)
+       else:
+               items = [('__class__', inst.__class__)] + inst.__dict__.items()
+               return pack_items(items, indent)
+
+def unpack_class(clss, indent = 0):
+       items = [('__bases__', clss.__bases__), ('__name__', clss.__name__)] + clss.__dict__.items()
+       return pack_items(items, indent)
+
+def unpack_other(object, indent = 0):
+       attrs = []
+       if hasattr(object, '__members__'):
+               attrs = attrs + object.__members__
+       if hasattr(object, '__methods__'):
+               attrs = attrs + object.__methods__
+       items = []
+       for attr in attrs:
+               items.append((attr, getattr(object, attr)))
+       return pack_items(items, indent)
+
+def pack_items(items, indent = 0):
+       items = map(lambda (k, v), type = type, simp = SIMPLE_TYPES, indent = indent: 
+                               (k, v, not type(v) in simp, indent), 
+                       items)
+       return tuple_caselesssort(items)
+
+def caselesssort(alist):
+       """Return a sorted copy of a list. If there are only strings in the list, 
+       it will not consider case"""
+       
+       try:
+               # turn ['FOO',  'aaBc', 'ABcD'] into [('foo', 'FOO'), ('aabc', 'aaBc'), ('abcd', 'ABcD')], if possible
+               tupledlist = map(lambda item, lower = string.lower: (lower(item), item), alist)
+       except TypeError:
+               # at least one element in alist is not a string, proceed the normal way...
+               alist = alist[:]
+               alist.sort()
+               return alist
+       else:
+               tupledlist.sort()
+               # turn [('aabc', 'aaBc'), ('abcd', 'ABcD'), ('foo', 'FOO')] into ['aaBc', 'ABcD', 'FOO']
+               return map(lambda x: x[1], tupledlist)
+
+def tuple_caselesssort(items):
+       try:
+               tupledlist = map(lambda tuple, lower = string.lower: (lower(tuple[0]), tuple), items)
+       except TypeError:
+               items = items[:]
+               items.sort()
+               return items
+       else:
+               tupledlist.sort()
+               return map(lambda (low, tuple): tuple, tupledlist)
+
diff --git a/Mac/Tools/IDE/PyConsole.py b/Mac/Tools/IDE/PyConsole.py
new file mode 100644 (file)
index 0000000..ad71982
--- /dev/null
@@ -0,0 +1,380 @@
+import W
+import Wkeys
+import Fm
+import WASTEconst
+from types import *
+import Events
+import string
+import sys
+import traceback
+import MacOS
+import MacPrefs
+import Qd
+import PyInteractive
+
+if not hasattr(sys, 'ps1'):
+       sys.ps1 = '>>> '
+if not hasattr(sys, 'ps2'):
+       sys.ps2 = '... '
+
+def inspect(foo):                      # JJS 1/25/99
+       "Launch the browser on the given object.  This is a general built-in function."
+       import PyBrowser
+       PyBrowser.Browser(foo)
+
+class ConsoleTextWidget(W.EditText):
+       
+       def __init__(self, *args, **kwargs):
+               apply(W.EditText.__init__, (self,) + args, kwargs)
+               self._inputstart = 0
+               self._buf = ''
+               self.pyinteractive = PyInteractive.PyInteractive()
+       
+               import __main__
+               self._namespace = __main__.__dict__
+               self._namespace['inspect'] = inspect                    # JJS 1/25/99
+       
+       def insert(self, text):
+               self.checkselection()
+               self.ted.WEInsert(text, None, None)
+               self.changed = 1
+               self.selchanged = 1
+       
+       def set_namespace(self, dict):
+               if type(dict) <> DictionaryType:
+                       raise TypeError, "The namespace needs to be a dictionary"
+               if 'inspect' not in dict.keys(): dict['inspect'] = inspect                      # JJS 1/25/99
+               self._namespace = dict
+       
+       def open(self):
+               W.EditText.open(self)
+               self.write('Python ' + sys.version + '\n' + sys.copyright + '\n')
+               self.write(sys.ps1)
+               self.flush()
+       
+       def key(self, char, event):
+               (what, message, when, where, modifiers) = event
+               if self._enabled and not modifiers & Events.cmdKey or char in Wkeys.arrowkeys:
+                       if char not in Wkeys.navigationkeys:
+                               self.checkselection()
+                       if char == Wkeys.enterkey:
+                               char = Wkeys.returnkey
+                       selstart, selend = self.getselection()
+                       if char == Wkeys.backspacekey:
+                               if selstart <= (self._inputstart - (selstart <> selend)):
+                                       return
+                       self.ted.WEKey(ord(char), modifiers)
+                       if char not in Wkeys.navigationkeys:
+                               self.changed = 1
+                       if char not in Wkeys.scrollkeys:
+                               self.selchanged = 1
+                       self.updatescrollbars()
+                       if char == Wkeys.returnkey:
+                               text = self.get()[self._inputstart:selstart]
+                               text = string.join(string.split(text, "\r"), "\n")
+                               saveyield = MacOS.EnableAppswitch(0)
+                               self.pyinteractive.executeline(text, self, self._namespace)
+                               MacOS.EnableAppswitch(saveyield)
+                               selstart, selend = self.getselection()
+                               self._inputstart = selstart
+       
+       def domenu_save_as(self, *args):
+               import macfs
+               fss, ok = macfs.StandardPutFile('Save console text as:', 'console.txt')
+               if not ok:
+                       return
+               f = open(fss.as_pathname(), 'wb')
+               f.write(self.get())
+               f.close()
+               fss.SetCreatorType(W._signature, 'TEXT')
+       
+       def write(self, text):
+               self._buf = self._buf + text
+               if '\n' in self._buf:
+                       self.flush()
+       
+       def flush(self):
+               stuff = string.split(self._buf, '\n')
+               stuff = string.join(stuff, '\r')
+               self.setselection_at_end()
+               self.ted.WEInsert(stuff, None, None)
+               selstart, selend = self.getselection()
+               self._inputstart = selstart
+               self._buf = ""
+               self.ted.WEClearUndo()
+               self.updatescrollbars()
+       
+       def selection_ok(self):
+               selstart, selend = self.getselection()
+               return not (selstart < self._inputstart or selend < self._inputstart)
+       
+       def checkselection(self):
+               if not self.selection_ok():
+                       self.setselection_at_end()
+       
+       def setselection_at_end(self):
+               end = self.ted.WEGetTextLength()
+               self.setselection(end, end)
+               self.updatescrollbars()
+               
+       def domenu_cut(self, *args):
+               if not self.selection_ok():
+                       return
+               W.EditText.domenu_cut(self)
+       
+       def domenu_paste(self, *args):
+               if not self.selection_ok():
+                       self.setselection_at_end()
+               W.EditText.domenu_paste(self)
+       
+       def domenu_clear(self, *args):
+               if not self.selection_ok():
+                       return
+               W.EditText.domenu_clear(self)
+
+
+class PyConsole(W.Window):
+       
+       def __init__(self, bounds, show = 1, fontsettings = ("Monaco", 0, 9, (0, 0, 0)), 
+                       tabsettings = (32, 0), unclosable = 0):
+               W.Window.__init__(self,
+                                       bounds, 
+                                       "Python Interactive", 
+                                       minsize = (200, 100), 
+                                       tabbable = 0, 
+                                       show = show)
+               
+               self._unclosable = unclosable
+               consoletext = ConsoleTextWidget((-1, -1, -14, 1), inset = (6, 5), 
+                               fontsettings = fontsettings, tabsettings = tabsettings)
+               self._bary = W.Scrollbar((-15, 14, 16, -14), consoletext.vscroll, max = 32767)
+               self.consoletext = consoletext
+               self.namespacemenu = W.PopupMenu((-15, -1, 16, 16), [], self.consoletext.set_namespace)
+               self.namespacemenu.bind('<click>', self.makenamespacemenu)
+               self.open()
+       
+       def makenamespacemenu(self, *args):
+               W.SetCursor('watch')
+               namespacelist = self.getnamespacelist()
+               self.namespacemenu.set([("Clear window", self.clearbuffer), ("Font settingsÉ", self.dofontsettings), 
+                               ["Namespace"] + namespacelist, ("Browse namespaceÉ", self.browsenamespace)])
+               currentname = self.consoletext._namespace["__name__"]
+               for i in range(len(namespacelist)):
+                       if namespacelist[i][0] == currentname:
+                               break
+               else:
+                       return
+               # XXX this functionality should be generally available in Wmenus
+               submenuid = self.namespacemenu.menu.menu.GetItemMark(3)
+               menu = self.namespacemenu.menu.bar.menus[submenuid]
+               menu.menu.CheckItem(i + 1, 1)
+       
+       def browsenamespace(self):
+               import PyBrowser, W
+               W.SetCursor('watch')
+               PyBrowser.Browser(self.consoletext._namespace, self.consoletext._namespace["__name__"])
+       
+       def clearbuffer(self):
+               import Res
+               self.consoletext.ted.WEUseText(Res.Resource(''))
+               self.consoletext.write(sys.ps1)
+               self.consoletext.flush()
+       
+       def getnamespacelist(self):
+               import os
+               import __main__
+               editors = filter(lambda x: x.__class__.__name__ == "Editor", self.parent._windows.values())
+               
+               namespaces = [ ("__main__",__main__.__dict__) ]
+               for ed in editors:
+                       modname = os.path.splitext(ed.title)[0]
+                       if sys.modules.has_key(modname):
+                               module = sys.modules[modname] 
+                               namespaces.append((modname, module.__dict__))
+                       else:
+                               if ed.title[-3:] == '.py':
+                                       modname = ed.title[:-3]
+                               else:
+                                       modname = ed.title
+                               ed.globals["__name__"] = modname
+                               namespaces.append((modname, ed.globals))
+               return namespaces
+       
+       def dofontsettings(self):
+               import FontSettings
+               settings = FontSettings.FontDialog(self.consoletext.getfontsettings(),
+                               self.consoletext.gettabsettings())
+               if settings:
+                       fontsettings, tabsettings = settings
+                       self.consoletext.setfontsettings(fontsettings)
+                       self.consoletext.settabsettings(tabsettings)
+       
+       def show(self, onoff = 1):
+               W.Window.show(self, onoff)
+               if onoff:
+                       self.select()
+       
+       def close(self):
+               if self._unclosable:
+                       self.show(0)
+                       return -1
+               W.Window.close(self)
+       
+       def writeprefs(self):
+               prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
+               prefs.console.show = self.isvisible()
+               prefs.console.windowbounds = self.getbounds()
+               prefs.console.fontsettings = self.consoletext.getfontsettings()
+               prefs.console.tabsettings = self.consoletext.gettabsettings()
+               prefs.save()
+
+
+class OutputTextWidget(W.EditText):
+       
+       def domenu_save_as(self, *args):
+               title = self._parentwindow.gettitle()
+               import macfs
+               fss, ok = macfs.StandardPutFile('Save %s text as:' % title, title + '.txt')
+               if not ok:
+                       return
+               f = open(fss.as_pathname(), 'wb')
+               f.write(self.get())
+               f.close()
+               fss.SetCreatorType(W._signature, 'TEXT')
+       
+
+class PyOutput:
+       
+       def __init__(self, bounds, show = 1, fontsettings = ("Monaco", 0, 9, (0, 0, 0)), tabsettings = (32, 0)):
+               self.bounds = bounds
+               self.fontsettings = fontsettings
+               self.tabsettings = tabsettings
+               self.w = None
+               self.closed = 1
+               self._buf = ''
+               # should be able to set this
+               self.savestdout, self.savestderr = sys.stdout, sys.stderr
+               sys.stderr = sys.stdout = self
+               if show:
+                       self.show()
+       
+       def setupwidgets(self):
+               self.w = W.Window(self.bounds, "Output", 
+                               minsize = (200, 100), 
+                               tabbable = 0)
+               self.w.outputtext = OutputTextWidget((-1, -1, -14, 1), inset = (6, 5), 
+                               fontsettings = self.fontsettings, tabsettings = self.tabsettings, readonly = 1)
+               menuitems = [("Clear window", self.clearbuffer), ("Font settingsÉ", self.dofontsettings)]
+               self.w.popupmenu = W.PopupMenu((-15, -1, 16, 16), menuitems)
+               
+               self.w._bary = W.Scrollbar((-15, 14, 16, -14), self.w.outputtext.vscroll, max = 32767)
+               self.w.bind("<close>", self.close)
+               self.w.bind("<activate>", self.activate)
+       
+       def write(self, text):
+               oldyield = MacOS.EnableAppswitch(-1)
+               try:
+                       self._buf = self._buf + text
+                       if '\n' in self._buf:
+                               self.flush()
+               finally:
+                       MacOS.EnableAppswitch(oldyield)
+       
+       def flush(self):
+               self.show()
+               stuff = string.split(self._buf, '\n')
+               stuff = string.join(stuff, '\r')
+               end = self.w.outputtext.ted.WEGetTextLength()
+               self.w.outputtext.setselection(end, end)
+               self.w.outputtext.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 0)
+               self.w.outputtext.ted.WEInsert(stuff, None, None)
+               self._buf = ""
+               self.w.outputtext.updatescrollbars()
+               self.w.outputtext.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 1)
+       
+       def show(self):
+               if self.closed:
+                       if not self.w:
+                               self.setupwidgets()
+                               self.w.open()
+                               self.w.outputtext.updatescrollbars()
+                               self.closed = 0
+                       else:
+                               self.w.show(1)
+                               self.closed = 0
+                               self.w.select()
+       
+       def writeprefs(self):
+               if self.w is not None:
+                       prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
+                       prefs.output.show = self.w.isvisible()
+                       prefs.output.windowbounds = self.w.getbounds()
+                       prefs.output.fontsettings = self.w.outputtext.getfontsettings()
+                       prefs.output.tabsettings = self.w.outputtext.gettabsettings()
+                       prefs.save()
+       
+       def dofontsettings(self):
+               import FontSettings
+               settings = FontSettings.FontDialog(self.w.outputtext.getfontsettings(),
+                               self.w.outputtext.gettabsettings())
+               if settings:
+                       fontsettings, tabsettings = settings
+                       self.w.outputtext.setfontsettings(fontsettings)
+                       self.w.outputtext.settabsettings(tabsettings)
+       
+       def clearbuffer(self):
+               import Res
+               self.w.outputtext.set('')
+       
+       def activate(self, onoff):
+               if onoff:
+                       self.closed = 0
+       
+       def close(self):
+               self.w.show(0)
+               self.closed = 1
+               return -1
+
+
+class SimpleStdin:
+       
+       def readline(self):
+               import EasyDialogs
+               sys.stdout.flush()
+               rv = EasyDialogs.AskString("")
+               if rv is None:
+                       return ""
+               return rv + '\n'
+
+
+def installconsole(defaultshow = 1):
+       global console
+       prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
+       if not prefs.console or not hasattr(prefs.console, 'show'):
+               prefs.console.show = defaultshow
+       if not hasattr(prefs.console, "windowbounds"):
+               prefs.console.windowbounds = (450, 250)
+       if not hasattr(prefs.console, "fontsettings"):
+               prefs.console.fontsettings = ("Monaco", 0, 9, (0, 0, 0))
+       if not hasattr(prefs.console, "tabsettings"):
+               prefs.console.tabsettings = (32, 0)
+       console = PyConsole(prefs.console.windowbounds, prefs.console.show, 
+                       prefs.console.fontsettings, prefs.console.tabsettings, 1)
+
+def installoutput(defaultshow = 0, OutPutWindow = PyOutput):
+       global output
+       
+       # quick 'n' dirty std in emulation
+       sys.stdin = SimpleStdin()
+       
+       prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
+       if not prefs.output or not hasattr(prefs.output, 'show'):
+               prefs.output.show = defaultshow
+       if not hasattr(prefs.output, "windowbounds"):
+               prefs.output.windowbounds = (450, 250)
+       if not hasattr(prefs.output, "fontsettings"):
+               prefs.output.fontsettings = ("Monaco", 0, 9, (0, 0, 0))
+       if not hasattr(prefs.output, "tabsettings"):
+               prefs.output.tabsettings = (32, 0)
+       output = OutPutWindow(prefs.output.windowbounds, prefs.output.show, 
+                       prefs.output.fontsettings, prefs.output.tabsettings)
diff --git a/Mac/Tools/IDE/PyDebugger.py b/Mac/Tools/IDE/PyDebugger.py
new file mode 100644 (file)
index 0000000..b0d4fb4
--- /dev/null
@@ -0,0 +1,887 @@
+import sys
+import bdb
+import types
+import os
+
+import W
+import WASTEconst
+import PyBrowser
+import Qd
+import Evt
+import Lists
+import MacOS
+_filenames = {}
+
+SIMPLE_TYPES = (
+       types.NoneType,
+       types.IntType,
+       types.LongType,
+       types.FloatType,
+       types.ComplexType,
+       types.StringType
+)
+
+
+class Debugger(bdb.Bdb):
+       
+       def __init__(self, title = 'Debugger'):
+               bdb.Bdb.__init__(self)
+               self.closed = 1
+               self.title = title
+               self.breaksviewer = None
+               self.reset()
+               self.tracing = 0
+               self.tracingmonitortime = Evt.TickCount()
+               self.editors = {}
+               
+               prefs = W.getapplication().getprefs()
+               if prefs.debugger:
+                       for file, breaks in prefs.debugger.breaks.items():
+                               for b in breaks:
+                                       self.set_break(file, b)
+                       self.bounds, self.horpanes, self.verpanes = prefs.debugger.windowsettings
+                       self.tracemagic = prefs.debugger.tracemagic
+               else:
+                       self.breaks = {}
+                       self.horpanes = (0.4, 0.6)
+                       self.verpanes = (0.3, 0.35, 0.35)
+                       self.bounds = (600, 400)
+                       self.tracemagic = 0
+               self.laststacksel = None
+       
+       def reset(self):
+               self.currentframe = None
+               self.file = None
+               self.laststack = None
+               self.reason = 'Not running'
+               self.continuewithoutdebugger = 0
+               bdb.Bdb.reset(self)
+               self.forget()
+       
+       def start(self, bottomframe = None, running = 0):
+               W.getapplication().DebuggerQuit = bdb.BdbQuit
+               import Menu
+               Menu.HiliteMenu(0)
+               if self.closed:
+                       self.setupwidgets(self.title)
+                       self.closed = 0
+               if not self.w.parent.debugger_quitting:
+                       self.w.select()
+                       raise W.AlertError, 'There is another debugger session busy.'
+               self.reset()
+               self.botframe = bottomframe
+               if running:
+                       self.set_continue()
+                       self.reason = 'RunningÉ'
+                       self.setstate('running')
+               else:
+                       self.set_step()
+                       self.reason = 'stopped'
+                       self.setstate('stopped')
+               sys.settrace(self.trace_dispatch)
+       
+       def stop(self):
+               self.set_quit()
+               if self.w.parent:
+                       self.exit_mainloop()
+                       self.resetwidgets()
+       
+       def set_continue_without_debugger(self):
+               sys.settrace(None)
+               self.set_quit()
+               self.clear_tracefuncs()
+               self.continuewithoutdebugger = 1
+               if self.w.parent:
+                       self.exit_mainloop()
+                       self.resetwidgets()
+       
+       def clear_tracefuncs(self):
+               try:
+                       raise 'spam'
+               except:
+                       pass
+               frame = sys.exc_traceback.tb_frame
+               while frame is not None:
+                       del frame.f_trace
+                       frame = frame.f_back
+       
+       def postmortem(self, exc_type, exc_value, traceback):
+               if self.closed:
+                       self.setupwidgets(self.title)
+                       self.closed = 0
+               if not self.w.parent.debugger_quitting:
+                       raise W.AlertError, 'There is another debugger session busy.'
+               self.reset()
+               if traceback:
+                       self.botframe = traceback.tb_frame
+                       while traceback.tb_next <> None:
+                               traceback = traceback.tb_next
+                       frame = traceback.tb_frame
+               else:
+                       self.botframe = None
+                       frame = None
+               self.w.panes.bottom.buttons.killbutton.enable(1)
+               self.reason = '(dead) ' + self.formatexception(exc_type, exc_value)
+               self.w.select()
+               self.setup(frame, traceback)
+               self.setstate('dead')
+               self.showstack(self.curindex)
+               self.showframe(self.curindex)
+       
+       def setupwidgets(self, title):
+               self.w = w = W.Window(self.bounds, title, minsize = (500, 300))
+               
+               w.panes = W.HorizontalPanes((8, 4, -8, -8), self.horpanes)
+               
+               w.panes.browserpanes = browserpanes = W.VerticalPanes(None, self.verpanes)
+               
+               browserpanes.stacklist = W.Group(None)
+               browserpanes.stacklist.title = W.TextBox((4, 0, 0, 12), 'Stack')
+               browserpanes.stacklist.stack = W.List((0, 16, 0, 0), callback = self.do_stack, flags = Lists.lOnlyOne)
+               
+               browserpanes.locals = W.Group(None)
+               browserpanes.locals.title = W.TextBox((4, 0, 0, 12), 'Local variables')
+               browserpanes.locals.browser = PyBrowser.BrowserWidget((0, 16, 0, 0))
+               
+               browserpanes.globals = W.Group(None)
+               browserpanes.globals.title = W.TextBox((4, 0, 0, 12), 'Global variables')
+               browserpanes.globals.browser = PyBrowser.BrowserWidget((0, 16, 0, 0))
+               
+               w.panes.bottom = bottom = W.Group(None)
+               bottom.src = src = W.Group((0, 52, 0, 0))
+               source = SourceViewer((1, 1, -15, -15), readonly = 1, debugger = self)
+               src.optionsmenu = W.PopupMenu((-16, 0, 16, 16), [])
+               src.optionsmenu.bind('<click>', self.makeoptionsmenu)
+               
+               src._barx = W.Scrollbar((0, -16, -15, 16), source.hscroll, max = 32767)
+               src._bary = W.Scrollbar((-16, 15, 16, -15), source.vscroll, max = 32767)
+               src.source = source
+               src.frame = W.Frame((0, 0, -15, -15))
+               
+               bottom.tracingmonitor = TracingMonitor((0, 23, 6, 6))
+               bottom.state = W.TextBox((12, 20, 0, 16), self.reason)
+               
+               bottom.srctitle = W.TextBox((12, 36, 0, 14))
+               bottom.buttons = buttons = W.Group((12, 0, 0, 16))
+               
+               buttons.runbutton = W.Button((0, 0, 50, 16), "Run", self.do_run)
+               buttons.stopbutton = W.Button((58, 0, 50, 16), "Stop", self.do_stop)
+               buttons.killbutton = W.Button((116, 0, 50, 16), "Kill", self.do_kill)
+               buttons.line = W.VerticalLine((173, 0, 0, 0))
+               buttons.stepbutton = W.Button((181, 0, 50, 16), "Step", self.do_step)
+               buttons.stepinbutton = W.Button((239, 0, 50, 16), "Step in", self.do_stepin)
+               buttons.stepoutbutton = W.Button((297, 0, 50, 16), "Step out", self.do_stepout)
+               
+               w.bind('cmdr', buttons.runbutton.push)
+               w.bind('cmd.', buttons.stopbutton.push)
+               w.bind('cmdk', buttons.killbutton.push)
+               w.bind('cmds', buttons.stepbutton.push)
+               w.bind('cmdt', buttons.stepinbutton.push)
+               w.bind('cmdu', buttons.stepoutbutton.push)
+               
+               w.bind('<close>', self.close)
+               
+               w.open()
+               w.xxx___select(w.panes.bottom.src.source)
+       
+       def makeoptionsmenu(self):
+               options = [('Clear breakpoints', self.w.panes.bottom.src.source.clearbreakpoints), 
+                               ('Clear all breakpoints', self.clear_all_breaks),
+                               ('Edit breakpointsÉ', self.edit_breaks), '-',
+                               (self.tracemagic and 
+                                       'Disable __magic__ tracing' or 'Enable __magic__ tracing', self.togglemagic)]
+               self.w.panes.bottom.src.optionsmenu.set(options)
+       
+       def edit_breaks(self):
+               if self.breaksviewer:
+                       self.breaksviewer.select()
+               else:
+                       self.breaksviewer = BreakpointsViewer(self)
+       
+       def togglemagic(self):
+               self.tracemagic = not self.tracemagic
+       
+       def setstate(self, state):
+               self.w.panes.bottom.tracingmonitor.reset()
+               self.w.panes.bottom.state.set(self.reason)
+               buttons = self.w.panes.bottom.buttons
+               if state == 'stopped':
+                       buttons.runbutton.enable(1)
+                       buttons.stopbutton.enable(0)
+                       buttons.killbutton.enable(1)
+                       buttons.stepbutton.enable(1)
+                       buttons.stepinbutton.enable(1)
+                       buttons.stepoutbutton.enable(1)
+               elif state == 'running':
+                       buttons.runbutton.enable(0)
+                       buttons.stopbutton.enable(1)
+                       buttons.killbutton.enable(1)
+                       buttons.stepbutton.enable(0)
+                       buttons.stepinbutton.enable(0)
+                       buttons.stepoutbutton.enable(0)
+               elif state == 'idle':
+                       buttons.runbutton.enable(0)
+                       buttons.stopbutton.enable(0)
+                       buttons.killbutton.enable(0)
+                       buttons.stepbutton.enable(0)
+                       buttons.stepinbutton.enable(0)
+                       buttons.stepoutbutton.enable(0)
+               elif state == 'dead':
+                       buttons.runbutton.enable(0)
+                       buttons.stopbutton.enable(0)
+                       buttons.killbutton.enable(1)
+                       buttons.stepbutton.enable(0)
+                       buttons.stepinbutton.enable(0)
+                       buttons.stepoutbutton.enable(0)
+               else:
+                       print 'unknown state:', state
+       
+       def resetwidgets(self):
+               self.reason = ''
+               self.w.panes.bottom.srctitle.set('')
+               self.w.panes.bottom.src.source.set('')
+               self.w.panes.browserpanes.stacklist.stack.set([])
+               self.w.panes.browserpanes.locals.browser.set({})
+               self.w.panes.browserpanes.globals.browser.set({})
+               self.setstate('idle')
+       
+       # W callbacks
+       
+       def close(self):
+               self.set_quit()
+               self.exit_mainloop()
+               self.closed = 1
+               
+               self.unregister_editor(self.w.panes.bottom.src.source, 
+                               self.w.panes.bottom.src.source.file)
+               self.horpanes = self.w.panes.getpanesizes()
+               self.verpanes = self.w.panes.browserpanes.getpanesizes()
+               self.bounds = self.w.getbounds()
+               prefs = W.getapplication().getprefs()
+               prefs.debugger.breaks = self.breaks
+               prefs.debugger.windowsettings = self.bounds, self.horpanes, self.verpanes
+               prefs.debugger.tracemagic = self.tracemagic
+               prefs.save()
+       
+       # stack list callback
+       
+       def do_stack(self, isdbl):
+               sel = self.w.panes.browserpanes.stacklist.stack.getselection()
+               if isdbl:
+                       if sel:
+                               frame, lineno = self.stack[sel[0] + 1]
+                               filename = frame.f_code.co_filename
+                               editor = self.w._parentwindow.parent.openscript(filename, lineno)
+                               if self.breaks.has_key(filename):
+                                       editor.showbreakpoints(1)
+               else:
+                       if sel and sel <> self.laststacksel:
+                               self.showframe(sel[0] + 1)
+                       self.laststacksel = sel
+       
+       def geteditor(self, filename):
+               if filename[:1] == '<' and filename[-1:] == '>':
+                       editor = W.getapplication().getscript(filename[1:-1])
+               else:
+                       editor = W.getapplication().getscript(filename)
+               return editor
+       
+       # button callbacks
+       
+       def do_run(self):
+               self.running()
+               self.set_continue()
+               self.exit_mainloop()
+       
+       def do_stop(self):
+               self.set_step()
+       
+       def do_kill(self):
+               self.set_quit()
+               self.exit_mainloop()
+               self.resetwidgets()
+       
+       def do_step(self):
+               self.running()
+               self.set_next(self.curframe)
+               self.exit_mainloop()
+       
+       def do_stepin(self):
+               self.running()
+               self.set_step()
+               self.exit_mainloop()
+       
+       def do_stepout(self):
+               self.running()
+               self.set_return(self.curframe)
+               self.exit_mainloop()
+       
+       def running(self):
+               W.SetCursor('watch')
+               self.reason = 'RunningÉ'
+               self.setstate('running')
+               #self.w.panes.bottom.src.source.set('')
+               #self.w.panes.browserpanes.stacklist.stack.set([])
+               #self.w.panes.browserpanes.locals.browser.set({})
+               #self.w.panes.browserpanes.globals.browser.set({})
+       
+       def exit_mainloop(self):
+               self.w.parent.debugger_quitting = 1
+       
+       #
+       
+       def showframe(self, stackindex):
+               (frame, lineno) = self.stack[stackindex]
+               W.SetCursor('watch')
+               filename = frame.f_code.co_filename
+               if filename <> self.file:
+                       editor = self.geteditor(filename)
+                       if editor:
+                               self.w.panes.bottom.src.source.set(editor.get(), filename)
+                       else:
+                               try:
+                                       f = open(filename, 'rb')
+                                       data = f.read()
+                                       f.close()
+                               except IOError:
+                                       if filename[-3:] == '.py':
+                                               import imp
+                                               modname = os.path.basename(filename)[:-3]
+                                               try:
+                                                       f, filename, (suff, mode, dummy) = imp.find_module(modname)
+                                               except ImportError:
+                                                       self.w.panes.bottom.src.source.set('canĂ•t find file')
+                                               else:
+                                                       if f:
+                                                               f.close()
+                                                       if f and suff == '.py':
+                                                               f = open(filename, 'rb')
+                                                               data = f.read()
+                                                               f.close()
+                                                               self.w.panes.bottom.src.source.set(data, filename)
+                                                       else:
+                                                               self.w.panes.bottom.src.source.set('canĂ•t find file')
+                                       else:
+                                               self.w.panes.bottom.src.source.set('canĂ•t find file')
+                               else:
+                                       self.w.panes.bottom.src.source.set(data, filename)
+                       self.file = filename
+               self.w.panes.bottom.srctitle.set('Source: ' + filename + ((lineno > 0) and (' (line %d)' % lineno) or ' '))
+               self.goto_line(lineno)
+               self.lineno = lineno
+               self.showvars((frame, lineno))
+       
+       def showvars(self, (frame, lineno)):
+               if frame.f_locals is not frame.f_globals:
+                       locals = frame.f_locals
+               else:
+                       locals = {'Same as Globals':''}
+               filteredlocals = {}
+               for key, value in locals.items():
+                       # empty key is magic for Python 1.4; '.' is magic for 1.5...
+                       if not key or key[0] <> '.':
+                               filteredlocals[key] = value
+               self.w.panes.browserpanes.locals.browser.set(filteredlocals)
+               self.w.panes.browserpanes.globals.browser.set(frame.f_globals)
+       
+       def showstack(self, stackindex):
+               stack = []
+               for frame, lineno in self.stack[1:]:
+                       filename = frame.f_code.co_filename
+                       try:
+                               filename = _filenames[filename]
+                       except KeyError:
+                               if filename[:1] + filename[-1:] <> '<>':
+                                       filename = os.path.basename(filename)
+                               _filenames[frame.f_code.co_filename] = filename
+                       funcname = frame.f_code.co_name
+                       if funcname == '?':
+                               funcname = '<toplevel>'
+                       stack.append(filename + ': ' + funcname)
+               if stack <> self.laststack:
+                       self.w.panes.browserpanes.stacklist.stack.set(stack)
+                       self.laststack = stack
+               sel = [stackindex - 1]
+               self.w.panes.browserpanes.stacklist.stack.setselection(sel)
+               self.laststacksel = sel
+       
+       def goto_line(self, lineno):
+               if lineno > 0:
+                       self.w.panes.bottom.src.source.selectline(lineno - 1)
+               else:
+                       self.w.panes.bottom.src.source.setselection(0, 0)
+       
+       # bdb entry points
+       
+#      def user_call(self, frame, argument_list):
+#              self.reason = 'Calling'
+#              self.interaction(frame, None)
+       
+       def user_line(self, frame):
+               # This function is called when we stop or break at this line
+               self.reason = 'Stopped'
+               self.interaction(frame, None)
+       
+       def user_return(self, frame, return_value):
+               # This function is called when a return trap is set here
+               fname = frame.f_code.co_name
+               if fname <> '?':
+                       self.reason = 'Returning from %s()' % frame.f_code.co_name
+                       frame.f_locals['__return__'] = return_value
+               elif frame.f_back is self.botframe:
+                       self.reason = 'Done'
+               else:
+                       self.reason = 'Returning'
+               self.interaction(frame, None, 1)
+       
+       def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
+               # This function is called when we stop or break at this line
+               self.reason = self.formatexception(exc_type, exc_value)
+               self.interaction(frame, exc_traceback)
+       
+       def formatexception(self, exc_type, exc_value):
+               if exc_type == SyntaxError:
+                       try:
+                               value, (filename, lineno, charno, line) = exc_value
+                       except:
+                               pass
+                       else:
+                               return str(exc_type) + ': ' + str(value)
+               if type(exc_type) == types.ClassType:
+                       nice = exc_type.__name__
+               else:
+                       nice = str(exc_type)
+               value = str(exc_value)
+               if exc_value and value:
+                       nice = nice + ": " + value
+               return nice
+       
+       def forget(self):
+               self.stack = []
+               self.curindex = 0
+               self.curframe = None
+       
+       def setup(self, f, t, isreturning = 0):
+               self.forget()
+               self.stack, self.curindex = self.get_stack(f, t)
+               self.curframe = self.stack[self.curindex - isreturning][0]
+       
+       def interaction(self, frame, traceback, isreturning = 0):
+               saveport = Qd.GetPort()
+               self.w.select()
+               try:
+                       self.setup(frame, traceback, isreturning)
+                       self.setstate('stopped')
+                       stackindex = self.curindex
+                       if isreturning:
+                               if frame.f_back is not self.botframe:
+                                       stackindex = stackindex - 1
+                       self.showstack(stackindex)
+                       self.showframe(stackindex)
+                       self.w.parent.debugger_mainloop()
+                       self.forget()
+               finally:
+                       Qd.SetPort(saveport)
+       
+       # bdb customization
+       
+       def trace_dispatch(self, frame, event, arg, TickCount = Evt.TickCount):
+               if TickCount() - self.tracingmonitortime > 15:
+                       self.tracingmonitortime = TickCount()
+                       self.w.panes.bottom.tracingmonitor.toggle()
+               try:
+                       try:
+                               MacOS.EnableAppswitch(0)
+                               if self.quitting:
+                                       # returning None is not enough, a former BdbQuit exception
+                                       # might have been eaten by the print statement
+                                       raise bdb.BdbQuit
+                               if event == 'line':
+                                       return self.dispatch_line(frame)
+                               if event == 'call':
+                                       return self.dispatch_call(frame, arg)
+                               if event == 'return':
+                                       return self.dispatch_return(frame, arg)
+                               if event == 'exception':
+                                       return self.dispatch_exception(frame, arg)
+                               print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
+                               return self.trace_dispatch
+                       finally:
+                               MacOS.EnableAppswitch(-1)
+               except KeyboardInterrupt:
+                       self.set_step()
+                       return self.trace_dispatch
+               except bdb.BdbQuit:
+                       if self.continuewithoutdebugger:
+                               self.clear_tracefuncs()
+                               return
+                       else:
+                               raise bdb.BdbQuit
+               except:
+                       print 'XXX Exception during debugger interaction.', \
+                                       self.formatexception(sys.exc_type, sys.exc_value)
+                       import traceback
+                       traceback.print_exc()
+                       return self.trace_dispatch
+       
+       def dispatch_call(self, frame, arg):
+               if not self.tracemagic and \
+                               frame.f_code.co_name[:2] == '__' == frame.f_code.co_name[-2:] and \
+                               frame.f_code.co_name <> '__init__':
+                       return
+               if self.botframe is None:
+                       # First call of dispatch since reset()
+                       self.botframe = frame.f_back    # xxx !!! added f_back
+                       return self.trace_dispatch
+               if not (self.stop_here(frame) or self.break_anywhere(frame)):
+                       # No need to trace this function
+                       return # None
+               self.user_call(frame, arg)
+               if self.quitting:
+                       raise bdb.BdbQuit
+               return self.trace_dispatch
+       
+       def set_continue(self):
+               # Don't stop except at breakpoints or when finished
+               self.stopframe = self.botframe
+               self.returnframe = None
+               self.quitting = 0
+               # unlike in bdb/pdb, there's a chance that breakpoints change 
+               # *while* a program (this program ;-) is running. It's actually quite likely.
+               # So we don't delete frame.f_trace until the bottom frame if there are no breakpoints.
+       
+       def set_break(self, filename, lineno):
+               if not self.breaks.has_key(filename):
+                       self.breaks[filename] = []
+               list = self.breaks[filename]
+               if lineno in list:
+                       return 'There is already a breakpoint there!'
+               list.append(lineno)
+               list.sort()     # I want to keep them neatly sorted; easier for drawing
+               if hasattr(bdb, "Breakpoint"):
+                       # 1.5.2b1 specific
+                       bp = bdb.Breakpoint(filename, lineno, 0, None)
+               self.update_breaks(filename)
+       
+       def clear_break(self, filename, lineno):
+               bdb.Bdb.clear_break(self, filename, lineno)
+               self.update_breaks(filename)
+       
+       def clear_all_file_breaks(self, filename):
+               bdb.Bdb.clear_all_file_breaks(self, filename)
+               self.update_breaks(filename)
+       
+       def clear_all_breaks(self):
+               bdb.Bdb.clear_all_breaks(self)
+               for editors in self.editors.values():
+                       for editor in editors:
+                               editor.drawbreakpoints()
+       
+       # special
+       
+       def toggle_break(self, filename, lineno):
+               if self.get_break(filename, lineno):
+                       self.clear_break(filename, lineno)
+               else:
+                       self.set_break(filename, lineno)
+       
+       def clear_breaks_above(self, filename, above):
+               if not self.breaks.has_key(filename):
+                       return 'There are no breakpoints in that file!'
+               for lineno in self.breaks[filename][:]:
+                       if lineno > above:
+                               self.breaks[filename].remove(lineno)
+               if not self.breaks[filename]:
+                       del self.breaks[filename]
+       
+       # editor stuff
+       
+       def update_breaks(self, filename):
+               if self.breaksviewer:
+                       self.breaksviewer.update()
+               if self.editors.has_key(filename):
+                       for editor in self.editors[filename]:
+                               if editor._debugger:    # XXX
+                                       editor.drawbreakpoints()
+                               else:
+                                       print 'xxx dead editor!'
+       
+       def update_allbreaks(self):
+               if self.breaksviewer:
+                       self.breaksviewer.update()
+               for filename in self.breaks.keys():
+                       if self.editors.has_key(filename):
+                               for editor in self.editors[filename]:
+                                       if editor._debugger:    # XXX
+                                               editor.drawbreakpoints()
+                                       else:
+                                               print 'xxx dead editor!'
+       
+       def register_editor(self, editor, filename):
+               if not filename:
+                       return
+               if not self.editors.has_key(filename):
+                       self.editors[filename] = [editor]
+               elif editor not in self.editors[filename]:
+                       self.editors[filename].append(editor)
+       
+       def unregister_editor(self, editor, filename):
+               if not filename:
+                       return
+               try:
+                       self.editors[filename].remove(editor)
+                       if not self.editors[filename]:
+                               del self.editors[filename]
+                               # if this was an untitled window, clear the breaks.
+                               if filename[:1] == '<' and filename[-1:] == '>' and \
+                                               self.breaks.has_key(filename):
+                                       self.clear_all_file_breaks(filename)
+               except (KeyError, ValueError):
+                       pass
+               
+
+class SourceViewer(W.PyEditor):
+       
+       def __init__(self, *args, **kwargs):
+               apply(W.PyEditor.__init__, (self,) + args, kwargs)
+               self.bind('<click>', self.clickintercept)
+       
+       def clickintercept(self, point, modifiers):
+               if self._parentwindow._currentwidget <> self and not self.pt_in_breaks(point):
+                       self._parentwindow.xxx___select(self)
+                       return 1
+       
+       def _getviewrect(self):
+               l, t, r, b = self._bounds
+               if self._debugger:
+                       return (l + 12, t + 2, r - 1, b - 2)
+               else:
+                       return (l + 5, t + 2, r - 1, b - 2)
+       
+       def select(self, onoff, isclick = 0):
+               if W.SelectableWidget.select(self, onoff):
+                       return
+               self.SetPort()
+               #if onoff:
+               #       self.ted.WEActivate()
+               #else:
+               #       self.ted.WEDeactivate()
+               self.drawselframe(onoff)
+       
+       def drawselframe(self, onoff):
+               pass
+
+
+class BreakpointsViewer:
+       
+       def __init__(self, debugger):
+               self.debugger = debugger
+               import Lists
+               self.w = W.Window((300, 250), 'Breakpoints', minsize = (200, 200))
+               self.w.panes = W.HorizontalPanes((8, 8, -8, -32), (0.3, 0.7))
+               self.w.panes.files = W.List(None, callback = self.filehit)              #, flags = Lists.lOnlyOne)
+               self.w.panes.gr = W.Group(None)
+               self.w.panes.gr.breaks = W.List((0, 0, -130, 0), callback = self.linehit)       #, flags = Lists.lOnlyOne)
+               self.w.panes.gr.openbutton = W.Button((-80, 4, 0, 16), 'ViewÉ', self.openbuttonhit)
+               self.w.panes.gr.deletebutton = W.Button((-80, 28, 0, 16), 'Delete', self.deletebuttonhit)
+               
+               self.w.bind('<close>', self.close)
+               self.w.bind('backspace', self.w.panes.gr.deletebutton.push)
+               
+               self.setup()
+               self.w.open()
+               self.w.panes.gr.openbutton.enable(0)
+               self.w.panes.gr.deletebutton.enable(0)
+               self.curfile = None
+       
+       def deletebuttonhit(self):
+               if self.w._currentwidget == self.w.panes.files:
+                       self.del_filename()
+               else:
+                       self.del_number()
+               self.checkbuttons()
+       
+       def del_number(self):
+               if self.curfile is None:
+                       return
+               sel = self.w.panes.gr.breaks.getselectedobjects()
+               for lineno in sel:
+                       self.debugger.clear_break(self.curfile, lineno)
+       
+       def del_filename(self):
+               sel = self.w.panes.files.getselectedobjects()
+               for filename in sel:
+                       self.debugger.clear_all_file_breaks(filename)
+               self.debugger.update_allbreaks()
+       
+       def setup(self):
+               files = self.debugger.breaks.keys()
+               files.sort()
+               self.w.panes.files.set(files)
+       
+       def close(self):
+               self.debugger.breaksviewer = None
+               self.debugger = None
+       
+       def update(self):
+               sel = self.w.panes.files.getselectedobjects()
+               self.setup()
+               self.w.panes.files.setselectedobjects(sel)
+               sel = self.w.panes.files.getselection()
+               if len(sel) == 0 and self.curfile:
+                       self.w.panes.files.setselectedobjects([self.curfile])
+               self.filehit(0)
+       
+       def select(self):
+               self.w.select()
+       
+       def selectfile(self, file):
+               self.w.panes.files.setselectedobjects([file])
+               self.filehit(0)                 
+       
+       def openbuttonhit(self):
+               self.filehit(1)
+       
+       def filehit(self, isdbl):
+               sel = self.w.panes.files.getselectedobjects()
+               if isdbl:
+                       for filename in sel:
+                               lineno = None
+                               if filename == self.curfile:
+                                       linesel = self.w.panes.gr.breaks.getselectedobjects()
+                                       if linesel:
+                                               lineno = linesel[-1]
+                                       elif self.w.panes.gr.breaks:
+                                               lineno = self.w.panes.gr.breaks[0]
+                               editor = self.w._parentwindow.parent.openscript(filename, lineno)
+                               editor.showbreakpoints(1)
+                       return
+               if len(sel) == 1:
+                       file = sel[0]
+                       filebreaks = self.debugger.breaks[file][:]
+                       if self.curfile == file:
+                               linesel = self.w.panes.gr.breaks.getselectedobjects()
+                       self.w.panes.gr.breaks.set(filebreaks)
+                       if self.curfile == file:
+                               self.w.panes.gr.breaks.setselectedobjects(linesel)
+                       self.curfile = file
+               else:
+                       if len(sel) <> 0:
+                               self.curfile = None
+                       self.w.panes.gr.breaks.set([])
+               self.checkbuttons()
+       
+       def linehit(self, isdbl):
+               if isdbl:
+                       files = self.w.panes.files.getselectedobjects()
+                       if len(files) <> 1:
+                               return
+                       filename = files[0]
+                       linenos = self.w.panes.gr.breaks.getselectedobjects()
+                       if not linenos:
+                               return
+                       lineno = linenos[-1]
+                       editor = self.w._parentwindow.parent.openscript(filename, lineno)
+                       editor.showbreakpoints(1)
+               self.checkbuttons()
+       
+       def checkbuttons(self):
+               if self.w.panes.files.getselection():
+                       self.w.panes.gr.openbutton.enable(1)
+                       self.w._parentwindow.setdefaultbutton(self.w.panes.gr.openbutton)
+                       if self.w._currentwidget == self.w.panes.files:
+                               if self.w.panes.files.getselection():
+                                       self.w.panes.gr.deletebutton.enable(1)
+                               else:
+                                       self.w.panes.gr.deletebutton.enable(0)
+                       else:
+                               if self.w.panes.gr.breaks.getselection():
+                                       self.w.panes.gr.deletebutton.enable(1)
+                               else:
+                                       self.w.panes.gr.deletebutton.enable(0)
+               else:
+                       self.w.panes.gr.openbutton.enable(0)
+                       self.w.panes.gr.deletebutton.enable(0)
+
+
+class TracingMonitor(W.Widget):
+       
+       def __init__(self, *args, **kwargs):
+               apply(W.Widget.__init__, (self,) + args, kwargs)
+               self.state = 0
+       
+       def toggle(self):
+               if hasattr(self, "_parentwindow") and self._parentwindow is not None:
+                       self.state = self.state % 2 + 1
+                       port = Qd.GetPort()
+                       self.SetPort()
+                       self.draw()
+                       Qd.SetPort(port)
+       
+       def reset(self):
+               if self._parentwindow:
+                       self.state = 0
+                       port = Qd.GetPort()
+                       self.SetPort()
+                       self.draw()
+                       Qd.SetPort(port)
+       
+       def draw(self, visRgn = None):
+               if self.state == 2:
+                       Qd.PaintOval(self._bounds)
+               else:
+                       Qd.EraseOval(self._bounds)
+
+
+# convenience funcs
+
+def postmortem(exc_type, exc_value, tb):
+       d = getdebugger()
+       d.postmortem(exc_type, exc_value, tb)
+
+def start(bottomframe = None):
+       d = getdebugger()
+       d.start(bottomframe)
+
+def startfromhere():
+       d = getdebugger()
+       try:
+               raise 'spam'
+       except:
+               frame = sys.exc_traceback.tb_frame.f_back
+       d.start(frame)
+
+def startfrombottom():
+       d = getdebugger()
+       d.start(_getbottomframe(), 1)
+
+def stop():
+       d = getdebugger()
+       d.stop()
+
+def cont():
+       sys.settrace(None)
+       d = getdebugger()
+       d.set_continue_without_debugger()
+
+def _getbottomframe():
+       try:
+               raise 'spam'
+       except:
+               pass
+       frame = sys.exc_traceback.tb_frame
+       while 1:
+               if frame.f_code.co_name == 'mainloop' or frame.f_back is None:
+                       break
+               frame = frame.f_back
+       return frame
+
+_debugger = None
+
+def getdebugger():
+       if not __debug__:
+               raise W.AlertError, "CanĂ•t debug in Ă’Optimize bytecodeĂ“ mode.\r(see Ă’Default startup optionsĂ“ in EditPythonPreferences)"
+       global _debugger
+       if _debugger is None:
+               _debugger = Debugger()
+       return _debugger
diff --git a/Mac/Tools/IDE/PyDocSearch.py b/Mac/Tools/IDE/PyDocSearch.py
new file mode 100644 (file)
index 0000000..cd0fdce
--- /dev/null
@@ -0,0 +1,290 @@
+import aetools
+import Standard_Suite
+import Required_Suite
+import WWW_Suite
+import regex
+import W
+import macfs
+import os
+import MacPrefs
+import MacOS
+import string
+
+if hasattr(WWW_Suite, "WWW_Suite"):
+       WWW = WWW_Suite.WWW_Suite
+else:
+       WWW = WWW_Suite.WorldWideWeb_suite_2c__as_defined_in_Spyglass_spec_2e_
+
+class WebBrowser(aetools.TalkTo, 
+               Standard_Suite.Standard_Suite, 
+               WWW):
+       
+       def openfile(self, path, activate = 1):
+               if activate:
+                       self.activate()
+               self.OpenURL("file:///" + string.join(string.split(path,':'), '/'))
+
+app = W.getapplication()
+
+#SIGNATURE='MSIE' # MS Explorer
+SIGNATURE='MOSS' # Netscape
+
+_titlepat = regex.compile('<title>\([^<]*\)</title>')
+
+def sucktitle(path):
+       f = open(path)
+       text = f.read(1024) # assume the title is in the first 1024 bytes
+       f.close()
+       lowertext = string.lower(text)
+       if _titlepat.search(lowertext) > 0:
+               a, b = _titlepat.regs[1]
+               return text[a:b]
+       return path
+
+def verifydocpath(docpath):
+       try:
+               tut = os.path.join(docpath, "tut")
+               lib = os.path.join(docpath, "lib")
+               ref = os.path.join(docpath, "ref")
+               for path in [tut, lib, ref]:
+                       if not os.path.exists(path):
+                               return 0
+       except:
+               return 0
+       return 1
+
+
+class TwoLineList(W.List):
+       
+       LDEF_ID = 468
+       
+       def createlist(self):
+               import List
+               self._calcbounds()
+               self.SetPort()
+               rect = self._bounds
+               rect = rect[0]+1, rect[1]+1, rect[2]-16, rect[3]-1
+               self._list = List.LNew(rect, (0, 0, 1, 0), (0, 28), self.LDEF_ID, self._parentwindow.wid,
+                                       0, 1, 0, 1)
+               self.set(self.items)
+
+
+_resultscounter = 1
+
+class Results:
+       
+       def __init__(self, hits):
+               global _resultscounter
+               hits = map(lambda (path, hits): (sucktitle(path), path, hits), hits)
+               hits.sort()
+               self.hits = hits
+               nicehits = map(
+                               lambda (title, path, hits):
+                               title + '\r' + string.join(
+                               map(lambda (c, p): "%s (%d)" % (p, c), hits), ', '), hits)
+               nicehits.sort()
+               self.w = W.Window((440, 300), "Search results %d" % _resultscounter, minsize = (200, 100))
+               self.w.results = TwoLineList((-1, -1, 1, -14), nicehits, self.listhit)
+               self.w.open()
+               self.w.bind('return', self.listhit)
+               self.w.bind('enter', self.listhit)
+               _resultscounter = _resultscounter + 1
+               self.browser = None
+       
+       def listhit(self, isdbl = 1):
+               if isdbl:
+                       for i in self.w.results.getselection():
+                               if self.browser is None:
+                                       self.browser = WebBrowser(SIGNATURE, start = 1)
+                               self.browser.openfile(self.hits[i][1])
+
+class Status:
+       
+       def __init__(self):
+               self.w = W.Dialog((440, 64), "SearchingÉ")
+               self.w.searching = W.TextBox((4, 4, -4, 16), "DevDev:PyPyDoc 1.5.1:ext:parseTupleAndKeywords.html")
+               self.w.hits = W.TextBox((4, 24, -4, 16), "Hits: 0")
+               self.w.canceltip = W.TextBox((4, 44, -4, 16), "Type cmd-period (.) to cancel.")
+               self.w.open()
+       
+       def set(self, path, hits):
+               self.w.searching.set(path)
+               self.w.hits.set('Hits: ' + `hits`)
+               app.breathe()
+       
+       def close(self):
+               self.w.close()
+
+
+def match(text, patterns, all):
+       hits = []
+       hitsappend = hits.append
+       stringcount = string.count
+       for pat in patterns:
+               c = stringcount(text, pat)
+               if c > 0:
+                       hitsappend((c, pat))
+               elif all:
+                       hits[:] = []
+                       break
+       hits.sort()
+       hits.reverse()
+       return hits
+
+def dosearch(docpath, searchstring, settings):
+       (docpath, kind, case, word, tut, lib, ref, ext, api) = settings
+       books = [(tut, 'tut'), (lib, 'lib'), (ref, 'ref'), (ext, 'ext'), (api, 'api')]
+       if not case:
+               searchstring = string.lower(searchstring)
+       
+       if kind == 1:
+               patterns = string.split(searchstring)
+               all = 1
+       elif kind == 2:
+               patterns = string.split(searchstring)
+               all = 0
+       else:
+               patterns = [searchstring]
+               all = 0 # not relevant
+       
+       ospathjoin = os.path.join
+       stringlower = string.lower
+       status = Status()
+       statusset = status.set
+       _match = match
+       _open = open
+       hits = {}
+       try:
+               MacOS.EnableAppswitch(0)
+               try:
+                       for do, name in books:
+                               if not do:
+                                       continue
+                               bookpath = ospathjoin(docpath, name)
+                               if not os.path.exists(bookpath):
+                                       continue
+                               files = os.listdir(bookpath)
+                               for file in files:
+                                       fullpath = ospathjoin(bookpath, file)
+                                       if fullpath[-5:] <> '.html':
+                                               continue
+                                       statusset(fullpath, len(hits))
+                                       f = _open(fullpath)
+                                       text = f.read()
+                                       if not case:
+                                               text = stringlower(text)
+                                       f.close()
+                                       filehits = _match(text, patterns, all)
+                                       if filehits:
+                                               hits[fullpath] = filehits
+               finally:
+                       MacOS.EnableAppswitch(-1)
+                       status.close()
+       except KeyboardInterrupt:
+               pass
+       hits = hits.items()
+       hits.sort()
+       return hits
+
+
+class PyDocSearch:
+       
+       def __init__(self):
+               prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
+               try:
+                       (docpath, kind, case, word, tut, lib, ref, ext, api) = prefs.docsearchengine
+               except:
+                       (docpath, kind, case, word, tut, lib, ref, ext, api) = prefs.docsearchengine = \
+                               ("", 0, 0, 0, 1, 1, 0, 0, 0)
+               
+               if docpath and not verifydocpath(docpath):
+                       docpath = ""
+               
+               self.w = W.Window((400, 200), "Search the Python Documentation")
+               self.w.searchtext = W.EditText((10, 10, -100, 20), callback = self.checkbuttons)
+               self.w.searchbutton = W.Button((-90, 12, 80, 16), "Search", self.search)
+               buttons = []
+               
+               gutter = 10
+               width = 130
+               bookstart = width + 2 * gutter
+               self.w.phraseradio = W.RadioButton((10, 38, width, 16), "As a phrase", buttons)
+               self.w.allwordsradio = W.RadioButton((10, 58, width, 16), "All words", buttons)
+               self.w.anywordsradio = W.RadioButton((10, 78, width, 16), "Any word", buttons)
+               self.w.casesens = W.CheckBox((10, 98, width, 16), "Case sensitive")
+               self.w.wholewords = W.CheckBox((10, 118, width, 16), "Whole words")
+               self.w.tutorial = W.CheckBox((bookstart, 38, -10, 16), "Tutorial")
+               self.w.library = W.CheckBox((bookstart, 58, -10, 16), "Library reference")
+               self.w.langueref = W.CheckBox((bookstart, 78, -10, 16), "Lanuage reference manual")
+               self.w.extending = W.CheckBox((bookstart, 98, -10, 16), "Extending & embedding")
+               self.w.api = W.CheckBox((bookstart, 118, -10, 16), "C/C++ API")
+               
+               self.w.setdocfolderbutton = W.Button((10, -30, 80, 16), "Set doc folder", self.setdocpath)
+               
+               if docpath:
+                       self.w.setdefaultbutton(self.w.searchbutton)
+               else:
+                       self.w.setdefaultbutton(self.w.setdocfolderbutton)
+               
+               self.docpath = docpath
+               if not docpath:
+                       docpath = "(please select the Python html documentation folder)"
+               self.w.docfolder = W.TextBox((100, -28, -10, 16), docpath)
+               
+               [self.w.phraseradio, self.w.allwordsradio, self.w.anywordsradio][kind].set(1)
+               
+               self.w.casesens.set(case)
+               self.w.wholewords.set(word)
+               self.w.tutorial.set(tut)
+               self.w.library.set(lib)
+               self.w.langueref.set(ref)
+               self.w.extending.set(ext)
+               self.w.api.set(api)
+               
+               self.w.open()
+               self.w.wholewords.enable(0)
+               self.w.bind('<close>', self.close)
+               self.w.searchbutton.enable(0)
+       
+       def search(self):
+               hits = dosearch(self.docpath, self.w.searchtext.get(), self.getsettings())
+               if hits:
+                       Results(hits)
+               elif hasattr(MacOS, 'SysBeep'):
+                       MacOS.SysBeep(0)
+               #import PyBrowser
+               #PyBrowser.Browser(hits)
+       
+       def setdocpath(self):
+               fss, ok = macfs.GetDirectory()
+               if ok:
+                       docpath = fss.as_pathname()
+                       if not verifydocpath(docpath):
+                               W.Message("This does not seem to be a Python documentation folder...")
+                       else:
+                               self.docpath = docpath
+                               self.w.docfolder.set(docpath)
+                               self.w.setdefaultbutton(self.w.searchbutton)
+       
+       def close(self):
+               prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
+               prefs.docsearchengine = self.getsettings()
+       
+       def getsettings(self):
+               radiobuttons = [self.w.phraseradio, self.w.allwordsradio, self.w.anywordsradio]
+               for i in range(3):
+                       if radiobuttons[i].get():
+                               kind = i
+                               break
+               docpath = self.docpath
+               case = self.w.casesens.get()
+               word = self.w.wholewords.get()
+               tut = self.w.tutorial.get()
+               lib = self.w.library.get()
+               ref = self.w.langueref.get()
+               ext = self.w.extending.get()
+               api = self.w.api.get()
+               return (docpath, kind, case, word, tut, lib, ref, ext, api)
+       
+       def checkbuttons(self):
+               self.w.searchbutton.enable(not not self.w.searchtext.get())
diff --git a/Mac/Tools/IDE/PyEdit.py b/Mac/Tools/IDE/PyEdit.py
new file mode 100644 (file)
index 0000000..f885d04
--- /dev/null
@@ -0,0 +1,1126 @@
+"""A (less & less) simple Python editor"""
+
+import W
+import Wtraceback
+from Wkeys import *
+
+import macfs
+import MacOS
+import Win
+import Res
+import Evt
+import os
+import imp
+import sys
+import string
+import marshal
+import regex
+
+_scriptuntitledcounter = 1
+_wordchars = string.letters + string.digits + "_"
+
+
+class Editor(W.Window):
+       
+       def __init__(self, path = "", title = ""):
+               defaultfontsettings, defaulttabsettings, defaultwindowsize = geteditorprefs()
+               global _scriptuntitledcounter
+               if not path:
+                       if title:
+                               self.title = title
+                       else:
+                               self.title = "Untitled Script " + `_scriptuntitledcounter`
+                               _scriptuntitledcounter = _scriptuntitledcounter + 1
+                       text = ""
+                       self._creator = W._signature
+               elif os.path.exists(path):
+                       path = resolvealiases(path)
+                       dir, name = os.path.split(path)
+                       self.title = name
+                       f = open(path, "rb")
+                       text = f.read()
+                       f.close()
+                       fss = macfs.FSSpec(path)
+                       self._creator, filetype = fss.GetCreatorType()
+               else:
+                       raise IOError, "file '%s' does not exist" % path
+               self.path = path
+               
+               self.settings = {}
+               if self.path:
+                       self.readwindowsettings()
+               if self.settings.has_key("windowbounds"):
+                       bounds = self.settings["windowbounds"]
+               else:
+                       bounds = defaultwindowsize
+               if self.settings.has_key("fontsettings"):
+                       self.fontsettings = self.settings["fontsettings"]
+               else:
+                       self.fontsettings = defaultfontsettings
+               if self.settings.has_key("tabsize"):
+                       try:
+                               self.tabsettings = (tabsize, tabmode) = self.settings["tabsize"]
+                       except:
+                               self.tabsettings = defaulttabsettings
+               else:
+                       self.tabsettings = defaulttabsettings
+               W.Window.__init__(self, bounds, self.title, minsize = (330, 120), tabbable = 0)
+               
+               self.setupwidgets(text)
+               if self.settings.has_key("selection"):
+                       selstart, selend = self.settings["selection"]
+                       self.setselection(selstart, selend)
+               self.open()
+               self.setinfotext()
+               self.globals = {}
+               self._buf = ""  # for write method
+               self.debugging = 0
+               self.profiling = 0
+               if self.settings.has_key("run_as_main"):
+                       self.run_as_main = self.settings["run_as_main"]
+               else:
+                       self.run_as_main = 0
+       
+       def readwindowsettings(self):
+               try:
+                       resref = Res.OpenResFile(self.path)
+               except Res.Error:
+                       return
+               try:
+                       Res.UseResFile(resref)
+                       data = Res.Get1Resource('PyWS', 128)
+                       self.settings = marshal.loads(data.data)
+               except:
+                       pass
+               Res.CloseResFile(resref)
+               
+       def writewindowsettings(self):
+               try:
+                       resref = Res.OpenResFile(self.path)
+               except Res.Error:
+                       Res.CreateResFile(self.path)
+                       resref = Res.OpenResFile(self.path)
+               try:
+                       data = Res.Resource(marshal.dumps(self.settings))
+                       Res.UseResFile(resref)
+                       try:
+                               temp = Res.Get1Resource('PyWS', 128)
+                               temp.RemoveResource()
+                       except Res.Error:
+                               pass
+                       data.AddResource('PyWS', 128, "window settings")
+               finally:
+                       Res.UpdateResFile(resref)
+                       Res.CloseResFile(resref)
+       
+       def getsettings(self):
+               self.settings = {}
+               self.settings["windowbounds"] = self.getbounds()
+               self.settings["selection"] = self.getselection()
+               self.settings["fontsettings"] = self.editgroup.editor.getfontsettings()
+               self.settings["tabsize"] = self.editgroup.editor.gettabsettings()
+               self.settings["run_as_main"] = self.run_as_main
+       
+       def get(self):
+               return self.editgroup.editor.get()
+       
+       def getselection(self):
+               return self.editgroup.editor.ted.WEGetSelection()
+       
+       def setselection(self, selstart, selend):
+               self.editgroup.editor.setselection(selstart, selend)
+       
+       def getfilename(self):
+               if self.path:
+                       return self.path
+               return '<%s>' % self.title
+       
+       def setupwidgets(self, text):
+               topbarheight = 24
+               popfieldwidth = 80
+               self.lastlineno = None
+               
+               # make an editor
+               self.editgroup = W.Group((0, topbarheight + 1, 0, 0))
+               editor = W.PyEditor((0, 0, -15,-15), text, 
+                               fontsettings = self.fontsettings, 
+                               tabsettings = self.tabsettings,
+                               file = self.getfilename())
+               
+               # make the widgets
+               self.popfield = ClassFinder((popfieldwidth - 17, -15, 16, 16), [], self.popselectline)
+               self.linefield = W.EditText((-1, -15, popfieldwidth - 15, 16), inset = (6, 1))
+               self.editgroup._barx = W.Scrollbar((popfieldwidth - 2, -15, -14, 16), editor.hscroll, max = 32767)
+               self.editgroup._bary = W.Scrollbar((-15, 14, 16, -14), editor.vscroll, max = 32767)
+               self.editgroup.editor = editor  # add editor *after* scrollbars
+               
+               self.editgroup.optionsmenu = W.PopupMenu((-15, -1, 16, 16), [])
+               self.editgroup.optionsmenu.bind('<click>', self.makeoptionsmenu)
+               
+               self.bevelbox = W.BevelBox((0, 0, 0, topbarheight))
+               self.hline = W.HorizontalLine((0, topbarheight, 0, 0))
+               self.infotext = W.TextBox((175, 6, -4, 14), backgroundcolor = (0xe000, 0xe000, 0xe000))
+               self.runbutton = W.Button((5, 4, 80, 16), "Run all", self.run)
+               self.runselbutton = W.Button((90, 4, 80, 16), "Run selection", self.runselection)
+               
+               # bind some keys
+               editor.bind("cmdr", self.runbutton.push)
+               editor.bind("enter", self.runselbutton.push)
+               editor.bind("cmdj", self.domenu_gotoline)
+               editor.bind("cmdd", self.domenu_toggledebugger)
+               editor.bind("<idle>", self.updateselection)
+               
+               editor.bind("cmde", searchengine.setfindstring)
+               editor.bind("cmdf", searchengine.show)
+               editor.bind("cmdg", searchengine.findnext)
+               editor.bind("cmdshiftr", searchengine.replace)
+               editor.bind("cmdt", searchengine.replacefind)
+               
+               self.linefield.bind("return", self.dolinefield)
+               self.linefield.bind("enter", self.dolinefield)
+               self.linefield.bind("tab", self.dolinefield)
+               
+               # intercept clicks
+               editor.bind("<click>", self.clickeditor)
+               self.linefield.bind("<click>", self.clicklinefield)
+       
+       def makeoptionsmenu(self):
+               menuitems = [('Font settingsÉ', self.domenu_fontsettings), 
+                               ('\0' + chr(self.run_as_main) + 'Run as __main__', self.domenu_toggle_run_as_main), 
+                               ('Modularize', self.domenu_modularize),
+                               ('Browse namespaceÉ', self.domenu_browsenamespace), 
+                               '-']
+               if self.profiling:
+                       menuitems = menuitems + [('Disable profiler', self.domenu_toggleprofiler)]
+               else:
+                       menuitems = menuitems + [('Enable profiler', self.domenu_toggleprofiler)]
+               if self.editgroup.editor._debugger:
+                       menuitems = menuitems + [('Disable debugger', self.domenu_toggledebugger),
+                               ('Clear breakpoints', self.domenu_clearbreakpoints),
+                               ('Edit breakpointsÉ', self.domenu_editbreakpoints)]
+               else:
+                       menuitems = menuitems + [('Enable debugger', self.domenu_toggledebugger)]
+               self.editgroup.optionsmenu.set(menuitems)
+       
+       def domenu_toggle_run_as_main(self):
+               self.run_as_main = not self.run_as_main
+               self.editgroup.editor.selchanged = 1
+       
+       def showbreakpoints(self, onoff):
+               self.editgroup.editor.showbreakpoints(onoff)
+               self.debugging = onoff
+       
+       def domenu_clearbreakpoints(self, *args):
+               self.editgroup.editor.clearbreakpoints()
+       
+       def domenu_editbreakpoints(self, *args):
+               self.editgroup.editor.editbreakpoints()
+       
+       def domenu_toggledebugger(self, *args):
+               if not self.debugging:
+                       W.SetCursor('watch')
+               self.debugging = not self.debugging
+               self.editgroup.editor.togglebreakpoints()
+               
+       def domenu_toggleprofiler(self, *args):
+               self.profiling = not self.profiling
+       
+       def domenu_browsenamespace(self, *args):
+               import PyBrowser, W
+               W.SetCursor('watch')
+               globals, file, modname = self.getenvironment()
+               if not modname:
+                       modname = self.title
+               PyBrowser.Browser(globals, "Object browser: " + modname)
+       
+       def domenu_modularize(self, *args):
+               modname = _filename_as_modname(self.title)
+               if not modname:
+                       raise W.AlertError, 'CanĂ•t modularize Ă’%sĂ“' % self.title
+               run_as_main = self.run_as_main
+               self.run_as_main = 0
+               self.run()
+               self.run_as_main = run_as_main
+               if self.path:
+                       file = self.path
+               else:
+                       file = self.title
+               
+               if self.globals and not sys.modules.has_key(modname):
+                       module = imp.new_module(modname)
+                       for attr in self.globals.keys():
+                               setattr(module,attr,self.globals[attr])
+                       sys.modules[modname] = module
+                       self.globals = {}
+       
+       def domenu_fontsettings(self, *args):
+               import FontSettings
+               fontsettings = self.editgroup.editor.getfontsettings()
+               tabsettings = self.editgroup.editor.gettabsettings()
+               settings = FontSettings.FontDialog(fontsettings, tabsettings)
+               if settings:
+                       fontsettings, tabsettings = settings
+                       self.editgroup.editor.setfontsettings(fontsettings)
+                       self.editgroup.editor.settabsettings(tabsettings)
+       
+       def clicklinefield(self):
+               if self._currentwidget <> self.linefield:
+                       self.linefield.select(1)
+                       self.linefield.selectall()
+                       return 1
+       
+       def clickeditor(self):
+               if self._currentwidget <> self.editgroup.editor:
+                       self.dolinefield()
+                       return 1
+       
+       def updateselection(self, force = 0):
+               sel = min(self.editgroup.editor.getselection())
+               lineno = self.editgroup.editor.offsettoline(sel)
+               if lineno <> self.lastlineno or force:
+                       self.lastlineno = lineno
+                       self.linefield.set(str(lineno + 1))
+                       self.linefield.selview()
+       
+       def dolinefield(self):
+               try:
+                       lineno = string.atoi(self.linefield.get()) - 1
+                       if lineno <> self.lastlineno:
+                               self.editgroup.editor.selectline(lineno)
+                               self.updateselection(1)
+               except:
+                       self.updateselection(1)
+               self.editgroup.editor.select(1)
+       
+       def setinfotext(self):
+               if not hasattr(self, 'infotext'):
+                       return
+               if self.path:
+                       self.infotext.set(self.path)
+               else:
+                       self.infotext.set("")
+       
+       def close(self):
+               if self.editgroup.editor.changed:
+                       import EasyDialogs
+                       import Qd
+                       Qd.InitCursor() # XXX should be done by dialog
+                       save = EasyDialogs.AskYesNoCancel('Save window Ă’%sĂ“ before closing?' % self.title, 1)
+                       if save > 0:
+                               if self.domenu_save():
+                                       return 1
+                       elif save < 0:
+                               return 1
+               self.globals = None          # XXX doesn't help... all globals leak :-(
+               W.Window.close(self)
+       
+       def domenu_close(self, *args):
+               return self.close()
+       
+       def domenu_save(self, *args):
+               if not self.path:
+                       # Will call us recursively
+                       return self.domenu_save_as()
+               data = self.editgroup.editor.get()
+               fp = open(self.path, 'wb')  # open file in binary mode, data has '\r' line-endings
+               fp.write(data)
+               fp.close()
+               fss = macfs.FSSpec(self.path)
+               fss.SetCreatorType(self._creator, 'TEXT')
+               self.getsettings()
+               self.writewindowsettings()
+               self.editgroup.editor.changed = 0
+               self.editgroup.editor.selchanged = 0
+               import linecache
+               if linecache.cache.has_key(self.path):
+                       del linecache.cache[self.path]
+               import macostools
+               macostools.touched(self.path)
+       
+       def can_save(self, menuitem):
+               return self.editgroup.editor.changed or self.editgroup.editor.selchanged
+       
+       def domenu_save_as(self, *args):
+               fss, ok = macfs.StandardPutFile('Save as:', self.title)
+               if not ok: 
+                       return 1
+               self.showbreakpoints(0)
+               self.path = fss.as_pathname()
+               self.setinfotext()
+               self.title = os.path.split(self.path)[-1]
+               self.wid.SetWTitle(self.title)
+               self.domenu_save()
+               self.editgroup.editor.setfile(self.getfilename())
+               app = W.getapplication()
+               app.makeopenwindowsmenu()
+               if hasattr(app, 'makescriptsmenu'):
+                       app = W.getapplication()
+                       fss, fss_changed = app.scriptsfolder.Resolve()
+                       path = fss.as_pathname()
+                       if path == self.path[:len(path)]:
+                               W.getapplication().makescriptsmenu()
+       
+       def domenu_save_as_applet(self, *args):
+               try:
+                       import buildtools
+               except ImportError:
+                       # only have buildtools in Python >= 1.5.2
+                       raise W.AlertError, "Ă’Save as AppletĂ“ is only supported in\rPython 1.5.2 and up."
+               
+               buildtools.DEBUG = 0    # ouch.
+               
+               if self.title[-3:] == ".py":
+                       destname = self.title[:-3]
+               else:
+                       destname = self.title + ".applet"
+               fss, ok = macfs.StandardPutFile('Save as Applet:', destname)
+               if not ok: 
+                       return 1
+               W.SetCursor("watch")
+               destname = fss.as_pathname()
+               if self.path:
+                       filename = self.path
+                       if filename[-3:] == ".py":
+                               rsrcname = filename[:-3] + '.rsrc'
+                       else:
+                               rsrcname = filename + '.rsrc'
+               else:
+                       filename = self.title
+                       rsrcname = ""
+               
+               pytext = self.editgroup.editor.get()
+               pytext = string.split(pytext, '\r')
+               pytext = string.join(pytext, '\n') + '\n'
+               try:
+                       code = compile(pytext, filename, "exec")
+               except (SyntaxError, EOFError):
+                       raise buildtools.BuildError, "Syntax error in script %s" % `filename`
+               
+               # Try removing the output file
+               try:
+                       os.remove(destname)
+               except os.error:
+                       pass
+               template = buildtools.findtemplate()
+               buildtools.process_common(template, None, code, rsrcname, destname, 0, 1)
+       
+       def domenu_gotoline(self, *args):
+               self.linefield.selectall()
+               self.linefield.select(1)
+               self.linefield.selectall()
+       
+       def domenu_selectline(self, *args):
+               self.editgroup.editor.expandselection()
+       
+       def domenu_find(self, *args):
+               searchengine.show()
+       
+       def domenu_entersearchstring(self, *args):
+               searchengine.setfindstring()
+       
+       def domenu_replace(self, *args):
+               searchengine.replace()
+       
+       def domenu_findnext(self, *args):
+               searchengine.findnext()
+       
+       def domenu_replacefind(self, *args):
+               searchengine.replacefind()
+       
+       def domenu_run(self, *args):
+               self.runbutton.push()
+       
+       def domenu_runselection(self, *args):
+               self.runselbutton.push()
+       
+       def run(self):
+               self._run()
+       
+       def _run(self):
+               pytext = self.editgroup.editor.get()
+               globals, file, modname = self.getenvironment()
+               self.execstring(pytext, globals, globals, file, modname)
+       
+       def runselection(self):
+               self._runselection()
+       
+       def _runselection(self):
+               globals, file, modname = self.getenvironment()
+               locals = globals
+               # select whole lines
+               self.editgroup.editor.expandselection()
+               
+               # get lineno of first selected line
+               selstart, selend = self.editgroup.editor.getselection()
+               selstart, selend = min(selstart, selend), max(selstart, selend)
+               selfirstline = self.editgroup.editor.offsettoline(selstart)
+               alltext = self.editgroup.editor.get()
+               pytext = alltext[selstart:selend]
+               lines = string.split(pytext, '\r')
+               indent = getminindent(lines)
+               if indent == 1:
+                       classname = ''
+                       alllines = string.split(alltext, '\r')
+                       identifieRE_match = _identifieRE.match
+                       for i in range(selfirstline - 1, -1, -1):
+                               line = alllines[i]
+                               if line[:6] == 'class ':
+                                       classname = string.split(string.strip(line[6:]))[0]
+                                       classend = identifieRE_match(classname)
+                                       if classend < 1:
+                                               raise W.AlertError, 'CanĂ•t find a class.'
+                                       classname = classname[:classend]
+                                       break
+                               elif line and line[0] not in '\t#':
+                                       raise W.AlertError, 'CanĂ•t find a class.'
+                       else:
+                               raise W.AlertError, 'CanĂ•t find a class.'
+                       if globals.has_key(classname):
+                               locals = globals[classname].__dict__
+                       else:
+                               raise W.AlertError, 'CanĂ•t find class Ă’%sĂ“.' % classname
+                       # dedent to top level
+                       for i in range(len(lines)):
+                               lines[i] = lines[i][1:]
+                       pytext = string.join(lines, '\r')
+               elif indent > 0:
+                       raise W.AlertError, 'CanĂ•t run indented code.'
+               
+               # add "newlines" to fool compile/exec: 
+               # now a traceback will give the right line number
+               pytext = selfirstline * '\r' + pytext
+               self.execstring(pytext, globals, locals, file, modname)
+       
+       def execstring(self, pytext, globals, locals, file, modname):
+               tracebackwindow.hide()
+               # update windows
+               W.getapplication().refreshwindows()
+               if self.run_as_main:
+                       modname = "__main__"
+               if self.path:
+                       dir = os.path.dirname(self.path)
+                       savedir = os.getcwd()
+                       os.chdir(dir)
+                       try:
+                               cwdindex = sys.path.index(os.curdir)
+                       except ValueError:
+                               cwdindex = None
+                       else:
+                               sys.path[cwdindex] = dir
+               else:
+                       cwdindex = None
+               try:
+                       execstring(pytext, globals, locals, file, self.debugging, 
+                                       modname, self.profiling)
+               finally:
+                       if self.path:
+                               os.chdir(savedir)
+                               if cwdindex is not None:
+                                       sys.path[cwdindex] = os.curdir
+       
+       def getenvironment(self):
+               if self.path:
+                       file = self.path
+                       dir = os.path.dirname(file)
+                       # check if we're part of a package
+                       modname = ""
+                       while os.path.exists(os.path.join(dir, "__init__.py")):
+                               dir, dirname = os.path.split(dir)
+                               modname = modname + dirname + '.'
+                       subname = _filename_as_modname(self.title)
+                       if modname:
+                               if subname == "__init__":
+                                       modname  = modname[:-1]  # strip trailing period
+                               else:
+                                       modname  = modname + subname
+                       else:
+                               modname = subname
+                       if sys.modules.has_key(modname):
+                               globals = sys.modules[modname].__dict__
+                               self.globals = {}
+                       else:
+                               globals = self.globals
+               else:
+                       file = '<%s>' % self.title
+                       globals = self.globals
+                       modname = file
+               return globals, file, modname
+       
+       def write(self, stuff):
+               """for use as stdout"""
+               self._buf = self._buf + stuff
+               if '\n' in self._buf:
+                       self.flush()
+       
+       def flush(self):
+               stuff = string.split(self._buf, '\n')
+               stuff = string.join(stuff, '\r')
+               end = self.editgroup.editor.ted.WEGetTextLength()
+               self.editgroup.editor.ted.WESetSelection(end, end)
+               self.editgroup.editor.ted.WEInsert(stuff, None, None)
+               self.editgroup.editor.updatescrollbars()
+               self._buf = ""
+               # ? optional:
+               #self.wid.SelectWindow()
+       
+       def getclasslist(self):
+               from string import find, strip
+               editor = self.editgroup.editor
+               text = editor.get()
+               list = []
+               append = list.append
+               functag = "func"
+               classtag = "class"
+               methodtag = "method"
+               pos = -1
+               if text[:4] == 'def ':
+                       append((pos + 4, functag))
+                       pos = 4
+               while 1:
+                       pos = find(text, '\rdef ', pos + 1)
+                       if pos < 0:
+                               break
+                       append((pos + 5, functag))
+               pos = -1
+               if text[:6] == 'class ':
+                       append((pos + 6, classtag))
+                       pos = 6
+               while 1:
+                       pos = find(text, '\rclass ', pos + 1)
+                       if pos < 0:
+                               break
+                       append((pos + 7, classtag))
+               pos = 0
+               while 1:
+                       pos = find(text, '\r\tdef ', pos + 1)
+                       if pos < 0:
+                               break
+                       append((pos + 6, methodtag))
+               list.sort()
+               classlist = []
+               methodlistappend = None
+               offsetToLine = editor.ted.WEOffsetToLine
+               getLineRange = editor.ted.WEGetLineRange
+               append = classlist.append
+               identifieRE_match = _identifieRE.match
+               for pos, tag in list:
+                       lineno = offsetToLine(pos)
+                       lineStart, lineEnd = getLineRange(lineno)
+                       line = strip(text[pos:lineEnd])
+                       line = line[:identifieRE_match(line)]
+                       if tag is functag:
+                               append(("def " + line, lineno + 1))
+                               methodlistappend = None
+                       elif tag is classtag:
+                               append(["class " + line])
+                               methodlistappend = classlist[-1].append
+                       elif methodlistappend and tag is methodtag:
+                               methodlistappend(("def " + line, lineno + 1))
+               return classlist
+       
+       def popselectline(self, lineno):
+               self.editgroup.editor.selectline(lineno - 1)
+       
+       def selectline(self, lineno, charoffset = 0):
+               self.editgroup.editor.selectline(lineno - 1, charoffset)
+
+
+def _escape(where, what) : 
+       return string.join(string.split(where, what), '\\' + what)
+
+def _makewholewordpattern(word):
+       # first, escape special regex chars
+       for esc in "\\[].*^+$?":
+               word = _escape(word, esc)
+       import regex
+       notwordcharspat = '[^' + _wordchars + ']'
+       pattern = '\(' + word + '\)'
+       if word[0] in _wordchars:
+               pattern = notwordcharspat + pattern
+       if word[-1] in _wordchars:
+               pattern = pattern + notwordcharspat
+       return regex.compile(pattern)
+
+class SearchEngine:
+       
+       def __init__(self):
+               self.visible = 0
+               self.w = None
+               self.parms = {  "find": "",
+                                       "replace": "",
+                                       "wrap": 1,
+                                       "casesens": 1,
+                                       "wholeword": 1
+                               }
+               import MacPrefs
+               prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
+               if prefs.searchengine:
+                       self.parms["casesens"] = prefs.searchengine.casesens
+                       self.parms["wrap"] = prefs.searchengine.wrap
+                       self.parms["wholeword"] = prefs.searchengine.wholeword
+       
+       def show(self):
+               self.visible = 1
+               if self.w:
+                       self.w.wid.ShowWindow()
+                       self.w.wid.SelectWindow()
+                       self.w.find.edit.select(1)
+                       self.w.find.edit.selectall()
+                       return
+               self.w = W.Dialog((420, 150), "Find")
+               
+               self.w.find = TitledEditText((10, 4, 300, 36), "Search for:")
+               self.w.replace = TitledEditText((10, 100, 300, 36), "Replace with:")
+               
+               self.w.boxes = W.Group((10, 50, 300, 40))
+               self.w.boxes.casesens = W.CheckBox((0, 0, 100, 16), "Case sensitive")
+               self.w.boxes.wholeword = W.CheckBox((0, 20, 100, 16), "Whole word")
+               self.w.boxes.wrap = W.CheckBox((110, 0, 100, 16), "Wrap around")
+               
+               self.buttons = [        ("Find",                "cmdf",  self.find), 
+                                       ("Replace",          "cmdr",     self.replace), 
+                                       ("Replace all",  None,   self.replaceall), 
+                                       ("DonĂ•t find",  "cmdd",  self.dont), 
+                                       ("Cancel",            "cmd.",    self.cancel)
+                               ]
+               for i in range(len(self.buttons)):
+                       bounds = -90, 22 + i * 24, 80, 16
+                       title, shortcut, callback = self.buttons[i]
+                       self.w[title] = W.Button(bounds, title, callback)
+                       if shortcut:
+                               self.w.bind(shortcut, self.w[title].push)
+               self.w.setdefaultbutton(self.w["DonĂ•t find"])
+               self.w.find.edit.bind("<key>", self.key)
+               self.w.bind("<activate>", self.activate)
+               self.w.bind("<close>", self.close)
+               self.w.open()
+               self.setparms()
+               self.w.find.edit.select(1)
+               self.w.find.edit.selectall()
+               self.checkbuttons()
+       
+       def close(self):
+               self.hide()
+               return -1
+       
+       def key(self, char, modifiers):
+               self.w.find.edit.key(char, modifiers)
+               self.checkbuttons()
+               return 1
+       
+       def activate(self, onoff):
+               if onoff:
+                       self.checkbuttons()
+       
+       def checkbuttons(self):
+               editor = findeditor(self)
+               if editor:
+                       if self.w.find.get():
+                               for title, cmd, call in self.buttons[:-2]:
+                                       self.w[title].enable(1)
+                               self.w.setdefaultbutton(self.w["Find"])
+                       else:
+                               for title, cmd, call in self.buttons[:-2]:
+                                       self.w[title].enable(0)
+                               self.w.setdefaultbutton(self.w["DonĂ•t find"])
+               else:
+                       for title, cmd, call in self.buttons[:-2]:
+                               self.w[title].enable(0)
+                       self.w.setdefaultbutton(self.w["DonĂ•t find"])
+       
+       def find(self):
+               self.getparmsfromwindow()
+               if self.findnext():
+                       self.hide()
+       
+       def replace(self):
+               editor = findeditor(self)
+               if not editor:
+                       return
+               if self.visible:
+                       self.getparmsfromwindow()
+               text = editor.getselectedtext()
+               find = self.parms["find"]
+               if not self.parms["casesens"]:
+                       find = string.lower(find)
+                       text = string.lower(text)
+               if text == find:
+                       self.hide()
+                       editor.insert(self.parms["replace"])
+       
+       def replaceall(self):
+               editor = findeditor(self)
+               if not editor:
+                       return
+               if self.visible:
+                       self.getparmsfromwindow()
+               W.SetCursor("watch")
+               find = self.parms["find"]
+               if not find:
+                       return
+               findlen = len(find)
+               replace = self.parms["replace"]
+               replacelen = len(replace)
+               Text = editor.get()
+               if not self.parms["casesens"]:
+                       find = string.lower(find)
+                       text = string.lower(Text)
+               else:
+                       text = Text
+               newtext = ""
+               pos = 0
+               counter = 0
+               while 1:
+                       if self.parms["wholeword"]:
+                               wholewordRE = _makewholewordpattern(find)
+                               wholewordRE.search(text, pos)
+                               if wholewordRE.regs:
+                                       pos = wholewordRE.regs[1][0]
+                               else:
+                                       pos = -1
+                       else:
+                               pos = string.find(text, find, pos)
+                       if pos < 0:
+                               break
+                       counter = counter + 1
+                       text = text[:pos] + replace + text[pos + findlen:]
+                       Text = Text[:pos] + replace + Text[pos + findlen:]
+                       pos = pos + replacelen
+               W.SetCursor("arrow")
+               if counter:
+                       self.hide()
+                       import EasyDialogs
+                       import Res
+                       editor.changed = 1
+                       editor.selchanged = 1
+                       editor.ted.WEUseText(Res.Resource(Text))
+                       editor.ted.WECalText()
+                       editor.SetPort()
+                       Win.InvalRect(editor._bounds)
+                       #editor.ted.WEUpdate(self.w.wid.GetWindowPort().visRgn)
+                       EasyDialogs.Message("Replaced %d occurrences" % counter)
+       
+       def dont(self):
+               self.getparmsfromwindow()
+               self.hide()
+       
+       def replacefind(self):
+               self.replace()
+               self.findnext()
+       
+       def setfindstring(self):
+               editor = findeditor(self)
+               if not editor:
+                       return
+               find = editor.getselectedtext()
+               if not find:
+                       return
+               self.parms["find"] = find
+               if self.w:
+                       self.w.find.edit.set(self.parms["find"])
+                       self.w.find.edit.selectall()
+       
+       def findnext(self):
+               editor = findeditor(self)
+               if not editor:
+                       return
+               find = self.parms["find"]
+               if not find:
+                       return
+               text = editor.get()
+               if not self.parms["casesens"]:
+                       find = string.lower(find)
+                       text = string.lower(text)
+               selstart, selend = editor.getselection()
+               selstart, selend = min(selstart, selend), max(selstart, selend)
+               if self.parms["wholeword"]:
+                       wholewordRE = _makewholewordpattern(find)
+                       wholewordRE.search(text, selend)
+                       if wholewordRE.regs:
+                               pos = wholewordRE.regs[1][0]
+                       else:
+                               pos = -1
+               else:
+                       pos = string.find(text, find, selend)
+               if pos >= 0:
+                       editor.setselection(pos, pos + len(find))
+                       return 1
+               elif self.parms["wrap"]:
+                       if self.parms["wholeword"]:
+                               wholewordRE.search(text, 0)
+                               if wholewordRE.regs:
+                                       pos = wholewordRE.regs[1][0]
+                               else:
+                                       pos = -1
+                       else:
+                               pos = string.find(text, find)
+                       if selstart > pos >= 0:
+                               editor.setselection(pos, pos + len(find))
+                               return 1
+       
+       def setparms(self):
+               for key, value in self.parms.items():
+                       try:
+                               self.w[key].set(value)
+                       except KeyError:
+                               self.w.boxes[key].set(value)
+       
+       def getparmsfromwindow(self):
+               if not self.w:
+                       return
+               for key, value in self.parms.items():
+                       try:
+                               value = self.w[key].get()
+                       except KeyError:
+                               value = self.w.boxes[key].get()
+                       self.parms[key] = value
+       
+       def cancel(self):
+               self.hide()
+               self.setparms()
+       
+       def hide(self):
+               if self.w:
+                       self.w.wid.HideWindow()
+                       self.visible = 0
+       
+       def writeprefs(self):
+               import MacPrefs
+               self.getparmsfromwindow()
+               prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
+               prefs.searchengine.casesens = self.parms["casesens"]
+               prefs.searchengine.wrap = self.parms["wrap"]
+               prefs.searchengine.wholeword = self.parms["wholeword"]
+               prefs.save()
+       
+
+class TitledEditText(W.Group):
+       
+       def __init__(self, possize, title, text = ""):
+               W.Group.__init__(self, possize)
+               self.title = W.TextBox((0, 0, 0, 16), title)
+               self.edit = W.EditText((0, 16, 0, 0), text)
+       
+       def set(self, value):
+               self.edit.set(value)
+       
+       def get(self):
+               return self.edit.get()
+
+
+class ClassFinder(W.PopupWidget):
+       
+       def click(self, point, modifiers):
+               W.SetCursor("watch")
+               self.set(self._parentwindow.getclasslist())
+               W.PopupWidget.click(self, point, modifiers)
+
+
+def getminindent(lines):
+       indent = -1
+       for line in lines:
+               stripped = string.strip(line)
+               if not stripped or stripped[0] == '#':
+                       continue
+               if indent < 0 or line[:indent] <> indent * '\t':
+                       indent = 0
+                       for c in line:
+                               if c <> '\t':
+                                       break
+                               indent = indent + 1
+       return indent
+
+
+def getoptionkey():
+       return not not ord(Evt.GetKeys()[7]) & 0x04
+
+
+def execstring(pytext, globals, locals, filename="<string>", debugging=0, 
+                       modname="__main__", profiling=0):
+       if debugging:
+               import PyDebugger, bdb
+               BdbQuit = bdb.BdbQuit
+       else:
+               BdbQuit = 'BdbQuitDummyException'
+       pytext = string.split(pytext, '\r')
+       pytext = string.join(pytext, '\n') + '\n'
+       W.SetCursor("watch")
+       globals['__name__'] = modname
+       globals['__file__'] = filename
+       sys.argv = [filename]
+       try:
+               code = compile(pytext, filename, "exec")
+       except:
+               # XXXX BAAAADDD.... We let tracebackwindow decide to treat SyntaxError 
+               # special. That's wrong because THIS case is special (could be literal 
+               # overflow!) and SyntaxError could mean we need a traceback (syntax error 
+               # in imported module!!!
+               tracebackwindow.traceback(1, filename)
+               return
+       try:
+               if debugging:
+                       PyDebugger.startfromhere()
+               else:
+                       MacOS.EnableAppswitch(0)
+               try:
+                       if profiling:
+                               import profile, ProfileBrowser
+                               p = profile.Profile()
+                               p.set_cmd(filename)
+                               try:
+                                       p.runctx(code, globals, locals)
+                               finally:
+                                       import pstats
+                                       
+                                       stats = pstats.Stats(p)
+                                       ProfileBrowser.ProfileBrowser(stats)
+                       else:
+                               exec code in globals, locals
+               finally:
+                       MacOS.EnableAppswitch(-1)
+       except W.AlertError, detail:
+               raise W.AlertError, detail
+       except (KeyboardInterrupt, BdbQuit):
+               pass
+       except:
+               if debugging:
+                       sys.settrace(None)
+                       PyDebugger.postmortem(sys.exc_type, sys.exc_value, sys.exc_traceback)
+                       return
+               else:
+                       tracebackwindow.traceback(1, filename)
+       if debugging:
+               sys.settrace(None)
+               PyDebugger.stop()
+
+
+_identifieRE = regex.compile("[A-Za-z_][A-Za-z_0-9]*")
+
+def _filename_as_modname(fname):
+       if fname[-3:] == '.py':
+               modname = fname[:-3]
+               if _identifieRE.match(modname) == len(modname):
+                       return string.join(string.split(modname, '.'), '_')
+
+def findeditor(topwindow, fromtop = 0):
+       wid = Win.FrontWindow()
+       if not fromtop:
+               if topwindow.w and wid == topwindow.w.wid:
+                       wid = topwindow.w.wid.GetNextWindow()
+       if not wid:
+               return
+       app = W.getapplication()
+       if app._windows.has_key(wid): # KeyError otherwise can happen in RoboFog :-(
+               window = W.getapplication()._windows[wid]
+       else:
+               return
+       if not isinstance(window, Editor):
+               return
+       return window.editgroup.editor
+
+
+class _EditorDefaultSettings:
+       
+       def __init__(self):
+               self.template = "%s, %d point"
+               self.fontsettings, self.tabsettings, self.windowsize = geteditorprefs()
+               self.w = W.Dialog((328, 120), "Editor default settings")
+               self.w.setfontbutton = W.Button((8, 8, 80, 16), "Set fontÉ", self.dofont)
+               self.w.fonttext = W.TextBox((98, 10, -8, 14), self.template % (self.fontsettings[0], self.fontsettings[2]))
+               
+               self.w.picksizebutton = W.Button((8, 50, 80, 16), "Front window", self.picksize)
+               self.w.xsizelabel = W.TextBox((98, 32, 40, 14), "Width:")
+               self.w.ysizelabel = W.TextBox((148, 32, 40, 14), "Height:")
+               self.w.xsize = W.EditText((98, 48, 40, 20), `self.windowsize[0]`)
+               self.w.ysize = W.EditText((148, 48, 40, 20), `self.windowsize[1]`)
+               
+               self.w.cancelbutton = W.Button((-180, -26, 80, 16), "Cancel", self.cancel)
+               self.w.okbutton = W.Button((-90, -26, 80, 16), "Done", self.ok)
+               self.w.setdefaultbutton(self.w.okbutton)
+               self.w.bind('cmd.', self.w.cancelbutton.push)
+               self.w.open()
+       
+       def picksize(self):
+               app = W.getapplication()
+               editor = findeditor(self)
+               if editor is not None:
+                       width, height = editor._parentwindow._bounds[2:]
+                       self.w.xsize.set(`width`)
+                       self.w.ysize.set(`height`)
+               else:
+                       raise W.AlertError, "No edit window found"
+       
+       def dofont(self):
+               import FontSettings
+               settings = FontSettings.FontDialog(self.fontsettings, self.tabsettings)
+               if settings:
+                       self.fontsettings, self.tabsettings = settings
+                       sys.exc_traceback = None
+                       self.w.fonttext.set(self.template % (self.fontsettings[0], self.fontsettings[2]))
+       
+       def close(self):
+               self.w.close()
+               del self.w
+       
+       def cancel(self):
+               self.close()
+       
+       def ok(self):
+               try:
+                       width = string.atoi(self.w.xsize.get())
+               except:
+                       self.w.xsize.select(1)
+                       self.w.xsize.selectall()
+                       raise W.AlertError, "Bad number for window width"
+               try:
+                       height = string.atoi(self.w.ysize.get())
+               except:
+                       self.w.ysize.select(1)
+                       self.w.ysize.selectall()
+                       raise W.AlertError, "Bad number for window height"
+               self.windowsize = width, height
+               seteditorprefs(self.fontsettings, self.tabsettings, self.windowsize)
+               self.close()
+
+def geteditorprefs():
+       import MacPrefs
+       prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
+       try:
+               fontsettings = prefs.pyedit.fontsettings
+               tabsettings = prefs.pyedit.tabsettings
+               windowsize = prefs.pyedit.windowsize
+       except:
+               fontsettings = prefs.pyedit.fontsettings = ("Python-Sans", 0, 9, (0, 0, 0))
+               tabsettings = prefs.pyedit.tabsettings = (8, 1)
+               windowsize = prefs.pyedit.windowsize = (500, 250)
+               sys.exc_traceback = None
+       return fontsettings, tabsettings, windowsize
+
+def seteditorprefs(fontsettings, tabsettings, windowsize):
+       import MacPrefs
+       prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
+       prefs.pyedit.fontsettings = fontsettings
+       prefs.pyedit.tabsettings = tabsettings
+       prefs.pyedit.windowsize = windowsize
+       prefs.save()
+
+_defaultSettingsEditor = None
+
+def EditorDefaultSettings():
+       global _defaultSettingsEditor
+       if _defaultSettingsEditor is None or not hasattr(_defaultSettingsEditor, "w"):
+               _defaultSettingsEditor = _EditorDefaultSettings()
+       else:
+               _defaultSettingsEditor.w.select()
+
+def resolvealiases(path):
+       try:
+               return macfs.ResolveAliasFile(path)[0].as_pathname()
+       except (macfs.error, ValueError), (error, str):
+               if error <> -120:
+                       raise
+               dir, file = os.path.split(path)
+               return os.path.join(resolvealiases(dir), file)
+
+searchengine = SearchEngine()
+tracebackwindow = Wtraceback.TraceBack()
diff --git a/Mac/Tools/IDE/PyFontify.py b/Mac/Tools/IDE/PyFontify.py
new file mode 100644 (file)
index 0000000..a61de65
--- /dev/null
@@ -0,0 +1,154 @@
+"""Module to analyze Python source code; for syntax coloring tools.
+
+Interface:
+       tags = fontify(pytext, searchfrom, searchto)
+
+The 'pytext' argument is a string containing Python source code.
+The (optional) arguments 'searchfrom' and 'searchto' may contain a slice in pytext. 
+The returned value is a list of tuples, formatted like this:
+       [('keyword', 0, 6, None), ('keyword', 11, 17, None), ('comment', 23, 53, None), etc. ]
+The tuple contents are always like this:
+       (tag, startindex, endindex, sublist)
+tag is one of 'keyword', 'string', 'comment' or 'identifier'
+sublist is not used, hence always None. 
+"""
+
+# Based on FontText.py by Mitchell S. Chapman,
+# which was modified by Zachary Roadhouse,
+# then un-Tk'd by Just van Rossum.
+# Many thanks for regular expression debugging & authoring are due to:
+#      Tim (the-incredib-ly y'rs) Peters and Cristian Tismer
+# So, who owns the copyright? ;-) How about this:
+# Copyright 1996-1997: 
+#      Mitchell S. Chapman,
+#      Zachary Roadhouse,
+#      Tim Peters,
+#      Just van Rossum
+
+__version__ = "0.3.1"
+
+import string, regex
+
+# First a little helper, since I don't like to repeat things. (Tismer speaking)
+import string
+def replace(where, what, with):
+       return string.join(string.split(where, what), with)
+
+# This list of keywords is taken from ref/node13.html of the
+# Python 1.3 HTML documentation. ("access" is intentionally omitted.)
+keywordsList = [
+       "assert",
+       "del", "from", "lambda", "return",
+       "and", "elif", "global", "not", "try",
+       "break", "else", "if", "or", "while",
+       "class", "except", "import", "pass",
+       "continue", "finally", "in", "print",
+       "def", "for", "is", "raise"]
+
+# Build up a regular expression which will match anything
+# interesting, including multi-line triple-quoted strings.
+commentPat = "#.*"
+
+pat = "q[^\q\n]*\(\\\\[\000-\377][^\q\n]*\)*q"
+quotePat = replace(pat, "q", "'") + "\|" + replace(pat, 'q', '"')
+
+# Way to go, Tim!
+pat = """
+       qqq
+       [^\\q]*
+       \(
+               \(      \\\\[\000-\377]
+               \|      q
+                       \(      \\\\[\000-\377]
+                       \|      [^\\q]
+                       \|      q
+                               \(      \\\\[\000-\377]
+                               \|      [^\\q]
+                               \)
+                       \)
+               \)
+               [^\\q]*
+       \)*
+       qqq
+"""
+pat = string.join(string.split(pat), '')       # get rid of whitespace
+tripleQuotePat = replace(pat, "q", "'") + "\|" + replace(pat, 'q', '"')
+
+# Build up a regular expression which matches all and only
+# Python keywords. This will let us skip the uninteresting
+# identifier references.
+# nonKeyPat identifies characters which may legally precede
+# a keyword pattern.
+nonKeyPat = "\(^\|[^a-zA-Z0-9_.\"']\)"
+
+keyPat = nonKeyPat + "\("
+for keyword in keywordsList:
+       keyPat = keyPat + keyword + "\|"
+keyPat = keyPat[:-2] + "\)" + nonKeyPat
+
+matchPat = keyPat + "\|" + commentPat + "\|" + tripleQuotePat + "\|" + quotePat
+matchRE = regex.compile(matchPat)
+
+idKeyPat = "[ \t]*[A-Za-z_][A-Za-z_0-9.]*"     # Ident w. leading whitespace.
+idRE = regex.compile(idKeyPat)
+
+
+def fontify(pytext, searchfrom = 0, searchto = None):
+       if searchto is None:
+               searchto = len(pytext)
+       # Cache a few attributes for quicker reference.
+       search = matchRE.search
+       group = matchRE.group
+       idSearch = idRE.search
+       idGroup = idRE.group
+       
+       tags = []
+       tags_append = tags.append
+       commentTag = 'comment'
+       stringTag = 'string'
+       keywordTag = 'keyword'
+       identifierTag = 'identifier'
+       
+       start = 0
+       end = searchfrom
+       while 1:
+               start = search(pytext, end)
+               if start < 0 or start >= searchto:
+                       break   # EXIT LOOP
+               match = group(0)
+               end = start + len(match)
+               c = match[0]
+               if c not in "#'\"":
+                       # Must have matched a keyword.
+                       if start <> searchfrom:
+                               # there's still a redundant char before and after it, strip!
+                               match = match[1:-1]
+                               start = start + 1
+                       else:
+                               # this is the first keyword in the text.
+                               # Only a space at the end.
+                               match = match[:-1]
+                       end = end - 1
+                       tags_append((keywordTag, start, end, None))
+                       # If this was a defining keyword, look ahead to the
+                       # following identifier.
+                       if match in ["def", "class"]:
+                               start = idSearch(pytext, end)
+                               if start == end:
+                                       match = idGroup(0)
+                                       end = start + len(match)
+                                       tags_append((identifierTag, start, end, None))
+               elif c == "#":
+                       tags_append((commentTag, start, end, None))
+               else:
+                       tags_append((stringTag, start, end, None))
+       return tags
+
+
+def test(path):
+       f = open(path)
+       text = f.read()
+       f.close()
+       tags = fontify(text)
+       for tag, start, end, sublist in tags:
+               print tag, `text[start:end]`
diff --git a/Mac/Tools/IDE/PyInteractive.py b/Mac/Tools/IDE/PyInteractive.py
new file mode 100644 (file)
index 0000000..dc75e4b
--- /dev/null
@@ -0,0 +1,115 @@
+import string
+import sys
+import traceback
+
+
+try:
+       sys.ps1
+except AttributeError:
+       sys.ps1 = ">>> "
+try:
+       sys.ps2
+except AttributeError:
+       sys.ps2 = "... "
+
+
+def print_exc(limit=None, file=None):
+       if not file:
+               file = sys.stderr
+       # we're going to skip the outermost traceback object, we don't
+       # want people to see the line which excecuted their code.
+       tb = sys.exc_traceback
+       if tb:
+               tb = tb.tb_next
+       try:
+               sys.last_type = sys.exc_type
+               sys.last_value = sys.exc_value
+               sys.last_traceback = tb
+               traceback.print_exception(sys.last_type, sys.last_value, 
+                                       sys.last_traceback, limit, file)
+       except:
+               print '--- hola! ---'
+               traceback.print_exception(sys.exc_type, sys.exc_value, 
+                                       sys.exc_traceback, limit, file)
+
+
+class PyInteractive:
+       
+       def __init__(self):
+               self._pybuf = ""
+       
+       def executeline(self, stuff, out = None, env = None):
+               if env is None:
+                       import __main__
+                       env = __main__.__dict__
+               if out:
+                       saveerr, saveout = sys.stderr, sys.stdout
+                       sys.stderr = sys.stdout = out
+               try:
+                       if self._pybuf:
+                               self._pybuf = self._pybuf + '\n' + stuff
+                       else:
+                               self._pybuf = stuff
+                       
+                       # Compile three times: as is, with \n, and with \n\n appended.
+                       # If it compiles as is, it's complete.  If it compiles with
+                       # one \n appended, we expect more.  If it doesn't compile
+                       # either way, we compare the error we get when compiling with
+                       # \n or \n\n appended.  If the errors are the same, the code
+                       # is broken.  But if the errors are different, we expect more.
+                       # Not intuitive; not even guaranteed to hold in future
+                       # releases; but this matches the compiler's behavior in Python
+                       # 1.4 and 1.5.
+                       err = err1 = err2 = None
+                       code = code1 = code2 = None
+                       
+                       # quickly get out of here when the line is 'empty' or is a comment
+                       stripped = string.strip(self._pybuf)
+                       if not stripped or stripped[0] == '#':
+                               self._pybuf = ''
+                               sys.stdout.write(sys.ps1)
+                               sys.stdout.flush()
+                               return
+                       
+                       try:
+                               code = compile(self._pybuf, "<input>", "single")
+                       except SyntaxError, err:
+                               pass
+                       except:
+                               # OverflowError. More?
+                               print_exc()
+                               self._pybuf = ""
+                               sys.stdout.write(sys.ps1)
+                               sys.stdout.flush()
+                               return
+                       
+                       try:
+                               code1 = compile(self._pybuf + "\n", "<input>", "single")
+                       except SyntaxError, err1:
+                               pass
+                       
+                       try:
+                               code2 = compile(self._pybuf + "\n\n", "<input>", "single")
+                       except SyntaxError, err2:
+                               pass
+                       
+                       if code:
+                               try:
+                                       exec code in env
+                               except:
+                                       print_exc()
+                               self._pybuf = ""
+                       elif code1:
+                               pass
+                       elif err1 == err2 or (not stuff and self._pybuf):
+                               print_exc()
+                               self._pybuf = ""
+                       if self._pybuf:
+                               sys.stdout.write(sys.ps2)
+                               sys.stdout.flush()
+                       else:
+                               sys.stdout.write(sys.ps1)
+                               sys.stdout.flush()
+               finally:
+                       if out:
+                               sys.stderr, sys.stdout = saveerr, saveout
diff --git a/Mac/Tools/IDE/PythonIDE.py b/Mac/Tools/IDE/PythonIDE.py
new file mode 100644 (file)
index 0000000..50f76f9
--- /dev/null
@@ -0,0 +1,33 @@
+# copyright 1996-1999 Just van Rossum, Letterror. just@letterror.com
+
+# keep this (__main__) as clean as possible, since we are using 
+# it like the "normal" interpreter.
+
+__version__ = '1.0b2'
+
+
+def init():
+       import MacOS
+       MacOS.EnableAppswitch(-1)
+       
+       import Qd, QuickDraw
+       Qd.SetCursor(Qd.GetCursor(QuickDraw.watchCursor).data)
+       
+       import Res
+       try:
+               Res.GetResource('DITL', 468)
+       except Res.Error:
+               # we're not an applet
+               Res.OpenResFile('Widgets.rsrc')
+               Res.OpenResFile('PythonIDE.rsrc')
+       else:
+               # we're an applet
+               import sys
+               if sys.argv[0] not in sys.path:
+                       sys.path[2:2] = [sys.argv[0]]
+
+
+init()
+del init
+
+import PythonIDEMain
diff --git a/Mac/Tools/IDE/PythonIDE.rsrc b/Mac/Tools/IDE/PythonIDE.rsrc
new file mode 100644 (file)
index 0000000..5663960
Binary files /dev/null and b/Mac/Tools/IDE/PythonIDE.rsrc differ
diff --git a/Mac/Tools/IDE/PythonIDEMain.py b/Mac/Tools/IDE/PythonIDEMain.py
new file mode 100644 (file)
index 0000000..b6f637b
--- /dev/null
@@ -0,0 +1,237 @@
+# copyright 1997-1998 Just van Rossum, Letterror. just@letterror.com
+
+import Splash
+
+import FrameWork
+import Win
+import Wapplication
+import W
+import os
+import macfs
+
+
+class PythonIDE(Wapplication.Application):
+       
+       def __init__(self):
+               self.preffilepath = ":Python:PythonIDE preferences"
+               Wapplication.Application.__init__(self, 'Pyth')
+               import AE, AppleEvents
+               
+               AE.AEInstallEventHandler(AppleEvents.kCoreEventClass, AppleEvents.kAEOpenApplication, 
+                               self.ignoreevent)
+               AE.AEInstallEventHandler(AppleEvents.kCoreEventClass, AppleEvents.kAEPrintDocuments, 
+                               self.ignoreevent)
+               AE.AEInstallEventHandler(AppleEvents.kCoreEventClass, AppleEvents.kAEOpenDocuments, 
+                               self.opendocsevent)
+               AE.AEInstallEventHandler(AppleEvents.kCoreEventClass, AppleEvents.kAEQuitApplication, 
+                               self.quitevent)
+               import PyConsole, PyEdit
+               Splash.wait()
+               Splash.uninstall_importhook()
+               PyConsole.installoutput()
+               PyConsole.installconsole()
+               import sys
+               for path in sys.argv[1:]:
+                       self.opendoc(path)
+               self.mainloop()
+       
+       def makeusermenus(self):
+               m = Wapplication.Menu(self.menubar, "File")
+               newitem = FrameWork.MenuItem(m, "New", "N", 'new')
+               openitem = FrameWork.MenuItem(m, "OpenÉ", "O", 'open')
+               FrameWork.Separator(m)
+               closeitem = FrameWork.MenuItem(m, "Close", "W", 'close')
+               saveitem = FrameWork.MenuItem(m, "Save", "S", 'save')
+               saveasitem = FrameWork.MenuItem(m, "Save asÉ", None, 'save_as')
+               FrameWork.Separator(m)
+               saveasappletitem = FrameWork.MenuItem(m, "Save as AppletÉ", None, 'save_as_applet')
+               FrameWork.Separator(m)
+               quititem = FrameWork.MenuItem(m, "Quit", "Q", 'quit')
+               
+               m = Wapplication.Menu(self.menubar, "Edit")
+               undoitem = FrameWork.MenuItem(m, "Undo", 'Z', "undo")
+               FrameWork.Separator(m)
+               cutitem = FrameWork.MenuItem(m, "Cut", 'X', "cut")
+               copyitem = FrameWork.MenuItem(m, "Copy", "C", "copy")
+               pasteitem = FrameWork.MenuItem(m, "Paste", "V", "paste")
+               FrameWork.MenuItem(m, "Clear", None,  "clear")
+               FrameWork.Separator(m)
+               selallitem = FrameWork.MenuItem(m, "Select all", "A", "selectall")
+               sellineitem = FrameWork.MenuItem(m, "Select line", "L", "selectline")
+               FrameWork.Separator(m)
+               finditem = FrameWork.MenuItem(m, "FindÉ", "F", "find")
+               findagainitem = FrameWork.MenuItem(m, "Find again", 'G', "findnext")
+               enterselitem = FrameWork.MenuItem(m, "Enter search string", "E", "entersearchstring")
+               replaceitem = FrameWork.MenuItem(m, "Replace", None, "replace")
+               replacefinditem = FrameWork.MenuItem(m, "Replace & find again", 'T', "replacefind")
+               FrameWork.Separator(m)
+               shiftleftitem = FrameWork.MenuItem(m, "Shift left", "[", "shiftleft")
+               shiftrightitem = FrameWork.MenuItem(m, "Shift right", "]", "shiftright")
+               
+               m = Wapplication.Menu(self.menubar, "Python")
+               runitem = FrameWork.MenuItem(m, "Run window", "R", 'run')
+               runselitem = FrameWork.MenuItem(m, "Run selection", None, 'runselection')
+               FrameWork.Separator(m)
+               moditem = FrameWork.MenuItem(m, "Module browserÉ", "M", self.domenu_modulebrowser)
+               FrameWork.Separator(m)
+               mm = FrameWork.SubMenu(m, "Preferences")
+               FrameWork.MenuItem(mm, "Set Scripts folderÉ", None, self.do_setscriptsfolder)
+               FrameWork.MenuItem(mm, "Editor default settingsÉ", None, self.do_editorprefs)
+               
+               self.openwindowsmenu = Wapplication.Menu(self.menubar, 'Windows')
+               self.makeopenwindowsmenu()
+               self._menustocheck = [closeitem, saveitem, saveasitem, saveasappletitem,
+                               undoitem, cutitem, copyitem, pasteitem, 
+                               selallitem, sellineitem, 
+                               finditem, findagainitem, enterselitem, replaceitem, replacefinditem,
+                               shiftleftitem, shiftrightitem, 
+                               runitem, runselitem]
+               
+               prefs = self.getprefs()
+               try:
+                       fss, fss_changed = macfs.RawAlias(prefs.scriptsfolder).Resolve()
+               except:
+                       path = os.path.join(os.getcwd(), 'Scripts')
+                       if not os.path.exists(path):
+                               os.mkdir(path)
+                       fss = macfs.FSSpec(path)
+                       self.scriptsfolder = fss.NewAlias()
+                       self.scriptsfoldermodtime = fss.GetDates()[1]
+               else:
+                       self.scriptsfolder = fss.NewAlias()
+                       self.scriptsfoldermodtime = fss.GetDates()[1]
+               prefs.scriptsfolder = self.scriptsfolder.data
+               self._scripts = {}
+               self.scriptsmenu = None
+               self.makescriptsmenu()
+       
+       def quitevent(self, theAppleEvent, theReply):
+               import AE
+               AE.AEInteractWithUser(50000000)
+               self._quit()
+       
+       def suspendresume(self, onoff):
+               if onoff:
+                       fss, fss_changed = self.scriptsfolder.Resolve()
+                       modtime = fss.GetDates()[1]
+                       if self.scriptsfoldermodtime <> modtime or fss_changed:
+                               self.scriptsfoldermodtime = modtime
+                               W.SetCursor('watch')
+                               self.makescriptsmenu()
+       
+       def ignoreevent(self, theAppleEvent, theReply):
+               pass
+       
+       def opendocsevent(self, theAppleEvent, theReply):
+               W.SetCursor('watch')
+               import aetools
+               parameters, args = aetools.unpackevent(theAppleEvent)
+               docs = parameters['----']
+               if type(docs) <> type([]):
+                       docs = [docs]
+               for doc in docs:
+                       fss, a = doc.Resolve()
+                       path = fss.as_pathname()
+                       self.opendoc(path)
+       
+       def opendoc(self, path):
+               fcreator, ftype = macfs.FSSpec(path).GetCreatorType()
+               if ftype == 'TEXT':
+                       self.openscript(path)
+               else:
+                       W.Message("CanĂ•t open file of type '%s'." % ftype)
+       
+       def getabouttext(self):
+               return "About Python IDEÉ"
+       
+       def do_about(self, id, item, window, event):
+               Splash.about()
+       
+       def do_setscriptsfolder(self, *args):
+               fss, ok = macfs.GetDirectory("Select Scripts Folder")
+               if ok:
+                       prefs = self.getprefs()
+                       alis = fss.NewAlias()
+                       prefs.scriptsfolder = alis.data
+                       self.scriptsfolder = alis
+                       self.makescriptsmenu()
+                       prefs.save()
+       
+       def domenu_modulebrowser(self, *args):
+               W.SetCursor('watch')
+               import ModuleBrowser
+               ModuleBrowser.ModuleBrowser()
+       
+       def domenu_open(self, *args):
+               fss, ok = macfs.StandardGetFile("TEXT")
+               if ok:
+                       self.openscript(fss.as_pathname())
+       
+       def domenu_new(self, *args):
+               W.SetCursor('watch')
+               import PyEdit
+               return PyEdit.Editor()
+       
+       def makescriptsmenu(self):
+               W.SetCursor('watch')
+               if self._scripts:
+                       for id, item in self._scripts.keys():
+                               if self.menubar.menus.has_key(id):
+                                       m = self.menubar.menus[id]
+                                       m.delete()
+                       self._scripts = {}
+               if self.scriptsmenu:
+                       if hasattr(self.scriptsmenu, 'id') and self.menubar.menus.has_key(self.scriptsmenu.id):
+                               self.scriptsmenu.delete()
+               self.scriptsmenu = FrameWork.Menu(self.menubar, "Scripts")
+               #FrameWork.MenuItem(self.scriptsmenu, "New script", None, self.domenu_new)
+               #self.scriptsmenu.addseparator()
+               fss, fss_changed = self.scriptsfolder.Resolve()
+               self.scriptswalk(fss.as_pathname(), self.scriptsmenu)
+       
+       def makeopenwindowsmenu(self):
+               for i in range(len(self.openwindowsmenu.items)):
+                       self.openwindowsmenu.menu.DeleteMenuItem(1)
+                       self.openwindowsmenu.items = []
+               windows = []
+               self._openwindows = {}
+               for window in self._windows.keys():
+                       title = window.GetWTitle()
+                       if not title:
+                               title = "<no title>"
+                       windows.append(title, window)
+               windows.sort()
+               for title, window in windows:
+                       if title == "Python Interactive":       # ugly but useful hack by Joe Strout
+                               shortcut = '0'
+                       else: 
+                               shortcut = None
+                       item = FrameWork.MenuItem(self.openwindowsmenu, title, shortcut, callback = self.domenu_openwindows)
+                       self._openwindows[item.item] = window
+               self._openwindowscheckmark = 0
+               self.checkopenwindowsmenu()
+               
+       def domenu_openwindows(self, id, item, window, event):
+               w = self._openwindows[item]
+               w.ShowWindow()
+               w.SelectWindow()
+       
+       def domenu_quit(self):
+               self._quit()
+       
+       def domenu_save(self, *args):
+               print "Save"
+       
+       def _quit(self):
+               import PyConsole, PyEdit
+               PyConsole.console.writeprefs()
+               PyConsole.output.writeprefs()
+               PyEdit.searchengine.writeprefs()
+               for window in self._windows.values():
+                       rv = window.close()
+                       if rv and rv > 0:
+                               return
+               self.quitting = 1
+
+PythonIDE()
+
diff --git a/Mac/Tools/IDE/Splash.py b/Mac/Tools/IDE/Splash.py
new file mode 100644 (file)
index 0000000..19f16fc
--- /dev/null
@@ -0,0 +1,162 @@
+import Dlg
+import Res
+
+splash = Dlg.GetNewDialog(468, -1)
+splash.DrawDialog()
+
+import Qd, TE, Fm, sys
+
+_real__import__ = None
+
+def install_importhook():
+       global _real__import__
+       import __builtin__
+       if _real__import__ is None:
+               _real__import__ = __builtin__.__import__
+               __builtin__.__import__ = my__import__
+
+def uninstall_importhook():
+       global _real__import__
+       if _real__import__ is not None:
+               import __builtin__
+               __builtin__.__import__ = _real__import__
+               _real__import__ = None
+
+_progress = 0
+
+def importing(module):
+       global _progress
+       Qd.SetPort(splash)
+       fontID = Fm.GetFNum("Python-Sans")
+       if not fontID:
+               fontID = geneva
+       Qd.TextFont(fontID)
+       Qd.TextSize(9)
+       rect = (35, 260, 365, 276)
+       if module:
+               TE.TETextBox('Importing: ' + module, rect, 0)
+               if not _progress:
+                       Qd.FrameRect((35, 276, 365, 284))
+               pos = min(36 + 330 * _progress / 44, 364)
+               Qd.PaintRect((36, 277, pos, 283))
+               _progress = _progress + 1
+       else:
+               Qd.EraseRect(rect)
+               Qd.PaintRect((36, 277, pos, 283))
+
+def my__import__(name, globals=None, locals=None, fromlist=None):
+       try:
+               return sys.modules[name]
+       except KeyError:
+               try:
+                       importing(name)
+               except:
+                       try:
+                               rv = _real__import__(name)
+                       finally:
+                               uninstall_importhook()
+                       return rv
+               return _real__import__(name)
+
+install_importhook()
+
+kHighLevelEvent = 23
+import Win
+from Fonts import *
+from QuickDraw import *
+from TextEdit import *
+import string
+import sys
+
+_keepsplashscreenopen = 0
+
+abouttext1 = """The Python Integrated Developement Environment for the Macintoshª
+Version: %s
+Copyright 1997-98 Just van Rossum, Letterror. <just@letterror.com>
+
+Python %s
+%s
+Written by Guido van Rossum with Jack Jansen (and others)
+
+See: <http://www.python.org/> for information and documentation."""
+
+flauwekul = [  'Goodday, Bruce.', 
+                       'WhatĂ•s new?', 
+                       'Nudge, nudge, say no more!', 
+                       'No, no sir, itĂ•s not dead. ItĂ•s resting.',
+                       'Albatros!',
+                       'ItĂ•s . . .',
+                       'Is your name not Bruce, then?',
+                       """But Mr F.G. Superman has a secret identity . . . 
+when trouble strikes at any time . . . 
+at any place . . . he is ready to become . . . 
+Bicycle Repair Man!"""
+                       ]
+
+def nl2return(text):
+       return string.join(string.split(text, '\n'), '\r')
+
+def UpdateSplash(drawdialog = 0, what = 0):
+       if drawdialog:
+               splash.DrawDialog()
+       drawtext(what)
+       Win.ValidRect(splash.GetWindowPort().portRect)
+
+def drawtext(what = 0):
+       Qd.SetPort(splash)
+       fontID = Fm.GetFNum("Python-Sans")
+       if not fontID:
+               fontID = geneva
+       Qd.TextFont(fontID)
+       Qd.TextSize(9)
+       rect = (10, 125, 390, 260)
+       if not what:
+               import __main__
+               abouttxt = nl2return(abouttext1  \
+                       % (__main__.__version__, sys.version, sys.copyright))
+       else:
+               import random
+               abouttxt = nl2return(random.choice(flauwekul))
+       TE.TETextBox(abouttxt, rect, teJustCenter)
+
+UpdateSplash(1)
+
+def wait():
+       import Evt
+       from Events import *
+       global splash
+       try:
+               splash
+       except NameError:
+               return
+       Qd.InitCursor()
+       time = Evt.TickCount()
+       whattext = 0
+       while _keepsplashscreenopen:
+               ok, event = Evt.EventAvail(highLevelEventMask)
+               if ok:
+                       # got apple event, back to mainloop
+                       break
+               ok, event = Evt.EventAvail(mDownMask | keyDownMask | updateMask)
+               if ok:
+                       ok, event = Evt.WaitNextEvent(mDownMask | keyDownMask | updateMask, 30)
+                       if ok:
+                               (what, message, when, where, modifiers) = event
+                               if what == updateEvt:
+                                       if Win.WhichWindow(message) == splash:
+                                               UpdateSplash(1, whattext)
+                               else:
+                                       break
+               if Evt.TickCount() - time > 360:
+                       whattext = not whattext
+                       drawtext(whattext)
+                       time = Evt.TickCount()
+       del splash
+       #Res.CloseResFile(splashresfile)
+
+def about():
+       global splash, splashresfile, _keepsplashscreenopen
+       _keepsplashscreenopen = 1
+       splash = Dlg.GetNewDialog(468, -1)
+       splash.DrawDialog()
+       wait()
diff --git a/Mac/Tools/IDE/W.py b/Mac/Tools/IDE/W.py
new file mode 100644 (file)
index 0000000..812134f
--- /dev/null
@@ -0,0 +1,34 @@
+"""Widgets for the Macintosh. Built on top of FrameWork"""
+
+__version__ = "0.3"
+
+from Wbase import *
+from Wcontrols import *
+from Wtext import *
+from Wlists import *
+from Wwindows import *
+from Wmenus import *
+
+_application = None
+_signature = None
+
+AlertError = 'AlertError'
+
+def setapplication(app, sig):
+       global _application, _signature
+       _application = app
+       _signature = sig
+
+def getapplication():
+       if _application is None:
+               raise WidgetsError, 'W not properly initialized: unknown Application'
+       return _application
+
+def Message(text):
+       import EasyDialogs, Qd, string
+       Qd.InitCursor()
+       text = string.replace(text, "\n", "\r")
+       if not text:
+               text = '<Alert text not specified>'
+       EasyDialogs.Message(text)
+
diff --git a/Mac/Tools/IDE/Wapplication.py b/Mac/Tools/IDE/Wapplication.py
new file mode 100644 (file)
index 0000000..8db9a1a
--- /dev/null
@@ -0,0 +1,411 @@
+import FrameWork
+import Win
+import Qd
+import Evt
+import MacOS
+import Events
+import traceback
+from types import *
+
+import Menu; MenuToolbox = Menu; del Menu
+
+
+class Application(FrameWork.Application):
+       
+       def __init__(self, signature='Pyth'):
+               import W
+               W.setapplication(self, signature)
+               FrameWork.Application.__init__(self)
+               self._suspended = 0
+               self.quitting = 0
+               self.debugger_quitting = 1
+               self.DebuggerQuit = 'DebuggerQuitDummyException'
+               self._idlefuncs = []
+               # map certain F key codes to equivalent command-letter combos (JJS)
+               self.fkeymaps = {122:"z", 120:"x", 99:"c", 118:"v"}
+       
+       def mainloop(self, mask=FrameWork.everyEvent, wait=0):
+               import W
+               self.quitting = 0
+               saveyield = MacOS.EnableAppswitch(-1)
+               try:
+                       while not self.quitting:
+                               try:
+                                       self.do1event(mask, wait)
+                               except W.AlertError, detail:
+                                       MacOS.EnableAppswitch(-1)
+                                       W.Message(detail)
+                               except self.DebuggerQuit:
+                                       MacOS.EnableAppswitch(-1)
+                               except:
+                                       MacOS.EnableAppswitch(-1)
+                                       import PyEdit
+                                       PyEdit.tracebackwindow.traceback()
+               finally:
+                       MacOS.EnableAppswitch(1)
+       
+       def debugger_mainloop(self, mask=FrameWork.everyEvent, wait=0):
+               import W
+               self.debugger_quitting = 0
+               saveyield = MacOS.EnableAppswitch(-1)
+               try:
+                       while not self.quitting and not self.debugger_quitting:
+                               try:
+                                       self.do1event(mask, wait)
+                               except W.AlertError, detail:
+                                       W.Message(detail)
+                               except:
+                                       import PyEdit
+                                       PyEdit.tracebackwindow.traceback()
+               finally:
+                       MacOS.EnableAppswitch(saveyield)
+       
+       def breathe(self, wait=1):
+               import W
+               ok, event = Evt.WaitNextEvent(FrameWork.updateMask | 
+                               FrameWork.mDownMask | FrameWork.osMask |
+                               FrameWork.activMask, 
+                               wait)
+               if ok:
+                       (what, message, when, where, modifiers) = event
+                       #print FrameWork.eventname[what]
+                       if FrameWork.eventname[what] == 'mouseDown':
+                               partcode, wid = Win.FindWindow(where)
+                               if FrameWork.partname[partcode] <> 'inDesk':
+                                       return
+                       else:
+                               W.SetCursor('watch')
+                       self.dispatch(event)
+       
+       def refreshwindows(self, wait=1):
+               import W
+               while 1:
+                       ok, event = Evt.WaitNextEvent(FrameWork.updateMask, wait)
+                       if not ok:
+                               break
+                       self.dispatch(event)
+       
+       def addidlefunc(self, func):
+               self._idlefuncs.append(func)
+       
+       def removeidlefunc(self, func):
+               self._idlefuncs.remove(func)
+       
+       def idle(self, event):
+               if not self._suspended:
+                       if not self.do_frontWindowMethod("idle", event):
+                               Qd.InitCursor()
+               if self._idlefuncs:
+                       for func in self._idlefuncs:
+                               try:
+                                       func()
+                               except:
+                                       import sys
+                                       sys.stderr.write("exception in idle function %s; killed:\n" % `func`)
+                                       traceback.print_exc()
+                                       self._idlefuncs.remove(func)
+                                       break
+       
+       def do_frontWindowMethod(self, attr, *args):
+               wid = Win.FrontWindow()
+               if wid and self._windows.has_key(wid):
+                       window = self._windows[wid]
+                       if hasattr(window, attr):
+                               handler = getattr(window, attr)
+                               apply(handler, args)
+                               return 1
+       
+       def appendwindow(self, wid, window):
+               self._windows[wid] = window
+               self.makeopenwindowsmenu()
+               
+       def removewindow(self, wid):
+               del self._windows[wid]
+               self.makeopenwindowsmenu()
+       
+       def do_key(self, event):
+               (what, message, when, where, modifiers) = event
+               ch = chr(message & FrameWork.charCodeMask)
+               rest = message & ~FrameWork.charCodeMask
+               keycode = (message & FrameWork.keyCodeMask) >> 8
+               if keycode in self.fkeymaps.keys():             # JJS
+                       ch = self.fkeymaps[keycode]
+                       modifiers = modifiers | FrameWork.cmdKey
+               wid = Win.FrontWindow()
+               if modifiers & FrameWork.cmdKey and not modifiers & FrameWork.shiftKey:
+                       if wid and self._windows.has_key(wid):
+                               self.checkmenus(self._windows[wid])
+                       else:
+                               self.checkmenus(None)
+                       event = (what, ord(ch) | rest, when, where, modifiers)
+                       result = MenuToolbox.MenuKey(ord(ch))
+                       id = (result>>16) & 0xffff      # Hi word
+                       item = result & 0xffff          # Lo word
+                       if id:
+                               self.do_rawmenu(id, item, None, event)
+                               return  # here! we had a menukey! 
+                       #else:
+                       #       print "XXX Command-" +`ch`
+               # See whether the front window wants it
+               if wid and self._windows.has_key(wid):
+                       window = self._windows[wid]
+                       try:
+                               do_char = window.do_char
+                       except AttributeError:
+                               do_char = self.do_char
+                       do_char(ch, event)
+               # else it wasn't for us, sigh...
+       
+       def do_inMenuBar(self, partcode, window, event):
+               Qd.InitCursor()
+               (what, message, when, where, modifiers) = event
+               self.checkopenwindowsmenu()
+               wid = Win.FrontWindow()
+               if wid and self._windows.has_key(wid):
+                       self.checkmenus(self._windows[wid])
+               else:
+                       self.checkmenus(None)
+               result = MenuToolbox.MenuSelect(where)
+               id = (result>>16) & 0xffff      # Hi word
+               item = result & 0xffff          # Lo word
+               self.do_rawmenu(id, item, window, event)
+       
+       def do_updateEvt(self, event):
+               (what, message, when, where, modifiers) = event
+               wid = Win.WhichWindow(message)
+               if wid and self._windows.has_key(wid):
+                       window = self._windows[wid]
+                       window.do_rawupdate(wid, event)
+               else:
+                       if wid:
+                               wid.HideWindow()
+                               import sys
+                               sys.stderr.write("XXX killed unknown (crashed?) Python window.\n")
+                       else:
+                               MacOS.HandleEvent(event)
+       
+       def suspendresume(self, onoff):
+               pass
+       
+       def do_suspendresume(self, event):
+               self._suspended = not event[1] & 1
+               FrameWork.Application.do_suspendresume(self, event)
+       
+       def checkopenwindowsmenu(self):
+               if self._openwindowscheckmark:
+                       self.openwindowsmenu.menu.CheckItem(self._openwindowscheckmark, 0)
+               window = Win.FrontWindow()
+               if window:
+                       for item, wid in self._openwindows.items():
+                               if wid == window:
+                                       #self.pythonwindowsmenuitem.check(1)
+                                       self.openwindowsmenu.menu.CheckItem(item, 1)
+                                       self._openwindowscheckmark = item
+                                       break
+               else:
+                       self._openwindowscheckmark = 0
+               #if self._openwindows:
+               #       self.pythonwindowsmenuitem.enable(1)
+               #else:
+               #       self.pythonwindowsmenuitem.enable(0)
+       
+       def checkmenus(self, window):
+               for item in self._menustocheck:
+                       callback = item.menu.items[item.item-1][2]
+                       if type(callback) <> StringType:
+                               item.enable(1)
+                       elif hasattr(window, "domenu_" + callback):
+                               if hasattr(window, "can_" + callback):
+                                       canhandler = getattr(window, "can_" + callback)
+                                       if canhandler(item):
+                                               item.enable(1)
+                                       else:
+                                               item.enable(0)
+                               else:
+                                       item.enable(1)
+                       else:
+                               item.enable(0)
+       
+       def enablemenubar(self, onoff):
+               for m in self.menubar.menus.values():
+                       if onoff:
+                               m.menu.EnableItem(0)
+                       else:
+                               m.menu.DisableItem(0)
+               MenuToolbox.DrawMenuBar()
+       
+       def makemenubar(self):
+               self.menubar = MenuBar(self)
+               FrameWork.AppleMenu(self.menubar, self.getabouttext(), self.do_about)
+               self.makeusermenus()
+
+       def scriptswalk(self, top, menu):
+               import os, macfs, string
+               try:
+                       names = os.listdir(top)
+               except os.error:
+                       FrameWork.MenuItem(menu, '(Scripts Folder not found)', None, None)
+                       return
+               savedir = os.getcwd()
+               os.chdir(top)
+               for name in names:
+                       fss, isdir, isalias = macfs.ResolveAliasFile(name)
+                       path = fss.as_pathname()
+                       name = string.strip(name)
+                       if name[-3:] == '---':
+                               menu.addseparator()
+                       elif isdir:
+                               submenu = FrameWork.SubMenu(menu, name)
+                               self.scriptswalk(path, submenu)
+                       else:
+                               creator, type = fss.GetCreatorType()
+                               if type == 'TEXT':
+                                       if name[-3:] == '.py':
+                                               name = name[:-3]
+                                       item = FrameWork.MenuItem(menu, name, None, self.domenu_script)
+                                       self._scripts[(menu.id, item.item)] = path
+               os.chdir(savedir)
+       
+       def domenu_script(self, id, item, window, event):
+               (what, message, when, where, modifiers) = event
+               path = self._scripts[(id, item)]
+               import os
+               if not os.path.exists(path):
+                       self.makescriptsmenu()
+                       import W
+                       raise W.AlertError, "File not found."
+               if ord(Evt.GetKeys()[7]) & 4:
+                       self.openscript(path)
+               else:
+                       import W, MacOS, sys
+                       W.SetCursor("watch")
+                       sys.argv = [path]
+                       #cwd = os.getcwd()
+                       #os.chdir(os.path.dirname(path) + ':')
+                       try:
+                               # xxx if there is a script window for this file,
+                               # exec in that window's namespace.
+                               # xxx what to do when it's not saved???
+                               # promt to save?
+                               MacOS.EnableAppswitch(0)
+                               execfile(path, {'__name__': '__main__', '__file__': path})
+                       except W.AlertError, detail:
+                               MacOS.EnableAppswitch(-1)
+                               raise W.AlertError, detail
+                       except KeyboardInterrupt:
+                               MacOS.EnableAppswitch(-1)
+                       except:
+                               MacOS.EnableAppswitch(-1)
+                               import PyEdit
+                               PyEdit.tracebackwindow.traceback(1)
+                       else:
+                               MacOS.EnableAppswitch(-1)
+                       #os.chdir(cwd)
+       
+       def openscript(self, filename, lineno=None, charoffset=0, modname=""):
+               import os, PyEdit, W
+               editor = self.getscript(filename)
+               if editor:
+                       editor.select()
+               elif os.path.exists(filename):
+                       editor = PyEdit.Editor(filename)
+               elif filename[-3:] == '.py' or filename[-4:] == '.pyc':
+                       import imp
+                       if not modname:
+                               if filename[-1] == 'c':
+                                       modname = os.path.basename(filename)[:-4]
+                               else:
+                                       modname = os.path.basename(filename)[:-3]
+                       try:
+                               # XXX This does not work correctly with packages!
+                               # XXX The docs say we should do it manually, pack, then sub, then sub2 etc. 
+                               # XXX It says we should use imp.load_module(), but that *reloads* a package,
+                               # XXX and that's the last thing we want here.
+                               f, filename, (suff, mode, dummy) = imp.find_module(modname)
+                       except ImportError:
+                               raise W.AlertError, "CanĂ•t find file for Ă’%sĂ“" % modname
+                       else:
+                               if not f:
+                                       raise W.AlertError, "CanĂ•t find file for Ă’%sĂ“" % modname
+                               f.close()
+                       if suff == '.py':
+                               self.openscript(filename, lineno, charoffset)
+                               return
+                       else:
+                               raise W.AlertError, "CanĂ•t find file for Ă’%sĂ“" % modname
+               else:
+                       raise W.AlertError, "CanĂ•t find file Ă”%sĂ•" % filename
+               if lineno is not None:
+                       editor.selectline(lineno, charoffset)
+               return editor
+       
+       def getscript(self, filename):
+               if filename[:1] == '<' and filename[-1:] == '>':
+                       filename = filename[1:-1]
+               import string
+               lowpath = string.lower(filename)
+               for wid, window in self._windows.items():
+                       if hasattr(window, "path") and lowpath == string.lower(window.path):
+                               return window
+                       elif hasattr(window, "path") and filename == wid.GetWTitle():
+                               return window
+       
+       def getprefs(self):
+               import MacPrefs
+               return MacPrefs.GetPrefs(self.preffilepath)
+
+       def do_editorprefs(self, *args):
+               import PyEdit
+               PyEdit.EditorDefaultSettings()
+       
+
+
+class MenuBar(FrameWork.MenuBar):
+       
+       possibleIDs = range(10, 256)
+       
+       def getnextid(self):
+               id = self.possibleIDs[0]
+               del self.possibleIDs[0]
+               return id
+       
+       def __init__(self, parent = None):
+               self.bar = MenuToolbox.GetMenuBar()
+               MenuToolbox.ClearMenuBar()
+               self.menus = {}
+               self.parent = parent
+       
+       def dispatch(self, id, item, window, event):
+               if self.menus.has_key(id):
+                       self.menus[id].dispatch(id, item, window, event)
+       
+       def delmenu(self, id):
+               MenuToolbox.DeleteMenu(id)
+               if id in self.possibleIDs:
+                       print "XXX duplicate menu ID!", id
+               self.possibleIDs.append(id)
+       
+
+class Menu(FrameWork.Menu):
+       
+       def dispatch(self, id, item, window, event):
+               title, shortcut, callback, kind = self.items[item-1]
+               if type(callback) == StringType:
+                       callback = self._getmenuhandler(callback)
+               if callback:
+                       import W
+                       W.CallbackCall(callback, 0, id, item, window, event)
+       
+       def _getmenuhandler(self, callback):
+               menuhandler = None
+               wid = Win.FrontWindow()
+               if wid and self.bar.parent._windows.has_key(wid):
+                       window = self.bar.parent._windows[wid]
+                       if hasattr(window, "domenu_" + callback):
+                               menuhandler = getattr(window, "domenu_" + callback)
+                       elif hasattr(self.bar.parent, "domenu_" + callback):
+                               menuhandler = getattr(self.bar.parent, "domenu_" + callback)
+               elif hasattr(self.bar.parent, "domenu_" + callback):
+                       menuhandler = getattr(self.bar.parent, "domenu_" + callback)
+               return menuhandler
+
diff --git a/Mac/Tools/IDE/Wbase.py b/Mac/Tools/IDE/Wbase.py
new file mode 100644 (file)
index 0000000..46a8c96
--- /dev/null
@@ -0,0 +1,720 @@
+import Qd
+import Win
+import QuickDraw
+import Evt
+import string
+from types import *
+import sys
+
+WidgetsError = "WidgetsError"
+
+DEBUG = 0
+
+class Widget:
+       
+       """Base class for all widgets."""
+       
+       _selectable = 0
+       
+       def __init__(self, possize):
+               self._widgets = []
+               self._widgetsdict = {}
+               self._possize = possize
+               self._bounds = None
+               self._visible = 1
+               self._enabled = 0
+               self._selected = 0
+               self._activated = 0
+               self._callback = None
+               self._parent = None
+               self._parentwindow = None
+               self._bindings = {}
+               self._backcolor = None
+       
+       def show(self, onoff):
+               self._visible = onoff
+               for w in self._widgets:
+                       w.show(onoff)
+               if self._parentwindow is not None and self._parentwindow.wid is not None:
+                       self.SetPort()
+                       if onoff:
+                               self.draw()
+                       else:
+                               Qd.EraseRect(self._bounds)
+       
+       def draw(self, visRgn = None):
+               if self._visible:
+                       # draw your stuff here
+                       pass
+       
+       def getpossize(self):
+               return self._possize
+       
+       def getbounds(self):
+               return self._bounds
+       
+       def move(self, x, y = None):
+               """absolute move"""
+               if y == None:
+                       x, y = x
+               if type(self._possize) <> TupleType:
+                       raise WidgetsError, "can't move widget with bounds function"
+               l, t, r, b = self._possize
+               self.resize(x, y, r, b)
+       
+       def rmove(self, x, y = None):
+               """relative move"""
+               if y == None:
+                       x, y = x
+               if type(self._possize) <> TupleType:
+                       raise WidgetsError, "can't move widget with bounds function"
+               l, t, r, b = self._possize
+               self.resize(l + x, t + y, r, b)
+               
+       def resize(self, *args):
+               if len(args) == 1:
+                       if type(args[0]) == FunctionType or type(args[0]) == MethodType:
+                               self._possize = args[0]
+                       else:
+                               apply(self.resize, args[0])
+               elif len(args) == 2:
+                       self._possize = (0, 0) + args
+               elif len(args) == 4:
+                       self._possize = args
+               else:
+                       raise TypeError, "wrong number of arguments"
+               self._calcbounds()
+       
+       def open(self):
+               self._calcbounds()
+       
+       def close(self):
+               del self._callback
+               del self._possize
+               del self._bindings
+               del self._parent
+               del self._parentwindow
+       
+       def bind(self, key, callback):
+               """bind a key or an 'event' to a callback"""
+               if callback:
+                       self._bindings[key] = callback
+               elif self._bindings.has_key(key):
+                       del self._bindings[key]
+       
+       def adjust(self, oldbounds):
+               self.SetPort()
+               Win.InvalRect(oldbounds)
+               Win.InvalRect(self._bounds)
+       
+       def _calcbounds(self):
+               # calculate absolute bounds relative to the window origin from our
+               # abstract _possize attribute, which is either a 4-tuple or a callable object
+               oldbounds = self._bounds
+               pl, pt, pr, pb = self._parent._bounds
+               if callable(self._possize):
+                       # _possize is callable, let it figure it out by itself: it should return 
+                       # the bounds relative to our parent widget.
+                       width = pr - pl
+                       height = pb - pt
+                       self._bounds = Qd.OffsetRect(self._possize(width, height), pl, pt)
+               else:
+                       # _possize must be a 4-tuple. This is where the algorithm by Peter Kriens and
+                       # Petr van Blokland kicks in. (*** Parts of this algorithm are applied for 
+                       # patents by Ericsson, Sweden ***)
+                       l, t, r, b = self._possize
+                       # depending on the values of l(eft), t(op), r(right) and b(ottom), 
+                       # they mean different things:
+                       if l < -1:
+                               # l is less than -1, this mean it measures from the *right* of it's parent
+                               l = pr + l
+                       else:
+                               # l is -1 or greater, this mean it measures from the *left* of it's parent
+                               l = pl + l
+                       if t < -1:
+                               # t is less than -1, this mean it measures from the *bottom* of it's parent
+                               t = pb + t
+                       else:
+                               # t is -1 or greater, this mean it measures from the *top* of it's parent
+                               t = pt + t
+                       if r > 1:
+                               # r is greater than 1, this means r is the *width* of the widget
+                               r = l + r
+                       else:
+                               # r is less than 1, this means it measures from the *right* of it's parent
+                               r = pr + r
+                       if b > 1:
+                               # b is greater than 1, this means b is the *height* of the widget
+                               b = t + b
+                       else:
+                               # b is less than 1, this means it measures from the *bottom* of it's parent
+                               b = pb + b
+                       self._bounds = (l, t, r, b)
+               if oldbounds and oldbounds <> self._bounds:
+                       self.adjust(oldbounds)
+               for w in self._widgets:
+                       w._calcbounds()
+       
+       def test(self, point):
+               if Qd.PtInRect(point, self._bounds):
+                       return 1
+       
+       def click(self, point, modifiers):
+               pass
+       
+       def findwidget(self, point, onlyenabled = 1):
+               if self.test(point):
+                       for w in self._widgets:
+                               widget = w.findwidget(point)
+                               if widget is not None:
+                                       return widget
+                       if self._enabled or not onlyenabled:
+                               return self
+       
+       def forall(self, methodname, *args):
+               for w in self._widgets:
+                       rv = apply(w.forall, (methodname,) + args)
+                       if rv: 
+                               return rv
+               if self._bindings.has_key("<" + methodname + ">"):
+                       callback = self._bindings["<" + methodname + ">"]
+                       rv = apply(callback, args)
+                       if rv: 
+                               return rv
+               if hasattr(self, methodname):
+                       method = getattr(self, methodname)
+                       return apply(method, args)
+       
+       def forall_butself(self, methodname, *args):
+               for w in self._widgets:
+                       rv = apply(w.forall, (methodname,) + args)
+                       if rv: 
+                               return rv
+       
+       def forall_frombottom(self, methodname, *args):
+               if self._bindings.has_key("<" + methodname + ">"):
+                       callback = self._bindings["<" + methodname + ">"]
+                       rv = apply(callback, args)
+                       if rv: 
+                               return rv
+               if hasattr(self, methodname):
+                       method = getattr(self, methodname)
+                       rv = apply(method, args)
+                       if rv: 
+                               return rv
+               for w in self._widgets:
+                       rv = apply(w.forall_frombottom, (methodname,) + args)
+                       if rv: 
+                               return rv
+       
+       def _addwidget(self, key, widget):
+               if widget in self._widgets:
+                       raise ValueError, "duplicate widget"
+               if self._widgetsdict.has_key(key):
+                       self._removewidget(key)
+               self._widgets.append(widget)
+               self._widgetsdict[key] = widget
+               widget._parent = self
+               self._setparentwindow(widget)
+               if self._parentwindow and self._parentwindow.wid:
+                       widget.forall_frombottom("open")
+                       Win.InvalRect(widget._bounds)
+       
+       def _setparentwindow(self, widget):
+               widget._parentwindow = self._parentwindow
+               for w in widget._widgets:
+                       self._setparentwindow(w)
+       
+       def _removewidget(self, key):
+               if not self._widgetsdict.has_key(key):
+                       raise KeyError, "no widget with key " + `key`
+               widget = self._widgetsdict[key]
+               for k in widget._widgetsdict.keys():
+                       widget._removewidget(k)
+               if self._parentwindow._currentwidget == widget:
+                       widget.select(0)
+                       self._parentwindow._currentwidget = None
+               self.SetPort()
+               Win.InvalRect(widget._bounds)
+               widget.close()
+               del self._widgetsdict[key]
+               self._widgets.remove(widget)
+       
+       def __setattr__(self, attr, value):
+               if type(value) == InstanceType and isinstance(value, Widget) and        \
+                               attr not in ("_currentwidget", "_lastrollover", 
+                               "_parent", "_parentwindow", "_defaultbutton"):
+                       if hasattr(self, attr):
+                               raise ValueError, "Can't replace existing attribute: " + attr
+                       self._addwidget(attr, value)
+               self.__dict__[attr] = value
+       
+       def __delattr__(self, attr):
+               if attr == "_widgetsdict":
+                       raise AttributeError, "cannot delete attribute _widgetsdict"
+               if self._widgetsdict.has_key(attr):
+                       self._removewidget(attr)
+                       if self.__dict__.has_key(attr):
+                               del self.__dict__[attr]
+               elif self.__dict__.has_key(attr):
+                       del self.__dict__[attr]
+               else:
+                       raise AttributeError, attr
+       
+       def __setitem__(self, key, value):
+               self._addwidget(key, value)
+       
+       def __getitem__(self, key):
+               if not self._widgetsdict.has_key(key):
+                       raise KeyError, key
+               return self._widgetsdict[key]
+       
+       def __delitem__(self, key):
+               self._removewidget(key)
+       
+       def SetPort(self):
+               self._parentwindow.SetPort()
+       
+       def __del__(self):
+               if DEBUG:
+                       print "%s instance deleted" % self.__class__.__name__
+       
+       def _drawbounds(self):
+               Qd.FrameRect(self._bounds)
+
+
+class ClickableWidget(Widget):
+       
+       """Base class for clickable widgets. (note: self._enabled must be true to receive click events.)"""
+       
+       def click(self, point, modifiers):
+               pass
+       
+       def enable(self, onoff):
+               self._enabled = onoff
+               self.SetPort()
+               self.draw()
+       
+       def callback(self):
+               if self._callback:
+                       return CallbackCall(self._callback, 1)
+       
+
+class SelectableWidget(ClickableWidget):
+
+       """Base class for selectable widgets."""
+       
+       _selectable = 1
+       
+       def select(self, onoff, isclick = 0):
+               if onoff == self._selected:
+                       return 1
+               if self._bindings.has_key("<select>"):
+                       callback = self._bindings["<select>"]
+                       if callback(onoff):
+                               return 1
+               self._selected = onoff
+               if onoff:
+                       if self._parentwindow._currentwidget is not None:
+                               self._parentwindow._currentwidget.select(0)
+                       self._parentwindow._currentwidget = self
+               else:
+                       self._parentwindow._currentwidget = None
+       
+       def key(self, char, event):
+               pass
+       
+       def drawselframe(self, onoff):
+               if not self._parentwindow._hasselframes:
+                       return
+               thickrect = Qd.InsetRect(self._bounds, -3, -3)
+               state = Qd.GetPenState()
+               Qd.PenSize(2, 2)
+               if onoff:
+                       Qd.PenPat(Qd.qd.black)
+               else:
+                       Qd.PenPat(Qd.qd.white)
+               Qd.FrameRect(thickrect)
+               Qd.SetPenState(state)
+       
+       def adjust(self, oldbounds):
+               self.SetPort()
+               if self._selected:
+                       Win.InvalRect(Qd.InsetRect(oldbounds, -3, -3))
+                       Win.InvalRect(Qd.InsetRect(self._bounds, -3, -3))
+               else:
+                       Win.InvalRect(oldbounds)
+                       Win.InvalRect(self._bounds)
+
+
+class _Line(Widget):
+       
+       def __init__(self, possize, thickness = 1):
+               Widget.__init__(self, possize)
+               self._thickness = thickness
+       
+       def open(self):
+               self._calcbounds()
+               self.SetPort()
+               self.draw()
+       
+       def draw(self, visRgn = None):
+               if self._visible:
+                       Qd.PaintRect(self._bounds)
+       
+       def _drawbounds(self):
+               pass
+
+class HorizontalLine(_Line):
+       
+       def _calcbounds(self):
+               Widget._calcbounds(self)
+               l, t, r, b = self._bounds
+               self._bounds = l, t, r, t + self._thickness
+
+class VerticalLine(_Line):
+       
+       def _calcbounds(self):
+               Widget._calcbounds(self)
+               l, t, r, b = self._bounds
+               self._bounds = l, t, l + self._thickness, b
+
+
+class Frame(Widget):
+       
+       def __init__(self, possize, pattern = Qd.qd.black, color = (0, 0, 0)):
+               Widget.__init__(self, possize)
+               self._framepattern = pattern
+               self._framecolor = color
+       
+       def setcolor(self, color):
+               self._framecolor = color
+               self.SetPort()
+               self.draw()
+       
+       def setpattern(self, pattern):
+               self._framepattern = pattern
+               self.SetPort()
+               self.draw()
+               
+       def draw(self, visRgn = None):
+               if self._visible:
+                       penstate = Qd.GetPenState()
+                       Qd.PenPat(self._framepattern)
+                       Qd.RGBForeColor(self._framecolor)
+                       Qd.FrameRect(self._bounds)
+                       Qd.RGBForeColor((0, 0, 0))
+                       Qd.SetPenState(penstate)
+
+def _darkencolor((r, g, b)):
+       return 0.75 * r, 0.75 * g, 0.75 * b
+
+class BevelBox(Widget):
+       
+       """'Platinum' beveled rectangle."""
+       
+       def __init__(self, possize, color = (0xe000, 0xe000, 0xe000)):
+               Widget.__init__(self, possize)
+               self._color = color
+               self._darkercolor = _darkencolor(color)
+       
+       def setcolor(self, color):
+               self._color = color
+               self.SetPort()
+               self.draw()
+       
+       def draw(self, visRgn = None):
+               if self._visible:
+                       l, t, r, b = Qd.InsetRect(self._bounds, 1, 1)
+                       Qd.RGBForeColor(self._color)
+                       Qd.PaintRect((l, t, r, b))
+                       Qd.RGBForeColor(self._darkercolor)
+                       Qd.MoveTo(l, b)
+                       Qd.LineTo(r, b)
+                       Qd.LineTo(r, t)
+                       Qd.RGBForeColor((0, 0, 0))
+
+
+class Group(Widget):
+       
+       """A container for subwidgets"""
+
+
+class HorizontalPanes(Widget):
+       
+       """Panes, a.k.a. frames. Works a bit like a group. Devides the widget area into "panes",
+       which can be resized by the user by clicking and dragging between the subwidgets."""
+       
+       _direction = 1
+       
+       def __init__(self, possize, panesizes = None, gutter = 8):
+               """panesizes should be a tuple of numbers. The length of the tuple is the number of panes, 
+               the items in the tuple are the relative sizes of these panes; these numbers should add up 
+               to 1 (the total size of all panes)."""
+               ClickableWidget.__init__(self, possize)
+               self._panesizes = panesizes
+               self._gutter = gutter
+               self._enabled = 1
+               self.setuppanes()
+       
+       #def open(self):
+       #       self.installbounds()
+       #       ClickableWidget.open(self)
+       
+       def _calcbounds(self):
+               # hmmm. It should not neccesary be override _calcbounds :-(
+               self.installbounds()
+               Widget._calcbounds(self)
+       
+       def setuppanes(self):
+               panesizes = self._panesizes
+               total = 0
+               if panesizes is not None:
+                       #if len(self._widgets) <> len(panesizes):
+                       #       raise TypeError, 'number of widgets does not match number of panes'
+                       for panesize in panesizes:
+                               if not 0 < panesize < 1:
+                                       raise TypeError, 'pane sizes must be between 0 and 1, not including.'
+                               total = total + panesize
+                       if round(total, 4) <> 1.0:
+                               raise TypeError, 'pane sizes must add up to 1'
+               else:
+                       # XXX does not work!
+                       step = 1.0 / len(self._widgets)
+                       panesizes = []
+                       for i in range(len(self._widgets)):
+                               panesizes.append(step)
+               current = 0
+               self._panesizes = []
+               self._gutters = []
+               for panesize in panesizes:
+                       if current:
+                               self._gutters.append(current)
+                       self._panesizes.append(current, current + panesize)
+                       current = current + panesize
+               self.makepanebounds()
+       
+       def getpanesizes(self):
+               return map(lambda (fr, to): to-fr,  self._panesizes)
+       
+       boundstemplate = "lambda width, height: (0, height * %s + %d, width, height * %s + %d)"
+       
+       def makepanebounds(self):
+               halfgutter = self._gutter / 2
+               self._panebounds = []
+               for i in range(len(self._panesizes)):
+                       panestart, paneend = self._panesizes[i]
+                       boundsstring = self.boundstemplate % (`panestart`, panestart and halfgutter, 
+                                                       `paneend`, (paneend <> 1.0) and -halfgutter)
+                       self._panebounds.append(eval(boundsstring))
+       
+       def installbounds(self):
+               #self.setuppanes()
+               for i in range(len(self._widgets)):
+                       w = self._widgets[i]
+                       w._possize = self._panebounds[i]
+                       #if hasattr(w, "setuppanes"):
+                       #       w.setuppanes()
+                       if hasattr(w, "installbounds"):
+                               w.installbounds()
+       
+       def rollover(self, point, onoff):
+               if onoff:
+                       orgmouse = point[self._direction]
+                       halfgutter = self._gutter / 2
+                       l, t, r, b = self._bounds
+                       if self._direction:
+                               begin, end = t, b
+                       else:
+                               begin, end = l, r
+                       
+                       i = self.findgutter(orgmouse, begin, end)
+                       if i is None:
+                               SetCursor("arrow")
+                       else:
+                               SetCursor(self._direction and 'vmover' or 'hmover')
+       
+       def findgutter(self, orgmouse, begin, end):
+               tolerance = max(4, self._gutter) / 2
+               for i in range(len(self._gutters)):
+                       pos = begin + (end - begin) * self._gutters[i]
+                       if abs(orgmouse - pos) <= tolerance:
+                               break
+               else:
+                       return
+               return i
+       
+       def click(self, point, modifiers):
+               # what a mess...
+               orgmouse = point[self._direction]
+               halfgutter = self._gutter / 2
+               l, t, r, b = self._bounds
+               if self._direction:
+                       begin, end = t, b
+               else:
+                       begin, end = l, r
+               
+               i = self.findgutter(orgmouse, begin, end)
+               if i is None:
+                       return
+               
+               pos = orgpos = begin + (end - begin) * self._gutters[i] # init pos too, for fast click on border, bug done by Petr
+               
+               minpos = self._panesizes[i][0]
+               maxpos = self._panesizes[i+1][1]
+               minpos = begin + (end - begin) * minpos + 64
+               maxpos = begin + (end - begin) * maxpos - 64
+               if minpos > orgpos and maxpos < orgpos:
+                       return
+               
+               #SetCursor("fist")
+               self.SetPort()
+               if self._direction:
+                       rect = l, orgpos - 1, r, orgpos
+               else:
+                       rect = orgpos - 1, t, orgpos, b
+               
+               # track mouse --- XXX  move to separate method?
+               Qd.PenMode(QuickDraw.srcXor)
+               Qd.PenPat(Qd.qd.gray)
+               Qd.PaintRect(rect)
+               lastpos = None
+               while Evt.Button():
+                       pos = orgpos - orgmouse + Evt.GetMouse()[self._direction]
+                       pos = max(pos, minpos)
+                       pos = min(pos, maxpos)
+                       if pos == lastpos:
+                               continue
+                       Qd.PenPat(Qd.qd.gray)
+                       Qd.PaintRect(rect)
+                       if self._direction:
+                               rect = l, pos - 1, r, pos
+                       else:
+                               rect = pos - 1, t, pos, b
+                       Qd.PenPat(Qd.qd.gray)
+                       Qd.PaintRect(rect)
+                       lastpos = pos
+               Qd.PaintRect(rect)
+               Qd.PenNormal()
+               SetCursor("watch")
+               
+               newpos = (pos - begin) / float(end - begin)
+               self._gutters[i] = newpos
+               self._panesizes[i] = self._panesizes[i][0], newpos
+               self._panesizes[i+1] = newpos, self._panesizes[i+1][1]
+               self.makepanebounds()
+               self.installbounds()
+               self._calcbounds()
+       
+
+class VerticalPanes(HorizontalPanes):
+       """see HorizontalPanes"""
+       _direction = 0
+       boundstemplate = "lambda width, height: (width * %s + %d, 0, width * %s + %d, height)"
+
+
+class ColorPicker(ClickableWidget):
+       
+       """Color picker widget. Allows the user to choose a color."""
+       
+       def __init__(self, possize, color = (0, 0, 0), callback = None):
+               ClickableWidget.__init__(self, possize)
+               self._color = color
+               self._callback = callback
+               self._enabled = 1
+       
+       def click(self, point, modifiers):
+               if not self._enabled:
+                       return
+               import ColorPicker
+               newcolor, ok = ColorPicker.GetColor("", self._color)
+               if ok:
+                       self._color = newcolor
+                       self.SetPort()
+                       self.draw()
+                       if self._callback:
+                               return CallbackCall(self._callback, 0, self._color)
+       
+       def set(self, color):
+               self._color = color
+               self.SetPort()
+               self.draw()
+       
+       def get(self):
+               return self._color
+       
+       def draw(self, visRgn=None):
+               if self._visible:
+                       if not visRgn:
+                               visRgn = self._parentwindow.wid.GetWindowPort().visRgn
+                       Qd.PenPat(Qd.qd.gray)
+                       rect = self._bounds
+                       Qd.FrameRect(rect)
+                       rect = Qd.InsetRect(rect, 3, 3)
+                       Qd.PenNormal()
+                       Qd.RGBForeColor(self._color)
+                       Qd.PaintRect(rect)
+                       Qd.RGBForeColor((0, 0, 0))
+                       
+
+# misc utils
+
+def CallbackCall(callback, mustfit, *args):
+       """internal helper routine for W"""
+       # XXX this function should die.
+       if type(callback) == FunctionType:
+               func = callback
+               maxargs = func.func_code.co_argcount
+       elif type(callback) == MethodType:
+               func = callback.im_func
+               maxargs = func.func_code.co_argcount - 1
+       else:
+               if callable(callback):
+                       return apply(callback, args)
+               else:
+                       raise TypeError, "uncallable callback object"
+       
+       if func.func_defaults:
+               minargs = maxargs - len(func.func_defaults)
+       else:
+               minargs = maxargs
+       if minargs <= len(args) <= maxargs:
+               return apply(callback, args)
+       elif not mustfit and minargs == 0:
+               return callback()
+       else:
+               if mustfit:
+                       raise TypeError, "callback accepts wrong number of arguments: " + `len(args)`
+               else:
+                       raise TypeError, "callback accepts wrong number of arguments: 0 or " + `len(args)`
+
+
+def HasBaseClass(obj, class_):
+       try:
+               raise obj
+       except class_:
+               return 1
+       except:
+               pass
+       return 0
+
+
+_cursors = {
+       "watch" : Qd.GetCursor(QuickDraw.watchCursor).data,
+       "arrow" : Qd.qd.arrow,
+       "iBeam" : Qd.GetCursor(QuickDraw.iBeamCursor).data,
+       "cross" : Qd.GetCursor(QuickDraw.crossCursor).data,
+       "plus"          : Qd.GetCursor(QuickDraw.plusCursor).data,
+       "hand"  : Qd.GetCursor(468).data,
+       "fist"          : Qd.GetCursor(469).data,
+       "hmover"        : Qd.GetCursor(470).data,
+       "vmover"        : Qd.GetCursor(471).data,
+       "zoomin"        : Qd.GetCursor(472).data,
+       "zoomout"       : Qd.GetCursor(473).data,
+       "zoom"  : Qd.GetCursor(474).data,
+}
+
+def SetCursor(what):
+       """Set the cursorshape to any of these: 'arrow', 'cross', 'fist', 'hand', 'hmover', 'iBeam', 
+       'plus', 'vmover', 'watch', 'zoom', 'zoomin', 'zoomout'."""
+       Qd.SetCursor(_cursors[what])
diff --git a/Mac/Tools/IDE/Wcontrols.py b/Mac/Tools/IDE/Wcontrols.py
new file mode 100644 (file)
index 0000000..f619bb9
--- /dev/null
@@ -0,0 +1,396 @@
+import Ctl
+import Controls
+import Win
+import Wbase
+import Qd
+import Evt
+
+class ControlWidget(Wbase.ClickableWidget):
+       
+       """Baseclass for all native controls."""
+       
+       def __init__(self, possize, title = "Control", procID = 0, callback = None, value = 0, min = 0, max = 1):
+               Wbase.ClickableWidget.__init__(self, possize)
+               self._control = None
+               self._title = title
+               self._callback = callback
+               self._procID = procID
+               self._value = value
+               self._min = min
+               self._max = max
+               self._enabled = 1
+       
+       def open(self):
+               self._calcbounds()
+               self._control = Ctl.NewControl(self._parentwindow.wid, 
+                                               self._bounds, 
+                                               self._title, 
+                                               1, 
+                                               self._value, 
+                                               self._min, 
+                                               self._max, 
+                                               self._procID, 
+                                               0)
+               self.SetPort()
+               #Win.ValidRect(self._bounds)
+               self.enable(self._enabled)
+       
+       def adjust(self, oldbounds):
+               self.SetPort()
+               self._control.HideControl()
+               self._control.MoveControl(self._bounds[0], self._bounds[1])
+               self._control.SizeControl(self._bounds[2] - self._bounds[0], self._bounds[3] - self._bounds[1])
+               if self._visible:
+                       Qd.EraseRect(self._bounds)
+                       self._control.ShowControl()
+                       Win.ValidRect(self._bounds)
+       
+       def close(self):
+               self._control.HideControl()
+               self._control = None
+               Wbase.ClickableWidget.close(self)
+       
+       def enable(self, onoff):
+               if self._control and self._enabled <> onoff:
+                       self._control.HiliteControl((not onoff) and 255)
+                       self._enabled = onoff
+       
+       def show(self, onoff):
+               self._visible = onoff
+               for w in self._widgets:
+                       w.show(onoff)
+               if onoff:
+                       self._control.ShowControl()
+               else:
+                       self._control.HideControl()
+       
+       def activate(self, onoff):
+               self._activated = onoff
+               if self._enabled:
+                       self._control.HiliteControl((not onoff) and 255)
+       
+       def draw(self, visRgn = None):
+               if self._visible:
+                       self._control.Draw1Control()
+       
+       def test(self, point):
+               ctltype, control = Ctl.FindControl(point, self._parentwindow.wid)
+               if self._enabled and control == self._control:
+                       return 1
+       
+       def click(self, point, modifiers):
+               if not self._enabled:
+                       return
+               part = self._control.TrackControl(point)
+               if part:
+                       if self._callback:
+                               Wbase.CallbackCall(self._callback, 0)
+       
+       def settitle(self, title):
+               if self._control:
+                       self._control.SetControlTitle(title)
+               self._title = title
+       
+       def gettitle(self):
+               return self._title
+
+class Button(ControlWidget):
+       
+       """Standard push button."""
+       
+       def __init__(self, possize, title = "Button", callback = None):
+               procID = Controls.pushButProc | Controls.useWFont
+               ControlWidget.__init__(self, possize, title, procID, callback, 0, 0, 1)
+               self._isdefault = 0
+       
+       def push(self):
+               if not self._enabled:
+                       return
+               import time
+               self._control.HiliteControl(1)
+               time.sleep(0.1)
+               self._control.HiliteControl(0)
+               if self._callback:
+                       Wbase.CallbackCall(self._callback, 0)
+       
+       def enable(self, onoff):
+               if self._control and self._enabled <> onoff:
+                       self._control.HiliteControl((not onoff) and 255)
+                       self._enabled = onoff
+                       if self._isdefault and self._visible:
+                               self.SetPort()
+                               self.drawfatframe(onoff)
+       
+       def activate(self, onoff):
+               self._activated = onoff
+               if self._enabled:
+                       self._control.HiliteControl((not onoff) and 255)
+                       if self._isdefault and self._visible:
+                               self.SetPort()
+                               self.drawfatframe(onoff)
+       
+       def show(self, onoff):
+               ControlWidget.show(self, onoff)
+               if self._isdefault:
+                       self.drawfatframe(onoff and self._enabled)
+       
+       def draw(self, visRgn = None):
+               if self._visible:
+                       self._control.Draw1Control()
+                       if self._isdefault and self._activated:
+                               self.drawfatframe(self._enabled)
+       
+       def drawfatframe(self, onoff):
+               state = Qd.GetPenState()
+               if onoff:
+                       Qd.PenPat(Qd.qd.black)
+               else:
+                       Qd.PenPat(Qd.qd.white)
+               fatrect = Qd.InsetRect(self._bounds, -4, -4)
+               Qd.PenSize(3, 3)
+               Qd.FrameRoundRect(fatrect, 16, 16)
+               Qd.SetPenState(state)
+       
+       def _setdefault(self, onoff):
+               self._isdefault = onoff
+               if self._control and self._enabled:
+                       self.SetPort()
+                       self.drawfatframe(onoff)
+       
+       def adjust(self, oldbounds):
+               if self._isdefault:
+                       old = Qd.InsetRect(oldbounds, -4, -4)
+                       new = Qd.InsetRect(self._bounds, -4, -4)
+                       Qd.EraseRect(old)
+                       Win.InvalRect(old)
+                       Win.InvalRect(new)
+               ControlWidget.adjust(self, oldbounds)
+
+
+class CheckBox(ControlWidget):
+       
+       """Standard checkbox."""
+       
+       def __init__(self, possize, title = "Checkbox", callback = None, value = 0):
+               procID = Controls.checkBoxProc | Controls.useWFont
+               ControlWidget.__init__(self, possize, title, procID, callback, value, 0, 1)
+       
+       def click(self, point, modifiers):
+               if not self._enabled:
+                       return
+               part = self._control.TrackControl(point)
+               if part:
+                       self.toggle()
+                       if self._callback:
+                               Wbase.CallbackCall(self._callback, 0, self.get())
+       
+       def push(self):
+               if not self._enabled:
+                       return
+               self.toggle()
+               if self._callback:
+                       Wbase.CallbackCall(self._callback, 0, self.get())
+       
+       def toggle(self):
+               self.set(not self.get())
+       
+       def set(self, value):
+               if self._control:
+                       self._control.SetControlValue(value)
+               else:
+                       self._value = value
+       
+       def get(self):
+               if self._control:
+                       return self._control.GetControlValue()
+               else:
+                       return self._value
+       
+
+class RadioButton(ControlWidget):
+       
+       """Standard radiobutton."""
+       
+       # XXX We need a radiogroup widget; this is too kludgy.
+       
+       def __init__(self, possize, title, thebuttons, callback = None, value = 0):
+               procID = Controls.radioButProc | Controls.useWFont
+               ControlWidget.__init__(self, possize, title, procID, callback, value, 0, 1)
+               self.thebuttons = thebuttons
+               thebuttons.append(self)
+       
+       def close(self):
+               self.thebuttons = None
+               ControlWidget.close(self)
+       
+       def click(self, point, modifiers):
+               if not self._enabled:
+                       return
+               part = self._control.TrackControl(point)
+               if part:
+                       self.set(1)
+                       if self._callback:
+                               Wbase.CallbackCall(self._callback, 0, 1)
+       
+       def push(self):
+               if not self._enabled:
+                       return
+               self.set(1)
+               if self._callback:
+                       Wbase.CallbackCall(self._callback, 0, 1)
+       
+       def set(self, value):
+               for button in self.thebuttons:
+                       if button._control:
+                               button._control.SetControlValue(button == self)
+                       else:
+                               button._value = (button == self)
+       
+       def get(self):
+               if self._control:
+                       return self._control.GetControlValue()
+               else:
+                       return self._value
+       
+
+class Scrollbar(ControlWidget):
+       
+       """Standard scrollbar."""
+       
+       def __init__(self, possize, callback = None, value = 0, min = 0, max = 0):
+               procID = Controls.scrollBarProc
+               ControlWidget.__init__(self, possize, "", procID, callback, value, min, max)
+       
+       # interface
+       def set(self, value):
+               if self._callback:
+                       Wbase.CallbackCall(self._callback, 1, value)
+       
+       def up(self):
+               if self._callback:
+                       Wbase.CallbackCall(self._callback, 1, '+')
+       
+       def down(self):
+               if self._callback:
+                       Wbase.CallbackCall(self._callback, 1, '-')
+       
+       def pageup(self):
+               if self._callback:
+                       Wbase.CallbackCall(self._callback, 1, '++')
+       
+       def pagedown(self):
+               if self._callback:
+                       Wbase.CallbackCall(self._callback, 1, '--')
+       
+       def setmin(self, min):
+               self._control.SetControlMinimum(min)
+       
+       def setmax(self, min):
+               self._control.SetControlMinimum(max)
+       
+       def getmin(self):
+               return self._control.GetControlMinimum()
+       
+       def getmax(self):
+               return self._control.GetControlMinimum()
+       
+       # internals
+       def click(self, point, modifiers):
+               if not self._enabled:
+                       return
+               # custom TrackControl. A mousedown in a scrollbar arrow or page area should
+               # generate _control hits as long as the mouse is a) down, b) still in the same part
+               part = self._control.TestControl(point)
+               if Controls.inUpButton <= part <= Controls.inPageDown:  
+                       self._control.HiliteControl(part)
+                       self._hit(part)
+                       oldpart = part
+                       while Evt.StillDown():
+                               part = self._control.TestControl(point)
+                               if part == oldpart:
+                                       self._control.HiliteControl(part)
+                                       self._hit(part)
+                               else:
+                                       self._control.HiliteControl(0)
+                               self.SetPort()
+                               point = Evt.GetMouse()
+                       self._control.HiliteControl(0)
+               elif part == Controls.inThumb:
+                       part = self._control.TrackControl(point)
+                       if part:
+                               self._hit(part)
+       
+       def _hit(self, part):
+               if part == Controls.inThumb:
+                       value = self._control.GetControlValue()
+               elif part == Controls.inUpButton:
+                       value = "+"
+               elif part == Controls.inDownButton:
+                       value = "-"
+               elif part == Controls.inPageUp:
+                       value = "++"
+               elif part == Controls.inPageDown:
+                       value = "--"
+               if self._callback:
+                       Wbase.CallbackCall(self._callback, 1, value)
+       
+       def draw(self, visRgn = None):
+               if self._visible:
+                       self._control.Draw1Control()
+                       Qd.FrameRect(self._bounds)
+       
+       def adjust(self, oldbounds):
+               self.SetPort()
+               Win.InvalRect(oldbounds)
+               self._control.HideControl()
+               self._control.MoveControl(self._bounds[0], self._bounds[1])
+               self._control.SizeControl(self._bounds[2] - self._bounds[0], self._bounds[3] - self._bounds[1])
+               if self._visible:
+                       Qd.EraseRect(self._bounds)
+                       if self._activated:
+                               self._control.ShowControl()
+                       else:
+                               Qd.FrameRect(self._bounds)
+                       Win.ValidRect(self._bounds)
+       
+       def activate(self, onoff):
+               self._activated = onoff
+               if self._visible:
+                       if onoff:
+                               self._control.ShowControl()
+                       else:
+                               self._control.HideControl()
+                               self.draw(None)
+                               Win.ValidRect(self._bounds)
+               
+       def set(self, value):
+               if self._control:
+                       self._control.SetControlValue(value)
+               else:
+                       self._value = value
+       
+       def get(self):
+               if self._control:
+                       return self._control.GetControlValue()
+               else:
+                       return self._value
+       
+
+class __xxxx_PopupControl(ControlWidget):
+       
+       def __init__(self, possize, title = "Button", callback = None):
+               procID = Controls.popupMenuProc # | Controls.useWFont
+               ControlWidget.__init__(self, possize, title, procID, callback, 0, 0, 1)
+               self._isdefault = 0
+       
+
+def _scalebarvalue(absmin, absmax, curmin, curmax):
+       if curmin <= absmin and curmax >= absmax:
+               return None
+       if curmin <= absmin:
+               return 0
+       if curmax >= absmax:
+               return 32767
+       perc = float(curmin-absmin) / float((absmax - absmin) - (curmax - curmin))
+       return int(perc*32767)
+
diff --git a/Mac/Tools/IDE/Widgets.rsrc b/Mac/Tools/IDE/Widgets.rsrc
new file mode 100644 (file)
index 0000000..ea2d55c
Binary files /dev/null and b/Mac/Tools/IDE/Widgets.rsrc differ
diff --git a/Mac/Tools/IDE/Wkeys.py b/Mac/Tools/IDE/Wkeys.py
new file mode 100644 (file)
index 0000000..45155f2
--- /dev/null
@@ -0,0 +1,45 @@
+spacekey               = ' '
+returnkey              = '\r'
+tabkey                 = '\t'
+enterkey               = '\003'
+backspacekey   = '\010'
+deletekey              = '\177'
+
+helpkey                = '\005'
+
+leftarrowkey   = '\034'
+rightarrowkey  = '\035'
+uparrowkey             = '\036'
+downarrowkey   = '\037'
+arrowkeys              = [leftarrowkey, rightarrowkey, uparrowkey, downarrowkey]
+
+topkey                 = '\001'
+bottomkey              = '\004'
+pageupkey              = '\013'
+pagedownkey    = '\014'
+scrollkeys             = [topkey, bottomkey, pageupkey, pagedownkey]
+
+navigationkeys = arrowkeys + scrollkeys
+
+keycodes = {
+       "space"         : ' ',
+       "return"                : '\r',
+       "tab"                   : '\t',
+       "enter"                 : '\003',
+       "backspace"     : '\010',
+       "delete"                : '\177',
+       "help"          : '\005',
+       "leftarrow"             : '\034',
+       "rightarrow"    : '\035',
+       "uparrow"               : '\036',
+       "downarrow"     : '\037',
+       "top"                   : '\001',
+       "bottom"                : '\004',
+       "pageup"                : '\013',
+       "pagedown"      : '\014'
+}
+
+keynames = {}
+for k, v in keycodes.items():
+       keynames[v] = k
+del k, v
diff --git a/Mac/Tools/IDE/Wlists.py b/Mac/Tools/IDE/Wlists.py
new file mode 100644 (file)
index 0000000..d49ca93
--- /dev/null
@@ -0,0 +1,457 @@
+import Wbase
+import Wkeys
+import Scrap
+import string
+import Evt
+import Events
+import Qd
+import Win
+
+
+class List(Wbase.SelectableWidget):
+       
+       """Standard list widget."""
+       
+       LDEF_ID = 0
+       
+       def __init__(self, possize, items = None, callback = None, flags = 0, cols = 1):
+               if items is None:
+                       items = []
+               self.items = items
+               Wbase.SelectableWidget.__init__(self, possize)
+               self._selected = 0
+               self._enabled = 1
+               self._list = None
+               self._cols = cols
+               self._callback = callback
+               self._flags = flags
+               self.lasttyping = ""
+               self.lasttime = Evt.TickCount()
+               self.timelimit = 30
+               self.setitems(items)
+               self.drawingmode = 0
+       
+       def open(self):
+               self.setdrawingmode(0)
+               self.createlist()
+               self.setdrawingmode(1)
+       
+       def createlist(self):
+               import List
+               self._calcbounds()
+               self.SetPort()
+               rect = self._bounds
+               rect = rect[0]+1, rect[1]+1, rect[2]-16, rect[3]-1
+               self._list = List.LNew(rect, (0, 0, self._cols, 0), (0, 0), self.LDEF_ID, self._parentwindow.wid,
+                                       0, 1, 0, 1)
+               if self.drawingmode:
+                       self._list.LSetDrawingMode(0)
+               self._list.selFlags = self._flags
+               self.setitems(self.items)
+               if hasattr(self, "_sel"):
+                       self.setselection(self._sel)
+                       del self._sel
+       
+       def adjust(self, oldbounds):
+               self.SetPort()
+               if self._selected:
+                       Win.InvalRect(Qd.InsetRect(oldbounds, -3, -3))
+                       Win.InvalRect(Qd.InsetRect(self._bounds, -3, -3))
+               else:
+                       Win.InvalRect(oldbounds)
+                       Win.InvalRect(self._bounds)
+               if oldbounds[:2] == self._bounds[:2]:
+                       # set visRgn to empty, to prevent nasty drawing side effect of LSize()
+                       Qd.RectRgn(self._parentwindow.wid.GetWindowPort().visRgn, (0, 0, 0, 0))
+                       # list still has the same upper/left coordinates, use LSize
+                       l, t, r, b = self._bounds
+                       width = r - l - 17
+                       height = b - t - 2
+                       self._list.LSize(width, height)
+                       # now *why* doesn't the list manager recalc the cellrect???
+                       l, t, r, b = self._list.LRect((0,0))
+                       cellheight = b - t
+                       self._list.LCellSize((width, cellheight))
+                       # reset visRgn
+                       self._parentwindow.wid.CalcVis()
+               else:
+                       # oh well, since the list manager doesn't have a LMove call,
+                       # we have to make the list all over again...
+                       sel = self.getselection()
+                       topcell = self.gettopcell()
+                       self._list = None
+                       self.setdrawingmode(0)
+                       self.createlist()
+                       self.setselection(sel)
+                       self.settopcell(topcell)
+                       self.setdrawingmode(1)
+       
+       def close(self):
+               self._list = None
+               self._callback = None
+               self.items[:] = []
+               Wbase.SelectableWidget.close(self)
+       
+       def set(self, items):
+               self.setitems(items)
+       
+       def setitems(self, items):
+               self.items = items
+               the_list = self._list
+               if not self._parent or not self._list:
+                       return
+               self.setdrawingmode(0)
+               topcell = self.gettopcell()
+               the_list.LDelRow(0, 1)
+               the_list.LAddRow(len(self.items), 0)
+               self_itemrepr = self.itemrepr
+               set_cell = the_list.LSetCell
+               for i in range(len(items)):
+                       set_cell(self_itemrepr(items[i]), (0, i))
+               self.settopcell(topcell)
+               self.setdrawingmode(1)
+       
+       def click(self, point, modifiers):
+               if not self._enabled:
+                       return
+               isdoubleclick = self._list.LClick(point, modifiers)
+               if self._callback:
+                       Wbase.CallbackCall(self._callback, 0, isdoubleclick)
+               return 1
+       
+       def key(self, char, event):
+               (what, message, when, where, modifiers) = event
+               sel = self.getselection()
+               newselection = []
+               if char == Wkeys.uparrowkey:
+                       if len(sel) >= 1 and min(sel) > 0:
+                               newselection = [min(sel) - 1]
+                       else:
+                               newselection = [0]
+               elif char == Wkeys.downarrowkey:
+                       if len(sel) >= 1 and max(sel) < (len(self.items) - 1):
+                                       newselection = [max(sel) + 1]
+                       else:
+                               newselection = [len(self.items) - 1]
+               else:
+                       modifiers = 0
+                       if (self.lasttime + self.timelimit) < Evt.TickCount():
+                               self.lasttyping = ""
+                       self.lasttyping = self.lasttyping + string.lower(char)
+                       self.lasttime = Evt.TickCount()
+                       i = self.findmatch(self.lasttyping)
+                       newselection = [i]
+               if modifiers & Events.shiftKey:
+                       newselection = newselection + sel
+               self.setselection(newselection)
+               self._list.LAutoScroll()
+               self.click((-1, -1), 0)
+       
+       def findmatch(self, tag):
+               lower = string.lower
+               items = self.items
+               taglen = len(tag)
+               match = '\377' * 100
+               match_i = -1
+               for i in range(len(items)):
+                       item = lower(str(items[i]))
+                       if tag <= item < match:
+                               match = item
+                               match_i = i
+               if match_i >= 0:
+                       return match_i
+               else:
+                       return len(items) - 1
+       
+       def domenu_copy(self, *args):
+               sel = self.getselection()
+               selitems = []
+               for i in sel:
+                       selitems.append(str(self.items[i]))
+               text = string.join(selitems, '\r')
+               if text:
+                       Scrap.ZeroScrap()
+                       Scrap.PutScrap('TEXT', text)
+       
+       def can_copy(self, *args):
+               return len(self.getselection()) <> 0
+       
+       def domenu_selectall(self, *args):
+               self.selectall()
+       
+       def selectall(self):
+               self.setselection(range(len(self.items)))
+               self._list.LAutoScroll()
+               self.click((-1, -1), 0)
+       
+       def getselection(self):
+               if not self._parent or not self._list:
+                       if hasattr(self, "_sel"):
+                               return self._sel
+                       return []
+               items = []
+               point = (0,0)
+               while 1:
+                       ok, point = self._list.LGetSelect(1, point)
+                       if not ok:
+                               break
+                       items.append(point[1])
+                       point = point[0], point[1]+1
+               return items
+       
+       def setselection(self, selection):
+               if not self._parent or not self._list:
+                       self._sel = selection
+                       return
+               set_sel = self._list.LSetSelect
+               for i in range(len(self.items)):
+                       if i in selection:
+                               set_sel(1, (0, i))
+                       else:
+                               set_sel(0, (0, i))
+               self._list.LAutoScroll()
+       
+       def getselectedobjects(self):
+               sel = self.getselection()
+               objects = []
+               for i in sel:
+                       objects.append(self.items[i])
+               return objects
+       
+       def setselectedobjects(self, objects):
+               sel = []
+               for o in objects:
+                       try:
+                               sel.append(self.items.index(o))
+                       except:
+                               pass
+               self.setselection(sel)
+       
+       def gettopcell(self):
+               l, t, r, b = self._bounds
+               t = t + 1
+               cl, ct, cr, cb = self._list.LRect((0, 0))
+               cellheight = cb - ct
+               return (t - ct) / cellheight
+       
+       def settopcell(self, topcell):
+               top = self.gettopcell()
+               diff = topcell - top
+               self._list.LScroll(0, diff)
+       
+       def draw(self, visRgn = None):
+               if self._visible:
+                       if not visRgn:
+                               visRgn = self._parentwindow.wid.GetWindowPort().visRgn
+                       self._list.LUpdate(visRgn)
+                       Qd.FrameRect(self._bounds)
+                       if self._selected and self._activated:
+                               self.drawselframe(1)
+       
+       def select(self, onoff, isclick = 0):
+               if Wbase.SelectableWidget.select(self, onoff):
+                       return
+               self.SetPort()
+               self.drawselframe(onoff)
+       
+       def activate(self, onoff):
+               self._activated = onoff
+               if self._visible:
+                       self._list.LActivate(onoff)
+                       if self._selected:
+                               self.drawselframe(onoff)
+       
+       def get(self):
+               return self.items
+       
+       def itemrepr(self, item):
+               return str(item)[:255]
+       
+       def __getitem__(self, index):
+               return self.items[index]
+       
+       def __setitem__(self, index, item):
+               if self._parent and self._list:
+                       self._list.LSetCell(self.itemrepr(item), (0, index))
+               self.items[index] = item
+       
+       def __delitem__(self, index):
+               if self._parent and self._list:
+                       self._list.LDelRow(1, index)
+               del self.items[index]
+       
+       def __getslice__(self, a, b):
+               return self.items[a:b]
+       
+       def __delslice__(self, a, b):
+               if b-a:
+                       if self._parent and self._list:
+                               self._list.LDelRow(b-a, a)
+                       del self.items[a:b]
+       
+       def __setslice__(self, a, b, items):
+               if self._parent and self._list:
+                       l = len(items)
+                       the_list = self._list
+                       self.setdrawingmode(0)
+                       if b-a:
+                               if b > len(self.items):
+                                       # fix for new 1.5 "feature" where b is sys.maxint instead of len(self)...
+                                       # LDelRow doesn't like maxint.
+                                       b = len(self.items)
+                               the_list.LDelRow(b-a, a)
+                       the_list.LAddRow(l, a)
+                       self_itemrepr = self.itemrepr
+                       set_cell = the_list.LSetCell
+                       for i in range(len(items)):
+                               set_cell(self_itemrepr(items[i]), (0, i + a))
+                       self.items[a:b] = items
+                       self.setdrawingmode(1)
+               else:
+                       self.items[a:b] = items
+       
+       def __len__(self):
+               return len(self.items)
+       
+       def append(self, item):
+               if self._parent and self._list:
+                       index = len(self.items)
+                       self._list.LAddRow(1, index)
+                       self._list.LSetCell(self.itemrepr(item), (0, index))
+               self.items.append(item)
+       
+       def remove(self, item):
+               index = self.items.index(item)
+               self.__delitem__(index)
+       
+       def index(self, item):
+               return self.items.index(item)
+       
+       def insert(self, index, item):
+               if index < 0:
+                       index = 0
+               if self._parent and self._list:
+                       self._list.LAddRow(1, index)
+                       self._list.LSetCell(self.itemrepr(item), (0, index))
+               self.items.insert(index, item)
+       
+       def setdrawingmode(self, onoff):
+               if onoff:
+                       self.drawingmode = self.drawingmode - 1
+                       if self.drawingmode == 0 and self._list is not None:
+                               self._list.LSetDrawingMode(1)
+                               if self._visible:
+                                       bounds = l, t, r, b = Qd.InsetRect(self._bounds, 1, 1)
+                                       cl, ct, cr, cb = self._list.LRect((0, len(self.items)-1))
+                                       if cb < b:
+                                               self.SetPort()
+                                               Qd.EraseRect((l, cb, cr, b))
+                                       self._list.LUpdate(self._parentwindow.wid.GetWindowPort().visRgn)
+                                       Win.ValidRect(bounds)
+               else:
+                       if self.drawingmode == 0 and self._list is not None:
+                               self._list.LSetDrawingMode(0)
+                       self.drawingmode = self.drawingmode + 1
+
+
+class TwoLineList(List):
+       
+       LDEF_ID = 468
+       
+       def createlist(self):
+               import List
+               self._calcbounds()
+               self.SetPort()
+               rect = self._bounds
+               rect = rect[0]+1, rect[1]+1, rect[2]-16, rect[3]-1
+               self._list = List.LNew(rect, (0, 0, 1, 0), (0, 28), self.LDEF_ID, self._parentwindow.wid,
+                                       0, 1, 0, 1)
+               self.set(self.items)
+       
+
+class ResultsWindow:
+       
+       """Simple results window. The user cannot make this window go away completely:
+       closing it will just hide it. It will remain in the windows list. The owner of this window
+       should call the done() method to indicate it's done with it.
+       """
+       
+       def __init__(self, possize=None, title="Results", callback=None):
+               import W
+               if possize is None:
+                       possize = (500, 200)
+               self.w = W.Window(possize, title, minsize=(200, 100))
+               self.w.results = W.TwoLineList((-1, -1, 1, -14), callback=None)
+               self.w.bind("<close>", self.hide)
+               self.w.open()
+               self._done = 0
+       
+       def done(self):
+               self._done = 1
+               if not self.w.isvisible():
+                       self.w.close()
+       
+       def hide(self):
+               if not self._done:
+                       self.w.show(0)
+                       return -1
+       
+       def append(self, msg):
+               if not self.w.isvisible():
+                       self.w.show(1)
+                       self.w.select()
+               msg = string.replace(msg, '\n', '\r')
+               self.w.results.append(msg)
+               self.w.results.setselection([len(self.w.results)-1])
+       
+       def __getattr__(self, attr):
+               return getattr(self.w.results, attr)
+
+
+class MultiList(List):
+       
+       """XXX Experimantal!!!"""
+       
+       def setitems(self, items):
+               self.items = items
+               if not self._parent or not self._list:
+                       return
+               self._list.LDelRow(0, 1)
+               self.setdrawingmode(0)
+               self._list.LAddRow(len(self.items), 0)
+               self_itemrepr = self.itemrepr
+               set_cell = self._list.LSetCell
+               for i in range(len(items)):
+                       row = items[i]
+                       for j in range(len(row)):
+                               item = row[j]
+                               set_cell(self_itemrepr(item), (j, i))
+               self.setdrawingmode(1)
+       
+       def getselection(self):
+               if not self._parent or not self._list:
+                       if hasattr(self, "_sel"):
+                               return self._sel
+                       return []
+               items = []
+               point = (0,0)
+               while 1:
+                       ok, point = self._list.LGetSelect(1, point)
+                       if not ok:
+                               break
+                       items.append(point[1])
+                       point = point[0], point[1]+1
+               return items
+       
+       def setselection(self, selection):
+               if not self._parent or not self._list:
+                       self._sel = selection
+                       return
+               set_sel = self._list.LSetSelect
+               for i in range(len(self.items)):
+                       if i in selection:
+                               set_sel(1, (0, i))
+                       else:
+                               set_sel(0, (0, i))
+               #self._list.LAutoScroll()
+
diff --git a/Mac/Tools/IDE/Wmenus.py b/Mac/Tools/IDE/Wmenus.py
new file mode 100644 (file)
index 0000000..387590b
--- /dev/null
@@ -0,0 +1,196 @@
+import FrameWork
+import Qd
+import Wbase
+from types import *
+import Wapplication
+
+_arrowright = Qd.GetPicture(472)
+_arrowdown = Qd.GetPicture(473)
+
+
+
+class PopupWidget(Wbase.ClickableWidget):
+       
+       """Simple title-less popup widget. Should be 16x16 pixels. 
+       Builds the menu items on the fly, good for dynamic popup menus."""
+       
+       def __init__(self, possize, items = [], callback = None):
+               Wbase.Widget.__init__(self, possize)
+               self._items = items
+               self._itemsdict = {}
+               self._callback = callback
+               self._enabled = 1
+       
+       def close(self):
+               Wbase.Widget.close(self)
+               self._items = None
+               self._itemsdict = {}
+       
+       def draw(self, visRgn = None):
+               if self._visible:
+                       Qd.FrameRect(self._bounds)
+                       Qd.EraseRect(Qd.InsetRect(self._bounds, 1, 1))
+                       l, t, r, b = self._bounds
+                       l = l + 2
+                       t = t + 3
+                       pictframe = (l, t, l + 10, t + 10)
+                       Qd.DrawPicture(_arrowright, pictframe)
+       
+       def click(self, point, modifiers):
+               if not self._enabled:
+                       return
+               self.menu = FrameWork.Menu(self._parentwindow.parent.menubar, 'Foo', -1)
+               self._additems(self._items, self.menu)
+               self.SetPort()
+               l, t, r, b = self._bounds
+               l, t = Qd.LocalToGlobal((l+1, t+1))
+               Wbase.SetCursor("arrow")
+               self.menu.menu.EnableItem(0)
+               reply = self.menu.menu.PopUpMenuSelect(t, l, 1)
+               if reply:
+                       id = (reply & 0xffff0000) >> 16
+                       item = reply & 0xffff
+                       self._menu_callback(id, item)
+               self._emptymenu()
+       
+       def set(self, items):
+               self._items = items
+       
+       def get(self):
+               return self._items
+       
+       def _additems(self, items, menu):
+               from FrameWork import SubMenu, MenuItem
+               menu_id = menu.id
+               for item in items:
+                       if item == "-":
+                               menu.addseparator()
+                               continue
+                       elif type(item) == ListType:
+                               submenu = SubMenu(menu, item[0])
+                               self._additems(item[1:], submenu)
+                               continue
+                       elif type(item) == StringType:
+                               menuitemtext = object = item
+                       elif type(item) == TupleType and len(item) == 2:
+                               menuitemtext, object = item
+                       else:
+                               raise Wbase.WidgetsError, "illegal itemlist for popup menu"
+                       
+                       if menuitemtext[:1] == '\0':
+                               check = ord(menuitemtext[1])
+                               menuitemtext = menuitemtext[2:]
+                       else:
+                               check = 0
+                       menuitem = MenuItem(menu, menuitemtext, None, None)
+                       if check:
+                               menuitem.check(1)
+                       self._itemsdict[(menu_id, menuitem.item)] = object
+       
+       def _emptymenu(self):
+               menus = self._parentwindow.parent.menubar.menus
+               for id, item in self._itemsdict.keys():
+                       if menus.has_key(id):
+                               self.menu = menus[id]
+                               self.menu.delete()
+               self._itemsdict = {}
+       
+       def _menu_callback(self, id, item):
+               thing = self._itemsdict[(id, item)]
+               if callable(thing):
+                       thing()
+               elif self._callback:
+                       Wbase.CallbackCall(self._callback, 0, thing)
+
+
+class PopupMenu(PopupWidget):
+       
+       """Simple title-less popup widget. Should be 16x16 pixels. 
+       Prebuilds the menu items, good for static (non changing) popup menus."""
+       
+       def open(self):
+               self._calcbounds()
+               self.menu = Wapplication.Menu(self._parentwindow.parent.menubar, 'Foo', -1)
+               self._additems(self._items, self.menu)
+       
+       def close(self):
+               self._emptymenu()
+               Wbase.Widget.close(self)
+               self._items = None
+               self._itemsdict = {}
+               self.menu = None
+       
+       def set(self, items):
+               if self._itemsdict:
+                       self._emptymenu()
+               self.menu = Wapplication.Menu(self._parentwindow.parent.menubar, 'Foo', -1)
+               self._items = items
+               self._additems(self._items, self.menu)
+       
+       def click(self, point, modifiers):
+               if not self._enabled:
+                       return
+               self.SetPort()
+               l, t, r, b = self._bounds
+               l, t = Qd.LocalToGlobal((l+1, t+1))
+               Wbase.SetCursor("arrow")
+               self.menu.menu.EnableItem(0)
+               reply = self.menu.menu.PopUpMenuSelect(t, l, 1)
+               if reply:
+                       id = (reply & 0xffff0000) >> 16
+                       item = reply & 0xffff
+                       self._menu_callback(id, item)
+
+
+class FontMenu(PopupMenu):
+       
+       """A font popup menu."""
+       
+       menu = None
+       
+       def __init__(self, possize, callback):
+               PopupMenu.__init__(self, possize)
+               _makefontmenu()
+               self._callback = callback
+               self._enabled = 1
+       
+       def open(self):
+               self._calcbounds()
+       
+       def close(self):
+               del self._callback
+       
+       def set(self):
+               raise Wbase.WidgetsError, "can't change font menu widget"
+       
+       def _menu_callback(self, id, item):
+               fontname = self.menu.menu.GetMenuItemText(item)
+               if self._callback:
+                       Wbase.CallbackCall(self._callback, 0, fontname)
+
+       def click(self, point, modifiers):
+               if not self._enabled:
+                       return
+               _makefontmenu()
+               return PopupMenu.click(self, point, modifiers)
+       
+
+def _makefontmenu():
+       """helper for font menu"""
+       if FontMenu.menu is not None:
+               return
+       import W
+       FontMenu.menu = Wapplication.Menu(W.getapplication().menubar, 'Foo', -1)
+       W.SetCursor('watch')
+       for i in range(FontMenu.menu.menu.CountMItems(), 0, -1):
+               FontMenu.menu.menu.DeleteMenuItem(i)
+       FontMenu.menu.menu.AppendResMenu('FOND')
+
+
+def _getfontlist():
+       import Res
+       fontnames = []
+       for i in range(1, Res.CountResources('FOND') + 1):
+               r = Res.GetIndResource('FOND', i)
+               fontnames.append(r.GetResInfo()[2])
+       return fontnames
diff --git a/Mac/Tools/IDE/Wquicktime.py b/Mac/Tools/IDE/Wquicktime.py
new file mode 100644 (file)
index 0000000..a33462a
--- /dev/null
@@ -0,0 +1,119 @@
+import os
+import Qd
+import Win
+import Qt, QuickTime
+import W
+import macfs
+import Evt, Events
+
+_moviesinitialized = 0
+
+def EnterMovies():
+       global _moviesinitialized
+       if not _moviesinitialized:
+               Qt.EnterMovies()
+               _moviesinitialized = 1
+
+class Movie(W.Widget):
+       
+       def __init__(self, possize):
+               EnterMovies()
+               self.movie = None
+               self.running = 0
+               W.Widget.__init__(self, possize)
+       
+       def adjust(self, oldbounds):
+               self.SetPort()
+               Win.InvalRect(oldbounds)
+               Win.InvalRect(self._bounds)
+               self.calcmoviebox()
+       
+       def set(self, path_or_fss, start = 0):
+               self.SetPort()
+               if self.movie:
+                       #Win.InvalRect(self.movie.GetMovieBox())
+                       Qd.PaintRect(self.movie.GetMovieBox())
+               if type(path_or_fss) == type(''):
+                       path = path_or_fss
+                       fss = macfs.FSSpec(path)
+               else:
+                       path = path_or_fss.as_pathname()
+                       fss = path_or_fss
+               self.movietitle = os.path.basename(path)
+               movieResRef = Qt.OpenMovieFile(fss, 1)
+               self.movie, dummy, dummy = Qt.NewMovieFromFile(movieResRef, 0, QuickTime.newMovieActive)
+               self.moviebox = self.movie.GetMovieBox()
+               self.calcmoviebox()
+               Qd.ObscureCursor()      # XXX does this work at all?
+               self.movie.GoToBeginningOfMovie()
+               if start:
+                       self.movie.StartMovie()
+                       self.running = 1
+               else:
+                       self.running = 0
+                       self.movie.MoviesTask(0)
+       
+       def get(self):
+               return self.movie
+       
+       def getmovietitle(self):
+               return self.movietitle
+       
+       def start(self):
+               if self.movie:
+                       Qd.ObscureCursor()
+                       self.movie.StartMovie()
+                       self.running = 1
+       
+       def stop(self):
+               if self.movie:
+                       self.movie.StopMovie()
+                       self.running = 0
+       
+       def rewind(self):
+               if self.movie:
+                       self.movie.GoToBeginningOfMovie()
+       
+       def calcmoviebox(self):
+               if not self.movie:
+                       return
+               ml, mt, mr, mb = self.moviebox
+               wl, wt, wr, wb = widgetbox = self._bounds
+               mheight = mb - mt
+               mwidth = mr - ml
+               wheight = wb - wt
+               wwidth = wr - wl
+               if (mheight * 2 < wheight) and (mwidth * 2 < wwidth):
+                       scale = 2
+               elif mheight > wheight or mwidth > wwidth:
+                       scale = min(float(wheight) / mheight, float(wwidth) / mwidth)
+               else:
+                       scale = 1
+               mwidth, mheight = mwidth * scale, mheight * scale
+               ml, mt = wl + (wwidth - mwidth) / 2, wt + (wheight - mheight) / 2
+               mr, mb = ml + mwidth, mt + mheight
+               self.movie.SetMovieBox((ml, mt, mr, mb))
+       
+       def idle(self, *args):
+               if self.movie:
+                       if not self.movie.IsMovieDone() and self.running:
+                               Qd.ObscureCursor()
+                               while 1:
+                                       self.movie.MoviesTask(0)
+                                       gotone, event = Evt.EventAvail(Events.everyEvent)
+                                       if gotone or self.movie.IsMovieDone():
+                                               break
+                       elif self.running:
+                               box = self.movie.GetMovieBox()
+                               self.SetPort()
+                               Win.InvalRect(box)
+                               self.movie = None
+                               self.running = 0
+       
+       def draw(self, visRgn = None):
+               if self._visible:
+                       Qd.PaintRect(self._bounds)
+                       if self.movie:
+                               self.movie.UpdateMovie()
+                               self.movie.MoviesTask(0)
+
diff --git a/Mac/Tools/IDE/Wtext.py b/Mac/Tools/IDE/Wtext.py
new file mode 100644 (file)
index 0000000..821e4c5
--- /dev/null
@@ -0,0 +1,979 @@
+import Qd
+import TE
+import Fm
+import waste
+import WASTEconst
+import Res
+import Evt
+import Events
+import Scrap
+import string
+
+import Win
+import Wbase
+import Wkeys
+import Wcontrols
+import PyFontify
+from types import *
+import Fonts
+import TextEdit
+
+
+
+class TextBox(Wbase.Widget):
+       
+       """A static text widget"""
+       
+       def __init__(self, possize, text = "", align = TextEdit.teJustLeft, 
+                               fontsettings = ("Python-Sans", 0, 9, (0, 0, 0)),
+                               backgroundcolor = (0xffff, 0xffff, 0xffff)
+                               ):
+       
+               Wbase.Widget.__init__(self, possize)
+               self.fontsettings = fontsettings
+               self.text = text
+               self.align = align
+               self._backgroundcolor = backgroundcolor
+       
+       def draw(self, visRgn = None):
+               if self._visible:
+                       (font, style, size, color) = self.fontsettings
+                       fontid = GetFNum(font)
+                       savestate = Qd.GetPenState()
+                       Qd.TextFont(fontid)
+                       Qd.TextFace(style)
+                       Qd.TextSize(size)
+                       Qd.RGBForeColor(color)
+                       Qd.RGBBackColor(self._backgroundcolor)
+                       TE.TETextBox(self.text, self._bounds, self.align)
+                       Qd.RGBBackColor((0xffff, 0xffff, 0xffff))
+                       Qd.SetPenState(savestate)
+       
+       def get(self):
+               return self.text
+       
+       def set(self, text):
+               self.text = text
+               if self._parentwindow and self._parentwindow.wid:
+                       self.SetPort()
+                       self.draw()
+
+
+class _ScrollWidget:
+       
+       # to be overridden
+       def getscrollbarvalues(self):
+               return None, None
+       
+       # internal method
+       def updatescrollbars(self):
+               vx, vy = self.getscrollbarvalues()
+               if self._parent._barx:
+                       if vx <> None:
+                               self._parent._barx.enable(1)
+                               self._parent._barx.set(vx)
+                       else:
+                               self._parent._barx.enable(0)
+               if self._parent._bary:
+                       if vy <> None:
+                               self._parent._bary.enable(1)
+                               self._parent._bary.set(vy)
+                       else:
+                               self._parent._bary.enable(0)
+       
+
+UNDOLABELS = [ # Indexed by WEGetUndoInfo() value
+       None, "", "typing", "Cut", "Paste", "Clear", "Drag", "Style"]
+
+
+class EditText(Wbase.SelectableWidget, _ScrollWidget):
+       
+       """A text edit widget, mainly for simple entry fields."""
+       
+       def __init__(self, possize, text = "", 
+                               callback = None, inset = (3, 3), 
+                               fontsettings = ("Python-Sans", 0, 9, (0, 0, 0)),
+                               tabsettings = (32, 0),
+                               readonly = 0):
+                               
+               Wbase.SelectableWidget.__init__(self, possize)
+               self.temptext = text
+               self.ted = None
+               self.selection = None
+               self._callback = callback
+               self.changed = 0
+               self.selchanged = 0
+               self._selected = 0
+               self._enabled = 1
+               self.wrap = 1
+               self.readonly = readonly
+               self.fontsettings = fontsettings
+               self.tabsettings = tabsettings
+               if type(inset) <> TupleType:
+                       self.inset = (inset, inset)
+               else:
+                       self.inset = inset
+       
+       def open(self):
+               if not hasattr(self._parent, "_barx"):
+                       self._parent._barx = None
+               if not hasattr(self._parent, "_bary"):
+                       self._parent._bary = None
+               self._calcbounds()
+               self.SetPort()
+               viewrect, destrect = self._calctextbounds()
+               flags = self._getflags()
+               self.ted = waste.WENew(destrect, viewrect, flags)
+               self.ted.WEInstallTabHooks()
+               self.ted.WESetAlignment(WASTEconst.weFlushLeft)
+               self.setfontsettings(self.fontsettings)
+               self.settabsettings(self.tabsettings)
+               self.ted.WEUseText(Res.Resource(self.temptext))
+               self.ted.WECalText()
+               if self.selection:
+                       self.setselection(self.selection[0], self.selection[1])
+                       self.selection = None
+               else:
+                       self.selview()
+               self.temptext = None
+               self.updatescrollbars()
+               self.bind("pageup", self.scrollpageup)
+               self.bind("pagedown", self.scrollpagedown)
+               self.bind("top", self.scrolltop)
+               self.bind("bottom", self.scrollbottom)
+               self.selchanged = 0
+       
+       def close(self):
+               self._parent._barx = None
+               self._parent._bary = None
+               self.ted = None
+               self.temptext = None
+               Wbase.SelectableWidget.close(self)
+       
+       def gettabsettings(self):
+               return self.tabsettings
+       
+       def settabsettings(self, (tabsize, tabmode)):
+               self.tabsettings = (tabsize, tabmode)
+               if hasattr(self.ted, "WESetTabSize"):
+                       port = self._parentwindow.wid.GetWindowPort()
+                       if tabmode:
+                               (font, style, size, color) = self.getfontsettings()
+                               savesettings = GetPortFontSettings(port)
+                               SetPortFontSettings(port, (font, style, size))
+                               tabsize = Qd.StringWidth(' ' * tabsize)
+                               SetPortFontSettings(port, savesettings)
+                       tabsize = max(tabsize, 1)
+                       self.ted.WESetTabSize(tabsize)
+                       self.SetPort()
+                       Qd.EraseRect(self.ted.WEGetViewRect())
+                       self.ted.WEUpdate(port.visRgn)
+       
+       def getfontsettings(self):
+               import Res
+               (font, style, size, color) = self.ted.WEGetRunInfo(0)[4]
+               font = Fm.GetFontName(font)
+               return (font, style, size, color)
+       
+       def setfontsettings(self, (font, style, size, color)):
+               self.SetPort()
+               if type(font) <> StringType:
+                       font = Fm.GetFontName(font)
+               self.fontsettings = (font, style, size, color)
+               fontid = GetFNum(font)
+               readonly = self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, -1)
+               if readonly:
+                       self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 0)
+               try:
+                       self.ted.WEFeatureFlag(WASTEconst.weFInhibitRecal, 1)
+                       selstart, selend = self.ted.WEGetSelection()
+                       self.ted.WESetSelection(0, self.ted.WEGetTextLength())
+                       self.ted.WESetStyle(WASTEconst.weDoFace, (0, 0, 0, (0, 0, 0)))
+                       self.ted.WESetStyle(WASTEconst.weDoFace | 
+                                               WASTEconst.weDoColor | 
+                                               WASTEconst.weDoFont | 
+                                               WASTEconst.weDoSize, 
+                                               (fontid, style, size, color))
+                       self.ted.WEFeatureFlag(WASTEconst.weFInhibitRecal, 0)
+                       self.ted.WECalText()
+                       self.ted.WESetSelection(selstart, selend)
+               finally:
+                       if readonly:
+                               self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 1)
+               viewrect = self.ted.WEGetViewRect()
+               Qd.EraseRect(viewrect)
+               self.ted.WEUpdate(self._parentwindow.wid.GetWindowPort().visRgn)
+               self.selchanged = 1
+               self.updatescrollbars()
+       
+       def adjust(self, oldbounds):
+               self.SetPort()
+               if self._selected and self._parentwindow._hasselframes:
+                       Win.InvalRect(Qd.InsetRect(oldbounds, -3, -3))
+                       Win.InvalRect(Qd.InsetRect(self._bounds, -3, -3))
+               else:
+                       Win.InvalRect(oldbounds)
+                       Win.InvalRect(self._bounds)
+               viewrect, destrect = self._calctextbounds()
+               self.ted.WESetViewRect(viewrect)
+               self.ted.WESetDestRect(destrect)
+               if self.wrap:
+                       self.ted.WECalText()
+               if self.ted.WEGetDestRect()[3] < viewrect[1]:
+                       self.selview()
+               self.updatescrollbars()
+       
+       # interface -----------------------
+       # selection stuff
+       def selview(self):
+               self.ted.WESelView()
+       
+       def selectall(self):
+               self.ted.WESetSelection(0, self.ted.WEGetTextLength())
+               self.selchanged = 1
+               self.updatescrollbars()
+       
+       def selectline(self, lineno, charoffset = 0):
+               newselstart, newselend = self.ted.WEGetLineRange(lineno)
+               # Autoscroll makes the *end* of the selection visible, which, 
+               # in the case of a whole line, is the beginning of the *next* line. 
+               # So sometimes it leaves our line just above the view rect. 
+               # Let's fool Waste by initially selecting one char less:
+               self.ted.WESetSelection(newselstart + charoffset, newselend-1)
+               self.ted.WESetSelection(newselstart + charoffset, newselend)
+               self.selchanged = 1
+               self.updatescrollbars()
+       
+       def getselection(self):
+               if self.ted:
+                       return self.ted.WEGetSelection()
+               else:
+                       return self.selection
+       
+       def setselection(self, selstart, selend):
+               self.selchanged = 1
+               if self.ted:
+                       self.ted.WESetSelection(selstart, selend)
+                       self.ted.WESelView()
+                       self.updatescrollbars()
+               else:
+                       self.selection = selstart, selend
+       
+       def offsettoline(self, offset):
+               return self.ted.WEOffsetToLine(offset)
+       
+       def countlines(self):
+               return self.ted.WECountLines()
+       
+       def getselectedtext(self):
+               selstart, selend = self.ted.WEGetSelection()
+               return self.ted.WEGetText().data[selstart:selend]
+       
+       def expandselection(self):
+               oldselstart, oldselend = self.ted.WEGetSelection()
+               selstart, selend = min(oldselstart, oldselend), max(oldselstart, oldselend)
+               if selstart <> selend and chr(self.ted.WEGetChar(selend-1)) == '\r':
+                       selend = selend - 1
+               newselstart, dummy = self.ted.WEFindLine(selstart, 0)
+               dummy, newselend = self.ted.WEFindLine(selend, 0)
+               if oldselstart <> newselstart or  oldselend <> newselend:
+                       self.ted.WESetSelection(newselstart, newselend)
+                       self.updatescrollbars()
+               self.selchanged = 1
+       
+       def insert(self, text):
+               self.ted.WEInsert(text, None, None)
+               self.changed = 1
+               self.selchanged = 1
+       
+       # text
+       def set(self, text):
+               if not self.ted:
+                       self.temptext = text
+               else:
+                       self.ted.WEUseText(Res.Resource(text))
+                       self.ted.WECalText()
+                       self.SetPort()
+                       viewrect, destrect = self._calctextbounds()
+                       self.ted.WESetViewRect(viewrect)
+                       self.ted.WESetDestRect(destrect)
+                       rgn = Qd.NewRgn()
+                       Qd.RectRgn(rgn, viewrect)
+                       Qd.EraseRect(viewrect)
+                       self.draw(rgn)
+                       #Win.InvalRect(self.ted.WEGetViewRect())
+                       self.updatescrollbars()
+       
+       def get(self):
+               if not self._parent:
+                       return self.temptext
+               else:
+                       return self.ted.WEGetText().data
+       
+       # events
+       def key(self, char, event):
+               (what, message, when, where, modifiers) = event
+               if self._enabled and not modifiers & Events.cmdKey or char in Wkeys.arrowkeys:
+                       self.ted.WEKey(ord(char), modifiers)
+                       if char not in Wkeys.navigationkeys:
+                               self.changed = 1
+                       if char not in Wkeys.scrollkeys:
+                               self.selchanged = 1
+                       self.updatescrollbars()
+                       if self._callback:
+                               Wbase.CallbackCall(self._callback, 0, char, modifiers)
+       
+       def click(self, point, modifiers):
+               if not self._enabled:
+                       return
+               self.ted.WEClick(point, modifiers, Evt.TickCount())
+               self.selchanged = 1
+               self.updatescrollbars()
+               return 1
+       
+       def idle(self):
+               self.SetPort()
+               self.ted.WEIdle()
+       
+       def rollover(self, point, onoff):
+               if onoff:
+                       Wbase.SetCursor("iBeam")
+       
+       def activate(self, onoff):
+               self._activated = onoff
+               if self._selected and self._visible:
+                       if onoff:
+                               self.ted.WEActivate()
+                       else:
+                               self.ted.WEDeactivate()
+                       if self._selected:
+                               self.drawselframe(onoff)
+       
+       def select(self, onoff, isclick = 0):
+               if Wbase.SelectableWidget.select(self, onoff):
+                       return
+               self.SetPort()
+               if onoff:
+                       self.ted.WEActivate()
+                       if self._parentwindow._tabbable and not isclick:
+                               self.selectall()
+               else:
+                       self.ted.WEDeactivate()
+               self.drawselframe(onoff)
+       
+       def draw(self, visRgn = None):
+               if self._visible:
+                       if not visRgn:
+                               visRgn = self._parentwindow.wid.GetWindowPort().visRgn
+                       self.ted.WEUpdate(visRgn)
+                       if self._selected and self._activated:
+                               self.drawselframe(1)
+                       Qd.FrameRect(self._bounds)
+       
+       # scrolling
+       def scrollpageup(self):
+               if self._parent._bary and self._parent._bary._enabled:
+                       self.vscroll("++")
+       
+       def scrollpagedown(self):
+               if self._parent._bary and self._parent._bary._enabled:
+                       self.vscroll("--")
+       
+       def scrolltop(self):
+               if self._parent._bary and self._parent._bary._enabled:
+                       self.vscroll(0)
+               if self._parent._barx and self._parent._barx._enabled:
+                       self.hscroll(0)
+       
+       def scrollbottom(self):
+               if self._parent._bary and self._parent._bary._enabled:
+                       self.vscroll(32767)
+       
+       # menu handlers
+       def domenu_copy(self, *args):
+               selbegin, selend = self.ted.WEGetSelection()
+               if selbegin == selend:
+                       return
+               Scrap.ZeroScrap()
+               self.ted.WECopy()
+               self.updatescrollbars()
+       
+       def domenu_cut(self, *args):
+               selbegin, selend = self.ted.WEGetSelection()
+               if selbegin == selend:
+                       return
+               Scrap.ZeroScrap()
+               self.ted.WECut()
+               self.updatescrollbars()
+               self.selview()
+               self.changed = 1
+               self.selchanged = 1
+               if self._callback:
+                       Wbase.CallbackCall(self._callback, 0, "", None)
+       
+       def domenu_paste(self, *args):
+               if not self.ted.WECanPaste():
+                       return
+               self.selview()
+               self.ted.WEPaste()
+               self.updatescrollbars()
+               self.changed = 1
+               self.selchanged = 1
+               if self._callback:
+                       Wbase.CallbackCall(self._callback, 0, "", None)
+       
+       def domenu_clear(self, *args):
+               self.ted.WEDelete()
+               self.selview()
+               self.updatescrollbars()
+               self.changed = 1
+               self.selchanged = 1
+               if self._callback:
+                       Wbase.CallbackCall(self._callback, 0, "", None)
+       
+       def domenu_undo(self, *args):
+               which, redo = self.ted.WEGetUndoInfo()
+               if not which: 
+                       return
+               self.ted.WEUndo()
+               self.updatescrollbars()
+               self.changed = 1
+               self.selchanged = 1
+               if self._callback:
+                       Wbase.CallbackCall(self._callback, 0, "", None)
+       
+       def can_undo(self, menuitem):
+               #doundo = self.ted.WEFeatureFlag(WASTEconst.weFUndo, -1)
+               #print doundo
+               #if not doundo:
+               #       return 0
+               which, redo = self.ted.WEGetUndoInfo()
+               which = UNDOLABELS[which]
+               if which == None: 
+                       return None
+               if redo:
+                       which = "Redo "+which
+               else:
+                       which = "Undo "+which
+               menuitem.settext(which)
+               return 1
+       
+       def domenu_selectall(self, *args):
+               self.selectall()
+       
+       # private
+       def getscrollbarvalues(self):
+               dr = self.ted.WEGetDestRect()
+               vr = self.ted.WEGetViewRect()
+               vx = Wcontrols._scalebarvalue(dr[0], dr[2], vr[0], vr[2])
+               vy = Wcontrols._scalebarvalue(dr[1], dr[3], vr[1], vr[3])
+               return vx, vy
+       
+       def vscroll(self, value):
+               lineheight = self.ted.WEGetHeight(0, 1)
+               dr = self.ted.WEGetDestRect()
+               vr = self.ted.WEGetViewRect()
+               destheight = dr[3] - dr[1]
+               viewheight = vr[3] - vr[1]
+               viewoffset = maxdelta = vr[1] - dr[1]
+               mindelta = vr[3] - dr[3]
+               if value == "+":
+                       delta = lineheight
+               elif value == "-":
+                       delta = - lineheight
+               elif value == "++":
+                       delta = viewheight - lineheight
+               elif value == "--":
+                       delta = lineheight - viewheight
+               else:   # in thumb
+                       cur = (32767 * viewoffset) / (destheight - viewheight)
+                       delta = (cur-value)*(destheight - viewheight)/32767
+                       if abs(delta - viewoffset) <=2:
+                               # compensate for irritating rounding error
+                               delta = viewoffset
+               delta = min(maxdelta, delta)
+               delta = max(mindelta, delta)
+               self.ted.WEScroll(0, delta)
+               self.updatescrollbars()
+       
+       def hscroll(self, value):
+               dr = self.ted.WEGetDestRect()
+               vr = self.ted.WEGetViewRect()
+               destwidth = dr[2] - dr[0]
+               viewwidth = vr[2] - vr[0]
+               viewoffset = maxdelta = vr[0] - dr[0]
+               mindelta = vr[2] - dr[2]
+               if value == "+":
+                       delta = 32
+               elif value == "-":
+                       delta = - 32
+               elif value == "++":
+                       delta = 0.5 * (vr[2] - vr[0])
+               elif value == "--":
+                       delta = 0.5 * (vr[0] - vr[2])
+               else:   # in thumb
+                       cur = (32767 * viewoffset) / (destwidth - viewwidth)
+                       delta = (cur-value)*(destwidth - viewwidth)/32767
+                       if abs(delta - viewoffset) <=2:
+                               # compensate for irritating rounding error
+                               delta = viewoffset
+               delta = min(maxdelta, delta)
+               delta = max(mindelta, delta)
+               self.ted.WEScroll(delta, 0)
+               self.updatescrollbars()
+       
+       # some internals
+       def _getflags(self):
+               flags = WASTEconst.weDoAutoScroll | WASTEconst.weDoMonoStyled
+               if self.readonly:
+                       flags = flags | WASTEconst.weDoReadOnly
+               else:
+                       flags = flags | WASTEconst.weDoUndo
+               return flags
+       
+       def _getviewrect(self):
+               return Qd.InsetRect(self._bounds, self.inset[0], self.inset[1])
+       
+       def _calctextbounds(self):
+               viewrect = l, t, r, b = self._getviewrect()
+               if self.ted:
+                       dl, dt, dr, db = self.ted.WEGetDestRect()
+                       vl, vt, vr, vb = self.ted.WEGetViewRect()
+                       yshift = t - vt
+                       if (db - dt) < (b - t):
+                               destrect = viewrect
+                       else:
+                               destrect = l, dt + yshift, r, db + yshift
+               else:
+                       destrect = viewrect
+               return viewrect, destrect
+               
+
+class TextEditor(EditText):
+       
+       """A text edit widget."""
+       
+       def __init__(self, possize, text = "", callback = None, wrap = 1, inset = (4, 4),
+                               fontsettings = ("Python-Sans", 0, 9, (0, 0, 0)),
+                               tabsettings = (32, 0),
+                               readonly = 0):
+               EditText.__init__(self, possize, text, callback, inset, fontsettings, tabsettings, readonly)
+               self.wrap = wrap
+       
+       def _getflags(self):
+               flags = WASTEconst.weDoAutoScroll | WASTEconst.weDoMonoStyled | \
+                               WASTEconst.weDoOutlineHilite
+               if self.readonly:
+                       flags = flags | WASTEconst.weDoReadOnly
+               else:
+                       flags = flags | WASTEconst.weDoUndo
+               return flags
+       
+       def _getviewrect(self):
+               l, t, r, b = self._bounds
+               return (l + 5, t + 2, r, b - 2)
+       
+       def _calctextbounds(self):
+               if self.wrap:
+                       return EditText._calctextbounds(self)
+               else:
+                       viewrect = l, t, r, b = self._getviewrect()
+                       if self.ted:
+                               dl, dt, dr, db = self.ted.WEGetDestRect()
+                               vl, vt, vr, vb = self.ted.WEGetViewRect()
+                               xshift = l - vl
+                               yshift = t - vt
+                               if (db - dt) < (b - t):
+                                       yshift = t - dt
+                               destrect = (dl + xshift, dt + yshift, dr + xshift, db + yshift)
+                       else:
+                               destrect = (l, t, r + 5000, b)
+                       return viewrect, destrect
+       
+       def draw(self, visRgn = None):
+               if self._visible:
+                       if not visRgn:
+                               visRgn = self._parentwindow.wid.GetWindowPort().visRgn
+                       self.ted.WEUpdate(visRgn)
+                       if self._selected and self._activated:
+                               self.drawselframe(1)
+
+
+import regex
+commentPat = regex.compile("[ \t]*\(#\)")
+indentPat = regex.compile("\t*")
+
+class PyEditor(TextEditor):
+       
+       """A specialized Python source edit widget"""
+       
+       def __init__(self, possize, text = "", callback = None, inset = (4, 4),
+                               fontsettings = ("Python-Sans", 0, 9, (0, 0, 0)),
+                               tabsettings = (32, 0),
+                               readonly = 0,
+                               debugger = None,
+                               file = ''):
+               TextEditor.__init__(self, possize, text, callback, 0, inset, fontsettings, tabsettings, readonly)
+               self.bind("cmd[", self.domenu_shiftleft)
+               self.bind("cmd]", self.domenu_shiftright)
+               self.bind("cmdshift[", self.domenu_uncomment)
+               self.bind("cmdshift]", self.domenu_comment)
+               self.file = file        # only for debugger reference
+               self._debugger = debugger
+               if debugger:
+                       debugger.register_editor(self, self.file)
+       
+       def domenu_shiftleft(self):
+               self.expandselection()
+               selstart, selend = self.ted.WEGetSelection()
+               selstart, selend = min(selstart, selend), max(selstart, selend)
+               snippet = self.getselectedtext()
+               lines = string.split(snippet, '\r')
+               for i in range(len(lines)):
+                       if lines[i][:1] == '\t':
+                               lines[i] = lines[i][1:]
+               snippet = string.join(lines, '\r')
+               self.insert(snippet)
+               self.ted.WESetSelection(selstart, selstart + len(snippet))
+       
+       def domenu_shiftright(self):
+               self.expandselection()
+               selstart, selend = self.ted.WEGetSelection()
+               selstart, selend = min(selstart, selend), max(selstart, selend)
+               snippet = self.getselectedtext()
+               lines = string.split(snippet, '\r')
+               for i in range(len(lines) - (not lines[-1])):
+                       lines[i] = '\t' + lines[i]
+               snippet = string.join(lines, '\r')
+               self.insert(snippet)
+               self.ted.WESetSelection(selstart, selstart + len(snippet))
+       
+       def domenu_uncomment(self):
+               self.expandselection()
+               selstart, selend = self.ted.WEGetSelection()
+               selstart, selend = min(selstart, selend), max(selstart, selend)
+               snippet = self.getselectedtext()
+               lines = string.split(snippet, '\r')
+               for i in range(len(lines)):
+                       res = commentPat.match(lines[i]) >= 0
+                       if res > 0:
+                               pos = commentPat.regs[1][0]
+                               lines[i] = lines[i][:pos] + lines[i][pos+1:]
+               snippet = string.join(lines, '\r')
+               self.insert(snippet)
+               self.ted.WESetSelection(selstart, selstart + len(snippet))
+       
+       def domenu_comment(self):
+               self.expandselection()
+               selstart, selend = self.ted.WEGetSelection()
+               selstart, selend = min(selstart, selend), max(selstart, selend)
+               snippet = self.getselectedtext()
+               lines = string.split(snippet, '\r')
+               indent = 3000 # arbitrary large number...
+               for line in lines:
+                       if string.strip(line):
+                               if indentPat.match(line):
+                                       indent = min(indent, indentPat.regs[0][1])
+                               else:
+                                       indent = 0
+                                       break
+               for i in range(len(lines) - (not lines[-1])):
+                       lines[i] = lines[i][:indent] + "#" + lines[i][indent:]
+               snippet = string.join(lines, '\r')
+               self.insert(snippet)
+               self.ted.WESetSelection(selstart, selstart + len(snippet))
+       
+       def setfile(self, file):
+               self.file = file
+       
+       def set(self, text, file = ''):
+               oldfile = self.file
+               self.file = file
+               if self._debugger:
+                       self._debugger.unregister_editor(self, oldfile)
+                       self._debugger.register_editor(self, file)
+               TextEditor.set(self, text)
+       
+       def close(self):
+               if self._debugger:
+                       self._debugger.unregister_editor(self, self.file)
+                       self._debugger = None
+               TextEditor.close(self)          
+       
+       def click(self, point, modifiers):
+               if not self._enabled:
+                       return
+               if self._debugger and self.pt_in_breaks(point):
+                       self.breakhit(point, modifiers)
+               elif self._debugger:
+                       bl, bt, br, bb = self._getbreakrect()
+                       Qd.EraseRect((bl, bt, br-1, bb))
+                       TextEditor.click(self, point, modifiers)
+                       self.drawbreakpoints()
+               else:
+                       TextEditor.click(self, point, modifiers)
+                       if self.ted.WEGetClickCount() >= 3:
+                               # select block with our indent
+                               lines = string.split(self.get(), '\r')
+                               selstart, selend = self.ted.WEGetSelection()
+                               lineno = self.ted.WEOffsetToLine(selstart)
+                               tabs = 0
+                               line = lines[lineno]
+                               while line[tabs:] and line[tabs] == '\t':
+                                       tabs = tabs + 1
+                               tabstag = '\t' * tabs
+                               fromline = 0
+                               toline = len(lines)
+                               if tabs:
+                                       for i in range(lineno - 1, -1, -1):
+                                               line = lines[i]
+                                               if line[:tabs] <> tabstag:
+                                                       fromline = i + 1
+                                                       break
+                                       for i in range(lineno + 1, toline):
+                                               line = lines[i]
+                                               if line[:tabs] <> tabstag:
+                                                       toline = i - 1
+                                                       break
+                               selstart, dummy = self.ted.WEGetLineRange(fromline)
+                               dummy, selend = self.ted.WEGetLineRange(toline)
+                               self.ted.WESetSelection(selstart, selend)
+       
+       def breakhit(self, point, modifiers):
+               if not self.file:
+                       return
+               destrect = self.ted.WEGetDestRect()
+               offset, edge = self.ted.WEGetOffset(point)
+               lineno = self.ted.WEOffsetToLine(offset) + 1
+               if point[1] <= destrect[3]:
+                       self._debugger.clear_breaks_above(self.file, self.countlines())
+                       self._debugger.toggle_break(self.file, lineno)
+               else:
+                       self._debugger.clear_breaks_above(self.file, lineno)
+       
+       def key(self, char, event):
+               (what, message, when, where, modifiers) = event
+               if modifiers & Events.cmdKey and not char in Wkeys.arrowkeys:
+                       return
+               if char == '\r':
+                       selstart, selend = self.ted.WEGetSelection()
+                       selstart, selend = min(selstart, selend), max(selstart, selend)
+                       lastchar = chr(self.ted.WEGetChar(selstart-1))
+                       if lastchar <> '\r' and selstart:
+                               pos, dummy = self.ted.WEFindLine(selstart, 0)
+                               lineres = Res.Resource('')
+                               self.ted.WECopyRange(pos, selstart, lineres, None, None)
+                               line = lineres.data + '\n'
+                               tabcount = self.extratabs(line)
+                               self.ted.WEKey(ord('\r'), 0)
+                               for i in range(tabcount):
+                                       self.ted.WEKey(ord('\t'), 0)
+                       else:
+                               self.ted.WEKey(ord('\r'), 0)
+               elif char in ')]}':
+                       self.ted.WEKey(ord(char), modifiers)
+                       self.balanceparens(char)
+               else:
+                       self.ted.WEKey(ord(char), modifiers)
+               if char not in Wkeys.navigationkeys:
+                       self.changed = 1
+               self.selchanged = 1
+               self.updatescrollbars()
+       
+       def balanceparens(self, char):
+               if char == ')':
+                       target = '('
+               elif char == ']':
+                       target = '['
+               elif char == '}':
+                       target = '{'
+               recursionlevel = 1
+               selstart, selend = self.ted.WEGetSelection()
+               count = min(selstart, selend) - 2
+               mincount = max(0, count - 2048)
+               lastquote = None
+               while count > mincount:
+                       testchar = chr(self.ted.WEGetChar(count))
+                       if testchar in "\"'" and chr(self.ted.WEGetChar(count - 1)) <> '\\':
+                               if lastquote == testchar:
+                                       recursionlevel = recursionlevel - 1
+                                       lastquote = None
+                               elif not lastquote:
+                                       recursionlevel = recursionlevel + 1
+                                       lastquote = testchar
+                       elif not lastquote and testchar == char:
+                               recursionlevel = recursionlevel + 1
+                       elif not lastquote and testchar == target:
+                               recursionlevel = recursionlevel - 1
+                               if recursionlevel == 0:
+                                       import time
+                                       autoscroll = self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, -1)
+                                       if autoscroll:
+                                               self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 0)
+                                       self.ted.WESetSelection(count, count + 1)
+                                       time.sleep(0.2)
+                                       self.ted.WESetSelection(selstart, selend)
+                                       if autoscroll:
+                                               self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 1)
+                                       break
+                       count = count - 1
+       
+       def extratabs(self, line):
+               tabcount = 0
+               for c in line:
+                       if c <> '\t':
+                               break
+                       tabcount = tabcount + 1
+               last = 0
+               cleanline = ''
+               tags = PyFontify.fontify(line)
+               # strip comments and strings
+               for tag, start, end, sublist in tags:
+                       if tag in ('string', 'comment'):
+                               cleanline = cleanline + line[last:start]
+                               last = end
+               cleanline = cleanline + line[last:]
+               cleanline = string.strip(cleanline)
+               if cleanline and cleanline[-1] == ':':
+                       tabcount = tabcount + 1
+               else:
+                       # extra indent after unbalanced (, [ or {
+                       for open, close in (('(', ')'), ('[', ']'), ('{', '}')):
+                               count = string.count(cleanline, open)
+                               if count and count > string.count(cleanline, close):
+                                       tabcount = tabcount + 2
+                                       break
+               return tabcount
+       
+       def rollover(self, point, onoff):
+               if onoff:
+                       if self._debugger and self.pt_in_breaks(point):
+                               Wbase.SetCursor("arrow")
+                       else:
+                               Wbase.SetCursor("iBeam")
+       
+       def draw(self, visRgn = None):
+               TextEditor.draw(self, visRgn)
+               if self._debugger:
+                       self.drawbreakpoints()
+       
+       def showbreakpoints(self, onoff):
+               if (not not self._debugger) <> onoff:
+                       if onoff:
+                               if not __debug__:
+                                       import W
+                                       raise W.AlertError, "CanĂ•t debug in Ă’Optimize bytecodeĂ“ mode.\r(see Ă’Default startup optionsĂ“ in EditPythonPreferences)"
+                               import PyDebugger
+                               self._debugger = PyDebugger.getdebugger()
+                               self._debugger.register_editor(self, self.file)
+                       elif self._debugger:
+                               self._debugger.unregister_editor(self, self.file)
+                               self._debugger = None
+                       self.adjust(self._bounds)
+       
+       def togglebreakpoints(self):
+               self.showbreakpoints(not self._debugger)
+       
+       def clearbreakpoints(self):
+               if self.file:
+                       self._debugger.clear_all_file_breaks(self.file)
+       
+       def editbreakpoints(self):
+               if self._debugger:
+                       self._debugger.edit_breaks()
+                       self._debugger.breaksviewer.selectfile(self.file)
+       
+       def drawbreakpoints(self, eraseall = 0):
+               breakrect = bl, bt, br, bb = self._getbreakrect()
+               br = br - 1
+               self.SetPort()
+               Qd.PenPat(Qd.qd.gray)
+               Qd.PaintRect((br, bt, br + 1, bb))
+               Qd.PenNormal()
+               self._parentwindow.tempcliprect(breakrect)
+               Qd.RGBForeColor((0xffff, 0, 0))
+               try:
+                       lasttop = bt
+                       self_ted = self.ted
+                       Qd_PaintOval = Qd.PaintOval
+                       Qd_EraseRect = Qd.EraseRect
+                       for lineno in self._debugger.get_file_breaks(self.file):
+                               start, end = self_ted.WEGetLineRange(lineno - 1)
+                               if lineno <> self_ted.WEOffsetToLine(start) + 1:
+                                       # breakpoints beyond our text: erase rest, and back out
+                                       Qd_EraseRect((bl, lasttop, br, bb))
+                                       break
+                               (x, y), h = self_ted.WEGetPoint(start, 0)
+                               bottom = y + h
+                               #print y, (lasttop, bottom)
+                               if bottom > lasttop:
+                                       Qd_EraseRect((bl, lasttop, br, y + h * eraseall))
+                                       lasttop = bottom
+                               redbullet = bl + 2, y + 3, bl + 8, y + 9
+                               Qd_PaintOval(redbullet)
+                       else:
+                               Qd_EraseRect((bl, lasttop, br, bb))
+                       Qd.RGBForeColor((0, 0, 0))
+               finally:
+                       self._parentwindow.restoreclip()
+       
+       def updatescrollbars(self):
+               if self._debugger:
+                       self.drawbreakpoints(1)
+               TextEditor.updatescrollbars(self)
+       
+       def pt_in_breaks(self, point):
+               return Qd.PtInRect(point, self._getbreakrect())
+       
+       def _getbreakrect(self):
+               if self._debugger:
+                       l, t, r, b = self._bounds
+                       return (l+1, t+1, l + 12, b-1)
+               else:
+                       return (0, 0, 0, 0)
+       
+       def _getviewrect(self):
+               l, t, r, b = self._bounds
+               if self._debugger:
+                       return (l + 17, t + 2, r, b - 2)
+               else:
+                       return (l + 5, t + 2, r, b - 2)
+       
+       def _calctextbounds(self):
+               viewrect = l, t, r, b = self._getviewrect()
+               if self.ted:
+                       dl, dt, dr, db = self.ted.WEGetDestRect()
+                       vl, vt, vr, vb = self.ted.WEGetViewRect()
+                       xshift = l - vl
+                       yshift = t - vt
+                       if (db - dt) < (b - t):
+                               yshift = t - dt
+                       destrect = (dl + xshift, dt + yshift, dr + xshift, db + yshift)
+               else:
+                       destrect = (l, t, r + 5000, b)
+               return viewrect, destrect
+
+
+def GetFNum(fontname):
+       """Same as Fm.GetFNum(), but maps a missing font to Monaco instead of the system font."""
+       if fontname <> Fm.GetFontName(0):
+               fontid = Fm.GetFNum(fontname)
+               if fontid == 0:
+                       fontid = Fonts.monaco
+       else:
+               fontid = 0
+       return fontid
+
+# b/w compat. Anyone using this?
+GetFName = Fm.GetFontName
+
+def GetPortFontSettings(port):
+       return Fm.GetFontName(port.txFont), port.txFace, port.txSize
+
+def SetPortFontSettings(port, (font, face, size)):
+       saveport = Qd.GetPort()
+       Qd.SetPort(port)
+       Qd.TextFont(GetFNum(font))
+       Qd.TextFace(face)
+       Qd.TextSize(size)
+       Qd.SetPort(saveport)
diff --git a/Mac/Tools/IDE/Wtraceback.py b/Mac/Tools/IDE/Wtraceback.py
new file mode 100644 (file)
index 0000000..82e487f
--- /dev/null
@@ -0,0 +1,188 @@
+import traceback
+import sys
+import W
+import os
+import types
+import List
+
+
+class TraceBack:
+       
+       def __init__(self, title = "Traceback"):
+               app = W.getapplication()  # checks if W is properly initialized
+               self.title = title
+               self.w = None
+               self.closed = 1
+               self.start = 0
+               self.lastwindowtitle = ""
+               self.bounds = (360, 298)
+       
+       def traceback(self, start = 0, lastwindowtitle = ""):
+               try:
+                       self.lastwindowtitle = lastwindowtitle
+                       self.start = start
+                       self.type, self.value, self.tb = sys.exc_type, sys.exc_value, sys.exc_traceback
+                       if self.type is not SyntaxError:
+                               self.show()
+                               if type(self.type) == types.ClassType:
+                                       errortext = self.type.__name__
+                               else:
+                                       errortext = str(self.type)
+                               value = str(self.value)
+                               if self.value and value:
+                                       errortext = errortext + ": " + value
+                               self.w.text.set(errortext)
+                               self.buildtblist()
+                               self.w.list.set(self.textlist)
+                               self.w.list.setselection([len(self.textlist) - 1])
+                               self.w.wid.SelectWindow()
+                               self.closed = 0
+                       else:
+                               self.syntaxerror()
+               except:
+                       traceback.print_exc()
+       
+       def syntaxerror(self):
+               try:
+                       value, (filename, lineno, charno, line) = self.value
+               except:
+                       filename = ""
+                       lineno = None
+                       value = self.value
+               if not filename and self.lastwindowtitle:
+                       filename = self.lastwindowtitle
+               elif not filename:
+                       filename = "<unknown>"
+               if filename and os.path.exists(filename):
+                       filename = os.path.split(filename)[1]
+               if lineno:
+                       charno = charno - 1
+                       text = str(value) + '\rFile: "' + str(filename) + '", line ' + str(lineno) + '\r\r' + line[:charno] + "Â¥" + line[charno:-1]
+               else:
+                       text = str(value) + '\rFile: "' + str(filename) + '"'
+               self.syntaxdialog = W.ModalDialog((360, 120), "Syntax Error")
+               self.syntaxdialog.text = W.TextBox((10, 10, -10, -40), text)
+               self.syntaxdialog.cancel = W.Button((-190, -32, 80, 16), "Cancel", self.syntaxclose)
+               self.syntaxdialog.edit = W.Button((-100, -32, 80, 16), "Edit", self.syntaxedit)
+               self.syntaxdialog.setdefaultbutton(self.syntaxdialog.edit)
+               self.syntaxdialog.bind("cmd.", self.syntaxdialog.cancel.push)
+               self.syntaxdialog.open()
+       
+       def syntaxclose(self):
+               self.syntaxdialog.close()
+               del self.syntaxdialog
+       
+       def syntaxedit(self):
+               try:
+                       value, (filename, lineno, charno, line) = self.value
+               except:
+                       filename = ""
+                       lineno = None
+               if not filename and self.lastwindowtitle:
+                       filename = self.lastwindowtitle
+               elif not filename:
+                       filename = "<unknown>"
+               self.syntaxclose()
+               if lineno:
+                       W.getapplication().openscript(filename, lineno, charno - 1)
+               else:
+                       W.getapplication().openscript(filename)
+       
+       def show(self):
+               if self.closed:
+                       self.setupwidgets()
+                       self.w.open()
+               else:
+                       self.w.wid.ShowWindow()
+                       self.w.wid.SelectWindow()
+       
+       def hide(self):
+               if self.closed:
+                       return
+               self.w.close()
+       
+       def close(self):
+               self.bounds = self.w.getbounds()
+               self.closed = 1
+               self.type, self.value, self.tb = None, None, None
+               self.tblist = None
+       
+       def activate(self, onoff):
+               if onoff:
+                       if self.closed:
+                               self.traceback()
+                       self.closed = 0
+                       self.checkbuttons()
+       
+       def setupwidgets(self):
+               self.w = W.Window(self.bounds, self.title, minsize = (316, 168))
+               self.w.text = W.TextBox((10, 10, -10, 30))
+               self.w.tbtitle = W.TextBox((10, 40, -10, 10), "Traceback (innermost last):")
+               self.w.list = W.TwoLineList((10, 60, -10, -40), callback = self.listhit)
+               
+               self.w.editbutton = W.Button((10, -30, 60, 16), "Edit", self.edit)
+               self.w.editbutton.enable(0)
+               
+               self.w.browselocalsbutton = W.Button((80, -30, 100, 16), "Browse localsÉ", self.browselocals)
+               self.w.browselocalsbutton.enable(0)
+               
+               self.w.postmortembutton = W.Button((190, -30, 100, 16), "Post mortemÉ", self.postmortem)
+               
+               self.w.setdefaultbutton(self.w.editbutton)
+               self.w.bind("cmdb", self.w.browselocalsbutton.push)
+               self.w.bind("<close>", self.close)
+               self.w.bind("<activate>", self.activate)
+       
+       def buildtblist(self):
+               tb = self.tb
+               for i in range(self.start):
+                       if tb.tb_next is None:
+                               break
+                       tb = tb.tb_next
+               self.tblist = traceback.extract_tb(tb)
+               self.textlist = []
+               for filename, lineno, func, line in self.tblist:
+                       tbline = ""
+                       if os.path.exists(filename):
+                               filename = os.path.split(filename)[1]
+                               tbline = 'File "' + filename + '", line ' + `lineno` + ', in ' + func
+                       else:
+                               tbline = 'File "' + filename + '", line ' + `lineno` + ', in ' + func
+                       if line:
+                               tbline = tbline + '\r      ' + line
+                       self.textlist.append(tbline[:255])
+       
+       def edit(self):
+               sel = self.w.list.getselection()
+               for i in sel:
+                       filename, lineno, func, line = self.tblist[i]
+                       W.getapplication().openscript(filename, lineno)
+       
+       def browselocals(self):
+               sel = self.w.list.getselection()
+               for i in sel:
+                       tb = self.tb
+                       for j in range(i + self.start):
+                               tb = tb.tb_next
+                       self.browse(tb.tb_frame.f_locals)
+       
+       def browse(self, object):
+               import PyBrowser
+               PyBrowser.Browser(object)
+       
+       def postmortem(self):
+               import PyDebugger
+               PyDebugger.postmortem(self.type, self.value, self.tb)
+       
+       def listhit(self, isdbl):
+               if isdbl:
+                       self.w.editbutton.push()
+               else:
+                       self.checkbuttons()
+       
+       def checkbuttons(self):
+               havefile = len(self.w.list.getselection()) > 0
+               self.w.editbutton.enable(havefile)
+               self.w.browselocalsbutton.enable(havefile)
+               self.w.setdefaultbutton(havefile and self.w.editbutton or self.w.postmortembutton)
+
diff --git a/Mac/Tools/IDE/Wwindows.py b/Mac/Tools/IDE/Wwindows.py
new file mode 100644 (file)
index 0000000..4c16794
--- /dev/null
@@ -0,0 +1,611 @@
+import Qd
+import Win
+import Evt
+import Fm
+import FrameWork
+import Windows
+import Events
+import Wbase
+import Dlg
+import MacOS
+import Menu
+import struct
+import traceback
+
+from types import *
+
+
+class Window(FrameWork.Window, Wbase.SelectableWidget):
+       
+       windowkind = Windows.documentProc
+       
+       def __init__(self, possize, title = "", minsize = None, maxsize = None, tabbable = 1, show = 1):
+               import W
+               W.SelectableWidget.__init__(self, possize)
+               self._globalbounds = l, t, r, b = self.getwindowbounds(possize, minsize)
+               self._bounds = (0, 0, r - l, b - t)
+               self._tabchain = []
+               self._currentwidget = None
+               self.title = title
+               self._parentwindow = self
+               self._tabbable = tabbable
+               self._defaultbutton = None
+               self._drawwidgetbounds = 0
+               self._show = show
+               self._lastrollover = None
+               # XXX the following is not really compatible with the
+               #  new (system >= 7.5) window procs. 
+               if minsize:
+                       self._hasgrowbox = 1
+                       self.windowkind = self.windowkind | 8
+                       l, t = minsize
+                       if maxsize:
+                               r, b = maxsize[0] + 1, maxsize[1] + 1
+                       else:
+                               r, b = 32000, 32000
+                       self.growlimit = (l, t, r, b)
+               else:
+                       self._hasgrowbox = 0
+                       if (self.windowkind == 0 or self.windowkind >= 8) and self.windowkind < 1000:
+                               self.windowkind = self.windowkind | 4
+               FrameWork.Window.__init__(self, W.getapplication())
+       
+       def gettitle(self):
+               return self.title
+       
+       def settitle(self, title):
+               self.title = title
+               if self.wid:
+                       self.wid.SetWTitle(title)
+       
+       def getwindowbounds(self, size, minsize = None):
+               return windowbounds(size, minsize)      
+       
+       def getcurrentwidget(self):
+               return self._currentwidget
+       
+       def show(self, onoff):
+               if onoff:
+                       self.wid.ShowWindow()
+               else:
+                       self.wid.HideWindow()
+       
+       def isvisible(self):
+               return self.wid.IsWindowVisible()
+       
+       def getbounds(self):
+               if 0:   #self.isvisible():
+                       self.wid.GetWindowContentRgn(scratchRegion)
+                       self._globalbounds = GetRgnBounds(scratchRegion)
+               return self._globalbounds
+               
+       def select(self):
+               self.wid.SelectWindow()
+               # not sure if this is the best place, I need it when
+               # an editor gets selected, and immediately scrolled
+               # to a certain line, waste scroll assumes everything 
+               # to be in tact.
+               self.do_rawupdate(self.wid, "DummyEvent")
+       
+       def open(self):
+               self.wid = Win.NewCWindow(self._globalbounds, self.title, self._show,
+                       self.windowkind, -1, 1, 0)
+               self.SetPort()
+               fnum = Fm.GetFNum("Python-Sans")
+               if fnum == 0:
+                       fnum = Fm.GetFNum("Geneva")
+               Qd.TextFont(fnum)       # XXX font&size from a function?
+               Qd.TextSize(9)  # XXX font&size from a function?
+               if self._bindings.has_key("<open>"):
+                       callback = self._bindings["<open>"]
+                       callback()
+               for w in self._widgets:
+                       w.forall_frombottom("open")
+               self._maketabchain()
+               if self._tabchain:
+                       self._tabchain[0].select(1)
+               if self._tabbable:
+                       self.bind('tab', self.nextwidget)
+                       self.bind('shifttab', self.previouswidget)
+               self.do_postopen()
+       
+       def close(self):
+               if not self.wid:
+                       return  # we are already closed
+               if self._bindings.has_key("<close>"):
+                       callback = self._bindings["<close>"]
+                       try:
+                               rv = callback()
+                       except:
+                               print 'error in <close> callback'
+                               traceback.print_exc()
+                       else:
+                               if rv:
+                                       return rv
+               #for key in self._widgetsdict.keys():
+               #       self._removewidget(key)
+               self.forall_butself("close")
+               Wbase.SelectableWidget.close(self)
+               self._tabchain = []
+               self._currentwidget = None
+               self.wid.HideWindow()
+               self.do_postclose()
+       
+       def domenu_close(self, *args):
+               self.close()
+       
+       def move(self, x, y = None):
+               """absolute move"""
+               if y == None:
+                       x, y = x
+               self.wid.MoveWindow(x, y, 0)
+       
+       def resize(self, x, y = None):
+               if y == None:
+                       x, y = x
+               if self._hasgrowbox:
+                       self.SetPort()
+                       Win.InvalRect(self.getgrowrect())
+               self.wid.SizeWindow(x, y, 1)
+               self._calcbounds()
+       
+       def test(self, point):
+               return 1
+       
+       def draw(self, visRgn = None):
+               if self._hasgrowbox:
+                       self.tempcliprect(self.getgrowrect())
+                       self.wid.DrawGrowIcon()
+                       self.restoreclip()
+       
+       def idle(self, *args):
+               self.SetPort()
+               point = Evt.GetMouse()
+               widget = self.findwidget(point, 0)
+               if self._bindings.has_key("<idle>"):
+                       callback = self._bindings["<idle>"]
+                       if callback():
+                               return
+               if self._currentwidget is not None and hasattr(self._currentwidget, "idle"):
+                       if self._currentwidget._bindings.has_key("<idle>"):
+                               callback = self._currentwidget._bindings["<idle>"]
+                               if callback():
+                                       return
+                       if self._currentwidget.idle():
+                               return
+               if widget is not None and hasattr(widget, "rollover"):
+                       if 1:   #self._lastrollover <> widget:
+                               if self._lastrollover:
+                                       self._lastrollover.rollover(point, 0)
+                               self._lastrollover = widget
+                               self._lastrollover.rollover(point, 1)
+               else:
+                       if self._lastrollover:
+                               self._lastrollover.rollover(point, 0)
+                       self._lastrollover = None
+                       Wbase.SetCursor("arrow")
+
+       def xxx___select(self, widget):
+               if self._currentwidget == widget:
+                       return
+               if self._bindings.has_key("<select>"):
+                       callback = self._bindings["<select>"]
+                       if callback(widget):
+                               return
+               if widget is None:
+                       if self._currentwidget is not None:
+                               self._currentwidget.select(0)
+               elif type(widget) == InstanceType and widget._selectable:
+                       widget.select(1)
+               elif widget == -1 or widget == 1:
+                       if len(self._tabchain) <= 1:
+                               return
+                       temp = self._tabchain[(self._tabchain.index(self._currentwidget) + widget) % len(self._tabchain)]
+                       temp.select(1)
+               else:
+                       raise TypeError, "Widget is not selectable"
+       
+       def setdefaultbutton(self, newdefaultbutton = None, *keys):
+               if newdefaultbutton == self._defaultbutton:
+                       return
+               if self._defaultbutton:
+                       self._defaultbutton._setdefault(0)
+               if not newdefaultbutton:
+                       self.bind("return", None)
+                       self.bind("enter", None)
+                       return
+               import Wcontrols
+               if not isinstance(newdefaultbutton, Wcontrols.Button):
+                       raise TypeError, "widget is not a button"
+               self._defaultbutton = newdefaultbutton
+               self._defaultbutton._setdefault(1)
+               if not keys:
+                       self.bind("return", self._defaultbutton.push)
+                       self.bind("enter", self._defaultbutton.push)
+               else:
+                       for key in keys:
+                               self.bind(key, self._defaultbutton.push)
+       
+       def nextwidget(self):
+               self.xxx___select(1)
+       
+       def previouswidget(self):
+               self.xxx___select(-1)
+       
+       def drawwidgetbounds(self, onoff):
+               self._drawwidgetbounds = onoff
+               self.SetPort()
+               Win.InvalRect(self._bounds)
+       
+       def _drawbounds(self):
+               pass
+
+       def _maketabchain(self):
+               # XXX This has to change, it's no good when we are adding or deleting widgets.
+               # XXX Perhaps we shouldn't keep a "tabchain" at all.
+               self._hasselframes = 0
+               self._collectselectablewidgets(self._widgets)
+               if self._hasselframes and len(self._tabchain) > 1:
+                       self._hasselframes = 1
+               else:
+                       self._hasselframes = 0
+       
+       def _collectselectablewidgets(self, widgets):
+               import W
+               for w in widgets:
+                       if w._selectable:
+                               self._tabchain.append(w)
+                               if isinstance(w, W.List):
+                                       self._hasselframes = 1
+                       self._collectselectablewidgets(w._widgets)
+       
+       def _calcbounds(self):
+               self._possize = self.wid.GetWindowPort().portRect[2:]
+               w, h = self._possize
+               self._bounds = (0, 0, w, h)
+               self.wid.GetWindowContentRgn(scratchRegion)
+               l, t, r, b = GetRgnBounds(scratchRegion)
+               self._globalbounds = l, t, l + w, t + h
+               for w in self._widgets:
+                       w._calcbounds()
+       
+       # FrameWork override methods
+       def do_inDrag(self, partcode, window, event):
+               where = event[3]
+               self.wid.GetWindowContentRgn(scratchRegion)
+               was_l, was_t, r, b = GetRgnBounds(scratchRegion)
+               window.DragWindow(where, self.draglimit)
+               self.wid.GetWindowContentRgn(scratchRegion)
+               is_l, is_t, r, b = GetRgnBounds(scratchRegion)
+               self._globalbounds = Qd.OffsetRect(self._globalbounds, 
+                                       is_l - was_l, is_t - was_t)
+       
+       def do_char(self, char, event):
+               import Wkeys
+               (what, message, when, where, modifiers) = event
+               key = char
+               if Wkeys.keynames.has_key(key):
+                       key = Wkeys.keynames[char]
+               if modifiers & Events.shiftKey:
+                       key = 'shift' + key
+               if modifiers & Events.cmdKey:
+                       key = 'cmd' + key
+               if modifiers & Events.controlKey:
+                       key = 'control' + key
+               if self._bindings.has_key("<key>"):
+                       callback = self._bindings["<key>"]
+                       if Wbase.CallbackCall(callback, 0, char, event):
+                               return
+               if self._bindings.has_key(key):
+                       callback = self._bindings[key]
+                       Wbase.CallbackCall(callback, 0, char, event)
+               elif self._currentwidget is not None:
+                       if self._currentwidget._bindings.has_key(key):
+                               callback = self._currentwidget._bindings[key]
+                               Wbase.CallbackCall(callback, 0, char, event)
+                       else:
+                               if self._currentwidget._bindings.has_key("<key>"):
+                                       callback = self._currentwidget._bindings["<key>"]
+                                       if Wbase.CallbackCall(callback, 0, char, event):
+                                               return
+                               self._currentwidget.key(char, event)
+       
+       def do_contentclick(self, point, modifiers, event):
+               widget = self.findwidget(point)
+               if widget is not None:
+                       if self._bindings.has_key("<click>"):
+                               callback = self._bindings["<click>"]
+                               if Wbase.CallbackCall(callback, 0, point, modifiers):
+                                       return
+                       if widget._bindings.has_key("<click>"):
+                               callback = widget._bindings["<click>"]
+                               if Wbase.CallbackCall(callback, 0, point, modifiers):
+                                       return
+                       if widget._selectable:
+                               widget.select(1, 1)
+                       widget.click(point, modifiers)
+       
+       def do_update(self, window, event):
+               Qd.EraseRgn(window.GetWindowPort().visRgn)
+               self.forall_frombottom("draw", window.GetWindowPort().visRgn)
+               if self._drawwidgetbounds:
+                       self.forall_frombottom("_drawbounds")
+       
+       def do_activate(self, onoff, event):
+               if not onoff:
+                       if self._lastrollover:
+                               self._lastrollover.rollover((0, 0), 0)
+                               self._lastrollover = None
+               self.SetPort()
+               self.forall("activate", onoff)
+               self.draw()
+       
+       def do_postresize(self, width, height, window):
+               Win.InvalRect(self.getgrowrect())
+               self._calcbounds()
+       
+       def do_inGoAway(self, partcode, window, event):
+               where = event[3]
+               closeall = event[4] & Events.optionKey
+               if window.TrackGoAway(where):
+                       if not closeall:
+                               self.close()
+                       else:
+                               for window in self.parent._windows.values():
+                                       rv = window.close()
+                                       if rv and rv > 0:
+                                               return
+       
+       # utilities
+       def tempcliprect(self, tempcliprect):
+               tempclip = Qd.NewRgn()
+               Qd.RectRgn(tempclip, tempcliprect)
+               self.tempclip(tempclip)
+               Qd.DisposeRgn(tempclip)
+       
+       def tempclip(self, tempclip):
+               if not hasattr(self, "saveclip"):
+                       self.saveclip = []
+               saveclip = Qd.NewRgn()
+               Qd.GetClip(saveclip)
+               self.saveclip.append(saveclip)
+               Qd.SetClip(tempclip)
+       
+       def restoreclip(self):
+               Qd.SetClip(self.saveclip[-1])
+               Qd.DisposeRgn(self.saveclip[-1])
+               del self.saveclip[-1]
+       
+       def getgrowrect(self):
+               l, t, r, b = self.wid.GetWindowPort().portRect
+               return (r - 15, b - 15, r, b)
+       
+       def has_key(self, key):
+               return self._widgetsdict.has_key(key)
+       
+       def __getattr__(self, attr):
+               global _successcount, _failcount, _magiccount
+               if self._widgetsdict.has_key(attr):
+                       _successcount = _successcount + 1
+                       return self._widgetsdict[attr]
+               if self._currentwidget is None or (attr[:7] <> 'domenu_' and 
+                               attr[:4] <> 'can_' and attr <> 'insert'):
+                       _failcount = _failcount + 1
+                       raise AttributeError, attr
+               # special case: if a domenu_xxx, can_xxx or insert method is asked for, 
+               # see if the active widget supports it
+               _magiccount = _magiccount + 1
+               return getattr(self._currentwidget, attr)
+
+_successcount = 0
+_failcount = 0
+_magiccount = 0
+
+class Dialog(Window):
+       
+       windowkind = Windows.movableDBoxProc
+       
+       # this __init__ seems redundant, but it's not: it has less args
+       def __init__(self, possize, title = ""):
+               Window.__init__(self, possize, title)
+       
+       def can_close(self, *args):
+               return 0
+       
+       def getwindowbounds(self, size, minsize = None):
+               screenbounds = sl, st, sr, sb = Qd.qd.screenBits.bounds
+               w, h = size
+               l = sl + (sr - sl - w) / 2
+               t = st + (sb - st - h) / 3
+               return l, t, l + w, t + h
+
+
+class ModalDialog(Dialog):
+       
+       def __init__(self, possize, title = ""):
+               Dialog.__init__(self, possize, title)
+               if title:
+                       self.windowkind = Windows.movableDBoxProc
+               else:
+                       self.windowkind = Windows.dBoxProc
+       
+       def open(self):
+               import W
+               Dialog.open(self)
+               self.app = W.getapplication()
+               self.done = 0
+               Menu.HiliteMenu(0)
+               app = self.parent
+               app.enablemenubar(0)
+               try:
+                       self.mainloop()
+               finally:
+                       app.enablemenubar(1)
+       
+       def close(self):
+               if not self.wid:
+                       return  # we are already closed
+               self.done = 1
+               del self.app
+               Dialog.close(self)
+       
+       def mainloop(self):
+               saveyield = MacOS.EnableAppswitch(-1)
+               while not self.done:
+                       #self.do1event()
+                       self.do1event(  Events.keyDownMask + 
+                                               Events.autoKeyMask + 
+                                               Events.activMask + 
+                                               Events.updateMask + 
+                                               Events.mDownMask +
+                                               Events.mUpMask, 
+                                               10)
+               MacOS.EnableAppswitch(saveyield)
+       
+       def do1event(self, mask = Events.everyEvent, wait = 0):
+               ok, event = self.app.getevent(mask, wait)
+               if Dlg.IsDialogEvent(event):
+                       if self.app.do_dialogevent(event):
+                               return
+               if ok:
+                       self.dispatch(event)
+               else:
+                       self.app.idle(event)
+       
+       def do_keyDown(self, event):
+               self.do_key(event)
+       
+       def do_autoKey(self, event):
+               if not event[-1] & Events.cmdKey:
+                       self.do_key(event)
+       
+       def do_key(self, event):
+               (what, message, when, where, modifiers) = event
+               w = Win.FrontWindow()
+               if w <> self.wid:
+                       return
+               c = chr(message & Events.charCodeMask)
+               if modifiers & Events.cmdKey:
+                       self.app.checkmenus(self)
+                       result = Menu.MenuKey(ord(c))
+                       id = (result>>16) & 0xffff      # Hi word
+                       item = result & 0xffff          # Lo word
+                       if id:
+                               self.app.do_rawmenu(id, item, None, event)
+                               return
+               self.do_char(c, event)
+       
+       def do_mouseDown(self, event):
+               (what, message, when, where, modifiers) = event
+               partcode, wid = Win.FindWindow(where)
+               #
+               # Find the correct name.
+               #
+               if FrameWork.partname.has_key(partcode):
+                       name = "do_" + FrameWork.partname[partcode]
+               else:
+                       name = "do_%d" % partcode
+               
+               if name == "do_inDesk":
+                       MacOS.HandleEvent(event)
+                       return
+               if wid == self.wid:
+                       try:
+                               handler = getattr(self, name)
+                       except AttributeError:
+                               handler = self.app.do_unknownpartcode
+               else:
+                       #MacOS.HandleEvent(event)
+                       if name == 'do_inMenuBar':
+                               handler = getattr(self.parent, name)
+                       else:
+                               return          
+               handler(partcode, wid, event)
+       
+       def dispatch(self, event):
+               (what, message, when, where, modifiers) = event
+               if FrameWork.eventname.has_key(what):
+                       name = "do_" + FrameWork.eventname[what]
+               else:
+                       name = "do_%d" % what
+               try:
+                       handler = getattr(self, name)
+               except AttributeError:
+                       try:
+                               handler = getattr(self.app, name)
+                       except AttributeError:
+                               handler = self.app.do_unknownevent
+               handler(event)
+       
+
+def FrontWindowInsert(stuff):
+       if not stuff:
+               return
+       if type(stuff) <> StringType:
+               raise TypeError, 'string expected'
+       import W
+       app = W.getapplication()
+       wid = Win.FrontWindow()
+       if wid and app._windows.has_key(wid):
+               window = app._windows[wid]
+               if hasattr(window, "insert"):
+                       try:
+                               window.insert(stuff)
+                               return
+                       except:
+                               pass
+       import EasyDialogs
+       if EasyDialogs.AskYesNoCancel(
+                       "CanĂ•t find window or widget to insert text into; copy to clipboard instead?", 
+                       1) == 1:
+               import Scrap
+               Scrap.ZeroScrap()
+               Scrap.PutScrap('TEXT', stuff)
+
+
+# not quite based on the same function in FrameWork    
+_windowcounter = 0
+
+def getnextwindowpos():
+       global _windowcounter
+       rows = 8
+       l = 4 * (rows + 1 - (_windowcounter % rows) + _windowcounter / rows)
+       t = 44 + 20 * (_windowcounter % rows)
+       _windowcounter = _windowcounter + 1
+       return l, t
+
+def windowbounds(preferredsize, minsize = None):
+       "Return sensible window bounds"
+       
+       global _windowcounter
+       if len(preferredsize) == 4:
+               bounds = l, t, r, b = preferredsize
+               union = Qd.UnionRect(bounds, Qd.qd.screenBits.bounds)
+               if union == Qd.qd.screenBits.bounds:
+                       return bounds
+               else:
+                       preferredsize = r - l, b - t
+       if not minsize:
+               minsize = preferredsize
+       minwidth, minheight = minsize
+       width, height = preferredsize
+       
+       sl, st, sr, sb = screenbounds = Qd.InsetRect(Qd.qd.screenBits.bounds, 4, 4)
+       l, t = getnextwindowpos()
+       if (l + width) > sr:
+               _windowcounter = 0
+               l, t = getnextwindowpos()
+       r = l + width
+       b = t + height
+       if (t + height) > sb:
+               b = sb
+               if (b - t) < minheight:
+                       b = t + minheight
+       return l, t, r, b
+
+scratchRegion = Qd.NewRgn()
+
+# util -- move somewhere convenient???
+def GetRgnBounds(the_Rgn):
+       (t, l, b, r) = struct.unpack("hhhh", the_Rgn.data[2:10])
+       return (l, t, r, b)