1 # Copyright (C) 2007, One Laptop Per Child
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 from gettext
import gettext
as _
25 from sugar
.activity
import activity
26 from sugar
.datastore
import datastore
27 from sugar
.graphics
import units
28 from sugar
.graphics
.radiotoolbutton
import RadioToolButton
29 from sugar
.graphics
.palette
import Palette
31 HAL_SERVICE_NAME
= 'org.freedesktop.Hal'
32 HAL_MANAGER_PATH
= '/org/freedesktop/Hal/Manager'
33 HAL_MANAGER_IFACE
= 'org.freedesktop.Hal.Manager'
34 HAL_DEVICE_IFACE
= 'org.freedesktop.Hal.Device'
35 HAL_VOLUME_IFACE
= 'org.freedesktop.Hal.Device.Volume'
38 # We are duplicating DBUS calls too much, and
39 # mixing HAL calls with the UI code is ugly.
40 # We should split the model out to Volume/VolumeManager.
42 class VolumesToolbar(gtk
.Toolbar
):
43 __gtype_name__
= 'VolumesToolbar'
46 'volume-changed': (gobject
.SIGNAL_RUN_FIRST
,
52 gtk
.Toolbar
.__init
__(self
)
54 # Internal flash is not in HAL
55 internal_flash
= datastore
.mounts()[0]
56 self
._add
_button
(internal_flash
['id'], _('Journal'), 'activity-journal')
58 bus
= dbus
.SystemBus()
59 proxy
= bus
.get_object(HAL_SERVICE_NAME
, HAL_MANAGER_PATH
)
60 hal_manager
= dbus
.Interface(proxy
, HAL_MANAGER_IFACE
)
61 hal_manager
.connect_to_signal('DeviceAdded', self
._hal
_device
_added
_cb
)
63 self
._udi
_to
_mount
_id
= {}
65 gobject
.idle_add(self
._add
_current
_hal
_devices
)
67 def _add_current_hal_devices(self
):
68 logging
.debug('VolumeToolbar._add_current_hal_devices')
69 bus
= dbus
.SystemBus()
70 proxy
= bus
.get_object(HAL_SERVICE_NAME
, HAL_MANAGER_PATH
)
71 hal_manager
= dbus
.Interface(proxy
, HAL_MANAGER_IFACE
)
72 for udi
in hal_manager
.FindDeviceByCapability('volume'):
73 self
._mount
_hal
_device
(udi
)
75 def _hal_device_added_cb(self
, udi
):
76 bus
= dbus
.SystemBus()
77 device_object
= bus
.get_object(HAL_SERVICE_NAME
, udi
)
78 device
= dbus
.Interface(device_object
, HAL_DEVICE_IFACE
)
79 if device
.QueryCapability('volume'):
80 logging
.debug('VolumesToolbar._hal_device_added_cb: %r', udi
)
81 self
._mount
_hal
_device
(udi
)
83 def _hal_device_property_modified_cb(self
, udi
, count
, changes
):
84 if 'volume.is_mounted' in [change
[0] for change
in changes
]:
85 logging
.debug('VolumesToolbar._hal_device_property_modified: %r' % (udi
))
86 bus
= dbus
.SystemBus()
87 #proxy = bus.get_object(HAL_SERVICE_NAME, HAL_MANAGER_PATH)
88 #hal_manager = dbus.Interface(proxy, HAL_MANAGER_IFACE)
89 # TODO: Why this doesn't work?
90 #if not hal_manager.DeviceExists(udi):
93 proxy
= bus
.get_object(HAL_SERVICE_NAME
, udi
)
94 device
= dbus
.Interface(proxy
, HAL_DEVICE_IFACE
)
96 is_mounted
= device
.GetProperty('volume.is_mounted')
97 except dbus
.DBusException
, e
:
98 logging
.debug('e: %s' % e
)
102 if self
._udi
_to
_mount
_id
.has_key(udi
):
103 # device already mounted in datastore and being displayed as such
105 volume_id
= self
._mount
_in
_datastore
(udi
)
106 volume_name
, icon_name
= self
._get
_volume
_name
_and
_icon
(udi
)
107 self
._add
_button
(volume_id
, volume_name
, icon_name
, udi
)
109 self
._remove
_button
(udi
)
112 def _mount_hal_device(self
, udi
):
113 bus
= dbus
.SystemBus()
114 device_object
= bus
.get_object(HAL_SERVICE_NAME
, udi
)
115 device
= dbus
.Interface(device_object
, HAL_DEVICE_IFACE
)
117 # Ignore volumes with a filesystem.
118 if device
.GetProperty('volume.fsusage') != 'filesystem':
121 if device
.GetProperty('volume.mount_point') == '/':
124 storage_udi
= device
.GetProperty('block.storage_device')
125 obj
= bus
.get_object(HAL_SERVICE_NAME
, storage_udi
)
126 storage_device
= dbus
.Interface(obj
, HAL_DEVICE_IFACE
)
128 # Ignore not removable storage.
129 if not storage_device
.GetProperty('storage.removable'):
132 logging
.debug('VolumeToolbar._mount_hal_device: %r' % udi
)
134 # listen to mount/unmount
135 device
.connect_to_signal('PropertyModified',
136 lambda *args
: self
._hal
_device
_property
_modified
_cb
(udi
, *args
))
138 bus
.add_signal_receiver(self
._hal
_device
_removed
_cb
,
140 HAL_MANAGER_IFACE
, HAL_SERVICE_NAME
,
141 HAL_MANAGER_PATH
, arg0
=udi
)
143 if device
.GetProperty('volume.is_mounted'):
144 volume_id
= self
._mount
_in
_datastore
(udi
)
145 volume_name
, icon_name
= self
._get
_volume
_name
_and
_icon
(udi
)
146 self
._add
_button
(volume_id
, volume_name
, icon_name
, udi
)
149 label
= device
.GetProperty('volume.label')
150 fs_type
= device
.GetProperty('volume.fstype')
151 valid_options
= device
.GetProperty('volume.mount.valid_options')
154 if 'uid=' in valid_options
:
155 options
.append('uid=%i' % os
.getuid())
159 mount_point
= device
.GetProperty('volume.uuid')
161 volume
= dbus
.Interface(device_object
, HAL_VOLUME_IFACE
)
162 volume
.Mount(mount_point
, fs_type
, options
)
164 def _mount_in_datastore(self
, udi
):
165 logging
.debug('VolumeToolbar._mount_in_datastore: %r' % udi
)
167 bus
= dbus
.SystemBus()
168 device_object
= bus
.get_object(HAL_SERVICE_NAME
, udi
)
169 device
= dbus
.Interface(device_object
, HAL_DEVICE_IFACE
)
171 mount_point
= device
.GetProperty('volume.mount_point')
172 ds_mounts
= datastore
.mounts()
173 for ds_mount
in ds_mounts
:
174 if mount_point
== ds_mount
['uri']:
175 return ds_mount
['id']
177 mount_id
= datastore
.mount('inplace:' + mount_point
,
178 dict(title
=mount_point
))
180 self
._udi
_to
_mount
_id
[udi
] = mount_id
183 self
._unmount
_hal
_device
(udi
)
184 raise RuntimeError('datastore.mount(%r, %r) failed.' % (
185 'inplace:' + mount_point
,
186 dict(title
=mount_point
)))
188 logging
.debug('mounted volume %s' % mount_point
)
190 def _add_button(self
, volume_id
, volume_name
, icon_name
, udi
=None):
191 logging
.debug('VolumeToolbar._add_button: %r' % volume_name
)
193 if self
.get_children():
194 group
= self
.get_children()[0]
198 palette
= Palette(volume_name
)
199 palette
.props
.position
= Palette
.TOP
201 button
= VolumeButton(icon_name
, group
, volume_id
)
202 button
.set_palette(palette
)
203 button
.connect('toggled', self
._button
_toggled
_cb
, volume_id
)
204 self
.insert(button
, -1)
206 button
.set_data('udi', udi
)
209 menu_item
= gtk
.MenuItem(_('Unmount'))
210 menu_item
.connect('activate', self
._unmount
_activated
_cb
, udi
, button
)
211 palette
.append_menu_item(menu_item
)
214 if len(self
.get_children()) > 1:
217 def _button_toggled_cb(self
, button
, volume_id
):
218 if button
.props
.active
:
219 self
.emit('volume-changed', volume_id
)
221 def _unmount_activated_cb(self
, menu_item
, udi
, button
):
222 logging
.debug('VolumesToolbar._unmount_activated_cb: %r', udi
)
223 self
._unmount
_from
_datastore
(udi
)
224 self
._unmount
_hal
_device
(udi
)
226 def _hal_device_removed_cb(self
, udi
):
227 logging
.debug('VolumesToolbar._hal_device_removed_cb: %r', udi
)
228 bus
= dbus
.SystemBus()
229 #proxy = bus.get_object(HAL_SERVICE_NAME, HAL_MANAGER_PATH)
230 #hal_manager = dbus.Interface(proxy, HAL_MANAGER_IFACE)
231 # TODO: Why this doesn't work?
232 #if not hal_manager.DeviceExists(udi):
233 # self._unmount_from_datastore(udi)
234 # self._remove_button(udi)
237 proxy
= bus
.get_object(HAL_SERVICE_NAME
, udi
)
238 device
= dbus
.Interface(proxy
, HAL_DEVICE_IFACE
)
240 is_mounted
= device
.GetProperty('volume.is_mounted')
241 except dbus
.DBusException
, e
:
242 logging
.debug('e: %s' % e
)
243 self
._unmount
_from
_datastore
(udi
)
244 self
._remove
_button
(udi
)
248 self
._unmount
_from
_datastore
(udi
)
249 self
._unmount
_hal
_device
(udi
)
250 self
._remove
_button
(udi
)
252 def _unmount_from_datastore(self
, udi
):
253 logging
.debug('VolumesToolbar._unmount_from_datastore: %r', udi
)
254 if not self
._udi
_to
_mount
_id
.has_key(udi
):
256 mount_id
= self
._udi
_to
_mount
_id
[udi
]
257 datastore
.unmount(mount_id
)
258 del self
._udi
_to
_mount
_id
[udi
]
260 def _unmount_hal_device(self
, udi
):
261 logging
.debug('VolumesToolbar._unmount_hal_device: %r', udi
)
262 bus
= dbus
.SystemBus()
263 device_object
= bus
.get_object(HAL_SERVICE_NAME
, udi
)
264 volume
= dbus
.Interface(device_object
, HAL_VOLUME_IFACE
)
267 def _remove_button(self
, udi
):
268 for button
in self
.get_children():
269 if udi
== button
.get_data('udi'):
270 logging
.debug('_remove_button: removing button for udi: %r' % udi
)
272 self
.get_children()[0].props
.active
= True
274 if len(self
.get_children()) < 2:
277 logging
.debug('_remove_button: couldn''t find a button for udi: %r' % udi
)
279 def _get_volume_name_and_icon(self
, udi
):
280 bus
= dbus
.SystemBus()
281 device_object
= bus
.get_object(HAL_SERVICE_NAME
, udi
)
282 device
= dbus
.Interface(device_object
, HAL_DEVICE_IFACE
)
284 volume_name
= device
.GetProperty('volume.label')
286 volume_name
= device
.GetProperty('volume.uuid')
288 storage_udi
= device
.GetProperty('block.storage_device')
289 obj
= bus
.get_object(HAL_SERVICE_NAME
, storage_udi
)
290 storage_device
= dbus
.Interface(obj
, HAL_DEVICE_IFACE
)
292 storage_drive_type
= storage_device
.GetProperty('storage.drive_type')
293 if storage_drive_type
== 'sd_mmc':
294 icon_name
= 'media-flash-sd-mmc'
296 icon_name
= 'media-flash-usb'
298 return volume_name
, icon_name
300 class VolumeButton(RadioToolButton
):
301 def __init__(self
, icon_name
, group
, volume_id
):
302 RadioToolButton
.__init
__(self
, icon_name
, group
)
303 self
._volume
_id
= volume_id
304 self
.drag_dest_set(gtk
.DEST_DEFAULT_ALL
,
305 [('journal-object-id', 0, 0)],
307 self
.connect('drag-data-received', self
._drag
_data
_received
_cb
)
309 def _drag_data_received_cb(self
, widget
, drag_context
, x
, y
, selection_data
, info
, timestamp
):
310 """ Copy a dropped entry to this volume
312 jobject
= datastore
.get(selection_data
.data
)
313 if jobject
.metadata
.has_key('mountpoint') and jobject
.metadata
['mountpoint'] == self
._volume
_id
:
316 jobject
.metadata
['mountpoint'] = self
._volume
_id
318 # this will cause a the file path be retrieved from the DS
319 jobject
.file_path
= jobject
.file_path
321 # this will cause a new object be created in the dest mount point
322 jobject
.object_id
= None
324 datastore
.write(jobject
)