1 #----------------------------------------------------------------------------
3 # Purpose: Text Editor for pydocview
9 # Copyright: (c) 2003-2005 ActiveGrid, Inc.
10 # License: ASL 2.0 http://apache.org/licenses/LICENSE-2.0
11 #----------------------------------------------------------------------------
14 import wx
.lib
.pydocview
19 class TextDocument(wx
.lib
.docview
.Document
):
22 def OnSaveDocument(self
, filename
):
23 view
= self
.GetFirstView()
24 if not view
.GetTextCtrl().SaveFile(filename
):
27 self
.SetDocumentSaved(True)
28 #if wx.Platform == "__WXMAC__":
29 # fn = wx.Filename(filename)
30 # fn.MacSetDefaultTypeAndCreator()
34 def OnOpenDocument(self
, filename
):
35 view
= self
.GetFirstView()
36 if not view
.GetTextCtrl().LoadFile(filename
):
38 self
.SetFilename(filename
, True)
46 view
= self
.GetFirstView()
47 if view
and view
.GetTextCtrl():
48 return wx
.lib
.docview
.Document
.IsModified(self
) or view
.GetTextCtrl().IsModified()
50 return wx
.lib
.docview
.Document
.IsModified(self
)
53 def Modify(self
, mod
):
54 view
= self
.GetFirstView()
55 wx
.lib
.docview
.Document
.Modify(self
, mod
)
56 if not mod
and view
and view
.GetTextCtrl():
57 view
.GetTextCtrl().DiscardEdits()
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 sizer
.Add(self
._textCtrl
, 1, wx
.EXPAND
, 0)
86 def _BuildTextCtrl(self
, parent
, font
, color
= wx
.BLACK
, value
= "", selection
= [0, 0]):
88 wordWrapStyle
= wx
.TE_WORDWRAP
90 wordWrapStyle
= wx
.TE_DONTWRAP
91 textCtrl
= wx
.TextCtrl(parent
, -1, pos
= wx
.DefaultPosition
, size
= parent
.GetClientSize(), style
= wx
.TE_MULTILINE | wordWrapStyle
)
92 textCtrl
.SetFont(font
)
93 textCtrl
.SetForegroundColour(color
)
94 textCtrl
.SetValue(value
)
98 def _GetFontAndColorFromConfig(self
):
99 font
= wx
.Font(10, wx
.DEFAULT
, wx
.NORMAL
, wx
.NORMAL
)
100 config
= wx
.ConfigBase_Get()
101 fontData
= config
.Read("TextEditorFont", "")
103 nativeFont
= wx
.NativeFontInfo()
104 nativeFont
.FromString(fontData
)
105 font
.SetNativeFontInfo(nativeFont
)
107 colorData
= config
.Read("TextEditorColor", "")
109 red
= int("0x" + colorData
[0:2], 16)
110 green
= int("0x" + colorData
[2:4], 16)
111 blue
= int("0x" + colorData
[4:6], 16)
112 color
= wx
.Color(red
, green
, blue
)
116 def OnCreateCommandProcessor(self
):
117 # Don't create a command processor, it has its own
121 def OnActivateView(self
, activate
, activeView
, deactiveView
):
122 if activate
and self
._textCtrl
:
123 # In MDI mode just calling set focus doesn't work and in SDI mode using CallAfter causes an endless loop
124 if self
.GetDocumentManager().GetFlags() & wx
.lib
.docview
.DOC_SDI
:
125 self
._textCtrl
.SetFocus()
127 def SetFocusToTextCtrl():
128 if self
._textCtrl
: # Need to make sure it is there in case we are in the closeall mode of the MDI window
129 self
._textCtrl
.SetFocus()
130 wx
.CallAfter(SetFocusToTextCtrl
)
133 def OnUpdate(self
, sender
= None, hint
= None):
134 if hint
== "Word Wrap":
135 self
.SetWordWrap(wx
.ConfigBase_Get().ReadInt("TextEditorWordWrap", True))
137 font
, color
= self
._GetFontAndColorFromConfig
()
138 self
.SetFont(font
, color
)
141 def OnClose(self
, deleteWindow
= True):
142 if not wx
.lib
.docview
.View
.OnClose(self
, deleteWindow
):
145 if deleteWindow
and self
.GetFrame():
146 self
.GetFrame().Destroy()
150 # Since ProcessEvent is not virtual, we have to trap the relevant events using this pseudo-ProcessEvent instead of EVT_MENU
151 def ProcessEvent(self
, event
):
154 if not self
._textCtrl
:
156 self
._textCtrl
.Undo()
158 elif id == wx
.ID_REDO
:
159 if not self
._textCtrl
:
161 self
._textCtrl
.Redo()
163 elif id == wx
.ID_CUT
:
164 if not self
._textCtrl
:
168 elif id == wx
.ID_COPY
:
169 if not self
._textCtrl
:
171 self
._textCtrl
.Copy()
173 elif id == wx
.ID_PASTE
:
174 if not self
._textCtrl
:
176 self
._textCtrl
.Paste()
178 elif id == wx
.ID_CLEAR
:
179 if not self
._textCtrl
:
181 self
._textCtrl
.Replace(self
._textCtrl
.GetSelection()[0], self
._textCtrl
.GetSelection()[1], '')
183 elif id == wx
.ID_SELECTALL
:
184 if not self
._textCtrl
:
186 self
._textCtrl
.SetSelection(-1, -1)
188 elif id == TextService
.CHOOSE_FONT_ID
:
189 if not self
._textCtrl
:
191 self
.OnChooseFont(event
)
193 elif id == TextService
.WORD_WRAP_ID
:
194 if not self
._textCtrl
:
196 self
.OnWordWrap(event
)
198 elif id == FindService
.FindService
.FIND_ID
:
201 elif id == FindService
.FindService
.FIND_PREVIOUS_ID
:
202 self
.DoFind(forceFindPrevious
= True)
204 elif id == FindService
.FindService
.FIND_NEXT_ID
:
205 self
.DoFind(forceFindNext
= True)
207 elif id == FindService
.FindService
.REPLACE_ID
:
208 self
.OnFind(replace
= True)
210 elif id == FindService
.FindService
.FINDONE_ID
:
213 elif id == FindService
.FindService
.REPLACEONE_ID
:
214 self
.DoFind(replace
= True)
216 elif id == FindService
.FindService
.REPLACEALL_ID
:
217 self
.DoFind(replaceAll
= True)
219 elif id == FindService
.FindService
.GOTO_LINE_ID
:
220 self
.OnGotoLine(event
)
223 return wx
.lib
.docview
.View
.ProcessEvent(self
, event
)
226 def ProcessUpdateUIEvent(self
, event
):
227 if not self
._textCtrl
:
230 hasText
= len(self
._textCtrl
.GetValue()) > 0
234 event
.Enable(self
._textCtrl
.CanUndo())
236 elif id == wx
.ID_REDO
:
237 event
.Enable(self
._textCtrl
.CanRedo())
240 event
.Enable(self
._textCtrl
.CanCut())
242 elif id == wx
.ID_COPY
:
243 event
.Enable(self
._textCtrl
.CanCopy())
245 elif id == wx
.ID_PASTE
:
246 event
.Enable(self
._textCtrl
.CanPaste())
248 elif id == wx
.ID_CLEAR
:
249 event
.Enable(self
._textCtrl
.CanCopy())
251 elif id == wx
.ID_SELECTALL
:
252 event
.Enable(hasText
)
254 elif id == TextService
.CHOOSE_FONT_ID
:
257 elif id == TextService
.WORD_WRAP_ID
:
260 elif id == FindService
.FindService
.FIND_ID
:
261 event
.Enable(hasText
)
263 elif id == FindService
.FindService
.FIND_PREVIOUS_ID
:
264 event
.Enable(hasText
and
265 self
._FindServiceHasString
() and
266 self
._textCtrl
.GetSelection()[0] > 0)
268 elif id == FindService
.FindService
.FIND_NEXT_ID
:
269 event
.Enable(hasText
and
270 self
._FindServiceHasString
() and
271 self
._textCtrl
.GetSelection()[0] < len(self
._textCtrl
.GetValue()))
273 elif id == FindService
.FindService
.REPLACE_ID
:
274 event
.Enable(hasText
)
276 elif id == FindService
.FindService
.GOTO_LINE_ID
:
280 return wx
.lib
.docview
.View
.ProcessUpdateUIEvent(self
, event
)
283 #----------------------------------------------------------------------------
284 # Methods for TextDocument to call
285 #----------------------------------------------------------------------------
287 def GetTextCtrl(self
):
288 return self
._textCtrl
291 #----------------------------------------------------------------------------
293 #----------------------------------------------------------------------------
295 def OnChooseFont(self
, event
):
297 data
.EnableEffects(True)
298 data
.SetInitialFont(self
._textCtrl
.GetFont())
299 data
.SetColour(self
._textCtrl
.GetForegroundColour())
300 fontDialog
= wx
.FontDialog(self
.GetFrame(), data
)
301 if fontDialog
.ShowModal() == wx
.ID_OK
:
302 data
= fontDialog
.GetFontData()
303 self
.SetFont(data
.GetChosenFont(), data
.GetColour())
307 def SetFont(self
, font
, color
):
308 self
._textCtrl
.SetFont(font
)
309 self
._textCtrl
.SetForegroundColour(color
)
310 self
._textCtrl
.Refresh()
311 self
._textCtrl
.Layout()
314 def OnWordWrap(self
, event
):
315 self
.SetWordWrap(not self
.GetWordWrap())
318 def GetWordWrap(self
):
319 return self
._wordWrap
322 def SetWordWrap(self
, wordWrap
= True):
323 self
._wordWrap
= wordWrap
324 temp
= self
._textCtrl
325 self
._textCtrl
= self
._BuildTextCtrl
(temp
.GetParent(),
326 font
= temp
.GetFont(),
327 color
= temp
.GetForegroundColour(),
328 value
= temp
.GetValue(),
329 selection
= temp
.GetSelection())
330 self
.GetDocument().Modify(temp
.IsModified())
334 #----------------------------------------------------------------------------
336 #----------------------------------------------------------------------------
338 def OnFind(self
, replace
= False):
339 findService
= wx
.GetApp().GetService(FindService
.FindService
)
341 findService
.ShowFindReplaceDialog(findString
= self
._textCtrl
.GetStringSelection(), replace
= replace
)
344 def DoFind(self
, forceFindNext
= False, forceFindPrevious
= False, replace
= False, replaceAll
= False):
345 findService
= wx
.GetApp().GetService(FindService
.FindService
)
348 findString
= findService
.GetFindString()
349 if len(findString
) == 0:
351 replaceString
= findService
.GetReplaceString()
352 flags
= findService
.GetFlags()
353 startLoc
, endLoc
= self
._textCtrl
.GetSelection()
355 wholeWord
= flags
& wx
.FR_WHOLEWORD
> 0
356 matchCase
= flags
& wx
.FR_MATCHCASE
> 0
357 regExp
= flags
& FindService
.FindService
.FR_REGEXP
> 0
358 down
= flags
& wx
.FR_DOWN
> 0
359 wrap
= flags
& FindService
.FindService
.FR_WRAP
> 0
361 if forceFindPrevious
: # this is from function keys, not dialog box
363 wrap
= False # user would want to know they're at the end of file
366 wrap
= False # user would want to know they're at the end of file
368 # On replace dialog operations, user is allowed to replace the currently highlighted text to determine if it should be replaced or not.
369 # 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.
370 # If the text is a match, then replace it.
372 result
, start
, end
, replText
= findService
.DoFind(findString
, replaceString
, self
._textCtrl
.GetStringSelection(), 0, 0, True, matchCase
, wholeWord
, regExp
, replace
)
374 self
._textCtrl
.Replace(startLoc
, endLoc
, replaceString
)
375 self
.GetDocument().Modify(True)
376 wx
.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString
)
378 startLoc
+= len(replText
) # advance start location past replacement string to new text
381 text
= self
._textCtrl
.GetValue()
382 if wx
.Platform
== "__WXMSW__":
383 text
= string
.replace(text
, '\n', '\r\n')
385 # Find the next matching text occurance or if it is a ReplaceAll, replace all occurances
386 # 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
387 result
, start
, end
, text
= findService
.DoFind(findString
, replaceString
, text
, startLoc
, endLoc
, down
, matchCase
, wholeWord
, regExp
, False, replaceAll
, wrap
)
389 self
._textCtrl
.SetValue(text
)
390 self
.GetDocument().Modify(True)
392 wx
.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString
)
394 wx
.GetApp().GetTopWindow().PushStatusText(_("%i occurrences of \"%s\" replaced") % (result
, findString
))
396 self
._textCtrl
.SetSelection(start
, end
)
397 self
._textCtrl
.SetFocus()
398 wx
.GetApp().GetTopWindow().PushStatusText(_("Found \"%s\"") % findString
)
400 wx
.GetApp().GetTopWindow().PushStatusText(_("Can't find \"%s\"") % findString
)
403 def _FindServiceHasString(self
):
404 findService
= wx
.GetApp().GetService(FindService
.FindService
)
405 if not findService
or not findService
.GetFindString():
410 def OnGotoLine(self
, event
):
411 findService
= wx
.GetApp().GetService(FindService
.FindService
)
413 line
= findService
.GetLineNumber(self
.GetDocumentManager().FindSuitableParent())
415 pos
= self
._textCtrl
.XYToPosition(0, line
- 1)
416 self
._textCtrl
.SetSelection(pos
, pos
)
419 class TextService(wx
.lib
.pydocview
.DocService
):
422 WORD_WRAP_ID
= wx
.NewId()
423 CHOOSE_FONT_ID
= wx
.NewId()
427 wx
.lib
.pydocview
.DocService
.__init
__(self
)
430 def InstallControls(self
, frame
, menuBar
= None, toolBar
= None, statusBar
= None, document
= None):
431 if document
and document
.GetDocumentTemplate().GetDocumentType() != TextDocument
:
433 config
= wx
.ConfigBase_Get()
435 formatMenuIndex
= menuBar
.FindMenu(_("&Format"))
436 if formatMenuIndex
> -1:
437 formatMenu
= menuBar
.GetMenu(formatMenuIndex
)
439 formatMenu
= wx
.Menu()
440 formatMenu
= wx
.Menu()
441 if not menuBar
.FindItemById(TextService
.WORD_WRAP_ID
):
442 formatMenu
.AppendCheckItem(TextService
.WORD_WRAP_ID
, _("Word Wrap"), _("Wraps text horizontally when checked"))
443 formatMenu
.Check(TextService
.WORD_WRAP_ID
, config
.ReadInt("TextEditorWordWrap", True))
444 wx
.EVT_MENU(frame
, TextService
.WORD_WRAP_ID
, frame
.ProcessEvent
)
445 wx
.EVT_UPDATE_UI(frame
, TextService
.WORD_WRAP_ID
, frame
.ProcessUpdateUIEvent
)
446 if not menuBar
.FindItemById(TextService
.CHOOSE_FONT_ID
):
447 formatMenu
.Append(TextService
.CHOOSE_FONT_ID
, _("Font..."), _("Sets the font to use"))
448 wx
.EVT_MENU(frame
, TextService
.CHOOSE_FONT_ID
, frame
.ProcessEvent
)
449 wx
.EVT_UPDATE_UI(frame
, TextService
.CHOOSE_FONT_ID
, frame
.ProcessUpdateUIEvent
)
450 if formatMenuIndex
== -1:
451 viewMenuIndex
= menuBar
.FindMenu(_("&View"))
452 menuBar
.Insert(viewMenuIndex
+ 1, formatMenu
, _("&Format"))
455 def ProcessUpdateUIEvent(self
, event
):
457 if id == TextService
.CHOOSE_FONT_ID
:
460 elif id == TextService
.WORD_WRAP_ID
:
467 class TextOptionsPanel(wx
.Panel
):
470 def __init__(self
, parent
, id):
471 wx
.Panel
.__init
__(self
, parent
, id)
474 config
= wx
.ConfigBase_Get()
475 self
._textFont
= wx
.Font(10, wx
.DEFAULT
, wx
.NORMAL
, wx
.NORMAL
)
476 fontData
= config
.Read("TextEditorFont", "")
478 nativeFont
= wx
.NativeFontInfo()
479 nativeFont
.FromString(fontData
)
480 self
._textFont
.SetNativeFontInfo(nativeFont
)
481 self
._originalTextFont
= self
._textFont
482 self
._textColor
= wx
.BLACK
483 colorData
= config
.Read("TextEditorColor", "")
485 red
= int("0x" + colorData
[0:2], 16)
486 green
= int("0x" + colorData
[2:4], 16)
487 blue
= int("0x" + colorData
[4:6], 16)
488 self
._textColor
= wx
.Color(red
, green
, blue
)
489 self
._originalTextColor
= self
._textColor
490 parent
.AddPage(self
, _("Text"))
491 fontLabel
= wx
.StaticText(self
, -1, _("Font:"))
492 self
._sampleTextCtrl
= wx
.TextCtrl(self
, -1, "", size
= (125, -1))
493 self
._sampleTextCtrl
.SetEditable(False)
494 chooseFontButton
= wx
.Button(self
, -1, _("Choose Font..."))
495 wx
.EVT_BUTTON(self
, chooseFontButton
.GetId(), self
.OnChooseFont
)
496 self
._wordWrapCheckBox
= wx
.CheckBox(self
, -1, _("Wrap words inside text area"))
497 self
._wordWrapCheckBox
.SetValue(wx
.ConfigBase_Get().ReadInt("TextEditorWordWrap", True))
498 textPanelBorderSizer
= wx
.BoxSizer(wx
.VERTICAL
)
499 textPanelSizer
= wx
.BoxSizer(wx
.VERTICAL
)
500 textFontSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
501 textFontSizer
.Add(fontLabel
, 0, wx
.ALIGN_LEFT | wx
.RIGHT | wx
.TOP
, HALF_SPACE
)
502 textFontSizer
.Add(self
._sampleTextCtrl
, 0, wx
.ALIGN_LEFT | wx
.EXPAND | wx
.RIGHT
, HALF_SPACE
)
503 textFontSizer
.Add(chooseFontButton
, 0, wx
.ALIGN_RIGHT | wx
.LEFT
, HALF_SPACE
)
504 textPanelSizer
.Add(textFontSizer
, 0, wx
.ALL
, HALF_SPACE
)
505 textPanelSizer
.Add(self
._wordWrapCheckBox
, 0, wx
.ALL
, HALF_SPACE
)
506 textPanelBorderSizer
.Add(textPanelSizer
, 0, wx
.ALL
, SPACE
)
507 self
.SetSizer(textPanelBorderSizer
)
508 self
.UpdateSampleFont()
511 def UpdateSampleFont(self
):
512 nativeFont
= wx
.NativeFontInfo()
513 nativeFont
.FromString(self
._textFont
.GetNativeFontInfoDesc())
515 font
.SetNativeFontInfo(nativeFont
)
516 font
.SetPointSize(self
._sampleTextCtrl
.GetFont().GetPointSize()) # Use the standard point size
517 self
._sampleTextCtrl
.SetFont(font
)
518 self
._sampleTextCtrl
.SetForegroundColour(self
._textColor
)
519 self
._sampleTextCtrl
.SetValue(_("%d pt. %s") % (self
._textFont
.GetPointSize(), self
._textFont
.GetFaceName()))
520 self
._sampleTextCtrl
.Refresh()
524 def OnChooseFont(self
, event
):
526 data
.EnableEffects(True)
527 data
.SetInitialFont(self
._textFont
)
528 data
.SetColour(self
._textColor
)
529 fontDialog
= wx
.FontDialog(self
, data
)
530 if fontDialog
.ShowModal() == wx
.ID_OK
:
531 data
= fontDialog
.GetFontData()
532 self
._textFont
= data
.GetChosenFont()
533 self
._textColor
= data
.GetColour()
534 self
.UpdateSampleFont()
538 def OnOK(self
, optionsDialog
):
539 config
= wx
.ConfigBase_Get()
540 doWordWrapUpdate
= config
.ReadInt("TextEditorWordWrap", True) != self
._wordWrapCheckBox
.GetValue()
541 config
.WriteInt("TextEditorWordWrap", self
._wordWrapCheckBox
.GetValue())
542 doFontUpdate
= self
._originalTextFont
!= self
._textFont
or self
._originalTextColor
!= self
._textColor
543 config
.Write("TextEditorFont", self
._textFont
.GetNativeFontInfoDesc())
544 config
.Write("TextEditorColor", "%02x%02x%02x" % (self
._textColor
.Red(), self
._textColor
.Green(), self
._textColor
.Blue()))
545 if doWordWrapUpdate
or doFontUpdate
:
546 for document
in optionsDialog
.GetDocManager().GetDocuments():
547 if document
.GetDocumentTemplate().GetDocumentType() == TextDocument
:
549 document
.UpdateAllViews(hint
= "Word Wrap")
551 document
.UpdateAllViews(hint
= "Font")