##// END OF EJS Templates
pager cleanup
ldufrechou -
Show More
@@ -1,705 +1,702 b''
1 1 #!/usr/bin/python
2 2 # -*- coding: iso-8859-15 -*-
3 3 '''
4 4 Provides IPython WX console widgets.
5 5
6 6 @author: Laurent Dufrechou
7 7 laurent.dufrechou _at_ gmail.com
8 8 This WX widget is based on the original work of Eitan Isaacson
9 9 that provided the console for the GTK toolkit.
10 10
11 11 Original work from:
12 12 @author: Eitan Isaacson
13 13 @organization: IBM Corporation
14 14 @copyright: Copyright (c) 2007 IBM Corporation
15 15 @license: BSD
16 16
17 17 All rights reserved. This program and the accompanying materials are made
18 18 available under the terms of the BSD which accompanies this distribution, and
19 19 is available at U{http://www.opensource.org/licenses/bsd-license.php}
20 20 '''
21 21
22 22 __version__ = 0.8
23 23 __author__ = "Laurent Dufrechou"
24 24 __email__ = "laurent.dufrechou _at_ gmail.com"
25 25 __license__ = "BSD"
26 26
27 27 import wx
28 28 import wx.stc as stc
29 29 import wx.lib.newevent
30 30
31 31 import re
32 32 import sys
33 33 import os
34 34 import locale
35 35 import time
36 36 from StringIO import StringIO
37 37 try:
38 38 import IPython
39 39 except Exception,e:
40 40 raise "Error importing IPython (%s)" % str(e)
41 41
42 42
43 43 from ipshell_nonblocking import NonBlockingIPShell
44 44
45 45 class WxNonBlockingIPShell(NonBlockingIPShell):
46 46 '''
47 47 An NonBlockingIPShell Thread that is WX dependent.
48 48 Thus it permits direct interaction with a WX GUI without OnIdle event state machine trick...
49 49 '''
50 50 def __init__(self,wx_instance,
51 51 argv=[],user_ns={},user_global_ns=None,
52 52 cin=None, cout=None, cerr=None,
53 53 ask_exit_handler=None):
54 54
55 55 #user_ns['addGUIShortcut'] = self.addGUIShortcut
56 56 NonBlockingIPShell.__init__(self,argv,user_ns,user_global_ns,
57 57 cin, cout, cerr,
58 58 ask_exit_handler)
59 59
60 60 # This creates a new Event class and a EVT binder function
61 61 (self.IPythonAskExitEvent, EVT_IP_ASK_EXIT) = wx.lib.newevent.NewEvent()
62 62 #(self.IPythonAddButtonEvent, EVT_IP_ADD_BUTTON_EXIT) = \
63 63 # wx.lib.newevent.NewEvent()
64 64 (self.IPythonExecuteDoneEvent, EVT_IP_EXECUTE_DONE) = \
65 65 wx.lib.newevent.NewEvent()
66 66
67 67 wx_instance.Bind(EVT_IP_ASK_EXIT, wx_instance.ask_exit_handler)
68 68 #wx_instance.Bind(EVT_IP_ADD_BUTTON_EXIT, wx_instance.add_button_handler)
69 69 wx_instance.Bind(EVT_IP_EXECUTE_DONE, wx_instance.evtStateExecuteDone)
70 70
71 71 self.wx_instance = wx_instance
72 72 self._IP.ask_exit = self._askExit
73 73 self._IP.exit = self._askExit
74 74
75 75 #def addGUIShortcut(self,text,func):
76 76 # evt = self.IPythonAddButtonEvent(
77 77 # button_info={ 'text':text,
78 78 # 'func':self.wx_instance.doExecuteLine(func)})
79 79 # wx.PostEvent(self.wx_instance, evt)
80 80
81 81 def _askExit(self):
82 82 evt = self.IPythonAskExitEvent()
83 83 wx.PostEvent(self.wx_instance, evt)
84 84
85 85 def _afterExecute(self):
86 86 evt = self.IPythonExecuteDoneEvent()
87 87 wx.PostEvent(self.wx_instance, evt)
88 88
89 89
90 90 class WxConsoleView(stc.StyledTextCtrl):
91 91 '''
92 92 Specialized styled text control view for console-like workflow.
93 93 We use here a scintilla frontend thus it can be reused in any GUI taht supports
94 94 scintilla with less work.
95 95
96 96 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.(with Black background)
97 97 @type ANSI_COLORS_BLACK: dictionary
98 98
99 99 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.(with White background)
100 100 @type ANSI_COLORS_WHITE: dictionary
101 101
102 102 @ivar color_pat: Regex of terminal color pattern
103 103 @type color_pat: _sre.SRE_Pattern
104 104 '''
105 105 ANSI_STYLES_BLACK ={'0;30': [0,'WHITE'], '0;31': [1,'RED'],
106 106 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
107 107 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
108 108 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
109 109 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
110 110 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
111 111 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
112 112 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
113 113
114 114 ANSI_STYLES_WHITE ={'0;30': [0,'BLACK'], '0;31': [1,'RED'],
115 115 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
116 116 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
117 117 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
118 118 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
119 119 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
120 120 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
121 121 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
122 122
123 123 def __init__(self,parent,prompt,intro="",background_color="BLACK",pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
124 124 style=0):
125 125 '''
126 126 Initialize console view.
127 127
128 128 @param parent: Parent widget
129 129 @param prompt: User specified prompt
130 130 @type intro: string
131 131 @param intro: User specified startup introduction string
132 132 @type intro: string
133 133 @param background_color: Can be BLACK or WHITE
134 134 @type background_color: string
135 135 @param other: init param of styledTextControl (can be used as-is)
136 136 '''
137 137 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
138 138
139 139 ####### Scintilla configuration ##################################################
140 140
141 141 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside the widget
142 142 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
143 143 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
144 144
145 145 #we define platform specific fonts
146 146 if wx.Platform == '__WXMSW__':
147 147 faces = { 'times': 'Times New Roman',
148 148 'mono' : 'Courier New',
149 149 'helv' : 'Arial',
150 150 'other': 'Comic Sans MS',
151 151 'size' : 10,
152 152 'size2': 8,
153 153 }
154 154 elif wx.Platform == '__WXMAC__':
155 155 faces = { 'times': 'Times New Roman',
156 156 'mono' : 'Monaco',
157 157 'helv' : 'Arial',
158 158 'other': 'Comic Sans MS',
159 159 'size' : 10,
160 160 'size2': 8,
161 161 }
162 162 else:
163 163 faces = { 'times': 'Times',
164 164 'mono' : 'Courier',
165 165 'helv' : 'Helvetica',
166 166 'other': 'new century schoolbook',
167 167 'size' : 10,
168 168 'size2': 8,
169 169 }
170 170
171 171 #We draw a line at position 80
172 172 self.SetEdgeMode(stc.STC_EDGE_LINE)
173 173 self.SetEdgeColumn(80)
174 174 self.SetEdgeColour(wx.LIGHT_GREY)
175 175
176 176 #self.SetViewWhiteSpace(True)
177 177 #self.SetViewEOL(True)
178 178 self.SetEOLMode(stc.STC_EOL_CRLF)
179 179 #self.SetWrapMode(stc.STC_WRAP_CHAR)
180 180 #self.SetWrapMode(stc.STC_WRAP_WORD)
181 181 self.SetBufferedDraw(True)
182 182 #self.SetUseAntiAliasing(True)
183 183 self.SetLayoutCache(stc.STC_CACHE_PAGE)
184 184
185 185 self.EnsureCaretVisible()
186 186
187 187 self.SetMargins(3,3) #text is moved away from border with 3px
188 188 # Suppressing Scintilla margins
189 189 self.SetMarginWidth(0,0)
190 190 self.SetMarginWidth(1,0)
191 191 self.SetMarginWidth(2,0)
192 192
193 193 # make some styles
194 194 if background_color != "BLACK":
195 195 self.background_color = "WHITE"
196 196 self.SetCaretForeground("BLACK")
197 197 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
198 198 else:
199 199 self.background_color = background_color
200 200 self.SetCaretForeground("WHITE")
201 201 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
202 202
203 203 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:%s,back:%s,size:%d,face:%s" % (self.ANSI_STYLES['0;30'][1],
204 204 self.background_color,
205 205 faces['size'], faces['mono']))
206 206 self.StyleClearAll()
207 207 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FF0000,back:#0000FF,bold")
208 208 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
209 209
210 210 for style in self.ANSI_STYLES.values():
211 211 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
212 212
213 213 #######################################################################
214 214
215 215 self.indent = 0
216 216 self.prompt_count = 0
217 217 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
218 218
219 219 self.write(intro)
220 220 self.setPrompt(prompt)
221 221 self.showPrompt()
222 222
223 223 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress, self)
224 224 #self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
225 225
226 226 def write(self, text):
227 227 '''
228 228 Write given text to buffer.
229 229
230 230 @param text: Text to append.
231 231 @type text: string
232 232 '''
233 233 segments = self.color_pat.split(text)
234 234 segment = segments.pop(0)
235 235 self.StartStyling(self.getCurrentLineEnd(),0xFF)
236 236 self.AppendText(segment)
237 237
238 238 if segments:
239 239 ansi_tags = self.color_pat.findall(text)
240 240
241 241 for tag in ansi_tags:
242 242 i = segments.index(tag)
243 243 self.StartStyling(self.getCurrentLineEnd(),0xFF)
244 244 self.AppendText(segments[i+1])
245 245
246 246 if tag != '0':
247 247 self.SetStyling(len(segments[i+1]),self.ANSI_STYLES[tag][0])
248 248
249 249 segments.pop(i)
250 250
251 251 self.moveCursor(self.getCurrentLineEnd())
252 252
253 253 def getPromptLen(self):
254 254 '''
255 255 Return the length of current prompt
256 256 '''
257 257 return len(str(self.prompt_count)) + 7
258 258
259 259 def setPrompt(self,prompt):
260 260 self.prompt = prompt
261 261
262 262 def setIndentation(self,indentation):
263 263 self.indent = indentation
264 264
265 265 def setPromptCount(self,count):
266 266 self.prompt_count = count
267 267
268 268 def showPrompt(self):
269 269 '''
270 270 Prints prompt at start of line.
271 271
272 272 @param prompt: Prompt to print.
273 273 @type prompt: string
274 274 '''
275 275 self.write(self.prompt)
276 276 #now we update the position of end of prompt
277 277 self.current_start = self.getCurrentLineEnd()
278 278
279 279 autoindent = self.indent*' '
280 280 autoindent = autoindent.replace(' ','\t')
281 281 self.write(autoindent)
282 282
283 283 def changeLine(self, text):
284 284 '''
285 285 Replace currently entered command line with given text.
286 286
287 287 @param text: Text to use as replacement.
288 288 @type text: string
289 289 '''
290 290 self.SetSelection(self.getCurrentPromptStart(),self.getCurrentLineEnd())
291 291 self.ReplaceSelection(text)
292 292 self.moveCursor(self.getCurrentLineEnd())
293 293
294 294 def getCurrentPromptStart(self):
295 295 return self.current_start
296 296
297 297 def getCurrentLineStart(self):
298 298 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
299 299
300 300 def getCurrentLineEnd(self):
301 301 return self.GetLength()
302 302
303 303 def getCurrentLine(self):
304 304 '''
305 305 Get text in current command line.
306 306
307 307 @return: Text of current command line.
308 308 @rtype: string
309 309 '''
310 310 return self.GetTextRange(self.getCurrentPromptStart(),
311 311 self.getCurrentLineEnd())
312 312
313 313 def showReturned(self, text):
314 314 '''
315 315 Show returned text from last command and print new prompt.
316 316
317 317 @param text: Text to show.
318 318 @type text: string
319 319 '''
320 320 self.write('\n'+text)
321 321 if text:
322 322 self.write('\n')
323 323 self.showPrompt()
324 324
325 325 def moveCursorOnNewValidKey(self):
326 326 #If cursor is at wrong position put it at last line...
327 327 if self.GetCurrentPos() < self.getCurrentPromptStart():
328 328 self.GotoPos(self.getCurrentPromptStart())
329 329
330 330 def removeFromTo(self,from_pos,to_pos):
331 331 if from_pos < to_pos:
332 332 self.SetSelection(from_pos,to_pos)
333 333 self.DeleteBack()
334 334
335 335 def removeCurrentLine(self):
336 336 self.LineDelete()
337 337
338 338 def moveCursor(self,position):
339 339 self.GotoPos(position)
340 340
341 341 def getCursorPos(self):
342 342 return self.GetCurrentPos()
343 343
344 344 def selectFromTo(self,from_pos,to_pos):
345 345 self.SetSelectionStart(from_pos)
346 346 self.SetSelectionEnd(to_pos)
347 347
348 348 def writeHistory(self,history):
349 349 self.removeFromTo(self.getCurrentPromptStart(),self.getCurrentLineEnd())
350 350 self.changeLine(history)
351 351
352 352 def writeCompletion(self, possibilities):
353 353 max_len = len(max(possibilities,key=len))
354 354 max_symbol =' '*max_len
355 355
356 356 #now we check how much symbol we can put on a line...
357 357 cursor_pos = self.getCursorPos()
358 358 test_buffer = max_symbol + ' '*4
359 359 current_lines = self.GetLineCount()
360 360
361 361 allowed_symbols = 80/len(test_buffer)
362 362 if allowed_symbols == 0:
363 363 allowed_symbols = 1
364 364
365 365 pos = 1
366 366 buf = ''
367 367 for symbol in possibilities:
368 368 #buf += symbol+'\n'#*spaces)
369 369 if pos<allowed_symbols:
370 370 spaces = max_len - len(symbol) + 4
371 371 buf += symbol+' '*spaces
372 372 pos += 1
373 373 else:
374 374 buf+=symbol+'\n'
375 375 pos = 1
376 376 self.write(buf)
377 377
378 378 def _onKeypress(self, event, skip=True):
379 379 '''
380 380 Key press callback used for correcting behavior for console-like
381 381 interfaces. For example 'home' should go to prompt, not to begining of
382 382 line.
383 383
384 384 @param widget: Widget that key press accored in.
385 385 @type widget: gtk.Widget
386 386 @param event: Event object
387 387 @type event: gtk.gdk.Event
388 388
389 389 @return: Return True if event as been catched.
390 390 @rtype: boolean
391 391 '''
392 392
393 393 if event.GetKeyCode() == wx.WXK_HOME:
394 394 if event.Modifiers == wx.MOD_NONE:
395 395 self.moveCursorOnNewValidKey()
396 396 self.moveCursor(self.getCurrentPromptStart())
397 397 return True
398 398 elif event.Modifiers == wx.MOD_SHIFT:
399 399 self.moveCursorOnNewValidKey()
400 400 self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos())
401 401 return True
402 402 else:
403 403 return False
404 404
405 405 elif event.GetKeyCode() == wx.WXK_LEFT:
406 406 if event.Modifiers == wx.MOD_NONE:
407 407 self.moveCursorOnNewValidKey()
408 408
409 409 self.moveCursor(self.getCursorPos()-1)
410 410 if self.getCursorPos() < self.getCurrentPromptStart():
411 411 self.moveCursor(self.getCurrentPromptStart())
412 412 return True
413 413
414 414 elif event.GetKeyCode() == wx.WXK_BACK:
415 415 self.moveCursorOnNewValidKey()
416 416 if self.getCursorPos() > self.getCurrentPromptStart():
417 417 self.removeFromTo(self.getCursorPos()-1,self.getCursorPos())
418 418 return True
419 419
420 420 if skip:
421 421 if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE:
422 422 self.moveCursorOnNewValidKey()
423 423
424 424 event.Skip()
425 425 return True
426 426 return False
427 427
428 428 def OnUpdateUI(self, evt):
429 429 # check for matching braces
430 430 braceAtCaret = -1
431 431 braceOpposite = -1
432 432 charBefore = None
433 433 caretPos = self.GetCurrentPos()
434 434
435 435 if caretPos > 0:
436 436 charBefore = self.GetCharAt(caretPos - 1)
437 437 styleBefore = self.GetStyleAt(caretPos - 1)
438 438
439 439 # check before
440 440 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
441 441 braceAtCaret = caretPos - 1
442 442
443 443 # check after
444 444 if braceAtCaret < 0:
445 445 charAfter = self.GetCharAt(caretPos)
446 446 styleAfter = self.GetStyleAt(caretPos)
447 447
448 448 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
449 449 braceAtCaret = caretPos
450 450
451 451 if braceAtCaret >= 0:
452 452 braceOpposite = self.BraceMatch(braceAtCaret)
453 453
454 454 if braceAtCaret != -1 and braceOpposite == -1:
455 455 self.BraceBadLight(braceAtCaret)
456 456 else:
457 457 self.BraceHighlight(braceAtCaret, braceOpposite)
458 458 #pt = self.PointFromPosition(braceOpposite)
459 459 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
460 460 #print pt
461 461 #self.Refresh(False)
462 462
463 463 class WxIPythonViewPanel(wx.Panel):
464 464 '''
465 465 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
466 466 If you want to port this to any other GUI toolkit, just replace the WxConsoleView
467 467 by YOURGUIConsoleView and make YOURGUIIPythonView derivate from whatever container you want.
468 468 I've choosed to derivate from a wx.Panel because it seems to be ore usefull
469 469 Any idea to make it more 'genric' welcomed.
470 470 '''
471 471
472 472 def __init__(self, parent, ask_exit_handler=None, intro=None,
473 473 background_color="BLACK", add_button_handler=None,
474 474 wx_ip_shell=None,
475 475 ):
476 476 '''
477 477 Initialize.
478 478 Instanciate an IPython thread.
479 479 Instanciate a WxConsoleView.
480 480 Redirect I/O to console.
481 481 '''
482 482 wx.Panel.__init__(self,parent,-1)
483 483
484 484 ### IPython non blocking shell instanciation ###
485 485 self.cout = StringIO()
486 486
487 487 self.add_button_handler = add_button_handler
488 488 self.ask_exit_handler = ask_exit_handler
489 489
490 490 if wx_ip_shell is not None:
491 491 self.IP = wx_ip_shell
492 492 else:
493 493 self.IP = WxNonBlockingIPShell(self,
494 494 cout=self.cout,cerr=self.cout,
495 495 ask_exit_handler = ask_exit_handler)
496 496
497 497 ### IPython wx console view instanciation ###
498 498 #If user didn't defined an intro text, we create one for him
499 499 #If you really wnat an empty intrp just call wxIPythonViewPanel with intro=''
500 500 if intro == None:
501 501 welcome_text = "Welcome to WxIPython Shell.\n\n"
502 502 welcome_text+= self.IP.getBanner()
503 503 welcome_text+= "!command -> Execute command in shell\n"
504 504 welcome_text+= "TAB -> Autocompletion\n"
505 505
506 506 self.text_ctrl = WxConsoleView(self,
507 507 self.IP.getPrompt(),
508 508 intro=welcome_text,
509 509 background_color=background_color)
510 510
511 511 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress, self.text_ctrl)
512 512
513 513 ### making the layout of the panel ###
514 514 sizer = wx.BoxSizer(wx.VERTICAL)
515 515 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
516 516 self.SetAutoLayout(True)
517 517 sizer.Fit(self)
518 518 sizer.SetSizeHints(self)
519 519 self.SetSizer(sizer)
520 520 #and we focus on the widget :)
521 521 self.SetFocus()
522 522
523 523 #widget state management (for key handling different cases)
524 524 self.setCurrentState('IDLE')
525 525 self.pager_state = 'DONE'
526 526
527 527 def __del__(self):
528 528 WxConsoleView.__del__()
529 529
530 530 #---------------------------- IPython Thread Management ---------------------------------------
531 531 def stateDoExecuteLine(self):
532 532 #print >>sys.__stdout__,"command:",self.getCurrentLine()
533 533 line=self.text_ctrl.getCurrentLine()
534 534 self.IP.doExecute(line.replace('\t',' '*4))
535 535 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
536 536 self.setCurrentState('WAIT_END_OF_EXECUTION')
537 537
538 538 def evtStateExecuteDone(self,evt):
539 539 self.doc = self.IP.getDocText()
540 540 self.help = self.IP.getHelpText()
541 541 if self.doc:
542 542 self.pager_lines = self.doc[7:].split('\n')
543 543 self.pager_state = 'INIT'
544 544 self.setCurrentState('SHOW_DOC')
545 545 self.pager(self.doc)
546 546 elif self.help:
547 547 self.pager_lines = self.help.split('\n')
548 548 self.pager_state = 'INIT'
549 549 self.setCurrentState('SHOW_DOC')
550 550 self.pager(self.help)
551 551 else:
552 552 self.stateShowPrompt()
553 553
554 554 def stateShowPrompt(self):
555 555 self.setCurrentState('SHOW_PROMPT')
556 556 self.text_ctrl.setPrompt(self.IP.getPrompt())
557 557 self.text_ctrl.setIndentation(self.IP.getIndentation())
558 558 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
559 559 rv = self.cout.getvalue()
560 560 if rv: rv = rv.strip('\n')
561 561 self.text_ctrl.showReturned(rv)
562 562 self.cout.truncate(0)
563 563 self.IP.initHistoryIndex()
564 564 self.setCurrentState('IDLE')
565 565
566 566 def setCurrentState(self, state):
567 567 self.cur_state = state
568 568 self.updateStatusTracker(self.cur_state)
569 569
570 570 #---------------------------- IPython pager ---------------------------------------
571 571 def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None):
572 if self.pager_state == 'WAITING':
573 #print >>sys.__stdout__,"PAGER waiting"
574 return
575
576 if self.pager_state == 'INIT':
572
573 if self.pager_state == 'INIT':
577 574 #print >>sys.__stdout__,"PAGER state:",self.pager_state
578 575 self.pager_nb_lines = len(self.pager_lines)
579 576 self.pager_index = 0
580 577 self.pager_do_remove = False
581 578 self.text_ctrl.write('\n')
582 579 self.pager_state = 'PROCESS_LINES'
583 580
584 581 if self.pager_state == 'PROCESS_LINES':
585 582 #print >>sys.__stdout__,"PAGER state:",self.pager_state
586 583 if self.pager_do_remove == True:
587 584 self.text_ctrl.removeCurrentLine()
588 585 self.pager_do_remove = False
589 586
590 587 if self.pager_nb_lines > 10:
591 588 #print >>sys.__stdout__,"PAGER processing 10 lines"
592 589 if self.pager_index > 0:
593 590 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
594 591 else:
595 592 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
596 593
597 594 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
598 595 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
599 596 self.pager_index += 10
600 597 self.pager_nb_lines -= 10
601 598 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
602 599 self.pager_do_remove = True
603 600 self.pager_state = 'WAITING'
604 601 return
605 602 else:
606 603 #print >>sys.__stdout__,"PAGER processing last lines"
607 604 if self.pager_nb_lines > 0:
608 605 if self.pager_index > 0:
609 606 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
610 607 else:
611 608 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
612 609
613 610 self.pager_index += 1
614 611 self.pager_nb_lines -= 1
615 612 if self.pager_nb_lines > 0:
616 613 for line in self.pager_lines[self.pager_index:]:
617 614 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
618 615 self.pager_nb_lines = 0
619 616 self.pager_state = 'DONE'
620 617 self.stateShowPrompt()
621 618
622 619 #---------------------------- Key Handler --------------------------------------------
623 620 def keyPress(self, event):
624 621 '''
625 622 Key press callback with plenty of shell goodness, like history,
626 623 autocompletions, etc.
627 624 '''
628 625
629 626 if event.GetKeyCode() == ord('C'):
630 627 if event.Modifiers == wx.MOD_CONTROL:
631 628 if self.cur_state == 'WAIT_END_OF_EXECUTION':
632 629 #we raise an exception inside the IPython thread container
633 630 self.IP.ce.raise_exc(KeyboardInterrupt)
634 631 return
635 632
636 633 if event.KeyCode == wx.WXK_RETURN:
637 634 if self.cur_state == 'IDLE':
638 635 #we change the state ot the state machine
639 636 self.setCurrentState('DO_EXECUTE_LINE')
640 637 self.stateDoExecuteLine()
641 638 return
642 639 if self.pager_state == 'WAITING':
643 640 self.pager_state = 'PROCESS_LINES'
644 641 self.pager(self.doc)
645 642 return
646 643
647 644 if event.GetKeyCode() in [ord('q'),ord('Q')]:
648 645 if self.pager_state == 'WAITING':
649 646 self.pager_state = 'DONE'
650 647 self.stateShowPrompt()
651 648 return
652 649
653 650 #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL)
654 651 if self.cur_state == 'IDLE':
655 652 if event.KeyCode == wx.WXK_UP:
656 653 history = self.IP.historyBack()
657 654 self.text_ctrl.writeHistory(history)
658 655 return
659 656 if event.KeyCode == wx.WXK_DOWN:
660 657 history = self.IP.historyForward()
661 658 self.text_ctrl.writeHistory(history)
662 659 return
663 660 if event.KeyCode == wx.WXK_TAB:
664 661 #if line empty we disable tab completion
665 662 if not self.text_ctrl.getCurrentLine().strip():
666 663 self.text_ctrl.write('\t')
667 664 return
668 665 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
669 666 if len(possibilities) > 1:
670 667 cur_slice = self.text_ctrl.getCurrentLine()
671 668 self.text_ctrl.write('\n')
672 669 self.text_ctrl.writeCompletion(possibilities)
673 670 self.text_ctrl.write('\n')
674 671
675 672 self.text_ctrl.showPrompt()
676 673 self.text_ctrl.write(cur_slice)
677 674 self.text_ctrl.changeLine(completed or cur_slice)
678 675
679 676 return
680 677 event.Skip()
681 678
682 679 #---------------------------- Hook Section --------------------------------------------
683 680 def updateHistoryTracker(self,command_line):
684 681 '''
685 682 Default history tracker (does nothing)
686 683 '''
687 684 pass
688 685
689 686 def setHistoryTrackerHook(self,func):
690 687 '''
691 688 Define a new history tracker
692 689 '''
693 690 self.updateHistoryTracker = func
694 691 def updateStatusTracker(self,status):
695 692 '''
696 693 Default status tracker (does nothing)
697 694 '''
698 695 pass
699 696
700 697 def setStatusTrackerHook(self,func):
701 698 '''
702 699 Define a new status tracker
703 700 '''
704 701 self.updateStatusTracker = func
705 702
General Comments 0
You need to be logged in to leave comments. Login now