##// END OF EJS Templates
Make pageup work.
Gael Varoquaux -
Show More
@@ -1,413 +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:#FFFFFF,back:#0000FF,bold',
45 'bracebad' : 'fore:#000000,back:#FF0000,bold',
45 'bracebad' : 'fore:#000000,back:#FF0000,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 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
240 # stc.STC_CMD_PAGEUP)
241
242 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
243 # stc.STC_CMD_PAGEDOWN)
244
245 # 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
246 # well with a console.
240 # well with a console.
247 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
241 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
248 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
242 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
249 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
243 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
250
244
251
245
252 self.SetEOLMode(stc.STC_EOL_CRLF)
246 self.SetEOLMode(stc.STC_EOL_CRLF)
253 self.SetWrapMode(stc.STC_WRAP_CHAR)
247 self.SetWrapMode(stc.STC_WRAP_CHAR)
254 self.SetWrapMode(stc.STC_WRAP_WORD)
248 self.SetWrapMode(stc.STC_WRAP_WORD)
255 self.SetBufferedDraw(True)
249 self.SetBufferedDraw(True)
256 self.SetUseAntiAliasing(True)
250 self.SetUseAntiAliasing(True)
257 self.SetLayoutCache(stc.STC_CACHE_PAGE)
251 self.SetLayoutCache(stc.STC_CACHE_PAGE)
258 self.SetUndoCollection(False)
252 self.SetUndoCollection(False)
259 self.SetUseTabs(True)
253 self.SetUseTabs(True)
260 self.SetIndent(4)
254 self.SetIndent(4)
261 self.SetTabWidth(4)
255 self.SetTabWidth(4)
262
256
263 self.EnsureCaretVisible()
257 self.EnsureCaretVisible()
264 # we don't want scintilla's autocompletion to choose
258 # we don't want scintilla's autocompletion to choose
265 # 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
266 # automaticaly
260 # automaticaly
267 self.AutoCompSetChooseSingle(False)
261 self.AutoCompSetChooseSingle(False)
268 self.AutoCompSetMaxHeight(10)
262 self.AutoCompSetMaxHeight(10)
269
263
270 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
271 # Suppressing Scintilla margins
265 # Suppressing Scintilla margins
272 self.SetMarginWidth(0, 0)
266 self.SetMarginWidth(0, 0)
273 self.SetMarginWidth(1, 0)
267 self.SetMarginWidth(1, 0)
274 self.SetMarginWidth(2, 0)
268 self.SetMarginWidth(2, 0)
275
269
276 self._apply_style()
270 self._apply_style()
277
271
278 # Xterm escape sequences
272 # Xterm escape sequences
279 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
273 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
280 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
274 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
281
275
282 #self.SetEdgeMode(stc.STC_EDGE_LINE)
276 #self.SetEdgeMode(stc.STC_EDGE_LINE)
283 #self.SetEdgeColumn(80)
277 #self.SetEdgeColumn(80)
284
278
285 # styles
279 # styles
286 p = self.style
280 p = self.style
287 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
281 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
288 self.StyleClearAll()
282 self.StyleClearAll()
289 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
283 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
290 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
284 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
291 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
285 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
292
286
293 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
287 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
294 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
288 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
295 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
289 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
296 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
290 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
297 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
291 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
298 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
292 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
299 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
293 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
300 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
294 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
301 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
295 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
302 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
296 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
303 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
297 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
304 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
298 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
305 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
299 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
306 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
300 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
307
301
308
302
309 def _on_key_down(self, event, skip=True):
303 def _on_key_down(self, event, skip=True):
310 """ Key press callback used for correcting behavior for
304 """ Key press callback used for correcting behavior for
311 console-like interfaces: the cursor is constraint to be after
305 console-like interfaces: the cursor is constraint to be after
312 the last prompt.
306 the last prompt.
313
307
314 Return True if event as been catched.
308 Return True if event as been catched.
315 """
309 """
316 catched = True
310 catched = True
317 # Intercept some specific keys.
311 # Intercept some specific keys.
318 if event.KeyCode == ord('L') and event.ControlDown() :
312 if event.KeyCode == ord('L') and event.ControlDown() :
319 self.scroll_to_bottom()
313 self.scroll_to_bottom()
320 elif event.KeyCode == ord('K') and event.ControlDown() :
314 elif event.KeyCode == ord('K') and event.ControlDown() :
321 self.input_buffer = ''
315 self.input_buffer = ''
322 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
316 elif event.KeyCode == wx.WXK_PAGEUP:
323 self.ScrollPages(-1)
317 self.ScrollPages(-1)
324 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
318 elif event.KeyCode == wx.WXK_PAGEDOWN:
325 self.ScrollPages(1)
319 self.ScrollPages(1)
326 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
320 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
327 self.ScrollLines(-1)
321 self.ScrollLines(-1)
328 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
322 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
329 self.ScrollLines(1)
323 self.ScrollLines(1)
330 else:
324 else:
331 catched = False
325 catched = False
332
326
333 if self.AutoCompActive():
327 if self.AutoCompActive():
334 event.Skip()
328 event.Skip()
335 else:
329 else:
336 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
330 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
337 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
331 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
338 catched = True
332 catched = True
339 self.CallTipCancel()
333 self.CallTipCancel()
340 self.write('\n', refresh=False)
334 self.write('\n', refresh=False)
341 # Under windows scintilla seems to be doing funny stuff to the
335 # Under windows scintilla seems to be doing funny stuff to the
342 # line returns here, but the getter for input_buffer filters
336 # line returns here, but the getter for input_buffer filters
343 # this out.
337 # this out.
344 if sys.platform == 'win32':
338 if sys.platform == 'win32':
345 self.input_buffer = self.input_buffer
339 self.input_buffer = self.input_buffer
346 self._on_enter()
340 self._on_enter()
347
341
348 elif event.KeyCode == wx.WXK_HOME:
342 elif event.KeyCode == wx.WXK_HOME:
349 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
343 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
350 self.GotoPos(self.current_prompt_pos)
344 self.GotoPos(self.current_prompt_pos)
351 catched = True
345 catched = True
352
346
353 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
347 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
354 # FIXME: This behavior is not ideal: if the selection
348 # FIXME: This behavior is not ideal: if the selection
355 # is already started, it will jump.
349 # is already started, it will jump.
356 self.SetSelectionStart(self.current_prompt_pos)
350 self.SetSelectionStart(self.current_prompt_pos)
357 self.SetSelectionEnd(self.GetCurrentPos())
351 self.SetSelectionEnd(self.GetCurrentPos())
358 catched = True
352 catched = True
359
353
360 elif event.KeyCode == wx.WXK_UP:
354 elif event.KeyCode == wx.WXK_UP:
361 if self.GetCurrentLine() > self.current_prompt_line:
355 if self.GetCurrentLine() > self.current_prompt_line:
362 if self.GetCurrentLine() == self.current_prompt_line + 1 \
356 if self.GetCurrentLine() == self.current_prompt_line + 1 \
363 and self.GetColumn(self.GetCurrentPos()) < \
357 and self.GetColumn(self.GetCurrentPos()) < \
364 self.GetColumn(self.current_prompt_pos):
358 self.GetColumn(self.current_prompt_pos):
365 self.GotoPos(self.current_prompt_pos)
359 self.GotoPos(self.current_prompt_pos)
366 else:
360 else:
367 event.Skip()
361 event.Skip()
368 catched = True
362 catched = True
369
363
370 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
364 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
371 if self.GetCurrentPos() > self.current_prompt_pos:
365 if self.GetCurrentPos() > self.current_prompt_pos:
372 event.Skip()
366 event.Skip()
373 catched = True
367 catched = True
374
368
375 if skip and not catched:
369 if skip and not catched:
376 # Put the cursor back in the edit region
370 # Put the cursor back in the edit region
377 if self.GetCurrentPos() < self.current_prompt_pos:
371 if self.GetCurrentPos() < self.current_prompt_pos:
378 self.GotoPos(self.current_prompt_pos)
372 self.GotoPos(self.current_prompt_pos)
379 else:
373 else:
380 event.Skip()
374 event.Skip()
381
375
382 return catched
376 return catched
383
377
384
378
385 def _on_key_up(self, event, skip=True):
379 def _on_key_up(self, event, skip=True):
386 """ If cursor is outside the editing region, put it back.
380 """ If cursor is outside the editing region, put it back.
387 """
381 """
388 event.Skip()
382 event.Skip()
389 if self.GetCurrentPos() < self.current_prompt_pos:
383 if self.GetCurrentPos() < self.current_prompt_pos:
390 self.GotoPos(self.current_prompt_pos)
384 self.GotoPos(self.current_prompt_pos)
391
385
392
386
393
387
394 if __name__ == '__main__':
388 if __name__ == '__main__':
395 # Some simple code to test the console widget.
389 # Some simple code to test the console widget.
396 class MainWindow(wx.Frame):
390 class MainWindow(wx.Frame):
397 def __init__(self, parent, id, title):
391 def __init__(self, parent, id, title):
398 wx.Frame.__init__(self, parent, id, title, size=(300,250))
392 wx.Frame.__init__(self, parent, id, title, size=(300,250))
399 self._sizer = wx.BoxSizer(wx.VERTICAL)
393 self._sizer = wx.BoxSizer(wx.VERTICAL)
400 self.console_widget = ConsoleWidget(self)
394 self.console_widget = ConsoleWidget(self)
401 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
395 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
402 self.SetSizer(self._sizer)
396 self.SetSizer(self._sizer)
403 self.SetAutoLayout(1)
397 self.SetAutoLayout(1)
404 self.Show(True)
398 self.Show(True)
405
399
406 app = wx.PySimpleApp()
400 app = wx.PySimpleApp()
407 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
401 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
408 w.SetSize((780, 460))
402 w.SetSize((780, 460))
409 w.Show()
403 w.Show()
410
404
411 app.MainLoop()
405 app.MainLoop()
412
406
413
407
General Comments 0
You need to be logged in to leave comments. Login now