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