From d9fbf9db79b9abab2491ebbdc169985e38a59055 Mon Sep 17 00:00:00 2001 From: Simon Schampijer Date: Mon, 6 Aug 2012 09:59:48 +0200 Subject: [PATCH] Views: Replace the hippo based layout with one using GTK+ containers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The requirement was to be able to position and size the widgets with different layouts in the Views. We need to be able to have fixed positions (e.g. the owner icon and current activity icon) and we need to be able to change the position (e.g. BuddyIcon in the Neighborhood View) and move the widget by drag and drop (e.g. RandomLayout). The implementation uses a gtk.Container (SugarViewContainer) without an associated GdkWindow to hold the widgets since we do not want to do drawing in this container. The desired layout and the owner icon and optional activity icon are passed at initialization time. A ViewLayout is responsible to calculate the positions and sizes for the SugarViewContainer. To keep track of the positions it uses a grid that is initialized on the first allocation. The owner icon (activity icon) will be added to the grid to make sure it keeps the fixed position. The SpreadLayout derives now from the ViewLayout. The SpreadLayout which is used in the GroupBox and in the MeshContainer does add all it's children to the grid in order to use the collision detection from the grid to not place icons over each other. The RandomLayout does place all it's children in the grid as well for collision detection purposes. The Journal has been made hippo-free as well. We set white background in the ExpandedEntry of the Detail View. For that we change the ExpandedEntry class to subclass gtk.EventBox because the gtk.VBox doesn't have a gtk.gdk.Window associated and the background can't be set otherwise. This patch is based on the work from Walter Bender, Daniel Drake and Raul Gutierrez Segales. Signed-off-by: Simon Schampijer Signed-off-by: Manuel QuiƱones Signed-off-by: Daniel Narvaez --- src/jarabe/desktop/Makefile.am | 4 +- src/jarabe/desktop/favoriteslayout.py | 390 +++++++++++++------------ src/jarabe/desktop/favoritesview.py | 404 ++++++++++++-------------- src/jarabe/desktop/friendview.py | 31 +- src/jarabe/desktop/grid.py | 3 + src/jarabe/desktop/groupbox.py | 51 +--- src/jarabe/desktop/homebox.py | 22 +- src/jarabe/desktop/homewindow.py | 2 + src/jarabe/desktop/meshbox.py | 119 ++++---- src/jarabe/desktop/networkviews.py | 28 +- src/jarabe/desktop/snowflakelayout.py | 103 ++++--- src/jarabe/desktop/spreadlayout.py | 89 ------ src/jarabe/desktop/transitionbox.py | 50 +--- src/jarabe/desktop/viewcontainer.py | 82 ++++++ src/jarabe/journal/detailview.py | 73 +++-- src/jarabe/journal/expandedentry.py | 342 ++++++++++------------ src/jarabe/journal/keepicon.py | 61 ++-- src/jarabe/journal/listview.py | 50 ++-- src/jarabe/view/buddyicon.py | 10 +- src/jarabe/view/pulsingicon.py | 11 +- 20 files changed, 917 insertions(+), 1008 deletions(-) delete mode 100644 src/jarabe/desktop/spreadlayout.py create mode 100644 src/jarabe/desktop/viewcontainer.py diff --git a/src/jarabe/desktop/Makefile.am b/src/jarabe/desktop/Makefile.am index 25fb0b42e9..b36404e048 100644 --- a/src/jarabe/desktop/Makefile.am +++ b/src/jarabe/desktop/Makefile.am @@ -14,5 +14,5 @@ sugar_PYTHON = \ networkviews.py \ schoolserver.py \ snowflakelayout.py \ - spreadlayout.py \ - transitionbox.py + transitionbox.py \ + viewcontainer.py diff --git a/src/jarabe/desktop/favoriteslayout.py b/src/jarabe/desktop/favoriteslayout.py index 360c147dd9..0f63f95264 100644 --- a/src/jarabe/desktop/favoriteslayout.py +++ b/src/jarabe/desktop/favoriteslayout.py @@ -20,9 +20,7 @@ import hashlib from gettext import gettext as _ -import gobject import gtk -import hippo from sugar.graphics import style @@ -42,77 +40,134 @@ style.SMALL_ICON_SIZE] -class FavoritesLayout(gobject.GObject, hippo.CanvasLayout): - """Base class of the different layout types.""" - - __gtype_name__ = 'FavoritesLayout' - +class Layout(object): def __init__(self): - gobject.GObject.__init__(self) - self.box = None - self.fixed_positions = {} + pass - def do_set_box(self, box): - self.box = box + def remove(self, child): + pass - def do_get_height_request(self, for_width): - return 0, gtk.gdk.screen_height() - style.GRID_CELL_SIZE + def allocate_children(self, allocation, children): + pass - def do_get_width_request(self): - return 0, gtk.gdk.screen_width() - def compare_activities(self, icon_a, icon_b): - return 0 +class ViewLayout(Layout): + def __init__(self): + self._grid = None - def append(self, icon, locked=False): - if not hasattr(type(icon), 'fixed_position'): - logging.debug('Icon without fixed_position: %r', icon) + def setup(self, allocation, owner_icon, activity_icon=None): + if self._grid is not None: return - - icon.props.size = max(icon.props.size, style.STANDARD_ICON_SIZE) - - relative_x, relative_y = icon.fixed_position - if relative_x < 0 or relative_y < 0: - logging.debug('Icon out of bounds: %r', icon) + self._grid = Grid(int(allocation.width / _CELL_SIZE), + int(allocation.height / _CELL_SIZE)) + self._grid.connect('child-changed', self.__grid_child_changed_cb) + self._allocate_owner_icon(allocation, owner_icon, activity_icon) + + def _allocate_owner_icon(self, allocation, owner_icon, activity_icon): + # add owner icon to the grid, precisely centered on the screen + # if not None, add an activity icon directly below the owner icon + owner_width, owner_height = owner_icon.size_request() + height = allocation.height + allocation.y + width = allocation.width + + # Find vertical center point of screen + y = height / 2 + + # This container may be offset from the top by a certain amount + # (e.g. for a toolbar at the top of the screen). Adjust the + # center-point for that + y -= allocation.y + + # Now subtract half of the owner height. This gives us the y + # coordinate for the top of the owner icon. + y -= owner_height / 2 + + # calculate x coordinate and create allocation + x = (width - owner_width) / 2 + owner_icon_allocation = gtk.gdk.Rectangle(x, allocation.y + y, + owner_width, owner_height) + owner_icon.size_allocate(owner_icon_allocation) + + # Determine grid coordinates and add to grid + owner_grid_width, owner_grid_height = \ + self._get_child_grid_size(owner_icon) + x = int(x / float(_CELL_SIZE)) + y = int(y / float(_CELL_SIZE)) + self._grid.add(owner_icon, owner_grid_width, owner_grid_height, + x, y, locked=True) + + if activity_icon is None: return - min_width_, width = self.box.get_width_request() - min_height_, height = self.box.get_height_request(width) - self.fixed_positions[icon] = \ - (int(relative_x * _BASE_SCALE / float(width)), - int(relative_y * _BASE_SCALE / float(height))) + # Position the current activity below the XO icon + # FIXME must ensure we cross into next grid cell here.. + activity_width, activity_height = activity_icon.size_request() + x = (width - activity_width) / 2 + y = owner_icon_allocation.y + owner_height + activity_icon_allocation = gtk.gdk.Rectangle(x, y, activity_width, + activity_height) + activity_icon.size_allocate(activity_icon_allocation) + + # Determine grid coordinates and add to grid + activity_grid_width, activity_grid_height = \ + self._get_child_grid_size(activity_icon) + x = int(x / float(_CELL_SIZE)) + y = int(y / float(_CELL_SIZE)) + self._grid.add(activity_icon, activity_grid_width, + activity_grid_height, x, y, locked=True) + + def allocate_children(self, allocation, children): + pass + + def move(self, child, x, y, allocation=None): + self._grid.move(child, x / _CELL_SIZE, y / _CELL_SIZE, locked=True) + width, height = child.size_request() + rect = self._grid.get_child_rect(child) + child_allocation = gtk.gdk.Rectangle(int(round(rect.x * _CELL_SIZE)), + int(round(rect.y * _CELL_SIZE)), + width, + height) + child.size_allocate(child_allocation) + + def _get_child_grid_size(self, child): + width, height = child.size_request() + width = math.ceil(width / _CELL_SIZE) + height = math.ceil(height / _CELL_SIZE) + return int(width), int(height) - def remove(self, icon): - if icon in self.fixed_positions: - del self.fixed_positions[icon] + def __grid_child_changed_cb(self, grid, child): + width, height = child.size_request() + rect = self._grid.get_child_rect(child) + child_allocation = gtk.gdk.Rectangle(int(round(rect.x * _CELL_SIZE)), + int(round(rect.y * _CELL_SIZE)), + width, + height) + child.size_allocate(child_allocation) - def move_icon(self, icon, x, y, locked=False): - if icon not in self.box.get_children(): - raise ValueError('Child not in box.') - if not (hasattr(icon, 'get_bundle_id') and - hasattr(icon, 'get_version')): - logging.debug('Not an activity icon %r', icon) - return +class SpreadLayout(ViewLayout): + def __init__(self): + ViewLayout.__init__(self) - min_width_, width = self.box.get_width_request() - min_height_, height = self.box.get_height_request(width) - registry = bundleregistry.get_registry() - registry.set_bundle_position( - icon.get_bundle_id(), icon.get_version(), - x * width / float(_BASE_SCALE), - y * height / float(_BASE_SCALE)) - self.fixed_positions[icon] = (x, y) + def remove(self, child): + if self._grid.is_in_grid(child): + self._grid.remove(child) - def do_allocate(self, x, y, width, height, req_width, req_height, - origin_changed): - raise NotImplementedError() + def allocate_children(self, allocation, children): + for child in children: + if not self._grid.is_in_grid(child): + width, height = self._get_child_grid_size(child) + self._grid.add(child, width, height, None, None, locked=False) - def allow_dnd(self): - return False + width, height = child.size_request() + rect = self._grid.get_child_rect(child) + x = int(round(rect.x * _CELL_SIZE)) + y = int(round(rect.y * _CELL_SIZE)) + allocation.y + child_allocation = gtk.gdk.Rectangle(x, y, width, height) + child.size_allocate(child_allocation) -class RandomLayout(FavoritesLayout): +class RandomLayout(SpreadLayout): """Lay out icons randomly; try to nudge them around to resolve overlaps.""" __gtype_name__ = 'RandomLayout' @@ -128,69 +183,75 @@ class RandomLayout(FavoritesLayout): """String used to identify this layout in home view dropdown palette.""" def __init__(self): - FavoritesLayout.__init__(self) - - min_width_, width = self.do_get_width_request() - min_height_, height = self.do_get_height_request(width) - - self._grid = Grid(width / _CELL_SIZE, height / _CELL_SIZE) - self._grid.connect('child-changed', self.__grid_child_changed_cb) - - def __grid_child_changed_cb(self, grid, child): - child.emit_request_changed() - - def append(self, icon, locked=False): - FavoritesLayout.append(self, icon, locked) - - min_width_, child_width = icon.get_width_request() - min_height_, child_height = icon.get_height_request(child_width) - min_width_, width = self.box.get_width_request() - min_height_, height = self.box.get_height_request(width) - - if icon in self.fixed_positions: - x, y = self.fixed_positions[icon] - x = min(x, width - child_width) - y = min(y, height - child_height) - elif hasattr(icon, 'get_bundle_id'): - name_hash = hashlib.md5(icon.get_bundle_id()) - x = int(name_hash.hexdigest()[:5], 16) % (width - child_width) - y = int(name_hash.hexdigest()[-5:], 16) % (height - child_height) - else: - x = None - y = None - - if x is None or y is None: - self._grid.add(icon, - child_width / _CELL_SIZE, child_height / _CELL_SIZE) - else: - self._grid.add(icon, - child_width / _CELL_SIZE, child_height / _CELL_SIZE, - x / _CELL_SIZE, y / _CELL_SIZE) + SpreadLayout.__init__(self) + self.fixed_positions = {} - def remove(self, icon): - self._grid.remove(icon) - FavoritesLayout.remove(self, icon) + def _add_fixed_position(self, icon, allocation, locked=False): + if not hasattr(type(icon), 'fixed_position'): + logging.debug('Icon without fixed_position: %r', icon) + return - def move_icon(self, icon, x, y, locked=False): - self._grid.move(icon, x / _CELL_SIZE, y / _CELL_SIZE, locked) - FavoritesLayout.move_icon(self, icon, x, y, locked) + icon.props.pixel_size = max(icon.props.pixel_size, + style.STANDARD_ICON_SIZE) - def do_allocate(self, x, y, width, height, req_width, req_height, - origin_changed): - for child in self.box.get_layout_children(): - # We need to always get requests to not confuse hippo - min_w_, child_width = child.get_width_request() - min_h_, child_height = child.get_height_request(child_width) + relative_x, relative_y = icon.fixed_position + if relative_x < 0 or relative_y < 0: + logging.debug('Icon out of bounds: %r', icon) + return - rect = self._grid.get_child_rect(child.item) - child.allocate(rect.x * _CELL_SIZE, - rect.y * _CELL_SIZE, - child_width, - child_height, - origin_changed) + self.fixed_positions[icon] = \ + (int(relative_x * _BASE_SCALE / float(allocation.width)), + int(relative_y * _BASE_SCALE / float(allocation.height))) + + def allocate_children(self, allocation, children): + for child in children: + child_width, child_height = child.size_request() + if not self._grid.is_in_grid(child): + self._add_fixed_position(child, allocation) + + if child in self.fixed_positions: + x, y = self.fixed_positions[child] + x = min(x, allocation.width - child_width) + y = min(y, allocation.height - child_height) + elif hasattr(child, 'get_bundle_id'): + name_hash = hashlib.md5(child.get_bundle_id()) + x = int(name_hash.hexdigest()[:5], 16) % \ + (allocation.width - child_width) + y = int(name_hash.hexdigest()[-5:], 16) % \ + (allocation.height - child_height) + else: + x = None + y = None + + if x is None or y is None: + self._grid.add(child, child_width / _CELL_SIZE, + child_height / _CELL_SIZE) + else: + self._grid.add(child, child_width / _CELL_SIZE, + child_height / _CELL_SIZE, + x / _CELL_SIZE, y / _CELL_SIZE) + + rect = self._grid.get_child_rect(child) + x = int(round(rect.x * _CELL_SIZE)) + y = int(round(rect.y * _CELL_SIZE)) + allocation.y + child_allocation = gtk.gdk.Rectangle(x, y, + child_width, child_height) + child.size_allocate(child_allocation) + + def move_icon(self, child, x, y, allocation): + ViewLayout.move(self, child, x, y) + + if not (hasattr(child, 'get_bundle_id') and + hasattr(child, 'get_version')): + logging.debug('Not an activity icon %r', child) + return - def allow_dnd(self): - return True + registry = bundleregistry.get_registry() + registry.set_bundle_position( + child.get_bundle_id(), child.get_version(), + x * allocation.width / float(_BASE_SCALE), + y * allocation.height / float(_BASE_SCALE)) + self.fixed_positions[child] = (x, y) _MINIMUM_RADIUS = style.XLARGE_ICON_SIZE / 2 + style.DEFAULT_SPACING + \ @@ -203,7 +264,7 @@ def allow_dnd(self): _INITIAL_ANGLE = math.pi -class RingLayout(FavoritesLayout): +class RingLayout(ViewLayout): """Lay out icons in a ring or spiral around the XO man.""" __gtype_name__ = 'RingLayout' @@ -216,28 +277,9 @@ class RingLayout(FavoritesLayout): """String used to identify this layout in home view dropdown palette.""" def __init__(self): - FavoritesLayout.__init__(self) - self._locked_children = {} + ViewLayout.__init__(self) self._spiral_mode = False - def append(self, icon, locked=False): - FavoritesLayout.append(self, icon, locked) - if locked: - child = self.box.find_box_child(icon) - self._locked_children[child] = (0, 0) - - def remove(self, icon): - child = self.box.find_box_child(icon) - if child in self._locked_children: - del self._locked_children[child] - FavoritesLayout.remove(self, icon) - - def move_icon(self, icon, x, y, locked=False): - FavoritesLayout.move_icon(self, icon, x, y, locked) - if locked: - child = self.box.find_box_child(icon) - self._locked_children[child] = (x, y) - def _calculate_radius_and_icon_size(self, children_count): """ Adjust the ring or spiral radius and icon size as needed. """ self._spiral_mode = False @@ -270,12 +312,10 @@ def _calculate_radius_and_icon_size(self, children_count): return radius, icon_size def _calculate_position(self, radius, icon_size, icon_index, - children_count, sin=math.sin, cos=math.cos): + children_count, width, height, + sin=math.sin, cos=math.cos): """ Calculate an icon position on a circle or a spiral. """ - width, height = self.box.get_allocation() if self._spiral_mode: - min_width_, box_width = self.box.get_width_request() - min_height_, box_height = self.box.get_height_request(box_width) angle, radius = self._calculate_angle_and_radius(icon_index, icon_size) x, y = self._convert_from_polar_to_cartesian(angle, radius, @@ -286,7 +326,7 @@ def _calculate_position(self, radius, icon_size, icon_index, x = radius * cos(angle) + (width - icon_size) / 2 y = radius * sin(angle) + (height - icon_size - \ (style.GRID_CELL_SIZE / 2)) / 2 - return x, y + return int(x), int(y) def _convert_from_polar_to_cartesian(self, angle, radius, icon_size, width, height): @@ -311,49 +351,27 @@ def _calculate_angle_and_radius(self, icon_count, icon_size): radius += (float(icon_spacing) * spiral_spacing / n) return angle, radius - def _get_children_in_ring(self): - children_in_ring = [child for child in self.box.get_layout_children() \ - if child not in self._locked_children] - return children_in_ring - - def do_allocate(self, x, y, width, height, req_width, req_height, - origin_changed): - children_in_ring = self._get_children_in_ring() - if children_in_ring: - radius, icon_size = \ - self._calculate_radius_and_icon_size(len(children_in_ring)) - - for n in range(len(children_in_ring)): - child = children_in_ring[n] - - x, y = self._calculate_position(radius, icon_size, n, - len(children_in_ring)) - - # We need to always get requests to not confuse hippo - min_w_, child_width = child.get_width_request() - min_h_, child_height = child.get_height_request(child_width) + def allocate_children(self, allocation, children): + radius, icon_size = self._calculate_radius_and_icon_size(len(children)) - child.allocate(int(x), int(y), child_width, child_height, - origin_changed) - child.item.props.size = icon_size + children.sort(self.compare_activities) + for n in range(len(children)): + child = children[n] - for child in self._locked_children.keys(): - x, y = self._locked_children[child] - - # We need to always get requests to not confuse hippo - min_w_, child_width = child.get_width_request() - min_h_, child_height = child.get_height_request(child_width) - - if child_width <= 0 or child_height <= 0: - return - - child.allocate(int(x), int(y), child_width, child_height, - origin_changed) + x, y = self._calculate_position(radius, icon_size, n, + len(children), allocation.width, + allocation.height) + child.size_request() + child.set_size(icon_size) + child_allocation = gtk.gdk.Rectangle(allocation.x + x, + allocation.y + y, + icon_size, icon_size) + child.size_allocate(child_allocation) def compare_activities(self, icon_a, icon_b): if hasattr(icon_a, 'installation_time') and \ hasattr(icon_b, 'installation_time'): - return icon_b.installation_time - icon_a.installation_time + return int(icon_b.installation_time - icon_a.installation_time) else: return 0 @@ -420,13 +438,11 @@ def adjust_index(self, i): return i def _calculate_position(self, radius, icon_size, oindex, children_count, - sin=math.sin, cos=math.cos): + width, height, sin=math.sin, cos=math.cos): """Calculate the position of sunflower floret number 'oindex'. If the result is outside the bounding box, use the next index which is inside the bounding box.""" - width, height = self.box.get_allocation() - while True: index = self.adjust_index(oindex) @@ -454,7 +470,7 @@ def _calculate_position(self, radius, icon_size, oindex, children_count, # try again continue - return x, y + return int(x), int(y) class BoxLayout(RingLayout): @@ -476,7 +492,7 @@ def __init__(self): RingLayout.__init__(self) def _calculate_position(self, radius, icon_size, index, children_count, - sin=None, cos=None): + width, height, sin=None, cos=None): # use "orthogonal" versions of cos and sin in order to square the # circle and turn the 'ring view' into a 'box view' @@ -496,8 +512,8 @@ def cos_d(d): sin = lambda r: cos_d(math.degrees(r) - 90) return RingLayout._calculate_position(self, radius, icon_size, index, - children_count, sin=sin, - cos=cos) + children_count, width, height, + sin=sin, cos=cos) class TriangleLayout(RingLayout): @@ -526,7 +542,7 @@ def _calculate_radius_and_icon_size(self, children_count): return max(radius, _MINIMUM_RADIUS + style.MEDIUM_ICON_SIZE), icon_size def _calculate_position(self, radius, icon_size, index, children_count, - sin=math.sin, cos=math.cos): + width, height, sin=math.sin, cos=math.cos): # tweak cos and sin in order to make the 'ring' into an equilateral # triangle. @@ -556,5 +572,5 @@ def sin_d(d): sin = lambda r: sin_d(math.degrees(r)) return RingLayout._calculate_position(self, radius, icon_size, index, - children_count, sin=sin, - cos=cos) + children_count, width, height, + sin=sin, cos=cos) diff --git a/src/jarabe/desktop/favoritesview.py b/src/jarabe/desktop/favoritesview.py index 654f4002b5..35c4053c19 100644 --- a/src/jarabe/desktop/favoritesview.py +++ b/src/jarabe/desktop/favoritesview.py @@ -23,10 +23,9 @@ import gconf import glib import gtk -import hippo from sugar.graphics import style -from sugar.graphics.icon import Icon, CanvasIcon +from sugar.graphics.icon import Icon from sugar.graphics.menuitem import MenuItem from sugar.graphics.alert import Alert from sugar.graphics.xocolor import XoColor @@ -35,9 +34,11 @@ from sugar.datastore import datastore from jarabe.view.palettes import JournalPalette -from jarabe.view.palettes import CurrentActivityPalette, ActivityPalette +from jarabe.view.palettes import CurrentActivityPalette +from jarabe.view.palettes import ActivityPalette from jarabe.view.buddyicon import BuddyIcon from jarabe.view.buddymenu import BuddyMenu +from jarabe.view.eventicon import EventIcon from jarabe.model.buddy import get_owner_instance from jarabe.model import shell from jarabe.model import bundleregistry @@ -46,6 +47,7 @@ from jarabe.desktop import schoolserver from jarabe.desktop.schoolserver import RegisterError from jarabe.desktop import favoriteslayout +from jarabe.desktop.viewcontainer import ViewContainer _logger = logging.getLogger('FavoritesView') @@ -64,169 +66,127 @@ _favorites_settings = None -class FavoritesView(hippo.Canvas): - __gtype_name__ = 'SugarFavoritesView' +class FavoritesBox(gtk.VBox): + __gtype_name__ = 'SugarFavoritesBox' - def __init__(self, **kwargs): - logging.debug('STARTUP: Loading the favorites view') + def __init__(self): + gtk.VBox.__init__(self) - gobject.GObject.__init__(self, **kwargs) + self._view = FavoritesView(self) + self.pack_start(self._view) + self._view.show() - # DND stuff - self._pressed_button = None - self._press_start_x = None - self._press_start_y = None - self._hot_x = None - self._hot_y = None - self._last_clicked_icon = None + self._alert = None - self._box = hippo.CanvasBox() - self._box.props.background_color = style.COLOR_WHITE.get_int() - self.set_root(self._box) + def set_filter(self, query): + self._view.set_filter(query) - self._my_icon = OwnerIcon(style.XLARGE_ICON_SIZE) - self._my_icon.connect('register-activate', self.__register_activate_cb) - self._box.append(self._my_icon) + def set_resume_mode(self, resume_mode): + self._view.set_resume_mode(resume_mode) - self._current_activity = CurrentActivityIcon() - self._box.append(self._current_activity) + def add_alert(self, alert): + if self._alert is not None: + self.remove_alert() + self._alert = alert + self.pack_start(alert, False) + self.reorder_child(alert, 0) - self._layout = None + def remove_alert(self): + self.remove(self._alert) self._alert = None - self._resume_mode = True - # More DND stuff - self.add_events(gtk.gdk.BUTTON_PRESS_MASK | - gtk.gdk.POINTER_MOTION_HINT_MASK) - self.connect('motion-notify-event', self.__motion_notify_event_cb) - self.connect('button-press-event', self.__button_press_event_cb) - self.connect('drag-begin', self.__drag_begin_cb) - self.connect('drag-motion', self.__drag_motion_cb) - self.connect('drag-drop', self.__drag_drop_cb) - self.connect('drag-data-received', self.__drag_data_received_cb) - gobject.idle_add(self.__connect_to_bundle_registry_cb) +class FavoritesView(ViewContainer): + __gtype_name__ = 'SugarFavoritesView' + + def __init__(self, box): + self._box = box + self._layout = None favorites_settings = get_settings() favorites_settings.changed.connect(self.__settings_changed_cb) self._set_layout(favorites_settings.layout) - def set_filter(self, query): - query = query.strip() - for icon in self._box.get_children(): - if icon not in [self._my_icon, self._current_activity]: - activity_name = icon.get_activity_name().lower() - if activity_name.find(query) > -1: - icon.alpha = 1.0 - else: - icon.alpha = 0.33 + owner_icon = OwnerIcon(style.XLARGE_ICON_SIZE) + owner_icon.connect('register-activate', self.__register_activate_cb) - def __settings_changed_cb(self, **kwargs): - favorites_settings = get_settings() - self._set_layout(favorites_settings.layout) + current_activity = CurrentActivityIcon() - def __connect_to_bundle_registry_cb(self): - registry = bundleregistry.get_registry() + ViewContainer.__init__(self, layout=self._layout, + owner_icon=owner_icon, + activity_icon=current_activity) - for info in registry: - if registry.is_bundle_favorite(info.get_bundle_id(), - info.get_activity_version()): - self._add_activity(info) + self.add_events(gtk.gdk.BUTTON_PRESS_MASK | + gtk.gdk.POINTER_MOTION_HINT_MASK) + self.drag_dest_set(0, [], 0) + self.connect('drag-motion', self.__drag_motion_cb) + self.connect('drag-drop', self.__drag_drop_cb) + self.connect('drag-data-received', self.__drag_data_received_cb) - registry.connect('bundle-added', self.__activity_added_cb) - registry.connect('bundle-removed', self.__activity_removed_cb) - registry.connect('bundle-changed', self.__activity_changed_cb) + self._dragging = False + self._pressed_button = None + self._press_start_x = 0 + self._press_start_y = 0 + self._hot_x = None + self._hot_y = None + self._last_clicked_icon = None - def _add_activity(self, activity_info): - if activity_info.get_bundle_id() == 'org.laptop.JournalActivity': - return - icon = ActivityIcon(activity_info) - icon.props.size = style.STANDARD_ICON_SIZE - icon.set_resume_mode(self._resume_mode) - self._box.insert_sorted(icon, 0, self._layout.compare_activities) - self._layout.append(icon) + self._alert = None + self._resume_mode = True - def __activity_added_cb(self, activity_registry, activity_info): - registry = bundleregistry.get_registry() - if registry.is_bundle_favorite(activity_info.get_bundle_id(), - activity_info.get_activity_version()): - self._add_activity(activity_info) + gobject.idle_add(self.__connect_to_bundle_registry_cb) - def _find_activity_icon(self, bundle_id, version): - for icon in self._box.get_children(): - if isinstance(icon, ActivityIcon) and \ - icon.bundle_id == bundle_id and icon.version == version: - return icon - return None + def __settings_changed_cb(self, **kwargs): + favorites_settings = get_settings() + layout_set = self._set_layout(favorites_settings.layout) + if layout_set: + self.set_layout(self._layout) + registry = bundleregistry.get_registry() + for info in registry: + if registry.is_bundle_favorite(info.get_bundle_id(), + info.get_activity_version()): + self._add_activity(info) - def __activity_removed_cb(self, activity_registry, activity_info): - icon = self._find_activity_icon(activity_info.get_bundle_id(), - activity_info.get_activity_version()) - if icon is not None: - self._layout.remove(icon) - self._box.remove(icon) + def _set_layout(self, layout): + if layout not in LAYOUT_MAP: + logging.warn('Unknown favorites layout: %r', layout) + layout = favoriteslayout.RingLayout.key + assert layout in LAYOUT_MAP - def __activity_changed_cb(self, activity_registry, activity_info): - if activity_info.get_bundle_id() == 'org.laptop.JournalActivity': - return - icon = self._find_activity_icon(activity_info.get_bundle_id(), - activity_info.get_activity_version()) - if icon is not None: - self._box.remove(icon) + if type(self._layout) == LAYOUT_MAP[layout]: + return False - registry = bundleregistry.get_registry() - if registry.is_bundle_favorite(activity_info.get_bundle_id(), - activity_info.get_activity_version()): - self._add_activity(activity_info) + self._layout = LAYOUT_MAP[layout]() + return True - def do_size_allocate(self, allocation): - width = allocation.width - height = allocation.height + layout = property(None, _set_layout) - min_w_, my_icon_width = self._my_icon.get_width_request() - min_h_, my_icon_height = self._my_icon.get_height_request( - my_icon_width) - x = (width - my_icon_width) / 2 - y = (height - my_icon_height - style.GRID_CELL_SIZE) / 2 - self._layout.move_icon(self._my_icon, x, y, locked=True) - - min_w_, icon_width = self._current_activity.get_width_request() - min_h_, icon_height = \ - self._current_activity.get_height_request(icon_width) - x = (width - icon_width) / 2 - y = (height - my_icon_height - style.GRID_CELL_SIZE) / 2 + \ - my_icon_height + style.DEFAULT_PADDING - self._layout.move_icon(self._current_activity, x, y, locked=True) - - hippo.Canvas.do_size_allocate(self, allocation) - - # TODO: Dnd methods. This should be merged somehow inside hippo-canvas. - def __button_press_event_cb(self, widget, event): - 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 is not None: - self._pressed_button = event.button - self._press_start_x = event.x - self._press_start_y = event.y + def do_add(self, child): + if child != self._owner_icon and child != self._activity_icon: + self._children.append(child) + child.connect('button-press-event', self.__button_press_cb) + child.connect('button-release-event', self.__button_release_cb) + child.connect('motion-notify-event', self.__motion_notify_event_cb) + child.connect('drag-begin', self.__drag_begin_cb) + if child.flags() & gtk.REALIZED: + child.set_parent_window(self.get_parent_window()) + child.set_parent(self) + + def __button_release_cb(self, widget, event): + if self._dragging: + return True + else: + return False + def __button_press_cb(self, widget, event): + if event.button == 1 and event.type == gtk.gdk.BUTTON_PRESS: + self._last_clicked_icon = widget + self._pressed_button = event.button + self._press_start_x = event.x + self._press_start_y = event.y return False - def _get_icon_at_coords(self, x, y): - for icon in self._box.get_children(): - icon_x, icon_y = icon.get_context().translate_to_widget(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) and \ - isinstance(icon, ActivityIcon): - return icon - return None - def __motion_notify_event_cb(self, widget, event): - if not self._pressed_button: - return False - # if the mouse button is not pressed, no drag should occurr if not event.state & gtk.gdk.BUTTON1_MASK: self._pressed_button = None @@ -242,6 +202,7 @@ def __motion_notify_event_cb(self, widget, event): int(self._press_start_y), int(x), int(y)): + self._dragging = True context_ = widget.drag_begin([_ICON_DND_TARGET], gtk.gdk.ACTION_MOVE, 1, @@ -249,9 +210,7 @@ def __motion_notify_event_cb(self, widget, event): return False def __drag_begin_cb(self, widget, context): - icon_file_name = self._last_clicked_icon.props.file_name - # TODO: we should get the pixbuf from the widget, so it has colors, etc - pixbuf = gtk.gdk.pixbuf_new_from_file(icon_file_name) + pixbuf = gtk.gdk.pixbuf_new_from_file(widget.props.file_name) self._hot_x = pixbuf.props.width / 2 self._hot_y = pixbuf.props.height / 2 @@ -267,9 +226,9 @@ def __drag_motion_cb(self, widget, context, x, y, time): def __drag_drop_cb(self, widget, context, x, y, time): if self._last_clicked_icon is not None: self.drag_get_data(context, _ICON_DND_TARGET[0]) - self._layout.move_icon(self._last_clicked_icon, - x - self._hot_x, y - self._hot_y) + x - self._hot_x, y - self._hot_y, + self.get_allocation()) self._pressed_button = None self._press_start_x = None @@ -277,6 +236,7 @@ def __drag_drop_cb(self, widget, context, x, y, time): self._hot_x = None self._hot_y = None self._last_clicked_icon = None + self._dragging = False return True else: @@ -286,49 +246,68 @@ def __drag_data_received_cb(self, widget, context, x, y, selection_data, info, time): context.drop_finish(success=True, time=time) - def _set_layout(self, layout): - if layout not in LAYOUT_MAP: - logging.warn('Unknown favorites layout: %r', layout) - layout = favoriteslayout.RingLayout.key - assert layout in LAYOUT_MAP + def __connect_to_bundle_registry_cb(self): + registry = bundleregistry.get_registry() - if type(self._layout) == LAYOUT_MAP[layout]: - return + for info in registry: + if registry.is_bundle_favorite(info.get_bundle_id(), + info.get_activity_version()): + self._add_activity(info) - self._layout = LAYOUT_MAP[layout]() - self._box.set_layout(self._layout) + registry.connect('bundle-added', self.__activity_added_cb) + registry.connect('bundle-removed', self.__activity_removed_cb) + registry.connect('bundle-changed', self.__activity_changed_cb) - #TODO: compatibility hack while sort() gets added to the hippo python - # bindings - if hasattr(self._box, 'sort'): - self._box.sort(self._layout.compare_activities) + def _add_activity(self, activity_info): + if activity_info.get_bundle_id() == 'org.laptop.JournalActivity': + return + icon = ActivityIcon(activity_info) + icon.props.pixel_size = style.STANDARD_ICON_SIZE + #icon.set_resume_mode(self._resume_mode) + self.add(icon) + icon.show() - for icon in self._box.get_children(): - if icon not in [self._my_icon, self._current_activity]: - self._layout.append(icon) + def __activity_added_cb(self, activity_registry, activity_info): + registry = bundleregistry.get_registry() + if registry.is_bundle_favorite(activity_info.get_bundle_id(), + activity_info.get_activity_version()): + self._add_activity(activity_info) - self._layout.append(self._my_icon, locked=True) - self._layout.append(self._current_activity, locked=True) + def __activity_removed_cb(self, activity_registry, activity_info): + icon = self._find_activity_icon(activity_info.get_bundle_id(), + activity_info.get_activity_version()) + if icon is not None: + self.remove(icon) - if self._layout.allow_dnd(): - self.drag_source_set(0, [], 0) - self.drag_dest_set(0, [], 0) - else: - self.drag_source_unset() - self.drag_dest_unset() + def _find_activity_icon(self, bundle_id, version): + for icon in self.get_children(): + if isinstance(icon, ActivityIcon) and \ + icon.bundle_id == bundle_id and icon.version == version: + return icon + return None - layout = property(None, _set_layout) + def __activity_changed_cb(self, activity_registry, activity_info): + if activity_info.get_bundle_id() == 'org.laptop.JournalActivity': + return + icon = self._find_activity_icon(activity_info.get_bundle_id(), + activity_info.get_activity_version()) + if icon is not None: + self.remove(icon) - def add_alert(self, alert): - if self._alert is not None: - self.remove_alert() - alert.set_size_request(gtk.gdk.screen_width(), -1) - self._alert = hippo.CanvasWidget(widget=alert) - self._box.append(self._alert, hippo.PACK_FIXED) + registry = bundleregistry.get_registry() + if registry.is_bundle_favorite(activity_info.get_bundle_id(), + activity_info.get_activity_version()): + self._add_activity(activity_info) - def remove_alert(self): - self._box.remove(self._alert) - self._alert = None + def set_filter(self, query): + query = query.strip() + for icon in self.get_children(): + if icon not in [self._owner_icon, self._activity_icon]: + activity_name = icon.get_activity_name().lower() + if activity_name.find(query) > -1: + icon.alpha = 1.0 + else: + icon.alpha = 0.33 def __register_activate_cb(self, icon): alert = Alert() @@ -341,41 +320,43 @@ def __register_activate_cb(self, icon): alert.props.title = _('Registration Successful') alert.props.msg = _('You are now registered ' \ 'with your school server.') - self._my_icon.set_registered() + self._owner_icon.set_registered() ok_icon = Icon(icon_name='dialog-ok') alert.add_button(gtk.RESPONSE_OK, _('Ok'), ok_icon) - self.add_alert(alert) + self._box.add_alert(alert) alert.connect('response', self.__register_alert_response_cb) def __register_alert_response_cb(self, alert, response_id): - self.remove_alert() + self._box.remove_alert() def set_resume_mode(self, resume_mode): self._resume_mode = resume_mode - for icon in self._box.get_children(): + for icon in self.get_children(): if hasattr(icon, 'set_resume_mode'): icon.set_resume_mode(self._resume_mode) -class ActivityIcon(CanvasIcon): +class ActivityIcon(EventIcon): __gtype_name__ = 'SugarFavoriteActivityIcon' _BORDER_WIDTH = style.zoom(3) _MAX_RESUME_ENTRIES = 5 def __init__(self, activity_info): - CanvasIcon.__init__(self, cache=True, - file_name=activity_info.get_icon()) + EventIcon.__init__(self, cache=True, + file_name=activity_info.get_icon()) self._activity_info = activity_info self._journal_entries = [] self._hovering = False self._resume_mode = True - self.connect('hovering-changed', self.__hovering_changed_event_cb) - self.connect('button-release-event', self.__button_release_event_cb) + self.connect('enter-notify-event', self.__enter_notify_event_cb) + self.connect('leave-notify-event', self.__leave_notify_event_cb) + self.connect_after('button-release-event', + self.__button_release_event_cb) datastore.updated.connect(self.__datastore_listener_updated_cb) datastore.deleted.connect(self.__datastore_listener_deleted_cb) @@ -443,45 +424,47 @@ def __palette_activate_cb(self, palette): def __palette_entry_activate_cb(self, palette, metadata): self._resume(metadata) - def __hovering_changed_event_cb(self, icon, hovering): - self._hovering = hovering - self.emit_paint_needed(0, 0, -1, -1) + def __enter_notify_event_cb(self, icon, event): + self._hovering = True + self.queue_draw() + + def __leave_notify_event_cb(self, icon, event): + self._hovering = False + self.queue_draw() + + def do_expose_event(self, event): + EventIcon.do_expose_event(self, event) - def do_paint_above_children(self, cr, damaged_box): if not self._hovering: return - width, height = self.get_allocation() + allocation = self.get_allocation() + width = allocation.width + height = allocation.height - x = ActivityIcon._BORDER_WIDTH / 2.0 - y = ActivityIcon._BORDER_WIDTH / 2.0 + x = allocation.x + ActivityIcon._BORDER_WIDTH / 2.0 + y = allocation.y + ActivityIcon._BORDER_WIDTH / 2.0 width -= ActivityIcon._BORDER_WIDTH height -= ActivityIcon._BORDER_WIDTH radius = width / 10.0 + cr = self.window.cairo_create() cr.move_to(x + radius, y) cr.arc(x + width - radius, y + radius, radius, math.pi * 1.5, math.pi * 2.0) - cr.arc(x + width - radius, x + height - radius, radius, 0, + cr.arc(x + width - radius, y + height - radius, radius, 0, math.pi * 0.5) cr.arc(x + radius, y + height - radius, radius, math.pi * 0.5, math.pi) cr.arc(x + radius, y + radius, radius, math.pi, math.pi * 1.5) - color = style.COLOR_SELECTION_GREY.get_int() - hippo.cairo_set_source_rgba32(cr, color) + cr.set_source_color(style.COLOR_SELECTION_GREY.get_gdk_color()) cr.set_line_width(ActivityIcon._BORDER_WIDTH) cr.stroke() - def do_get_content_height_request(self, for_width): - height, height = CanvasIcon.do_get_content_height_request(self, - for_width) - height += ActivityIcon._BORDER_WIDTH * 2 - return height, height - - def do_get_content_width_request(self): - width, width = CanvasIcon.do_get_content_width_request(self) - width += ActivityIcon._BORDER_WIDTH * 2 - return width, width + def do_size_request(self, req): + EventIcon.do_size_request(self, req) + req.height += ActivityIcon._BORDER_WIDTH * 2 + req.width += ActivityIcon._BORDER_WIDTH * 2 def __button_release_event_cb(self, icon, event): self._activate() @@ -575,9 +558,10 @@ def __resume_entry_cb(self, menu_item, entry): self.emit('entry-activate', entry) -class CurrentActivityIcon(CanvasIcon, hippo.CanvasItem): +class CurrentActivityIcon(EventIcon): def __init__(self): - CanvasIcon.__init__(self, cache=True) + EventIcon.__init__(self, icon_name='activity-journal', + pixel_size=style.STANDARD_ICON_SIZE, cache=True) self._home_model = shell.get_model() self._home_activity = self._home_model.get_active_activity() @@ -587,7 +571,8 @@ def __init__(self): self._home_model.connect('active-activity-changed', self.__active_activity_changed_cb) - self.connect('button-release-event', self.__button_release_event_cb) + self.connect_after('button-release-event', + self.__button_release_event_cb) def __button_release_event_cb(self, icon, event): window = self._home_model.get_active_activity().get_window() @@ -596,7 +581,7 @@ def __button_release_event_cb(self, icon, event): def _update(self): self.props.file_name = self._home_activity.get_icon_path() self.props.xo_color = self._home_activity.get_icon_color() - self.props.size = style.STANDARD_ICON_SIZE + self.props.pixel_size = style.STANDARD_ICON_SIZE if self.palette is not None: self.palette.destroy() @@ -623,7 +608,7 @@ class OwnerIcon(BuddyIcon): } def __init__(self, size): - BuddyIcon.__init__(self, buddy=get_owner_instance(), size=size) + BuddyIcon.__init__(self, buddy=get_owner_instance(), pixel_size=size) self.palette_invoker.cache_palette = True @@ -652,9 +637,6 @@ def create_palette(self): return palette - def get_toplevel(self): - return hippo.get_canvas_for_item(self).get_toplevel() - def __register_activate_cb(self, menuitem): self.emit('register-activate') diff --git a/src/jarabe/desktop/friendview.py b/src/jarabe/desktop/friendview.py index 8dab35f519..01c2b71202 100644 --- a/src/jarabe/desktop/friendview.py +++ b/src/jarabe/desktop/friendview.py @@ -15,27 +15,30 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -import hippo +import gtk -from sugar.graphics.icon import CanvasIcon from sugar.graphics import style from jarabe.view.buddyicon import BuddyIcon +from jarabe.view.eventicon import EventIcon from jarabe.model import bundleregistry -class FriendView(hippo.CanvasBox): +class FriendView(gtk.VBox): def __init__(self, buddy, **kwargs): - hippo.CanvasBox.__init__(self, **kwargs) + gtk.VBox.__init__(self) + + # round icon sizes to an even number so that it can be accurately + # centered in a larger bounding box also of even dimensions + size = style.LARGE_ICON_SIZE & ~1 self._buddy = buddy self._buddy_icon = BuddyIcon(buddy) - self._buddy_icon.props.size = style.LARGE_ICON_SIZE - self.append(self._buddy_icon) - - self._activity_icon = CanvasIcon(size=style.LARGE_ICON_SIZE) - self._activity_icon_visible = False + self._buddy_icon.props.pixel_size = size + self.add(self._buddy_icon) + self._buddy_icon.show() + self._activity_icon = EventIcon(pixel_size=size) self._update_activity() self._buddy.connect('notify::current-activity', @@ -51,9 +54,9 @@ def _get_new_icon_name(self, ps_activity): return None def _remove_activity_icon(self): - if self._activity_icon_visible: + if self._activity_icon.get_visible(): + self._activity_icon.hide() self.remove(self._activity_icon) - self._activity_icon_visible = False def __buddy_notify_current_activity_cb(self, buddy, pspec): self._update_activity() @@ -70,9 +73,9 @@ def _update_activity(self): if name: self._activity_icon.props.file_name = name self._activity_icon.props.xo_color = self._buddy.props.color - if not self._activity_icon_visible: - self.append(self._activity_icon, hippo.PACK_EXPAND) - self._activity_icon_visible = True + if not self._activity_icon.get_visible(): + self.add(self._activity_icon) + self._activity_icon.show() else: self._remove_activity_icon() diff --git a/src/jarabe/desktop/grid.py b/src/jarabe/desktop/grid.py index eab403335c..65b33b230a 100644 --- a/src/jarabe/desktop/grid.py +++ b/src/jarabe/desktop/grid.py @@ -75,6 +75,9 @@ def add(self, child, width, height, x=None, y=None, locked=False): if weight > 0: self._detect_collisions(child) + def is_in_grid(self, child): + return child in self._children + def remove(self, child): self._children.remove(child) self.remove_weight(self._child_rects[child]) diff --git a/src/jarabe/desktop/groupbox.py b/src/jarabe/desktop/groupbox.py index ed8f8ae79a..4fcd6c22ba 100644 --- a/src/jarabe/desktop/groupbox.py +++ b/src/jarabe/desktop/groupbox.py @@ -16,47 +16,40 @@ import logging -import gobject -import hippo import gconf from sugar.graphics import style -from sugar.graphics.icon import CanvasIcon from sugar.graphics.xocolor import XoColor from jarabe.view.buddymenu import BuddyMenu +from jarabe.view.eventicon import EventIcon from jarabe.model.buddy import get_owner_instance from jarabe.model import friends from jarabe.desktop.friendview import FriendView -from jarabe.desktop.spreadlayout import SpreadLayout +from jarabe.desktop.viewcontainer import ViewContainer +from jarabe.desktop.favoriteslayout import SpreadLayout -class GroupBox(hippo.Canvas): +class GroupBox(ViewContainer): __gtype_name__ = 'SugarGroupBox' def __init__(self): logging.debug('STARTUP: Loading the group view') - gobject.GObject.__init__(self) - - self._box = hippo.CanvasBox() - self._box.props.background_color = style.COLOR_WHITE.get_int() - self.set_root(self._box) - - self._friends = {} - - self._layout = SpreadLayout() - self._box.set_layout(self._layout) + layout = SpreadLayout() client = gconf.client_get_default() color = XoColor(client.get_string('/desktop/sugar/user/color')) + owner_icon = EventIcon(icon_name='computer-xo', cache=True, + xo_color=color) + # Round off icon size to an even number to ensure that the icon + # is placed evenly in the grid + owner_icon.props.pixel_size = style.LARGE_ICON_SIZE & ~1 + owner_icon.set_palette(BuddyMenu(get_owner_instance())) - self._owner_icon = CanvasIcon(icon_name='computer-xo', cache=True, - xo_color=color) - self._owner_icon.props.size = style.LARGE_ICON_SIZE + ViewContainer.__init__(self, layout, owner_icon) - self._owner_icon.set_palette(BuddyMenu(get_owner_instance())) - self._layout.add(self._owner_icon) + self._friends = {} friends_model = friends.get_model() @@ -68,27 +61,15 @@ def __init__(self): def add_friend(self, buddy_info): icon = FriendView(buddy_info) - self._layout.add(icon) - + self.add(icon) self._friends[buddy_info.get_key()] = icon + icon.show() def _friend_added_cb(self, data_model, buddy_info): self.add_friend(buddy_info) def _friend_removed_cb(self, data_model, key): icon = self._friends[key] - self._layout.remove(icon) + self.remove(icon) del self._friends[key] icon.destroy() - - def do_size_allocate(self, allocation): - width = allocation.width - height = allocation.height - - min_w_, icon_width = self._owner_icon.get_width_request() - min_h_, icon_height = self._owner_icon.get_height_request(icon_width) - x = (width - icon_width) / 2 - y = (height - icon_height) / 2 - self._layout.move(self._owner_icon, x, y) - - hippo.Canvas.do_size_allocate(self, allocation) diff --git a/src/jarabe/desktop/homebox.py b/src/jarabe/desktop/homebox.py index 2ee6ae7d30..33c69658d7 100644 --- a/src/jarabe/desktop/homebox.py +++ b/src/jarabe/desktop/homebox.py @@ -45,7 +45,7 @@ def __init__(self): gobject.GObject.__init__(self) - self._favorites_view = favoritesview.FavoritesView() + self._favorites_box = favoritesview.FavoritesBox() self._list_view = ActivitiesList() self._toolbar = HomeToolbar() @@ -78,14 +78,14 @@ def show_software_updates_alert(self): if self._list_view in self.get_children(): self._list_view.add_alert(alert) else: - self._favorites_view.add_alert(alert) + self._favorites_box.add_alert(alert) alert.connect('response', self.__software_update_response_cb) def __software_update_response_cb(self, alert, response_id): if self._list_view in self.get_children(): self._list_view.remove_alert() else: - self._favorites_view.remove_alert() + self._favorites_box.remove_alert() if response_id != gtk.RESPONSE_REJECT: update_trigger_file = os.path.expanduser('~/.sugar-update') @@ -106,7 +106,7 @@ def __software_update_response_cb(self, alert, response_id): def __toolbar_query_changed_cb(self, toolbar, query): self._query = query.lower() self._list_view.set_filter(self._query) - self._favorites_view.set_filter(self._query) + self._favorites_box.set_filter(self._query) def __toolbar_view_changed_cb(self, toolbar, view): self._set_view(view) @@ -116,12 +116,12 @@ def _set_view(self, view): if self._list_view in self.get_children(): self.remove(self._list_view) - if self._favorites_view not in self.get_children(): - self.add(self._favorites_view) - self._favorites_view.show() + if self._favorites_box not in self.get_children(): + self.add(self._favorites_box) + self._favorites_box.show() elif view == _LIST_VIEW: - if self._favorites_view in self.get_children(): - self.remove(self._favorites_view) + if self._favorites_box in self.get_children(): + self.remove(self._favorites_box) if self._list_view not in self.get_children(): self.add(self._list_view) @@ -146,10 +146,10 @@ def focus_search_entry(self): self._toolbar.search_entry.grab_focus() def set_resume_mode(self, resume_mode): - self._favorites_view.set_resume_mode(resume_mode) + self._favorites_box.set_resume_mode(resume_mode) if resume_mode and self._query != '': self._list_view.set_filter(self._query) - self._favorites_view.set_filter(self._query) + self._favorites_box.set_filter(self._query) class HomeToolbar(gtk.Toolbar): diff --git a/src/jarabe/desktop/homewindow.py b/src/jarabe/desktop/homewindow.py index 07deff7937..a5536c0e4e 100644 --- a/src/jarabe/desktop/homewindow.py +++ b/src/jarabe/desktop/homewindow.py @@ -57,6 +57,8 @@ def __init__(self): self.realize() self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DESKTOP) + self.modify_bg(gtk.STATE_NORMAL, + style.COLOR_WHITE.get_gdk_color()) self.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) self.connect('visibility-notify-event', diff --git a/src/jarabe/desktop/meshbox.py b/src/jarabe/desktop/meshbox.py index 20dc4138d2..1de37796df 100644 --- a/src/jarabe/desktop/meshbox.py +++ b/src/jarabe/desktop/meshbox.py @@ -21,26 +21,29 @@ import logging import dbus -import hippo import glib import gobject import gtk import gconf -from sugar.graphics.icon import CanvasIcon, Icon +from sugar.graphics.icon import Icon from sugar.graphics import style from sugar.graphics import palette from sugar.graphics import iconentry from sugar.graphics.menuitem import MenuItem +from sugar.graphics.xocolor import XoColor +from jarabe.desktop.snowflakelayout import SnowflakeLayout from jarabe.model import neighborhood from jarabe.model.buddy import get_owner_instance from jarabe.view.buddyicon import BuddyIcon -from jarabe.desktop.snowflakelayout import SnowflakeLayout -from jarabe.desktop.spreadlayout import SpreadLayout +from jarabe.view.buddymenu import BuddyMenu +from jarabe.view.eventicon import EventIcon from jarabe.desktop.networkviews import WirelessNetworkView from jarabe.desktop.networkviews import OlpcMeshView from jarabe.desktop.networkviews import SugarAdhocView +from jarabe.desktop.viewcontainer import ViewContainer +from jarabe.desktop.favoriteslayout import SpreadLayout from jarabe.model import network from jarabe.model.network import AccessPoint from jarabe.model.olpcmesh import OlpcMeshManager @@ -55,14 +58,13 @@ _FILTERED_ALPHA = 0.33 -class _ActivityIcon(CanvasIcon): +class _ActivityIcon(EventIcon): def __init__(self, model, file_name, xo_color, size=style.STANDARD_ICON_SIZE): - CanvasIcon.__init__(self, file_name=file_name, - xo_color=xo_color, - size=size) + EventIcon.__init__(self, file_name=file_name, + xo_color=xo_color, pixel_size=size) self._model = model - self.connect('activated', self._clicked_cb) + self.connect('button-release-event', self._button_release_cb) def create_palette(self): primary_text = glib.markup_escape_text(self._model.bundle.get_name()) @@ -91,15 +93,18 @@ def create_palette(self): return p + def _button_release_cb(self, widget, event): + return self._clicked_cb(item=None) + def _clicked_cb(self, item): bundle = self._model.get_bundle() misc.launch(bundle, activity_id=self._model.activity_id, color=self._model.get_color()) -class ActivityView(hippo.CanvasBox): +class ActivityView(SnowflakeLayout): def __init__(self, model): - hippo.CanvasBox.__init__(self) + SnowflakeLayout.__init__(self) self._model = model self._model.connect('current-buddy-added', self.__buddy_added_cb) @@ -107,11 +112,9 @@ def __init__(self, model): self._icons = {} - self._layout = SnowflakeLayout() - self.set_layout(self._layout) - self._icon = self._create_icon() - self._layout.add(self._icon, center=True) + self._icon.show() + self.add_icon(self._icon, center=True) self._icon.palette_invoker.cache_palette = False @@ -134,11 +137,13 @@ def __buddy_added_cb(self, activity, buddy): def _add_buddy(self, buddy): icon = BuddyIcon(buddy, style.STANDARD_ICON_SIZE) self._icons[buddy.props.key] = icon - self._layout.add(icon) + self.add_icon(icon) + icon.show() def __buddy_removed_cb(self, activity, buddy): icon = self._icons[buddy.props.key] del self._icons[buddy.props.key] + self.remove(icon) icon.destroy() def set_filter(self, query): @@ -401,13 +406,32 @@ def __properties_changed_cb(self, properties): self._box.add_adhoc_networks(device) +class MeshContainer(ViewContainer): + __gtype_name__ = 'SugarMeshContainer' + + def __init__(self): + + layout = SpreadLayout() + + client = gconf.client_get_default() + color = XoColor(client.get_string('/desktop/sugar/user/color')) + owner_icon = EventIcon(icon_name='computer-xo', cache=True, + xo_color=color) + # Round off icon size to an even number to ensure that the icon + # is placed evenly in the grid + owner_icon.props.pixel_size = style.STANDARD_ICON_SIZE & ~1 + owner_icon.set_palette(BuddyMenu(get_owner_instance())) + + ViewContainer.__init__(self, layout, owner_icon) + + class MeshBox(gtk.VBox): __gtype_name__ = 'SugarMeshBox' def __init__(self): logging.debug('STARTUP: Loading the mesh view') - gobject.GObject.__init__(self) + gtk.VBox.__init__(self) self.wireless_networks = {} self._adhoc_manager = None @@ -420,23 +444,15 @@ def __init__(self): self._buddy_to_activity = {} self._suspended = True self._query = '' - self._owner_icon = None self._toolbar = MeshToolbar() self._toolbar.connect('query-changed', self._toolbar_query_changed_cb) self.pack_start(self._toolbar, expand=False) self._toolbar.show() - canvas = hippo.Canvas() - self.add(canvas) - canvas.show() - - self._layout_box = hippo.CanvasBox( \ - background_color=style.COLOR_WHITE.get_int()) - canvas.set_root(self._layout_box) - - self._layout = SpreadLayout() - self._layout_box.set_layout(self._layout) + self._mesh_container = MeshContainer() + self.add(self._mesh_container) + self._mesh_container.show() for buddy_model in self._model.get_buddies(): self._add_buddy(buddy_model) @@ -453,18 +469,6 @@ def __init__(self): netmgr_observer = NetworkManagerObserver(self) netmgr_observer.listen() - def do_size_allocate(self, allocation): - width = allocation.width - height = allocation.height - - min_w_, icon_width = self._owner_icon.get_width_request() - min_h_, icon_height = self._owner_icon.get_height_request(icon_width) - x = (width - icon_width) / 2 - y = (height - icon_height) / 2 - style.GRID_CELL_SIZE - self._layout.move(self._owner_icon, x, y) - - gtk.VBox.do_size_allocate(self, allocation) - def _buddy_added_cb(self, model, buddy_model): self._add_buddy(buddy_model) @@ -482,10 +486,11 @@ def _add_buddy(self, buddy_model): self.__buddy_notify_current_activity_cb) if buddy_model.props.current_activity is not None: return - icon = BuddyIcon(buddy_model) if buddy_model.is_owner(): - self._owner_icon = icon - self._layout.add(icon) + return + icon = BuddyIcon(buddy_model) + self._mesh_container.add(icon) + icon.show() if hasattr(icon, 'set_filter'): icon.set_filter(self._query) @@ -495,9 +500,8 @@ def _add_buddy(self, buddy_model): def _remove_buddy(self, buddy_model): logging.debug('MeshBox._remove_buddy') icon = self._buddies[buddy_model.props.key] - self._layout.remove(icon) + self._mesh_container.remove(icon) del self._buddies[buddy_model.props.key] - icon.destroy() def __buddy_notify_current_activity_cb(self, buddy_model, pspec): logging.debug('MeshBox.__buddy_notify_current_activity_cb %s', @@ -510,7 +514,8 @@ def __buddy_notify_current_activity_cb(self, buddy_model, pspec): def _add_activity(self, activity_model): icon = ActivityView(activity_model) - self._layout.add(icon) + self._mesh_container.add(icon) + icon.show() if hasattr(icon, 'set_filter'): icon.set_filter(self._query) @@ -519,9 +524,8 @@ def _add_activity(self, activity_model): def _remove_activity(self, activity_model): icon = self._activities[activity_model.activity_id] - self._layout.remove(icon) + self._mesh_container.remove(icon) del self._activities[activity_model.activity_id] - icon.destroy() # add AP to its corresponding network icon on the desktop, # creating one if it doesn't already exist @@ -533,7 +537,8 @@ def _add_ap_to_network(self, ap): # this is a new network icon = WirelessNetworkView(ap) self.wireless_networks[hash_value] = icon - self._layout.add(icon) + self._mesh_container.add(icon) + icon.show() if hasattr(icon, 'set_filter'): icon.set_filter(self._query) @@ -541,7 +546,7 @@ def _remove_net_if_empty(self, net, hash_value): # remove a network if it has no APs left if net.num_aps() == 0: net.disconnect() - self._layout.remove(net) + self._mesh_container.remove(net) del self.wireless_networks[hash_value] def _ap_props_changed_cb(self, ap, old_hash_value): @@ -619,18 +624,20 @@ def add_adhoc_networks(self, device): def remove_adhoc_networks(self): for icon in self._adhoc_networks: - self._layout.remove(icon) + self._mesh_container.remove(icon) self._adhoc_networks = [] self._adhoc_manager.stop_listening() def _add_adhoc_network_icon(self, channel): icon = SugarAdhocView(channel) - self._layout.add(icon) + self._mesh_container.add(icon) + icon.show() self._adhoc_networks.append(icon) def _add_olpc_mesh_icon(self, mesh_mgr, channel): icon = OlpcMeshView(mesh_mgr, channel) - self._layout.add(icon) + self._mesh_container.add(icon) + icon.show() self._mesh.append(icon) def enable_olpc_mesh(self, mesh_device): @@ -648,13 +655,13 @@ def enable_olpc_mesh(self, mesh_device): logging.debug('removing OLPC mesh IBSS') net.remove_all_aps() net.disconnect() - self._layout.remove(net) + self._mesh_container.remove(net) del self.wireless_networks[hash_value] def disable_olpc_mesh(self, mesh_device): for icon in self._mesh: icon.disconnect() - self._layout.remove(icon) + self._mesh_container.remove(icon) self._mesh = [] def suspend(self): @@ -671,7 +678,7 @@ def resume(self): def _toolbar_query_changed_cb(self, toolbar, query): self._query = query.lower() - for icon in self._layout_box.get_children(): + for icon in self._mesh_container.get_children(): if hasattr(icon, 'set_filter'): icon.set_filter(self._query) diff --git a/src/jarabe/desktop/networkviews.py b/src/jarabe/desktop/networkviews.py index f42bfed839..d2531bf567 100644 --- a/src/jarabe/desktop/networkviews.py +++ b/src/jarabe/desktop/networkviews.py @@ -33,7 +33,7 @@ from sugar.util import unique_id from sugar import profile -from jarabe.view.pulsingicon import CanvasPulsingIcon +from jarabe.view.pulsingicon import EventPulsingIcon from jarabe.desktop import keydialog from jarabe.model import network from jarabe.model.network import Settings @@ -48,10 +48,10 @@ _FILTERED_ALPHA = 0.33 -class WirelessNetworkView(CanvasPulsingIcon): +class WirelessNetworkView(EventPulsingIcon): def __init__(self, initial_ap): - CanvasPulsingIcon.__init__(self, size=style.STANDARD_ICON_SIZE, - cache=True) + EventPulsingIcon.__init__(self, pixel_size=style.STANDARD_ICON_SIZE, + cache=True) self._bus = dbus.SystemBus() self._access_points = {initial_ap.model.object_path: initial_ap} self._active_ap = None @@ -255,9 +255,9 @@ def _update_color(self): self.props.base_color = self._color if self._filtered: self.props.pulsing = False - self.alpha = _FILTERED_ALPHA + self.props.alpha = _FILTERED_ALPHA else: - self.alpha = 1.0 + self.props.alpha = 1.0 def _disconnect_activate_cb(self, item): ap_paths = self._access_points.keys() @@ -436,7 +436,7 @@ def disconnect(self): dbus_interface=network.NM_WIRELESS_IFACE) -class SugarAdhocView(CanvasPulsingIcon): +class SugarAdhocView(EventPulsingIcon): """To mimic the mesh behavior on devices where mesh hardware is not available we support the creation of an Ad-hoc network on three channels 1, 6, 11. This is the class for an icon @@ -448,9 +448,10 @@ class SugarAdhocView(CanvasPulsingIcon): _NAME = 'Ad-hoc Network ' def __init__(self, channel): - CanvasPulsingIcon.__init__(self, - icon_name=self._ICON_NAME + str(channel), - size=style.STANDARD_ICON_SIZE, cache=True) + EventPulsingIcon.__init__(self, + icon_name=self._ICON_NAME + str(channel), + pixel_size=style.STANDARD_ICON_SIZE, + cache=True) self._bus = dbus.SystemBus() self._channel = channel self._disconnect_item = None @@ -572,10 +573,11 @@ def set_filter(self, query): self._update_color() -class OlpcMeshView(CanvasPulsingIcon): +class OlpcMeshView(EventPulsingIcon): def __init__(self, mesh_mgr, channel): - CanvasPulsingIcon.__init__(self, icon_name=_OLPC_MESH_ICON_NAME, - size=style.STANDARD_ICON_SIZE, cache=True) + EventPulsingIcon.__init__(self, icon_name=_OLPC_MESH_ICON_NAME, + pixel_size=style.STANDARD_ICON_SIZE, + cache=True) self._bus = dbus.SystemBus() self._channel = channel self._mesh_mgr = mesh_mgr diff --git a/src/jarabe/desktop/snowflakelayout.py b/src/jarabe/desktop/snowflakelayout.py index e4963ba9f8..25cae76586 100644 --- a/src/jarabe/desktop/snowflakelayout.py +++ b/src/jarabe/desktop/snowflakelayout.py @@ -16,8 +16,7 @@ import math -import gobject -import hippo +import gtk from sugar.graphics import style @@ -26,54 +25,71 @@ _CHILDREN_FACTOR = style.zoom(3) -class SnowflakeLayout(gobject.GObject, hippo.CanvasLayout): +class SnowflakeLayout(gtk.Container): __gtype_name__ = 'SugarSnowflakeLayout' def __init__(self): - gobject.GObject.__init__(self) + gtk.Container.__init__(self) + self.set_has_window(False) self._nflakes = 0 - self._box = None - - def add(self, child, center=False): + self._children = {} + + def do_realize(self): + # FIXME what is this for? + self.set_flags(gtk.REALIZED) + self.set_window(self.get_parent_window()) + self.style.attach(self.window) + for child in self._children.keys(): + child.set_parent_window(self.get_parent_window()) + self.queue_resize() + + def do_add(self, child): + if child.flags() & gtk.REALIZED: + child.set_parent_window(self.get_parent_window()) + child.set_parent(self) + + def do_forall(self, include_internals, callback, data): + for child in self._children.keys(): + callback(child, data) + + def do_remove(self, child): + child.unparent() + + def add_icon(self, child, center=False): if not center: self._nflakes += 1 - self._box.append(child) - - box_child = self._box.find_box_child(child) - box_child.is_center = center + self._children[child] = center + self.add(child) def remove(self, child): - box_child = self._box.find_box_child(child) - if not box_child.is_center: - self._nflakes -= 1 - - self._box.remove(child) + if not child in self._children: + return - def do_set_box(self, box): - self._box = box + if not self._children[child]: # not centered + self._nflakes -= 1 - def do_get_height_request(self, for_width): - size = self._calculate_size() - return (size, size) + del self._children[child] + self.remove(child) - def do_get_width_request(self): + def do_size_request(self, requisition): size = self._calculate_size() - return (size, size) + requisition.width = size + requisition.height = size - def do_allocate(self, x, y, width, height, - req_width, req_height, origin_changed): + def do_size_allocate(self, allocation): r = self._get_radius() index = 0 - for child in self._box.get_layout_children(): - min_width, child_width = child.get_width_request() - min_height, child_height = child.get_height_request(child_width) + for child, centered in self._children.items(): + child_width, child_height = child.size_request() + rect = gtk.gdk.Rectangle(0, 0, child_width, child_height) - if child.is_center: - child.allocate(x + (width - child_width) / 2, - y + (height - child_height) / 2, - child_width, child_height, origin_changed) + width = allocation.width - child_width + height = allocation.height - child_height + if centered: + rect.x = allocation.x + width / 2 + rect.y = allocation.y + height / 2 else: angle = 2 * math.pi * index / self._nflakes @@ -83,29 +99,26 @@ def do_allocate(self, x, y, width, height, dx = math.cos(angle) * r dy = math.sin(angle) * r - child_x = int(x + (width - child_width) / 2 + dx) - child_y = int(y + (height - child_height) / 2 + dy) - - child.allocate(child_x, child_y, child_width, - child_height, origin_changed) + rect.x = int(allocation.x + width / 2 + dx) + rect.y = int(allocation.y + height / 2 + dy) index += 1 + child.size_allocate(rect) + def _get_radius(self): radius = int(_BASE_DISTANCE + _CHILDREN_FACTOR * self._nflakes) - for child in self._box.get_layout_children(): - if child.is_center: - [min_w, child_w] = child.get_width_request() - [min_h, child_h] = child.get_height_request(child_w) + for child, centered in self._children.items(): + if centered: + child_w, child_h = child.size_request() radius += max(child_w, child_h) / 2 return radius def _calculate_size(self): thickness = 0 - for child in self._box.get_layout_children(): - [min_width, child_width] = child.get_width_request() - [min_height, child_height] = child.get_height_request(child_width) - thickness = max(thickness, max(child_width, child_height)) + for child in self._children.keys(): + width, height = child.size_request() + thickness = max(thickness, max(width, height)) return self._get_radius() * 2 + thickness diff --git a/src/jarabe/desktop/spreadlayout.py b/src/jarabe/desktop/spreadlayout.py deleted file mode 100644 index b5c623e492..0000000000 --- a/src/jarabe/desktop/spreadlayout.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (C) 2007 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -import math - -import hippo -import gobject -import gtk - -from sugar.graphics import style - -from jarabe.desktop.grid import Grid - - -_CELL_SIZE = 4.0 - - -class SpreadLayout(gobject.GObject, hippo.CanvasLayout): - __gtype_name__ = 'SugarSpreadLayout' - - def __init__(self): - gobject.GObject.__init__(self) - self._box = None - - min_width, width = self.do_get_width_request() - min_height, height = self.do_get_height_request(width) - - self._grid = Grid(int(width / _CELL_SIZE), int(height / _CELL_SIZE)) - self._grid.connect('child-changed', self._grid_child_changed_cb) - - def add(self, child): - self._box.append(child) - - width, height = self._get_child_grid_size(child) - self._grid.add(child, width, height) - - def remove(self, child): - self._grid.remove(child) - self._box.remove(child) - - def move(self, child, x, y): - self._grid.move(child, x / _CELL_SIZE, y / _CELL_SIZE, locked=True) - - def do_set_box(self, box): - self._box = box - - def do_get_height_request(self, for_width): - return 0, gtk.gdk.screen_height() - style.GRID_CELL_SIZE - - def do_get_width_request(self): - return 0, gtk.gdk.screen_width() - - def do_allocate(self, x, y, width, height, - req_width, req_height, origin_changed): - for child in self._box.get_layout_children(): - # We need to always get requests to not confuse hippo - min_w, child_width = child.get_width_request() - min_h, child_height = child.get_height_request(child_width) - - rect = self._grid.get_child_rect(child.item) - child.allocate(int(round(rect.x * _CELL_SIZE)), - int(round(rect.y * _CELL_SIZE)), - child_width, - child_height, - origin_changed) - - def _get_child_grid_size(self, child): - min_width, width = child.get_width_request() - min_height, height = child.get_height_request(width) - width = math.ceil(width / _CELL_SIZE) - height = math.ceil(height / _CELL_SIZE) - - return int(width), int(height) - - def _grid_child_changed_cb(self, grid, child): - child.emit_request_changed() diff --git a/src/jarabe/desktop/transitionbox.py b/src/jarabe/desktop/transitionbox.py index fd2112c90c..54a70de088 100644 --- a/src/jarabe/desktop/transitionbox.py +++ b/src/jarabe/desktop/transitionbox.py @@ -14,7 +14,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -import hippo import gobject from sugar.graphics import style @@ -34,37 +33,10 @@ def __init__(self, icon, start_size, end_size): def next_frame(self, current): d = (self.end_size - self.start_size) * current - self._icon.props.size = int(self.start_size + d) + self._icon.props.pixel_size = int(self.start_size + d) -class _Layout(gobject.GObject, hippo.CanvasLayout): - __gtype_name__ = 'SugarTransitionBoxLayout' - - def __init__(self): - gobject.GObject.__init__(self) - self._box = None - - def do_set_box(self, box): - self._box = box - - def do_get_height_request(self, for_width): - return 0, 0 - - def do_get_width_request(self): - return 0, 0 - - def do_allocate(self, x, y, width, height, - req_width, req_height, origin_changed): - for child in self._box.get_layout_children(): - min_width, child_width = child.get_width_request() - min_height, child_height = child.get_height_request(child_width) - - child.allocate(x + (width - child_width) / 2, - y + (height - child_height) / 2, - child_width, child_height, origin_changed) - - -class TransitionBox(hippo.Canvas): +class TransitionBox(BuddyIcon): __gtype_name__ = 'SugarTransitionBox' __gsignals__ = { @@ -72,18 +44,8 @@ class TransitionBox(hippo.Canvas): } def __init__(self): - gobject.GObject.__init__(self) - - self._box = hippo.CanvasBox() - self._box.props.background_color = style.COLOR_WHITE.get_int() - self.set_root(self._box) - - self._layout = _Layout() - self._box.set_layout(self._layout) - - self._my_icon = BuddyIcon(buddy=get_owner_instance(), - size=style.XLARGE_ICON_SIZE) - self._box.append(self._my_icon) + BuddyIcon.__init__(self, buddy=get_owner_instance(), + pixel_size=style.XLARGE_ICON_SIZE) self._animator = animator.Animator(0.3) self._animator.connect('completed', self._animation_completed_cb) @@ -92,8 +54,6 @@ def _animation_completed_cb(self, anim): self.emit('completed') def start_transition(self, start_size, end_size): - self._my_icon.props.size = start_size - self._animator.remove_all() - self._animator.add(_Animation(self._my_icon, start_size, end_size)) + self._animator.add(_Animation(self, start_size, end_size)) self._animator.start() diff --git a/src/jarabe/desktop/viewcontainer.py b/src/jarabe/desktop/viewcontainer.py new file mode 100644 index 0000000000..1c76fb1a73 --- /dev/null +++ b/src/jarabe/desktop/viewcontainer.py @@ -0,0 +1,82 @@ +# Copyright (C) 2011-2012 One Laptop Per Child +# Copyright (C) 2010 Tomeu Vizoso +# Copyright (C) 2011 Walter Bender +# Copyright (C) 2011 Raul Gutierrez Segales +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import gtk + + +class ViewContainer(gtk.Container): + __gtype_name__ = 'SugarViewContainer' + + def __init__(self, layout, owner_icon, activity_icon=None, **kwargs): + gtk.Container.__init__(self, **kwargs) + self.set_has_window(False) + + self._activity_icon = None + self._owner_icon = None + self._layout = None + + self._children = [] + self.set_layout(layout) + + if owner_icon: + self._owner_icon = owner_icon + self.add(self._owner_icon) + self._owner_icon.show() + + if activity_icon: + self._activity_icon = activity_icon + self.add(self._activity_icon) + self._activity_icon.show() + + def do_add(self, child): + if child != self._owner_icon and child != self._activity_icon: + self._children.append(child) + if child.flags() & gtk.REALIZED: + child.set_parent_window(self.get_parent_window()) + child.set_parent(self) + + def do_remove(self, child): + was_visible = child.get_visible() + if child in self._children: + self._children.remove(child) + child.unparent() + self._layout.remove(child) + if was_visible and self.get_visible(): + self.queue_resize() + + def do_size_allocate(self, allocation): + self.allocation = allocation + if self._owner_icon: + self._layout.setup(allocation, self._owner_icon, + self._activity_icon) + + self._layout.allocate_children(allocation, self._children) + + def do_forall(self, include_internals, callback, callback_data): + for child in self._children: + callback(child, callback_data) + if self._owner_icon: + callback(self._owner_icon, callback_data) + if self._activity_icon: + callback(self._activity_icon, callback_data) + + def set_layout(self, layout): + for child in self.get_children(): + self.remove(child) + self._layout = layout diff --git a/src/jarabe/journal/detailview.py b/src/jarabe/journal/detailview.py index aa8c039812..628af015ad 100644 --- a/src/jarabe/journal/detailview.py +++ b/src/jarabe/journal/detailview.py @@ -19,10 +19,9 @@ import gobject import gtk -import hippo from sugar.graphics import style -from sugar.graphics.icon import CanvasIcon +from sugar.graphics.icon import Icon from jarabe.journal.expandedentry import ExpandedEntry from jarabe.journal import model @@ -39,21 +38,15 @@ def __init__(self, **kwargs): self._metadata = None self._expanded_entry = None - canvas = hippo.Canvas() - - self._root = hippo.CanvasBox() - self._root.props.background_color = style.COLOR_PANEL_GREY.get_int() - canvas.set_root(self._root) + gobject.GObject.__init__(self, **kwargs) + gtk.VBox.__init__(self) back_bar = BackBar() back_bar.connect('button-release-event', self.__back_bar_release_event_cb) - self._root.append(back_bar) - - gobject.GObject.__init__(self, **kwargs) + self.pack_start(back_bar, expand=False) - self.pack_start(canvas) - canvas.show() + self.show_all() def _fav_icon_activated_cb(self, fav_icon): keep = not self._expanded_entry.get_keep() @@ -67,8 +60,9 @@ def __back_bar_release_event_cb(self, back_bar, event): def _update_view(self): if self._expanded_entry is None: self._expanded_entry = ExpandedEntry() - self._root.append(self._expanded_entry, hippo.PACK_EXPAND) + self.pack_start(self._expanded_entry) self._expanded_entry.set_metadata(self._metadata) + self.show_all() def refresh(self): logging.debug('DetailView.refresh') @@ -86,34 +80,37 @@ def set_metadata(self, metadata): type=object, getter=get_metadata, setter=set_metadata) -class BackBar(hippo.CanvasBox): +class BackBar(gtk.EventBox): def __init__(self): - hippo.CanvasBox.__init__(self, - orientation=hippo.ORIENTATION_HORIZONTAL, - border=style.LINE_WIDTH, - background_color=style.COLOR_PANEL_GREY.get_int(), - border_color=style.COLOR_SELECTION_GREY.get_int(), - padding=style.DEFAULT_PADDING, - padding_left=style.DEFAULT_SPACING, - spacing=style.DEFAULT_SPACING) - - icon = CanvasIcon(icon_name='go-previous', - size=style.SMALL_ICON_SIZE, - fill_color=style.COLOR_TOOLBAR_GREY.get_svg()) - self.append(icon) - - label = hippo.CanvasText(text=_('Back'), - font_desc=style.FONT_NORMAL.get_pango_desc()) - self.append(label) + gtk.EventBox.__init__(self) + self.modify_bg(gtk.STATE_NORMAL, + style.COLOR_PANEL_GREY.get_gdk_color()) + hbox = gtk.HBox(spacing=style.DEFAULT_PADDING) + hbox.set_border_width(style.DEFAULT_PADDING) + icon = Icon(icon_name='go-previous', icon_size=gtk.ICON_SIZE_MENU, + fill_color=style.COLOR_TOOLBAR_GREY.get_svg()) + hbox.pack_start(icon, False, False) + + label = gtk.Label() + label.set_text(_('Back')) + halign = gtk.Alignment(0, 0.5, 0, 1) + halign.add(label) + hbox.pack_start(halign, True, True) + hbox.show() + self.add(hbox) if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL: - self.reverse() + hbox.reverse() + + self.connect('enter-notify-event', self.__enter_notify_event_cb) + self.connect('leave-notify-event', self.__leave_notify_event_cb) - self.connect('motion-notify-event', self.__motion_notify_event_cb) + def __enter_notify_event_cb(self, box, event): + box.modify_bg(gtk.STATE_NORMAL, + style.COLOR_SELECTION_GREY.get_gdk_color()) + return False - def __motion_notify_event_cb(self, box, event): - if event.detail == hippo.MOTION_DETAIL_ENTER: - box.props.background_color = style.COLOR_SELECTION_GREY.get_int() - elif event.detail == hippo.MOTION_DETAIL_LEAVE: - box.props.background_color = style.COLOR_PANEL_GREY.get_int() + def __leave_notify_event_cb(self, box, event): + box.modify_bg(gtk.STATE_NORMAL, + style.COLOR_PANEL_GREY.get_gdk_color()) return False diff --git a/src/jarabe/journal/expandedentry.py b/src/jarabe/journal/expandedentry.py index 03f8cd13a4..e0c603fd31 100644 --- a/src/jarabe/journal/expandedentry.py +++ b/src/jarabe/journal/expandedentry.py @@ -20,7 +20,6 @@ import time import os -import hippo import cairo import gobject import glib @@ -28,158 +27,144 @@ import simplejson from sugar.graphics import style -from sugar.graphics.icon import CanvasIcon from sugar.graphics.xocolor import XoColor -from sugar.graphics.canvastextview import CanvasTextView from sugar.util import format_size from jarabe.journal.keepicon import KeepIcon from jarabe.journal.palettes import ObjectPalette, BuddyPalette from jarabe.journal import misc from jarabe.journal import model +from jarabe.view.eventicon import EventIcon -class Separator(hippo.CanvasBox, hippo.CanvasItem): +class Separator(gtk.VBox): def __init__(self, orientation): - hippo.CanvasBox.__init__(self, - background_color=style.COLOR_PANEL_GREY.get_int()) - - if orientation == hippo.ORIENTATION_VERTICAL: - self.props.box_width = style.LINE_WIDTH - else: - self.props.box_height = style.LINE_WIDTH + gtk.VBox.__init__(self, + background_color=style.COLOR_PANEL_GREY.get_gdk_color()) -class BuddyList(hippo.CanvasBox): +class BuddyList(gtk.Alignment): def __init__(self, buddies): - hippo.CanvasBox.__init__(self, xalign=hippo.ALIGNMENT_START, - orientation=hippo.ORIENTATION_HORIZONTAL) + gtk.Alignment.__init__(self, 0, 0, 0, 0) + hbox = gtk.HBox() for buddy in buddies: nick_, color = buddy - hbox = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL) - icon = CanvasIcon(icon_name='computer-xo', - xo_color=XoColor(color), - size=style.STANDARD_ICON_SIZE) + icon = EventIcon(icon_name='computer-xo', + xo_color=XoColor(color), + pixel_size=style.STANDARD_ICON_SIZE) icon.set_palette(BuddyPalette(buddy)) - hbox.append(icon) - self.append(hbox) + hbox.pack_start(icon) + self.add(hbox) -class ExpandedEntry(hippo.CanvasBox): +class ExpandedEntry(gtk.EventBox): def __init__(self): - hippo.CanvasBox.__init__(self) - self.props.orientation = hippo.ORIENTATION_VERTICAL - self.props.background_color = style.COLOR_WHITE.get_int() - self.props.padding_top = style.DEFAULT_SPACING * 3 + gtk.EventBox.__init__(self) + self._vbox = gtk.VBox() + self.add(self._vbox) self._metadata = None self._update_title_sid = None - # Create header - header = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL, - padding=style.DEFAULT_PADDING, - padding_right=style.GRID_CELL_SIZE, - spacing=style.DEFAULT_SPACING) - self.append(header) - - # Create two column body + self.modify_bg(gtk.STATE_NORMAL, style.COLOR_WHITE.get_gdk_color()) - body = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL, - spacing=style.DEFAULT_SPACING * 3, - padding_left=style.GRID_CELL_SIZE, - padding_right=style.GRID_CELL_SIZE, - padding_top=style.DEFAULT_SPACING * 3) + # Create a header + header = gtk.HBox() + self._vbox.pack_start(header, False, False, style.DEFAULT_SPACING * 2) - self.append(body, hippo.PACK_EXPAND) + # Create a two-column body + body_box = gtk.EventBox() + body_box.set_border_width(style.DEFAULT_SPACING) + body_box.modify_bg(gtk.STATE_NORMAL, style.COLOR_WHITE.get_gdk_color()) + self._vbox.pack_start(body_box) + body = gtk.HBox() + body_box.add(body) - first_column = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL, - spacing=style.DEFAULT_SPACING) - body.append(first_column) + first_column = gtk.VBox() + body.pack_start(first_column, False, False, style.DEFAULT_SPACING) - second_column = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL, - spacing=style.DEFAULT_SPACING) - body.append(second_column, hippo.PACK_EXPAND) + second_column = gtk.VBox() + body.pack_start(second_column) # Header - self._keep_icon = self._create_keep_icon() - header.append(self._keep_icon) + header.pack_start(self._keep_icon, False, False, style.DEFAULT_SPACING) self._icon = None - self._icon_box = hippo.CanvasBox() - header.append(self._icon_box) + self._icon_box = gtk.HBox() + header.pack_start(self._icon_box, False, False, style.DEFAULT_SPACING) self._title = self._create_title() - header.append(self._title, hippo.PACK_EXPAND) + header.pack_start(self._title) # TODO: create a version list popup instead of a date label self._date = self._create_date() - header.append(self._date) + header.pack_start(self._date, False, False, style.DEFAULT_SPACING) if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL: header.reverse() - # First column - - self._preview_box = hippo.CanvasBox() - first_column.append(self._preview_box) + # First body column + self._preview_box = gtk.Frame() + first_column.pack_start(self._preview_box, expand=False) - self._technical_box = hippo.CanvasBox() - first_column.append(self._technical_box) - - # Second column + self._technical_box = gtk.VBox() + first_column.pack_start(self._technical_box) + # Second body column description_box, self._description = self._create_description() - second_column.append(description_box) + second_column.pack_start(description_box, True, True, + style.DEFAULT_SPACING) tags_box, self._tags = self._create_tags() - second_column.append(tags_box) + second_column.pack_start(tags_box, True, True, + style.DEFAULT_SPACING) + + self._buddy_list = gtk.VBox() + second_column.pack_start(self._buddy_list) - self._buddy_list = hippo.CanvasBox() - second_column.append(self._buddy_list) + self.show_all() def set_metadata(self, metadata): if self._metadata == metadata: return self._metadata = metadata - self._keep_icon.keep = (str(metadata.get('keep', 0)) == '1') + self._keep_icon.set_active(int(metadata.get('keep', 0)) == 1) self._icon = self._create_icon() - self._icon_box.clear() - self._icon_box.append(self._icon) - - self._date.props.text = misc.get_date(metadata) + self._icon_box.foreach(self._icon_box.remove) + self._icon_box.pack_start(self._icon, False, False) - title = self._title.props.widget - title.props.text = metadata.get('title', _('Untitled')) - title.props.editable = model.is_editable(metadata) + self._date.set_text(misc.get_date(metadata)) - self._preview_box.clear() - self._preview_box.append(self._create_preview()) + self._title.set_text(metadata.get('title', _('Untitled'))) - self._technical_box.clear() - self._technical_box.append(self._create_technical()) + if self._preview_box.get_child(): + self._preview_box.remove(self._preview_box.get_child()) + self._preview_box.add(self._create_preview()) - self._buddy_list.clear() - self._buddy_list.append(self._create_buddy_list()) + self._technical_box.foreach(self._technical_box.remove) + self._technical_box.pack_start(self._create_technical(), + False, False, style.DEFAULT_SPACING) - description = self._description.text_view_widget - description.props.buffer.props.text = metadata.get('description', '') - description.props.editable = model.is_editable(metadata) + self._buddy_list.foreach(self._buddy_list.remove) + self._buddy_list.pack_start(self._create_buddy_list(), False, False, + style.DEFAULT_SPACING) - tags = self._tags.text_view_widget - tags.props.buffer.props.text = metadata.get('tags', '') - tags.props.editable = model.is_editable(metadata) + description = metadata.get('description', '') + self._description.get_buffer().set_text(description) + tags = metadata.get('tags', '') + self._tags.get_buffer().set_text(tags) def _create_keep_icon(self): - keep_icon = KeepIcon(False) - keep_icon.connect('activated', self._keep_icon_activated_cb) + keep_icon = KeepIcon() + keep_icon.connect('toggled', self._keep_icon_toggled_cb) return keep_icon def _create_icon(self): - icon = CanvasIcon(file_name=misc.get_icon_name(self._metadata)) + icon = EventIcon(file_name=misc.get_icon_name(self._metadata)) icon.connect_after('button-release-event', self._icon_button_release_event_cb) @@ -202,17 +187,17 @@ def _create_title(self): entry.modify_bg(gtk.STATE_INSENSITIVE, bg_color) entry.modify_base(gtk.STATE_INSENSITIVE, bg_color) - return hippo.CanvasWidget(widget=entry) + return entry def _create_date(self): - date = hippo.CanvasText(xalign=hippo.ALIGNMENT_START, - font_desc=style.FONT_NORMAL.get_pango_desc()) + date = gtk.Label() return date def _create_preview(self): width = style.zoom(320) height = style.zoom(240) - box = hippo.CanvasBox() + box = gtk.EventBox() + box.modify_bg(gtk.STATE_NORMAL, style.COLOR_WHITE.get_gdk_color()) if len(self._metadata.get('preview', '')) > 4: if self._metadata['preview'][1:4] == 'PNG': @@ -225,7 +210,17 @@ def _create_preview(self): png_file = StringIO.StringIO(preview_data) try: + # Load image and scale to dimensions surface = cairo.ImageSurface.create_from_png(png_file) + png_width = surface.get_width() + png_height = surface.get_height() + pixmap = gtk.gdk.Pixmap(None, png_width, png_height, 24) + cr = pixmap.cairo_create() + cr.set_source_surface(surface, 0, 0) + cr.scale(width / png_width, height / png_height) + cr.paint() + + im = gtk.image_new_from_pixmap(pixmap, None) has_preview = True except Exception: logging.exception('Error while loading the preview') @@ -234,50 +229,35 @@ def _create_preview(self): has_preview = False if has_preview: - preview_box = hippo.CanvasImage(image=surface, - border=style.LINE_WIDTH, - border_color=style.COLOR_BUTTON_GREY.get_int(), - xalign=hippo.ALIGNMENT_CENTER, - yalign=hippo.ALIGNMENT_CENTER, - scale_width=width, - scale_height=height) + box.add(im) else: - preview_box = hippo.CanvasText(text=_('No preview'), - font_desc=style.FONT_NORMAL.get_pango_desc(), - xalign=hippo.ALIGNMENT_CENTER, - yalign=hippo.ALIGNMENT_CENTER, - border=style.LINE_WIDTH, - border_color=style.COLOR_BUTTON_GREY.get_int(), - color=style.COLOR_BUTTON_GREY.get_int(), - box_width=width, - box_height=height) - preview_box.connect_after('button-release-event', - self._preview_box_button_release_event_cb) - box.append(preview_box) + label = gtk.Label() + label.set_text(_('No preview')) + label.set_size_request(width, height) + box.add(label) + + box.connect_after('button-release-event', + self._preview_box_button_release_event_cb) return box def _create_technical(self): - vbox = hippo.CanvasBox() + vbox = gtk.VBox() vbox.props.spacing = style.DEFAULT_SPACING - lines = [ - _('Kind: %s') % (self._metadata.get('mime_type') or _('Unknown'),), - _('Date: %s') % (self._format_date(),), - _('Size: %s') % (format_size(int(self._metadata.get('filesize', - model.get_file_size(self._metadata['uid']))))), - ] - - for line in lines: - text = hippo.CanvasText(text=line, - font_desc=style.FONT_NORMAL.get_pango_desc()) - text.props.color = style.COLOR_BUTTON_GREY.get_int() - - if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL: - text.props.xalign = hippo.ALIGNMENT_END - else: - text.props.xalign = hippo.ALIGNMENT_START - - vbox.append(text) + label = \ + _('Kind: %s') % (self._metadata.get('mime_type') or \ + _('Unknown'),) + '\n' + \ + _('Date: %s') % (self._format_date(),) + '\n' + \ + _('Size: %s') % (format_size(int(self._metadata.get( + 'filesize', + model.get_file_size(self._metadata['uid']))))) + + text = gtk.Label() + text.set_markup('%s' % ( + style.COLOR_BUTTON_GREY.get_html(), label)) + halign = gtk.Alignment(0, 0, 0, 0) + halign.add(text) + vbox.pack_start(halign, False, False, 0) return vbox @@ -295,76 +275,55 @@ def _format_date(self): def _create_buddy_list(self): - vbox = hippo.CanvasBox() + vbox = gtk.VBox() vbox.props.spacing = style.DEFAULT_SPACING - text = hippo.CanvasText(text=_('Participants:'), - font_desc=style.FONT_NORMAL.get_pango_desc()) - text.props.color = style.COLOR_BUTTON_GREY.get_int() - - if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL: - text.props.xalign = hippo.ALIGNMENT_END - else: - text.props.xalign = hippo.ALIGNMENT_START - - vbox.append(text) + text = gtk.Label() + text.set_markup('%s' % ( + style.COLOR_BUTTON_GREY.get_html(), _('Participants:'))) + halign = gtk.Alignment(0, 0, 0, 0) + halign.add(text) + vbox.pack_start(halign, False, False, 0) if self._metadata.get('buddies'): buddies = simplejson.loads(self._metadata['buddies']).values() - vbox.append(BuddyList(buddies)) + vbox.pack_start(BuddyList(buddies), False, False, 0) return vbox else: return vbox - def _create_description(self): - vbox = hippo.CanvasBox() + def _create_scrollable(self, label): + vbox = gtk.VBox() vbox.props.spacing = style.DEFAULT_SPACING - text = hippo.CanvasText(text=_('Description:'), - font_desc=style.FONT_NORMAL.get_pango_desc()) - text.props.color = style.COLOR_BUTTON_GREY.get_int() + text = gtk.Label() + text.set_markup('%s' % ( + style.COLOR_BUTTON_GREY.get_html(), label)) - if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL: - text.props.xalign = hippo.ALIGNMENT_END - else: - text.props.xalign = hippo.ALIGNMENT_START + halign = gtk.Alignment(0, 0, 0, 0) + halign.add(text) + vbox.pack_start(halign, False, False, 0) - vbox.append(text) + scrolled_window = gtk.ScrolledWindow() + scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + scrolled_window.set_border_width(style.LINE_WIDTH) + text_buffer = gtk.TextBuffer() + text_view = gtk.TextView(text_buffer) + text_view.set_left_margin(style.DEFAULT_PADDING) + text_view.set_wrap_mode(gtk.WRAP_WORD_CHAR) + scrolled_window.add_with_viewport(text_view) + vbox.pack_start(scrolled_window) - text_view = CanvasTextView('', - box_height=style.GRID_CELL_SIZE * 2) - vbox.append(text_view, hippo.PACK_EXPAND) - - text_view.text_view_widget.props.accepts_tab = False - text_view.text_view_widget.connect('focus-out-event', - self._description_focus_out_event_cb) + # text_view.text_view_widget.connect('focus-out-event', + # self._description_focus_out_event_cb) return vbox, text_view - def _create_tags(self): - vbox = hippo.CanvasBox() - vbox.props.spacing = style.DEFAULT_SPACING - - text = hippo.CanvasText(text=_('Tags:'), - font_desc=style.FONT_NORMAL.get_pango_desc()) - text.props.color = style.COLOR_BUTTON_GREY.get_int() - - if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL: - text.props.xalign = hippo.ALIGNMENT_END - else: - text.props.xalign = hippo.ALIGNMENT_START - - vbox.append(text) - - text_view = CanvasTextView('', - box_height=style.GRID_CELL_SIZE * 2) - vbox.append(text_view, hippo.PACK_EXPAND) - - text_view.text_view_widget.props.accepts_tab = False - text_view.text_view_widget.connect('focus-out-event', - self._tags_focus_out_event_cb) + def _create_description(self): + return self._create_scrollable(_('Description:')) - return vbox, text_view + def _create_tags(self): + return self._create_scrollable(_('Tags:')) def _title_notify_text_cb(self, entry, pspec): if not self._update_title_sid: @@ -385,7 +344,7 @@ def _update_entry(self, needs_update=False): return old_title = self._metadata.get('title', None) - new_title = self._title.props.widget.props.text + new_title = self._title.get_text() if old_title != new_title: label = glib.markup_escape_text(new_title) self._icon.palette.props.primary_text = label @@ -393,15 +352,18 @@ def _update_entry(self, needs_update=False): self._metadata['title_set_by_user'] = '1' needs_update = True + bounds = self._tags.get_buffer().get_bounds() old_tags = self._metadata.get('tags', None) - new_tags = self._tags.text_view_widget.props.buffer.props.text + new_tags = self._tags.get_buffer().get_text(bounds[0], bounds[1]) + if old_tags != new_tags: self._metadata['tags'] = new_tags needs_update = True + bounds = self._description.get_buffer().get_bounds() old_description = self._metadata.get('description', None) - new_description = \ - self._description.text_view_widget.props.buffer.props.text + new_description = self._description.get_buffer().get_text( + bounds[0], bounds[1]) if old_description != new_description: self._metadata['description'] = new_description needs_update = True @@ -418,16 +380,12 @@ def _update_entry(self, needs_update=False): self._update_title_sid = None - def get_keep(self): - return (str(self._metadata.get('keep', 0)) == '1') - - def _keep_icon_activated_cb(self, keep_icon): - if self.get_keep(): - self._metadata['keep'] = 0 - else: + def _keep_icon_toggled_cb(self, keep_icon): + if keep_icon.get_active(): self._metadata['keep'] = 1 + else: + self._metadata['keep'] = 0 self._update_entry(needs_update=True) - keep_icon.props.keep = self.get_keep() def _icon_button_release_event_cb(self, button, event): logging.debug('_icon_button_release_event_cb') diff --git a/src/jarabe/journal/keepicon.py b/src/jarabe/journal/keepicon.py index 5bc299bca9..85b1728de7 100644 --- a/src/jarabe/journal/keepicon.py +++ b/src/jarabe/journal/keepicon.py @@ -14,51 +14,42 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -import gobject -import hippo +import gtk import gconf +import logging -from sugar.graphics.icon import CanvasIcon +from sugar.graphics.icon import Icon from sugar.graphics import style from sugar.graphics.xocolor import XoColor -class KeepIcon(CanvasIcon): - def __init__(self, keep): - CanvasIcon.__init__(self, icon_name='emblem-favorite', - box_width=style.GRID_CELL_SIZE * 3 / 5, - size=style.SMALL_ICON_SIZE) - self.connect('motion-notify-event', self.__motion_notify_event_cb) +class KeepIcon(gtk.ToggleButton): + def __init__(self): + gtk.ToggleButton.__init__(self) + self.set_relief(gtk.RELIEF_NONE) + self.set_focus_on_click(False) - self._keep = None - self.set_keep(keep) + self._icon = Icon(icon_name='emblem-favorite', + pixel_size=style.SMALL_ICON_SIZE) + self.set_image(self._icon) + self.connect('toggled', self.__toggled_cb) + self.connect('leave-notify-event', self.__leave_notify_event_cb) + self.connect('enter-notify-event', self.__enter_notify_event_cb) - def set_keep(self, keep): - if keep == self._keep: - return - - self._keep = keep - if keep: + def __toggled_cb(self, widget): + if self.get_active(): client = gconf.client_get_default() color = XoColor(client.get_string('/desktop/sugar/user/color')) - self.props.xo_color = color + self._icon.props.xo_color = color + logging.debug('KEEPICON: setting xo_color') else: - self.props.stroke_color = style.COLOR_BUTTON_GREY.get_svg() - self.props.fill_color = style.COLOR_TRANSPARENT.get_svg() - - def get_keep(self): - return self._keep + self._icon.props.stroke_color = style.COLOR_BUTTON_GREY.get_svg() + self._icon.props.fill_color = style.COLOR_TRANSPARENT.get_svg() - keep = gobject.property(type=int, default=0, getter=get_keep, - setter=set_keep) + def __enter_notify_event_cb(self, icon, event): + if not self.get_active(): + self._icon.props.fill_color = style.COLOR_BUTTON_GREY.get_svg() - def __motion_notify_event_cb(self, icon, event): - if not self._keep: - if event.detail == hippo.MOTION_DETAIL_ENTER: - client = gconf.client_get_default() - prelit_color = XoColor(client.get_string('/desktop/sugar/user/color')) - icon.props.stroke_color = prelit_color.get_stroke_color() - icon.props.fill_color = prelit_color.get_fill_color() - elif event.detail == hippo.MOTION_DETAIL_LEAVE: - icon.props.stroke_color = style.COLOR_BUTTON_GREY.get_svg() - icon.props.fill_color = style.COLOR_TRANSPARENT.get_svg() + def __leave_notify_event_cb(self, icon, event): + if not self.get_active(): + self._icon.props.fill_color = style.COLOR_TRANSPARENT.get_svg() diff --git a/src/jarabe/journal/listview.py b/src/jarabe/journal/listview.py index 57836f250d..f6a867f0ec 100644 --- a/src/jarabe/journal/listview.py +++ b/src/jarabe/journal/listview.py @@ -18,14 +18,14 @@ from gettext import gettext as _ import time +import glib import gobject import gtk -import hippo import gconf import pango from sugar.graphics import style -from sugar.graphics.icon import CanvasIcon, Icon, CellRendererIcon +from sugar.graphics.icon import Icon, CellRendererIcon from sugar.graphics.xocolor import XoColor from sugar import util @@ -33,6 +33,7 @@ from jarabe.journal.palettes import ObjectPalette, BuddyPalette from jarabe.journal import model from jarabe.journal import misc +from jarabe.view.eventicon import EventIcon UPDATE_INTERVAL = 300 @@ -370,38 +371,33 @@ def _stop_progress_bar(self): self._progress_bar = None def _show_message(self, message, show_clear_query=False): - canvas = hippo.Canvas() + box = gtk.VBox() self.remove(self.child) - self.add(canvas) - canvas.show() - - box = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL, - background_color=style.COLOR_WHITE.get_int(), - yalign=hippo.ALIGNMENT_CENTER, - spacing=style.DEFAULT_SPACING, - padding_bottom=style.GRID_CELL_SIZE) - canvas.set_root(box) - - icon = CanvasIcon(size=style.LARGE_ICON_SIZE, - icon_name='activity-journal', - stroke_color=style.COLOR_BUTTON_GREY.get_svg(), - fill_color=style.COLOR_TRANSPARENT.get_svg()) - box.append(icon) - - text = hippo.CanvasText(text=message, - xalign=hippo.ALIGNMENT_CENTER, - font_desc=style.FONT_BOLD.get_pango_desc(), - color=style.COLOR_BUTTON_GREY.get_int()) - box.append(text) + + alignment = gtk.Alignment(0.5, 0.5, 0.1, 0.1) + self.add(alignment) + + icon = EventIcon(pixel_size=style.LARGE_ICON_SIZE, + icon_name='activity-journal', + stroke_color=style.COLOR_BUTTON_GREY.get_svg(), + fill_color=style.COLOR_TRANSPARENT.get_svg()) + box.pack_start(icon, expand=True, fill=False) + + label = gtk.Label() + color = style.COLOR_BUTTON_GREY.get_html() + label.set_markup('%s' % ( \ + color, glib.markup_escape_text(message))) + box.pack_start(label, expand=True, fill=False) if show_clear_query: button = gtk.Button(label=_('Clear search')) button.connect('clicked', self.__clear_button_clicked_cb) button.props.image = Icon(icon_name='dialog-cancel', icon_size=gtk.ICON_SIZE_BUTTON) - canvas_button = hippo.CanvasWidget(widget=button, - xalign=hippo.ALIGNMENT_CENTER) - box.append(canvas_button) + box.pack_start(button, expand=True, fill=False) + + alignment.add(box) + alignment.show_all() def __clear_button_clicked_cb(self, button): self.emit('clear-clicked') diff --git a/src/jarabe/view/buddyicon.py b/src/jarabe/view/buddyicon.py index e0e8b3fea3..663bd9221c 100644 --- a/src/jarabe/view/buddyicon.py +++ b/src/jarabe/view/buddyicon.py @@ -14,17 +14,19 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -from sugar.graphics.icon import CanvasIcon from sugar.graphics import style from jarabe.view.buddymenu import BuddyMenu +from jarabe.view.eventicon import EventIcon + _FILTERED_ALPHA = 0.33 -class BuddyIcon(CanvasIcon): - def __init__(self, buddy, size=style.STANDARD_ICON_SIZE): - CanvasIcon.__init__(self, icon_name='computer-xo', size=size) +class BuddyIcon(EventIcon): + def __init__(self, buddy, pixel_size=style.STANDARD_ICON_SIZE): + EventIcon.__init__(self, icon_name='computer-xo', + pixel_size=pixel_size) self._filtered = False self._buddy = buddy diff --git a/src/jarabe/view/pulsingicon.py b/src/jarabe/view/pulsingicon.py index 9a98a80e41..39e0babd76 100644 --- a/src/jarabe/view/pulsingicon.py +++ b/src/jarabe/view/pulsingicon.py @@ -18,9 +18,12 @@ import gobject -from sugar.graphics.icon import Icon, CanvasIcon +from sugar.graphics.icon import Icon from sugar.graphics import style +from jarabe.view.eventicon import EventIcon + + _INTERVAL = 100 _STEP = math.pi / 10 # must be a fraction of pi, for clean caching _MINIMAL_ALPHA_VALUE = 0.33 @@ -169,8 +172,8 @@ def __destroy_cb(self, icon): self._palette.destroy() -class CanvasPulsingIcon(CanvasIcon): - __gtype_name__ = 'SugarCanvasPulsingIcon' +class EventPulsingIcon(EventIcon): + __gtype_name__ = 'SugarEventPulsingIcon' def __init__(self, **kwargs): self._pulser = Pulser(self) @@ -179,7 +182,7 @@ def __init__(self, **kwargs): self._paused = False self._pulsing = False - CanvasIcon.__init__(self, **kwargs) + EventIcon.__init__(self, **kwargs) self.connect('destroy', self.__destroy_cb)