From 05c1244133aa5657c168b052cbef3899bfd79e04 Mon Sep 17 00:00:00 2001 From: Simon Schampijer Date: Tue, 14 Oct 2008 12:49:50 +0000 Subject: Merge branch 'master' of ssh+git://dev.laptop.org/git/sugar --- diff --git a/bin/sugar-ui-check b/bin/sugar-ui-check index 168ef7e..4d71796 100644 --- a/bin/sugar-ui-check +++ b/bin/sugar-ui-check @@ -149,7 +149,6 @@ def main(): checks_queue.append(ActivityCheck('org.laptop.Pippy')) checks_queue.append(ActivityCheck('org.laptop.Terminal')) checks_queue.append(ActivityCheck('org.laptop.AbiWordActivity')) - checks_queue.append(ActivityCheck('org.laptop.sugar.ReadActivity')) checks_queue[0].start() gobject.timeout_add(500, _timeout_cb) diff --git a/extensions/deviceicon/Makefile.am b/extensions/deviceicon/Makefile.am index 66268fd..865ef27 100644 --- a/extensions/deviceicon/Makefile.am +++ b/extensions/deviceicon/Makefile.am @@ -4,4 +4,5 @@ sugar_PYTHON = \ __init__.py \ wireless.py \ battery.py \ - speaker.py + speaker.py \ + volume.py diff --git a/extensions/deviceicon/volume.py b/extensions/deviceicon/volume.py new file mode 100644 index 0000000..7db84ad --- /dev/null +++ b/extensions/deviceicon/volume.py @@ -0,0 +1,111 @@ +# Copyright (C) 2008 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 + +import os +import statvfs +from gettext import gettext as _ + +import gtk + +from sugar.graphics.tray import TrayIcon +from sugar.graphics.palette import Palette +from sugar.graphics.menuitem import MenuItem +from sugar.graphics.icon import Icon + +from jarabe.model import volume + +_icons = {} + +class DeviceView(TrayIcon): + + FRAME_POSITION_RELATIVE = 800 + + def __init__(self, model): + TrayIcon.__init__(self, icon_name=model.icon_name, + xo_color=model.icon_color) + self._model = model + + def create_palette(self): + return VolumePalette(self._model) + +class VolumePalette(Palette): + def __init__(self, model): + Palette.__init__(self, label=model.name, + secondary_text=model.mount_point) + self._model = model + + vbox = gtk.VBox() + self.set_content(vbox) + vbox.show() + + self._progress_bar = gtk.ProgressBar() + vbox.add(self._progress_bar) + self._progress_bar.show() + + self._free_space_label = gtk.Label() + self._free_space_label.set_alignment(0.5, 0.5) + vbox.add(self._free_space_label) + self._free_space_label.show() + + self.connect('popup', self.__popup_cb) + + menu_item = MenuItem(_('Unmount')) + + icon = Icon(icon_name='media-eject', icon_size=gtk.ICON_SIZE_MENU) + menu_item.set_image(icon) + icon.show() + + menu_item.connect('activate', self.__unmount_activate_cb) + self.menu.append(menu_item) + menu_item.show() + + def __unmount_activate_cb(self, menu_item): + self._model.unmount() + + def __popup_cb(self, palette): + stat = os.statvfs(self._model.mount_point) + free_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL] + total_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BLOCKS] + + fraction = (total_space - free_space) / float(total_space) + self._progress_bar.props.fraction = fraction + self._free_space_label.props.label = _('%(free_space)d MB Free') % \ + {'free_space': free_space / (1024 * 1024)} + +def setup(tray): + volumes_manager = volume.get_volumes_manager() + + for vol in volumes_manager.get_volumes(): + _add_device(vol, tray) + + volumes_manager.connect('volume-added', _volume_added_cb, tray) + volumes_manager.connect('volume-removed', _volume_removed_cb, tray) + +def _volume_added_cb(volumes_manager, vol, tray): + _add_device(vol, tray) + +def _volume_removed_cb(volumes_manager, vol, tray): + _remove_device(vol, tray) + +def _add_device(vol, tray): + icon = DeviceView(vol) + _icons[vol] = icon + tray.add_device(icon) + +def _remove_device(vol, tray): + icon = _icons[vol] + tray.remove_device(icon) + del _icons[vol] diff --git a/src/jarabe/desktop/activitieslist.py b/src/jarabe/desktop/activitieslist.py index db88c47..a39e525 100644 --- a/src/jarabe/desktop/activitieslist.py +++ b/src/jarabe/desktop/activitieslist.py @@ -91,10 +91,10 @@ class ActivitiesList(gtk.VBox): for entry in self._box.get_children(): entry.set_visible(entry.matches(query)) - def __key_press_event_cb(self, widget, event): + def __key_press_event_cb(self, scrolled_window, event): keyname = gtk.gdk.keyval_name(event.keyval) - vadjustment = self.props.vadjustment + vadjustment = scrolled_window.props.vadjustment if keyname == 'Up': if vadjustment.props.value > vadjustment.props.lower: vadjustment.props.value -= vadjustment.props.step_increment diff --git a/src/jarabe/frame/clipboardicon.py b/src/jarabe/frame/clipboardicon.py index f67ec1f..ff182dc 100644 --- a/src/jarabe/frame/clipboardicon.py +++ b/src/jarabe/frame/clipboardicon.py @@ -35,6 +35,9 @@ class ClipboardIcon(RadioToolButton): def __init__(self, cb_object, group): RadioToolButton.__init__(self, group=group) + + self.props.palette_invoker = FrameWidgetInvoker(self) + self._cb_object = cb_object self.owns_clipboard = False self.props.sensitive = False @@ -53,13 +56,15 @@ class ClipboardIcon(RadioToolButton): cb_service.connect('object-state-changed', self._object_state_changed_cb) - self.palette = ClipboardMenu(cb_object) - self.palette.props.invoker = FrameWidgetInvoker(self) - child = self.get_child() child.connect('drag_data_get', self._drag_data_get_cb) self.connect('notify::active', self._notify_active_cb) + def create_palette(self): + palette = ClipboardMenu(self._cb_object) + palette.set_group_id('frame') + return palette + def get_object_id(self): return self._cb_object.get_id() diff --git a/src/jarabe/frame/clipboardmenu.py b/src/jarabe/frame/clipboardmenu.py index 266dc81..0a5e615 100644 --- a/src/jarabe/frame/clipboardmenu.py +++ b/src/jarabe/frame/clipboardmenu.py @@ -37,7 +37,7 @@ from jarabe.model import bundleregistry class ClipboardMenu(Palette): def __init__(self, cb_object): - Palette.__init__(self, cb_object.get_name()) + Palette.__init__(self, text_maxlen=100) self._cb_object = cb_object @@ -70,8 +70,7 @@ class ClipboardMenu(Palette): self.menu.append(self._journal_item) self._journal_item.show() - self._update_items_visibility() - self._update_open_submenu() + self._update() def _update_open_submenu(self): activities = self._get_activities() @@ -156,7 +155,13 @@ class ClipboardMenu(Palette): def _object_state_changed_cb(self, cb_service, cb_object): if cb_object != self._cb_object: return - self.set_primary_text(cb_object.get_name()) + self._update() + + def _update(self): + self.props.primary_text = self._cb_object.get_name() + preview = self._cb_object.get_preview() + if preview: + self.props.secondary_text = preview self._update_progress_bar() self._update_items_visibility() self._update_open_submenu() diff --git a/src/jarabe/frame/clipboardobject.py b/src/jarabe/frame/clipboardobject.py index a4cd388..cf758ef 100644 --- a/src/jarabe/frame/clipboardobject.py +++ b/src/jarabe/frame/clipboardobject.py @@ -48,8 +48,9 @@ class ClipboardObject(object): return mime.get_mime_icon(self.get_mime_type()) def get_preview(self): - # TODO: should previews really be here? - #return self._get_type_info().get_preview() + for mime_type in ['text/plain']: + if mime_type in self._formats: + return self._formats[mime_type].get_data() return '' def is_bundle(self): diff --git a/src/jarabe/journal/Makefile.am b/src/jarabe/journal/Makefile.am index 11886d6..a8ef90f 100644 --- a/src/jarabe/journal/Makefile.am +++ b/src/jarabe/journal/Makefile.am @@ -14,5 +14,4 @@ sugar_PYTHON = \ objectchooser.py \ palettes.py \ query.py \ - volumesmanager.py \ volumestoolbar.py diff --git a/src/jarabe/journal/journaltoolbox.py b/src/jarabe/journal/journaltoolbox.py index 7e2709f..637965f 100644 --- a/src/jarabe/journal/journaltoolbox.py +++ b/src/jarabe/journal/journaltoolbox.py @@ -36,7 +36,7 @@ from sugar import mime from sugar.datastore import datastore from jarabe.model import bundleregistry -from jarabe.journal import volumesmanager +from jarabe.model import volume from jarabe.journal import misc _AUTOSEARCH_TIMEOUT = 1000 @@ -374,9 +374,9 @@ class EntryToolbar(gtk.Toolbar): if self._jobject: misc.resume(self._jobject, service_name) - def _copy_menu_item_activate_cb(self, menu_item, volume): + def _copy_menu_item_activate_cb(self, menu_item, vol): if self._jobject: - datastore.copy(self._jobject, volume.id) + datastore.copy(self._jobject, vol.id) def _refresh_copy_palette(self): palette = self._copy.get_palette() @@ -385,16 +385,16 @@ class EntryToolbar(gtk.Toolbar): palette.menu.remove(menu_item) menu_item.destroy() - volumes_manager = volumesmanager.get_volumes_manager() - for volume in volumes_manager.get_volumes(): - if self._jobject.metadata['mountpoint'] == volume.id: + volumes_manager = volume.get_volumes_manager() + for vol in volumes_manager.get_volumes(): + if self._jobject.metadata['mountpoint'] == vol.id: continue - menu_item = MenuItem(volume.name) - menu_item.set_image(Icon(icon_name=volume.icon_name, + menu_item = MenuItem(vol.name) + menu_item.set_image(Icon(icon_name=vol.icon_name, icon_size=gtk.ICON_SIZE_MENU)) menu_item.connect('activate', self._copy_menu_item_activate_cb, - volume) + vol) palette.menu.append(menu_item) menu_item.show() diff --git a/src/jarabe/journal/volumestoolbar.py b/src/jarabe/journal/volumestoolbar.py index 736a130..b29f325 100644 --- a/src/jarabe/journal/volumestoolbar.py +++ b/src/jarabe/journal/volumestoolbar.py @@ -24,7 +24,7 @@ from sugar.datastore import datastore from sugar.graphics.radiotoolbutton import RadioToolButton from sugar.graphics.palette import Palette -from jarabe.journal import volumesmanager +from jarabe.model import volume class VolumesToolbar(gtk.Toolbar): __gtype_name__ = 'VolumesToolbar' @@ -43,43 +43,45 @@ class VolumesToolbar(gtk.Toolbar): self.connect('destroy', self.__destroy_cb) - gobject.idle_add(self._set_up_volumes) + # TODO: It's unclear now how removable devices will be handled in the + # Journal. Disable for now. + #gobject.idle_add(self._set_up_volumes) def __destroy_cb(self, widget): - volumes_manager = volumesmanager.get_volumes_manager() + volumes_manager = volume.get_volumes_manager() volumes_manager.disconnect(self._volume_added_hid) volumes_manager.disconnect(self._volume_removed_hid) def _set_up_volumes(self): - volumes_manager = volumesmanager.get_volumes_manager() + volumes_manager = volume.get_volumes_manager() self._volume_added_hid = \ volumes_manager.connect('volume-added', self._volume_added_cb) self._volume_removed_hid = \ volumes_manager.connect('volume-removed', self._volume_removed_cb) - for volume in volumes_manager.get_volumes(): - self._add_button(volume) + for vol in volumes_manager.get_volumes(): + self._add_button(vol) - def _volume_added_cb(self, volumes_manager, volume): - self._add_button(volume) + def _volume_added_cb(self, volumes_manager, vol): + self._add_button(vol) - def _volume_removed_cb(self, volumes_manager, volume): - self._remove_button(volume) + def _volume_removed_cb(self, volumes_manager, vol): + self._remove_button(vol) - def _add_button(self, volume): - logging.debug('VolumeToolbar._add_button: %r' % volume.name) + def _add_button(self, vol): + logging.debug('VolumeToolbar._add_button: %r' % vol.name) if self._volume_buttons: group = self._volume_buttons[0] else: group = None - palette = Palette(volume.name) + palette = Palette(vol.name) - button = VolumeButton(volume, group) + button = VolumeButton(vol, group) button.set_palette(palette) - button.connect('toggled', self._button_toggled_cb, volume) + button.connect('toggled', self._button_toggled_cb, vol) if self._volume_buttons: position = self.get_item_index(self._volume_buttons[-1]) + 1 else: @@ -89,26 +91,26 @@ class VolumesToolbar(gtk.Toolbar): self._volume_buttons.append(button) - if volume.can_unmount: + if vol.can_unmount: menu_item = gtk.MenuItem(_('Unmount')) - menu_item.connect('activate', self._unmount_activated_cb, volume) + menu_item.connect('activate', self._unmount_activated_cb, vol) palette.menu.append(menu_item) menu_item.show() if len(self.get_children()) > 1: self.show() - def _button_toggled_cb(self, button, volume): + def _button_toggled_cb(self, button, vol): if button.props.active: - self.emit('volume-changed', volume.id) + self.emit('volume-changed', vol.id) - def _unmount_activated_cb(self, menu_item, volume): - logging.debug('VolumesToolbar._unmount_activated_cb: %r', volume.udi) - volume.unmount() + def _unmount_activated_cb(self, menu_item, vol): + logging.debug('VolumesToolbar._unmount_activated_cb: %r', vol.udi) + vol.unmount() - def _remove_button(self, volume): + def _remove_button(self, vol): for button in self.get_children(): - if button.volume.id == volume.id: + if button.volume.id == vol.id: self._volume_buttons.remove(button) self.remove(button) self.get_children()[0].props.active = True @@ -118,13 +120,13 @@ class VolumesToolbar(gtk.Toolbar): return class VolumeButton(RadioToolButton): - def __init__(self, volume, group): + def __init__(self, vol, group): RadioToolButton.__init__(self) - self.props.named_icon = volume.icon_name - self.props.xo_color = volume.icon_color + self.props.named_icon = vol.icon_name + self.props.xo_color = vol.icon_color self.props.group = group - self.volume = volume + self.volume = vol self.drag_dest_set(gtk.DEST_DEFAULT_ALL, [('journal-object-id', 0, 0)], gtk.gdk.ACTION_COPY) diff --git a/src/jarabe/model/Makefile.am b/src/jarabe/model/Makefile.am index d480f0f..a0828d6 100644 --- a/src/jarabe/model/Makefile.am +++ b/src/jarabe/model/Makefile.am @@ -11,4 +11,5 @@ sugar_PYTHON = \ network.py \ shell.py \ screen.py \ - sound.py + sound.py \ + volume.py diff --git a/src/jarabe/journal/volumesmanager.py b/src/jarabe/model/volume.py index b7e1e37..6afa6a6 100644 --- a/src/jarabe/journal/volumesmanager.py +++ b/src/jarabe/model/volume.py @@ -1,4 +1,4 @@ -# Copyright (C) 2007, One Laptop Per Child +# Copyright (C) 2007-2008, 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 @@ -15,14 +15,11 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import logging -from gettext import gettext as _ -import gconf import gobject import dbus -from sugar.graphics.xocolor import XoColor -from sugar.datastore import datastore +from sugar import profile HAL_SERVICE_NAME = 'org.freedesktop.Hal' HAL_MANAGER_PATH = '/org/freedesktop/Hal/Manager' @@ -51,16 +48,7 @@ class VolumesManager(gobject.GObject): def __init__(self): gobject.GObject.__init__(self) - self._volumes = [] - - client = gconf.client_get_default() - color = XoColor(client.get_string('/desktop/sugar/user/color')) - - # Internal flash is not in HAL - internal_fash_id = datastore.mounts()[0]['id'] - self._volumes.append(Volume(internal_fash_id, _('Journal'), - 'activity-journal', color, - None, False)) + self._volumes = {} bus = dbus.SystemBus() proxy = bus.get_object(HAL_SERVICE_NAME, HAL_MANAGER_PATH) @@ -77,13 +65,7 @@ class VolumesManager(gobject.GObject): (udi, e)) def get_volumes(self): - return self._volumes - - def _get_volume_by_udi(self, udi): - for volume in self._volumes: - if volume.udi == udi: - return volume - return None + return self._volumes.values() def _hal_device_added_cb(self, udi): bus = dbus.SystemBus() @@ -102,7 +84,7 @@ class VolumesManager(gobject.GObject): # Ignore volumes without a filesystem. if device.GetProperty('volume.fsusage') != 'filesystem': return False - # Ignore root. + # Ignore root. if device.GetProperty('volume.mount_point') == '/': return False @@ -133,7 +115,7 @@ class VolumesManager(gobject.GObject): HAL_MANAGER_PATH, arg0=udi) if device.GetProperty('volume.is_mounted'): - volume_id = self._mount_in_datastore(udi) + self._add_volume(udi) return label = device.GetProperty('volume.label') @@ -199,96 +181,45 @@ class VolumesManager(gobject.GObject): return if is_mounted: - if self._get_volume_by_udi(udi) is not None: - # device already mounted in the datastore - return - volume_id = self._mount_in_datastore(udi) + if udi not in self._volumes: + self._add_volume(udi) else: - self.unmount_from_datastore(udi) - return - - def _mount_in_datastore(self, udi): - logging.debug('VolumeToolbar._mount_in_datastore: %r' % udi) + if udi in self._volumes: + self._remove_volume(udi) + def _add_volume(self, udi): bus = dbus.SystemBus() device_object = bus.get_object(HAL_SERVICE_NAME, udi) device = dbus.Interface(device_object, HAL_DEVICE_IFACE) - - mount_point = device.GetProperty('volume.mount_point') - ds_mounts = datastore.mounts() - for ds_mount in ds_mounts: - if mount_point == ds_mount['uri']: - return ds_mount['id'] - - mount_id = datastore.mount('inplace:' + mount_point, - dict(title=mount_point)) - if not mount_id: - self._unmount_hal_device(udi) - raise RuntimeError('datastore.mount(%r, %r) failed.' % ( - 'inplace:' + mount_point, - dict(title=mount_point))) volume_name = device.GetProperty('volume.label') if not volume_name: volume_name = device.GetProperty('volume.uuid') - client = gconf.client_get_default() - color = XoColor(client.get_string('/desktop/sugar/user/color')) - volume = Volume(mount_id, - volume_name, - self._get_icon_for_volume(udi), - color, + + mount_point = device.GetProperty('volume.mount_point') + + volume = Volume(volume_name, + self._get_icon_for_volume(device), + profile.get_color(), udi, - True) - self._volumes.append(volume) - self.emit('volume-added', volume) + mount_point) + self._volumes[udi] = volume - logging.debug('mounted volume %s' % mount_point) + logging.debug('mounted volume %s' % udi) + self.emit('volume-added', volume) + + def _remove_volume(self, udi): + volume = self._volumes[udi] + del self._volumes[udi] + self.emit('volume-removed', volume) def _hal_device_removed_cb(self, udi): logging.debug('VolumesManager._hal_device_removed_cb: %r', udi) - bus = dbus.SystemBus() - #proxy = bus.get_object(HAL_SERVICE_NAME, HAL_MANAGER_PATH) - #hal_manager = dbus.Interface(proxy, HAL_MANAGER_IFACE) - # TODO: Why this doesn't work? - #if not hal_manager.DeviceExists(udi): - # self._unmount_from_datastore(udi) - # self._remove_button(udi) - # return - - proxy = bus.get_object(HAL_SERVICE_NAME, udi) - device = dbus.Interface(proxy, HAL_DEVICE_IFACE) - try: - is_mounted = device.GetProperty('volume.is_mounted') - except dbus.DBusException, e: - logging.debug('e: %s' % e) - self.unmount_from_datastore(udi) - return + if udi in self._volumes: + self._remove_volume(udi) - if is_mounted: - self._unmount_from_datastore(udi) - self._unmount_hal_device(udi) - - def unmount_from_datastore(self, udi): - logging.debug('VolumesManager._unmount_from_datastore: %r', udi) - volume = self._get_volume_by_udi(udi) - if volume is not None: - datastore.unmount(volume.id) - - self._volumes.remove(volume) - self.emit('volume-removed', volume) - - def unmount_hal_device(self, udi): - logging.debug('VolumesManager._unmount_hal_device: %r', udi) - bus = dbus.SystemBus() - device_object = bus.get_object(HAL_SERVICE_NAME, udi) - volume = dbus.Interface(device_object, HAL_VOLUME_IFACE) - volume.Unmount([]) - - def _get_icon_for_volume(self, udi): + def _get_icon_for_volume(self, device): bus = dbus.SystemBus() - device_object = bus.get_object(HAL_SERVICE_NAME, udi) - device = dbus.Interface(device_object, HAL_DEVICE_IFACE) - storage_udi = device.GetProperty('block.storage_device') obj = bus.get_object(HAL_SERVICE_NAME, storage_udi) storage_device = dbus.Interface(obj, HAL_DEVICE_IFACE) @@ -300,18 +231,19 @@ class VolumesManager(gobject.GObject): return 'media-flash-usb' class Volume(object): - def __init__(self, volume_id, name, icon_name, icon_color, udi, - can_unmount): - self.id = volume_id + def __init__(self, name, icon_name, icon_color, udi, mount_point): self.name = name self.icon_name = icon_name self.icon_color = icon_color self.udi = udi - self.can_unmount = can_unmount + self.mount_point = mount_point def unmount(self): - get_volumes_manager().unmount_from_datastore(self.udi) - get_volumes_manager().unmount_hal_device(self.udi) + logging.debug('Volumes.unmount: %r', self.udi) + bus = dbus.SystemBus() + device_object = bus.get_object(HAL_SERVICE_NAME, self.udi) + volume = dbus.Interface(device_object, HAL_VOLUME_IFACE) + volume.Unmount([]) def get_volumes_manager(): global _volumes_manager -- cgit v0.9.1