Adapt to sugar API changes. This makes the colors ugly,
[journal-activity.git] / volumestoolbar.py
blob4d3443084f18bf89570774e960b76f1af8f20756
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
17 import os
18 import logging
19 from gettext import gettext as _
21 import gobject
22 import gtk
23 import dbus
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'
37 # FIXME
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'
45 __gsignals__ = {
46 'volume-changed': (gobject.SIGNAL_RUN_FIRST,
47 gobject.TYPE_NONE,
48 ([str]))
51 def __init__(self):
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):
91 # return
93 proxy = bus.get_object(HAL_SERVICE_NAME, udi)
94 device = dbus.Interface(proxy, HAL_DEVICE_IFACE)
95 try:
96 is_mounted = device.GetProperty('volume.is_mounted')
97 except dbus.DBusException, e:
98 logging.debug('e: %s' % e)
99 return
101 if is_mounted:
102 if self._udi_to_mount_id.has_key(udi):
103 # device already mounted in datastore and being displayed as such
104 return
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)
108 else:
109 self._remove_button(udi)
110 return
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':
119 return
120 # Ignore root.
121 if device.GetProperty('volume.mount_point') == '/':
122 return
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'):
130 return
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,
139 'DeviceRemoved',
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)
147 return
149 label = device.GetProperty('volume.label')
150 fs_type = device.GetProperty('volume.fstype')
151 valid_options = device.GetProperty('volume.mount.valid_options')
152 options = []
154 if 'uid=' in valid_options:
155 options.append('uid=%i' % os.getuid())
157 mount_point = label
158 if not mount_point:
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))
179 if mount_id:
180 self._udi_to_mount_id[udi] = mount_id
181 return mount_id
182 else:
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]
195 else:
196 group = None
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)
205 button.show()
206 button.set_data('udi', udi)
208 if not udi is None:
209 menu_item = gtk.MenuItem(_('Unmount'))
210 menu_item.connect('activate', self._unmount_activated_cb, udi, button)
211 palette.append_menu_item(menu_item)
212 menu_item.show()
214 if len(self.get_children()) > 1:
215 self.show()
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)
235 # return
237 proxy = bus.get_object(HAL_SERVICE_NAME, udi)
238 device = dbus.Interface(proxy, HAL_DEVICE_IFACE)
239 try:
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)
245 return
247 if is_mounted:
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):
255 return
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)
265 volume.Unmount([])
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)
271 self.remove(button)
272 self.get_children()[0].props.active = True
274 if len(self.get_children()) < 2:
275 self.hide()
276 return
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')
285 if not volume_name:
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'
295 else:
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)],
306 gtk.gdk.ACTION_COPY)
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:
314 return
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)