cpython/Mac/Tools/IDE/Wtext.py

1135 lines
32 KiB
Python

from Carbon import Evt, Events, Fm, Fonts
from Carbon import Qd, Res, Scrap
from Carbon import TE, TextEdit, Win
from Carbon import App
from Carbon.Appearance import kThemeStateActive, kThemeStateInactive
import waste
import WASTEconst
import Wbase
import Wkeys
import Wcontrols
import PyFontify
import string
from types import TupleType, StringType
class TextBox(Wbase.Widget):
"""A static text widget"""
def __init__(self, possize, text="", align=TextEdit.teJustLeft,
fontsettings=None,
backgroundcolor=(0xffff, 0xffff, 0xffff)
):
if fontsettings is None:
import W
fontsettings = W.getdefaultfont()
Wbase.Widget.__init__(self, possize)
self.fontsettings = fontsettings
self.text = text
self.align = align
self._backgroundcolor = backgroundcolor
def draw(self, visRgn = None):
if self._visible:
(font, style, size, color) = self.fontsettings
fontid = GetFNum(font)
savestate = Qd.GetPenState()
Qd.TextFont(fontid)
Qd.TextFace(style)
Qd.TextSize(size)
Qd.RGBForeColor(color)
Qd.RGBBackColor(self._backgroundcolor)
TE.TETextBox(self.text, self._bounds, self.align)
Qd.RGBBackColor((0xffff, 0xffff, 0xffff))
Qd.SetPenState(savestate)
def get(self):
return self.text
def set(self, text):
self.text = text
if self._parentwindow and self._parentwindow.wid:
self.SetPort()
self.draw()
class _ScrollWidget:
# to be overridden
def getscrollrects(self):
"""Return (destrect, viewrect)."""
return None, None
# internal method
def updatescrollbars(self):
(dl, dt, dr, db), (vl, vt, vr, vb) = self.getscrollrects()
if self._parent._barx:
viewwidth = vr - vl
destwidth = dr - dl
bar = self._parent._barx
bar.setmax(destwidth - viewwidth)
# MacOS 8.1 doesn't automatically disable
# scrollbars whose max <= min
bar.enable(destwidth > viewwidth)
bar.setviewsize(viewwidth)
bar.set(vl - dl)
if self._parent._bary:
viewheight = vb - vt
destheight = db - dt
bar = self._parent._bary
bar.setmax(destheight - viewheight)
# MacOS 8.1 doesn't automatically disable
# scrollbars whose max <= min
bar.enable(destheight > viewheight)
bar.setviewsize(viewheight)
bar.set(vt - dt)
UNDOLABELS = [ # Indexed by WEGetUndoInfo() value
None, "", "typing", "Cut", "Paste", "Clear", "Drag", "Style",
"Ruler", "backspace", "delete", "transform", "resize"]
class EditText(Wbase.SelectableWidget, _ScrollWidget):
"""A text edit widget, mainly for simple entry fields."""
def __init__(self, possize, text="",
callback=None, inset=(3, 3),
fontsettings=None,
tabsettings = (32, 0),
readonly = 0):
if fontsettings is None:
import W
fontsettings = W.getdefaultfont()
Wbase.SelectableWidget.__init__(self, possize)
self.temptext = text
self.ted = None
self.selection = None
self.oldselection = None
self._callback = callback
self.changed = 0
self.selchanged = 0
self._selected = 0
self._enabled = 1
self.wrap = 1
self.readonly = readonly
self.fontsettings = fontsettings
self.tabsettings = tabsettings
if type(inset) <> TupleType:
self.inset = (inset, inset)
else:
self.inset = inset
def open(self):
if not hasattr(self._parent, "_barx"):
self._parent._barx = None
if not hasattr(self._parent, "_bary"):
self._parent._bary = None
self._calcbounds()
self.SetPort()
viewrect, destrect = self._calctextbounds()
flags = self._getflags()
self.ted = waste.WENew(destrect, viewrect, flags)
self.ted.WEInstallTabHooks()
self.ted.WESetAlignment(WASTEconst.weFlushLeft)
self.setfontsettings(self.fontsettings)
self.settabsettings(self.tabsettings)
self.ted.WEUseText(Res.Resource(self.temptext))
self.ted.WECalText()
if self.selection:
self.setselection(self.selection[0], self.selection[1])
self.selection = None
else:
self.selview()
self.temptext = None
self.updatescrollbars()
self.bind("pageup", self.scrollpageup)
self.bind("pagedown", self.scrollpagedown)
self.bind("top", self.scrolltop)
self.bind("bottom", self.scrollbottom)
self.selchanged = 0
def close(self):
self._parent._barx = None
self._parent._bary = None
self.ted = None
self.temptext = None
Wbase.SelectableWidget.close(self)
def textchanged(self, all=0):
self.changed = 1
def selectionchanged(self):
self.selchanged = 1
self.oldselection = self.getselection()
def gettabsettings(self):
return self.tabsettings
def settabsettings(self, (tabsize, tabmode)):
self.tabsettings = (tabsize, tabmode)
if hasattr(self.ted, "WESetTabSize"):
port = self._parentwindow.wid.GetWindowPort()
if tabmode:
(font, style, size, color) = self.getfontsettings()
savesettings = GetPortFontSettings(port)
SetPortFontSettings(port, (font, style, size))
tabsize = Qd.StringWidth(' ' * tabsize)
SetPortFontSettings(port, savesettings)
tabsize = max(tabsize, 1)
self.ted.WESetTabSize(tabsize)
self.SetPort()
Qd.EraseRect(self.ted.WEGetViewRect())
self.ted.WEUpdate(port.visRgn)
def getfontsettings(self):
from Carbon import Res
(font, style, size, color) = self.ted.WEGetRunInfo(0)[4]
font = Fm.GetFontName(font)
return (font, style, size, color)
def setfontsettings(self, (font, style, size, color)):
self.SetPort()
if type(font) <> StringType:
font = Fm.GetFontName(font)
self.fontsettings = (font, style, size, color)
fontid = GetFNum(font)
readonly = self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, -1)
if readonly:
self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 0)
try:
self.ted.WEFeatureFlag(WASTEconst.weFInhibitRecal, 1)
selstart, selend = self.ted.WEGetSelection()
self.ted.WESetSelection(0, self.ted.WEGetTextLength())
self.ted.WESetStyle(WASTEconst.weDoFace, (0, 0, 0, (0, 0, 0)))
self.ted.WESetStyle(WASTEconst.weDoFace |
WASTEconst.weDoColor |
WASTEconst.weDoFont |
WASTEconst.weDoSize,
(fontid, style, size, color))
self.ted.WEFeatureFlag(WASTEconst.weFInhibitRecal, 0)
self.ted.WECalText()
self.ted.WESetSelection(selstart, selend)
finally:
if readonly:
self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 1)
viewrect = self.ted.WEGetViewRect()
Qd.EraseRect(viewrect)
self.ted.WEUpdate(self._parentwindow.wid.GetWindowPort().visRgn)
self.selectionchanged()
self.updatescrollbars()
def adjust(self, oldbounds):
self.SetPort()
# Note: if App.DrawThemeEditTextFrame is ever used, it will be necessary
# to unconditionally outset the invalidated rectangles, since Appearance
# frames are drawn outside the bounds.
if self._selected and self._parentwindow._hasselframes:
self.GetWindow().InvalWindowRect(Qd.InsetRect(oldbounds, -3, -3))
self.GetWindow().InvalWindowRect(Qd.InsetRect(self._bounds, -3, -3))
else:
self.GetWindow().InvalWindowRect(oldbounds)
self.GetWindow().InvalWindowRect(self._bounds)
viewrect, destrect = self._calctextbounds()
self.ted.WESetViewRect(viewrect)
self.ted.WESetDestRect(destrect)
if self.wrap:
self.ted.WECalText()
if self.ted.WEGetDestRect()[3] < viewrect[1]:
self.selview()
self.updatescrollbars()
# interface -----------------------
# selection stuff
def selview(self):
self.ted.WESelView()
def selectall(self):
self.ted.WESetSelection(0, self.ted.WEGetTextLength())
self.selectionchanged()
self.updatescrollbars()
def selectline(self, lineno, charoffset = 0):
newselstart, newselend = self.ted.WEGetLineRange(lineno)
# Autoscroll makes the *end* of the selection visible, which,
# in the case of a whole line, is the beginning of the *next* line.
# So sometimes it leaves our line just above the view rect.
# Let's fool Waste by initially selecting one char less:
self.ted.WESetSelection(newselstart + charoffset, newselend-1)
self.ted.WESetSelection(newselstart + charoffset, newselend)
self.selectionchanged()
self.updatescrollbars()
def getselection(self):
if self.ted:
return self.ted.WEGetSelection()
else:
return self.selection
def setselection(self, selstart, selend):
self.selectionchanged()
if self.ted:
self.ted.WESetSelection(selstart, selend)
self.ted.WESelView()
self.updatescrollbars()
else:
self.selection = selstart, selend
def offsettoline(self, offset):
return self.ted.WEOffsetToLine(offset)
def countlines(self):
return self.ted.WECountLines()
def getselectedtext(self):
selstart, selend = self.ted.WEGetSelection()
return self.ted.WEGetText().data[selstart:selend]
def expandselection(self):
oldselstart, oldselend = self.ted.WEGetSelection()
selstart, selend = min(oldselstart, oldselend), max(oldselstart, oldselend)
if selstart <> selend and chr(self.ted.WEGetChar(selend-1)) == '\r':
selend = selend - 1
newselstart, dummy = self.ted.WEFindLine(selstart, 1)
dummy, newselend = self.ted.WEFindLine(selend, 1)
if oldselstart <> newselstart or oldselend <> newselend:
self.ted.WESetSelection(newselstart, newselend)
self.updatescrollbars()
self.selectionchanged()
def insert(self, text):
self.ted.WEInsert(text, None, None)
self.textchanged()
self.selectionchanged()
# text
def set(self, text):
if not self.ted:
self.temptext = text
else:
self.ted.WEUseText(Res.Resource(text))
self.ted.WECalText()
self.SetPort()
viewrect, destrect = self._calctextbounds()
self.ted.WESetViewRect(viewrect)
self.ted.WESetDestRect(destrect)
rgn = Qd.NewRgn()
Qd.RectRgn(rgn, viewrect)
Qd.EraseRect(viewrect)
self.draw(rgn)
self.updatescrollbars()
self.textchanged(1)
def get(self):
if not self._parent:
return self.temptext
else:
return self.ted.WEGetText().data
# events
def key(self, char, event):
(what, message, when, where, modifiers) = event
if self._enabled and not modifiers & Events.cmdKey or char in Wkeys.arrowkeys:
self.ted.WEKey(ord(char), modifiers)
if char not in Wkeys.navigationkeys:
self.textchanged()
if char not in Wkeys.scrollkeys:
self.selectionchanged()
self.updatescrollbars()
if self._callback:
Wbase.CallbackCall(self._callback, 0, char, modifiers)
def click(self, point, modifiers):
if not self._enabled:
return
self.ted.WEClick(point, modifiers, Evt.TickCount())
self.selectionchanged()
self.updatescrollbars()
return 1
def idle(self):
self.SetPort()
self.ted.WEIdle()
def rollover(self, point, onoff):
if onoff:
Wbase.SetCursor("iBeam")
def activate(self, onoff):
self._activated = onoff
if self._visible:
self.SetPort()
# DISABLED! There are too many places where it is assumed that
# the frame of an EditText item is 1 pixel, inside the bounds.
#state = [kThemeStateActive, kThemeStateInactive][not onoff]
#App.DrawThemeEditTextFrame(Qd.InsetRect(self._bounds, 1, 1), state)
if self._selected:
if onoff:
self.ted.WEActivate()
else:
self.ted.WEDeactivate()
self.drawselframe(onoff)
def select(self, onoff, isclick = 0):
if Wbase.SelectableWidget.select(self, onoff):
return
self.SetPort()
if onoff:
self.ted.WEActivate()
if self._parentwindow._tabbable and not isclick:
self.selectall()
else:
self.ted.WEDeactivate()
self.drawselframe(onoff)
def draw(self, visRgn = None):
if self._visible:
if not visRgn:
visRgn = self._parentwindow.wid.GetWindowPort().visRgn
self.ted.WEUpdate(visRgn)
# DISABLED! There are too many places where it is assumed that
# the frame of an EditText item is 1 pixel, inside the bounds.
#state = [kThemeStateActive, kThemeStateInactive][not self._activated]
#App.DrawThemeEditTextFrame(Qd.InsetRect(self._bounds, 1, 1), state)
Qd.FrameRect(self._bounds)
if self._selected and self._activated:
self.drawselframe(1)
# scrolling
def scrollpageup(self):
if self._parent._bary and self._parent._bary._enabled:
self.vscroll("++")
def scrollpagedown(self):
if self._parent._bary and self._parent._bary._enabled:
self.vscroll("--")
def scrolltop(self):
if self._parent._bary and self._parent._bary._enabled:
self.vscroll(self._parent._bary.getmin())
if self._parent._barx and self._parent._barx._enabled:
self.hscroll(self._parent._barx.getmin())
def scrollbottom(self):
if self._parent._bary and self._parent._bary._enabled:
self.vscroll(self._parent._bary.getmax())
# menu handlers
def domenu_copy(self, *args):
selbegin, selend = self.ted.WEGetSelection()
if selbegin == selend:
return
if hasattr(Scrap, 'ZeroScrap'):
Scrap.ZeroScrap()
else:
Scrap.ClearCurrentScrap()
self.ted.WECopy()
self.updatescrollbars()
def domenu_cut(self, *args):
selbegin, selend = self.ted.WEGetSelection()
if selbegin == selend:
return
if hasattr(Scrap, 'ZeroScrap'):
Scrap.ZeroScrap()
else:
Scrap.ClearCurrentScrap()
self.ted.WECut()
self.updatescrollbars()
self.selview()
self.textchanged()
self.selectionchanged()
if self._callback:
Wbase.CallbackCall(self._callback, 0, "", None)
def domenu_paste(self, *args):
if not self.ted.WECanPaste():
return
self.selview()
self.ted.WEPaste()
self.updatescrollbars()
self.textchanged()
self.selectionchanged()
if self._callback:
Wbase.CallbackCall(self._callback, 0, "", None)
def domenu_clear(self, *args):
self.ted.WEDelete()
self.selview()
self.updatescrollbars()
self.textchanged()
self.selectionchanged()
if self._callback:
Wbase.CallbackCall(self._callback, 0, "", None)
def domenu_undo(self, *args):
which, redo = self.ted.WEGetUndoInfo()
if not which:
return
self.ted.WEUndo()
self.updatescrollbars()
self.textchanged()
self.selectionchanged()
if self._callback:
Wbase.CallbackCall(self._callback, 0, "", None)
def can_undo(self, menuitem):
#doundo = self.ted.WEFeatureFlag(WASTEconst.weFUndo, -1)
#print doundo
#if not doundo:
# return 0
which, redo = self.ted.WEGetUndoInfo()
if which < len(UNDOLABELS):
which = UNDOLABELS[which]
else:
which = ""
if which == None:
return None
if redo:
which = "Redo "+which
else:
which = "Undo "+which
menuitem.settext(which)
return 1
def domenu_selectall(self, *args):
self.selectall()
# private
def getscrollrects(self):
return self.ted.WEGetDestRect(), self.ted.WEGetViewRect()
def vscroll(self, value):
lineheight = self.ted.WEGetHeight(0, 1)
dr = self.ted.WEGetDestRect()
vr = self.ted.WEGetViewRect()
viewheight = vr[3] - vr[1]
maxdelta = vr[1] - dr[1]
mindelta = vr[3] - dr[3]
if value == "+":
delta = lineheight
elif value == "-":
delta = - lineheight
elif value == "++":
delta = viewheight - lineheight
elif value == "--":
delta = lineheight - viewheight
else: # in thumb
delta = vr[1] - dr[1] - value
delta = min(maxdelta, delta)
delta = max(mindelta, delta)
self.ted.WEScroll(0, delta)
self.updatescrollbars()
def hscroll(self, value):
dr = self.ted.WEGetDestRect()
vr = self.ted.WEGetViewRect()
destwidth = dr[2] - dr[0]
viewwidth = vr[2] - vr[0]
viewoffset = maxdelta = vr[0] - dr[0]
mindelta = vr[2] - dr[2]
if value == "+":
delta = 32
elif value == "-":
delta = - 32
elif value == "++":
delta = 0.5 * (vr[2] - vr[0])
elif value == "--":
delta = 0.5 * (vr[0] - vr[2])
else: # in thumb
delta = vr[0] - dr[0] - value
#cur = (32767 * viewoffset) / (destwidth - viewwidth)
#delta = (cur-value)*(destwidth - viewwidth)/32767
#if abs(delta - viewoffset) <=2:
# # compensate for irritating rounding error
# delta = viewoffset
delta = min(maxdelta, delta)
delta = max(mindelta, delta)
self.ted.WEScroll(delta, 0)
self.updatescrollbars()
# some internals
def _getflags(self):
flags = WASTEconst.weDoAutoScroll | WASTEconst.weDoMonoStyled
if self.readonly:
flags = flags | WASTEconst.weDoReadOnly
else:
flags = flags | WASTEconst.weDoUndo
return flags
def _getviewrect(self):
return Qd.InsetRect(self._bounds, self.inset[0], self.inset[1])
def _calctextbounds(self):
viewrect = l, t, r, b = self._getviewrect()
if self.ted:
dl, dt, dr, db = self.ted.WEGetDestRect()
vl, vt, vr, vb = self.ted.WEGetViewRect()
yshift = t - vt
if (db - dt) < (b - t):
destrect = viewrect
else:
destrect = l, dt + yshift, r, db + yshift
else:
destrect = viewrect
return viewrect, destrect
class TextEditor(EditText):
"""A text edit widget."""
def __init__(self, possize, text="", callback=None, wrap=1, inset=(4, 4),
fontsettings=None,
tabsettings=(32, 0),
readonly=0):
EditText.__init__(self, possize, text, callback, inset, fontsettings, tabsettings, readonly)
self.wrap = wrap
def _getflags(self):
flags = WASTEconst.weDoAutoScroll | WASTEconst.weDoMonoStyled | \
WASTEconst.weDoOutlineHilite
if self.readonly:
flags = flags | WASTEconst.weDoReadOnly
else:
flags = flags | WASTEconst.weDoUndo
return flags
def _getviewrect(self):
l, t, r, b = self._bounds
return (l + 5, t + 2, r, b - 2)
def _calctextbounds(self):
if self.wrap:
return EditText._calctextbounds(self)
else:
viewrect = l, t, r, b = self._getviewrect()
if self.ted:
dl, dt, dr, db = self.ted.WEGetDestRect()
vl, vt, vr, vb = self.ted.WEGetViewRect()
xshift = l - vl
yshift = t - vt
if (db - dt) < (b - t):
yshift = t - dt
destrect = (dl + xshift, dt + yshift, dr + xshift, db + yshift)
else:
destrect = (l, t, r + 5000, b)
return viewrect, destrect
def draw(self, visRgn = None):
if self._visible:
if not visRgn:
visRgn = self._parentwindow.wid.GetWindowPort().visRgn
self.ted.WEUpdate(visRgn)
if self._selected and self._activated:
self.drawselframe(1)
def activate(self, onoff):
self._activated = onoff
if self._visible:
self.SetPort()
# doesn't draw frame, as EditText.activate does
if self._selected:
if onoff:
self.ted.WEActivate()
else:
self.ted.WEDeactivate()
self.drawselframe(onoff)
import re
commentPat = re.compile("[ \t]*(#)")
indentPat = re.compile("[ \t]*")
kStringColor = (0, 0x7fff, 0)
kCommentColor = (0, 0, 0xb000)
class PyEditor(TextEditor):
"""A specialized Python source edit widget"""
def __init__(self, possize, text="", callback=None, inset=(4, 4),
fontsettings=None,
tabsettings=(32, 0),
readonly=0,
debugger=None,
file=''):
TextEditor.__init__(self, possize, text, callback, 0, inset, fontsettings, tabsettings, readonly)
self.bind("cmd[", self.domenu_shiftleft)
self.bind("cmd]", self.domenu_shiftright)
self.bind("cmdshift[", self.domenu_uncomment)
self.bind("cmdshift]", self.domenu_comment)
self.bind("cmdshiftd", self.alldirty)
self.file = file # only for debugger reference
self._debugger = debugger
if debugger:
debugger.register_editor(self, self.file)
self._dirty = (0, None)
self.do_fontify = 0
#def open(self):
# TextEditor.open(self)
# if self.do_fontify:
# self.fontify()
# self._dirty = (None, None)
def _getflags(self):
flags = (WASTEconst.weDoDrawOffscreen | WASTEconst.weDoUseTempMem |
WASTEconst.weDoAutoScroll | WASTEconst.weDoOutlineHilite)
if self.readonly:
flags = flags | WASTEconst.weDoReadOnly
else:
flags = flags | WASTEconst.weDoUndo
return flags
def textchanged(self, all=0):
self.changed = 1
if all:
self._dirty = (0, None)
return
oldsel = self.oldselection
sel = self.getselection()
if not sel:
# XXX what to do?
return
selstart, selend = sel
selstart, selend = min(selstart, selend), max(selstart, selend)
if oldsel:
oldselstart, oldselend = min(oldsel), max(oldsel)
selstart, selend = min(selstart, oldselstart), max(selend, oldselend)
startline = self.offsettoline(selstart)
endline = self.offsettoline(selend)
selstart, _ = self.ted.WEGetLineRange(startline)
_, selend = self.ted.WEGetLineRange(endline)
if selstart > 0:
selstart = selstart - 1
self._dirty = (selstart, selend)
def idle(self):
self.SetPort()
self.ted.WEIdle()
if not self.do_fontify:
return
start, end = self._dirty
if start is None:
return
textLength = self.ted.WEGetTextLength()
if end is None:
end = textLength
if start >= end:
self._dirty = (None, None)
else:
self.fontify(start, end)
self._dirty = (None, None)
def alldirty(self, *args):
self._dirty = (0, None)
def fontify(self, start=0, end=None):
#W.SetCursor('watch')
if self.readonly:
self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 0)
self.ted.WEFeatureFlag(WASTEconst.weFOutlineHilite, 0)
self.ted.WEDeactivate()
self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 0)
self.ted.WEFeatureFlag(WASTEconst.weFUndo, 0)
pytext = self.get().replace("\r", "\n")
if end is None:
end = len(pytext)
else:
end = min(end, len(pytext))
selstart, selend = self.ted.WEGetSelection()
self.ted.WESetSelection(start, end)
self.ted.WESetStyle(WASTEconst.weDoFace | WASTEconst.weDoColor,
(0, 0, 12, (0, 0, 0)))
tags = PyFontify.fontify(pytext, start, end)
styles = {
'string': (WASTEconst.weDoColor, (0, 0, 0, kStringColor)),
'keyword': (WASTEconst.weDoFace, (0, 1, 0, (0, 0, 0))),
'comment': (WASTEconst.weDoFace | WASTEconst.weDoColor, (0, 0, 0, kCommentColor)),
'identifier': (WASTEconst.weDoColor, (0, 0, 0, (0xbfff, 0, 0)))
}
setselection = self.ted.WESetSelection
setstyle = self.ted.WESetStyle
for tag, start, end, sublist in tags:
setselection(start, end)
mode, style = styles[tag]
setstyle(mode, style)
self.ted.WESetSelection(selstart, selend)
self.SetPort()
self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 1)
self.ted.WEFeatureFlag(WASTEconst.weFUndo, 1)
self.ted.WEActivate()
self.ted.WEFeatureFlag(WASTEconst.weFOutlineHilite, 1)
if self.readonly:
self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 1)
def domenu_shiftleft(self):
self.expandselection()
selstart, selend = self.ted.WEGetSelection()
selstart, selend = min(selstart, selend), max(selstart, selend)
snippet = self.getselectedtext()
lines = string.split(snippet, '\r')
for i in range(len(lines)):
if lines[i][:1] == '\t':
lines[i] = lines[i][1:]
snippet = string.join(lines, '\r')
self.insert(snippet)
self.ted.WESetSelection(selstart, selstart + len(snippet))
def domenu_shiftright(self):
self.expandselection()
selstart, selend = self.ted.WEGetSelection()
selstart, selend = min(selstart, selend), max(selstart, selend)
snippet = self.getselectedtext()
lines = string.split(snippet, '\r')
for i in range(len(lines) - (not lines[-1])):
lines[i] = '\t' + lines[i]
snippet = string.join(lines, '\r')
self.insert(snippet)
self.ted.WESetSelection(selstart, selstart + len(snippet))
def domenu_uncomment(self):
self.expandselection()
selstart, selend = self.ted.WEGetSelection()
selstart, selend = min(selstart, selend), max(selstart, selend)
snippet = self.getselectedtext()
lines = string.split(snippet, '\r')
for i in range(len(lines)):
m = commentPat.match(lines[i])
if m:
pos = m.start(1)
lines[i] = lines[i][:pos] + lines[i][pos+1:]
snippet = string.join(lines, '\r')
self.insert(snippet)
self.ted.WESetSelection(selstart, selstart + len(snippet))
def domenu_comment(self):
self.expandselection()
selstart, selend = self.ted.WEGetSelection()
selstart, selend = min(selstart, selend), max(selstart, selend)
snippet = self.getselectedtext()
lines = string.split(snippet, '\r')
indent = 3000 # arbitrary large number...
for line in lines:
if string.strip(line):
m = indentPat.match(line)
if m:
indent = min(indent, m.regs[0][1])
else:
indent = 0
break
for i in range(len(lines) - (not lines[-1])):
lines[i] = lines[i][:indent] + "#" + lines[i][indent:]
snippet = string.join(lines, '\r')
self.insert(snippet)
self.ted.WESetSelection(selstart, selstart + len(snippet))
def setfile(self, file):
self.file = file
def set(self, text, file = ''):
oldfile = self.file
self.file = file
if self._debugger:
self._debugger.unregister_editor(self, oldfile)
self._debugger.register_editor(self, file)
TextEditor.set(self, text)
def close(self):
if self._debugger:
self._debugger.unregister_editor(self, self.file)
self._debugger = None
TextEditor.close(self)
def click(self, point, modifiers):
if not self._enabled:
return
if self._debugger and self.pt_in_breaks(point):
self.breakhit(point, modifiers)
elif self._debugger:
bl, bt, br, bb = self._getbreakrect()
Qd.EraseRect((bl, bt, br-1, bb))
TextEditor.click(self, point, modifiers)
self.drawbreakpoints()
else:
TextEditor.click(self, point, modifiers)
if self.ted.WEGetClickCount() >= 3:
# select block with our indent
lines = string.split(self.get(), '\r')
selstart, selend = self.ted.WEGetSelection()
lineno = self.ted.WEOffsetToLine(selstart)
tabs = 0
line = lines[lineno]
while line[tabs:] and line[tabs] == '\t':
tabs = tabs + 1
tabstag = '\t' * tabs
fromline = 0
toline = len(lines)
if tabs:
for i in range(lineno - 1, -1, -1):
line = lines[i]
if line[:tabs] <> tabstag:
fromline = i + 1
break
for i in range(lineno + 1, toline):
line = lines[i]
if line[:tabs] <> tabstag:
toline = i - 1
break
selstart, dummy = self.ted.WEGetLineRange(fromline)
dummy, selend = self.ted.WEGetLineRange(toline)
self.ted.WESetSelection(selstart, selend)
def breakhit(self, point, modifiers):
if not self.file:
return
destrect = self.ted.WEGetDestRect()
offset, edge = self.ted.WEGetOffset(point)
lineno = self.ted.WEOffsetToLine(offset) + 1
if point[1] <= destrect[3]:
self._debugger.clear_breaks_above(self.file, self.countlines())
self._debugger.toggle_break(self.file, lineno)
else:
self._debugger.clear_breaks_above(self.file, lineno)
def key(self, char, event):
(what, message, when, where, modifiers) = event
if modifiers & Events.cmdKey and not char in Wkeys.arrowkeys:
return
if char == '\r':
selstart, selend = self.ted.WEGetSelection()
selstart, selend = min(selstart, selend), max(selstart, selend)
lastchar = chr(self.ted.WEGetChar(selstart-1))
if lastchar <> '\r' and selstart:
pos, dummy = self.ted.WEFindLine(selstart, 0)
lineres = Res.Resource('')
self.ted.WECopyRange(pos, selstart, lineres, None, None)
line = lineres.data + '\n'
tabcount = self.extratabs(line)
self.ted.WEKey(ord('\r'), 0)
for i in range(tabcount):
self.ted.WEKey(ord('\t'), 0)
else:
self.ted.WEKey(ord('\r'), 0)
elif char in ')]}':
self.ted.WEKey(ord(char), modifiers)
self.balanceparens(char)
else:
self.ted.WEKey(ord(char), modifiers)
if char not in Wkeys.navigationkeys:
self.textchanged()
self.selectionchanged()
self.updatescrollbars()
def balanceparens(self, char):
if char == ')':
target = '('
elif char == ']':
target = '['
elif char == '}':
target = '{'
recursionlevel = 1
selstart, selend = self.ted.WEGetSelection()
count = min(selstart, selend) - 2
mincount = max(0, count - 2048)
lastquote = None
while count > mincount:
testchar = chr(self.ted.WEGetChar(count))
if testchar in "\"'" and chr(self.ted.WEGetChar(count - 1)) <> '\\':
if lastquote == testchar:
recursionlevel = recursionlevel - 1
lastquote = None
elif not lastquote:
recursionlevel = recursionlevel + 1
lastquote = testchar
elif not lastquote and testchar == char:
recursionlevel = recursionlevel + 1
elif not lastquote and testchar == target:
recursionlevel = recursionlevel - 1
if recursionlevel == 0:
import time
autoscroll = self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, -1)
if autoscroll:
self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 0)
self.ted.WESetSelection(count, count + 1)
self._parentwindow.wid.GetWindowPort().QDFlushPortBuffer(None) # needed under OSX
time.sleep(0.2)
self.ted.WESetSelection(selstart, selend)
if autoscroll:
self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 1)
break
count = count - 1
def extratabs(self, line):
tabcount = 0
for c in line:
if c <> '\t':
break
tabcount = tabcount + 1
last = 0
cleanline = ''
tags = PyFontify.fontify(line)
# strip comments and strings
for tag, start, end, sublist in tags:
if tag in ('string', 'comment'):
cleanline = cleanline + line[last:start]
last = end
cleanline = cleanline + line[last:]
cleanline = string.strip(cleanline)
if cleanline and cleanline[-1] == ':':
tabcount = tabcount + 1
else:
# extra indent after unbalanced (, [ or {
for open, close in (('(', ')'), ('[', ']'), ('{', '}')):
count = string.count(cleanline, open)
if count and count > string.count(cleanline, close):
tabcount = tabcount + 2
break
return tabcount
def rollover(self, point, onoff):
if onoff:
if self._debugger and self.pt_in_breaks(point):
Wbase.SetCursor("arrow")
else:
Wbase.SetCursor("iBeam")
def draw(self, visRgn = None):
TextEditor.draw(self, visRgn)
if self._debugger:
self.drawbreakpoints()
def showbreakpoints(self, onoff):
if (not not self._debugger) <> onoff:
if onoff:
if not __debug__:
import W
raise W.AlertError, "Can't debug in \"Optimize bytecode\" mode.\r(see \"Default startup options\" in EditPythonPreferences)"
import PyDebugger
self._debugger = PyDebugger.getdebugger()
self._debugger.register_editor(self, self.file)
elif self._debugger:
self._debugger.unregister_editor(self, self.file)
self._debugger = None
self.adjust(self._bounds)
def togglebreakpoints(self):
self.showbreakpoints(not self._debugger)
def clearbreakpoints(self):
if self.file:
self._debugger.clear_all_file_breaks(self.file)
def editbreakpoints(self):
if self._debugger:
self._debugger.edit_breaks()
self._debugger.breaksviewer.selectfile(self.file)
def drawbreakpoints(self, eraseall = 0):
breakrect = bl, bt, br, bb = self._getbreakrect()
br = br - 1
self.SetPort()
Qd.PenPat(Qd.GetQDGlobalsGray())
Qd.PaintRect((br, bt, br + 1, bb))
Qd.PenNormal()
self._parentwindow.tempcliprect(breakrect)
Qd.RGBForeColor((0xffff, 0, 0))
try:
lasttop = bt
self_ted = self.ted
Qd_PaintOval = Qd.PaintOval
Qd_EraseRect = Qd.EraseRect
for lineno in self._debugger.get_file_breaks(self.file):
start, end = self_ted.WEGetLineRange(lineno - 1)
if lineno <> self_ted.WEOffsetToLine(start) + 1:
# breakpoints beyond our text: erase rest, and back out
Qd_EraseRect((bl, lasttop, br, bb))
break
(x, y), h = self_ted.WEGetPoint(start, 0)
bottom = y + h
#print y, (lasttop, bottom)
if bottom > lasttop:
Qd_EraseRect((bl, lasttop, br, y + h * eraseall))
lasttop = bottom
redbullet = bl + 2, y + 3, bl + 8, y + 9
Qd_PaintOval(redbullet)
else:
Qd_EraseRect((bl, lasttop, br, bb))
Qd.RGBForeColor((0, 0, 0))
finally:
self._parentwindow.restoreclip()
def updatescrollbars(self):
if self._debugger:
self.drawbreakpoints(1)
TextEditor.updatescrollbars(self)
def pt_in_breaks(self, point):
return Qd.PtInRect(point, self._getbreakrect())
def _getbreakrect(self):
if self._debugger:
l, t, r, b = self._bounds
return (l+1, t+1, l + 12, b-1)
else:
return (0, 0, 0, 0)
def _getviewrect(self):
l, t, r, b = self._bounds
if self._debugger:
return (l + 17, t + 2, r, b - 2)
else:
return (l + 5, t + 2, r, b - 2)
def _calctextbounds(self):
viewrect = l, t, r, b = self._getviewrect()
if self.ted:
dl, dt, dr, db = self.ted.WEGetDestRect()
vl, vt, vr, vb = self.ted.WEGetViewRect()
xshift = l - vl
yshift = t - vt
if (db - dt) < (b - t):
yshift = t - dt
destrect = (dl + xshift, dt + yshift, dr + xshift, db + yshift)
else:
destrect = (l, t, r + 5000, b)
return viewrect, destrect
def GetFNum(fontname):
"""Same as Fm.GetFNum(), but maps a missing font to Monaco instead of the system font."""
if fontname <> Fm.GetFontName(0):
fontid = Fm.GetFNum(fontname)
if fontid == 0:
fontid = Fonts.monaco
else:
fontid = 0
return fontid
# b/w compat. Anyone using this?
GetFName = Fm.GetFontName
def GetPortFontSettings(port):
return Fm.GetFontName(port.GetPortTextFont()), port.GetPortTextFace(), port.GetPortTextSize()
def SetPortFontSettings(port, (font, face, size)):
saveport = Qd.GetPort()
Qd.SetPort(port)
Qd.TextFont(GetFNum(font))
Qd.TextFace(face)
Qd.TextSize(size)
Qd.SetPort(saveport)