1 """A (less & less) simple Python editor"""
10 from Carbon
import Res
11 from Carbon
import Evt
13 from Carbon
import File
23 if hasattr(Win
, "FrontNonFloatingWindow"):
24 MyFrontWindow
= Win
.FrontNonFloatingWindow
26 MyFrontWindow
= Win
.FrontWindow
29 _scriptuntitledcounter
= 1
30 _wordchars
= string
.ascii_letters
+ string
.digits
+ "_"
33 runButtonLabels
= ["Run all", "Stop!"]
34 runSelButtonLabels
= ["Run selection", "Pause!", "Resume"]
37 class Editor(W
.Window
):
39 def __init__(self
, path
= "", title
= ""):
40 defaultfontsettings
, defaulttabsettings
, defaultwindowsize
= geteditorprefs()
41 global _scriptuntitledcounter
46 self
.title
= "Untitled Script " + `_scriptuntitledcounter`
47 _scriptuntitledcounter
= _scriptuntitledcounter
+ 1
49 self
._creator
= W
._signature
50 self
._eoln
= os
.linesep
51 elif os
.path
.exists(path
):
52 path
= resolvealiases(path
)
53 dir, name
= os
.path
.split(path
)
58 self
._creator
, filetype
= MacOS
.GetCreatorAndType(path
)
59 self
.addrecentfile(path
)
61 raise IOError, "file '%s' does not exist" % path
65 if string
.find(text
, '\r\n') >= 0:
69 text
= string
.replace(text
, self
._eoln
, '\r')
77 self
.readwindowsettings()
78 if self
.settings
.has_key("windowbounds"):
79 bounds
= self
.settings
["windowbounds"]
81 bounds
= defaultwindowsize
82 if self
.settings
.has_key("fontsettings"):
83 self
.fontsettings
= self
.settings
["fontsettings"]
85 self
.fontsettings
= defaultfontsettings
86 if self
.settings
.has_key("tabsize"):
88 self
.tabsettings
= (tabsize
, tabmode
) = self
.settings
["tabsize"]
90 self
.tabsettings
= defaulttabsettings
92 self
.tabsettings
= defaulttabsettings
94 W
.Window
.__init
__(self
, bounds
, self
.title
, minsize
= (330, 120), tabbable
= 0)
95 self
.setupwidgets(text
)
97 self
.editgroup
.editor
.textchanged()
99 if self
.settings
.has_key("selection"):
100 selstart
, selend
= self
.settings
["selection"]
101 self
.setselection(selstart
, selend
)
105 self
._buf
= "" # for write method
108 self
.run_as_main
= self
.settings
.get("run_as_main", 0)
109 self
.run_with_interpreter
= self
.settings
.get("run_with_interpreter", 0)
110 self
.run_with_cl_interpreter
= self
.settings
.get("run_with_cl_interpreter", 0)
112 def readwindowsettings(self
):
114 resref
= Res
.FSpOpenResFile(self
.path
, 1)
118 Res
.UseResFile(resref
)
119 data
= Res
.Get1Resource('PyWS', 128)
120 self
.settings
= marshal
.loads(data
.data
)
123 Res
.CloseResFile(resref
)
125 def writewindowsettings(self
):
127 resref
= Res
.FSpOpenResFile(self
.path
, 3)
129 Res
.FSpCreateResFile(self
.path
, self
._creator
, 'TEXT', smAllScripts
)
130 resref
= Res
.FSpOpenResFile(self
.path
, 3)
132 data
= Res
.Resource(marshal
.dumps(self
.settings
))
133 Res
.UseResFile(resref
)
135 temp
= Res
.Get1Resource('PyWS', 128)
136 temp
.RemoveResource()
139 data
.AddResource('PyWS', 128, "window settings")
141 Res
.UpdateResFile(resref
)
142 Res
.CloseResFile(resref
)
144 def getsettings(self
):
146 self
.settings
["windowbounds"] = self
.getbounds()
147 self
.settings
["selection"] = self
.getselection()
148 self
.settings
["fontsettings"] = self
.editgroup
.editor
.getfontsettings()
149 self
.settings
["tabsize"] = self
.editgroup
.editor
.gettabsettings()
150 self
.settings
["run_as_main"] = self
.run_as_main
151 self
.settings
["run_with_interpreter"] = self
.run_with_interpreter
152 self
.settings
["run_with_cl_interpreter"] = self
.run_with_cl_interpreter
155 return self
.editgroup
.editor
.get()
157 def getselection(self
):
158 return self
.editgroup
.editor
.ted
.WEGetSelection()
160 def setselection(self
, selstart
, selend
):
161 self
.editgroup
.editor
.setselection(selstart
, selend
)
163 def getselectedtext(self
):
164 return self
.editgroup
.editor
.getselectedtext()
166 def getfilename(self
):
169 return '<%s>' % self
.title
171 def setupwidgets(self
, text
):
174 self
.lastlineno
= None
177 self
.editgroup
= W
.Group((0, topbarheight
+ 1, 0, 0))
178 editor
= W
.PyEditor((0, 0, -15,-15), text
,
179 fontsettings
= self
.fontsettings
,
180 tabsettings
= self
.tabsettings
,
181 file = self
.getfilename())
184 self
.popfield
= ClassFinder((popfieldwidth
- 17, -15, 16, 16), [], self
.popselectline
)
185 self
.linefield
= W
.EditText((-1, -15, popfieldwidth
- 15, 16), inset
= (6, 1))
186 self
.editgroup
._barx
= W
.Scrollbar((popfieldwidth
- 2, -15, -14, 16), editor
.hscroll
, max = 32767)
187 self
.editgroup
._bary
= W
.Scrollbar((-15, 14, 16, -14), editor
.vscroll
, max = 32767)
188 self
.editgroup
.editor
= editor
# add editor *after* scrollbars
190 self
.editgroup
.optionsmenu
= W
.PopupMenu((-15, -1, 16, 16), [])
191 self
.editgroup
.optionsmenu
.bind('<click>', self
.makeoptionsmenu
)
193 self
.bevelbox
= W
.BevelBox((0, 0, 0, topbarheight
))
194 self
.hline
= W
.HorizontalLine((0, topbarheight
, 0, 0))
195 self
.infotext
= W
.TextBox((175, 6, -4, 14), backgroundcolor
= (0xe000, 0xe000, 0xe000))
196 self
.runbutton
= W
.BevelButton((6, 4, 80, 16), runButtonLabels
[0], self
.run
)
197 self
.runselbutton
= W
.BevelButton((90, 4, 80, 16), runSelButtonLabels
[0], self
.runselection
)
200 editor
.bind("cmdr", self
.runbutton
.push
)
201 editor
.bind("enter", self
.runselbutton
.push
)
202 editor
.bind("cmdj", self
.domenu_gotoline
)
203 editor
.bind("cmdd", self
.domenu_toggledebugger
)
204 editor
.bind("<idle>", self
.updateselection
)
206 editor
.bind("cmde", searchengine
.setfindstring
)
207 editor
.bind("cmdf", searchengine
.show
)
208 editor
.bind("cmdg", searchengine
.findnext
)
209 editor
.bind("cmdshiftr", searchengine
.replace
)
210 editor
.bind("cmdt", searchengine
.replacefind
)
212 self
.linefield
.bind("return", self
.dolinefield
)
213 self
.linefield
.bind("enter", self
.dolinefield
)
214 self
.linefield
.bind("tab", self
.dolinefield
)
217 editor
.bind("<click>", self
.clickeditor
)
218 self
.linefield
.bind("<click>", self
.clicklinefield
)
220 def makeoptionsmenu(self
):
221 menuitems
= [('Font settings\xc9', self
.domenu_fontsettings
),
222 ("Save options\xc9", self
.domenu_options
),
224 ('\0' + chr(self
.run_as_main
) + 'Run as __main__', self
.domenu_toggle_run_as_main
),
225 #('\0' + chr(self.run_with_interpreter) + 'Run with Interpreter', self.domenu_dtoggle_run_with_interpreter),
226 ('\0' + chr(self
.run_with_cl_interpreter
) + 'Run with commandline Python', self
.domenu_toggle_run_with_cl_interpreter
),
228 ('Modularize', self
.domenu_modularize
),
229 ('Browse namespace\xc9', self
.domenu_browsenamespace
),
232 menuitems
= menuitems
+ [('Disable profiler', self
.domenu_toggleprofiler
)]
234 menuitems
= menuitems
+ [('Enable profiler', self
.domenu_toggleprofiler
)]
235 if self
.editgroup
.editor
._debugger
:
236 menuitems
= menuitems
+ [('Disable debugger', self
.domenu_toggledebugger
),
237 ('Clear breakpoints', self
.domenu_clearbreakpoints
),
238 ('Edit breakpoints\xc9', self
.domenu_editbreakpoints
)]
240 menuitems
= menuitems
+ [('Enable debugger', self
.domenu_toggledebugger
)]
241 self
.editgroup
.optionsmenu
.set(menuitems
)
243 def domenu_toggle_run_as_main(self
):
244 self
.run_as_main
= not self
.run_as_main
245 self
.run_with_interpreter
= 0
246 self
.run_with_cl_interpreter
= 0
247 self
.editgroup
.editor
.selectionchanged()
249 def XXdomenu_toggle_run_with_interpreter(self
):
250 self
.run_with_interpreter
= not self
.run_with_interpreter
252 self
.run_with_cl_interpreter
= 0
253 self
.editgroup
.editor
.selectionchanged()
255 def domenu_toggle_run_with_cl_interpreter(self
):
256 self
.run_with_cl_interpreter
= not self
.run_with_cl_interpreter
258 self
.run_with_interpreter
= 0
259 self
.editgroup
.editor
.selectionchanged()
261 def showbreakpoints(self
, onoff
):
262 self
.editgroup
.editor
.showbreakpoints(onoff
)
263 self
.debugging
= onoff
265 def domenu_clearbreakpoints(self
, *args
):
266 self
.editgroup
.editor
.clearbreakpoints()
268 def domenu_editbreakpoints(self
, *args
):
269 self
.editgroup
.editor
.editbreakpoints()
271 def domenu_toggledebugger(self
, *args
):
272 if not self
.debugging
:
274 self
.debugging
= not self
.debugging
275 self
.editgroup
.editor
.togglebreakpoints()
277 def domenu_toggleprofiler(self
, *args
):
278 self
.profiling
= not self
.profiling
280 def domenu_browsenamespace(self
, *args
):
283 globals, file, modname
= self
.getenvironment()
286 PyBrowser
.Browser(globals, "Object browser: " + modname
)
288 def domenu_modularize(self
, *args
):
289 modname
= _filename_as_modname(self
.title
)
291 raise W
.AlertError
, "Can't modularize \"%s\"" % self
.title
292 run_as_main
= self
.run_as_main
295 self
.run_as_main
= run_as_main
301 if self
.globals and not sys
.modules
.has_key(modname
):
302 module
= imp
.new_module(modname
)
303 for attr
in self
.globals.keys():
304 setattr(module
,attr
,self
.globals[attr
])
305 sys
.modules
[modname
] = module
308 def domenu_fontsettings(self
, *args
):
310 fontsettings
= self
.editgroup
.editor
.getfontsettings()
311 tabsettings
= self
.editgroup
.editor
.gettabsettings()
312 settings
= FontSettings
.FontDialog(fontsettings
, tabsettings
)
314 fontsettings
, tabsettings
= settings
315 self
.editgroup
.editor
.setfontsettings(fontsettings
)
316 self
.editgroup
.editor
.settabsettings(tabsettings
)
318 def domenu_options(self
, *args
):
319 rv
= SaveOptions(self
._creator
, self
._eoln
)
321 self
.editgroup
.editor
.selectionchanged() # ouch...
322 self
._creator
, self
._eoln
= rv
324 def clicklinefield(self
):
325 if self
._currentwidget
<> self
.linefield
:
326 self
.linefield
.select(1)
327 self
.linefield
.selectall()
330 def clickeditor(self
):
331 if self
._currentwidget
<> self
.editgroup
.editor
:
335 def updateselection(self
, force
= 0):
336 sel
= min(self
.editgroup
.editor
.getselection())
337 lineno
= self
.editgroup
.editor
.offsettoline(sel
)
338 if lineno
<> self
.lastlineno
or force
:
339 self
.lastlineno
= lineno
340 self
.linefield
.set(str(lineno
+ 1))
341 self
.linefield
.selview()
343 def dolinefield(self
):
345 lineno
= string
.atoi(self
.linefield
.get()) - 1
346 if lineno
<> self
.lastlineno
:
347 self
.editgroup
.editor
.selectline(lineno
)
348 self
.updateselection(1)
350 self
.updateselection(1)
351 self
.editgroup
.editor
.select(1)
353 def setinfotext(self
):
354 if not hasattr(self
, 'infotext'):
357 self
.infotext
.set(self
.path
)
359 self
.infotext
.set("")
362 if self
.editgroup
.editor
.changed
:
364 save
= EasyDialogs
.AskYesNoCancel('Save window "%s" before closing?' % self
.title
,
365 default
=1, no
="Don\xd5t save")
367 if self
.domenu_save():
374 def domenu_close(self
, *args
):
377 def domenu_save(self
, *args
):
379 # Will call us recursively
380 return self
.domenu_save_as()
381 data
= self
.editgroup
.editor
.get()
382 if self
._eoln
!= '\r':
383 data
= string
.replace(data
, '\r', self
._eoln
)
384 fp
= open(self
.path
, 'wb') # open file in binary mode, data has '\r' line-endings
387 MacOS
.SetCreatorAndType(self
.path
, self
._creator
, 'TEXT')
389 self
.writewindowsettings()
390 self
.editgroup
.editor
.changed
= 0
391 self
.editgroup
.editor
.selchanged
= 0
393 if linecache
.cache
.has_key(self
.path
):
394 del linecache
.cache
[self
.path
]
396 macostools
.touched(self
.path
)
397 self
.addrecentfile(self
.path
)
399 def can_save(self
, menuitem
):
400 return self
.editgroup
.editor
.changed
or self
.editgroup
.editor
.selchanged
402 def domenu_save_as(self
, *args
):
403 path
= EasyDialogs
.AskFileForSave(message
='Save as:', savedFileName
=self
.title
)
406 self
.showbreakpoints(0)
409 self
.title
= os
.path
.split(self
.path
)[-1]
410 self
.wid
.SetWTitle(self
.title
)
412 self
.editgroup
.editor
.setfile(self
.getfilename())
413 app
= W
.getapplication()
414 app
.makeopenwindowsmenu()
415 if hasattr(app
, 'makescriptsmenu'):
416 app
= W
.getapplication()
417 fsr
, changed
= app
.scriptsfolder
.FSResolveAlias(None)
418 path
= fsr
.as_pathname()
419 if path
== self
.path
[:len(path
)]:
420 W
.getapplication().makescriptsmenu()
422 def domenu_save_as_applet(self
, *args
):
425 buildtools
.DEBUG
= 0 # ouch.
427 if self
.title
[-3:] == ".py":
428 destname
= self
.title
[:-3]
430 destname
= self
.title
+ ".applet"
431 destname
= EasyDialogs
.AskFileForSave(message
='Save as Applet:',
432 savedFileName
=destname
)
438 if filename
[-3:] == ".py":
439 rsrcname
= filename
[:-3] + '.rsrc'
441 rsrcname
= filename
+ '.rsrc'
443 filename
= self
.title
446 pytext
= self
.editgroup
.editor
.get()
447 pytext
= string
.split(pytext
, '\r')
448 pytext
= string
.join(pytext
, '\n') + '\n'
450 code
= compile(pytext
, filename
, "exec")
451 except (SyntaxError, EOFError):
452 raise buildtools
.BuildError
, "Syntax error in script %s" % `filename`
455 tmpdir
= tempfile
.mkdtemp()
457 if filename
[-3:] != ".py":
458 filename
= filename
+ ".py"
459 filename
= os
.path
.join(tmpdir
, os
.path
.split(filename
)[1])
460 fp
= open(filename
, "w")
464 # Try removing the output file
469 template
= buildtools
.findtemplate()
470 buildtools
.process(template
, filename
, destname
, 1, rsrcname
=rsrcname
, progress
=None)
477 def domenu_gotoline(self
, *args
):
478 self
.linefield
.selectall()
479 self
.linefield
.select(1)
480 self
.linefield
.selectall()
482 def domenu_selectline(self
, *args
):
483 self
.editgroup
.editor
.expandselection()
485 def domenu_find(self
, *args
):
488 def domenu_entersearchstring(self
, *args
):
489 searchengine
.setfindstring()
491 def domenu_replace(self
, *args
):
492 searchengine
.replace()
494 def domenu_findnext(self
, *args
):
495 searchengine
.findnext()
497 def domenu_replacefind(self
, *args
):
498 searchengine
.replacefind()
500 def domenu_run(self
, *args
):
501 self
.runbutton
.push()
503 def domenu_runselection(self
, *args
):
504 self
.runselbutton
.push()
510 if self
.run_with_interpreter
:
511 if self
.editgroup
.editor
.changed
:
513 save
= EasyDialogs
.AskYesNoCancel('Save "%s" before running?' % self
.title
, 1)
515 if self
.domenu_save():
520 raise W
.AlertError
, "Can't run unsaved file"
521 self
._run
_with
_interpreter
()
522 elif self
.run_with_cl_interpreter
:
523 if self
.editgroup
.editor
.changed
:
525 save
= EasyDialogs
.AskYesNoCancel('Save "%s" before running?' % self
.title
, 1)
527 if self
.domenu_save():
532 raise W
.AlertError
, "Can't run unsaved file"
533 self
._run
_with
_cl
_interpreter
()
535 pytext
= self
.editgroup
.editor
.get()
536 globals, file, modname
= self
.getenvironment()
537 self
.execstring(pytext
, globals, globals, file, modname
)
539 def _run_with_interpreter(self
):
540 interp_path
= os
.path
.join(sys
.exec_prefix
, "PythonInterpreter")
541 if not os
.path
.exists(interp_path
):
542 raise W
.AlertError
, "Can't find interpreter"
546 def _run_with_cl_interpreter(self
):
548 interp_path
= os
.path
.join(sys
.exec_prefix
, "bin", "python")
549 file_path
= self
.path
550 if not os
.path
.exists(interp_path
):
551 # This "can happen" if we are running IDE under MacPython-OS9.
552 raise W
.AlertError
, "Can't find command-line Python"
553 cmd
= '"%s" "%s" ; exit' % (interp_path
, file_path
)
554 t
= Terminal
.Terminal()
555 t
.do_script(with_command
=cmd
)
557 def runselection(self
):
560 def _runselection(self
):
561 if self
.run_with_interpreter
or self
.run_with_cl_interpreter
:
562 raise W
.AlertError
, "Can't run selection with Interpreter"
563 globals, file, modname
= self
.getenvironment()
566 self
.editgroup
.editor
.expandselection()
568 # get lineno of first selected line
569 selstart
, selend
= self
.editgroup
.editor
.getselection()
570 selstart
, selend
= min(selstart
, selend
), max(selstart
, selend
)
571 selfirstline
= self
.editgroup
.editor
.offsettoline(selstart
)
572 alltext
= self
.editgroup
.editor
.get()
573 pytext
= alltext
[selstart
:selend
]
574 lines
= string
.split(pytext
, '\r')
575 indent
= getminindent(lines
)
578 alllines
= string
.split(alltext
, '\r')
579 for i
in range(selfirstline
- 1, -1, -1):
581 if line
[:6] == 'class ':
582 classname
= string
.split(string
.strip(line
[6:]))[0]
583 classend
= identifieRE_match(classname
)
585 raise W
.AlertError
, "Can't find a class."
586 classname
= classname
[:classend
]
588 elif line
and line
[0] not in '\t#':
589 raise W
.AlertError
, "Can't find a class."
591 raise W
.AlertError
, "Can't find a class."
592 if globals.has_key(classname
):
593 klass
= globals[classname
]
595 raise W
.AlertError
, "Can't find class \"%s\"." % classname
597 pytext
= ("class %s:\n" % classname
) + pytext
598 selfirstline
= selfirstline
- 1
600 raise W
.AlertError
, "Can't run indented code."
602 # add "newlines" to fool compile/exec:
603 # now a traceback will give the right line number
604 pytext
= selfirstline
* '\r' + pytext
605 self
.execstring(pytext
, globals, locals, file, modname
)
606 if indent
== 1 and globals[classname
] is not klass
:
607 # update the class in place
608 klass
.__dict
__.update(globals[classname
].__dict
__)
609 globals[classname
] = klass
611 def execstring(self
, pytext
, globals, locals, file, modname
):
612 tracebackwindow
.hide()
614 W
.getapplication().refreshwindows()
618 dir = os
.path
.dirname(self
.path
)
619 savedir
= os
.getcwd()
621 sys
.path
.insert(0, dir)
622 self
._scriptDone
= False
623 if sys
.platform
== "darwin":
624 # On MacOSX, MacPython doesn't poll for command-period
625 # (cancel), so to enable the user to cancel a running
626 # script, we have to spawn a thread which does the
627 # polling. It will send a SIGINT to the main thread
628 # (in which the script is running) when the user types
630 from threading
import Thread
631 t
= Thread(target
=self
._userCancelledMonitor
,
632 name
="UserCancelledMonitor")
635 execstring(pytext
, globals, locals, file, self
.debugging
,
636 modname
, self
.profiling
)
638 self
._scriptDone
= True
643 def _userCancelledMonitor(self
):
645 from signal
import SIGINT
646 while not self
._scriptDone
:
647 if Evt
.CheckEventQueueForUserCancel():
648 # Send a SIGINT signal to ourselves.
649 # This gets delivered to the main thread,
650 # cancelling the running script.
651 os
.kill(os
.getpid(), SIGINT
)
655 def getenvironment(self
):
658 dir = os
.path
.dirname(file)
659 # check if we're part of a package
661 while os
.path
.exists(os
.path
.join(dir, "__init__.py")):
662 dir, dirname
= os
.path
.split(dir)
663 modname
= dirname
+ '.' + modname
664 subname
= _filename_as_modname(self
.title
)
666 return self
.globals, file, None
668 if subname
== "__init__":
669 # strip trailing period
670 modname
= modname
[:-1]
672 modname
= modname
+ subname
675 if sys
.modules
.has_key(modname
):
676 globals = sys
.modules
[modname
].__dict
__
679 globals = self
.globals
682 file = '<%s>' % self
.title
683 globals = self
.globals
685 return globals, file, modname
687 def write(self
, stuff
):
688 """for use as stdout"""
689 self
._buf
= self
._buf
+ stuff
690 if '\n' in self
._buf
:
694 stuff
= string
.split(self
._buf
, '\n')
695 stuff
= string
.join(stuff
, '\r')
696 end
= self
.editgroup
.editor
.ted
.WEGetTextLength()
697 self
.editgroup
.editor
.ted
.WESetSelection(end
, end
)
698 self
.editgroup
.editor
.ted
.WEInsert(stuff
, None, None)
699 self
.editgroup
.editor
.updatescrollbars()
702 #self.wid.SelectWindow()
704 def getclasslist(self
):
705 from string
import find
, strip
706 methodRE
= re
.compile(r
"\r[ \t]+def ")
707 findMethod
= methodRE
.search
708 editor
= self
.editgroup
.editor
716 if text
[:4] == 'def ':
717 append((pos
+ 4, functag
))
720 pos
= find(text
, '\rdef ', pos
+ 1)
723 append((pos
+ 5, functag
))
725 if text
[:6] == 'class ':
726 append((pos
+ 6, classtag
))
729 pos
= find(text
, '\rclass ', pos
+ 1)
732 append((pos
+ 7, classtag
))
735 m
= findMethod(text
, pos
+ 1)
739 #pos = find(text, '\r\tdef ', pos + 1)
740 append((m
.regs
[0][1], methodtag
))
743 methodlistappend
= None
744 offsetToLine
= editor
.ted
.WEOffsetToLine
745 getLineRange
= editor
.ted
.WEGetLineRange
746 append
= classlist
.append
747 for pos
, tag
in list:
748 lineno
= offsetToLine(pos
)
749 lineStart
, lineEnd
= getLineRange(lineno
)
750 line
= strip(text
[pos
:lineEnd
])
751 line
= line
[:identifieRE_match(line
)]
753 append(("def " + line
, lineno
+ 1))
754 methodlistappend
= None
755 elif tag
is classtag
:
756 append(["class " + line
])
757 methodlistappend
= classlist
[-1].append
758 elif methodlistappend
and tag
is methodtag
:
759 methodlistappend(("def " + line
, lineno
+ 1))
762 def popselectline(self
, lineno
):
763 self
.editgroup
.editor
.selectline(lineno
- 1)
765 def selectline(self
, lineno
, charoffset
= 0):
766 self
.editgroup
.editor
.selectline(lineno
- 1, charoffset
)
768 def addrecentfile(self
, filename
):
769 app
= W
.getapplication()
770 app
.addrecentfile(filename
)
774 def __init__(self
, creator
, eoln
):
777 self
.w
= w
= W
.ModalDialog((260, 160), 'Save options')
779 w
.label
= W
.TextBox((8, 8, 80, 18), "File creator:")
780 w
.ide_radio
= W
.RadioButton((8, 22, 160, 18), "This application", radiobuttons
, self
.ide_hit
)
781 w
.interp_radio
= W
.RadioButton((8, 42, 160, 18), "MacPython Interpreter", radiobuttons
, self
.interp_hit
)
782 w
.interpx_radio
= W
.RadioButton((8, 62, 160, 18), "OSX PythonW Interpreter", radiobuttons
, self
.interpx_hit
)
783 w
.other_radio
= W
.RadioButton((8, 82, 50, 18), "Other:", radiobuttons
)
784 w
.other_creator
= W
.EditText((62, 82, 40, 20), creator
, self
.otherselect
)
785 w
.none_radio
= W
.RadioButton((8, 102, 160, 18), "None", radiobuttons
, self
.none_hit
)
786 w
.cancelbutton
= W
.Button((-180, -30, 80, 16), "Cancel", self
.cancelbuttonhit
)
787 w
.okbutton
= W
.Button((-90, -30, 80, 16), "Done", self
.okbuttonhit
)
788 w
.setdefaultbutton(w
.okbutton
)
789 if creator
== 'Pyth':
790 w
.interp_radio
.set(1)
791 elif creator
== W
._signature
:
793 elif creator
== 'PytX':
794 w
.interpx_radio
.set(1)
795 elif creator
== '\0\0\0\0':
800 w
.eolnlabel
= W
.TextBox((168, 8, 80, 18), "Newline style:")
802 w
.unix_radio
= W
.RadioButton((168, 22, 80, 18), "Unix", radiobuttons
, self
.unix_hit
)
803 w
.mac_radio
= W
.RadioButton((168, 42, 80, 18), "Macintosh", radiobuttons
, self
.mac_hit
)
804 w
.win_radio
= W
.RadioButton((168, 62, 80, 18), "Windows", radiobuttons
, self
.win_hit
)
805 if self
.eoln
== '\n':
807 elif self
.eoln
== '\r\n':
812 w
.bind("cmd.", w
.cancelbutton
.push
)
816 self
.w
.other_creator
.set(W
._signature
)
818 def interp_hit(self
):
819 self
.w
.other_creator
.set("Pyth")
821 def interpx_hit(self
):
822 self
.w
.other_creator
.set("PytX")
825 self
.w
.other_creator
.set("\0\0\0\0")
827 def otherselect(self
, *args
):
828 sel_from
, sel_to
= self
.w
.other_creator
.getselection()
829 creator
= self
.w
.other_creator
.get()[:4]
830 creator
= creator
+ " " * (4 - len(creator
))
831 self
.w
.other_creator
.set(creator
)
832 self
.w
.other_creator
.setselection(sel_from
, sel_to
)
833 self
.w
.other_radio
.set(1)
844 def cancelbuttonhit(self
):
847 def okbuttonhit(self
):
848 self
.rv
= (self
.w
.other_creator
.get()[:4], self
.eoln
)
852 def SaveOptions(creator
, eoln
):
853 s
= _saveoptions(creator
, eoln
)
857 def _escape(where
, what
) :
858 return string
.join(string
.split(where
, what
), '\\' + what
)
860 def _makewholewordpattern(word
):
861 # first, escape special regex chars
862 for esc
in "\\[]()|.*^+$?":
863 word
= _escape(word
, esc
)
864 notwordcharspat
= '[^' + _wordchars
+ ']'
865 pattern
= '(' + word
+ ')'
866 if word
[0] in _wordchars
:
867 pattern
= notwordcharspat
+ pattern
868 if word
[-1] in _wordchars
:
869 pattern
= pattern
+ notwordcharspat
870 return re
.compile(pattern
)
878 self
.parms
= { "find": "",
885 prefs
= MacPrefs
.GetPrefs(W
.getapplication().preffilepath
)
886 if prefs
.searchengine
:
887 self
.parms
["casesens"] = prefs
.searchengine
.casesens
888 self
.parms
["wrap"] = prefs
.searchengine
.wrap
889 self
.parms
["wholeword"] = prefs
.searchengine
.wholeword
894 self
.w
.wid
.ShowWindow()
895 self
.w
.wid
.SelectWindow()
896 self
.w
.find
.edit
.select(1)
897 self
.w
.find
.edit
.selectall()
899 self
.w
= W
.Dialog((420, 150), "Find")
901 self
.w
.find
= TitledEditText((10, 4, 300, 36), "Search for:")
902 self
.w
.replace
= TitledEditText((10, 100, 300, 36), "Replace with:")
904 self
.w
.boxes
= W
.Group((10, 50, 300, 40))
905 self
.w
.boxes
.casesens
= W
.CheckBox((0, 0, 100, 16), "Case sensitive")
906 self
.w
.boxes
.wholeword
= W
.CheckBox((0, 20, 100, 16), "Whole word")
907 self
.w
.boxes
.wrap
= W
.CheckBox((110, 0, 100, 16), "Wrap around")
909 self
.buttons
= [ ("Find", "cmdf", self
.find
),
910 ("Replace", "cmdr", self
.replace
),
911 ("Replace all", None, self
.replaceall
),
912 ("Don't find", "cmdd", self
.dont
),
913 ("Cancel", "cmd.", self
.cancel
)
915 for i
in range(len(self
.buttons
)):
916 bounds
= -90, 22 + i
* 24, 80, 16
917 title
, shortcut
, callback
= self
.buttons
[i
]
918 self
.w
[title
] = W
.Button(bounds
, title
, callback
)
920 self
.w
.bind(shortcut
, self
.w
[title
].push
)
921 self
.w
.setdefaultbutton(self
.w
["Don't find"])
922 self
.w
.find
.edit
.bind("<key>", self
.key
)
923 self
.w
.bind("<activate>", self
.activate
)
924 self
.w
.bind("<close>", self
.close
)
927 self
.w
.find
.edit
.select(1)
928 self
.w
.find
.edit
.selectall()
935 def key(self
, char
, modifiers
):
936 self
.w
.find
.edit
.key(char
, modifiers
)
940 def activate(self
, onoff
):
944 def checkbuttons(self
):
945 editor
= findeditor(self
)
947 if self
.w
.find
.get():
948 for title
, cmd
, call
in self
.buttons
[:-2]:
949 self
.w
[title
].enable(1)
950 self
.w
.setdefaultbutton(self
.w
["Find"])
952 for title
, cmd
, call
in self
.buttons
[:-2]:
953 self
.w
[title
].enable(0)
954 self
.w
.setdefaultbutton(self
.w
["Don't find"])
956 for title
, cmd
, call
in self
.buttons
[:-2]:
957 self
.w
[title
].enable(0)
958 self
.w
.setdefaultbutton(self
.w
["Don't find"])
961 self
.getparmsfromwindow()
966 editor
= findeditor(self
)
970 self
.getparmsfromwindow()
971 text
= editor
.getselectedtext()
972 find
= self
.parms
["find"]
973 if not self
.parms
["casesens"]:
974 find
= string
.lower(find
)
975 text
= string
.lower(text
)
978 editor
.insert(self
.parms
["replace"])
980 def replaceall(self
):
981 editor
= findeditor(self
)
985 self
.getparmsfromwindow()
987 find
= self
.parms
["find"]
991 replace
= self
.parms
["replace"]
992 replacelen
= len(replace
)
994 if not self
.parms
["casesens"]:
995 find
= string
.lower(find
)
996 text
= string
.lower(Text
)
1003 if self
.parms
["wholeword"]:
1004 wholewordRE
= _makewholewordpattern(find
)
1005 match
= wholewordRE
.search(text
, pos
)
1007 pos
= match
.start(1)
1011 pos
= string
.find(text
, find
, pos
)
1014 counter
= counter
+ 1
1015 text
= text
[:pos
] + replace
+ text
[pos
+ findlen
:]
1016 Text
= Text
[:pos
] + replace
+ Text
[pos
+ findlen
:]
1017 pos
= pos
+ replacelen
1018 W
.SetCursor("arrow")
1021 from Carbon
import Res
1022 editor
.textchanged()
1023 editor
.selectionchanged()
1025 EasyDialogs
.Message("Replaced %d occurrences" % counter
)
1028 self
.getparmsfromwindow()
1031 def replacefind(self
):
1035 def setfindstring(self
):
1036 editor
= findeditor(self
)
1039 find
= editor
.getselectedtext()
1042 self
.parms
["find"] = find
1044 self
.w
.find
.edit
.set(self
.parms
["find"])
1045 self
.w
.find
.edit
.selectall()
1048 editor
= findeditor(self
)
1051 find
= self
.parms
["find"]
1055 if not self
.parms
["casesens"]:
1056 find
= string
.lower(find
)
1057 text
= string
.lower(text
)
1058 selstart
, selend
= editor
.getselection()
1059 selstart
, selend
= min(selstart
, selend
), max(selstart
, selend
)
1060 if self
.parms
["wholeword"]:
1061 wholewordRE
= _makewholewordpattern(find
)
1062 match
= wholewordRE
.search(text
, selend
)
1064 pos
= match
.start(1)
1068 pos
= string
.find(text
, find
, selend
)
1070 editor
.setselection(pos
, pos
+ len(find
))
1072 elif self
.parms
["wrap"]:
1073 if self
.parms
["wholeword"]:
1074 match
= wholewordRE
.search(text
, 0)
1076 pos
= match
.start(1)
1080 pos
= string
.find(text
, find
)
1081 if selstart
> pos
>= 0:
1082 editor
.setselection(pos
, pos
+ len(find
))
1086 for key
, value
in self
.parms
.items():
1088 self
.w
[key
].set(value
)
1090 self
.w
.boxes
[key
].set(value
)
1092 def getparmsfromwindow(self
):
1095 for key
, value
in self
.parms
.items():
1097 value
= self
.w
[key
].get()
1099 value
= self
.w
.boxes
[key
].get()
1100 self
.parms
[key
] = value
1108 self
.w
.wid
.HideWindow()
1111 def writeprefs(self
):
1113 self
.getparmsfromwindow()
1114 prefs
= MacPrefs
.GetPrefs(W
.getapplication().preffilepath
)
1115 prefs
.searchengine
.casesens
= self
.parms
["casesens"]
1116 prefs
.searchengine
.wrap
= self
.parms
["wrap"]
1117 prefs
.searchengine
.wholeword
= self
.parms
["wholeword"]
1121 class TitledEditText(W
.Group
):
1123 def __init__(self
, possize
, title
, text
= ""):
1124 W
.Group
.__init
__(self
, possize
)
1125 self
.title
= W
.TextBox((0, 0, 0, 16), title
)
1126 self
.edit
= W
.EditText((0, 16, 0, 0), text
)
1128 def set(self
, value
):
1129 self
.edit
.set(value
)
1132 return self
.edit
.get()
1135 class ClassFinder(W
.PopupWidget
):
1137 def click(self
, point
, modifiers
):
1138 W
.SetCursor("watch")
1139 self
.set(self
._parentwindow
.getclasslist())
1140 W
.PopupWidget
.click(self
, point
, modifiers
)
1143 def getminindent(lines
):
1146 stripped
= string
.strip(line
)
1147 if not stripped
or stripped
[0] == '#':
1149 if indent
< 0 or line
[:indent
] <> indent
* '\t':
1159 return not not ord(Evt
.GetKeys()[7]) & 0x04
1162 def execstring(pytext
, globals, locals, filename
="<string>", debugging
=0,
1163 modname
="__main__", profiling
=0):
1165 import PyDebugger
, bdb
1166 BdbQuit
= bdb
.BdbQuit
1168 BdbQuit
= 'BdbQuitDummyException'
1169 pytext
= string
.split(pytext
, '\r')
1170 pytext
= string
.join(pytext
, '\n') + '\n'
1171 W
.SetCursor("watch")
1172 globals['__name__'] = modname
1173 globals['__file__'] = filename
1174 sys
.argv
= [filename
]
1176 code
= compile(pytext
, filename
, "exec")
1178 # XXXX BAAAADDD.... We let tracebackwindow decide to treat SyntaxError
1179 # special. That's wrong because THIS case is special (could be literal
1180 # overflow!) and SyntaxError could mean we need a traceback (syntax error
1181 # in imported module!!!
1182 tracebackwindow
.traceback(1, filename
)
1186 PyDebugger
.startfromhere()
1188 if hasattr(MacOS
, 'EnableAppswitch'):
1189 MacOS
.EnableAppswitch(0)
1192 import profile
, ProfileBrowser
1193 p
= profile
.Profile()
1196 p
.runctx(code
, globals, locals)
1200 stats
= pstats
.Stats(p
)
1201 ProfileBrowser
.ProfileBrowser(stats
)
1203 exec code
in globals, locals
1205 if hasattr(MacOS
, 'EnableAppswitch'):
1206 MacOS
.EnableAppswitch(-1)
1207 except W
.AlertError
, detail
:
1208 raise W
.AlertError
, detail
1209 except (KeyboardInterrupt, BdbQuit
):
1211 except SystemExit, arg
:
1213 sys
.stderr
.write("Script exited with status code: %s\n" % repr(arg
.code
))
1217 PyDebugger
.postmortem(sys
.exc_type
, sys
.exc_value
, sys
.exc_traceback
)
1220 tracebackwindow
.traceback(1, filename
)
1226 _identifieRE
= re
.compile(r
"[A-Za-z_][A-Za-z_0-9]*")
1228 def identifieRE_match(str):
1229 match
= _identifieRE
.match(str)
1234 def _filename_as_modname(fname
):
1235 if fname
[-3:] == '.py':
1236 modname
= fname
[:-3]
1237 match
= _identifieRE
.match(modname
)
1238 if match
and match
.start() == 0 and match
.end() == len(modname
):
1239 return string
.join(string
.split(modname
, '.'), '_')
1241 def findeditor(topwindow
, fromtop
= 0):
1242 wid
= MyFrontWindow()
1244 if topwindow
.w
and wid
== topwindow
.w
.wid
:
1245 wid
= topwindow
.w
.wid
.GetNextWindow()
1248 app
= W
.getapplication()
1249 if app
._windows
.has_key(wid
): # KeyError otherwise can happen in RoboFog :-(
1250 window
= W
.getapplication()._windows
[wid
]
1253 if not isinstance(window
, Editor
):
1255 return window
.editgroup
.editor
1258 class _EditorDefaultSettings
:
1261 self
.template
= "%s, %d point"
1262 self
.fontsettings
, self
.tabsettings
, self
.windowsize
= geteditorprefs()
1263 self
.w
= W
.Dialog((328, 120), "Editor default settings")
1264 self
.w
.setfontbutton
= W
.Button((8, 8, 80, 16), "Set font\xc9", self
.dofont
)
1265 self
.w
.fonttext
= W
.TextBox((98, 10, -8, 14), self
.template
% (self
.fontsettings
[0], self
.fontsettings
[2]))
1267 self
.w
.picksizebutton
= W
.Button((8, 50, 80, 16), "Front window", self
.picksize
)
1268 self
.w
.xsizelabel
= W
.TextBox((98, 32, 40, 14), "Width:")
1269 self
.w
.ysizelabel
= W
.TextBox((148, 32, 40, 14), "Height:")
1270 self
.w
.xsize
= W
.EditText((98, 48, 40, 20), `self
.windowsize
[0]`
)
1271 self
.w
.ysize
= W
.EditText((148, 48, 40, 20), `self
.windowsize
[1]`
)
1273 self
.w
.cancelbutton
= W
.Button((-180, -26, 80, 16), "Cancel", self
.cancel
)
1274 self
.w
.okbutton
= W
.Button((-90, -26, 80, 16), "Done", self
.ok
)
1275 self
.w
.setdefaultbutton(self
.w
.okbutton
)
1276 self
.w
.bind('cmd.', self
.w
.cancelbutton
.push
)
1280 app
= W
.getapplication()
1281 editor
= findeditor(self
)
1282 if editor
is not None:
1283 width
, height
= editor
._parentwindow
._bounds
[2:]
1284 self
.w
.xsize
.set(`width`
)
1285 self
.w
.ysize
.set(`height`
)
1287 raise W
.AlertError
, "No edit window found"
1291 settings
= FontSettings
.FontDialog(self
.fontsettings
, self
.tabsettings
)
1293 self
.fontsettings
, self
.tabsettings
= settings
1294 sys
.exc_traceback
= None
1295 self
.w
.fonttext
.set(self
.template
% (self
.fontsettings
[0], self
.fontsettings
[2]))
1306 width
= string
.atoi(self
.w
.xsize
.get())
1308 self
.w
.xsize
.select(1)
1309 self
.w
.xsize
.selectall()
1310 raise W
.AlertError
, "Bad number for window width"
1312 height
= string
.atoi(self
.w
.ysize
.get())
1314 self
.w
.ysize
.select(1)
1315 self
.w
.ysize
.selectall()
1316 raise W
.AlertError
, "Bad number for window height"
1317 self
.windowsize
= width
, height
1318 seteditorprefs(self
.fontsettings
, self
.tabsettings
, self
.windowsize
)
1321 def geteditorprefs():
1323 prefs
= MacPrefs
.GetPrefs(W
.getapplication().preffilepath
)
1325 fontsettings
= prefs
.pyedit
.fontsettings
1326 tabsettings
= prefs
.pyedit
.tabsettings
1327 windowsize
= prefs
.pyedit
.windowsize
1329 fontsettings
= prefs
.pyedit
.fontsettings
= ("Geneva", 0, 10, (0, 0, 0))
1330 tabsettings
= prefs
.pyedit
.tabsettings
= (8, 1)
1331 windowsize
= prefs
.pyedit
.windowsize
= (500, 250)
1332 sys
.exc_traceback
= None
1333 return fontsettings
, tabsettings
, windowsize
1335 def seteditorprefs(fontsettings
, tabsettings
, windowsize
):
1337 prefs
= MacPrefs
.GetPrefs(W
.getapplication().preffilepath
)
1338 prefs
.pyedit
.fontsettings
= fontsettings
1339 prefs
.pyedit
.tabsettings
= tabsettings
1340 prefs
.pyedit
.windowsize
= windowsize
1343 _defaultSettingsEditor
= None
1345 def EditorDefaultSettings():
1346 global _defaultSettingsEditor
1347 if _defaultSettingsEditor
is None or not hasattr(_defaultSettingsEditor
, "w"):
1348 _defaultSettingsEditor
= _EditorDefaultSettings()
1350 _defaultSettingsEditor
.w
.select()
1352 def resolvealiases(path
):
1354 fsr
, d1
, d2
= File
.FSResolveAliasFile(path
, 1)
1355 path
= fsr
.as_pathname()
1357 except (File
.Error
, ValueError), (error
, str):
1360 dir, file = os
.path
.split(path
)
1361 return os
.path
.join(resolvealiases(dir), file)
1363 searchengine
= SearchEngine()
1364 tracebackwindow
= Wtraceback
.TraceBack()