From bf81c2c834c7d7d9632a93bfb009ae05f35811dc Mon Sep 17 00:00:00 2001 From: Sayamindu Dasgupta Date: Sun, 06 Sep 2009 20:01:32 +0000 Subject: Merge branch 'master' of git://git.sugarlabs.org/terminal/toolbars --- diff --git a/icons/tab-add.svg b/icons/tab-add.svg new file mode 100644 index 0000000..c1457bd --- /dev/null +++ b/icons/tab-add.svg @@ -0,0 +1,12 @@ + + +]> + + + + + + + + \ No newline at end of file diff --git a/icons/tab-next.svg b/icons/tab-next.svg new file mode 100644 index 0000000..9b29d8d --- /dev/null +++ b/icons/tab-next.svg @@ -0,0 +1,12 @@ + + +]> + + + + + + + + \ No newline at end of file diff --git a/icons/tab-previous.svg b/icons/tab-previous.svg new file mode 100644 index 0000000..6861c30 --- /dev/null +++ b/icons/tab-previous.svg @@ -0,0 +1,12 @@ + + +]> + + + + + + + + \ No newline at end of file diff --git a/icons/tab-remove.svg b/icons/tab-remove.svg new file mode 100644 index 0000000..8521c5b --- /dev/null +++ b/icons/tab-remove.svg @@ -0,0 +1,12 @@ + + +]> + + + + + + + + \ No newline at end of file diff --git a/icons/toolbar-tab.svg b/icons/toolbar-tab.svg new file mode 100644 index 0000000..4d69921 --- /dev/null +++ b/icons/toolbar-tab.svg @@ -0,0 +1,12 @@ + + +]> + + + + + + + + \ No newline at end of file diff --git a/terminal.py b/terminal.py index cc0a868..d00be08 100644 --- a/terminal.py +++ b/terminal.py @@ -1,5 +1,6 @@ # Copyright (C) 2007, Eduardo Silva . # Copyright (C) 2008, One Laptop Per Child +# Copyright (C) 2009, Simon Schampijer # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,180 +16,241 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -import os, os.path, simplejson, ConfigParser - -from gettext import gettext as _ - -# Initialize logging. +import os +import simplejson +import ConfigParser import logging -log = logging.getLogger('Terminal') -log.setLevel(logging.DEBUG) -logging.basicConfig() +from gettext import gettext as _ import gtk import vte import pango -import sugar.graphics.toolbutton -import sugar.activity.activity -import sugar.env +from sugar.graphics.toolbutton import ToolButton +from sugar.graphics.toolbarbox import ToolbarBox +from sugar.graphics.toolbarbox import ToolbarButton +from sugar.activity.widgets import ActivityToolbarButton +from sugar.activity.widgets import StopButton +from sugar.activity import activity +from sugar import env MASKED_ENVIRONMENT = [ 'DBUS_SESSION_BUS_ADDRESS', - 'PPID' -] + 'PPID'] + +log = logging.getLogger('Terminal') +log.setLevel(logging.DEBUG) +logging.basicConfig() -class TerminalActivity(sugar.activity.activity.Activity): + +class TerminalActivity(activity.Activity): def __init__(self, handle): - sugar.activity.activity.Activity.__init__(self, handle) - - self.data_file = None - - self.set_title(_('Terminal Activity')) - - # Non-working attempt to hide the Escape key from Sugar. - #self.connect('key-press-event', self._key_press_cb) - - toolbox = sugar.activity.activity.ActivityToolbox(self) - - editbar = sugar.activity.activity.EditToolbar() - toolbox.add_toolbar(_('Edit'), editbar) - editbar.show() - editbar.undo.props.visible = False - editbar.redo.props.visible = False - editbar.separator.props.visible = False - editbar.copy.connect('clicked', self._copy_cb) - editbar.copy.props.accelerator = 'C' - editbar.paste.connect('clicked', self._paste_cb) - editbar.paste.props.accelerator = 'V' - - newtabbtn = sugar.graphics.toolbutton.ToolButton('list-add') - newtabbtn.set_tooltip(_("Open New Tab")) - newtabbtn.props.accelerator = 'T' - newtabbtn.connect('clicked', self._open_tab_cb) - - deltabbtn = sugar.graphics.toolbutton.ToolButton('list-remove') - deltabbtn.set_tooltip(_("Close Tab")) - deltabbtn.props.accelerator = 'X' - deltabbtn.connect('clicked', self._close_tab_cb) - - tabsep = gtk.SeparatorToolItem() - tabsep.set_expand(True) - tabsep.set_draw(False) + activity.Activity.__init__(self, handle) + + self.max_participants = 1 + + toolbar_box = ToolbarBox() + + activity_button = ActivityToolbarButton(self) + toolbar_box.toolbar.insert(activity_button, 0) + activity_button.page.keep.props.accelerator = 'S' + activity_button.show() + + edit_toolbar = self._create_edit_toolbar() + edit_toolbar_button = ToolbarButton( + page=edit_toolbar, + icon_name='toolbar-edit') + edit_toolbar.show() + toolbar_box.toolbar.insert(edit_toolbar_button, -1) + edit_toolbar_button.show() + + view_toolbar = self._create_view_toolbar() + view_toolbar_button = ToolbarButton( + page=view_toolbar, + icon_name='toolbar-view') + view_toolbar.show() + toolbar_box.toolbar.insert(view_toolbar_button, -1) + view_toolbar_button.show() + + self._delete_tab_toolbar = None + self._previous_tab_toolbar = None + self._next_tab_toolbar = None + tab_toolbar = self._create_tab_toolbar() + tab_toolbar_button = ToolbarButton( + page=tab_toolbar, + icon_name='toolbar-tab') + tab_toolbar.show() + toolbar_box.toolbar.insert(tab_toolbar_button, -1) + tab_toolbar_button.show() # Add a button that will be used to become root easily. - rootbtn = sugar.graphics.toolbutton.ToolButton('activity-become-root') - rootbtn.set_tooltip(_('Become root')) - rootbtn.connect('clicked', self._become_root_cb) - - prevtabbtn = sugar.graphics.toolbutton.ToolButton('go-previous') - prevtabbtn.set_tooltip(_("Previous Tab")) - prevtabbtn.props.accelerator = 'Left' - prevtabbtn.connect('clicked', self._prev_tab_cb) - - nexttabbtn = sugar.graphics.toolbutton.ToolButton('go-next') - nexttabbtn.set_tooltip(_("Next Tab")) - nexttabbtn.props.accelerator = 'Right' - nexttabbtn.connect('clicked', self._next_tab_cb) - - tabbar = gtk.Toolbar() - tabbar.insert(newtabbtn, -1) - tabbar.insert(deltabbtn, -1) - tabbar.insert(tabsep, -1) - tabbar.insert(rootbtn, -1) - tabbar.insert(prevtabbtn, -1) - tabbar.insert(nexttabbtn, -1) - tabbar.show_all() - - toolbox.add_toolbar(_('Tab'), tabbar) - - activity_toolbar = toolbox.get_activity_toolbar() - activity_toolbar.share.props.visible = False - activity_toolbar.keep.props.visible = False - activity_toolbar.keep.props.accelerator = 'S' - activity_toolbar.stop.props.accelerator = 'Q' - - fullscreenbtn = sugar.graphics.toolbutton.ToolButton('view-fullscreen') - fullscreenbtn.set_tooltip(_("Fullscreen")) - fullscreenbtn.props.accelerator = 'Enter' - fullscreenbtn.connect('clicked', self._fullscreen_cb) - activity_toolbar.insert(fullscreenbtn, 2) - fullscreenbtn.show() - - self.set_toolbox(toolbox) - toolbox.show() - - self.notebook = gtk.Notebook() - self.notebook.set_property("tab-pos", gtk.POS_BOTTOM) - self.notebook.set_scrollable(True) - self.notebook.show() - - self.set_canvas(self.notebook) + root_button = ToolButton('activity-become-root') + root_button.set_tooltip(_('Become root')) + root_button.connect('clicked', self.__become_root_cb) + toolbar_box.toolbar.insert(root_button, -1) + root_button.show() + + separator = gtk.SeparatorToolItem() + separator.props.draw = False + separator.set_expand(True) + toolbar_box.toolbar.insert(separator, -1) + separator.show() + + stop_button = StopButton(self) + stop_button.props.accelerator = 'Q' + toolbar_box.toolbar.insert(stop_button, -1) + stop_button.show() + + self.set_toolbar_box(toolbar_box) + toolbar_box.show() + + self._notebook = gtk.Notebook() + self._notebook.set_property("tab-pos", gtk.POS_TOP) + self._notebook.set_scrollable(True) + self._notebook.show() + + self.set_canvas(self._notebook) self._create_tab(None) - def _open_tab_cb(self, btn): - index = self._create_tab(None) - self.notebook.page = index + def _create_edit_toolbar(self): + edit_toolbar = activity.EditToolbar() + edit_toolbar.undo.props.visible = False + edit_toolbar.redo.props.visible = False + edit_toolbar.separator.props.visible = False + edit_toolbar.copy.connect('clicked', self.__copy_cb) + edit_toolbar.copy.props.accelerator = 'C' + edit_toolbar.paste.connect('clicked', self.__paste_cb) + edit_toolbar.paste.props.accelerator = 'V' + return edit_toolbar + + def __copy_cb(self, button): + vt = self._notebook.get_nth_page(self._notebook.get_current_page()).vt + if vt.get_has_selection(): + vt.copy_clipboard() - def _close_tab_cb(self, btn): - self._close_tab(self.notebook.props.page) + def __paste_cb(self, button): + vt = self._notebook.get_nth_page(self._notebook.get_current_page()).vt + vt.paste_clipboard() + + def _create_view_toolbar(self): + view_toolbar = gtk.Toolbar() + fullscreen_button = ToolButton('view-fullscreen') + fullscreen_button.set_tooltip(_("Fullscreen")) + fullscreen_button.props.accelerator = 'Enter' + fullscreen_button.connect('clicked', self.__fullscreen_cb) + view_toolbar.insert(fullscreen_button, -1) + fullscreen_button.show() + return view_toolbar + + def __fullscreen_cb(self, button): + self.fullscreen() - def _prev_tab_cb(self, btn): - if self.notebook.props.page == 0: - self.notebook.props.page = self.notebook.get_n_pages() - 1 + def _create_tab_toolbar(self): + tab_toolbar = gtk.Toolbar() + new_tab_button = ToolButton('tab-add') + new_tab_button.set_tooltip(_("Add New Tab")) + new_tab_button.props.accelerator = 'T' + new_tab_button.connect('clicked', self.__open_tab_cb) + tab_toolbar.insert(new_tab_button, -1) + new_tab_button.show() + + self._delete_tab_button = ToolButton('tab-remove') + self._delete_tab_button.set_tooltip(_("Remove Tab")) + self._delete_tab_button.props.accelerator = 'X' + self._delete_tab_button.props.sensitive = False + self._delete_tab_button.connect('clicked', self.__close_tab_cb) + tab_toolbar.insert(self._delete_tab_button, -1) + self._delete_tab_button.show() + + self._previous_tab_button = ToolButton('tab-previous') + self._previous_tab_button.set_tooltip(_("Previous Tab")) + self._previous_tab_button.props.accelerator = 'Left' + self._previous_tab_button.props.sensitive = False + self._previous_tab_button.connect('clicked', self.__prev_tab_cb) + tab_toolbar.insert(self._previous_tab_button, -1) + self._previous_tab_button.show() + + self._next_tab_button = ToolButton('tab-next') + self._next_tab_button.set_tooltip(_("Next Tab")) + self._next_tab_button.props.accelerator = 'Right' + self._next_tab_button.props.sensitive = False + self._next_tab_button.connect('clicked', self.__next_tab_cb) + tab_toolbar.insert(self._next_tab_button, -1) + self._next_tab_button.show() + return tab_toolbar + + def __open_tab_cb(self, btn): + index = self._create_tab(None) + self._notebook.page = index + if self._notebook.get_n_pages() == 2: + self._delete_tab_button.props.sensitive = True + self._previous_tab_button.props.sensitive = True + self._next_tab_button.props.sensitive = True + + def __close_tab_cb(self, btn): + self._close_tab(self._notebook.props.page) + if self._notebook.get_n_pages() == 1: + self._delete_tab_button.props.sensitive = False + self._previous_tab_button.props.sensitive = False + self._next_tab_button.props.sensitive = False + + def __prev_tab_cb(self, btn): + if self._notebook.props.page == 0: + self._notebook.props.page = self._notebook.get_n_pages() - 1 else: - self.notebook.props.page = self.notebook.props.page - 1 - vt = self.notebook.get_nth_page(self.notebook.get_current_page()).vt + self._notebook.props.page = self._notebook.props.page - 1 + vt = self._notebook.get_nth_page(self._notebook.get_current_page()).vt vt.grab_focus() - def _next_tab_cb(self, btn): - if self.notebook.props.page == self.notebook.get_n_pages() - 1: - self.notebook.props.page = 0 + def __next_tab_cb(self, btn): + if self._notebook.props.page == self._notebook.get_n_pages() - 1: + self._notebook.props.page = 0 else: - self.notebook.props.page = self.notebook.props.page + 1 - vt = self.notebook.get_nth_page(self.notebook.get_current_page()).vt + self._notebook.props.page = self._notebook.props.page + 1 + vt = self._notebook.get_nth_page(self._notebook.get_current_page()).vt vt.grab_focus() def _close_tab(self, index): - self.notebook.remove_page(index) - if self.notebook.get_n_pages() == 0: + self._notebook.remove_page(index) + if self._notebook.get_n_pages() == 0: self.close() - - def _tab_child_exited_cb(self, vt): - for i in range(self.notebook.get_n_pages()): - if self.notebook.get_nth_page(i).vt == vt: + + def __tab_child_exited_cb(self, vt): + for i in range(self._notebook.get_n_pages()): + if self._notebook.get_nth_page(i).vt == vt: self._close_tab(i) return - - def _tab_title_changed_cb(self, vt): - for i in range(self.notebook.get_n_pages()): - if self.notebook.get_nth_page(i).vt == vt: - label = self.notebook.get_nth_page(i).label + + def __tab_title_changed_cb(self, vt): + for i in range(self._notebook.get_n_pages()): + if self._notebook.get_nth_page(i).vt == vt: + label = self._notebook.get_nth_page(i).label label.set_text(vt.get_window_title()) return - - def _drag_data_received_cb(self, widget, context, x, y, selection, target, time): + + def __drag_data_received_cb(self, widget, context, x, y, selection, + target, time): widget.feed_child(selection.data) context.finish(True, False, time) return True def _create_tab(self, tab_state): vt = vte.Terminal() - vt.connect("child-exited", self._tab_child_exited_cb) - vt.connect("window-title-changed", self._tab_title_changed_cb) + vt.connect("child-exited", self.__tab_child_exited_cb) + vt.connect("window-title-changed", self.__tab_title_changed_cb) vt.drag_dest_set(gtk.DEST_DEFAULT_MOTION|gtk.DEST_DEFAULT_DROP, [('text/plain', 0, 0), ('STRING', 0, 1)], gtk.gdk.ACTION_DEFAULT| gtk.gdk.ACTION_COPY) - vt.connect('drag_data_received', self._drag_data_received_cb) - + vt.connect('drag_data_received', self.__drag_data_received_cb) + self._configure_vt(vt) vt.show() - + label = gtk.Label() scrollbar = gtk.VScrollbar(vt.get_adjustment()) @@ -200,13 +262,14 @@ class TerminalActivity(sugar.activity.activity.Activity): box.vt = vt box.label = label - - index = self.notebook.append_page(box, label) - self.notebook.show_all() - # Uncomment this to only show the tab bar when there is at least one tab. - # I think it's useful to always see it, since it displays the 'window title'. - #self.notebook.props.show_tabs = self.notebook.get_n_pages() > 1 + index = self._notebook.append_page(box, label) + self._notebook.show_all() + + # Uncomment this to only show the tab bar when there is at least + # one tab. I think it's useful to always see it, since it displays + # the 'window title'. + # self._notebook.props.show_tabs = self._notebook.get_n_pages() > 1 # Launch the default shell in the HOME directory. os.chdir(os.environ["HOME"]) @@ -214,19 +277,20 @@ class TerminalActivity(sugar.activity.activity.Activity): if tab_state: # Restore the environment. # This is currently not enabled. - env = tab_state['env'] + environment = tab_state['env'] filtered_env = [] - for e in env: + for e in environment: var, sep, value = e.partition('=') if var not in MASKED_ENVIRONMENT: filtered_env.append(var + sep + value) - # TODO: Make the shell restore these environment variables, then clear out TERMINAL_ENV. - #os.environ['TERMINAL_ENV'] = '\n'.join(filtered_env) - + # TODO: Make the shell restore these environment variables, + # then clear out TERMINAL_ENV. + # os.environ['TERMINAL_ENV'] = '\n'.join(filtered_env) + # Restore the working directory. - if tab_state.has_key('cwd'): + if 'cwd' in tab_state: os.chdir(tab_state['cwd']) # Restore the scrollback buffer. @@ -235,33 +299,23 @@ class TerminalActivity(sugar.activity.activity.Activity): box.pid = vt.fork_command() - self.notebook.props.page = index + self._notebook.props.page = index vt.grab_focus() return index - def _copy_cb(self, button): - vt = self.notebook.get_nth_page(self.notebook.get_current_page()).vt - if vt.get_has_selection(): - vt.copy_clipboard() - - def _paste_cb(self, button): - vt = self.notebook.get_nth_page(self.notebook.get_current_page()).vt - vt.paste_clipboard() - - def _become_root_cb(self, button): - vt = self.notebook.get_nth_page(self.notebook.get_current_page()).vt + def __become_root_cb(self, button): + vt = self._notebook.get_nth_page(self._notebook.get_current_page()).vt vt.feed('\r\n') vt.fork_command("/bin/su", ('/bin/su', '-')) - def _fullscreen_cb(self, btn): - self.fullscreen() - - def _key_press_cb(self, window, event): + def __key_press_cb(self, window, event): # Escape keypresses are routed directly to the vte and then dropped. - # This hack prevents Sugar from hijacking them and canceling fullscreen mode. + # This hack prevents Sugar from hijacking them and canceling + # fullscreen mode. if gtk.gdk.keyval_name(event.keyval) == 'Escape': - vt = self.notebook.get_nth_page(self.notebook.get_current_page()).vt + current_page = self._notebook.get_current_page() + vt = self._notebook.get_nth_page(current_page).vt vt.event(event) return True @@ -279,44 +333,51 @@ class TerminalActivity(sugar.activity.activity.Activity): data_file = file_path # Clean out any existing tabs. - while self.notebook.get_n_pages(): - self.notebook.remove_page(0) + while self._notebook.get_n_pages(): + self._notebook.remove_page(0) # Create new tabs from saved state. for tab_state in data['tabs']: self._create_tab(tab_state) # Restore active tab. - self.notebook.props.page = data['current-tab'] + self._notebook.props.page = data['current-tab'] # Create a blank one if this state had no terminals. - if self.notebook.get_n_pages() == 0: + if self._notebook.get_n_pages() == 0: self._create_tab(None) + if self._notebook.get_n_pages() > 1: + self._delete_tab_button.props.sensitive = True + self._previous_tab_button.props.sensitive = True + self._next_tab_button.props.sensitive = True + def write_file(self, file_path): if not self.metadata['mime_type']: self.metadata['mime_type'] = 'text/plain' data = {} - data['current-tab'] = self.notebook.get_current_page() + data['current-tab'] = self._notebook.get_current_page() data['tabs'] = [] - for i in range(self.notebook.get_n_pages()): - page = self.notebook.get_nth_page(i) + for i in range(self._notebook.get_n_pages()): + page = self._notebook.get_nth_page(i) def selected_cb(terminal, c, row, cb_data): return 1 - (scrollback_text, attrs) = page.vt.get_text(selected_cb, 1) + (scrollback_text, attributes_) = page.vt.get_text(selected_cb, 1) scrollback_lines = scrollback_text.split('\n') - # Note- this currently gets the child's initial environment rather than the current - # environment, making it not very useful. - environment = open('/proc/%d/environ' % page.pid, 'r').read().split('\0') + # Note- this currently gets the child's initial environment + # rather than the current environment, making it not very useful. + environment = open('/proc/%d/environ' % + page.pid, 'r').read().split('\0') cwd = os.readlink('/proc/%d/cwd' % page.pid) - tab_state = { 'env': environment, 'cwd': cwd, 'scrollback': scrollback_lines } + tab_state = {'env': environment, 'cwd': cwd, + 'scrollback': scrollback_lines} data['tabs'].append(tab_state) @@ -324,7 +385,7 @@ class TerminalActivity(sugar.activity.activity.Activity): text = simplejson.dumps(data) fd.write(text) fd.close() - + def _get_conf(self, conf, var, default): if conf.has_option('terminal', var): if isinstance(default, bool): @@ -340,39 +401,40 @@ class TerminalActivity(sugar.activity.activity.Activity): def _configure_vt(self, vt): conf = ConfigParser.ConfigParser() - conf_file = os.path.join(sugar.env.get_profile_path(), 'terminalrc') - + conf_file = os.path.join(env.get_profile_path(), 'terminalrc') + if os.path.isfile(conf_file): f = open(conf_file, 'r') conf.readfp(f) f.close() else: conf.add_section('terminal') - + font = self._get_conf(conf, 'font', 'Monospace') vt.set_font(pango.FontDescription(font)) fg_color = self._get_conf(conf, 'fg_color', '#000000') bg_color = self._get_conf(conf, 'bg_color', '#FFFFFF') - vt.set_colors(gtk.gdk.color_parse(fg_color), gtk.gdk.color_parse(bg_color), []) + vt.set_colors(gtk.gdk.color_parse(fg_color), + gtk.gdk.color_parse(bg_color), []) blink = self._get_conf(conf, 'cursor_blink', False) vt.set_cursor_blinks(blink) bell = self._get_conf(conf, 'bell', False) vt.set_audible_bell(bell) - + scrollback_lines = self._get_conf(conf, 'scrollback_lines', 1000) vt.set_scrollback_lines(scrollback_lines) vt.set_allow_bold(True) - + scroll_key = self._get_conf(conf, 'scroll_on_keystroke', True) vt.set_scroll_on_keystroke(scroll_key) scroll_output = self._get_conf(conf, 'scroll_on_output', False) vt.set_scroll_on_output(scroll_output) - + emulation = self._get_conf(conf, 'emulation', 'xterm') vt.set_emulation(emulation) -- cgit v0.9.1