]>
Commit | Line | Data |
---|---|---|
1f780e48 RD |
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 | ||
26ee3a06 | 50 | def SaveObject(self, fileObject): |
1f780e48 | 51 | view = self.GetFirstView() |
26ee3a06 | 52 | fileObject.write(view.GetValue()) |
1f780e48 | 53 | return True |
26ee3a06 | 54 | |
1f780e48 | 55 | |
26ee3a06 | 56 | def LoadObject(self, fileObject): |
1f780e48 | 57 | view = self.GetFirstView() |
26ee3a06 | 58 | data = fileObject.read() |
1f780e48 | 59 | view.SetValue(data) |
1f780e48 RD |
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 | ||
1f780e48 | 82 | |
02b800ce RD |
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 | ||
1f780e48 RD |
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 | |
26ee3a06 | 103 | self._dynSash = None |
1f780e48 RD |
104 | |
105 | ||
106 | def GetCtrlClass(self): | |
26ee3a06 | 107 | """ Used in split window to instantiate new instances """ |
1f780e48 | 108 | return TextCtrl |
26ee3a06 | 109 | |
1f780e48 RD |
110 | |
111 | def GetCtrl(self): | |
02b800ce RD |
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) | |
1f780e48 | 118 | return self._textEditor |
1f780e48 | 119 | |
26ee3a06 RD |
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()) | |
1f780e48 RD |
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) | |
02b800ce RD |
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) | |
1f780e48 RD |
145 | self._CreateSizer(frame) |
146 | self.Activate() | |
147 | frame.Show(True) | |
148 | frame.Layout() | |
149 | return True | |
150 | ||
151 | ||
1f780e48 RD |
152 | def _CreateSizer(self, frame): |
153 | sizer = wx.BoxSizer(wx.HORIZONTAL) | |
26ee3a06 | 154 | sizer.Add(self._dynSash, 1, wx.EXPAND) |
1f780e48 | 155 | frame.SetSizer(sizer) |
02b800ce RD |
156 | |
157 | ||
158 | def OnLeftClick(self, event): | |
159 | self.Activate() | |
160 | event.Skip() | |
1f780e48 RD |
161 | |
162 | ||
163 | def OnUpdate(self, sender = None, hint = None): | |
164 | if hint == "ViewStuff": | |
165 | self.GetCtrl().SetViewDefaults() | |
166 | elif hint == "Font": | |
02b800ce | 167 | font, color = self.GetCtrl().GetFontAndColorFromConfig() |
1f780e48 RD |
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: | |
6f1a3f9c | 176 | self.SetFocus() |
1f780e48 | 177 | else: |
6f1a3f9c RD |
178 | wx.CallAfter(self.SetFocus) |
179 | ||
180 | ||
181 | def SetFocus(self): | |
182 | if self.GetCtrl(): | |
183 | self.GetCtrl().SetFocus() | |
184 | ||
1f780e48 RD |
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: | |
02b800ce | 225 | self.GetCtrl().SetIndentationGuides(not self.GetCtrl().GetIndentationGuides()) |
1f780e48 RD |
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 | ||
1f780e48 RD |
280 | id = event.GetId() |
281 | if id == wx.ID_UNDO: | |
26ee3a06 RD |
282 | event.Enable(self.GetCtrl().CanUndo()) |
283 | event.SetText(_("&Undo\tCtrl+Z")) # replace menu string | |
284 | return True | |
1f780e48 RD |
285 | elif id == wx.ID_REDO: |
286 | event.Enable(self.GetCtrl().CanRedo()) | |
26ee3a06 | 287 | event.SetText(_("&Redo\tCtrl+Y")) # replace menu string |
1f780e48 | 288 | return True |
b792147d RD |
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() | |
1f780e48 RD |
293 | event.Enable(hasSelection) |
294 | return True | |
295 | elif id == wx.ID_PASTE: | |
296 | event.Enable(self.GetCtrl().CanPaste()) | |
297 | return True | |
1f780e48 | 298 | elif id == wx.ID_SELECTALL: |
b792147d | 299 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
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: | |
b792147d | 306 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
307 | event.Enable(hasText) |
308 | event.Check(self.GetCtrl().GetViewWhiteSpace()) | |
309 | return True | |
310 | elif id == VIEW_EOL_ID: | |
b792147d | 311 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
312 | event.Enable(hasText) |
313 | event.Check(self.GetCtrl().GetViewEOL()) | |
314 | return True | |
315 | elif id == VIEW_INDENTATION_GUIDES_ID: | |
b792147d | 316 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
317 | event.Enable(hasText) |
318 | event.Check(self.GetCtrl().GetIndentationGuides()) | |
319 | return True | |
320 | elif id == VIEW_RIGHT_EDGE_ID: | |
b792147d | 321 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
322 | event.Enable(hasText) |
323 | event.Check(self.GetCtrl().GetViewRightEdge()) | |
324 | return True | |
325 | elif id == VIEW_LINE_NUMBERS_ID: | |
b792147d | 326 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
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: | |
b792147d | 350 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
351 | event.Enable(hasText) |
352 | return True | |
353 | elif id == FindService.FindService.FIND_PREVIOUS_ID: | |
b792147d | 354 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
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: | |
b792147d | 360 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
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: | |
b792147d | 366 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
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 | ||
02b800ce RD |
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 | ||
1f780e48 RD |
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) | |
02b800ce | 454 | fontDialog.CenterOnParent() |
1f780e48 RD |
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 | ||
26ee3a06 | 592 | |
1f780e48 RD |
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 | ||
26ee3a06 | 608 | |
1f780e48 RD |
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 | ||
02b800ce RD |
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 | ||
1f780e48 RD |
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() | |
b792147d RD |
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): | |
1f780e48 RD |
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) | |
02b800ce | 867 | textFontSizer.Add(self._sampleTextCtrl, 1, wx.ALIGN_LEFT | wx.EXPAND | wx.RIGHT, HALF_SPACE) |
1f780e48 | 868 | textFontSizer.Add(chooseFontButton, 0, wx.ALIGN_RIGHT | wx.LEFT, HALF_SPACE) |
02b800ce | 869 | textPanelSizer.Add(textFontSizer, 0, wx.ALL|wx.EXPAND, HALF_SPACE) |
1f780e48 RD |
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) | |
02b800ce | 883 | textPanelBorderSizer.Add(textPanelSizer, 0, wx.ALL|wx.EXPAND, SPACE) |
1f780e48 RD |
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) | |
02b800ce | 916 | ## dlg.CenterOnParent() |
1f780e48 RD |
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) | |
02b800ce | 929 | fontDialog.CenterOnParent() |
1f780e48 RD |
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") | |
02b800ce RD |
971 | |
972 | ||
973 | def GetIcon(self): | |
974 | return getTextIcon() | |
1f780e48 RD |
975 | |
976 | ||
977 | class TextCtrl(wx.stc.StyledTextCtrl): | |
978 | ||
26ee3a06 RD |
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) | |
1f780e48 RD |
987 | |
988 | self._font = None | |
989 | self._fontColor = None | |
990 | ||
bbf7159c | 991 | self.SetVisiblePolicy(wx.stc.STC_VISIBLE_STRICT,1) |
1f780e48 RD |
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) | |
26ee3a06 RD |
999 | wx.EVT_KILL_FOCUS(self, self.OnKillFocus) |
1000 | wx.EVT_SET_FOCUS(self, self.OnFocus) | |
1f780e48 RD |
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 | ||
02b800ce RD |
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 | ||
26ee3a06 RD |
1037 | |
1038 | def OnFocus(self, event): | |
02b800ce RD |
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 | ||
26ee3a06 RD |
1045 | self.SetSelBackground(1, "BLUE") |
1046 | self.SetSelForeground(1, "WHITE") | |
1047 | if hasattr(self, "_dynSash"): | |
1048 | self._dynSash._view.SetCtrl(self) | |
1049 | event.Skip() | |
1f780e48 RD |
1050 | |
1051 | ||
26ee3a06 | 1052 | def OnKillFocus(self, event): |
02b800ce RD |
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 | |
26ee3a06 RD |
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() | |
1f780e48 | 1063 | |
26ee3a06 | 1064 | |
1f780e48 RD |
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 | ||
1f780e48 RD |
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 | ||
26ee3a06 | 1115 | |
1f780e48 RD |
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 | ||
26ee3a06 RD |
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 | ||
1f780e48 RD |
1408 | |
1409 | #---------------------------------------------------------------------------- | |
1410 | # Icon Bitmaps - generated by encode_bitmaps.py | |
1411 | #---------------------------------------------------------------------------- | |
1412 | from wx import ImageFromStream, BitmapFromImage | |
1f780e48 RD |
1413 | import cStringIO |
1414 | ||
1415 | ||
1416 | def getTextData(): | |
1417 | return \ | |
02b800ce | 1418 | "\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ |
1f780e48 | 1419 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ |
02b800ce RD |
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" | |
1f780e48 RD |
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(): | |
bbf7159c | 1443 | return wx.IconFromBitmap(getTextBitmap()) |
1f780e48 RD |
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\ | |
02b800ce RD |
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' | |
1f780e48 RD |
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\ | |
02b800ce RD |
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' | |
1f780e48 RD |
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 |