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