From eeb09294d8713c6b8a1e3d7b8b53e692b989cdcb Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Wed, 11 Apr 2007 18:22:52 +0200 Subject: [PATCH] Revert "Start reworking the cliboard services to use the types registry." This reverts commit fcb57779a275e8a3343b71f991c23cb98f5c1e7a. --- services/clipboard/Makefile.am | 8 +- services/clipboard/clipboardobject.py | 22 ++- services/clipboard/clipboardservice.py | 50 +++-- services/clipboard/typeregistry.py | 243 +++++++++++++++++++++++++ shell/view/clipboardicon.py | 57 ++++-- shell/view/clipboardmenu.py | 45 +---- shell/view/frame/clipboardbox.py | 11 +- sugar/clipboard/clipboardservice.py | 24 ++- 8 files changed, 369 insertions(+), 91 deletions(-) create mode 100644 services/clipboard/typeregistry.py diff --git a/services/clipboard/Makefile.am b/services/clipboard/Makefile.am index 28282702..a656ee2f 100644 --- a/services/clipboard/Makefile.am +++ b/services/clipboard/Makefile.am @@ -7,9 +7,11 @@ $(service_DATA): $(service_in_files) Makefile sugardir = $(pkgdatadir)/services/clipboard sugar_PYTHON = \ - __init__.py \ - clipboardobject.py \ - clipboardservice.py + __init__.py \ + clipboardobject.py \ + clipboardservice.py \ + typeregistry.py + bin_SCRIPTS = sugar-clipboard diff --git a/services/clipboard/clipboardobject.py b/services/clipboard/clipboardobject.py index 385de080..919acd00 100644 --- a/services/clipboard/clipboardobject.py +++ b/services/clipboard/clipboardobject.py @@ -1,4 +1,7 @@ +import typeregistry + class ClipboardObject: + def __init__(self, object_path, name): self._id = object_path self._name = name @@ -8,8 +11,24 @@ class ClipboardObject: def get_id(self): return self._id + def _get_type_info(self): + type_registry = typeregistry.get_instance() + return type_registry.get_type(self._formats) + def get_name(self): - return self._name + if self._name: + return self._name + else: + return self._get_type_info().get_name() + + def get_icon(self): + return self._get_type_info().get_icon() + + def get_preview(self): + return self._get_type_info().get_preview() + + def get_activity(self): + return self._get_type_info().get_activity() def get_percent(self): return self._percent @@ -24,6 +43,7 @@ class ClipboardObject: return self._formats class Format: + def __init__(self, type, data, on_disk): self._type = type self._data = data diff --git a/services/clipboard/clipboardservice.py b/services/clipboard/clipboardservice.py index 2616ea77..0ed423b5 100644 --- a/services/clipboard/clipboardservice.py +++ b/services/clipboard/clipboardservice.py @@ -27,6 +27,9 @@ import typeregistry NAME_KEY = 'NAME' PERCENT_KEY = 'PERCENT' +ICON_KEY = 'ICON' +PREVIEW_KEY = 'PREVIEW' +ACTIVITY_KEY = 'ACTIVITY' FORMATS_KEY = 'FORMATS' class ClipboardDBusServiceHelper(dbus.service.Object): @@ -48,18 +51,6 @@ class ClipboardDBusServiceHelper(dbus.service.Object): self._next_id += 1 return self._next_id - def _get_object_dict(self, object_path): - cb_object = self._objects[str(object_path)] - formats = cb_object.get_formats() - format_types = dbus.Array([], 's') - - for type, format in formats.iteritems(): - format_types.append(type) - - return { NAME_KEY: cb_object.get_name(), - PERCENT_KEY: cb_object.get_percent(), - FORMATS_KEY: format_types } - def _handle_file_completed(self, cb_object): """If the object is an on-disk file, and it's at 100%, and we care about it's file type, copy that file to $HOME and upate the clipboard object's @@ -72,6 +63,10 @@ class ClipboardDBusServiceHelper(dbus.service.Object): if not format.get_on_disk(): return + if not len(cb_object.get_activity()): + # no activity to handle this, don't autosave it + return + # copy to homedir src = format.get_data() if not os.path.exists(src): @@ -105,7 +100,11 @@ class ClipboardDBusServiceHelper(dbus.service.Object): else: logging.debug('Added in-memory format of type ' + format_type + '.') - self.object_changed(object_path, self._get_object_dict(object_path)) + self.object_state_changed(object_path, {NAME_KEY: cb_object.get_name(), + PERCENT_KEY: cb_object.get_percent(), + ICON_KEY: cb_object.get_icon(), + PREVIEW_KEY: cb_object.get_preview(), + ACTIVITY_KEY: cb_object.get_activity()}) @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, in_signature="o", out_signature="") @@ -131,12 +130,29 @@ class ClipboardDBusServiceHelper(dbus.service.Object): if percent == 100: self._handle_file_completed(cb_object) - self.object_state_changed(object_path, { PERCENT_KEY: percent }) + self.object_state_changed(object_path, {NAME_KEY: cb_object.get_name(), + PERCENT_KEY: percent, + ICON_KEY: cb_object.get_icon(), + PREVIEW_KEY: cb_object.get_preview(), + ACTIVITY_KEY: cb_object.get_activity()}) @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, in_signature="o", out_signature="a{sv}") def get_object(self, object_path): - return dbus.Dictionary(self._get_object_dict(object_path)) + cb_object = self._objects[str(object_path)] + formats = cb_object.get_formats() + format_types = dbus.Array([], 's') + + for type, format in formats.iteritems(): + format_types.append(type) + + result_dict = {NAME_KEY: cb_object.get_name(), + PERCENT_KEY: cb_object.get_percent(), + ICON_KEY: cb_object.get_icon(), + PREVIEW_KEY: cb_object.get_preview(), + ACTIVITY_KEY: cb_object.get_activity(), + FORMATS_KEY: format_types} + return dbus.Dictionary(result_dict) @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, in_signature="os", out_signature="ay") @@ -150,10 +166,6 @@ class ClipboardDBusServiceHelper(dbus.service.Object): def object_added(self, object_path, name): pass - @dbus.service.signal(_CLIPBOARD_DBUS_INTERFACE, signature="oa{sv}") - def object_changed(self, object_path, values): - pass - @dbus.service.signal(_CLIPBOARD_DBUS_INTERFACE, signature="o") def object_deleted(self, object_path): pass diff --git a/services/clipboard/typeregistry.py b/services/clipboard/typeregistry.py new file mode 100644 index 00000000..b794cee9 --- /dev/null +++ b/services/clipboard/typeregistry.py @@ -0,0 +1,243 @@ +import logging +from gettext import gettext as _ + +class FileType: + def __init__(self, formats): + self._formats = formats + + def get_name(self): + raise NotImplementedError + + def get_icon(self): + raise NotImplementedError + + def get_preview(self): + raise NotImplementedError + + def get_activity(self): + raise NotImplementedError + + def matches_mime_type(cls, mime_type): + raise NotImplementedError + matches_mime_type = classmethod(matches_mime_type) + +class TextFileType(FileType): + + _types = set(['text/plain', 'UTF8_STRING', 'STRING']) + + def get_name(self): + return _('Text snippet') + + def get_icon(self): + return 'theme:object-text' + + def get_preview(self): + for format, data in self._formats.iteritems(): + if format in TextFileType._types: + text = data.get_data() + if len(text) < 50: + return text + else: + return text[0:49] + "..." + + return '' + + def get_activity(self): + return 'org.laptop.AbiWordActivity' + + def matches_mime_type(cls, mime_type): + return mime_type in cls._types + matches_mime_type = classmethod(matches_mime_type) + +class ImageFileType(FileType): + + _types = set(['image/jpeg', 'image/gif', 'image/png', 'image/tiff']) + + def get_name(self): + return _('Image') + + def get_icon(self): + return 'theme:object-image' + + def get_preview(self): + return '' + + def get_activity(self): + return '' + + def matches_mime_type(cls, mime_type): + return mime_type in cls._types + matches_mime_type = classmethod(matches_mime_type) + +class UriFileType(FileType): + + _types = set(['_NETSCAPE_URL']) + + def get_name(self): + return _('Web Page') + + def get_icon(self): + return 'theme:object-link' + + def get_preview(self): + for format, data in self._formats.iteritems(): + if format in UriFileType._types: + string = data.get_data() + title = string.split("\n")[1] + return title + + return '' + + def get_activity(self): + return '' + + def matches_mime_type(cls, mime_type): + return mime_type in cls._types + matches_mime_type = classmethod(matches_mime_type) + +class PdfFileType(FileType): + + _types = set(['application/pdf', 'application/x-pdf']) + + def get_name(self): + return _('PDF file') + + def get_icon(self): + return 'theme:object-text' + + def get_preview(self): + return '' + + def get_activity(self): + return 'org.laptop.sugar.Xbook' + + def matches_mime_type(cls, mime_type): + return mime_type in cls._types + matches_mime_type = classmethod(matches_mime_type) + +class MsWordFileType(FileType): + + _types = set(['application/msword']) + + def get_name(self): + return _('MS Word file') + + def get_icon(self): + return 'theme:object-text' + + def get_preview(self): + return '' + + def get_activity(self): + return 'org.laptop.AbiWordActivity' + + def matches_mime_type(cls, mime_type): + return mime_type in cls._types + matches_mime_type = classmethod(matches_mime_type) + +class RtfFileType(TextFileType): + + _types = set(['application/rtf', 'text/rtf']) + + def get_name(self): + return _('RTF file') + + def matches_mime_type(cls, mime_type): + return mime_type in cls._types + matches_mime_type = classmethod(matches_mime_type) + +class AbiwordFileType(TextFileType): + + _types = set(['application/x-abiword']) + + def get_name(self): + return _('Abiword file') + + def matches_mime_type(cls, mime_type): + return mime_type in cls._types + matches_mime_type = classmethod(matches_mime_type) + +class SqueakProjectFileType(FileType): + + _types = set(['application/x-squeak-project']) + + def get_name(self): + return _('Squeak project') + + def get_icon(self): + return 'theme:object-squeak-project' + + def get_preview(self): + return '' + + def get_activity(self): + return 'org.vpri.EtoysActivity' + + def matches_mime_type(cls, mime_type): + return mime_type in cls._types + matches_mime_type = classmethod(matches_mime_type) + +class OOTextFileType(FileType): + + _types = set(['application/vnd.oasis.opendocument.text']) + + def get_name(self): + return _('OpenOffice text file') + + def get_icon(self): + return 'theme:object-text' + + def get_preview(self): + return '' + + def get_activity(self): + return 'org.laptop.AbiWordActivity' + + def matches_mime_type(cls, mime_type): + return mime_type in cls._types + matches_mime_type = classmethod(matches_mime_type) + +class UnknownFileType(FileType): + def get_name(self): + return _('Object') + + def get_icon(self): + return 'theme:stock-missing' + + def get_preview(self): + return '' + + def get_activity(self): + return '' + + def matches_mime_type(cls, mime_type): + return true + matches_mime_type = classmethod(matches_mime_type) + +class TypeRegistry: + def __init__(self): + self._types = [] + self._types.append(PdfFileType) + self._types.append(MsWordFileType) + self._types.append(RtfFileType) + self._types.append(OOTextFileType) + self._types.append(UriFileType) + self._types.append(ImageFileType) + self._types.append(AbiwordFileType) + self._types.append(TextFileType) + self._types.append(SqueakProjectFileType) + + def get_type(self, formats): + for file_type in self._types: + for format, data in formats.iteritems(): + if file_type.matches_mime_type(format): + return file_type(formats) + + return UnknownFileType(formats) + +_type_registry = None +def get_instance(): + global _type_registry + if not _type_registry: + _type_registry = TypeRegistry() + return _type_registry diff --git a/shell/view/clipboardicon.py b/shell/view/clipboardicon.py index e6b4a3f0..ef0de29e 100644 --- a/shell/view/clipboardicon.py +++ b/shell/view/clipboardicon.py @@ -44,6 +44,7 @@ class ClipboardIcon(CanvasIcon): self._name = name self._percent = 0 self._preview = None + self._activity = None self._selected = False self._hover = False self.props.box_width = units.grid_to_pixels(1) @@ -73,26 +74,60 @@ class ClipboardIcon(CanvasIcon): self.props.background_color = color.TOOLBAR_BACKGROUND.get_int() def get_popup(self): - self._menu = ClipboardMenu(self._name, self._percent, self._preview) + self._menu = ClipboardMenu(self._name, self._percent, self._preview, + self._activity) self._menu.connect('action', self._popup_action_cb) return self._menu def get_popup_context(self): return self._popup_context - def set_name(self, name): + def set_state(self, name, percent, icon_name, preview, activity): self._name = name - if self._menu: - self._menu.set_title(name) - - def set_formats(self, formats): - self._preview = None - self.props.icon_name = 'theme:stock-missing' - - def set_state(self, percent): self._percent = percent + self._preview = preview + self._activity = activity + self.set_property("icon_name", icon_name) if self._menu: - self._menu.set_state(percent) + self._menu.set_state(name, percent, preview, activity) + + if activity and percent < 100: + self.props.xo_color = XoColor("#000000,#424242") + else: + self.props.xo_color = XoColor("#000000,#FFFFFF") + + if activity and percent == 100: + # FIXME: restrict based on file type rather than activity once + # we have a better type registry + # restrict auto-open to a specific set of activities + allowed = ["org.laptop.AbiWordActivity", + "org.laptop.sugar.Xbook", + "org.vpri.EtoysActivity"] + if activity in allowed: + self._open_file() + + def _open_file(self): + if self._percent < 100 or not self._activity: + return + + # Get the file path + cb_service = clipboardservice.get_instance() + obj = cb_service.get_object(self._object_id) + formats = obj['FORMATS'] + if len(formats) > 0: + path = cb_service.get_object_data(self._object_id, formats[0]) + + # FIXME: would be better to check for format.onDisk + try: + path_exists = os.path.exists(path) + except TypeError: + path_exists = False + + if path_exists: + uri = 'file://' + path + activityfactory.create_with_uri(self._activity, uri) + else: + logging.debug("Clipboard item file path %s didn't exist" % path) def _popup_action_cb(self, popup, menu_item): action = menu_item.props.action_id diff --git a/shell/view/clipboardmenu.py b/shell/view/clipboardmenu.py index 96999728..3e8239d6 100644 --- a/shell/view/clipboardmenu.py +++ b/shell/view/clipboardmenu.py @@ -33,11 +33,9 @@ class ClipboardMenu(Menu): ACTION_OPEN = 1 ACTION_STOP_DOWNLOAD = 2 - def __init__(self, name, percent, preview): + def __init__(self, name, percent, preview, activity): Menu.__init__(self, name) self.props.border = 0 - - self._activities = None if percent < 100: self._progress_bar = ClipboardProgressBar(percent) @@ -56,10 +54,10 @@ class ClipboardMenu(Menu): self._preview_text.props.font_desc = font.DEFAULT.get_pango_desc() self.append(self._preview_text) - self._update_icons(percent) + self._update_icons(percent, activity) - def _update_icons(self, percent): - if percent == 100 and self._activities: + def _update_icons(self, percent, activity): + if percent == 100 and activity: if not self._remove_item: self._remove_item = MenuItem(ClipboardMenu.ACTION_DELETE, _('Remove'), @@ -75,7 +73,7 @@ class ClipboardMenu(Menu): if self._stop_item: self.remove_item(self._stop_item) self._stop_item = None - elif percent == 100 and not self._activities: + elif percent == 100 and not activity: if not self._remove_item: self._remove_item = MenuItem(ClipboardMenu.ACTION_DELETE, _('Remove'), @@ -104,33 +102,8 @@ class ClipboardMenu(Menu): self.remove_item(self._open_item) self._open_item = None - def _open_file(self): - if self._percent < 100 or not self._activity: - return - - # Get the file path - cb_service = clipboardservice.get_instance() - obj = cb_service.get_object(self._object_id) - formats = obj['FORMATS'] - if len(formats) > 0: - path = cb_service.get_object_data(self._object_id, formats[0]) - - # FIXME: would be better to check for format.onDisk - try: - path_exists = os.path.exists(path) - except TypeError: - path_exists = False - - if path_exists: - uri = 'file://' + path - activityfactory.create_with_uri(self._activity, uri) - else: - logging.debug("Clipboard item file path %s didn't exist" % path) - - def set_activities(self, activities): - self._activities = activities - - def set_state(self, percent): + def set_state(self, name, percent, preview, activity): + self.set_title(name) if self._progress_bar: - self._progress_bar.props.percent = percent - self._update_icons(percent) + self._progress_bar.set_property('percent', percent) + self._update_icons(percent, activity) diff --git a/shell/view/frame/clipboardbox.py b/shell/view/frame/clipboardbox.py index 1baca587..2dcad10e 100644 --- a/shell/view/frame/clipboardbox.py +++ b/shell/view/frame/clipboardbox.py @@ -52,7 +52,6 @@ class ClipboardBox(hippo.CanvasBox): cb_service = clipboardservice.get_instance() cb_service.connect('object-added', self._object_added_cb) cb_service.connect('object-deleted', self._object_deleted_cb) - cb_service.connect('object-changed', self._object_changed_cb) cb_service.connect('object-state-changed', self._object_state_changed_cb) def owns_clipboard(self): @@ -137,14 +136,10 @@ class ClipboardBox(hippo.CanvasBox): del self._icons[object_id] logging.debug('ClipboardBox: ' + object_id + ' was deleted.') - def _object_changed_cb(self, cb_service, object_id, name, formats): + def _object_state_changed_cb(self, cb_service, object_id, name, percent, + icon_name, preview, activity): icon = self._icons[object_id] - icon.set_name(name) - icon.set_formats(formats) - - def _object_state_changed_cb(self, cb_service, object_id, percent): - icon = self._icons[object_id] - icon.set_state(percent) + icon.set_state(name, percent, icon_name, preview, activity) if icon.props.selected and percent == 100: self._put_in_clipboard(object_id) diff --git a/sugar/clipboard/clipboardservice.py b/sugar/clipboard/clipboardservice.py index 17f6998d..c7d68e46 100644 --- a/sugar/clipboard/clipboardservice.py +++ b/sugar/clipboard/clipboardservice.py @@ -4,6 +4,9 @@ import gobject NAME_KEY = 'NAME' PERCENT_KEY = 'PERCENT' +ICON_KEY = 'ICON' +PREVIEW_KEY = 'PREVIEW' +ACTIVITY_KEY = 'ACTIVITY' FORMATS_KEY = 'FORMATS' DBUS_SERVICE = "org.laptop.Clipboard" @@ -11,15 +14,14 @@ DBUS_INTERFACE = "org.laptop.Clipboard" DBUS_PATH = "/org/laptop/Clipboard" class ClipboardService(gobject.GObject): + __gsignals__ = { - 'object-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([str, str])), - 'object-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([str, str, gobject.TYPE_PYOBJECT])), + 'object-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([str, str])), 'object-deleted': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([str])), + ([str])), 'object-state-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([str, int])), + ([str, str, int, str, str, str])), } def __init__(self): @@ -52,8 +54,6 @@ class ClipboardService(gobject.GObject): self._object_deleted_cb) self._dbus_service.connect_to_signal('object_state_changed', self._object_state_changed_cb) - self._dbus_service.connect_to_signal('object_changed', - self._object_changed_cb) self._connected = True bus.remove_signal_receiver(self._nameOwnerChangedHandler) @@ -66,15 +66,13 @@ class ClipboardService(gobject.GObject): def _object_added_cb(self, object_id, name): self.emit('object-added', str(object_id), name) - def _object_changed_cb(self, object_id, values): - self.emit('object-changed', str(object_id), - values[NAME_KEY], values[FORMATS_KEY]) - def _object_deleted_cb(self, object_id): self.emit('object-deleted', str(object_id)) def _object_state_changed_cb(self, object_id, values): - self.emit('object-state-changed', str(object_id), values[PERCENT_KEY]) + self.emit('object-state-changed', str(object_id), values[NAME_KEY], + values[PERCENT_KEY], values[ICON_KEY], values[PREVIEW_KEY], + values[ACTIVITY_KEY]) def add_object(self, name): return str(self._dbus_service.add_object(name))