class RestoreActivity(activity.Activity): def __init__(self, handle): activity.Activity.__init__(self, handle, create_jobject=False) self.max_participants = 1 self._progress_bar = None self._message_box = None self._restore = None self._restore_button = None self._no_bundle_warning = None self._path = None self._setup_widgets() def read_file(self, file_path): """Set path to bundle to restore.""" self._path = file_path self._no_bundle_warning.hide() self._restore_button.set_sensitive(True) def write_file(self, file_path): """We don't have any state to save in the Journal.""" return def save(self): """We don't have any state to save in the Journal.""" return def close(self, skip_save=False): """We don't have any state to save in the Journal.""" activity.Activity.close(self, skip_save=True) def _setup_widgets(self): self._setup_toolbar() self._setup_main_view() def _setup_main_view(self): vbox = gtk.VBox() warning = _('No bundle selected. Please close this activity and' ' choose a bundle to restore from the Journal.') self._no_bundle_warning = gtk.Label(warning.encode('utf-8')) vbox.pack_start(self._no_bundle_warning, True) self.set_canvas(vbox) vbox.show_all() def _setup_toolbar(self): if pre_086_toolbars: self.toolbox = Toolbox() self.set_toolbox(self.toolbox) toolbar = gtk.Toolbar() self.toolbox.add_toolbar('Toolbar', toolbar) toolbar_box = self.toolbox else: toolbar_box = ToolbarBox() toolbar = toolbar_box.toolbar self.set_toolbar_box(toolbar_box) self._restore_button = RestoreButton() self._restore_button.connect('clicked', self._restore_cb) self._restore_button.set_sensitive(False) toolbar.insert(self._restore_button, -1) separator = gtk.SeparatorToolItem() separator.props.draw = False separator.set_expand(True) toolbar.insert(separator, -1) stop_button = StopButton(self) toolbar.insert(stop_button, -1) toolbar_box.show_all() def _restore_cb(self, button): """Callback for Restore button.""" self._setup_restore_view() self._start_restore() def _start_restore(self): """Set up and start background worker process.""" base_dir = os.environ.get('SUGAR_ACTIVITY_ROOT') if not base_dir: base_dir = sugar.env.get_profile_path(self.get_bundle_id()) tmp_dir = os.path.join(base_dir, 'instance') self._restore = AsyncRestore(self._path, tmp_dir) self._restore.connect('progress', self._progress_cb) self._restore.connect('error', self._error_cb) self._restore.connect('done', self._done_cb) self._restore.start() def _setup_restore_view(self): """Set up UI for showing feedback from worker process.""" self._restore_button.set_sensitive(False) vbox = gtk.VBox(False) label_text = _('Restoring Journal from %s') % (self._path, ) label = gtk.Label(label_text.encode('utf-8')) label.show() vbox.pack_start(label) alignment = gtk.Alignment(xalign=0.5, yalign=0.5, xscale=0.5) alignment.show() self._progress_bar = gtk.ProgressBar() self._progress_bar.props.text = _('Scanning bundle').encode('utf-8') self._progress_bar.show() alignment.add(self._progress_bar) vbox.add(alignment) self._message_box = gtk.Label() vbox.pack_start(self._message_box) # FIXME # self._close_button = gtk.Button(_('Abort')) # self._close_button.connect('clicked', self._close_cb) # self._close_button.show() # button_box = gtk.HButtonBox() # button_box.show() # button_box.add(self._close_button) # vbox.pack_start(button_box, False) vbox.show() self.set_canvas(vbox) self.show() def _progress_cb(self, restore_, position, num_entries): """Update progress bar with information from child process.""" self._progress_bar.props.text = '%d / %d' % (position, num_entries) self._progress_bar.props.fraction = float(position) / num_entries def _done_cb(self, restore_): """Restore finished.""" logging.debug('_done_cb') self._restore_button.set_sensitive(True) # self._close_button.set_label(_('Finish')) def _error_cb(self, restore_, message): """Receive error message from child process.""" self._show_error(unicode(message, 'utf-8')) self._restore_button.set_sensitive(True) def _show_error(self, message): """Present error message to user.""" self._message_box.props.label = unicode(message).encode('utf-8') self._message_box.show()
class Help(Window): def __init__(self, parent): self.parent_obj = parent hulahop.startup(os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],'data','gecko')) #from hulahop.webview import WebView from browser import Browser import xpcom from xpcom.components import interfaces self.help_id = None self.handle = ActivityHandle() self.handle.activity_id = util.unique_id() Window.__init__(self) self.connect('realize',self.realize_cb) #self.props.max_participants = 1 self._web_view = Browser() #Mimic the other tabs (hide the fact that this is another window) self.toolbox = Toolbox() self.toolbox.connect_after('current_toolbar_changed',self.goto_cb) self.set_toolbox(self.toolbox) self.toolbox.show() activitybar = gtk.Toolbar() self.toolbox.add_toolbar(_('Activity'), activitybar) activitybar.show_all() editbar = gtk.Toolbar() self.toolbox.add_toolbar(_('Edit'), editbar) editbar.show_all() usebar = gtk.Toolbar() self.toolbox.add_toolbar(_('Output'), usebar) usebar.show_all() self.help_toolbar = Toolbar(self._web_view) self.help_toolbar.show() self.toolbox.add_toolbar(_('Help'), self.help_toolbar) self.toolbox._notebook.set_current_page(HELP_PANE) self.set_canvas(self._web_view) self._web_view.show() self.toolbox.set_current_toolbar(HELP_PANE) def get_help_toolbar(self): return self.help_toolbar def realize_help(self): #trial and error suggest the following parent activation is necesssary to return reliably to parent window if version > 0.839: self.pywin = self.get_wnck_window_from_activity_id(str(self.parent_obj.handle.activity_id)) if self.pywin: self.pywin.activate(gtk.get_current_event_time()) _logger.debug('pywin.activate called') self.show_all() self.toolbox._notebook.set_current_page(HELP_PANE) return self def realize_cb(self, window): self.help_id = util.unique_id() wm.set_activity_id(window.window, self.help_id) self.help_window = window self._web_view.load_uri(HOME) def activate_help(self): _logger.debug('activate_help called') self.help_window.show() if version < 0.838: return window = self.get_wnck_window_from_activity_id(self.help_id) self.toolbox._notebook.set_current_page(HELP_PANE) if window: window.activate(gtk.get_current_event_time()) else: _logger.debug('failed to get window') def goto_cb(self, page, tab): _logger.debug('current_toolbar_changed event called goto_cb. tab: %s'%tab) if tab == HELP_PANE: return if not self.help_id: return self.parent_obj.set_toolbar(tab) self.help_window.hide() if version < 0.838: return self.pywin = self.get_wnck_window_from_activity_id(str(self.parent_obj.handle.activity_id)) if self.pywin: self.pywin.activate(gtk.get_current_event_time()) def get_wnck_window_from_activity_id(self, activity_id): """Use shell model to look up the wmck window associated with activity_id --the home_model code changed between .82 and .84 sugar --so do the lookup differently depending on sugar version """ _logger.debug('get_wnck_window_from_activity_id. id:%s'%activity_id) _logger.debug('sugar version %s'%version) if version and version >= 0.839: home_model = shell.get_model() activity = home_model.get_activity_by_id(activity_id) else: instance = view.Shell.get_instance() home_model = instance.get_model().get_home() activity = home_model._get_activity_by_id(activity_id) if activity: return activity.get_window() else: _logger.debug('wnck_window was none') return None
class BackupActivity(activity.Activity): _METADATA_JSON_NAME = '_metadata.json' _MEDIA_COMBO_ICON_COLUMN = 0 _MEDIA_COMBO_NAME_COLUMN = 1 _MEDIA_COMBO_PATH_COLUMN = 2 _MEDIA_COMBO_FREE_COLUMN = 3 def __init__(self, handle): activity.Activity.__init__(self, handle, create_jobject=False) self.max_participants = 1 self._progress_bar = None self._message_box = None self._media_combo_model = None self._media_combo = None self._backup_button = None self._backup = None self._hal_devices = {} self._color = profile.get_color() self._setup_widgets() self._find_media() def read_file(self, file_path): """We don't have any state to save in the Journal.""" return def write_file(self, file_path): """We don't have any state to save in the Journal.""" return def close(self, skip_save=False): """We don't have any state to save in the Journal.""" activity.Activity.close(self, skip_save=True) def _setup_widgets(self): self._setup_toolbar() self._setup_main_view() def _setup_main_view(self): vbox = gtk.VBox() self.set_canvas(vbox) vbox.show() def _setup_toolbar(self): if pre_086_toolbars: self.toolbox = Toolbox() self.set_toolbox(self.toolbox) toolbar = gtk.Toolbar() self.toolbox.add_toolbar('Toolbar', toolbar) toolbar_box = self.toolbox else: toolbar_box = ToolbarBox() toolbar = toolbar_box.toolbar self.set_toolbar_box(toolbar_box) self._media_combo_model = gtk.ListStore(str, str, str, str) self._media_combo = gtk.ComboBox(self._media_combo_model) if not pre_086_toolbars: icon_renderer = CellRendererIcon(self._media_combo) icon_renderer.props.xo_color = self._color icon_renderer.props.width = style.STANDARD_ICON_SIZE + \ style.DEFAULT_SPACING icon_renderer.props.height = style.STANDARD_ICON_SIZE icon_renderer.props.size = style.STANDARD_ICON_SIZE icon_renderer.props.xpad = style.DEFAULT_PADDING icon_renderer.props.ypad = style.DEFAULT_PADDING self._media_combo.pack_start(icon_renderer, False) self._media_combo.add_attribute(icon_renderer, 'icon_name', self._MEDIA_COMBO_ICON_COLUMN) # FIXME: icon_renderer for pre-0.86 name_renderer = gtk.CellRendererText() self._media_combo.pack_start(name_renderer, False) self._media_combo.add_attribute(name_renderer, 'text', self._MEDIA_COMBO_NAME_COLUMN) free_renderer = gtk.CellRendererText() self._media_combo.pack_start(free_renderer, False) self._media_combo.add_attribute(free_renderer, 'text', self._MEDIA_COMBO_FREE_COLUMN) tool_item = gtk.ToolItem() tool_item.add(self._media_combo) # FIXME: looks like plain GTK, not like Sugar tooltip_text = _('Storage medium to store the backup on') tool_item.set_tooltip_text(tooltip_text.encode('utf-8')) toolbar.insert(tool_item, -1) if pre_086_toolbars: self._backup_button = gtk.ToolButton() self._backup_button.props.icon_name = 'journal-export' label_text = _('Backup Journal') try: self._backup_button.props.tooltip = label_text.encode('utf-8') except AttributeError: pass else: self._backup_button = BackupButton() self._backup_button.connect('clicked', self._backup_cb) self._backup_button.set_sensitive(False) toolbar.insert(self._backup_button, -1) separator = gtk.SeparatorToolItem() separator.props.draw = False separator.set_expand(True) toolbar.insert(separator, -1) stop_button = StopButton(self) toolbar.insert(stop_button, -1) toolbar_box.show_all() def _backup_cb(self, button): """Callback for Backup button.""" if not len(self._media_combo_model): return row = self._media_combo_model[self._media_combo.get_active()] mount_point = row[self._MEDIA_COMBO_PATH_COLUMN] self._setup_backup_view(mount_point) self._backup_button.set_sensitive(False) self._start_backup(mount_point) def _start_backup(self, mount_point): """Set up and start background worker process.""" self._backup = AsyncBackup(mount_point) self._backup.connect('progress', self._progress_cb) self._backup.connect('error', self._error_cb) self._backup.connect('done', self._done_cb) self._backup.start() def _setup_backup_view(self, mount_point): """Set up UI for showing feedback from worker process.""" vbox = gtk.VBox(False) label_text = _('Backing up Journal to %s') % (mount_point, ) label = gtk.Label(label_text.encode('utf-8')) label.show() vbox.pack_start(label) alignment = gtk.Alignment(xalign=0.5, yalign=0.5, xscale=0.5) alignment.show() self._progress_bar = gtk.ProgressBar() self._progress_bar.props.text = _('Scanning Journal').encode('utf-8') self._progress_bar.show() alignment.add(self._progress_bar) vbox.add(alignment) self._message_box = gtk.Label() vbox.pack_start(self._message_box) # FIXME # self._close_button = gtk.Button(_('Abort')) # self._close_button.connect('clicked', self._close_cb) # self._close_button.show() # button_box = gtk.HButtonBox() # button_box.show() # button_box.add(self._close_button) # vbox.pack_start(button_box, False) vbox.show() self.set_canvas(vbox) self.show() def _progress_cb(self, backup, position, num_entries): """Update progress bar with information from child process.""" self._progress_bar.props.text = '%d / %d' % (position, num_entries) self._progress_bar.props.fraction = float(position) / num_entries def _done_cb(self, backup): """Backup finished.""" self._backup_button.set_sensitive(True) logging.debug('_done_cb') def _error_cb(self, backup, message): """Receive error message from child process.""" self._backup_button.set_sensitive(True) self._show_error(unicode(message, 'utf-8')) def _show_error(self, message): """Present error message to user.""" self._message_box.props.label = unicode(message).encode('utf-8') self._message_box.show() # def _close_cb(self, button): # if not self._done: # self._backup.abort() # self.emit('close') def _find_media(self): """Fill the combo box with available storage media. Also sets up a callback to keep the list current. """ try: import gio except ImportError: return self._find_media_hal() volume_monitor = gio.volume_monitor_get() volume_monitor.connect('mount-added', self._gio_mount_added_cb) volume_monitor.connect('mount-removed', self._gio_mount_removed_cb) for mount in volume_monitor.get_mounts(): self._gio_mount_added_cb(volume_monitor, mount) def _gio_mount_added_cb(self, volume_monitor, mount): """Handle notification from GIO that a medium was mounted.""" icon_name = self._choose_icon_name(mount.get_icon().props.names) path = mount.get_root().get_path() name = mount.get_name() self._add_medium(path, name, icon_name) def _gio_mount_removed_cb(self, volume_monitor, mount): """Handle notification from GIO that a medium was unmounted.""" path = mount.get_root().get_path() self._remove_medium(path) def _find_media_hal(self): """Use HAL to fill in the available storage media.""" bus = dbus.SystemBus() proxy = bus.get_object(HAL_SERVICE_NAME, HAL_MANAGER_PATH) hal_manager = dbus.Interface(proxy, HAL_MANAGER_IFACE) hal_manager.connect_to_signal('DeviceAdded', self._hal_device_added_cb) for udi in hal_manager.FindDeviceByCapability('volume'): self._hal_device_added_cb(udi) def _hal_device_added_cb(self, udi): """Handle notification from GIO that a device was added.""" bus = dbus.SystemBus() device_object = bus.get_object(HAL_SERVICE_NAME, udi) device = dbus.Interface(device_object, HAL_DEVICE_IFACE) # A just-added device might lack one of the attributes we're # looking for, so we need to listen for changes on ALL devices. device.connect_to_signal( 'PropertyModified', lambda *args: self._hal_property_modified_cb(udi, *args)) try: if device.GetProperty('volume.fsusage') != 'filesystem': logging.debug('Ignoring non-filesystem UDI %s', udi) return except dbus.DBusException: logging.debug('Ignoring UDI %s without volume.fsusage', udi) return self._hal_try_device(device, udi) def _hal_try_device(self, device, udi): """Possibly add device to UI.""" if not device.GetProperty('volume.is_mounted'): return path = device.GetProperty('volume.mount_point') if path == '/': return name = device.GetProperty('volume.label') if not name: name = device.GetProperty('volume.uuid') self._hal_devices[udi] = path bus = dbus.SystemBus() bus.add_signal_receiver(self._hal_device_removed_cb, 'DeviceRemoved', HAL_MANAGER_IFACE, HAL_SERVICE_NAME, HAL_MANAGER_PATH, arg0=udi) self._add_medium( path, name, self._choose_icon_name(self._hal_get_icons_for_volume(device))) def _hal_device_removed_cb(self, udi): """Handle notification from GIO that a device was removed.""" path = self._hal_devices.pop(udi, None) if not path: return self._remove_medium(path) def _hal_property_modified_cb(self, udi, count, changes): """Handle notification from HAL that a property has changed.""" if 'volume.is_mounted' in [change[0] for change in changes]: logging.debug('volume.is_mounted changed for UDI %s', udi) bus = dbus.SystemBus() device_object = bus.get_object(HAL_SERVICE_NAME, udi) device = dbus.Interface(device_object, HAL_DEVICE_IFACE) # We can get multiple notifications, so need to figure out # the current state and ignore non-changes. if device.GetProperty('volume.is_mounted'): if udi not in self._hal_devices: self._hal_try_device(device, udi) else: logging.debug('Ignoring duplicate notification') else: if udi in self._hal_devices: self._hal_device_removed_cb(udi) else: logging.debug('Ignoring duplicate notification') else: logging.debug('Ignoring change for UDI %s', udi) def _hal_get_icons_for_volume(self, device): bus = dbus.SystemBus() storage_udi = device.GetProperty('block.storage_device') obj = bus.get_object(HAL_SERVICE_NAME, storage_udi) storage_device = dbus.Interface(obj, HAL_DEVICE_IFACE) storage_drive_type = storage_device.GetProperty('storage.drive_type') bus_type = storage_device.GetProperty('storage.bus') if storage_drive_type == 'sd_mmc': return ['media-flash-sd', 'media-flash-sd-mmc', 'media'] elif bus_type == 'usb': return ['media-flash-usb', 'media', 'media-disk'] else: return ['media', 'media-disk', 'media-flash-usb'] def _add_medium(self, path, medium_name, icon_name): """Make storage medium selectable in the UI.""" # FIXME: update space information periodically or at least after # backup run stat = os.statvfs(path) free_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL] # total_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BLOCKS] self._media_combo_model.append([ icon_name, medium_name, path, _('%s Free') % (format_size(free_space), ) ]) self._backup_button.set_sensitive(True) if self._media_combo.get_active() == -1: self._media_combo.set_active(0) def _remove_medium(self, path): """Remove storage medium from UI.""" active = self._media_combo.get_active() for position, row in enumerate(self._media_combo_model): if path == row[self._MEDIA_COMBO_PATH_COLUMN]: del self._media_combo_model[position] if not len(self._media_combo_model): self._backup_button.set_sensitive(False) return if position != active: return self._media_combo.set_active(max(0, position - 1)) return logging.warning("Asked to remove %s from UI, but didn't find it!", path) def _choose_icon_name(self, names): """Choose the first valid icon name or fall back to a default name.""" theme = gtk.icon_theme_get_default() for name in names: if theme.lookup_icon(name, gtk.ICON_SIZE_LARGE_TOOLBAR, 0): return name return 'drive'
class Help(Window): def __init__(self, parent): self.pydebug = parent if version < 0.838: from hulahop.webview import WebView as Browser else: from browser import Browser self.help_id = None self.handle = ActivityHandle() self.handle.activity_id = util.unique_id() Window.__init__(self) self.connect('realize', self.realize_cb) self._web_view = Browser() #determine which language we are going to be using help_root = self.get_help_root() self.HOME = os.path.join(help_root, 'PyDebug.htm') self.toolbox = Toolbox() self.toolbox.connect('current_toolbar_changed', self.goto_cb) self.set_toolbox(self.toolbox) self.toolbox.show() activitybar = gtk.Toolbar() self.toolbox.add_toolbar(_('Activity'), activitybar) activitybar.show_all() editbar = gtk.Toolbar() self.toolbox.add_toolbar(_('Edit'), editbar) editbar.show_all() projectbar = gtk.Toolbar() self.toolbox.add_toolbar(_('Project'), projectbar) projectbar.show_all() self.help_toolbar = Toolbar(self, self._web_view) self.help_toolbar.show() self.toolbox.add_toolbar(_('Help'), self.help_toolbar) self.toolbox._notebook.set_current_page(HELP_PANE) self.set_canvas(self._web_view) self._web_view.show() self.toolbox.set_current_toolbar(HELP_PANE) self._web_view.load_uri(self.HOME) self.pid = Popen(['/usr/bin/pydoc', '-p', '23432']) def close_pydoc(self): _logger.debug('closing pid %s' % (self.pid.pid, )) self.killpid = Popen([ 'kill', str(self.pid.pid), ]) #self.pid.kill() def get_help_toolbar(self): return self.help_toolbar def realize_help(self): _logger.debug( 'realize help called Version: %s pydebug activity id:%s' % (version, self.pydebug.handle.activity_id)) #trial and error suggest the following pydebug activation is necesssary to return reliably to pydebug window if version > 0.839: self.pywin = self.get_wnck_window_from_activity_id( str(self.pydebug.handle.activity_id)) if self.pywin: self.pywin.activate(gtk.get_current_event_time()) _logger.debug('pywin.activate called') self.show_all() self.toolbox._notebook.set_current_page(HELP_PANE) return self def realize_cb(self, window): self.help_id = util.unique_id() wm.set_activity_id(window.window, self.help_id) self.help_window = window def activate_help(self): _logger.debug('activate_help called') self.help_window.show() self.toolbox._notebook.set_current_page(HELP_PANE) if version < 0.838: return window = self.get_wnck_window_from_activity_id(self.help_id) if window: window.activate(gtk.get_current_event_time()) else: _logger.debug('failed to get window') def goto_cb(self, page, tab): _logger.debug('current_toolbar_changed event called goto_cb. tab: %s' % tab) if tab == HELP_PANE: return if not self.help_id: return self.pydebug.set_toolbar(tab) self.help_window.hide() if version < 0.838: return self.pywin = self.get_wnck_window_from_activity_id( str(self.pydebug.handle.activity_id)) if self.pywin: self.pywin.activate(gtk.get_current_event_time()) def get_wnck_window_from_activity_id(self, activity_id): """Use shell model to look up the wmck window associated with activity_id --the home_model code changed between .82 and .84 sugar --so do the lookup differently depending on sugar version """ _logger.debug('get_wnck_window_from_activity_id. id:%s' % activity_id) _logger.debug('sugar version %s' % version) if version and version >= 0.839: home_model = shell.get_model() activity = home_model.get_activity_by_id(activity_id) else: instance = view.Shell.get_instance() home_model = instance.get_model().get_home() activity = home_model._get_activity_by_id(activity_id) if activity: return activity.get_window() else: _logger.debug('wnck_window was none') return None def get_help_root(self): lang = os.environ.get('LANGUAGE') if not lang: lang = os.environ.get('LANG') if not lang: lang = 'en_US' if len(lang) > 1: two_char = lang[:2] root = os.path.join(os.environ['SUGAR_BUNDLE_PATH'], 'help', two_char) if os.path.isdir(root): return root root = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'], 'help', two_char) if os.path.isdir(root): return root #default to a non localized root root = os.path.join(os.environ['SUGAR_BUNDLE_PATH'], 'help') return root