diff options
author | root <root@ghunt-desktop.(none)> | 2010-04-23 17:53:01 (GMT) |
---|---|---|
committer | root <root@ghunt-desktop.(none)> | 2010-04-23 17:53:01 (GMT) |
commit | 91384302ef04ee915a9a80f9efa0215836975473 (patch) | |
tree | 78c330a1091ce2ef82f48f51357790b42fde5a6f /pydebug.py.bk | |
parent | 0378b3d4fb15f29752431b7477b69091797dfb74 (diff) |
second try to get git to track the files I care about
Diffstat (limited to 'pydebug.py.bk')
-rw-r--r-- | pydebug.py.bk | 1856 |
1 files changed, 1856 insertions, 0 deletions
diff --git a/pydebug.py.bk b/pydebug.py.bk new file mode 100644 index 0000000..d0d48cc --- /dev/null +++ b/pydebug.py.bk @@ -0,0 +1,1856 @@ +# Copyright (C) 2009, George Hunt <georgejhunt@gmail.com> +# Copyright (C) 2009, One Laptop Per Child +# +# 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 +from __future__ import with_statement +import os, os.path, simplejson, ConfigParser, shutil, sys +from subprocess import Popen, PIPE + +from gettext import gettext as _ + +#major packages +import gtk +import gtk.glade +import vte +import pango +#import gconf +#import glib +import pickle +import hashlib +import time +#import gio +import datetime +import gobject + +#sugar stuff +from sugar.graphics.toolbutton import ToolButton +import sugar.graphics.toolbutton + +import sugar.env +from sugar.graphics.xocolor import XoColor +from sugar.graphics.icon import Icon +from sugar.graphics.objectchooser import ObjectChooser +from sugar.datastore import datastore +from sugar.graphics.alert import * +import sugar.activity.bundlebuilder as bundlebuilder +from sugar.bundle.activitybundle import ActivityBundle +from sugar.activity import activityfactory +#from jarabe.model import shell + +#application stuff +from terminal_pd import Terminal +#public api for ipython + +#from IPython.core import ipapi 0.11 requires this +import IPython.ipapi + +#from sourceview import SourceViewPd +import sourceview_editor +from sugar.activity.activity import Activity +from help_pd import Help + +#following taken from Rpyc module +#import Rpyc +from Rpyc.Utils.Serving import start_threaded_server, DEFAULT_PORT +from Rpyc.Connection import * +from Rpyc.Stream import * +import select +from filetree import FileTree +from datastoretree import DataStoreTree +import pytoolbar +#from pytoolbar import ActivityToolBox + +import logging +from pydebug_logging import _logger, log_environment + +MASKED_ENVIRONMENT = [ + 'DBUS_SESSION_BUS_ADDRESS', + 'PPID' +] +#PANES = ['TERMINAL','EDITOR','CHILD','PROJECT','HELP'] +PANES = ['TERMINAL','EDITOR','PROJECT','HELP'] + +#colors for the playpen side of the project page +PROJECT_FG = '#990000' +PROJECT_BASE = "#fdd99b" +PROJECT_BG = '#FFFFCC' + +#global module variable communicates to debugged programs +pydebug_instance = None + +#following options taken from Develop_App +class Options: + def __init__(self, template = None, **kw): + if template: + self.__dict__ = template.__dict__.copy() + else: + self.__dict__ = {} + self.__dict__.update(kw) + +class SearchOptions(Options): + pass +S_WHERE = sourceview_editor.S_WHERE + +class PyDebugActivity(Activity,Terminal): + MIME_TYPE = 'application/vnd.olpc-sugar' + DEPRECATED_MIME_TYPE = 'application/vnd.olpc-x-sugar' + _zipped_extension = '.xo' + _unzipped_extension = '.activity' + dirty = False + + def __init__(self, handle): + #handle object contains command line inputs to this activity + self.handle = handle + _logger.debug('Activity id:%s.Object id: %s. uri:%s'%(handle.activity_id, + handle.object_id, handle.uri)) + if handle.object_id and handle.object_id != '': + self.passed_in_ds_object = datastore.get(handle.object_id) + debugstr = '' + if self.passed_in_ds_object: + for key in self.passed_in_ds_object.metadata.keys(): + if key == 'preview': continue + debugstr += key + ':'+str(self.passed_in_ds_object.metadata[key]) + ', ' + _logger.debug('initial datastore metadata dictionary==>: %r'%debugstr) + + #Save a global poiinter so remote procedure calls can communicate with pydebug + global pydebug_instance + pydebug_instance = self + + #init variables + self.make_paths() + self.save_icon_clicked = False + self.source_directory = None + self.data_file = None + self.help = None + self.help_x11 = None + self.project_dirty = False + self.sock = None + self.last_filename = None + self.debug_dict = {} + self.activity_dict = {} + self.file_pane_is_activities = False + self.manifest_treeview = None #set up to recognize an re-display of playpen + self.manifest_class = None + #self.set_title(_('PyDebug Activity')) + self.ds = None #datastore pointer + self._logger = _logger + self.traceback = 'Context' + self.abandon_changes = False + self.journal_class = None + self.delete_after_load = None + self.find_window = None + self.passed_in_db_object = None + + # init the Classes we are subclassing + Activity.__init__(self, handle, create_jobject = False) + #Terminal has no needs for init + #Help.__init__(self,self) + + # setup the search options + self.s_opts = SearchOptions(where = S_WHERE.multifile, + use_regex = False, + ignore_caps = True, + replace_all = False, + + #defaults to avoid creating + #a new SearchOptions object for normal searches + #should never be changed, just make a copy like: + #SearchOptions(self.s_opts, forward=False) + forward = True, + stay = False + ) + self.safe_to_replace = False + + + #set up the PANES for the different functions of the debugger + self.canvas_list = [] + self.panes = {} + pane_index = 0 + pane_index = self.new_pane(self._get_terminal_canvas,pane_index) + pane_index = self.new_pane(self._get_edit_canvas,pane_index) + #pane_index = self.new_pane(self._get_child_canvas,pane_index) + pane_index = self.new_pane(self._get_project_canvas,pane_index) + pane_index = self.new_pane(self._get_help_canvas,pane_index) + + nb = gtk.Notebook() + nb.show() + nb.set_show_tabs(False) + + for c in self.canvas_list: + nb.append_page(c) + + self.pydebug_notebook = nb + #the following call to the activity code puts our notebook under the stock toolbar + self.set_canvas(nb) + + #set up tool box/menu buttons + self.toolbox = pytoolbar.ActivityToolbox(self) + self.toolbox.connect_after('current_toolbar_changed',self._toolbar_changed_cb) + + activity_toolbar = self.toolbox.get_activity_toolbar() + activity_toolbar.share.props.visible = True + #activity_toolbar.keep.props.visible = True + + separator = gtk.SeparatorToolItem() + separator.set_draw(True) + separator.show() + activity_toolbar.insert(separator, 0) + + activity_go = ToolButton() + activity_go.set_stock_id('gtk-media-forward') + activity_go.set_icon_widget(None) + activity_go.set_tooltip(_('Start Debugging')) + activity_go.connect('clicked', self.project_run_cb) + #activity_go.props.accelerator = '<Ctrl>O' + activity_go.show() + activity_toolbar.insert(activity_go, 0) + + + activity_copy_tb = ToolButton('edit-copy') + activity_copy_tb.set_tooltip(_('Copy')) + activity_copy_tb.connect('clicked', self._copy_cb) + #activity_copy_tb.props.accelerator = '<Ctrl>C' + activity_toolbar.insert(activity_copy_tb, 3) + activity_copy_tb.show() + + activity_paste_tb = ToolButton('edit-paste') + activity_paste_tb.set_tooltip(_('Paste')) + activity_paste_tb.connect('clicked', self._paste_cb) + #activity_paste_tb.props.accelerator = '<Ctrl>V' + activity_toolbar.insert(activity_paste_tb, 4) + activity_paste_tb.show() + + activity_tab_tb = sugar.graphics.toolbutton.ToolButton('list-add') + activity_tab_tb.set_tooltip(_("Open New Tab")) + activity_tab_tb.props.accelerator = '<Ctrl>T' + activity_tab_tb.show() + activity_tab_tb.connect('clicked', self._open_tab_cb) + activity_toolbar.insert(activity_tab_tb, 5) + + activity_tab_delete_tv = sugar.graphics.toolbutton.ToolButton('list-remove') + activity_tab_delete_tv.set_tooltip(_("Close Tab")) + activity_tab_delete_tv.props.accelerator = '<Ctrl><Shift>X' + activity_tab_delete_tv.show() + activity_tab_delete_tv.connect('clicked', self._close_tab_cb) + activity_toolbar.insert(activity_tab_delete_tv, 6) + + + activity_fullscreen_tb = sugar.graphics.toolbutton.ToolButton('view-fullscreen') + activity_fullscreen_tb.set_tooltip(_("Fullscreen")) + activity_fullscreen_tb.props.accelerator = '<Alt>Enter' + activity_fullscreen_tb.connect('clicked', self._fullscreen_cb) + activity_toolbar.insert(activity_fullscreen_tb, 7) + activity_fullscreen_tb.hide() + + #Add editor functionality to the debugger + editbar = gtk.Toolbar() + + editopen = ToolButton() + editopen.set_stock_id('gtk-new') + editopen.set_icon_widget(None) + editopen.set_tooltip(_('New File')) + editopen.connect('clicked', self._new_file_cb) + #editopen.props.accelerator = '<Ctrl>O' + editopen.show() + editbar.insert(editopen, -1) + + editfile = ToolButton() + editfile.set_stock_id('gtk-open') + editfile.set_icon_widget(None) + editfile.set_tooltip(_('Open File')) + editfile.connect('clicked', self._read_file_cb) + editfile.props.accelerator = '<Ctrl>O' + editfile.show() + editbar.insert(editfile, -1) + + editsave = ToolButton() + editsave.set_stock_id('gtk-save') + editsave.set_icon_widget(None) + editsave.set_tooltip(_('Save File')) + editsave.props.accelerator = '<Ctrl>S' + editsave.connect('clicked', self.save_cb) + editsave.show() + editbar.insert(editsave, -1) + + editsaveas = ToolButton() + editsaveas.set_stock_id('gtk-save-as') + editsaveas.set_icon_widget(None) + editsaveas.set_tooltip(_('Save As')) + #editsaveas.props.accelerator = '<Ctrl>S' + editsaveas.connect('clicked', self.save_file_cb) + editsaveas.show() + editbar.insert(editsaveas, -1) + + + """ + editjournal = ToolButton(tooltip=_('Open Journal')) + client = gconf.client_get_default() + color = XoColor(client.get_string('/desktop/sugar/user/color')) + journal_icon = Icon(icon_name='document-save', xo_color=color) + editjournal.set_icon_widget(journal_icon) + editjournal.connect('clicked', self._show_journal_object_picker_cb) + editjournal.props.accelerator = '<Ctrl>J' + editjournal.show() + editbar.insert(editjournal, -1) + """ + + separator = gtk.SeparatorToolItem() + separator.set_draw(True) + separator.show() + editbar.insert(separator, -1) + + editundo = ToolButton('undo') + editundo.set_tooltip(_('Undo')) + editundo.connect('clicked', self.editor.undo) + editundo.props.accelerator = '<Ctrl>Z' + editundo.show() + editbar.insert(editundo, -1) + + editredo = ToolButton('redo') + editredo.set_tooltip(_('Redo')) + editredo.connect('clicked', self.editor.redo) + editredo.props.accelerator = '<Ctrl>Y' + editredo.show() + editbar.insert(editredo, -1) + + separator = gtk.SeparatorToolItem() + separator.set_draw(True) + separator.show() + editbar.insert(separator, -1) + + editcut = ToolButton() + editcut.set_stock_id('gtk-cut') + editcut.set_icon_widget(None) + editcut.set_tooltip(_('Cut')) + self.edit_cut_handler_id = editcut.connect('clicked', self.editor.cut) + editcut.props.accelerator = '<Ctrl>X' + editbar.insert(editcut, -1) + editcut.show() + + editcopy = ToolButton('edit-copy') + editcopy.set_tooltip(_('Copy')) + self.edit_copy_handler_id = editcopy.connect('clicked', self.editor.copy) + editcopy.props.accelerator = '<Ctrl>C' + editbar.insert(editcopy, -1) + editcopy.show() + + editpaste = ToolButton('edit-paste') + editpaste.set_tooltip(_('Paste')) + self.edit_paste_handler_id = editpaste.connect('clicked', self.editor.paste) + editpaste.props.accelerator = '<Ctrl>V' + editpaste.show() + editbar.insert(editpaste, -1) + + separator = gtk.SeparatorToolItem() + separator.set_draw(True) + separator.show() + editbar.insert(separator, -1) + + editfind = ToolButton('viewmag1') + editfind.set_tooltip(_('Find and Replace')) + editfind.connect('clicked', self.show_find) + editfind.props.accelerator = '<Ctrl>F' + editfind.show() + editbar.insert(editfind, -1) + + separator = gtk.SeparatorToolItem() + separator.set_draw(True) + separator.show() + editbar.insert(separator, -1) + + self.zoomout = ToolButton('zoom-out') + self.zoomout.set_tooltip(_('Zoom out')) + self.zoomout.connect('clicked', self.__zoomout_clicked_cb) + editbar.insert(self.zoomout, -1) + self.zoomout.show() + + self.zoomin = ToolButton('zoom-in') + self.zoomin.set_tooltip(_('Zoom in')) + self.zoomin.connect('clicked', self.__zoomin_clicked_cb) + editbar.insert(self.zoomin, -1) + self.zoomin.show() + + editbar.show_all() + self.toolbox.add_toolbar(_('Edit'), editbar) + + #childbar = gtk.Toolbar() + #childbar.show_all() + #self.toolbox.add_toolbar(_('Your Program'), childbar) + + project_run = ToolButton() + project_run.set_stock_id('gtk-media-forward') + project_run.set_icon_widget(None) + project_run.set_tooltip(_('Start Debugging')) + project_run.connect('clicked', self.project_run_cb) + #project_run.props.accelerator = '<Ctrl>C' + project_run.show() + + separator = gtk.SeparatorToolItem() + separator.set_draw(False) + separator.set_expand(True) + separator.show() + + self.keep = ToolButton(tooltip=_('Save Snapshot of the program in the Playpen to the Journal')) + #client = gconf.client_get_default() + #color = XoColor(client.get_string('/desktop/sugar/user/color')) + #keep_icon = Icon(icon_name='document-save', xo_color=color) + keep_icon = Icon(icon_name='document-save') + self.keep.set_icon_widget(keep_icon) + keep_icon.show() + #self.keep.props.accelerator = '<Ctrl>S' + self.keep.connect('clicked', self.__keep_clicked_cb) + #self.insert(self.keep, -1) + self.keep.show() + + projectbar = gtk.Toolbar() + projectbar.show_all() + projectbar.insert(project_run, -1) + projectbar.insert(separator, -1) + projectbar.insert(self.keep,-1) + self.toolbox.add_toolbar(_('Project'), projectbar) + + self.help = Help(self) + helpbar = self.help.get_help_toolbar() + self.toolbox.add_toolbar(_('Help'), helpbar) + + + self.set_toolbox(self.toolbox) + self.toolbox.show() + + #get the persistent data across all debug sessions + self.get_config () + + #set the default contents for edit + self.font_size = self.debug_dict.get('font_size',8) + + #get the journal datastore information and resume previous activity + #self.metadata = self.ds + if self.passed_in_ds_object and self.passed_in_ds_object.get_file_path(): + ds_file = self.passed_in_ds_object.get_file_path() + else: + ds_file = '' + self.read_file(ds_file) + + #set which PANE is visible initially + self.set_visible_canvas(self.panes['PROJECT']) + self.set_toolbar(self.panes['PROJECT']) + self.non_blocking_server() + #glib.idle_add(self.non_blocking_server) + self.setup_project_page() + _logger.debug('child path for program to be debugged is %r\nUser Id:%s'%(self.child_path,os.geteuid())) + + #create the terminal tabs, start up the socket between 1st and 2nd terminal instances + self.setup_terminal() + + + def __stop_clicked_cb(self,button): + _logger('caught stop clicked call back') + self.close(skip_save = True) + + def __zoomin_clicked_cb(self,button): + self.font_size += 1 + self.editor.change_font_size(self.font_size) + self.debug_dict['font_size'] = self.font_size + + def __zoomout_clicked_cb(self,botton): + self.font_size -= 1 + self.editor.change_font_size(self.font_size) + self.debug_dict['font_size'] = self.font_size + + def command_line(self,cmd, alert_error=True): + _logger.debug('command_line cmd:%s'%cmd) + p1 = Popen(cmd,stdout=PIPE, shell=True) + output = p1.communicate() + if p1.returncode != 0 : + _logger.debug('error returned from shell command: %s was %s'%(cmd,output[0])) + if alert_error: self.alert(_('%s Command returned non zero\n'%cmd+output[0])) + return output[0],p1.returncode + + def sugar_version(self): + cmd = '/bin/rpm -q sugar' + reply = self.command_line(cmd) + if reply and reply[0].find('sugar') > -1: + version = reply.split('-')[1] + version_chunks = version.split('.') + major_minor = version_chunks[0] + '.' + version_chunks[1] + return float(major_minor) + return None + + def non_blocking_server(self): + start_threaded_server() + + def new_pane(self,funct,i): + self.panes[PANES[i]] = i + self.canvas_list.append(funct()) + i += 1 + return i + + def make_paths(self): + self.pydebug_path = os.environ['SUGAR_BUNDLE_PATH'] + p_path = os.environ['SUGAR_BUNDLE_PATH'] + if not os.environ.get("PYTHONPATH",'') == '': + p_path = p_path + ':' + os.environ['PYTHONPATH'] = p_path + os.environ.get("PYTHONPATH",'') + _logger.debug('sugar_bundle_path:%s\nsugar_activity_root:%s'%(os.environ['SUGAR_BUNDLE_PATH'], + os.environ['SUGAR_ACTIVITY_ROOT'])) + self.debugger_home = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data') + self.child_path = None + os.environ["HOME"]=self.debugger_home + os.environ['PATH'] = os.path.join(self.pydebug_path,'bin:') + os.environ['PATH'] + self.storage = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data/pydebug') + self.sugar_bundle_path = os.environ['SUGAR_BUNDLE_PATH'] + self.activity_playpen = os.path.join(self.storage,'playpen') + if not os.path.isdir(self.activity_playpen): + os.makedirs(self.activity_playpen) + + def _get_edit_canvas(self): + self.editor = sourceview_editor.GtkSourceview2Editor(self) + return self.editor + + def setup_terminal(self): + os.environ['IPYTHONDIR'] = self.pydebug_path + _logger.debug('Set IPYTHONDIR to %s'%self.pydebug_path) + self._create_tab({'cwd':self.sugar_bundle_path}) + self._create_tab({'cwd':self.sugar_bundle_path}) + #start the debugger user interface + alias_cmd = 'alias go="%s"\n'%('./bin/ipython.py',) + self.feed_virtual_terminal(0,alias_cmd) + + self.feed_virtual_terminal(0,'./bin/ipython.py \n') + #cmd = 'run ' + os.path.join(self.sugar_bundle_path,'bin','start_debug.py') + '\n' + #self.feed_virtual_terminal(0,cmd) + + + def start_debugging(self): #check for a start up script in bundle root or bundle_root/bin + command = self.activity_dict.get('command','') + if command == '': + self.alert('No Activity Loaded') + return + _logger.debug("Command to execute:%s."%command) + self.editor.save_all() + + #try to restore a clean debugging environment + #self.feed_virtual_terminal(0,'quit()\r\n\r\n') + + self.set_visible_canvas(self.panes['TERMINAL']) + #change the menus + self.toolbox.set_current_toolbar(self.panes['TERMINAL']) + message = _('\n\n Use the HELP in the Ipython interpreter to learn to DEBUG your program.\n') + self.message_terminal(0,message) + gr + #get the ipython shell object + """ this works but is not needed now + ip = ipapi.get() + arg_str = 'run -d -b %s %s'%(self.pydebug_path,self.child_path) + ip.user_ns['go'] = arg_str + _logger.debug('about to use "%s" to start ipython debugger\n'%(arg_str)) + """ + self.feed_virtual_terminal(0,'go\n') + + def set_ipython_traceback(self): + pass + """ + tb = self.debug_dict['traceback'] + ip = IPython.ipapi.get() + ipmagic = ip.user_ns['ipmagic'] + ipmagic('xmode ' + tb) + """ + + def find_import(self,fn): + _logger.debug('find_import in file %s'%fn) + try_fn = os.path.join(self.child_path,fn) + if not os.path.isfile(try_fn): + try_fn += '.py' + if not os.path.isfile(try_fn): + _logger.debug('in find_import, failed to find file %s'%try_fn) + return + line_no = 0 + for line in open(try_fn,'r'): + if line.startswith('import'): + return line_no, try_fn + line_no += 1 + return -1, None + + def _get_child_canvas(self): + fr = gtk.Frame() + label = gtk.Label("This page will be replaced with the \noutput from your program") + label.show() + fr.add(label) + fr.show() + return fr + + + def _get_help_canvas(self): + fr = gtk.Frame() + label = gtk.Label(_("Loading Help Page")) + label.show() + fr.add(label) + fr.show() + return fr + + def get_icon_pixbuf(self, stock): + return self.treeview.render_icon(stock_id=getattr(gtk, stock), + size=gtk.ICON_SIZE_MENU, + detail=None) + + + """ + def _get_help_canvas(self): + fr = gtk.Frame() #FIXME explore whether frame is still needed--was to fix webview problem + fr.show() + nb = gtk.Notebook() + nb.show() + fr.add(nb) + nb.append_page(self.get_first_webview()) + self.help_notebook = nb + return fr + """ + + def _child_cb(self,event): + pass + + def _project_cb(self,event): + pass + + #lots of state to change whenever one of the major tabs is clicked + def set_visible_canvas(self,index): #track the toolbox tab clicks + self.pydebug_notebook.set_current_page(index) + if index == self.panes['TERMINAL']: + gobject.idle_add(self.set_terminal_focus) + self.editor.save_all() + elif index == self.panes['HELP']: + self.help_selected() + elif index == self.panes['PROJECT'] and self.manifest_class: + self.manifest_class.set_file_sys_root(self.child_path) + self.current_pd_page = index + gobject.idle_add(self.grab_notebook_focus) + + def grab_notebook_focus(self): + self.pydebug_notebook.grab_focus() + return False + + def _toolbar_changed_cb(self,widget,tab_no): + _logger.debug('tool tab changed notification %d'%tab_no) + self.set_visible_canvas(tab_no) + + def set_toolbar(self,page_no): + self.toolbox.set_current_toolbar(page_no) + + def key_press_cb(self,widget,event): + state = event.get_state() + if state and gtk.gdk.SHIFT_MASK and gtk.gdk.CONTROL_MASK and gtk.gdk.MOD1_MASK == 0: + self.file_changed = True + #put a star in front of the filename + return False + + ### following routines are copied from develop_app for use with sourceview_editor + def _replace_cb(self, button=None): + ftext = self._search_entry.props.text + rtext = self._replace_entry.props.text + _logger.debug('replace %s with %s usiing options %r'%(ftext,rtext,self.s_opts)) + replaced, found = self.editor.replace(ftext, rtext, + self.s_opts) + if found: + self._replace_button.set_sensitive(True) + + def _search_entry_activated_cb(self, entry): + text = self._search_entry.props.text + if text: + self._findnext_cb(None) + + def _search_entry_changed_cb(self, entry): + self.safe_to_replace = False + text = self._search_entry.props.text + if not text: + self._findprev.set_sensitive(False) + self._findnext.set_sensitive(False) + else: + self._findprev.set_sensitive(True) + self._findnext.set_sensitive(True) + if not self.s_opts.use_regex: #do not do partial searches for regex + if self.editor.find_next(text, + SearchOptions(self.s_opts, + stay=True, + where=(self.s_opts.where if + self.s_opts.where != S_WHERE.multifile + else S_WHERE.file))): + #no multifile, or focus gets grabbed + self._replace_button.set_sensitive(True) + + def _replace_entry_changed_cb(self, entry): + if self._replace_entry.props.text: + self.safe_to_replace = True + + def _findprev_cb(self, button=None): + ftext = self._search_entry.props.text + if ftext: + if self.editor.find_next(ftext, + SearchOptions(self.s_opts, + forward=False)): + self._replace_button.set_sensitive(True) + + def _findnext_cb(self, button=None): + ftext = self._search_entry.props.text + _logger.debug('find next %s'%ftext) + if ftext: + if self.editor.find_next(ftext, self.s_opts): + self._replace_button.set_sensitive(True) + self.editor.set_focus() + + + def show_find(self,button): + if not self.find_window: + self._find_width = 400 + self._find_height = 300 + self.find_window = self.wTree.get_widget("find") + self.find_window.connect('destroy',self.close_find_window) + self.find_window.connect('delete_event',self.close_find_window) + self.find_connect() + self.find_window.set_title(_('FIND OR REPLACE')) + self.find_window.set_size_request(self._find_width,self._find_height) + self.find_window.set_decorated(False) + self.find_window.set_resizable(False) + self.find_window.set_modal(False) + self.find_window.connect('size_request',self._size_request_cb) + self.find_window.show() + + def _size_request_cb(self, widget, req): + x = gtk.gdk.screen_width() -self._find_width - 50 + self.find_window._width = req.width + self.find_window._height = req.height + self.find_window.move(x,150) + + def find_connect(self): + mdict = { + 'find_close_clicked_cb':self.close_find_window, + 'find_entry_changed_cb':self.find_entry_changed_cb, + 'replace_entry_changed_cb':self.replace_entry_changed_cb, + 'find_previous_clicked_cb':self._findprev_cb, + 'find_next_clicked_cb':self._findnext_cb, + 'find_entry_changed_cb':self._search_entry_changed_cb, + 'replace_entry_changed_cb':self._replace_entry_changed_cb, + 'replace_clicked_cb':self._replace_cb, + #'replace_all_clicked_cb':self._findprev_cb, + } + self.wTree.signal_autoconnect(mdict) + self._findnext = self.wTree.get_widget("find_next") + self._findprev = self.wTree.get_widget("find_previous") + self._search_entry = self.wTree.get_widget("find_entry") + self._replace_entry = self.wTree.get_widget("replace_entry") + self._replace_button = self.wTree.get_widget("replace") + #self.replace_all = self.wTree.get_widget("replace_all") + self.find_where = self.wTree.get_widget("find_where") + + + def close_find_window(self,button): + self.find_window.hide() + return True + + def find_entry_changed_cb(self,button): + #self.editor.search_for = + pass + def replace_entry_changed_cb(self,button): + #self.editor.search_for = + pass + + def project_run_cb(self,button): + _logger.debug('entered project_run_cb') + """ + start_script = ['python','import sys','from Rpyc import *','from Rpyc.Utils import remote_interpreter', + 'c = SocketConnection("localhost")','remote_interpreter(c)'] + for l in start_script: + self.feed_virtual_terminal(0,l+'\r\n') + """ + self.start_debugging() + + def __keep_clicked_cb(self, button): + self.write_binary_to_datastore(None) + + + ###### SUGAR defined read and write routines -- do not let them overwrite what's being debugged + + def read_file(self, file_path): + """ + If the ds_object passed to PyDebug is the last one saved, then just assume that the playpen is valid. + If the ds_object is not the most recent one, try to load the playpen with the contents referenced by the git_id + (reading the wiki, I discover we cannot count on metadata -- so cancel the git_id scheme) + """ + #keep our own copy of the metadata + self.activity_dict = self.metadata.copy() + debugstr = '' + for key in self.activity_dict.keys(): + if key == 'preview': continue + debugstr += key + ':'+str(self.activity_dict[key]) + ', ' + _logger.debug ('In read_file: activity dictionary==> %s'%debugstr) + if not self.debug_dict: self.get_config() + if self.activity_dict.get('uid','XxXxXx') == self.debug_dict.get('jobject_id','YyYyY'): + _logger.debug('pick up where we left off') + if self.activity_dict.get('child_path') and os.path.isdir(self.activity_dict.get('child_path')): + self.child_path = self.activity_dict['child_path'] + self.setup_new_activity() + return + if self.activity_dict.has_key('git_id') and self.debug_dict['git_id'] != '': + os.chdir(self.activity_dict['git_home']) + cmd = 'git checkout %s'%self.activity_dict.get('git_id') + git_output = self.command_line(cmd) + if git_output[1]==0: + self.load_activity_to_playpen(self.activity_dict.get('git_home')) + return + #update the project display + if self.journal_class: + self.journal_class.new_directory() + + + def write_file(self, file_path): + """ + The paradigm designed into the XO, ie an automatic load from the Journal at activity startup + does not make sense during a debugging session. An errant stack overflow can easily crash + the system and require a reboot. For the session manager to overwrite the changes that are stored + on disk, but not yet saved in the journal is highly undesireable. So we'll let the user save to + the journal, and perhaps optionally to the sd card (because it is removable, should the system die) + """ + if self.save_icon_clicked == True: #this was a specific request to save a commit to git + self.save_icon_clicked = False + _logger.debug('saving current playpen contents') + alert = self.alert(_('Saving current Activity to Home Directory')) + self.to_home_clicked_cb(None) #copies playpen and sets self._to_home_dest + if alert: self.remove_alert(alert) + alert = self.alert(_('Copying changes to GIT repository')) + git_id = self.do_git_commit(self._to_home_dest) + if alert: self.remove_alert(alert) + if git_id: + self.alert(_('GIT commit successful. ID:%s')%git_id) + self._jobject.metadata['commit_id'] = git_id + self._jobject.metadata['git_home'] = self._to_home_dest + today = datetime.date.today() + self._jobject.metadata['title'] = self.activity_dict['name'] +' GIT-'+ git_id #self.activity_dict.get('commit_text',today) + datastore.write(self._jobject) + self._jobject.destroy() + #get a new dsobject + self.debug_dict['jobject_id'] = str(self.get_new_dsobject()) + self._jobject = self.debug_dict['jobject_id'] + else: + alert = self.alert(_('GIT repository unchanged. File Tree is unchanged from previous COMMIT')) + jid = self.debug_dict.get('jobject_id','') + _logger.debug('write file debug-dict.jobject_id: %s. Jobject passed to write:%s'%(jid,self.metadata.get('uid',''))) + chunk = self.activity_dict.get('name','') + self._jobject.metadata['title'] = 'PyDebug_' + chunk + self._jobject.metadata['activity'] = 'org.laptop.PyDebug' + self._jobject.metadata['version'] = self.activity_dict.get('version','') + self._jobject.metadata['name'] = self.activity_dict.get('name','') + self._jobject.metadata['bundle_id'] = self.activity_dict.get('bundle_id','') + self._jobject.metadata['icon'] = self.activity_dict.get('icon','') + self._jobject.metadata['child_path'] = self.child_path + + datastore.write(self._jobject) + self.write_activity_info() + self.put_config() + return + + def do_git_commit(self, tree): + """ First check the status of the source tree, if it is modified, do a commit, and store the + commit id in the metadata. + Input: path of the source tree + Returns: Commit id or None if there is an error + """ + os.chdir(tree) + if not os.path.isdir(os.path.join(tree,'.git')): + cmd = 'git init' + cmd_output = self.command_line(cmd, alert_error=False) + cmd = 'git add *' + cmd_output = self.command_line(cmd, alert_error=False) + cmd = 'git status' + cmd_output = self.command_line(cmd, alert_error=False) + do_commit = False + if cmd_output[0]: + for line in cmd_output[0].split('\n'): + if line.find('modified') > -1: do_commit = True + if line.find('new file:') > -1: do_commit = True + if not do_commit: + _logger.debug('git did not commit because no unmodified files were detected') + return None #there have been no changes to write to library + today = datetime.date.today() + commit_message = self.activity_dict.get('commit_text',today) + cmd = 'git commit -a -m "%s"'%commit_message + cmd_output = self.command_line(cmd) + git_id = None + if not cmd_output[1]!=0: + _logger.debug('git commit returned no error') + return None + _logger.debug('git commit response:%s'%cmd_output) + for line in cmd_output.split('\n'): + chunks = line.split() + if len(chunks) < 2: continue + if chunks[0] == '[master': + if chunks[1][:-1] == ']': + git_id = chunks[1][:-1] + if len(chunks) < 3: continue + if chunks[2][:-1] == ']': + git_id = chunks[2][:-1] + if not git_id: return None + self.activity_dict['git_id'] = git_id + return git_id + + def write_binary_to_datastore(self): + """ + Check to see if there is a child loaded. + #Then copy the home directory data for this application into the bundle + then bundle it up and write it to the journal + lastly serialize the project information and write it to the journal + + + --fixme create an "untitled.activity" directory when child path is none and get rid of following check: + _logger.debug('Entered write_binary_to_datastore with child_path:%s'%self.child_path) + if not (os.path.isdir(self.child_path) and self.child_path.split('.')[-1:][0] == 'activity'): + self.alert(_('No Program loaded')) + return + """ + + + """ + fixme get rid of home directory management + #copy the home directory config stuff into the bundle + home_dir = os.path.join(self.child_path,'HOME') + try: + os.rmtree(home_dir) + except: + pass + try: + os.mkdir(home_dir) + except: + pass + source = self.debugger_home + dest = os.path.join(self.child_path,'HOME') + _logger.debug('writing HOME info from %s to %s.'%(source,dest)) + for f in os.listdir(self.debugger_home): + if f == '.': continue + if f == '..': continue + if f == 'pydebug': continue + if f == '.sugar': continue + try: + if os.path.isdir(f): + shutil.copytree(f,dest) + else: + shutil.copy(f,dest) + except: + pass + """ + dist_dir = os.path.join(self.child_path,'dist') + try: + os.rmtree(dist_dir) + except: + pass + try: + os.mkdir(dist_dir) + except: + pass + #create the manifest for the bundle + config = self.write_manifest() + do_tgz = True + #if manifest was successful, write the xo bundle to the instance directory + if config: + do_tgz = False + try: + #actually write the xo file + packager = bundlebuilder.XOPackager(bundlebuilder.Builder(config)) + packager.package() + source = os.path.join(self.child_path,'dist',str(config.xo_name)) + dest = os.path.join(self.get_activity_root(),'instance',str(config.xo_name)) + _logger.debug('writing to the journal from %s to %s.'%(source,dest)) + if os.path.isfile(dest): + os.unlink(dest) + try: + shutil.copy(source,dest) + package = config.xo_name + except IOError: + _logger.debug('shutil.copy error %d: %s. ',IOError[0],IOError[1]) + do_tgz = True + except: + do_tgz = True + if do_tgz: + dest = self.just_do_tar_gz() + if dest: + package = os.path.basename(dest) + dsobject = datastore.create() + dsobject.metadata['package'] = package + dsobject.metadata['title'] = package + dsobject.metadata['mime_type'] = 'binary' + dsobject.metadata['activity'] = 'org.laptop.PyDebug' + dsobject.metadata['icon'] = self.debug_dict.get('icon','') + #calculate and store the new md5sum + self.debug_dict['tree_md5'] = self.md5sum_tree(self.child_path) + dsobject.metadata['tree_md5'] = self.debug_dict['tree_md5'] + if dest: dsobject.set_file_path(dest) + + #actually make the call which writes to the journal + try: + datastore.write(dsobject,transfer_ownership=True) + _logger.debug('succesfully wrote to the journal from %s.'%(dest)) + except: + return + #update the project display + if self.journal_class: + self.journal_class.new_directory() + + def write_manifest(self): + try: + os.remove(os.path.join(self.child_path,'MANIFEST')) + except: + pass + dest = self.child_path + _logger.debug('Writing manifest to %s.'%(dest)) + try: + config = bundlebuilder.Config(dest) + b = bundlebuilder.Builder(config) + b.fix_manifest() + except: + _logger.debug('fix manifest error: ',sys.exc_type,sys.exc_info()[0],sys.exc_info()[1]) + return None + return config + + def just_do_tar_gz(self): + """ + tar and compress the child_path tree to the journal + """ + name = self.child_path.split('/')[-1].split(".")[0]+'.tar.gz' + os.chdir(self.activity_playpen) + dest = os.path.join(self.get_activity_root(),'instance',name) + cmd = 'tar czf %s %s'%(dest,'./'+os.path.basename(self.child_path)) + ans = self.command_line(cmd) + _logger.debug('cmd:%s'%cmd) + if ans[1]!=0: + return None + return dest + + def load_activity_to_playpen(self,file_path): + """loads from a disk tree""" + self._new_child_path = os.path.join(self.activity_playpen,os.path.basename(file_path)) + _logger.debug('copying file for %s to %s'%(file_path,self._new_child_path)) + self._load_playpen(file_path) + + def try_to_load_from_journal(self,object_id): + """ + loads a zipped XO or tar.gz application file (tar.gz if bundler cannot parse the activity.info file) + """ + self.ds = datastore.get(object_id[0]) + if not self.ds: + _logger.debug('failed to get datastore object with id:%s'%object_id[0]) + return + dsdict=self.ds.get_metadata() + file_name_from_ds = self.ds.get_file_path() + project = dsdict.get('package','') + if not (project.endswith('.xo') or project.endswith('.tar.gz')): + self.alert(_('This journal item does not appear to be a zipped activity. Package:%s.'%project)) + self.ds.destroy() + self.ds = None + return + filestat = os.stat(file_name_from_ds) + size = filestat.st_size + _logger.debug('In try_to_load_from_journal. Object_id %s. File_path %s. Size:%s'%(object_id[0], file_name_from_ds, size)) + if project.endswith('.xo'): + try: + self._bundler = ActivityBundle(file_name_from_ds) + name = self._bundler.get_name() + iszip=True + istar = False + except: + self.alert('Error: Malformed Activity Bundle') + self.ds.destroy() + self.ds = None + return + else: + name = project.split('.')[0] + #self.delete_after_load = os.path.abspath(file_name_from_ds,name) + iszip = False + istar = True + self._new_child_path = os.path.join(self.activity_playpen,name+'.activity') + self._load_playpen(file_name_from_ds, iszip, istar) + + def _load_playpen(self,source_fn, iszip = False, istar=False): + """entry point for both xo and file tree sources""" + self._load_to_playpen_source = source_fn + #if necessary clean up contents of playpen + if self.child_path and os.path.isdir(self.child_path): + self.abandon_changes = True + self.debug_dict['tree_md5'] = '' + self.debug_dict['child_path'] = '' + self.editor.remove_all() + if self.child_path: + shutil.rmtree(self.child_path) + self.abandon_changes = False + if self._load_to_playpen_source == None: + #having done the clearing, just stop + return + if iszip: + self._bundler.install(self.activity_playpen) + if self.ds: self.ds.destroy() + self.ds = None + elif istar: + dsdict = self.ds.get_metadata() + project = dsdict.get('package','dummy.tar.gz') + name = project.split('.')[0] + dest = os.path.join(self.activity_playpen,project) + shutil.copy(source_fn,dest) + os.chdir(self.activity_playpen) + cmd = 'tar zxf %s'%dest + _logger.debug('loading tar.gz with cmd %s'%cmd) + rtn = self.command_line(cmd) + if rtn[1] != 0: return + #os.unlink(dest) + if self.ds: self.ds.destroy() + self.ds = None + else: + if self.activity_playpen[-1] == '/': + dest = self.activity_playpen[:-1] + else: + dest = self.activity_playpen + os.chdir(self._load_to_playpen_source) + cmd = 'rsync -av %s %s'%(self._load_to_playpen_source,self.activity_playpen) + output = self.command_line(cmd) + if not output[1] == 0: + return + if self.delete_after_load: + shutil.rmtree(self.delete_after_load, ignore_errors=True) + self.debug_dict['source_tree'] = self._load_to_playpen_source + self.child_path = self._new_child_path + self.setup_new_activity() + + def setup_new_activity(self): + _logger.debug('in setup_new_activity. child path before chdir:%s'%self.child_path) + if self.child_path == None: + return + os.chdir(self.child_path) + self.read_activity_info(self.child_path) + self.display_current_project() + + #add the bin directory to path + os.environ['PATH'] = os.path.join(self.child_path,'bin') + ':' + os.environ['PATH'] + + #calculate and store the md5sum + self.debug_dict['tree_md5'] = self.md5sum_tree(self.child_path) + + #find largest python files for editor + list = [f for f in os.listdir(self.child_path) if f[0] <> '.'] + #list = self.manifest_class.get_filenames_list(self.child_path) + if not list: return + sizes = [] + for f in list: + full_path = os.path.join(self.child_path,f) + if not f.endswith('.py'):continue + size = self.manifest_class.file_size(full_path) + sizes.append((size,full_path,)) + #_logger.debug('python file "%s size %d'%(f,size)) + for s,f in sorted(sizes,reverse=True)[:5]: + self.editor.load_object(f,os.path.basename(f)) + self.editor.set_current_page(0) + + ##################### ALERT ROUTINES ################################## + + def alert(self,msg,title=None): + alert = NotifyAlert(10) + if title != None: + alert.props.title=_('There is no Activity file') + alert.props.msg = msg + alert.connect('response',self.no_file_cb) + self.add_alert(alert) + return alert + + def no_file_cb(self,alert,response_id): + self.remove_alert(alert) + + from sugar.graphics.alert import ConfirmationAlert + + def confirmation_alert(self,msg,title=None,confirmation_cb = None): + alert = ConfirmationAlert() + alert.props.title=title + alert.props.msg = msg + alert.pydebug_cb = confirmation_cb + alert.connect('response', self._alert_response_cb) + self.add_alert(alert) + return alert + + #### Method: _alert_response_cb, called when an alert object throws a + #response event. + def _alert_response_cb(self, alert, response_id): + #remove the alert from the screen, since either a response button + #was clicked or there was a timeout + this_alert = alert #keep a reference to it + self.remove_alert(alert) + #Do any work that is specific to the type of button clicked. + if response_id is gtk.RESPONSE_OK and this_alert.pydebug_cb != None: + this_alert.pydebug_cb (this_alert, response_id) + + def _new_file_cb(self, widget): + full_path = self.non_conflicting(self.child_path,'Unsaved_Document.py') + self.editor.load_object(full_path,os.path.basename(full_path)) + + def _read_file_cb(self,widget): + _logger.debug('Reading a file into editor') + dialog = gtk.FileChooserDialog("Open..", + None, + gtk.FILE_CHOOSER_ACTION_OPEN, + (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, + gtk.STOCK_OPEN, gtk.RESPONSE_OK)) + dialog.set_default_response(gtk.RESPONSE_OK) + if self.last_filename == None: + self.last_filename = self.child_path + dialog.set_current_folder(os.path.dirname(self.last_filename)) + + filter = gtk.FileFilter() + filter.set_name("All files") + filter.add_pattern("*") + dialog.add_filter(filter) + + filter = gtk.FileFilter() + filter.set_name("Python") + filter.add_pattern("*.py") + dialog.add_filter(filter) + + filter = gtk.FileFilter() + filter.set_name("Activity") + filter.add_pattern("*.xo") + dialog.add_filter(filter) + + response = dialog.run() + if response == gtk.RESPONSE_OK: + _logger.debug(dialog.get_filename(), 'selected') + fname = dialog.get_filename() + self.last_filename = fname + self.editor.load_object(fname,os.path.basename(fname)) + elif response == gtk.RESPONSE_CANCEL: + _logger.debug( 'File chooseer closed, no files selected') + dialog.destroy() + + def save_file_cb(self, button): + """ + impliments the SaveAs function + """ + chooser = gtk.FileChooserDialog(title=None,action=gtk.FILE_CHOOSER_ACTION_SAVE, + buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_SAVE, + gtk.RESPONSE_OK)) + file_path = self.editor.get_full_path() + _logger.debug('Saving file %s'%(file_path)) + chooser.set_filename(file_path) + response = chooser.run() + new_fn = chooser.get_filename() + chooser.destroy() + if response == gtk.RESPONSE_CANCEL: + return + if response == gtk.RESPONSE_OK: + self.save_cb(None,new_fn) + + def save_cb(self,button,new_fn=None): + if new_fn: + full_path = new_fn + else: + full_path = self.editor.get_full_path() + if os.path.basename(full_path).startswith('Unsaved_Document'): #force a choice to keep or change the name + #fd = open(full_path,'w') + #fd.close() + self.save_file_cb(None) + return + page = self.editor._get_page() + if new_fn: + page.fullPath = new_fn + page.save(skip_md5 = True) + else: + page.save() + self.editor.clear_changed_star() + page.save_hash() + + def set_dirty(self, dirty): + self.dirty = dirty + + def md5sum_buffer(self, buffer, hash = None): + if hash == None: + hash = hashlib.md5() + hash.update(buffer) + return hash.hexdigest() + + def md5sum(self, filename, hash = None): + h = self._md5sum(filename,hash) + return h.hexdigest() + + def _md5sum(self, filename, hash = None): + if hash == None: + hash = hashlib.md5() + try: + fd = None + fd = open(filename, 'rb') + while True: + block = fd.read(128) + if not block: break + hash.update(block) + finally: + if fd != None: + fd.close() + return hash + + def md5sum_tree(self,root): + if not os.path.isdir(root): + return None + h = hashlib.md5() + for dirpath, dirnames, filenames in os.walk(root): + for filename in filenames: + abs_path = os.path.join(dirpath, filename) + h = self._md5sum(abs_path,h) + #print abs_path + return h.hexdigest() + + + + ############## Following code services the Project page ##################### + + def _get_project_canvas(self): + #initialize the link between program and the glade XML file + self.wTree=gtk.glade.XML(os.path.join(self.sugar_bundle_path,"project.glade")) + self.contents = self.wTree.get_widget("contents") + self.contents.unparent() + return self.contents + + def setup_project_page(self): + self.activity_treeview = self.wTree.get_widget('file_system') + self.activity_window = FileTree(self, self.activity_treeview,self.wTree) + self.activity_window.set_file_sys_root('/home/olpc/Activities') + self.examples_treeview = self.wTree.get_widget('examples') + self.examples_window = FileTree(self, self.examples_treeview,self.wTree) + self.examples_window.set_file_sys_root(os.path.join(self.sugar_bundle_path,'examples')) + self.journal_treeview = self.wTree.get_widget('journal') + self.journal_class = DataStoreTree(self,self.journal_treeview,self.wTree) + self.connect_object() #make connections to signals from buttons + self.activity_toggled_cb(None) + if self.child_path and self.child_path.endswith('.activity') and \ + os.path.isdir(self.child_path): + self.setup_new_activity() + """ + #get set to sense addition of a usb flash drive + self.volume_monitor = gio.volume_monitor_get() + self.volume_monitor.connect('mount-added',self.__mount_added_cb) + self.volume_monitor.connect('mount-removed',self.__mount_removed_cb) + mount_list = self.volume_monitor.get_mounts() + for m in mount_list: + s = m.get_root().get_path() + if s.startswith('/media'):_logger.debug('volume:%s',s) + """ + def __mount_added_cb(self, vm, mount): + pass + + def __mount_removed_cb(self, vm, mount): + pass + + def display_current_project(self): + #try to colorize the playpen + pp = self.wTree.get_widget('playpen_event_box') + map = pp.get_colormap() + color = map.alloc_color(PROJECT_BG) + style = pp.get_style().copy() + style.bg[gtk.STATE_NORMAL] = color + pp.set_style(style) + + self.manifest_treeview = self.wTree.get_widget('manifest') + map =self.manifest_treeview.get_colormap() + color = map.alloc_color(PROJECT_BASE) + style = self.manifest_treeview.get_style().copy() + style.bg[gtk.STATE_NORMAL] = color + color = map.alloc_color(PROJECT_BASE) + style.base[gtk.STATE_NORMAL] = color + self.manifest_treeview.set_style(style) + if self.manifest_class == None: + self.manifest_class = FileTree(self, self.manifest_treeview,self.wTree) + self.manifest_class.set_file_sys_root(self.child_path) + + name = self.wTree.get_widget('name') + #map = name.get_colormap() + color = map.alloc_color(PROJECT_BASE) + style = name.get_style().copy() + style.base[gtk.STATE_NORMAL] = color + name.set_style(style) + name.set_text(self.activity_dict.get('name','')) + + version = self.wTree.get_widget('version') + version.set_style(style) + version.set_text(self.activity_dict.get('version','')) + + bundle = self.wTree.get_widget('bundle_id') + bundle.set_style(style) + bundle.set_text(self.activity_dict.get('bundle_id','')) + + pyclass = self.wTree.get_widget('class') + pyclass.set_style(style) + pyclass.set_text(self.activity_dict.get('class','')) + + pymodule = self.wTree.get_widget('module') + pymodule.set_style(style) + pymodule.set_text(self.activity_dict.get('module','')) + """ + self.wTree.get_widget('home_save').set_text(self.activity_dict.get('home_save','')) + self.wTree.get_widget('host').set_text(self.debug_dict.get('host','')) + self.wTree.get_widget('port').set_text(str(self.debug_dict.get('port',''))) + activity_size = os.system('du --max-depth=0') + self.wTree.get_widget('activity_size').set_text(str(activity_size)) + self.wTree.get_widget('icon').set_text(self.activity_dict.get('icon','').split('/')[-1:][0]) + """ + + def manifest_point_to(self,fullpath): + if self.child_path: + self.manifest_class.set_file_sys_root(self.child_path) + else: + self.manifest_class.set_file_sys_root(self.activity_playpen) + self.manifest_class.position_to(fullpath) + + #first connect the glade xml file to the servicing call backs + def connect_object(self, wTree=None): + """if wTree: + self.wTree=wTree + if self.wTree:""" + mdict = { + 'name_changed_cb':self.name_changed_cb, + 'bundle_id_changed_cb':self.bundle_id_changed_cb, + 'class_changed_cb':self.class_changed_cb, + 'icon_changed_cb':self.icon_changed_cb, + 'version_changed_cb':self.version_changed_cb, + 'file_toggle_clicked_cb':self.activity_toggled_cb, + 'to_activities_clicked_cb':self.to_home_clicked_cb, + 'from_activities_clicked_cb':self.from_home_clicked_cb, + 'from_examples_clicked_cb':self.from_examples_clicked_cb, + 'help_clicked_cb':self.project_help_cb, + 'delete_file_clicked_cb':self.delete_file_cb, + 'clear_clicked_cb':self.clear_clicked_cb, + } + self.wTree.signal_autoconnect(mdict) + button = self.wTree.get_widget('file_toggle') + button.set_tooltip_text(_('Switch views between the "Installed" Activities directory and your "home" storage directory')) + button = self.wTree.get_widget('to_activities') + button.set_tooltip_text(_('Copy the files in the debug workplace to your "home" storage directory')) + button = self.wTree.get_widget('from_examples') + button.set_tooltip_text(_('Load and modify these example programs. See the help Tutorials')) + + button = self.wTree.get_widget('delete_file') + map = button.get_colormap() + color = map.alloc_color(PROJECT_FG) + style = button.get_style().copy() + style.bg[gtk.STATE_NORMAL] = color + button.set_style(style) + + button = self.wTree.get_widget('clear') + button.set_style(style) + + button = self.wTree.get_widget('help') + button.set_style(style) + + def project_help_cb(self): + self.help_selected() + + def name_changed_cb(self, widget): + self.activity_dict['name'] = widget.get_text() + + def bundle_id_changed_cb(self,widget): + self.activity_dict['bundle_id'] = widget.get_text() + + def class_changed_cb(self, widget): + self.activity_dict['class'] = widget.get_text() + + def icon_changed_cb(self, widget): + self.activity_dict['icon'] = widget.get_text() + + def version_changed_cb(self, widget): + self.activity_dict['version'] = widget.get_text() + + def name_changed_cb(self, widget): + self.activity_dict['name'] = widget.get_text() + + def commit_changed_cb(self, widget): + self.activity_dict['commit_text'] = widget.get_text() + + def volume_drop_changed_cb(self, widget): + self.activity_dict['name'] = widget.get_text() + + def volume_save_cb(self, widget): + self.activity_dict['save'] = widget.get_text() + + def activity_toggled_cb(self, widget): + _logger.debug('Entered activity_toggled_cb. Button: %r'%self.file_pane_is_activities) + but = self.wTree.get_widget('to_activities') + to_what = self.wTree.get_widget('file_toggle') + window_label = self.wTree.get_widget('file_system_label') + if self.file_pane_is_activities == True: + to_what.set_label('Installed_') + but.show() + #display_label = self.storage[:18]+' . . . '+self.storage[-24:] + display_label = 'PyDebug HOME storage:' + self.activity_window.set_file_sys_root(self.storage) + button = self.wTree.get_widget('from_activities') + button.set_tooltip_text(_('Copy the selected directory or file from your "home" storage to the debug workplace')) + window_label.set_text(display_label) + else: + to_what.set_label('home') + but.hide() + self.activity_window.set_file_sys_root('/home/olpc/Activities') + button = self.wTree.get_widget('from_activities') + button.set_tooltip_text(_('Copy the selected Activity or file to the debug workplace')) + window_label.set_text('INSTALLED ACTIVITIES:') + self.file_pane_is_activities = not self.file_pane_is_activities + + def to_home_clicked_cb(self,widget): + _logger.debug('Entered to_home_clicked_cb') + self._to_home_dest = os.path.join(self.storage,self.activity_dict['name']+'.activity') + if False: #os.path.isdir(self._to_home_dest): + target_md5sum = self.md5sum_tree(self._to_home_dest) + if target_md5sum != self.debug_dict.get('tree_md5',''): + self.confirmation_alert(_('OK to delete/overwrite %s?'%self._to_home_dest), + _('This destination has been changed by another application'), + self._to_home_cb) + return + self._to_home_cb( None, gtk.RESPONSE_OK) + + def _to_home_cb(self, alert, response_id): + if alert != None: self.remove_alert(alert) + if response_id is gtk.RESPONSE_OK: + cmd = ['rsync','-av',self.child_path + '/',self._to_home_dest] + _logger.debug('do to_home_cb with cmd:%s'%cmd) + p1 = Popen(cmd,stdout=PIPE) + output = p1.communicate() + if p1.returncode != 0: + self.alert('rsync command returned non zero\n'+output[0]+ 'COPY FAILURE') + return + #redraw the treeview + self.activity_window.set_file_sys_root(self.storage) + + def from_home_clicked_cb(self,widget): + _logger.debug('Entered from_home_clicked_cb') + selection=self.activity_treeview.get_selection() + (model,iter)=selection.get_selected() + if iter == None: + self.alert(_('Must select File or Directory item to Load')) + return + fullpath = model.get(iter,4)[0] + if os.path.isdir(fullpath): + if not fullpath.endswith('.activity'): + self.alert(_('Use this button for Activities or Files'), + _('ERROR: This folder name does not end with ".activity"')) + return + self.load_activity_to_playpen(fullpath) + else: + #selected is a file, just copy it into the current project + basename = os.path.basename(fullpath) + if os.path.isfile(os.path.join(self.child_path,basename)): + #change name if necessary to prevent collision + basename = self.non_conflicting(self.child_path,basename) + shutil.copy(fullpath,os.path.join(self.child_path,basename)) + self.manifest_point_to(os.path.join(self.child_path,basename)) + + def non_conflicting(self,root,basename): + """ + create a non-conflicting filename by adding '-<number>' to a filename before extension + """ + ext = '' + basename = basename.split('.') + word = basename[0] + if len(basename) > 1: + ext = basename[1] + adder = '' + index = 0 + while os.path.isfile(os.path.join(root,word+adder+'.'+ext)): + index +=1 + adder = '-%s'%index + return os.path.join(root,word+adder+'.'+ext) + + def from_examples_clicked_cb(self,widget): + _logger.debug('Entered from_examples_clicked_cb') + selection=self.examples_treeview.get_selection() + (model,iter)=selection.get_selected() + if iter == None: + self.alert(_('Must select File or Directory item to Load')) + return + fullpath = model.get(iter,4)[0] + if fullpath.endswith('.activity'): + self.load_activity_to_playpen(fullpath) + return + self._load_to_playpen_source = fullpath + try: + self._bundler = ActivityBundle(fullpath) + except: + self.alert('Error: Malformed Activity Bundle') + return + + self._new_child_path = os.path.join(self.activity_playpen,self._bundler._zip_root_dir) + #check to see if current activity in playpen needs to be saved, and load new activity if save is ok + self._load_playpen(fullpath, iszip=True) + + def filetree_activated(self): + _logger.debug('entered pydebug filetree_activated') + + def read_activity_info(self, path): + """ + Parses the ./activity/activity.info file + + filen = os.path.join(self.child_path,'activity','activity.info') + try: + fd = open(filen,'r') + except: + _logger.debug('failed to open %s'%filen) + return + for line in fd.readlines(): + if line.lstrip() == '': continue + _logger.debug('activity line %s'%line) + tokens = line.split('=') + + if len(tokens) > 1: + keyword = tokens[0].lower().rstrip() + rside = tokens[1].split() + if keyword == 'class': + if '.' in rside[0]: + self.activity_dict['class'] = rside[0].split('.')[1] + self.activity_dict['module'] = rside[0].split('.')[0] + elif keyword == 'exec': + if rside[0] == 'sugar-activity' and '.' in rside[1]: + self.activity_dict['class'] = rside[1].split('.')[1] + self.activity_dict['module'] = rside[1].split('.')[0] + else: + self.activity_dict['module'] = rside[0] + elif keyword == 'bundle_id' or keyword == 'service_name': + self.activity_dict['bundle_id'] = rside[0] + elif keyword == 'activity_version': + self.activity_dict['version'] = rside[0] + elif keyword == 'name': + self.activity_dict['name'] = rside[0] + elif keyword == 'icon': + self.activity_dict['icon'] = rside[0] + fd.close() + + debugstr = '' + for key in self.activity_dict.keys(): + debugstr += key + ':'+str(self.activity_dict[key]) + ', ' + _logger.debug ('In read_activity: activity dictionary==> %s'%debugstr) + """ + try: + bundle = ActivityBundle(path) + except: + #msg = _('%s not recognized by ActivityBundle parser. Does activity/activity.info exist?'%os.path.basename(path)) + #self.alert(msg) + self.init_activity_dict() + return #maybe should issue an alert here + self.activity_dict['version'] = str(bundle.get_activity_version()) + self.activity_dict['name'] = bundle.get_name() + self.activity_dict['bundle_id'] = bundle.get_bundle_id() + self.activity_dict['command'] = bundle.get_command() + cmd_args = activityfactory.get_command(bundle) + mod_class = cmd_args[1] + if '.' in mod_class: + self.activity_dict['class'] = mod_class.split('.')[1] + self.activity_dict['module'] = mod_class.split('.')[0] + self.activity_dict['icon'] = bundle.get_icon() + + def init_activity_dict(self): + self.activity_dict['version'] = '1' + self.activity_dict['name'] = 'untitled' + self.activity_dict['bundle_id'] = '' + self.activity_dict['command'] = '' + self.activity_dict['class'] = '' + self.activity_dict['module'] = '' + self.activity_dict['icon'] = '' + + def write_activity_info(self): + #write the activity.info file + _logger.debug('entered write_actiity_info') + if self.child_path == None: return + filen = os.path.join(self.child_path,'activity','activity.info') + try: + with open(filen,'r') as fd: + #and also write to a new file + filewr = os.path.join(self.child_path,'activity','activity.new') + #try: + with open(filewr,'w') as fdw: + #write the required lines + _logger.debug('writing activity info to %s'%filewr) + fdw.write('[Activity]\n') + fdw.write('name = %s\n'%self.activity_dict.get('name')) + fdw.write('bundle_id = %s\n'%self.activity_dict.get('bundle_id')) + fdw.write('activity_version = %s\n'%self.activity_dict.get('version')) + icon = self.activity_dict.get('icon')[len(self.child_path)+10:-4] + fdw.write('icon = %s\n'%icon) + if self.activity_dict.get('class','') == '': + fdw.write('exec = %s\n'%self.activity_dict.get('module')) + else: + fdw.write('class = %s.%s\n'%(self.activity_dict.get('module'), + self.activity_dict.get('class'))) + #pass the rest of the input to the output + passup = ('[activity]','exec','activity_version','name','bundle_id', + 'service_name','icon','class') + for line in fd.readlines(): + tokens = line.split('=') + keyword = tokens[0].lower().rstrip() + if keyword in passup: continue + fdw.write(line) + return True + except: + return False + """ + except EnvironmentError: + _logger.debug('failed to open %s for writing. msg:%s'%(filewr,EnvironmentError[1])) + + except EnvironmentError: + _logger.debug('failed to open %s msg:%s'%(filen,EnvironmentError[1])) + """ + + def delete_file_cb(self,widget): + selection=self.manifest_treeview.get_selection() + (model,iter)=selection.get_selected() + if iter == None: + self.alert(_('Must select a File or Folder to delete')) + return + fullpath = model.get(iter,4)[0] + _logger.debug(' delete_file_clicked_cb. File: %s'%(fullpath)) + self.delete_file_storage = fullpath + if os.path.isdir(fullpath): + self.confirmation_alert(_('Would you like to continue deleting %s?'%os.path.basename(fullpath)), + _('CAUTION: You are about to DELETE a FOLDER!!'),self.do_delete_folder) + return + self.confirmation_alert(_('Would you like to continue deleting %s?'%os.path.basename(fullpath)), + _('ABOUT TO DELETE A FILE!!'),self.do_delete) + + def do_delete(self, alert, response): + _logger.debug('doing delete of: %s'%self.delete_file_storage) + self.manifest_point_to(self.delete_file_storage) + os.unlink(self.delete_file_storage) + self.manifest_class.set_file_sys_root(self.child_path) + self.manifest_class.position_recent() + + def do_delete_folder(self, alert, response): + _logger.debug('doing delete of: %s'%self.delete_file_storage) + self.manifest_point_to(self.delete_file_storage) + shutil.rmtree(self.delete_file_storage) + self.manifest_class.set_file_sys_root(self.child_path) + self.manifest_class.position_recent() + + def clear_clicked_cb(self, button): + self.editor.remove_all() + self.init_activity_dict() + self.child_path = None + self.manifest_class.set_file_sys_root(self.child_path) + self.display_current_project() + + ################ Help routines + def help_selected(self): + """ + if help is not created in a gtk.mainwindow then create it + else just switch to that viewport + """ + if not self.help_x11: + #self.window_instance = self.window.window + self.help_x11 = self.help.realize_help() + #self.x11_window = self.get_x11()os.geteuid() + else: + self.help.activate_help() + #self.help.reshow() + #self.help.toolbox.set_current_page(self.panes['HELP'] + """ + def get_x11(self): + home_model = shell.get_model() + activity = home_model.get_active_activity() + if activity and activity.get_window(): + + return activity.get_window().activate(1) + else: + return None + """ + ################ save config state from one invocation to another -- not activity state + def get_config(self): + try: + fd = open(os.path.join(self.debugger_home,'pickl'),'rb') + local = pickle.load(fd) + self.debug_dict = local.copy() + _logger.debug('unpickled successfully') + """ + object_id = self.debug_dict.get('jobject_id','') + if object_id != '': + self._jobject = datastore.get(object_id) + else: + self._jobject = None + """ + except: + try: + fd = open(os.path.join(self.debugger_home,'pickl'),'wb') + self.debug_dict['host'] = 'localhost' + self.debug_dict['port'] = 18812 + self.debug_dict['autosave'] = True + self.debug_dict['child_path'] = '' + local = self.debug_dict.copy() + pickle.dump(local,fd,pickle.HIGHEST_PROTOCOL) + except IOError: + _logger.debug('get_config -- Error writing pickle file %s' + %os.path.join(self.debugger_home,'pickl')) + finally: + fd.close() + object_id = self.debug_dict.get('jobject_id','') + if object_id == '': + jobject = self.get_new_dsobject() + self._jobject = jobject + self.debug_dict['jobject_id'] = str(self._jobject.object_id) + _logger.debug('in get_config created jobject id:%s'%self.debug_dict['jobject_id']) + else: + pass + #self._jobject = datastore.get(object_id) + self.child_path = self.debug_dict.get('child_path','') + if self.child_path == '' or not os.path.isdir(self.child_path): + self.child_path = None + + debugstr = '' + for key in self.debug_dict.keys(): + debugstr += key + ':'+str(self.debug_dict[key]) + ', ' + _logger.debug ('In get_config: debug dictionary==> %s'%debugstr) + + def get_new_dsobject(self): + jobject = datastore.create() + jobject.metadata['title'] = 'PyDebug' + jobject.metadata['activity'] = 'org.laptop.PyDebug' + jobject.metadata['keep'] = '1' + jobject.metadata['preview'] = '' + datastore.write(jobject) + return jobject + + def check_child_md5(self): + if self.child_path and self.debug_dict.get('tree_md5',''): + if self.debug_dict.get('tree_md5','') == self.md5sum_tree(self.child_path): + self.setup_new_activity() + #the tree is valid so take up where we left off + else: + self.confirmation_alert(_('Continue even though stored checksum does not match current checksum'), + _('CAUTION: The program in the playpen may have been changed.'), + self.startup_continue) + + def startup_continue(self,alert,response): + self.setup_new_activity() + + def put_config(self): + if self.child_path: + self.debug_dict['tree_md5'] = self.md5sum_tree(self.child_path) + self.debug_dict['child_path'] = self.child_path + try: + fd = open(os.path.join(self.debugger_home,'pickl'),'wb') + local = self.debug_dict.copy() + pickle.dump(local,fd,pickle.HIGHEST_PROTOCOL) + except IOError: + _logger.debug('put_config routine Error writing pickle file %s' + %os.path.join(self.debugger_home,'pickl')) + return + finally: + fd.close() + debugstr = '' + return + for key in self.debug_dict.keys(): + debugstr += key + ':'+str(self.debug_dict[key]) + ', ' + _logger.debug ('In put_config: debug dictionary==> %s'%debugstr) + |