1 #----------------------------------------------------------------------------
2 # Name: STCTextEditor.py
3 # Purpose: Text Editor for wx.lib.pydocview tbat uses the Styled Text Control
5 # Author: Peter Yared, Morgan Hua
9 # Copyright: (c) 2003-2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
16 import wx
.lib
.multisash
17 import wx
.lib
.pydocview
24 #----------------------------------------------------------------------------
26 #----------------------------------------------------------------------------
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()
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()
43 #----------------------------------------------------------------------------
45 #----------------------------------------------------------------------------
47 class TextDocument(wx
.lib
.docview
.Document
):
50 def SaveObject(self
, fileObject
):
51 view
= self
.GetFirstView()
52 fileObject
.write(view
.GetValue())
56 def LoadObject(self
, fileObject
):
57 view
= self
.GetFirstView()
58 data
= fileObject
.read()
64 view
= self
.GetFirstView()
66 return wx
.lib
.docview
.Document
.IsModified(self
) or view
.IsModified()
68 return wx
.lib
.docview
.Document
.IsModified(self
)
71 def Modify(self
, mod
):
72 view
= self
.GetFirstView()
73 wx
.lib
.docview
.Document
.Modify(self
, mod
)
78 def OnCreateCommandProcessor(self
):
79 # Don't create a command processor, it has its own
83 # Use this to override MultiClient.Select to prevent yellow background.
84 def MultiClientSelectBGNotYellow(a
):
85 a
.GetParent().multiView
.UnSelect()
87 #a.SetBackgroundColour(wx.Colour(255,255,0)) # Yellow
90 class TextView(wx
.lib
.docview
.View
):
94 #----------------------------------------------------------------------------
96 #----------------------------------------------------------------------------
99 wx
.lib
.docview
.View
.__init
__(self
)
100 self
._textEditor
= None
101 self
._markerCount
= 0
102 self
._commandProcessor
= None
106 def GetCtrlClass(self
):
107 """ Used in split window to instantiate new instances """
112 if wx
.Platform
== "__WXMAC__":
113 # look for active one first
114 self
._textEditor
= self
._GetActiveCtrl
(self
._dynSash
)
115 if self
._textEditor
== None: # it is possible none are active
116 # look for any existing one
117 self
._textEditor
= self
._FindCtrl
(self
._dynSash
)
118 return self
._textEditor
121 def SetCtrl(self
, ctrl
):
122 self
._textEditor
= ctrl
125 def OnCreatePrintout(self
):
126 """ for Print Preview and Print """
127 return TextPrintout(self
, self
.GetDocument().GetPrintableName())
130 def OnCreate(self
, doc
, flags
):
131 frame
= wx
.GetApp().CreateDocumentFrame(self
, doc
, flags
, style
= wx
.DEFAULT_FRAME_STYLE | wx
.NO_FULL_REPAINT_ON_RESIZE
)
132 # wxBug: DynamicSashWindow doesn't work on Mac, so revert to
133 # multisash implementation
134 if wx
.Platform
== "__WXMAC__":
135 wx
.lib
.multisash
.MultiClient
.Select
= MultiClientSelectBGNotYellow
136 self
._dynSash
= wx
.lib
.multisash
.MultiSash(frame
, -1)
137 self
._dynSash
.SetDefaultChildClass(self
.GetCtrlClass()) # wxBug: MultiSash instantiates the first TextCtrl with this call
139 self
._textEditor
= self
.GetCtrl() # wxBug: grab the TextCtrl from the MultiSash datastructure
141 self
._dynSash
= wx
.gizmos
.DynamicSashWindow(frame
, -1, style
=wx
.CLIP_CHILDREN
)
142 self
._dynSash
._view
= self
143 self
._textEditor
= self
.GetCtrlClass()(self
._dynSash
, -1, style
=wx
.NO_BORDER
)
144 wx
.EVT_LEFT_DOWN(self
._textEditor
, self
.OnLeftClick
)
145 self
._CreateSizer
(frame
)
152 def _CreateSizer(self
, frame
):
153 sizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
154 sizer
.Add(self
._dynSash
, 1, wx
.EXPAND
)
155 frame
.SetSizer(sizer
)
158 def OnLeftClick(self
, event
):
163 def OnUpdate(self
, sender
= None, hint
= None):
164 if hint
== "ViewStuff":
165 self
.GetCtrl().SetViewDefaults()
167 font
, color
= self
.GetCtrl().GetFontAndColorFromConfig()
168 self
.GetCtrl().SetFont(font
)
169 self
.GetCtrl().SetFontColor(color
)
172 def OnActivateView(self
, activate
, activeView
, deactiveView
):
173 if activate
and self
.GetCtrl():
174 # In MDI mode just calling set focus doesn't work and in SDI mode using CallAfter causes an endless loop
175 if self
.GetDocumentManager().GetFlags() & wx
.lib
.docview
.DOC_SDI
:
178 wx
.CallAfter(self
.SetFocus
)
183 self
.GetCtrl().SetFocus()
186 def OnClose(self
, deleteWindow
= True):
187 if not wx
.lib
.docview
.View
.OnClose(self
, deleteWindow
):
190 if deleteWindow
and self
.GetFrame():
191 self
.GetFrame().Destroy()
195 def ProcessEvent(self
, event
):
198 self
.GetCtrl().Undo()
200 elif id == wx
.ID_REDO
:
201 self
.GetCtrl().Redo()
203 elif id == wx
.ID_CUT
:
206 elif id == wx
.ID_COPY
:
207 self
.GetCtrl().Copy()
209 elif id == wx
.ID_PASTE
:
210 self
.GetCtrl().OnPaste()
212 elif id == wx
.ID_CLEAR
:
213 self
.GetCtrl().OnClear()
215 elif id == wx
.ID_SELECTALL
:
216 self
.GetCtrl().SetSelection(0, -1)
218 elif id == VIEW_WHITESPACE_ID
:
219 self
.GetCtrl().SetViewWhiteSpace(not self
.GetCtrl().GetViewWhiteSpace())
221 elif id == VIEW_EOL_ID
:
222 self
.GetCtrl().SetViewEOL(not self
.GetCtrl().GetViewEOL())
224 elif id == VIEW_INDENTATION_GUIDES_ID
:
225 self
.GetCtrl().SetIndentationGuides(not self
.GetCtrl().GetIndentationGuides())
227 elif id == VIEW_RIGHT_EDGE_ID
:
228 self
.GetCtrl().SetViewRightEdge(not self
.GetCtrl().GetViewRightEdge())
230 elif id == VIEW_LINE_NUMBERS_ID
:
231 self
.GetCtrl().SetViewLineNumbers(not self
.GetCtrl().GetViewLineNumbers())
233 elif id == ZOOM_NORMAL_ID
:
234 self
.GetCtrl().SetZoom(0)
236 elif id == ZOOM_IN_ID
:
237 self
.GetCtrl().CmdKeyExecute(wx
.stc
.STC_CMD_ZOOMIN
)
239 elif id == ZOOM_OUT_ID
:
240 self
.GetCtrl().CmdKeyExecute(wx
.stc
.STC_CMD_ZOOMOUT
)
242 elif id == CHOOSE_FONT_ID
:
245 elif id == WORD_WRAP_ID
:
246 self
.GetCtrl().SetWordWrap(not self
.GetCtrl().GetWordWrap())
248 elif id == FindService
.FindService
.FIND_ID
:
251 elif id == FindService
.FindService
.FIND_PREVIOUS_ID
:
252 self
.DoFind(forceFindPrevious
= True)
254 elif id == FindService
.FindService
.FIND_NEXT_ID
:
255 self
.DoFind(forceFindNext
= True)
257 elif id == FindService
.FindService
.REPLACE_ID
:
258 self
.OnFind(replace
= True)
260 elif id == FindService
.FindService
.FINDONE_ID
:
263 elif id == FindService
.FindService
.REPLACEONE_ID
:
264 self
.DoFind(replace
= True)
266 elif id == FindService
.FindService
.REPLACEALL_ID
:
267 self
.DoFind(replaceAll
= True)
269 elif id == FindService
.FindService
.GOTO_LINE_ID
:
270 self
.OnGotoLine(event
)
273 return wx
.lib
.docview
.View
.ProcessEvent(self
, event
)
276 def ProcessUpdateUIEvent(self
, event
):
277 if not self
.GetCtrl():
282 event
.Enable(self
.GetCtrl().CanUndo())
283 event
.SetText(_("&Undo\tCtrl+Z")) # replace menu string
285 elif id == wx
.ID_REDO
:
286 event
.Enable(self
.GetCtrl().CanRedo())
287 event
.SetText(_("&Redo\tCtrl+Y")) # replace menu string
289 elif (id == wx
.ID_CUT
291 or id == wx
.ID_CLEAR
):
292 hasSelection
= self
.GetCtrl().GetSelectionStart() != self
.GetCtrl().GetSelectionEnd()
293 event
.Enable(hasSelection
)
295 elif id == wx
.ID_PASTE
:
296 event
.Enable(self
.GetCtrl().CanPaste())
298 elif id == wx
.ID_SELECTALL
:
299 hasText
= self
.GetCtrl().GetTextLength() > 0
300 event
.Enable(hasText
)
305 elif id == VIEW_WHITESPACE_ID
:
306 hasText
= self
.GetCtrl().GetTextLength() > 0
307 event
.Enable(hasText
)
308 event
.Check(self
.GetCtrl().GetViewWhiteSpace())
310 elif id == VIEW_EOL_ID
:
311 hasText
= self
.GetCtrl().GetTextLength() > 0
312 event
.Enable(hasText
)
313 event
.Check(self
.GetCtrl().GetViewEOL())
315 elif id == VIEW_INDENTATION_GUIDES_ID
:
316 hasText
= self
.GetCtrl().GetTextLength() > 0
317 event
.Enable(hasText
)
318 event
.Check(self
.GetCtrl().GetIndentationGuides())
320 elif id == VIEW_RIGHT_EDGE_ID
:
321 hasText
= self
.GetCtrl().GetTextLength() > 0
322 event
.Enable(hasText
)
323 event
.Check(self
.GetCtrl().GetViewRightEdge())
325 elif id == VIEW_LINE_NUMBERS_ID
:
326 hasText
= self
.GetCtrl().GetTextLength() > 0
327 event
.Enable(hasText
)
328 event
.Check(self
.GetCtrl().GetViewLineNumbers())
333 elif id == ZOOM_NORMAL_ID
:
334 event
.Enable(self
.GetCtrl().GetZoom() != 0)
336 elif id == ZOOM_IN_ID
:
337 event
.Enable(self
.GetCtrl().GetZoom() < 20)
339 elif id == ZOOM_OUT_ID
:
340 event
.Enable(self
.GetCtrl().GetZoom() > -10)
342 elif id == CHOOSE_FONT_ID
:
345 elif id == WORD_WRAP_ID
:
346 event
.Enable(self
.GetCtrl().CanWordWrap())
347 event
.Check(self
.GetCtrl().CanWordWrap() and self
.GetCtrl().GetWordWrap())
349 elif id == FindService
.FindService
.FIND_ID
:
350 hasText
= self
.GetCtrl().GetTextLength() > 0
351 event
.Enable(hasText
)
353 elif id == FindService
.FindService
.FIND_PREVIOUS_ID
:
354 hasText
= self
.GetCtrl().GetTextLength() > 0
355 event
.Enable(hasText
and
356 self
._FindServiceHasString
() and
357 self
.GetCtrl().GetSelection()[0] > 0)
359 elif id == FindService
.FindService
.FIND_NEXT_ID
:
360 hasText
= self
.GetCtrl().GetTextLength() > 0
361 event
.Enable(hasText
and
362 self
._FindServiceHasString
() and
363 self
.GetCtrl().GetSelection()[0] < self
.GetCtrl().GetLength())
365 elif id == FindService
.FindService
.REPLACE_ID
:
366 hasText
= self
.GetCtrl().GetTextLength() > 0
367 event
.Enable(hasText
)
369 elif id == FindService
.FindService
.GOTO_LINE_ID
:
372 elif id == TEXT_STATUS_BAR_ID
:
373 self
.OnUpdateStatusBar(event
)
376 return wx
.lib
.docview
.View
.ProcessUpdateUIEvent(self
, event
)
379 def _GetParentFrame(self
):
380 return wx
.GetTopLevelParent(self
.GetFrame())
382 def _GetActiveCtrl(self
, parent
):
383 """ Walk through the MultiSash windows and find the active Control """
384 if isinstance(parent
, wx
.lib
.multisash
.MultiClient
) and parent
.selected
:
386 if hasattr(parent
, "GetChildren"):
387 for child
in parent
.GetChildren():
388 found
= self
._GetActiveCtrl
(child
)
394 def _FindCtrl(self
, parent
):
395 """ Walk through the MultiSash windows and find the first TextCtrl """
396 if isinstance(parent
, self
.GetCtrlClass()):
398 if hasattr(parent
, "GetChildren"):
399 for child
in parent
.GetChildren():
400 found
= self
._FindCtrl
(child
)
406 #----------------------------------------------------------------------------
407 # Methods for TextDocument to call
408 #----------------------------------------------------------------------------
410 def IsModified(self
):
411 if not self
.GetCtrl():
413 return self
.GetCtrl().GetModify()
416 def SetModifyFalse(self
):
417 self
.GetCtrl().SetSavePoint()
422 return self
.GetCtrl().GetText()
427 def SetValue(self
, value
):
428 self
.GetCtrl().SetText(value
)
429 self
.GetCtrl().UpdateLineNumberMarginWidth()
430 self
.GetCtrl().EmptyUndoBuffer()
433 #----------------------------------------------------------------------------
435 #----------------------------------------------------------------------------
437 def OnUpdateStatusBar(self
, event
):
438 statusBar
= self
._GetParentFrame
().GetStatusBar()
439 statusBar
.SetInsertMode(self
.GetCtrl().GetOvertype() == 0)
440 statusBar
.SetLineNumber(self
.GetCtrl().GetCurrentLine() + 1)
441 statusBar
.SetColumnNumber(self
.GetCtrl().GetColumn(self
.GetCtrl().GetCurrentPos()) + 1)
444 #----------------------------------------------------------------------------
446 #----------------------------------------------------------------------------
448 def OnChooseFont(self
):
450 data
.EnableEffects(True)
451 data
.SetInitialFont(self
.GetCtrl().GetFont())
452 data
.SetColour(self
.GetCtrl().GetFontColor())
453 fontDialog
= wx
.FontDialog(self
.GetFrame(), data
)
454 fontDialog
.CenterOnParent()
455 if fontDialog
.ShowModal() == wx
.ID_OK
:
456 data
= fontDialog
.GetFontData()
457 self
.GetCtrl().SetFont(data
.GetChosenFont())
458 self
.GetCtrl().SetFontColor(data
.GetColour())
459 self
.GetCtrl().UpdateStyles()
463 #----------------------------------------------------------------------------
465 #----------------------------------------------------------------------------
467 def OnFind(self
, replace
= False):
468 findService
= wx
.GetApp().GetService(FindService
.FindService
)
470 findService
.ShowFindReplaceDialog(findString
= self
.GetCtrl().GetSelectedText(), replace
= replace
)
473 def DoFind(self
, forceFindNext
= False, forceFindPrevious
= False, replace
= False, replaceAll
= False):
474 findService
= wx
.GetApp().GetService(FindService
.FindService
)
477 findString
= findService
.GetFindString()
478 if len(findString
) == 0:
480 replaceString
= findService
.GetReplaceString()
481 flags
= findService
.GetFlags()
482 startLoc
, endLoc
= self
.GetCtrl().GetSelection()
484 wholeWord
= flags
& wx
.FR_WHOLEWORD
> 0
485 matchCase
= flags
& wx
.FR_MATCHCASE
> 0
486 regExp
= flags
& FindService
.FindService
.FR_REGEXP
> 0
487 down
= flags
& wx
.FR_DOWN
> 0
488 wrap
= flags
& FindService
.FindService
.FR_WRAP
> 0
490 if forceFindPrevious
: # this is from function keys, not dialog box
492 wrap
= False # user would want to know they're at the end of file
495 wrap
= False # user would want to know they're at the end of file
499 # On replace dialog operations, user is allowed to replace the currently highlighted text to determine if it should be replaced or not.
500 # 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.
501 # If the text is a match, then replace it.
503 result
, start
, end
, replText
= findService
.DoFind(findString
, replaceString
, self
.GetCtrl().GetSelectedText(), 0, 0, True, matchCase
, wholeWord
, regExp
, replace
)
505 self
.GetCtrl().ReplaceSelection(replText
)
506 self
.GetDocument().Modify(True)
507 wx
.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString
)
509 startLoc
+= len(replText
) # advance start location past replacement string to new text
511 elif result
== FindService
.FIND_SYNTAXERROR
:
513 wx
.GetApp().GetTopWindow().PushStatusText(_("Invalid regular expression \"%s\"") % findString
)
516 text
= self
.GetCtrl().GetText()
518 # Find the next matching text occurance or if it is a ReplaceAll, replace all occurances
519 # 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
520 result
, start
, end
, text
= findService
.DoFind(findString
, replaceString
, text
, startLoc
, endLoc
, down
, matchCase
, wholeWord
, regExp
, False, replaceAll
, wrap
)
522 self
.GetCtrl().SetTargetStart(0)
523 self
.GetCtrl().SetTargetEnd(self
.GetCtrl().GetLength())
524 self
.GetCtrl().ReplaceTarget(text
) # Doing a SetText causes a clear document to be shown when undoing, so using replacetarget instead
525 self
.GetDocument().Modify(True)
527 wx
.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString
)
529 wx
.GetApp().GetTopWindow().PushStatusText(_("%i occurrences of \"%s\" replaced") % (result
, findString
))
531 self
.GetCtrl().SetSelection(start
, end
)
532 self
.GetCtrl().EnsureVisible(self
.GetCtrl().LineFromPosition(end
)) # show bottom then scroll up to top
533 self
.GetCtrl().EnsureVisible(self
.GetCtrl().LineFromPosition(start
)) # do this after ensuring bottom is visible
534 wx
.GetApp().GetTopWindow().PushStatusText(_("Found \"%s\".") % findString
)
535 elif result
== FindService
.FIND_SYNTAXERROR
:
536 # Dialog for this case gets popped up by the FindService.
537 wx
.GetApp().GetTopWindow().PushStatusText(_("Invalid regular expression \"%s\"") % findString
)
539 wx
.MessageBox(_("Can't find \"%s\".") % findString
, "Find",
540 wx
.OK | wx
.ICON_INFORMATION
)
543 def _FindServiceHasString(self
):
544 findService
= wx
.GetApp().GetService(FindService
.FindService
)
545 if not findService
or not findService
.GetFindString():
550 def OnGotoLine(self
, event
):
551 findService
= wx
.GetApp().GetService(FindService
.FindService
)
553 line
= findService
.GetLineNumber(self
.GetDocumentManager().FindSuitableParent())
556 self
.GetCtrl().EnsureVisible(line
)
557 self
.GetCtrl().GotoLine(line
)
560 def GotoLine(self
, lineNum
):
562 lineNum
= lineNum
- 1 # line numbering for editor is 0 based, we are 1 based.
563 self
.GetCtrl().EnsureVisibleEnforcePolicy(lineNum
)
564 self
.GetCtrl().GotoLine(lineNum
)
567 def SetSelection(self
, start
, end
):
568 self
.GetCtrl().SetSelection(start
, end
)
571 def EnsureVisible(self
, line
):
572 self
.GetCtrl().EnsureVisible(line
-1) # line numbering for editor is 0 based, we are 1 based.
574 def EnsureVisibleEnforcePolicy(self
, line
):
575 self
.GetCtrl().EnsureVisibleEnforcePolicy(line
-1) # line numbering for editor is 0 based, we are 1 based.
577 def LineFromPosition(self
, pos
):
578 return self
.GetCtrl().LineFromPosition(pos
)+1 # line numbering for editor is 0 based, we are 1 based.
581 def PositionFromLine(self
, line
):
582 return self
.GetCtrl().PositionFromLine(line
-1) # line numbering for editor is 0 based, we are 1 based.
585 def GetLineEndPosition(self
, line
):
586 return self
.GetCtrl().GetLineEndPosition(line
-1) # line numbering for editor is 0 based, we are 1 based.
589 def GetLine(self
, lineNum
):
590 return self
.GetCtrl().GetLine(lineNum
-1) # line numbering for editor is 0 based, we are 1 based.
593 def MarkerDefine(self
):
594 """ This must be called after the texteditor is instantiated """
595 self
.GetCtrl().MarkerDefine(TextView
.MARKER_NUM
, wx
.stc
.STC_MARK_CIRCLE
, wx
.BLACK
, wx
.BLUE
)
598 def MarkerToggle(self
, lineNum
= -1, marker_index
=MARKER_NUM
, mask
=MARKER_MASK
):
600 lineNum
= self
.GetCtrl().GetCurrentLine()
601 if self
.GetCtrl().MarkerGet(lineNum
) & mask
:
602 self
.GetCtrl().MarkerDelete(lineNum
, marker_index
)
603 self
._markerCount
-= 1
605 self
.GetCtrl().MarkerAdd(lineNum
, marker_index
)
606 self
._markerCount
+= 1
609 def MarkerAdd(self
, lineNum
= -1, marker_index
=MARKER_NUM
, mask
=MARKER_MASK
):
611 lineNum
= self
.GetCtrl().GetCurrentLine()
612 self
.GetCtrl().MarkerAdd(lineNum
, marker_index
)
613 self
._markerCount
+= 1
616 def MarkerDelete(self
, lineNum
= -1, marker_index
=MARKER_NUM
, mask
=MARKER_MASK
):
618 lineNum
= self
.GetCtrl().GetCurrentLine()
619 if self
.GetCtrl().MarkerGet(lineNum
) & mask
:
620 self
.GetCtrl().MarkerDelete(lineNum
, marker_index
)
621 self
._markerCount
-= 1
623 def MarkerDeleteAll(self
, marker_num
=MARKER_NUM
):
624 self
.GetCtrl().MarkerDeleteAll(marker_num
)
625 if marker_num
== self
.MARKER_NUM
:
626 self
._markerCount
= 0
629 def MarkerNext(self
, lineNum
= -1):
631 lineNum
= self
.GetCtrl().GetCurrentLine() + 1 # start search below current line
632 foundLine
= self
.GetCtrl().MarkerNext(lineNum
, self
.MARKER_MASK
)
634 # wrap to top of file
635 foundLine
= self
.GetCtrl().MarkerNext(0, self
.MARKER_MASK
)
637 wx
.GetApp().GetTopWindow().PushStatusText(_("No markers"))
640 self
.GotoLine(foundLine
+ 1)
643 def MarkerPrevious(self
, lineNum
= -1):
645 lineNum
= self
.GetCtrl().GetCurrentLine() - 1 # start search above current line
647 lineNum
= self
.GetCtrl().GetLineCount()
649 foundLine
= self
.GetCtrl().MarkerPrevious(lineNum
, self
.MARKER_MASK
)
651 # wrap to bottom of file
652 foundLine
= self
.GetCtrl().MarkerPrevious(self
.GetCtrl().GetLineCount(), self
.MARKER_MASK
)
654 wx
.GetApp().GetTopWindow().PushStatusText(_("No markers"))
657 self
.GotoLine(foundLine
+ 1)
660 def MarkerExists(self
, lineNum
= -1, mask
=MARKER_MASK
):
662 lineNum
= self
.GetCtrl().GetCurrentLine()
663 if self
.GetCtrl().MarkerGet(lineNum
) & mask
:
668 def GetMarkerLines(self
, mask
=MARKER_MASK
):
670 for lineNum
in range(self
.GetCtrl().GetLineCount()):
671 if self
.GetCtrl().MarkerGet(lineNum
) & mask
:
672 retval
.append(lineNum
)
675 def GetMarkerCount(self
):
676 return self
._markerCount
679 class TextService(wx
.lib
.pydocview
.DocService
):
683 wx
.lib
.pydocview
.DocService
.__init
__(self
)
686 def InstallControls(self
, frame
, menuBar
= None, toolBar
= None, statusBar
= None, document
= None):
687 if document
and document
.GetDocumentTemplate().GetDocumentType() != TextDocument
:
689 if not document
and wx
.GetApp().GetDocumentManager().GetFlags() & wx
.lib
.docview
.DOC_SDI
:
692 statusBar
= TextStatusBar(frame
, TEXT_STATUS_BAR_ID
)
693 frame
.SetStatusBar(statusBar
)
694 wx
.EVT_UPDATE_UI(frame
, TEXT_STATUS_BAR_ID
, frame
.ProcessUpdateUIEvent
)
696 viewMenu
= menuBar
.GetMenu(menuBar
.FindMenu(_("&View")))
698 viewMenu
.AppendSeparator()
700 textMenu
.AppendCheckItem(VIEW_WHITESPACE_ID
, _("&Whitespace"), _("Shows or hides whitespace"))
701 wx
.EVT_MENU(frame
, VIEW_WHITESPACE_ID
, frame
.ProcessEvent
)
702 wx
.EVT_UPDATE_UI(frame
, VIEW_WHITESPACE_ID
, frame
.ProcessUpdateUIEvent
)
703 textMenu
.AppendCheckItem(VIEW_EOL_ID
, _("&End of Line Markers"), _("Shows or hides indicators at the end of each line"))
704 wx
.EVT_MENU(frame
, VIEW_EOL_ID
, frame
.ProcessEvent
)
705 wx
.EVT_UPDATE_UI(frame
, VIEW_EOL_ID
, frame
.ProcessUpdateUIEvent
)
706 textMenu
.AppendCheckItem(VIEW_INDENTATION_GUIDES_ID
, _("&Indentation Guides"), _("Shows or hides indentations"))
707 wx
.EVT_MENU(frame
, VIEW_INDENTATION_GUIDES_ID
, frame
.ProcessEvent
)
708 wx
.EVT_UPDATE_UI(frame
, VIEW_INDENTATION_GUIDES_ID
, frame
.ProcessUpdateUIEvent
)
709 textMenu
.AppendCheckItem(VIEW_RIGHT_EDGE_ID
, _("&Right Edge"), _("Shows or hides the right edge marker"))
710 wx
.EVT_MENU(frame
, VIEW_RIGHT_EDGE_ID
, frame
.ProcessEvent
)
711 wx
.EVT_UPDATE_UI(frame
, VIEW_RIGHT_EDGE_ID
, frame
.ProcessUpdateUIEvent
)
712 textMenu
.AppendCheckItem(VIEW_LINE_NUMBERS_ID
, _("&Line Numbers"), _("Shows or hides the line numbers"))
713 wx
.EVT_MENU(frame
, VIEW_LINE_NUMBERS_ID
, frame
.ProcessEvent
)
714 wx
.EVT_UPDATE_UI(frame
, VIEW_LINE_NUMBERS_ID
, frame
.ProcessUpdateUIEvent
)
716 viewMenu
.AppendMenu(TEXT_ID
, _("&Text"), textMenu
)
717 wx
.EVT_UPDATE_UI(frame
, TEXT_ID
, frame
.ProcessUpdateUIEvent
)
719 isWindows
= (wx
.Platform
== '__WXMSW__')
722 zoomMenu
.Append(ZOOM_NORMAL_ID
, _("Normal Size"), _("Sets the document to its normal size"))
723 wx
.EVT_MENU(frame
, ZOOM_NORMAL_ID
, frame
.ProcessEvent
)
724 wx
.EVT_UPDATE_UI(frame
, ZOOM_NORMAL_ID
, frame
.ProcessUpdateUIEvent
)
726 zoomMenu
.Append(ZOOM_IN_ID
, _("Zoom In\tCtrl+Page Up"), _("Zooms the document to a larger size"))
728 zoomMenu
.Append(ZOOM_IN_ID
, _("Zoom In"), _("Zooms the document to a larger size"))
729 wx
.EVT_MENU(frame
, ZOOM_IN_ID
, frame
.ProcessEvent
)
730 wx
.EVT_UPDATE_UI(frame
, ZOOM_IN_ID
, frame
.ProcessUpdateUIEvent
)
732 zoomMenu
.Append(ZOOM_OUT_ID
, _("Zoom Out\tCtrl+Page Down"), _("Zooms the document to a smaller size"))
734 zoomMenu
.Append(ZOOM_OUT_ID
, _("Zoom Out"), _("Zooms the document to a smaller size"))
735 wx
.EVT_MENU(frame
, ZOOM_OUT_ID
, frame
.ProcessEvent
)
736 wx
.EVT_UPDATE_UI(frame
, ZOOM_OUT_ID
, frame
.ProcessUpdateUIEvent
)
738 viewMenu
.AppendMenu(ZOOM_ID
, _("&Zoom"), zoomMenu
)
739 wx
.EVT_UPDATE_UI(frame
, ZOOM_ID
, frame
.ProcessUpdateUIEvent
)
741 formatMenuIndex
= menuBar
.FindMenu(_("&Format"))
742 if formatMenuIndex
> -1:
743 formatMenu
= menuBar
.GetMenu(formatMenuIndex
)
745 formatMenu
= wx
.Menu()
746 if not menuBar
.FindItemById(CHOOSE_FONT_ID
):
747 formatMenu
.Append(CHOOSE_FONT_ID
, _("&Font..."), _("Sets the font to use"))
748 wx
.EVT_MENU(frame
, CHOOSE_FONT_ID
, frame
.ProcessEvent
)
749 wx
.EVT_UPDATE_UI(frame
, CHOOSE_FONT_ID
, frame
.ProcessUpdateUIEvent
)
750 if not menuBar
.FindItemById(WORD_WRAP_ID
):
751 formatMenu
.AppendCheckItem(WORD_WRAP_ID
, _("Word Wrap"), _("Wraps text horizontally when checked"))
752 wx
.EVT_MENU(frame
, WORD_WRAP_ID
, frame
.ProcessEvent
)
753 wx
.EVT_UPDATE_UI(frame
, WORD_WRAP_ID
, frame
.ProcessUpdateUIEvent
)
754 if formatMenuIndex
== -1:
755 viewMenuIndex
= menuBar
.FindMenu(_("&View"))
756 menuBar
.Insert(viewMenuIndex
+ 1, formatMenu
, _("&Format"))
758 # wxBug: wxToolBar::GetToolPos doesn't exist, need it to find cut tool and then insert find in front of it.
759 toolBar
.AddSeparator()
760 toolBar
.AddTool(ZOOM_IN_ID
, getZoomInBitmap(), shortHelpString
= _("Zoom In"), longHelpString
= _("Zooms the document to a larger size"))
761 toolBar
.AddTool(ZOOM_OUT_ID
, getZoomOutBitmap(), shortHelpString
= _("Zoom Out"), longHelpString
= _("Zooms the document to a smaller size"))
765 def ProcessUpdateUIEvent(self
, event
):
768 or id == VIEW_WHITESPACE_ID
770 or id == VIEW_INDENTATION_GUIDES_ID
771 or id == VIEW_RIGHT_EDGE_ID
772 or id == VIEW_LINE_NUMBERS_ID
774 or id == ZOOM_NORMAL_ID
777 or id == CHOOSE_FONT_ID
778 or id == WORD_WRAP_ID
):
785 class TextStatusBar(wx
.StatusBar
):
787 # 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
789 def __init__(self
, parent
, id, style
= wx
.ST_SIZEGRIP
, name
= "statusBar"):
790 wx
.StatusBar
.__init
__(self
, parent
, id, style
, name
)
791 self
.SetFieldsCount(4)
792 self
.SetStatusWidths([-1, 50, 50, 55])
794 def SetInsertMode(self
, insert
= True):
799 if self
.GetStatusText(1) != newText
: # wxBug: Need to check if the text has changed, otherwise it flickers under win32
800 self
.SetStatusText(newText
, 1)
802 def SetLineNumber(self
, lineNumber
):
803 newText
= _("Ln %i") % lineNumber
804 if self
.GetStatusText(2) != newText
:
805 self
.SetStatusText(newText
, 2)
807 def SetColumnNumber(self
, colNumber
):
808 newText
= _("Col %i") % colNumber
809 if self
.GetStatusText(3) != newText
:
810 self
.SetStatusText(newText
, 3)
813 class TextOptionsPanel(wx
.Panel
):
816 def __init__(self
, parent
, id, configPrefix
= "Text", label
= "Text", hasWordWrap
= True, hasTabs
= False, addPage
=True):
817 wx
.Panel
.__init
__(self
, parent
, id)
818 self
._configPrefix
= configPrefix
819 self
._hasWordWrap
= hasWordWrap
820 self
._hasTabs
= hasTabs
823 config
= wx
.ConfigBase_Get()
824 self
._textFont
= wx
.Font(10, wx
.MODERN
, wx
.NORMAL
, wx
.NORMAL
)
825 fontData
= config
.Read(self
._configPrefix
+ "EditorFont", "")
827 nativeFont
= wx
.NativeFontInfo()
828 nativeFont
.FromString(fontData
)
829 self
._textFont
.SetNativeFontInfo(nativeFont
)
830 self
._originalTextFont
= self
._textFont
831 self
._textColor
= wx
.BLACK
832 colorData
= config
.Read(self
._configPrefix
+ "EditorColor", "")
834 red
= int("0x" + colorData
[0:2], 16)
835 green
= int("0x" + colorData
[2:4], 16)
836 blue
= int("0x" + colorData
[4:6], 16)
837 self
._textColor
= wx
.Color(red
, green
, blue
)
838 self
._originalTextColor
= self
._textColor
839 fontLabel
= wx
.StaticText(self
, -1, _("Font:"))
840 self
._sampleTextCtrl
= wx
.TextCtrl(self
, -1, "", size
= (125, 21))
841 self
._sampleTextCtrl
.SetEditable(False)
842 chooseFontButton
= wx
.Button(self
, -1, _("Choose Font..."))
843 wx
.EVT_BUTTON(self
, chooseFontButton
.GetId(), self
.OnChooseFont
)
844 if self
._hasWordWrap
:
845 self
._wordWrapCheckBox
= wx
.CheckBox(self
, -1, _("Wrap words inside text area"))
846 self
._wordWrapCheckBox
.SetValue(wx
.ConfigBase_Get().ReadInt(self
._configPrefix
+ "EditorWordWrap", False))
847 self
._viewWhitespaceCheckBox
= wx
.CheckBox(self
, -1, _("Show whitespace"))
848 self
._viewWhitespaceCheckBox
.SetValue(config
.ReadInt(self
._configPrefix
+ "EditorViewWhitespace", False))
849 self
._viewEOLCheckBox
= wx
.CheckBox(self
, -1, _("Show end of line markers"))
850 self
._viewEOLCheckBox
.SetValue(config
.ReadInt(self
._configPrefix
+ "EditorViewEOL", False))
851 self
._viewIndentationGuideCheckBox
= wx
.CheckBox(self
, -1, _("Show indentation guides"))
852 self
._viewIndentationGuideCheckBox
.SetValue(config
.ReadInt(self
._configPrefix
+ "EditorViewIndentationGuides", False))
853 self
._viewRightEdgeCheckBox
= wx
.CheckBox(self
, -1, _("Show right edge"))
854 self
._viewRightEdgeCheckBox
.SetValue(config
.ReadInt(self
._configPrefix
+ "EditorViewRightEdge", False))
855 self
._viewLineNumbersCheckBox
= wx
.CheckBox(self
, -1, _("Show line numbers"))
856 self
._viewLineNumbersCheckBox
.SetValue(config
.ReadInt(self
._configPrefix
+ "EditorViewLineNumbers", True))
858 self
._hasTabsCheckBox
= wx
.CheckBox(self
, -1, _("Use spaces instead of tabs"))
859 self
._hasTabsCheckBox
.SetValue(not wx
.ConfigBase_Get().ReadInt(self
._configPrefix
+ "EditorUseTabs", False))
860 indentWidthLabel
= wx
.StaticText(self
, -1, _("Indent Width:"))
861 self
._indentWidthChoice
= wx
.Choice(self
, -1, choices
= ["2", "4", "6", "8", "10"])
862 self
._indentWidthChoice
.SetStringSelection(str(config
.ReadInt(self
._configPrefix
+ "EditorIndentWidth", 4)))
863 textPanelBorderSizer
= wx
.BoxSizer(wx
.VERTICAL
)
864 textPanelSizer
= wx
.BoxSizer(wx
.VERTICAL
)
865 textFontSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
866 textFontSizer
.Add(fontLabel
, 0, wx
.ALIGN_LEFT | wx
.RIGHT | wx
.TOP
, HALF_SPACE
)
867 textFontSizer
.Add(self
._sampleTextCtrl
, 1, wx
.ALIGN_LEFT | wx
.EXPAND | wx
.RIGHT
, HALF_SPACE
)
868 textFontSizer
.Add(chooseFontButton
, 0, wx
.ALIGN_RIGHT | wx
.LEFT
, HALF_SPACE
)
869 textPanelSizer
.Add(textFontSizer
, 0, wx
.ALL|wx
.EXPAND
, HALF_SPACE
)
870 if self
._hasWordWrap
:
871 textPanelSizer
.Add(self
._wordWrapCheckBox
, 0, wx
.ALL
, HALF_SPACE
)
872 textPanelSizer
.Add(self
._viewWhitespaceCheckBox
, 0, wx
.ALL
, HALF_SPACE
)
873 textPanelSizer
.Add(self
._viewEOLCheckBox
, 0, wx
.ALL
, HALF_SPACE
)
874 textPanelSizer
.Add(self
._viewIndentationGuideCheckBox
, 0, wx
.ALL
, HALF_SPACE
)
875 textPanelSizer
.Add(self
._viewRightEdgeCheckBox
, 0, wx
.ALL
, HALF_SPACE
)
876 textPanelSizer
.Add(self
._viewLineNumbersCheckBox
, 0, wx
.ALL
, HALF_SPACE
)
878 textPanelSizer
.Add(self
._hasTabsCheckBox
, 0, wx
.ALL
, HALF_SPACE
)
879 textIndentWidthSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
880 textIndentWidthSizer
.Add(indentWidthLabel
, 0, wx
.ALIGN_LEFT | wx
.RIGHT | wx
.TOP
, HALF_SPACE
)
881 textIndentWidthSizer
.Add(self
._indentWidthChoice
, 0, wx
.ALIGN_LEFT | wx
.EXPAND
, HALF_SPACE
)
882 textPanelSizer
.Add(textIndentWidthSizer
, 0, wx
.ALL
, HALF_SPACE
)
883 textPanelBorderSizer
.Add(textPanelSizer
, 0, wx
.ALL|wx
.EXPAND
, SPACE
)
884 ## styleButton = wx.Button(self, -1, _("Choose Style..."))
885 ## wx.EVT_BUTTON(self, styleButton.GetId(), self.OnChooseStyle)
886 ## textPanelBorderSizer.Add(styleButton, 0, wx.ALL, SPACE)
887 self
.SetSizer(textPanelBorderSizer
)
888 self
.UpdateSampleFont()
890 parent
.AddPage(self
, _(label
))
892 def UpdateSampleFont(self
):
893 nativeFont
= wx
.NativeFontInfo()
894 nativeFont
.FromString(self
._textFont
.GetNativeFontInfoDesc())
896 font
.SetNativeFontInfo(nativeFont
)
897 font
.SetPointSize(self
._sampleTextCtrl
.GetFont().GetPointSize()) # Use the standard point size
898 self
._sampleTextCtrl
.SetFont(font
)
899 self
._sampleTextCtrl
.SetForegroundColour(self
._textColor
)
900 self
._sampleTextCtrl
.SetValue(str(self
._textFont
.GetPointSize()) + _(" pt. ") + self
._textFont
.GetFaceName())
901 self
._sampleTextCtrl
.Refresh()
905 ## def OnChooseStyle(self, event):
906 ## import STCStyleEditor
908 ## base = os.path.split(__file__)[0]
909 ## config = os.path.abspath(os.path.join(base, 'stc-styles.rc.cfg'))
911 ## dlg = STCStyleEditor.STCStyleEditDlg(None,
912 ## 'Python', 'python',
916 ## dlg.CenterOnParent()
923 def OnChooseFont(self
, event
):
925 data
.EnableEffects(True)
926 data
.SetInitialFont(self
._textFont
)
927 data
.SetColour(self
._textColor
)
928 fontDialog
= wx
.FontDialog(self
, data
)
929 fontDialog
.CenterOnParent()
930 if fontDialog
.ShowModal() == wx
.ID_OK
:
931 data
= fontDialog
.GetFontData()
932 self
._textFont
= data
.GetChosenFont()
933 self
._textColor
= data
.GetColour()
934 self
.UpdateSampleFont()
938 def OnOK(self
, optionsDialog
):
939 config
= wx
.ConfigBase_Get()
940 doViewStuffUpdate
= config
.ReadInt(self
._configPrefix
+ "EditorViewWhitespace", False) != self
._viewWhitespaceCheckBox
.GetValue()
941 config
.WriteInt(self
._configPrefix
+ "EditorViewWhitespace", self
._viewWhitespaceCheckBox
.GetValue())
942 doViewStuffUpdate
= doViewStuffUpdate
or config
.ReadInt(self
._configPrefix
+ "EditorViewEOL", False) != self
._viewEOLCheckBox
.GetValue()
943 config
.WriteInt(self
._configPrefix
+ "EditorViewEOL", self
._viewEOLCheckBox
.GetValue())
944 doViewStuffUpdate
= doViewStuffUpdate
or config
.ReadInt(self
._configPrefix
+ "EditorViewIndentationGuides", False) != self
._viewIndentationGuideCheckBox
.GetValue()
945 config
.WriteInt(self
._configPrefix
+ "EditorViewIndentationGuides", self
._viewIndentationGuideCheckBox
.GetValue())
946 doViewStuffUpdate
= doViewStuffUpdate
or config
.ReadInt(self
._configPrefix
+ "EditorViewRightEdge", False) != self
._viewRightEdgeCheckBox
.GetValue()
947 config
.WriteInt(self
._configPrefix
+ "EditorViewRightEdge", self
._viewRightEdgeCheckBox
.GetValue())
948 doViewStuffUpdate
= doViewStuffUpdate
or config
.ReadInt(self
._configPrefix
+ "EditorViewLineNumbers", True) != self
._viewLineNumbersCheckBox
.GetValue()
949 config
.WriteInt(self
._configPrefix
+ "EditorViewLineNumbers", self
._viewLineNumbersCheckBox
.GetValue())
950 if self
._hasWordWrap
:
951 doViewStuffUpdate
= doViewStuffUpdate
or config
.ReadInt(self
._configPrefix
+ "EditorWordWrap", False) != self
._wordWrapCheckBox
.GetValue()
952 config
.WriteInt(self
._configPrefix
+ "EditorWordWrap", self
._wordWrapCheckBox
.GetValue())
954 doViewStuffUpdate
= doViewStuffUpdate
or not config
.ReadInt(self
._configPrefix
+ "EditorUseTabs", True) != self
._hasTabsCheckBox
.GetValue()
955 config
.WriteInt(self
._configPrefix
+ "EditorUseTabs", not self
._hasTabsCheckBox
.GetValue())
956 newIndentWidth
= int(self
._indentWidthChoice
.GetStringSelection())
957 oldIndentWidth
= config
.ReadInt(self
._configPrefix
+ "EditorIndentWidth", 4)
958 if newIndentWidth
!= oldIndentWidth
:
959 doViewStuffUpdate
= True
960 config
.WriteInt(self
._configPrefix
+ "EditorIndentWidth", newIndentWidth
)
961 doFontUpdate
= self
._originalTextFont
!= self
._textFont
or self
._originalTextColor
!= self
._textColor
962 config
.Write(self
._configPrefix
+ "EditorFont", self
._textFont
.GetNativeFontInfoDesc())
963 config
.Write(self
._configPrefix
+ "EditorColor", "%02x%02x%02x" % (self
._textColor
.Red(), self
._textColor
.Green(), self
._textColor
.Blue()))
964 if doViewStuffUpdate
or doFontUpdate
:
965 for document
in optionsDialog
.GetDocManager().GetDocuments():
966 if issubclass(document
.GetDocumentTemplate().GetDocumentType(), TextDocument
):
967 if doViewStuffUpdate
:
968 document
.UpdateAllViews(hint
= "ViewStuff")
970 document
.UpdateAllViews(hint
= "Font")
977 class TextCtrl(wx
.stc
.StyledTextCtrl
):
979 def __init__(self
, parent
, id=-1, style
=wx
.NO_FULL_REPAINT_ON_RESIZE
):
980 wx
.stc
.StyledTextCtrl
.__init
__(self
, parent
, id, style
=style
)
982 if isinstance(parent
, wx
.gizmos
.DynamicSashWindow
):
983 self
._dynSash
= parent
984 self
.SetupDSScrollBars()
985 self
.Bind(wx
.gizmos
.EVT_DYNAMIC_SASH_SPLIT
, self
.OnDSSplit
)
986 self
.Bind(wx
.gizmos
.EVT_DYNAMIC_SASH_UNIFY
, self
.OnDSUnify
)
989 self
._fontColor
= None
991 self
.SetVisiblePolicy(wx
.stc
.STC_VISIBLE_STRICT
,1)
993 self
.CmdKeyClear(wx
.stc
.STC_KEY_ADD
, wx
.stc
.STC_SCMOD_CTRL
)
994 self
.CmdKeyClear(wx
.stc
.STC_KEY_SUBTRACT
, wx
.stc
.STC_SCMOD_CTRL
)
995 self
.CmdKeyAssign(wx
.stc
.STC_KEY_PRIOR
, wx
.stc
.STC_SCMOD_CTRL
, wx
.stc
.STC_CMD_ZOOMIN
)
996 self
.CmdKeyAssign(wx
.stc
.STC_KEY_NEXT
, wx
.stc
.STC_SCMOD_CTRL
, wx
.stc
.STC_CMD_ZOOMOUT
)
997 self
.Bind(wx
.stc
.EVT_STC_ZOOM
, self
.OnUpdateLineNumberMarginWidth
) # auto update line num width on zoom
998 wx
.EVT_KEY_DOWN(self
, self
.OnKeyPressed
)
999 wx
.EVT_KILL_FOCUS(self
, self
.OnKillFocus
)
1000 wx
.EVT_SET_FOCUS(self
, self
.OnFocus
)
1001 self
.SetMargins(0,0)
1007 self
.SetViewWhiteSpace(False)
1008 self
.SetEOLMode(wx
.stc
.STC_EOL_LF
)
1009 self
.SetEdgeMode(wx
.stc
.STC_EDGE_NONE
)
1010 self
.SetEdgeColumn(78)
1012 self
.SetMarginType(1, wx
.stc
.STC_MARGIN_NUMBER
)
1013 self
.SetMarginWidth(1, self
.EstimatedLineNumberMarginWidth())
1016 self
.SetCaretForeground("BLACK")
1018 self
.SetViewDefaults()
1019 font
, color
= self
.GetFontAndColorFromConfig()
1021 self
.SetFontColor(color
)
1022 self
.MarkerDefineDefault()
1024 # for multisash initialization
1025 if isinstance(parent
, wx
.lib
.multisash
.MultiClient
):
1026 while parent
.GetParent():
1027 parent
= parent
.GetParent()
1028 if hasattr(parent
, "GetView"):
1030 if hasattr(parent
, "GetView"):
1031 textEditor
= parent
.GetView()._textEditor
1033 doc
= textEditor
.GetDocPointer()
1035 self
.SetDocPointer(doc
)
1038 def OnFocus(self
, event
):
1039 # wxBug: On Mac, the STC control may fire a focus/kill focus event
1040 # on shutdown even if the control is in an invalid state. So check
1041 # before handling the event.
1042 if self
.IsBeingDeleted():
1045 self
.SetSelBackground(1, "BLUE")
1046 self
.SetSelForeground(1, "WHITE")
1047 if hasattr(self
, "_dynSash"):
1048 self
._dynSash
._view
.SetCtrl(self
)
1052 def OnKillFocus(self
, event
):
1053 # wxBug: On Mac, the STC control may fire a focus/kill focus event
1054 # on shutdown even if the control is in an invalid state. So check
1055 # before handling the event.
1056 if self
.IsBeingDeleted():
1058 self
.SetSelBackground(0, "BLUE")
1059 self
.SetSelForeground(0, "WHITE")
1060 self
.SetSelBackground(1, "#C0C0C0")
1061 # Don't set foreground color, use syntax highlighted default colors.
1065 def SetViewDefaults(self
, configPrefix
= "Text", hasWordWrap
= True, hasTabs
= False):
1066 config
= wx
.ConfigBase_Get()
1067 self
.SetViewWhiteSpace(config
.ReadInt(configPrefix
+ "EditorViewWhitespace", False))
1068 self
.SetViewEOL(config
.ReadInt(configPrefix
+ "EditorViewEOL", False))
1069 self
.SetIndentationGuides(config
.ReadInt(configPrefix
+ "EditorViewIndentationGuides", False))
1070 self
.SetViewRightEdge(config
.ReadInt(configPrefix
+ "EditorViewRightEdge", False))
1071 self
.SetViewLineNumbers(config
.ReadInt(configPrefix
+ "EditorViewLineNumbers", True))
1073 self
.SetWordWrap(config
.ReadInt(configPrefix
+ "EditorWordWrap", False))
1074 if hasTabs
: # These methods do not exist in STCTextEditor and are meant for subclasses
1075 self
.SetUseTabs(config
.ReadInt(configPrefix
+ "EditorUseTabs", False))
1076 self
.SetIndent(config
.ReadInt(configPrefix
+ "EditorIndentWidth", 4))
1077 self
.SetTabWidth(config
.ReadInt(configPrefix
+ "EditorIndentWidth", 4))
1079 self
.SetUseTabs(True)
1084 def GetDefaultFont(self
):
1085 """ Subclasses should override this """
1086 return wx
.Font(10, wx
.MODERN
, wx
.NORMAL
, wx
.NORMAL
)
1089 def GetDefaultColor(self
):
1090 """ Subclasses should override this """
1094 def GetFontAndColorFromConfig(self
, configPrefix
= "Text"):
1095 font
= self
.GetDefaultFont()
1096 config
= wx
.ConfigBase_Get()
1097 fontData
= config
.Read(configPrefix
+ "EditorFont", "")
1099 nativeFont
= wx
.NativeFontInfo()
1100 nativeFont
.FromString(fontData
)
1101 font
.SetNativeFontInfo(nativeFont
)
1102 color
= self
.GetDefaultColor()
1103 colorData
= config
.Read(configPrefix
+ "EditorColor", "")
1105 red
= int("0x" + colorData
[0:2], 16)
1106 green
= int("0x" + colorData
[2:4], 16)
1107 blue
= int("0x" + colorData
[4:6], 16)
1108 color
= wx
.Color(red
, green
, blue
)
1116 def SetFont(self
, font
):
1118 self
.StyleSetFont(wx
.stc
.STC_STYLE_DEFAULT
, self
._font
)
1121 def GetFontColor(self
):
1122 return self
._fontColor
1125 def SetFontColor(self
, fontColor
= wx
.BLACK
):
1126 self
._fontColor
= fontColor
1127 self
.StyleSetForeground(wx
.stc
.STC_STYLE_DEFAULT
, "#%02x%02x%02x" % (self
._fontColor
.Red(), self
._fontColor
.Green(), self
._fontColor
.Blue()))
1130 def UpdateStyles(self
):
1131 self
.StyleClearAll()
1135 def EstimatedLineNumberMarginWidth(self
):
1138 lineNum
= self
.GetLineCount()
1139 lineNum
= lineNum
/100
1140 while lineNum
>= 10:
1141 lineNum
= lineNum
/10
1142 baseNumbers
= baseNumbers
+ "0"
1144 return self
.TextWidth(wx
.stc
.STC_STYLE_LINENUMBER
, baseNumbers
) + MARGIN
1147 def OnUpdateLineNumberMarginWidth(self
, event
):
1148 self
.UpdateLineNumberMarginWidth()
1151 def UpdateLineNumberMarginWidth(self
):
1152 if self
.GetViewLineNumbers():
1153 self
.SetMarginWidth(1, self
.EstimatedLineNumberMarginWidth())
1155 def MarkerDefineDefault(self
):
1156 """ This must be called after the textcontrol is instantiated """
1157 self
.MarkerDefine(TextView
.MARKER_NUM
, wx
.stc
.STC_MARK_ROUNDRECT
, wx
.BLACK
, wx
.BLUE
)
1161 # Used when Delete key is hit.
1162 sel
= self
.GetSelection()
1164 # Delete the selection or if no selection, the character after the caret.
1165 if sel
[0] == sel
[1]:
1166 self
.SetSelection(sel
[0], sel
[0] + 1)
1168 # remove any folded lines also.
1169 startLine
= self
.LineFromPosition(sel
[0])
1170 endLine
= self
.LineFromPosition(sel
[1])
1171 endLineStart
= self
.PositionFromLine(endLine
)
1172 if startLine
!= endLine
and sel
[1] - endLineStart
== 0:
1173 while not self
.GetLineVisible(endLine
):
1175 self
.SetSelectionEnd(self
.PositionFromLine(endLine
))
1181 # replace any folded lines also.
1182 sel
= self
.GetSelection()
1183 startLine
= self
.LineFromPosition(sel
[0])
1184 endLine
= self
.LineFromPosition(sel
[1])
1185 endLineStart
= self
.PositionFromLine(endLine
)
1186 if startLine
!= endLine
and sel
[1] - endLineStart
== 0:
1187 while not self
.GetLineVisible(endLine
):
1189 self
.SetSelectionEnd(self
.PositionFromLine(endLine
))
1194 def OnKeyPressed(self
, event
):
1195 key
= event
.GetKeyCode()
1196 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
1197 if event
.ControlDown():
1198 self
.ToggleFoldAll(expand
= True, topLevelOnly
= True)
1199 elif event
.ShiftDown():
1200 self
.ToggleFoldAll(expand
= True)
1202 self
.ToggleFold(self
.GetCurrentLine())
1203 elif key
== wx
.WXK_NUMPAD_SUBTRACT
:
1204 if event
.ControlDown():
1205 self
.ToggleFoldAll(expand
= False, topLevelOnly
= True)
1206 elif event
.ShiftDown():
1207 self
.ToggleFoldAll(expand
= False)
1209 self
.ToggleFold(self
.GetCurrentLine())
1214 #----------------------------------------------------------------------------
1216 #----------------------------------------------------------------------------
1218 def GetViewRightEdge(self
):
1219 return self
.GetEdgeMode() != wx
.stc
.STC_EDGE_NONE
1222 def SetViewRightEdge(self
, viewRightEdge
):
1224 self
.SetEdgeMode(wx
.stc
.STC_EDGE_LINE
)
1226 self
.SetEdgeMode(wx
.stc
.STC_EDGE_NONE
)
1229 def GetViewLineNumbers(self
):
1230 return self
.GetMarginWidth(1) > 0
1233 def SetViewLineNumbers(self
, viewLineNumbers
= True):
1235 self
.SetMarginWidth(1, self
.EstimatedLineNumberMarginWidth())
1237 self
.SetMarginWidth(1, 0)
1240 def CanWordWrap(self
):
1244 def GetWordWrap(self
):
1245 return self
.GetWrapMode() == wx
.stc
.STC_WRAP_WORD
1248 def SetWordWrap(self
, wordWrap
):
1250 self
.SetWrapMode(wx
.stc
.STC_WRAP_WORD
)
1252 self
.SetWrapMode(wx
.stc
.STC_WRAP_NONE
)
1255 #----------------------------------------------------------------------------
1256 # DynamicSashWindow methods
1257 #----------------------------------------------------------------------------
1259 def SetupDSScrollBars(self
):
1260 # hook the scrollbars provided by the wxDynamicSashWindow
1262 v_bar
= self
._dynSash
.GetVScrollBar(self
)
1263 h_bar
= self
._dynSash
.GetHScrollBar(self
)
1264 v_bar
.Bind(wx
.EVT_SCROLL
, self
.OnDSSBScroll
)
1265 h_bar
.Bind(wx
.EVT_SCROLL
, self
.OnDSSBScroll
)
1266 v_bar
.Bind(wx
.EVT_SET_FOCUS
, self
.OnDSSBFocus
)
1267 h_bar
.Bind(wx
.EVT_SET_FOCUS
, self
.OnDSSBFocus
)
1269 # And set the wxStyledText to use these scrollbars instead
1270 # of its built-in ones.
1271 self
.SetVScrollBar(v_bar
)
1272 self
.SetHScrollBar(h_bar
)
1275 def OnDSSplit(self
, evt
):
1276 newCtrl
= self
._dynSash
._view
.GetCtrlClass()(self
._dynSash
, -1, style
=wx
.NO_BORDER
)
1277 newCtrl
.SetDocPointer(self
.GetDocPointer()) # use the same document
1278 self
.SetupDSScrollBars()
1279 if self
== self
._dynSash
._view
.GetCtrl(): # originally had focus
1280 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.
1283 def OnDSUnify(self
, evt
):
1284 self
.SetupDSScrollBars()
1285 self
.SetFocus() # do this to set colors correctly
1288 def OnDSSBScroll(self
, evt
):
1289 # redirect the scroll events from the _dynSash's scrollbars to the STC
1290 self
.GetEventHandler().ProcessEvent(evt
)
1293 def OnDSSBFocus(self
, evt
):
1294 # when the scrollbar gets the focus move it back to the STC
1298 def DSProcessEvent(self
, event
):
1299 # wxHack: Needed for customized right mouse click menu items.
1300 if hasattr(self
, "_dynSash"):
1301 if event
.GetId() == wx
.ID_SELECTALL
:
1302 # force focus so that select all occurs in the window user right clicked on.
1305 return self
._dynSash
._view
.ProcessEvent(event
)
1309 def DSProcessUpdateUIEvent(self
, event
):
1310 # wxHack: Needed for customized right mouse click menu items.
1311 if hasattr(self
, "_dynSash"):
1313 if (id == wx
.ID_SELECTALL
# allow select all even in non-active window, then force focus to it, see above ProcessEvent
1315 or id == wx
.ID_REDO
):
1316 pass # allow these actions even in non-active window
1317 else: # disallow events in non-active windows. Cut/Copy/Paste/Delete is too confusing user experience.
1318 if self
._dynSash
._view
.GetCtrl() != self
:
1322 return self
._dynSash
._view
.ProcessUpdateUIEvent(event
)
1326 class TextPrintout(wx
.lib
.docview
.DocPrintout
):
1327 """ for Print Preview and Print """
1330 def OnPreparePrinting(self
):
1331 """ initialization """
1334 ppiScreenX
, ppiScreenY
= self
.GetPPIScreen()
1335 ppiPrinterX
, ppiPrinterY
= self
.GetPPIPrinter()
1336 scaleX
= float(ppiPrinterX
)/ppiScreenX
1337 scaleY
= float(ppiPrinterY
)/ppiScreenY
1339 pageWidth
, pageHeight
= self
.GetPageSizePixels()
1340 self
._scaleFactorX
= scaleX
/pageWidth
1341 self
._scaleFactorY
= scaleY
/pageHeight
1344 overallScaleX
= self
._scaleFactorX
* w
1345 overallScaleY
= self
._scaleFactorY
* h
1347 txtCtrl
= self
._printoutView
.GetCtrl()
1348 font
, color
= txtCtrl
.GetFontAndColorFromConfig()
1351 self
._fontHeight
= font
.GetPointSize() + 1
1352 self
._pageLines
= int((h
/overallScaleY
- (2 * self
._margin
))/self
._fontHeight
)
1353 self
._maxLines
= txtCtrl
.GetLineCount()
1354 self
._numPages
, remainder
= divmod(self
._maxLines
, self
._pageLines
)
1359 lineNum
= self
._maxLines
1360 while lineNum
>= 10:
1361 lineNum
= lineNum
/10
1363 self
._printFormat
= "%%0%sd: %%s" % spaces
1366 def OnPrintPage(self
, page
):
1367 """ Prints the given page of the view """
1370 txtCtrl
= self
._printoutView
.GetCtrl()
1371 font
, color
= txtCtrl
.GetFontAndColorFromConfig()
1375 dc
.SetUserScale(self
._scaleFactorX
* w
, self
._scaleFactorY
* h
)
1379 dc
.DrawText("%s - page %s" % (self
.GetTitle(), page
), self
._margin
, self
._margin
/2)
1381 startY
= self
._margin
1382 startLine
= (page
- 1) * self
._pageLines
1383 endLine
= min((startLine
+ self
._pageLines
), self
._maxLines
)
1384 for i
in range(startLine
, endLine
):
1385 text
= txtCtrl
.GetLine(i
).rstrip()
1386 startY
+= self
._fontHeight
1387 if txtCtrl
.GetViewLineNumbers():
1388 dc
.DrawText(self
._printFormat
% (i
+1, text
), self
._margin
, startY
)
1390 dc
.DrawText(text
, self
._margin
, startY
)
1397 def HasPage(self
, pageNum
):
1398 return pageNum
<= self
._numPages
1401 def GetPageInfo(self
):
1403 maxPage
= self
._numPages
1405 selPageTo
= self
._numPages
1406 return (minPage
, maxPage
, selPageFrom
, selPageTo
)
1409 #----------------------------------------------------------------------------
1410 # Icon Bitmaps - generated by encode_bitmaps.py
1411 #----------------------------------------------------------------------------
1412 from wx
import ImageFromStream
, BitmapFromImage
1418 "\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
1419 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
1420 \x00\x015IDAT8\x8d\xad\x90\xb1N\xc2P\x14\x86\xbf\x02/\xe0\xec#\x18g\xc3\xe6T\
1421 \x13':1\x18H\x98\x14\x12\x17G\x177\x17\x9c4a\xc5\xc0d0\xc2\xccdLx\x02^@+\t\
1422 \xc1\x90\xf6r\xdb\xc6\x94\xe5:\\\xdbP)\xc5DOr\x92\x9b{\xff\xfb\xfd\xff9\xc6h\
1423 l+\xbek.\x02\x00\xec\x99\x03\x80\xeb\xf8\\\x9d\x1d\x1bd\xd5hl\xab\xd7O\x15\
1424 \xf7x\xa1\xfb\xeeq\xa4^>\x94\xba\xb8yRF.\xcf\xa6.D\xa0Nw\x18C\xad\xb2\x19\
1425 \x9f\x0f\xca\x165\xd1V\xed\xebZj\x92\xc2\\\x04\xec\x02\xd5\x8a\x89\xb7\xd4\
1426 \x97n\xa8\xe3?\x0f\x86\x08\x19dNP\x00\xf0\x96\xd0\x7f\xd0\t\x84\x0c(U-\x0eK&\
1427 \xd3P\x8bz\xcdV6 \x8a\xed\x86\x99f\xe9\x00{\xe6\xb0\x13\xc2\xa0\xd3\xd7\t\
1428 \x84\x9f\x10\xec\x9dTp\x1d\xb1=A\xa9j\x01\xc4\xb1\x01&\xfe\x9a~\x1d\xe0:Zu\
1429 \x7f\xdb\x05@J/!(\xd6\x1bL\xde\xec\xcd\x00!\x03\xa6!\x1c\x9dVR\x9d\xdf\xe5\
1430 \x96\x04\xd1au\xd3\xab3\xef\x9f_f\x03\xa2\xa5\x15\xeb\x8d\xc4\xc36\xe7\x18 \
1431 \xa5G\xaf\xd9J\xb8f\xcd\xfc\xb3\x0c#\x97\xff\xb58\xadr\x7f\xfa\xfd\x1f\x80/\
1432 \x04\x1f\x8fW\x0e^\xc3\x12\x00\x00\x00\x00IEND\xaeB`\x82"
1435 def getTextBitmap():
1436 return BitmapFromImage(getTextImage())
1439 stream
= cStringIO
.StringIO(getTextData())
1440 return ImageFromStream(stream
)
1443 return wx
.IconFromBitmap(getTextBitmap())
1446 #----------------------------------------------------------------------------
1447 # Menu Bitmaps - generated by encode_bitmaps.py
1448 #----------------------------------------------------------------------------
1449 #----------------------------------------------------------------------
1450 def getZoomInData():
1452 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
1453 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
1454 \x00\x01TIDAT8\x8d\x8d\x93\xbbJ\x03A\x14\x86\xbf\xd9,\xc6\xd8E%`)VF[{\xc1v\
1455 \xf1\x82\x8f\xb0\xb94\xda\xa5\x13\x11\x8b`\xa9h\x10F\xe3#H.\xa6\x15\xccKhg\
1456 \x10\xc1B\x8bTF\x90\xc0X\x8c3\xbb\xd9\xcdF\x7f\x18\xf6\xec\x9cs\xbe\xfd\xe70\
1457 +\x84\x93"\xacb\xc1W\xe1\xf7\xeb\xfa\x8d`\x82\xdcXcI\x8e\x02AM\x02\t\xe1\xa4\
1458 (\x16|uz)y\x19\xc0\xc9\xdd;\x99\xee!\x00\xd9\xbd\x00\xd6\xaf\x95\xc7B\xac\
1459 \x03\xd3\x1c\xd6\xc2t\x10\xf7\x13\x8e\xe0\x14\x0b\xbe\xa2$m\xf3\xca\xea\xacM\
1460 \xe6\xd2\xc1\xcaWdl>#\x0e\x8c\xed\xe7n\x90|\xa8\x96m\xbc~ y\x04Z\xcd\x86\xda\
1461 \xda\xde\xb1Gq\x00\xb2S\t\xfeB\x9aK\xa8\xb1\x0e\xf2\x15I.\xad\x0bo\x8f\xf4\
1462 \x97\xab\xe7z\x88\x1f\xdf\xf0\xfa9\x1e\xe0x\x9eG\xbf\x16X\xcd\xb8Ar\xc6\xd5\
1463 \x0b4\xd4\xf3\xbcd\x07F_\xc3 \x1e\x0c\xa3Y\x08\x9f\x1f~\xefA\xab\xd9P\x9dN\
1464 \x07\x80\xddcI\xc6\x85\xf9\xb4.8\xabhwK\xbd+6\x16\xf5\xdeZ=%F\x00\xa0\xa7\
1465 \x0b`@F\xc6\xf6\xd3\xc5&@\x0c"\xa2\xff\x82\x01\x85-\xb7\x9a\re\x00QH\x0c0N\
1466 \x06\x1a\x85\xbcym}\x0f\xfe\x92\x19\xdc\xf2~\xdb\xee\xdd\xf7\xf4\xf3_\x0e\
1467 \xa2N\xc2\xfa\x01MYp\xbc\xe4a\x0f\xa9\x00\x00\x00\x00IEND\xaeB`\x82'
1469 def getZoomInBitmap():
1470 return BitmapFromImage(getZoomInImage())
1472 def getZoomInImage():
1473 stream
= cStringIO
.StringIO(getZoomInData())
1474 return ImageFromStream(stream
)
1476 #----------------------------------------------------------------------
1477 def getZoomOutData():
1479 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
1480 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
1481 \x00\x01RIDAT8\x8d\x8d\x93\xbbJ\x03A\x14\x86\xbf\xd9\x04\x93\x90J\x0cj#Dl\
1482 \xf4\x01\xec\x05\xdb\xc5\x0b>B\x92]\x1b+\xed,D\xb0\xb4\x08\x9afc|\x04\xc9\
1483 \x85\xb4>\x84\x95`\x93\x80`\x15\xd8*\x98\x84\xc0X\xcc\xce\xde7\xf8\xc30\x97=\
1484 \xf3\xcd\x7f\xce\xcc\na\xe4\x08\xabQ\xaf\xc9\xf0\xfc\xa5\xf3*X\xa1|b\xa3\xe5\
1485 D\x81 W\x81\x840r4\xea5\xf9\xf0\xe40Y@\xf3+\xf8\xb8\xbe\x16\x8c\xdd\x96\x9d\
1486 \n1\xf4\xc0\xdf\xdc\xb6\x01\xa8\xca\x19[\x05\xfc\x96%aY\x96\x0c\xdb\xae\xca\
1487 \x99\xea7\x8b\x91@w.\xf9x\xbcL\xb8\xf0k\xa0O\x1e{\xd31Q\x1d\xdd\xaaC\xfa\xbd\
1488 \xae<=;\xf7!F<\xd7,md\xc4\xf8\x0e\xf6\xaf\x1d\xb6\x8b*p\xa7\x0c\x95\xd0\x86\
1489 \xc9\x02\xbe\xa7\xe9\x00\xc34M\xdc\x96MA\xa8[,y\xc8r>h\x00ow6\xa6if;\x98K\
1490 \x95\xd6\xef\x12(\xc0t\x99~b8\x7f\xf0\xdeA\xbf\xd7\x95\xc3\xe1\x10\x80\x8b{\
1491 \x87R\x1e*\xde\xd55oTq\xf7Fm\x8ew\xd5\xdaa\'\'"\x00P\xd5\x05\xd0 -m\xfb\xf3\
1492 \xf9\x04 \x01\x11\xf1\x7fA\x83\xc2\x96\xfb\xbd\xae\xd4\x808$\x01H\x93\x86\
1493 \xc6!?\xe6 x\xca\xab\xa4\x0bwp5\xf0\xd7\xdeG\xaa\xff\x97\x83\xb8\x93\xb0\xfe\
1494 \x00\xc3\xa8ov\xfd\xe4\x9c\xa2\x00\x00\x00\x00IEND\xaeB`\x82'
1497 def getZoomOutBitmap():
1498 return BitmapFromImage(getZoomOutImage())
1500 def getZoomOutImage():
1501 stream
= cStringIO
.StringIO(getZoomOutData())
1502 return ImageFromStream(stream
)