From cc7ce538c3e7d126cc8a1051d4f38e58761c7eaf Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Wed, 03 Dec 2008 12:23:37 +0000 Subject: Merge branch 'master' of git+ssh://dev.laptop.org/git/sugar --- diff --git a/bin/sugar-session b/bin/sugar-session index 12e1f9e..7118c21 100644 --- a/bin/sugar-session +++ b/bin/sugar-session @@ -77,20 +77,6 @@ def cleanup_logs(): dest_path = os.path.join(backup_dir, log) os.rename(source_path, dest_path) -def start_datastore(): - from sugar.datastore import datastore - - # Mount the datastore in internal flash - ds_path = env.get_profile_path('datastore') - try: - datastore.mount(ds_path, [], timeout=120) - except Exception, e: - # Don't explode if there's corruption; move the data out of the way - # and attempt to create a store from scratch. - logging.error(e) - shutil.move(ds_path, os.path.abspath(ds_path) + str(time.time())) - datastore.mount(ds_path, [], timeout=120) - def start_ui_service(): from jarabe.view.service import UIService @@ -146,7 +132,6 @@ def main(): if timezone is not '': os.environ['TZ'] = timezone - start_datastore() start_ui_service() start_session_manager() diff --git a/extensions/deviceicon/volume.py b/extensions/deviceicon/volume.py index 8342cf9..80cf242 100644 --- a/extensions/deviceicon/volume.py +++ b/extensions/deviceicon/volume.py @@ -17,15 +17,19 @@ import os import statvfs from gettext import gettext as _ +import logging +import gobject +import gio import gtk +import gconf 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 sugar.graphics.xocolor import XoColor -from jarabe.model import volume from jarabe.journal import journalactivity _icons = {} @@ -34,26 +38,35 @@ 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 __init__(self, mount): + TrayIcon.__init__(self) + self._mount = mount + + # TODO: fallback to the more generic icons when needed + self.get_icon().props.icon_name = self._mount.get_icon().props.names[0] + + # TODO: retrieve the colors from the owner of the device + client = gconf.client_get_default() + color = XoColor(client.get_string('/desktop/sugar/user/color')) + self.get_icon().props.xo_color = color + self.connect('button-release-event', self.__button_release_event_cb) def create_palette(self): - return VolumePalette(self._model) + return VolumePalette(self._mount) def __button_release_event_cb(self, widget, event): journal = journalactivity.get_journal() - journal.set_active_volume(self._model.mount_point) + journal.set_active_volume(self._mount) journal.present() return True class VolumePalette(Palette): - def __init__(self, model): - Palette.__init__(self, label=model.name, - secondary_text=model.mount_point) - self._model = model + def __init__(self, mount): + Palette.__init__(self, label=mount.get_name()) + self._mount = mount + + self.props.secondary_text = mount.get_root().get_path() vbox = gtk.VBox() self.set_content(vbox) @@ -81,10 +94,14 @@ class VolumePalette(Palette): menu_item.show() def __unmount_activate_cb(self, menu_item): - self._model.unmount() + self._mount.unmount(self.__unmount_cb) + + def __unmount_cb(self, source, result): + logging.debug('__unmount_cb %r %r' % (source, result)) def __popup_cb(self, palette): - stat = os.statvfs(self._model.mount_point) + mount_point = self._mount.get_root().get_path() + stat = os.statvfs(mount_point) free_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL] total_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BLOCKS] @@ -94,28 +111,44 @@ class VolumePalette(Palette): {'free_space': free_space / (1024 * 1024)} def setup(tray): - volumes_manager = volume.get_volumes_manager() + gobject.idle_add(_setup_volumes, tray) - for vol in volumes_manager.get_volumes(): - if vol.mount_point != '/': - _add_device(vol, tray) +def _setup_volumes(tray): + volume_monitor = gio.volume_monitor_get() - volumes_manager.connect('volume-added', _volume_added_cb, tray) - volumes_manager.connect('volume-removed', _volume_removed_cb, tray) + for volume in volume_monitor.get_volumes(): + _mount(volume, tray) -def _volume_added_cb(volumes_manager, vol, tray): - if vol.mount_point != '/': - _add_device(vol, tray) + for mount in volume_monitor.get_mounts(): + _add_device(mount, tray) -def _volume_removed_cb(volumes_manager, vol, tray): - _remove_device(vol, tray) + #volume_monitor.connect('volume-added', _volume_added_cb, tray) + volume_monitor.connect('mount-added', _mount_added_cb, tray) + volume_monitor.connect('mount-removed', _mount_removed_cb, tray) -def _add_device(vol, tray): - icon = DeviceView(vol) - _icons[vol] = icon - tray.add_device(icon) +def _volume_added_cb(volume_monitor, volume, tray): + _mount(volume, tray) -def _remove_device(vol, tray): - icon = _icons[vol] +def _mount(volume, tray): + #TODO: this should be done by some other process, like gvfs-hal-volume-monitor + #TODO: use volume.should_automount() when it gets into pygtk + if volume.get_mount() is None and volume.can_mount(): + #TODO: pass None as mount_operation, or better, SugarMountOperation + volume.mount(gtk.MountOperation(tray.get_toplevel()), _mount_cb) + +def _mount_cb(source, result): + logging.debug('mount finished %r %r' % (source, result)) + +def _mount_added_cb(volume_monitor, mount, tray): + _add_device(mount, tray) + +def _mount_removed_cb(volume_monitor, mount, tray): + icon = _icons[mount] tray.remove_device(icon) - del _icons[vol] + del _icons[mount] + +def _add_device(mount, tray): + icon = DeviceView(mount) + _icons[mount] = icon + tray.add_device(icon) + diff --git a/src/jarabe/journal/journalactivity.py b/src/jarabe/journal/journalactivity.py index 5ab99ef..e948162 100644 --- a/src/jarabe/journal/journalactivity.py +++ b/src/jarabe/journal/journalactivity.py @@ -239,9 +239,9 @@ class JournalActivity(Window): self._show_secondary_view(metadata) return True - def __volume_changed_cb(self, volume_toolbar, volume): - logging.debug('Selected volume: %r.' % volume.udi) - self._main_toolbox.search_toolbar.set_mount_point(volume.mount_point) + def __volume_changed_cb(self, volume_toolbar, mount_point): + logging.debug('Selected volume: %r.' % mount_point) + self._main_toolbox.search_toolbar.set_mount_point(mount_point) self._main_toolbox.set_current_toolbar(0) def __model_created_cb(self, object_id): @@ -320,8 +320,8 @@ class JournalActivity(Window): self.present() self._critical_space_alert = None - def set_active_volume(self, mount_point): - self._volumes_toolbar.set_active_volume(mount_point) + def set_active_volume(self, mount): + self._volumes_toolbar.set_active_volume(mount) _journal = None diff --git a/src/jarabe/journal/journaltoolbox.py b/src/jarabe/journal/journaltoolbox.py index beda184..f8052eb 100644 --- a/src/jarabe/journal/journaltoolbox.py +++ b/src/jarabe/journal/journaltoolbox.py @@ -21,6 +21,7 @@ import os import gconf import gobject +import gio import gtk from sugar.graphics.toolbox import Toolbox @@ -35,7 +36,6 @@ from sugar.graphics import style from sugar import mime from jarabe.model import bundleregistry -from jarabe.model import volume from jarabe.journal import misc from jarabe.journal import model @@ -373,8 +373,8 @@ class EntryToolbar(gtk.Toolbar): def _resume_menu_item_activate_cb(self, menu_item, service_name): misc.resume(self._metadata, service_name) - def _copy_menu_item_activate_cb(self, menu_item, vol): - model.copy(self._metadata, vol.mount_point) + def _copy_menu_item_activate_cb(self, menu_item, mount): + model.copy(self._metadata, mount.get_root().get_path()) def _refresh_copy_palette(self): palette = self._copy.get_palette() @@ -383,16 +383,19 @@ class EntryToolbar(gtk.Toolbar): palette.menu.remove(menu_item) menu_item.destroy() - volumes_manager = volume.get_volumes_manager() - for vol in volumes_manager.get_volumes(): - if self._metadata['mountpoint'] == vol.mount_point: + volume_monitor = gio.volume_monitor_get() + for mount in volume_monitor.get_mounts(): + if self._metadata['mountpoint'] == mount.get_root().get_path(): continue - menu_item = MenuItem(vol.name) - menu_item.set_image(Icon(icon_name=vol.icon_name, + menu_item = MenuItem(mount.get_name()) + + # TODO: fallback to the more generic icons when needed + menu_item.set_image(Icon(icon_name=mount.get_icon().props.names[0], icon_size=gtk.ICON_SIZE_MENU)) + menu_item.connect('activate', self._copy_menu_item_activate_cb, - vol) + mount) palette.menu.append(menu_item) menu_item.show() diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py index 19068f2..375345a 100644 --- a/src/jarabe/journal/model.py +++ b/src/jarabe/journal/model.py @@ -18,6 +18,7 @@ import logging import os from datetime import datetime import time +import shutil import dbus import gconf @@ -95,14 +96,54 @@ class ResultSet(object): length = property(get_length) + def _get_all_files(self, dir_path): + files = [] + for entry in os.listdir(dir_path): + full_path = os.path.join(dir_path, entry) + if os.path.isdir(full_path): + files.extend(self._get_all_files(full_path)) + elif os.path.isfile(full_path): + stat = os.stat(full_path) + files.append((full_path, stat.st_mtime)) + return files + + def _query_mount_point(self, mount_point, query): + t = time.time() + + files = self._get_all_files(mount_point) + offset = int(query.get('offset', 0)) + limit = int(query.get('limit', len(files))) + + total_count = len(files) + files.sort(lambda a, b: int(b[1] - a[1])) + files = files[offset:offset + limit] + + result = [] + for file_path, timestamp_ in files: + metadata = _get_file_metadata(file_path) + result.append(metadata) + + logging.debug('_query_mount_point took %f s.' % (time.time() - t)) + + return result, total_count + def _find(self, query): mount_points = query.get('mountpoints', ['/']) if mount_points is None or len(mount_points) != 1: raise ValueError('Exactly one mount point must be specified') + if mount_points[0] == '/': - return _get_datastore().find(query, PROPERTIES, byte_arrays=True) + data_store = _get_datastore() + entries, total_count = _get_datastore().find(query, PROPERTIES, + byte_arrays=True) else: - return _query_mount_point(mount_points[0], query) + entries, total_count = self._query_mount_point(mount_points[0], + query) + + for entry in entries: + entry['mountpoint'] = mount_points[0] + + return entries, total_count def seek(self, position): self._position = position @@ -206,38 +247,6 @@ def _get_file_metadata(path): 'activity_id': '', 'icon-color': client.get_string('/desktop/sugar/user/color')} -def _get_all_files(dir_path): - files = [] - for entry in os.listdir(dir_path): - full_path = os.path.join(dir_path, entry) - if os.path.isdir(full_path): - files.extend(_get_all_files(full_path)) - elif os.path.isfile(full_path): - stat = os.stat(full_path) - files.append((full_path, stat.st_mtime)) - return files - -def _query_mount_point(mount_point, query): - t = time.time() - - files = _get_all_files(mount_point) - offset = int(query.get('offset', 0)) - limit = int(query.get('limit', len(files))) - - total_count = len(files) - files.sort(lambda a, b: int(b[1] - a[1])) - files = files[offset:offset + limit] - - result = [] - for file_path, timestamp in files: - metadata = _get_file_metadata(file_path) - metadata['mountpoint'] = mount_point - result.append(metadata) - - logging.debug('_query_mount_point took %f s.' % (time.time() - t)) - - return result, total_count - _datastore = None def _get_datastore(): global _datastore @@ -278,8 +287,10 @@ def get_file(object_id): """Returns the file for an object """ if os.path.exists(object_id): + logging.debug('get_file asked for file with path %r' % object_id) return object_id else: + logging.debug('get_file asked for entry with id %r' % object_id) return _get_datastore().get_filename(object_id) def get_unique_values(key): @@ -307,11 +318,13 @@ def copy(metadata, mount_point): metadata['mountpoint'] = mount_point del metadata['uid'] + #TODO: should we transfer ownership? return write(metadata, file_path) def write(metadata, file_path='', update_mtime=True): """Creates or updates an entry for that id """ + logging.debug('model.write %r %r %r' % (metadata, file_path, update_mtime)) if update_mtime: metadata['mtime'] = datetime.now().isoformat() metadata['timestamp'] = int(time.time()) @@ -327,10 +340,22 @@ def write(metadata, file_path='', update_mtime=True): file_path, True) else: - pass + if not os.path.exists(file_path): + raise ValueError('Entries without a file cannot be copied to ' + 'removable devices') + file_name = _get_file_name(metadata['title'], metadata['mime_type']) + destination_path = os.path.join(metadata['mountpoint'], file_name) + shutil.copy(file_path, destination_path) + object_id = destination_path return object_id +def _get_file_name(title, mime_type): + # TODO: sanitize title for common filesystems + # TODO: make as robust as possible, this function should never fail. + # TODO: don't append the same extension again and again + return '%s.%s' % (title, mime.get_primary_extension(mime_type)) + created = dispatch.Signal() updated = dispatch.Signal() deleted = dispatch.Signal() diff --git a/src/jarabe/journal/volumestoolbar.py b/src/jarabe/journal/volumestoolbar.py index 50b9aa7..bb27ab7 100644 --- a/src/jarabe/journal/volumestoolbar.py +++ b/src/jarabe/journal/volumestoolbar.py @@ -18,12 +18,14 @@ import logging from gettext import gettext as _ import gobject +import gio import gtk +import gconf from sugar.graphics.radiotoolbutton import RadioToolButton from sugar.graphics.palette import Palette +from sugar.graphics.xocolor import XoColor -from jarabe.model import volume from jarabe.journal import model class VolumesToolbar(gtk.Toolbar): @@ -32,108 +34,108 @@ class VolumesToolbar(gtk.Toolbar): __gsignals__ = { 'volume-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([object])) + ([str])) } def __init__(self): gtk.Toolbar.__init__(self) - self._volume_buttons = [] - self._volume_added_hid = None - self._volume_removed_hid = None + self._mount_added_hid = None + self._mount_removed_hid = None + + button = JournalButton() + button.set_palette(Palette(_('Journal'))) + button.connect('toggled', self._button_toggled_cb) + self.insert(button, 0) + button.show() + self._volume_buttons = [button] self.connect('destroy', self.__destroy_cb) gobject.idle_add(self._set_up_volumes) def __destroy_cb(self, widget): - volumes_manager = volume.get_volumes_manager() - volumes_manager.disconnect(self._volume_added_hid) - volumes_manager.disconnect(self._volume_removed_hid) + volume_monitor = gio.volume_monitor_get() + volume_monitor.disconnect(self._mount_added_hid) + volume_monitor.disconnect(self._mount_removed_hid) def _set_up_volumes(self): - 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 vol in volumes_manager.get_volumes(): - self._add_button(vol) + volume_monitor = gio.volume_monitor_get() + self._mount_added_hid = \ + volume_monitor.connect('mount-added', self.__mount_added_cb) + self._mount_removed_hid = \ + volume_monitor.connect('mount-removed', self.__mount_removed_cb) - def _volume_added_cb(self, volumes_manager, vol): - self._add_button(vol) + for mount in volume_monitor.get_mounts(): + self._add_button(mount) - def _volume_removed_cb(self, volumes_manager, vol): - self._remove_button(vol) + def __mount_added_cb(self, volume_monitor, mount): + self._add_button(mount) - def _add_button(self, vol): - logging.debug('VolumeToolbar._add_button: %r' % vol.name) + def __mount_removed_cb(self, volume_monitor, mount): + self._remove_button(mount) - if self._volume_buttons: - group = self._volume_buttons[0] - else: - group = None + def _add_button(self, mount): + logging.debug('VolumeToolbar._add_button: %r' % mount.get_name()) - palette = Palette(vol.name) + palette = Palette(mount.get_name()) - button = VolumeButton(vol, group) + button = VolumeButton(mount) + button.props.group = self._volume_buttons[0] button.set_palette(palette) - button.connect('toggled', self._button_toggled_cb, vol) - if self._volume_buttons: - position = self.get_item_index(self._volume_buttons[-1]) + 1 - else: - position = 0 + button.connect('toggled', self._button_toggled_cb) + position = self.get_item_index(self._volume_buttons[-1]) + 1 self.insert(button, position) button.show() self._volume_buttons.append(button) - if vol.can_eject: + if mount.can_unmount(): menu_item = gtk.MenuItem(_('Unmount')) - menu_item.connect('activate', self._unmount_activated_cb, vol) + menu_item.connect('activate', self._unmount_activated_cb, mount) palette.menu.append(menu_item) menu_item.show() if len(self.get_children()) > 1: self.show() - def _button_toggled_cb(self, button, vol): + def _button_toggled_cb(self, button): if button.props.active: - self.emit('volume-changed', vol) + self.emit('volume-changed', button.mount_point) - def _unmount_activated_cb(self, menu_item, vol): - logging.debug('VolumesToolbar._unmount_activated_cb: %r', vol.udi) - vol.unmount() + def _unmount_activated_cb(self, menu_item, mount): + logging.debug('VolumesToolbar._unmount_activated_cb: %r', mount) + mount.unmount(self.__unmount_cb) - def _remove_button(self, vol): - for button in self.get_children(): - if button.volume.udi == vol.udi: - self._volume_buttons.remove(button) - self.remove(button) - self.get_children()[0].props.active = True - - if len(self.get_children()) < 2: - self.hide() - return - logging.error('Couldnt find volume with udi %r' % vol.udi) - - def set_active_volume(self, mount_point): + def __unmount_cb(self, source, result): + logging.debug('__unmount_cb %r %r' % (source, result)) + + def _get_button_for_mount(self, mount): + mount_point = mount.get_root().get_path() for button in self.get_children(): - logging.error('udi %r' % button.volume.mount_point) - if button.volume.mount_point == mount_point: - button.props.active = True - return - logging.error('Couldnt find volume with mount_point %r' % mount_point) - -class VolumeButton(RadioToolButton): - def __init__(self, vol, group): + if button.mount_point == mount_point: + return button + logging.error('Couldnt find button with mount_point %r' % mount_point) + return None + + def _remove_button(self, mount): + button = self._get_button_for_mount(mount) + self._volume_buttons.remove(button) + self.remove(button) + self.get_children()[0].props.active = True + + if len(self.get_children()) < 2: + self.hide() + + def set_active_volume(self, mount): + button = self._get_button_for_mount(mount) + button.props.active = True + +class BaseButton(RadioToolButton): + def __init__(self, mount_point): RadioToolButton.__init__(self) - self.props.named_icon = vol.icon_name - self.props.xo_color = vol.icon_color - self.props.group = group - self.volume = vol + self.mount_point = mount_point + self.drag_dest_set(gtk.DEST_DEFAULT_ALL, [('journal-object-id', 0, 0)], gtk.gdk.ACTION_COPY) @@ -143,5 +145,28 @@ class VolumeButton(RadioToolButton): info, timestamp): object_id = selection_data.data metadata = model.get(object_id) - model.copy(metadata, self.volume.mount_point) + model.copy(metadata, self.mount_point) + +class VolumeButton(BaseButton): + def __init__(self, mount): + mount_point = mount.get_root().get_path() + BaseButton.__init__(self, mount_point) + + # TODO: fallback to the more generic icons when needed + self.props.named_icon = mount.get_icon().props.names[0] + + # TODO: retrieve the colors from the owner of the device + client = gconf.client_get_default() + color = XoColor(client.get_string('/desktop/sugar/user/color')) + self.props.xo_color = color + +class JournalButton(BaseButton): + def __init__(self): + BaseButton.__init__(self, mount_point='/') + + self.props.named_icon = 'computer-xo' + + client = gconf.client_get_default() + color = XoColor(client.get_string('/desktop/sugar/user/color')) + self.props.xo_color = color diff --git a/src/jarabe/model/Makefile.am b/src/jarabe/model/Makefile.am index 71ba988..91d9a3e 100644 --- a/src/jarabe/model/Makefile.am +++ b/src/jarabe/model/Makefile.am @@ -12,5 +12,4 @@ sugar_PYTHON = \ shell.py \ screen.py \ session.py \ - sound.py \ - volume.py + sound.py diff --git a/src/jarabe/model/volume.py b/src/jarabe/model/volume.py deleted file mode 100644 index 0d3d9a5..0000000 --- a/src/jarabe/model/volume.py +++ /dev/null @@ -1,253 +0,0 @@ -# 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 -# 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 logging - -import gobject -import dbus - -from sugar import profile - -HAL_SERVICE_NAME = 'org.freedesktop.Hal' -HAL_MANAGER_PATH = '/org/freedesktop/Hal/Manager' -HAL_MANAGER_IFACE = 'org.freedesktop.Hal.Manager' -HAL_DEVICE_IFACE = 'org.freedesktop.Hal.Device' -HAL_VOLUME_IFACE = 'org.freedesktop.Hal.Device.Volume' - -MOUNT_OPTION_UID = 500 -MOUNT_OPTION_UMASK = 000 - -_volumes_manager = None - -class VolumesManager(gobject.GObject): - - __gtype_name__ = 'VolumesManager' - - __gsignals__ = { - 'volume-added': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, - ([object])), - 'volume-removed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, - ([object])) - } - - def __init__(self): - gobject.GObject.__init__(self) - - self._volumes = {} - - bus = dbus.SystemBus() - proxy = bus.get_object(HAL_SERVICE_NAME, HAL_MANAGER_PATH) - self._hal_manager = dbus.Interface(proxy, HAL_MANAGER_IFACE) - self._hal_manager.connect_to_signal('DeviceAdded', - self._hal_device_added_cb) - - for udi in self._hal_manager.FindDeviceByCapability('volume'): - if self._is_device_relevant(udi): - try: - self._add_hal_device(udi) - except Exception, e: - logging.error('Exception when mounting device %r: %r' % \ - (udi, e)) - - def get_volumes(self): - return self._volumes.values() - - def _hal_device_added_cb(self, udi): - bus = dbus.SystemBus() - device_object = bus.get_object(HAL_SERVICE_NAME, udi) - device = dbus.Interface(device_object, HAL_DEVICE_IFACE) - if device.QueryCapability('volume'): - logging.debug('VolumesManager._hal_device_added_cb: %r', udi) - if self._is_device_relevant(udi): - self._add_hal_device(udi) - - def _is_device_relevant(self, udi): - bus = dbus.SystemBus() - device_object = bus.get_object(HAL_SERVICE_NAME, udi) - device = dbus.Interface(device_object, HAL_DEVICE_IFACE) - - # Ignore volumes without a filesystem. - if device.GetProperty('volume.fsusage') != 'filesystem': - return False - - return True - - def _add_hal_device(self, udi): - logging.debug('VolumeToolbar._add_hal_device: %r' % udi) - - bus = dbus.SystemBus() - device_object = bus.get_object(HAL_SERVICE_NAME, udi) - device = dbus.Interface(device_object, HAL_DEVICE_IFACE) - - # listen to mount/unmount - device.connect_to_signal('PropertyModified', - lambda *args: self._hal_device_property_modified_cb(udi, *args)) - - bus.add_signal_receiver(self._hal_device_removed_cb, - 'DeviceRemoved', - HAL_MANAGER_IFACE, HAL_SERVICE_NAME, - HAL_MANAGER_PATH, arg0=udi) - - if device.GetProperty('volume.is_mounted'): - self._add_volume(udi) - return - - label = device.GetProperty('volume.label') - fs_type = device.GetProperty('volume.fstype') - valid_options = device.GetProperty('volume.mount.valid_options') - options = [] - - if 'uid=' in valid_options: - options.append('uid=%i' % MOUNT_OPTION_UID) - - if 'umask=' in valid_options: - options.append('umask=%i' % MOUNT_OPTION_UMASK) - - if 'noatime' in valid_options: - options.append('noatime') - - if 'utf8' in valid_options: - options.append('utf8') - - if 'iocharset=' in valid_options: - options.append('iocharset=utf8') - - mount_point = label - if not mount_point: - mount_point = device.GetProperty('volume.uuid') - - volume = dbus.Interface(device_object, HAL_VOLUME_IFACE) - - # Try 100 times to get a mount point - mounted = False - i = 0 - while not mounted: - try: - if i > 0: - volume.Mount('%s_%d' % (mount_point, i), fs_type, options) - else: - volume.Mount(mount_point, fs_type, options) - mounted = True - except dbus.DBusException, e: - s = 'org.freedesktop.Hal.Device.Volume.MountPointNotAvailable' - if i < 100 and e.get_dbus_name() == s: - i += 1 - else: - raise - - def _hal_device_property_modified_cb(self, udi, count, changes): - if 'volume.is_mounted' in [change[0] for change in changes]: - logging.debug('VolumesManager._hal_device_property_modified: %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): - # 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) - return - - if is_mounted: - if udi not in self._volumes: - self._add_volume(udi) - else: - if udi in self._volumes: - self._remove_volume(udi) - - def _add_volume(self, udi): - logging.debug('_add_volume %r' % udi) - bus = dbus.SystemBus() - device_object = bus.get_object(HAL_SERVICE_NAME, udi) - device = dbus.Interface(device_object, HAL_DEVICE_IFACE) - - volume_name = device.GetProperty('volume.label') - if not volume_name: - volume_name = device.GetProperty('volume.uuid') - - mount_point = device.GetProperty('volume.mount_point') - - storage_udi = device.GetProperty('block.storage_device') - obj = bus.get_object(HAL_SERVICE_NAME, storage_udi) - storage_device = dbus.Interface(obj, HAL_DEVICE_IFACE) - can_eject = storage_device.GetProperty('storage.hotpluggable') - - volume = Volume(volume_name, - self._get_icon_for_volume(device), - profile.get_color(), - udi, - mount_point, - can_eject) - self._volumes[udi] = volume - - 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) - if udi in self._volumes: - self._remove_volume(udi) - - def _get_icon_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') - if storage_drive_type == 'sd_mmc': - return 'media-flash-sd-mmc' - elif device.GetProperty('volume.mount_point') == '/': - return 'computer-xo' - else: - return 'media-flash-usb' - -class Volume(object): - def __init__(self, name, icon_name, icon_color, udi, mount_point, - can_eject): - self.name = name - self.icon_name = icon_name - self.icon_color = icon_color - self.udi = udi - self.mount_point = mount_point - self.can_eject = can_eject - - def unmount(self): - 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 - if _volumes_manager is None: - _volumes_manager = VolumesManager() - return _volumes_manager - -- cgit v0.9.1