Updated Finnish translation
[rhythmbox.git] / plugins / pythonconsole / pythonconsole.py
blobab603924c77ffa8cda2b9a4bfc94380d11ed4d9f
1 # -*- coding: utf-8 -*-
3 # pythonconsole.py
5 # Copyright (C) 2006 - Steve Frécinaux
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2, or (at your option)
10 # any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 # Parts from "Interactive Python-GTK Console" (stolen from epiphany's console.py)
22 # Copyright (C), 1998 James Henstridge <james@daa.com.au>
23 # Copyright (C), 2005 Adam Hooper <adamh@densi.com>
24 # Bits from gedit Python Console Plugin
25 # Copyrignt (C), 2005 Raphaël Slinckx
27 import string
28 import sys
29 import re
30 import traceback
31 import gobject
32 import gtk
33 import pango
34 import rhythmdb, rb
36 ui_str = """
37 <ui>
38 <menubar name="MenuBar">
39 <menu name="ToolsMenu" action="Tools">
40 <placeholder name="ToolsOps_5">
41 <menuitem name="PythonConsole" action="PythonConsole"/>
42 </placeholder>
43 </menu>
44 </menubar>
45 </ui>
46 """
48 class PythonConsolePlugin(rb.Plugin):
49 def __init__(self):
50 rb.Plugin.__init__(self)
51 self.window = None
53 def activate(self, shell):
54 data = dict()
55 manager = shell.get_player().get_property('ui-manager')
57 action = gtk.Action('PythonConsole', _('_Python Console'),
58 _("Show Rhythmbox's python console"),
59 'gnome-mime-text-x-python');
60 action.connect('activate', self.show_console, shell)
62 data['action_group'] = gtk.ActionGroup('PythonConsolePluginActions')
63 data['action_group'].add_action(action)
64 manager.insert_action_group(data['action_group'], 0)
65 data['ui_id'] = manager.add_ui_from_string(ui_str)
66 manager.ensure_update()
68 shell.set_data('PythonConsolePluginInfo', data)
70 def deactivate(self, shell):
71 data = shell.get_data('PythonConsolePluginInfo')
73 manager = shell.get_player().get_property('ui-manager')
74 manager.remove_ui(data['ui_id'])
75 manager.remove_action_group(data['action_group'])
76 manager.ensure_update()
78 shell.set_data('PythonConsolePluginInfo', None)
80 if self.window is not None:
81 self.window.destroy()
83 def show_console(self, action, shell):
84 if not self.window:
85 ns = {'__builtins__' : __builtins__,
86 'rb' : rb,
87 'rhythmdb' : rhythmdb,
88 'shell' : shell}
89 console = PythonConsole(namespace = ns,
90 destroy_cb = self.destroy_console)
91 console.set_size_request(600, 400)
92 console.eval('print "' + \
93 _('You can access the main window ' \
94 'through the \'shell\' variable :') +
95 '\\n%s" % shell', False)
97 self.window = gtk.Window()
98 self.window.set_title('Rhythmbox Python Console')
99 self.window.add(console)
100 self.window.connect('destroy', self.destroy_console)
101 self.window.show_all()
102 else:
103 self.window.show_all()
104 self.window.grab_focus()
106 def destroy_console(self, *args):
107 self.window.destroy()
108 self.window = None
110 class PythonConsole(gtk.ScrolledWindow):
111 def __init__(self, namespace = {}, destroy_cb = None):
112 gtk.ScrolledWindow.__init__(self)
114 self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC);
115 self.set_shadow_type(gtk.SHADOW_IN)
116 self.view = gtk.TextView()
117 self.view.modify_font(pango.FontDescription('Monospace'))
118 self.view.set_editable(True)
119 self.view.set_wrap_mode(gtk.WRAP_WORD_CHAR)
120 self.add(self.view)
121 self.view.show()
123 buffer = self.view.get_buffer()
124 self.normal = buffer.create_tag("normal")
125 self.error = buffer.create_tag("error")
126 self.error.set_property("foreground", "red")
127 self.command = buffer.create_tag("command")
128 self.command.set_property("foreground", "blue")
130 self.__spaces_pattern = re.compile(r'^\s+')
131 self.namespace = namespace
133 self.destroy_cb = destroy_cb
135 # Init first line
136 buffer.create_mark("input-line", buffer.get_end_iter(), True)
137 buffer.insert(buffer.get_end_iter(), ">>> ")
138 buffer.create_mark("input", buffer.get_end_iter(), True)
140 # Init history
141 self.history = ['']
142 self.history_pos = 0
143 self.current_command = ''
144 self.namespace['__history__'] = self.history
146 # Set up hooks for standard output.
147 self.stdout = gtkoutfile(self, sys.stdout.fileno(), self.normal)
148 self.stderr = gtkoutfile(self, sys.stderr.fileno(), self.error)
150 # Signals
151 self.view.connect("key-press-event", self.__key_press_event_cb)
152 buffer.connect("mark-set", self.__mark_set_cb)
155 def __key_press_event_cb(self, view, event):
156 if event.keyval == gtk.keysyms.d and \
157 event.state == gtk.gdk.CONTROL_MASK:
158 self.destroy()
160 elif event.keyval == gtk.keysyms.Return and \
161 event.state == gtk.gdk.CONTROL_MASK:
162 # Get the command
163 buffer = view.get_buffer()
164 inp_mark = buffer.get_mark("input")
165 inp = buffer.get_iter_at_mark(inp_mark)
166 cur = buffer.get_end_iter()
167 line = buffer.get_text(inp, cur)
168 self.current_command = self.current_command + line + "\n"
169 self.history_add(line)
171 # Prepare the new line
172 cur = buffer.get_end_iter()
173 buffer.insert(cur, "\n... ")
174 cur = buffer.get_end_iter()
175 buffer.move_mark(inp_mark, cur)
177 # Keep indentation of precendent line
178 spaces = re.match(self.__spaces_pattern, line)
179 if spaces is not None:
180 buffer.insert(cur, line[spaces.start() : spaces.end()])
181 cur = buffer.get_end_iter()
183 buffer.place_cursor(cur)
184 gobject.idle_add(self.scroll_to_end)
185 return True
187 elif event.keyval == gtk.keysyms.Return:
188 # Get the marks
189 buffer = view.get_buffer()
190 lin_mark = buffer.get_mark("input-line")
191 inp_mark = buffer.get_mark("input")
193 # Get the command line
194 inp = buffer.get_iter_at_mark(inp_mark)
195 cur = buffer.get_end_iter()
196 line = buffer.get_text(inp, cur)
197 self.current_command = self.current_command + line + "\n"
198 self.history_add(line)
200 # Make the line blue
201 lin = buffer.get_iter_at_mark(lin_mark)
202 buffer.apply_tag(self.command, lin, cur)
203 buffer.insert(cur, "\n")
205 # Eval the command
206 self.__run(self.current_command)
207 self.current_command = ''
209 # Prepare the new line
210 cur = buffer.get_end_iter()
211 buffer.move_mark(lin_mark, cur)
212 buffer.insert(cur, ">>> ")
213 cur = buffer.get_end_iter()
214 buffer.move_mark(inp_mark, cur)
215 buffer.place_cursor(cur)
216 gobject.idle_add(self.scroll_to_end)
217 return True
219 elif event.keyval == gtk.keysyms.KP_Down or \
220 event.keyval == gtk.keysyms.Down:
221 # Next entry from history
222 view.emit_stop_by_name("key_press_event")
223 self.history_down()
224 gobject.idle_add(self.scroll_to_end)
225 return True
227 elif event.keyval == gtk.keysyms.KP_Up or \
228 event.keyval == gtk.keysyms.Up:
229 # Previous entry from history
230 view.emit_stop_by_name("key_press_event")
231 self.history_up()
232 gobject.idle_add(self.scroll_to_end)
233 return True
235 elif event.keyval == gtk.keysyms.KP_Left or \
236 event.keyval == gtk.keysyms.Left or \
237 event.keyval == gtk.keysyms.BackSpace:
238 buffer = view.get_buffer()
239 inp = buffer.get_iter_at_mark(buffer.get_mark("input"))
240 cur = buffer.get_iter_at_mark(buffer.get_insert())
241 return inp.compare(cur) == 0
243 elif event.keyval == gtk.keysyms.Home:
244 # Go to the begin of the command instead of the begin of
245 # the line
246 buffer = view.get_buffer()
247 inp = buffer.get_iter_at_mark(buffer.get_mark("input"))
248 if event.state == gtk.gdk.SHIFT_MASK:
249 buffer.move_mark_by_name("insert", inp)
250 else:
251 buffer.place_cursor(inp)
252 return True
254 def __mark_set_cb(self, buffer, iter, name):
255 input = buffer.get_iter_at_mark(buffer.get_mark("input"))
256 pos = buffer.get_iter_at_mark(buffer.get_insert())
257 self.view.set_editable(pos.compare(input) != -1)
259 def get_command_line(self):
260 buffer = self.view.get_buffer()
261 inp = buffer.get_iter_at_mark(buffer.get_mark("input"))
262 cur = buffer.get_end_iter()
263 return buffer.get_text(inp, cur)
265 def set_command_line(self, command):
266 buffer = self.view.get_buffer()
267 mark = buffer.get_mark("input")
268 inp = buffer.get_iter_at_mark(mark)
269 cur = buffer.get_end_iter()
270 buffer.delete(inp, cur)
271 buffer.insert(inp, command)
272 buffer.select_range(buffer.get_iter_at_mark(mark),
273 buffer.get_end_iter())
274 self.view.grab_focus()
276 def history_add(self, line):
277 if line.strip() != '':
278 self.history_pos = len(self.history)
279 self.history[self.history_pos - 1] = line
280 self.history.append('')
282 def history_up(self):
283 if self.history_pos > 0:
284 self.history[self.history_pos] = self.get_command_line()
285 self.history_pos = self.history_pos - 1
286 self.set_command_line(self.history[self.history_pos])
288 def history_down(self):
289 if self.history_pos < len(self.history) - 1:
290 self.history[self.history_pos] = self.get_command_line()
291 self.history_pos = self.history_pos + 1
292 self.set_command_line(self.history[self.history_pos])
294 def scroll_to_end(self):
295 iter = self.view.get_buffer().get_end_iter()
296 self.view.scroll_to_iter(iter, 0.0)
297 return False
299 def write(self, text, tag = None):
300 buffer = self.view.get_buffer()
301 if tag is None:
302 buffer.insert(buffer.get_end_iter(), text)
303 else:
304 buffer.insert_with_tags(buffer.get_end_iter(), text, tag)
305 gobject.idle_add(self.scroll_to_end)
307 def eval(self, command, display_command = False):
308 buffer = self.view.get_buffer()
309 lin = buffer.get_mark("input-line")
310 buffer.delete(buffer.get_iter_at_mark(lin),
311 buffer.get_end_iter())
313 if isinstance(command, list) or isinstance(command, tuple):
314 for c in command:
315 if display_command:
316 self.write(">>> " + c + "\n", self.command)
317 self.__run(c)
318 else:
319 if display_command:
320 self.write(">>> " + c + "\n", self.command)
321 self.__run(command)
323 cur = buffer.get_end_iter()
324 buffer.move_mark_by_name("input-line", cur)
325 buffer.insert(cur, ">>> ")
326 cur = buffer.get_end_iter()
327 buffer.move_mark_by_name("input", cur)
328 self.view.scroll_to_iter(buffer.get_end_iter(), 0.0)
330 def __run(self, command):
331 sys.stdout, self.stdout = self.stdout, sys.stdout
332 sys.stderr, self.stderr = self.stderr, sys.stderr
334 try:
335 try:
336 r = eval(command, self.namespace, self.namespace)
337 if r is not None:
338 print `r`
339 except SyntaxError:
340 exec command in self.namespace
341 except:
342 if hasattr(sys, 'last_type') and sys.last_type == SystemExit:
343 self.destroy()
344 else:
345 traceback.print_exc()
347 sys.stdout, self.stdout = self.stdout, sys.stdout
348 sys.stderr, self.stderr = self.stderr, sys.stderr
350 def destroy(self):
351 if self.destroy_cb is not None:
352 self.destroy_cb()
354 class gtkoutfile:
355 """A fake output file object. It sends output to a TK test widget,
356 and if asked for a file number, returns one set on instance creation"""
357 def __init__(self, console, fn, tag):
358 self.fn = fn
359 self.console = console
360 self.tag = tag
361 def close(self): pass
362 def flush(self): pass
363 def fileno(self): return self.fn
364 def isatty(self): return 0
365 def read(self, a): return ''
366 def readline(self): return ''
367 def readlines(self): return []
368 def write(self, s): self.console.write(s, self.tag)
369 def writelines(self, l): self.console.write(l, self.tag)
370 def seek(self, a): raise IOError, (29, 'Illegal seek')
371 def tell(self): raise IOError, (29, 'Illegal seek')
372 truncate = tell
374 # ex:noet:ts=8: