]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/STCTextEditor.py
Add code to remove the selection (if any) in wxTextCtrl::WriteText for multi-line...
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / STCTextEditor.py
1 #----------------------------------------------------------------------------
2 # Name: STCTextEditor.py
3 # Purpose: Text Editor for wx.lib.pydocview tbat uses the Styled Text Control
4 #
5 # Author: Peter Yared, Morgan Hua
6 #
7 # Created: 8/10/03
8 # CVS-ID: $Id$
9 # Copyright: (c) 2003-2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
12
13 import wx
14 import wx.stc
15 import wx.lib.docview
16 import wx.lib.multisash
17 import wx.lib.pydocview
18 import string
19 import FindService
20 import os
21 import sys
22 _ = wx.GetTranslation
23
24 #----------------------------------------------------------------------------
25 # Constants
26 #----------------------------------------------------------------------------
27
28 TEXT_ID = wx.NewId()
29 VIEW_WHITESPACE_ID = wx.NewId()
30 VIEW_EOL_ID = wx.NewId()
31 VIEW_INDENTATION_GUIDES_ID = wx.NewId()
32 VIEW_RIGHT_EDGE_ID = wx.NewId()
33 VIEW_LINE_NUMBERS_ID = wx.NewId()
34 ZOOM_ID = wx.NewId()
35 ZOOM_NORMAL_ID = wx.NewId()
36 ZOOM_IN_ID = wx.NewId()
37 ZOOM_OUT_ID = wx.NewId()
38 CHOOSE_FONT_ID = wx.NewId()
39 WORD_WRAP_ID = wx.NewId()
40 TEXT_STATUS_BAR_ID = wx.NewId()
41
42
43 #----------------------------------------------------------------------------
44 # Classes
45 #----------------------------------------------------------------------------
46
47 class TextDocument(wx.lib.docview.Document):
48
49
50 def SaveObject(self, fileObject):
51 view = self.GetFirstView()
52 fileObject.write(view.GetValue())
53 return True
54
55
56 def LoadObject(self, fileObject):
57 view = self.GetFirstView()
58 data = fileObject.read()
59 view.SetValue(data)
60 return True
61
62
63 def IsModified(self):
64 view = self.GetFirstView()
65 if view:
66 return wx.lib.docview.Document.IsModified(self) or view.IsModified()
67 else:
68 return wx.lib.docview.Document.IsModified(self)
69
70
71 def Modify(self, mod):
72 view = self.GetFirstView()
73 wx.lib.docview.Document.Modify(self, mod)
74 if not mod and view:
75 view.SetModifyFalse()
76
77
78 def OnCreateCommandProcessor(self):
79 # Don't create a command processor, it has its own
80 pass
81
82
83 # Use this to override MultiClient.Select to prevent yellow background.
84 def MultiClientSelectBGNotYellow(a):
85 a.GetParent().multiView.UnSelect()
86 a.selected = True
87 #a.SetBackgroundColour(wx.Colour(255,255,0)) # Yellow
88 a.Refresh()
89
90 class TextView(wx.lib.docview.View):
91 MARKER_NUM = 0
92 MARKER_MASK = 0x1
93
94 #----------------------------------------------------------------------------
95 # Overridden methods
96 #----------------------------------------------------------------------------
97
98 def __init__(self):
99 wx.lib.docview.View.__init__(self)
100 self._textEditor = None
101 self._markerCount = 0
102 self._commandProcessor = None
103 self._dynSash = None
104
105
106 def GetCtrlClass(self):
107 """ Used in split window to instantiate new instances """
108 return TextCtrl
109
110
111 def GetCtrl(self):
112 if wx.Platform == "__WXMAC__":
113 # look for active one first
114 self._textEditor = self._GetActiveCtrl(self._dynSash)
115 if self._textEditor == None: # it is possible none are active
116 # look for any existing one
117 self._textEditor = self._FindCtrl(self._dynSash)
118 return self._textEditor
119
120
121 def SetCtrl(self, ctrl):
122 self._textEditor = ctrl
123
124
125 def OnCreatePrintout(self):
126 """ for Print Preview and Print """
127 return TextPrintout(self, self.GetDocument().GetPrintableName())
128
129
130 def OnCreate(self, doc, flags):
131 frame = wx.GetApp().CreateDocumentFrame(self, doc, flags, style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
132 # wxBug: DynamicSashWindow doesn't work on Mac, so revert to
133 # multisash implementation
134 if wx.Platform == "__WXMAC__":
135 wx.lib.multisash.MultiClient.Select = MultiClientSelectBGNotYellow
136 self._dynSash = wx.lib.multisash.MultiSash(frame, -1)
137 self._dynSash.SetDefaultChildClass(self.GetCtrlClass()) # wxBug: MultiSash instantiates the first TextCtrl with this call
138
139 self._textEditor = self.GetCtrl() # wxBug: grab the TextCtrl from the MultiSash datastructure
140 else:
141 self._dynSash = wx.gizmos.DynamicSashWindow(frame, -1, style=wx.CLIP_CHILDREN)
142 self._dynSash._view = self
143 self._textEditor = self.GetCtrlClass()(self._dynSash, -1, style=wx.NO_BORDER)
144 wx.EVT_LEFT_DOWN(self._textEditor, self.OnLeftClick)
145 self._CreateSizer(frame)
146 self.Activate()
147 frame.Show(True)
148 frame.Layout()
149 return True
150
151
152 def _CreateSizer(self, frame):
153 sizer = wx.BoxSizer(wx.HORIZONTAL)
154 sizer.Add(self._dynSash, 1, wx.EXPAND)
155 frame.SetSizer(sizer)
156
157
158 def OnLeftClick(self, event):
159 self.Activate()
160 event.Skip()
161
162
163 def OnUpdate(self, sender = None, hint = None):
164 if hint == "ViewStuff":
165 self.GetCtrl().SetViewDefaults()
166 elif hint == "Font":
167 font, color = self.GetCtrl().GetFontAndColorFromConfig()
168 self.GetCtrl().SetFont(font)
169 self.GetCtrl().SetFontColor(color)
170
171
172 def OnActivateView(self, activate, activeView, deactiveView):
173 if activate and self.GetCtrl():
174 # In MDI mode just calling set focus doesn't work and in SDI mode using CallAfter causes an endless loop
175 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
176 self.SetFocus()
177 else:
178 wx.CallAfter(self.SetFocus)
179
180
181 def SetFocus(self):
182 if self.GetCtrl():
183 self.GetCtrl().SetFocus()
184
185
186 def OnClose(self, deleteWindow = True):
187 if not wx.lib.docview.View.OnClose(self, deleteWindow):
188 return False
189 self.Activate(False)
190 if deleteWindow and self.GetFrame():
191 self.GetFrame().Destroy()
192 return True
193
194
195 def ProcessEvent(self, event):
196 id = event.GetId()
197 if id == wx.ID_UNDO:
198 self.GetCtrl().Undo()
199 return True
200 elif id == wx.ID_REDO:
201 self.GetCtrl().Redo()
202 return True
203 elif id == wx.ID_CUT:
204 self.GetCtrl().Cut()
205 return True
206 elif id == wx.ID_COPY:
207 self.GetCtrl().Copy()
208 return True
209 elif id == wx.ID_PASTE:
210 self.GetCtrl().OnPaste()
211 return True
212 elif id == wx.ID_CLEAR:
213 self.GetCtrl().OnClear()
214 return True
215 elif id == wx.ID_SELECTALL:
216 self.GetCtrl().SetSelection(0, -1)
217 return True
218 elif id == VIEW_WHITESPACE_ID:
219 self.GetCtrl().SetViewWhiteSpace(not self.GetCtrl().GetViewWhiteSpace())
220 return True
221 elif id == VIEW_EOL_ID:
222 self.GetCtrl().SetViewEOL(not self.GetCtrl().GetViewEOL())
223 return True
224 elif id == VIEW_INDENTATION_GUIDES_ID:
225 self.GetCtrl().SetIndentationGuides(not self.GetCtrl().GetIndentationGuides())
226 return True
227 elif id == VIEW_RIGHT_EDGE_ID:
228 self.GetCtrl().SetViewRightEdge(not self.GetCtrl().GetViewRightEdge())
229 return True
230 elif id == VIEW_LINE_NUMBERS_ID:
231 self.GetCtrl().SetViewLineNumbers(not self.GetCtrl().GetViewLineNumbers())
232 return True
233 elif id == ZOOM_NORMAL_ID:
234 self.GetCtrl().SetZoom(0)
235 return True
236 elif id == ZOOM_IN_ID:
237 self.GetCtrl().CmdKeyExecute(wx.stc.STC_CMD_ZOOMIN)
238 return True
239 elif id == ZOOM_OUT_ID:
240 self.GetCtrl().CmdKeyExecute(wx.stc.STC_CMD_ZOOMOUT)
241 return True
242 elif id == CHOOSE_FONT_ID:
243 self.OnChooseFont()
244 return True
245 elif id == WORD_WRAP_ID:
246 self.GetCtrl().SetWordWrap(not self.GetCtrl().GetWordWrap())
247 return True
248 elif id == FindService.FindService.FIND_ID:
249 self.OnFind()
250 return True
251 elif id == FindService.FindService.FIND_PREVIOUS_ID:
252 self.DoFind(forceFindPrevious = True)
253 return True
254 elif id == FindService.FindService.FIND_NEXT_ID:
255 self.DoFind(forceFindNext = True)
256 return True
257 elif id == FindService.FindService.REPLACE_ID:
258 self.OnFind(replace = True)
259 return True
260 elif id == FindService.FindService.FINDONE_ID:
261 self.DoFind()
262 return True
263 elif id == FindService.FindService.REPLACEONE_ID:
264 self.DoFind(replace = True)
265 return True
266 elif id == FindService.FindService.REPLACEALL_ID:
267 self.DoFind(replaceAll = True)
268 return True
269 elif id == FindService.FindService.GOTO_LINE_ID:
270 self.OnGotoLine(event)
271 return True
272 else:
273 return wx.lib.docview.View.ProcessEvent(self, event)
274
275
276 def ProcessUpdateUIEvent(self, event):
277 if not self.GetCtrl():
278 return False
279
280 id = event.GetId()
281 if id == wx.ID_UNDO:
282 event.Enable(self.GetCtrl().CanUndo())
283 event.SetText(_("&Undo\tCtrl+Z")) # replace menu string
284 return True
285 elif id == wx.ID_REDO:
286 event.Enable(self.GetCtrl().CanRedo())
287 event.SetText(_("&Redo\tCtrl+Y")) # replace menu string
288 return True
289 elif (id == wx.ID_CUT
290 or id == wx.ID_COPY
291 or id == wx.ID_CLEAR):
292 hasSelection = self.GetCtrl().GetSelectionStart() != self.GetCtrl().GetSelectionEnd()
293 event.Enable(hasSelection)
294 return True
295 elif id == wx.ID_PASTE:
296 event.Enable(self.GetCtrl().CanPaste())
297 return True
298 elif id == wx.ID_SELECTALL:
299 hasText = self.GetCtrl().GetTextLength() > 0
300 event.Enable(hasText)
301 return True
302 elif id == TEXT_ID:
303 event.Enable(True)
304 return True
305 elif id == VIEW_WHITESPACE_ID:
306 hasText = self.GetCtrl().GetTextLength() > 0
307 event.Enable(hasText)
308 event.Check(self.GetCtrl().GetViewWhiteSpace())
309 return True
310 elif id == VIEW_EOL_ID:
311 hasText = self.GetCtrl().GetTextLength() > 0
312 event.Enable(hasText)
313 event.Check(self.GetCtrl().GetViewEOL())
314 return True
315 elif id == VIEW_INDENTATION_GUIDES_ID:
316 hasText = self.GetCtrl().GetTextLength() > 0
317 event.Enable(hasText)
318 event.Check(self.GetCtrl().GetIndentationGuides())
319 return True
320 elif id == VIEW_RIGHT_EDGE_ID:
321 hasText = self.GetCtrl().GetTextLength() > 0
322 event.Enable(hasText)
323 event.Check(self.GetCtrl().GetViewRightEdge())
324 return True
325 elif id == VIEW_LINE_NUMBERS_ID:
326 hasText = self.GetCtrl().GetTextLength() > 0
327 event.Enable(hasText)
328 event.Check(self.GetCtrl().GetViewLineNumbers())
329 return True
330 elif id == ZOOM_ID:
331 event.Enable(True)
332 return True
333 elif id == ZOOM_NORMAL_ID:
334 event.Enable(self.GetCtrl().GetZoom() != 0)
335 return True
336 elif id == ZOOM_IN_ID:
337 event.Enable(self.GetCtrl().GetZoom() < 20)
338 return True
339 elif id == ZOOM_OUT_ID:
340 event.Enable(self.GetCtrl().GetZoom() > -10)
341 return True
342 elif id == CHOOSE_FONT_ID:
343 event.Enable(True)
344 return True
345 elif id == WORD_WRAP_ID:
346 event.Enable(self.GetCtrl().CanWordWrap())
347 event.Check(self.GetCtrl().CanWordWrap() and self.GetCtrl().GetWordWrap())
348 return True
349 elif id == FindService.FindService.FIND_ID:
350 hasText = self.GetCtrl().GetTextLength() > 0
351 event.Enable(hasText)
352 return True
353 elif id == FindService.FindService.FIND_PREVIOUS_ID:
354 hasText = self.GetCtrl().GetTextLength() > 0
355 event.Enable(hasText and
356 self._FindServiceHasString() and
357 self.GetCtrl().GetSelection()[0] > 0)
358 return True
359 elif id == FindService.FindService.FIND_NEXT_ID:
360 hasText = self.GetCtrl().GetTextLength() > 0
361 event.Enable(hasText and
362 self._FindServiceHasString() and
363 self.GetCtrl().GetSelection()[0] < self.GetCtrl().GetLength())
364 return True
365 elif id == FindService.FindService.REPLACE_ID:
366 hasText = self.GetCtrl().GetTextLength() > 0
367 event.Enable(hasText)
368 return True
369 elif id == FindService.FindService.GOTO_LINE_ID:
370 event.Enable(True)
371 return True
372 elif id == TEXT_STATUS_BAR_ID:
373 self.OnUpdateStatusBar(event)
374 return True
375 else:
376 return wx.lib.docview.View.ProcessUpdateUIEvent(self, event)
377
378
379 def _GetParentFrame(self):
380 return wx.GetTopLevelParent(self.GetFrame())
381
382 def _GetActiveCtrl(self, parent):
383 """ Walk through the MultiSash windows and find the active Control """
384 if isinstance(parent, wx.lib.multisash.MultiClient) and parent.selected:
385 return parent.child
386 if hasattr(parent, "GetChildren"):
387 for child in parent.GetChildren():
388 found = self._GetActiveCtrl(child)
389 if found:
390 return found
391 return None
392
393
394 def _FindCtrl(self, parent):
395 """ Walk through the MultiSash windows and find the first TextCtrl """
396 if isinstance(parent, self.GetCtrlClass()):
397 return parent
398 if hasattr(parent, "GetChildren"):
399 for child in parent.GetChildren():
400 found = self._FindCtrl(child)
401 if found:
402 return found
403 return None
404
405
406 #----------------------------------------------------------------------------
407 # Methods for TextDocument to call
408 #----------------------------------------------------------------------------
409
410 def IsModified(self):
411 if not self.GetCtrl():
412 return False
413 return self.GetCtrl().GetModify()
414
415
416 def SetModifyFalse(self):
417 self.GetCtrl().SetSavePoint()
418
419
420 def GetValue(self):
421 if self.GetCtrl():
422 return self.GetCtrl().GetText()
423 else:
424 return None
425
426
427 def SetValue(self, value):
428 self.GetCtrl().SetText(value)
429 self.GetCtrl().UpdateLineNumberMarginWidth()
430 self.GetCtrl().EmptyUndoBuffer()
431
432
433 #----------------------------------------------------------------------------
434 # STC events
435 #----------------------------------------------------------------------------
436
437 def OnUpdateStatusBar(self, event):
438 statusBar = self._GetParentFrame().GetStatusBar()
439 statusBar.SetInsertMode(self.GetCtrl().GetOvertype() == 0)
440 statusBar.SetLineNumber(self.GetCtrl().GetCurrentLine() + 1)
441 statusBar.SetColumnNumber(self.GetCtrl().GetColumn(self.GetCtrl().GetCurrentPos()) + 1)
442
443
444 #----------------------------------------------------------------------------
445 # Format methods
446 #----------------------------------------------------------------------------
447
448 def OnChooseFont(self):
449 data = wx.FontData()
450 data.EnableEffects(True)
451 data.SetInitialFont(self.GetCtrl().GetFont())
452 data.SetColour(self.GetCtrl().GetFontColor())
453 fontDialog = wx.FontDialog(self.GetFrame(), data)
454 fontDialog.CenterOnParent()
455 if fontDialog.ShowModal() == wx.ID_OK:
456 data = fontDialog.GetFontData()
457 self.GetCtrl().SetFont(data.GetChosenFont())
458 self.GetCtrl().SetFontColor(data.GetColour())
459 self.GetCtrl().UpdateStyles()
460 fontDialog.Destroy()
461
462
463 #----------------------------------------------------------------------------
464 # Find methods
465 #----------------------------------------------------------------------------
466
467 def OnFind(self, replace = False):
468 findService = wx.GetApp().GetService(FindService.FindService)
469 if findService:
470 findService.ShowFindReplaceDialog(findString = self.GetCtrl().GetSelectedText(), replace = replace)
471
472
473 def DoFind(self, forceFindNext = False, forceFindPrevious = False, replace = False, replaceAll = False):
474 findService = wx.GetApp().GetService(FindService.FindService)
475 if not findService:
476 return
477 findString = findService.GetFindString()
478 if len(findString) == 0:
479 return -1
480 replaceString = findService.GetReplaceString()
481 flags = findService.GetFlags()
482 startLoc, endLoc = self.GetCtrl().GetSelection()
483
484 wholeWord = flags & wx.FR_WHOLEWORD > 0
485 matchCase = flags & wx.FR_MATCHCASE > 0
486 regExp = flags & FindService.FindService.FR_REGEXP > 0
487 down = flags & wx.FR_DOWN > 0
488 wrap = flags & FindService.FindService.FR_WRAP > 0
489
490 if forceFindPrevious: # this is from function keys, not dialog box
491 down = False
492 wrap = False # user would want to know they're at the end of file
493 elif forceFindNext:
494 down = True
495 wrap = False # user would want to know they're at the end of file
496
497 badSyntax = False
498
499 # On replace dialog operations, user is allowed to replace the currently highlighted text to determine if it should be replaced or not.
500 # 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.
501 # If the text is a match, then replace it.
502 if replace:
503 result, start, end, replText = findService.DoFind(findString, replaceString, self.GetCtrl().GetSelectedText(), 0, 0, True, matchCase, wholeWord, regExp, replace)
504 if result > 0:
505 self.GetCtrl().ReplaceSelection(replText)
506 self.GetDocument().Modify(True)
507 wx.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString)
508 if down:
509 startLoc += len(replText) # advance start location past replacement string to new text
510 endLoc = startLoc
511 elif result == FindService.FIND_SYNTAXERROR:
512 badSyntax = True
513 wx.GetApp().GetTopWindow().PushStatusText(_("Invalid regular expression \"%s\"") % findString)
514
515 if not badSyntax:
516 text = self.GetCtrl().GetText()
517
518 # Find the next matching text occurance or if it is a ReplaceAll, replace all occurances
519 # 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
520 result, start, end, text = findService.DoFind(findString, replaceString, text, startLoc, endLoc, down, matchCase, wholeWord, regExp, False, replaceAll, wrap)
521 if result > 0:
522 self.GetCtrl().SetTargetStart(0)
523 self.GetCtrl().SetTargetEnd(self.GetCtrl().GetLength())
524 self.GetCtrl().ReplaceTarget(text) # Doing a SetText causes a clear document to be shown when undoing, so using replacetarget instead
525 self.GetDocument().Modify(True)
526 if result == 1:
527 wx.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString)
528 else:
529 wx.GetApp().GetTopWindow().PushStatusText(_("%i occurrences of \"%s\" replaced") % (result, findString))
530 elif result == 0:
531 self.GetCtrl().SetSelection(start, end)
532 self.GetCtrl().EnsureVisible(self.GetCtrl().LineFromPosition(end)) # show bottom then scroll up to top
533 self.GetCtrl().EnsureVisible(self.GetCtrl().LineFromPosition(start)) # do this after ensuring bottom is visible
534 wx.GetApp().GetTopWindow().PushStatusText(_("Found \"%s\".") % findString)
535 elif result == FindService.FIND_SYNTAXERROR:
536 # Dialog for this case gets popped up by the FindService.
537 wx.GetApp().GetTopWindow().PushStatusText(_("Invalid regular expression \"%s\"") % findString)
538 else:
539 wx.MessageBox(_("Can't find \"%s\".") % findString, "Find",
540 wx.OK | wx.ICON_INFORMATION)
541
542
543 def _FindServiceHasString(self):
544 findService = wx.GetApp().GetService(FindService.FindService)
545 if not findService or not findService.GetFindString():
546 return False
547 return True
548
549
550 def OnGotoLine(self, event):
551 findService = wx.GetApp().GetService(FindService.FindService)
552 if findService:
553 line = findService.GetLineNumber(self.GetDocumentManager().FindSuitableParent())
554 if line > -1:
555 line = line - 1
556 self.GetCtrl().EnsureVisible(line)
557 self.GetCtrl().GotoLine(line)
558
559
560 def GotoLine(self, lineNum):
561 if lineNum > -1:
562 lineNum = lineNum - 1 # line numbering for editor is 0 based, we are 1 based.
563 self.GetCtrl().EnsureVisibleEnforcePolicy(lineNum)
564 self.GetCtrl().GotoLine(lineNum)
565
566
567 def SetSelection(self, start, end):
568 self.GetCtrl().SetSelection(start, end)
569
570
571 def EnsureVisible(self, line):
572 self.GetCtrl().EnsureVisible(line-1) # line numbering for editor is 0 based, we are 1 based.
573
574 def EnsureVisibleEnforcePolicy(self, line):
575 self.GetCtrl().EnsureVisibleEnforcePolicy(line-1) # line numbering for editor is 0 based, we are 1 based.
576
577 def LineFromPosition(self, pos):
578 return self.GetCtrl().LineFromPosition(pos)+1 # line numbering for editor is 0 based, we are 1 based.
579
580
581 def PositionFromLine(self, line):
582 return self.GetCtrl().PositionFromLine(line-1) # line numbering for editor is 0 based, we are 1 based.
583
584
585 def GetLineEndPosition(self, line):
586 return self.GetCtrl().GetLineEndPosition(line-1) # line numbering for editor is 0 based, we are 1 based.
587
588
589 def GetLine(self, lineNum):
590 return self.GetCtrl().GetLine(lineNum-1) # line numbering for editor is 0 based, we are 1 based.
591
592
593 def MarkerDefine(self):
594 """ This must be called after the texteditor is instantiated """
595 self.GetCtrl().MarkerDefine(TextView.MARKER_NUM, wx.stc.STC_MARK_CIRCLE, wx.BLACK, wx.BLUE)
596
597
598 def MarkerToggle(self, lineNum = -1, marker_index=MARKER_NUM, mask=MARKER_MASK):
599 if lineNum == -1:
600 lineNum = self.GetCtrl().GetCurrentLine()
601 if self.GetCtrl().MarkerGet(lineNum) & mask:
602 self.GetCtrl().MarkerDelete(lineNum, marker_index)
603 self._markerCount -= 1
604 else:
605 self.GetCtrl().MarkerAdd(lineNum, marker_index)
606 self._markerCount += 1
607
608
609 def MarkerAdd(self, lineNum = -1, marker_index=MARKER_NUM, mask=MARKER_MASK):
610 if lineNum == -1:
611 lineNum = self.GetCtrl().GetCurrentLine()
612 self.GetCtrl().MarkerAdd(lineNum, marker_index)
613 self._markerCount += 1
614
615
616 def MarkerDelete(self, lineNum = -1, marker_index=MARKER_NUM, mask=MARKER_MASK):
617 if lineNum == -1:
618 lineNum = self.GetCtrl().GetCurrentLine()
619 if self.GetCtrl().MarkerGet(lineNum) & mask:
620 self.GetCtrl().MarkerDelete(lineNum, marker_index)
621 self._markerCount -= 1
622
623 def MarkerDeleteAll(self, marker_num=MARKER_NUM):
624 self.GetCtrl().MarkerDeleteAll(marker_num)
625 if marker_num == self.MARKER_NUM:
626 self._markerCount = 0
627
628
629 def MarkerNext(self, lineNum = -1):
630 if lineNum == -1:
631 lineNum = self.GetCtrl().GetCurrentLine() + 1 # start search below current line
632 foundLine = self.GetCtrl().MarkerNext(lineNum, self.MARKER_MASK)
633 if foundLine == -1:
634 # wrap to top of file
635 foundLine = self.GetCtrl().MarkerNext(0, self.MARKER_MASK)
636 if foundLine == -1:
637 wx.GetApp().GetTopWindow().PushStatusText(_("No markers"))
638 return
639
640 self.GotoLine(foundLine + 1)
641
642
643 def MarkerPrevious(self, lineNum = -1):
644 if lineNum == -1:
645 lineNum = self.GetCtrl().GetCurrentLine() - 1 # start search above current line
646 if lineNum == -1:
647 lineNum = self.GetCtrl().GetLineCount()
648
649 foundLine = self.GetCtrl().MarkerPrevious(lineNum, self.MARKER_MASK)
650 if foundLine == -1:
651 # wrap to bottom of file
652 foundLine = self.GetCtrl().MarkerPrevious(self.GetCtrl().GetLineCount(), self.MARKER_MASK)
653 if foundLine == -1:
654 wx.GetApp().GetTopWindow().PushStatusText(_("No markers"))
655 return
656
657 self.GotoLine(foundLine + 1)
658
659
660 def MarkerExists(self, lineNum = -1, mask=MARKER_MASK):
661 if lineNum == -1:
662 lineNum = self.GetCtrl().GetCurrentLine()
663 if self.GetCtrl().MarkerGet(lineNum) & mask:
664 return True
665 else:
666 return False
667
668 def GetMarkerLines(self, mask=MARKER_MASK):
669 retval = []
670 for lineNum in range(self.GetCtrl().GetLineCount()):
671 if self.GetCtrl().MarkerGet(lineNum) & mask:
672 retval.append(lineNum)
673 return retval
674
675 def GetMarkerCount(self):
676 return self._markerCount
677
678
679 class TextService(wx.lib.pydocview.DocService):
680
681
682 def __init__(self):
683 wx.lib.pydocview.DocService.__init__(self)
684
685
686 def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
687 if document and document.GetDocumentTemplate().GetDocumentType() != TextDocument:
688 return
689 if not document and wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
690 return
691
692 statusBar = TextStatusBar(frame, TEXT_STATUS_BAR_ID)
693 frame.SetStatusBar(statusBar)
694 wx.EVT_UPDATE_UI(frame, TEXT_STATUS_BAR_ID, frame.ProcessUpdateUIEvent)
695
696 viewMenu = menuBar.GetMenu(menuBar.FindMenu(_("&View")))
697
698 viewMenu.AppendSeparator()
699 textMenu = wx.Menu()
700 textMenu.AppendCheckItem(VIEW_WHITESPACE_ID, _("&Whitespace"), _("Shows or hides whitespace"))
701 wx.EVT_MENU(frame, VIEW_WHITESPACE_ID, frame.ProcessEvent)
702 wx.EVT_UPDATE_UI(frame, VIEW_WHITESPACE_ID, frame.ProcessUpdateUIEvent)
703 textMenu.AppendCheckItem(VIEW_EOL_ID, _("&End of Line Markers"), _("Shows or hides indicators at the end of each line"))
704 wx.EVT_MENU(frame, VIEW_EOL_ID, frame.ProcessEvent)
705 wx.EVT_UPDATE_UI(frame, VIEW_EOL_ID, frame.ProcessUpdateUIEvent)
706 textMenu.AppendCheckItem(VIEW_INDENTATION_GUIDES_ID, _("&Indentation Guides"), _("Shows or hides indentations"))
707 wx.EVT_MENU(frame, VIEW_INDENTATION_GUIDES_ID, frame.ProcessEvent)
708 wx.EVT_UPDATE_UI(frame, VIEW_INDENTATION_GUIDES_ID, frame.ProcessUpdateUIEvent)
709 textMenu.AppendCheckItem(VIEW_RIGHT_EDGE_ID, _("&Right Edge"), _("Shows or hides the right edge marker"))
710 wx.EVT_MENU(frame, VIEW_RIGHT_EDGE_ID, frame.ProcessEvent)
711 wx.EVT_UPDATE_UI(frame, VIEW_RIGHT_EDGE_ID, frame.ProcessUpdateUIEvent)
712 textMenu.AppendCheckItem(VIEW_LINE_NUMBERS_ID, _("&Line Numbers"), _("Shows or hides the line numbers"))
713 wx.EVT_MENU(frame, VIEW_LINE_NUMBERS_ID, frame.ProcessEvent)
714 wx.EVT_UPDATE_UI(frame, VIEW_LINE_NUMBERS_ID, frame.ProcessUpdateUIEvent)
715
716 viewMenu.AppendMenu(TEXT_ID, _("&Text"), textMenu)
717 wx.EVT_UPDATE_UI(frame, TEXT_ID, frame.ProcessUpdateUIEvent)
718
719 isWindows = (wx.Platform == '__WXMSW__')
720
721 zoomMenu = wx.Menu()
722 zoomMenu.Append(ZOOM_NORMAL_ID, _("Normal Size"), _("Sets the document to its normal size"))
723 wx.EVT_MENU(frame, ZOOM_NORMAL_ID, frame.ProcessEvent)
724 wx.EVT_UPDATE_UI(frame, ZOOM_NORMAL_ID, frame.ProcessUpdateUIEvent)
725 if isWindows:
726 zoomMenu.Append(ZOOM_IN_ID, _("Zoom In\tCtrl+Page Up"), _("Zooms the document to a larger size"))
727 else:
728 zoomMenu.Append(ZOOM_IN_ID, _("Zoom In"), _("Zooms the document to a larger size"))
729 wx.EVT_MENU(frame, ZOOM_IN_ID, frame.ProcessEvent)
730 wx.EVT_UPDATE_UI(frame, ZOOM_IN_ID, frame.ProcessUpdateUIEvent)
731 if isWindows:
732 zoomMenu.Append(ZOOM_OUT_ID, _("Zoom Out\tCtrl+Page Down"), _("Zooms the document to a smaller size"))
733 else:
734 zoomMenu.Append(ZOOM_OUT_ID, _("Zoom Out"), _("Zooms the document to a smaller size"))
735 wx.EVT_MENU(frame, ZOOM_OUT_ID, frame.ProcessEvent)
736 wx.EVT_UPDATE_UI(frame, ZOOM_OUT_ID, frame.ProcessUpdateUIEvent)
737
738 viewMenu.AppendMenu(ZOOM_ID, _("&Zoom"), zoomMenu)
739 wx.EVT_UPDATE_UI(frame, ZOOM_ID, frame.ProcessUpdateUIEvent)
740
741 formatMenuIndex = menuBar.FindMenu(_("&Format"))
742 if formatMenuIndex > -1:
743 formatMenu = menuBar.GetMenu(formatMenuIndex)
744 else:
745 formatMenu = wx.Menu()
746 if not menuBar.FindItemById(CHOOSE_FONT_ID):
747 formatMenu.Append(CHOOSE_FONT_ID, _("&Font..."), _("Sets the font to use"))
748 wx.EVT_MENU(frame, CHOOSE_FONT_ID, frame.ProcessEvent)
749 wx.EVT_UPDATE_UI(frame, CHOOSE_FONT_ID, frame.ProcessUpdateUIEvent)
750 if not menuBar.FindItemById(WORD_WRAP_ID):
751 formatMenu.AppendCheckItem(WORD_WRAP_ID, _("Word Wrap"), _("Wraps text horizontally when checked"))
752 wx.EVT_MENU(frame, WORD_WRAP_ID, frame.ProcessEvent)
753 wx.EVT_UPDATE_UI(frame, WORD_WRAP_ID, frame.ProcessUpdateUIEvent)
754 if formatMenuIndex == -1:
755 viewMenuIndex = menuBar.FindMenu(_("&View"))
756 menuBar.Insert(viewMenuIndex + 1, formatMenu, _("&Format"))
757
758 # wxBug: wxToolBar::GetToolPos doesn't exist, need it to find cut tool and then insert find in front of it.
759 toolBar.AddSeparator()
760 toolBar.AddTool(ZOOM_IN_ID, getZoomInBitmap(), shortHelpString = _("Zoom In"), longHelpString = _("Zooms the document to a larger size"))
761 toolBar.AddTool(ZOOM_OUT_ID, getZoomOutBitmap(), shortHelpString = _("Zoom Out"), longHelpString = _("Zooms the document to a smaller size"))
762 toolBar.Realize()
763
764
765 def ProcessUpdateUIEvent(self, event):
766 id = event.GetId()
767 if (id == TEXT_ID
768 or id == VIEW_WHITESPACE_ID
769 or id == VIEW_EOL_ID
770 or id == VIEW_INDENTATION_GUIDES_ID
771 or id == VIEW_RIGHT_EDGE_ID
772 or id == VIEW_LINE_NUMBERS_ID
773 or id == ZOOM_ID
774 or id == ZOOM_NORMAL_ID
775 or id == ZOOM_IN_ID
776 or id == ZOOM_OUT_ID
777 or id == CHOOSE_FONT_ID
778 or id == WORD_WRAP_ID):
779 event.Enable(False)
780 return True
781 else:
782 return False
783
784
785 class TextStatusBar(wx.StatusBar):
786
787 # wxBug: Would be nice to show num key status in statusbar, but can't figure out how to detect if it is enabled or disabled
788
789 def __init__(self, parent, id, style = wx.ST_SIZEGRIP, name = "statusBar"):
790 wx.StatusBar.__init__(self, parent, id, style, name)
791 self.SetFieldsCount(4)
792 self.SetStatusWidths([-1, 50, 50, 55])
793
794 def SetInsertMode(self, insert = True):
795 if insert:
796 newText = _("Ins")
797 else:
798 newText = _("")
799 if self.GetStatusText(1) != newText: # wxBug: Need to check if the text has changed, otherwise it flickers under win32
800 self.SetStatusText(newText, 1)
801
802 def SetLineNumber(self, lineNumber):
803 newText = _("Ln %i") % lineNumber
804 if self.GetStatusText(2) != newText:
805 self.SetStatusText(newText, 2)
806
807 def SetColumnNumber(self, colNumber):
808 newText = _("Col %i") % colNumber
809 if self.GetStatusText(3) != newText:
810 self.SetStatusText(newText, 3)
811
812
813 class TextOptionsPanel(wx.Panel):
814
815
816 def __init__(self, parent, id, configPrefix = "Text", label = "Text", hasWordWrap = True, hasTabs = False, addPage=True):
817 wx.Panel.__init__(self, parent, id)
818 self._configPrefix = configPrefix
819 self._hasWordWrap = hasWordWrap
820 self._hasTabs = hasTabs
821 SPACE = 10
822 HALF_SPACE = 5
823 config = wx.ConfigBase_Get()
824 self._textFont = wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL)
825 fontData = config.Read(self._configPrefix + "EditorFont", "")
826 if fontData:
827 nativeFont = wx.NativeFontInfo()
828 nativeFont.FromString(fontData)
829 self._textFont.SetNativeFontInfo(nativeFont)
830 self._originalTextFont = self._textFont
831 self._textColor = wx.BLACK
832 colorData = config.Read(self._configPrefix + "EditorColor", "")
833 if colorData:
834 red = int("0x" + colorData[0:2], 16)
835 green = int("0x" + colorData[2:4], 16)
836 blue = int("0x" + colorData[4:6], 16)
837 self._textColor = wx.Color(red, green, blue)
838 self._originalTextColor = self._textColor
839 fontLabel = wx.StaticText(self, -1, _("Font:"))
840 self._sampleTextCtrl = wx.TextCtrl(self, -1, "", size = (125, 21))
841 self._sampleTextCtrl.SetEditable(False)
842 chooseFontButton = wx.Button(self, -1, _("Choose Font..."))
843 wx.EVT_BUTTON(self, chooseFontButton.GetId(), self.OnChooseFont)
844 if self._hasWordWrap:
845 self._wordWrapCheckBox = wx.CheckBox(self, -1, _("Wrap words inside text area"))
846 self._wordWrapCheckBox.SetValue(wx.ConfigBase_Get().ReadInt(self._configPrefix + "EditorWordWrap", False))
847 self._viewWhitespaceCheckBox = wx.CheckBox(self, -1, _("Show whitespace"))
848 self._viewWhitespaceCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewWhitespace", False))
849 self._viewEOLCheckBox = wx.CheckBox(self, -1, _("Show end of line markers"))
850 self._viewEOLCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewEOL", False))
851 self._viewIndentationGuideCheckBox = wx.CheckBox(self, -1, _("Show indentation guides"))
852 self._viewIndentationGuideCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewIndentationGuides", False))
853 self._viewRightEdgeCheckBox = wx.CheckBox(self, -1, _("Show right edge"))
854 self._viewRightEdgeCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewRightEdge", False))
855 self._viewLineNumbersCheckBox = wx.CheckBox(self, -1, _("Show line numbers"))
856 self._viewLineNumbersCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewLineNumbers", True))
857 if self._hasTabs:
858 self._hasTabsCheckBox = wx.CheckBox(self, -1, _("Use spaces instead of tabs"))
859 self._hasTabsCheckBox.SetValue(not wx.ConfigBase_Get().ReadInt(self._configPrefix + "EditorUseTabs", False))
860 indentWidthLabel = wx.StaticText(self, -1, _("Indent Width:"))
861 self._indentWidthChoice = wx.Choice(self, -1, choices = ["2", "4", "6", "8", "10"])
862 self._indentWidthChoice.SetStringSelection(str(config.ReadInt(self._configPrefix + "EditorIndentWidth", 4)))
863 textPanelBorderSizer = wx.BoxSizer(wx.VERTICAL)
864 textPanelSizer = wx.BoxSizer(wx.VERTICAL)
865 textFontSizer = wx.BoxSizer(wx.HORIZONTAL)
866 textFontSizer.Add(fontLabel, 0, wx.ALIGN_LEFT | wx.RIGHT | wx.TOP, HALF_SPACE)
867 textFontSizer.Add(self._sampleTextCtrl, 1, wx.ALIGN_LEFT | wx.EXPAND | wx.RIGHT, HALF_SPACE)
868 textFontSizer.Add(chooseFontButton, 0, wx.ALIGN_RIGHT | wx.LEFT, HALF_SPACE)
869 textPanelSizer.Add(textFontSizer, 0, wx.ALL|wx.EXPAND, HALF_SPACE)
870 if self._hasWordWrap:
871 textPanelSizer.Add(self._wordWrapCheckBox, 0, wx.ALL, HALF_SPACE)
872 textPanelSizer.Add(self._viewWhitespaceCheckBox, 0, wx.ALL, HALF_SPACE)
873 textPanelSizer.Add(self._viewEOLCheckBox, 0, wx.ALL, HALF_SPACE)
874 textPanelSizer.Add(self._viewIndentationGuideCheckBox, 0, wx.ALL, HALF_SPACE)
875 textPanelSizer.Add(self._viewRightEdgeCheckBox, 0, wx.ALL, HALF_SPACE)
876 textPanelSizer.Add(self._viewLineNumbersCheckBox, 0, wx.ALL, HALF_SPACE)
877 if self._hasTabs:
878 textPanelSizer.Add(self._hasTabsCheckBox, 0, wx.ALL, HALF_SPACE)
879 textIndentWidthSizer = wx.BoxSizer(wx.HORIZONTAL)
880 textIndentWidthSizer.Add(indentWidthLabel, 0, wx.ALIGN_LEFT | wx.RIGHT | wx.TOP, HALF_SPACE)
881 textIndentWidthSizer.Add(self._indentWidthChoice, 0, wx.ALIGN_LEFT | wx.EXPAND, HALF_SPACE)
882 textPanelSizer.Add(textIndentWidthSizer, 0, wx.ALL, HALF_SPACE)
883 textPanelBorderSizer.Add(textPanelSizer, 0, wx.ALL|wx.EXPAND, SPACE)
884 ## styleButton = wx.Button(self, -1, _("Choose Style..."))
885 ## wx.EVT_BUTTON(self, styleButton.GetId(), self.OnChooseStyle)
886 ## textPanelBorderSizer.Add(styleButton, 0, wx.ALL, SPACE)
887 self.SetSizer(textPanelBorderSizer)
888 self.UpdateSampleFont()
889 if addPage:
890 parent.AddPage(self, _(label))
891
892 def UpdateSampleFont(self):
893 nativeFont = wx.NativeFontInfo()
894 nativeFont.FromString(self._textFont.GetNativeFontInfoDesc())
895 font = wx.NullFont
896 font.SetNativeFontInfo(nativeFont)
897 font.SetPointSize(self._sampleTextCtrl.GetFont().GetPointSize()) # Use the standard point size
898 self._sampleTextCtrl.SetFont(font)
899 self._sampleTextCtrl.SetForegroundColour(self._textColor)
900 self._sampleTextCtrl.SetValue(str(self._textFont.GetPointSize()) + _(" pt. ") + self._textFont.GetFaceName())
901 self._sampleTextCtrl.Refresh()
902 self.Layout()
903
904
905 ## def OnChooseStyle(self, event):
906 ## import STCStyleEditor
907 ## import os
908 ## base = os.path.split(__file__)[0]
909 ## config = os.path.abspath(os.path.join(base, 'stc-styles.rc.cfg'))
910 ##
911 ## dlg = STCStyleEditor.STCStyleEditDlg(None,
912 ## 'Python', 'python',
913 ## #'HTML', 'html',
914 ## #'XML', 'xml',
915 ## config)
916 ## dlg.CenterOnParent()
917 ## try:
918 ## dlg.ShowModal()
919 ## finally:
920 ## dlg.Destroy()
921
922
923 def OnChooseFont(self, event):
924 data = wx.FontData()
925 data.EnableEffects(True)
926 data.SetInitialFont(self._textFont)
927 data.SetColour(self._textColor)
928 fontDialog = wx.FontDialog(self, data)
929 fontDialog.CenterOnParent()
930 if fontDialog.ShowModal() == wx.ID_OK:
931 data = fontDialog.GetFontData()
932 self._textFont = data.GetChosenFont()
933 self._textColor = data.GetColour()
934 self.UpdateSampleFont()
935 fontDialog.Destroy()
936
937
938 def OnOK(self, optionsDialog):
939 config = wx.ConfigBase_Get()
940 doViewStuffUpdate = config.ReadInt(self._configPrefix + "EditorViewWhitespace", False) != self._viewWhitespaceCheckBox.GetValue()
941 config.WriteInt(self._configPrefix + "EditorViewWhitespace", self._viewWhitespaceCheckBox.GetValue())
942 doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewEOL", False) != self._viewEOLCheckBox.GetValue()
943 config.WriteInt(self._configPrefix + "EditorViewEOL", self._viewEOLCheckBox.GetValue())
944 doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewIndentationGuides", False) != self._viewIndentationGuideCheckBox.GetValue()
945 config.WriteInt(self._configPrefix + "EditorViewIndentationGuides", self._viewIndentationGuideCheckBox.GetValue())
946 doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewRightEdge", False) != self._viewRightEdgeCheckBox.GetValue()
947 config.WriteInt(self._configPrefix + "EditorViewRightEdge", self._viewRightEdgeCheckBox.GetValue())
948 doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewLineNumbers", True) != self._viewLineNumbersCheckBox.GetValue()
949 config.WriteInt(self._configPrefix + "EditorViewLineNumbers", self._viewLineNumbersCheckBox.GetValue())
950 if self._hasWordWrap:
951 doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorWordWrap", False) != self._wordWrapCheckBox.GetValue()
952 config.WriteInt(self._configPrefix + "EditorWordWrap", self._wordWrapCheckBox.GetValue())
953 if self._hasTabs:
954 doViewStuffUpdate = doViewStuffUpdate or not config.ReadInt(self._configPrefix + "EditorUseTabs", True) != self._hasTabsCheckBox.GetValue()
955 config.WriteInt(self._configPrefix + "EditorUseTabs", not self._hasTabsCheckBox.GetValue())
956 newIndentWidth = int(self._indentWidthChoice.GetStringSelection())
957 oldIndentWidth = config.ReadInt(self._configPrefix + "EditorIndentWidth", 4)
958 if newIndentWidth != oldIndentWidth:
959 doViewStuffUpdate = True
960 config.WriteInt(self._configPrefix + "EditorIndentWidth", newIndentWidth)
961 doFontUpdate = self._originalTextFont != self._textFont or self._originalTextColor != self._textColor
962 config.Write(self._configPrefix + "EditorFont", self._textFont.GetNativeFontInfoDesc())
963 config.Write(self._configPrefix + "EditorColor", "%02x%02x%02x" % (self._textColor.Red(), self._textColor.Green(), self._textColor.Blue()))
964 if doViewStuffUpdate or doFontUpdate:
965 for document in optionsDialog.GetDocManager().GetDocuments():
966 if issubclass(document.GetDocumentTemplate().GetDocumentType(), TextDocument):
967 if doViewStuffUpdate:
968 document.UpdateAllViews(hint = "ViewStuff")
969 if doFontUpdate:
970 document.UpdateAllViews(hint = "Font")
971
972
973 def GetIcon(self):
974 return getTextIcon()
975
976
977 class TextCtrl(wx.stc.StyledTextCtrl):
978
979 def __init__(self, parent, id=-1, style=wx.NO_FULL_REPAINT_ON_RESIZE):
980 wx.stc.StyledTextCtrl.__init__(self, parent, id, style=style)
981
982 if isinstance(parent, wx.gizmos.DynamicSashWindow):
983 self._dynSash = parent
984 self.SetupDSScrollBars()
985 self.Bind(wx.gizmos.EVT_DYNAMIC_SASH_SPLIT, self.OnDSSplit)
986 self.Bind(wx.gizmos.EVT_DYNAMIC_SASH_UNIFY, self.OnDSUnify)
987
988 self._font = None
989 self._fontColor = None
990
991 self.SetVisiblePolicy(wx.stc.STC_VISIBLE_STRICT,1)
992
993 self.CmdKeyClear(wx.stc.STC_KEY_ADD, wx.stc.STC_SCMOD_CTRL)
994 self.CmdKeyClear(wx.stc.STC_KEY_SUBTRACT, wx.stc.STC_SCMOD_CTRL)
995 self.CmdKeyAssign(wx.stc.STC_KEY_PRIOR, wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_ZOOMIN)
996 self.CmdKeyAssign(wx.stc.STC_KEY_NEXT, wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_ZOOMOUT)
997 self.Bind(wx.stc.EVT_STC_ZOOM, self.OnUpdateLineNumberMarginWidth) # auto update line num width on zoom
998 wx.EVT_KEY_DOWN(self, self.OnKeyPressed)
999 wx.EVT_KILL_FOCUS(self, self.OnKillFocus)
1000 wx.EVT_SET_FOCUS(self, self.OnFocus)
1001 self.SetMargins(0,0)
1002
1003 self.SetUseTabs(0)
1004 self.SetTabWidth(4)
1005 self.SetIndent(4)
1006
1007 self.SetViewWhiteSpace(False)
1008 self.SetEOLMode(wx.stc.STC_EOL_LF)
1009 self.SetEdgeMode(wx.stc.STC_EDGE_NONE)
1010 self.SetEdgeColumn(78)
1011
1012 self.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER)
1013 self.SetMarginWidth(1, self.EstimatedLineNumberMarginWidth())
1014 self.UpdateStyles()
1015
1016 self.SetCaretForeground("BLACK")
1017
1018 self.SetViewDefaults()
1019 font, color = self.GetFontAndColorFromConfig()
1020 self.SetFont(font)
1021 self.SetFontColor(color)
1022 self.MarkerDefineDefault()
1023
1024 # for multisash initialization
1025 if isinstance(parent, wx.lib.multisash.MultiClient):
1026 while parent.GetParent():
1027 parent = parent.GetParent()
1028 if hasattr(parent, "GetView"):
1029 break
1030 if hasattr(parent, "GetView"):
1031 textEditor = parent.GetView()._textEditor
1032 if textEditor:
1033 doc = textEditor.GetDocPointer()
1034 if doc:
1035 self.SetDocPointer(doc)
1036
1037
1038 def OnFocus(self, event):
1039 # wxBug: On Mac, the STC control may fire a focus/kill focus event
1040 # on shutdown even if the control is in an invalid state. So check
1041 # before handling the event.
1042 if self.IsBeingDeleted():
1043 return
1044
1045 self.SetSelBackground(1, "BLUE")
1046 self.SetSelForeground(1, "WHITE")
1047 if hasattr(self, "_dynSash"):
1048 self._dynSash._view.SetCtrl(self)
1049 event.Skip()
1050
1051
1052 def OnKillFocus(self, event):
1053 # wxBug: On Mac, the STC control may fire a focus/kill focus event
1054 # on shutdown even if the control is in an invalid state. So check
1055 # before handling the event.
1056 if self.IsBeingDeleted():
1057 return
1058 self.SetSelBackground(0, "BLUE")
1059 self.SetSelForeground(0, "WHITE")
1060 self.SetSelBackground(1, "#C0C0C0")
1061 # Don't set foreground color, use syntax highlighted default colors.
1062 event.Skip()
1063
1064
1065 def SetViewDefaults(self, configPrefix = "Text", hasWordWrap = True, hasTabs = False):
1066 config = wx.ConfigBase_Get()
1067 self.SetViewWhiteSpace(config.ReadInt(configPrefix + "EditorViewWhitespace", False))
1068 self.SetViewEOL(config.ReadInt(configPrefix + "EditorViewEOL", False))
1069 self.SetIndentationGuides(config.ReadInt(configPrefix + "EditorViewIndentationGuides", False))
1070 self.SetViewRightEdge(config.ReadInt(configPrefix + "EditorViewRightEdge", False))
1071 self.SetViewLineNumbers(config.ReadInt(configPrefix + "EditorViewLineNumbers", True))
1072 if hasWordWrap:
1073 self.SetWordWrap(config.ReadInt(configPrefix + "EditorWordWrap", False))
1074 if hasTabs: # These methods do not exist in STCTextEditor and are meant for subclasses
1075 self.SetUseTabs(config.ReadInt(configPrefix + "EditorUseTabs", False))
1076 self.SetIndent(config.ReadInt(configPrefix + "EditorIndentWidth", 4))
1077 self.SetTabWidth(config.ReadInt(configPrefix + "EditorIndentWidth", 4))
1078 else:
1079 self.SetUseTabs(True)
1080 self.SetIndent(4)
1081 self.SetTabWidth(4)
1082
1083
1084 def GetDefaultFont(self):
1085 """ Subclasses should override this """
1086 return wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL)
1087
1088
1089 def GetDefaultColor(self):
1090 """ Subclasses should override this """
1091 return wx.BLACK
1092
1093
1094 def GetFontAndColorFromConfig(self, configPrefix = "Text"):
1095 font = self.GetDefaultFont()
1096 config = wx.ConfigBase_Get()
1097 fontData = config.Read(configPrefix + "EditorFont", "")
1098 if fontData:
1099 nativeFont = wx.NativeFontInfo()
1100 nativeFont.FromString(fontData)
1101 font.SetNativeFontInfo(nativeFont)
1102 color = self.GetDefaultColor()
1103 colorData = config.Read(configPrefix + "EditorColor", "")
1104 if colorData:
1105 red = int("0x" + colorData[0:2], 16)
1106 green = int("0x" + colorData[2:4], 16)
1107 blue = int("0x" + colorData[4:6], 16)
1108 color = wx.Color(red, green, blue)
1109 return font, color
1110
1111
1112 def GetFont(self):
1113 return self._font
1114
1115
1116 def SetFont(self, font):
1117 self._font = font
1118 self.StyleSetFont(wx.stc.STC_STYLE_DEFAULT, self._font)
1119
1120
1121 def GetFontColor(self):
1122 return self._fontColor
1123
1124
1125 def SetFontColor(self, fontColor = wx.BLACK):
1126 self._fontColor = fontColor
1127 self.StyleSetForeground(wx.stc.STC_STYLE_DEFAULT, "#%02x%02x%02x" % (self._fontColor.Red(), self._fontColor.Green(), self._fontColor.Blue()))
1128
1129
1130 def UpdateStyles(self):
1131 self.StyleClearAll()
1132 return
1133
1134
1135 def EstimatedLineNumberMarginWidth(self):
1136 MARGIN = 4
1137 baseNumbers = "000"
1138 lineNum = self.GetLineCount()
1139 lineNum = lineNum/100
1140 while lineNum >= 10:
1141 lineNum = lineNum/10
1142 baseNumbers = baseNumbers + "0"
1143
1144 return self.TextWidth(wx.stc.STC_STYLE_LINENUMBER, baseNumbers) + MARGIN
1145
1146
1147 def OnUpdateLineNumberMarginWidth(self, event):
1148 self.UpdateLineNumberMarginWidth()
1149
1150
1151 def UpdateLineNumberMarginWidth(self):
1152 if self.GetViewLineNumbers():
1153 self.SetMarginWidth(1, self.EstimatedLineNumberMarginWidth())
1154
1155 def MarkerDefineDefault(self):
1156 """ This must be called after the textcontrol is instantiated """
1157 self.MarkerDefine(TextView.MARKER_NUM, wx.stc.STC_MARK_ROUNDRECT, wx.BLACK, wx.BLUE)
1158
1159
1160 def OnClear(self):
1161 # Used when Delete key is hit.
1162 sel = self.GetSelection()
1163
1164 # Delete the selection or if no selection, the character after the caret.
1165 if sel[0] == sel[1]:
1166 self.SetSelection(sel[0], sel[0] + 1)
1167 else:
1168 # remove any folded lines also.
1169 startLine = self.LineFromPosition(sel[0])
1170 endLine = self.LineFromPosition(sel[1])
1171 endLineStart = self.PositionFromLine(endLine)
1172 if startLine != endLine and sel[1] - endLineStart == 0:
1173 while not self.GetLineVisible(endLine):
1174 endLine += 1
1175 self.SetSelectionEnd(self.PositionFromLine(endLine))
1176
1177 self.Clear()
1178
1179
1180 def OnPaste(self):
1181 # replace any folded lines also.
1182 sel = self.GetSelection()
1183 startLine = self.LineFromPosition(sel[0])
1184 endLine = self.LineFromPosition(sel[1])
1185 endLineStart = self.PositionFromLine(endLine)
1186 if startLine != endLine and sel[1] - endLineStart == 0:
1187 while not self.GetLineVisible(endLine):
1188 endLine += 1
1189 self.SetSelectionEnd(self.PositionFromLine(endLine))
1190
1191 self.Paste()
1192
1193
1194 def OnKeyPressed(self, event):
1195 key = event.GetKeyCode()
1196 if key == wx.WXK_NUMPAD_ADD: #wxBug: For whatever reason, the key accelerators for numpad add and subtract with modifiers are not working so have to trap them here
1197 if event.ControlDown():
1198 self.ToggleFoldAll(expand = True, topLevelOnly = True)
1199 elif event.ShiftDown():
1200 self.ToggleFoldAll(expand = True)
1201 else:
1202 self.ToggleFold(self.GetCurrentLine())
1203 elif key == wx.WXK_NUMPAD_SUBTRACT:
1204 if event.ControlDown():
1205 self.ToggleFoldAll(expand = False, topLevelOnly = True)
1206 elif event.ShiftDown():
1207 self.ToggleFoldAll(expand = False)
1208 else:
1209 self.ToggleFold(self.GetCurrentLine())
1210 else:
1211 event.Skip()
1212
1213
1214 #----------------------------------------------------------------------------
1215 # View Text methods
1216 #----------------------------------------------------------------------------
1217
1218 def GetViewRightEdge(self):
1219 return self.GetEdgeMode() != wx.stc.STC_EDGE_NONE
1220
1221
1222 def SetViewRightEdge(self, viewRightEdge):
1223 if viewRightEdge:
1224 self.SetEdgeMode(wx.stc.STC_EDGE_LINE)
1225 else:
1226 self.SetEdgeMode(wx.stc.STC_EDGE_NONE)
1227
1228
1229 def GetViewLineNumbers(self):
1230 return self.GetMarginWidth(1) > 0
1231
1232
1233 def SetViewLineNumbers(self, viewLineNumbers = True):
1234 if viewLineNumbers:
1235 self.SetMarginWidth(1, self.EstimatedLineNumberMarginWidth())
1236 else:
1237 self.SetMarginWidth(1, 0)
1238
1239
1240 def CanWordWrap(self):
1241 return True
1242
1243
1244 def GetWordWrap(self):
1245 return self.GetWrapMode() == wx.stc.STC_WRAP_WORD
1246
1247
1248 def SetWordWrap(self, wordWrap):
1249 if wordWrap:
1250 self.SetWrapMode(wx.stc.STC_WRAP_WORD)
1251 else:
1252 self.SetWrapMode(wx.stc.STC_WRAP_NONE)
1253
1254
1255 #----------------------------------------------------------------------------
1256 # DynamicSashWindow methods
1257 #----------------------------------------------------------------------------
1258
1259 def SetupDSScrollBars(self):
1260 # hook the scrollbars provided by the wxDynamicSashWindow
1261 # to this view
1262 v_bar = self._dynSash.GetVScrollBar(self)
1263 h_bar = self._dynSash.GetHScrollBar(self)
1264 v_bar.Bind(wx.EVT_SCROLL, self.OnDSSBScroll)
1265 h_bar.Bind(wx.EVT_SCROLL, self.OnDSSBScroll)
1266 v_bar.Bind(wx.EVT_SET_FOCUS, self.OnDSSBFocus)
1267 h_bar.Bind(wx.EVT_SET_FOCUS, self.OnDSSBFocus)
1268
1269 # And set the wxStyledText to use these scrollbars instead
1270 # of its built-in ones.
1271 self.SetVScrollBar(v_bar)
1272 self.SetHScrollBar(h_bar)
1273
1274
1275 def OnDSSplit(self, evt):
1276 newCtrl = self._dynSash._view.GetCtrlClass()(self._dynSash, -1, style=wx.NO_BORDER)
1277 newCtrl.SetDocPointer(self.GetDocPointer()) # use the same document
1278 self.SetupDSScrollBars()
1279 if self == self._dynSash._view.GetCtrl(): # originally had focus
1280 wx.CallAfter(self.SetFocus) # do this to set colors correctly. wxBug: for some reason, if we don't do a CallAfter, it immediately calls OnKillFocus right after our SetFocus.
1281
1282
1283 def OnDSUnify(self, evt):
1284 self.SetupDSScrollBars()
1285 self.SetFocus() # do this to set colors correctly
1286
1287
1288 def OnDSSBScroll(self, evt):
1289 # redirect the scroll events from the _dynSash's scrollbars to the STC
1290 self.GetEventHandler().ProcessEvent(evt)
1291
1292
1293 def OnDSSBFocus(self, evt):
1294 # when the scrollbar gets the focus move it back to the STC
1295 self.SetFocus()
1296
1297
1298 def DSProcessEvent(self, event):
1299 # wxHack: Needed for customized right mouse click menu items.
1300 if hasattr(self, "_dynSash"):
1301 if event.GetId() == wx.ID_SELECTALL:
1302 # force focus so that select all occurs in the window user right clicked on.
1303 self.SetFocus()
1304
1305 return self._dynSash._view.ProcessEvent(event)
1306 return False
1307
1308
1309 def DSProcessUpdateUIEvent(self, event):
1310 # wxHack: Needed for customized right mouse click menu items.
1311 if hasattr(self, "_dynSash"):
1312 id = event.GetId()
1313 if (id == wx.ID_SELECTALL # allow select all even in non-active window, then force focus to it, see above ProcessEvent
1314 or id == wx.ID_UNDO
1315 or id == wx.ID_REDO):
1316 pass # allow these actions even in non-active window
1317 else: # disallow events in non-active windows. Cut/Copy/Paste/Delete is too confusing user experience.
1318 if self._dynSash._view.GetCtrl() != self:
1319 event.Enable(False)
1320 return True
1321
1322 return self._dynSash._view.ProcessUpdateUIEvent(event)
1323 return False
1324
1325
1326 class TextPrintout(wx.lib.docview.DocPrintout):
1327 """ for Print Preview and Print """
1328
1329
1330 def OnPreparePrinting(self):
1331 """ initialization """
1332 dc = self.GetDC()
1333
1334 ppiScreenX, ppiScreenY = self.GetPPIScreen()
1335 ppiPrinterX, ppiPrinterY = self.GetPPIPrinter()
1336 scaleX = float(ppiPrinterX)/ppiScreenX
1337 scaleY = float(ppiPrinterY)/ppiScreenY
1338
1339 pageWidth, pageHeight = self.GetPageSizePixels()
1340 self._scaleFactorX = scaleX/pageWidth
1341 self._scaleFactorY = scaleY/pageHeight
1342
1343 w, h = dc.GetSize()
1344 overallScaleX = self._scaleFactorX * w
1345 overallScaleY = self._scaleFactorY * h
1346
1347 txtCtrl = self._printoutView.GetCtrl()
1348 font, color = txtCtrl.GetFontAndColorFromConfig()
1349
1350 self._margin = 40
1351 self._fontHeight = font.GetPointSize() + 1
1352 self._pageLines = int((h/overallScaleY - (2 * self._margin))/self._fontHeight)
1353 self._maxLines = txtCtrl.GetLineCount()
1354 self._numPages, remainder = divmod(self._maxLines, self._pageLines)
1355 if remainder != 0:
1356 self._numPages += 1
1357
1358 spaces = 1
1359 lineNum = self._maxLines
1360 while lineNum >= 10:
1361 lineNum = lineNum/10
1362 spaces += 1
1363 self._printFormat = "%%0%sd: %%s" % spaces
1364
1365
1366 def OnPrintPage(self, page):
1367 """ Prints the given page of the view """
1368 dc = self.GetDC()
1369
1370 txtCtrl = self._printoutView.GetCtrl()
1371 font, color = txtCtrl.GetFontAndColorFromConfig()
1372 dc.SetFont(font)
1373
1374 w, h = dc.GetSize()
1375 dc.SetUserScale(self._scaleFactorX * w, self._scaleFactorY * h)
1376
1377 dc.BeginDrawing()
1378
1379 dc.DrawText("%s - page %s" % (self.GetTitle(), page), self._margin, self._margin/2)
1380
1381 startY = self._margin
1382 startLine = (page - 1) * self._pageLines
1383 endLine = min((startLine + self._pageLines), self._maxLines)
1384 for i in range(startLine, endLine):
1385 text = txtCtrl.GetLine(i).rstrip()
1386 startY += self._fontHeight
1387 if txtCtrl.GetViewLineNumbers():
1388 dc.DrawText(self._printFormat % (i+1, text), self._margin, startY)
1389 else:
1390 dc.DrawText(text, self._margin, startY)
1391
1392 dc.EndDrawing()
1393
1394 return True
1395
1396
1397 def HasPage(self, pageNum):
1398 return pageNum <= self._numPages
1399
1400
1401 def GetPageInfo(self):
1402 minPage = 1
1403 maxPage = self._numPages
1404 selPageFrom = 1
1405 selPageTo = self._numPages
1406 return (minPage, maxPage, selPageFrom, selPageTo)
1407
1408
1409 #----------------------------------------------------------------------------
1410 # Icon Bitmaps - generated by encode_bitmaps.py
1411 #----------------------------------------------------------------------------
1412 from wx import ImageFromStream, BitmapFromImage
1413 import cStringIO
1414
1415
1416 def getTextData():
1417 return \
1418 "\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
1419 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
1420 \x00\x015IDAT8\x8d\xad\x90\xb1N\xc2P\x14\x86\xbf\x02/\xe0\xec#\x18g\xc3\xe6T\
1421 \x13':1\x18H\x98\x14\x12\x17G\x177\x17\x9c4a\xc5\xc0d0\xc2\xccdLx\x02^@+\t\
1422 \xc1\x90\xf6r\xdb\xc6\x94\xe5:\\\xdbP)\xc5DOr\x92\x9b{\xff\xfb\xfd\xff9\xc6h\
1423 l+\xbek.\x02\x00\xec\x99\x03\x80\xeb\xf8\\\x9d\x1d\x1bd\xd5hl\xab\xd7O\x15\
1424 \xf7x\xa1\xfb\xeeq\xa4^>\x94\xba\xb8yRF.\xcf\xa6.D\xa0Nw\x18C\xad\xb2\x19\
1425 \x9f\x0f\xca\x165\xd1V\xed\xebZj\x92\xc2\\\x04\xec\x02\xd5\x8a\x89\xb7\xd4\
1426 \x97n\xa8\xe3?\x0f\x86\x08\x19dNP\x00\xf0\x96\xd0\x7f\xd0\t\x84\x0c(U-\x0eK&\
1427 \xd3P\x8bz\xcdV6 \x8a\xed\x86\x99f\xe9\x00{\xe6\xb0\x13\xc2\xa0\xd3\xd7\t\
1428 \x84\x9f\x10\xec\x9dTp\x1d\xb1=A\xa9j\x01\xc4\xb1\x01&\xfe\x9a~\x1d\xe0:Zu\
1429 \x7f\xdb\x05@J/!(\xd6\x1bL\xde\xec\xcd\x00!\x03\xa6!\x1c\x9dVR\x9d\xdf\xe5\
1430 \x96\x04\xd1au\xd3\xab3\xef\x9f_f\x03\xa2\xa5\x15\xeb\x8d\xc4\xc36\xe7\x18 \
1431 \xa5G\xaf\xd9J\xb8f\xcd\xfc\xb3\x0c#\x97\xff\xb58\xadr\x7f\xfa\xfd\x1f\x80/\
1432 \x04\x1f\x8fW\x0e^\xc3\x12\x00\x00\x00\x00IEND\xaeB`\x82"
1433
1434
1435 def getTextBitmap():
1436 return BitmapFromImage(getTextImage())
1437
1438 def getTextImage():
1439 stream = cStringIO.StringIO(getTextData())
1440 return ImageFromStream(stream)
1441
1442 def getTextIcon():
1443 return wx.IconFromBitmap(getTextBitmap())
1444
1445
1446 #----------------------------------------------------------------------------
1447 # Menu Bitmaps - generated by encode_bitmaps.py
1448 #----------------------------------------------------------------------------
1449 #----------------------------------------------------------------------
1450 def getZoomInData():
1451 return \
1452 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
1453 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
1454 \x00\x01TIDAT8\x8d\x8d\x93\xbbJ\x03A\x14\x86\xbf\xd9,\xc6\xd8E%`)VF[{\xc1v\
1455 \xf1\x82\x8f\xb0\xb94\xda\xa5\x13\x11\x8b`\xa9h\x10F\xe3#H.\xa6\x15\xccKhg\
1456 \x10\xc1B\x8bTF\x90\xc0X\x8c3\xbb\xd9\xcdF\x7f\x18\xf6\xec\x9cs\xbe\xfd\xe70\
1457 +\x84\x93"\xacb\xc1W\xe1\xf7\xeb\xfa\x8d`\x82\xdcXcI\x8e\x02AM\x02\t\xe1\xa4\
1458 (\x16|uz)y\x19\xc0\xc9\xdd;\x99\xee!\x00\xd9\xbd\x00\xd6\xaf\x95\xc7B\xac\
1459 \x03\xd3\x1c\xd6\xc2t\x10\xf7\x13\x8e\xe0\x14\x0b\xbe\xa2$m\xf3\xca\xea\xacM\
1460 \xe6\xd2\xc1\xcaWdl>#\x0e\x8c\xed\xe7n\x90|\xa8\x96m\xbc~ y\x04Z\xcd\x86\xda\
1461 \xda\xde\xb1Gq\x00\xb2S\t\xfeB\x9aK\xa8\xb1\x0e\xf2\x15I.\xad\x0bo\x8f\xf4\
1462 \x97\xab\xe7z\x88\x1f\xdf\xf0\xfa9\x1e\xe0x\x9eG\xbf\x16X\xcd\xb8Ar\xc6\xd5\
1463 \x0b4\xd4\xf3\xbcd\x07F_\xc3 \x1e\x0c\xa3Y\x08\x9f\x1f~\xefA\xab\xd9P\x9dN\
1464 \x07\x80\xddcI\xc6\x85\xf9\xb4.8\xabhwK\xbd+6\x16\xf5\xdeZ=%F\x00\xa0\xa7\
1465 \x0b`@F\xc6\xf6\xd3\xc5&@\x0c"\xa2\xff\x82\x01\x85-\xb7\x9a\re\x00QH\x0c0N\
1466 \x06\x1a\x85\xbcym}\x0f\xfe\x92\x19\xdc\xf2~\xdb\xee\xdd\xf7\xf4\xf3_\x0e\
1467 \xa2N\xc2\xfa\x01MYp\xbc\xe4a\x0f\xa9\x00\x00\x00\x00IEND\xaeB`\x82'
1468
1469 def getZoomInBitmap():
1470 return BitmapFromImage(getZoomInImage())
1471
1472 def getZoomInImage():
1473 stream = cStringIO.StringIO(getZoomInData())
1474 return ImageFromStream(stream)
1475
1476 #----------------------------------------------------------------------
1477 def getZoomOutData():
1478 return \
1479 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
1480 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
1481 \x00\x01RIDAT8\x8d\x8d\x93\xbbJ\x03A\x14\x86\xbf\xd9\x04\x93\x90J\x0cj#Dl\
1482 \xf4\x01\xec\x05\xdb\xc5\x0b>B\x92]\x1b+\xed,D\xb0\xb4\x08\x9afc|\x04\xc9\
1483 \x85\xb4>\x84\x95`\x93\x80`\x15\xd8*\x98\x84\xc0X\xcc\xce\xde7\xf8\xc30\x97=\
1484 \xf3\xcd\x7f\xce\xcc\na\xe4\x08\xabQ\xaf\xc9\xf0\xfc\xa5\xf3*X\xa1|b\xa3\xe5\
1485 D\x81 W\x81\x840r4\xea5\xf9\xf0\xe40Y@\xf3+\xf8\xb8\xbe\x16\x8c\xdd\x96\x9d\
1486 \n1\xf4\xc0\xdf\xdc\xb6\x01\xa8\xca\x19[\x05\xfc\x96%aY\x96\x0c\xdb\xae\xca\
1487 \x99\xea7\x8b\x91@w.\xf9x\xbcL\xb8\xf0k\xa0O\x1e{\xd31Q\x1d\xdd\xaaC\xfa\xbd\
1488 \xae<=;\xf7!F<\xd7,md\xc4\xf8\x0e\xf6\xaf\x1d\xb6\x8b*p\xa7\x0c\x95\xd0\x86\
1489 \xc9\x02\xbe\xa7\xe9\x00\xc34M\xdc\x96MA\xa8[,y\xc8r>h\x00ow6\xa6if;\x98K\
1490 \x95\xd6\xef\x12(\xc0t\x99~b8\x7f\xf0\xdeA\xbf\xd7\x95\xc3\xe1\x10\x80\x8b{\
1491 \x87R\x1e*\xde\xd55oTq\xf7Fm\x8ew\xd5\xdaa\'\'"\x00P\xd5\x05\xd0 -m\xfb\xf3\
1492 \xf9\x04 \x01\x11\xf1\x7fA\x83\xc2\x96\xfb\xbd\xae\xd4\x808$\x01H\x93\x86\
1493 \xc6!?\xe6 x\xca\xab\xa4\x0bwp5\xf0\xd7\xdeG\xaa\xff\x97\x83\xb8\x93\xb0\xfe\
1494 \x00\xc3\xa8ov\xfd\xe4\x9c\xa2\x00\x00\x00\x00IEND\xaeB`\x82'
1495
1496
1497 def getZoomOutBitmap():
1498 return BitmapFromImage(getZoomOutImage())
1499
1500 def getZoomOutImage():
1501 stream = cStringIO.StringIO(getZoomOutData())
1502 return ImageFromStream(stream)
1503
1504
1505