]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/pydocview/TextEditor.py
don't use wxChar* arguments in wxLogWindow API
[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 val = view.GetTextCtrl().GetValue()
29 if wx.USE_UNICODE:
30 val = val.encode('utf-8')
31 fileObject.write(val)
32 return True
33
34
35 def LoadObject(self, fileObject):
36 view = self.GetFirstView()
37 data = fileObject.read()
38 if wx.USE_UNICODE:
39 data = data.decode('utf-8')
40 view.GetTextCtrl().SetValue(data)
41 return True
42
43
44 def IsModified(self):
45 view = self.GetFirstView()
46 if view and view.GetTextCtrl():
47 return view.GetTextCtrl().IsModified()
48 return False
49
50
51 def Modify(self, modify):
52 if self._inModify:
53 return
54 self._inModify = True
55
56 view = self.GetFirstView()
57 if not modify and view and view.GetTextCtrl():
58 view.GetTextCtrl().DiscardEdits()
59
60 wx.lib.docview.Document.Modify(self, modify) # this must called be after the DiscardEdits call above.
61
62 self._inModify = False
63
64
65 class TextView(wx.lib.docview.View):
66
67
68 #----------------------------------------------------------------------------
69 # Overridden methods
70 #----------------------------------------------------------------------------
71
72 def __init__(self):
73 wx.lib.docview.View.__init__(self)
74 self._textCtrl = None
75 self._wordWrap = wx.ConfigBase_Get().ReadInt("TextEditorWordWrap", True)
76
77
78 def OnCreate(self, doc, flags):
79 frame = wx.GetApp().CreateDocumentFrame(self, doc, flags)
80 sizer = wx.BoxSizer()
81 font, color = self._GetFontAndColorFromConfig()
82 self._textCtrl = self._BuildTextCtrl(frame, font, color = color)
83 self._textCtrl.Bind(wx.EVT_TEXT, self.OnModify)
84 sizer.Add(self._textCtrl, 1, wx.EXPAND, 0)
85 frame.SetSizer(sizer)
86 frame.Layout()
87 frame.Show(True)
88 self.Activate()
89 return True
90
91
92 def OnModify(self, event):
93 self.GetDocument().Modify(True)
94
95
96 def _BuildTextCtrl(self, parent, font, color = wx.BLACK, value = "", selection = [0, 0]):
97 if self._wordWrap:
98 wordWrapStyle = wx.TE_WORDWRAP
99 else:
100 wordWrapStyle = wx.TE_DONTWRAP
101 textCtrl = wx.TextCtrl(parent, -1, pos = wx.DefaultPosition, size = parent.GetClientSize(), style = wx.TE_MULTILINE | wx.TE_RICH | wordWrapStyle)
102 textCtrl.SetFont(font)
103 textCtrl.SetForegroundColour(color)
104 textCtrl.SetValue(value)
105 return textCtrl
106
107
108 def _GetFontAndColorFromConfig(self):
109 font = wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.NORMAL)
110 config = wx.ConfigBase_Get()
111 fontData = config.Read("TextEditorFont", "")
112 if fontData:
113 nativeFont = wx.NativeFontInfo()
114 nativeFont.FromString(fontData)
115 font.SetNativeFontInfo(nativeFont)
116 color = wx.BLACK
117 colorData = config.Read("TextEditorColor", "")
118 if colorData:
119 red = int("0x" + colorData[0:2], 16)
120 green = int("0x" + colorData[2:4], 16)
121 blue = int("0x" + colorData[4:6], 16)
122 color = wx.Color(red, green, blue)
123 return font, color
124
125
126 def OnCreateCommandProcessor(self):
127 # Don't create a command processor, it has its own
128 pass
129
130
131 def OnActivateView(self, activate, activeView, deactiveView):
132 if activate and self._textCtrl:
133 # In MDI mode just calling set focus doesn't work and in SDI mode using CallAfter causes an endless loop
134 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
135 self._textCtrl.SetFocus()
136 else:
137 def SetFocusToTextCtrl():
138 if self._textCtrl: # Need to make sure it is there in case we are in the closeall mode of the MDI window
139 self._textCtrl.SetFocus()
140 wx.CallAfter(SetFocusToTextCtrl)
141
142
143 def OnUpdate(self, sender = None, hint = None):
144 if wx.lib.docview.View.OnUpdate(self, sender, hint):
145 return
146
147 if hint == "Word Wrap":
148 self.SetWordWrap(wx.ConfigBase_Get().ReadInt("TextEditorWordWrap", True))
149 elif hint == "Font":
150 font, color = self._GetFontAndColorFromConfig()
151 self.SetFont(font, color)
152
153
154 def OnClose(self, deleteWindow = True):
155 if not wx.lib.docview.View.OnClose(self, deleteWindow):
156 return False
157 self.Activate(False)
158 if deleteWindow and self.GetFrame():
159 self.GetFrame().Destroy()
160 return True
161
162
163 # Since ProcessEvent is not virtual, we have to trap the relevant events using this pseudo-ProcessEvent instead of EVT_MENU
164 def ProcessEvent(self, event):
165 id = event.GetId()
166 if id == wx.ID_UNDO:
167 if not self._textCtrl:
168 return False
169 self._textCtrl.Undo()
170 return True
171 elif id == wx.ID_REDO:
172 if not self._textCtrl:
173 return False
174 self._textCtrl.Redo()
175 return True
176 elif id == wx.ID_CUT:
177 if not self._textCtrl:
178 return False
179 self._textCtrl.Cut()
180 return True
181 elif id == wx.ID_COPY:
182 if not self._textCtrl:
183 return False
184 self._textCtrl.Copy()
185 return True
186 elif id == wx.ID_PASTE:
187 if not self._textCtrl:
188 return False
189 self._textCtrl.Paste()
190 return True
191 elif id == wx.ID_CLEAR:
192 if not self._textCtrl:
193 return False
194 self._textCtrl.Replace(self._textCtrl.GetSelection()[0], self._textCtrl.GetSelection()[1], '')
195 return True
196 elif id == wx.ID_SELECTALL:
197 if not self._textCtrl:
198 return False
199 self._textCtrl.SetSelection(-1, -1)
200 return True
201 elif id == TextService.CHOOSE_FONT_ID:
202 if not self._textCtrl:
203 return False
204 self.OnChooseFont(event)
205 return True
206 elif id == TextService.WORD_WRAP_ID:
207 if not self._textCtrl:
208 return False
209 self.OnWordWrap(event)
210 return True
211 elif id == FindService.FindService.FIND_ID:
212 self.OnFind()
213 return True
214 elif id == FindService.FindService.FIND_PREVIOUS_ID:
215 self.DoFind(forceFindPrevious = True)
216 return True
217 elif id == FindService.FindService.FIND_NEXT_ID:
218 self.DoFind(forceFindNext = True)
219 return True
220 elif id == FindService.FindService.REPLACE_ID:
221 self.OnFind(replace = True)
222 return True
223 elif id == FindService.FindService.FINDONE_ID:
224 self.DoFind()
225 return True
226 elif id == FindService.FindService.REPLACEONE_ID:
227 self.DoFind(replace = True)
228 return True
229 elif id == FindService.FindService.REPLACEALL_ID:
230 self.DoFind(replaceAll = True)
231 return True
232 elif id == FindService.FindService.GOTO_LINE_ID:
233 self.OnGotoLine(event)
234 return True
235 else:
236 return wx.lib.docview.View.ProcessEvent(self, event)
237
238
239 def ProcessUpdateUIEvent(self, event):
240 if not self._textCtrl:
241 return False
242
243 hasText = len(self._textCtrl.GetValue()) > 0
244
245 id = event.GetId()
246 if id == wx.ID_UNDO:
247 event.Enable(self._textCtrl.CanUndo())
248 return True
249 elif id == wx.ID_REDO:
250 event.Enable(self._textCtrl.CanRedo())
251 return True
252 if id == wx.ID_CUT:
253 event.Enable(self._textCtrl.CanCut())
254 return True
255 elif id == wx.ID_COPY:
256 event.Enable(self._textCtrl.CanCopy())
257 return True
258 elif id == wx.ID_PASTE:
259 event.Enable(self._textCtrl.CanPaste())
260 return True
261 elif id == wx.ID_CLEAR:
262 event.Enable(self._textCtrl.CanCopy())
263 return True
264 elif id == wx.ID_SELECTALL:
265 event.Enable(hasText)
266 return True
267 elif id == TextService.CHOOSE_FONT_ID:
268 event.Enable(True)
269 return True
270 elif id == TextService.WORD_WRAP_ID:
271 event.Enable(True)
272 return True
273 elif id == FindService.FindService.FIND_ID:
274 event.Enable(hasText)
275 return True
276 elif id == FindService.FindService.FIND_PREVIOUS_ID:
277 event.Enable(hasText and
278 self._FindServiceHasString() and
279 self._textCtrl.GetSelection()[0] > 0)
280 return True
281 elif id == FindService.FindService.FIND_NEXT_ID:
282 event.Enable(hasText and
283 self._FindServiceHasString() and
284 self._textCtrl.GetSelection()[0] < len(self._textCtrl.GetValue()))
285 return True
286 elif id == FindService.FindService.REPLACE_ID:
287 event.Enable(hasText)
288 return True
289 elif id == FindService.FindService.GOTO_LINE_ID:
290 event.Enable(True)
291 return True
292 else:
293 return wx.lib.docview.View.ProcessUpdateUIEvent(self, event)
294
295
296 #----------------------------------------------------------------------------
297 # Methods for TextDocument to call
298 #----------------------------------------------------------------------------
299
300 def GetTextCtrl(self):
301 return self._textCtrl
302
303
304 #----------------------------------------------------------------------------
305 # Format methods
306 #----------------------------------------------------------------------------
307
308 def OnChooseFont(self, event):
309 data = wx.FontData()
310 data.EnableEffects(True)
311 data.SetInitialFont(self._textCtrl.GetFont())
312 data.SetColour(self._textCtrl.GetForegroundColour())
313 fontDialog = wx.FontDialog(self.GetFrame(), data)
314 if fontDialog.ShowModal() == wx.ID_OK:
315 data = fontDialog.GetFontData()
316 self.SetFont(data.GetChosenFont(), data.GetColour())
317 fontDialog.Destroy()
318
319
320 def SetFont(self, font, color):
321 self._textCtrl.SetFont(font)
322 self._textCtrl.SetForegroundColour(color)
323 self._textCtrl.Refresh()
324 self._textCtrl.Layout()
325
326
327 def OnWordWrap(self, event):
328 self.SetWordWrap(not self.GetWordWrap())
329
330
331 def GetWordWrap(self):
332 return self._wordWrap
333
334
335 def SetWordWrap(self, wordWrap = True):
336 self._wordWrap = wordWrap
337 temp = self._textCtrl
338 self._textCtrl = self._BuildTextCtrl(temp.GetParent(),
339 font = temp.GetFont(),
340 color = temp.GetForegroundColour(),
341 value = temp.GetValue(),
342 selection = temp.GetSelection())
343 self.GetDocument().Modify(temp.IsModified())
344 temp.Destroy()
345
346
347 #----------------------------------------------------------------------------
348 # Find methods
349 #----------------------------------------------------------------------------
350
351 def OnFind(self, replace = False):
352 findService = wx.GetApp().GetService(FindService.FindService)
353 if findService:
354 findService.ShowFindReplaceDialog(findString = self._textCtrl.GetStringSelection(), replace = replace)
355
356
357 def DoFind(self, forceFindNext = False, forceFindPrevious = False, replace = False, replaceAll = False):
358 findService = wx.GetApp().GetService(FindService.FindService)
359 if not findService:
360 return
361 findString = findService.GetFindString()
362 if len(findString) == 0:
363 return -1
364 replaceString = findService.GetReplaceString()
365 flags = findService.GetFlags()
366 startLoc, endLoc = self._textCtrl.GetSelection()
367
368 wholeWord = flags & wx.FR_WHOLEWORD > 0
369 matchCase = flags & wx.FR_MATCHCASE > 0
370 regExp = flags & FindService.FindService.FR_REGEXP > 0
371 down = flags & wx.FR_DOWN > 0
372 wrap = flags & FindService.FindService.FR_WRAP > 0
373
374 if forceFindPrevious: # this is from function keys, not dialog box
375 down = False
376 wrap = False # user would want to know they're at the end of file
377 elif forceFindNext:
378 down = True
379 wrap = False # user would want to know they're at the end of file
380
381 # On replace dialog operations, user is allowed to replace the currently highlighted text to determine if it should be replaced or not.
382 # 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.
383 # If the text is a match, then replace it.
384 if replace:
385 result, start, end, replText = findService.DoFind(findString, replaceString, self._textCtrl.GetStringSelection(), 0, 0, True, matchCase, wholeWord, regExp, replace)
386 if result > 0:
387 self._textCtrl.Replace(startLoc, endLoc, replaceString)
388 self.GetDocument().Modify(True)
389 wx.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString)
390 if down:
391 startLoc += len(replText) # advance start location past replacement string to new text
392 endLoc = startLoc
393
394 text = self._textCtrl.GetValue()
395 if wx.Platform == "__WXMSW__":
396 text = string.replace(text, '\n', '\r\n')
397
398 # Find the next matching text occurance or if it is a ReplaceAll, replace all occurances
399 # 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
400 result, start, end, text = findService.DoFind(findString, replaceString, text, startLoc, endLoc, down, matchCase, wholeWord, regExp, False, replaceAll, wrap)
401 if result > 0:
402 self._textCtrl.SetValue(text)
403 self.GetDocument().Modify(True)
404 if result == 1:
405 wx.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString)
406 else:
407 wx.GetApp().GetTopWindow().PushStatusText(_("%i occurrences of \"%s\" replaced") % (result, findString))
408 elif result == 0:
409 self._textCtrl.SetSelection(start, end)
410 self._textCtrl.SetFocus()
411 wx.GetApp().GetTopWindow().PushStatusText(_("Found \"%s\"") % findString)
412 else:
413 wx.GetApp().GetTopWindow().PushStatusText(_("Can't find \"%s\"") % findString)
414
415
416 def _FindServiceHasString(self):
417 findService = wx.GetApp().GetService(FindService.FindService)
418 if not findService or not findService.GetFindString():
419 return False
420 return True
421
422
423 def OnGotoLine(self, event):
424 findService = wx.GetApp().GetService(FindService.FindService)
425 if findService:
426 line = findService.GetLineNumber(self.GetDocumentManager().FindSuitableParent())
427 if line > -1:
428 pos = self._textCtrl.XYToPosition(0, line - 1)
429 self._textCtrl.SetSelection(pos, pos)
430
431
432 class TextService(wx.lib.pydocview.DocService):
433
434
435 WORD_WRAP_ID = wx.NewId()
436 CHOOSE_FONT_ID = wx.NewId()
437
438
439 def __init__(self):
440 wx.lib.pydocview.DocService.__init__(self)
441
442
443 def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
444 if document and document.GetDocumentTemplate().GetDocumentType() != TextDocument:
445 return
446 config = wx.ConfigBase_Get()
447
448 formatMenuIndex = menuBar.FindMenu(_("&Format"))
449 if formatMenuIndex > -1:
450 formatMenu = menuBar.GetMenu(formatMenuIndex)
451 else:
452 formatMenu = wx.Menu()
453 formatMenu = wx.Menu()
454 if not menuBar.FindItemById(TextService.WORD_WRAP_ID):
455 formatMenu.AppendCheckItem(TextService.WORD_WRAP_ID, _("Word Wrap"), _("Wraps text horizontally when checked"))
456 formatMenu.Check(TextService.WORD_WRAP_ID, config.ReadInt("TextEditorWordWrap", True))
457 wx.EVT_MENU(frame, TextService.WORD_WRAP_ID, frame.ProcessEvent)
458 wx.EVT_UPDATE_UI(frame, TextService.WORD_WRAP_ID, frame.ProcessUpdateUIEvent)
459 if not menuBar.FindItemById(TextService.CHOOSE_FONT_ID):
460 formatMenu.Append(TextService.CHOOSE_FONT_ID, _("Font..."), _("Sets the font to use"))
461 wx.EVT_MENU(frame, TextService.CHOOSE_FONT_ID, frame.ProcessEvent)
462 wx.EVT_UPDATE_UI(frame, TextService.CHOOSE_FONT_ID, frame.ProcessUpdateUIEvent)
463 if formatMenuIndex == -1:
464 viewMenuIndex = menuBar.FindMenu(_("&View"))
465 menuBar.Insert(viewMenuIndex + 1, formatMenu, _("&Format"))
466
467
468 def ProcessUpdateUIEvent(self, event):
469 id = event.GetId()
470 if id == TextService.CHOOSE_FONT_ID:
471 event.Enable(False)
472 return True
473 elif id == TextService.WORD_WRAP_ID:
474 event.Enable(False)
475 return True
476 else:
477 return False
478
479
480 class TextOptionsPanel(wx.Panel):
481
482
483 def __init__(self, parent, id):
484 wx.Panel.__init__(self, parent, id)
485 SPACE = 10
486 HALF_SPACE = 5
487 config = wx.ConfigBase_Get()
488 self._textFont = wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.NORMAL)
489 fontData = config.Read("TextEditorFont", "")
490 if fontData:
491 nativeFont = wx.NativeFontInfo()
492 nativeFont.FromString(fontData)
493 self._textFont.SetNativeFontInfo(nativeFont)
494 self._originalTextFont = self._textFont
495 self._textColor = wx.BLACK
496 colorData = config.Read("TextEditorColor", "")
497 if colorData:
498 red = int("0x" + colorData[0:2], 16)
499 green = int("0x" + colorData[2:4], 16)
500 blue = int("0x" + colorData[4:6], 16)
501 self._textColor = wx.Color(red, green, blue)
502 self._originalTextColor = self._textColor
503 parent.AddPage(self, _("Text"))
504 fontLabel = wx.StaticText(self, -1, _("Font:"))
505 self._sampleTextCtrl = wx.TextCtrl(self, -1, "", size = (125, -1))
506 self._sampleTextCtrl.SetEditable(False)
507 chooseFontButton = wx.Button(self, -1, _("Choose Font..."))
508 wx.EVT_BUTTON(self, chooseFontButton.GetId(), self.OnChooseFont)
509 self._wordWrapCheckBox = wx.CheckBox(self, -1, _("Wrap words inside text area"))
510 self._wordWrapCheckBox.SetValue(wx.ConfigBase_Get().ReadInt("TextEditorWordWrap", True))
511 textPanelBorderSizer = wx.BoxSizer(wx.VERTICAL)
512 textPanelSizer = wx.BoxSizer(wx.VERTICAL)
513 textFontSizer = wx.BoxSizer(wx.HORIZONTAL)
514 textFontSizer.Add(fontLabel, 0, wx.ALIGN_LEFT | wx.RIGHT | wx.TOP, HALF_SPACE)
515 textFontSizer.Add(self._sampleTextCtrl, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.RIGHT, HALF_SPACE)
516 textFontSizer.Add(chooseFontButton, 0, wx.ALIGN_RIGHT | wx.LEFT, HALF_SPACE)
517 textPanelSizer.Add(textFontSizer, 0, wx.ALL, HALF_SPACE)
518 textPanelSizer.Add(self._wordWrapCheckBox, 0, wx.ALL, HALF_SPACE)
519 textPanelBorderSizer.Add(textPanelSizer, 0, wx.ALL, SPACE)
520 self.SetSizer(textPanelBorderSizer)
521 self.UpdateSampleFont()
522
523
524 def UpdateSampleFont(self):
525 nativeFont = wx.NativeFontInfo()
526 nativeFont.FromString(self._textFont.GetNativeFontInfoDesc())
527 font = wx.NullFont
528 font.SetNativeFontInfo(nativeFont)
529 #font.SetPointSize(self._sampleTextCtrl.GetFont().GetPointSize()) # Use the standard point size
530 self._sampleTextCtrl.SetFont(font)
531 self._sampleTextCtrl.SetForegroundColour(self._textColor)
532 self._sampleTextCtrl.SetValue(_("%d pt. %s") % (self._textFont.GetPointSize(), self._textFont.GetFaceName()))
533 self._sampleTextCtrl.Refresh()
534 self.Layout()
535
536
537 def OnChooseFont(self, event):
538 data = wx.FontData()
539 data.EnableEffects(True)
540 data.SetInitialFont(self._textFont)
541 data.SetColour(self._textColor)
542 fontDialog = wx.FontDialog(self, data)
543 if fontDialog.ShowModal() == wx.ID_OK:
544 data = fontDialog.GetFontData()
545 self._textFont = data.GetChosenFont()
546 self._textColor = data.GetColour()
547 self.UpdateSampleFont()
548 fontDialog.Destroy()
549
550
551 def OnOK(self, optionsDialog):
552 config = wx.ConfigBase_Get()
553 doWordWrapUpdate = config.ReadInt("TextEditorWordWrap", True) != self._wordWrapCheckBox.GetValue()
554 config.WriteInt("TextEditorWordWrap", self._wordWrapCheckBox.GetValue())
555 doFontUpdate = self._originalTextFont != self._textFont or self._originalTextColor != self._textColor
556 config.Write("TextEditorFont", self._textFont.GetNativeFontInfoDesc())
557 config.Write("TextEditorColor", "%02x%02x%02x" % (self._textColor.Red(), self._textColor.Green(), self._textColor.Blue()))
558 if doWordWrapUpdate or doFontUpdate:
559 for document in optionsDialog.GetDocManager().GetDocuments():
560 if document.GetDocumentTemplate().GetDocumentType() == TextDocument:
561 if doWordWrapUpdate:
562 document.UpdateAllViews(hint = "Word Wrap")
563 if doFontUpdate:
564 document.UpdateAllViews(hint = "Font")