"""A (less & less) simple Python editor""" import W import Wtraceback from Wkeys import * import MacOS import EasyDialogs from Carbon import Win from Carbon import Res from Carbon import Evt from Carbon import Qd from Carbon import File import os import imp import sys import string import marshal import re smAllScripts = -3 if hasattr(Win, "FrontNonFloatingWindow"): MyFrontWindow = Win.FrontNonFloatingWindow else: MyFrontWindow = Win.FrontWindow try: import Wthreading except ImportError: haveThreading = 0 else: haveThreading = Wthreading.haveThreading _scriptuntitledcounter = 1 _wordchars = string.ascii_letters + string.digits + "_" runButtonLabels = ["Run all", "Stop!"] runSelButtonLabels = ["Run selection", "Pause!", "Resume"] 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 self._eoln = os.linesep 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() self._creator, filetype = MacOS.GetCreatorAndType(path) else: raise IOError, "file '%s' does not exist" % path self.path = path if '\n' in text: if string.find(text, '\r\n') >= 0: self._eoln = '\r\n' else: self._eoln = '\n' text = string.replace(text, self._eoln, '\r') change = 0 else: change = 0 self._eoln = '\r' 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 change > 0: self.editgroup.editor.textchanged() 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 self.run_as_main = self.settings.get("run_as_main", 0) self.run_with_interpreter = self.settings.get("run_with_interpreter", 0) self.run_with_cl_interpreter = self.settings.get("run_with_cl_interpreter", 0) self._threadstate = (0, 0) self._thread = None def readwindowsettings(self): try: resref = Res.FSpOpenResFile(self.path, 1) 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.FSpOpenResFile(self.path, 3) except Res.Error: Res.FSpCreateResFile(self.path, self._creator, 'TEXT', smAllScripts) resref = Res.FSpOpenResFile(self.path, 3) 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 self.settings["run_with_interpreter"] = self.run_with_interpreter self.settings["run_with_cl_interpreter"] = self.run_with_cl_interpreter 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.BevelButton((6, 4, 80, 16), runButtonLabels[0], self.run) self.runselbutton = W.BevelButton((90, 4, 80, 16), runSelButtonLabels[0], 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\xc9', self.domenu_fontsettings), ("Save options\xc9", self.domenu_options), '-', ('\0' + chr(self.run_as_main) + 'Run as __main__', self.domenu_toggle_run_as_main), #('\0' + chr(self.run_with_interpreter) + 'Run with Interpreter', self.domenu_dtoggle_run_with_interpreter), ('\0' + chr(self.run_with_cl_interpreter) + 'Run with commandline Python', self.domenu_toggle_run_with_cl_interpreter), '-', ('Modularize', self.domenu_modularize), ('Browse namespace\xc9', 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\xc9', 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.run_with_interpreter = 0 self.run_with_cl_interpreter = 0 self.editgroup.editor.selectionchanged() def XXdomenu_toggle_run_with_interpreter(self): self.run_with_interpreter = not self.run_with_interpreter self.run_as_main = 0 self.run_with_cl_interpreter = 0 self.editgroup.editor.selectionchanged() def domenu_toggle_run_with_cl_interpreter(self): self.run_with_cl_interpreter = not self.run_with_cl_interpreter self.run_as_main = 0 self.run_with_interpreter = 0 self.editgroup.editor.selectionchanged() 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 domenu_options(self, *args): rv = SaveOptions(self._creator, self._eoln) if rv: self.editgroup.editor.selectionchanged() # ouch... self._creator, self._eoln = rv 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: Qd.InitCursor() save = EasyDialogs.AskYesNoCancel('Save window "%s" before closing?' % self.title, default=1, no="Don\xd5t save") if save > 0: if self.domenu_save(): return 1 elif save < 0: return 1 self.globals = None 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() if self._eoln != '\r': data = string.replace(data, '\r', self._eoln) fp = open(self.path, 'wb') # open file in binary mode, data has '\r' line-endings fp.write(data) fp.close() MacOS.SetCreatorAndType(self.path, 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): path = EasyDialogs.AskFileForSave(message='Save as:', savedFileName=self.title) if not path: return 1 self.showbreakpoints(0) self.path = path 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() fsr, changed = app.scriptsfolder.FSResolveAlias(None) path = fsr.as_pathname() if path == self.path[:len(path)]: W.getapplication().makescriptsmenu() def domenu_save_as_applet(self, *args): import buildtools buildtools.DEBUG = 0 # ouch. if self.title[-3:] == ".py": destname = self.title[:-3] else: destname = self.title + ".applet" destname = EasyDialogs.AskFileForSave(message='Save as Applet:', savedFileName=destname) if not destname: return 1 W.SetCursor("watch") 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` import tempfile tmpdir = tempfile.mkdtemp() if filename[-3:] != ".py": filename = filename + ".py" filename = os.path.join(tmpdir, os.path.split(filename)[1]) fp = open(filename, "w") fp.write(pytext) fp.close() # Try removing the output file try: os.remove(destname) except os.error: pass template = buildtools.findtemplate() buildtools.process(template, filename, destname, rsrcname=rsrcname, progress=None) 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): if self._threadstate == (0, 0): self._run() else: lock = Wthreading.Lock() lock.acquire() self._thread.postException(KeyboardInterrupt) if self._thread.isBlocked(): self._thread.start() lock.release() def _run(self): if self.run_with_interpreter: if self.editgroup.editor.changed: Qd.InitCursor() save = EasyDialogs.AskYesNoCancel('Save "%s" before running?' % self.title, 1) if save > 0: if self.domenu_save(): return elif save < 0: return if not self.path: raise W.AlertError, "Can't run unsaved file" self._run_with_interpreter() elif self.run_with_cl_interpreter: if self.editgroup.editor.changed: Qd.InitCursor() save = EasyDialogs.AskYesNoCancel('Save "%s" before running?' % self.title, 1) if save > 0: if self.domenu_save(): return elif save < 0: return if not self.path: raise W.AlertError, "Can't run unsaved file" self._run_with_cl_interpreter() else: pytext = self.editgroup.editor.get() globals, file, modname = self.getenvironment() self.execstring(pytext, globals, globals, file, modname) def _run_with_interpreter(self): interp_path = os.path.join(sys.exec_prefix, "PythonInterpreter") if not os.path.exists(interp_path): raise W.AlertError, "Can't find interpreter" import findertools XXX def _run_with_cl_interpreter(self): import Terminal interp_path = os.path.join(sys.exec_prefix, "bin", "python") file_path = self.path if not os.path.exists(interp_path): # This "can happen" if we are running IDE under MacPython-OS9. raise W.AlertError, "Can't find command-line Python" cmd = '"%s" "%s" ; exit' % (interp_path, file_path) t = Terminal.Terminal() t.do_script(with_command=cmd) def runselection(self): if self._threadstate == (0, 0): self._runselection() elif self._threadstate == (1, 1): self._thread.block() self.setthreadstate((1, 2)) elif self._threadstate == (1, 2): self._thread.start() self.setthreadstate((1, 1)) def _runselection(self): if self.run_with_interpreter or self.run_with_cl_interpreter: raise W.AlertError, "Can't run selection with Interpreter" 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') 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): klass = globals[classname] else: raise W.AlertError, "Can't find class \"%s\"." % classname # add class def pytext = ("class %s:\n" % classname) + pytext selfirstline = selfirstline - 1 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) if indent == 1 and globals[classname] is not klass: # update the class in place klass.__dict__.update(globals[classname].__dict__) globals[classname] = klass def setthreadstate(self, state): oldstate = self._threadstate if oldstate[0] <> state[0]: self.runbutton.settitle(runButtonLabels[state[0]]) if oldstate[1] <> state[1]: self.runselbutton.settitle(runSelButtonLabels[state[1]]) self._threadstate = state def _exec_threadwrapper(self, *args, **kwargs): apply(execstring, args, kwargs) self.setthreadstate((0, 0)) self._thread = None 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) sys.path.insert(0, dir) else: cwdindex = None try: if haveThreading: self._thread = Wthreading.Thread(os.path.basename(file), self._exec_threadwrapper, pytext, globals, locals, file, self.debugging, modname, self.profiling) self.setthreadstate((1, 1)) self._thread.start() else: execstring(pytext, globals, locals, file, self.debugging, modname, self.profiling) finally: if self.path: os.chdir(savedir) del sys.path[0] 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 = dirname + '.' + modname subname = _filename_as_modname(self.title) if subname is None: return self.globals, file, None if modname: if subname == "__init__": # strip trailing period modname = modname[:-1] else: modname = modname + subname else: modname = subname if sys.modules.has_key(modname): globals = sys.modules[modname].__dict__ self.globals = {} else: globals = self.globals modname = subname 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 methodRE = re.compile(r"\r[ \t]+def ") findMethod = methodRE.search 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: m = findMethod(text, pos + 1) if m is None: break pos = m.regs[0][0] #pos = find(text, '\r\tdef ', pos + 1) append((m.regs[0][1], methodtag)) list.sort() classlist = [] methodlistappend = None offsetToLine = editor.ted.WEOffsetToLine getLineRange = editor.ted.WEGetLineRange append = classlist.append 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) class _saveoptions: def __init__(self, creator, eoln): self.rv = None self.eoln = eoln self.w = w = W.ModalDialog((260, 160), 'Save options') radiobuttons = [] w.label = W.TextBox((8, 8, 80, 18), "File creator:") w.ide_radio = W.RadioButton((8, 22, 160, 18), "This application", radiobuttons, self.ide_hit) w.interp_radio = W.RadioButton((8, 42, 160, 18), "MacPython Interpreter", radiobuttons, self.interp_hit) w.interpx_radio = W.RadioButton((8, 62, 160, 18), "OSX PythonW Interpreter", radiobuttons, self.interpx_hit) w.other_radio = W.RadioButton((8, 82, 50, 18), "Other:", radiobuttons) w.other_creator = W.EditText((62, 82, 40, 20), creator, self.otherselect) w.none_radio = W.RadioButton((8, 102, 160, 18), "None", radiobuttons, self.none_hit) w.cancelbutton = W.Button((-180, -30, 80, 16), "Cancel", self.cancelbuttonhit) w.okbutton = W.Button((-90, -30, 80, 16), "Done", self.okbuttonhit) w.setdefaultbutton(w.okbutton) if creator == 'Pyth': w.interp_radio.set(1) elif creator == W._signature: w.ide_radio.set(1) elif creator == 'PytX': w.interpx_radio.set(1) elif creator == '\0\0\0\0': w.none_radio.set(1) else: w.other_radio.set(1) w.eolnlabel = W.TextBox((168, 8, 80, 18), "Newline style:") radiobuttons = [] w.unix_radio = W.RadioButton((168, 22, 80, 18), "Unix", radiobuttons, self.unix_hit) w.mac_radio = W.RadioButton((168, 42, 80, 18), "Macintosh", radiobuttons, self.mac_hit) w.win_radio = W.RadioButton((168, 62, 80, 18), "Windows", radiobuttons, self.win_hit) if self.eoln == '\n': w.unix_radio.set(1) elif self.eoln == '\r\n': w.win_radio.set(1) else: w.mac_radio.set(1) w.bind("cmd.", w.cancelbutton.push) w.open() def ide_hit(self): self.w.other_creator.set(W._signature) def interp_hit(self): self.w.other_creator.set("Pyth") def interpx_hit(self): self.w.other_creator.set("PytX") def none_hit(self): self.w.other_creator.set("\0\0\0\0") def otherselect(self, *args): sel_from, sel_to = self.w.other_creator.getselection() creator = self.w.other_creator.get()[:4] creator = creator + " " * (4 - len(creator)) self.w.other_creator.set(creator) self.w.other_creator.setselection(sel_from, sel_to) self.w.other_radio.set(1) def mac_hit(self): self.eoln = '\r' def unix_hit(self): self.eoln = '\n' def win_hit(self): self.eoln = '\r\n' def cancelbuttonhit(self): self.w.close() def okbuttonhit(self): self.rv = (self.w.other_creator.get()[:4], self.eoln) self.w.close() def SaveOptions(creator, eoln): s = _saveoptions(creator, eoln) return s.rv 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) notwordcharspat = '[^' + _wordchars + ']' pattern = '(' + word + ')' if word[0] in _wordchars: pattern = notwordcharspat + pattern if word[-1] in _wordchars: pattern = pattern + notwordcharspat return re.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) match = wholewordRE.search(text, pos) if match: pos = match.start(1) 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() from Carbon import Res editor.textchanged() editor.selectionchanged() editor.set(Text) 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) match = wholewordRE.search(text, selend) if match: pos = match.start(1) 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"]: match = wholewordRE.search(text, 0) if match: pos = match.start(1) 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: if haveThreading: lock = Wthreading.Lock() lock.acquire() PyDebugger.startfromhere() lock.release() else: PyDebugger.startfromhere() elif not haveThreading: if hasattr(MacOS, 'EnableAppswitch'): 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: if not haveThreading: if hasattr(MacOS, 'EnableAppswitch'): MacOS.EnableAppswitch(-1) except W.AlertError, detail: raise W.AlertError, detail except (KeyboardInterrupt, BdbQuit): pass except SystemExit, arg: if arg.code: sys.stderr.write("Script exited with status code: %s\n" % repr(arg.code)) except: if haveThreading: import continuation lock = Wthreading.Lock() lock.acquire() if debugging: sys.settrace(None) PyDebugger.postmortem(sys.exc_type, sys.exc_value, sys.exc_traceback) return else: tracebackwindow.traceback(1, filename) if haveThreading: lock.release() if debugging: sys.settrace(None) PyDebugger.stop() _identifieRE = re.compile(r"[A-Za-z_][A-Za-z_0-9]*") def identifieRE_match(str): match = _identifieRE.match(str) if not match: return -1 return match.end() def _filename_as_modname(fname): if fname[-3:] == '.py': modname = fname[:-3] match = _identifieRE.match(modname) if match and match.start() == 0 and match.end() == len(modname): return string.join(string.split(modname, '.'), '_') def findeditor(topwindow, fromtop = 0): wid = MyFrontWindow() 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\xc9", 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 = ("Geneva", 0, 10, (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: fsr, d1, d2 = File.FSResolveAliasFile(path, 1) path = fsr.as_pathname() return path except (File.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()