From 40f9b7bd7cb54645a7c15668b683a8d830ba5219 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sat, 30 Jan 1999 22:39:17 +0000 Subject: First Checked In. --- Mac/Tools/IDE/BuildIDE.py | 32 ++ Mac/Tools/IDE/FontSettings.py | 223 ++++++++ Mac/Tools/IDE/MacPrefs.py | 108 ++++ Mac/Tools/IDE/ModuleBrowser.py | 178 +++++++ Mac/Tools/IDE/ProfileBrowser.py | 91 ++++ Mac/Tools/IDE/PyBrowser.py | 440 +++++++++++++++ Mac/Tools/IDE/PyConsole.py | 380 +++++++++++++ Mac/Tools/IDE/PyDebugger.py | 887 ++++++++++++++++++++++++++++++ Mac/Tools/IDE/PyDocSearch.py | 290 ++++++++++ Mac/Tools/IDE/PyEdit.py | 1126 +++++++++++++++++++++++++++++++++++++++ Mac/Tools/IDE/PyFontify.py | 154 ++++++ Mac/Tools/IDE/PyInteractive.py | 115 ++++ Mac/Tools/IDE/PythonIDE.py | 33 ++ Mac/Tools/IDE/PythonIDE.rsrc | Bin 0 -> 11252 bytes Mac/Tools/IDE/PythonIDEMain.py | 237 ++++++++ Mac/Tools/IDE/Splash.py | 162 ++++++ Mac/Tools/IDE/W.py | 34 ++ Mac/Tools/IDE/Wapplication.py | 411 ++++++++++++++ Mac/Tools/IDE/Wbase.py | 720 +++++++++++++++++++++++++ Mac/Tools/IDE/Wcontrols.py | 396 ++++++++++++++ Mac/Tools/IDE/Widgets.rsrc | Bin 0 -> 5077 bytes Mac/Tools/IDE/Wkeys.py | 45 ++ Mac/Tools/IDE/Wlists.py | 457 ++++++++++++++++ Mac/Tools/IDE/Wmenus.py | 196 +++++++ Mac/Tools/IDE/Wquicktime.py | 119 +++++ Mac/Tools/IDE/Wtext.py | 979 ++++++++++++++++++++++++++++++++++ Mac/Tools/IDE/Wtraceback.py | 188 +++++++ Mac/Tools/IDE/Wwindows.py | 611 +++++++++++++++++++++ 28 files changed, 8612 insertions(+) create mode 100644 Mac/Tools/IDE/BuildIDE.py create mode 100644 Mac/Tools/IDE/FontSettings.py create mode 100644 Mac/Tools/IDE/MacPrefs.py create mode 100644 Mac/Tools/IDE/ModuleBrowser.py create mode 100644 Mac/Tools/IDE/ProfileBrowser.py create mode 100644 Mac/Tools/IDE/PyBrowser.py create mode 100644 Mac/Tools/IDE/PyConsole.py create mode 100644 Mac/Tools/IDE/PyDebugger.py create mode 100644 Mac/Tools/IDE/PyDocSearch.py create mode 100644 Mac/Tools/IDE/PyEdit.py create mode 100644 Mac/Tools/IDE/PyFontify.py create mode 100644 Mac/Tools/IDE/PyInteractive.py create mode 100644 Mac/Tools/IDE/PythonIDE.py create mode 100644 Mac/Tools/IDE/PythonIDE.rsrc create mode 100644 Mac/Tools/IDE/PythonIDEMain.py create mode 100644 Mac/Tools/IDE/Splash.py create mode 100644 Mac/Tools/IDE/W.py create mode 100644 Mac/Tools/IDE/Wapplication.py create mode 100644 Mac/Tools/IDE/Wbase.py create mode 100644 Mac/Tools/IDE/Wcontrols.py create mode 100644 Mac/Tools/IDE/Widgets.rsrc create mode 100644 Mac/Tools/IDE/Wkeys.py create mode 100644 Mac/Tools/IDE/Wlists.py create mode 100644 Mac/Tools/IDE/Wmenus.py create mode 100644 Mac/Tools/IDE/Wquicktime.py create mode 100644 Mac/Tools/IDE/Wtext.py create mode 100644 Mac/Tools/IDE/Wtraceback.py create mode 100644 Mac/Tools/IDE/Wwindows.py diff --git a/Mac/Tools/IDE/BuildIDE.py b/Mac/Tools/IDE/BuildIDE.py new file mode 100644 index 0000000..ad6a4ea --- /dev/null +++ b/Mac/Tools/IDE/BuildIDE.py @@ -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 index 0000000..09886ae --- /dev/null +++ b/Mac/Tools/IDE/FontSettings.py @@ -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 index 0000000..8a442ea --- /dev/null +++ b/Mac/Tools/IDE/MacPrefs.py @@ -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 index 0000000..d515901 --- /dev/null +++ b/Mac/Tools/IDE/ModuleBrowser.py @@ -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("", self.activate) + self.window.bind("", 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 index 0000000..f5b4a38 --- /dev/null +++ b/Mac/Tools/IDE/ProfileBrowser.py @@ -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 index 0000000..d62d256 --- /dev/null +++ b/Mac/Tools/IDE/PyBrowser.py @@ -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 index 0000000..ad71982 --- /dev/null +++ b/Mac/Tools/IDE/PyConsole.py @@ -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('', 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("", self.close) + self.w.bind("", 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 index 0000000..b0d4fb4 --- /dev/null +++ b/Mac/Tools/IDE/PyDebugger.py @@ -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('', 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('', 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 = '' + 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('', 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('', 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 index 0000000..cd0fdce --- /dev/null +++ b/Mac/Tools/IDE/PyDocSearch.py @@ -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('\([^<]*\)') + +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('', 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 index 0000000..f885d04 --- /dev/null +++ b/Mac/Tools/IDE/PyEdit.py @@ -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('', 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("", 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("", self.clickeditor) + self.linefield.bind("", 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("", self.key) + self.w.bind("", self.activate) + self.w.bind("", 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="", 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 index 0000000..a61de65 --- /dev/null +++ b/Mac/Tools/IDE/PyFontify.py @@ -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 index 0000000..dc75e4b --- /dev/null +++ b/Mac/Tools/IDE/PyInteractive.py @@ -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, "", "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", "", "single") + except SyntaxError, err1: + pass + + try: + code2 = compile(self._pybuf + "\n\n", "", "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 index 0000000..50f76f9 --- /dev/null +++ b/Mac/Tools/IDE/PythonIDE.py @@ -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 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 index 0000000..b6f637b --- /dev/null +++ b/Mac/Tools/IDE/PythonIDEMain.py @@ -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 = "" + 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 index 0000000..19f16fc --- /dev/null +++ b/Mac/Tools/IDE/Splash.py @@ -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. + +Python %s +%s +Written by Guido van Rossum with Jack Jansen (and others) + +See: 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 index 0000000..812134f --- /dev/null +++ b/Mac/Tools/IDE/W.py @@ -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 = '' + EasyDialogs.Message(text) + diff --git a/Mac/Tools/IDE/Wapplication.py b/Mac/Tools/IDE/Wapplication.py new file mode 100644 index 0000000..8db9a1a --- /dev/null +++ b/Mac/Tools/IDE/Wapplication.py @@ -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 index 0000000..46a8c96 --- /dev/null +++ b/Mac/Tools/IDE/Wbase.py @@ -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(""] + 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 index 0000000..f619bb9 --- /dev/null +++ b/Mac/Tools/IDE/Wcontrols.py @@ -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 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 index 0000000..45155f2 --- /dev/null +++ b/Mac/Tools/IDE/Wkeys.py @@ -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 index 0000000..d49ca93 --- /dev/null +++ b/Mac/Tools/IDE/Wlists.py @@ -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("", 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 index 0000000..387590b --- /dev/null +++ b/Mac/Tools/IDE/Wmenus.py @@ -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 index 0000000..a33462a --- /dev/null +++ b/Mac/Tools/IDE/Wquicktime.py @@ -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 index 0000000..821e4c5 --- /dev/null +++ b/Mac/Tools/IDE/Wtext.py @@ -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 index 0000000..82e487f --- /dev/null +++ b/Mac/Tools/IDE/Wtraceback.py @@ -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 = "" + 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 = "" + 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("", self.close) + self.w.bind("", 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 index 0000000..4c16794 --- /dev/null +++ b/Mac/Tools/IDE/Wwindows.py @@ -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(""): + callback = self._bindings[""] + 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(""): + callback = self._bindings[""] + try: + rv = callback() + except: + print 'error in 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(""): + callback = self._bindings[""] + if callback(): + return + if self._currentwidget is not None and hasattr(self._currentwidget, "idle"): + if self._currentwidget._bindings.has_key(""): + callback = self._currentwidget._bindings[""] + 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(""] + 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(""): + callback = self._bindings[""] + 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(""): + callback = self._currentwidget._bindings[""] + 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(""): + callback = self._bindings[""] + if Wbase.CallbackCall(callback, 0, point, modifiers): + return + if widget._bindings.has_key(""): + callback = widget._bindings[""] + 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) -- cgit v0.12