##// END OF EJS Templates
Get matching parenthesis to display well.
Gael Varoquaux -
Show More
@@ -1,407 +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 import sys
26 import sys
27 LINESEP = '\n'
27 LINESEP = '\n'
28 if sys.platform == 'win32':
28 if sys.platform == 'win32':
29 LINESEP = '\n\r'
29 LINESEP = '\n\r'
30
30
31 import re
31 import re
32
32
33 # FIXME: Need to provide an API for non user-generated display on the
33 # FIXME: Need to provide an API for non user-generated display on the
34 # screen: this should not be editable by the user.
34 # screen: this should not be editable by the user.
35
35
36 _DEFAULT_SIZE = 10
36 _DEFAULT_SIZE = 10
37
37
38 _DEFAULT_STYLE = {
38 _DEFAULT_STYLE = {
39 'stdout' : 'fore:#0000FF',
39 'stdout' : 'fore:#0000FF',
40 'stderr' : 'fore:#007f00',
40 'stderr' : 'fore:#007f00',
41 'trace' : 'fore:#FF0000',
41 'trace' : 'fore:#FF0000',
42
42
43 'default' : 'size:%d' % _DEFAULT_SIZE,
43 'default' : 'size:%d' % _DEFAULT_SIZE,
44 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
44 'bracegood' : 'fore:#00AA00,back:#000000,bold',
45 'bracebad' : 'fore:#000000,back:#FF0000,bold',
45 'bracebad' : 'fore:#FF0000,back:#000000,bold',
46
46
47 # properties for the various Python lexer styles
47 # properties for the various Python lexer styles
48 'comment' : 'fore:#007F00',
48 'comment' : 'fore:#007F00',
49 'number' : 'fore:#007F7F',
49 'number' : 'fore:#007F7F',
50 'string' : 'fore:#7F007F,italic',
50 'string' : 'fore:#7F007F,italic',
51 'char' : 'fore:#7F007F,italic',
51 'char' : 'fore:#7F007F,italic',
52 'keyword' : 'fore:#00007F,bold',
52 'keyword' : 'fore:#00007F,bold',
53 'triple' : 'fore:#7F0000',
53 'triple' : 'fore:#7F0000',
54 'tripledouble' : 'fore:#7F0000',
54 'tripledouble' : 'fore:#7F0000',
55 'class' : 'fore:#0000FF,bold,underline',
55 'class' : 'fore:#0000FF,bold,underline',
56 'def' : 'fore:#007F7F,bold',
56 'def' : 'fore:#007F7F,bold',
57 'operator' : 'bold'
57 'operator' : 'bold'
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 # system colors
66 # system colors
67 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
67 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
68
68
69 #-------------------------------------------------------------------------------
69 #-------------------------------------------------------------------------------
70 # The console widget class
70 # The console widget class
71 #-------------------------------------------------------------------------------
71 #-------------------------------------------------------------------------------
72 class ConsoleWidget(editwindow.EditWindow):
72 class ConsoleWidget(editwindow.EditWindow):
73 """ Specialized styled text control view for console-like workflow.
73 """ Specialized styled text control view for console-like workflow.
74
74
75 This widget is mainly interested in dealing with the prompt and
75 This widget is mainly interested in dealing with the prompt and
76 keeping the cursor inside the editing line.
76 keeping the cursor inside the editing line.
77 """
77 """
78
78
79 # This is where the title captured from the ANSI escape sequences are
79 # This is where the title captured from the ANSI escape sequences are
80 # stored.
80 # stored.
81 title = 'Console'
81 title = 'Console'
82
82
83 # The buffer being edited.
83 # The buffer being edited.
84 def _set_input_buffer(self, string):
84 def _set_input_buffer(self, string):
85 self.SetSelection(self.current_prompt_pos, self.GetLength())
85 self.SetSelection(self.current_prompt_pos, self.GetLength())
86 self.ReplaceSelection(string)
86 self.ReplaceSelection(string)
87 self.GotoPos(self.GetLength())
87 self.GotoPos(self.GetLength())
88
88
89 def _get_input_buffer(self):
89 def _get_input_buffer(self):
90 """ Returns the text in current edit buffer.
90 """ Returns the text in current edit buffer.
91 """
91 """
92 input_buffer = self.GetTextRange(self.current_prompt_pos,
92 input_buffer = self.GetTextRange(self.current_prompt_pos,
93 self.GetLength())
93 self.GetLength())
94 input_buffer = input_buffer.replace(LINESEP, '\n')
94 input_buffer = input_buffer.replace(LINESEP, '\n')
95 return input_buffer
95 return input_buffer
96
96
97 input_buffer = property(_get_input_buffer, _set_input_buffer)
97 input_buffer = property(_get_input_buffer, _set_input_buffer)
98
98
99 style = _DEFAULT_STYLE.copy()
99 style = _DEFAULT_STYLE.copy()
100
100
101 # Translation table from ANSI escape sequences to color. Override
101 # Translation table from ANSI escape sequences to color. Override
102 # this to specify your colors.
102 # this to specify your colors.
103 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
103 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
104 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
104 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
105 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
105 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
106 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
106 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
107 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
107 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
108 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
108 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
109 '1;34': [12, 'LIGHT BLUE'], '1;35':
109 '1;34': [12, 'LIGHT BLUE'], '1;35':
110 [13, 'MEDIUM VIOLET RED'],
110 [13, 'MEDIUM VIOLET RED'],
111 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
111 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
112
112
113 # The color of the carret (call _apply_style() after setting)
113 # The color of the carret (call _apply_style() after setting)
114 carret_color = 'BLACK'
114 carret_color = 'BLACK'
115
115
116 #--------------------------------------------------------------------------
116 #--------------------------------------------------------------------------
117 # Public API
117 # Public API
118 #--------------------------------------------------------------------------
118 #--------------------------------------------------------------------------
119
119
120 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
120 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
121 size=wx.DefaultSize, style=0, ):
121 size=wx.DefaultSize, style=0, ):
122 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
122 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
123 self._configure_scintilla()
123 self._configure_scintilla()
124
124
125 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
125 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
126 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
126 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
127
127
128
128
129 def write(self, text, refresh=True):
129 def write(self, text, refresh=True):
130 """ Write given text to buffer, while translating the ansi escape
130 """ Write given text to buffer, while translating the ansi escape
131 sequences.
131 sequences.
132 """
132 """
133 # XXX: do not put print statements to sys.stdout/sys.stderr in
133 # XXX: do not put print statements to sys.stdout/sys.stderr in
134 # this method, the print statements will call this method, as
134 # this method, the print statements will call this method, as
135 # you will end up with an infinit loop
135 # you will end up with an infinit loop
136 title = self.title_pat.split(text)
136 title = self.title_pat.split(text)
137 if len(title)>1:
137 if len(title)>1:
138 self.title = title[-2]
138 self.title = title[-2]
139
139
140 text = self.title_pat.sub('', text)
140 text = self.title_pat.sub('', text)
141 segments = self.color_pat.split(text)
141 segments = self.color_pat.split(text)
142 segment = segments.pop(0)
142 segment = segments.pop(0)
143 self.GotoPos(self.GetLength())
143 self.GotoPos(self.GetLength())
144 self.StartStyling(self.GetLength(), 0xFF)
144 self.StartStyling(self.GetLength(), 0xFF)
145 try:
145 try:
146 self.AppendText(segment)
146 self.AppendText(segment)
147 except UnicodeDecodeError:
147 except UnicodeDecodeError:
148 # XXX: Do I really want to skip the exception?
148 # XXX: Do I really want to skip the exception?
149 pass
149 pass
150
150
151 if segments:
151 if segments:
152 for ansi_tag, text in zip(segments[::2], segments[1::2]):
152 for ansi_tag, text in zip(segments[::2], segments[1::2]):
153 self.StartStyling(self.GetLength(), 0xFF)
153 self.StartStyling(self.GetLength(), 0xFF)
154 try:
154 try:
155 self.AppendText(text)
155 self.AppendText(text)
156 except UnicodeDecodeError:
156 except UnicodeDecodeError:
157 # XXX: Do I really want to skip the exception?
157 # XXX: Do I really want to skip the exception?
158 pass
158 pass
159
159
160 if ansi_tag not in self.ANSI_STYLES:
160 if ansi_tag not in self.ANSI_STYLES:
161 style = 0
161 style = 0
162 else:
162 else:
163 style = self.ANSI_STYLES[ansi_tag][0]
163 style = self.ANSI_STYLES[ansi_tag][0]
164
164
165 self.SetStyling(len(text), style)
165 self.SetStyling(len(text), style)
166
166
167 self.GotoPos(self.GetLength())
167 self.GotoPos(self.GetLength())
168 if refresh:
168 if refresh:
169 wx.Yield()
169 wx.Yield()
170
170
171
171
172 def new_prompt(self, prompt):
172 def new_prompt(self, prompt):
173 """ Prints a prompt at start of line, and move the start of the
173 """ Prints a prompt at start of line, and move the start of the
174 current block there.
174 current block there.
175
175
176 The prompt can be given with ascii escape sequences.
176 The prompt can be given with ascii escape sequences.
177 """
177 """
178 self.write(prompt)
178 self.write(prompt)
179 # now we update our cursor giving end of prompt
179 # now we update our cursor giving end of prompt
180 self.current_prompt_pos = self.GetLength()
180 self.current_prompt_pos = self.GetLength()
181 self.current_prompt_line = self.GetCurrentLine()
181 self.current_prompt_line = self.GetCurrentLine()
182 wx.Yield()
182 wx.Yield()
183 self.EnsureCaretVisible()
183 self.EnsureCaretVisible()
184
184
185
185
186 def scroll_to_bottom(self):
186 def scroll_to_bottom(self):
187 maxrange = self.GetScrollRange(wx.VERTICAL)
187 maxrange = self.GetScrollRange(wx.VERTICAL)
188 self.ScrollLines(maxrange)
188 self.ScrollLines(maxrange)
189
189
190
190
191 def pop_completion(self, possibilities, offset=0):
191 def pop_completion(self, possibilities, offset=0):
192 """ Pops up an autocompletion menu. Offset is the offset
192 """ Pops up an autocompletion menu. Offset is the offset
193 in characters of the position at which the menu should
193 in characters of the position at which the menu should
194 appear, relativ to the cursor.
194 appear, relativ to the cursor.
195 """
195 """
196 self.AutoCompSetIgnoreCase(False)
196 self.AutoCompSetIgnoreCase(False)
197 self.AutoCompSetAutoHide(False)
197 self.AutoCompSetAutoHide(False)
198 self.AutoCompSetMaxHeight(len(possibilities))
198 self.AutoCompSetMaxHeight(len(possibilities))
199 self.AutoCompShow(offset, " ".join(possibilities))
199 self.AutoCompShow(offset, " ".join(possibilities))
200
200
201
201
202 def get_line_width(self):
202 def get_line_width(self):
203 """ Return the width of the line in characters.
203 """ Return the width of the line in characters.
204 """
204 """
205 return self.GetSize()[0]/self.GetCharWidth()
205 return self.GetSize()[0]/self.GetCharWidth()
206
206
207
207
208 #--------------------------------------------------------------------------
208 #--------------------------------------------------------------------------
209 # Private API
209 # Private API
210 #--------------------------------------------------------------------------
210 #--------------------------------------------------------------------------
211
211
212 def _apply_style(self):
212 def _apply_style(self):
213 """ Applies the colors for the different text elements and the
213 """ Applies the colors for the different text elements and the
214 carret.
214 carret.
215 """
215 """
216 self.SetCaretForeground(self.carret_color)
216 self.SetCaretForeground(self.carret_color)
217
217
218 #self.StyleClearAll()
218 #self.StyleClearAll()
219 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
219 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
220 "fore:#FF0000,back:#0000FF,bold")
220 "fore:#FF0000,back:#0000FF,bold")
221 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
221 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
222 "fore:#000000,back:#FF0000,bold")
222 "fore:#000000,back:#FF0000,bold")
223
223
224 for style in self.ANSI_STYLES.values():
224 for style in self.ANSI_STYLES.values():
225 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
225 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
226
226
227
227
228 def _configure_scintilla(self):
228 def _configure_scintilla(self):
229 self.SetEOLMode(stc.STC_EOL_LF)
229 self.SetEOLMode(stc.STC_EOL_LF)
230
230
231 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
231 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
232 # the widget
232 # the widget
233 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
233 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
234 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
234 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
235 # Also allow Ctrl Shift "=" for poor non US keyboard users.
235 # Also allow Ctrl Shift "=" for poor non US keyboard users.
236 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
236 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
237 stc.STC_CMD_ZOOMIN)
237 stc.STC_CMD_ZOOMIN)
238
238
239 # Keys: we need to clear some of the keys the that don't play
239 # Keys: we need to clear some of the keys the that don't play
240 # well with a console.
240 # well with a console.
241 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
241 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
242 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
242 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
243 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
243 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
244
244
245
245
246 self.SetEOLMode(stc.STC_EOL_CRLF)
246 self.SetEOLMode(stc.STC_EOL_CRLF)
247 self.SetWrapMode(stc.STC_WRAP_CHAR)
247 self.SetWrapMode(stc.STC_WRAP_CHAR)
248 self.SetWrapMode(stc.STC_WRAP_WORD)
248 self.SetWrapMode(stc.STC_WRAP_WORD)
249 self.SetBufferedDraw(True)
249 self.SetBufferedDraw(True)
250 self.SetUseAntiAliasing(True)
250 self.SetUseAntiAliasing(True)
251 self.SetLayoutCache(stc.STC_CACHE_PAGE)
251 self.SetLayoutCache(stc.STC_CACHE_PAGE)
252 self.SetUndoCollection(False)
252 self.SetUndoCollection(False)
253 self.SetUseTabs(True)
253 self.SetUseTabs(True)
254 self.SetIndent(4)
254 self.SetIndent(4)
255 self.SetTabWidth(4)
255 self.SetTabWidth(4)
256
256
257 self.EnsureCaretVisible()
257 self.EnsureCaretVisible()
258 # we don't want scintilla's autocompletion to choose
258 # we don't want scintilla's autocompletion to choose
259 # automaticaly out of a single choice list, as we pop it up
259 # automaticaly out of a single choice list, as we pop it up
260 # automaticaly
260 # automaticaly
261 self.AutoCompSetChooseSingle(False)
261 self.AutoCompSetChooseSingle(False)
262 self.AutoCompSetMaxHeight(10)
262 self.AutoCompSetMaxHeight(10)
263
263
264 self.SetMargins(3, 3) #text is moved away from border with 3px
264 self.SetMargins(3, 3) #text is moved away from border with 3px
265 # Suppressing Scintilla margins
265 # Suppressing Scintilla margins
266 self.SetMarginWidth(0, 0)
266 self.SetMarginWidth(0, 0)
267 self.SetMarginWidth(1, 0)
267 self.SetMarginWidth(1, 0)
268 self.SetMarginWidth(2, 0)
268 self.SetMarginWidth(2, 0)
269
269
270 self._apply_style()
270 self._apply_style()
271
271
272 # Xterm escape sequences
272 # Xterm escape sequences
273 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
273 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
274 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
274 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
275
275
276 #self.SetEdgeMode(stc.STC_EDGE_LINE)
276 #self.SetEdgeMode(stc.STC_EDGE_LINE)
277 #self.SetEdgeColumn(80)
277 #self.SetEdgeColumn(80)
278
278
279 # styles
279 # styles
280 p = self.style
280 p = self.style
281 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
281 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
282 self.StyleClearAll()
282 self.StyleClearAll()
283 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
283 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
284 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
284 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
285 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
285 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
286
286
287 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
287 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
288 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
288 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
289 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
289 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
290 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
290 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
291 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
291 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
292 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
292 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
293 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
293 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
294 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
294 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
295 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
295 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
296 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
296 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
297 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
297 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
298 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
298 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
299 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
299 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
300 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
300 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
301
301
302
302
303 def _on_key_down(self, event, skip=True):
303 def _on_key_down(self, event, skip=True):
304 """ Key press callback used for correcting behavior for
304 """ Key press callback used for correcting behavior for
305 console-like interfaces: the cursor is constraint to be after
305 console-like interfaces: the cursor is constraint to be after
306 the last prompt.
306 the last prompt.
307
307
308 Return True if event as been catched.
308 Return True if event as been catched.
309 """
309 """
310 catched = True
310 catched = True
311 # Intercept some specific keys.
311 # Intercept some specific keys.
312 if event.KeyCode == ord('L') and event.ControlDown() :
312 if event.KeyCode == ord('L') and event.ControlDown() :
313 self.scroll_to_bottom()
313 self.scroll_to_bottom()
314 elif event.KeyCode == ord('K') and event.ControlDown() :
314 elif event.KeyCode == ord('K') and event.ControlDown() :
315 self.input_buffer = ''
315 self.input_buffer = ''
316 elif event.KeyCode == wx.WXK_PAGEUP:
316 elif event.KeyCode == wx.WXK_PAGEUP:
317 self.ScrollPages(-1)
317 self.ScrollPages(-1)
318 elif event.KeyCode == wx.WXK_PAGEDOWN:
318 elif event.KeyCode == wx.WXK_PAGEDOWN:
319 self.ScrollPages(1)
319 self.ScrollPages(1)
320 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
320 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
321 self.ScrollLines(-1)
321 self.ScrollLines(-1)
322 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
322 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
323 self.ScrollLines(1)
323 self.ScrollLines(1)
324 else:
324 else:
325 catched = False
325 catched = False
326
326
327 if self.AutoCompActive():
327 if self.AutoCompActive():
328 event.Skip()
328 event.Skip()
329 else:
329 else:
330 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
330 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
331 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
331 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
332 catched = True
332 catched = True
333 self.CallTipCancel()
333 self.CallTipCancel()
334 self.write('\n', refresh=False)
334 self.write('\n', refresh=False)
335 # Under windows scintilla seems to be doing funny stuff to the
335 # Under windows scintilla seems to be doing funny stuff to the
336 # line returns here, but the getter for input_buffer filters
336 # line returns here, but the getter for input_buffer filters
337 # this out.
337 # this out.
338 if sys.platform == 'win32':
338 if sys.platform == 'win32':
339 self.input_buffer = self.input_buffer
339 self.input_buffer = self.input_buffer
340 self._on_enter()
340 self._on_enter()
341
341
342 elif event.KeyCode == wx.WXK_HOME:
342 elif event.KeyCode == wx.WXK_HOME:
343 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
343 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
344 self.GotoPos(self.current_prompt_pos)
344 self.GotoPos(self.current_prompt_pos)
345 catched = True
345 catched = True
346
346
347 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
347 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
348 # FIXME: This behavior is not ideal: if the selection
348 # FIXME: This behavior is not ideal: if the selection
349 # is already started, it will jump.
349 # is already started, it will jump.
350 self.SetSelectionStart(self.current_prompt_pos)
350 self.SetSelectionStart(self.current_prompt_pos)
351 self.SetSelectionEnd(self.GetCurrentPos())
351 self.SetSelectionEnd(self.GetCurrentPos())
352 catched = True
352 catched = True
353
353
354 elif event.KeyCode == wx.WXK_UP:
354 elif event.KeyCode == wx.WXK_UP:
355 if self.GetCurrentLine() > self.current_prompt_line:
355 if self.GetCurrentLine() > self.current_prompt_line:
356 if self.GetCurrentLine() == self.current_prompt_line + 1 \
356 if self.GetCurrentLine() == self.current_prompt_line + 1 \
357 and self.GetColumn(self.GetCurrentPos()) < \
357 and self.GetColumn(self.GetCurrentPos()) < \
358 self.GetColumn(self.current_prompt_pos):
358 self.GetColumn(self.current_prompt_pos):
359 self.GotoPos(self.current_prompt_pos)
359 self.GotoPos(self.current_prompt_pos)
360 else:
360 else:
361 event.Skip()
361 event.Skip()
362 catched = True
362 catched = True
363
363
364 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
364 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
365 if self.GetCurrentPos() > self.current_prompt_pos:
365 if self.GetCurrentPos() > self.current_prompt_pos:
366 event.Skip()
366 event.Skip()
367 catched = True
367 catched = True
368
368
369 if skip and not catched:
369 if skip and not catched:
370 # Put the cursor back in the edit region
370 # Put the cursor back in the edit region
371 if self.GetCurrentPos() < self.current_prompt_pos:
371 if self.GetCurrentPos() < self.current_prompt_pos:
372 self.GotoPos(self.current_prompt_pos)
372 self.GotoPos(self.current_prompt_pos)
373 else:
373 else:
374 event.Skip()
374 event.Skip()
375
375
376 return catched
376 return catched
377
377
378
378
379 def _on_key_up(self, event, skip=True):
379 def _on_key_up(self, event, skip=True):
380 """ If cursor is outside the editing region, put it back.
380 """ If cursor is outside the editing region, put it back.
381 """
381 """
382 event.Skip()
382 event.Skip()
383 if self.GetCurrentPos() < self.current_prompt_pos:
383 if self.GetCurrentPos() < self.current_prompt_pos:
384 self.GotoPos(self.current_prompt_pos)
384 self.GotoPos(self.current_prompt_pos)
385
385
386
386
387
387
388 if __name__ == '__main__':
388 if __name__ == '__main__':
389 # Some simple code to test the console widget.
389 # Some simple code to test the console widget.
390 class MainWindow(wx.Frame):
390 class MainWindow(wx.Frame):
391 def __init__(self, parent, id, title):
391 def __init__(self, parent, id, title):
392 wx.Frame.__init__(self, parent, id, title, size=(300,250))
392 wx.Frame.__init__(self, parent, id, title, size=(300,250))
393 self._sizer = wx.BoxSizer(wx.VERTICAL)
393 self._sizer = wx.BoxSizer(wx.VERTICAL)
394 self.console_widget = ConsoleWidget(self)
394 self.console_widget = ConsoleWidget(self)
395 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
395 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
396 self.SetSizer(self._sizer)
396 self.SetSizer(self._sizer)
397 self.SetAutoLayout(1)
397 self.SetAutoLayout(1)
398 self.Show(True)
398 self.Show(True)
399
399
400 app = wx.PySimpleApp()
400 app = wx.PySimpleApp()
401 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
401 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
402 w.SetSize((780, 460))
402 w.SetSize((780, 460))
403 w.Show()
403 w.Show()
404
404
405 app.MainLoop()
405 app.MainLoop()
406
406
407
407
General Comments 0
You need to be logged in to leave comments. Login now