blob: dfc21c5b133d2c22fb5ba8623b9d61804063583f [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 Rossum12710051999-02-27 17:18:30 +0000189 ("Save optionsŠ", self.domenu_options),
190 '-',
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000191 ('\0' + chr(self.run_as_main) + 'Run as __main__', self.domenu_toggle_run_as_main),
192 ('Modularize', self.domenu_modularize),
Just van Rossumedab9391999-02-02 22:31:05 +0000193 ('Browse namespaceŠ', self.domenu_browsenamespace),
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000194 '-']
195 if self.profiling:
196 menuitems = menuitems + [('Disable profiler', self.domenu_toggleprofiler)]
197 else:
198 menuitems = menuitems + [('Enable profiler', self.domenu_toggleprofiler)]
199 if self.editgroup.editor._debugger:
200 menuitems = menuitems + [('Disable debugger', self.domenu_toggledebugger),
201 ('Clear breakpoints', self.domenu_clearbreakpoints),
Just van Rossumedab9391999-02-02 22:31:05 +0000202 ('Edit breakpointsŠ', self.domenu_editbreakpoints)]
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000203 else:
204 menuitems = menuitems + [('Enable debugger', self.domenu_toggledebugger)]
205 self.editgroup.optionsmenu.set(menuitems)
206
207 def domenu_toggle_run_as_main(self):
208 self.run_as_main = not self.run_as_main
209 self.editgroup.editor.selchanged = 1
210
211 def showbreakpoints(self, onoff):
212 self.editgroup.editor.showbreakpoints(onoff)
213 self.debugging = onoff
214
215 def domenu_clearbreakpoints(self, *args):
216 self.editgroup.editor.clearbreakpoints()
217
218 def domenu_editbreakpoints(self, *args):
219 self.editgroup.editor.editbreakpoints()
220
221 def domenu_toggledebugger(self, *args):
222 if not self.debugging:
223 W.SetCursor('watch')
224 self.debugging = not self.debugging
225 self.editgroup.editor.togglebreakpoints()
226
227 def domenu_toggleprofiler(self, *args):
228 self.profiling = not self.profiling
229
230 def domenu_browsenamespace(self, *args):
231 import PyBrowser, W
232 W.SetCursor('watch')
233 globals, file, modname = self.getenvironment()
234 if not modname:
235 modname = self.title
236 PyBrowser.Browser(globals, "Object browser: " + modname)
237
238 def domenu_modularize(self, *args):
239 modname = _filename_as_modname(self.title)
240 if not modname:
Just van Rossumedab9391999-02-02 22:31:05 +0000241 raise W.AlertError, 'Can¹t modularize ³%s²' % self.title
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000242 run_as_main = self.run_as_main
243 self.run_as_main = 0
244 self.run()
245 self.run_as_main = run_as_main
246 if self.path:
247 file = self.path
248 else:
249 file = self.title
250
251 if self.globals and not sys.modules.has_key(modname):
252 module = imp.new_module(modname)
253 for attr in self.globals.keys():
254 setattr(module,attr,self.globals[attr])
255 sys.modules[modname] = module
256 self.globals = {}
257
258 def domenu_fontsettings(self, *args):
259 import FontSettings
260 fontsettings = self.editgroup.editor.getfontsettings()
261 tabsettings = self.editgroup.editor.gettabsettings()
262 settings = FontSettings.FontDialog(fontsettings, tabsettings)
263 if settings:
264 fontsettings, tabsettings = settings
265 self.editgroup.editor.setfontsettings(fontsettings)
266 self.editgroup.editor.settabsettings(tabsettings)
267
Just van Rossum12710051999-02-27 17:18:30 +0000268 def domenu_options(self, *args):
269 rv = SaveOptions(self._creator)
270 if rv:
Just van Rossum3af507d1999-04-22 22:23:46 +0000271 self.editgroup.editor.selchanged = 1 # ouch...
Just van Rossum12710051999-02-27 17:18:30 +0000272 self._creator = rv
273
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000274 def clicklinefield(self):
275 if self._currentwidget <> self.linefield:
276 self.linefield.select(1)
277 self.linefield.selectall()
278 return 1
279
280 def clickeditor(self):
281 if self._currentwidget <> self.editgroup.editor:
282 self.dolinefield()
283 return 1
284
285 def updateselection(self, force = 0):
286 sel = min(self.editgroup.editor.getselection())
287 lineno = self.editgroup.editor.offsettoline(sel)
288 if lineno <> self.lastlineno or force:
289 self.lastlineno = lineno
290 self.linefield.set(str(lineno + 1))
291 self.linefield.selview()
292
293 def dolinefield(self):
294 try:
295 lineno = string.atoi(self.linefield.get()) - 1
296 if lineno <> self.lastlineno:
297 self.editgroup.editor.selectline(lineno)
298 self.updateselection(1)
299 except:
300 self.updateselection(1)
301 self.editgroup.editor.select(1)
302
303 def setinfotext(self):
304 if not hasattr(self, 'infotext'):
305 return
306 if self.path:
307 self.infotext.set(self.path)
308 else:
309 self.infotext.set("")
310
311 def close(self):
312 if self.editgroup.editor.changed:
313 import EasyDialogs
314 import Qd
315 Qd.InitCursor() # XXX should be done by dialog
Just van Rossumedab9391999-02-02 22:31:05 +0000316 save = EasyDialogs.AskYesNoCancel('Save window ³%s² before closing?' % self.title, 1)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000317 if save > 0:
318 if self.domenu_save():
319 return 1
320 elif save < 0:
321 return 1
322 self.globals = None # XXX doesn't help... all globals leak :-(
323 W.Window.close(self)
324
325 def domenu_close(self, *args):
326 return self.close()
327
328 def domenu_save(self, *args):
329 if not self.path:
330 # Will call us recursively
331 return self.domenu_save_as()
332 data = self.editgroup.editor.get()
333 fp = open(self.path, 'wb') # open file in binary mode, data has '\r' line-endings
334 fp.write(data)
335 fp.close()
336 fss = macfs.FSSpec(self.path)
337 fss.SetCreatorType(self._creator, 'TEXT')
338 self.getsettings()
339 self.writewindowsettings()
340 self.editgroup.editor.changed = 0
341 self.editgroup.editor.selchanged = 0
342 import linecache
343 if linecache.cache.has_key(self.path):
344 del linecache.cache[self.path]
345 import macostools
346 macostools.touched(self.path)
347
348 def can_save(self, menuitem):
349 return self.editgroup.editor.changed or self.editgroup.editor.selchanged
350
351 def domenu_save_as(self, *args):
352 fss, ok = macfs.StandardPutFile('Save as:', self.title)
353 if not ok:
354 return 1
355 self.showbreakpoints(0)
356 self.path = fss.as_pathname()
357 self.setinfotext()
358 self.title = os.path.split(self.path)[-1]
359 self.wid.SetWTitle(self.title)
360 self.domenu_save()
361 self.editgroup.editor.setfile(self.getfilename())
362 app = W.getapplication()
363 app.makeopenwindowsmenu()
364 if hasattr(app, 'makescriptsmenu'):
365 app = W.getapplication()
366 fss, fss_changed = app.scriptsfolder.Resolve()
367 path = fss.as_pathname()
368 if path == self.path[:len(path)]:
369 W.getapplication().makescriptsmenu()
370
371 def domenu_save_as_applet(self, *args):
372 try:
373 import buildtools
374 except ImportError:
375 # only have buildtools in Python >= 1.5.2
Just van Rossumedab9391999-02-02 22:31:05 +0000376 raise W.AlertError, "³Save as Applet² is only supported in\rPython 1.5.2 and up."
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000377
378 buildtools.DEBUG = 0 # ouch.
379
380 if self.title[-3:] == ".py":
381 destname = self.title[:-3]
382 else:
383 destname = self.title + ".applet"
384 fss, ok = macfs.StandardPutFile('Save as Applet:', destname)
385 if not ok:
386 return 1
387 W.SetCursor("watch")
388 destname = fss.as_pathname()
389 if self.path:
390 filename = self.path
391 if filename[-3:] == ".py":
392 rsrcname = filename[:-3] + '.rsrc'
393 else:
394 rsrcname = filename + '.rsrc'
395 else:
396 filename = self.title
397 rsrcname = ""
398
399 pytext = self.editgroup.editor.get()
400 pytext = string.split(pytext, '\r')
401 pytext = string.join(pytext, '\n') + '\n'
402 try:
403 code = compile(pytext, filename, "exec")
404 except (SyntaxError, EOFError):
405 raise buildtools.BuildError, "Syntax error in script %s" % `filename`
406
407 # Try removing the output file
408 try:
409 os.remove(destname)
410 except os.error:
411 pass
412 template = buildtools.findtemplate()
413 buildtools.process_common(template, None, code, rsrcname, destname, 0, 1)
414
415 def domenu_gotoline(self, *args):
416 self.linefield.selectall()
417 self.linefield.select(1)
418 self.linefield.selectall()
419
420 def domenu_selectline(self, *args):
421 self.editgroup.editor.expandselection()
422
423 def domenu_find(self, *args):
424 searchengine.show()
425
426 def domenu_entersearchstring(self, *args):
427 searchengine.setfindstring()
428
429 def domenu_replace(self, *args):
430 searchengine.replace()
431
432 def domenu_findnext(self, *args):
433 searchengine.findnext()
434
435 def domenu_replacefind(self, *args):
436 searchengine.replacefind()
437
438 def domenu_run(self, *args):
439 self.runbutton.push()
440
441 def domenu_runselection(self, *args):
442 self.runselbutton.push()
443
444 def run(self):
445 self._run()
446
447 def _run(self):
448 pytext = self.editgroup.editor.get()
449 globals, file, modname = self.getenvironment()
450 self.execstring(pytext, globals, globals, file, modname)
451
452 def runselection(self):
453 self._runselection()
454
455 def _runselection(self):
456 globals, file, modname = self.getenvironment()
457 locals = globals
458 # select whole lines
459 self.editgroup.editor.expandselection()
460
461 # get lineno of first selected line
462 selstart, selend = self.editgroup.editor.getselection()
463 selstart, selend = min(selstart, selend), max(selstart, selend)
464 selfirstline = self.editgroup.editor.offsettoline(selstart)
465 alltext = self.editgroup.editor.get()
466 pytext = alltext[selstart:selend]
467 lines = string.split(pytext, '\r')
468 indent = getminindent(lines)
469 if indent == 1:
470 classname = ''
471 alllines = string.split(alltext, '\r')
472 identifieRE_match = _identifieRE.match
473 for i in range(selfirstline - 1, -1, -1):
474 line = alllines[i]
475 if line[:6] == 'class ':
476 classname = string.split(string.strip(line[6:]))[0]
477 classend = identifieRE_match(classname)
478 if classend < 1:
Just van Rossumedab9391999-02-02 22:31:05 +0000479 raise W.AlertError, 'Can¹t find a class.'
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000480 classname = classname[:classend]
481 break
482 elif line and line[0] not in '\t#':
Just van Rossumedab9391999-02-02 22:31:05 +0000483 raise W.AlertError, 'Can¹t find a class.'
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000484 else:
Just van Rossumedab9391999-02-02 22:31:05 +0000485 raise W.AlertError, 'Can¹t find a class.'
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000486 if globals.has_key(classname):
487 locals = globals[classname].__dict__
488 else:
Just van Rossumedab9391999-02-02 22:31:05 +0000489 raise W.AlertError, 'Can¹t find class ³%s².' % classname
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000490 # dedent to top level
491 for i in range(len(lines)):
492 lines[i] = lines[i][1:]
493 pytext = string.join(lines, '\r')
494 elif indent > 0:
Just van Rossumedab9391999-02-02 22:31:05 +0000495 raise W.AlertError, 'Can¹t run indented code.'
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000496
497 # add "newlines" to fool compile/exec:
498 # now a traceback will give the right line number
499 pytext = selfirstline * '\r' + pytext
500 self.execstring(pytext, globals, locals, file, modname)
501
502 def execstring(self, pytext, globals, locals, file, modname):
503 tracebackwindow.hide()
504 # update windows
505 W.getapplication().refreshwindows()
506 if self.run_as_main:
507 modname = "__main__"
508 if self.path:
509 dir = os.path.dirname(self.path)
510 savedir = os.getcwd()
511 os.chdir(dir)
Just van Rossuma61f4ac1999-02-01 16:34:08 +0000512 sys.path.insert(0, dir)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000513 else:
514 cwdindex = None
515 try:
516 execstring(pytext, globals, locals, file, self.debugging,
517 modname, self.profiling)
518 finally:
519 if self.path:
520 os.chdir(savedir)
Just van Rossuma61f4ac1999-02-01 16:34:08 +0000521 del sys.path[0]
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000522
523 def getenvironment(self):
524 if self.path:
525 file = self.path
526 dir = os.path.dirname(file)
527 # check if we're part of a package
528 modname = ""
529 while os.path.exists(os.path.join(dir, "__init__.py")):
530 dir, dirname = os.path.split(dir)
Just van Rossum2aaeb521999-02-05 21:58:25 +0000531 modname = dirname + '.' + modname
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000532 subname = _filename_as_modname(self.title)
533 if modname:
534 if subname == "__init__":
Just van Rossum2aaeb521999-02-05 21:58:25 +0000535 # strip trailing period
536 modname = modname[:-1]
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000537 else:
Just van Rossum2aaeb521999-02-05 21:58:25 +0000538 modname = modname + subname
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000539 else:
540 modname = subname
541 if sys.modules.has_key(modname):
542 globals = sys.modules[modname].__dict__
543 self.globals = {}
544 else:
545 globals = self.globals
546 else:
547 file = '<%s>' % self.title
548 globals = self.globals
549 modname = file
550 return globals, file, modname
551
552 def write(self, stuff):
553 """for use as stdout"""
554 self._buf = self._buf + stuff
555 if '\n' in self._buf:
556 self.flush()
557
558 def flush(self):
559 stuff = string.split(self._buf, '\n')
560 stuff = string.join(stuff, '\r')
561 end = self.editgroup.editor.ted.WEGetTextLength()
562 self.editgroup.editor.ted.WESetSelection(end, end)
563 self.editgroup.editor.ted.WEInsert(stuff, None, None)
564 self.editgroup.editor.updatescrollbars()
565 self._buf = ""
566 # ? optional:
567 #self.wid.SelectWindow()
568
569 def getclasslist(self):
570 from string import find, strip
571 editor = self.editgroup.editor
572 text = editor.get()
573 list = []
574 append = list.append
575 functag = "func"
576 classtag = "class"
577 methodtag = "method"
578 pos = -1
579 if text[:4] == 'def ':
580 append((pos + 4, functag))
581 pos = 4
582 while 1:
583 pos = find(text, '\rdef ', pos + 1)
584 if pos < 0:
585 break
586 append((pos + 5, functag))
587 pos = -1
588 if text[:6] == 'class ':
589 append((pos + 6, classtag))
590 pos = 6
591 while 1:
592 pos = find(text, '\rclass ', pos + 1)
593 if pos < 0:
594 break
595 append((pos + 7, classtag))
596 pos = 0
597 while 1:
598 pos = find(text, '\r\tdef ', pos + 1)
599 if pos < 0:
600 break
601 append((pos + 6, methodtag))
602 list.sort()
603 classlist = []
604 methodlistappend = None
605 offsetToLine = editor.ted.WEOffsetToLine
606 getLineRange = editor.ted.WEGetLineRange
607 append = classlist.append
608 identifieRE_match = _identifieRE.match
609 for pos, tag in list:
610 lineno = offsetToLine(pos)
611 lineStart, lineEnd = getLineRange(lineno)
612 line = strip(text[pos:lineEnd])
613 line = line[:identifieRE_match(line)]
614 if tag is functag:
615 append(("def " + line, lineno + 1))
616 methodlistappend = None
617 elif tag is classtag:
618 append(["class " + line])
619 methodlistappend = classlist[-1].append
620 elif methodlistappend and tag is methodtag:
621 methodlistappend(("def " + line, lineno + 1))
622 return classlist
623
624 def popselectline(self, lineno):
625 self.editgroup.editor.selectline(lineno - 1)
626
627 def selectline(self, lineno, charoffset = 0):
628 self.editgroup.editor.selectline(lineno - 1, charoffset)
629
Just van Rossum12710051999-02-27 17:18:30 +0000630class _saveoptions:
631
632 def __init__(self, creator):
633 self.rv = None
634 self.w = w = W.ModalDialog((240, 140), 'Save options')
635 radiobuttons = []
636 w.label = W.TextBox((8, 8, 80, 18), "File creator:")
Just van Rossum3af507d1999-04-22 22:23:46 +0000637 w.ide_radio = W.RadioButton((8, 22, 160, 18), "This application", radiobuttons, self.ide_hit)
638 w.interp_radio = W.RadioButton((8, 42, 160, 18), "Python Interpreter", radiobuttons, self.interp_hit)
Just van Rossum12710051999-02-27 17:18:30 +0000639 w.other_radio = W.RadioButton((8, 62, 50, 18), "Other:", radiobuttons)
640 w.other_creator = W.EditText((62, 62, 40, 20), creator, self.otherselect)
641 w.cancelbutton = W.Button((-180, -30, 80, 16), "Cancel", self.cancelbuttonhit)
642 w.okbutton = W.Button((-90, -30, 80, 16), "Done", self.okbuttonhit)
643 w.setdefaultbutton(w.okbutton)
644 if creator == 'Pyth':
645 w.interp_radio.set(1)
Just van Rossum3af507d1999-04-22 22:23:46 +0000646 elif creator == W._signature:
Just van Rossum12710051999-02-27 17:18:30 +0000647 w.ide_radio.set(1)
648 else:
649 w.other_radio.set(1)
650 w.bind("cmd.", w.cancelbutton.push)
651 w.open()
652
653 def ide_hit(self):
Just van Rossum3af507d1999-04-22 22:23:46 +0000654 self.w.other_creator.set(W._signature)
Just van Rossum12710051999-02-27 17:18:30 +0000655
656 def interp_hit(self):
657 self.w.other_creator.set("Pyth")
658
659 def otherselect(self, *args):
660 sel_from, sel_to = self.w.other_creator.getselection()
661 creator = self.w.other_creator.get()[:4]
662 creator = creator + " " * (4 - len(creator))
663 self.w.other_creator.set(creator)
664 self.w.other_creator.setselection(sel_from, sel_to)
665 self.w.other_radio.set(1)
666
667 def cancelbuttonhit(self):
668 self.w.close()
669
670 def okbuttonhit(self):
671 self.rv = self.w.other_creator.get()[:4]
672 self.w.close()
673
674
675def SaveOptions(creator):
676 s = _saveoptions(creator)
677 return s.rv
678
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000679
680def _escape(where, what) :
681 return string.join(string.split(where, what), '\\' + what)
682
683def _makewholewordpattern(word):
684 # first, escape special regex chars
685 for esc in "\\[].*^+$?":
686 word = _escape(word, esc)
687 import regex
688 notwordcharspat = '[^' + _wordchars + ']'
689 pattern = '\(' + word + '\)'
690 if word[0] in _wordchars:
691 pattern = notwordcharspat + pattern
692 if word[-1] in _wordchars:
693 pattern = pattern + notwordcharspat
694 return regex.compile(pattern)
695
696class SearchEngine:
697
698 def __init__(self):
699 self.visible = 0
700 self.w = None
701 self.parms = { "find": "",
702 "replace": "",
703 "wrap": 1,
704 "casesens": 1,
705 "wholeword": 1
706 }
707 import MacPrefs
708 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
709 if prefs.searchengine:
710 self.parms["casesens"] = prefs.searchengine.casesens
711 self.parms["wrap"] = prefs.searchengine.wrap
712 self.parms["wholeword"] = prefs.searchengine.wholeword
713
714 def show(self):
715 self.visible = 1
716 if self.w:
717 self.w.wid.ShowWindow()
718 self.w.wid.SelectWindow()
719 self.w.find.edit.select(1)
720 self.w.find.edit.selectall()
721 return
722 self.w = W.Dialog((420, 150), "Find")
723
724 self.w.find = TitledEditText((10, 4, 300, 36), "Search for:")
725 self.w.replace = TitledEditText((10, 100, 300, 36), "Replace with:")
726
727 self.w.boxes = W.Group((10, 50, 300, 40))
728 self.w.boxes.casesens = W.CheckBox((0, 0, 100, 16), "Case sensitive")
729 self.w.boxes.wholeword = W.CheckBox((0, 20, 100, 16), "Whole word")
730 self.w.boxes.wrap = W.CheckBox((110, 0, 100, 16), "Wrap around")
731
732 self.buttons = [ ("Find", "cmdf", self.find),
733 ("Replace", "cmdr", self.replace),
734 ("Replace all", None, self.replaceall),
Just van Rossumedab9391999-02-02 22:31:05 +0000735 ("Don¹t find", "cmdd", self.dont),
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000736 ("Cancel", "cmd.", self.cancel)
737 ]
738 for i in range(len(self.buttons)):
739 bounds = -90, 22 + i * 24, 80, 16
740 title, shortcut, callback = self.buttons[i]
741 self.w[title] = W.Button(bounds, title, callback)
742 if shortcut:
743 self.w.bind(shortcut, self.w[title].push)
Just van Rossumedab9391999-02-02 22:31:05 +0000744 self.w.setdefaultbutton(self.w["Don¹t find"])
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000745 self.w.find.edit.bind("<key>", self.key)
746 self.w.bind("<activate>", self.activate)
747 self.w.bind("<close>", self.close)
748 self.w.open()
749 self.setparms()
750 self.w.find.edit.select(1)
751 self.w.find.edit.selectall()
752 self.checkbuttons()
753
754 def close(self):
755 self.hide()
756 return -1
757
758 def key(self, char, modifiers):
759 self.w.find.edit.key(char, modifiers)
760 self.checkbuttons()
761 return 1
762
763 def activate(self, onoff):
764 if onoff:
765 self.checkbuttons()
766
767 def checkbuttons(self):
768 editor = findeditor(self)
769 if editor:
770 if self.w.find.get():
771 for title, cmd, call in self.buttons[:-2]:
772 self.w[title].enable(1)
773 self.w.setdefaultbutton(self.w["Find"])
774 else:
775 for title, cmd, call in self.buttons[:-2]:
776 self.w[title].enable(0)
Just van Rossumedab9391999-02-02 22:31:05 +0000777 self.w.setdefaultbutton(self.w["Don¹t find"])
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000778 else:
779 for title, cmd, call in self.buttons[:-2]:
780 self.w[title].enable(0)
Just van Rossumedab9391999-02-02 22:31:05 +0000781 self.w.setdefaultbutton(self.w["Don¹t find"])
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000782
783 def find(self):
784 self.getparmsfromwindow()
785 if self.findnext():
786 self.hide()
787
788 def replace(self):
789 editor = findeditor(self)
790 if not editor:
791 return
792 if self.visible:
793 self.getparmsfromwindow()
794 text = editor.getselectedtext()
795 find = self.parms["find"]
796 if not self.parms["casesens"]:
797 find = string.lower(find)
798 text = string.lower(text)
799 if text == find:
800 self.hide()
801 editor.insert(self.parms["replace"])
802
803 def replaceall(self):
804 editor = findeditor(self)
805 if not editor:
806 return
807 if self.visible:
808 self.getparmsfromwindow()
809 W.SetCursor("watch")
810 find = self.parms["find"]
811 if not find:
812 return
813 findlen = len(find)
814 replace = self.parms["replace"]
815 replacelen = len(replace)
816 Text = editor.get()
817 if not self.parms["casesens"]:
818 find = string.lower(find)
819 text = string.lower(Text)
820 else:
821 text = Text
822 newtext = ""
823 pos = 0
824 counter = 0
825 while 1:
826 if self.parms["wholeword"]:
827 wholewordRE = _makewholewordpattern(find)
828 wholewordRE.search(text, pos)
829 if wholewordRE.regs:
830 pos = wholewordRE.regs[1][0]
831 else:
832 pos = -1
833 else:
834 pos = string.find(text, find, pos)
835 if pos < 0:
836 break
837 counter = counter + 1
838 text = text[:pos] + replace + text[pos + findlen:]
839 Text = Text[:pos] + replace + Text[pos + findlen:]
840 pos = pos + replacelen
841 W.SetCursor("arrow")
842 if counter:
843 self.hide()
844 import EasyDialogs
845 import Res
846 editor.changed = 1
847 editor.selchanged = 1
848 editor.ted.WEUseText(Res.Resource(Text))
849 editor.ted.WECalText()
850 editor.SetPort()
851 Win.InvalRect(editor._bounds)
852 #editor.ted.WEUpdate(self.w.wid.GetWindowPort().visRgn)
853 EasyDialogs.Message("Replaced %d occurrences" % counter)
854
855 def dont(self):
856 self.getparmsfromwindow()
857 self.hide()
858
859 def replacefind(self):
860 self.replace()
861 self.findnext()
862
863 def setfindstring(self):
864 editor = findeditor(self)
865 if not editor:
866 return
867 find = editor.getselectedtext()
868 if not find:
869 return
870 self.parms["find"] = find
871 if self.w:
872 self.w.find.edit.set(self.parms["find"])
873 self.w.find.edit.selectall()
874
875 def findnext(self):
876 editor = findeditor(self)
877 if not editor:
878 return
879 find = self.parms["find"]
880 if not find:
881 return
882 text = editor.get()
883 if not self.parms["casesens"]:
884 find = string.lower(find)
885 text = string.lower(text)
886 selstart, selend = editor.getselection()
887 selstart, selend = min(selstart, selend), max(selstart, selend)
888 if self.parms["wholeword"]:
889 wholewordRE = _makewholewordpattern(find)
890 wholewordRE.search(text, selend)
891 if wholewordRE.regs:
892 pos = wholewordRE.regs[1][0]
893 else:
894 pos = -1
895 else:
896 pos = string.find(text, find, selend)
897 if pos >= 0:
898 editor.setselection(pos, pos + len(find))
899 return 1
900 elif self.parms["wrap"]:
901 if self.parms["wholeword"]:
902 wholewordRE.search(text, 0)
903 if wholewordRE.regs:
904 pos = wholewordRE.regs[1][0]
905 else:
906 pos = -1
907 else:
908 pos = string.find(text, find)
909 if selstart > pos >= 0:
910 editor.setselection(pos, pos + len(find))
911 return 1
912
913 def setparms(self):
914 for key, value in self.parms.items():
915 try:
916 self.w[key].set(value)
917 except KeyError:
918 self.w.boxes[key].set(value)
919
920 def getparmsfromwindow(self):
921 if not self.w:
922 return
923 for key, value in self.parms.items():
924 try:
925 value = self.w[key].get()
926 except KeyError:
927 value = self.w.boxes[key].get()
928 self.parms[key] = value
929
930 def cancel(self):
931 self.hide()
932 self.setparms()
933
934 def hide(self):
935 if self.w:
936 self.w.wid.HideWindow()
937 self.visible = 0
938
939 def writeprefs(self):
940 import MacPrefs
941 self.getparmsfromwindow()
942 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
943 prefs.searchengine.casesens = self.parms["casesens"]
944 prefs.searchengine.wrap = self.parms["wrap"]
945 prefs.searchengine.wholeword = self.parms["wholeword"]
946 prefs.save()
947
948
949class TitledEditText(W.Group):
950
951 def __init__(self, possize, title, text = ""):
952 W.Group.__init__(self, possize)
953 self.title = W.TextBox((0, 0, 0, 16), title)
954 self.edit = W.EditText((0, 16, 0, 0), text)
955
956 def set(self, value):
957 self.edit.set(value)
958
959 def get(self):
960 return self.edit.get()
961
962
963class ClassFinder(W.PopupWidget):
964
965 def click(self, point, modifiers):
966 W.SetCursor("watch")
967 self.set(self._parentwindow.getclasslist())
968 W.PopupWidget.click(self, point, modifiers)
969
970
971def getminindent(lines):
972 indent = -1
973 for line in lines:
974 stripped = string.strip(line)
975 if not stripped or stripped[0] == '#':
976 continue
977 if indent < 0 or line[:indent] <> indent * '\t':
978 indent = 0
979 for c in line:
980 if c <> '\t':
981 break
982 indent = indent + 1
983 return indent
984
985
986def getoptionkey():
987 return not not ord(Evt.GetKeys()[7]) & 0x04
988
989
990def execstring(pytext, globals, locals, filename="<string>", debugging=0,
991 modname="__main__", profiling=0):
992 if debugging:
993 import PyDebugger, bdb
994 BdbQuit = bdb.BdbQuit
995 else:
996 BdbQuit = 'BdbQuitDummyException'
997 pytext = string.split(pytext, '\r')
998 pytext = string.join(pytext, '\n') + '\n'
999 W.SetCursor("watch")
1000 globals['__name__'] = modname
1001 globals['__file__'] = filename
1002 sys.argv = [filename]
1003 try:
1004 code = compile(pytext, filename, "exec")
1005 except:
1006 # XXXX BAAAADDD.... We let tracebackwindow decide to treat SyntaxError
1007 # special. That's wrong because THIS case is special (could be literal
1008 # overflow!) and SyntaxError could mean we need a traceback (syntax error
1009 # in imported module!!!
1010 tracebackwindow.traceback(1, filename)
1011 return
1012 try:
1013 if debugging:
1014 PyDebugger.startfromhere()
1015 else:
1016 MacOS.EnableAppswitch(0)
1017 try:
1018 if profiling:
1019 import profile, ProfileBrowser
1020 p = profile.Profile()
1021 p.set_cmd(filename)
1022 try:
1023 p.runctx(code, globals, locals)
1024 finally:
1025 import pstats
1026
1027 stats = pstats.Stats(p)
1028 ProfileBrowser.ProfileBrowser(stats)
1029 else:
1030 exec code in globals, locals
1031 finally:
1032 MacOS.EnableAppswitch(-1)
1033 except W.AlertError, detail:
1034 raise W.AlertError, detail
1035 except (KeyboardInterrupt, BdbQuit):
1036 pass
1037 except:
1038 if debugging:
1039 sys.settrace(None)
1040 PyDebugger.postmortem(sys.exc_type, sys.exc_value, sys.exc_traceback)
1041 return
1042 else:
1043 tracebackwindow.traceback(1, filename)
1044 if debugging:
1045 sys.settrace(None)
1046 PyDebugger.stop()
1047
1048
1049_identifieRE = regex.compile("[A-Za-z_][A-Za-z_0-9]*")
1050
1051def _filename_as_modname(fname):
1052 if fname[-3:] == '.py':
1053 modname = fname[:-3]
1054 if _identifieRE.match(modname) == len(modname):
1055 return string.join(string.split(modname, '.'), '_')
1056
1057def findeditor(topwindow, fromtop = 0):
1058 wid = Win.FrontWindow()
1059 if not fromtop:
1060 if topwindow.w and wid == topwindow.w.wid:
1061 wid = topwindow.w.wid.GetNextWindow()
1062 if not wid:
1063 return
1064 app = W.getapplication()
1065 if app._windows.has_key(wid): # KeyError otherwise can happen in RoboFog :-(
1066 window = W.getapplication()._windows[wid]
1067 else:
1068 return
1069 if not isinstance(window, Editor):
1070 return
1071 return window.editgroup.editor
1072
1073
1074class _EditorDefaultSettings:
1075
1076 def __init__(self):
1077 self.template = "%s, %d point"
1078 self.fontsettings, self.tabsettings, self.windowsize = geteditorprefs()
1079 self.w = W.Dialog((328, 120), "Editor default settings")
Just van Rossumedab9391999-02-02 22:31:05 +00001080 self.w.setfontbutton = W.Button((8, 8, 80, 16), "Set fontŠ", self.dofont)
Just van Rossum40f9b7b1999-01-30 22:39:17 +00001081 self.w.fonttext = W.TextBox((98, 10, -8, 14), self.template % (self.fontsettings[0], self.fontsettings[2]))
1082
1083 self.w.picksizebutton = W.Button((8, 50, 80, 16), "Front window", self.picksize)
1084 self.w.xsizelabel = W.TextBox((98, 32, 40, 14), "Width:")
1085 self.w.ysizelabel = W.TextBox((148, 32, 40, 14), "Height:")
1086 self.w.xsize = W.EditText((98, 48, 40, 20), `self.windowsize[0]`)
1087 self.w.ysize = W.EditText((148, 48, 40, 20), `self.windowsize[1]`)
1088
1089 self.w.cancelbutton = W.Button((-180, -26, 80, 16), "Cancel", self.cancel)
1090 self.w.okbutton = W.Button((-90, -26, 80, 16), "Done", self.ok)
1091 self.w.setdefaultbutton(self.w.okbutton)
1092 self.w.bind('cmd.', self.w.cancelbutton.push)
1093 self.w.open()
1094
1095 def picksize(self):
1096 app = W.getapplication()
1097 editor = findeditor(self)
1098 if editor is not None:
1099 width, height = editor._parentwindow._bounds[2:]
1100 self.w.xsize.set(`width`)
1101 self.w.ysize.set(`height`)
1102 else:
1103 raise W.AlertError, "No edit window found"
1104
1105 def dofont(self):
1106 import FontSettings
1107 settings = FontSettings.FontDialog(self.fontsettings, self.tabsettings)
1108 if settings:
1109 self.fontsettings, self.tabsettings = settings
1110 sys.exc_traceback = None
1111 self.w.fonttext.set(self.template % (self.fontsettings[0], self.fontsettings[2]))
1112
1113 def close(self):
1114 self.w.close()
1115 del self.w
1116
1117 def cancel(self):
1118 self.close()
1119
1120 def ok(self):
1121 try:
1122 width = string.atoi(self.w.xsize.get())
1123 except:
1124 self.w.xsize.select(1)
1125 self.w.xsize.selectall()
1126 raise W.AlertError, "Bad number for window width"
1127 try:
1128 height = string.atoi(self.w.ysize.get())
1129 except:
1130 self.w.ysize.select(1)
1131 self.w.ysize.selectall()
1132 raise W.AlertError, "Bad number for window height"
1133 self.windowsize = width, height
1134 seteditorprefs(self.fontsettings, self.tabsettings, self.windowsize)
1135 self.close()
1136
1137def geteditorprefs():
1138 import MacPrefs
1139 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
1140 try:
1141 fontsettings = prefs.pyedit.fontsettings
1142 tabsettings = prefs.pyedit.tabsettings
1143 windowsize = prefs.pyedit.windowsize
1144 except:
1145 fontsettings = prefs.pyedit.fontsettings = ("Python-Sans", 0, 9, (0, 0, 0))
1146 tabsettings = prefs.pyedit.tabsettings = (8, 1)
1147 windowsize = prefs.pyedit.windowsize = (500, 250)
1148 sys.exc_traceback = None
1149 return fontsettings, tabsettings, windowsize
1150
1151def seteditorprefs(fontsettings, tabsettings, windowsize):
1152 import MacPrefs
1153 prefs = MacPrefs.GetPrefs(W.getapplication().preffilepath)
1154 prefs.pyedit.fontsettings = fontsettings
1155 prefs.pyedit.tabsettings = tabsettings
1156 prefs.pyedit.windowsize = windowsize
1157 prefs.save()
1158
1159_defaultSettingsEditor = None
1160
1161def EditorDefaultSettings():
1162 global _defaultSettingsEditor
1163 if _defaultSettingsEditor is None or not hasattr(_defaultSettingsEditor, "w"):
1164 _defaultSettingsEditor = _EditorDefaultSettings()
1165 else:
1166 _defaultSettingsEditor.w.select()
1167
1168def resolvealiases(path):
1169 try:
1170 return macfs.ResolveAliasFile(path)[0].as_pathname()
1171 except (macfs.error, ValueError), (error, str):
1172 if error <> -120:
1173 raise
1174 dir, file = os.path.split(path)
1175 return os.path.join(resolvealiases(dir), file)
1176
1177searchengine = SearchEngine()
1178tracebackwindow = Wtraceback.TraceBack()