1 #----------------------------------------------------------------------------
3 # Purpose: Text Editor for pydocview
9 # Copyright: (c) 2003-2004 ActiveGrid, Inc.
10 # License: wxWindows license
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
):
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(True) # wxBug: No matter what we do, the wxTextCtrl disables the Clear menu item and the menu item traps the Del key and the wxTextCtrl doesn't get it and can't delete the next character
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")