]>
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 | ||
50 | def OnSaveDocument(self, filename): | |
51 | view = self.GetFirstView() | |
52 | docFile = file(self._documentFile, "w") | |
53 | docFile.write(view.GetValue()) | |
54 | docFile.close() | |
55 | self.Modify(False) | |
bbf7159c | 56 | self.SetDocumentModificationDate() |
1f780e48 RD |
57 | self.SetDocumentSaved(True) |
58 | return True | |
59 | ||
60 | ||
61 | def OnOpenDocument(self, filename): | |
62 | view = self.GetFirstView() | |
63 | docFile = file(self._documentFile, 'r') | |
64 | data = docFile.read() | |
65 | view.SetValue(data) | |
66 | self.SetFilename(filename, True) | |
67 | self.Modify(False) | |
bbf7159c | 68 | self.SetDocumentModificationDate() |
1f780e48 RD |
69 | self.UpdateAllViews() |
70 | self._savedYet = True | |
71 | return True | |
72 | ||
73 | ||
74 | def IsModified(self): | |
75 | view = self.GetFirstView() | |
76 | if view: | |
77 | return wx.lib.docview.Document.IsModified(self) or view.IsModified() | |
78 | else: | |
79 | return wx.lib.docview.Document.IsModified(self) | |
80 | ||
81 | ||
82 | def Modify(self, mod): | |
83 | view = self.GetFirstView() | |
84 | wx.lib.docview.Document.Modify(self, mod) | |
85 | if not mod and view: | |
86 | view.SetModifyFalse() | |
87 | ||
88 | ||
89 | def OnCreateCommandProcessor(self): | |
90 | # Don't create a command processor, it has its own | |
91 | pass | |
92 | ||
93 | # Use this to override MultiClient.Select to prevent yellow background. | |
94 | def MultiClientSelectBGNotYellow(a): | |
95 | a.GetParent().multiView.UnSelect() | |
96 | a.selected = True | |
97 | #a.SetBackgroundColour(wx.Colour(255,255,0)) # Yellow | |
98 | a.Refresh() | |
99 | ||
100 | class TextView(wx.lib.docview.View): | |
101 | MARKER_NUM = 0 | |
102 | MARKER_MASK = 0x1 | |
103 | ||
104 | #---------------------------------------------------------------------------- | |
105 | # Overridden methods | |
106 | #---------------------------------------------------------------------------- | |
107 | ||
108 | def __init__(self): | |
109 | wx.lib.docview.View.__init__(self) | |
110 | self._textEditor = None | |
111 | self._markerCount = 0 | |
112 | self._commandProcessor = None | |
113 | self._multiSash = None | |
114 | ||
115 | ||
116 | def GetCtrlClass(self): | |
117 | return TextCtrl | |
118 | ||
119 | ||
120 | def GetCtrl(self): | |
121 | # look for active one first | |
122 | self._textEditor = self._GetActiveCtrl(self._multiSash) | |
123 | if self._textEditor == None: # it is possible none are active | |
124 | # look for any existing one | |
125 | self._textEditor = self._FindCtrl(self._multiSash) | |
126 | return self._textEditor | |
127 | ||
128 | ||
129 | ## def GetCtrls(self, parent = None): | |
130 | ## """ Walk through the MultiSash windows and find all Ctrls """ | |
131 | ## controls = [] | |
132 | ## if isinstance(parent, self.GetCtrlClass()): | |
133 | ## return [parent] | |
134 | ## if hasattr(parent, "GetChildren"): | |
135 | ## for child in parent.GetChildren(): | |
136 | ## controls = controls + self.GetCtrls(child) | |
137 | ## return controls | |
138 | ||
139 | ||
140 | def OnCreate(self, doc, flags): | |
141 | frame = wx.GetApp().CreateDocumentFrame(self, doc, flags, style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE) | |
142 | wx.lib.multisash.MultiClient.Select = MultiClientSelectBGNotYellow | |
143 | self._multiSash = wx.lib.multisash.MultiSash(frame, -1) | |
144 | self._multiSash.SetDefaultChildClass(self.GetCtrlClass()) # wxBug: MultiSash instantiates the first TextCtrl with this call | |
145 | self._textEditor = self.GetCtrl() # wxBug: grab the TextCtrl from the MultiSash datastructure | |
146 | self._CreateSizer(frame) | |
147 | self.Activate() | |
148 | frame.Show(True) | |
149 | frame.Layout() | |
150 | return True | |
151 | ||
152 | ||
153 | def _GetActiveCtrl(self, parent): | |
154 | """ Walk through the MultiSash windows and find the active Control """ | |
155 | if isinstance(parent, wx.lib.multisash.MultiClient) and parent.selected: | |
156 | return parent.child | |
157 | if hasattr(parent, "GetChildren"): | |
158 | for child in parent.GetChildren(): | |
159 | found = self._GetActiveCtrl(child) | |
160 | if found: | |
161 | return found | |
162 | return None | |
163 | ||
164 | ||
165 | def _FindCtrl(self, parent): | |
166 | """ Walk through the MultiSash windows and find the first TextCtrl """ | |
167 | if isinstance(parent, self.GetCtrlClass()): | |
168 | return parent | |
169 | if hasattr(parent, "GetChildren"): | |
170 | for child in parent.GetChildren(): | |
171 | found = self._FindCtrl(child) | |
172 | if found: | |
173 | return found | |
174 | return None | |
175 | ||
176 | ||
177 | def _CreateSizer(self, frame): | |
178 | sizer = wx.BoxSizer(wx.HORIZONTAL) | |
179 | sizer.Add(self._multiSash, 1, wx.EXPAND) | |
180 | frame.SetSizer(sizer) | |
181 | frame.SetAutoLayout(True) | |
182 | ||
183 | ||
184 | def OnUpdate(self, sender = None, hint = None): | |
185 | if hint == "ViewStuff": | |
186 | self.GetCtrl().SetViewDefaults() | |
187 | elif hint == "Font": | |
188 | font, color = self.GetFontAndColorFromConfig() | |
189 | self.GetCtrl().SetFont(font) | |
190 | self.GetCtrl().SetFontColor(color) | |
191 | ||
192 | ||
193 | def OnActivateView(self, activate, activeView, deactiveView): | |
194 | if activate and self.GetCtrl(): | |
195 | # In MDI mode just calling set focus doesn't work and in SDI mode using CallAfter causes an endless loop | |
196 | if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI: | |
6f1a3f9c | 197 | self.SetFocus() |
1f780e48 | 198 | else: |
6f1a3f9c RD |
199 | wx.CallAfter(self.SetFocus) |
200 | ||
201 | ||
202 | def SetFocus(self): | |
203 | if self.GetCtrl(): | |
204 | self.GetCtrl().SetFocus() | |
205 | ||
1f780e48 RD |
206 | |
207 | def OnClose(self, deleteWindow = True): | |
208 | if not wx.lib.docview.View.OnClose(self, deleteWindow): | |
209 | return False | |
210 | self.Activate(False) | |
211 | if deleteWindow and self.GetFrame(): | |
212 | self.GetFrame().Destroy() | |
213 | return True | |
214 | ||
215 | ||
216 | def ProcessEvent(self, event): | |
217 | id = event.GetId() | |
218 | if id == wx.ID_UNDO: | |
219 | self.GetCtrl().Undo() | |
220 | return True | |
221 | elif id == wx.ID_REDO: | |
222 | self.GetCtrl().Redo() | |
223 | return True | |
224 | elif id == wx.ID_CUT: | |
225 | self.GetCtrl().Cut() | |
226 | return True | |
227 | elif id == wx.ID_COPY: | |
228 | self.GetCtrl().Copy() | |
229 | return True | |
230 | elif id == wx.ID_PASTE: | |
231 | self.GetCtrl().OnPaste() | |
232 | return True | |
233 | elif id == wx.ID_CLEAR: | |
234 | self.GetCtrl().OnClear() | |
235 | return True | |
236 | elif id == wx.ID_SELECTALL: | |
237 | self.GetCtrl().SetSelection(0, -1) | |
238 | return True | |
239 | elif id == VIEW_WHITESPACE_ID: | |
240 | self.GetCtrl().SetViewWhiteSpace(not self.GetCtrl().GetViewWhiteSpace()) | |
241 | return True | |
242 | elif id == VIEW_EOL_ID: | |
243 | self.GetCtrl().SetViewEOL(not self.GetCtrl().GetViewEOL()) | |
244 | return True | |
245 | elif id == VIEW_INDENTATION_GUIDES_ID: | |
246 | self.GetCtrl().SetViewIndentationGuides(not self.GetCtrl().GetViewIndentationGuides()) | |
247 | return True | |
248 | elif id == VIEW_RIGHT_EDGE_ID: | |
249 | self.GetCtrl().SetViewRightEdge(not self.GetCtrl().GetViewRightEdge()) | |
250 | return True | |
251 | elif id == VIEW_LINE_NUMBERS_ID: | |
252 | self.GetCtrl().SetViewLineNumbers(not self.GetCtrl().GetViewLineNumbers()) | |
253 | return True | |
254 | elif id == ZOOM_NORMAL_ID: | |
255 | self.GetCtrl().SetZoom(0) | |
256 | return True | |
257 | elif id == ZOOM_IN_ID: | |
258 | self.GetCtrl().CmdKeyExecute(wx.stc.STC_CMD_ZOOMIN) | |
259 | return True | |
260 | elif id == ZOOM_OUT_ID: | |
261 | self.GetCtrl().CmdKeyExecute(wx.stc.STC_CMD_ZOOMOUT) | |
262 | return True | |
263 | elif id == CHOOSE_FONT_ID: | |
264 | self.OnChooseFont() | |
265 | return True | |
266 | elif id == WORD_WRAP_ID: | |
267 | self.GetCtrl().SetWordWrap(not self.GetCtrl().GetWordWrap()) | |
268 | return True | |
269 | elif id == FindService.FindService.FIND_ID: | |
270 | self.OnFind() | |
271 | return True | |
272 | elif id == FindService.FindService.FIND_PREVIOUS_ID: | |
273 | self.DoFind(forceFindPrevious = True) | |
274 | return True | |
275 | elif id == FindService.FindService.FIND_NEXT_ID: | |
276 | self.DoFind(forceFindNext = True) | |
277 | return True | |
278 | elif id == FindService.FindService.REPLACE_ID: | |
279 | self.OnFind(replace = True) | |
280 | return True | |
281 | elif id == FindService.FindService.FINDONE_ID: | |
282 | self.DoFind() | |
283 | return True | |
284 | elif id == FindService.FindService.REPLACEONE_ID: | |
285 | self.DoFind(replace = True) | |
286 | return True | |
287 | elif id == FindService.FindService.REPLACEALL_ID: | |
288 | self.DoFind(replaceAll = True) | |
289 | return True | |
290 | elif id == FindService.FindService.GOTO_LINE_ID: | |
291 | self.OnGotoLine(event) | |
292 | return True | |
293 | else: | |
294 | return wx.lib.docview.View.ProcessEvent(self, event) | |
295 | ||
296 | ||
297 | def ProcessUpdateUIEvent(self, event): | |
298 | if not self.GetCtrl(): | |
299 | return False | |
300 | ||
1f780e48 RD |
301 | id = event.GetId() |
302 | if id == wx.ID_UNDO: | |
303 | event.Enable(self.GetCtrl().CanUndo()) | |
304 | event.SetText(_("Undo") + '\t' + _('Ctrl+Z')) | |
305 | return True | |
306 | elif id == wx.ID_REDO: | |
307 | event.Enable(self.GetCtrl().CanRedo()) | |
308 | event.SetText(_("Redo") + '\t' + _('Ctrl+Y')) | |
309 | return True | |
b792147d RD |
310 | elif (id == wx.ID_CUT |
311 | or id == wx.ID_COPY | |
312 | or id == wx.ID_CLEAR): | |
313 | hasSelection = self.GetCtrl().GetSelectionStart() != self.GetCtrl().GetSelectionEnd() | |
1f780e48 RD |
314 | event.Enable(hasSelection) |
315 | return True | |
316 | elif id == wx.ID_PASTE: | |
317 | event.Enable(self.GetCtrl().CanPaste()) | |
318 | return True | |
1f780e48 | 319 | elif id == wx.ID_SELECTALL: |
b792147d | 320 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
321 | event.Enable(hasText) |
322 | return True | |
323 | elif id == TEXT_ID: | |
324 | event.Enable(True) | |
325 | return True | |
326 | elif id == VIEW_WHITESPACE_ID: | |
b792147d | 327 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
328 | event.Enable(hasText) |
329 | event.Check(self.GetCtrl().GetViewWhiteSpace()) | |
330 | return True | |
331 | elif id == VIEW_EOL_ID: | |
b792147d | 332 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
333 | event.Enable(hasText) |
334 | event.Check(self.GetCtrl().GetViewEOL()) | |
335 | return True | |
336 | elif id == VIEW_INDENTATION_GUIDES_ID: | |
b792147d | 337 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
338 | event.Enable(hasText) |
339 | event.Check(self.GetCtrl().GetIndentationGuides()) | |
340 | return True | |
341 | elif id == VIEW_RIGHT_EDGE_ID: | |
b792147d | 342 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
343 | event.Enable(hasText) |
344 | event.Check(self.GetCtrl().GetViewRightEdge()) | |
345 | return True | |
346 | elif id == VIEW_LINE_NUMBERS_ID: | |
b792147d | 347 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
348 | event.Enable(hasText) |
349 | event.Check(self.GetCtrl().GetViewLineNumbers()) | |
350 | return True | |
351 | elif id == ZOOM_ID: | |
352 | event.Enable(True) | |
353 | return True | |
354 | elif id == ZOOM_NORMAL_ID: | |
355 | event.Enable(self.GetCtrl().GetZoom() != 0) | |
356 | return True | |
357 | elif id == ZOOM_IN_ID: | |
358 | event.Enable(self.GetCtrl().GetZoom() < 20) | |
359 | return True | |
360 | elif id == ZOOM_OUT_ID: | |
361 | event.Enable(self.GetCtrl().GetZoom() > -10) | |
362 | return True | |
363 | elif id == CHOOSE_FONT_ID: | |
364 | event.Enable(True) | |
365 | return True | |
366 | elif id == WORD_WRAP_ID: | |
367 | event.Enable(self.GetCtrl().CanWordWrap()) | |
368 | event.Check(self.GetCtrl().CanWordWrap() and self.GetCtrl().GetWordWrap()) | |
369 | return True | |
370 | elif id == FindService.FindService.FIND_ID: | |
b792147d | 371 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
372 | event.Enable(hasText) |
373 | return True | |
374 | elif id == FindService.FindService.FIND_PREVIOUS_ID: | |
b792147d | 375 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
376 | event.Enable(hasText and |
377 | self._FindServiceHasString() and | |
378 | self.GetCtrl().GetSelection()[0] > 0) | |
379 | return True | |
380 | elif id == FindService.FindService.FIND_NEXT_ID: | |
b792147d | 381 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
382 | event.Enable(hasText and |
383 | self._FindServiceHasString() and | |
384 | self.GetCtrl().GetSelection()[0] < self.GetCtrl().GetLength()) | |
385 | return True | |
386 | elif id == FindService.FindService.REPLACE_ID: | |
b792147d | 387 | hasText = self.GetCtrl().GetTextLength() > 0 |
1f780e48 RD |
388 | event.Enable(hasText) |
389 | return True | |
390 | elif id == FindService.FindService.GOTO_LINE_ID: | |
391 | event.Enable(True) | |
392 | return True | |
393 | elif id == TEXT_STATUS_BAR_ID: | |
394 | self.OnUpdateStatusBar(event) | |
395 | return True | |
396 | else: | |
397 | return wx.lib.docview.View.ProcessUpdateUIEvent(self, event) | |
398 | ||
399 | ||
400 | def _GetParentFrame(self): | |
401 | return wx.GetTopLevelParent(self.GetFrame()) | |
402 | ||
403 | ||
404 | #---------------------------------------------------------------------------- | |
405 | # Methods for TextDocument to call | |
406 | #---------------------------------------------------------------------------- | |
407 | ||
408 | def IsModified(self): | |
409 | if not self.GetCtrl(): | |
410 | return False | |
411 | return self.GetCtrl().GetModify() | |
412 | ||
413 | ||
414 | def SetModifyFalse(self): | |
415 | self.GetCtrl().SetSavePoint() | |
416 | ||
417 | ||
418 | def GetValue(self): | |
419 | if self.GetCtrl(): | |
420 | return self.GetCtrl().GetText() | |
421 | else: | |
422 | return None | |
423 | ||
424 | ||
425 | def SetValue(self, value): | |
426 | self.GetCtrl().SetText(value) | |
427 | self.GetCtrl().UpdateLineNumberMarginWidth() | |
428 | self.GetCtrl().EmptyUndoBuffer() | |
429 | ||
430 | ||
431 | #---------------------------------------------------------------------------- | |
432 | # STC events | |
433 | #---------------------------------------------------------------------------- | |
434 | ||
435 | def OnUpdateStatusBar(self, event): | |
436 | statusBar = self._GetParentFrame().GetStatusBar() | |
437 | statusBar.SetInsertMode(self.GetCtrl().GetOvertype() == 0) | |
438 | statusBar.SetLineNumber(self.GetCtrl().GetCurrentLine() + 1) | |
439 | statusBar.SetColumnNumber(self.GetCtrl().GetColumn(self.GetCtrl().GetCurrentPos()) + 1) | |
440 | ||
441 | ||
442 | #---------------------------------------------------------------------------- | |
443 | # Format methods | |
444 | #---------------------------------------------------------------------------- | |
445 | ||
446 | def OnChooseFont(self): | |
447 | data = wx.FontData() | |
448 | data.EnableEffects(True) | |
449 | data.SetInitialFont(self.GetCtrl().GetFont()) | |
450 | data.SetColour(self.GetCtrl().GetFontColor()) | |
451 | fontDialog = wx.FontDialog(self.GetFrame(), data) | |
452 | if fontDialog.ShowModal() == wx.ID_OK: | |
453 | data = fontDialog.GetFontData() | |
454 | self.GetCtrl().SetFont(data.GetChosenFont()) | |
455 | self.GetCtrl().SetFontColor(data.GetColour()) | |
456 | self.GetCtrl().UpdateStyles() | |
457 | fontDialog.Destroy() | |
458 | ||
459 | ||
460 | #---------------------------------------------------------------------------- | |
461 | # Find methods | |
462 | #---------------------------------------------------------------------------- | |
463 | ||
464 | def OnFind(self, replace = False): | |
465 | findService = wx.GetApp().GetService(FindService.FindService) | |
466 | if findService: | |
467 | findService.ShowFindReplaceDialog(findString = self.GetCtrl().GetSelectedText(), replace = replace) | |
468 | ||
469 | ||
470 | def DoFind(self, forceFindNext = False, forceFindPrevious = False, replace = False, replaceAll = False): | |
471 | findService = wx.GetApp().GetService(FindService.FindService) | |
472 | if not findService: | |
473 | return | |
474 | findString = findService.GetFindString() | |
475 | if len(findString) == 0: | |
476 | return -1 | |
477 | replaceString = findService.GetReplaceString() | |
478 | flags = findService.GetFlags() | |
479 | startLoc, endLoc = self.GetCtrl().GetSelection() | |
480 | ||
481 | wholeWord = flags & wx.FR_WHOLEWORD > 0 | |
482 | matchCase = flags & wx.FR_MATCHCASE > 0 | |
483 | regExp = flags & FindService.FindService.FR_REGEXP > 0 | |
484 | down = flags & wx.FR_DOWN > 0 | |
485 | wrap = flags & FindService.FindService.FR_WRAP > 0 | |
486 | ||
487 | if forceFindPrevious: # this is from function keys, not dialog box | |
488 | down = False | |
489 | wrap = False # user would want to know they're at the end of file | |
490 | elif forceFindNext: | |
491 | down = True | |
492 | wrap = False # user would want to know they're at the end of file | |
493 | ||
494 | badSyntax = False | |
495 | ||
496 | # On replace dialog operations, user is allowed to replace the currently highlighted text to determine if it should be replaced or not. | |
497 | # 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. | |
498 | # If the text is a match, then replace it. | |
499 | if replace: | |
500 | result, start, end, replText = findService.DoFind(findString, replaceString, self.GetCtrl().GetSelectedText(), 0, 0, True, matchCase, wholeWord, regExp, replace) | |
501 | if result > 0: | |
502 | self.GetCtrl().ReplaceSelection(replText) | |
503 | self.GetDocument().Modify(True) | |
504 | wx.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString) | |
505 | if down: | |
506 | startLoc += len(replText) # advance start location past replacement string to new text | |
507 | endLoc = startLoc | |
508 | elif result == FindService.FIND_SYNTAXERROR: | |
509 | badSyntax = True | |
510 | wx.GetApp().GetTopWindow().PushStatusText(_("Invalid regular expression \"%s\"") % findString) | |
511 | ||
512 | if not badSyntax: | |
513 | text = self.GetCtrl().GetText() | |
514 | ||
515 | # Find the next matching text occurance or if it is a ReplaceAll, replace all occurances | |
516 | # 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 | |
517 | result, start, end, text = findService.DoFind(findString, replaceString, text, startLoc, endLoc, down, matchCase, wholeWord, regExp, False, replaceAll, wrap) | |
518 | if result > 0: | |
519 | self.GetCtrl().SetTargetStart(0) | |
520 | self.GetCtrl().SetTargetEnd(self.GetCtrl().GetLength()) | |
521 | self.GetCtrl().ReplaceTarget(text) # Doing a SetText causes a clear document to be shown when undoing, so using replacetarget instead | |
522 | self.GetDocument().Modify(True) | |
523 | if result == 1: | |
524 | wx.GetApp().GetTopWindow().PushStatusText(_("1 occurrence of \"%s\" replaced") % findString) | |
525 | else: | |
526 | wx.GetApp().GetTopWindow().PushStatusText(_("%i occurrences of \"%s\" replaced") % (result, findString)) | |
527 | elif result == 0: | |
528 | self.GetCtrl().SetSelection(start, end) | |
529 | self.GetCtrl().EnsureVisible(self.GetCtrl().LineFromPosition(end)) # show bottom then scroll up to top | |
530 | self.GetCtrl().EnsureVisible(self.GetCtrl().LineFromPosition(start)) # do this after ensuring bottom is visible | |
531 | wx.GetApp().GetTopWindow().PushStatusText(_("Found \"%s\".") % findString) | |
532 | elif result == FindService.FIND_SYNTAXERROR: | |
533 | # Dialog for this case gets popped up by the FindService. | |
534 | wx.GetApp().GetTopWindow().PushStatusText(_("Invalid regular expression \"%s\"") % findString) | |
535 | else: | |
536 | wx.MessageBox(_("Can't find \"%s\".") % findString, "Find", | |
537 | wx.OK | wx.ICON_INFORMATION) | |
538 | ||
539 | ||
540 | def _FindServiceHasString(self): | |
541 | findService = wx.GetApp().GetService(FindService.FindService) | |
542 | if not findService or not findService.GetFindString(): | |
543 | return False | |
544 | return True | |
545 | ||
546 | ||
547 | def OnGotoLine(self, event): | |
548 | findService = wx.GetApp().GetService(FindService.FindService) | |
549 | if findService: | |
550 | line = findService.GetLineNumber(self.GetDocumentManager().FindSuitableParent()) | |
551 | if line > -1: | |
552 | line = line - 1 | |
553 | self.GetCtrl().EnsureVisible(line) | |
554 | self.GetCtrl().GotoLine(line) | |
555 | ||
556 | ||
557 | def GotoLine(self, lineNum): | |
558 | if lineNum > -1: | |
559 | lineNum = lineNum - 1 # line numbering for editor is 0 based, we are 1 based. | |
560 | self.GetCtrl().EnsureVisibleEnforcePolicy(lineNum) | |
561 | self.GetCtrl().GotoLine(lineNum) | |
562 | ||
563 | ||
564 | def SetSelection(self, start, end): | |
565 | self.GetCtrl().SetSelection(start, end) | |
566 | ||
567 | ||
568 | def EnsureVisible(self, line): | |
569 | self.GetCtrl().EnsureVisible(line-1) # line numbering for editor is 0 based, we are 1 based. | |
570 | ||
571 | def EnsureVisibleEnforcePolicy(self, line): | |
572 | self.GetCtrl().EnsureVisibleEnforcePolicy(line-1) # line numbering for editor is 0 based, we are 1 based. | |
573 | ||
574 | def LineFromPosition(self, pos): | |
575 | return self.GetCtrl().LineFromPosition(pos)+1 # line numbering for editor is 0 based, we are 1 based. | |
576 | ||
577 | ||
578 | def PositionFromLine(self, line): | |
579 | return self.GetCtrl().PositionFromLine(line-1) # line numbering for editor is 0 based, we are 1 based. | |
580 | ||
581 | ||
582 | def GetLineEndPosition(self, line): | |
583 | return self.GetCtrl().GetLineEndPosition(line-1) # line numbering for editor is 0 based, we are 1 based. | |
584 | ||
585 | ||
586 | def GetLine(self, lineNum): | |
587 | return self.GetCtrl().GetLine(lineNum-1) # line numbering for editor is 0 based, we are 1 based. | |
588 | ||
589 | def MarkerDefine(self): | |
590 | """ This must be called after the texteditor is instantiated """ | |
591 | self.GetCtrl().MarkerDefine(TextView.MARKER_NUM, wx.stc.STC_MARK_CIRCLE, wx.BLACK, wx.BLUE) | |
592 | ||
593 | ||
594 | def MarkerToggle(self, lineNum = -1, marker_index=MARKER_NUM, mask=MARKER_MASK): | |
595 | if lineNum == -1: | |
596 | lineNum = self.GetCtrl().GetCurrentLine() | |
597 | if self.GetCtrl().MarkerGet(lineNum) & mask: | |
598 | self.GetCtrl().MarkerDelete(lineNum, marker_index) | |
599 | self._markerCount -= 1 | |
600 | else: | |
601 | self.GetCtrl().MarkerAdd(lineNum, marker_index) | |
602 | self._markerCount += 1 | |
603 | ||
604 | def MarkerAdd(self, lineNum = -1, marker_index=MARKER_NUM, mask=MARKER_MASK): | |
605 | if lineNum == -1: | |
606 | lineNum = self.GetCtrl().GetCurrentLine() | |
607 | self.GetCtrl().MarkerAdd(lineNum, marker_index) | |
608 | self._markerCount += 1 | |
609 | ||
610 | ||
611 | def MarkerDelete(self, lineNum = -1, marker_index=MARKER_NUM, mask=MARKER_MASK): | |
612 | if lineNum == -1: | |
613 | lineNum = self.GetCtrl().GetCurrentLine() | |
614 | if self.GetCtrl().MarkerGet(lineNum) & mask: | |
615 | self.GetCtrl().MarkerDelete(lineNum, marker_index) | |
616 | self._markerCount -= 1 | |
617 | ||
618 | def MarkerDeleteAll(self, marker_num=MARKER_NUM): | |
619 | self.GetCtrl().MarkerDeleteAll(marker_num) | |
620 | if marker_num == self.MARKER_NUM: | |
621 | self._markerCount = 0 | |
622 | ||
623 | ||
624 | def MarkerNext(self, lineNum = -1): | |
625 | if lineNum == -1: | |
626 | lineNum = self.GetCtrl().GetCurrentLine() + 1 # start search below current line | |
627 | foundLine = self.GetCtrl().MarkerNext(lineNum, self.MARKER_MASK) | |
628 | if foundLine == -1: | |
629 | # wrap to top of file | |
630 | foundLine = self.GetCtrl().MarkerNext(0, self.MARKER_MASK) | |
631 | if foundLine == -1: | |
632 | wx.GetApp().GetTopWindow().PushStatusText(_("No markers")) | |
633 | return | |
634 | ||
635 | self.GotoLine(foundLine + 1) | |
636 | ||
637 | ||
638 | def MarkerPrevious(self, lineNum = -1): | |
639 | if lineNum == -1: | |
640 | lineNum = self.GetCtrl().GetCurrentLine() - 1 # start search above current line | |
641 | if lineNum == -1: | |
642 | lineNum = self.GetCtrl().GetLineCount() | |
643 | ||
644 | foundLine = self.GetCtrl().MarkerPrevious(lineNum, self.MARKER_MASK) | |
645 | if foundLine == -1: | |
646 | # wrap to bottom of file | |
647 | foundLine = self.GetCtrl().MarkerPrevious(self.GetCtrl().GetLineCount(), self.MARKER_MASK) | |
648 | if foundLine == -1: | |
649 | wx.GetApp().GetTopWindow().PushStatusText(_("No markers")) | |
650 | return | |
651 | ||
652 | self.GotoLine(foundLine + 1) | |
653 | ||
654 | ||
655 | def MarkerExists(self, lineNum = -1, mask=MARKER_MASK): | |
656 | if lineNum == -1: | |
657 | lineNum = self.GetCtrl().GetCurrentLine() | |
658 | if self.GetCtrl().MarkerGet(lineNum) & mask: | |
659 | return True | |
660 | else: | |
661 | return False | |
662 | ||
663 | ||
664 | def GetMarkerCount(self): | |
665 | return self._markerCount | |
666 | ||
667 | ||
668 | class TextService(wx.lib.pydocview.DocService): | |
669 | ||
670 | ||
671 | def __init__(self): | |
672 | wx.lib.pydocview.DocService.__init__(self) | |
673 | ||
674 | ||
675 | def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None): | |
676 | if document and document.GetDocumentTemplate().GetDocumentType() != TextDocument: | |
677 | return | |
678 | if not document and wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI: | |
679 | return | |
680 | ||
681 | statusBar = TextStatusBar(frame, TEXT_STATUS_BAR_ID) | |
682 | frame.SetStatusBar(statusBar) | |
683 | wx.EVT_UPDATE_UI(frame, TEXT_STATUS_BAR_ID, frame.ProcessUpdateUIEvent) | |
684 | ||
685 | viewMenu = menuBar.GetMenu(menuBar.FindMenu(_("&View"))) | |
686 | ||
687 | viewMenu.AppendSeparator() | |
688 | textMenu = wx.Menu() | |
689 | textMenu.AppendCheckItem(VIEW_WHITESPACE_ID, _("&Whitespace"), _("Shows or hides whitespace")) | |
690 | wx.EVT_MENU(frame, VIEW_WHITESPACE_ID, frame.ProcessEvent) | |
691 | wx.EVT_UPDATE_UI(frame, VIEW_WHITESPACE_ID, frame.ProcessUpdateUIEvent) | |
692 | textMenu.AppendCheckItem(VIEW_EOL_ID, _("&End of Line Markers"), _("Shows or hides indicators at the end of each line")) | |
693 | wx.EVT_MENU(frame, VIEW_EOL_ID, frame.ProcessEvent) | |
694 | wx.EVT_UPDATE_UI(frame, VIEW_EOL_ID, frame.ProcessUpdateUIEvent) | |
695 | textMenu.AppendCheckItem(VIEW_INDENTATION_GUIDES_ID, _("&Indentation Guides"), _("Shows or hides indentations")) | |
696 | wx.EVT_MENU(frame, VIEW_INDENTATION_GUIDES_ID, frame.ProcessEvent) | |
697 | wx.EVT_UPDATE_UI(frame, VIEW_INDENTATION_GUIDES_ID, frame.ProcessUpdateUIEvent) | |
698 | textMenu.AppendCheckItem(VIEW_RIGHT_EDGE_ID, _("&Right Edge"), _("Shows or hides the right edge marker")) | |
699 | wx.EVT_MENU(frame, VIEW_RIGHT_EDGE_ID, frame.ProcessEvent) | |
700 | wx.EVT_UPDATE_UI(frame, VIEW_RIGHT_EDGE_ID, frame.ProcessUpdateUIEvent) | |
701 | textMenu.AppendCheckItem(VIEW_LINE_NUMBERS_ID, _("&Line Numbers"), _("Shows or hides the line numbers")) | |
702 | wx.EVT_MENU(frame, VIEW_LINE_NUMBERS_ID, frame.ProcessEvent) | |
703 | wx.EVT_UPDATE_UI(frame, VIEW_LINE_NUMBERS_ID, frame.ProcessUpdateUIEvent) | |
704 | ||
705 | viewMenu.AppendMenu(TEXT_ID, _("&Text"), textMenu) | |
706 | wx.EVT_UPDATE_UI(frame, TEXT_ID, frame.ProcessUpdateUIEvent) | |
707 | ||
708 | isWindows = (wx.Platform == '__WXMSW__') | |
709 | ||
710 | zoomMenu = wx.Menu() | |
711 | zoomMenu.Append(ZOOM_NORMAL_ID, _("Normal Size"), _("Sets the document to its normal size")) | |
712 | wx.EVT_MENU(frame, ZOOM_NORMAL_ID, frame.ProcessEvent) | |
713 | wx.EVT_UPDATE_UI(frame, ZOOM_NORMAL_ID, frame.ProcessUpdateUIEvent) | |
714 | if isWindows: | |
715 | zoomMenu.Append(ZOOM_IN_ID, _("Zoom In\tCtrl+Page Up"), _("Zooms the document to a larger size")) | |
716 | else: | |
717 | zoomMenu.Append(ZOOM_IN_ID, _("Zoom In"), _("Zooms the document to a larger size")) | |
718 | wx.EVT_MENU(frame, ZOOM_IN_ID, frame.ProcessEvent) | |
719 | wx.EVT_UPDATE_UI(frame, ZOOM_IN_ID, frame.ProcessUpdateUIEvent) | |
720 | if isWindows: | |
721 | zoomMenu.Append(ZOOM_OUT_ID, _("Zoom Out\tCtrl+Page Down"), _("Zooms the document to a smaller size")) | |
722 | else: | |
723 | zoomMenu.Append(ZOOM_OUT_ID, _("Zoom Out"), _("Zooms the document to a smaller size")) | |
724 | wx.EVT_MENU(frame, ZOOM_OUT_ID, frame.ProcessEvent) | |
725 | wx.EVT_UPDATE_UI(frame, ZOOM_OUT_ID, frame.ProcessUpdateUIEvent) | |
726 | ||
727 | viewMenu.AppendMenu(ZOOM_ID, _("&Zoom"), zoomMenu) | |
728 | wx.EVT_UPDATE_UI(frame, ZOOM_ID, frame.ProcessUpdateUIEvent) | |
729 | ||
730 | formatMenuIndex = menuBar.FindMenu(_("&Format")) | |
731 | if formatMenuIndex > -1: | |
732 | formatMenu = menuBar.GetMenu(formatMenuIndex) | |
733 | else: | |
734 | formatMenu = wx.Menu() | |
735 | if not menuBar.FindItemById(CHOOSE_FONT_ID): | |
736 | formatMenu.Append(CHOOSE_FONT_ID, _("&Font..."), _("Sets the font to use")) | |
737 | wx.EVT_MENU(frame, CHOOSE_FONT_ID, frame.ProcessEvent) | |
738 | wx.EVT_UPDATE_UI(frame, CHOOSE_FONT_ID, frame.ProcessUpdateUIEvent) | |
739 | if not menuBar.FindItemById(WORD_WRAP_ID): | |
740 | formatMenu.AppendCheckItem(WORD_WRAP_ID, _("Word Wrap"), _("Wraps text horizontally when checked")) | |
741 | wx.EVT_MENU(frame, WORD_WRAP_ID, frame.ProcessEvent) | |
742 | wx.EVT_UPDATE_UI(frame, WORD_WRAP_ID, frame.ProcessUpdateUIEvent) | |
743 | if formatMenuIndex == -1: | |
744 | viewMenuIndex = menuBar.FindMenu(_("&View")) | |
745 | menuBar.Insert(viewMenuIndex + 1, formatMenu, _("&Format")) | |
746 | ||
747 | # wxBug: wxToolBar::GetToolPos doesn't exist, need it to find cut tool and then insert find in front of it. | |
748 | toolBar.AddSeparator() | |
749 | toolBar.AddTool(ZOOM_IN_ID, getZoomInBitmap(), shortHelpString = _("Zoom In"), longHelpString = _("Zooms the document to a larger size")) | |
750 | toolBar.AddTool(ZOOM_OUT_ID, getZoomOutBitmap(), shortHelpString = _("Zoom Out"), longHelpString = _("Zooms the document to a smaller size")) | |
751 | toolBar.Realize() | |
752 | ||
753 | ||
754 | def ProcessUpdateUIEvent(self, event): | |
755 | id = event.GetId() | |
b792147d RD |
756 | if (id == TEXT_ID |
757 | or id == VIEW_WHITESPACE_ID | |
758 | or id == VIEW_EOL_ID | |
759 | or id == VIEW_INDENTATION_GUIDES_ID | |
760 | or id == VIEW_RIGHT_EDGE_ID | |
761 | or id == VIEW_LINE_NUMBERS_ID | |
762 | or id == ZOOM_ID | |
763 | or id == ZOOM_NORMAL_ID | |
764 | or id == ZOOM_IN_ID | |
765 | or id == ZOOM_OUT_ID | |
766 | or id == CHOOSE_FONT_ID | |
767 | or id == WORD_WRAP_ID): | |
1f780e48 RD |
768 | event.Enable(False) |
769 | return True | |
770 | else: | |
771 | return False | |
772 | ||
773 | ||
774 | class TextStatusBar(wx.StatusBar): | |
775 | ||
776 | # 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 | |
777 | ||
778 | def __init__(self, parent, id, style = wx.ST_SIZEGRIP, name = "statusBar"): | |
779 | wx.StatusBar.__init__(self, parent, id, style, name) | |
780 | self.SetFieldsCount(4) | |
781 | self.SetStatusWidths([-1, 50, 50, 55]) | |
782 | ||
783 | def SetInsertMode(self, insert = True): | |
784 | if insert: | |
785 | newText = _("Ins") | |
786 | else: | |
787 | newText = _("") | |
788 | if self.GetStatusText(1) != newText: # wxBug: Need to check if the text has changed, otherwise it flickers under win32 | |
789 | self.SetStatusText(newText, 1) | |
790 | ||
791 | def SetLineNumber(self, lineNumber): | |
792 | newText = _("Ln %i") % lineNumber | |
793 | if self.GetStatusText(2) != newText: | |
794 | self.SetStatusText(newText, 2) | |
795 | ||
796 | def SetColumnNumber(self, colNumber): | |
797 | newText = _("Col %i") % colNumber | |
798 | if self.GetStatusText(3) != newText: | |
799 | self.SetStatusText(newText, 3) | |
800 | ||
801 | ||
802 | class TextOptionsPanel(wx.Panel): | |
803 | ||
804 | ||
805 | def __init__(self, parent, id, configPrefix = "Text", label = "Text", hasWordWrap = True, hasTabs = False, addPage=True): | |
806 | wx.Panel.__init__(self, parent, id) | |
807 | self._configPrefix = configPrefix | |
808 | self._hasWordWrap = hasWordWrap | |
809 | self._hasTabs = hasTabs | |
810 | SPACE = 10 | |
811 | HALF_SPACE = 5 | |
812 | config = wx.ConfigBase_Get() | |
813 | self._textFont = wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL) | |
814 | fontData = config.Read(self._configPrefix + "EditorFont", "") | |
815 | if fontData: | |
816 | nativeFont = wx.NativeFontInfo() | |
817 | nativeFont.FromString(fontData) | |
818 | self._textFont.SetNativeFontInfo(nativeFont) | |
819 | self._originalTextFont = self._textFont | |
820 | self._textColor = wx.BLACK | |
821 | colorData = config.Read(self._configPrefix + "EditorColor", "") | |
822 | if colorData: | |
823 | red = int("0x" + colorData[0:2], 16) | |
824 | green = int("0x" + colorData[2:4], 16) | |
825 | blue = int("0x" + colorData[4:6], 16) | |
826 | self._textColor = wx.Color(red, green, blue) | |
827 | self._originalTextColor = self._textColor | |
828 | fontLabel = wx.StaticText(self, -1, _("Font:")) | |
829 | self._sampleTextCtrl = wx.TextCtrl(self, -1, "", size = (125, 21)) | |
830 | self._sampleTextCtrl.SetEditable(False) | |
831 | chooseFontButton = wx.Button(self, -1, _("Choose Font...")) | |
832 | wx.EVT_BUTTON(self, chooseFontButton.GetId(), self.OnChooseFont) | |
833 | if self._hasWordWrap: | |
834 | self._wordWrapCheckBox = wx.CheckBox(self, -1, _("Wrap words inside text area")) | |
835 | self._wordWrapCheckBox.SetValue(wx.ConfigBase_Get().ReadInt(self._configPrefix + "EditorWordWrap", False)) | |
836 | self._viewWhitespaceCheckBox = wx.CheckBox(self, -1, _("Show whitespace")) | |
837 | self._viewWhitespaceCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewWhitespace", False)) | |
838 | self._viewEOLCheckBox = wx.CheckBox(self, -1, _("Show end of line markers")) | |
839 | self._viewEOLCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewEOL", False)) | |
840 | self._viewIndentationGuideCheckBox = wx.CheckBox(self, -1, _("Show indentation guides")) | |
841 | self._viewIndentationGuideCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewIndentationGuides", False)) | |
842 | self._viewRightEdgeCheckBox = wx.CheckBox(self, -1, _("Show right edge")) | |
843 | self._viewRightEdgeCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewRightEdge", False)) | |
844 | self._viewLineNumbersCheckBox = wx.CheckBox(self, -1, _("Show line numbers")) | |
845 | self._viewLineNumbersCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewLineNumbers", True)) | |
846 | if self._hasTabs: | |
847 | self._hasTabsCheckBox = wx.CheckBox(self, -1, _("Use spaces instead of tabs")) | |
848 | self._hasTabsCheckBox.SetValue(not wx.ConfigBase_Get().ReadInt(self._configPrefix + "EditorUseTabs", False)) | |
849 | indentWidthLabel = wx.StaticText(self, -1, _("Indent Width:")) | |
850 | self._indentWidthChoice = wx.Choice(self, -1, choices = ["2", "4", "6", "8", "10"]) | |
851 | self._indentWidthChoice.SetStringSelection(str(config.ReadInt(self._configPrefix + "EditorIndentWidth", 4))) | |
852 | textPanelBorderSizer = wx.BoxSizer(wx.VERTICAL) | |
853 | textPanelSizer = wx.BoxSizer(wx.VERTICAL) | |
854 | textFontSizer = wx.BoxSizer(wx.HORIZONTAL) | |
855 | textFontSizer.Add(fontLabel, 0, wx.ALIGN_LEFT | wx.RIGHT | wx.TOP, HALF_SPACE) | |
856 | textFontSizer.Add(self._sampleTextCtrl, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.RIGHT, HALF_SPACE) | |
857 | textFontSizer.Add(chooseFontButton, 0, wx.ALIGN_RIGHT | wx.LEFT, HALF_SPACE) | |
858 | textPanelSizer.Add(textFontSizer, 0, wx.ALL, HALF_SPACE) | |
859 | if self._hasWordWrap: | |
860 | textPanelSizer.Add(self._wordWrapCheckBox, 0, wx.ALL, HALF_SPACE) | |
861 | textPanelSizer.Add(self._viewWhitespaceCheckBox, 0, wx.ALL, HALF_SPACE) | |
862 | textPanelSizer.Add(self._viewEOLCheckBox, 0, wx.ALL, HALF_SPACE) | |
863 | textPanelSizer.Add(self._viewIndentationGuideCheckBox, 0, wx.ALL, HALF_SPACE) | |
864 | textPanelSizer.Add(self._viewRightEdgeCheckBox, 0, wx.ALL, HALF_SPACE) | |
865 | textPanelSizer.Add(self._viewLineNumbersCheckBox, 0, wx.ALL, HALF_SPACE) | |
866 | if self._hasTabs: | |
867 | textPanelSizer.Add(self._hasTabsCheckBox, 0, wx.ALL, HALF_SPACE) | |
868 | textIndentWidthSizer = wx.BoxSizer(wx.HORIZONTAL) | |
869 | textIndentWidthSizer.Add(indentWidthLabel, 0, wx.ALIGN_LEFT | wx.RIGHT | wx.TOP, HALF_SPACE) | |
870 | textIndentWidthSizer.Add(self._indentWidthChoice, 0, wx.ALIGN_LEFT | wx.EXPAND, HALF_SPACE) | |
871 | textPanelSizer.Add(textIndentWidthSizer, 0, wx.ALL, HALF_SPACE) | |
872 | textPanelBorderSizer.Add(textPanelSizer, 0, wx.ALL, SPACE) | |
873 | ## styleButton = wx.Button(self, -1, _("Choose Style...")) | |
874 | ## wx.EVT_BUTTON(self, styleButton.GetId(), self.OnChooseStyle) | |
875 | ## textPanelBorderSizer.Add(styleButton, 0, wx.ALL, SPACE) | |
876 | self.SetSizer(textPanelBorderSizer) | |
877 | self.UpdateSampleFont() | |
878 | if addPage: | |
879 | parent.AddPage(self, _(label)) | |
880 | ||
881 | def UpdateSampleFont(self): | |
882 | nativeFont = wx.NativeFontInfo() | |
883 | nativeFont.FromString(self._textFont.GetNativeFontInfoDesc()) | |
884 | font = wx.NullFont | |
885 | font.SetNativeFontInfo(nativeFont) | |
886 | font.SetPointSize(self._sampleTextCtrl.GetFont().GetPointSize()) # Use the standard point size | |
887 | self._sampleTextCtrl.SetFont(font) | |
888 | self._sampleTextCtrl.SetForegroundColour(self._textColor) | |
889 | self._sampleTextCtrl.SetValue(str(self._textFont.GetPointSize()) + _(" pt. ") + self._textFont.GetFaceName()) | |
890 | self._sampleTextCtrl.Refresh() | |
891 | self.Layout() | |
892 | ||
893 | ||
894 | ## def OnChooseStyle(self, event): | |
895 | ## import STCStyleEditor | |
896 | ## import os | |
897 | ## base = os.path.split(__file__)[0] | |
898 | ## config = os.path.abspath(os.path.join(base, 'stc-styles.rc.cfg')) | |
899 | ## | |
900 | ## dlg = STCStyleEditor.STCStyleEditDlg(None, | |
901 | ## 'Python', 'python', | |
902 | ## #'HTML', 'html', | |
903 | ## #'XML', 'xml', | |
904 | ## config) | |
905 | ## try: | |
906 | ## dlg.ShowModal() | |
907 | ## finally: | |
908 | ## dlg.Destroy() | |
909 | ||
910 | ||
911 | def OnChooseFont(self, event): | |
912 | data = wx.FontData() | |
913 | data.EnableEffects(True) | |
914 | data.SetInitialFont(self._textFont) | |
915 | data.SetColour(self._textColor) | |
916 | fontDialog = wx.FontDialog(self, data) | |
917 | if fontDialog.ShowModal() == wx.ID_OK: | |
918 | data = fontDialog.GetFontData() | |
919 | self._textFont = data.GetChosenFont() | |
920 | self._textColor = data.GetColour() | |
921 | self.UpdateSampleFont() | |
922 | fontDialog.Destroy() | |
923 | ||
924 | ||
925 | def OnOK(self, optionsDialog): | |
926 | config = wx.ConfigBase_Get() | |
927 | doViewStuffUpdate = config.ReadInt(self._configPrefix + "EditorViewWhitespace", False) != self._viewWhitespaceCheckBox.GetValue() | |
928 | config.WriteInt(self._configPrefix + "EditorViewWhitespace", self._viewWhitespaceCheckBox.GetValue()) | |
929 | doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewEOL", False) != self._viewEOLCheckBox.GetValue() | |
930 | config.WriteInt(self._configPrefix + "EditorViewEOL", self._viewEOLCheckBox.GetValue()) | |
931 | doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewIndentationGuides", False) != self._viewIndentationGuideCheckBox.GetValue() | |
932 | config.WriteInt(self._configPrefix + "EditorViewIndentationGuides", self._viewIndentationGuideCheckBox.GetValue()) | |
933 | doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewRightEdge", False) != self._viewRightEdgeCheckBox.GetValue() | |
934 | config.WriteInt(self._configPrefix + "EditorViewRightEdge", self._viewRightEdgeCheckBox.GetValue()) | |
935 | doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewLineNumbers", True) != self._viewLineNumbersCheckBox.GetValue() | |
936 | config.WriteInt(self._configPrefix + "EditorViewLineNumbers", self._viewLineNumbersCheckBox.GetValue()) | |
937 | if self._hasWordWrap: | |
938 | doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorWordWrap", False) != self._wordWrapCheckBox.GetValue() | |
939 | config.WriteInt(self._configPrefix + "EditorWordWrap", self._wordWrapCheckBox.GetValue()) | |
940 | if self._hasTabs: | |
941 | doViewStuffUpdate = doViewStuffUpdate or not config.ReadInt(self._configPrefix + "EditorUseTabs", True) != self._hasTabsCheckBox.GetValue() | |
942 | config.WriteInt(self._configPrefix + "EditorUseTabs", not self._hasTabsCheckBox.GetValue()) | |
943 | newIndentWidth = int(self._indentWidthChoice.GetStringSelection()) | |
944 | oldIndentWidth = config.ReadInt(self._configPrefix + "EditorIndentWidth", 4) | |
945 | if newIndentWidth != oldIndentWidth: | |
946 | doViewStuffUpdate = True | |
947 | config.WriteInt(self._configPrefix + "EditorIndentWidth", newIndentWidth) | |
948 | doFontUpdate = self._originalTextFont != self._textFont or self._originalTextColor != self._textColor | |
949 | config.Write(self._configPrefix + "EditorFont", self._textFont.GetNativeFontInfoDesc()) | |
950 | config.Write(self._configPrefix + "EditorColor", "%02x%02x%02x" % (self._textColor.Red(), self._textColor.Green(), self._textColor.Blue())) | |
951 | if doViewStuffUpdate or doFontUpdate: | |
952 | for document in optionsDialog.GetDocManager().GetDocuments(): | |
953 | if issubclass(document.GetDocumentTemplate().GetDocumentType(), TextDocument): | |
954 | if doViewStuffUpdate: | |
955 | document.UpdateAllViews(hint = "ViewStuff") | |
956 | if doFontUpdate: | |
957 | document.UpdateAllViews(hint = "Font") | |
958 | ||
959 | ||
960 | class TextCtrl(wx.stc.StyledTextCtrl): | |
961 | ||
962 | def __init__(self, parent, ID = -1, style = wx.NO_FULL_REPAINT_ON_RESIZE): | |
963 | if ID == -1: | |
964 | ID = wx.NewId() | |
965 | wx.stc.StyledTextCtrl.__init__(self, parent, ID, style = style) | |
966 | ||
967 | self._font = None | |
968 | self._fontColor = None | |
969 | ||
bbf7159c | 970 | self.SetVisiblePolicy(wx.stc.STC_VISIBLE_STRICT,1) |
1f780e48 RD |
971 | |
972 | self.CmdKeyClear(wx.stc.STC_KEY_ADD, wx.stc.STC_SCMOD_CTRL) | |
973 | self.CmdKeyClear(wx.stc.STC_KEY_SUBTRACT, wx.stc.STC_SCMOD_CTRL) | |
974 | self.CmdKeyAssign(wx.stc.STC_KEY_PRIOR, wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_ZOOMIN) | |
975 | self.CmdKeyAssign(wx.stc.STC_KEY_NEXT, wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_ZOOMOUT) | |
976 | self.Bind(wx.stc.EVT_STC_ZOOM, self.OnUpdateLineNumberMarginWidth) # auto update line num width on zoom | |
977 | wx.EVT_KEY_DOWN(self, self.OnKeyPressed) | |
978 | self.SetMargins(0,0) | |
979 | ||
980 | self.SetUseTabs(0) | |
981 | self.SetTabWidth(4) | |
982 | self.SetIndent(4) | |
983 | ||
984 | self.SetViewWhiteSpace(False) | |
985 | self.SetEOLMode(wx.stc.STC_EOL_LF) | |
986 | self.SetEdgeMode(wx.stc.STC_EDGE_NONE) | |
987 | self.SetEdgeColumn(78) | |
988 | ||
989 | self.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER) | |
990 | self.SetMarginWidth(1, self.EstimatedLineNumberMarginWidth()) | |
991 | self.UpdateStyles() | |
992 | ||
993 | self.SetCaretForeground("BLACK") | |
994 | ||
995 | self.SetViewDefaults() | |
996 | font, color = self.GetFontAndColorFromConfig() | |
997 | self.SetFont(font) | |
998 | self.SetFontColor(color) | |
999 | self.MarkerDefineDefault() | |
1000 | ||
1001 | # for multisash initialization | |
1002 | if isinstance(parent, wx.lib.multisash.MultiClient): | |
1003 | while parent.GetParent(): | |
1004 | parent = parent.GetParent() | |
1005 | if hasattr(parent, "GetView"): | |
1006 | break | |
1007 | if hasattr(parent, "GetView"): | |
1008 | textEditor = parent.GetView()._textEditor | |
1009 | if textEditor: | |
1010 | doc = textEditor.GetDocPointer() | |
1011 | if doc: | |
1012 | self.SetDocPointer(doc) | |
1013 | ||
1014 | ||
1015 | ||
1016 | def SetViewDefaults(self, configPrefix = "Text", hasWordWrap = True, hasTabs = False): | |
1017 | config = wx.ConfigBase_Get() | |
1018 | self.SetViewWhiteSpace(config.ReadInt(configPrefix + "EditorViewWhitespace", False)) | |
1019 | self.SetViewEOL(config.ReadInt(configPrefix + "EditorViewEOL", False)) | |
1020 | self.SetIndentationGuides(config.ReadInt(configPrefix + "EditorViewIndentationGuides", False)) | |
1021 | self.SetViewRightEdge(config.ReadInt(configPrefix + "EditorViewRightEdge", False)) | |
1022 | self.SetViewLineNumbers(config.ReadInt(configPrefix + "EditorViewLineNumbers", True)) | |
1023 | if hasWordWrap: | |
1024 | self.SetWordWrap(config.ReadInt(configPrefix + "EditorWordWrap", False)) | |
1025 | if hasTabs: # These methods do not exist in STCTextEditor and are meant for subclasses | |
1026 | self.SetUseTabs(config.ReadInt(configPrefix + "EditorUseTabs", False)) | |
1027 | self.SetIndent(config.ReadInt(configPrefix + "EditorIndentWidth", 4)) | |
1028 | self.SetTabWidth(config.ReadInt(configPrefix + "EditorIndentWidth", 4)) | |
1029 | else: | |
1030 | self.SetUseTabs(True) | |
1031 | self.SetIndent(4) | |
1032 | self.SetTabWidth(4) | |
1033 | ||
1034 | ||
1035 | ||
1036 | def GetDefaultFont(self): | |
1037 | """ Subclasses should override this """ | |
1038 | return wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL) | |
1039 | ||
1040 | ||
1041 | def GetDefaultColor(self): | |
1042 | """ Subclasses should override this """ | |
1043 | return wx.BLACK | |
1044 | ||
1045 | ||
1046 | def GetFontAndColorFromConfig(self, configPrefix = "Text"): | |
1047 | font = self.GetDefaultFont() | |
1048 | config = wx.ConfigBase_Get() | |
1049 | fontData = config.Read(configPrefix + "EditorFont", "") | |
1050 | if fontData: | |
1051 | nativeFont = wx.NativeFontInfo() | |
1052 | nativeFont.FromString(fontData) | |
1053 | font.SetNativeFontInfo(nativeFont) | |
1054 | color = self.GetDefaultColor() | |
1055 | colorData = config.Read(configPrefix + "EditorColor", "") | |
1056 | if colorData: | |
1057 | red = int("0x" + colorData[0:2], 16) | |
1058 | green = int("0x" + colorData[2:4], 16) | |
1059 | blue = int("0x" + colorData[4:6], 16) | |
1060 | color = wx.Color(red, green, blue) | |
1061 | return font, color | |
1062 | ||
1063 | ||
1064 | def GetFont(self): | |
1065 | return self._font | |
1066 | ||
1067 | def SetFont(self, font): | |
1068 | self._font = font | |
1069 | self.StyleSetFont(wx.stc.STC_STYLE_DEFAULT, self._font) | |
1070 | ||
1071 | ||
1072 | def GetFontColor(self): | |
1073 | return self._fontColor | |
1074 | ||
1075 | ||
1076 | def SetFontColor(self, fontColor = wx.BLACK): | |
1077 | self._fontColor = fontColor | |
1078 | self.StyleSetForeground(wx.stc.STC_STYLE_DEFAULT, "#%02x%02x%02x" % (self._fontColor.Red(), self._fontColor.Green(), self._fontColor.Blue())) | |
1079 | ||
1080 | ||
1081 | def UpdateStyles(self): | |
1082 | self.StyleClearAll() | |
1083 | return | |
1084 | ||
1085 | ||
1086 | def EstimatedLineNumberMarginWidth(self): | |
1087 | MARGIN = 4 | |
1088 | baseNumbers = "000" | |
1089 | lineNum = self.GetLineCount() | |
1090 | lineNum = lineNum/100 | |
1091 | while lineNum >= 10: | |
1092 | lineNum = lineNum/10 | |
1093 | baseNumbers = baseNumbers + "0" | |
1094 | ||
1095 | return self.TextWidth(wx.stc.STC_STYLE_LINENUMBER, baseNumbers) + MARGIN | |
1096 | ||
1097 | ||
1098 | def OnUpdateLineNumberMarginWidth(self, event): | |
1099 | self.UpdateLineNumberMarginWidth() | |
1100 | ||
1101 | ||
1102 | def UpdateLineNumberMarginWidth(self): | |
1103 | if self.GetViewLineNumbers(): | |
1104 | self.SetMarginWidth(1, self.EstimatedLineNumberMarginWidth()) | |
1105 | ||
1106 | def MarkerDefineDefault(self): | |
1107 | """ This must be called after the textcontrol is instantiated """ | |
1108 | self.MarkerDefine(TextView.MARKER_NUM, wx.stc.STC_MARK_ROUNDRECT, wx.BLACK, wx.BLUE) | |
1109 | ||
1110 | ||
1111 | def OnClear(self): | |
1112 | # Used when Delete key is hit. | |
1113 | sel = self.GetSelection() | |
1114 | ||
1115 | # Delete the selection or if no selection, the character after the caret. | |
1116 | if sel[0] == sel[1]: | |
1117 | self.SetSelection(sel[0], sel[0] + 1) | |
1118 | else: | |
1119 | # remove any folded lines also. | |
1120 | startLine = self.LineFromPosition(sel[0]) | |
1121 | endLine = self.LineFromPosition(sel[1]) | |
1122 | endLineStart = self.PositionFromLine(endLine) | |
1123 | if startLine != endLine and sel[1] - endLineStart == 0: | |
1124 | while not self.GetLineVisible(endLine): | |
1125 | endLine += 1 | |
1126 | self.SetSelectionEnd(self.PositionFromLine(endLine)) | |
1127 | ||
1128 | self.Clear() | |
1129 | ||
1130 | ||
1131 | def OnPaste(self): | |
1132 | # replace any folded lines also. | |
1133 | sel = self.GetSelection() | |
1134 | startLine = self.LineFromPosition(sel[0]) | |
1135 | endLine = self.LineFromPosition(sel[1]) | |
1136 | endLineStart = self.PositionFromLine(endLine) | |
1137 | if startLine != endLine and sel[1] - endLineStart == 0: | |
1138 | while not self.GetLineVisible(endLine): | |
1139 | endLine += 1 | |
1140 | self.SetSelectionEnd(self.PositionFromLine(endLine)) | |
1141 | ||
1142 | self.Paste() | |
1143 | ||
1144 | ||
1145 | def OnKeyPressed(self, event): | |
1146 | key = event.GetKeyCode() | |
1147 | 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 | |
1148 | if event.ControlDown(): | |
1149 | self.ToggleFoldAll(expand = True, topLevelOnly = True) | |
1150 | elif event.ShiftDown(): | |
1151 | self.ToggleFoldAll(expand = True) | |
1152 | else: | |
1153 | self.ToggleFold(self.GetCurrentLine()) | |
1154 | elif key == wx.WXK_NUMPAD_SUBTRACT: | |
1155 | if event.ControlDown(): | |
1156 | self.ToggleFoldAll(expand = False, topLevelOnly = True) | |
1157 | elif event.ShiftDown(): | |
1158 | self.ToggleFoldAll(expand = False) | |
1159 | else: | |
1160 | self.ToggleFold(self.GetCurrentLine()) | |
1161 | else: | |
1162 | event.Skip() | |
1163 | ||
1164 | ||
1165 | #---------------------------------------------------------------------------- | |
1166 | # View Text methods | |
1167 | #---------------------------------------------------------------------------- | |
1168 | ||
1169 | def GetViewRightEdge(self): | |
1170 | return self.GetEdgeMode() != wx.stc.STC_EDGE_NONE | |
1171 | ||
1172 | ||
1173 | def SetViewRightEdge(self, viewRightEdge): | |
1174 | if viewRightEdge: | |
1175 | self.SetEdgeMode(wx.stc.STC_EDGE_LINE) | |
1176 | else: | |
1177 | self.SetEdgeMode(wx.stc.STC_EDGE_NONE) | |
1178 | ||
1179 | ||
1180 | def GetViewLineNumbers(self): | |
1181 | return self.GetMarginWidth(1) > 0 | |
1182 | ||
1183 | ||
1184 | def SetViewLineNumbers(self, viewLineNumbers = True): | |
1185 | if viewLineNumbers: | |
1186 | self.SetMarginWidth(1, self.EstimatedLineNumberMarginWidth()) | |
1187 | else: | |
1188 | self.SetMarginWidth(1, 0) | |
1189 | ||
1190 | ||
1191 | def CanWordWrap(self): | |
1192 | return True | |
1193 | ||
1194 | ||
1195 | def GetWordWrap(self): | |
1196 | return self.GetWrapMode() == wx.stc.STC_WRAP_WORD | |
1197 | ||
1198 | ||
1199 | def SetWordWrap(self, wordWrap): | |
1200 | if wordWrap: | |
1201 | self.SetWrapMode(wx.stc.STC_WRAP_WORD) | |
1202 | else: | |
1203 | self.SetWrapMode(wx.stc.STC_WRAP_NONE) | |
1204 | ||
1205 | ||
1206 | #---------------------------------------------------------------------------- | |
1207 | # Icon Bitmaps - generated by encode_bitmaps.py | |
1208 | #---------------------------------------------------------------------------- | |
1209 | from wx import ImageFromStream, BitmapFromImage | |
1f780e48 RD |
1210 | import cStringIO |
1211 | ||
1212 | ||
1213 | def getTextData(): | |
1214 | return \ | |
1215 | '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ | |
1216 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ | |
1217 | \x00\x00`IDAT8\x8d\xed\x931\x0e\xc00\x08\x03m\x92\xff\xff8q\xa7JU!$\x12\x1d\ | |
1218 | \xeb\t\t8n\x81\xb4\x86J\xfa]h\x0ee\x83\xb4\xc6\x14\x00\x00R\xcc \t\xcd\xa1\ | |
1219 | \x08\xd2\xa3\xe1\x08*\t$\x1d\xc4\x012\x0b\x00\xce\xe4\xc8\xe0\t}\xf7\x8f\rV\ | |
1220 | \xd9\x1a\xec\xe0\xbf\xc1\xd7\x06\xd9\xf5UX\xfdF+m\x03\xb8\x00\xe4\xc74B"x\ | |
1221 | \xf1\xf4\x00\x00\x00\x00IEND\xaeB`\x82' | |
1222 | ||
1223 | ||
1224 | def getTextBitmap(): | |
1225 | return BitmapFromImage(getTextImage()) | |
1226 | ||
1227 | def getTextImage(): | |
1228 | stream = cStringIO.StringIO(getTextData()) | |
1229 | return ImageFromStream(stream) | |
1230 | ||
1231 | def getTextIcon(): | |
bbf7159c | 1232 | return wx.IconFromBitmap(getTextBitmap()) |
1f780e48 RD |
1233 | |
1234 | ||
1235 | #---------------------------------------------------------------------------- | |
1236 | # Menu Bitmaps - generated by encode_bitmaps.py | |
1237 | #---------------------------------------------------------------------------- | |
1238 | #---------------------------------------------------------------------- | |
1239 | def getZoomInData(): | |
1240 | return \ | |
1241 | '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ | |
1242 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ | |
1243 | \x00\x00wIDAT8\x8d\xa5\x93Q\x12\x80 \x08D\xb5\xe9X\xee\xe9\xb7{\xd5Gc\xa9\ | |
1244 | \xacX\xca\x1f\xa0\x8fE0\x92<\xc3\x82\xed*\x08\xa0\xf2I~\x07\x000\x17T,\xdb\ | |
1245 | \xd6;\x08\xa4\x00\xa4GA\xab\xca\x00\xbc*\x1eD\xb4\x90\xa4O\x1e\xe3\x16f\xcc(\ | |
1246 | \xc8\x95F\x95\x8d\x02\xef\xa1n\xa0\xce\xc5v\x91zc\xacU\xbey\x03\xf0.\xa8\xb8\ | |
1247 | \x04\x8c\xac\x04MM\xa1lA\xfe\x85?\x90\xe5=X\x06\\\xebCA\xb3Q\xf34\x14\x00\ | |
1248 | \x00\x00\x00IEND\xaeB`\x82' | |
1249 | ||
1250 | def getZoomInBitmap(): | |
1251 | return BitmapFromImage(getZoomInImage()) | |
1252 | ||
1253 | def getZoomInImage(): | |
1254 | stream = cStringIO.StringIO(getZoomInData()) | |
1255 | return ImageFromStream(stream) | |
1256 | ||
1257 | #---------------------------------------------------------------------- | |
1258 | def getZoomOutData(): | |
1259 | return \ | |
1260 | '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ | |
1261 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ | |
1262 | \x00\x00qIDAT8\x8d\xa5\x92Q\x0e\xc0 \x08C-z\xff\x13O\xd9\xd7\x16"\x05\x8d\ | |
1263 | \xf6O\xa2\x8f"\x05\xa4\x96\x1b5V\xd4\xd1\xd5\x9e!\x15\xdb\x00\x1d]\xe7\x07\ | |
1264 | \xac\xf6Iv.B*fW\x0e\x90u\xc9 d\x84\x87v\x82\xb4\xf5\x08\'r\x0e\xa2N\x91~\x07\ | |
1265 | \xd9G\x95\xe2W\xeb\x00\x19\xc4\xd6\\FX\x12\xa3 \xb1:\x05\xacdAG[\xb0y9r`u\ | |
1266 | \x9d\x83k\xc0\x0b#3@0A\x0c"\x93\x00\x00\x00\x00IEND\xaeB`\x82' | |
1267 | ||
1268 | ||
1269 | def getZoomOutBitmap(): | |
1270 | return BitmapFromImage(getZoomOutImage()) | |
1271 | ||
1272 | def getZoomOutImage(): | |
1273 | stream = cStringIO.StringIO(getZoomOutData()) | |
1274 | return ImageFromStream(stream) | |
1275 | ||
1276 | ||
1277 |