blob: 80397a18eebfc469b4930fd5650803af93d634b2 [file] [log] [blame]
Just van Rossum40f9b7b1999-01-30 22:39:17 +00001"""A (less & less) simple Python editor"""
2
3import W
4import Wtraceback
5from Wkeys import *
6
7import macfs
8import MacOS
9import Win
10import Res
11import Evt
12import os
13import imp
14import sys
15import string
16import marshal
17import regex
18
19_scriptuntitledcounter = 1
20_wordchars = string.letters + string.digits + "_"
21
22
23class Editor(W.Window):
24
25 def __init__(self, path = "", title = ""):
26 defaultfontsettings, defaulttabsettings, defaultwindowsize = geteditorprefs()
27 global _scriptuntitledcounter
28 if not path:
29 if title:
30 self.title = title
31 else:
32 self.title = "Untitled Script " + `_scriptuntitledcounter`
33 _scriptuntitledcounter = _scriptuntitledcounter + 1
34 text = ""
35 self._creator = W._signature
36 elif os.path.exists(path):
37 path = resolvealiases(path)
38 dir, name = os.path.split(path)
39 self.title = name
40 f = open(path, "rb")
41 text = f.read()
42 f.close()
43 fss = macfs.FSSpec(path)
44 self._creator, filetype = fss.GetCreatorType()
45 else:
46 raise IOError, "file '%s' does not exist" % path
47 self.path = path
48
49 self.settings = {}
50 if self.path:
51 self.readwindowsettings()
52 if self.settings.has_key("windowbounds"):
53 bounds = self.settings["windowbounds"]
54 else:
55 bounds = defaultwindowsize
56 if self.settings.has_key("fontsettings"):
57 self.fontsettings = self.settings["fontsettings"]
58 else:
59 self.fontsettings = defaultfontsettings
60 if self.settings.has_key("tabsize"):
61 try:
62 self.tabsettings = (tabsize, tabmode) = self.settings["tabsize"]
63 except:
64 self.tabsettings = defaulttabsettings
65 else:
66 self.tabsettings = defaulttabsettings
67 W.Window.__init__(self, bounds, self.title, minsize = (330, 120), tabbable = 0)
68
69 self.setupwidgets(text)
70 if self.settings.has_key("selection"):
71 selstart, selend = self.settings["selection"]
72 self.setselection(selstart, selend)
73 self.open()
74 self.setinfotext()
75 self.globals = {}
76 self._buf = "" # for write method
77 self.debugging = 0
78 self.profiling = 0
79 if self.settings.has_key("run_as_main"):
80 self.run_as_main = self.settings["run_as_main"]
81 else:
82 self.run_as_main = 0
83
84 def readwindowsettings(self):
85 try:
86 resref = Res.OpenResFile(self.path)
87 except Res.Error:
88 return
89 try:
90 Res.UseResFile(resref)
91 data = Res.Get1Resource('PyWS', 128)
92 self.settings = marshal.loads(data.data)
93 except:
94 pass
95 Res.CloseResFile(resref)
96
97 def writewindowsettings(self):
98 try:
99 resref = Res.OpenResFile(self.path)
100 except Res.Error:
101 Res.CreateResFile(self.path)
102 resref = Res.OpenResFile(self.path)
103 try:
104 data = Res.Resource(marshal.dumps(self.settings))
105 Res.UseResFile(resref)
106 try:
107 temp = Res.Get1Resource('PyWS', 128)
108 temp.RemoveResource()
109 except Res.Error:
110 pass
111 data.AddResource('PyWS', 128, "window settings")
112 finally:
113 Res.UpdateResFile(resref)
114 Res.CloseResFile(resref)
115
116 def getsettings(self):
117 self.settings = {}
118 self.settings["windowbounds"] = self.getbounds()
119 self.settings["selection"] = self.getselection()
120 self.settings["fontsettings"] = self.editgroup.editor.getfontsettings()
121 self.settings["tabsize"] = self.editgroup.editor.gettabsettings()
122 self.settings["run_as_main"] = self.run_as_main
123
124 def get(self):
125 return self.editgroup.editor.get()
126
127 def getselection(self):
128 return self.editgroup.editor.ted.WEGetSelection()
129
130 def setselection(self, selstart, selend):
131 self.editgroup.editor.setselection(selstart, selend)
132
133 def getfilename(self):
134 if self.path:
135 return self.path
136 return '<%s>' % self.title
137
138 def setupwidgets(self, text):
139 topbarheight = 24
140 popfieldwidth = 80
141 self.lastlineno = None
142
143 # make an editor
144 self.editgroup = W.Group((0, topbarheight + 1, 0, 0))
145 editor = W.PyEditor((0, 0, -15,-15), text,
146 fontsettings = self.fontsettings,
147 tabsettings = self.tabsettings,
148 file = self.getfilename())
149
150 # make the widgets
151 self.popfield = ClassFinder((popfieldwidth - 17, -15, 16, 16), [], self.popselectline)
152 self.linefield = W.EditText((-1, -15, popfieldwidth - 15, 16), inset = (6, 1))
153 self.editgroup._barx = W.Scrollbar((popfieldwidth - 2, -15, -14, 16), editor.hscroll, max = 32767)
154 self.editgroup._bary = W.Scrollbar((-15, 14, 16, -14), editor.vscroll, max = 32767)
155 self.editgroup.editor = editor # add editor *after* scrollbars
156
157 self.editgroup.optionsmenu = W.PopupMenu((-15, -1, 16, 16), [])
158 self.editgroup.optionsmenu.bind('<click>', self.makeoptionsmenu)
159
160 self.bevelbox = W.BevelBox((0, 0, 0, topbarheight))
161 self.hline = W.HorizontalLine((0, topbarheight, 0, 0))
162 self.infotext = W.TextBox((175, 6, -4, 14), backgroundcolor = (0xe000, 0xe000, 0xe000))
163 self.runbutton = W.Button((5, 4, 80, 16), "Run all", self.run)
164 self.runselbutton = W.Button((90, 4, 80, 16), "Run selection", self.runselection)
165
166 # bind some keys
167 editor.bind("cmdr", self.runbutton.push)
168 editor.bind("enter", self.runselbutton.push)
169 editor.bind("cmdj", self.domenu_gotoline)
170 editor.bind("cmdd", self.domenu_toggledebugger)
171 editor.bind("<idle>", self.updateselection)
172
173 editor.bind("cmde", searchengine.setfindstring)
174 editor.bind("cmdf", searchengine.show)
175 editor.bind("cmdg", searchengine.findnext)
176 editor.bind("cmdshiftr", searchengine.replace)
177 editor.bind("cmdt", searchengine.replacefind)
178
179 self.linefield.bind("return", self.dolinefield)
180 self.linefield.bind("enter", self.dolinefield)
181 self.linefield.bind("tab", self.dolinefield)
182
183 # intercept clicks
184 editor.bind("<click>", self.clickeditor)
185 self.linefield.bind("<click>", self.clicklinefield)
186
187 def makeoptionsmenu(self):
Just van Rossumedab9391999-02-02 22:31:05 +0000188 menuitems = [('Font settingsŠ', self.domenu_fontsettings),
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000189 ('\0' + chr(self.run_as_main) + 'Run as __main__', self.domenu_toggle_run_as_main),
190 ('Modularize', self.domenu_modularize),
Just van Rossumedab9391999-02-02 22:31:05 +0000191 ('Browse namespaceŠ', self.domenu_browsenamespace),
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000192 '-']
193 if self.profiling:
194 menuitems = menuitems + [('Disable profiler', self.domenu_toggleprofiler)]
195 else:
196 menuitems = menuitems + [('Enable profiler', self.domenu_toggleprofiler)]
197 if self.editgroup.editor._debugger:
198 menuitems = menuitems + [('Disable debugger', self.domenu_toggledebugger),
199 ('Clear breakpoints', self.domenu_clearbreakpoints),
Just van Rossumedab9391999-02-02 22:31:05 +0000200 ('Edit breakpointsŠ', self.domenu_editbreakpoints)]
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000201 else:
202 menuitems = menuitems + [('Enable debugger', self.domenu_toggledebugger)]
203 self.editgroup.optionsmenu.set(menuitems)
204
205 def domenu_toggle_run_as_main(self):
206 self.run_as_main = not self.run_as_main
207 self.editgroup.editor.selchanged = 1
208
209 def showbreakpoints(self, onoff):
210 self.editgroup.editor.showbreakpoints(onoff)
211 self.debugging = onoff
212
213 def domenu_clearbreakpoints(self, *args):
214 self.editgroup.editor.clearbreakpoints()
215
216 def domenu_editbreakpoints(self, *args):
217 self.editgroup.editor.editbreakpoints()
218
219 def domenu_toggledebugger(self, *args):
220 if not self.debugging:
221 W.SetCursor('watch')
222 self.debugging = not self.debugging
223 self.editgroup.editor.togglebreakpoints()
224
225 def domenu_toggleprofiler(self, *args):
226 self.profiling = not self.profiling
227
228 def domenu_browsenamespace(self, *args):
229 import PyBrowser, W
230 W.SetCursor('watch')
231 globals, file, modname = self.getenvironment()
232 if not modname:
233 modname = self.title
234 PyBrowser.Browser(globals, "Object browser: " + modname)
235
236 def domenu_modularize(self, *args):
237 modname = _filename_as_modname(self.title)
238 if not modname:
Just van Rossumedab9391999-02-02 22:31:05 +0000239 raise W.AlertError, 'Can¹t modularize ³%s²' % self.title
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000240 run_as_main = self.run_as_main
241 self.run_as_main = 0
242 self.run()
243 self.run_as_main = run_as_main
244 if self.path:
245 file = self.path
246 else:
247 file = self.title
248
249 if self.globals and not sys.modules.has_key(modname):
250 module = imp.new_module(modname)
251 for attr in self.globals.keys():
252 setattr(module,attr,self.globals[attr])
253 sys.modules[modname] = module
254 self.globals = {}
255
256 def domenu_fontsettings(self, *args):
257 import FontSettings
258 fontsettings = self.editgroup.editor.getfontsettings()
259 tabsettings = self.editgroup.editor.gettabsettings()
260 settings = FontSettings.FontDialog(fontsettings, tabsettings)
261 if settings:
262 fontsettings, tabsettings = settings
263 self.editgroup.editor.setfontsettings(fontsettings)
264 self.editgroup.editor.settabsettings(tabsettings)
265
266 def clicklinefield(self):
267 if self._currentwidget <> self.linefield:
268 self.linefield.select(1)
269 self.linefield.selectall()
270 return 1
271
272 def clickeditor(self):
273 if self._currentwidget <> self.editgroup.editor:
274 self.dolinefield()
275 return 1
276
277 def updateselection(self, force = 0):
278 sel = min(self.editgroup.editor.getselection())
279 lineno = self.editgroup.editor.offsettoline(sel)
280 if lineno <> self.lastlineno or force:
281 self.lastlineno = lineno
282 self.linefield.set(str(lineno + 1))
283 self.linefield.selview()
284
285 def dolinefield(self):
286 try:
287 lineno = string.atoi(self.linefield.get()) - 1
288 if lineno <> self.lastlineno:
289 self.editgroup.editor.selectline(lineno)
290 self.updateselection(1)
291 except:
292 self.updateselection(1)
293 self.editgroup.editor.select(1)
294
295 def setinfotext(self):
296 if not hasattr(self, 'infotext'):
297 return
298 if self.path:
299 self.infotext.set(self.path)
300 else:
301 self.infotext.set("")
302
303 def close(self):
304 if self.editgroup.editor.changed:
305 import EasyDialogs
306 import Qd
307 Qd.InitCursor() # XXX should be done by dialog
Just van Rossumedab9391999-02-02 22:31:05 +0000308 save = EasyDialogs.AskYesNoCancel('Save window ³%s² before closing?' % self.title, 1)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000309 if save > 0:
310 if self.domenu_save():
311 return 1
312 elif save < 0:
313 return 1
314 self.globals = None # XXX doesn't help... all globals leak :-(
315 W.Window.close(self)
316
317 def domenu_close(self, *args):
318 return self.close()
319
320 def domenu_save(self, *args):
321 if not self.path:
322 # Will call us recursively
323 return self.domenu_save_as()
324 data = self.editgroup.editor.get()
325 fp = open(self.path, 'wb') # open file in binary mode, data has '\r' line-endings
326 fp.write(data)
327 fp.close()
328 fss = macfs.FSSpec(self.path)
329 fss.SetCreatorType(self._creator, 'TEXT')
330 self.getsettings()
331 self.writewindowsettings()
332 self.editgroup.editor.changed = 0
333 self.editgroup.editor.selchanged = 0
334 import linecache
335 if linecache.cache.has_key(self.path):
336 del linecache.cache[self.path]
337 import macostools
338 macostools.touched(self.path)
339
340 def can_save(self, menuitem):
341 return self.editgroup.editor.changed or self.editgroup.editor.selchanged
342
343 def domenu_save_as(self, *args):
344 fss, ok = macfs.StandardPutFile('Save as:', self.title)
345 if not ok:
346 return 1
347 self.showbreakpoints(0)
348 self.path = fss.as_pathname()
349 self.setinfotext()
350 self.title = os.path.split(self.path)[-1]
351 self.wid.SetWTitle(self.title)
352 self.domenu_save()
353 self.editgroup.editor.setfile(self.getfilename())
354 app = W.getapplication()
355 app.makeopenwindowsmenu()
356 if hasattr(app, 'makescriptsmenu'):
357 app = W.getapplication()
358 fss, fss_changed = app.scriptsfolder.Resolve()
359 path = fss.as_pathname()
360 if path == self.path[:len(path)]:
361 W.getapplication().makescriptsmenu()
362
363 def domenu_save_as_applet(self, *args):
364 try:
365 import buildtools
366 except ImportError:
367 # only have buildtools in Python >= 1.5.2
Just van Rossumedab9391999-02-02 22:31:05 +0000368 raise W.AlertError, "³Save as Applet² is only supported in\rPython 1.5.2 and up."
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000369
370 buildtools.DEBUG = 0 # ouch.
371
372 if self.title[-3:] == ".py":
373 destname = self.title[:-3]
374 else:
375 destname = self.title + ".applet"
376 fss, ok = macfs.StandardPutFile('Save as Applet:', destname)
377 if not ok:
378 return 1
379 W.SetCursor("watch")
380 destname = fss.as_pathname()
381 if self.path:
382 filename = self.path
383 if filename[-3:] == ".py":
384 rsrcname = filename[:-3] + '.rsrc'
385 else:
386 rsrcname = filename + '.rsrc'
387 else:
388 filename = self.title
389 rsrcname = ""
390
391 pytext = self.editgroup.editor.get()
392 pytext = string.split(pytext, '\r')
393 pytext = string.join(pytext, '\n') + '\n'
394 try:
395 code = compile(pytext, filename, "exec")
396 except (SyntaxError, EOFError):
397 raise buildtools.BuildError, "Syntax error in script %s" % `filename`
398
399 # Try removing the output file
400 try:
401 os.remove(destname)
402 except os.error:
403 pass
404 template = buildtools.findtemplate()
405 buildtools.process_common(template, None, code, rsrcname, destname, 0, 1)
406
407 def domenu_gotoline(self, *args):
408 self.linefield.selectall()
409 self.linefield.select(1)
410 self.linefield.selectall()
411
412 def domenu_selectline(self, *args):
413 self.editgroup.editor.expandselection()
414
415 def domenu_find(self, *args):
416 searchengine.show()
417
418 def domenu_entersearchstring(self, *args):
419 searchengine.setfindstring()
420
421 def domenu_replace(self, *args):
422 searchengine.replace()
423
424 def domenu_findnext(self, *args):
425 searchengine.findnext()
426
427 def domenu_replacefind(self, *args):
428 searchengine.replacefind()
429
430 def domenu_run(self, *args):
431 self.runbutton.push()
432
433 def domenu_runselection(self, *args):
434 self.runselbutton.push()
435
436 def run(self):
437 self._run()
438
439 def _run(self):
440 pytext = self.editgroup.editor.get()
441 globals, file, modname = self.getenvironment()
442 self.execstring(pytext, globals, globals, file, modname)
443
444 def runselection(self):
445 self._runselection()
446
447 def _runselection(self):
448 globals, file, modname = self.getenvironment()
449 locals = globals
450 # select whole lines
451 self.editgroup.editor.expandselection()
452
453 # get lineno of first selected line
454 selstart, selend = self.editgroup.editor.getselection()
455 selstart, selend = min(selstart, selend), max(selstart, selend)
456 selfirstline = self.editgroup.editor.offsettoline(selstart)
457 alltext = self.editgroup.editor.get()
458 pytext = alltext[selstart:selend]
459 lines = string.split(pytext, '\r')
460 indent = getminindent(lines)
461 if indent == 1:
462 classname = ''
463 alllines = string.split(alltext, '\r')
464 identifieRE_match = _identifieRE.match
465 for i in range(selfirstline - 1, -1, -1):
466 line = alllines[i]
467 if line[:6] == 'class ':
468 classname = string.split(string.strip(line[6:]))[0]
469 classend = identifieRE_match(classname)
470 if classend < 1:
Just van Rossumedab9391999-02-02 22:31:05 +0000471 raise W.AlertError, 'Can¹t find a class.'
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000472 classname = classname[:classend]
473 break
474 elif line and line[0] not in '\t#':
Just van Rossumedab9391999-02-02 22:31:05 +0000475 raise W.AlertError, 'Can¹t find a class.'
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000476 else:
Just van Rossumedab9391999-02-02 22:31:05 +0000477 raise W.AlertError, 'Can¹t find a class.'
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000478 if globals.has_key(classname):
479 locals = globals[classname].__dict__
480 else:
Just van Rossumedab9391999-02-02 22:31:05 +0000481 raise W.AlertError, 'Can¹t find class ³%s².' % classname
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000482 # dedent to top level
483 for i in range(len(lines)):
484 lines[i] = lines[i][1:]
485 pytext = string.join(lines, '\r')
486 elif indent > 0:
Just van Rossumedab9391999-02-02 22:31:05 +0000487 raise W.AlertError, 'Can¹t run indented code.'
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000488
489 # add "newlines" to fool compile/exec:
490 # now a traceback will give the right line number
491 pytext = selfirstline * '\r' + pytext
492 self.execstring(pytext, globals, locals, file, modname)
493
494 def execstring(self, pytext, globals, locals, file, modname):
495 tracebackwindow.hide()
496 # update windows
497 W.getapplication().refreshwindows()
498 if self.run_as_main:
499 modname = "__main__"
500 if self.path:
501 dir = os.path.dirname(self.path)
502 savedir = os.getcwd()
503 os.chdir(dir)
Just van Rossuma61f4ac1999-02-01 16:34:08 +0000504 sys.path.insert(0, dir)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000505 else:
506 cwdindex = None
507 try:
508 execstring(pytext, globals, locals, file, self.debugging,
509 modname, self.profiling)
510 finally:
511 if self.path:
512 os.chdir(savedir)
Just van Rossuma61f4ac1999-02-01 16:34:08 +0000513 del sys.path[0]
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000514
515 def getenvironment(self):
516 if self.path:
517 file = self.path
518 dir = os.path.dirname(file)
519 # check if we're part of a package
520 modname = ""
521 while os.path.exists(os.path.join(dir, "__init__.py")):
522 dir, dirname = os.path.split(dir)
523 modname = modname + dirname + '.'
524 subname = _filename_as_modname(self.title)
525 if modname:
526 if subname == "__init__":
527 modname = modname[:-1] # strip trailing period
528 else:
529 modname = modname + subname
530 else:
531 modname = subname
532 if sys.modules.has_key(modname):
533 globals = sys.modules[modname].__dict__
534 self.globals = {}
535 else:
536 globals = self.globals
537 else:
538 file = '<%s>' % self.title
539 globals = self.globals
540 modname = file
541 return globals, file, modname
542
543 def write(self, stuff):
544 """for use as stdout"""
545 self._buf = self._buf + stuff
546 if '\n' in self._buf:
547 self.flush()
548
549 def flush(self):
550 stuff = string.split(self._buf, '\n')
551 stuff = string.join(stuff, '\r')
552 end = self.editgroup.editor.ted.WEGetTextLength()
553 self.editgroup.editor.ted.WESetSelection(end, end)
554 self.editgroup.editor.ted.WEInsert(stuff, None, None)
555 self.editgroup.editor.updatescrollbars()
556 self._buf = ""
557 # ? optional:
558 #self.wid.SelectWindow()
559
560 def getclasslist(self):
561 from string import find, strip
562 editor = self.editgroup.editor
563 text = editor.get()
564 list = []
565 append = list.append
566 functag = "func"
567 classtag = "class"
568 methodtag = "method"
569 pos = -1
570 if text[:4] == 'def ':
571 append((pos + 4, functag))
572 pos = 4
573 while 1:
574 pos = find(text, '\rdef ', pos + 1)
575 if pos < 0:
576 break
577 append((pos + 5, functag))
578 pos = -1
579 if text[:6] == 'class ':
580 append((pos + 6, classtag))
581 pos = 6
582 while 1:
583 pos = find(text, '\rclass ', pos + 1)
584 if pos < 0:
585 break
586 append((pos + 7, classtag))
587 pos = 0
588 while 1:
589 pos = find(text, '\r\tdef ', pos + 1)
590 if pos < 0:
591 break
592 append((pos + 6, methodtag))
593 list.sort()
594 classlist = []
595 methodlistappend = None
596 offsetToLine = editor.ted.WEOffsetToLine
597 getLineRange = editor.ted.WEGetLineRange
598 append = classlist.append
599 identifieRE_match = _identifieRE.match
600 for pos, tag in list:
601 lineno = offsetToLine(pos)
602 lineStart, lineEnd = getLineRange(lineno)
603 line = strip(text[pos:lineEnd])
604 line = line[:identifieRE_match(line)]
605 if tag is functag:
606 append(("def " + line, lineno + 1))
607 methodlistappend = None
608 elif tag is classtag:
609 append(["class " + line])
610 methodlistappend = classlist[-1].append
611 elif methodlistappend and tag is methodtag:
612 methodlistappend(("def " + line, lineno + 1))
613 return classlist
614
615 def popselectline(self, lineno):
616 self.editgroup.editor.selectline(lineno - 1)
617
618 def selectline(self, lineno, charoffset = 0):
619 self.editgroup.editor.selectline(lineno - 1, charoffset)
620
621
622def _escape(where, what) :
623 return string.join(string.split(where, what), '\\' + what)
624
625def _makewholewordpattern(word):
626 # first, escape special regex chars
627 for esc in "\\[].*^+$?":
628 word = _escape(word, esc)
629 import regex
630 notwordcharspat = '[^' + _wordchars + ']'
631 pattern = '\(' + word + '\)'
632 if word[0] in _wordchars:
633 pattern = notwordcharspat + pattern
634 if word[-1] in _wordchars:
635 pattern = pattern + notwordcharspat
636 return regex.compile(pattern)
637
638class SearchEngine:
639
640 def __init__(self):
641 self.visible = 0
642 self.w = None
643 self.parms = { "find": "",
644 "replace": "",
645 "wrap": 1,
646 "casesens": 1,
647 "wholeword": 1
648 }
649 import MacPrefs
650 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
651 if prefs.searchengine:
652 self.parms["casesens"] = prefs.searchengine.casesens
653 self.parms["wrap"] = prefs.searchengine.wrap
654 self.parms["wholeword"] = prefs.searchengine.wholeword
655
656 def show(self):
657 self.visible = 1
658 if self.w:
659 self.w.wid.ShowWindow()
660 self.w.wid.SelectWindow()
661 self.w.find.edit.select(1)
662 self.w.find.edit.selectall()
663 return
664 self.w = W.Dialog((420, 150), "Find")
665
666 self.w.find = TitledEditText((10, 4, 300, 36), "Search for:")
667 self.w.replace = TitledEditText((10, 100, 300, 36), "Replace with:")
668
669 self.w.boxes = W.Group((10, 50, 300, 40))
670 self.w.boxes.casesens = W.CheckBox((0, 0, 100, 16), "Case sensitive")
671 self.w.boxes.wholeword = W.CheckBox((0, 20, 100, 16), "Whole word")
672 self.w.boxes.wrap = W.CheckBox((110, 0, 100, 16), "Wrap around")
673
674 self.buttons = [ ("Find", "cmdf", self.find),
675 ("Replace", "cmdr", self.replace),
676 ("Replace all", None, self.replaceall),
Just van Rossumedab9391999-02-02 22:31:05 +0000677 ("Don¹t find", "cmdd", self.dont),
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000678 ("Cancel", "cmd.", self.cancel)
679 ]
680 for i in range(len(self.buttons)):
681 bounds = -90, 22 + i * 24, 80, 16
682 title, shortcut, callback = self.buttons[i]
683 self.w[title] = W.Button(bounds, title, callback)
684 if shortcut:
685 self.w.bind(shortcut, self.w[title].push)
Just van Rossumedab9391999-02-02 22:31:05 +0000686 self.w.setdefaultbutton(self.w["Don¹t find"])
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000687 self.w.find.edit.bind("<key>", self.key)
688 self.w.bind("<activate>", self.activate)
689 self.w.bind("<close>", self.close)
690 self.w.open()
691 self.setparms()
692 self.w.find.edit.select(1)
693 self.w.find.edit.selectall()
694 self.checkbuttons()
695
696 def close(self):
697 self.hide()
698 return -1
699
700 def key(self, char, modifiers):
701 self.w.find.edit.key(char, modifiers)
702 self.checkbuttons()
703 return 1
704
705 def activate(self, onoff):
706 if onoff:
707 self.checkbuttons()
708
709 def checkbuttons(self):
710 editor = findeditor(self)
711 if editor:
712 if self.w.find.get():
713 for title, cmd, call in self.buttons[:-2]:
714 self.w[title].enable(1)
715 self.w.setdefaultbutton(self.w["Find"])
716 else:
717 for title, cmd, call in self.buttons[:-2]:
718 self.w[title].enable(0)
Just van Rossumedab9391999-02-02 22:31:05 +0000719 self.w.setdefaultbutton(self.w["Don¹t find"])
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000720 else:
721 for title, cmd, call in self.buttons[:-2]:
722 self.w[title].enable(0)
Just van Rossumedab9391999-02-02 22:31:05 +0000723 self.w.setdefaultbutton(self.w["Don¹t find"])
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000724
725 def find(self):
726 self.getparmsfromwindow()
727 if self.findnext():
728 self.hide()
729
730 def replace(self):
731 editor = findeditor(self)
732 if not editor:
733 return
734 if self.visible:
735 self.getparmsfromwindow()
736 text = editor.getselectedtext()
737 find = self.parms["find"]
738 if not self.parms["casesens"]:
739 find = string.lower(find)
740 text = string.lower(text)
741 if text == find:
742 self.hide()
743 editor.insert(self.parms["replace"])
744
745 def replaceall(self):
746 editor = findeditor(self)
747 if not editor:
748 return
749 if self.visible:
750 self.getparmsfromwindow()
751 W.SetCursor("watch")
752 find = self.parms["find"]
753 if not find:
754 return
755 findlen = len(find)
756 replace = self.parms["replace"]
757 replacelen = len(replace)
758 Text = editor.get()
759 if not self.parms["casesens"]:
760 find = string.lower(find)
761 text = string.lower(Text)
762 else:
763 text = Text
764 newtext = ""
765 pos = 0
766 counter = 0
767 while 1:
768 if self.parms["wholeword"]:
769 wholewordRE = _makewholewordpattern(find)
770 wholewordRE.search(text, pos)
771 if wholewordRE.regs:
772 pos = wholewordRE.regs[1][0]
773 else:
774 pos = -1
775 else:
776 pos = string.find(text, find, pos)
777 if pos < 0:
778 break
779 counter = counter + 1
780 text = text[:pos] + replace + text[pos + findlen:]
781 Text = Text[:pos] + replace + Text[pos + findlen:]
782 pos = pos + replacelen
783 W.SetCursor("arrow")
784 if counter:
785 self.hide()
786 import EasyDialogs
787 import Res
788 editor.changed = 1
789 editor.selchanged = 1
790 editor.ted.WEUseText(Res.Resource(Text))
791 editor.ted.WECalText()
792 editor.SetPort()
793 Win.InvalRect(editor._bounds)
794 #editor.ted.WEUpdate(self.w.wid.GetWindowPort().visRgn)
795 EasyDialogs.Message("Replaced %d occurrences" % counter)
796
797 def dont(self):
798 self.getparmsfromwindow()
799 self.hide()
800
801 def replacefind(self):
802 self.replace()
803 self.findnext()
804
805 def setfindstring(self):
806 editor = findeditor(self)
807 if not editor:
808 return
809 find = editor.getselectedtext()
810 if not find:
811 return
812 self.parms["find"] = find
813 if self.w:
814 self.w.find.edit.set(self.parms["find"])
815 self.w.find.edit.selectall()
816
817 def findnext(self):
818 editor = findeditor(self)
819 if not editor:
820 return
821 find = self.parms["find"]
822 if not find:
823 return
824 text = editor.get()
825 if not self.parms["casesens"]:
826 find = string.lower(find)
827 text = string.lower(text)
828 selstart, selend = editor.getselection()
829 selstart, selend = min(selstart, selend), max(selstart, selend)
830 if self.parms["wholeword"]:
831 wholewordRE = _makewholewordpattern(find)
832 wholewordRE.search(text, selend)
833 if wholewordRE.regs:
834 pos = wholewordRE.regs[1][0]
835 else:
836 pos = -1
837 else:
838 pos = string.find(text, find, selend)
839 if pos >= 0:
840 editor.setselection(pos, pos + len(find))
841 return 1
842 elif self.parms["wrap"]:
843 if self.parms["wholeword"]:
844 wholewordRE.search(text, 0)
845 if wholewordRE.regs:
846 pos = wholewordRE.regs[1][0]
847 else:
848 pos = -1
849 else:
850 pos = string.find(text, find)
851 if selstart > pos >= 0:
852 editor.setselection(pos, pos + len(find))
853 return 1
854
855 def setparms(self):
856 for key, value in self.parms.items():
857 try:
858 self.w[key].set(value)
859 except KeyError:
860 self.w.boxes[key].set(value)
861
862 def getparmsfromwindow(self):
863 if not self.w:
864 return
865 for key, value in self.parms.items():
866 try:
867 value = self.w[key].get()
868 except KeyError:
869 value = self.w.boxes[key].get()
870 self.parms[key] = value
871
872 def cancel(self):
873 self.hide()
874 self.setparms()
875
876 def hide(self):
877 if self.w:
878 self.w.wid.HideWindow()
879 self.visible = 0
880
881 def writeprefs(self):
882 import MacPrefs
883 self.getparmsfromwindow()
884 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
885 prefs.searchengine.casesens = self.parms["casesens"]
886 prefs.searchengine.wrap = self.parms["wrap"]
887 prefs.searchengine.wholeword = self.parms["wholeword"]
888 prefs.save()
889
890
891class TitledEditText(W.Group):
892
893 def __init__(self, possize, title, text = ""):
894 W.Group.__init__(self, possize)
895 self.title = W.TextBox((0, 0, 0, 16), title)
896 self.edit = W.EditText((0, 16, 0, 0), text)
897
898 def set(self, value):
899 self.edit.set(value)
900
901 def get(self):
902 return self.edit.get()
903
904
905class ClassFinder(W.PopupWidget):
906
907 def click(self, point, modifiers):
908 W.SetCursor("watch")
909 self.set(self._parentwindow.getclasslist())
910 W.PopupWidget.click(self, point, modifiers)
911
912
913def getminindent(lines):
914 indent = -1
915 for line in lines:
916 stripped = string.strip(line)
917 if not stripped or stripped[0] == '#':
918 continue
919 if indent < 0 or line[:indent] <> indent * '\t':
920 indent = 0
921 for c in line:
922 if c <> '\t':
923 break
924 indent = indent + 1
925 return indent
926
927
928def getoptionkey():
929 return not not ord(Evt.GetKeys()[7]) & 0x04
930
931
932def execstring(pytext, globals, locals, filename="<string>", debugging=0,
933 modname="__main__", profiling=0):
934 if debugging:
935 import PyDebugger, bdb
936 BdbQuit = bdb.BdbQuit
937 else:
938 BdbQuit = 'BdbQuitDummyException'
939 pytext = string.split(pytext, '\r')
940 pytext = string.join(pytext, '\n') + '\n'
941 W.SetCursor("watch")
942 globals['__name__'] = modname
943 globals['__file__'] = filename
944 sys.argv = [filename]
945 try:
946 code = compile(pytext, filename, "exec")
947 except:
948 # XXXX BAAAADDD.... We let tracebackwindow decide to treat SyntaxError
949 # special. That's wrong because THIS case is special (could be literal
950 # overflow!) and SyntaxError could mean we need a traceback (syntax error
951 # in imported module!!!
952 tracebackwindow.traceback(1, filename)
953 return
954 try:
955 if debugging:
956 PyDebugger.startfromhere()
957 else:
958 MacOS.EnableAppswitch(0)
959 try:
960 if profiling:
961 import profile, ProfileBrowser
962 p = profile.Profile()
963 p.set_cmd(filename)
964 try:
965 p.runctx(code, globals, locals)
966 finally:
967 import pstats
968
969 stats = pstats.Stats(p)
970 ProfileBrowser.ProfileBrowser(stats)
971 else:
972 exec code in globals, locals
973 finally:
974 MacOS.EnableAppswitch(-1)
975 except W.AlertError, detail:
976 raise W.AlertError, detail
977 except (KeyboardInterrupt, BdbQuit):
978 pass
979 except:
980 if debugging:
981 sys.settrace(None)
982 PyDebugger.postmortem(sys.exc_type, sys.exc_value, sys.exc_traceback)
983 return
984 else:
985 tracebackwindow.traceback(1, filename)
986 if debugging:
987 sys.settrace(None)
988 PyDebugger.stop()
989
990
991_identifieRE = regex.compile("[A-Za-z_][A-Za-z_0-9]*")
992
993def _filename_as_modname(fname):
994 if fname[-3:] == '.py':
995 modname = fname[:-3]
996 if _identifieRE.match(modname) == len(modname):
997 return string.join(string.split(modname, '.'), '_')
998
999def findeditor(topwindow, fromtop = 0):
1000 wid = Win.FrontWindow()
1001 if not fromtop:
1002 if topwindow.w and wid == topwindow.w.wid:
1003 wid = topwindow.w.wid.GetNextWindow()
1004 if not wid:
1005 return
1006 app = W.getapplication()
1007 if app._windows.has_key(wid): # KeyError otherwise can happen in RoboFog :-(
1008 window = W.getapplication()._windows[wid]
1009 else:
1010 return
1011 if not isinstance(window, Editor):
1012 return
1013 return window.editgroup.editor
1014
1015
1016class _EditorDefaultSettings:
1017
1018 def __init__(self):
1019 self.template = "%s, %d point"
1020 self.fontsettings, self.tabsettings, self.windowsize = geteditorprefs()
1021 self.w = W.Dialog((328, 120), "Editor default settings")
Just van Rossumedab9391999-02-02 22:31:05 +00001022 self.w.setfontbutton = W.Button((8, 8, 80, 16), "Set fontŠ", self.dofont)
Just van Rossum40f9b7b1999-01-30 22:39:17 +00001023 self.w.fonttext = W.TextBox((98, 10, -8, 14), self.template % (self.fontsettings[0], self.fontsettings[2]))
1024
1025 self.w.picksizebutton = W.Button((8, 50, 80, 16), "Front window", self.picksize)
1026 self.w.xsizelabel = W.TextBox((98, 32, 40, 14), "Width:")
1027 self.w.ysizelabel = W.TextBox((148, 32, 40, 14), "Height:")
1028 self.w.xsize = W.EditText((98, 48, 40, 20), `self.windowsize[0]`)
1029 self.w.ysize = W.EditText((148, 48, 40, 20), `self.windowsize[1]`)
1030
1031 self.w.cancelbutton = W.Button((-180, -26, 80, 16), "Cancel", self.cancel)
1032 self.w.okbutton = W.Button((-90, -26, 80, 16), "Done", self.ok)
1033 self.w.setdefaultbutton(self.w.okbutton)
1034 self.w.bind('cmd.', self.w.cancelbutton.push)
1035 self.w.open()
1036
1037 def picksize(self):
1038 app = W.getapplication()
1039 editor = findeditor(self)
1040 if editor is not None:
1041 width, height = editor._parentwindow._bounds[2:]
1042 self.w.xsize.set(`width`)
1043 self.w.ysize.set(`height`)
1044 else:
1045 raise W.AlertError, "No edit window found"
1046
1047 def dofont(self):
1048 import FontSettings
1049 settings = FontSettings.FontDialog(self.fontsettings, self.tabsettings)
1050 if settings:
1051 self.fontsettings, self.tabsettings = settings
1052 sys.exc_traceback = None
1053 self.w.fonttext.set(self.template % (self.fontsettings[0], self.fontsettings[2]))
1054
1055 def close(self):
1056 self.w.close()
1057 del self.w
1058
1059 def cancel(self):
1060 self.close()
1061
1062 def ok(self):
1063 try:
1064 width = string.atoi(self.w.xsize.get())
1065 except:
1066 self.w.xsize.select(1)
1067 self.w.xsize.selectall()
1068 raise W.AlertError, "Bad number for window width"
1069 try:
1070 height = string.atoi(self.w.ysize.get())
1071 except:
1072 self.w.ysize.select(1)
1073 self.w.ysize.selectall()
1074 raise W.AlertError, "Bad number for window height"
1075 self.windowsize = width, height
1076 seteditorprefs(self.fontsettings, self.tabsettings, self.windowsize)
1077 self.close()
1078
1079def geteditorprefs():
1080 import MacPrefs
1081 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
1082 try:
1083 fontsettings = prefs.pyedit.fontsettings
1084 tabsettings = prefs.pyedit.tabsettings
1085 windowsize = prefs.pyedit.windowsize
1086 except:
1087 fontsettings = prefs.pyedit.fontsettings = ("Python-Sans", 0, 9, (0, 0, 0))
1088 tabsettings = prefs.pyedit.tabsettings = (8, 1)
1089 windowsize = prefs.pyedit.windowsize = (500, 250)
1090 sys.exc_traceback = None
1091 return fontsettings, tabsettings, windowsize
1092
1093def seteditorprefs(fontsettings, tabsettings, windowsize):
1094 import MacPrefs
1095 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
1096 prefs.pyedit.fontsettings = fontsettings
1097 prefs.pyedit.tabsettings = tabsettings
1098 prefs.pyedit.windowsize = windowsize
1099 prefs.save()
1100
1101_defaultSettingsEditor = None
1102
1103def EditorDefaultSettings():
1104 global _defaultSettingsEditor
1105 if _defaultSettingsEditor is None or not hasattr(_defaultSettingsEditor, "w"):
1106 _defaultSettingsEditor = _EditorDefaultSettings()
1107 else:
1108 _defaultSettingsEditor.w.select()
1109
1110def resolvealiases(path):
1111 try:
1112 return macfs.ResolveAliasFile(path)[0].as_pathname()
1113 except (macfs.error, ValueError), (error, str):
1114 if error <> -120:
1115 raise
1116 dir, file = os.path.split(path)
1117 return os.path.join(resolvealiases(dir), file)
1118
1119searchengine = SearchEngine()
1120tracebackwindow = Wtraceback.TraceBack()