]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/STCTextEditor.py
Docview and IDE patch from Morag Hua with fix for bug #1217890
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / STCTextEditor.py
1 #----------------------------------------------------------------------------
2 # Name: STCTextEditor.py
3 # Purpose: Text Editor for wx.lib.pydocview tbat uses the Styled Text Control
4 #
5 # Author: Peter Yared, Morgan Hua
6 #
7 # Created: 8/10/03
8 # CVS-ID: $Id$
9 # Copyright: (c) 2003-2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
12
13 import wx
14 import wx.stc
15 import wx.lib.docview
16 import wx.lib.multisash
17 import wx.lib.pydocview
18 import string
19 import FindService
20 import os
21 import sys
22 _ = wx.GetTranslation
23
24 #----------------------------------------------------------------------------
25 # Constants
26 #----------------------------------------------------------------------------
27
28 TEXT_ID = wx.NewId()
29 VIEW_WHITESPACE_ID = wx.NewId()
30 VIEW_EOL_ID = wx.NewId()
31 VIEW_INDENTATION_GUIDES_ID = wx.NewId()
32 VIEW_RIGHT_EDGE_ID = wx.NewId()
33 VIEW_LINE_NUMBERS_ID = wx.NewId()
34 ZOOM_ID = wx.NewId()
35 ZOOM_NORMAL_ID = wx.NewId()
36 ZOOM_IN_ID = wx.NewId()
37 ZOOM_OUT_ID = wx.NewId()
38 CHOOSE_FONT_ID = wx.NewId()
39 WORD_WRAP_ID = wx.NewId()
40 TEXT_STATUS_BAR_ID = wx.NewId()
41
42
43 #----------------------------------------------------------------------------
44 # Classes
45 #----------------------------------------------------------------------------
46
47 class TextDocument(wx.lib.docview.Document):
48
49
50 def SaveObject(self, fileObject):
51 view = self.GetFirstView()
52 fileObject.write(view.GetValue())
53 return True
54
55
56 def LoadObject(self, fileObject):
57 view = self.GetFirstView()
58 data = fileObject.read()
59 view.SetValue(data)
60 return True
61
62
63 def IsModified(self):
64 view = self.GetFirstView()
65 if view:
66 return wx.lib.docview.Document.IsModified(self) or view.IsModified()
67 else:
68 return wx.lib.docview.Document.IsModified(self)
69
70
71 def Modify(self, mod):
72 view = self.GetFirstView()
73 wx.lib.docview.Document.Modify(self, mod)
74 if not mod and view:
75 view.SetModifyFalse()
76
77
78 def OnCreateCommandProcessor(self):
79 # Don't create a command processor, it has its own
80 pass
81
82
83 class TextView(wx.lib.docview.View):
84 MARKER_NUM = 0
85 MARKER_MASK = 0x1
86
87 #----------------------------------------------------------------------------
88 # Overridden methods
89 #----------------------------------------------------------------------------
90
91 def __init__(self):
92 wx.lib.docview.View.__init__(self)
93 self._textEditor = None
94 self._markerCount = 0
95 self._commandProcessor = None
96 self._dynSash = None
97
98
99 def GetCtrlClass(self):
100 """ Used in split window to instantiate new instances """
101 return TextCtrl
102
103
104 def GetCtrl(self):
105 return self._textEditor
106
107
108 def SetCtrl(self, ctrl):
109 self._textEditor = ctrl
110
111
112 def OnCreatePrintout(self):
113 """ for Print Preview and Print """
114 return TextPrintout(self, self.GetDocument().GetPrintableName())
115
116
117 def OnCreate(self, doc, flags):
118 frame = wx.GetApp().CreateDocumentFrame(self, doc, flags, style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
119 self._dynSash = wx.gizmos.DynamicSashWindow(frame, -1, style=wx.CLIP_CHILDREN)
120 self._dynSash._view = self
121 self._textEditor = self.GetCtrlClass()(self._dynSash, -1, style=wx.NO_BORDER)
122 self._CreateSizer(frame)
123 self.Activate()
124 frame.Show(True)
125 frame.Layout()
126 return True
127
128
129 def _CreateSizer(self, frame):
130 sizer = wx.BoxSizer(wx.HORIZONTAL)
131 sizer.Add(self._dynSash, 1, wx.EXPAND)
132 frame.SetSizer(sizer)
133 frame.SetAutoLayout(True)
134
135
136 def OnUpdate(self, sender = None, hint = None):
137 if hint == "ViewStuff":
138 self.GetCtrl().SetViewDefaults()
139 elif hint == "Font":
140 font, color = self.GetFontAndColorFromConfig()
141 self.GetCtrl().SetFont(font)
142 self.GetCtrl().SetFontColor(color)
143
144
145 def OnActivateView(self, activate, activeView, deactiveView):
146 if activate and self.GetCtrl():
147 # In MDI mode just calling set focus doesn't work and in SDI mode using CallAfter causes an endless loop
148 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
149 self.SetFocus()
150 else:
151 wx.CallAfter(self.SetFocus)
152
153
154 def SetFocus(self):
155 if self.GetCtrl():
156 self.GetCtrl().SetFocus()
157
158
159 def OnClose(self, deleteWindow = True):
160 if not wx.lib.docview.View.OnClose(self, deleteWindow):
161 return False
162 self.Activate(False)
163 if deleteWindow and self.GetFrame():
164 self.GetFrame().Destroy()
165 return True
166
167
168 def ProcessEvent(self, event):
169 id = event.GetId()
170 if id == wx.ID_UNDO:
171 self.GetCtrl().Undo()
172 return True
173 elif id == wx.ID_REDO:
174 self.GetCtrl().Redo()
175 return True
176 elif id == wx.ID_CUT:
177 self.GetCtrl().Cut()
178 return True
179 elif id == wx.ID_COPY:
180 self.GetCtrl().Copy()
181 return True
182 elif id == wx.ID_PASTE:
183 self.GetCtrl().OnPaste()
184 return True
185 elif id == wx.ID_CLEAR:
186 self.GetCtrl().OnClear()
187 return True
188 elif id == wx.ID_SELECTALL:
189 self.GetCtrl().SetSelection(0, -1)
190 return True
191 elif id == VIEW_WHITESPACE_ID:
192 self.GetCtrl().SetViewWhiteSpace(not self.GetCtrl().GetViewWhiteSpace())
193 return True
194 elif id == VIEW_EOL_ID:
195 self.GetCtrl().SetViewEOL(not self.GetCtrl().GetViewEOL())
196 return True
197 elif id == VIEW_INDENTATION_GUIDES_ID:
198 self.GetCtrl().SetViewIndentationGuides(not self.GetCtrl().GetViewIndentationGuides())
199 return True
200 elif id == VIEW_RIGHT_EDGE_ID:
201 self.GetCtrl().SetViewRightEdge(not self.GetCtrl().GetViewRightEdge())
202 return True
203 elif id == VIEW_LINE_NUMBERS_ID:
204 self.GetCtrl().SetViewLineNumbers(not self.GetCtrl().GetViewLineNumbers())
205 return True
206 elif id == ZOOM_NORMAL_ID:
207 self.GetCtrl().SetZoom(0)
208 return True
209 elif id == ZOOM_IN_ID:
210 self.GetCtrl().CmdKeyExecute(wx.stc.STC_CMD_ZOOMIN)
211 return True
212 elif id == ZOOM_OUT_ID:
213 self.GetCtrl().CmdKeyExecute(wx.stc.STC_CMD_ZOOMOUT)
214 return True
215 elif id == CHOOSE_FONT_ID:
216 self.OnChooseFont()
217 return True
218 elif id == WORD_WRAP_ID:
219 self.GetCtrl().SetWordWrap(not self.GetCtrl().GetWordWrap())
220 return True
221 elif id == FindService.FindService.FIND_ID:
222 self.OnFind()
223 return True
224 elif id == FindService.FindService.FIND_PREVIOUS_ID:
225 self.DoFind(forceFindPrevious = True)
226 return True
227 elif id == FindService.FindService.FIND_NEXT_ID:
228 self.DoFind(forceFindNext = True)
229 return True
230 elif id == FindService.FindService.REPLACE_ID:
231 self.OnFind(replace = True)
232 return True
233 elif id == FindService.FindService.FINDONE_ID:
234 self.DoFind()
235 return True
236 elif id == FindService.FindService.REPLACEONE_ID:
237 self.DoFind(replace = True)
238 return True
239 elif id == FindService.FindService.REPLACEALL_ID:
240 self.DoFind(replaceAll = True)
241 return True
242 elif id == FindService.FindService.GOTO_LINE_ID:
243 self.OnGotoLine(event)
244 return True
245 else:
246 return wx.lib.docview.View.ProcessEvent(self, event)
247
248
249 def ProcessUpdateUIEvent(self, event):
250 if not self.GetCtrl():
251 return False
252
253 id = event.GetId()
254 if id == wx.ID_UNDO:
255 event.Enable(self.GetCtrl().CanUndo())
256 event.SetText(_("&Undo\tCtrl+Z")) # replace menu string
257 return True
258 elif id == wx.ID_REDO:
259 event.Enable(self.GetCtrl().CanRedo())
260 event.SetText(_("&Redo\tCtrl+Y")) # replace menu string
261 return True
262 elif (id == wx.ID_CUT
263 or id == wx.ID_COPY
264 or id == wx.ID_CLEAR):
265 hasSelection = self.GetCtrl().GetSelectionStart() != self.GetCtrl().GetSelectionEnd()
266 event.Enable(hasSelection)
267 return True
268 elif id == wx.ID_PASTE:
269 event.Enable(self.GetCtrl().CanPaste())
270 return True
271 elif id == wx.ID_SELECTALL:
272 hasText = self.GetCtrl().GetTextLength() > 0
273 event.Enable(hasText)
274 return True
275 elif id == TEXT_ID:
276 event.Enable(True)
277 return True
278 elif id == VIEW_WHITESPACE_ID:
279 hasText = self.GetCtrl().GetTextLength() > 0
280 event.Enable(hasText)
281 event.Check(self.GetCtrl().GetViewWhiteSpace())
282 return True
283 elif id == VIEW_EOL_ID:
284 hasText = self.GetCtrl().GetTextLength() > 0
285 event.Enable(hasText)
286 event.Check(self.GetCtrl().GetViewEOL())
287 return True
288 elif id == VIEW_INDENTATION_GUIDES_ID:
289 hasText = self.GetCtrl().GetTextLength() > 0
290 event.Enable(hasText)
291 event.Check(self.GetCtrl().GetIndentationGuides())
292 return True
293 elif id == VIEW_RIGHT_EDGE_ID:
294 hasText = self.GetCtrl().GetTextLength() > 0
295 event.Enable(hasText)
296 event.Check(self.GetCtrl().GetViewRightEdge())
297 return True
298 elif id == VIEW_LINE_NUMBERS_ID:
299 hasText = self.GetCtrl().GetTextLength() > 0
300 event.Enable(hasText)
301 event.Check(self.GetCtrl().GetViewLineNumbers())
302 return True
303 elif id == ZOOM_ID:
304 event.Enable(True)
305 return True
306 elif id == ZOOM_NORMAL_ID:
307 event.Enable(self.GetCtrl().GetZoom() != 0)
308 return True
309 elif id == ZOOM_IN_ID:
310 event.Enable(self.GetCtrl().GetZoom() < 20)
311 return True
312 elif id == ZOOM_OUT_ID:
313 event.Enable(self.GetCtrl().GetZoom() > -10)
314 return True
315 elif id == CHOOSE_FONT_ID:
316 event.Enable(True)
317 return True
318 elif id == WORD_WRAP_ID:
319 event.Enable(self.GetCtrl().CanWordWrap())
320 event.Check(self.GetCtrl().CanWordWrap() and self.GetCtrl().GetWordWrap())
321 return True
322 elif id == FindService.FindService.FIND_ID:
323 hasText = self.GetCtrl().GetTextLength() > 0
324 event.Enable(hasText)
325 return True
326 elif id == FindService.FindService.FIND_PREVIOUS_ID:
327 hasText = self.GetCtrl().GetTextLength() > 0
328 event.Enable(hasText and
329 self._FindServiceHasString() and
330 self.GetCtrl().GetSelection()[0] > 0)
331 return True
332 elif id == FindService.FindService.FIND_NEXT_ID:
333 hasText = self.GetCtrl().GetTextLength() > 0
334 event.Enable(hasText and
335 self._FindServiceHasString() and
336 self.GetCtrl().GetSelection()[0] < self.GetCtrl().GetLength())
337 return True
338 elif id == FindService.FindService.REPLACE_ID:
339 hasText = self.GetCtrl().GetTextLength() > 0
340 event.Enable(hasText)
341 return True
342 elif id == FindService.FindService.GOTO_LINE_ID:
343 event.Enable(True)
344 return True
345 elif id == TEXT_STATUS_BAR_ID:
346 self.OnUpdateStatusBar(event)
347 return True
348 else:
349 return wx.lib.docview.View.ProcessUpdateUIEvent(self, event)
350
351
352 def _GetParentFrame(self):
353 return wx.GetTopLevelParent(self.GetFrame())
354
355
356 #----------------------------------------------------------------------------
357 # Methods for TextDocument to call
358 #----------------------------------------------------------------------------
359
360 def IsModified(self):
361 if not self.GetCtrl():
362 return False
363 return self.GetCtrl().GetModify()
364
365
366 def SetModifyFalse(self):
367 self.GetCtrl().SetSavePoint()
368
369
370 def GetValue(self):
371 if self.GetCtrl():
372 return self.GetCtrl().GetText()
373 else:
374 return None
375
376
377 def SetValue(self, value):
378 self.GetCtrl().SetText(value)
379 self.GetCtrl().UpdateLineNumberMarginWidth()
380 self.GetCtrl().EmptyUndoBuffer()
381
382
383 #----------------------------------------------------------------------------
384 # STC events
385 #----------------------------------------------------------------------------
386
387 def OnUpdateStatusBar(self, event):
388 statusBar = self._GetParentFrame().GetStatusBar()
389 statusBar.SetInsertMode(self.GetCtrl().GetOvertype() == 0)
390 statusBar.SetLineNumber(self.GetCtrl().GetCurrentLine() + 1)
391 statusBar.SetColumnNumber(self.GetCtrl().GetColumn(self.GetCtrl().GetCurrentPos()) + 1)
392
393
394 #----------------------------------------------------------------------------
395 # Format methods
396 #----------------------------------------------------------------------------
397
398 def OnChooseFont(self):
399 data = wx.FontData()
400 data.EnableEffects(True)
401 data.SetInitialFont(self.GetCtrl().GetFont())
402 data.SetColour(self.GetCtrl().GetFontColor())
403 fontDialog = wx.FontDialog(self.GetFrame(), data)
404 if fontDialog.ShowModal() == wx.ID_OK:
405 data = fontDialog.GetFontData()
406 self.GetCtrl().SetFont(data.GetChosenFont())
407 self.GetCtrl().SetFontColor(data.GetColour())
408 self.GetCtrl().UpdateStyles()
409 fontDialog.Destroy()
410
411
412 #----------------------------------------------------------------------------
413 # Find methods
414 #----------------------------------------------------------------------------
415
416 def OnFind(self, replace = False):
417 findService = wx.GetApp().GetService(FindService.FindService)
418 if findService:
419 findService.ShowFindReplaceDialog(findString = self.GetCtrl().GetSelectedText(), replace = replace)
420
421
422 def DoFind(self, forceFindNext = False, forceFindPrevious = False, replace = False, replaceAll = False):
423 findService = wx.GetApp().GetService(FindService.FindService)
424 if not findService:
425 return
426 findString = findService.GetFindString()
427 if len(findString) == 0:
428 return -1
429 replaceString = findService.GetReplaceString()
430 flags = findService.GetFlags()
431 startLoc, endLoc = self.GetCtrl().GetSelection()
432
433 wholeWord = flags & wx.FR_WHOLEWORD > 0
434 matchCase = flags & wx.FR_MATCHCASE > 0
435 regExp = flags & FindService.FindService.FR_REGEXP > 0
436 down = flags & wx.FR_DOWN > 0
437 wrap = flags & FindService.FindService.FR_WRAP > 0
438
439 if forceFindPrevious: # this is from function keys, not dialog box
440 down = False
441 wrap = False # user would want to know they're at the end of file
442 elif forceFindNext:
443 down = True
444 wrap = False # user would want to know they're at the end of file
445
446 badSyntax = False
447
448 # On replace dialog operations, user is allowed to replace the currently highlighted text to determine if it should be replaced or not.
449 # Typically, it is the text from a previous find operation, but we must check to see if it isn't, user may have moved the cursor or selected some other text accidentally.
450 # If the text is a match, then replace it.
451 if replace:
452 result, start, end, replText = findService.DoFind(findString, replaceString, self.GetCtrl().GetSelectedText(), 0, 0, True, matchCase, wholeWord, regExp, replace)
453 if result > 0:
454 self.GetCtrl().ReplaceSelection(replText)
455 self.GetDocument().Modify(True)
456 wx.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString)
457 if down:
458 startLoc += len(replText) # advance start location past replacement string to new text
459 endLoc = startLoc
460 elif result == FindService.FIND_SYNTAXERROR:
461 badSyntax = True
462 wx.GetApp().GetTopWindow().PushStatusText(_("Invalid regular expression \"%s\"") % findString)
463
464 if not badSyntax:
465 text = self.GetCtrl().GetText()
466
467 # Find the next matching text occurance or if it is a ReplaceAll, replace all occurances
468 # Even if the user is Replacing, we should replace here, but only select the text and let the user replace it with the next Replace operation
469 result, start, end, text = findService.DoFind(findString, replaceString, text, startLoc, endLoc, down, matchCase, wholeWord, regExp, False, replaceAll, wrap)
470 if result > 0:
471 self.GetCtrl().SetTargetStart(0)
472 self.GetCtrl().SetTargetEnd(self.GetCtrl().GetLength())
473 self.GetCtrl().ReplaceTarget(text) # Doing a SetText causes a clear document to be shown when undoing, so using replacetarget instead
474 self.GetDocument().Modify(True)
475 if result == 1:
476 wx.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString)
477 else:
478 wx.GetApp().GetTopWindow().PushStatusText(_("%i occurrences of \"%s\" replaced") % (result, findString))
479 elif result == 0:
480 self.GetCtrl().SetSelection(start, end)
481 self.GetCtrl().EnsureVisible(self.GetCtrl().LineFromPosition(end)) # show bottom then scroll up to top
482 self.GetCtrl().EnsureVisible(self.GetCtrl().LineFromPosition(start)) # do this after ensuring bottom is visible
483 wx.GetApp().GetTopWindow().PushStatusText(_("Found \"%s\".") % findString)
484 elif result == FindService.FIND_SYNTAXERROR:
485 # Dialog for this case gets popped up by the FindService.
486 wx.GetApp().GetTopWindow().PushStatusText(_("Invalid regular expression \"%s\"") % findString)
487 else:
488 wx.MessageBox(_("Can't find \"%s\".") % findString, "Find",
489 wx.OK | wx.ICON_INFORMATION)
490
491
492 def _FindServiceHasString(self):
493 findService = wx.GetApp().GetService(FindService.FindService)
494 if not findService or not findService.GetFindString():
495 return False
496 return True
497
498
499 def OnGotoLine(self, event):
500 findService = wx.GetApp().GetService(FindService.FindService)
501 if findService:
502 line = findService.GetLineNumber(self.GetDocumentManager().FindSuitableParent())
503 if line > -1:
504 line = line - 1
505 self.GetCtrl().EnsureVisible(line)
506 self.GetCtrl().GotoLine(line)
507
508
509 def GotoLine(self, lineNum):
510 if lineNum > -1:
511 lineNum = lineNum - 1 # line numbering for editor is 0 based, we are 1 based.
512 self.GetCtrl().EnsureVisibleEnforcePolicy(lineNum)
513 self.GetCtrl().GotoLine(lineNum)
514
515
516 def SetSelection(self, start, end):
517 self.GetCtrl().SetSelection(start, end)
518
519
520 def EnsureVisible(self, line):
521 self.GetCtrl().EnsureVisible(line-1) # line numbering for editor is 0 based, we are 1 based.
522
523 def EnsureVisibleEnforcePolicy(self, line):
524 self.GetCtrl().EnsureVisibleEnforcePolicy(line-1) # line numbering for editor is 0 based, we are 1 based.
525
526 def LineFromPosition(self, pos):
527 return self.GetCtrl().LineFromPosition(pos)+1 # line numbering for editor is 0 based, we are 1 based.
528
529
530 def PositionFromLine(self, line):
531 return self.GetCtrl().PositionFromLine(line-1) # line numbering for editor is 0 based, we are 1 based.
532
533
534 def GetLineEndPosition(self, line):
535 return self.GetCtrl().GetLineEndPosition(line-1) # line numbering for editor is 0 based, we are 1 based.
536
537
538 def GetLine(self, lineNum):
539 return self.GetCtrl().GetLine(lineNum-1) # line numbering for editor is 0 based, we are 1 based.
540
541
542 def MarkerDefine(self):
543 """ This must be called after the texteditor is instantiated """
544 self.GetCtrl().MarkerDefine(TextView.MARKER_NUM, wx.stc.STC_MARK_CIRCLE, wx.BLACK, wx.BLUE)
545
546
547 def MarkerToggle(self, lineNum = -1, marker_index=MARKER_NUM, mask=MARKER_MASK):
548 if lineNum == -1:
549 lineNum = self.GetCtrl().GetCurrentLine()
550 if self.GetCtrl().MarkerGet(lineNum) & mask:
551 self.GetCtrl().MarkerDelete(lineNum, marker_index)
552 self._markerCount -= 1
553 else:
554 self.GetCtrl().MarkerAdd(lineNum, marker_index)
555 self._markerCount += 1
556
557
558 def MarkerAdd(self, lineNum = -1, marker_index=MARKER_NUM, mask=MARKER_MASK):
559 if lineNum == -1:
560 lineNum = self.GetCtrl().GetCurrentLine()
561 self.GetCtrl().MarkerAdd(lineNum, marker_index)
562 self._markerCount += 1
563
564
565 def MarkerDelete(self, lineNum = -1, marker_index=MARKER_NUM, mask=MARKER_MASK):
566 if lineNum == -1:
567 lineNum = self.GetCtrl().GetCurrentLine()
568 if self.GetCtrl().MarkerGet(lineNum) & mask:
569 self.GetCtrl().MarkerDelete(lineNum, marker_index)
570 self._markerCount -= 1
571
572 def MarkerDeleteAll(self, marker_num=MARKER_NUM):
573 self.GetCtrl().MarkerDeleteAll(marker_num)
574 if marker_num == self.MARKER_NUM:
575 self._markerCount = 0
576
577
578 def MarkerNext(self, lineNum = -1):
579 if lineNum == -1:
580 lineNum = self.GetCtrl().GetCurrentLine() + 1 # start search below current line
581 foundLine = self.GetCtrl().MarkerNext(lineNum, self.MARKER_MASK)
582 if foundLine == -1:
583 # wrap to top of file
584 foundLine = self.GetCtrl().MarkerNext(0, self.MARKER_MASK)
585 if foundLine == -1:
586 wx.GetApp().GetTopWindow().PushStatusText(_("No markers"))
587 return
588
589 self.GotoLine(foundLine + 1)
590
591
592 def MarkerPrevious(self, lineNum = -1):
593 if lineNum == -1:
594 lineNum = self.GetCtrl().GetCurrentLine() - 1 # start search above current line
595 if lineNum == -1:
596 lineNum = self.GetCtrl().GetLineCount()
597
598 foundLine = self.GetCtrl().MarkerPrevious(lineNum, self.MARKER_MASK)
599 if foundLine == -1:
600 # wrap to bottom of file
601 foundLine = self.GetCtrl().MarkerPrevious(self.GetCtrl().GetLineCount(), self.MARKER_MASK)
602 if foundLine == -1:
603 wx.GetApp().GetTopWindow().PushStatusText(_("No markers"))
604 return
605
606 self.GotoLine(foundLine + 1)
607
608
609 def MarkerExists(self, lineNum = -1, mask=MARKER_MASK):
610 if lineNum == -1:
611 lineNum = self.GetCtrl().GetCurrentLine()
612 if self.GetCtrl().MarkerGet(lineNum) & mask:
613 return True
614 else:
615 return False
616
617
618 def GetMarkerCount(self):
619 return self._markerCount
620
621
622 class TextService(wx.lib.pydocview.DocService):
623
624
625 def __init__(self):
626 wx.lib.pydocview.DocService.__init__(self)
627
628
629 def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
630 if document and document.GetDocumentTemplate().GetDocumentType() != TextDocument:
631 return
632 if not document and wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
633 return
634
635 statusBar = TextStatusBar(frame, TEXT_STATUS_BAR_ID)
636 frame.SetStatusBar(statusBar)
637 wx.EVT_UPDATE_UI(frame, TEXT_STATUS_BAR_ID, frame.ProcessUpdateUIEvent)
638
639 viewMenu = menuBar.GetMenu(menuBar.FindMenu(_("&View")))
640
641 viewMenu.AppendSeparator()
642 textMenu = wx.Menu()
643 textMenu.AppendCheckItem(VIEW_WHITESPACE_ID, _("&Whitespace"), _("Shows or hides whitespace"))
644 wx.EVT_MENU(frame, VIEW_WHITESPACE_ID, frame.ProcessEvent)
645 wx.EVT_UPDATE_UI(frame, VIEW_WHITESPACE_ID, frame.ProcessUpdateUIEvent)
646 textMenu.AppendCheckItem(VIEW_EOL_ID, _("&End of Line Markers"), _("Shows or hides indicators at the end of each line"))
647 wx.EVT_MENU(frame, VIEW_EOL_ID, frame.ProcessEvent)
648 wx.EVT_UPDATE_UI(frame, VIEW_EOL_ID, frame.ProcessUpdateUIEvent)
649 textMenu.AppendCheckItem(VIEW_INDENTATION_GUIDES_ID, _("&Indentation Guides"), _("Shows or hides indentations"))
650 wx.EVT_MENU(frame, VIEW_INDENTATION_GUIDES_ID, frame.ProcessEvent)
651 wx.EVT_UPDATE_UI(frame, VIEW_INDENTATION_GUIDES_ID, frame.ProcessUpdateUIEvent)
652 textMenu.AppendCheckItem(VIEW_RIGHT_EDGE_ID, _("&Right Edge"), _("Shows or hides the right edge marker"))
653 wx.EVT_MENU(frame, VIEW_RIGHT_EDGE_ID, frame.ProcessEvent)
654 wx.EVT_UPDATE_UI(frame, VIEW_RIGHT_EDGE_ID, frame.ProcessUpdateUIEvent)
655 textMenu.AppendCheckItem(VIEW_LINE_NUMBERS_ID, _("&Line Numbers"), _("Shows or hides the line numbers"))
656 wx.EVT_MENU(frame, VIEW_LINE_NUMBERS_ID, frame.ProcessEvent)
657 wx.EVT_UPDATE_UI(frame, VIEW_LINE_NUMBERS_ID, frame.ProcessUpdateUIEvent)
658
659 viewMenu.AppendMenu(TEXT_ID, _("&Text"), textMenu)
660 wx.EVT_UPDATE_UI(frame, TEXT_ID, frame.ProcessUpdateUIEvent)
661
662 isWindows = (wx.Platform == '__WXMSW__')
663
664 zoomMenu = wx.Menu()
665 zoomMenu.Append(ZOOM_NORMAL_ID, _("Normal Size"), _("Sets the document to its normal size"))
666 wx.EVT_MENU(frame, ZOOM_NORMAL_ID, frame.ProcessEvent)
667 wx.EVT_UPDATE_UI(frame, ZOOM_NORMAL_ID, frame.ProcessUpdateUIEvent)
668 if isWindows:
669 zoomMenu.Append(ZOOM_IN_ID, _("Zoom In\tCtrl+Page Up"), _("Zooms the document to a larger size"))
670 else:
671 zoomMenu.Append(ZOOM_IN_ID, _("Zoom In"), _("Zooms the document to a larger size"))
672 wx.EVT_MENU(frame, ZOOM_IN_ID, frame.ProcessEvent)
673 wx.EVT_UPDATE_UI(frame, ZOOM_IN_ID, frame.ProcessUpdateUIEvent)
674 if isWindows:
675 zoomMenu.Append(ZOOM_OUT_ID, _("Zoom Out\tCtrl+Page Down"), _("Zooms the document to a smaller size"))
676 else:
677 zoomMenu.Append(ZOOM_OUT_ID, _("Zoom Out"), _("Zooms the document to a smaller size"))
678 wx.EVT_MENU(frame, ZOOM_OUT_ID, frame.ProcessEvent)
679 wx.EVT_UPDATE_UI(frame, ZOOM_OUT_ID, frame.ProcessUpdateUIEvent)
680
681 viewMenu.AppendMenu(ZOOM_ID, _("&Zoom"), zoomMenu)
682 wx.EVT_UPDATE_UI(frame, ZOOM_ID, frame.ProcessUpdateUIEvent)
683
684 formatMenuIndex = menuBar.FindMenu(_("&Format"))
685 if formatMenuIndex > -1:
686 formatMenu = menuBar.GetMenu(formatMenuIndex)
687 else:
688 formatMenu = wx.Menu()
689 if not menuBar.FindItemById(CHOOSE_FONT_ID):
690 formatMenu.Append(CHOOSE_FONT_ID, _("&Font..."), _("Sets the font to use"))
691 wx.EVT_MENU(frame, CHOOSE_FONT_ID, frame.ProcessEvent)
692 wx.EVT_UPDATE_UI(frame, CHOOSE_FONT_ID, frame.ProcessUpdateUIEvent)
693 if not menuBar.FindItemById(WORD_WRAP_ID):
694 formatMenu.AppendCheckItem(WORD_WRAP_ID, _("Word Wrap"), _("Wraps text horizontally when checked"))
695 wx.EVT_MENU(frame, WORD_WRAP_ID, frame.ProcessEvent)
696 wx.EVT_UPDATE_UI(frame, WORD_WRAP_ID, frame.ProcessUpdateUIEvent)
697 if formatMenuIndex == -1:
698 viewMenuIndex = menuBar.FindMenu(_("&View"))
699 menuBar.Insert(viewMenuIndex + 1, formatMenu, _("&Format"))
700
701 # wxBug: wxToolBar::GetToolPos doesn't exist, need it to find cut tool and then insert find in front of it.
702 toolBar.AddSeparator()
703 toolBar.AddTool(ZOOM_IN_ID, getZoomInBitmap(), shortHelpString = _("Zoom In"), longHelpString = _("Zooms the document to a larger size"))
704 toolBar.AddTool(ZOOM_OUT_ID, getZoomOutBitmap(), shortHelpString = _("Zoom Out"), longHelpString = _("Zooms the document to a smaller size"))
705 toolBar.Realize()
706
707
708 def ProcessUpdateUIEvent(self, event):
709 id = event.GetId()
710 if (id == TEXT_ID
711 or id == VIEW_WHITESPACE_ID
712 or id == VIEW_EOL_ID
713 or id == VIEW_INDENTATION_GUIDES_ID
714 or id == VIEW_RIGHT_EDGE_ID
715 or id == VIEW_LINE_NUMBERS_ID
716 or id == ZOOM_ID
717 or id == ZOOM_NORMAL_ID
718 or id == ZOOM_IN_ID
719 or id == ZOOM_OUT_ID
720 or id == CHOOSE_FONT_ID
721 or id == WORD_WRAP_ID):
722 event.Enable(False)
723 return True
724 else:
725 return False
726
727
728 class TextStatusBar(wx.StatusBar):
729
730 # wxBug: Would be nice to show num key status in statusbar, but can't figure out how to detect if it is enabled or disabled
731
732 def __init__(self, parent, id, style = wx.ST_SIZEGRIP, name = "statusBar"):
733 wx.StatusBar.__init__(self, parent, id, style, name)
734 self.SetFieldsCount(4)
735 self.SetStatusWidths([-1, 50, 50, 55])
736
737 def SetInsertMode(self, insert = True):
738 if insert:
739 newText = _("Ins")
740 else:
741 newText = _("")
742 if self.GetStatusText(1) != newText: # wxBug: Need to check if the text has changed, otherwise it flickers under win32
743 self.SetStatusText(newText, 1)
744
745 def SetLineNumber(self, lineNumber):
746 newText = _("Ln %i") % lineNumber
747 if self.GetStatusText(2) != newText:
748 self.SetStatusText(newText, 2)
749
750 def SetColumnNumber(self, colNumber):
751 newText = _("Col %i") % colNumber
752 if self.GetStatusText(3) != newText:
753 self.SetStatusText(newText, 3)
754
755
756 class TextOptionsPanel(wx.Panel):
757
758
759 def __init__(self, parent, id, configPrefix = "Text", label = "Text", hasWordWrap = True, hasTabs = False, addPage=True):
760 wx.Panel.__init__(self, parent, id)
761 self._configPrefix = configPrefix
762 self._hasWordWrap = hasWordWrap
763 self._hasTabs = hasTabs
764 SPACE = 10
765 HALF_SPACE = 5
766 config = wx.ConfigBase_Get()
767 self._textFont = wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL)
768 fontData = config.Read(self._configPrefix + "EditorFont", "")
769 if fontData:
770 nativeFont = wx.NativeFontInfo()
771 nativeFont.FromString(fontData)
772 self._textFont.SetNativeFontInfo(nativeFont)
773 self._originalTextFont = self._textFont
774 self._textColor = wx.BLACK
775 colorData = config.Read(self._configPrefix + "EditorColor", "")
776 if colorData:
777 red = int("0x" + colorData[0:2], 16)
778 green = int("0x" + colorData[2:4], 16)
779 blue = int("0x" + colorData[4:6], 16)
780 self._textColor = wx.Color(red, green, blue)
781 self._originalTextColor = self._textColor
782 fontLabel = wx.StaticText(self, -1, _("Font:"))
783 self._sampleTextCtrl = wx.TextCtrl(self, -1, "", size = (125, 21))
784 self._sampleTextCtrl.SetEditable(False)
785 chooseFontButton = wx.Button(self, -1, _("Choose Font..."))
786 wx.EVT_BUTTON(self, chooseFontButton.GetId(), self.OnChooseFont)
787 if self._hasWordWrap:
788 self._wordWrapCheckBox = wx.CheckBox(self, -1, _("Wrap words inside text area"))
789 self._wordWrapCheckBox.SetValue(wx.ConfigBase_Get().ReadInt(self._configPrefix + "EditorWordWrap", False))
790 self._viewWhitespaceCheckBox = wx.CheckBox(self, -1, _("Show whitespace"))
791 self._viewWhitespaceCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewWhitespace", False))
792 self._viewEOLCheckBox = wx.CheckBox(self, -1, _("Show end of line markers"))
793 self._viewEOLCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewEOL", False))
794 self._viewIndentationGuideCheckBox = wx.CheckBox(self, -1, _("Show indentation guides"))
795 self._viewIndentationGuideCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewIndentationGuides", False))
796 self._viewRightEdgeCheckBox = wx.CheckBox(self, -1, _("Show right edge"))
797 self._viewRightEdgeCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewRightEdge", False))
798 self._viewLineNumbersCheckBox = wx.CheckBox(self, -1, _("Show line numbers"))
799 self._viewLineNumbersCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewLineNumbers", True))
800 if self._hasTabs:
801 self._hasTabsCheckBox = wx.CheckBox(self, -1, _("Use spaces instead of tabs"))
802 self._hasTabsCheckBox.SetValue(not wx.ConfigBase_Get().ReadInt(self._configPrefix + "EditorUseTabs", False))
803 indentWidthLabel = wx.StaticText(self, -1, _("Indent Width:"))
804 self._indentWidthChoice = wx.Choice(self, -1, choices = ["2", "4", "6", "8", "10"])
805 self._indentWidthChoice.SetStringSelection(str(config.ReadInt(self._configPrefix + "EditorIndentWidth", 4)))
806 textPanelBorderSizer = wx.BoxSizer(wx.VERTICAL)
807 textPanelSizer = wx.BoxSizer(wx.VERTICAL)
808 textFontSizer = wx.BoxSizer(wx.HORIZONTAL)
809 textFontSizer.Add(fontLabel, 0, wx.ALIGN_LEFT | wx.RIGHT | wx.TOP, HALF_SPACE)
810 textFontSizer.Add(self._sampleTextCtrl, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.RIGHT, HALF_SPACE)
811 textFontSizer.Add(chooseFontButton, 0, wx.ALIGN_RIGHT | wx.LEFT, HALF_SPACE)
812 textPanelSizer.Add(textFontSizer, 0, wx.ALL, HALF_SPACE)
813 if self._hasWordWrap:
814 textPanelSizer.Add(self._wordWrapCheckBox, 0, wx.ALL, HALF_SPACE)
815 textPanelSizer.Add(self._viewWhitespaceCheckBox, 0, wx.ALL, HALF_SPACE)
816 textPanelSizer.Add(self._viewEOLCheckBox, 0, wx.ALL, HALF_SPACE)
817 textPanelSizer.Add(self._viewIndentationGuideCheckBox, 0, wx.ALL, HALF_SPACE)
818 textPanelSizer.Add(self._viewRightEdgeCheckBox, 0, wx.ALL, HALF_SPACE)
819 textPanelSizer.Add(self._viewLineNumbersCheckBox, 0, wx.ALL, HALF_SPACE)
820 if self._hasTabs:
821 textPanelSizer.Add(self._hasTabsCheckBox, 0, wx.ALL, HALF_SPACE)
822 textIndentWidthSizer = wx.BoxSizer(wx.HORIZONTAL)
823 textIndentWidthSizer.Add(indentWidthLabel, 0, wx.ALIGN_LEFT | wx.RIGHT | wx.TOP, HALF_SPACE)
824 textIndentWidthSizer.Add(self._indentWidthChoice, 0, wx.ALIGN_LEFT | wx.EXPAND, HALF_SPACE)
825 textPanelSizer.Add(textIndentWidthSizer, 0, wx.ALL, HALF_SPACE)
826 textPanelBorderSizer.Add(textPanelSizer, 0, wx.ALL, SPACE)
827 ## styleButton = wx.Button(self, -1, _("Choose Style..."))
828 ## wx.EVT_BUTTON(self, styleButton.GetId(), self.OnChooseStyle)
829 ## textPanelBorderSizer.Add(styleButton, 0, wx.ALL, SPACE)
830 self.SetSizer(textPanelBorderSizer)
831 self.UpdateSampleFont()
832 if addPage:
833 parent.AddPage(self, _(label))
834
835 def UpdateSampleFont(self):
836 nativeFont = wx.NativeFontInfo()
837 nativeFont.FromString(self._textFont.GetNativeFontInfoDesc())
838 font = wx.NullFont
839 font.SetNativeFontInfo(nativeFont)
840 font.SetPointSize(self._sampleTextCtrl.GetFont().GetPointSize()) # Use the standard point size
841 self._sampleTextCtrl.SetFont(font)
842 self._sampleTextCtrl.SetForegroundColour(self._textColor)
843 self._sampleTextCtrl.SetValue(str(self._textFont.GetPointSize()) + _(" pt. ") + self._textFont.GetFaceName())
844 self._sampleTextCtrl.Refresh()
845 self.Layout()
846
847
848 ## def OnChooseStyle(self, event):
849 ## import STCStyleEditor
850 ## import os
851 ## base = os.path.split(__file__)[0]
852 ## config = os.path.abspath(os.path.join(base, 'stc-styles.rc.cfg'))
853 ##
854 ## dlg = STCStyleEditor.STCStyleEditDlg(None,
855 ## 'Python', 'python',
856 ## #'HTML', 'html',
857 ## #'XML', 'xml',
858 ## config)
859 ## try:
860 ## dlg.ShowModal()
861 ## finally:
862 ## dlg.Destroy()
863
864
865 def OnChooseFont(self, event):
866 data = wx.FontData()
867 data.EnableEffects(True)
868 data.SetInitialFont(self._textFont)
869 data.SetColour(self._textColor)
870 fontDialog = wx.FontDialog(self, data)
871 if fontDialog.ShowModal() == wx.ID_OK:
872 data = fontDialog.GetFontData()
873 self._textFont = data.GetChosenFont()
874 self._textColor = data.GetColour()
875 self.UpdateSampleFont()
876 fontDialog.Destroy()
877
878
879 def OnOK(self, optionsDialog):
880 config = wx.ConfigBase_Get()
881 doViewStuffUpdate = config.ReadInt(self._configPrefix + "EditorViewWhitespace", False) != self._viewWhitespaceCheckBox.GetValue()
882 config.WriteInt(self._configPrefix + "EditorViewWhitespace", self._viewWhitespaceCheckBox.GetValue())
883 doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewEOL", False) != self._viewEOLCheckBox.GetValue()
884 config.WriteInt(self._configPrefix + "EditorViewEOL", self._viewEOLCheckBox.GetValue())
885 doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewIndentationGuides", False) != self._viewIndentationGuideCheckBox.GetValue()
886 config.WriteInt(self._configPrefix + "EditorViewIndentationGuides", self._viewIndentationGuideCheckBox.GetValue())
887 doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewRightEdge", False) != self._viewRightEdgeCheckBox.GetValue()
888 config.WriteInt(self._configPrefix + "EditorViewRightEdge", self._viewRightEdgeCheckBox.GetValue())
889 doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewLineNumbers", True) != self._viewLineNumbersCheckBox.GetValue()
890 config.WriteInt(self._configPrefix + "EditorViewLineNumbers", self._viewLineNumbersCheckBox.GetValue())
891 if self._hasWordWrap:
892 doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorWordWrap", False) != self._wordWrapCheckBox.GetValue()
893 config.WriteInt(self._configPrefix + "EditorWordWrap", self._wordWrapCheckBox.GetValue())
894 if self._hasTabs:
895 doViewStuffUpdate = doViewStuffUpdate or not config.ReadInt(self._configPrefix + "EditorUseTabs", True) != self._hasTabsCheckBox.GetValue()
896 config.WriteInt(self._configPrefix + "EditorUseTabs", not self._hasTabsCheckBox.GetValue())
897 newIndentWidth = int(self._indentWidthChoice.GetStringSelection())
898 oldIndentWidth = config.ReadInt(self._configPrefix + "EditorIndentWidth", 4)
899 if newIndentWidth != oldIndentWidth:
900 doViewStuffUpdate = True
901 config.WriteInt(self._configPrefix + "EditorIndentWidth", newIndentWidth)
902 doFontUpdate = self._originalTextFont != self._textFont or self._originalTextColor != self._textColor
903 config.Write(self._configPrefix + "EditorFont", self._textFont.GetNativeFontInfoDesc())
904 config.Write(self._configPrefix + "EditorColor", "%02x%02x%02x" % (self._textColor.Red(), self._textColor.Green(), self._textColor.Blue()))
905 if doViewStuffUpdate or doFontUpdate:
906 for document in optionsDialog.GetDocManager().GetDocuments():
907 if issubclass(document.GetDocumentTemplate().GetDocumentType(), TextDocument):
908 if doViewStuffUpdate:
909 document.UpdateAllViews(hint = "ViewStuff")
910 if doFontUpdate:
911 document.UpdateAllViews(hint = "Font")
912
913
914 class TextCtrl(wx.stc.StyledTextCtrl):
915
916 def __init__(self, parent, id=-1, style=wx.NO_FULL_REPAINT_ON_RESIZE):
917 wx.stc.StyledTextCtrl.__init__(self, parent, id, style=style)
918
919 if isinstance(parent, wx.gizmos.DynamicSashWindow):
920 self._dynSash = parent
921 self.SetupDSScrollBars()
922 self.Bind(wx.gizmos.EVT_DYNAMIC_SASH_SPLIT, self.OnDSSplit)
923 self.Bind(wx.gizmos.EVT_DYNAMIC_SASH_UNIFY, self.OnDSUnify)
924
925 self._font = None
926 self._fontColor = None
927
928 self.SetVisiblePolicy(wx.stc.STC_VISIBLE_STRICT,1)
929
930 self.CmdKeyClear(wx.stc.STC_KEY_ADD, wx.stc.STC_SCMOD_CTRL)
931 self.CmdKeyClear(wx.stc.STC_KEY_SUBTRACT, wx.stc.STC_SCMOD_CTRL)
932 self.CmdKeyAssign(wx.stc.STC_KEY_PRIOR, wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_ZOOMIN)
933 self.CmdKeyAssign(wx.stc.STC_KEY_NEXT, wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_ZOOMOUT)
934 self.Bind(wx.stc.EVT_STC_ZOOM, self.OnUpdateLineNumberMarginWidth) # auto update line num width on zoom
935 wx.EVT_KEY_DOWN(self, self.OnKeyPressed)
936 wx.EVT_KILL_FOCUS(self, self.OnKillFocus)
937 wx.EVT_SET_FOCUS(self, self.OnFocus)
938 self.SetMargins(0,0)
939
940 self.SetUseTabs(0)
941 self.SetTabWidth(4)
942 self.SetIndent(4)
943
944 self.SetViewWhiteSpace(False)
945 self.SetEOLMode(wx.stc.STC_EOL_LF)
946 self.SetEdgeMode(wx.stc.STC_EDGE_NONE)
947 self.SetEdgeColumn(78)
948
949 self.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER)
950 self.SetMarginWidth(1, self.EstimatedLineNumberMarginWidth())
951 self.UpdateStyles()
952
953 self.SetCaretForeground("BLACK")
954
955 self.SetViewDefaults()
956 font, color = self.GetFontAndColorFromConfig()
957 self.SetFont(font)
958 self.SetFontColor(color)
959 self.MarkerDefineDefault()
960
961
962 def OnFocus(self, event):
963 self.SetSelBackground(1, "BLUE")
964 self.SetSelForeground(1, "WHITE")
965 if hasattr(self, "_dynSash"):
966 self._dynSash._view.SetCtrl(self)
967 event.Skip()
968
969
970 def OnKillFocus(self, event):
971 self.SetSelBackground(0, "BLUE")
972 self.SetSelForeground(0, "WHITE")
973 self.SetSelBackground(1, "#C0C0C0")
974 # Don't set foreground color, use syntax highlighted default colors.
975 event.Skip()
976
977
978 def SetViewDefaults(self, configPrefix = "Text", hasWordWrap = True, hasTabs = False):
979 config = wx.ConfigBase_Get()
980 self.SetViewWhiteSpace(config.ReadInt(configPrefix + "EditorViewWhitespace", False))
981 self.SetViewEOL(config.ReadInt(configPrefix + "EditorViewEOL", False))
982 self.SetIndentationGuides(config.ReadInt(configPrefix + "EditorViewIndentationGuides", False))
983 self.SetViewRightEdge(config.ReadInt(configPrefix + "EditorViewRightEdge", False))
984 self.SetViewLineNumbers(config.ReadInt(configPrefix + "EditorViewLineNumbers", True))
985 if hasWordWrap:
986 self.SetWordWrap(config.ReadInt(configPrefix + "EditorWordWrap", False))
987 if hasTabs: # These methods do not exist in STCTextEditor and are meant for subclasses
988 self.SetUseTabs(config.ReadInt(configPrefix + "EditorUseTabs", False))
989 self.SetIndent(config.ReadInt(configPrefix + "EditorIndentWidth", 4))
990 self.SetTabWidth(config.ReadInt(configPrefix + "EditorIndentWidth", 4))
991 else:
992 self.SetUseTabs(True)
993 self.SetIndent(4)
994 self.SetTabWidth(4)
995
996
997 def GetDefaultFont(self):
998 """ Subclasses should override this """
999 return wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL)
1000
1001
1002 def GetDefaultColor(self):
1003 """ Subclasses should override this """
1004 return wx.BLACK
1005
1006
1007 def GetFontAndColorFromConfig(self, configPrefix = "Text"):
1008 font = self.GetDefaultFont()
1009 config = wx.ConfigBase_Get()
1010 fontData = config.Read(configPrefix + "EditorFont", "")
1011 if fontData:
1012 nativeFont = wx.NativeFontInfo()
1013 nativeFont.FromString(fontData)
1014 font.SetNativeFontInfo(nativeFont)
1015 color = self.GetDefaultColor()
1016 colorData = config.Read(configPrefix + "EditorColor", "")
1017 if colorData:
1018 red = int("0x" + colorData[0:2], 16)
1019 green = int("0x" + colorData[2:4], 16)
1020 blue = int("0x" + colorData[4:6], 16)
1021 color = wx.Color(red, green, blue)
1022 return font, color
1023
1024
1025 def GetFont(self):
1026 return self._font
1027
1028
1029 def SetFont(self, font):
1030 self._font = font
1031 self.StyleSetFont(wx.stc.STC_STYLE_DEFAULT, self._font)
1032
1033
1034 def GetFontColor(self):
1035 return self._fontColor
1036
1037
1038 def SetFontColor(self, fontColor = wx.BLACK):
1039 self._fontColor = fontColor
1040 self.StyleSetForeground(wx.stc.STC_STYLE_DEFAULT, "#%02x%02x%02x" % (self._fontColor.Red(), self._fontColor.Green(), self._fontColor.Blue()))
1041
1042
1043 def UpdateStyles(self):
1044 self.StyleClearAll()
1045 return
1046
1047
1048 def EstimatedLineNumberMarginWidth(self):
1049 MARGIN = 4
1050 baseNumbers = "000"
1051 lineNum = self.GetLineCount()
1052 lineNum = lineNum/100
1053 while lineNum >= 10:
1054 lineNum = lineNum/10
1055 baseNumbers = baseNumbers + "0"
1056
1057 return self.TextWidth(wx.stc.STC_STYLE_LINENUMBER, baseNumbers) + MARGIN
1058
1059
1060 def OnUpdateLineNumberMarginWidth(self, event):
1061 self.UpdateLineNumberMarginWidth()
1062
1063
1064 def UpdateLineNumberMarginWidth(self):
1065 if self.GetViewLineNumbers():
1066 self.SetMarginWidth(1, self.EstimatedLineNumberMarginWidth())
1067
1068 def MarkerDefineDefault(self):
1069 """ This must be called after the textcontrol is instantiated """
1070 self.MarkerDefine(TextView.MARKER_NUM, wx.stc.STC_MARK_ROUNDRECT, wx.BLACK, wx.BLUE)
1071
1072
1073 def OnClear(self):
1074 # Used when Delete key is hit.
1075 sel = self.GetSelection()
1076
1077 # Delete the selection or if no selection, the character after the caret.
1078 if sel[0] == sel[1]:
1079 self.SetSelection(sel[0], sel[0] + 1)
1080 else:
1081 # remove any folded lines also.
1082 startLine = self.LineFromPosition(sel[0])
1083 endLine = self.LineFromPosition(sel[1])
1084 endLineStart = self.PositionFromLine(endLine)
1085 if startLine != endLine and sel[1] - endLineStart == 0:
1086 while not self.GetLineVisible(endLine):
1087 endLine += 1
1088 self.SetSelectionEnd(self.PositionFromLine(endLine))
1089
1090 self.Clear()
1091
1092
1093 def OnPaste(self):
1094 # replace any folded lines also.
1095 sel = self.GetSelection()
1096 startLine = self.LineFromPosition(sel[0])
1097 endLine = self.LineFromPosition(sel[1])
1098 endLineStart = self.PositionFromLine(endLine)
1099 if startLine != endLine and sel[1] - endLineStart == 0:
1100 while not self.GetLineVisible(endLine):
1101 endLine += 1
1102 self.SetSelectionEnd(self.PositionFromLine(endLine))
1103
1104 self.Paste()
1105
1106
1107 def OnKeyPressed(self, event):
1108 key = event.GetKeyCode()
1109 if key == wx.WXK_NUMPAD_ADD: #wxBug: For whatever reason, the key accelerators for numpad add and subtract with modifiers are not working so have to trap them here
1110 if event.ControlDown():
1111 self.ToggleFoldAll(expand = True, topLevelOnly = True)
1112 elif event.ShiftDown():
1113 self.ToggleFoldAll(expand = True)
1114 else:
1115 self.ToggleFold(self.GetCurrentLine())
1116 elif key == wx.WXK_NUMPAD_SUBTRACT:
1117 if event.ControlDown():
1118 self.ToggleFoldAll(expand = False, topLevelOnly = True)
1119 elif event.ShiftDown():
1120 self.ToggleFoldAll(expand = False)
1121 else:
1122 self.ToggleFold(self.GetCurrentLine())
1123 else:
1124 event.Skip()
1125
1126
1127 #----------------------------------------------------------------------------
1128 # View Text methods
1129 #----------------------------------------------------------------------------
1130
1131 def GetViewRightEdge(self):
1132 return self.GetEdgeMode() != wx.stc.STC_EDGE_NONE
1133
1134
1135 def SetViewRightEdge(self, viewRightEdge):
1136 if viewRightEdge:
1137 self.SetEdgeMode(wx.stc.STC_EDGE_LINE)
1138 else:
1139 self.SetEdgeMode(wx.stc.STC_EDGE_NONE)
1140
1141
1142 def GetViewLineNumbers(self):
1143 return self.GetMarginWidth(1) > 0
1144
1145
1146 def SetViewLineNumbers(self, viewLineNumbers = True):
1147 if viewLineNumbers:
1148 self.SetMarginWidth(1, self.EstimatedLineNumberMarginWidth())
1149 else:
1150 self.SetMarginWidth(1, 0)
1151
1152
1153 def CanWordWrap(self):
1154 return True
1155
1156
1157 def GetWordWrap(self):
1158 return self.GetWrapMode() == wx.stc.STC_WRAP_WORD
1159
1160
1161 def SetWordWrap(self, wordWrap):
1162 if wordWrap:
1163 self.SetWrapMode(wx.stc.STC_WRAP_WORD)
1164 else:
1165 self.SetWrapMode(wx.stc.STC_WRAP_NONE)
1166
1167
1168 #----------------------------------------------------------------------------
1169 # DynamicSashWindow methods
1170 #----------------------------------------------------------------------------
1171
1172 def SetupDSScrollBars(self):
1173 # hook the scrollbars provided by the wxDynamicSashWindow
1174 # to this view
1175 v_bar = self._dynSash.GetVScrollBar(self)
1176 h_bar = self._dynSash.GetHScrollBar(self)
1177 v_bar.Bind(wx.EVT_SCROLL, self.OnDSSBScroll)
1178 h_bar.Bind(wx.EVT_SCROLL, self.OnDSSBScroll)
1179 v_bar.Bind(wx.EVT_SET_FOCUS, self.OnDSSBFocus)
1180 h_bar.Bind(wx.EVT_SET_FOCUS, self.OnDSSBFocus)
1181
1182 # And set the wxStyledText to use these scrollbars instead
1183 # of its built-in ones.
1184 self.SetVScrollBar(v_bar)
1185 self.SetHScrollBar(h_bar)
1186
1187
1188 def OnDSSplit(self, evt):
1189 newCtrl = self._dynSash._view.GetCtrlClass()(self._dynSash, -1, style=wx.NO_BORDER)
1190 newCtrl.SetDocPointer(self.GetDocPointer()) # use the same document
1191 self.SetupDSScrollBars()
1192 if self == self._dynSash._view.GetCtrl(): # originally had focus
1193 wx.CallAfter(self.SetFocus) # do this to set colors correctly. wxBug: for some reason, if we don't do a CallAfter, it immediately calls OnKillFocus right after our SetFocus.
1194
1195
1196 def OnDSUnify(self, evt):
1197 self.SetupDSScrollBars()
1198 self.SetFocus() # do this to set colors correctly
1199
1200
1201 def OnDSSBScroll(self, evt):
1202 # redirect the scroll events from the _dynSash's scrollbars to the STC
1203 self.GetEventHandler().ProcessEvent(evt)
1204
1205
1206 def OnDSSBFocus(self, evt):
1207 # when the scrollbar gets the focus move it back to the STC
1208 self.SetFocus()
1209
1210
1211 def DSProcessEvent(self, event):
1212 # wxHack: Needed for customized right mouse click menu items.
1213 if hasattr(self, "_dynSash"):
1214 if event.GetId() == wx.ID_SELECTALL:
1215 # force focus so that select all occurs in the window user right clicked on.
1216 self.SetFocus()
1217
1218 return self._dynSash._view.ProcessEvent(event)
1219 return False
1220
1221
1222 def DSProcessUpdateUIEvent(self, event):
1223 # wxHack: Needed for customized right mouse click menu items.
1224 if hasattr(self, "_dynSash"):
1225 id = event.GetId()
1226 if (id == wx.ID_SELECTALL # allow select all even in non-active window, then force focus to it, see above ProcessEvent
1227 or id == wx.ID_UNDO
1228 or id == wx.ID_REDO):
1229 pass # allow these actions even in non-active window
1230 else: # disallow events in non-active windows. Cut/Copy/Paste/Delete is too confusing user experience.
1231 if self._dynSash._view.GetCtrl() != self:
1232 event.Enable(False)
1233 return True
1234
1235 return self._dynSash._view.ProcessUpdateUIEvent(event)
1236 return False
1237
1238
1239 class TextPrintout(wx.lib.docview.DocPrintout):
1240 """ for Print Preview and Print """
1241
1242
1243 def OnPreparePrinting(self):
1244 """ initialization """
1245 dc = self.GetDC()
1246
1247 ppiScreenX, ppiScreenY = self.GetPPIScreen()
1248 ppiPrinterX, ppiPrinterY = self.GetPPIPrinter()
1249 scaleX = float(ppiPrinterX)/ppiScreenX
1250 scaleY = float(ppiPrinterY)/ppiScreenY
1251
1252 pageWidth, pageHeight = self.GetPageSizePixels()
1253 self._scaleFactorX = scaleX/pageWidth
1254 self._scaleFactorY = scaleY/pageHeight
1255
1256 w, h = dc.GetSize()
1257 overallScaleX = self._scaleFactorX * w
1258 overallScaleY = self._scaleFactorY * h
1259
1260 txtCtrl = self._printoutView.GetCtrl()
1261 font, color = txtCtrl.GetFontAndColorFromConfig()
1262
1263 self._margin = 40
1264 self._fontHeight = font.GetPointSize() + 1
1265 self._pageLines = int((h/overallScaleY - (2 * self._margin))/self._fontHeight)
1266 self._maxLines = txtCtrl.GetLineCount()
1267 self._numPages, remainder = divmod(self._maxLines, self._pageLines)
1268 if remainder != 0:
1269 self._numPages += 1
1270
1271 spaces = 1
1272 lineNum = self._maxLines
1273 while lineNum >= 10:
1274 lineNum = lineNum/10
1275 spaces += 1
1276 self._printFormat = "%%0%sd: %%s" % spaces
1277
1278
1279 def OnPrintPage(self, page):
1280 """ Prints the given page of the view """
1281 dc = self.GetDC()
1282
1283 txtCtrl = self._printoutView.GetCtrl()
1284 font, color = txtCtrl.GetFontAndColorFromConfig()
1285 dc.SetFont(font)
1286
1287 w, h = dc.GetSize()
1288 dc.SetUserScale(self._scaleFactorX * w, self._scaleFactorY * h)
1289
1290 dc.BeginDrawing()
1291
1292 dc.DrawText("%s - page %s" % (self.GetTitle(), page), self._margin, self._margin/2)
1293
1294 startY = self._margin
1295 startLine = (page - 1) * self._pageLines
1296 endLine = min((startLine + self._pageLines), self._maxLines)
1297 for i in range(startLine, endLine):
1298 text = txtCtrl.GetLine(i).rstrip()
1299 startY += self._fontHeight
1300 if txtCtrl.GetViewLineNumbers():
1301 dc.DrawText(self._printFormat % (i+1, text), self._margin, startY)
1302 else:
1303 dc.DrawText(text, self._margin, startY)
1304
1305 dc.EndDrawing()
1306
1307 return True
1308
1309
1310 def HasPage(self, pageNum):
1311 return pageNum <= self._numPages
1312
1313
1314 def GetPageInfo(self):
1315 minPage = 1
1316 maxPage = self._numPages
1317 selPageFrom = 1
1318 selPageTo = self._numPages
1319 return (minPage, maxPage, selPageFrom, selPageTo)
1320
1321
1322 #----------------------------------------------------------------------------
1323 # Icon Bitmaps - generated by encode_bitmaps.py
1324 #----------------------------------------------------------------------------
1325 from wx import ImageFromStream, BitmapFromImage
1326 import cStringIO
1327
1328
1329 def getTextData():
1330 return \
1331 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
1332 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
1333 \x00\x00`IDAT8\x8d\xed\x931\x0e\xc00\x08\x03m\x92\xff\xff8q\xa7JU!$\x12\x1d\
1334 \xeb\t\t8n\x81\xb4\x86J\xfa]h\x0ee\x83\xb4\xc6\x14\x00\x00R\xcc \t\xcd\xa1\
1335 \x08\xd2\xa3\xe1\x08*\t$\x1d\xc4\x012\x0b\x00\xce\xe4\xc8\xe0\t}\xf7\x8f\rV\
1336 \xd9\x1a\xec\xe0\xbf\xc1\xd7\x06\xd9\xf5UX\xfdF+m\x03\xb8\x00\xe4\xc74B"x\
1337 \xf1\xf4\x00\x00\x00\x00IEND\xaeB`\x82'
1338
1339
1340 def getTextBitmap():
1341 return BitmapFromImage(getTextImage())
1342
1343 def getTextImage():
1344 stream = cStringIO.StringIO(getTextData())
1345 return ImageFromStream(stream)
1346
1347 def getTextIcon():
1348 return wx.IconFromBitmap(getTextBitmap())
1349
1350
1351 #----------------------------------------------------------------------------
1352 # Menu Bitmaps - generated by encode_bitmaps.py
1353 #----------------------------------------------------------------------------
1354 #----------------------------------------------------------------------
1355 def getZoomInData():
1356 return \
1357 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
1358 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
1359 \x00\x00wIDAT8\x8d\xa5\x93Q\x12\x80 \x08D\xb5\xe9X\xee\xe9\xb7{\xd5Gc\xa9\
1360 \xacX\xca\x1f\xa0\x8fE0\x92<\xc3\x82\xed*\x08\xa0\xf2I~\x07\x000\x17T,\xdb\
1361 \xd6;\x08\xa4\x00\xa4GA\xab\xca\x00\xbc*\x1eD\xb4\x90\xa4O\x1e\xe3\x16f\xcc(\
1362 \xc8\x95F\x95\x8d\x02\xef\xa1n\xa0\xce\xc5v\x91zc\xacU\xbey\x03\xf0.\xa8\xb8\
1363 \x04\x8c\xac\x04MM\xa1lA\xfe\x85?\x90\xe5=X\x06\\\xebCA\xb3Q\xf34\x14\x00\
1364 \x00\x00\x00IEND\xaeB`\x82'
1365
1366 def getZoomInBitmap():
1367 return BitmapFromImage(getZoomInImage())
1368
1369 def getZoomInImage():
1370 stream = cStringIO.StringIO(getZoomInData())
1371 return ImageFromStream(stream)
1372
1373 #----------------------------------------------------------------------
1374 def getZoomOutData():
1375 return \
1376 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
1377 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
1378 \x00\x00qIDAT8\x8d\xa5\x92Q\x0e\xc0 \x08C-z\xff\x13O\xd9\xd7\x16"\x05\x8d\
1379 \xf6O\xa2\x8f"\x05\xa4\x96\x1b5V\xd4\xd1\xd5\x9e!\x15\xdb\x00\x1d]\xe7\x07\
1380 \xac\xf6Iv.B*fW\x0e\x90u\xc9 d\x84\x87v\x82\xb4\xf5\x08\'r\x0e\xa2N\x91~\x07\
1381 \xd9G\x95\xe2W\xeb\x00\x19\xc4\xd6\\FX\x12\xa3 \xb1:\x05\xacdAG[\xb0y9r`u\
1382 \x9d\x83k\xc0\x0b#3@0A\x0c"\x93\x00\x00\x00\x00IEND\xaeB`\x82'
1383
1384
1385 def getZoomOutBitmap():
1386 return BitmapFromImage(getZoomOutImage())
1387
1388 def getZoomOutImage():
1389 stream = cStringIO.StringIO(getZoomOutData())
1390 return ImageFromStream(stream)
1391
1392
1393