Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/terminal.py
diff options
context:
space:
mode:
Diffstat (limited to 'terminal.py')
-rw-r--r--terminal.py373
1 files changed, 373 insertions, 0 deletions
diff --git a/terminal.py b/terminal.py
new file mode 100644
index 0000000..8690a16
--- /dev/null
+++ b/terminal.py
@@ -0,0 +1,373 @@
+# Copyright (C) 2007, Eduardo Silva <edsiper@gmail.com>.
+# 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# 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
+import sys
+#import simplejson
+import pickle
+import ConfigParser
+import shutil
+import gobject
+from exceptions import *
+
+import logging
+from gettext import gettext as _
+
+import gtk
+import vte
+import pango
+
+from sugar.graphics.toolbutton import ToolButton
+from sugar.activity import activity
+from sugar import env
+#from help.help import Help
+
+# Attempt to import the new toolbar classes. If the import fails,
+# fall back to the old toolbar style.
+try:
+ from sugar.graphics.toolbarbox import ToolbarBox
+ from sugar.graphics.toolbarbox import ToolbarButton
+ from sugar.activity.widgets import ActivityToolbarButton
+ from sugar.activity.widgets import StopButton
+ NEW_TOOLBARS = True
+except ImportError:
+ from sugar.activity.activity import ActivityToolbox
+ NEW_TOOLBARS = False
+
+
+MASKED_ENVIRONMENT = [
+ 'DBUS_SESSION_BUS_ADDRESS',
+ 'PPID']
+
+logging.basicConfig()
+_logger = logging.getLogger('vimtutor')
+_logger.setLevel(logging.DEBUG)
+
+#A place where a student can make world writeable, hence multi-Activity
+ICTDIR = '/home/olpc/.ictcore'
+
+class Error(EnvironmentError):
+ pass
+
+class TerminalBase:
+
+ def __init__(self, vimtutor, handle):
+ self._vimtutor = vimtutor
+ self._notebook = gtk.Notebook()
+ self._notebook.set_property("tab-pos", gtk.POS_TOP)
+ self._notebook.set_scrollable(True)
+ self._notebook.show()
+
+ #self._create_tab(None)
+
+ def tab_child_exited_cb(self, vt):
+ #override in parent to provide action
+ pass
+
+ def tab_title_changed_cb(self, vt):
+ pass
+
+ def drag_data_received_cb(self, widget, context, x, y, selection,
+ target, time):
+ pass
+
+ 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)
+
+ # FIXME have to resend motion events to parent, see #1402
+ vt.connect('motion-notify-event', self.__motion_notify_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)
+
+ self._configure_vt(vt)
+
+ vt.show()
+
+ label = gtk.Label()
+
+ scrollbar = gtk.VScrollbar(vt.get_adjustment())
+ scrollbar.show()
+
+ box = gtk.HBox()
+ box.pack_start(vt)
+ box.pack_start(scrollbar)
+
+ 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
+
+ # Launch the default shell in the HOME directory.
+ os.chdir(os.environ["HOME"])
+
+ session = file_path = ''
+ if not tab_state:
+ box.pid = vt.fork_command()
+ self._notebook.props.page = index
+ vt.grab_focus()
+ os.chdir(os.path.join(self._vimtutor.home,'vimtutor'))
+ return index
+ else:
+ # Restore the environment.
+ # This is currently not enabled.
+ environment = tab_state['env']
+
+ filtered_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)
+
+ # startup vim if we have session info
+ session = tab_state.get('vim_session','')
+ file_path = tab_state.get('session_path','')
+ server = tab_state.get('server_name','')
+ if session and session in self.session_cwd:
+ cwd = self._vimtutor.session_cwd[session]
+ elif 'cwd' in tab_state:
+ cwd = tab_state['cwd']
+
+ # Restore the working directory.
+ if os.path.exists(cwd):
+ try:
+ os.chdir(cwd)
+ except:
+ # ACLs may deny access
+ sys.stdout.write("Could not chdir to " + cwd)
+
+ # Restore the scrollback buffer.
+ for l in tab_state['scrollback']:
+ vt.feed(l + '\r\n')
+
+ box.pid = vt.fork_command()
+
+ if session and file_path:
+ fd = open(file_path, 'w')
+ fd.write(session)
+ fd.close()
+ if server and server[0] == 'G':
+ exe = 'gvim'
+ else:
+ exe = 'vim'
+ start_vim = os.path.join(activity.get_bundle_path(),
+ 'bin',self._vimtutor.vim_version_selector, exe) + \
+ ' -S ' + file_path + '\n\r'
+ vt.feed_child(start_vim)
+
+ self._notebook.props.page = index
+ vt.grab_focus()
+
+ return index
+
+ def __motion_notify_cb(self, widget, event):
+ self.canvas.parent.emit('motion-notify-event', 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.
+ if gtk.gdk.keyval_name(event.keyval) == 'Escape':
+ current_page = self._notebook.get_current_page()
+ vt = self._notebook.get_nth_page(current_page).vt
+ vt.event(event)
+ return True
+
+ return False
+
+ def read_file(self, file_path):
+ if self.metadata['mime_type'] != 'text/plain':
+ return
+
+ fd = open(file_path, 'r')
+ text = fd.read()
+ data = pickle.loads(text)
+ fd.close()
+
+ if data.has_key('tutor_count'):
+ if data['tutor_count'] > 0:
+ data['tutor_count'] -= 1
+ self._vimtutor.tutor_count = data['tutor_count']
+ _logger.debug('in read_file. tutor_count:%s sessions:%r'%(
+ self._vimtutor.tutor_count,data.get('sessions','')))
+
+ # Let user choose not to autostart tutor next time
+ if self._vimtutor.tutor_count == 0:
+ self._vimtutor.util.confirmation_alert(_('Click OK to disable tutorial auto-start?'),
+ _('Are you finished with VimTutor tutorial?'),self.disable_autotutor)
+
+ # Clean out any existing tabs.
+ while self._notebook.get_n_pages():
+ self._notebook.remove_page(0)
+
+ # create tabs from saved state.
+ for tab_state in data['tabs']:
+ self._create_tab(tab_state)
+
+ if self._vimtutor.tutor_count > -1:
+ #self._create_tab(None)
+ self.feed_terminal(0, self.tutor)
+
+ # Restore active tab.
+ self._notebook.props.page = data['current-tab']
+
+ 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 disable_autotutor(self, alert, response):
+ self._vimtutor.tutor_count = -1
+
+ def write_file(self, file_path):
+ _logger.debug('entered write file')
+ if not self.metadata['mime_type']:
+ self.metadata['mime_type'] = 'text/plain'
+
+ # if keep button, ojbect_id will be None
+ if not self._jobject.object_id:
+ self._vimtutor.tutor_count = 1
+ self.metadata['title'] = 'VimTutor'
+
+ data = {}
+ data['current-tab'] = self._notebook.get_current_page()
+ data['tabs'] = []
+ data['sessions'] = []
+ data['tutor_count'] = self._vimtutor.tutor_count
+
+ 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 = page.vt.get_text(selected_cb, False)
+
+ 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')
+
+ cwd = os.readlink('/proc/%d/cwd' % page.pid)
+
+ #=======vim additions========
+ text = session_file = server = ''
+ #reserve the first tab for vimtutor
+ j = i
+ if self._vimtutor.tutor_count > -1:
+ j -= 1
+ if j > -1 and j < len(self._vimtutor.session_list):
+
+ server = self._vimtutor.session_list[j]
+ if server:
+ session_file = os.path.join(self.home, '.vim', 'sessions', server + '.vim')
+ try:
+ fd = open(session_file, 'r')
+ text = fd.read()
+ fd.close()
+ #os.unlink(session_file)
+ except:
+ pass
+ data['sessions'].append(server)
+ #======= end vim additions========
+
+ tab_state = {'env': environment, 'cwd': cwd,
+ 'scrollback': scrollback_lines,
+ 'vim_session':text,
+ 'session_path': session_file,
+ 'server_name' : server,
+ }
+
+ data['tabs'].append(tab_state)
+ fd = open(file_path, 'w')
+ text = pickle.dumps(data)
+ fd.write(text)
+ fd.close()
+
+ def _get_conf(self, conf, var, default):
+ if conf.has_option('terminal', var):
+ if isinstance(default, bool):
+ return conf.getboolean('terminal', var)
+ elif isinstance(default, int):
+ return conf.getint('terminal', var)
+ else:
+ return conf.get('terminal', var)
+ else:
+ conf.set('terminal', var, default)
+
+ return default
+
+ def _configure_vt(self, vt):
+ conf = ConfigParser.ConfigParser()
+ 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), [])
+
+ 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)
+
+ visible_bell = self._get_conf(conf, 'visible_bell', False)
+ vt.set_visible_bell(visible_bell)
+
+ conf.write(open(conf_file, 'w'))