##// END OF EJS Templates
Capture Xterm title sequence.
Gael Varoquaux -
Show More
@@ -1,398 +1,407 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 A Wx widget to act as a console and input commands.
3 A Wx widget to act as a console and input commands.
4
4
5 This widget deals with prompts and provides an edit buffer
5 This widget deals with prompts and provides an edit buffer
6 restricted to after the last prompt.
6 restricted to after the last prompt.
7 """
7 """
8
8
9 __docformat__ = "restructuredtext en"
9 __docformat__ = "restructuredtext en"
10
10
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12 # Copyright (C) 2008 The IPython Development Team
12 # Copyright (C) 2008 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is
14 # Distributed under the terms of the BSD License. The full license is
15 # in the file COPYING, distributed as part of this software.
15 # in the file COPYING, distributed as part of this software.
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21
21
22 import wx
22 import wx
23 import wx.stc as stc
23 import wx.stc as stc
24
24
25 from wx.py import editwindow
25 from wx.py import editwindow
26
26
27 import re
27 import re
28
28
29 # FIXME: Need to provide an API for non user-generated display on the
29 # FIXME: Need to provide an API for non user-generated display on the
30 # screen: this should not be editable by the user.
30 # screen: this should not be editable by the user.
31
31
32 if wx.Platform == '__WXMSW__':
32 if wx.Platform == '__WXMSW__':
33 _DEFAULT_SIZE = 80
33 _DEFAULT_SIZE = 80
34 else:
34 else:
35 _DEFAULT_SIZE = 10
35 _DEFAULT_SIZE = 10
36
36
37 _DEFAULT_STYLE = {
37 _DEFAULT_STYLE = {
38 'stdout' : 'fore:#0000FF',
38 'stdout' : 'fore:#0000FF',
39 'stderr' : 'fore:#007f00',
39 'stderr' : 'fore:#007f00',
40 'trace' : 'fore:#FF0000',
40 'trace' : 'fore:#FF0000',
41
41
42 'default' : 'size:%d' % _DEFAULT_SIZE,
42 'default' : 'size:%d' % _DEFAULT_SIZE,
43 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
43 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
44 'bracebad' : 'fore:#000000,back:#FF0000,bold',
44 'bracebad' : 'fore:#000000,back:#FF0000,bold',
45
45
46 # properties for the various Python lexer styles
46 # properties for the various Python lexer styles
47 'comment' : 'fore:#007F00',
47 'comment' : 'fore:#007F00',
48 'number' : 'fore:#007F7F',
48 'number' : 'fore:#007F7F',
49 'string' : 'fore:#7F007F,italic',
49 'string' : 'fore:#7F007F,italic',
50 'char' : 'fore:#7F007F,italic',
50 'char' : 'fore:#7F007F,italic',
51 'keyword' : 'fore:#00007F,bold',
51 'keyword' : 'fore:#00007F,bold',
52 'triple' : 'fore:#7F0000',
52 'triple' : 'fore:#7F0000',
53 'tripledouble': 'fore:#7F0000',
53 'tripledouble': 'fore:#7F0000',
54 'class' : 'fore:#0000FF,bold,underline',
54 'class' : 'fore:#0000FF,bold,underline',
55 'def' : 'fore:#007F7F,bold',
55 'def' : 'fore:#007F7F,bold',
56 'operator' : 'bold',
56 'operator' : 'bold',
57
57
58 }
58 }
59
59
60 # new style numbers
60 # new style numbers
61 _STDOUT_STYLE = 15
61 _STDOUT_STYLE = 15
62 _STDERR_STYLE = 16
62 _STDERR_STYLE = 16
63 _TRACE_STYLE = 17
63 _TRACE_STYLE = 17
64
64
65
65
66 #-------------------------------------------------------------------------------
66 #-------------------------------------------------------------------------------
67 # The console widget class
67 # The console widget class
68 #-------------------------------------------------------------------------------
68 #-------------------------------------------------------------------------------
69 class ConsoleWidget(editwindow.EditWindow):
69 class ConsoleWidget(editwindow.EditWindow):
70 """ Specialized styled text control view for console-like workflow.
70 """ Specialized styled text control view for console-like workflow.
71
71
72 This widget is mainly interested in dealing with the prompt and
72 This widget is mainly interested in dealing with the prompt and
73 keeping the cursor inside the editing line.
73 keeping the cursor inside the editing line.
74 """
74 """
75
75
76 title = 'Console'
77
76 style = _DEFAULT_STYLE.copy()
78 style = _DEFAULT_STYLE.copy()
77
79
78 # Translation table from ANSI escape sequences to color. Override
80 # Translation table from ANSI escape sequences to color. Override
79 # this to specify your colors.
81 # this to specify your colors.
80 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
82 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
81 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
83 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
82 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
84 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
83 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
85 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
84 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
86 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
85 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
87 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
86 '1;34': [12, 'LIGHT BLUE'], '1;35':
88 '1;34': [12, 'LIGHT BLUE'], '1;35':
87 [13, 'MEDIUM VIOLET RED'],
89 [13, 'MEDIUM VIOLET RED'],
88 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
90 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
89
91
90 # The color of the carret (call _apply_style() after setting)
92 # The color of the carret (call _apply_style() after setting)
91 carret_color = 'BLACK'
93 carret_color = 'BLACK'
92
94
93
95
94 #--------------------------------------------------------------------------
96 #--------------------------------------------------------------------------
95 # Public API
97 # Public API
96 #--------------------------------------------------------------------------
98 #--------------------------------------------------------------------------
97
99
98 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
100 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
99 size=wx.DefaultSize, style=0,
101 size=wx.DefaultSize, style=0,
100 autocomplete_mode='IPYTHON'):
102 autocomplete_mode='IPYTHON'):
101 """ Autocomplete_mode: Can be 'IPYTHON' or 'STC'
103 """ Autocomplete_mode: Can be 'IPYTHON' or 'STC'
102 'IPYTHON' show autocompletion the ipython way
104 'IPYTHON' show autocompletion the ipython way
103 'STC" show it scintilla text control way
105 'STC" show it scintilla text control way
104 """
106 """
105 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
107 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
106 self.configure_scintilla()
108 self.configure_scintilla()
107
109
108 # FIXME: we need to retrieve this from the interpreter.
110 # FIXME: we need to retrieve this from the interpreter.
109 self.prompt = \
111 self.prompt = \
110 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
112 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
111 self.new_prompt(self.prompt % 1)
113 self.new_prompt(self.prompt % 1)
112
114
113 self.autocomplete_mode = autocomplete_mode
115 self.autocomplete_mode = autocomplete_mode
114
116
115 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
117 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
116 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
118 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
117
119
118
120
119 def configure_scintilla(self):
121 def configure_scintilla(self):
120 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
122 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
121 # the widget
123 # the widget
122 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
124 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
123 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
125 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
124 # Also allow Ctrl Shift "=" for poor non US keyboard users.
126 # Also allow Ctrl Shift "=" for poor non US keyboard users.
125 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
127 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
126 stc.STC_CMD_ZOOMIN)
128 stc.STC_CMD_ZOOMIN)
127
129
128 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
130 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
129 # stc.STC_CMD_PAGEUP)
131 # stc.STC_CMD_PAGEUP)
130
132
131 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
133 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
132 # stc.STC_CMD_PAGEDOWN)
134 # stc.STC_CMD_PAGEDOWN)
133
135
134 # Keys: we need to clear some of the keys the that don't play
136 # Keys: we need to clear some of the keys the that don't play
135 # well with a console.
137 # well with a console.
136 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
138 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
137 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
139 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
138 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
140 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
139
141
140
142
141 self.SetEOLMode(stc.STC_EOL_CRLF)
143 self.SetEOLMode(stc.STC_EOL_CRLF)
142 self.SetWrapMode(stc.STC_WRAP_CHAR)
144 self.SetWrapMode(stc.STC_WRAP_CHAR)
143 self.SetWrapMode(stc.STC_WRAP_WORD)
145 self.SetWrapMode(stc.STC_WRAP_WORD)
144 self.SetBufferedDraw(True)
146 self.SetBufferedDraw(True)
145 self.SetUseAntiAliasing(True)
147 self.SetUseAntiAliasing(True)
146 self.SetLayoutCache(stc.STC_CACHE_PAGE)
148 self.SetLayoutCache(stc.STC_CACHE_PAGE)
147 self.SetUndoCollection(False)
149 self.SetUndoCollection(False)
148 self.SetUseTabs(True)
150 self.SetUseTabs(True)
149 self.SetIndent(4)
151 self.SetIndent(4)
150 self.SetTabWidth(4)
152 self.SetTabWidth(4)
151
153
152 self.EnsureCaretVisible()
154 self.EnsureCaretVisible()
153
155
154 self.SetMargins(3, 3) #text is moved away from border with 3px
156 self.SetMargins(3, 3) #text is moved away from border with 3px
155 # Suppressing Scintilla margins
157 # Suppressing Scintilla margins
156 self.SetMarginWidth(0, 0)
158 self.SetMarginWidth(0, 0)
157 self.SetMarginWidth(1, 0)
159 self.SetMarginWidth(1, 0)
158 self.SetMarginWidth(2, 0)
160 self.SetMarginWidth(2, 0)
159
161
160 self._apply_style()
162 self._apply_style()
161
163
164 # Xterm escape sequences
162 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
165 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
166 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
163
167
164 #self.SetEdgeMode(stc.STC_EDGE_LINE)
168 #self.SetEdgeMode(stc.STC_EDGE_LINE)
165 #self.SetEdgeColumn(80)
169 #self.SetEdgeColumn(80)
166
170
167 # styles
171 # styles
168 p = self.style
172 p = self.style
169 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
173 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
170 self.StyleClearAll()
174 self.StyleClearAll()
171 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
175 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
172 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
176 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
173 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
177 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
174
178
175 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
179 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
176 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
180 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
177 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
181 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
178 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
182 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
179 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
183 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
180 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
184 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
181 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
185 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
182 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
186 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
183 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
187 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
184 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
188 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
185 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
189 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
186 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
190 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
187 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
191 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
188
192
189
193
190 def write(self, text):
194 def write(self, text):
191 """ Write given text to buffer, while translating the ansi escape
195 """ Write given text to buffer, while translating the ansi escape
192 sequences.
196 sequences.
193 """
197 """
198 title = self.title_pat.split(text)
199 if len(title)>0:
200 self.title = title[-1]
201
202 text = self.title_pat.sub('', text)
194 segments = self.color_pat.split(text)
203 segments = self.color_pat.split(text)
195 segment = segments.pop(0)
204 segment = segments.pop(0)
196 self.StartStyling(self.GetLength(), 0xFF)
205 self.StartStyling(self.GetLength(), 0xFF)
197 self.AppendText(segment)
206 self.AppendText(segment)
198
207
199 if segments:
208 if segments:
200 ansi_tags = self.color_pat.findall(text)
209 ansi_tags = self.color_pat.findall(text)
201
210
202 for tag in ansi_tags:
211 for tag in ansi_tags:
203 i = segments.index(tag)
212 i = segments.index(tag)
204 self.StartStyling(self.GetLength(), 0xFF)
213 self.StartStyling(self.GetLength(), 0xFF)
205 self.AppendText(segments[i+1])
214 self.AppendText(segments[i+1])
206
215
207 if tag != '0':
216 if tag != '0':
208 self.SetStyling(len(segments[i+1]),
217 self.SetStyling(len(segments[i+1]),
209 self.ANSI_STYLES[tag][0])
218 self.ANSI_STYLES[tag][0])
210
219
211 segments.pop(i)
220 segments.pop(i)
212
221
213 self.GotoPos(self.GetLength())
222 self.GotoPos(self.GetLength())
214
223
215
224
216 def new_prompt(self, prompt):
225 def new_prompt(self, prompt):
217 """ Prints a prompt at start of line, and move the start of the
226 """ Prints a prompt at start of line, and move the start of the
218 current block there.
227 current block there.
219
228
220 The prompt can be give with ascii escape sequences.
229 The prompt can be give with ascii escape sequences.
221 """
230 """
222 self.write(prompt)
231 self.write(prompt)
223 # now we update our cursor giving end of prompt
232 # now we update our cursor giving end of prompt
224 self.current_prompt_pos = self.GetLength()
233 self.current_prompt_pos = self.GetLength()
225 self.current_prompt_line = self.GetCurrentLine()
234 self.current_prompt_line = self.GetCurrentLine()
226
235
227
236
228 def replace_current_edit_buffer(self, text):
237 def replace_current_edit_buffer(self, text):
229 """ Replace currently entered command line with given text.
238 """ Replace currently entered command line with given text.
230 """
239 """
231 self.SetSelection(self.current_prompt_pos, self.GetLength())
240 self.SetSelection(self.current_prompt_pos, self.GetLength())
232 self.ReplaceSelection(text)
241 self.ReplaceSelection(text)
233 self.GotoPos(self.GetLength())
242 self.GotoPos(self.GetLength())
234
243
235
244
236 def get_current_edit_buffer(self):
245 def get_current_edit_buffer(self):
237 """ Returns the text in current edit buffer.
246 """ Returns the text in current edit buffer.
238 """
247 """
239 return self.GetTextRange(self.current_prompt_pos,
248 return self.GetTextRange(self.current_prompt_pos,
240 self.GetLength())
249 self.GetLength())
241
250
242
251
243 #--------------------------------------------------------------------------
252 #--------------------------------------------------------------------------
244 # Private API
253 # Private API
245 #--------------------------------------------------------------------------
254 #--------------------------------------------------------------------------
246
255
247 def _apply_style(self):
256 def _apply_style(self):
248 """ Applies the colors for the different text elements and the
257 """ Applies the colors for the different text elements and the
249 carret.
258 carret.
250 """
259 """
251 self.SetCaretForeground(self.carret_color)
260 self.SetCaretForeground(self.carret_color)
252
261
253 self.StyleClearAll()
262 self.StyleClearAll()
254 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
263 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
255 "fore:#FF0000,back:#0000FF,bold")
264 "fore:#FF0000,back:#0000FF,bold")
256 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
265 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
257 "fore:#000000,back:#FF0000,bold")
266 "fore:#000000,back:#FF0000,bold")
258
267
259 for style in self.ANSI_STYLES.values():
268 for style in self.ANSI_STYLES.values():
260 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
269 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
261
270
262
271
263 def writeCompletion(self, possibilities):
272 def writeCompletion(self, possibilities):
264 if self.autocomplete_mode == 'IPYTHON':
273 if self.autocomplete_mode == 'IPYTHON':
265 max_len = len(max(possibilities, key=len))
274 max_len = len(max(possibilities, key=len))
266 max_symbol = ' '*max_len
275 max_symbol = ' '*max_len
267
276
268 #now we check how much symbol we can put on a line...
277 #now we check how much symbol we can put on a line...
269 test_buffer = max_symbol + ' '*4
278 test_buffer = max_symbol + ' '*4
270
279
271 allowed_symbols = 80/len(test_buffer)
280 allowed_symbols = 80/len(test_buffer)
272 if allowed_symbols == 0:
281 if allowed_symbols == 0:
273 allowed_symbols = 1
282 allowed_symbols = 1
274
283
275 pos = 1
284 pos = 1
276 buf = ''
285 buf = ''
277 for symbol in possibilities:
286 for symbol in possibilities:
278 #buf += symbol+'\n'#*spaces)
287 #buf += symbol+'\n'#*spaces)
279 if pos < allowed_symbols:
288 if pos < allowed_symbols:
280 spaces = max_len - len(symbol) + 4
289 spaces = max_len - len(symbol) + 4
281 buf += symbol+' '*spaces
290 buf += symbol+' '*spaces
282 pos += 1
291 pos += 1
283 else:
292 else:
284 buf += symbol+'\n'
293 buf += symbol+'\n'
285 pos = 1
294 pos = 1
286 self.write(buf)
295 self.write(buf)
287 else:
296 else:
288 possibilities.sort() # Python sorts are case sensitive
297 possibilities.sort() # Python sorts are case sensitive
289 self.AutoCompSetIgnoreCase(False)
298 self.AutoCompSetIgnoreCase(False)
290 self.AutoCompSetAutoHide(False)
299 self.AutoCompSetAutoHide(False)
291 #let compute the length ot last word
300 #let compute the length ot last word
292 splitter = [' ', '(', '[', '{']
301 splitter = [' ', '(', '[', '{']
293 last_word = self.get_current_edit_buffer()
302 last_word = self.get_current_edit_buffer()
294 for breaker in splitter:
303 for breaker in splitter:
295 last_word = last_word.split(breaker)[-1]
304 last_word = last_word.split(breaker)[-1]
296 self.AutoCompShow(len(last_word), " ".join(possibilities))
305 self.AutoCompShow(len(last_word), " ".join(possibilities))
297
306
298
307
299 def scroll_to_bottom(self):
308 def scroll_to_bottom(self):
300 maxrange = self.GetScrollRange(wx.VERTICAL)
309 maxrange = self.GetScrollRange(wx.VERTICAL)
301 self.ScrollLines(maxrange)
310 self.ScrollLines(maxrange)
302
311
303 def on_enter(self):
312 def on_enter(self):
304 """ Called when the return key is hit.
313 """ Called when the return key is hit.
305 """
314 """
306 pass
315 pass
307
316
308
317
309 def _on_key_down(self, event, skip=True):
318 def _on_key_down(self, event, skip=True):
310 """ Key press callback used for correcting behavior for
319 """ Key press callback used for correcting behavior for
311 console-like interfaces: the cursor is constraint to be after
320 console-like interfaces: the cursor is constraint to be after
312 the last prompt.
321 the last prompt.
313
322
314 Return True if event as been catched.
323 Return True if event as been catched.
315 """
324 """
316 catched = False
325 catched = False
317 # Intercept some specific keys.
326 # Intercept some specific keys.
318 if event.KeyCode == ord('L') and event.ControlDown() :
327 if event.KeyCode == ord('L') and event.ControlDown() :
319 catched = True
328 catched = True
320 self.scroll_to_bottom()
329 self.scroll_to_bottom()
321 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
330 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
322 catched = True
331 catched = True
323 self.ScrollPages(-1)
332 self.ScrollPages(-1)
324 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
333 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
325 catched = True
334 catched = True
326 self.ScrollPages(1)
335 self.ScrollPages(1)
327
336
328 if self.AutoCompActive():
337 if self.AutoCompActive():
329 event.Skip()
338 event.Skip()
330 else:
339 else:
331 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
340 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
332 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
341 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
333 catched = True
342 catched = True
334 self._on_enter()
343 self._on_enter()
335
344
336 elif event.KeyCode == wx.WXK_HOME:
345 elif event.KeyCode == wx.WXK_HOME:
337 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
346 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
338 self.GotoPos(self.current_prompt_pos)
347 self.GotoPos(self.current_prompt_pos)
339 catched = True
348 catched = True
340
349
341 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
350 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
342 # FIXME: This behavior is not ideal: if the selection
351 # FIXME: This behavior is not ideal: if the selection
343 # is already started, it will jump.
352 # is already started, it will jump.
344 self.SetSelectionStart(self.current_prompt_pos)
353 self.SetSelectionStart(self.current_prompt_pos)
345 self.SetSelectionEnd(self.GetCurrentPos())
354 self.SetSelectionEnd(self.GetCurrentPos())
346 catched = True
355 catched = True
347
356
348 elif event.KeyCode == wx.WXK_UP:
357 elif event.KeyCode == wx.WXK_UP:
349 if self.GetCurrentLine() > self.current_prompt_line:
358 if self.GetCurrentLine() > self.current_prompt_line:
350 if self.GetCurrentLine() == self.current_prompt_line + 1 \
359 if self.GetCurrentLine() == self.current_prompt_line + 1 \
351 and self.GetColumn(self.GetCurrentPos()) < \
360 and self.GetColumn(self.GetCurrentPos()) < \
352 self.GetColumn(self.current_prompt_pos):
361 self.GetColumn(self.current_prompt_pos):
353 self.GotoPos(self.current_prompt_pos)
362 self.GotoPos(self.current_prompt_pos)
354 else:
363 else:
355 event.Skip()
364 event.Skip()
356 catched = True
365 catched = True
357
366
358 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
367 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
359 if self.GetCurrentPos() > self.current_prompt_pos:
368 if self.GetCurrentPos() > self.current_prompt_pos:
360 event.Skip()
369 event.Skip()
361 catched = True
370 catched = True
362
371
363 if skip and not catched:
372 if skip and not catched:
364 event.Skip()
373 event.Skip()
365
374
366 return catched
375 return catched
367
376
368
377
369 def _on_key_up(self, event, skip=True):
378 def _on_key_up(self, event, skip=True):
370 """ If cursor is outside the editing region, put it back.
379 """ If cursor is outside the editing region, put it back.
371 """
380 """
372 event.Skip()
381 event.Skip()
373 if self.GetCurrentPos() < self.current_prompt_pos:
382 if self.GetCurrentPos() < self.current_prompt_pos:
374 self.GotoPos(self.current_prompt_pos)
383 self.GotoPos(self.current_prompt_pos)
375
384
376
385
377
386
378
387
379 if __name__ == '__main__':
388 if __name__ == '__main__':
380 # Some simple code to test the console widget.
389 # Some simple code to test the console widget.
381 class MainWindow(wx.Frame):
390 class MainWindow(wx.Frame):
382 def __init__(self, parent, id, title):
391 def __init__(self, parent, id, title):
383 wx.Frame.__init__(self, parent, id, title, size=(300,250))
392 wx.Frame.__init__(self, parent, id, title, size=(300,250))
384 self._sizer = wx.BoxSizer(wx.VERTICAL)
393 self._sizer = wx.BoxSizer(wx.VERTICAL)
385 self.console_widget = ConsoleWidget(self)
394 self.console_widget = ConsoleWidget(self)
386 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
395 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
387 self.SetSizer(self._sizer)
396 self.SetSizer(self._sizer)
388 self.SetAutoLayout(1)
397 self.SetAutoLayout(1)
389 self.Show(True)
398 self.Show(True)
390
399
391 app = wx.PySimpleApp()
400 app = wx.PySimpleApp()
392 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
401 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
393 w.SetSize((780, 460))
402 w.SetSize((780, 460))
394 w.Show()
403 w.Show()
395
404
396 app.MainLoop()
405 app.MainLoop()
397
406
398
407
General Comments 0
You need to be logged in to leave comments. Login now