##// END OF EJS Templates
Minor formatting.
Gael Varoquaux -
Show More
@@ -1,655 +1,656 b''
1 1 # encoding: utf-8
2 2 """
3 3 A Wx widget to act as a console and input commands.
4 4
5 5 This widget deals with prompts and provides an edit buffer
6 6 restricted to after the last prompt.
7 7 """
8 8
9 9 __docformat__ = "restructuredtext en"
10 10
11 11 #-------------------------------------------------------------------------------
12 12 # Copyright (C) 2008 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is
15 15 # in the file COPYING, distributed as part of this software.
16 16 #-------------------------------------------------------------------------------
17 17
18 18 #-------------------------------------------------------------------------------
19 19 # Imports
20 20 #-------------------------------------------------------------------------------
21 21
22 22 import wx
23 23 import wx.stc as stc
24 24
25 25 from wx.py import editwindow
26 26 import time
27 27 import sys
28 28 import string
29 29
30 30 LINESEP = '\n'
31 31 if sys.platform == 'win32':
32 32 LINESEP = '\n\r'
33 33
34 34 import re
35 35
36 36 # FIXME: Need to provide an API for non user-generated display on the
37 37 # screen: this should not be editable by the user.
38 38 #-------------------------------------------------------------------------------
39 39 # Constants
40 40 #-------------------------------------------------------------------------------
41 41 _COMPLETE_BUFFER_MARKER = 31
42 42 _ERROR_MARKER = 30
43 43 _INPUT_MARKER = 29
44 44
45 45 _DEFAULT_SIZE = 10
46 46 if sys.platform == 'darwin':
47 47 _DEFAULT_SIZE = 12
48 48
49 49 _DEFAULT_STYLE = {
50 50 #background definition
51 51 'default' : 'size:%d' % _DEFAULT_SIZE,
52 52 'bracegood' : 'fore:#00AA00,back:#000000,bold',
53 53 'bracebad' : 'fore:#FF0000,back:#000000,bold',
54 54
55 55 # Edge column: a number of None
56 56 'edge_column' : -1,
57 57
58 58 # properties for the various Python lexer styles
59 59 'comment' : 'fore:#007F00',
60 60 'number' : 'fore:#007F7F',
61 61 'string' : 'fore:#7F007F,italic',
62 62 'char' : 'fore:#7F007F,italic',
63 63 'keyword' : 'fore:#00007F,bold',
64 64 'triple' : 'fore:#7F0000',
65 65 'tripledouble' : 'fore:#7F0000',
66 66 'class' : 'fore:#0000FF,bold,underline',
67 67 'def' : 'fore:#007F7F,bold',
68 68 'operator' : 'bold'
69 69 }
70 70
71 71 # new style numbers
72 72 _STDOUT_STYLE = 15
73 73 _STDERR_STYLE = 16
74 74 _TRACE_STYLE = 17
75 75
76 76
77 77 # system colors
78 78 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
79 79
80 80 # Translation table from ANSI escape sequences to color.
81 81 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
82 82 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
83 83 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
84 84 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
85 85 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
86 86 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
87 87 '1;34': [12, 'LIGHT BLUE'], '1;35':
88 88 [13, 'MEDIUM VIOLET RED'],
89 89 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
90 90
91 91 #we define platform specific fonts
92 92 if wx.Platform == '__WXMSW__':
93 93 FACES = { 'times': 'Times New Roman',
94 94 'mono' : 'Courier New',
95 95 'helv' : 'Arial',
96 96 'other': 'Comic Sans MS',
97 97 'size' : 10,
98 98 'size2': 8,
99 99 }
100 100 elif wx.Platform == '__WXMAC__':
101 101 FACES = { 'times': 'Times New Roman',
102 102 'mono' : 'Monaco',
103 103 'helv' : 'Arial',
104 104 'other': 'Comic Sans MS',
105 105 'size' : 10,
106 106 'size2': 8,
107 107 }
108 108 else:
109 109 FACES = { 'times': 'Times',
110 110 'mono' : 'Courier',
111 111 'helv' : 'Helvetica',
112 112 'other': 'new century schoolbook',
113 113 'size' : 10,
114 114 'size2': 8,
115 115 }
116 116
117 117
118 118 #-------------------------------------------------------------------------------
119 119 # The console widget class
120 120 #-------------------------------------------------------------------------------
121 121 class ConsoleWidget(editwindow.EditWindow):
122 122 """ Specialized styled text control view for console-like workflow.
123 123
124 124 This widget is mainly interested in dealing with the prompt and
125 125 keeping the cursor inside the editing line.
126 126 """
127 127
128 128 # This is where the title captured from the ANSI escape sequences are
129 129 # stored.
130 130 title = 'Console'
131 131
132 132 # Last prompt printed
133 133 last_prompt = ''
134 134
135 135 # The buffer being edited.
136 136 def _set_input_buffer(self, string):
137 137 self.SetSelection(self.current_prompt_pos, self.GetLength())
138 138 self.ReplaceSelection(string)
139 139 self.GotoPos(self.GetLength())
140 140
141 141 def _get_input_buffer(self):
142 142 """ Returns the text in current edit buffer.
143 143 """
144 144 input_buffer = self.GetTextRange(self.current_prompt_pos,
145 145 self.GetLength())
146 146 input_buffer = input_buffer.replace(LINESEP, '\n')
147 147 return input_buffer
148 148
149 149 input_buffer = property(_get_input_buffer, _set_input_buffer)
150 150
151 151 style = _DEFAULT_STYLE.copy()
152 152
153 153 # Translation table from ANSI escape sequences to color. Override
154 154 # this to specify your colors.
155 155 ANSI_STYLES = ANSI_STYLES.copy()
156 156
157 157 # Font faces
158 158 faces = FACES.copy()
159 159
160 160 # Store the last time a refresh was done
161 161 _last_refresh_time = 0
162 162
163 163 #--------------------------------------------------------------------------
164 164 # Public API
165 165 #--------------------------------------------------------------------------
166 166
167 167 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
168 168 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
169 169 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
170 170 self.configure_scintilla()
171 171 # Track if 'enter' key as ever been processed
172 172 # This variable will only be reallowed until key goes up
173 173 self.enter_catched = False
174 174 self.current_prompt_pos = 0
175 175
176 176 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
177 177 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
178 178
179 179
180 180 def write(self, text, refresh=True):
181 181 """ Write given text to buffer, while translating the ansi escape
182 182 sequences.
183 183 """
184 184 # XXX: do not put print statements to sys.stdout/sys.stderr in
185 185 # this method, the print statements will call this method, as
186 186 # you will end up with an infinit loop
187 187 title = self.title_pat.split(text)
188 188 if len(title)>1:
189 189 self.title = title[-2]
190 190
191 191 text = self.title_pat.sub('', text)
192 192 segments = self.color_pat.split(text)
193 193 segment = segments.pop(0)
194 194 self.GotoPos(self.GetLength())
195 195 self.StartStyling(self.GetLength(), 0xFF)
196 196 try:
197 197 self.AppendText(segment)
198 198 except UnicodeDecodeError:
199 199 # XXX: Do I really want to skip the exception?
200 200 pass
201 201
202 202 if segments:
203 203 for ansi_tag, text in zip(segments[::2], segments[1::2]):
204 204 self.StartStyling(self.GetLength(), 0xFF)
205 205 try:
206 206 self.AppendText(text)
207 207 except UnicodeDecodeError:
208 208 # XXX: Do I really want to skip the exception?
209 209 pass
210 210
211 211 if ansi_tag not in self.ANSI_STYLES:
212 212 style = 0
213 213 else:
214 214 style = self.ANSI_STYLES[ansi_tag][0]
215 215
216 216 self.SetStyling(len(text), style)
217 217
218 218 self.GotoPos(self.GetLength())
219 219 if refresh:
220 220 current_time = time.time()
221 221 if current_time - self._last_refresh_time > 0.03:
222 222 if sys.platform == 'win32':
223 223 wx.SafeYield()
224 224 else:
225 225 wx.Yield()
226 226 # self.ProcessEvent(wx.PaintEvent())
227 227 self._last_refresh_time = current_time
228 228
229 229
230 230 def new_prompt(self, prompt):
231 231 """ Prints a prompt at start of line, and move the start of the
232 232 current block there.
233 233
234 234 The prompt can be given with ascii escape sequences.
235 235 """
236 236 self.write(prompt, refresh=False)
237 237 # now we update our cursor giving end of prompt
238 238 self.current_prompt_pos = self.GetLength()
239 239 self.current_prompt_line = self.GetCurrentLine()
240 240 self.EnsureCaretVisible()
241 241 self.last_prompt = prompt
242 242
243 243
244 244 def continuation_prompt(self):
245 245 """ Returns the current continuation prompt.
246 246 We need to implement this method here to deal with the
247 247 ascii escape sequences cleaning up.
248 248 """
249 249 # ASCII-less prompt
250 250 ascii_less = ''.join(self.color_pat.split(self.last_prompt)[2::2])
251 251 return "."*(len(ascii_less)-2) + ': '
252 252
253 253
254 254 def scroll_to_bottom(self):
255 255 maxrange = self.GetScrollRange(wx.VERTICAL)
256 256 self.ScrollLines(maxrange)
257 257
258 258
259 259 def pop_completion(self, possibilities, offset=0):
260 260 """ Pops up an autocompletion menu. Offset is the offset
261 261 in characters of the position at which the menu should
262 262 appear, relativ to the cursor.
263 263 """
264 264 self.AutoCompSetIgnoreCase(False)
265 265 self.AutoCompSetAutoHide(False)
266 266 self.AutoCompSetMaxHeight(len(possibilities))
267 267 self.AutoCompShow(offset, " ".join(possibilities))
268 268
269 269
270 270 def get_line_width(self):
271 271 """ Return the width of the line in characters.
272 272 """
273 273 return self.GetSize()[0]/self.GetCharWidth()
274 274
275 275
276 276 def configure_scintilla(self):
277 277
278 278 p = self.style
279 279
280 280 #First we define the special background colors
281 281 if 'trace' in p:
282 282 _COMPLETE_BUFFER_BG = p['trace']
283 283 else:
284 284 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
285 285
286 286 if 'stdout' in p:
287 287 _INPUT_BUFFER_BG = p['stdout']
288 288 else:
289 289 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
290 290
291 291 if 'stderr' in p:
292 292 _ERROR_BG = p['stderr']
293 293 else:
294 294 _ERROR_BG = '#FFF1F1' # Nice red
295 295
296 296 # Marker for complete buffer.
297 297 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
298 298 background = _COMPLETE_BUFFER_BG)
299 299
300 300 # Marker for current input buffer.
301 301 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
302 302 background = _INPUT_BUFFER_BG)
303 303 # Marker for tracebacks.
304 304 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
305 305 background = _ERROR_BG)
306 306
307 307 self.SetEOLMode(stc.STC_EOL_LF)
308 308
309 309 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
310 310 # the widget
311 311 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
312 312 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
313 313 # Also allow Ctrl Shift "=" for poor non US keyboard users.
314 314 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
315 315 stc.STC_CMD_ZOOMIN)
316 316
317 317 # Keys: we need to clear some of the keys the that don't play
318 318 # well with a console.
319 319 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
320 320 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
321 321 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
322 322 self.CmdKeyClear(ord('A'), stc.STC_SCMOD_CTRL)
323 323
324 324 self.SetEOLMode(stc.STC_EOL_CRLF)
325 325 self.SetWrapMode(stc.STC_WRAP_CHAR)
326 326 self.SetWrapMode(stc.STC_WRAP_WORD)
327 327 self.SetBufferedDraw(True)
328 328
329 329 if 'antialiasing' in p:
330 330 self.SetUseAntiAliasing(p['antialiasing'])
331 331 else:
332 332 self.SetUseAntiAliasing(True)
333 333
334 334 self.SetLayoutCache(stc.STC_CACHE_PAGE)
335 335 self.SetUndoCollection(False)
336 336 self.SetUseTabs(True)
337 337 self.SetIndent(4)
338 338 self.SetTabWidth(4)
339 339
340 340 # we don't want scintilla's autocompletion to choose
341 341 # automaticaly out of a single choice list, as we pop it up
342 342 # automaticaly
343 343 self.AutoCompSetChooseSingle(False)
344 344 self.AutoCompSetMaxHeight(10)
345 345 # XXX: this doesn't seem to have an effect.
346 346 self.AutoCompSetFillUps('\n')
347 347
348 348 self.SetMargins(3, 3) #text is moved away from border with 3px
349 349 # Suppressing Scintilla margins
350 350 self.SetMarginWidth(0, 0)
351 351 self.SetMarginWidth(1, 0)
352 352 self.SetMarginWidth(2, 0)
353 353
354 354 # Xterm escape sequences
355 355 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
356 356 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
357 357
358 358 # styles
359 359
360 360 if 'carret_color' in p:
361 361 self.SetCaretForeground(p['carret_color'])
362 362 else:
363 363 self.SetCaretForeground('BLACK')
364 364
365 365 if 'background_color' in p:
366 366 background_color = p['background_color']
367 367 else:
368 368 background_color = 'WHITE'
369 369
370 370 if 'default' in p:
371 371 if 'back' not in p['default']:
372 372 p['default'] += ',back:%s' % background_color
373 373 if 'size' not in p['default']:
374 374 p['default'] += ',size:%s' % self.faces['size']
375 375 if 'face' not in p['default']:
376 376 p['default'] += ',face:%s' % self.faces['mono']
377 377
378 378 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
379 379 else:
380 380 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
381 381 "fore:%s,back:%s,size:%d,face:%s"
382 382 % (self.ANSI_STYLES['0;30'][1],
383 383 background_color,
384 384 self.faces['size'], self.faces['mono']))
385 385
386 386 #all styles = default one
387 387 self.StyleClearAll()
388 388
389 389 # XXX: two lines below are usefull if not using the lexer
390 390 #for style in self.ANSI_STYLES.values():
391 391 # self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
392 392
393 393 #prompt definition
394 394 if 'prompt_in1' in p:
395 395 self.prompt_in1 = p['prompt_in1']
396 396 else:
397 397 self.prompt_in1 = \
398 398 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
399 399
400 400 if 'prompt_out' in p:
401 401 self.prompt_out = p['prompt_out']
402 402 else:
403 403 self.prompt_out = \
404 404 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
405 405
406 406 self.output_prompt_template = string.Template(self.prompt_out)
407 407 self.input_prompt_template = string.Template(self.prompt_in1)
408 408
409 409 if 'stdout' in p:
410 410 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
411 411 if 'stderr' in p:
412 412 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
413 413 if 'trace' in p:
414 414 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
415 415 if 'bracegood' in p:
416 416 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
417 417 if 'bracebad' in p:
418 418 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
419 419 if 'comment' in p:
420 420 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
421 421 if 'number' in p:
422 422 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
423 423 if 'string' in p:
424 424 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
425 425 if 'char' in p:
426 426 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
427 427 if 'keyword' in p:
428 428 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
429 429 if 'keyword' in p:
430 430 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
431 431 if 'triple' in p:
432 432 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
433 433 if 'tripledouble' in p:
434 434 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
435 435 if 'class' in p:
436 436 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
437 437 if 'def' in p:
438 438 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
439 439 if 'operator' in p:
440 440 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
441 441 if 'comment' in p:
442 442 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
443 443
444 444 if 'edge_column' in p:
445 445 edge_column = p['edge_column']
446 446 if edge_column is not None and edge_column > 0:
447 447 #we add a vertical line to console widget
448 448 self.SetEdgeMode(stc.STC_EDGE_LINE)
449 449 self.SetEdgeColumn(88)
450 450
451 451
452 452 #--------------------------------------------------------------------------
453 453 # EditWindow API
454 454 #--------------------------------------------------------------------------
455 455
456 456 def OnUpdateUI(self, event):
457 457 """ Override the OnUpdateUI of the EditWindow class, to prevent
458 458 syntax highlighting both for faster redraw, and for more
459 459 consistent look and feel.
460 460 """
461 461
462 462
463 463 #--------------------------------------------------------------------------
464 464 # Private API
465 465 #--------------------------------------------------------------------------
466 466
467 467 def _on_key_down(self, event, skip=True):
468 468 """ Key press callback used for correcting behavior for
469 469 console-like interfaces: the cursor is constraint to be after
470 470 the last prompt.
471 471
472 472 Return True if event as been catched.
473 473 """
474 474 catched = True
475 475 # XXX: Would the right way to do this be to have a
476 476 # dictionary at the instance level associating keys with
477 477 # callbacks? How would we deal with inheritance? And Do the
478 478 # different callbacks share local variables?
479 479
480 480 # Intercept some specific keys.
481 481 if event.KeyCode == ord('L') and event.ControlDown() :
482 482 self.scroll_to_bottom()
483 483 elif event.KeyCode == ord('K') and event.ControlDown() :
484 484 self.input_buffer = ''
485 485 elif event.KeyCode == ord('A') and event.ControlDown() :
486 486 self.GotoPos(self.GetLength())
487 487 self.SetSelectionStart(self.current_prompt_pos)
488 488 self.SetSelectionEnd(self.GetCurrentPos())
489 489 catched = True
490 490 elif event.KeyCode == ord('E') and event.ControlDown() :
491 491 self.GotoPos(self.GetLength())
492 492 catched = True
493 493 elif event.KeyCode == wx.WXK_PAGEUP:
494 494 self.ScrollPages(-1)
495 495 elif event.KeyCode == wx.WXK_PAGEDOWN:
496 496 self.ScrollPages(1)
497 497 elif event.KeyCode == wx.WXK_HOME:
498 498 self.GotoPos(self.GetLength())
499 499 elif event.KeyCode == wx.WXK_END:
500 500 self.GotoPos(self.GetLength())
501 501 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
502 502 self.ScrollLines(-1)
503 503 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
504 504 self.ScrollLines(1)
505 505 else:
506 506 catched = False
507 507
508 508 if self.AutoCompActive():
509 509 event.Skip()
510 510 else:
511 511 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
512 512 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN,
513 513 wx.MOD_SHIFT):
514 514 catched = True
515 515 if not self.enter_catched:
516 516 self.CallTipCancel()
517 517 if event.Modifiers == wx.MOD_SHIFT:
518 518 # Try to force execution
519 519 self.GotoPos(self.GetLength())
520 520 self.write('\n' + self.continuation_prompt(),
521 521 refresh=False)
522 522 self._on_enter()
523 523 else:
524 524 self._on_enter()
525 525 self.enter_catched = True
526 526
527 527 elif event.KeyCode == wx.WXK_HOME:
528 528 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
529 529 self.GotoPos(self.current_prompt_pos)
530 530 catched = True
531 531
532 532 elif event.Modifiers == wx.MOD_SHIFT:
533 533 # FIXME: This behavior is not ideal: if the selection
534 534 # is already started, it will jump.
535 535 self.SetSelectionStart(self.current_prompt_pos)
536 536 self.SetSelectionEnd(self.GetCurrentPos())
537 537 catched = True
538 538
539 539 elif event.KeyCode == wx.WXK_UP:
540 540 if self.GetCurrentLine() > self.current_prompt_line:
541 541 if self.GetCurrentLine() == self.current_prompt_line + 1 \
542 542 and self.GetColumn(self.GetCurrentPos()) < \
543 543 self.GetColumn(self.current_prompt_pos):
544 544 self.GotoPos(self.current_prompt_pos)
545 545 else:
546 546 event.Skip()
547 547 catched = True
548 548
549 549 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
550 550 if not self._keep_cursor_in_buffer(self.GetCurrentPos() - 1):
551 551 event.Skip()
552 552 catched = True
553 553
554 554 elif event.KeyCode == wx.WXK_RIGHT:
555 555 if not self._keep_cursor_in_buffer(self.GetCurrentPos() + 1):
556 556 event.Skip()
557 557 catched = True
558 558
559 559
560 560 elif event.KeyCode == wx.WXK_DELETE:
561 561 if not self._keep_cursor_in_buffer(self.GetCurrentPos() - 1):
562 562 event.Skip()
563 563 catched = True
564 564
565 565 if skip and not catched:
566 566 # Put the cursor back in the edit region
567 567 if not self._keep_cursor_in_buffer():
568 568 if not (self.GetCurrentPos() == self.GetLength()
569 569 and event.KeyCode == wx.WXK_DELETE):
570 570 event.Skip()
571 571 catched = True
572 572
573 573 return catched
574 574
575 575
576 576 def _on_key_up(self, event, skip=True):
577 577 """ If cursor is outside the editing region, put it back.
578 578 """
579 579 if skip:
580 580 event.Skip()
581 581 self._keep_cursor_in_buffer()
582 582
583 583
584 584 # XXX: I need to avoid the problem of having an empty glass;
585 585 def _keep_cursor_in_buffer(self, pos=None):
586 586 """ Checks if the cursor is where it is allowed to be. If not,
587 587 put it back.
588 588
589 589 Returns
590 590 -------
591 591 cursor_moved: Boolean
592 592 whether or not the cursor was moved by this routine.
593 593
594 594 Notes
595 595 ------
596 596 WARNING: This does proper checks only for horizontal
597 597 movements.
598 598 """
599 599 if pos is None:
600 600 current_pos = self.GetCurrentPos()
601 601 else:
602 602 current_pos = pos
603 603 if current_pos < self.current_prompt_pos:
604 604 self.GotoPos(self.current_prompt_pos)
605 605 return True
606 606 line_num = self.LineFromPosition(current_pos)
607 607 if not current_pos > self.GetLength():
608 608 line_pos = self.GetColumn(current_pos)
609 609 else:
610 610 line_pos = self.GetColumn(self.GetLength())
611 611 line = self.GetLine(line_num)
612 612 # Jump the continuation prompt
613 613 continuation_prompt = self.continuation_prompt()
614 614 if ( line.startswith(continuation_prompt)
615 615 and line_pos < len(continuation_prompt)):
616 616 if line_pos < 2:
617 617 # We are at the beginning of the line, trying to move
618 618 # forward: jump forward.
619 619 self.GotoPos(current_pos + 1 +
620 620 len(continuation_prompt) - line_pos)
621 621 else:
622 622 # Jump back up
623 623 self.GotoPos(self.GetLineEndPosition(line_num-1))
624 624 return True
625 625 elif ( current_pos > self.GetLineEndPosition(line_num)
626 626 and not current_pos == self.GetLength()):
627 627 # Jump to next line
628 628 self.GotoPos(current_pos + 1 +
629 629 len(continuation_prompt))
630 630 return True
631 631
632 self.enter_catched = False #we re-allow enter event processing
632 # We re-allow enter event processing
633 self.enter_catched = False
633 634 return False
634 635
635 636
636 637 if __name__ == '__main__':
637 638 # Some simple code to test the console widget.
638 639 class MainWindow(wx.Frame):
639 640 def __init__(self, parent, id, title):
640 641 wx.Frame.__init__(self, parent, id, title, size=(300, 250))
641 642 self._sizer = wx.BoxSizer(wx.VERTICAL)
642 643 self.console_widget = ConsoleWidget(self)
643 644 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
644 645 self.SetSizer(self._sizer)
645 646 self.SetAutoLayout(1)
646 647 self.Show(True)
647 648
648 649 app = wx.PySimpleApp()
649 650 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
650 651 w.SetSize((780, 460))
651 652 w.Show()
652 653
653 654 app.MainLoop()
654 655
655 656
General Comments 0
You need to be logged in to leave comments. Login now