1 #----------------------------------------------------------------------------
3 # Purpose: Text Editor for pydocview
9 # Copyright: (c) 2003-2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
14 import wx
.lib
.pydocview
19 class TextDocument(wx
.lib
.docview
.Document
):
22 wx
.lib
.docview
.Document
.__init
__(self
)
23 self
._inModify
= False
26 def SaveObject(self
, fileObject
):
27 view
= self
.GetFirstView()
28 fileObject
.write(view
.GetTextCtrl().GetValue())
32 def LoadObject(self
, fileObject
):
33 view
= self
.GetFirstView()
34 data
= fileObject
.read()
35 view
.GetTextCtrl().SetValue(data
)
40 view
= self
.GetFirstView()
41 if view
and view
.GetTextCtrl():
42 return view
.GetTextCtrl().IsModified()
46 def Modify(self
, modify
):
51 view
= self
.GetFirstView()
52 if not modify
and view
and view
.GetTextCtrl():
53 view
.GetTextCtrl().DiscardEdits()
55 wx
.lib
.docview
.Document
.Modify(self
, modify
) # this must called be after the DiscardEdits call above.
57 self
._inModify
= False
60 class TextView(wx
.lib
.docview
.View
):
63 #----------------------------------------------------------------------------
65 #----------------------------------------------------------------------------
68 wx
.lib
.docview
.View
.__init
__(self
)
70 self
._wordWrap
= wx
.ConfigBase_Get().ReadInt("TextEditorWordWrap", True)
73 def OnCreate(self
, doc
, flags
):
74 frame
= wx
.GetApp().CreateDocumentFrame(self
, doc
, flags
)
76 font
, color
= self
._GetFontAndColorFromConfig
()
77 self
._textCtrl
= self
._BuildTextCtrl
(frame
, font
, color
= color
)
78 self
._textCtrl
.Bind(wx
.EVT_TEXT
, self
.OnModify
)
79 sizer
.Add(self
._textCtrl
, 1, wx
.EXPAND
, 0)
87 def OnModify(self
, event
):
88 self
.GetDocument().Modify(True)
91 def _BuildTextCtrl(self
, parent
, font
, color
= wx
.BLACK
, value
= "", selection
= [0, 0]):
93 wordWrapStyle
= wx
.TE_WORDWRAP
95 wordWrapStyle
= wx
.TE_DONTWRAP
96 textCtrl
= wx
.TextCtrl(parent
, -1, pos
= wx
.DefaultPosition
, size
= parent
.GetClientSize(), style
= wx
.TE_MULTILINE | wordWrapStyle
)
97 textCtrl
.SetFont(font
)
98 textCtrl
.SetForegroundColour(color
)
99 textCtrl
.SetValue(value
)
103 def _GetFontAndColorFromConfig(self
):
104 font
= wx
.Font(10, wx
.DEFAULT
, wx
.NORMAL
, wx
.NORMAL
)
105 config
= wx
.ConfigBase_Get()
106 fontData
= config
.Read("TextEditorFont", "")
108 nativeFont
= wx
.NativeFontInfo()
109 nativeFont
.FromString(fontData
)
110 font
.SetNativeFontInfo(nativeFont
)
112 colorData
= config
.Read("TextEditorColor", "")
114 red
= int("0x" + colorData
[0:2], 16)
115 green
= int("0x" + colorData
[2:4], 16)
116 blue
= int("0x" + colorData
[4:6], 16)
117 color
= wx
.Color(red
, green
, blue
)
121 def OnCreateCommandProcessor(self
):
122 # Don't create a command processor, it has its own
126 def OnActivateView(self
, activate
, activeView
, deactiveView
):
127 if activate
and self
._textCtrl
:
128 # In MDI mode just calling set focus doesn't work and in SDI mode using CallAfter causes an endless loop
129 if self
.GetDocumentManager().GetFlags() & wx
.lib
.docview
.DOC_SDI
:
130 self
._textCtrl
.SetFocus()
132 def SetFocusToTextCtrl():
133 if self
._textCtrl
: # Need to make sure it is there in case we are in the closeall mode of the MDI window
134 self
._textCtrl
.SetFocus()
135 wx
.CallAfter(SetFocusToTextCtrl
)
138 def OnUpdate(self
, sender
= None, hint
= None):
139 if wx
.lib
.docview
.View
.OnUpdate(self
, sender
, hint
):
142 if hint
== "Word Wrap":
143 self
.SetWordWrap(wx
.ConfigBase_Get().ReadInt("TextEditorWordWrap", True))
145 font
, color
= self
._GetFontAndColorFromConfig
()
146 self
.SetFont(font
, color
)
149 def OnClose(self
, deleteWindow
= True):
150 if not wx
.lib
.docview
.View
.OnClose(self
, deleteWindow
):
153 if deleteWindow
and self
.GetFrame():
154 self
.GetFrame().Destroy()
158 # Since ProcessEvent is not virtual, we have to trap the relevant events using this pseudo-ProcessEvent instead of EVT_MENU
159 def ProcessEvent(self
, event
):
162 if not self
._textCtrl
:
164 self
._textCtrl
.Undo()
166 elif id == wx
.ID_REDO
:
167 if not self
._textCtrl
:
169 self
._textCtrl
.Redo()
171 elif id == wx
.ID_CUT
:
172 if not self
._textCtrl
:
176 elif id == wx
.ID_COPY
:
177 if not self
._textCtrl
:
179 self
._textCtrl
.Copy()
181 elif id == wx
.ID_PASTE
:
182 if not self
._textCtrl
:
184 self
._textCtrl
.Paste()
186 elif id == wx
.ID_CLEAR
:
187 if not self
._textCtrl
:
189 self
._textCtrl
.Replace(self
._textCtrl
.GetSelection()[0], self
._textCtrl
.GetSelection()[1], '')
191 elif id == wx
.ID_SELECTALL
:
192 if not self
._textCtrl
:
194 self
._textCtrl
.SetSelection(-1, -1)
196 elif id == TextService
.CHOOSE_FONT_ID
:
197 if not self
._textCtrl
:
199 self
.OnChooseFont(event
)
201 elif id == TextService
.WORD_WRAP_ID
:
202 if not self
._textCtrl
:
204 self
.OnWordWrap(event
)
206 elif id == FindService
.FindService
.FIND_ID
:
209 elif id == FindService
.FindService
.FIND_PREVIOUS_ID
:
210 self
.DoFind(forceFindPrevious
= True)
212 elif id == FindService
.FindService
.FIND_NEXT_ID
:
213 self
.DoFind(forceFindNext
= True)
215 elif id == FindService
.FindService
.REPLACE_ID
:
216 self
.OnFind(replace
= True)
218 elif id == FindService
.FindService
.FINDONE_ID
:
221 elif id == FindService
.FindService
.REPLACEONE_ID
:
222 self
.DoFind(replace
= True)
224 elif id == FindService
.FindService
.REPLACEALL_ID
:
225 self
.DoFind(replaceAll
= True)
227 elif id == FindService
.FindService
.GOTO_LINE_ID
:
228 self
.OnGotoLine(event
)
231 return wx
.lib
.docview
.View
.ProcessEvent(self
, event
)
234 def ProcessUpdateUIEvent(self
, event
):
235 if not self
._textCtrl
:
238 hasText
= len(self
._textCtrl
.GetValue()) > 0
242 event
.Enable(self
._textCtrl
.CanUndo())
244 elif id == wx
.ID_REDO
:
245 event
.Enable(self
._textCtrl
.CanRedo())
248 event
.Enable(self
._textCtrl
.CanCut())
250 elif id == wx
.ID_COPY
:
251 event
.Enable(self
._textCtrl
.CanCopy())
253 elif id == wx
.ID_PASTE
:
254 event
.Enable(self
._textCtrl
.CanPaste())
256 elif id == wx
.ID_CLEAR
:
257 event
.Enable(self
._textCtrl
.CanCopy())
259 elif id == wx
.ID_SELECTALL
:
260 event
.Enable(hasText
)
262 elif id == TextService
.CHOOSE_FONT_ID
:
265 elif id == TextService
.WORD_WRAP_ID
:
268 elif id == FindService
.FindService
.FIND_ID
:
269 event
.Enable(hasText
)
271 elif id == FindService
.FindService
.FIND_PREVIOUS_ID
:
272 event
.Enable(hasText
and
273 self
._FindServiceHasString
() and
274 self
._textCtrl
.GetSelection()[0] > 0)
276 elif id == FindService
.FindService
.FIND_NEXT_ID
:
277 event
.Enable(hasText
and
278 self
._FindServiceHasString
() and
279 self
._textCtrl
.GetSelection()[0] < len(self
._textCtrl
.GetValue()))
281 elif id == FindService
.FindService
.REPLACE_ID
:
282 event
.Enable(hasText
)
284 elif id == FindService
.FindService
.GOTO_LINE_ID
:
288 return wx
.lib
.docview
.View
.ProcessUpdateUIEvent(self
, event
)
291 #----------------------------------------------------------------------------
292 # Methods for TextDocument to call
293 #----------------------------------------------------------------------------
295 def GetTextCtrl(self
):
296 return self
._textCtrl
299 #----------------------------------------------------------------------------
301 #----------------------------------------------------------------------------
303 def OnChooseFont(self
, event
):
305 data
.EnableEffects(True)
306 data
.SetInitialFont(self
._textCtrl
.GetFont())
307 data
.SetColour(self
._textCtrl
.GetForegroundColour())
308 fontDialog
= wx
.FontDialog(self
.GetFrame(), data
)
309 if fontDialog
.ShowModal() == wx
.ID_OK
:
310 data
= fontDialog
.GetFontData()
311 self
.SetFont(data
.GetChosenFont(), data
.GetColour())
315 def SetFont(self
, font
, color
):
316 self
._textCtrl
.SetFont(font
)
317 self
._textCtrl
.SetForegroundColour(color
)
318 self
._textCtrl
.Refresh()
319 self
._textCtrl
.Layout()
322 def OnWordWrap(self
, event
):
323 self
.SetWordWrap(not self
.GetWordWrap())
326 def GetWordWrap(self
):
327 return self
._wordWrap
330 def SetWordWrap(self
, wordWrap
= True):
331 self
._wordWrap
= wordWrap
332 temp
= self
._textCtrl
333 self
._textCtrl
= self
._BuildTextCtrl
(temp
.GetParent(),
334 font
= temp
.GetFont(),
335 color
= temp
.GetForegroundColour(),
336 value
= temp
.GetValue(),
337 selection
= temp
.GetSelection())
338 self
.GetDocument().Modify(temp
.IsModified())
342 #----------------------------------------------------------------------------
344 #----------------------------------------------------------------------------
346 def OnFind(self
, replace
= False):
347 findService
= wx
.GetApp().GetService(FindService
.FindService
)
349 findService
.ShowFindReplaceDialog(findString
= self
._textCtrl
.GetStringSelection(), replace
= replace
)
352 def DoFind(self
, forceFindNext
= False, forceFindPrevious
= False, replace
= False, replaceAll
= False):
353 findService
= wx
.GetApp().GetService(FindService
.FindService
)
356 findString
= findService
.GetFindString()
357 if len(findString
) == 0:
359 replaceString
= findService
.GetReplaceString()
360 flags
= findService
.GetFlags()
361 startLoc
, endLoc
= self
._textCtrl
.GetSelection()
363 wholeWord
= flags
& wx
.FR_WHOLEWORD
> 0
364 matchCase
= flags
& wx
.FR_MATCHCASE
> 0
365 regExp
= flags
& FindService
.FindService
.FR_REGEXP
> 0
366 down
= flags
& wx
.FR_DOWN
> 0
367 wrap
= flags
& FindService
.FindService
.FR_WRAP
> 0
369 if forceFindPrevious
: # this is from function keys, not dialog box
371 wrap
= False # user would want to know they're at the end of file
374 wrap
= False # user would want to know they're at the end of file
376 # On replace dialog operations, user is allowed to replace the currently highlighted text to determine if it should be replaced or not.
377 # 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.
378 # If the text is a match, then replace it.
380 result
, start
, end
, replText
= findService
.DoFind(findString
, replaceString
, self
._textCtrl
.GetStringSelection(), 0, 0, True, matchCase
, wholeWord
, regExp
, replace
)
382 self
._textCtrl
.Replace(startLoc
, endLoc
, replaceString
)
383 self
.GetDocument().Modify(True)
384 wx
.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString
)
386 startLoc
+= len(replText
) # advance start location past replacement string to new text
389 text
= self
._textCtrl
.GetValue()
390 if wx
.Platform
== "__WXMSW__":
391 text
= string
.replace(text
, '\n', '\r\n')
393 # Find the next matching text occurance or if it is a ReplaceAll, replace all occurances
394 # 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
395 result
, start
, end
, text
= findService
.DoFind(findString
, replaceString
, text
, startLoc
, endLoc
, down
, matchCase
, wholeWord
, regExp
, False, replaceAll
, wrap
)
397 self
._textCtrl
.SetValue(text
)
398 self
.GetDocument().Modify(True)
400 wx
.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString
)
402 wx
.GetApp().GetTopWindow().PushStatusText(_("%i occurrences of \"%s\" replaced") % (result
, findString
))
404 self
._textCtrl
.SetSelection(start
, end
)
405 self
._textCtrl
.SetFocus()
406 wx
.GetApp().GetTopWindow().PushStatusText(_("Found \"%s\"") % findString
)
408 wx
.GetApp().GetTopWindow().PushStatusText(_("Can't find \"%s\"") % findString
)
411 def _FindServiceHasString(self
):
412 findService
= wx
.GetApp().GetService(FindService
.FindService
)
413 if not findService
or not findService
.GetFindString():
418 def OnGotoLine(self
, event
):
419 findService
= wx
.GetApp().GetService(FindService
.FindService
)
421 line
= findService
.GetLineNumber(self
.GetDocumentManager().FindSuitableParent())
423 pos
= self
._textCtrl
.XYToPosition(0, line
- 1)
424 self
._textCtrl
.SetSelection(pos
, pos
)
427 class TextService(wx
.lib
.pydocview
.DocService
):
430 WORD_WRAP_ID
= wx
.NewId()
431 CHOOSE_FONT_ID
= wx
.NewId()
435 wx
.lib
.pydocview
.DocService
.__init
__(self
)
438 def InstallControls(self
, frame
, menuBar
= None, toolBar
= None, statusBar
= None, document
= None):
439 if document
and document
.GetDocumentTemplate().GetDocumentType() != TextDocument
:
441 config
= wx
.ConfigBase_Get()
443 formatMenuIndex
= menuBar
.FindMenu(_("&Format"))
444 if formatMenuIndex
> -1:
445 formatMenu
= menuBar
.GetMenu(formatMenuIndex
)
447 formatMenu
= wx
.Menu()
448 formatMenu
= wx
.Menu()
449 if not menuBar
.FindItemById(TextService
.WORD_WRAP_ID
):
450 formatMenu
.AppendCheckItem(TextService
.WORD_WRAP_ID
, _("Word Wrap"), _("Wraps text horizontally when checked"))
451 formatMenu
.Check(TextService
.WORD_WRAP_ID
, config
.ReadInt("TextEditorWordWrap", True))
452 wx
.EVT_MENU(frame
, TextService
.WORD_WRAP_ID
, frame
.ProcessEvent
)
453 wx
.EVT_UPDATE_UI(frame
, TextService
.WORD_WRAP_ID
, frame
.ProcessUpdateUIEvent
)
454 if not menuBar
.FindItemById(TextService
.CHOOSE_FONT_ID
):
455 formatMenu
.Append(TextService
.CHOOSE_FONT_ID
, _("Font..."), _("Sets the font to use"))
456 wx
.EVT_MENU(frame
, TextService
.CHOOSE_FONT_ID
, frame
.ProcessEvent
)
457 wx
.EVT_UPDATE_UI(frame
, TextService
.CHOOSE_FONT_ID
, frame
.ProcessUpdateUIEvent
)
458 if formatMenuIndex
== -1:
459 viewMenuIndex
= menuBar
.FindMenu(_("&View"))
460 menuBar
.Insert(viewMenuIndex
+ 1, formatMenu
, _("&Format"))
463 def ProcessUpdateUIEvent(self
, event
):
465 if id == TextService
.CHOOSE_FONT_ID
:
468 elif id == TextService
.WORD_WRAP_ID
:
475 class TextOptionsPanel(wx
.Panel
):
478 def __init__(self
, parent
, id):
479 wx
.Panel
.__init
__(self
, parent
, id)
482 config
= wx
.ConfigBase_Get()
483 self
._textFont
= wx
.Font(10, wx
.DEFAULT
, wx
.NORMAL
, wx
.NORMAL
)
484 fontData
= config
.Read("TextEditorFont", "")
486 nativeFont
= wx
.NativeFontInfo()
487 nativeFont
.FromString(fontData
)
488 self
._textFont
.SetNativeFontInfo(nativeFont
)
489 self
._originalTextFont
= self
._textFont
490 self
._textColor
= wx
.BLACK
491 colorData
= config
.Read("TextEditorColor", "")
493 red
= int("0x" + colorData
[0:2], 16)
494 green
= int("0x" + colorData
[2:4], 16)
495 blue
= int("0x" + colorData
[4:6], 16)
496 self
._textColor
= wx
.Color(red
, green
, blue
)
497 self
._originalTextColor
= self
._textColor
498 parent
.AddPage(self
, _("Text"))
499 fontLabel
= wx
.StaticText(self
, -1, _("Font:"))
500 self
._sampleTextCtrl
= wx
.TextCtrl(self
, -1, "", size
= (125, -1))
501 self
._sampleTextCtrl
.SetEditable(False)
502 chooseFontButton
= wx
.Button(self
, -1, _("Choose Font..."))
503 wx
.EVT_BUTTON(self
, chooseFontButton
.GetId(), self
.OnChooseFont
)
504 self
._wordWrapCheckBox
= wx
.CheckBox(self
, -1, _("Wrap words inside text area"))
505 self
._wordWrapCheckBox
.SetValue(wx
.ConfigBase_Get().ReadInt("TextEditorWordWrap", True))
506 textPanelBorderSizer
= wx
.BoxSizer(wx
.VERTICAL
)
507 textPanelSizer
= wx
.BoxSizer(wx
.VERTICAL
)
508 textFontSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
509 textFontSizer
.Add(fontLabel
, 0, wx
.ALIGN_LEFT | wx
.RIGHT | wx
.TOP
, HALF_SPACE
)
510 textFontSizer
.Add(self
._sampleTextCtrl
, 0, wx
.ALIGN_LEFT | wx
.EXPAND | wx
.RIGHT
, HALF_SPACE
)
511 textFontSizer
.Add(chooseFontButton
, 0, wx
.ALIGN_RIGHT | wx
.LEFT
, HALF_SPACE
)
512 textPanelSizer
.Add(textFontSizer
, 0, wx
.ALL
, HALF_SPACE
)
513 textPanelSizer
.Add(self
._wordWrapCheckBox
, 0, wx
.ALL
, HALF_SPACE
)
514 textPanelBorderSizer
.Add(textPanelSizer
, 0, wx
.ALL
, SPACE
)
515 self
.SetSizer(textPanelBorderSizer
)
516 self
.UpdateSampleFont()
519 def UpdateSampleFont(self
):
520 nativeFont
= wx
.NativeFontInfo()
521 nativeFont
.FromString(self
._textFont
.GetNativeFontInfoDesc())
523 font
.SetNativeFontInfo(nativeFont
)
524 font
.SetPointSize(self
._sampleTextCtrl
.GetFont().GetPointSize()) # Use the standard point size
525 self
._sampleTextCtrl
.SetFont(font
)
526 self
._sampleTextCtrl
.SetForegroundColour(self
._textColor
)
527 self
._sampleTextCtrl
.SetValue(_("%d pt. %s") % (self
._textFont
.GetPointSize(), self
._textFont
.GetFaceName()))
528 self
._sampleTextCtrl
.Refresh()
532 def OnChooseFont(self
, event
):
534 data
.EnableEffects(True)
535 data
.SetInitialFont(self
._textFont
)
536 data
.SetColour(self
._textColor
)
537 fontDialog
= wx
.FontDialog(self
, data
)
538 if fontDialog
.ShowModal() == wx
.ID_OK
:
539 data
= fontDialog
.GetFontData()
540 self
._textFont
= data
.GetChosenFont()
541 self
._textColor
= data
.GetColour()
542 self
.UpdateSampleFont()
546 def OnOK(self
, optionsDialog
):
547 config
= wx
.ConfigBase_Get()
548 doWordWrapUpdate
= config
.ReadInt("TextEditorWordWrap", True) != self
._wordWrapCheckBox
.GetValue()
549 config
.WriteInt("TextEditorWordWrap", self
._wordWrapCheckBox
.GetValue())
550 doFontUpdate
= self
._originalTextFont
!= self
._textFont
or self
._originalTextColor
!= self
._textColor
551 config
.Write("TextEditorFont", self
._textFont
.GetNativeFontInfoDesc())
552 config
.Write("TextEditorColor", "%02x%02x%02x" % (self
._textColor
.Red(), self
._textColor
.Green(), self
._textColor
.Blue()))
553 if doWordWrapUpdate
or doFontUpdate
:
554 for document
in optionsDialog
.GetDocManager().GetDocuments():
555 if document
.GetDocumentTemplate().GetDocumentType() == TextDocument
:
557 document
.UpdateAllViews(hint
= "Word Wrap")
559 document
.UpdateAllViews(hint
= "Font")