]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/pydocview/TextEditor.py
added support for the 'richtext' library
[wxWidgets.git] / wxPython / samples / pydocview / TextEditor.py
1 #----------------------------------------------------------------------------
2 # Name: TextEditor.py
3 # Purpose: Text Editor for pydocview
4 #
5 # Author: Peter Yared
6 #
7 # Created: 8/15/03
8 # CVS-ID: $Id$
9 # Copyright: (c) 2003-2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
12 import wx
13 import wx.lib.docview
14 import wx.lib.pydocview
15 import string
16 import FindService
17 _ = wx.GetTranslation
18
19 class TextDocument(wx.lib.docview.Document):
20
21 def __init__(self):
22 wx.lib.docview.Document .__init__(self)
23 self._inModify = False
24
25
26 def SaveObject(self, fileObject):
27 view = self.GetFirstView()
28 fileObject.write(view.GetTextCtrl().GetValue())
29 return True
30
31
32 def LoadObject(self, fileObject):
33 view = self.GetFirstView()
34 data = fileObject.read()
35 view.GetTextCtrl().SetValue(data)
36 return True
37
38
39 def IsModified(self):
40 view = self.GetFirstView()
41 if view and view.GetTextCtrl():
42 return view.GetTextCtrl().IsModified()
43 return False
44
45
46 def Modify(self, modify):
47 if self._inModify:
48 return
49 self._inModify = True
50
51 view = self.GetFirstView()
52 if not modify and view and view.GetTextCtrl():
53 view.GetTextCtrl().DiscardEdits()
54
55 wx.lib.docview.Document.Modify(self, modify) # this must called be after the DiscardEdits call above.
56
57 self._inModify = False
58
59
60 class TextView(wx.lib.docview.View):
61
62
63 #----------------------------------------------------------------------------
64 # Overridden methods
65 #----------------------------------------------------------------------------
66
67 def __init__(self):
68 wx.lib.docview.View.__init__(self)
69 self._textCtrl = None
70 self._wordWrap = wx.ConfigBase_Get().ReadInt("TextEditorWordWrap", True)
71
72
73 def OnCreate(self, doc, flags):
74 frame = wx.GetApp().CreateDocumentFrame(self, doc, flags)
75 sizer = wx.BoxSizer()
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)
80 frame.SetSizer(sizer)
81 frame.Layout()
82 frame.Show(True)
83 self.Activate()
84 return True
85
86
87 def OnModify(self, event):
88 self.GetDocument().Modify(True)
89
90
91 def _BuildTextCtrl(self, parent, font, color = wx.BLACK, value = "", selection = [0, 0]):
92 if self._wordWrap:
93 wordWrapStyle = wx.TE_WORDWRAP
94 else:
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)
100 return textCtrl
101
102
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", "")
107 if fontData:
108 nativeFont = wx.NativeFontInfo()
109 nativeFont.FromString(fontData)
110 font.SetNativeFontInfo(nativeFont)
111 color = wx.BLACK
112 colorData = config.Read("TextEditorColor", "")
113 if colorData:
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)
118 return font, color
119
120
121 def OnCreateCommandProcessor(self):
122 # Don't create a command processor, it has its own
123 pass
124
125
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()
131 else:
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)
136
137
138 def OnUpdate(self, sender = None, hint = None):
139 if wx.lib.docview.View.OnUpdate(self, sender, hint):
140 return
141
142 if hint == "Word Wrap":
143 self.SetWordWrap(wx.ConfigBase_Get().ReadInt("TextEditorWordWrap", True))
144 elif hint == "Font":
145 font, color = self._GetFontAndColorFromConfig()
146 self.SetFont(font, color)
147
148
149 def OnClose(self, deleteWindow = True):
150 if not wx.lib.docview.View.OnClose(self, deleteWindow):
151 return False
152 self.Activate(False)
153 if deleteWindow and self.GetFrame():
154 self.GetFrame().Destroy()
155 return True
156
157
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):
160 id = event.GetId()
161 if id == wx.ID_UNDO:
162 if not self._textCtrl:
163 return False
164 self._textCtrl.Undo()
165 return True
166 elif id == wx.ID_REDO:
167 if not self._textCtrl:
168 return False
169 self._textCtrl.Redo()
170 return True
171 elif id == wx.ID_CUT:
172 if not self._textCtrl:
173 return False
174 self._textCtrl.Cut()
175 return True
176 elif id == wx.ID_COPY:
177 if not self._textCtrl:
178 return False
179 self._textCtrl.Copy()
180 return True
181 elif id == wx.ID_PASTE:
182 if not self._textCtrl:
183 return False
184 self._textCtrl.Paste()
185 return True
186 elif id == wx.ID_CLEAR:
187 if not self._textCtrl:
188 return False
189 self._textCtrl.Replace(self._textCtrl.GetSelection()[0], self._textCtrl.GetSelection()[1], '')
190 return True
191 elif id == wx.ID_SELECTALL:
192 if not self._textCtrl:
193 return False
194 self._textCtrl.SetSelection(-1, -1)
195 return True
196 elif id == TextService.CHOOSE_FONT_ID:
197 if not self._textCtrl:
198 return False
199 self.OnChooseFont(event)
200 return True
201 elif id == TextService.WORD_WRAP_ID:
202 if not self._textCtrl:
203 return False
204 self.OnWordWrap(event)
205 return True
206 elif id == FindService.FindService.FIND_ID:
207 self.OnFind()
208 return True
209 elif id == FindService.FindService.FIND_PREVIOUS_ID:
210 self.DoFind(forceFindPrevious = True)
211 return True
212 elif id == FindService.FindService.FIND_NEXT_ID:
213 self.DoFind(forceFindNext = True)
214 return True
215 elif id == FindService.FindService.REPLACE_ID:
216 self.OnFind(replace = True)
217 return True
218 elif id == FindService.FindService.FINDONE_ID:
219 self.DoFind()
220 return True
221 elif id == FindService.FindService.REPLACEONE_ID:
222 self.DoFind(replace = True)
223 return True
224 elif id == FindService.FindService.REPLACEALL_ID:
225 self.DoFind(replaceAll = True)
226 return True
227 elif id == FindService.FindService.GOTO_LINE_ID:
228 self.OnGotoLine(event)
229 return True
230 else:
231 return wx.lib.docview.View.ProcessEvent(self, event)
232
233
234 def ProcessUpdateUIEvent(self, event):
235 if not self._textCtrl:
236 return False
237
238 hasText = len(self._textCtrl.GetValue()) > 0
239
240 id = event.GetId()
241 if id == wx.ID_UNDO:
242 event.Enable(self._textCtrl.CanUndo())
243 return True
244 elif id == wx.ID_REDO:
245 event.Enable(self._textCtrl.CanRedo())
246 return True
247 if id == wx.ID_CUT:
248 event.Enable(self._textCtrl.CanCut())
249 return True
250 elif id == wx.ID_COPY:
251 event.Enable(self._textCtrl.CanCopy())
252 return True
253 elif id == wx.ID_PASTE:
254 event.Enable(self._textCtrl.CanPaste())
255 return True
256 elif id == wx.ID_CLEAR:
257 event.Enable(self._textCtrl.CanCopy())
258 return True
259 elif id == wx.ID_SELECTALL:
260 event.Enable(hasText)
261 return True
262 elif id == TextService.CHOOSE_FONT_ID:
263 event.Enable(True)
264 return True
265 elif id == TextService.WORD_WRAP_ID:
266 event.Enable(True)
267 return True
268 elif id == FindService.FindService.FIND_ID:
269 event.Enable(hasText)
270 return True
271 elif id == FindService.FindService.FIND_PREVIOUS_ID:
272 event.Enable(hasText and
273 self._FindServiceHasString() and
274 self._textCtrl.GetSelection()[0] > 0)
275 return True
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()))
280 return True
281 elif id == FindService.FindService.REPLACE_ID:
282 event.Enable(hasText)
283 return True
284 elif id == FindService.FindService.GOTO_LINE_ID:
285 event.Enable(True)
286 return True
287 else:
288 return wx.lib.docview.View.ProcessUpdateUIEvent(self, event)
289
290
291 #----------------------------------------------------------------------------
292 # Methods for TextDocument to call
293 #----------------------------------------------------------------------------
294
295 def GetTextCtrl(self):
296 return self._textCtrl
297
298
299 #----------------------------------------------------------------------------
300 # Format methods
301 #----------------------------------------------------------------------------
302
303 def OnChooseFont(self, event):
304 data = wx.FontData()
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())
312 fontDialog.Destroy()
313
314
315 def SetFont(self, font, color):
316 self._textCtrl.SetFont(font)
317 self._textCtrl.SetForegroundColour(color)
318 self._textCtrl.Refresh()
319 self._textCtrl.Layout()
320
321
322 def OnWordWrap(self, event):
323 self.SetWordWrap(not self.GetWordWrap())
324
325
326 def GetWordWrap(self):
327 return self._wordWrap
328
329
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())
339 temp.Destroy()
340
341
342 #----------------------------------------------------------------------------
343 # Find methods
344 #----------------------------------------------------------------------------
345
346 def OnFind(self, replace = False):
347 findService = wx.GetApp().GetService(FindService.FindService)
348 if findService:
349 findService.ShowFindReplaceDialog(findString = self._textCtrl.GetStringSelection(), replace = replace)
350
351
352 def DoFind(self, forceFindNext = False, forceFindPrevious = False, replace = False, replaceAll = False):
353 findService = wx.GetApp().GetService(FindService.FindService)
354 if not findService:
355 return
356 findString = findService.GetFindString()
357 if len(findString) == 0:
358 return -1
359 replaceString = findService.GetReplaceString()
360 flags = findService.GetFlags()
361 startLoc, endLoc = self._textCtrl.GetSelection()
362
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
368
369 if forceFindPrevious: # this is from function keys, not dialog box
370 down = False
371 wrap = False # user would want to know they're at the end of file
372 elif forceFindNext:
373 down = True
374 wrap = False # user would want to know they're at the end of file
375
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.
379 if replace:
380 result, start, end, replText = findService.DoFind(findString, replaceString, self._textCtrl.GetStringSelection(), 0, 0, True, matchCase, wholeWord, regExp, replace)
381 if result > 0:
382 self._textCtrl.Replace(startLoc, endLoc, replaceString)
383 self.GetDocument().Modify(True)
384 wx.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString)
385 if down:
386 startLoc += len(replText) # advance start location past replacement string to new text
387 endLoc = startLoc
388
389 text = self._textCtrl.GetValue()
390 if wx.Platform == "__WXMSW__":
391 text = string.replace(text, '\n', '\r\n')
392
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)
396 if result > 0:
397 self._textCtrl.SetValue(text)
398 self.GetDocument().Modify(True)
399 if result == 1:
400 wx.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString)
401 else:
402 wx.GetApp().GetTopWindow().PushStatusText(_("%i occurrences of \"%s\" replaced") % (result, findString))
403 elif result == 0:
404 self._textCtrl.SetSelection(start, end)
405 self._textCtrl.SetFocus()
406 wx.GetApp().GetTopWindow().PushStatusText(_("Found \"%s\"") % findString)
407 else:
408 wx.GetApp().GetTopWindow().PushStatusText(_("Can't find \"%s\"") % findString)
409
410
411 def _FindServiceHasString(self):
412 findService = wx.GetApp().GetService(FindService.FindService)
413 if not findService or not findService.GetFindString():
414 return False
415 return True
416
417
418 def OnGotoLine(self, event):
419 findService = wx.GetApp().GetService(FindService.FindService)
420 if findService:
421 line = findService.GetLineNumber(self.GetDocumentManager().FindSuitableParent())
422 if line > -1:
423 pos = self._textCtrl.XYToPosition(0, line - 1)
424 self._textCtrl.SetSelection(pos, pos)
425
426
427 class TextService(wx.lib.pydocview.DocService):
428
429
430 WORD_WRAP_ID = wx.NewId()
431 CHOOSE_FONT_ID = wx.NewId()
432
433
434 def __init__(self):
435 wx.lib.pydocview.DocService.__init__(self)
436
437
438 def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
439 if document and document.GetDocumentTemplate().GetDocumentType() != TextDocument:
440 return
441 config = wx.ConfigBase_Get()
442
443 formatMenuIndex = menuBar.FindMenu(_("&Format"))
444 if formatMenuIndex > -1:
445 formatMenu = menuBar.GetMenu(formatMenuIndex)
446 else:
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"))
461
462
463 def ProcessUpdateUIEvent(self, event):
464 id = event.GetId()
465 if id == TextService.CHOOSE_FONT_ID:
466 event.Enable(False)
467 return True
468 elif id == TextService.WORD_WRAP_ID:
469 event.Enable(False)
470 return True
471 else:
472 return False
473
474
475 class TextOptionsPanel(wx.Panel):
476
477
478 def __init__(self, parent, id):
479 wx.Panel.__init__(self, parent, id)
480 SPACE = 10
481 HALF_SPACE = 5
482 config = wx.ConfigBase_Get()
483 self._textFont = wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.NORMAL)
484 fontData = config.Read("TextEditorFont", "")
485 if fontData:
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", "")
492 if colorData:
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()
517
518
519 def UpdateSampleFont(self):
520 nativeFont = wx.NativeFontInfo()
521 nativeFont.FromString(self._textFont.GetNativeFontInfoDesc())
522 font = wx.NullFont
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()
529 self.Layout()
530
531
532 def OnChooseFont(self, event):
533 data = wx.FontData()
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()
543 fontDialog.Destroy()
544
545
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:
556 if doWordWrapUpdate:
557 document.UpdateAllViews(hint = "Word Wrap")
558 if doFontUpdate:
559 document.UpdateAllViews(hint = "Font")