43
43
class Entry(Gtk.EventBox):
44
__gtype_name__ = "CommanderEntry"
46
def __init__(self, view):
47
Gtk.EventBox.__init__(self)
50
self.set_visible_window(False)
52
hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 3)
54
hbox.set_border_width(3)
56
# context for the view
57
self._entry = Gtk.Entry()
58
self._entry.set_has_frame(False)
59
self._entry.set_name('gedit-commander-entry')
62
css = Gtk.CssProvider()
63
css.load_from_data("""
44
__gtype_name__ = "CommanderEntry"
46
def __init__(self, view):
47
Gtk.EventBox.__init__(self)
50
self.set_visible_window(False)
52
hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 3)
54
hbox.set_border_width(3)
56
# context for the view
57
self._entry = Gtk.Entry()
58
self._entry.set_has_frame(False)
59
self._entry.set_name('gedit-commander-entry')
62
css = Gtk.CssProvider()
63
css.load_from_data("""
64
64
@binding-set terminal-like-bindings {
67
bind "<Control>W" { "delete-from-cursor" (word-ends, -1) };
68
bind "<Control>A" { "move-cursor" (buffer-ends, -1, 0) };
69
bind "<Control>U" { "delete-from-cursor" (display-line-ends, -1) };
70
bind "<Control>K" { "delete-from-cursor" (display-line-ends, 1) };
71
bind "<Control>E" { "move-cursor" (buffer-ends, 1, 0) };
72
bind "Escape" { "delete-from-cursor" (display-lines, 1) };
67
bind "<Control>W" { "delete-from-cursor" (word-ends, -1) };
68
bind "<Control>A" { "move-cursor" (buffer-ends, -1, 0) };
69
bind "<Control>U" { "delete-from-cursor" (display-line-ends, -1) };
70
bind "<Control>K" { "delete-from-cursor" (display-line-ends, 1) };
71
bind "<Control>E" { "move-cursor" (buffer-ends, 1, 0) };
72
bind "Escape" { "delete-from-cursor" (display-lines, 1) };
75
75
GtkEntry#gedit-commander-entry {
76
gtk-key-bindings: terminal-like-bindings;
76
gtk-key-bindings: terminal-like-bindings;
78
/* Override background to anything. This is weird, but doing this we can
79
then in code use widget.override_background to set the color dynamically
80
to the same color as the gedit view */
81
background: transparent;
83
box-shadow: 0 0 transparent;
80
# FIXME: remove hardcopy of 600 (GTK_STYLE_PROVIDER_PRIORITY_APPLICATION)
81
# https://bugzilla.gnome.org/show_bug.cgi?id=646860
82
self._entry.get_style_context().add_provider(css, 600)
84
self._prompt_label = Gtk.Label(label='<b>>>></b>', use_markup=True)
85
self._prompt_label.show()
87
self._entry.connect('focus-out-event', self.on_entry_focus_out)
88
self._entry.connect('key-press-event', self.on_entry_key_press)
90
self._history = History(os.path.join(GLib.get_user_config_dir(), 'gedit/commander/history'))
93
self._accel_group = None
95
hbox.pack_start(self._prompt_label, False, False, 0)
96
hbox.pack_start(self._entry, True, True, 0)
98
self.copy_style_from_view()
99
self.view_style_updated_id = self._view.connect('style-updated', self.on_view_style_updated)
103
self._entry.grab_focus()
105
self._wait_timeout = 0
106
self._info_window = None
108
self.connect('destroy', self.on_destroy)
109
self.connect_after('size-allocate', self.on_size_allocate)
110
self.view_draw_id = self._view.connect_after('draw', self.on_draw)
112
self._history_prefix = None
113
self._suspended = None
115
[0, Gdk.KEY_Up, self.on_history_move, -1],
116
[0, Gdk.KEY_Down, self.on_history_move, 1],
117
[None, Gdk.KEY_Return, self.on_execute, None],
118
[None, Gdk.KEY_KP_Enter, self.on_execute, None],
119
[0, Gdk.KEY_Tab, self.on_complete, None],
120
[0, Gdk.KEY_ISO_Left_Tab, self.on_complete, None]
123
self._re_complete = re.compile('("((?:\\\\"|[^"])*)"?|\'((?:\\\\\'|[^\'])*)\'?|[^\s]+)')
124
self._command_state = commands.Commands.State()
126
def on_view_style_updated(self, widget):
127
self.copy_style_from_view()
129
def get_border_color(self):
130
color = self.get_background_color().copy()
131
color.red = 1 - color.red
132
color.green = 1 - color.green
133
color.blue = 1 - color.blue
138
def get_background_color(self):
139
context = self._view.get_style_context()
140
return context.get_background_color(Gtk.StateFlags.NORMAL)
142
def get_foreground_color(self):
143
context = self._view.get_style_context()
144
return context.get_color(Gtk.StateFlags.NORMAL)
147
context = self._view.get_style_context()
148
return context.get_font(Gtk.StateFlags.NORMAL)
150
def copy_style_from_view(self, widget=None):
152
context = self._view.get_style_context()
153
font = context.get_font(Gtk.StateFlags.NORMAL)
155
widget.override_color(Gtk.StateFlags.NORMAL, self.get_foreground_color())
156
widget.override_background_color(Gtk.StateFlags.NORMAL, self.get_background_color())
157
widget.override_font(self.get_font())
160
self.copy_style_from_view(self._entry)
162
if self._prompt_label:
163
self.copy_style_from_view(self._prompt_label)
168
def on_size_allocate(self, widget, alloc):
169
alloc = self.get_allocation()
170
self._view.set_border_window_size(Gtk.TextWindowType.BOTTOM, alloc.height)
172
win = self._view.get_window(Gtk.TextWindowType.BOTTOM)
173
self.set_size_request(win.get_width(), -1)
175
# NOTE: we need to do this explicitly somehow, otherwise the window
176
# size will not be updated unless something else happens, not exactly
177
# sure what. This might be caused by the multi notebook, or custom
178
# animation layouting?
179
self._view.get_parent().resize_children()
182
# Attach ourselves in the text view, and position just above the
184
win = self._view.get_window(Gtk.TextWindowType.TEXT)
185
alloc = self.get_allocation()
187
self._view.set_border_window_size(Gtk.TextWindowType.BOTTOM, max(alloc.height, 1))
188
self._view.add_child_in_window(self, Gtk.TextWindowType.BOTTOM, 0, 0)
190
win = self._view.get_window(Gtk.TextWindowType.BOTTOM)
192
# Set same color as gutter, i.e. bg color of the view
193
context = self._view.get_style_context()
194
color = context.get_background_color(Gtk.StateFlags.NORMAL)
195
win.set_background_rgba(color)
198
self.set_size_request(win.get_width(), -1)
200
def on_entry_focus_out(self, widget, evnt):
201
if self._entry.get_sensitive():
204
def on_entry_key_press(self, widget, evnt):
205
state = evnt.state & Gtk.accelerator_get_default_mod_mask()
206
text = self._entry.get_text()
208
if evnt.keyval == Gdk.KEY_Escape:
209
if self._info_window:
211
self._suspended.resume()
213
if self._info_window:
214
self._info_window.destroy()
216
self._entry.set_sensitive(True)
217
elif self._accel_group:
218
self._accel_group = self._accel_group.parent
220
if not self._accel_group or not self._accel_group.parent:
221
self._entry.set_editable(True)
222
self._accel_group = None
226
self._entry.set_text('')
227
elif self._command_state:
228
self._command_state.clear()
231
self._view.grab_focus()
236
if state or self._accel_group:
237
# Check if it should be handled by the accel group
238
group = self._accel_group
240
if not self._accel_group:
241
group = commands.Commands().accelerator_group()
243
accel = group.activate(evnt.keyval, state)
245
if isinstance(accel, commands.accel_group.AccelGroup):
246
self._accel_group = accel
247
self._entry.set_text('')
248
self._entry.set_editable(False)
252
elif isinstance(accel, commands.accel_group.AccelCallback):
253
self._entry.set_editable(True)
254
self.run_command(lambda: accel.activate(self._command_state, self))
257
if not self._entry.get_editable():
260
for handler in self._handlers:
261
if (handler[0] == None or handler[0] == state) and evnt.keyval == handler[1] and handler[2](handler[3], state):
264
if self._info_window and self._info_window.empty():
265
self._info_window.destroy()
267
self._history_prefix = None
270
def on_history_move(self, direction, modifier):
271
pos = self._entry.get_position()
273
self._history.update(self._entry.get_text())
275
if self._history_prefix == None:
276
if len(self._entry.get_text()) == pos:
277
self._history_prefix = self._entry.get_chars(0, pos)
279
self._history_prefix = ''
281
if self._history_prefix == None:
284
hist = self._history_prefix
286
next = self._history.move(direction, hist)
289
self._entry.set_text(next)
290
self._entry.set_position(-1)
294
def prompt(self, pr=''):
297
if self._accel_group != None:
298
pr = '<i>%s</i>' % (saxutils.escape(self._accel_group.full_name()),)
305
self._prompt_label.set_markup('<b>>>></b>%s' % pr)
308
if self._info_window == None:
309
self._info_window = Info(self)
310
self._info_window.show()
312
self._info_window.connect('destroy', self.on_info_window_destroy)
314
def on_info_window_destroy(self, widget):
315
self._info_window = None
317
def info_show(self, text='', use_markup=False):
319
self._info_window.add_lines(text, use_markup)
321
def info_status(self, text):
323
self._info_window.status(text)
325
def info_add_action(self, stock, callback, data=None):
327
return self._info_window.add_action(stock, callback, data)
329
def command_history_done(self):
330
self._history.add(self._entry.get_text())
331
self._history_prefix = None
332
self._entry.set_text('')
334
def on_wait_cancel(self):
336
self._suspended.resume()
338
if self._cancel_button:
339
self._cancel_button.destroy()
341
if self._info_window and self._info_window.empty():
342
self._info_window.destroy()
343
self._entry.grab_focus()
344
self._entry.set_sensitive(True)
346
def _show_wait_cancel(self):
347
self._cancel_button = self.info_add_action(Gtk.STOCK_STOP, self.on_wait_cancel)
348
self.info_status('<i>Waiting to finish...</i>')
350
self._wait_timeout = 0
353
def _complete_word_match(self, match):
355
if match.group(i) != None:
356
return [match.group(i), match.start(i), match.end(i)]
358
def on_suspend_resume(self):
359
if self._wait_timeout:
360
GObject.source_remove(self._wait_timeout)
361
self._wait_timeout = 0
363
self._cancel_button.destroy()
364
self._cancel_button = None
365
self.info_status(None)
367
self._entry.set_sensitive(True)
368
self.command_history_done()
370
if self._entry.props.has_focus or (self._info_window and not self._info_window.empty()):
371
self._entry.grab_focus()
373
self.on_execute(None, 0)
375
def ellipsize(self, s, size):
380
return s[:mid] + '...' + s[-mid:]
384
Gtk.EventBox.destroy(self)
386
def run_command(self, cb):
387
self._suspended = None
392
self.command_history_done()
393
self._command_state.clear()
398
self.info_show('<b><span color="#f66">Error:</span></b> ' + saxutils.escape(str(e)), True)
400
if not isinstance(e, commands.exceptions.Execute):
401
self.info_show(self.format_trace(), False)
405
if ret == commands.result.Result.SUSPEND:
407
self._suspended = ret
408
ret.register(self.on_suspend_resume)
410
self._wait_timeout = GObject.timeout_add(500, self._show_wait_cancel)
411
self._entry.set_sensitive(False)
413
self.command_history_done()
416
if ret == commands.result.Result.PROMPT:
417
self.prompt(ret.prompt)
418
elif (ret == None or ret == commands.result.HIDE) and not self._prompt and (not self._info_window or self._info_window.empty()):
419
self._command_state.clear()
420
self._view.grab_focus()
423
self._entry.grab_focus()
427
def format_trace(self):
428
tp, val, tb = sys.exc_info()
432
thisdir = os.path.dirname(__file__)
434
# Skip frames up until after the last entry.py...
436
filename = tb.tb_frame.f_code.co_filename
438
dname = os.path.dirname(filename)
440
if not dname.startswith(thisdir):
445
msg = traceback.format_exception(tp, val, tb)
446
r = ''.join(msg[0:-1])
448
# This is done to prevent cyclic references, see python
449
# documentation on sys.exc_info
454
def on_execute(self, dummy, modifier):
455
if self._info_window and not self._suspended:
456
self._info_window.destroy()
458
text = self._entry.get_text().strip()
459
words = list(self._re_complete.finditer(text))
463
spec = self._complete_word_match(word)
464
wordsstr.append(spec[0])
466
if not wordsstr and not self._command_state:
467
self._entry.set_text('')
470
self.run_command(lambda: commands.Commands().execute(self._command_state, text, words, wordsstr, self, modifier))
474
def on_complete(self, dummy, modifier):
475
# First split all the text in words
476
text = self._entry.get_text()
477
pos = self._entry.get_position()
479
words = list(self._re_complete.finditer(text))
483
spec = self._complete_word_match(word)
484
wordsstr.append(spec[0])
486
# Find out at which word the cursor actually is
496
for idx in xrange(0, len(words)):
497
spec = self._complete_word_match(words[idx])
499
if words[idx].start(0) > pos:
500
# Empty space, new completion
501
wordsstr.insert(idx, '')
502
words.insert(idx, None)
506
# At end of word, resume completion
509
elif spec[1] <= pos and spec[2] > pos:
510
# In middle of word, do not complete
516
posidx = len(wordsstr) - 1
518
# First word completes a command, if not in any special 'mode'
519
# otherwise, relay completion to the command, or complete by advice
520
# from the 'mode' (prompt)
521
cmds = commands.Commands()
523
if not self._command_state and posidx == 0:
524
# Complete the first command
525
ret = commands.completion.command(words=wordsstr, idx=posidx)
530
if not self._command_state:
531
# Get the command first
532
cmd = commands.completion.single_command(wordsstr, 0)
537
cmd = self._command_state.top()
541
complete = cmd.autocomplete_func()
546
# 'complete' contains a dict with arg -> func to do the completion
547
# of the named argument the command (or stack item) expects
548
args, varargs = cmd.args()
550
# Remove system arguments
551
s = ['argstr', 'args', 'entry', 'view']
552
args = filter(lambda x: not x in s, args)
554
if realidx < len(args):
561
if not arg in complete:
567
spec = utils.getargspec(func)
578
if not spec.keywords:
579
for k in kwargs.keys():
580
if not k in spec.args:
585
# Can be number of arguments, or return values or simply buggy
588
traceback.print_exc()
591
if not ret or not ret[0]:
603
if words[posidx] == None:
604
# At end of everything, just append
607
self._entry.insert_text(completed, self._entry.get_text_length())
608
self._entry.set_position(-1)
610
spec = self._complete_word_match(words[posidx])
612
self._entry.delete_text(spec[1], spec[2])
613
self._entry.insert_text(completed, spec[1])
614
self._entry.set_position(spec[1] + len(completed))
618
lastpos = self._entry.get_position()
620
if not isinstance(res[0], commands.module.Module) or not res[0].commands():
621
if words[posidx] and after == ' ' and (words[posidx].group(2) != None or words[posidx].group(3) != None):
622
lastpos = lastpos + 1
624
self._entry.insert_text(after, lastpos)
625
self._entry.set_position(lastpos + 1)
626
elif completed == wordsstr[posidx] or not res[0].method:
627
self._entry.insert_text('.', lastpos)
628
self._entry.set_position(lastpos + 1)
630
if self._info_window:
631
self._info_window.destroy()
633
# Show popup with completed items
634
if self._info_window:
635
self._info_window.clear()
640
if isinstance(x, commands.method.Method):
641
ret.append('<b>' + saxutils.escape(x.name) + '</b> (<i>' + x.oneline_doc() + '</i>)')
645
self.info_show("\n".join(ret), True)
649
def on_draw(self, widget, ct):
650
win = widget.get_window(Gtk.TextWindowType.BOTTOM)
652
if not Gtk.cairo_should_draw_window(ct, win):
655
Gtk.cairo_transform_to_window(ct, widget, win)
657
color = self.get_border_color()
658
width = win.get_width()
660
ct.set_source_rgba(color.red, color.green, color.blue, color.alpha)
667
def on_destroy(self, widget):
668
self._view.set_border_window_size(Gtk.TextWindowType.BOTTOM, 0)
669
self._view.disconnect(self.view_style_updated_id)
670
self._view.disconnect(self.view_draw_id)
672
if self._info_window:
673
self._info_window.destroy()
87
# FIXME: remove hardcopy of 600 (GTK_STYLE_PROVIDER_PRIORITY_APPLICATION)
88
# https://bugzilla.gnome.org/show_bug.cgi?id=646860
89
self._entry.get_style_context().add_provider(css, 600)
91
self._prompt_label = Gtk.Label(label='<b>>>></b>', use_markup=True)
92
self._prompt_label.show()
94
self._entry.connect('focus-out-event', self.on_entry_focus_out)
95
self._entry.connect('key-press-event', self.on_entry_key_press)
97
self._history = History(os.path.join(GLib.get_user_config_dir(), 'gedit/commander/history'))
100
self._accel_group = None
102
hbox.pack_start(self._prompt_label, False, False, 0)
103
hbox.pack_start(self._entry, True, True, 0)
105
self.copy_style_from_view()
106
self.view_style_updated_id = self._view.connect('style-updated', self.on_view_style_updated)
110
self._entry.grab_focus()
112
self._wait_timeout = 0
113
self._info_window = None
115
self.connect('destroy', self.on_destroy)
116
self.connect_after('size-allocate', self.on_size_allocate)
117
self.view_draw_id = self._view.connect_after('draw', self.on_draw)
119
self._history_prefix = None
120
self._suspended = None
122
[0, Gdk.KEY_Up, self.on_history_move, -1],
123
[0, Gdk.KEY_Down, self.on_history_move, 1],
124
[None, Gdk.KEY_Return, self.on_execute, None],
125
[None, Gdk.KEY_KP_Enter, self.on_execute, None],
126
[0, Gdk.KEY_Tab, self.on_complete, None],
127
[0, Gdk.KEY_ISO_Left_Tab, self.on_complete, None]
130
self._re_complete = re.compile('("((?:\\\\"|[^"])*)"?|\'((?:\\\\\'|[^\'])*)\'?|[^\s]+)')
131
self._command_state = commands.Commands.State()
133
def on_view_style_updated(self, widget):
134
self.copy_style_from_view()
136
def get_border_color(self):
137
color = self.get_background_color().copy()
138
color.red = 1 - color.red
139
color.green = 1 - color.green
140
color.blue = 1 - color.blue
145
def get_background_color(self):
146
context = self._view.get_style_context()
147
return context.get_background_color(Gtk.StateFlags.NORMAL)
149
def get_foreground_color(self):
150
context = self._view.get_style_context()
151
return context.get_color(Gtk.StateFlags.NORMAL)
154
context = self._view.get_style_context()
155
return context.get_font(Gtk.StateFlags.NORMAL)
157
def copy_style_from_view(self, widget=None):
159
context = self._view.get_style_context()
160
font = context.get_font(Gtk.StateFlags.NORMAL)
162
widget.override_color(Gtk.StateFlags.NORMAL, self.get_foreground_color())
163
widget.override_font(self.get_font())
166
self.copy_style_from_view(self._entry)
168
if self._prompt_label:
169
self.copy_style_from_view(self._prompt_label)
174
def on_size_allocate(self, widget, alloc):
175
alloc = self.get_allocation()
176
self._view.set_border_window_size(Gtk.TextWindowType.BOTTOM, alloc.height)
178
win = self._view.get_window(Gtk.TextWindowType.BOTTOM)
179
self.set_size_request(win.get_width(), -1)
181
# NOTE: we need to do this explicitly somehow, otherwise the window
182
# size will not be updated unless something else happens, not exactly
183
# sure what. This might be caused by the multi notebook, or custom
184
# animation layouting?
185
self._view.get_parent().resize_children()
188
# Attach ourselves in the text view, and position just above the
190
win = self._view.get_window(Gtk.TextWindowType.TEXT)
191
alloc = self.get_allocation()
193
self._view.set_border_window_size(Gtk.TextWindowType.BOTTOM, max(alloc.height, 1))
194
self._view.add_child_in_window(self, Gtk.TextWindowType.BOTTOM, 0, 0)
196
win = self._view.get_window(Gtk.TextWindowType.BOTTOM)
199
self.set_size_request(win.get_width(), -1)
201
def on_entry_focus_out(self, widget, evnt):
202
if self._entry.get_sensitive():
205
def on_entry_key_press(self, widget, evnt):
206
state = evnt.state & Gtk.accelerator_get_default_mod_mask()
207
text = self._entry.get_text()
209
if evnt.keyval == Gdk.KEY_Escape:
210
if self._info_window:
212
self._suspended.resume()
214
if self._info_window:
215
self._info_window.destroy()
217
self._entry.set_sensitive(True)
218
elif self._accel_group:
219
self._accel_group = self._accel_group.parent
221
if not self._accel_group or not self._accel_group.parent:
222
self._entry.set_editable(True)
223
self._accel_group = None
227
self._entry.set_text('')
228
elif self._command_state:
229
self._command_state.clear()
232
self._view.grab_focus()
237
if state or self._accel_group:
238
# Check if it should be handled by the accel group
239
group = self._accel_group
241
if not self._accel_group:
242
group = commands.Commands().accelerator_group()
244
accel = group.activate(evnt.keyval, state)
246
if isinstance(accel, commands.accel_group.AccelGroup):
247
self._accel_group = accel
248
self._entry.set_text('')
249
self._entry.set_editable(False)
253
elif isinstance(accel, commands.accel_group.AccelCallback):
254
self._entry.set_editable(True)
255
self.run_command(lambda: accel.activate(self._command_state, self))
258
if not self._entry.get_editable():
261
for handler in self._handlers:
262
if (handler[0] == None or handler[0] == state) and evnt.keyval == handler[1] and handler[2](handler[3], state):
265
if self._info_window and self._info_window.empty():
266
self._info_window.destroy()
268
self._history_prefix = None
271
def on_history_move(self, direction, modifier):
272
pos = self._entry.get_position()
274
self._history.update(self._entry.get_text())
276
if self._history_prefix == None:
277
if len(self._entry.get_text()) == pos:
278
self._history_prefix = self._entry.get_chars(0, pos)
280
self._history_prefix = ''
282
if self._history_prefix == None:
285
hist = self._history_prefix
287
next = self._history.move(direction, hist)
290
self._entry.set_text(next)
291
self._entry.set_position(-1)
295
def prompt(self, pr=''):
298
if self._accel_group != None:
299
pr = '<i>%s</i>' % (saxutils.escape(self._accel_group.full_name()),)
306
self._prompt_label.set_markup('<b>>>></b>%s' % pr)
309
if self._info_window == None:
310
self._info_window = Info(self)
311
self._info_window.show()
313
self._info_window.connect('destroy', self.on_info_window_destroy)
315
def on_info_window_destroy(self, widget):
316
self._info_window = None
318
def info_show(self, text='', use_markup=False):
320
self._info_window.add_lines(text, use_markup)
322
def info_status(self, text):
324
self._info_window.status(text)
326
def info_add_action(self, stock, callback, data=None):
328
return self._info_window.add_action(stock, callback, data)
330
def command_history_done(self):
331
self._history.add(self._entry.get_text())
332
self._history_prefix = None
333
self._entry.set_text('')
335
def on_wait_cancel(self):
337
self._suspended.resume()
339
if self._cancel_button:
340
self._cancel_button.destroy()
342
if self._info_window and self._info_window.empty():
343
self._info_window.destroy()
344
self._entry.grab_focus()
345
self._entry.set_sensitive(True)
347
def _show_wait_cancel(self):
348
self._cancel_button = self.info_add_action(Gtk.STOCK_STOP, self.on_wait_cancel)
349
self.info_status('<i>Waiting to finish...</i>')
351
self._wait_timeout = 0
354
def _complete_word_match(self, match):
356
if match.group(i) != None:
357
return [match.group(i), match.start(i), match.end(i)]
359
def on_suspend_resume(self):
360
if self._wait_timeout:
361
GObject.source_remove(self._wait_timeout)
362
self._wait_timeout = 0
364
self._cancel_button.destroy()
365
self._cancel_button = None
366
self.info_status(None)
368
self._entry.set_sensitive(True)
369
self.command_history_done()
371
if self._entry.props.has_focus or (self._info_window and not self._info_window.empty()):
372
self._entry.grab_focus()
374
self.on_execute(None, 0)
376
def ellipsize(self, s, size):
381
return s[:mid] + '...' + s[-mid:]
385
Gtk.EventBox.destroy(self)
387
def run_command(self, cb):
388
self._suspended = None
393
self.command_history_done()
394
self._command_state.clear()
399
self.info_show('<b><span color="#f66">Error:</span></b> ' + saxutils.escape(str(e)), True)
401
if not isinstance(e, commands.exceptions.Execute):
402
self.info_show(self.format_trace(), False)
406
if ret == commands.result.Result.SUSPEND:
408
self._suspended = ret
409
ret.register(self.on_suspend_resume)
411
self._wait_timeout = GObject.timeout_add(500, self._show_wait_cancel)
412
self._entry.set_sensitive(False)
414
self.command_history_done()
417
if ret == commands.result.Result.PROMPT:
418
self.prompt(ret.prompt)
419
elif (ret == None or ret == commands.result.HIDE) and not self._prompt and (not self._info_window or self._info_window.empty()):
420
self._command_state.clear()
421
self._view.grab_focus()
424
self._entry.grab_focus()
428
def format_trace(self):
429
tp, val, tb = sys.exc_info()
433
thisdir = os.path.dirname(__file__)
435
# Skip frames up until after the last entry.py...
437
filename = tb.tb_frame.f_code.co_filename
439
dname = os.path.dirname(filename)
441
if not dname.startswith(thisdir):
446
msg = traceback.format_exception(tp, val, tb)
447
r = ''.join(msg[0:-1])
449
# This is done to prevent cyclic references, see python
450
# documentation on sys.exc_info
455
def on_execute(self, dummy, modifier):
456
if self._info_window and not self._suspended:
457
self._info_window.destroy()
459
text = self._entry.get_text().strip()
460
words = list(self._re_complete.finditer(text))
464
spec = self._complete_word_match(word)
465
wordsstr.append(spec[0])
467
if not wordsstr and not self._command_state:
468
self._entry.set_text('')
471
self.run_command(lambda: commands.Commands().execute(self._command_state, text, words, wordsstr, self, modifier))
475
def on_complete(self, dummy, modifier):
476
# First split all the text in words
477
text = self._entry.get_text()
478
pos = self._entry.get_position()
480
words = list(self._re_complete.finditer(text))
484
spec = self._complete_word_match(word)
485
wordsstr.append(spec[0])
487
# Find out at which word the cursor actually is
497
for idx in xrange(0, len(words)):
498
spec = self._complete_word_match(words[idx])
500
if words[idx].start(0) > pos:
501
# Empty space, new completion
502
wordsstr.insert(idx, '')
503
words.insert(idx, None)
507
# At end of word, resume completion
510
elif spec[1] <= pos and spec[2] > pos:
511
# In middle of word, do not complete
517
posidx = len(wordsstr) - 1
519
# First word completes a command, if not in any special 'mode'
520
# otherwise, relay completion to the command, or complete by advice
521
# from the 'mode' (prompt)
522
cmds = commands.Commands()
524
if not self._command_state and posidx == 0:
525
# Complete the first command
526
ret = commands.completion.command(words=wordsstr, idx=posidx)
531
if not self._command_state:
532
# Get the command first
533
cmd = commands.completion.single_command(wordsstr, 0)
538
cmd = self._command_state.top()
542
complete = cmd.autocomplete_func()
547
# 'complete' contains a dict with arg -> func to do the completion
548
# of the named argument the command (or stack item) expects
549
args, varargs = cmd.args()
551
# Remove system arguments
552
s = ['argstr', 'args', 'entry', 'view']
553
args = filter(lambda x: not x in s, args)
555
if realidx < len(args):
562
if not arg in complete:
568
spec = utils.getargspec(func)
579
if not spec.keywords:
580
for k in kwargs.keys():
581
if not k in spec.args:
586
# Can be number of arguments, or return values or simply buggy
589
traceback.print_exc()
592
if not ret or not ret[0]:
604
if words[posidx] == None:
605
# At end of everything, just append
608
self._entry.insert_text(completed, self._entry.get_text_length())
609
self._entry.set_position(-1)
611
spec = self._complete_word_match(words[posidx])
613
self._entry.delete_text(spec[1], spec[2])
614
self._entry.insert_text(completed, spec[1])
615
self._entry.set_position(spec[1] + len(completed))
619
lastpos = self._entry.get_position()
621
if not isinstance(res[0], commands.module.Module) or not res[0].commands():
622
if words[posidx] and after == ' ' and (words[posidx].group(2) != None or words[posidx].group(3) != None):
623
lastpos = lastpos + 1
625
self._entry.insert_text(after, lastpos)
626
self._entry.set_position(lastpos + 1)
627
elif completed == wordsstr[posidx] or not res[0].method:
628
self._entry.insert_text('.', lastpos)
629
self._entry.set_position(lastpos + 1)
631
if self._info_window:
632
self._info_window.destroy()
634
# Show popup with completed items
635
if self._info_window:
636
self._info_window.clear()
641
if isinstance(x, commands.method.Method):
642
ret.append('<b>' + saxutils.escape(x.name) + '</b> (<i>' + x.oneline_doc() + '</i>)')
646
self.info_show("\n".join(ret), True)
650
def on_draw(self, widget, ct):
651
win = widget.get_window(Gtk.TextWindowType.BOTTOM)
653
if not Gtk.cairo_should_draw_window(ct, win):
656
Gtk.cairo_transform_to_window(ct, widget, win)
658
color = self.get_border_color()
659
width = win.get_width()
661
ct.set_source_rgba(color.red, color.green, color.blue, color.alpha)
668
def on_destroy(self, widget):
669
self._view.set_border_window_size(Gtk.TextWindowType.BOTTOM, 0)
670
self._view.disconnect(self.view_style_updated_id)
671
self._view.disconnect(self.view_draw_id)
673
if self._info_window:
674
self._info_window.destroy()