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")