From 3dc8505ec573d9526109d604812cc7367fb5b641 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Fri, 31 Aug 2007 14:53:59 +0200 Subject: [PATCH 1/7] #3045: Fix issues with the clipboard icons and palettes. --- NEWS | 1 + shell/view/clipboardicon.py | 139 +++++++++++++------- shell/view/clipboardmenu.py | 1 - shell/view/frame/clipboardbox.py | 160 +++-------------------- shell/view/frame/clipboardpanelwindow.py | 16 +-- 5 files changed, 111 insertions(+), 206 deletions(-) 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") From ad1818f985d1983d9f8a3ada008bf402812bfca0 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Fri, 31 Aug 2007 15:43:38 +0200 Subject: [PATCH 2/7] #3063: Make the 'Keep' button in the activity toolbar create a copy of the activity in the journal. Add a copy() method for activities to use. --- NEWS | 2 ++ sugar/activity/activity.py | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 639da773..6d784e44 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,5 @@ +* #3063: Make the 'Keep' button in the activity toolbar create a copy of the + activity in the journal. Add a copy() method for activities to use. (tomeu) * #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/sugar/activity/activity.py b/sugar/activity/activity.py index 3691a5ec..21a97719 100644 --- a/sugar/activity/activity.py +++ b/sugar/activity/activity.py @@ -123,7 +123,7 @@ class ActivityToolbar(gtk.Toolbar): self._activity.share(private=True) def _keep_clicked_cb(self, button): - self._activity.save() + self._activity.copy() def _stop_clicked_cb(self, button): self._activity.close() @@ -305,7 +305,7 @@ class Activity(Window, gtk.Container): elif scope == SHARE_NEIGHBORHOOD: self.share(private=False) else: - logging.debug("Unknown share scope %d" % scope) + logging.debug("Unknown share scope %r" % scope) except KeyError: pass elif create_jobject: @@ -455,6 +455,8 @@ class Activity(Window, gtk.Container): def save(self): """Request that the activity is saved to the Journal.""" + logging.debug('Activity.save: %r' % self._jobject.object_id) + if self._updating_jobject: return @@ -483,6 +485,11 @@ class Activity(Window, gtk.Container): reply_handler=self._internal_save_cb, error_handler=self._internal_save_error_cb) + def copy(self): + logging.debug('Activity.copy: %r' % self._jobject.object_id) + self.save() + self._jobject.object_id = None + def _internal_joined_cb(self, activity, success, err): """Callback when join has finished""" self._shared_activity.disconnect(self._join_id) From f4c7e03c34ba77b29e0eb3c703945ad2ea46fb18 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 31 Aug 2007 11:32:54 -0400 Subject: [PATCH 3/7] Fix traceback showing wireless networks --- shell/hardware/nmclient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/hardware/nmclient.py b/shell/hardware/nmclient.py index f61018c5..01f6cd93 100644 --- a/shell/hardware/nmclient.py +++ b/shell/hardware/nmclient.py @@ -134,7 +134,7 @@ class Network(gobject.GObject): fav_nets = [] if self._client.nminfo: - fav_nets = self._client.nminfo.get_networks() + fav_nets = self._client.nminfo.get_networks(nminfo.NETWORK_TYPE_ALLOWED) if self._ssid in fav_nets: self._favorite = True From 00ec48f21447205445aa2a34d31093a969cc549a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 31 Aug 2007 11:46:03 -0400 Subject: [PATCH 4/7] Make AP icons small size --- shell/view/home/MeshBox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/view/home/MeshBox.py b/shell/view/home/MeshBox.py index 09eb8693..d9eaca31 100644 --- a/shell/view/home/MeshBox.py +++ b/shell/view/home/MeshBox.py @@ -43,7 +43,7 @@ _ICON_NAME = 'network-wireless' class AccessPointView(PulsingIcon): def __init__(self, model): - PulsingIcon.__init__(self) + PulsingIcon.__init__(self, size=style.SMALL_ICON_SIZE) self._model = model self.connect('activated', self._activate_cb) From 0e60f07dd2bf08f2068a0f4b0516a96cff9cf4b7 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Fri, 31 Aug 2007 19:57:45 +0200 Subject: [PATCH 5/7] Save the temp file for the icon with the svg suffix so Icon will recognize it as a svg. --- sugar/activity/bundle.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sugar/activity/bundle.py b/sugar/activity/bundle.py index d361c621..8bf33866 100644 --- a/sugar/activity/bundle.py +++ b/sugar/activity/bundle.py @@ -219,11 +219,9 @@ class Bundle: file_names = zip_file.namelist() root_dir = self._get_bundle_root_dir(file_names) icon_path = os.path.join(root_dir, 'activity', self._icon + '.svg') - print icon_path - print file_names if icon_path in file_names: icon_data = zip_file.read(icon_path) - temp_file, temp_file_path = tempfile.mkstemp(self._icon) + temp_file, temp_file_path = tempfile.mkstemp(suffix='.svg', prefix=self._icon) os.write(temp_file, icon_data) os.close(temp_file) return temp_file_path From 8c411ceb70688045baecb350a2cd453a0fc9f308 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 31 Aug 2007 14:32:33 -0400 Subject: [PATCH 6/7] Return 404 error when path can't be translated --- sugar/network.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sugar/network.py b/sugar/network.py index 77de95e8..49d48826 100644 --- a/sugar/network.py +++ b/sugar/network.py @@ -144,6 +144,10 @@ class ChunkedGlibHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): ** [dcbw] modified to send Content-disposition filename too """ path = self.translate_path(self.path) + if not path or not os.path.exists(path): + self.send_error(404, "File not found") + return None + f = None if os.path.isdir(path): for index in "index.html", "index.htm": From ecbbcb0026a8883877ed0fabcbd40c4810bfa499 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Fri, 31 Aug 2007 20:37:44 +0200 Subject: [PATCH 7/7] Don't allow removing system-installed activities. --- NEWS | 1 + shell/view/frame/activitybutton.py | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 6d784e44..68ca39ca 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,4 @@ +* Don't allow removing system-installed activities. (tomeu) * #3063: Make the 'Keep' button in the activity toolbar create a copy of the activity in the journal. Add a copy() method for activities to use. (tomeu) * #3045: Fix issues with the clipboard icons and palettes. (tomeu) diff --git a/shell/view/frame/activitybutton.py b/shell/view/frame/activitybutton.py index d9a7ca4d..93933fef 100644 --- a/shell/view/frame/activitybutton.py +++ b/shell/view/frame/activitybutton.py @@ -54,10 +54,11 @@ class ActivityButton(TrayButton, gobject.GObject): self.set_palette(palette) palette.props.invoker = FrameWidgetInvoker(self) - menu_item = gtk.MenuItem(_('Remove')) - menu_item.connect('activate', self.item_remove_cb) - palette.menu.append(menu_item) - menu_item.show() + if os.path.dirname(self._activity_info.path) == os.path.expanduser('~/Activities'): + menu_item = gtk.MenuItem(_('Remove')) + menu_item.connect('activate', self.item_remove_cb) + palette.menu.append(menu_item) + menu_item.show() def item_remove_cb(self, widget): self.emit('remove_activity')