diff --git a/NEWS b/NEWS index b26e699d..639da773 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,4 @@ +* #3045: Fix issues with the clipboard icons and palettes. (tomeu) * Get invites back to work in the UI. (marco) * Get the title on activity palette in the mesh to work. (marco) diff --git a/shell/view/clipboardicon.py b/shell/view/clipboardicon.py index 72f88db0..0b1f93ab 100644 --- a/shell/view/clipboardicon.py +++ b/shell/view/clipboardicon.py @@ -19,65 +19,95 @@ import logging from gettext import gettext as _ import gobject +import gtk -from sugar.graphics.icon import CanvasIcon -from view.clipboardmenu import ClipboardMenu +from sugar.graphics.radiotoolbutton import RadioToolButton from sugar.graphics.xocolor import XoColor +from sugar.graphics.icon import Icon from sugar.graphics import style from sugar.clipboard import clipboardservice from sugar import util from sugar import profile -class ClipboardIcon(CanvasIcon): +from view.clipboardmenu import ClipboardMenu +from view.frame.frameinvoker import FrameWidgetInvoker + +class ClipboardIcon(RadioToolButton): __gtype_name__ = 'SugarClipboardIcon' - __gproperties__ = { - 'selected' : (bool, None, None, False, - gobject.PARAM_READWRITE) - } - - def __init__(self, object_id, name): - CanvasIcon.__init__(self) + def __init__(self, object_id, name, group): + RadioToolButton.__init__(self, group=group) self._object_id = object_id self._name = name self._percent = 0 self._preview = None self._activity = None - self._selected = False - self._hover = False - self.props.size = style.STANDARD_ICON_SIZE - self.props.xo_color = XoColor(profile.get_color().to_string()) + self.owns_clipboard = False + + self._icon = Icon() + self._icon.props.xo_color = profile.get_color() + self.set_icon_widget(self._icon) + self._icon.show() + + self.props.sensitive = False cb_service = clipboardservice.get_instance() + cb_service.connect('object-state-changed', self._object_state_changed_cb) obj = cb_service.get_object(self._object_id) formats = obj['FORMATS'] self.palette = ClipboardMenu(self._object_id, self._name, self._percent, self._preview, self._activity, formats and formats[0] == 'application/vnd.olpc-sugar') + self.palette.props.invoker = FrameWidgetInvoker(self) + + self.child.connect('drag_data_get', self._drag_data_get_cb) + self.connect('notify::active', self._notify_active_cb) - def do_set_property(self, pspec, value): - if pspec.name == 'selected': - self._set_selected(value) - self.emit_paint_needed(0, 0, -1, -1) - else: - CanvasIcon.do_set_property(self, pspec, value) + def get_object_id(self): + return self._object_id - def do_get_property(self, pspec): - if pspec.name == 'selected': - return self._selected - else: - return CanvasIcon.do_get_property(self, pspec) + def _drag_data_get_cb(self, widget, context, selection, targetType, eventTime): + logging.debug('_drag_data_get_cb: requested target ' + selection.target) + + cb_service = clipboardservice.get_instance() + data = cb_service.get_object_data(self._object_id, selection.target)['DATA'] + + selection.set(selection.target, 8, data) - def _set_selected(self, selected): - self._selected = selected - if selected: - if not self._hover: - self.props.background_color = style.COLOR_SELECTION_GREY.get_int() - else: - self.props.background_color = style.COLOR_PANEL_GREY.get_int() + def _put_in_clipboard(self): + logging.debug('ClipboardIcon._put_in_clipboard') + targets = self._get_targets() + if targets: + clipboard = gtk.Clipboard() + if not clipboard.set_with_data(targets, + self._clipboard_data_get_cb, + self._clipboard_clear_cb, + targets): + logging.error('GtkClipboard.set_with_data failed!') + else: + self.owns_clipboard = True + + def _clipboard_data_get_cb(self, clipboard, selection, info, targets): + if not selection.target in [target[0] for target in targets]: + logging.warning('ClipboardIcon._clipboard_data_get_cb: asked %s but' \ + ' only have %r.' % (selection.target, targets)) + return + cb_service = clipboardservice.get_instance() + data = cb_service.get_object_data(self._object_id, selection.target)['DATA'] + + selection.set(selection.target, 8, data) + + def _clipboard_clear_cb(self, clipboard, targets): + logging.debug('ClipboardIcon._clipboard_clear_cb') + self.owns_clipboard = False + + def _object_state_changed_cb(self, cb_service, object_id, name, percent, + icon_name, preview, activity): + + if object_id != self._object_id: + return - def set_state(self, name, percent, icon_name, preview, activity): cb_service = clipboardservice.get_instance() obj = cb_service.get_object(self._object_id) if obj['FORMATS'] and obj['FORMATS'][0] == 'application/vnd.olpc-sugar': @@ -86,32 +116,41 @@ class ClipboardIcon(CanvasIcon): installable = False if icon_name: - self.props.icon_name = icon_name + self._icon.props.icon_name = icon_name else: - self.props.icon_name = 'application-octet-stream' + self._icon.props.icon_name = 'application-octet-stream' + self.child.drag_source_set(gtk.gdk.BUTTON1_MASK, + self._get_targets(), + gtk.gdk.ACTION_COPY) + self.child.drag_source_set_icon_name(self._icon.props.icon_name) + self._name = name self._percent = percent self._preview = preview self._activity = activity self.palette.set_state(name, percent, preview, activity, installable) - if (activity or installable) and percent < 100: - self.props.xo_color = XoColor("#000000,#424242") + self.props.sensitive = (percent == 100) + + if self.props.active: + self._put_in_clipboard() + + def _notify_active_cb(self, widget, pspec): + if self.props.active: + self._put_in_clipboard() else: - self.props.xo_color = XoColor(profile.get_color().to_string()) + self.owns_clipboard = False - def get_object_id(self): - return self._object_id + def _get_targets(self): + cb_service = clipboardservice.get_instance() - def prelight(self, enter): - if enter: - self._hover = True - self.props.background_color = style.COLOR_BLACK.get_int() - else: - self._hover = False - if self._selected: - self.props.background_color = style.COLOR_SELECTION_GREY.get_int() - else: - self.props.background_color = style.COLOR_PANEL_GREY.get_int() + attrs = cb_service.get_object(self._object_id) + format_types = attrs[clipboardservice.FORMATS_KEY] + + targets = [] + for format_type in format_types: + targets.append((format_type, 0, 0)) + + return targets diff --git a/shell/view/clipboardmenu.py b/shell/view/clipboardmenu.py index 0875f702..1fc44497 100644 --- a/shell/view/clipboardmenu.py +++ b/shell/view/clipboardmenu.py @@ -37,7 +37,6 @@ class ClipboardMenu(Palette): 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._activities = activities diff --git a/shell/view/frame/clipboardbox.py b/shell/view/frame/clipboardbox.py index 33d583ca..a083b760 100644 --- a/shell/view/frame/clipboardbox.py +++ b/shell/view/frame/clipboardbox.py @@ -21,8 +21,10 @@ import hippo import gtk from sugar import util -from view.clipboardicon import ClipboardIcon from sugar.clipboard import clipboardservice +from sugar.graphics.tray import VTray + +from view.clipboardicon import ClipboardIcon class _ContextMap: """Maps a drag context to the clipboard object involved in the dragging.""" @@ -60,33 +62,20 @@ class ClipboardBox(hippo.CanvasBox): self._icons = {} self._context_map = _ContextMap() self._selected_icon = None - self._owns_clipboard = False - self._pressed_button = None - self._press_start_x = None - self._press_start_y = None + self._tray = VTray() + self.append(hippo.CanvasWidget(widget=self._tray), hippo.PACK_EXPAND) + self._tray.show() 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-state-changed', self._object_state_changed_cb) def owns_clipboard(self): - return self._owns_clipboard - - def _get_icon_at_coords(self, x, y): - box_x, box_y = self.get_context().translate_to_widget(self) - x -= box_x - y -= box_y - for object_id, icon in self._icons.iteritems(): - icon_x, icon_y = self.get_position(icon) - icon_width, icon_height = icon.get_allocation() - - if (x >= icon_x ) and (x <= icon_x + icon_width) and \ - (y >= icon_y ) and (y <= icon_y + icon_height): - return icon - - return None + for icon in self._icons.values(): + if icon.owns_clipboard: + return True + return False def _add_selection(self, object_id, selection): if not selection.data: @@ -111,73 +100,33 @@ class ClipboardBox(hippo.CanvasBox): on_disk=False) def _object_added_cb(self, cb_service, object_id, name): - icon = ClipboardIcon(object_id, name) - icon.connect('button-release-event', self._icon_button_release_event_cb) - self._set_icon_selected(icon) + if self._icons: + group = self._icons.values()[0] + else: + group = None - self.prepend(icon) + icon = ClipboardIcon(object_id, name, group) + self._tray.add_item(icon, 0) + icon.show() + + self._set_icon_selected(icon) self._icons[object_id] = icon logging.debug('ClipboardBox: ' + object_id + ' was added.') def _set_icon_selected(self, icon): logging.debug('_set_icon_selected') - icon.props.selected = True - if self._selected_icon: - self._selected_icon.props.selected = False + icon.props.active = True self._selected_icon = icon - def _put_in_clipboard(self, object_id): - logging.debug('ClipboardBox._put_in_clipboard') - targets = self._get_object_targets(object_id) - if targets: - clipboard = gtk.Clipboard() - if not clipboard.set_with_data(targets, - self._clipboard_data_get_cb, - self._clipboard_clear_cb, - targets): - logging.error('GtkClipboard.set_with_data failed!') - else: - self._owns_clipboard = True - - def _clipboard_data_get_cb(self, clipboard, selection, info, targets): - if not selection.target in [target[0] for target in targets]: - logging.warning('ClipboardBox._clipboard_data_get_cb: asked %s but' \ - ' only have %r.' % (selection.target, targets)) - return - object_id = self._selected_icon.get_object_id() - cb_service = clipboardservice.get_instance() - data = cb_service.get_object_data(object_id, selection.target)['DATA'] - - selection.set(selection.target, 8, data) - - def _clipboard_clear_cb(self, clipboard, targets): - logging.debug('ClipboardBox._clipboard_clear_cb') - self._owns_clipboard = False - - def _icon_button_release_event_cb(self, icon, event): - logging.debug('ClipboardBox._icon_button_release_event_cb: %r' % icon.props.selected) - if not icon.props.selected: - self._set_icon_selected(icon) - def _object_deleted_cb(self, cb_service, object_id): icon = self._icons[object_id] position = self.get_children().index(icon) self.remove(icon) - - if icon.props.selected and self.get_children(): - self._set_icon_selected(self.get_children()[position]) del self._icons[object_id] logging.debug('ClipboardBox: ' + object_id + ' was deleted.') - def _object_state_changed_cb(self, cb_service, object_id, name, percent, - icon_name, preview, activity): - icon = self._icons[object_id] - icon.set_state(name, percent, icon_name, preview, activity) - if icon.props.selected and percent == 100: - self._put_in_clipboard(object_id) - def drag_motion_cb(self, widget, context, x, y, time): logging.debug('ClipboardBox._drag_motion_cb') context.drag_status(gtk.gdk.ACTION_COPY, time) @@ -211,72 +160,3 @@ class ClipboardBox(hippo.CanvasBox): if not self._context_map.has_context(context): context.drop_finish(True, gtk.get_current_event_time()) - def drag_data_get_cb(self, widget, context, selection, targetType, eventTime): - logging.debug("drag_data_get_cb: requested target " + selection.target) - - object_id = self._last_clicked_icon.get_object_id() - cb_service = clipboardservice.get_instance() - data = cb_service.get_object_data(object_id, selection.target)['DATA'] - - selection.set(selection.target, 8, data) - - def button_press_event_cb(self, widget, event): - logging.debug("button_press_event_cb") - - if event.button == 1 and event.type == gtk.gdk.BUTTON_PRESS: - self._last_clicked_icon = self._get_icon_at_coords(event.x, event.y) - if self._last_clicked_icon: - self._pressed_button = event.button - self._press_start_x = event.x - self._press_start_y = event.y - - return False - - def motion_notify_event_cb(self, widget, event): - - if not self._pressed_button: - return True - - # if the mouse button is not pressed, no drag should occurr - if not event.state & gtk.gdk.BUTTON1_MASK: - self._pressed_button = None - return True - - logging.debug("motion_notify_event_cb") - - if event.is_hint: - x, y, state = event.window.get_pointer() - else: - x = event.x - y = event.y - state = event.state - - if widget.drag_check_threshold(int(self._press_start_x), - int(self._press_start_y), - int(x), - int(y)): - targets = self._get_object_targets( - self._last_clicked_icon.get_object_id()) - - context = widget.drag_begin(targets, - gtk.gdk.ACTION_COPY, - 1, - event); - - return True - - def drag_end_cb(self, widget, drag_context): - logging.debug("drag_end_cb") - self._pressed_button = None - - def _get_object_targets(self, object_id): - cb_service = clipboardservice.get_instance() - - attrs = cb_service.get_object(object_id) - format_types = attrs[clipboardservice.FORMATS_KEY] - - targets = [] - for format_type in format_types: - targets.append((format_type, 0, 0)) - - return targets diff --git a/shell/view/frame/clipboardpanelwindow.py b/shell/view/frame/clipboardpanelwindow.py index 217096a1..6a018448 100644 --- a/shell/view/frame/clipboardpanelwindow.py +++ b/shell/view/frame/clipboardpanelwindow.py @@ -37,7 +37,7 @@ class ClipboardPanelWindow(FrameWindow): self._clipboard.connect("owner-change", self._owner_change_cb) self._clipboard_box = ClipboardBox() - self.append(self._clipboard_box) + self.append(self._clipboard_box, hippo.PACK_EXPAND) # Receiving dnd drops self.drag_dest_set(0, [], 0) @@ -45,20 +45,6 @@ class ClipboardPanelWindow(FrameWindow): self.connect("drag_drop", self._clipboard_box.drag_drop_cb) self.connect("drag_data_received", self._clipboard_box.drag_data_received_cb) - - # Offering dnd drags - self.drag_source_set(0, [], 0) - self.add_events(gtk.gdk.BUTTON_PRESS_MASK | - gtk.gdk.POINTER_MOTION_HINT_MASK) - self.connect("motion_notify_event", - self._clipboard_box.motion_notify_event_cb) - - # FIXME I'm not sure we should expose the canvas in the Window API - self._canvas.connect("button_press_event", - self._clipboard_box.button_press_event_cb) - - self.connect("drag_end", self._clipboard_box.drag_end_cb) - self.connect("drag_data_get", self._clipboard_box.drag_data_get_cb) def _owner_change_cb(self, clipboard, event): logging.debug("owner_change_cb")