This commit was manufactured by cvs2svn to create tag 'r221c2'.
[python/dscho.git] / Mac / Tools / IDE / Wtext.py
blob86c79c3f03a4bb77652011a9dc49384cf8c3f753
1 from Carbon import Evt, Events, Fm, Fonts
2 from Carbon import Qd, Res, Scrap
3 from Carbon import TE, TextEdit, Win
4 from Carbon import App
5 from Carbon.Appearance import kThemeStateActive, kThemeStateInactive
6 import waste
7 import WASTEconst
8 import Wbase
9 import Wkeys
10 import Wcontrols
11 import PyFontify
12 import string
13 from types import TupleType, StringType
16 class TextBox(Wbase.Widget):
18 """A static text widget"""
20 def __init__(self, possize, text="", align=TextEdit.teJustLeft,
21 fontsettings=None,
22 backgroundcolor=(0xffff, 0xffff, 0xffff)
24 if fontsettings is None:
25 import W
26 fontsettings = W.getdefaultfont()
27 Wbase.Widget.__init__(self, possize)
28 self.fontsettings = fontsettings
29 self.text = text
30 self.align = align
31 self._backgroundcolor = backgroundcolor
33 def draw(self, visRgn = None):
34 if self._visible:
35 (font, style, size, color) = self.fontsettings
36 fontid = GetFNum(font)
37 savestate = Qd.GetPenState()
38 Qd.TextFont(fontid)
39 Qd.TextFace(style)
40 Qd.TextSize(size)
41 Qd.RGBForeColor(color)
42 Qd.RGBBackColor(self._backgroundcolor)
43 TE.TETextBox(self.text, self._bounds, self.align)
44 Qd.RGBBackColor((0xffff, 0xffff, 0xffff))
45 Qd.SetPenState(savestate)
47 def get(self):
48 return self.text
50 def set(self, text):
51 self.text = text
52 if self._parentwindow and self._parentwindow.wid:
53 self.SetPort()
54 self.draw()
57 class _ScrollWidget:
59 # to be overridden
60 def getscrollrects(self):
61 """Return (destrect, viewrect)."""
62 return None, None
64 # internal method
66 def updatescrollbars(self):
67 (dl, dt, dr, db), (vl, vt, vr, vb) = self.getscrollrects()
68 if self._parent._barx:
69 viewwidth = vr - vl
70 destwidth = dr - dl
71 bar = self._parent._barx
72 bar.setmax(destwidth - viewwidth)
74 # MacOS 8.1 doesn't automatically disable
75 # scrollbars whose max <= min
76 bar.enable(destwidth > viewwidth)
78 bar.setviewsize(viewwidth)
79 bar.set(vl - dl)
80 if self._parent._bary:
81 viewheight = vb - vt
82 destheight = db - dt
83 bar = self._parent._bary
84 bar.setmax(destheight - viewheight)
86 # MacOS 8.1 doesn't automatically disable
87 # scrollbars whose max <= min
88 bar.enable(destheight > viewheight)
90 bar.setviewsize(viewheight)
91 bar.set(vt - dt)
94 UNDOLABELS = [ # Indexed by WEGetUndoInfo() value
95 None, "", "typing", "Cut", "Paste", "Clear", "Drag", "Style",
96 "Ruler", "backspace", "delete", "transform", "resize"]
99 class EditText(Wbase.SelectableWidget, _ScrollWidget):
101 """A text edit widget, mainly for simple entry fields."""
103 def __init__(self, possize, text="",
104 callback=None, inset=(3, 3),
105 fontsettings=None,
106 tabsettings = (32, 0),
107 readonly = 0):
108 if fontsettings is None:
109 import W
110 fontsettings = W.getdefaultfont()
111 Wbase.SelectableWidget.__init__(self, possize)
112 self.temptext = text
113 self.ted = None
114 self.selection = None
115 self.oldselection = None
116 self._callback = callback
117 self.changed = 0
118 self.selchanged = 0
119 self._selected = 0
120 self._enabled = 1
121 self.wrap = 1
122 self.readonly = readonly
123 self.fontsettings = fontsettings
124 self.tabsettings = tabsettings
125 if type(inset) <> TupleType:
126 self.inset = (inset, inset)
127 else:
128 self.inset = inset
130 def open(self):
131 if not hasattr(self._parent, "_barx"):
132 self._parent._barx = None
133 if not hasattr(self._parent, "_bary"):
134 self._parent._bary = None
135 self._calcbounds()
136 self.SetPort()
137 viewrect, destrect = self._calctextbounds()
138 flags = self._getflags()
139 self.ted = waste.WENew(destrect, viewrect, flags)
140 self.ted.WEInstallTabHooks()
141 self.ted.WESetAlignment(WASTEconst.weFlushLeft)
142 self.setfontsettings(self.fontsettings)
143 self.settabsettings(self.tabsettings)
144 self.ted.WEUseText(Res.Resource(self.temptext))
145 self.ted.WECalText()
146 if self.selection:
147 self.setselection(self.selection[0], self.selection[1])
148 self.selection = None
149 else:
150 self.selview()
151 self.temptext = None
152 self.updatescrollbars()
153 self.bind("pageup", self.scrollpageup)
154 self.bind("pagedown", self.scrollpagedown)
155 self.bind("top", self.scrolltop)
156 self.bind("bottom", self.scrollbottom)
157 self.selchanged = 0
159 def close(self):
160 self._parent._barx = None
161 self._parent._bary = None
162 self.ted = None
163 self.temptext = None
164 Wbase.SelectableWidget.close(self)
166 def textchanged(self, all=0):
167 self.changed = 1
169 def selectionchanged(self):
170 self.selchanged = 1
171 self.oldselection = self.getselection()
173 def gettabsettings(self):
174 return self.tabsettings
176 def settabsettings(self, (tabsize, tabmode)):
177 self.tabsettings = (tabsize, tabmode)
178 if hasattr(self.ted, "WESetTabSize"):
179 port = self._parentwindow.wid.GetWindowPort()
180 if tabmode:
181 (font, style, size, color) = self.getfontsettings()
182 savesettings = GetPortFontSettings(port)
183 SetPortFontSettings(port, (font, style, size))
184 tabsize = Qd.StringWidth(' ' * tabsize)
185 SetPortFontSettings(port, savesettings)
186 tabsize = max(tabsize, 1)
187 self.ted.WESetTabSize(tabsize)
188 self.SetPort()
189 Qd.EraseRect(self.ted.WEGetViewRect())
190 self.ted.WEUpdate(port.visRgn)
192 def getfontsettings(self):
193 from Carbon import Res
194 (font, style, size, color) = self.ted.WEGetRunInfo(0)[4]
195 font = Fm.GetFontName(font)
196 return (font, style, size, color)
198 def setfontsettings(self, (font, style, size, color)):
199 self.SetPort()
200 if type(font) <> StringType:
201 font = Fm.GetFontName(font)
202 self.fontsettings = (font, style, size, color)
203 fontid = GetFNum(font)
204 readonly = self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, -1)
205 if readonly:
206 self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 0)
207 try:
208 self.ted.WEFeatureFlag(WASTEconst.weFInhibitRecal, 1)
209 selstart, selend = self.ted.WEGetSelection()
210 self.ted.WESetSelection(0, self.ted.WEGetTextLength())
211 self.ted.WESetStyle(WASTEconst.weDoFace, (0, 0, 0, (0, 0, 0)))
212 self.ted.WESetStyle(WASTEconst.weDoFace |
213 WASTEconst.weDoColor |
214 WASTEconst.weDoFont |
215 WASTEconst.weDoSize,
216 (fontid, style, size, color))
217 self.ted.WEFeatureFlag(WASTEconst.weFInhibitRecal, 0)
218 self.ted.WECalText()
219 self.ted.WESetSelection(selstart, selend)
220 finally:
221 if readonly:
222 self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 1)
223 viewrect = self.ted.WEGetViewRect()
224 Qd.EraseRect(viewrect)
225 self.ted.WEUpdate(self._parentwindow.wid.GetWindowPort().visRgn)
226 self.selectionchanged()
227 self.updatescrollbars()
229 def adjust(self, oldbounds):
230 self.SetPort()
231 # Note: if App.DrawThemeEditTextFrame is ever used, it will be necessary
232 # to unconditionally outset the invalidated rectangles, since Appearance
233 # frames are drawn outside the bounds.
234 if self._selected and self._parentwindow._hasselframes:
235 self.GetWindow().InvalWindowRect(Qd.InsetRect(oldbounds, -3, -3))
236 self.GetWindow().InvalWindowRect(Qd.InsetRect(self._bounds, -3, -3))
237 else:
238 self.GetWindow().InvalWindowRect(oldbounds)
239 self.GetWindow().InvalWindowRect(self._bounds)
240 viewrect, destrect = self._calctextbounds()
241 self.ted.WESetViewRect(viewrect)
242 self.ted.WESetDestRect(destrect)
243 if self.wrap:
244 self.ted.WECalText()
245 if self.ted.WEGetDestRect()[3] < viewrect[1]:
246 self.selview()
247 self.updatescrollbars()
249 # interface -----------------------
250 # selection stuff
251 def selview(self):
252 self.ted.WESelView()
254 def selectall(self):
255 self.ted.WESetSelection(0, self.ted.WEGetTextLength())
256 self.selectionchanged()
257 self.updatescrollbars()
259 def selectline(self, lineno, charoffset = 0):
260 newselstart, newselend = self.ted.WEGetLineRange(lineno)
261 # Autoscroll makes the *end* of the selection visible, which,
262 # in the case of a whole line, is the beginning of the *next* line.
263 # So sometimes it leaves our line just above the view rect.
264 # Let's fool Waste by initially selecting one char less:
265 self.ted.WESetSelection(newselstart + charoffset, newselend-1)
266 self.ted.WESetSelection(newselstart + charoffset, newselend)
267 self.selectionchanged()
268 self.updatescrollbars()
270 def getselection(self):
271 if self.ted:
272 return self.ted.WEGetSelection()
273 else:
274 return self.selection
276 def setselection(self, selstart, selend):
277 self.selectionchanged()
278 if self.ted:
279 self.ted.WESetSelection(selstart, selend)
280 self.ted.WESelView()
281 self.updatescrollbars()
282 else:
283 self.selection = selstart, selend
285 def offsettoline(self, offset):
286 return self.ted.WEOffsetToLine(offset)
288 def countlines(self):
289 return self.ted.WECountLines()
291 def getselectedtext(self):
292 selstart, selend = self.ted.WEGetSelection()
293 return self.ted.WEGetText().data[selstart:selend]
295 def expandselection(self):
296 oldselstart, oldselend = self.ted.WEGetSelection()
297 selstart, selend = min(oldselstart, oldselend), max(oldselstart, oldselend)
298 if selstart <> selend and chr(self.ted.WEGetChar(selend-1)) == '\r':
299 selend = selend - 1
300 newselstart, dummy = self.ted.WEFindLine(selstart, 1)
301 dummy, newselend = self.ted.WEFindLine(selend, 1)
302 if oldselstart <> newselstart or oldselend <> newselend:
303 self.ted.WESetSelection(newselstart, newselend)
304 self.updatescrollbars()
305 self.selectionchanged()
307 def insert(self, text):
308 self.ted.WEInsert(text, None, None)
309 self.textchanged()
310 self.selectionchanged()
312 # text
313 def set(self, text):
314 if not self.ted:
315 self.temptext = text
316 else:
317 self.ted.WEUseText(Res.Resource(text))
318 self.ted.WECalText()
319 self.SetPort()
320 viewrect, destrect = self._calctextbounds()
321 self.ted.WESetViewRect(viewrect)
322 self.ted.WESetDestRect(destrect)
323 rgn = Qd.NewRgn()
324 Qd.RectRgn(rgn, viewrect)
325 Qd.EraseRect(viewrect)
326 self.draw(rgn)
327 self.updatescrollbars()
328 self.textchanged(1)
330 def get(self):
331 if not self._parent:
332 return self.temptext
333 else:
334 return self.ted.WEGetText().data
336 # events
337 def key(self, char, event):
338 (what, message, when, where, modifiers) = event
339 if self._enabled and not modifiers & Events.cmdKey or char in Wkeys.arrowkeys:
340 self.ted.WEKey(ord(char), modifiers)
341 if char not in Wkeys.navigationkeys:
342 self.textchanged()
343 if char not in Wkeys.scrollkeys:
344 self.selectionchanged()
345 self.updatescrollbars()
346 if self._callback:
347 Wbase.CallbackCall(self._callback, 0, char, modifiers)
349 def click(self, point, modifiers):
350 if not self._enabled:
351 return
352 self.ted.WEClick(point, modifiers, Evt.TickCount())
353 self.selectionchanged()
354 self.updatescrollbars()
355 return 1
357 def idle(self):
358 self.SetPort()
359 self.ted.WEIdle()
361 def rollover(self, point, onoff):
362 if onoff:
363 Wbase.SetCursor("iBeam")
365 def activate(self, onoff):
366 self._activated = onoff
367 if self._visible:
368 self.SetPort()
370 # DISABLED! There are too many places where it is assumed that
371 # the frame of an EditText item is 1 pixel, inside the bounds.
372 #state = [kThemeStateActive, kThemeStateInactive][not onoff]
373 #App.DrawThemeEditTextFrame(Qd.InsetRect(self._bounds, 1, 1), state)
375 if self._selected:
376 if onoff:
377 self.ted.WEActivate()
378 else:
379 self.ted.WEDeactivate()
380 self.drawselframe(onoff)
382 def select(self, onoff, isclick = 0):
383 if Wbase.SelectableWidget.select(self, onoff):
384 return
385 self.SetPort()
386 if onoff:
387 self.ted.WEActivate()
388 if self._parentwindow._tabbable and not isclick:
389 self.selectall()
390 else:
391 self.ted.WEDeactivate()
392 self.drawselframe(onoff)
394 def draw(self, visRgn = None):
395 if self._visible:
396 if not visRgn:
397 visRgn = self._parentwindow.wid.GetWindowPort().visRgn
398 self.ted.WEUpdate(visRgn)
400 # DISABLED! There are too many places where it is assumed that
401 # the frame of an EditText item is 1 pixel, inside the bounds.
402 #state = [kThemeStateActive, kThemeStateInactive][not self._activated]
403 #App.DrawThemeEditTextFrame(Qd.InsetRect(self._bounds, 1, 1), state)
404 Qd.FrameRect(self._bounds)
406 if self._selected and self._activated:
407 self.drawselframe(1)
409 # scrolling
410 def scrollpageup(self):
411 if self._parent._bary and self._parent._bary._enabled:
412 self.vscroll("++")
414 def scrollpagedown(self):
415 if self._parent._bary and self._parent._bary._enabled:
416 self.vscroll("--")
418 def scrolltop(self):
419 if self._parent._bary and self._parent._bary._enabled:
420 self.vscroll(self._parent._bary.getmin())
421 if self._parent._barx and self._parent._barx._enabled:
422 self.hscroll(self._parent._barx.getmin())
424 def scrollbottom(self):
425 if self._parent._bary and self._parent._bary._enabled:
426 self.vscroll(self._parent._bary.getmax())
428 # menu handlers
429 def domenu_copy(self, *args):
430 selbegin, selend = self.ted.WEGetSelection()
431 if selbegin == selend:
432 return
433 if hasattr(Scrap, 'ZeroScrap'):
434 Scrap.ZeroScrap()
435 else:
436 Scrap.ClearCurrentScrap()
437 self.ted.WECopy()
438 self.updatescrollbars()
440 def domenu_cut(self, *args):
441 selbegin, selend = self.ted.WEGetSelection()
442 if selbegin == selend:
443 return
444 if hasattr(Scrap, 'ZeroScrap'):
445 Scrap.ZeroScrap()
446 else:
447 Scrap.ClearCurrentScrap()
448 self.ted.WECut()
449 self.updatescrollbars()
450 self.selview()
451 self.textchanged()
452 self.selectionchanged()
453 if self._callback:
454 Wbase.CallbackCall(self._callback, 0, "", None)
456 def domenu_paste(self, *args):
457 if not self.ted.WECanPaste():
458 return
459 self.selview()
460 self.ted.WEPaste()
461 self.updatescrollbars()
462 self.textchanged()
463 self.selectionchanged()
464 if self._callback:
465 Wbase.CallbackCall(self._callback, 0, "", None)
467 def domenu_clear(self, *args):
468 self.ted.WEDelete()
469 self.selview()
470 self.updatescrollbars()
471 self.textchanged()
472 self.selectionchanged()
473 if self._callback:
474 Wbase.CallbackCall(self._callback, 0, "", None)
476 def domenu_undo(self, *args):
477 which, redo = self.ted.WEGetUndoInfo()
478 if not which:
479 return
480 self.ted.WEUndo()
481 self.updatescrollbars()
482 self.textchanged()
483 self.selectionchanged()
484 if self._callback:
485 Wbase.CallbackCall(self._callback, 0, "", None)
487 def can_undo(self, menuitem):
488 #doundo = self.ted.WEFeatureFlag(WASTEconst.weFUndo, -1)
489 #print doundo
490 #if not doundo:
491 # return 0
492 which, redo = self.ted.WEGetUndoInfo()
493 if which < len(UNDOLABELS):
494 which = UNDOLABELS[which]
495 else:
496 which = ""
497 if which == None:
498 return None
499 if redo:
500 which = "Redo "+which
501 else:
502 which = "Undo "+which
503 menuitem.settext(which)
504 return 1
506 def domenu_selectall(self, *args):
507 self.selectall()
509 # private
510 def getscrollrects(self):
511 return self.ted.WEGetDestRect(), self.ted.WEGetViewRect()
513 def vscroll(self, value):
514 lineheight = self.ted.WEGetHeight(0, 1)
515 dr = self.ted.WEGetDestRect()
516 vr = self.ted.WEGetViewRect()
517 viewheight = vr[3] - vr[1]
518 maxdelta = vr[1] - dr[1]
519 mindelta = vr[3] - dr[3]
520 if value == "+":
521 delta = lineheight
522 elif value == "-":
523 delta = - lineheight
524 elif value == "++":
525 delta = viewheight - lineheight
526 elif value == "--":
527 delta = lineheight - viewheight
528 else: # in thumb
529 delta = vr[1] - dr[1] - value
530 delta = min(maxdelta, delta)
531 delta = max(mindelta, delta)
532 self.ted.WEScroll(0, delta)
533 self.updatescrollbars()
535 def hscroll(self, value):
536 dr = self.ted.WEGetDestRect()
537 vr = self.ted.WEGetViewRect()
538 destwidth = dr[2] - dr[0]
539 viewwidth = vr[2] - vr[0]
540 viewoffset = maxdelta = vr[0] - dr[0]
541 mindelta = vr[2] - dr[2]
542 if value == "+":
543 delta = 32
544 elif value == "-":
545 delta = - 32
546 elif value == "++":
547 delta = 0.5 * (vr[2] - vr[0])
548 elif value == "--":
549 delta = 0.5 * (vr[0] - vr[2])
550 else: # in thumb
551 delta = vr[0] - dr[0] - value
552 #cur = (32767 * viewoffset) / (destwidth - viewwidth)
553 #delta = (cur-value)*(destwidth - viewwidth)/32767
554 #if abs(delta - viewoffset) <=2:
555 # # compensate for irritating rounding error
556 # delta = viewoffset
557 delta = min(maxdelta, delta)
558 delta = max(mindelta, delta)
559 self.ted.WEScroll(delta, 0)
560 self.updatescrollbars()
562 # some internals
563 def _getflags(self):
564 flags = WASTEconst.weDoAutoScroll | WASTEconst.weDoMonoStyled
565 if self.readonly:
566 flags = flags | WASTEconst.weDoReadOnly
567 else:
568 flags = flags | WASTEconst.weDoUndo
569 return flags
571 def _getviewrect(self):
572 return Qd.InsetRect(self._bounds, self.inset[0], self.inset[1])
574 def _calctextbounds(self):
575 viewrect = l, t, r, b = self._getviewrect()
576 if self.ted:
577 dl, dt, dr, db = self.ted.WEGetDestRect()
578 vl, vt, vr, vb = self.ted.WEGetViewRect()
579 yshift = t - vt
580 if (db - dt) < (b - t):
581 destrect = viewrect
582 else:
583 destrect = l, dt + yshift, r, db + yshift
584 else:
585 destrect = viewrect
586 return viewrect, destrect
589 class TextEditor(EditText):
591 """A text edit widget."""
593 def __init__(self, possize, text="", callback=None, wrap=1, inset=(4, 4),
594 fontsettings=None,
595 tabsettings=(32, 0),
596 readonly=0):
597 EditText.__init__(self, possize, text, callback, inset, fontsettings, tabsettings, readonly)
598 self.wrap = wrap
600 def _getflags(self):
601 flags = WASTEconst.weDoAutoScroll | WASTEconst.weDoMonoStyled | \
602 WASTEconst.weDoOutlineHilite
603 if self.readonly:
604 flags = flags | WASTEconst.weDoReadOnly
605 else:
606 flags = flags | WASTEconst.weDoUndo
607 return flags
609 def _getviewrect(self):
610 l, t, r, b = self._bounds
611 return (l + 5, t + 2, r, b - 2)
613 def _calctextbounds(self):
614 if self.wrap:
615 return EditText._calctextbounds(self)
616 else:
617 viewrect = l, t, r, b = self._getviewrect()
618 if self.ted:
619 dl, dt, dr, db = self.ted.WEGetDestRect()
620 vl, vt, vr, vb = self.ted.WEGetViewRect()
621 xshift = l - vl
622 yshift = t - vt
623 if (db - dt) < (b - t):
624 yshift = t - dt
625 destrect = (dl + xshift, dt + yshift, dr + xshift, db + yshift)
626 else:
627 destrect = (l, t, r + 5000, b)
628 return viewrect, destrect
630 def draw(self, visRgn = None):
631 if self._visible:
632 if not visRgn:
633 visRgn = self._parentwindow.wid.GetWindowPort().visRgn
634 self.ted.WEUpdate(visRgn)
635 if self._selected and self._activated:
636 self.drawselframe(1)
638 def activate(self, onoff):
639 self._activated = onoff
640 if self._visible:
641 self.SetPort()
642 # doesn't draw frame, as EditText.activate does
643 if self._selected:
644 if onoff:
645 self.ted.WEActivate()
646 else:
647 self.ted.WEDeactivate()
648 self.drawselframe(onoff)
651 import re
652 commentPat = re.compile("[ \t]*(#)")
653 indentPat = re.compile("[ \t]*")
654 kStringColor = (0, 0x7fff, 0)
655 kCommentColor = (0, 0, 0xb000)
658 class PyEditor(TextEditor):
660 """A specialized Python source edit widget"""
662 def __init__(self, possize, text="", callback=None, inset=(4, 4),
663 fontsettings=None,
664 tabsettings=(32, 0),
665 readonly=0,
666 debugger=None,
667 file=''):
668 TextEditor.__init__(self, possize, text, callback, 0, inset, fontsettings, tabsettings, readonly)
669 self.bind("cmd[", self.domenu_shiftleft)
670 self.bind("cmd]", self.domenu_shiftright)
671 self.bind("cmdshift[", self.domenu_uncomment)
672 self.bind("cmdshift]", self.domenu_comment)
673 self.bind("cmdshiftd", self.alldirty)
674 self.file = file # only for debugger reference
675 self._debugger = debugger
676 if debugger:
677 debugger.register_editor(self, self.file)
678 self._dirty = (0, None)
679 self.do_fontify = 0
681 #def open(self):
682 # TextEditor.open(self)
683 # if self.do_fontify:
684 # self.fontify()
685 # self._dirty = (None, None)
687 def _getflags(self):
688 flags = (WASTEconst.weDoDrawOffscreen | WASTEconst.weDoUseTempMem |
689 WASTEconst.weDoAutoScroll | WASTEconst.weDoOutlineHilite)
690 if self.readonly:
691 flags = flags | WASTEconst.weDoReadOnly
692 else:
693 flags = flags | WASTEconst.weDoUndo
694 return flags
696 def textchanged(self, all=0):
697 self.changed = 1
698 if all:
699 self._dirty = (0, None)
700 return
701 oldsel = self.oldselection
702 sel = self.getselection()
703 if not sel:
704 # XXX what to do?
705 return
706 selstart, selend = sel
707 selstart, selend = min(selstart, selend), max(selstart, selend)
708 if oldsel:
709 oldselstart, oldselend = min(oldsel), max(oldsel)
710 selstart, selend = min(selstart, oldselstart), max(selend, oldselend)
711 startline = self.offsettoline(selstart)
712 endline = self.offsettoline(selend)
713 selstart, _ = self.ted.WEGetLineRange(startline)
714 _, selend = self.ted.WEGetLineRange(endline)
715 if selstart > 0:
716 selstart = selstart - 1
717 self._dirty = (selstart, selend)
719 def idle(self):
720 self.SetPort()
721 self.ted.WEIdle()
722 if not self.do_fontify:
723 return
724 start, end = self._dirty
725 if start is None:
726 return
727 textLength = self.ted.WEGetTextLength()
728 if end is None:
729 end = textLength
730 if start >= end:
731 self._dirty = (None, None)
732 else:
733 self.fontify(start, end)
734 self._dirty = (None, None)
736 def alldirty(self, *args):
737 self._dirty = (0, None)
739 def fontify(self, start=0, end=None):
740 #W.SetCursor('watch')
741 if self.readonly:
742 self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 0)
743 self.ted.WEFeatureFlag(WASTEconst.weFOutlineHilite, 0)
744 self.ted.WEDeactivate()
745 self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 0)
746 self.ted.WEFeatureFlag(WASTEconst.weFUndo, 0)
747 pytext = self.get().replace("\r", "\n")
748 if end is None:
749 end = len(pytext)
750 else:
751 end = min(end, len(pytext))
752 selstart, selend = self.ted.WEGetSelection()
753 self.ted.WESetSelection(start, end)
754 self.ted.WESetStyle(WASTEconst.weDoFace | WASTEconst.weDoColor,
755 (0, 0, 12, (0, 0, 0)))
757 tags = PyFontify.fontify(pytext, start, end)
758 styles = {
759 'string': (WASTEconst.weDoColor, (0, 0, 0, kStringColor)),
760 'keyword': (WASTEconst.weDoFace, (0, 1, 0, (0, 0, 0))),
761 'comment': (WASTEconst.weDoFace | WASTEconst.weDoColor, (0, 0, 0, kCommentColor)),
762 'identifier': (WASTEconst.weDoColor, (0, 0, 0, (0xbfff, 0, 0)))
764 setselection = self.ted.WESetSelection
765 setstyle = self.ted.WESetStyle
766 for tag, start, end, sublist in tags:
767 setselection(start, end)
768 mode, style = styles[tag]
769 setstyle(mode, style)
770 self.ted.WESetSelection(selstart, selend)
771 self.SetPort()
772 self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 1)
773 self.ted.WEFeatureFlag(WASTEconst.weFUndo, 1)
774 self.ted.WEActivate()
775 self.ted.WEFeatureFlag(WASTEconst.weFOutlineHilite, 1)
776 if self.readonly:
777 self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 1)
779 def domenu_shiftleft(self):
780 self.expandselection()
781 selstart, selend = self.ted.WEGetSelection()
782 selstart, selend = min(selstart, selend), max(selstart, selend)
783 snippet = self.getselectedtext()
784 lines = string.split(snippet, '\r')
785 for i in range(len(lines)):
786 if lines[i][:1] == '\t':
787 lines[i] = lines[i][1:]
788 snippet = string.join(lines, '\r')
789 self.insert(snippet)
790 self.ted.WESetSelection(selstart, selstart + len(snippet))
792 def domenu_shiftright(self):
793 self.expandselection()
794 selstart, selend = self.ted.WEGetSelection()
795 selstart, selend = min(selstart, selend), max(selstart, selend)
796 snippet = self.getselectedtext()
797 lines = string.split(snippet, '\r')
798 for i in range(len(lines) - (not lines[-1])):
799 lines[i] = '\t' + lines[i]
800 snippet = string.join(lines, '\r')
801 self.insert(snippet)
802 self.ted.WESetSelection(selstart, selstart + len(snippet))
804 def domenu_uncomment(self):
805 self.expandselection()
806 selstart, selend = self.ted.WEGetSelection()
807 selstart, selend = min(selstart, selend), max(selstart, selend)
808 snippet = self.getselectedtext()
809 lines = string.split(snippet, '\r')
810 for i in range(len(lines)):
811 m = commentPat.match(lines[i])
812 if m:
813 pos = m.start(1)
814 lines[i] = lines[i][:pos] + lines[i][pos+1:]
815 snippet = string.join(lines, '\r')
816 self.insert(snippet)
817 self.ted.WESetSelection(selstart, selstart + len(snippet))
819 def domenu_comment(self):
820 self.expandselection()
821 selstart, selend = self.ted.WEGetSelection()
822 selstart, selend = min(selstart, selend), max(selstart, selend)
823 snippet = self.getselectedtext()
824 lines = string.split(snippet, '\r')
825 indent = 3000 # arbitrary large number...
826 for line in lines:
827 if string.strip(line):
828 m = indentPat.match(line)
829 if m:
830 indent = min(indent, m.regs[0][1])
831 else:
832 indent = 0
833 break
834 for i in range(len(lines) - (not lines[-1])):
835 lines[i] = lines[i][:indent] + "#" + lines[i][indent:]
836 snippet = string.join(lines, '\r')
837 self.insert(snippet)
838 self.ted.WESetSelection(selstart, selstart + len(snippet))
840 def setfile(self, file):
841 self.file = file
843 def set(self, text, file = ''):
844 oldfile = self.file
845 self.file = file
846 if self._debugger:
847 self._debugger.unregister_editor(self, oldfile)
848 self._debugger.register_editor(self, file)
849 TextEditor.set(self, text)
851 def close(self):
852 if self._debugger:
853 self._debugger.unregister_editor(self, self.file)
854 self._debugger = None
855 TextEditor.close(self)
857 def click(self, point, modifiers):
858 if not self._enabled:
859 return
860 if self._debugger and self.pt_in_breaks(point):
861 self.breakhit(point, modifiers)
862 elif self._debugger:
863 bl, bt, br, bb = self._getbreakrect()
864 Qd.EraseRect((bl, bt, br-1, bb))
865 TextEditor.click(self, point, modifiers)
866 self.drawbreakpoints()
867 else:
868 TextEditor.click(self, point, modifiers)
869 if self.ted.WEGetClickCount() >= 3:
870 # select block with our indent
871 lines = string.split(self.get(), '\r')
872 selstart, selend = self.ted.WEGetSelection()
873 lineno = self.ted.WEOffsetToLine(selstart)
874 tabs = 0
875 line = lines[lineno]
876 while line[tabs:] and line[tabs] == '\t':
877 tabs = tabs + 1
878 tabstag = '\t' * tabs
879 fromline = 0
880 toline = len(lines)
881 if tabs:
882 for i in range(lineno - 1, -1, -1):
883 line = lines[i]
884 if line[:tabs] <> tabstag:
885 fromline = i + 1
886 break
887 for i in range(lineno + 1, toline):
888 line = lines[i]
889 if line[:tabs] <> tabstag:
890 toline = i - 1
891 break
892 selstart, dummy = self.ted.WEGetLineRange(fromline)
893 dummy, selend = self.ted.WEGetLineRange(toline)
894 self.ted.WESetSelection(selstart, selend)
896 def breakhit(self, point, modifiers):
897 if not self.file:
898 return
899 destrect = self.ted.WEGetDestRect()
900 offset, edge = self.ted.WEGetOffset(point)
901 lineno = self.ted.WEOffsetToLine(offset) + 1
902 if point[1] <= destrect[3]:
903 self._debugger.clear_breaks_above(self.file, self.countlines())
904 self._debugger.toggle_break(self.file, lineno)
905 else:
906 self._debugger.clear_breaks_above(self.file, lineno)
908 def key(self, char, event):
909 (what, message, when, where, modifiers) = event
910 if modifiers & Events.cmdKey and not char in Wkeys.arrowkeys:
911 return
912 if char == '\r':
913 selstart, selend = self.ted.WEGetSelection()
914 selstart, selend = min(selstart, selend), max(selstart, selend)
915 lastchar = chr(self.ted.WEGetChar(selstart-1))
916 if lastchar <> '\r' and selstart:
917 pos, dummy = self.ted.WEFindLine(selstart, 0)
918 lineres = Res.Resource('')
919 self.ted.WECopyRange(pos, selstart, lineres, None, None)
920 line = lineres.data + '\n'
921 tabcount = self.extratabs(line)
922 self.ted.WEKey(ord('\r'), 0)
923 for i in range(tabcount):
924 self.ted.WEKey(ord('\t'), 0)
925 else:
926 self.ted.WEKey(ord('\r'), 0)
927 elif char in ')]}':
928 self.ted.WEKey(ord(char), modifiers)
929 self.balanceparens(char)
930 else:
931 self.ted.WEKey(ord(char), modifiers)
932 if char not in Wkeys.navigationkeys:
933 self.textchanged()
934 self.selectionchanged()
935 self.updatescrollbars()
937 def balanceparens(self, char):
938 if char == ')':
939 target = '('
940 elif char == ']':
941 target = '['
942 elif char == '}':
943 target = '{'
944 recursionlevel = 1
945 selstart, selend = self.ted.WEGetSelection()
946 count = min(selstart, selend) - 2
947 mincount = max(0, count - 2048)
948 lastquote = None
949 while count > mincount:
950 testchar = chr(self.ted.WEGetChar(count))
951 if testchar in "\"'" and chr(self.ted.WEGetChar(count - 1)) <> '\\':
952 if lastquote == testchar:
953 recursionlevel = recursionlevel - 1
954 lastquote = None
955 elif not lastquote:
956 recursionlevel = recursionlevel + 1
957 lastquote = testchar
958 elif not lastquote and testchar == char:
959 recursionlevel = recursionlevel + 1
960 elif not lastquote and testchar == target:
961 recursionlevel = recursionlevel - 1
962 if recursionlevel == 0:
963 import time
964 autoscroll = self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, -1)
965 if autoscroll:
966 self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 0)
967 self.ted.WESetSelection(count, count + 1)
968 Qd.QDFlushPortBuffer(self._parentwindow.wid, None) # needed under OSX
969 time.sleep(0.2)
970 self.ted.WESetSelection(selstart, selend)
971 if autoscroll:
972 self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 1)
973 break
974 count = count - 1
976 def extratabs(self, line):
977 tabcount = 0
978 for c in line:
979 if c <> '\t':
980 break
981 tabcount = tabcount + 1
982 last = 0
983 cleanline = ''
984 tags = PyFontify.fontify(line)
985 # strip comments and strings
986 for tag, start, end, sublist in tags:
987 if tag in ('string', 'comment'):
988 cleanline = cleanline + line[last:start]
989 last = end
990 cleanline = cleanline + line[last:]
991 cleanline = string.strip(cleanline)
992 if cleanline and cleanline[-1] == ':':
993 tabcount = tabcount + 1
994 else:
995 # extra indent after unbalanced (, [ or {
996 for open, close in (('(', ')'), ('[', ']'), ('{', '}')):
997 count = string.count(cleanline, open)
998 if count and count > string.count(cleanline, close):
999 tabcount = tabcount + 2
1000 break
1001 return tabcount
1003 def rollover(self, point, onoff):
1004 if onoff:
1005 if self._debugger and self.pt_in_breaks(point):
1006 Wbase.SetCursor("arrow")
1007 else:
1008 Wbase.SetCursor("iBeam")
1010 def draw(self, visRgn = None):
1011 TextEditor.draw(self, visRgn)
1012 if self._debugger:
1013 self.drawbreakpoints()
1015 def showbreakpoints(self, onoff):
1016 if (not not self._debugger) <> onoff:
1017 if onoff:
1018 if not __debug__:
1019 import W
1020 raise W.AlertError, "Can't debug in \"Optimize bytecode\" mode.\r(see \"Default startup options\" in EditPythonPreferences)"
1021 import PyDebugger
1022 self._debugger = PyDebugger.getdebugger()
1023 self._debugger.register_editor(self, self.file)
1024 elif self._debugger:
1025 self._debugger.unregister_editor(self, self.file)
1026 self._debugger = None
1027 self.adjust(self._bounds)
1029 def togglebreakpoints(self):
1030 self.showbreakpoints(not self._debugger)
1032 def clearbreakpoints(self):
1033 if self.file:
1034 self._debugger.clear_all_file_breaks(self.file)
1036 def editbreakpoints(self):
1037 if self._debugger:
1038 self._debugger.edit_breaks()
1039 self._debugger.breaksviewer.selectfile(self.file)
1041 def drawbreakpoints(self, eraseall = 0):
1042 breakrect = bl, bt, br, bb = self._getbreakrect()
1043 br = br - 1
1044 self.SetPort()
1045 Qd.PenPat(Qd.qd.gray)
1046 Qd.PaintRect((br, bt, br + 1, bb))
1047 Qd.PenNormal()
1048 self._parentwindow.tempcliprect(breakrect)
1049 Qd.RGBForeColor((0xffff, 0, 0))
1050 try:
1051 lasttop = bt
1052 self_ted = self.ted
1053 Qd_PaintOval = Qd.PaintOval
1054 Qd_EraseRect = Qd.EraseRect
1055 for lineno in self._debugger.get_file_breaks(self.file):
1056 start, end = self_ted.WEGetLineRange(lineno - 1)
1057 if lineno <> self_ted.WEOffsetToLine(start) + 1:
1058 # breakpoints beyond our text: erase rest, and back out
1059 Qd_EraseRect((bl, lasttop, br, bb))
1060 break
1061 (x, y), h = self_ted.WEGetPoint(start, 0)
1062 bottom = y + h
1063 #print y, (lasttop, bottom)
1064 if bottom > lasttop:
1065 Qd_EraseRect((bl, lasttop, br, y + h * eraseall))
1066 lasttop = bottom
1067 redbullet = bl + 2, y + 3, bl + 8, y + 9
1068 Qd_PaintOval(redbullet)
1069 else:
1070 Qd_EraseRect((bl, lasttop, br, bb))
1071 Qd.RGBForeColor((0, 0, 0))
1072 finally:
1073 self._parentwindow.restoreclip()
1075 def updatescrollbars(self):
1076 if self._debugger:
1077 self.drawbreakpoints(1)
1078 TextEditor.updatescrollbars(self)
1080 def pt_in_breaks(self, point):
1081 return Qd.PtInRect(point, self._getbreakrect())
1083 def _getbreakrect(self):
1084 if self._debugger:
1085 l, t, r, b = self._bounds
1086 return (l+1, t+1, l + 12, b-1)
1087 else:
1088 return (0, 0, 0, 0)
1090 def _getviewrect(self):
1091 l, t, r, b = self._bounds
1092 if self._debugger:
1093 return (l + 17, t + 2, r, b - 2)
1094 else:
1095 return (l + 5, t + 2, r, b - 2)
1097 def _calctextbounds(self):
1098 viewrect = l, t, r, b = self._getviewrect()
1099 if self.ted:
1100 dl, dt, dr, db = self.ted.WEGetDestRect()
1101 vl, vt, vr, vb = self.ted.WEGetViewRect()
1102 xshift = l - vl
1103 yshift = t - vt
1104 if (db - dt) < (b - t):
1105 yshift = t - dt
1106 destrect = (dl + xshift, dt + yshift, dr + xshift, db + yshift)
1107 else:
1108 destrect = (l, t, r + 5000, b)
1109 return viewrect, destrect
1112 def GetFNum(fontname):
1113 """Same as Fm.GetFNum(), but maps a missing font to Monaco instead of the system font."""
1114 if fontname <> Fm.GetFontName(0):
1115 fontid = Fm.GetFNum(fontname)
1116 if fontid == 0:
1117 fontid = Fonts.monaco
1118 else:
1119 fontid = 0
1120 return fontid
1122 # b/w compat. Anyone using this?
1123 GetFName = Fm.GetFontName
1125 def GetPortFontSettings(port):
1126 return Fm.GetFontName(port.txFont), port.txFace, port.txSize
1128 def SetPortFontSettings(port, (font, face, size)):
1129 saveport = Qd.GetPort()
1130 Qd.SetPort(port)
1131 Qd.TextFont(GetFNum(font))
1132 Qd.TextFace(face)
1133 Qd.TextSize(size)
1134 Qd.SetPort(saveport)