diff --git a/NEWS b/NEWS index 0068c953..52999cb6 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,5 @@ +* #2543: Offer multiple activities for opening clipboard objects. + Snapshot d93122bf5e * #2751 Add keybindings for max/min brightness/volume diff --git a/services/shell/clipboardobject.py b/services/shell/clipboardobject.py index bc51f472..65f3bc56 100644 --- a/services/shell/clipboardobject.py +++ b/services/shell/clipboardobject.py @@ -65,16 +65,15 @@ class ClipboardObject: #return self._get_type_info().get_preview() return '' - def get_activity(self): + def get_activities(self): mime = self.get_mime_type() if not mime: return '' registry = bundleregistry.get_registry() activities = registry.get_activities_for_type(self.get_mime_type()) - # TODO: should we return several activities? if activities: - return activities[0].get_service_name() + return [activity.get_service_name() for activity in activities] else: return '' diff --git a/services/shell/clipboardservice.py b/services/shell/clipboardservice.py index 19958a7c..90e1b8ee 100644 --- a/services/shell/clipboardservice.py +++ b/services/shell/clipboardservice.py @@ -33,7 +33,7 @@ NAME_KEY = 'NAME' PERCENT_KEY = 'PERCENT' ICON_KEY = 'ICON' PREVIEW_KEY = 'PREVIEW' -ACTIVITY_KEY = 'ACTIVITY' +ACTIVITIES_KEY = 'ACTIVITIES' FORMATS_KEY = 'FORMATS' TYPE_KEY = 'TYPE' @@ -87,7 +87,7 @@ class ClipboardService(dbus.service.Object): PERCENT_KEY: cb_object.get_percent(), ICON_KEY: cb_object.get_icon(), PREVIEW_KEY: cb_object.get_preview(), - ACTIVITY_KEY: cb_object.get_activity()}) + ACTIVITIES_KEY: cb_object.get_activities()}) @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, in_signature="o", out_signature="") @@ -121,7 +121,7 @@ class ClipboardService(dbus.service.Object): PERCENT_KEY: percent, ICON_KEY: cb_object.get_icon(), PREVIEW_KEY: cb_object.get_preview(), - ACTIVITY_KEY: cb_object.get_activity()}) + ACTIVITIES_KEY: cb_object.get_activities()}) @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, in_signature="o", out_signature="a{sv}") @@ -142,7 +142,7 @@ class ClipboardService(dbus.service.Object): PERCENT_KEY: cb_object.get_percent(), ICON_KEY: cb_object.get_icon(), PREVIEW_KEY: cb_object.get_preview(), - ACTIVITY_KEY: cb_object.get_activity(), + ACTIVITIES_KEY: cb_object.get_activities(), FORMATS_KEY: format_types} return dbus.Dictionary(result_dict) diff --git a/shell/view/clipboardmenu.py b/shell/view/clipboardmenu.py index 28ea0bb6..3acf90ee 100644 --- a/shell/view/clipboardmenu.py +++ b/shell/view/clipboardmenu.py @@ -30,16 +30,17 @@ from sugar.clipboard import clipboardservice from sugar.datastore import datastore from sugar.objects import mime from sugar import profile +from sugar import activity class ClipboardMenu(Palette): - def __init__(self, object_id, name, percent, preview, activity, installable): + def __init__(self, object_id, name, percent, preview, activities, installable): Palette.__init__(self, name) self.props.position = Palette.RIGHT self._object_id = object_id self._percent = percent - self._activity = activity + self._activities = activities self.set_group_id('frame') @@ -68,7 +69,8 @@ class ClipboardMenu(Palette): self._remove_item.show() self._open_item = MenuItem(_('Open'), 'stock-keep') - self._open_item.connect('activate', self._open_item_activate_cb) + self._open_item_activate_sid = self._open_item.connect('activate', + self._open_item_activate_cb) self.menu.append(self._open_item) self._open_item.show() @@ -83,14 +85,49 @@ class ClipboardMenu(Palette): self._journal_item.show() self._update_items_visibility(installable) + self._update_open_submenu() + + def _update_open_submenu(self): + submenu = self._open_item.get_submenu() + if submenu: + for item in submenu.get_children(): + submenu.remove(item) + + if self._activities is None or len(self._activities) <= 1: + if self._open_item_activate_sid is None: + self._open_item_activate_sid = self._open_item.connect( + 'activate', + self._open_item_activate_cb) + return + else: + if self._open_item_activate_sid is not None: + self._open_item.disconnect(self._open_item_activate_sid) + self._open_item_activate_sid = None + + if not submenu: + submenu = gtk.Menu() + self._open_item.set_submenu(submenu) + submenu.show() + + for service_name in self._activities: + registry = activity.get_registry() + activity_info = registry.get_activity(service_name) + + if not activity_info: + logging.warning('Activity %s is unknown.' % service_name) + + item = gtk.MenuItem(activity_info.name) + item.connect('activate', self._open_submenu_item_activate_cb, service_name) + submenu.append(item) + item.show() def _update_items_visibility(self, installable): - if self._percent == 100 and (self._activity or installable): + if self._percent == 100 and (self._activities or installable): self._remove_item.props.sensitive = True self._open_item.props.sensitive = True #self._stop_item.props.sensitive = False self._journal_item.props.sensitive = True - elif self._percent == 100 and (not self._activity and not installable): + elif self._percent == 100 and (not self._activities and not installable): self._remove_item.props.sensitive = True self._open_item.props.sensitive = False #self._stop_item.props.sensitive = False @@ -112,19 +149,26 @@ class ClipboardMenu(Palette): self._progress_bar.props.fraction = self._percent / 100.0 self._progress_bar.props.text = '%.2f %%' % self._percent - def set_state(self, name, percent, preview, activity, installable): + def set_state(self, name, percent, preview, activities, installable): self.set_primary_text(name) self._percent = percent - self._activity = activity + self._activities = activities if self._progress_bar: self._update_progress_bar() self._update_items_visibility(installable) + self._update_open_submenu() def _open_item_activate_cb(self, menu_item): if self._percent < 100: return jobject = self._copy_to_journal() - jobject.resume() + jobject.resume(self._activities[0]) + + def _open_submenu_item_activate_cb(self, menu_item, service_name): + if self._percent < 100: + return + jobject = self._copy_to_journal() + jobject.resume(service_name) def _remove_item_activate_cb(self, menu_item): cb_service = clipboardservice.get_instance() diff --git a/sugar/clipboard/clipboardservice.py b/sugar/clipboard/clipboardservice.py index dbdf41d1..0e357fef 100644 --- a/sugar/clipboard/clipboardservice.py +++ b/sugar/clipboard/clipboardservice.py @@ -23,7 +23,7 @@ NAME_KEY = 'NAME' PERCENT_KEY = 'PERCENT' ICON_KEY = 'ICON' PREVIEW_KEY = 'PREVIEW' -ACTIVITY_KEY = 'ACTIVITY' +ACTIVITIES_KEY = 'ACTIVITIES' FORMATS_KEY = 'FORMATS' TYPE_KEY = 'TYPE' @@ -51,7 +51,7 @@ class ClipboardService(gobject.GObject): 'object-deleted': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([str])), 'object-state-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([str, str, int, str, str, str])), + ([str, str, int, str, str, object])), } def __init__(self): @@ -118,13 +118,13 @@ class ClipboardService(gobject.GObject): percent icon preview - activity + activities From the ClipboardObject instance which is being described. """ self.emit('object-state-changed', str(object_id), values[NAME_KEY], values[PERCENT_KEY], values[ICON_KEY], values[PREVIEW_KEY], - values[ACTIVITY_KEY]) + values[ACTIVITIES_KEY]) def add_object(self, name): """Add a new object to the path @@ -193,7 +193,7 @@ class ClipboardService(gobject.GObject): PERCENT_KEY: number, ICON_KEY: str, PREVIEW_KEY: XXX what is it?, - ACTIVITY_KEY: source activity id, + ACTIVITIES_KEY: activities that can open this object, FORMATS_KEY: list of XXX what is it? """ return self._dbus_service.get_object(dbus.ObjectPath(object_id),) diff --git a/sugar/datastore/datastore.py b/sugar/datastore/datastore.py index 5ba994db..0dbe35bd 100644 --- a/sugar/datastore/datastore.py +++ b/sugar/datastore/datastore.py @@ -120,6 +120,9 @@ class DSObject(object): def resume(self, service_name=None): if self.is_bundle(): + if service_name is not None: + raise ValueError('Object is a bundle, cannot be resumed as an activity.') + bundle = Bundle(self.file_path) if not bundle.is_installed(): bundle.install()