Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/view/home/favoriteslayout.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/home/favoriteslayout.py')
-rw-r--r--src/view/home/favoriteslayout.py225
1 files changed, 225 insertions, 0 deletions
diff --git a/src/view/home/favoriteslayout.py b/src/view/home/favoriteslayout.py
new file mode 100644
index 0000000..9ab4eff
--- /dev/null
+++ b/src/view/home/favoriteslayout.py
@@ -0,0 +1,225 @@
+# Copyright (C) 2008 One Laptop Per Child
+#
+# 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 logging
+import math
+import hashlib
+
+import gobject
+import gtk
+import hippo
+
+from sugar.graphics import style
+from sugar import activity
+
+_logger = logging.getLogger('FavoritesLayout')
+
+class FavoritesLayout(gobject.GObject, hippo.CanvasLayout):
+ __gtype_name__ = 'FavoritesLayout'
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+ self.box = None
+ self.fixed_positions = {}
+
+ 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 _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
+ else:
+ return 0
+
+ def append(self, icon):
+ self.box.insert_sorted(icon, 0, self._compare_activities)
+ relative_x, relative_y = icon.fixed_position
+ if relative_x >= 0 and relative_y >= 0:
+ width = gtk.gdk.screen_width()
+ height = gtk.gdk.screen_height() - style.GRID_CELL_SIZE
+ self.fixed_positions[icon] = (relative_x * 1000 / width,
+ relative_y * 1000 / height)
+ self.update_icon_sizes()
+
+ def remove(self, icon):
+ del self.fixed_positions[icon]
+ self.box.remove(icon)
+ self.update_icon_sizes()
+
+ def move_icon(self, icon, x, y):
+ if icon not in self.box.get_children():
+ raise ValueError('Child not in box.')
+
+ width = gtk.gdk.screen_width()
+ height = gtk.gdk.screen_height() - style.GRID_CELL_SIZE
+ registry = activity.get_registry()
+ registry.set_activity_position(icon.get_bundle_id(), icon.get_version(),
+ x * width / 1000, y * height / 1000)
+
+ self.fixed_positions[icon] = (x, y)
+ self.box.emit_request_changed()
+
+ def update_icon_sizes(self):
+ pass
+
+ def do_allocate(self, x, y, width, height, req_width, req_height,
+ origin_changed):
+ raise NotImplementedError()
+
+class RandomLayout(FavoritesLayout):
+ __gtype_name__ = 'RandomLayout'
+
+ def __init__(self):
+ FavoritesLayout.__init__(self)
+
+ def append(self, icon):
+ FavoritesLayout.append(self, icon)
+ icon.props.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)
+
+ if child.item in self.fixed_positions:
+ x, y = self.fixed_positions[child.item]
+ else:
+ name_hash = hashlib.md5(child.item.get_bundle_id())
+
+ x = int(name_hash.hexdigest()[:5], 16) % (width - child_width)
+ y = int(name_hash.hexdigest()[-5:], 16) % (height - child_height)
+
+ # TODO Handle collisions
+
+ child.allocate(int(x), int(y), child_width, child_height,
+ origin_changed)
+
+_MINIMUM_RADIUS = style.XLARGE_ICON_SIZE / 2 + style.DEFAULT_SPACING + \
+ style.STANDARD_ICON_SIZE * 2
+_MAXIMUM_RADIUS = (gtk.gdk.screen_height() - style.GRID_CELL_SIZE) / 2 - \
+ style.STANDARD_ICON_SIZE - style.DEFAULT_SPACING
+
+class RingLayout(FavoritesLayout):
+ __gtype_name__ = 'RingLayout'
+
+ def __init__(self):
+ FavoritesLayout.__init__(self)
+
+ def _calculate_radius_and_icon_size(self, children_count):
+ angle = 2 * math.pi / children_count
+
+ # what's the radius required without downscaling?
+ distance = style.STANDARD_ICON_SIZE + style.DEFAULT_SPACING
+ icon_size = style.STANDARD_ICON_SIZE
+
+ if children_count == 1:
+ radius = 0
+ else:
+ radius = math.sqrt(distance ** 2 /
+ (math.sin(angle) ** 2 + (math.cos(angle) - 1) ** 2))
+
+ if radius < _MINIMUM_RADIUS:
+ # we can upscale, if we want
+ icon_size += style.STANDARD_ICON_SIZE * \
+ (0.5 * (_MINIMUM_RADIUS - radius) / _MINIMUM_RADIUS)
+ radius = _MINIMUM_RADIUS
+ elif radius > _MAXIMUM_RADIUS:
+ radius = _MAXIMUM_RADIUS
+ # need to downscale. what's the icon size required?
+ distance = math.sqrt((radius * math.sin(angle)) ** 2 + \
+ (radius * (math.cos(angle) - 1)) ** 2)
+ icon_size = distance - style.DEFAULT_SPACING
+
+ return radius, icon_size
+
+ def _calculate_position(self, radius, icon_size, index, children_count):
+ width, height = self.box.get_allocation()
+ angle = index * (2 * math.pi / children_count) - math.pi / 2
+ x = radius * math.cos(angle) + (width - icon_size) / 2
+ y = radius * math.sin(angle) + (height - icon_size -
+ style.GRID_CELL_SIZE) / 2
+ return x, y
+
+ def _get_children_in_ring(self):
+ children = self.box.get_layout_children()
+ width, height = self.box.get_allocation()
+ children_in_ring = []
+ for child in children:
+ if child.item in self.fixed_positions:
+ x, y = self.fixed_positions[child.item]
+ distance_to_center = math.hypot(x - width / 2, y - height / 2)
+ # TODO at what distance should we consider a child inside the ring?
+ else:
+ children_in_ring.append(child)
+
+ return children_in_ring
+
+ def update_icon_sizes(self):
+ 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]
+ child.item.props.size = icon_size
+
+ for child in self.box.get_layout_children():
+ if child not in children_in_ring:
+ child.item.props.size = style.STANDARD_ICON_SIZE
+
+ 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)
+
+ child.allocate(int(x), int(y), child_width, child_height,
+ origin_changed)
+
+ for child in self.box.get_layout_children():
+ if child in children_in_ring:
+ continue
+
+ # 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)
+
+ x, y = self.fixed_positions[child.item]
+
+ child.allocate(int(x), int(y), child_width, child_height,
+ origin_changed)
+