blob: 8c1662d598d6707046b2465db323656669e960b7 [file] [log] [blame]
Just van Rossum40f9b7b1999-01-30 22:39:17 +00001import Qd
2import TE
3import Fm
4import waste
5import WASTEconst
6import Res
7import Evt
8import Events
9import Scrap
10import string
11
12import Win
13import Wbase
14import Wkeys
15import Wcontrols
16import PyFontify
17from types import *
18import Fonts
19import TextEdit
20
21
22
23class TextBox(Wbase.Widget):
24
25 """A static text widget"""
26
Just van Rossumb7ad8211999-09-26 12:21:32 +000027 def __init__(self, possize, text="", align=TextEdit.teJustLeft,
28 fontsettings=None,
29 backgroundcolor=(0xffff, 0xffff, 0xffff)
Just van Rossum40f9b7b1999-01-30 22:39:17 +000030 ):
Just van Rossumb7ad8211999-09-26 12:21:32 +000031 if fontsettings is None:
32 import W
33 fontsettings = W.getdefaultfont()
Just van Rossum40f9b7b1999-01-30 22:39:17 +000034 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
64class _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
87UNDOLABELS = [ # Indexed by WEGetUndoInfo() value
88 None, "", "typing", "Cut", "Paste", "Clear", "Drag", "Style"]
89
90
91class EditText(Wbase.SelectableWidget, _ScrollWidget):
92
93 """A text edit widget, mainly for simple entry fields."""
94
Just van Rossumb7ad8211999-09-26 12:21:32 +000095 def __init__(self, possize, text="",
96 callback=None, inset=(3, 3),
97 fontsettings=None,
Just van Rossum40f9b7b1999-01-30 22:39:17 +000098 tabsettings = (32, 0),
99 readonly = 0):
Just van Rossumb7ad8211999-09-26 12:21:32 +0000100 if fontsettings is None:
101 import W
102 fontsettings = W.getdefaultfont()
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000103 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:
Jack Jansen73023402001-01-23 14:58:20 +0000216 self.GetWindow().InvalWindowRect(Qd.InsetRect(oldbounds, -3, -3))
217 self.GetWindow().InvalWindowRect(Qd.InsetRect(self._bounds, -3, -3))
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000218 else:
Jack Jansen73023402001-01-23 14:58:20 +0000219 self.GetWindow().InvalWindowRect(oldbounds)
220 self.GetWindow().InvalWindowRect(self._bounds)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000221 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)
Jack Jansen73023402001-01-23 14:58:20 +0000308 #self.GetWindow().InvalWindowRect(self.ted.WEGetViewRect())
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000309 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
Just van Rossum6c487c41999-04-22 22:16:58 +0000493 cur = (32767L * viewoffset) / (destheight - viewheight)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000494 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
556class TextEditor(EditText):
557
558 """A text edit widget."""
559
Just van Rossumb7ad8211999-09-26 12:21:32 +0000560 def __init__(self, possize, text="", callback=None, wrap=1, inset=(4, 4),
561 fontsettings=None,
562 tabsettings=(32, 0),
563 readonly=0):
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000564 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
606import regex
607commentPat = regex.compile("[ \t]*\(#\)")
608indentPat = regex.compile("\t*")
609
610class PyEditor(TextEditor):
611
612 """A specialized Python source edit widget"""
613
Just van Rossumb7ad8211999-09-26 12:21:32 +0000614 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=''):
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000620 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
Just van Rossumedab9391999-02-02 22:31:05 +0000869 raise W.AlertError, "Can¹t debug in ³Optimize bytecode² mode.\r(see ³Default startup options² in EditPythonPreferences)"
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000870 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
961def 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?
972GetFName = Fm.GetFontName
973
974def GetPortFontSettings(port):
975 return Fm.GetFontName(port.txFont), port.txFace, port.txSize
976
977def 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)