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