home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / MacHacksBug / Python 1.5.2c1 / Mac / Tools / IDE / Wtext.py < prev    next >
Encoding:
Python Source  |  2000-06-23  |  27.1 KB  |  984 lines

  1. import Qd
  2. import TE
  3. import Fm
  4. import waste
  5. import WASTEconst
  6. import Res
  7. import Evt
  8. import Events
  9. import Scrap
  10. import string
  11.  
  12. import Win
  13. import Wbase
  14. import Wkeys
  15. import Wcontrols
  16. import PyFontify
  17. from types import *
  18. import Fonts
  19. import TextEdit
  20.  
  21.  
  22.  
  23. class TextBox(Wbase.Widget):
  24.     
  25.     """A static text widget"""
  26.     
  27.     def __init__(self, possize, text="", align=TextEdit.teJustLeft, 
  28.                 fontsettings=None,
  29.                 backgroundcolor=(0xffff, 0xffff, 0xffff)
  30.                 ):
  31.         if fontsettings is None:
  32.             import W
  33.             fontsettings = W.getdefaultfont()
  34.         Wbase.Widget.__init__(self, possize)
  35.         self.fontsettings = fontsettings
  36.         self.text = text
  37.         self.align = align
  38.         self._backgroundcolor = backgroundcolor
  39.     
  40.     def draw(self, visRgn = None):
  41.         if self._visible:
  42.             (font, style, size, color) = self.fontsettings
  43.             fontid = GetFNum(font)
  44.             savestate = Qd.GetPenState()
  45.             Qd.TextFont(fontid)
  46.             Qd.TextFace(style)
  47.             Qd.TextSize(size)
  48.             Qd.RGBForeColor(color)
  49.             Qd.RGBBackColor(self._backgroundcolor)
  50.             TE.TETextBox(self.text, self._bounds, self.align)
  51.             Qd.RGBBackColor((0xffff, 0xffff, 0xffff))
  52.             Qd.SetPenState(savestate)
  53.     
  54.     def get(self):
  55.         return self.text
  56.     
  57.     def set(self, text):
  58.         self.text = text
  59.         if self._parentwindow and self._parentwindow.wid:
  60.             self.SetPort()
  61.             self.draw()
  62.  
  63.  
  64. class _ScrollWidget:
  65.     
  66.     # to be overridden
  67.     def getscrollbarvalues(self):
  68.         return None, None
  69.     
  70.     # internal method
  71.     def updatescrollbars(self):
  72.         vx, vy = self.getscrollbarvalues()
  73.         if self._parent._barx:
  74.             if vx <> None:
  75.                 self._parent._barx.enable(1)
  76.                 self._parent._barx.set(vx)
  77.             else:
  78.                 self._parent._barx.enable(0)
  79.         if self._parent._bary:
  80.             if vy <> None:
  81.                 self._parent._bary.enable(1)
  82.                 self._parent._bary.set(vy)
  83.             else:
  84.                 self._parent._bary.enable(0)
  85.     
  86.  
  87. UNDOLABELS = [    # Indexed by WEGetUndoInfo() value
  88.     None, "", "typing", "Cut", "Paste", "Clear", "Drag", "Style"]
  89.  
  90.  
  91. class EditText(Wbase.SelectableWidget, _ScrollWidget):
  92.     
  93.     """A text edit widget, mainly for simple entry fields."""
  94.     
  95.     def __init__(self, possize, text="", 
  96.                 callback=None, inset=(3, 3), 
  97.                 fontsettings=None,
  98.                 tabsettings = (32, 0),
  99.                 readonly = 0):
  100.         if fontsettings is None:
  101.             import W
  102.             fontsettings = W.getdefaultfont()
  103.         Wbase.SelectableWidget.__init__(self, possize)
  104.         self.temptext = text
  105.         self.ted = None
  106.         self.selection = None
  107.         self._callback = callback
  108.         self.changed = 0
  109.         self.selchanged = 0
  110.         self._selected = 0
  111.         self._enabled = 1
  112.         self.wrap = 1
  113.         self.readonly = readonly
  114.         self.fontsettings = fontsettings
  115.         self.tabsettings = tabsettings
  116.         if type(inset) <> TupleType:
  117.             self.inset = (inset, inset)
  118.         else:
  119.             self.inset = inset
  120.     
  121.     def open(self):
  122.         if not hasattr(self._parent, "_barx"):
  123.             self._parent._barx = None
  124.         if not hasattr(self._parent, "_bary"):
  125.             self._parent._bary = None
  126.         self._calcbounds()
  127.         self.SetPort()
  128.         viewrect, destrect = self._calctextbounds()
  129.         flags = self._getflags()
  130.         self.ted = waste.WENew(destrect, viewrect, flags)
  131.         self.ted.WEInstallTabHooks()
  132.         self.ted.WESetAlignment(WASTEconst.weFlushLeft)
  133.         self.setfontsettings(self.fontsettings)
  134.         self.settabsettings(self.tabsettings)
  135.         self.ted.WEUseText(Res.Resource(self.temptext))
  136.         self.ted.WECalText()
  137.         if self.selection:
  138.             self.setselection(self.selection[0], self.selection[1])
  139.             self.selection = None
  140.         else:
  141.             self.selview()
  142.         self.temptext = None
  143.         self.updatescrollbars()
  144.         self.bind("pageup", self.scrollpageup)
  145.         self.bind("pagedown", self.scrollpagedown)
  146.         self.bind("top", self.scrolltop)
  147.         self.bind("bottom", self.scrollbottom)
  148.         self.selchanged = 0
  149.     
  150.     def close(self):
  151.         self._parent._barx = None
  152.         self._parent._bary = None
  153.         self.ted = None
  154.         self.temptext = None
  155.         Wbase.SelectableWidget.close(self)
  156.     
  157.     def gettabsettings(self):
  158.         return self.tabsettings
  159.     
  160.     def settabsettings(self, (tabsize, tabmode)):
  161.         self.tabsettings = (tabsize, tabmode)
  162.         if hasattr(self.ted, "WESetTabSize"):
  163.             port = self._parentwindow.wid.GetWindowPort()
  164.             if tabmode:
  165.                 (font, style, size, color) = self.getfontsettings()
  166.                 savesettings = GetPortFontSettings(port)
  167.                 SetPortFontSettings(port, (font, style, size))
  168.                 tabsize = Qd.StringWidth(' ' * tabsize)
  169.                 SetPortFontSettings(port, savesettings)
  170.             tabsize = max(tabsize, 1)
  171.             self.ted.WESetTabSize(tabsize)
  172.             self.SetPort()
  173.             Qd.EraseRect(self.ted.WEGetViewRect())
  174.             self.ted.WEUpdate(port.visRgn)
  175.     
  176.     def getfontsettings(self):
  177.         import Res
  178.         (font, style, size, color) = self.ted.WEGetRunInfo(0)[4]
  179.         font = Fm.GetFontName(font)
  180.         return (font, style, size, color)
  181.     
  182.     def setfontsettings(self, (font, style, size, color)):
  183.         self.SetPort()
  184.         if type(font) <> StringType:
  185.             font = Fm.GetFontName(font)
  186.         self.fontsettings = (font, style, size, color)
  187.         fontid = GetFNum(font)
  188.         readonly = self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, -1)
  189.         if readonly:
  190.             self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 0)
  191.         try:
  192.             self.ted.WEFeatureFlag(WASTEconst.weFInhibitRecal, 1)
  193.             selstart, selend = self.ted.WEGetSelection()
  194.             self.ted.WESetSelection(0, self.ted.WEGetTextLength())
  195.             self.ted.WESetStyle(WASTEconst.weDoFace, (0, 0, 0, (0, 0, 0)))
  196.             self.ted.WESetStyle(WASTEconst.weDoFace | 
  197.                         WASTEconst.weDoColor | 
  198.                         WASTEconst.weDoFont | 
  199.                         WASTEconst.weDoSize, 
  200.                         (fontid, style, size, color))
  201.             self.ted.WEFeatureFlag(WASTEconst.weFInhibitRecal, 0)
  202.             self.ted.WECalText()
  203.             self.ted.WESetSelection(selstart, selend)
  204.         finally:
  205.             if readonly:
  206.                 self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 1)
  207.         viewrect = self.ted.WEGetViewRect()
  208.         Qd.EraseRect(viewrect)
  209.         self.ted.WEUpdate(self._parentwindow.wid.GetWindowPort().visRgn)
  210.         self.selchanged = 1
  211.         self.updatescrollbars()
  212.     
  213.     def adjust(self, oldbounds):
  214.         self.SetPort()
  215.         if self._selected and self._parentwindow._hasselframes:
  216.             Win.InvalRect(Qd.InsetRect(oldbounds, -3, -3))
  217.             Win.InvalRect(Qd.InsetRect(self._bounds, -3, -3))
  218.         else:
  219.             Win.InvalRect(oldbounds)
  220.             Win.InvalRect(self._bounds)
  221.         viewrect, destrect = self._calctextbounds()
  222.         self.ted.WESetViewRect(viewrect)
  223.         self.ted.WESetDestRect(destrect)
  224.         if self.wrap:
  225.             self.ted.WECalText()
  226.         if self.ted.WEGetDestRect()[3] < viewrect[1]:
  227.             self.selview()
  228.         self.updatescrollbars()
  229.     
  230.     # interface -----------------------
  231.     # selection stuff
  232.     def selview(self):
  233.         self.ted.WESelView()
  234.     
  235.     def selectall(self):
  236.         self.ted.WESetSelection(0, self.ted.WEGetTextLength())
  237.         self.selchanged = 1
  238.         self.updatescrollbars()
  239.     
  240.     def selectline(self, lineno, charoffset = 0):
  241.         newselstart, newselend = self.ted.WEGetLineRange(lineno)
  242.         # Autoscroll makes the *end* of the selection visible, which, 
  243.         # in the case of a whole line, is the beginning of the *next* line. 
  244.         # So sometimes it leaves our line just above the view rect. 
  245.         # Let's fool Waste by initially selecting one char less:
  246.         self.ted.WESetSelection(newselstart + charoffset, newselend-1)
  247.         self.ted.WESetSelection(newselstart + charoffset, newselend)
  248.         self.selchanged = 1
  249.         self.updatescrollbars()
  250.     
  251.     def getselection(self):
  252.         if self.ted:
  253.             return self.ted.WEGetSelection()
  254.         else:
  255.             return self.selection
  256.     
  257.     def setselection(self, selstart, selend):
  258.         self.selchanged = 1
  259.         if self.ted:
  260.             self.ted.WESetSelection(selstart, selend)
  261.             self.ted.WESelView()
  262.             self.updatescrollbars()
  263.         else:
  264.             self.selection = selstart, selend
  265.     
  266.     def offsettoline(self, offset):
  267.         return self.ted.WEOffsetToLine(offset)
  268.     
  269.     def countlines(self):
  270.         return self.ted.WECountLines()
  271.     
  272.     def getselectedtext(self):
  273.         selstart, selend = self.ted.WEGetSelection()
  274.         return self.ted.WEGetText().data[selstart:selend]
  275.     
  276.     def expandselection(self):
  277.         oldselstart, oldselend = self.ted.WEGetSelection()
  278.         selstart, selend = min(oldselstart, oldselend), max(oldselstart, oldselend)
  279.         if selstart <> selend and chr(self.ted.WEGetChar(selend-1)) == '\r':
  280.             selend = selend - 1
  281.         newselstart, dummy = self.ted.WEFindLine(selstart, 0)
  282.         dummy, newselend = self.ted.WEFindLine(selend, 0)
  283.         if oldselstart <> newselstart or  oldselend <> newselend:
  284.             self.ted.WESetSelection(newselstart, newselend)
  285.             self.updatescrollbars()
  286.         self.selchanged = 1
  287.     
  288.     def insert(self, text):
  289.         self.ted.WEInsert(text, None, None)
  290.         self.changed = 1
  291.         self.selchanged = 1
  292.     
  293.     # text
  294.     def set(self, text):
  295.         if not self.ted:
  296.             self.temptext = text
  297.         else:
  298.             self.ted.WEUseText(Res.Resource(text))
  299.             self.ted.WECalText()
  300.             self.SetPort()
  301.             viewrect, destrect = self._calctextbounds()
  302.             self.ted.WESetViewRect(viewrect)
  303.             self.ted.WESetDestRect(destrect)
  304.             rgn = Qd.NewRgn()
  305.             Qd.RectRgn(rgn, viewrect)
  306.             Qd.EraseRect(viewrect)
  307.             self.draw(rgn)
  308.             #Win.InvalRect(self.ted.WEGetViewRect())
  309.             self.updatescrollbars()
  310.     
  311.     def get(self):
  312.         if not self._parent:
  313.             return self.temptext
  314.         else:
  315.             return self.ted.WEGetText().data
  316.     
  317.     # events
  318.     def key(self, char, event):
  319.         (what, message, when, where, modifiers) = event
  320.         if self._enabled and not modifiers & Events.cmdKey or char in Wkeys.arrowkeys:
  321.             self.ted.WEKey(ord(char), modifiers)
  322.             if char not in Wkeys.navigationkeys:
  323.                 self.changed = 1
  324.             if char not in Wkeys.scrollkeys:
  325.                 self.selchanged = 1
  326.             self.updatescrollbars()
  327.             if self._callback:
  328.                 Wbase.CallbackCall(self._callback, 0, char, modifiers)
  329.     
  330.     def click(self, point, modifiers):
  331.         if not self._enabled:
  332.             return
  333.         self.ted.WEClick(point, modifiers, Evt.TickCount())
  334.         self.selchanged = 1
  335.         self.updatescrollbars()
  336.         return 1
  337.     
  338.     def idle(self):
  339.         self.SetPort()
  340.         self.ted.WEIdle()
  341.     
  342.     def rollover(self, point, onoff):
  343.         if onoff:
  344.             Wbase.SetCursor("iBeam")
  345.     
  346.     def activate(self, onoff):
  347.         self._activated = onoff
  348.         if self._selected and self._visible:
  349.             if onoff:
  350.                 self.ted.WEActivate()
  351.             else:
  352.                 self.ted.WEDeactivate()
  353.             if self._selected:
  354.                 self.drawselframe(onoff)
  355.     
  356.     def select(self, onoff, isclick = 0):
  357.         if Wbase.SelectableWidget.select(self, onoff):
  358.             return
  359.         self.SetPort()
  360.         if onoff:
  361.             self.ted.WEActivate()
  362.             if self._parentwindow._tabbable and not isclick:
  363.                 self.selectall()
  364.         else:
  365.             self.ted.WEDeactivate()
  366.         self.drawselframe(onoff)
  367.     
  368.     def draw(self, visRgn = None):
  369.         if self._visible:
  370.             if not visRgn:
  371.                 visRgn = self._parentwindow.wid.GetWindowPort().visRgn
  372.             self.ted.WEUpdate(visRgn)
  373.             if self._selected and self._activated:
  374.                 self.drawselframe(1)
  375.             Qd.FrameRect(self._bounds)
  376.     
  377.     # scrolling
  378.     def scrollpageup(self):
  379.         if self._parent._bary and self._parent._bary._enabled:
  380.             self.vscroll("++")
  381.     
  382.     def scrollpagedown(self):
  383.         if self._parent._bary and self._parent._bary._enabled:
  384.             self.vscroll("--")
  385.     
  386.     def scrolltop(self):
  387.         if self._parent._bary and self._parent._bary._enabled:
  388.             self.vscroll(0)
  389.         if self._parent._barx and self._parent._barx._enabled:
  390.             self.hscroll(0)
  391.     
  392.     def scrollbottom(self):
  393.         if self._parent._bary and self._parent._bary._enabled:
  394.             self.vscroll(32767)
  395.     
  396.     # menu handlers
  397.     def domenu_copy(self, *args):
  398.         selbegin, selend = self.ted.WEGetSelection()
  399.         if selbegin == selend:
  400.             return
  401.         Scrap.ZeroScrap()
  402.         self.ted.WECopy()
  403.         self.updatescrollbars()
  404.     
  405.     def domenu_cut(self, *args):
  406.         selbegin, selend = self.ted.WEGetSelection()
  407.         if selbegin == selend:
  408.             return
  409.         Scrap.ZeroScrap()
  410.         self.ted.WECut()
  411.         self.updatescrollbars()
  412.         self.selview()
  413.         self.changed = 1
  414.         self.selchanged = 1
  415.         if self._callback:
  416.             Wbase.CallbackCall(self._callback, 0, "", None)
  417.     
  418.     def domenu_paste(self, *args):
  419.         if not self.ted.WECanPaste():
  420.             return
  421.         self.selview()
  422.         self.ted.WEPaste()
  423.         self.updatescrollbars()
  424.         self.changed = 1
  425.         self.selchanged = 1
  426.         if self._callback:
  427.             Wbase.CallbackCall(self._callback, 0, "", None)
  428.     
  429.     def domenu_clear(self, *args):
  430.         self.ted.WEDelete()
  431.         self.selview()
  432.         self.updatescrollbars()
  433.         self.changed = 1
  434.         self.selchanged = 1
  435.         if self._callback:
  436.             Wbase.CallbackCall(self._callback, 0, "", None)
  437.     
  438.     def domenu_undo(self, *args):
  439.         which, redo = self.ted.WEGetUndoInfo()
  440.         if not which: 
  441.             return
  442.         self.ted.WEUndo()
  443.         self.updatescrollbars()
  444.         self.changed = 1
  445.         self.selchanged = 1
  446.         if self._callback:
  447.             Wbase.CallbackCall(self._callback, 0, "", None)
  448.     
  449.     def can_undo(self, menuitem):
  450.         #doundo = self.ted.WEFeatureFlag(WASTEconst.weFUndo, -1)
  451.         #print doundo
  452.         #if not doundo:
  453.         #    return 0
  454.         which, redo = self.ted.WEGetUndoInfo()
  455.         which = UNDOLABELS[which]
  456.         if which == None: 
  457.             return None
  458.         if redo:
  459.             which = "Redo "+which
  460.         else:
  461.             which = "Undo "+which
  462.         menuitem.settext(which)
  463.         return 1
  464.     
  465.     def domenu_selectall(self, *args):
  466.         self.selectall()
  467.     
  468.     # private
  469.     def getscrollbarvalues(self):
  470.         dr = self.ted.WEGetDestRect()
  471.         vr = self.ted.WEGetViewRect()
  472.         vx = Wcontrols._scalebarvalue(dr[0], dr[2], vr[0], vr[2])
  473.         vy = Wcontrols._scalebarvalue(dr[1], dr[3], vr[1], vr[3])
  474.         return vx, vy
  475.     
  476.     def vscroll(self, value):
  477.         lineheight = self.ted.WEGetHeight(0, 1)
  478.         dr = self.ted.WEGetDestRect()
  479.         vr = self.ted.WEGetViewRect()
  480.         destheight = dr[3] - dr[1]
  481.         viewheight = vr[3] - vr[1]
  482.         viewoffset = maxdelta = vr[1] - dr[1]
  483.         mindelta = vr[3] - dr[3]
  484.         if value == "+":
  485.             delta = lineheight
  486.         elif value == "-":
  487.             delta = - lineheight
  488.         elif value == "++":
  489.             delta = viewheight - lineheight
  490.         elif value == "--":
  491.             delta = lineheight - viewheight
  492.         else:    # in thumb
  493.             cur = (32767L * viewoffset) / (destheight - viewheight)
  494.             delta = (cur-value)*(destheight - viewheight)/32767
  495.             if abs(delta - viewoffset) <=2:
  496.                 # compensate for irritating rounding error
  497.                 delta = viewoffset
  498.         delta = min(maxdelta, delta)
  499.         delta = max(mindelta, delta)
  500.         self.ted.WEScroll(0, delta)
  501.         self.updatescrollbars()
  502.     
  503.     def hscroll(self, value):
  504.         dr = self.ted.WEGetDestRect()
  505.         vr = self.ted.WEGetViewRect()
  506.         destwidth = dr[2] - dr[0]
  507.         viewwidth = vr[2] - vr[0]
  508.         viewoffset = maxdelta = vr[0] - dr[0]
  509.         mindelta = vr[2] - dr[2]
  510.         if value == "+":
  511.             delta = 32
  512.         elif value == "-":
  513.             delta = - 32
  514.         elif value == "++":
  515.             delta = 0.5 * (vr[2] - vr[0])
  516.         elif value == "--":
  517.             delta = 0.5 * (vr[0] - vr[2])
  518.         else:    # in thumb
  519.             cur = (32767 * viewoffset) / (destwidth - viewwidth)
  520.             delta = (cur-value)*(destwidth - viewwidth)/32767
  521.             if abs(delta - viewoffset) <=2:
  522.                 # compensate for irritating rounding error
  523.                 delta = viewoffset
  524.         delta = min(maxdelta, delta)
  525.         delta = max(mindelta, delta)
  526.         self.ted.WEScroll(delta, 0)
  527.         self.updatescrollbars()
  528.     
  529.     # some internals
  530.     def _getflags(self):
  531.         flags = WASTEconst.weDoAutoScroll | WASTEconst.weDoMonoStyled
  532.         if self.readonly:
  533.             flags = flags | WASTEconst.weDoReadOnly
  534.         else:
  535.             flags = flags | WASTEconst.weDoUndo
  536.         return flags
  537.     
  538.     def _getviewrect(self):
  539.         return Qd.InsetRect(self._bounds, self.inset[0], self.inset[1])
  540.     
  541.     def _calctextbounds(self):
  542.         viewrect = l, t, r, b = self._getviewrect()
  543.         if self.ted:
  544.             dl, dt, dr, db = self.ted.WEGetDestRect()
  545.             vl, vt, vr, vb = self.ted.WEGetViewRect()
  546.             yshift = t - vt
  547.             if (db - dt) < (b - t):
  548.                 destrect = viewrect
  549.             else:
  550.                 destrect = l, dt + yshift, r, db + yshift
  551.         else:
  552.             destrect = viewrect
  553.         return viewrect, destrect
  554.         
  555.  
  556. class TextEditor(EditText):
  557.     
  558.     """A text edit widget."""
  559.     
  560.     def __init__(self, possize, text="", callback=None, wrap=1, inset=(4, 4),
  561.                 fontsettings=None,
  562.                 tabsettings=(32, 0),
  563.                 readonly=0):
  564.         EditText.__init__(self, possize, text, callback, inset, fontsettings, tabsettings, readonly)
  565.         self.wrap = wrap
  566.     
  567.     def _getflags(self):
  568.         flags = WASTEconst.weDoAutoScroll | WASTEconst.weDoMonoStyled | \
  569.                 WASTEconst.weDoOutlineHilite
  570.         if self.readonly:
  571.             flags = flags | WASTEconst.weDoReadOnly
  572.         else:
  573.             flags = flags | WASTEconst.weDoUndo
  574.         return flags
  575.     
  576.     def _getviewrect(self):
  577.         l, t, r, b = self._bounds
  578.         return (l + 5, t + 2, r, b - 2)
  579.     
  580.     def _calctextbounds(self):
  581.         if self.wrap:
  582.             return EditText._calctextbounds(self)
  583.         else:
  584.             viewrect = l, t, r, b = self._getviewrect()
  585.             if self.ted:
  586.                 dl, dt, dr, db = self.ted.WEGetDestRect()
  587.                 vl, vt, vr, vb = self.ted.WEGetViewRect()
  588.                 xshift = l - vl
  589.                 yshift = t - vt
  590.                 if (db - dt) < (b - t):
  591.                     yshift = t - dt
  592.                 destrect = (dl + xshift, dt + yshift, dr + xshift, db + yshift)
  593.             else:
  594.                 destrect = (l, t, r + 5000, b)
  595.             return viewrect, destrect
  596.     
  597.     def draw(self, visRgn = None):
  598.         if self._visible:
  599.             if not visRgn:
  600.                 visRgn = self._parentwindow.wid.GetWindowPort().visRgn
  601.             self.ted.WEUpdate(visRgn)
  602.             if self._selected and self._activated:
  603.                 self.drawselframe(1)
  604.  
  605.  
  606. import regex
  607. commentPat = regex.compile("[ \t]*\(#\)")
  608. indentPat = regex.compile("\t*")
  609.  
  610. class PyEditor(TextEditor):
  611.     
  612.     """A specialized Python source edit widget"""
  613.     
  614.     def __init__(self, possize, text="", callback=None, inset=(4, 4),
  615.                 fontsettings=None,
  616.                 tabsettings=(32, 0),
  617.                 readonly=0,
  618.                 debugger=None,
  619.                 file=''):
  620.         TextEditor.__init__(self, possize, text, callback, 0, inset, fontsettings, tabsettings, readonly)
  621.         self.bind("cmd[", self.domenu_shiftleft)
  622.         self.bind("cmd]", self.domenu_shiftright)
  623.         self.bind("cmdshift[", self.domenu_uncomment)
  624.         self.bind("cmdshift]", self.domenu_comment)
  625.         self.file = file    # only for debugger reference
  626.         self._debugger = debugger
  627.         if debugger:
  628.             debugger.register_editor(self, self.file)
  629.     
  630.     def domenu_shiftleft(self):
  631.         self.expandselection()
  632.         selstart, selend = self.ted.WEGetSelection()
  633.         selstart, selend = min(selstart, selend), max(selstart, selend)
  634.         snippet = self.getselectedtext()
  635.         lines = string.split(snippet, '\r')
  636.         for i in range(len(lines)):
  637.             if lines[i][:1] == '\t':
  638.                 lines[i] = lines[i][1:]
  639.         snippet = string.join(lines, '\r')
  640.         self.insert(snippet)
  641.         self.ted.WESetSelection(selstart, selstart + len(snippet))
  642.     
  643.     def domenu_shiftright(self):
  644.         self.expandselection()
  645.         selstart, selend = self.ted.WEGetSelection()
  646.         selstart, selend = min(selstart, selend), max(selstart, selend)
  647.         snippet = self.getselectedtext()
  648.         lines = string.split(snippet, '\r')
  649.         for i in range(len(lines) - (not lines[-1])):
  650.             lines[i] = '\t' + lines[i]
  651.         snippet = string.join(lines, '\r')
  652.         self.insert(snippet)
  653.         self.ted.WESetSelection(selstart, selstart + len(snippet))
  654.     
  655.     def domenu_uncomment(self):
  656.         self.expandselection()
  657.         selstart, selend = self.ted.WEGetSelection()
  658.         selstart, selend = min(selstart, selend), max(selstart, selend)
  659.         snippet = self.getselectedtext()
  660.         lines = string.split(snippet, '\r')
  661.         for i in range(len(lines)):
  662.             res = commentPat.match(lines[i]) >= 0
  663.             if res > 0:
  664.                 pos = commentPat.regs[1][0]
  665.                 lines[i] = lines[i][:pos] + lines[i][pos+1:]
  666.         snippet = string.join(lines, '\r')
  667.         self.insert(snippet)
  668.         self.ted.WESetSelection(selstart, selstart + len(snippet))
  669.     
  670.     def domenu_comment(self):
  671.         self.expandselection()
  672.         selstart, selend = self.ted.WEGetSelection()
  673.         selstart, selend = min(selstart, selend), max(selstart, selend)
  674.         snippet = self.getselectedtext()
  675.         lines = string.split(snippet, '\r')
  676.         indent = 3000 # arbitrary large number...
  677.         for line in lines:
  678.             if string.strip(line):
  679.                 if indentPat.match(line):
  680.                     indent = min(indent, indentPat.regs[0][1])
  681.                 else:
  682.                     indent = 0
  683.                     break
  684.         for i in range(len(lines) - (not lines[-1])):
  685.             lines[i] = lines[i][:indent] + "#" + lines[i][indent:]
  686.         snippet = string.join(lines, '\r')
  687.         self.insert(snippet)
  688.         self.ted.WESetSelection(selstart, selstart + len(snippet))
  689.     
  690.     def setfile(self, file):
  691.         self.file = file
  692.     
  693.     def set(self, text, file = ''):
  694.         oldfile = self.file
  695.         self.file = file
  696.         if self._debugger:
  697.             self._debugger.unregister_editor(self, oldfile)
  698.             self._debugger.register_editor(self, file)
  699.         TextEditor.set(self, text)
  700.     
  701.     def close(self):
  702.         if self._debugger:
  703.             self._debugger.unregister_editor(self, self.file)
  704.             self._debugger = None
  705.         TextEditor.close(self)        
  706.     
  707.     def click(self, point, modifiers):
  708.         if not self._enabled:
  709.             return
  710.         if self._debugger and self.pt_in_breaks(point):
  711.             self.breakhit(point, modifiers)
  712.         elif self._debugger:
  713.             bl, bt, br, bb = self._getbreakrect()
  714.             Qd.EraseRect((bl, bt, br-1, bb))
  715.             TextEditor.click(self, point, modifiers)
  716.             self.drawbreakpoints()
  717.         else:
  718.             TextEditor.click(self, point, modifiers)
  719.             if self.ted.WEGetClickCount() >= 3:
  720.                 # select block with our indent
  721.                 lines = string.split(self.get(), '\r')
  722.                 selstart, selend = self.ted.WEGetSelection()
  723.                 lineno = self.ted.WEOffsetToLine(selstart)
  724.                 tabs = 0
  725.                 line = lines[lineno]
  726.                 while line[tabs:] and line[tabs] == '\t':
  727.                     tabs = tabs + 1
  728.                 tabstag = '\t' * tabs
  729.                 fromline = 0
  730.                 toline = len(lines)
  731.                 if tabs:
  732.                     for i in range(lineno - 1, -1, -1):
  733.                         line = lines[i]
  734.                         if line[:tabs] <> tabstag:
  735.                             fromline = i + 1
  736.                             break
  737.                     for i in range(lineno + 1, toline):
  738.                         line = lines[i]
  739.                         if line[:tabs] <> tabstag:
  740.                             toline = i - 1
  741.                             break
  742.                 selstart, dummy = self.ted.WEGetLineRange(fromline)
  743.                 dummy, selend = self.ted.WEGetLineRange(toline)
  744.                 self.ted.WESetSelection(selstart, selend)
  745.     
  746.     def breakhit(self, point, modifiers):
  747.         if not self.file:
  748.             return
  749.         destrect = self.ted.WEGetDestRect()
  750.         offset, edge = self.ted.WEGetOffset(point)
  751.         lineno = self.ted.WEOffsetToLine(offset) + 1
  752.         if point[1] <= destrect[3]:
  753.             self._debugger.clear_breaks_above(self.file, self.countlines())
  754.             self._debugger.toggle_break(self.file, lineno)
  755.         else:
  756.             self._debugger.clear_breaks_above(self.file, lineno)
  757.     
  758.     def key(self, char, event):
  759.         (what, message, when, where, modifiers) = event
  760.         if modifiers & Events.cmdKey and not char in Wkeys.arrowkeys:
  761.             return
  762.         if char == '\r':
  763.             selstart, selend = self.ted.WEGetSelection()
  764.             selstart, selend = min(selstart, selend), max(selstart, selend)
  765.             lastchar = chr(self.ted.WEGetChar(selstart-1))
  766.             if lastchar <> '\r' and selstart:
  767.                 pos, dummy = self.ted.WEFindLine(selstart, 0)
  768.                 lineres = Res.Resource('')
  769.                 self.ted.WECopyRange(pos, selstart, lineres, None, None)
  770.                 line = lineres.data + '\n'
  771.                 tabcount = self.extratabs(line)
  772.                 self.ted.WEKey(ord('\r'), 0)
  773.                 for i in range(tabcount):
  774.                     self.ted.WEKey(ord('\t'), 0)
  775.             else:
  776.                 self.ted.WEKey(ord('\r'), 0)
  777.         elif char in ')]}':
  778.             self.ted.WEKey(ord(char), modifiers)
  779.             self.balanceparens(char)
  780.         else:
  781.             self.ted.WEKey(ord(char), modifiers)
  782.         if char not in Wkeys.navigationkeys:
  783.             self.changed = 1
  784.         self.selchanged = 1
  785.         self.updatescrollbars()
  786.     
  787.     def balanceparens(self, char):
  788.         if char == ')':
  789.             target = '('
  790.         elif char == ']':
  791.             target = '['
  792.         elif char == '}':
  793.             target = '{'
  794.         recursionlevel = 1
  795.         selstart, selend = self.ted.WEGetSelection()
  796.         count = min(selstart, selend) - 2
  797.         mincount = max(0, count - 2048)
  798.         lastquote = None
  799.         while count > mincount:
  800.             testchar = chr(self.ted.WEGetChar(count))
  801.             if testchar in "\"'" and chr(self.ted.WEGetChar(count - 1)) <> '\\':
  802.                 if lastquote == testchar:
  803.                     recursionlevel = recursionlevel - 1
  804.                     lastquote = None
  805.                 elif not lastquote:
  806.                     recursionlevel = recursionlevel + 1
  807.                     lastquote = testchar
  808.             elif not lastquote and testchar == char:
  809.                 recursionlevel = recursionlevel + 1
  810.             elif not lastquote and testchar == target:
  811.                 recursionlevel = recursionlevel - 1
  812.                 if recursionlevel == 0:
  813.                     import time
  814.                     autoscroll = self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, -1)
  815.                     if autoscroll:
  816.                         self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 0)
  817.                     self.ted.WESetSelection(count, count + 1)
  818.                     time.sleep(0.2)
  819.                     self.ted.WESetSelection(selstart, selend)
  820.                     if autoscroll:
  821.                         self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 1)
  822.                     break
  823.             count = count - 1
  824.     
  825.     def extratabs(self, line):
  826.         tabcount = 0
  827.         for c in line:
  828.             if c <> '\t':
  829.                 break
  830.             tabcount = tabcount + 1
  831.         last = 0
  832.         cleanline = ''
  833.         tags = PyFontify.fontify(line)
  834.         # strip comments and strings
  835.         for tag, start, end, sublist in tags:
  836.             if tag in ('string', 'comment'):
  837.                 cleanline = cleanline + line[last:start]
  838.                 last = end
  839.         cleanline = cleanline + line[last:]
  840.         cleanline = string.strip(cleanline)
  841.         if cleanline and cleanline[-1] == ':':
  842.             tabcount = tabcount + 1
  843.         else:
  844.             # extra indent after unbalanced (, [ or {
  845.             for open, close in (('(', ')'), ('[', ']'), ('{', '}')):
  846.                 count = string.count(cleanline, open)
  847.                 if count and count > string.count(cleanline, close):
  848.                     tabcount = tabcount + 2
  849.                     break
  850.         return tabcount
  851.     
  852.     def rollover(self, point, onoff):
  853.         if onoff:
  854.             if self._debugger and self.pt_in_breaks(point):
  855.                 Wbase.SetCursor("arrow")
  856.             else:
  857.                 Wbase.SetCursor("iBeam")
  858.     
  859.     def draw(self, visRgn = None):
  860.         TextEditor.draw(self, visRgn)
  861.         if self._debugger:
  862.             self.drawbreakpoints()
  863.     
  864.     def showbreakpoints(self, onoff):
  865.         if (not not self._debugger) <> onoff:
  866.             if onoff:
  867.                 if not __debug__:
  868.                     import W
  869.                     raise W.AlertError, "Can’t debug in “Optimize bytecode” mode.\r(see “Default startup options” in EditPythonPreferences)"
  870.                 import PyDebugger
  871.                 self._debugger = PyDebugger.getdebugger()
  872.                 self._debugger.register_editor(self, self.file)
  873.             elif self._debugger:
  874.                 self._debugger.unregister_editor(self, self.file)
  875.                 self._debugger = None
  876.             self.adjust(self._bounds)
  877.     
  878.     def togglebreakpoints(self):
  879.         self.showbreakpoints(not self._debugger)
  880.     
  881.     def clearbreakpoints(self):
  882.         if self.file:
  883.             self._debugger.clear_all_file_breaks(self.file)
  884.     
  885.     def editbreakpoints(self):
  886.         if self._debugger:
  887.             self._debugger.edit_breaks()
  888.             self._debugger.breaksviewer.selectfile(self.file)
  889.     
  890.     def drawbreakpoints(self, eraseall = 0):
  891.         breakrect = bl, bt, br, bb = self._getbreakrect()
  892.         br = br - 1
  893.         self.SetPort()
  894.         Qd.PenPat(Qd.qd.gray)
  895.         Qd.PaintRect((br, bt, br + 1, bb))
  896.         Qd.PenNormal()
  897.         self._parentwindow.tempcliprect(breakrect)
  898.         Qd.RGBForeColor((0xffff, 0, 0))
  899.         try:
  900.             lasttop = bt
  901.             self_ted = self.ted
  902.             Qd_PaintOval = Qd.PaintOval
  903.             Qd_EraseRect = Qd.EraseRect
  904.             for lineno in self._debugger.get_file_breaks(self.file):
  905.                 start, end = self_ted.WEGetLineRange(lineno - 1)
  906.                 if lineno <> self_ted.WEOffsetToLine(start) + 1:
  907.                     # breakpoints beyond our text: erase rest, and back out
  908.                     Qd_EraseRect((bl, lasttop, br, bb))
  909.                     break
  910.                 (x, y), h = self_ted.WEGetPoint(start, 0)
  911.                 bottom = y + h
  912.                 #print y, (lasttop, bottom)
  913.                 if bottom > lasttop:
  914.                     Qd_EraseRect((bl, lasttop, br, y + h * eraseall))
  915.                     lasttop = bottom
  916.                 redbullet = bl + 2, y + 3, bl + 8, y + 9
  917.                 Qd_PaintOval(redbullet)
  918.             else:
  919.                 Qd_EraseRect((bl, lasttop, br, bb))
  920.             Qd.RGBForeColor((0, 0, 0))
  921.         finally:
  922.             self._parentwindow.restoreclip()
  923.     
  924.     def updatescrollbars(self):
  925.         if self._debugger:
  926.             self.drawbreakpoints(1)
  927.         TextEditor.updatescrollbars(self)
  928.     
  929.     def pt_in_breaks(self, point):
  930.         return Qd.PtInRect(point, self._getbreakrect())
  931.     
  932.     def _getbreakrect(self):
  933.         if self._debugger:
  934.             l, t, r, b = self._bounds
  935.             return (l+1, t+1, l + 12, b-1)
  936.         else:
  937.             return (0, 0, 0, 0)
  938.     
  939.     def _getviewrect(self):
  940.         l, t, r, b = self._bounds
  941.         if self._debugger:
  942.             return (l + 17, t + 2, r, b - 2)
  943.         else:
  944.             return (l + 5, t + 2, r, b - 2)
  945.     
  946.     def _calctextbounds(self):
  947.         viewrect = l, t, r, b = self._getviewrect()
  948.         if self.ted:
  949.             dl, dt, dr, db = self.ted.WEGetDestRect()
  950.             vl, vt, vr, vb = self.ted.WEGetViewRect()
  951.             xshift = l - vl
  952.             yshift = t - vt
  953.             if (db - dt) < (b - t):
  954.                 yshift = t - dt
  955.             destrect = (dl + xshift, dt + yshift, dr + xshift, db + yshift)
  956.         else:
  957.             destrect = (l, t, r + 5000, b)
  958.         return viewrect, destrect
  959.  
  960.  
  961. def GetFNum(fontname):
  962.     """Same as Fm.GetFNum(), but maps a missing font to Monaco instead of the system font."""
  963.     if fontname <> Fm.GetFontName(0):
  964.         fontid = Fm.GetFNum(fontname)
  965.         if fontid == 0:
  966.             fontid = Fonts.monaco
  967.     else:
  968.         fontid = 0
  969.     return fontid
  970.  
  971. # b/w compat. Anyone using this?
  972. GetFName = Fm.GetFontName
  973.  
  974. def GetPortFontSettings(port):
  975.     return Fm.GetFontName(port.txFont), port.txFace, port.txSize
  976.  
  977. def SetPortFontSettings(port, (font, face, size)):
  978.     saveport = Qd.GetPort()
  979.     Qd.SetPort(port)
  980.     Qd.TextFont(GetFNum(font))
  981.     Qd.TextFace(face)
  982.     Qd.TextSize(size)
  983.     Qd.SetPort(saveport)
  984.