# 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 # 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'))