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 val
= view
.GetTextCtrl().GetValue()
30 val
= val
.encode('utf-8')
35 def LoadObject(self
, fileObject
):
36 view
= self
.GetFirstView()
37 data
= fileObject
.read()
39 data
= data
.decode('utf-8')
40 view
.GetTextCtrl().SetValue(data
)
45 view
= self
.GetFirstView()
46 if view
and view
.GetTextCtrl():
47 return view
.GetTextCtrl().IsModified()
51 def Modify(self
, modify
):
56 view
= self
.GetFirstView()
57 if not modify
and view
and view
.GetTextCtrl():
58 view
.GetTextCtrl().DiscardEdits()
60 wx
.lib
.docview
.Document
.Modify(self
, modify
) # this must called be after the DiscardEdits call above.
62 self
._inModify
= False
65 class TextView(wx
.lib
.docview
.View
):
68 #----------------------------------------------------------------------------
70 #----------------------------------------------------------------------------
73 wx
.lib
.docview
.View
.__init
__(self
)
75 self
._wordWrap
= wx
.ConfigBase_Get().ReadInt("TextEditorWordWrap", True)
78 def OnCreate(self
, doc
, flags
):
79 frame
= wx
.GetApp().CreateDocumentFrame(self
, doc
, flags
)
81 font
, color
= self
._GetFontAndColorFromConfig
()
82 self
._textCtrl
= self
._BuildTextCtrl
(frame
, font
, color
= color
)
83 self
._textCtrl
.Bind(wx
.EVT_TEXT
, self
.OnModify
)
84 sizer
.Add(self
._textCtrl
, 1, wx
.EXPAND
, 0)
92 def OnModify(self
, event
):
93 self
.GetDocument().Modify(True)
96 def _BuildTextCtrl(self
, parent
, font
, color
= wx
.BLACK
, value
= "", selection
= [0, 0]):
98 wordWrapStyle
= wx
.TE_WORDWRAP
100 wordWrapStyle
= wx
.TE_DONTWRAP
101 textCtrl
= wx
.TextCtrl(parent
, -1, pos
= wx
.DefaultPosition
, size
= parent
.GetClientSize(), style
= wx
.TE_MULTILINE | wx
.TE_RICH | wordWrapStyle
)
102 textCtrl
.SetFont(font
)
103 textCtrl
.SetForegroundColour(color
)
104 textCtrl
.SetValue(value
)
108 def _GetFontAndColorFromConfig(self
):
109 font
= wx
.Font(10, wx
.DEFAULT
, wx
.NORMAL
, wx
.NORMAL
)
110 config
= wx
.ConfigBase_Get()
111 fontData
= config
.Read("TextEditorFont", "")
113 nativeFont
= wx
.NativeFontInfo()
114 nativeFont
.FromString(fontData
)
115 font
.SetNativeFontInfo(nativeFont
)
117 colorData
= config
.Read("TextEditorColor", "")
119 red
= int("0x" + colorData
[0:2], 16)
120 green
= int("0x" + colorData
[2:4], 16)
121 blue
= int("0x" + colorData
[4:6], 16)
122 color
= wx
.Color(red
, green
, blue
)
126 def OnCreateCommandProcessor(self
):
127 # Don't create a command processor, it has its own
131 def OnActivateView(self
, activate
, activeView
, deactiveView
):
132 if activate
and self
._textCtrl
:
133 # In MDI mode just calling set focus doesn't work and in SDI mode using CallAfter causes an endless loop
134 if self
.GetDocumentManager().GetFlags() & wx
.lib
.docview
.DOC_SDI
:
135 self
._textCtrl
.SetFocus()
137 def SetFocusToTextCtrl():
138 if self
._textCtrl
: # Need to make sure it is there in case we are in the closeall mode of the MDI window
139 self
._textCtrl
.SetFocus()
140 wx
.CallAfter(SetFocusToTextCtrl
)
143 def OnUpdate(self
, sender
= None, hint
= None):
144 if wx
.lib
.docview
.View
.OnUpdate(self
, sender
, hint
):
147 if hint
== "Word Wrap":
148 self
.SetWordWrap(wx
.ConfigBase_Get().ReadInt("TextEditorWordWrap", True))
150 font
, color
= self
._GetFontAndColorFromConfig
()
151 self
.SetFont(font
, color
)
154 def OnClose(self
, deleteWindow
= True):
155 if not wx
.lib
.docview
.View
.OnClose(self
, deleteWindow
):
158 if deleteWindow
and self
.GetFrame():
159 self
.GetFrame().Destroy()
163 # Since ProcessEvent is not virtual, we have to trap the relevant events using this pseudo-ProcessEvent instead of EVT_MENU
164 def ProcessEvent(self
, event
):
167 if not self
._textCtrl
:
169 self
._textCtrl
.Undo()
171 elif id == wx
.ID_REDO
:
172 if not self
._textCtrl
:
174 self
._textCtrl
.Redo()
176 elif id == wx
.ID_CUT
:
177 if not self
._textCtrl
:
181 elif id == wx
.ID_COPY
:
182 if not self
._textCtrl
:
184 self
._textCtrl
.Copy()
186 elif id == wx
.ID_PASTE
:
187 if not self
._textCtrl
:
189 self
._textCtrl
.Paste()
191 elif id == wx
.ID_CLEAR
:
192 if not self
._textCtrl
:
194 self
._textCtrl
.Replace(self
._textCtrl
.GetSelection()[0], self
._textCtrl
.GetSelection()[1], '')
196 elif id == wx
.ID_SELECTALL
:
197 if not self
._textCtrl
:
199 self
._textCtrl
.SetSelection(-1, -1)
201 elif id == TextService
.CHOOSE_FONT_ID
:
202 if not self
._textCtrl
:
204 self
.OnChooseFont(event
)
206 elif id == TextService
.WORD_WRAP_ID
:
207 if not self
._textCtrl
:
209 self
.OnWordWrap(event
)
211 elif id == FindService
.FindService
.FIND_ID
:
214 elif id == FindService
.FindService
.FIND_PREVIOUS_ID
:
215 self
.DoFind(forceFindPrevious
= True)
217 elif id == FindService
.FindService
.FIND_NEXT_ID
:
218 self
.DoFind(forceFindNext
= True)
220 elif id == FindService
.FindService
.REPLACE_ID
:
221 self
.OnFind(replace
= True)
223 elif id == FindService
.FindService
.FINDONE_ID
:
226 elif id == FindService
.FindService
.REPLACEONE_ID
:
227 self
.DoFind(replace
= True)
229 elif id == FindService
.FindService
.REPLACEALL_ID
:
230 self
.DoFind(replaceAll
= True)
232 elif id == FindService
.FindService
.GOTO_LINE_ID
:
233 self
.OnGotoLine(event
)
236 return wx
.lib
.docview
.View
.ProcessEvent(self
, event
)
239 def ProcessUpdateUIEvent(self
, event
):
240 if not self
._textCtrl
:
243 hasText
= len(self
._textCtrl
.GetValue()) > 0
247 event
.Enable(self
._textCtrl
.CanUndo())
249 elif id == wx
.ID_REDO
:
250 event
.Enable(self
._textCtrl
.CanRedo())
253 event
.Enable(self
._textCtrl
.CanCut())
255 elif id == wx
.ID_COPY
:
256 event
.Enable(self
._textCtrl
.CanCopy())
258 elif id == wx
.ID_PASTE
:
259 event
.Enable(self
._textCtrl
.CanPaste())
261 elif id == wx
.ID_CLEAR
:
262 event
.Enable(self
._textCtrl
.CanCopy())
264 elif id == wx
.ID_SELECTALL
:
265 event
.Enable(hasText
)
267 elif id == TextService
.CHOOSE_FONT_ID
:
270 elif id == TextService
.WORD_WRAP_ID
:
273 elif id == FindService
.FindService
.FIND_ID
:
274 event
.Enable(hasText
)
276 elif id == FindService
.FindService
.FIND_PREVIOUS_ID
:
277 event
.Enable(hasText
and
278 self
._FindServiceHasString
() and
279 self
._textCtrl
.GetSelection()[0] > 0)
281 elif id == FindService
.FindService
.FIND_NEXT_ID
:
282 event
.Enable(hasText
and
283 self
._FindServiceHasString
() and
284 self
._textCtrl
.GetSelection()[0] < len(self
._textCtrl
.GetValue()))
286 elif id == FindService
.FindService
.REPLACE_ID
:
287 event
.Enable(hasText
)
289 elif id == FindService
.FindService
.GOTO_LINE_ID
:
293 return wx
.lib
.docview
.View
.ProcessUpdateUIEvent(self
, event
)
296 #----------------------------------------------------------------------------
297 # Methods for TextDocument to call
298 #----------------------------------------------------------------------------
300 def GetTextCtrl(self
):
301 return self
._textCtrl
304 #----------------------------------------------------------------------------
306 #----------------------------------------------------------------------------
308 def OnChooseFont(self
, event
):
310 data
.EnableEffects(True)
311 data
.SetInitialFont(self
._textCtrl
.GetFont())
312 data
.SetColour(self
._textCtrl
.GetForegroundColour())
313 fontDialog
= wx
.FontDialog(self
.GetFrame(), data
)
314 if fontDialog
.ShowModal() == wx
.ID_OK
:
315 data
= fontDialog
.GetFontData()
316 self
.SetFont(data
.GetChosenFont(), data
.GetColour())
320 def SetFont(self
, font
, color
):
321 self
._textCtrl
.SetFont(font
)
322 self
._textCtrl
.SetForegroundColour(color
)
323 self
._textCtrl
.Refresh()
324 self
._textCtrl
.Layout()
327 def OnWordWrap(self
, event
):
328 self
.SetWordWrap(not self
.GetWordWrap())
331 def GetWordWrap(self
):
332 return self
._wordWrap
335 def SetWordWrap(self
, wordWrap
= True):
336 self
._wordWrap
= wordWrap
337 temp
= self
._textCtrl
338 self
._textCtrl
= self
._BuildTextCtrl
(temp
.GetParent(),
339 font
= temp
.GetFont(),
340 color
= temp
.GetForegroundColour(),
341 value
= temp
.GetValue(),
342 selection
= temp
.GetSelection())
343 self
.GetDocument().Modify(temp
.IsModified())
347 #----------------------------------------------------------------------------
349 #----------------------------------------------------------------------------
351 def OnFind(self
, replace
= False):
352 findService
= wx
.GetApp().GetService(FindService
.FindService
)
354 findService
.ShowFindReplaceDialog(findString
= self
._textCtrl
.GetStringSelection(), replace
= replace
)
357 def DoFind(self
, forceFindNext
= False, forceFindPrevious
= False, replace
= False, replaceAll
= False):
358 findService
= wx
.GetApp().GetService(FindService
.FindService
)
361 findString
= findService
.GetFindString()
362 if len(findString
) == 0:
364 replaceString
= findService
.GetReplaceString()
365 flags
= findService
.GetFlags()
366 startLoc
, endLoc
= self
._textCtrl
.GetSelection()
368 wholeWord
= flags
& wx
.FR_WHOLEWORD
> 0
369 matchCase
= flags
& wx
.FR_MATCHCASE
> 0
370 regExp
= flags
& FindService
.FindService
.FR_REGEXP
> 0
371 down
= flags
& wx
.FR_DOWN
> 0
372 wrap
= flags
& FindService
.FindService
.FR_WRAP
> 0
374 if forceFindPrevious
: # this is from function keys, not dialog box
376 wrap
= False # user would want to know they're at the end of file
379 wrap
= False # user would want to know they're at the end of file
381 # On replace dialog operations, user is allowed to replace the currently highlighted text to determine if it should be replaced or not.
382 # 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.
383 # If the text is a match, then replace it.
385 result
, start
, end
, replText
= findService
.DoFind(findString
, replaceString
, self
._textCtrl
.GetStringSelection(), 0, 0, True, matchCase
, wholeWord
, regExp
, replace
)
387 self
._textCtrl
.Replace(startLoc
, endLoc
, replaceString
)
388 self
.GetDocument().Modify(True)
389 wx
.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString
)
391 startLoc
+= len(replText
) # advance start location past replacement string to new text
394 text
= self
._textCtrl
.GetValue()
395 if wx
.Platform
== "__WXMSW__":
396 text
= string
.replace(text
, '\n', '\r\n')
398 # Find the next matching text occurance or if it is a ReplaceAll, replace all occurances
399 # 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
400 result
, start
, end
, text
= findService
.DoFind(findString
, replaceString
, text
, startLoc
, endLoc
, down
, matchCase
, wholeWord
, regExp
, False, replaceAll
, wrap
)
402 self
._textCtrl
.SetValue(text
)
403 self
.GetDocument().Modify(True)
405 wx
.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString
)
407 wx
.GetApp().GetTopWindow().PushStatusText(_("%i occurrences of \"%s\" replaced") % (result
, findString
))
409 self
._textCtrl
.SetSelection(start
, end
)
410 self
._textCtrl
.SetFocus()
411 wx
.GetApp().GetTopWindow().PushStatusText(_("Found \"%s\"") % findString
)
413 wx
.GetApp().GetTopWindow().PushStatusText(_("Can't find \"%s\"") % findString
)
416 def _FindServiceHasString(self
):
417 findService
= wx
.GetApp().GetService(FindService
.FindService
)
418 if not findService
or not findService
.GetFindString():
423 def OnGotoLine(self
, event
):
424 findService
= wx
.GetApp().GetService(FindService
.FindService
)
426 line
= findService
.GetLineNumber(self
.GetDocumentManager().FindSuitableParent())
428 pos
= self
._textCtrl
.XYToPosition(0, line
- 1)
429 self
._textCtrl
.SetSelection(pos
, pos
)
432 class TextService(wx
.lib
.pydocview
.DocService
):
435 WORD_WRAP_ID
= wx
.NewId()
436 CHOOSE_FONT_ID
= wx
.NewId()
440 wx
.lib
.pydocview
.DocService
.__init
__(self
)
443 def InstallControls(self
, frame
, menuBar
= None, toolBar
= None, statusBar
= None, document
= None):
444 if document
and document
.GetDocumentTemplate().GetDocumentType() != TextDocument
:
446 config
= wx
.ConfigBase_Get()
448 formatMenuIndex
= menuBar
.FindMenu(_("&Format"))
449 if formatMenuIndex
> -1:
450 formatMenu
= menuBar
.GetMenu(formatMenuIndex
)
452 formatMenu
= wx
.Menu()
453 formatMenu
= wx
.Menu()
454 if not menuBar
.FindItemById(TextService
.WORD_WRAP_ID
):
455 formatMenu
.AppendCheckItem(TextService
.WORD_WRAP_ID
, _("Word Wrap"), _("Wraps text horizontally when checked"))
456 formatMenu
.Check(TextService
.WORD_WRAP_ID
, config
.ReadInt("TextEditorWordWrap", True))
457 wx
.EVT_MENU(frame
, TextService
.WORD_WRAP_ID
, frame
.ProcessEvent
)
458 wx
.EVT_UPDATE_UI(frame
, TextService
.WORD_WRAP_ID
, frame
.ProcessUpdateUIEvent
)
459 if not menuBar
.FindItemById(TextService
.CHOOSE_FONT_ID
):
460 formatMenu
.Append(TextService
.CHOOSE_FONT_ID
, _("Font..."), _("Sets the font to use"))
461 wx
.EVT_MENU(frame
, TextService
.CHOOSE_FONT_ID
, frame
.ProcessEvent
)
462 wx
.EVT_UPDATE_UI(frame
, TextService
.CHOOSE_FONT_ID
, frame
.ProcessUpdateUIEvent
)
463 if formatMenuIndex
== -1:
464 viewMenuIndex
= menuBar
.FindMenu(_("&View"))
465 menuBar
.Insert(viewMenuIndex
+ 1, formatMenu
, _("&Format"))
468 def ProcessUpdateUIEvent(self
, event
):
470 if id == TextService
.CHOOSE_FONT_ID
:
473 elif id == TextService
.WORD_WRAP_ID
:
480 class TextOptionsPanel(wx
.Panel
):
483 def __init__(self
, parent
, id):
484 wx
.Panel
.__init
__(self
, parent
, id)
487 config
= wx
.ConfigBase_Get()
488 self
._textFont
= wx
.Font(10, wx
.DEFAULT
, wx
.NORMAL
, wx
.NORMAL
)
489 fontData
= config
.Read("TextEditorFont", "")
491 nativeFont
= wx
.NativeFontInfo()
492 nativeFont
.FromString(fontData
)
493 self
._textFont
.SetNativeFontInfo(nativeFont
)
494 self
._originalTextFont
= self
._textFont
495 self
._textColor
= wx
.BLACK
496 colorData
= config
.Read("TextEditorColor", "")
498 red
= int("0x" + colorData
[0:2], 16)
499 green
= int("0x" + colorData
[2:4], 16)
500 blue
= int("0x" + colorData
[4:6], 16)
501 self
._textColor
= wx
.Color(red
, green
, blue
)
502 self
._originalTextColor
= self
._textColor
503 parent
.AddPage(self
, _("Text"))
504 fontLabel
= wx
.StaticText(self
, -1, _("Font:"))
505 self
._sampleTextCtrl
= wx
.TextCtrl(self
, -1, "", size
= (125, -1))
506 self
._sampleTextCtrl
.SetEditable(False)
507 chooseFontButton
= wx
.Button(self
, -1, _("Choose Font..."))
508 wx
.EVT_BUTTON(self
, chooseFontButton
.GetId(), self
.OnChooseFont
)
509 self
._wordWrapCheckBox
= wx
.CheckBox(self
, -1, _("Wrap words inside text area"))
510 self
._wordWrapCheckBox
.SetValue(wx
.ConfigBase_Get().ReadInt("TextEditorWordWrap", True))
511 textPanelBorderSizer
= wx
.BoxSizer(wx
.VERTICAL
)
512 textPanelSizer
= wx
.BoxSizer(wx
.VERTICAL
)
513 textFontSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
514 textFontSizer
.Add(fontLabel
, 0, wx
.ALIGN_LEFT | wx
.RIGHT | wx
.TOP
, HALF_SPACE
)
515 textFontSizer
.Add(self
._sampleTextCtrl
, 0, wx
.ALIGN_LEFT | wx
.EXPAND | wx
.RIGHT
, HALF_SPACE
)
516 textFontSizer
.Add(chooseFontButton
, 0, wx
.ALIGN_RIGHT | wx
.LEFT
, HALF_SPACE
)
517 textPanelSizer
.Add(textFontSizer
, 0, wx
.ALL
, HALF_SPACE
)
518 textPanelSizer
.Add(self
._wordWrapCheckBox
, 0, wx
.ALL
, HALF_SPACE
)
519 textPanelBorderSizer
.Add(textPanelSizer
, 0, wx
.ALL
, SPACE
)
520 self
.SetSizer(textPanelBorderSizer
)
521 self
.UpdateSampleFont()
524 def UpdateSampleFont(self
):
525 nativeFont
= wx
.NativeFontInfo()
526 nativeFont
.FromString(self
._textFont
.GetNativeFontInfoDesc())
528 font
.SetNativeFontInfo(nativeFont
)
529 #font.SetPointSize(self._sampleTextCtrl.GetFont().GetPointSize()) # Use the standard point size
530 self
._sampleTextCtrl
.SetFont(font
)
531 self
._sampleTextCtrl
.SetForegroundColour(self
._textColor
)
532 self
._sampleTextCtrl
.SetValue(_("%d pt. %s") % (self
._textFont
.GetPointSize(), self
._textFont
.GetFaceName()))
533 self
._sampleTextCtrl
.Refresh()
537 def OnChooseFont(self
, event
):
539 data
.EnableEffects(True)
540 data
.SetInitialFont(self
._textFont
)
541 data
.SetColour(self
._textColor
)
542 fontDialog
= wx
.FontDialog(self
, data
)
543 if fontDialog
.ShowModal() == wx
.ID_OK
:
544 data
= fontDialog
.GetFontData()
545 self
._textFont
= data
.GetChosenFont()
546 self
._textColor
= data
.GetColour()
547 self
.UpdateSampleFont()
551 def OnOK(self
, optionsDialog
):
552 config
= wx
.ConfigBase_Get()
553 doWordWrapUpdate
= config
.ReadInt("TextEditorWordWrap", True) != self
._wordWrapCheckBox
.GetValue()
554 config
.WriteInt("TextEditorWordWrap", self
._wordWrapCheckBox
.GetValue())
555 doFontUpdate
= self
._originalTextFont
!= self
._textFont
or self
._originalTextColor
!= self
._textColor
556 config
.Write("TextEditorFont", self
._textFont
.GetNativeFontInfoDesc())
557 config
.Write("TextEditorColor", "%02x%02x%02x" % (self
._textColor
.Red(), self
._textColor
.Green(), self
._textColor
.Blue()))
558 if doWordWrapUpdate
or doFontUpdate
:
559 for document
in optionsDialog
.GetDocManager().GetDocuments():
560 if document
.GetDocumentTemplate().GetDocumentType() == TextDocument
:
562 document
.UpdateAllViews(hint
= "Word Wrap")
564 document
.UpdateAllViews(hint
= "Font")