# Copyright (C) 2011, George Hunt georgejhunt@gmail.com # 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 import subprocess 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 from terminal_gui import TerminalActivity from terminal import TerminalBase import util 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 VimTutor(TerminalActivity): def __init__(self, handle): TerminalActivity.__init__(self, self, handle) _logger.debug('lower level init completed') self.util = util.Utilities(self) self.tutor = 'vimtutor' self.session_list = [] self.session_cwd = {} #start up vimtutor 5 times automatically, 1 for testing self.tutor_count = 1 self.noautotutor = False ########## Additions for VimTutor ########### self.saved_env = os.environ.copy() self.python_version = sys.version[0:3] #set up HOME directory if not initialized to activity root self.home = self.get_home() self.mergetree( os.path.join(activity.get_bundle_path(),'home'), self.home, overwrite=False) #move the HOME directory to something writeable os.environ['HOME'] = self.home os.chdir(os.path.join(self.home,'vimtutor')) #add activity bin to PATH self.add_to_path('PATH', os.path.join(activity.get_bundle_path(),'bin:')) for f in sys.path: self.add_to_path('PYTHONPATH', f) #runtime libraries needed but not present in some builds: self.add_to_path('LD_LIBRARY_PATH', os.path.join(activity.get_bundle_path(), 'lib')) if self.python_version != '2.6': os.environ['VIMRUNTIME'] = os.path.join(activity.get_bundle_path(),'vim71') self.vim_version_selector = '71' else: os.environ['VIMRUNTIME'] = os.path.join(activity.get_bundle_path(),'vim73') self.vim_version_selector = '73' #ipython uses envirnment to find editor, PATH already set os.environ['EDITOR'] = 'gvim' self.vim_exe = os.path.join(activity.get_bundle_path(), 'bin',self.vim_version_selector, 'vim') #following env variable required by vimtutor script os.environ['VIM'] = self.vim_exe #rewrite .bashrc so that aliases are set for vi, vim, gvim correctly self.rewrite_bashrc() if self.tutor_count > 0: #start up vimtutor and switch to that vte.terminal self._create_tab(None) self.feed_terminal(0, self.tutor) self._create_tab(None) self.feed_terminal(1, self.vim_exe + ' --servername VIM -p') self.set_terminal(0) gobject.idle_add(self.set_first) _logger.debug('finished top level init') def set_first(self): self.set_terminal(0) ############### Routines for VimTutor ############ def can_close(self): _logger.debug('can close') cmd = self.vim_exe + ' --serverlist' results,error = self.util.command_line(cmd) _logger.debug('sessionlist is:%r Error: %s'%(results,error,) ) clean_list = [] if not error: self.session_list = results.split('\n') for session_name in self.session_list: if not session_name: continue #actually interogate server, is it really alive? cmd = self.vim_exe + ' --servername %s --remote-expr "getcwd()"'%(session_name,) results,error = self.util.command_line(cmd) if not error: clean_list.append(session_name) self.session_cwd[session_name] = results session_path = os.path.join(self.home, '.vim', 'sessions', session_name + '.vim') #write out the session information for later journal inclusion cmd = self.vim_exe + ' --servername %s --remote-send ":mks! %s"'%\ (session_name, session_path,) results,error = self.util.command_line(cmd) #write open buffers and quit cmd = self.vim_exe + ' --servername %s --remote-send ":wqa"'%\ (session_name,) results,error = self.util.command_line(cmd) self.session_list = clean_list return True def add_to_path(self, path, new_dir): if new_dir not in os.environ.get(path,'').split(':'): if os.environ.get(path): os.environ[path] = new_dir + ':' + os.environ[path] else: os.environ[path] = new_dir def page_selected(self, widget, tab_no, dummy): """tab clicked grabs focus away from terminal, grab it back""" gobject.idle_add(self.grab_vt_focus) def get_home(self): """Accomodates the change in Sugar for getting root""" #look for bitfrost antidote ictcore = self.get_writeable_ictcore() if ictcore: return ictcore if hasattr(activity, 'get_activity_root'): return os.path.join(activity.get_activity_root(), 'data') return os.path.join(self.get_activity_root(), 'data') def get_writeable_ictcore(self): """bitfrost isolates activities from one another, defeat this for ict activities""" if os.path.isdir(ICTDIR): #is it writeable? try: fd = open(os.path.join(ICTDIR,'test'),'w') fd.write('this is a test') fd.close() os.unlink(os.path.join(ICTDIR,'test')) except IOError,e: return None return ICTDIR def mergetree(self, src, dst, symlinks=False, overwrite=True, ignore=None): """Recursively copy a directory tree using copy(). If exception(s) occur, an Error is raised with a list of reasons. If the optional symlinks flag is true, symbolic links in the source tree result in symbolic links in the destination tree; if it is false, the contents of the files pointed to by symbolic links are copied. The optional ignore argument is a callable. If given, it is called with the `src` parameter, which is the directory being visited by copytree(), and `names` which is the list of `src` contents, as returned by os.listdir(): callable(src, names) -> ignored_names Since copytree() is called recursively, the callable will be called once for each directory that is copied. It returns a list of names relative to the `src` directory that should not be copied. XXX Consider this example code rather than the ultimate tool. """ names = os.listdir(src) if ignore is not None: ignored_names = ignore(src, names) else: ignored_names = set() if not os.path.isdir(dst): os.makedirs(dst) errors = [] for name in names: if name in ignored_names: continue srcname = os.path.join(src, name) dstname = os.path.join(dst, name) try: if symlinks and os.path.islink(srcname): linkto = os.readlink(srcname) os.symlink(linkto, dstname) elif os.path.isdir(srcname): self.mergetree(srcname, dstname, symlinks, ignore) else: if not os.path.isdir(srcname) and (overwrite or not os.path.isfile(dstname)): shutil.copy(srcname, dstname) # XXX What about devices, sockets etc.? except (IOError, os.error), why: errors.append((srcname, dstname, str(why))) _logger.exception('failed inner copy2') # catch the Error from the recursive copytree so that we can # continue with other files except Error, err: errors.extend(err.args[0]) try: #shutil.copystat(src, dst) pass except OSError, why: errors.extend((src, dst, str(why))) _logger.exception('failed copystat') if errors: raise Error, errors def rewrite_bashrc(self): text = '' try: f = open(os.path.join(self.home, '.bashrc'),"r") except IOError,e: _logger.error('unable to open%s Exception:%s'% (os.path.join(self.home, '.bashrc'),e)) return for line in f: if line.find('alias gvim=') != -1: continue if line.find('alias vim=') != -1: continue if line.find('alias vi=') != -1: continue text += line text += "alias vi='" + os.path.join(activity.get_bundle_path(), 'bin', self.vim_version_selector, 'vim') + " -u NONE -c set nocp'\n" text += "alias vim='" + os.path.join(activity.get_bundle_path(), 'bin',self.vim_version_selector, 'vim') + " -p --servername vim'\n" text += "alias gvim='" + os.path.join(activity.get_bundle_path(), 'bin', self.vim_version_selector, 'gvim') + " -g -p --servername gvim'\n" try: _file = file(os.path.join(self.home,'.bashrc'), 'w') _file.write(text) _file.close() except IOError,e: _logger.error('unable to rewrite%s Exception:%s'% (os.path.join(self.home, '.bashrc'),e)) def feed_terminal(self, tab_no, cmd): vt = self._notebook.get_nth_page(tab_no).vt vt.feed_child('%s\r\n'%cmd) def display_terminal(self, tab_no, cmd): vt = self._notebook.get_nth_page(tab_no).vt vt.feed('%s\r\n'%cmd) def set_terminal(self, tab_no): self._notebook.set_current_page(tab_no) """ def _create_gvim_tab(self): self.gvim_label = gtk.Label() self._socket = gtk.Socket() self._socket.show() #self.event_box.add(self._socket) self.gvim_label.set_text('GVIM') index = self._notebook.append_page(self._socket, self.gvim_label) self.socket_id = str(self._socket.get_id()) exe = (os.path.join(activity.get_bundle_path(), 'bin', self.vim_version_selector, 'gvim')) myargs = '--socketid ' + self.socket_id _logger.debug('exe:%s args:%s'%(exe, myargs,)) self.gvim_pid = subprocess.Popen([exe, myargs,]).pid self.set_terminal(index) self._notebook.show_all() """ ############### End Routines for VimTutor ############