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