Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--po/POTFILES.in2
-rw-r--r--po/it.po102
-rw-r--r--service/activityregistryservice.py12
-rw-r--r--service/bundleregistry.py106
-rw-r--r--src/view/home/FriendsBox.py26
-rw-r--r--src/view/home/HomeBox.py130
-rw-r--r--src/view/home/HomeWindow.py31
-rw-r--r--src/view/home/Makefile.am4
-rw-r--r--src/view/home/MeshBox.py45
-rw-r--r--src/view/home/activitieslist.py22
-rw-r--r--src/view/home/favoriteslayout.py273
-rw-r--r--src/view/home/favoritesview.py (renamed from src/view/home/activitiesring.py)298
-rw-r--r--src/view/home/grid.py223
-rw-r--r--src/view/home/launchbox.py8
-rw-r--r--src/view/home/spreadlayout.py197
-rw-r--r--src/view/home/transitionbox.py13
-rw-r--r--src/view/keyhandler.py23
-rw-r--r--src/view/palettes.py4
18 files changed, 1028 insertions, 491 deletions
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 322e691..ce883b2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -28,6 +28,6 @@ src/controlpanel/view/language.py
src/controlpanel/view/network.py
src/view/devices/network/mesh.py
src/view/frame/activitiestray.py
-src/view/home/activitiesring.py
+src/view/home/favoritesview.py
src/view/palettes.py
diff --git a/po/it.po b/po/it.po
index bfe9a76..16351db 100644
--- a/po/it.po
+++ b/po/it.po
@@ -6,8 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-06-05 00:30-0400\n"
-"PO-Revision-Date: 2008-06-06 11:16-0400\n"
+"POT-Creation-Date: 2008-06-14 00:30-0400\n"
+"PO-Revision-Date: 2008-06-18 04:33-0400\n"
"Last-Translator: Carlo Falciola <cfalciola@yahoo.it>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
@@ -65,9 +65,8 @@ msgstr "Apri"
#. self._stop_item.connect('activate', self._stop_item_activate_cb)
#. self.append_menu_item(self._stop_item)
#: ../src/view/clipboardmenu.py:63
-#, fuzzy
msgid "Keep"
-msgstr "Conserva"
+msgstr "Memorizza"
#: ../src/view/clipboardmenu.py:84
msgid "Open with"
@@ -90,24 +89,24 @@ msgstr "Tipo di Autenticazione:"
msgid "Encryption Type:"
msgstr "Tipo di Crittografia:"
-#: ../src/view/Shell.py:259
+#: ../src/view/Shell.py:262
msgid "Screenshot"
msgstr "Schermata"
# Ruota, piuttosto che anello per mantenere consistenza con lo shortcut da tastiera... cf 27_05_08
-#: ../src/view/home/HomeBox.py:149
+#: ../src/view/home/HomeBox.py:148
msgid "Ring view"
msgstr "Vista Ruota"
-#: ../src/view/home/HomeBox.py:150
+#: ../src/view/home/HomeBox.py:149
msgid "<Ctrl>R"
msgstr "<Ctrl>R"
-#: ../src/view/home/HomeBox.py:158
+#: ../src/view/home/HomeBox.py:157
msgid "List view"
msgstr "Vista Lista"
-#: ../src/view/home/HomeBox.py:159
+#: ../src/view/home/HomeBox.py:158
msgid "<Ctrl>L"
msgstr "<Ctrl>L"
@@ -157,7 +156,7 @@ msgstr "Disconnessione..."
msgid "Resume"
msgstr "Riprendi"
-#: ../src/view/home/MeshBox.py:307 ../src/view/frame/activitiestray.py:162
+#: ../src/view/home/MeshBox.py:307 ../src/view/frame/activitiestray.py:219
#, fuzzy
msgid "Join"
msgstr "Associa"
@@ -276,21 +275,15 @@ msgstr "Cancella"
msgid "Ok"
msgstr "Ok"
-#: ../src/controlpanel/sectionview.py:34
-msgid "Changes require a sugar restart to take effect."
-msgstr ""
-"Le modifiche effettuate necessitano di un riavvio di Sugar per avere "
-"effetto."
+#: ../src/controlpanel/sectionview.py:34 ../src/controlpanel/gui.py:250
+msgid "Changes require restart"
+msgstr "Le modifiche rendono necessario un riavvio"
-#: ../src/controlpanel/gui.py:248
+#: ../src/controlpanel/gui.py:249
msgid "Warning"
msgstr "Attenzione"
-#: ../src/controlpanel/gui.py:249
-msgid "Changes require restart to take effect"
-msgstr "Le modifiche effettuate necessitano di un riavvio per avere effetto"
-
-#: ../src/controlpanel/gui.py:252
+#: ../src/controlpanel/gui.py:253
msgid "Cancel changes"
msgstr "Annulla modifiche"
@@ -298,6 +291,10 @@ msgstr "Annulla modifiche"
msgid "Later"
msgstr "Dopo"
+#: ../src/controlpanel/gui.py:261
+msgid "Restart now"
+msgstr "Riavvia adesso"
+
#: ../src/controlpanel/model/aboutme.py:44
msgid "You must enter a name."
msgstr "Devi inserire un nome."
@@ -339,7 +336,8 @@ msgid "Error timezone does not exist."
msgstr "Errore, timezone non esistente."
#: ../src/controlpanel/model/frame.py:38 ../src/controlpanel/model/frame.py:60
-msgid "Value must be an int."
+#, fuzzy
+msgid "Value must be an integer."
msgstr "Valore deve essere un intero."
#: ../src/controlpanel/model/language.py:28
@@ -424,17 +422,22 @@ msgid "instantaneous"
msgstr "istantaneamente"
#: ../src/controlpanel/view/frame.py:32
-msgid "Delay in milliseconds:"
-msgstr "Ritardo in millisecondi:"
+#, python-format
+#, fuzzy
+msgid "%s seconds"
+msgstr "%d secondi"
-#: ../src/controlpanel/view/frame.py:66
-msgid "Hot Corners"
-msgstr "Angoli sensibili"
+#: ../src/controlpanel/view/frame.py:56
+msgid "Activation Delay"
+msgstr "Ritardo attivazione"
-#: ../src/controlpanel/view/frame.py:117
-#, fuzzy
-msgid "Warm Edges"
-msgstr "Bordi Attivi"
+#: ../src/controlpanel/view/frame.py:80
+msgid "Corner"
+msgstr "Angolo"
+
+#: ../src/controlpanel/view/frame.py:115
+msgid "Edge"
+msgstr "Margine"
#: ../src/controlpanel/view/language.py:29
#: ../src/controlpanel/view/language.py:74
@@ -466,29 +469,24 @@ msgid "Connected to a School Mesh Portal"
msgstr "Connesso ad un Portale Mesh di scuola"
#: ../src/view/devices/network/mesh.py:110
-#, fuzzy
msgid "Looking for a School Mesh Portal..."
msgstr "Ricerca di un Portale Mesh di scuola..."
#: ../src/view/devices/network/mesh.py:113
-#, fuzzy
msgid "Connected to an XO Mesh Portal"
msgstr "Connesso ad un Portale Mesh XO"
#: ../src/view/devices/network/mesh.py:115
-#, fuzzy
msgid "Looking for an XO Mesh Portal..."
msgstr "Sto cercando un Portale Mesh XO..."
# Diretto?
#: ../src/view/devices/network/mesh.py:118
-#, fuzzy
msgid "Connected to a Simple Mesh"
msgstr "Connesso ad un Mesh Semplice"
# Diretto?
#: ../src/view/devices/network/mesh.py:120
-#, fuzzy
msgid "Starting a Simple Mesh"
msgstr "Attivazione Mesh Semplice"
@@ -496,23 +494,23 @@ msgstr "Attivazione Mesh Semplice"
msgid "Unknown Mesh"
msgstr "Mesh sconosciuto"
-#: ../src/view/frame/activitiestray.py:167
+#: ../src/view/frame/activitiestray.py:224
msgid "Decline"
msgstr "Rinuncia"
-#: ../src/view/home/activitiesring.py:310
+#: ../src/view/home/activitiesring.py:305
msgid "Control Panel"
msgstr "Pannello di Controllo"
-#: ../src/view/home/activitiesring.py:321
+#: ../src/view/home/activitiesring.py:316
msgid "Restart"
msgstr "Riavvia"
-#: ../src/view/home/activitiesring.py:326
+#: ../src/view/home/activitiesring.py:321
msgid "Shutdown"
msgstr "Spegni"
-#: ../src/view/home/activitiesring.py:332
+#: ../src/view/home/activitiesring.py:327
msgid "Register"
msgstr "Registra"
@@ -545,6 +543,24 @@ msgstr "Mostra i contenuti"
msgid "%(free_space)d MB Free"
msgstr "%(free_space)d MB Liberi"
+#~ msgid "Changes require a sugar restart to take effect."
+#~ msgstr ""
+#~ "Le modifiche effettuate necessitano di un riavvio di Sugar per avere "
+#~ "effetto."
+
+#~ msgid "Changes require restart to take effect"
+#~ msgstr "Le modifiche effettuate necessitano di un riavvio per avere effetto"
+
+#~ msgid "Delay in milliseconds:"
+#~ msgstr "Ritardo in millisecondi:"
+
+#~ msgid "Hot Corners"
+#~ msgstr "Angoli sensibili"
+
+#, fuzzy
+#~ msgid "Warm Edges"
+#~ msgstr "Bordi Attivi"
+
#~ msgid "off"
#~ msgstr "spento"
@@ -745,10 +761,6 @@ msgstr "%(free_space)d MB Liberi"
#~ msgid "%d second"
#~ msgstr "%d secondo"
-#, python-format
-#~ msgid "%d seconds"
-#~ msgstr "%d secondi"
-
#~ msgid " and "
#~ msgstr " e "
diff --git a/service/activityregistryservice.py b/service/activityregistryservice.py
index bf98ef9..6ba5598 100644
--- a/service/activityregistryservice.py
+++ b/service/activityregistryservice.py
@@ -109,6 +109,12 @@ class ActivityRegistry(dbus.service.Object):
registry = bundleregistry.get_registry()
registry.set_bundle_favorite(bundle_id, version, favorite)
+ @dbus.service.method(_ACTIVITY_REGISTRY_IFACE,
+ in_signature='sidd', out_signature='')
+ def SetActivityPosition(self, bundle_id, version, x, y):
+ registry = bundleregistry.get_registry()
+ registry.set_bundle_position(bundle_id, version, float(x), float(y))
+
@dbus.service.signal(_ACTIVITY_REGISTRY_IFACE, signature='a{sv}')
def ActivityAdded(self, activity_info):
pass
@@ -125,6 +131,8 @@ class ActivityRegistry(dbus.service.Object):
registry = bundleregistry.get_registry()
favorite = registry.is_bundle_favorite(bundle.get_bundle_id(),
bundle.get_activity_version())
+ x, y = registry.get_bundle_position(bundle.get_bundle_id(),
+ bundle.get_activity_version())
return {'name': bundle.get_name(),
'icon': bundle.get_icon(),
'bundle_id': bundle.get_bundle_id(),
@@ -133,7 +141,9 @@ class ActivityRegistry(dbus.service.Object):
'command': bundle.get_command(),
'show_launcher': bundle.get_show_launcher(),
'favorite': favorite,
- 'installation_time': bundle.get_installation_time()}
+ 'installation_time': bundle.get_installation_time(),
+ 'position_x': x,
+ 'position_y': y}
def _bundle_added_cb(self, bundle_registry, bundle):
self.ActivityAdded(self._bundle_to_dict(bundle))
diff --git a/service/bundleregistry.py b/service/bundleregistry.py
index 8b7f09b..e7c30a8 100644
--- a/service/bundleregistry.py
+++ b/service/bundleregistry.py
@@ -16,6 +16,7 @@
import os
import logging
+import traceback
import gobject
import simplejson
@@ -48,7 +49,14 @@ class BundleRegistry(gobject.GObject):
self._scan_directory(activity_dir)
self._last_defaults_mtime = -1
- self._favorite_bundles = self._load_favorites()
+ self._favorite_bundles = {}
+
+ try:
+ self._load_favorites()
+ except Exception, e:
+ logging.error('Error while loading favorite_activities\n%s.' \
+ % traceback.format_exc())
+
self._merge_default_favorites()
def _get_activity_directories(self):
@@ -74,24 +82,34 @@ class BundleRegistry(gobject.GObject):
return defaults
+ def _get_favorite_key(self, bundle_id, version):
+ """We use a string as a composite key for the favorites dictionary
+ because JSON doesn't support tuples and python won't accept a list
+ as a dictionary key.
+ """
+ if ' ' in bundle_id:
+ raise ValueError('bundle_id cannot contain spaces')
+ return '%s %s' % (bundle_id, version)
+
def _load_favorites(self):
- favorite_bundles = []
favorites_path = env.get_profile_path('favorite_activities')
if os.path.exists(favorites_path):
- try:
- favorites_data = simplejson.load(open(favorites_path))
- except ValueError, e:
- logging.error('Error while loading favorite_activities: %r.' %
- e)
- else:
- # Old structure used to be a list, instead of a dictionary.
- if isinstance(favorites_data, list):
- favorite_bundles = favorites_data
- else:
- favorite_bundles = favorites_data['favorites']
- self._last_defaults_mtime = favorites_data['defaults-mtime']
+ favorites_data = simplejson.load(open(favorites_path))
+
+ favorite_bundles = favorites_data['favorites']
+ if not isinstance(favorite_bundles, dict):
+ raise ValueError('Invalid format in %s.' % favorites_path)
+ if favorite_bundles:
+ first_key = favorite_bundles.keys()[0]
+ if not isinstance(first_key, basestring):
+ raise ValueError('Invalid format in %s.' % favorites_path)
- return favorite_bundles
+ first_value = favorite_bundles.values()[0]
+ if first_value is not None and not isinstance(first_value, dict):
+ raise ValueError('Invalid format in %s.' % favorites_path)
+
+ self._last_defaults_mtime = float(favorites_data['defaults-mtime'])
+ self._favorite_bundles = favorite_bundles
def _merge_default_favorites(self):
default_activities = []
@@ -117,9 +135,9 @@ class BundleRegistry(gobject.GObject):
max_version < bundle.get_activity_version():
max_version = bundle.get_activity_version()
- if max_version > -1 and \
- [bundle_id, max_version] not in self._favorite_bundles:
- self._favorite_bundles.append([bundle_id, max_version])
+ key = self._get_favorite_key(bundle_id, max_version)
+ if max_version > -1 and key not in self._favorite_bundles:
+ self._favorite_bundles[key] = None
logging.debug('After merging: %r' % self._favorite_bundles)
@@ -205,24 +223,56 @@ class BundleRegistry(gobject.GObject):
(bundle_id, version))
def set_bundle_favorite(self, bundle_id, version, favorite):
+ key = self._get_favorite_key(bundle_id, version)
+ if favorite and not key in self._favorite_bundles:
+ self._favorite_bundles[key] = None
+ elif not favorite and key in self._favorite_bundles:
+ del self._favorite_bundles[key]
+ else:
+ return
+
+ self._write_favorites_file()
bundle = self._find_bundle(bundle_id, version)
- if favorite and not [bundle_id, version] in self._favorite_bundles:
- self._favorite_bundles.append([bundle_id, version])
- self.emit('bundle-changed', bundle)
- self._write_favorites_file()
- elif not favorite and [bundle_id, version] in self._favorite_bundles:
- self._favorite_bundles.remove([bundle_id, version])
- self.emit('bundle-changed', bundle)
- self._write_favorites_file()
+ self.emit('bundle-changed', bundle)
def is_bundle_favorite(self, bundle_id, version):
- return [bundle_id, version] in self._favorite_bundles
+ key = self._get_favorite_key(bundle_id, version)
+ return key in self._favorite_bundles
+
+ def set_bundle_position(self, bundle_id, version, x, y):
+ key = self._get_favorite_key(bundle_id, version)
+ if key not in self._favorite_bundles:
+ raise ValueError('Bundle %s %s not favorite' % (bundle_id, version))
+
+ if self._favorite_bundles[key] is None:
+ self._favorite_bundles[key] = {}
+ if 'position' not in self._favorite_bundles[key] or \
+ [x, y] != self._favorite_bundles[key]['position']:
+ self._favorite_bundles[key]['position'] = [x, y]
+ else:
+ return
+
+ self._write_favorites_file()
+ bundle = self._find_bundle(bundle_id, version)
+ self.emit('bundle-changed', bundle)
+
+ def get_bundle_position(self, bundle_id, version):
+ """Get the coordinates where the user wants the representation of this
+ bundle to be displayed. Coordinates are relative to a 1000x1000 area.
+ """
+ key = self._get_favorite_key(bundle_id, version)
+ if key not in self._favorite_bundles or \
+ self._favorite_bundles[key] is None or \
+ 'position' not in self._favorite_bundles[key]:
+ return (-1, -1)
+ else:
+ return tuple(self._favorite_bundles[key]['position'])
def _write_favorites_file(self):
path = env.get_profile_path('favorite_activities')
favorites_data = {'defaults-mtime': self._last_defaults_mtime,
'favorites': self._favorite_bundles}
- simplejson.dump(favorites_data, open(path, 'w'))
+ simplejson.dump(favorites_data, open(path, 'w'), indent=1)
_instance = None
diff --git a/src/view/home/FriendsBox.py b/src/view/home/FriendsBox.py
index 7c6648a..c45c1c6 100644
--- a/src/view/home/FriendsBox.py
+++ b/src/view/home/FriendsBox.py
@@ -14,6 +14,7 @@
# 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 gtk
import hippo
@@ -26,16 +27,19 @@ from model import shellmodel
from view.home.FriendView import FriendView
from view.home.spreadlayout import SpreadLayout
-class FriendsBox(hippo.CanvasBox):
+class FriendsBox(hippo.Canvas):
__gtype_name__ = 'SugarFriendsBox'
def __init__(self):
- hippo.CanvasBox.__init__(self,
- background_color=style.COLOR_WHITE.get_int())
+ 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.set_layout(self._layout)
+ self._box.set_layout(self._layout)
self._owner_icon = CanvasIcon(icon_name='computer-xo', cache=True,
xo_color=profile.get_color())
@@ -46,7 +50,7 @@ class FriendsBox(hippo.CanvasBox):
palette = Palette(None, primary_text=profile.get_nick_name(),
icon=palette_icon)
self._owner_icon.set_palette(palette)
- self._layout.add_center(self._owner_icon)
+ self._layout.add(self._owner_icon)
friends = shellmodel.get_instance().get_friends()
@@ -71,3 +75,15 @@ class FriendsBox(hippo.CanvasBox):
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/view/home/HomeBox.py b/src/view/home/HomeBox.py
index fa22a11..61ceee9 100644
--- a/src/view/home/HomeBox.py
+++ b/src/view/home/HomeBox.py
@@ -18,36 +18,38 @@ from gettext import gettext as _
import gobject
import gtk
-import hippo
from sugar.graphics import style
from sugar.graphics import iconentry
+from sugar.graphics.palette import Palette
+from sugar.graphics.menuitem import MenuItem
from sugar.graphics.radiotoolbutton import RadioToolButton
-from view.home.activitiesring import ActivitiesRing
+from view.home import favoritesview
from view.home.activitieslist import ActivitiesList
-_RING_VIEW = 0
+_FAVORITES_VIEW = 0
_LIST_VIEW = 1
_AUTOSEARCH_TIMEOUT = 1000
-class HomeBox(hippo.CanvasBox, hippo.CanvasItem):
+class HomeBox(gtk.VBox):
__gtype_name__ = 'SugarHomeBox'
def __init__(self):
- hippo.CanvasBox.__init__(self)
+ gobject.GObject.__init__(self)
- self._ring_view = ActivitiesRing()
+ self._favorites_view = favoritesview.FavoritesView()
self._list_view = ActivitiesList()
self._enable_xo_palette = False
self._toolbar = HomeToolbar()
self._toolbar.connect('query-changed', self.__toolbar_query_changed_cb)
self._toolbar.connect('view-changed', self.__toolbar_view_changed_cb)
- self.append(hippo.CanvasWidget(widget=self._toolbar))
+ self.pack_start(self._toolbar, expand=False)
+ self._toolbar.show()
- self._set_view(_RING_VIEW)
+ self._set_view(_FAVORITES_VIEW, favoritesview.RANDOM_LAYOUT)
def __toolbar_query_changed_cb(self, toolbar, query):
if self._list_view is None:
@@ -55,49 +57,39 @@ class HomeBox(hippo.CanvasBox, hippo.CanvasItem):
query = query.lower()
self._list_view.set_filter(query)
- def __toolbar_view_changed_cb(self, toolbar, view):
- self._set_view(view)
+ def __toolbar_view_changed_cb(self, toolbar, view, layout):
+ self._set_view(view, layout)
- def _set_view(self, view):
- if view == _RING_VIEW:
+ def _set_view(self, view, layout):
+ if view == _FAVORITES_VIEW:
if self._list_view in self.get_children():
self.remove(self._list_view)
- if self._enable_xo_palette:
- self._ring_view.enable_xo_palette()
+ self._favorites_view.layout = layout
- self.append(self._ring_view, hippo.PACK_EXPAND)
+ if self._enable_xo_palette:
+ self._favorites_view.enable_xo_palette()
+ if self._favorites_view not in self.get_children():
+ self.add(self._favorites_view)
+ self._favorites_view.show()
elif view == _LIST_VIEW:
- if self._ring_view in self.get_children():
- self.remove(self._ring_view)
+ if self._favorites_view in self.get_children():
+ self.remove(self._favorites_view)
- self.append(self._list_view, hippo.PACK_EXPAND)
+ self.add(self._list_view)
+ self._list_view.show()
else:
raise ValueError('Invalid view: %r' % view)
_REDRAW_TIMEOUT = 5 * 60 * 1000 # 5 minutes
def resume(self):
- # TODO: Do we need this?
- #if self._redraw_id is None:
- # self._redraw_id = gobject.timeout_add(self._REDRAW_TIMEOUT,
- # self._redraw_activity_ring)
- # self._redraw_activity_ring()
pass
def suspend(self):
- # TODO: Do we need this?
- #if self._redraw_id is not None:
- # gobject.source_remove(self._redraw_id)
- # self._redraw_id = None
pass
- def _redraw_activity_ring(self):
- # TODO: Do we need this?
- #self._donut.redraw()
- return True
-
def has_activities(self):
# TODO: Do we need this?
#return self._donut.has_activities()
@@ -105,8 +97,8 @@ class HomeBox(hippo.CanvasBox, hippo.CanvasItem):
def enable_xo_palette(self):
self._enable_xo_palette = True
- if self._ring_view is not None:
- self._ring_view.enable_xo_palette()
+ if self._favorites_view is not None:
+ self._favorites_view.enable_xo_palette()
class HomeToolbar(gtk.Toolbar):
__gtype_name__ = 'SugarHomeToolbar'
@@ -117,7 +109,7 @@ class HomeToolbar(gtk.Toolbar):
([str])),
'view-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
- ([int]))
+ ([object, object]))
}
def __init__(self):
@@ -144,16 +136,14 @@ class HomeToolbar(gtk.Toolbar):
self._add_separator(expand=True)
- ring_button = RadioToolButton(named_icon='view-radial', group=None)
- ring_button.props.tooltip = _('Ring view')
- ring_button.props.accelerator = _('<Ctrl>R')
- ring_button.connect('toggled', self.__view_button_toggled_cb,
- _RING_VIEW)
- self.insert(ring_button, -1)
- ring_button.show()
+ favorites_button = FavoritesButton()
+ favorites_button.connect('toggled', self.__view_button_toggled_cb,
+ _FAVORITES_VIEW)
+ self.insert(favorites_button, -1)
+ favorites_button.show()
list_button = RadioToolButton(named_icon='view-list')
- list_button.props.group = ring_button
+ list_button.props.group = favorites_button
list_button.props.tooltip = _('List view')
list_button.props.accelerator = _('<Ctrl>L')
list_button.connect('toggled', self.__view_button_toggled_cb,
@@ -165,8 +155,11 @@ class HomeToolbar(gtk.Toolbar):
def __view_button_toggled_cb(self, button, view):
if button.props.active:
- self.emit('view-changed', view)
-
+ if view == _FAVORITES_VIEW:
+ self.emit('view-changed', view, button.layout)
+ else:
+ self.emit('view-changed', view, None)
+
def _add_separator(self, expand=False):
separator = gtk.SeparatorToolItem()
separator.props.draw = False
@@ -201,3 +194,50 @@ class HomeToolbar(gtk.Toolbar):
self._search_entry.activate()
return False
+class FavoritesButton(RadioToolButton):
+ __gtype_name__ = 'SugarFavoritesButton'
+
+ def __init__(self):
+ RadioToolButton.__init__(self)
+
+ self.props.named_icon = 'view-radial'
+ self.props.tooltip = _('Favorites view')
+ self.props.accelerator = _('<Ctrl>R')
+ self.props.group = None
+
+ self._layout = favoritesview.RANDOM_LAYOUT
+
+ # TRANS: label for the free layout in the favorites view
+ menu_item = MenuItem(_('Free'), 'activity-start')
+ menu_item.connect('activate', self.__layout_activate_cb,
+ favoritesview.RANDOM_LAYOUT)
+ self.props.palette.menu.append(menu_item)
+ menu_item.show()
+
+ # TRANS: label for the ring layout in the favorites view
+ menu_item = MenuItem(_('Ring'), 'view-radial')
+ menu_item.connect('activate', self.__layout_activate_cb,
+ favoritesview.RING_LAYOUT)
+ self.props.palette.menu.append(menu_item)
+ menu_item.show()
+
+ def __layout_activate_cb(self, menu_item, layout):
+ if self._layout == layout and self.props.active:
+ return
+ elif self._layout != layout:
+ if layout == favoritesview.RANDOM_LAYOUT:
+ self.props.named_icon = 'activity-start'
+ elif layout == favoritesview.RING_LAYOUT:
+ self.props.named_icon = 'view-radial'
+ else:
+ raise ValueError('Invalid layout: %r' % layout)
+ self._layout = layout
+ if not self.props.active:
+ self.props.active = True
+ else:
+ self.emit('toggled')
+
+ def _get_layout(self):
+ return self._layout
+ layout = property(_get_layout, None)
+
diff --git a/src/view/home/HomeWindow.py b/src/view/home/HomeWindow.py
index 9151d46..03a571e 100644
--- a/src/view/home/HomeWindow.py
+++ b/src/view/home/HomeWindow.py
@@ -15,7 +15,6 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import gtk
-import hippo
from sugar.graphics import style
from sugar.graphics import palettegroup
@@ -44,10 +43,6 @@ class HomeWindow(gtk.Window):
self._active = False
self._level = ShellModel.ZOOM_HOME
- self._canvas = hippo.Canvas()
- self.add(self._canvas)
- self._canvas.show()
-
self.set_default_size(gtk.gdk.screen_width(),
gtk.gdk.screen_height())
@@ -70,8 +65,9 @@ class HomeWindow(gtk.Window):
self.launch_box = LaunchBox()
self._activate_view()
- self._canvas.set_root(self._home_box)
-
+ self.add(self._home_box)
+ self._home_box.show()
+
self._transition_box.connect('completed',
self._transition_completed_cb)
@@ -122,8 +118,9 @@ class HomeWindow(gtk.Window):
self._deactivate_view()
self._level = level
self._activate_view()
-
- self._canvas.set_root(self._transition_box)
+
+ self.remove(self.get_child())
+ self.add(self._transition_box)
if level == ShellModel.ZOOM_HOME:
size = style.XLARGE_ICON_SIZE
@@ -138,14 +135,22 @@ class HomeWindow(gtk.Window):
def _transition_completed_cb(self, transition_box):
if self._level == ShellModel.ZOOM_HOME:
- self._canvas.set_root(self._home_box)
+ self.remove(self.get_child())
+ self.add(self._home_box)
+ self._home_box.show()
elif self._level == ShellModel.ZOOM_FRIENDS:
- self._canvas.set_root(self._friends_box)
+ self.remove(self.get_child())
+ self.add(self._friends_box)
+ self._friends_box.show()
elif self._level == ShellModel.ZOOM_MESH:
- self._canvas.set_root(self._mesh_box)
+ self.remove(self.get_child())
+ self.add(self._mesh_box)
+ self._mesh_box.show()
self._mesh_box.focus_search_entry()
elif self._level == ShellModel.ZOOM_ACTIVITY:
- self._canvas.set_root(self.launch_box)
+ self.remove(self.get_child())
+ self.add(self.launch_box)
+ self.launch_box.show()
def get_home_box(self):
return self._home_box
diff --git a/src/view/home/Makefile.am b/src/view/home/Makefile.am
index 5306e2b..80781d7 100644
--- a/src/view/home/Makefile.am
+++ b/src/view/home/Makefile.am
@@ -2,7 +2,9 @@ sugardir = $(pkgdatadir)/shell/view/home
sugar_PYTHON = \
__init__.py \
activitieslist.py \
- activitiesring.py \
+ favoritesview.py \
+ favoriteslayout.py \
+ grid.py \
FriendView.py \
FriendsBox.py \
launchbox.py \
diff --git a/src/view/home/MeshBox.py b/src/view/home/MeshBox.py
index db2f80e..5e05623 100644
--- a/src/view/home/MeshBox.py
+++ b/src/view/home/MeshBox.py
@@ -429,9 +429,10 @@ class MeshToolbar(gtk.Toolbar):
self.search_entry.activate()
return False
-class MeshBox(hippo.CanvasBox):
+class MeshBox(gtk.VBox):
+ __gtype_name__ = 'SugarMeshBox'
def __init__(self):
- hippo.CanvasBox.__init__(self)
+ gobject.GObject.__init__(self)
self._model = shellmodel.get_instance().get_mesh()
self._buddies = {}
@@ -441,14 +442,20 @@ class MeshBox(hippo.CanvasBox):
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.append(hippo.CanvasWidget(widget=self._toolbar))
+ 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())
- self.append(self._layout_box, hippo.PACK_EXPAND)
+ canvas.set_root(self._layout_box)
self._layout = SpreadLayout()
self._layout_box.set_layout(self._layout)
@@ -477,21 +484,31 @@ class MeshBox(hippo.CanvasBox):
if self._model.get_mesh():
self._mesh_added_cb(self._model, self._model.get_mesh())
- self._model.connect('mesh-added',
- self._mesh_added_cb)
- self._model.connect('mesh-removed',
- self._mesh_removed_cb)
+ self._model.connect('mesh-added', self.__mesh_added_cb)
+ self._model.connect('mesh-removed', self.__mesh_removed_cb)
- def _mesh_added_cb(self, model, meshdev):
+ def __mesh_added_cb(self, model, meshdev):
self._add_mesh_icon(meshdev, 1)
self._add_mesh_icon(meshdev, 6)
self._add_mesh_icon(meshdev, 11)
- def _mesh_removed_cb(self, model):
+ def __mesh_removed_cb(self, model):
self._remove_mesh_icon(1)
self._remove_mesh_icon(6)
self._remove_mesh_icon(11)
+ 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_alone_buddy(buddy_model)
@@ -533,10 +550,8 @@ class MeshBox(hippo.CanvasBox):
def _add_alone_buddy(self, buddy_model):
icon = BuddyIcon(buddy_model)
if buddy_model.is_owner():
- vertical_offset = - style.GRID_CELL_SIZE
- self._layout.add_center(icon, vertical_offset)
- else:
- self._layout.add(icon)
+ self._owner_icon = icon
+ self._layout.add(icon)
if hasattr(icon, 'set_filter'):
icon.set_filter(self._query)
diff --git a/src/view/home/activitieslist.py b/src/view/home/activitieslist.py
index 8a824fd..5dab09d 100644
--- a/src/view/home/activitieslist.py
+++ b/src/view/home/activitieslist.py
@@ -27,19 +27,25 @@ from sugar.graphics.icon import CanvasIcon
import view.Shell
from view.palettes import ActivityPalette
-class ActivitiesList(hippo.CanvasScrollbars):
+class ActivitiesList(gtk.ScrolledWindow):
__gtype_name__ = 'SugarActivitiesList'
def __init__(self):
- hippo.CanvasScrollbars.__init__(self)
+ gobject.GObject.__init__(self)
- self.set_policy(hippo.ORIENTATION_HORIZONTAL, hippo.SCROLLBAR_NEVER)
- self.props.widget.connect('key-press-event', self.__key_press_event_cb)
+ self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+ self.set_shadow_type(gtk.SHADOW_NONE)
+ self.connect('key-press-event', self.__key_press_event_cb)
+
+ canvas = hippo.Canvas()
+ self.add_with_viewport(canvas)
+ self.child.set_shadow_type(gtk.SHADOW_NONE)
+ canvas.show()
self._query = ''
- self._box = hippo.CanvasBox( \
- background_color=style.COLOR_WHITE.get_int())
- self.set_root(self._box)
+ self._box = hippo.CanvasBox()
+ self._box.props.background_color = style.COLOR_WHITE.get_int()
+ canvas.set_root(self._box)
registry = activity.get_registry()
registry.get_activities_async(reply_handler=self._get_activities_cb)
@@ -81,7 +87,7 @@ class ActivitiesList(hippo.CanvasScrollbars):
def __key_press_event_cb(self, widget, event):
keyname = gtk.gdk.keyval_name(event.keyval)
- vadjustment = self.props.widget.props.vadjustment
+ vadjustment = self.props.vadjustment
if keyname == 'Up':
if vadjustment.props.value > vadjustment.props.lower:
vadjustment.props.value -= vadjustment.props.step_increment
diff --git a/src/view/home/favoriteslayout.py b/src/view/home/favoriteslayout.py
new file mode 100644
index 0000000..0400350
--- /dev/null
+++ b/src/view/home/favoriteslayout.py
@@ -0,0 +1,273 @@
+# 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
+
+from view.home.grid import Grid
+
+_logger = logging.getLogger('FavoritesLayout')
+
+_CELL_SIZE = 4
+_BASE_SCALE = 1000
+
+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):
+ return 0
+
+ def append(self, icon, locked=False):
+ self.box.insert_sorted(icon, 0, self.compare_activities)
+ if hasattr(icon, 'fixed_position'):
+ relative_x, relative_y = icon.fixed_position
+ if relative_x >= 0 and relative_y >= 0:
+ 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)))
+
+ def remove(self, icon):
+ del self.fixed_positions[icon]
+ self.box.remove(icon)
+
+ def move_icon(self, icon, x, y, locked=False):
+ if icon not in self.box.get_children():
+ raise ValueError('Child not in box.')
+
+ if hasattr(icon, 'get_bundle_id') and hasattr(icon, 'get_version'):
+ min_width_, width = self.box.get_width_request()
+ min_height_, height = self.box.get_height_request(width)
+ registry = activity.get_registry()
+ registry.set_activity_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 do_allocate(self, x, y, width, height, req_width, req_height,
+ origin_changed):
+ raise NotImplementedError()
+
+ def allow_dnd(self):
+ return False
+
+class RandomLayout(FavoritesLayout):
+ __gtype_name__ = 'RandomLayout'
+
+ 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)
+
+ def remove(self, icon):
+ self._grid.remove(icon)
+ FavoritesLayout.remove(self, icon)
+
+ 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)
+
+ 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(rect.x * _CELL_SIZE,
+ rect.y * _CELL_SIZE,
+ child_width,
+ child_height,
+ origin_changed)
+
+ def allow_dnd(self):
+ return True
+
+_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)
+ self._locked_children = {}
+
+ 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):
+ 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_in_ring = [child for child in self.box.get_layout_children() \
+ if child not in self._locked_children]
+ return children_in_ring
+
+ def _update_icon_sizes(self):
+ children_in_ring = self._get_children_in_ring()
+ radius_, icon_size = \
+ self._calculate_radius_and_icon_size(len(children_in_ring))
+
+ for child in children_in_ring:
+ child.item.props.size = 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._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)
+
+ child.allocate(int(x), int(y), child_width, child_height,
+ origin_changed)
+
+ 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
+
diff --git a/src/view/home/activitiesring.py b/src/view/home/favoritesview.py
index dccb066..a6e2268 100644
--- a/src/view/home/activitiesring.py
+++ b/src/view/home/favoritesview.py
@@ -34,48 +34,66 @@ import view.Shell
from view.palettes import JournalPalette
from view.palettes import CurrentActivityPalette, ActivityPalette
from view.home.MyIcon import MyIcon
+from view.home import favoriteslayout
from model import shellmodel
from model.shellmodel import ShellModel
from hardware import schoolserver
from controlpanel.gui import ControlPanel
from session import get_session_manager
-_logger = logging.getLogger('ActivitiesRing')
+_logger = logging.getLogger('FavoritesView')
-class ActivitiesRing(hippo.CanvasBox, hippo.CanvasItem):
- __gtype_name__ = 'SugarActivitiesRing'
+_ICON_DND_TARGET = ('activity-icon', gtk.TARGET_SAME_WIDGET, 0)
- def __init__(self):
- hippo.CanvasBox.__init__(self,
- background_color=style.COLOR_WHITE.get_int())
+RING_LAYOUT = 0
+RANDOM_LAYOUT = 1
- shell_model = shellmodel.get_instance()
- shell_model.connect('notify::state', self._shell_state_changed_cb)
+_LAYOUT_MAP = {RING_LAYOUT: favoriteslayout.RingLayout,
+ RANDOM_LAYOUT: favoriteslayout.RandomLayout}
+
+class FavoritesView(hippo.Canvas):
+ __gtype_name__ = 'SugarFavoritesView'
- self._my_icon = _MyIcon(style.XLARGE_ICON_SIZE)
- self.append(self._my_icon, hippo.PACK_FIXED)
+ def __init__(self, **kwargs):
+ gobject.GObject.__init__(self, **kwargs)
- self._current_activity = CurrentActivityIcon()
- self.append(self._current_activity, hippo.PACK_FIXED)
+ # DND stuff
+ self._pressed_button = None
+ self._press_start_x = None
+ self._press_start_y = None
+ self._last_clicked_icon = None
- self.set_layout(RingLayout())
+ self._box = hippo.CanvasBox()
+ self._box.props.background_color = style.COLOR_WHITE.get_int()
+ self.set_root(self._box)
+
+ self._my_icon = None
+ self._current_activity = None
+ self._layout = None
+ self._set_layout(RANDOM_LAYOUT)
registry = activity.get_registry()
- registry.get_activities_async(reply_handler=self._get_activities_cb)
registry.connect('activity-added', self.__activity_added_cb)
registry.connect('activity-removed', self.__activity_removed_cb)
registry.connect('activity-changed', self.__activity_changed_cb)
- 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
+ shell_model = shellmodel.get_instance()
+ shell_model.connect('notify::state', self._shell_state_changed_cb)
+
+ # 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)
def _add_activity(self, activity_info):
icon = ActivityIcon(activity_info)
- self.insert_sorted(icon, 0, self._compare_activities)
+ icon.props.size = style.STANDARD_ICON_SIZE
+ self._layout.append(icon)
def _get_activities_cb(self, activity_list):
for info in activity_list:
@@ -88,7 +106,7 @@ class ActivitiesRing(hippo.CanvasBox, hippo.CanvasItem):
self._add_activity(activity_info)
def _find_activity_icon(self, bundle_id, version):
- for icon in self.get_children():
+ for icon in self._box.get_children():
if isinstance(icon, ActivityIcon) and \
icon.bundle_id == bundle_id and icon.version == version:
return icon
@@ -98,16 +116,16 @@ class ActivitiesRing(hippo.CanvasBox, hippo.CanvasItem):
icon = self._find_activity_icon(activity_info.bundle_id,
activity_info.version)
if icon is not None:
- self.remove(icon)
+ self._layout.remove(icon)
def __activity_changed_cb(self, activity_registry, activity_info):
- if activity_info.bundle_id == "org.laptop.JournalActivity":
+ if activity_info.bundle_id == 'org.laptop.JournalActivity':
return
icon = self._find_activity_icon(activity_info.bundle_id,
activity_info.version)
- if icon is not None and not activity_info.favorite:
- self.remove(icon)
- elif icon is None and activity_info.favorite:
+ if icon is not None:
+ self._box.remove(icon)
+ if activity_info.favorite:
self._add_activity(activity_info)
def _shell_state_changed_cb(self, model, pspec):
@@ -115,23 +133,137 @@ class ActivitiesRing(hippo.CanvasBox, hippo.CanvasItem):
if model.props.state == ShellModel.STATE_SHUTDOWN:
pass
- def do_allocate(self, width, height, origin_changed):
- hippo.CanvasBox.do_allocate(self, width, height, origin_changed)
+ def do_size_allocate(self, allocation):
+ width = allocation.width
+ height = allocation.height
- [my_icon_width, my_icon_height] = self._my_icon.get_allocation()
+ 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.set_position(self._my_icon, x, y)
+ self._layout.move_icon(self._my_icon, x, y, locked=True)
- [icon_width, icon_height] = self._current_activity.get_allocation()
+ 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.DEFAULT_PADDING \
- - style.GRID_CELL_SIZE) / 2
- self.set_position(self._current_activity, x, y)
+ 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)
def enable_xo_palette(self):
self._my_icon.enable_palette()
+ # 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
+
+ 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
+ return False
+
+ if event.is_hint:
+ x, y, state_ = event.window.get_pointer()
+ else:
+ x = event.x
+ y = event.y
+
+ if widget.drag_check_threshold(int(self._press_start_x),
+ int(self._press_start_y),
+ int(x),
+ int(y)):
+ context_ = widget.drag_begin([_ICON_DND_TARGET],
+ gtk.gdk.ACTION_MOVE,
+ 1,
+ 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)
+
+ hot_spot = style.zoom(10)
+ context.set_icon_pixbuf(pixbuf, hot_spot, hot_spot)
+
+ def __drag_motion_cb(self, widget, context, x, y, time):
+ if self._last_clicked_icon is not None:
+ context.drag_status(context.suggested_action, time)
+ return True
+ else:
+ return False
+
+ 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, y)
+
+ self._pressed_button = None
+ self._press_start_x = None
+ self._press_start_y = None
+ self._last_clicked_icon = None
+
+ return True
+ else:
+ return False
+
+ 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:
+ raise ValueError('Unknown favorites layout: %r' % layout)
+ if type(self._layout) != _LAYOUT_MAP[layout]:
+ self._box.clear()
+ self._layout = _LAYOUT_MAP[layout]()
+ self._box.set_layout(self._layout)
+
+ self._my_icon = _MyIcon(style.XLARGE_ICON_SIZE)
+ self._my_icon.log = True
+ self._layout.append(self._my_icon, locked=True)
+
+ self._current_activity = CurrentActivityIcon()
+ self._layout.append(self._current_activity, locked=True)
+
+ registry = activity.get_registry()
+ registry.get_activities_async(reply_handler=self._get_activities_cb)
+
+ 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()
+
+ layout = property(None, _set_layout)
+
class ActivityIcon(CanvasIcon):
def __init__(self, activity_info):
CanvasIcon.__init__(self, cache=True, file_name=activity_info.icon)
@@ -167,6 +299,10 @@ class ActivityIcon(CanvasIcon):
return self._activity_info.installation_time
installation_time = property(_get_installation_time, None)
+ def _get_fixed_position(self):
+ return self._activity_info.position
+ fixed_position = property(_get_fixed_position, None)
+
class CurrentActivityIcon(CanvasIcon, hippo.CanvasItem):
def __init__(self):
CanvasIcon.__init__(self, cache=True)
@@ -184,12 +320,11 @@ class CurrentActivityIcon(CanvasIcon, hippo.CanvasItem):
self._home_model.get_active_activity().get_window().activate(1)
def _update(self, home_activity):
- _logger.debug('CurrentActivityIcon._update')
self.props.file_name = home_activity.get_icon_path()
self.props.xo_color = home_activity.get_icon_color()
self.props.size = style.STANDARD_ICON_SIZE
- if home_activity.get_type() == "org.laptop.JournalActivity":
+ if home_activity.get_type() == 'org.laptop.JournalActivity':
palette = JournalPalette(home_activity)
else:
palette = CurrentActivityPalette(home_activity)
@@ -198,95 +333,6 @@ class CurrentActivityIcon(CanvasIcon, hippo.CanvasItem):
def __active_activity_changed_cb(self, home_model, home_activity):
self._update(home_activity)
-class RingLayout(gobject.GObject, hippo.CanvasLayout):
- __gtype_name__ = 'SugarRingLayout'
- 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, gtk.gdk.screen_height() - style.GRID_CELL_SIZE
-
- def do_get_width_request(self):
- return 0, gtk.gdk.screen_width()
-
- def _calculate_radius_and_icon_size(self, children_count):
- 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
- angle = 2 * math.pi / children_count
-
- _logger.debug('minimum_radius %r maximum_radius %r angle %r' % \
- (minimum_radius, maximum_radius, angle))
-
- # 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))
-
- _logger.debug('radius 1 %r' % radius)
-
- 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
-
- _logger.debug('radius 2 %r icon_size %r' % (radius, icon_size))
-
- 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 do_allocate(self, x, y, width, height, req_width, req_height,
- origin_changed):
- _logger.debug('RingLayout.do_allocate: %r %r %r %r %r %r %r' % (x, y,
- width, height, req_width, req_height, origin_changed))
-
- children = self._box.get_layout_children()
- if not children:
- return
-
- radius, icon_size = self._calculate_radius_and_icon_size(len(children))
-
- for n in range(len(children)):
- child = children[n]
- # TODO: We get here a glib warning and I don't know why.
- child.item.props.size = icon_size
-
- x, y = self._calculate_position(radius, icon_size, n, len(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)
-
- child.allocate(int(x),
- int(y),
- child_width,
- child_height,
- origin_changed)
-
class _MyIcon(MyIcon):
def __init__(self, scale):
MyIcon.__init__(self, scale)
diff --git a/src/view/home/grid.py b/src/view/home/grid.py
new file mode 100644
index 0000000..abea706
--- /dev/null
+++ b/src/view/home/grid.py
@@ -0,0 +1,223 @@
+# Copyright (C) 2007 Red Hat, Inc.
+# 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
+
+from numpy import array
+import random
+
+import gobject
+import gtk
+
+_PLACE_TRIALS = 20
+_MAX_WEIGHT = 255
+_REFRESH_RATE = 200
+_MAX_COLLISIONS_PER_REFRESH = 20
+
+class Grid(gobject.GObject):
+ __gsignals__ = {
+ 'child-changed' : (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT]))
+ }
+ def __init__(self, width, height):
+ gobject.GObject.__init__(self)
+
+ self.width = width
+ self.height = height
+ self._children = []
+ self._child_rects = {}
+ self._locked_children = set()
+ self._collisions = []
+ self._collisions_sid = 0
+
+ self._array = array([0], dtype='b')
+ self._array.resize(width * height)
+
+ def add(self, child, width, height, x=None, y=None, locked=False):
+ if x is not None and y is not None:
+ rect = gtk.gdk.Rectangle(x, y, width, height)
+ weight = self._compute_weight(rect)
+ else:
+ trials = _PLACE_TRIALS
+ weight = _MAX_WEIGHT
+ while trials > 0 and weight:
+ x = int(random.random() * (self.width - width))
+ y = int(random.random() * (self.height - height))
+
+ rect = gtk.gdk.Rectangle(x, y, width, height)
+ new_weight = self._compute_weight(rect)
+ if weight > new_weight:
+ weight = new_weight
+
+ trials -= 1
+
+ self._child_rects[child] = rect
+ self._children.append(child)
+ self._add_weight(self._child_rects[child])
+ if locked:
+ self._locked_children.add(child)
+
+ if weight > 0:
+ self._detect_collisions(child)
+
+ def remove(self, child):
+ self._children.remove(child)
+ self._remove_weight(self._child_rects[child])
+ self._locked_children.discard(child)
+ del self._child_rects[child]
+
+ def move(self, child, x, y, locked=False):
+ self._remove_weight(self._child_rects[child])
+
+ rect = self._child_rects[child]
+ rect.x = x
+ rect.y = y
+
+ weight = self._compute_weight(rect)
+ self._add_weight(self._child_rects[child])
+
+ if locked:
+ self._locked_children.add(child)
+ else:
+ self._locked_children.discard(child)
+
+ if weight > 0:
+ self._detect_collisions(child)
+
+ def _shift_child(self, child, weight):
+ rect = self._child_rects[child]
+
+ new_rects = []
+
+ # Get rects right, left, bottom and top
+ if (rect.x + rect.width < self.width - 1):
+ new_rects.append(gtk.gdk.Rectangle(rect.x + 1, rect.y,
+ rect.width, rect.height))
+
+ if (rect.x - 1 > 0):
+ new_rects.append(gtk.gdk.Rectangle(rect.x - 1, rect.y,
+ rect.width, rect.height))
+
+ if (rect.y + rect.height < self.height - 1):
+ new_rects.append(gtk.gdk.Rectangle(rect.x, rect.y + 1,
+ rect.width, rect.height))
+
+ if (rect.y - 1 > 0):
+ new_rects.append(gtk.gdk.Rectangle(rect.x, rect.y - 1,
+ rect.width, rect.height))
+
+ # Get diagonal rects
+ if rect.x + rect.width < self.width - 1 and \
+ rect.y + rect.height < self.height - 1:
+ new_rects.append(gtk.gdk.Rectangle(rect.x + 1, rect.y + 1,
+ rect.width, rect.height))
+
+ if rect.x - 1 > 0 and rect.y + rect.height < self.height - 1:
+ new_rects.append(gtk.gdk.Rectangle(rect.x - 1, rect.y + 1,
+ rect.width, rect.height))
+
+ if rect.x + rect.width < self.width - 1 and rect.y - 1 > 0:
+ new_rects.append(gtk.gdk.Rectangle(rect.x + 1, rect.y - 1,
+ rect.width, rect.height))
+
+ if rect.x - 1 > 0 and rect.y - 1 > 0:
+ new_rects.append(gtk.gdk.Rectangle(rect.x - 1, rect.y - 1,
+ rect.width, rect.height))
+
+ random.shuffle(new_rects)
+
+ best_rect = None
+ for new_rect in new_rects:
+ new_weight = self._compute_weight(new_rect)
+ if new_weight < weight:
+ best_rect = new_rect
+ weight = new_weight
+
+ if best_rect:
+ self._child_rects[child] = best_rect
+ weight = self._shift_child(child, weight)
+
+ return weight
+
+ def __solve_collisions_cb(self):
+ for i in range(_MAX_COLLISIONS_PER_REFRESH):
+ collision = self._collisions.pop(0)
+
+ old_rect = self._child_rects[collision]
+ self._remove_weight(old_rect)
+ weight = self._compute_weight(old_rect)
+ weight = self._shift_child(collision, weight)
+ self._add_weight(self._child_rects[collision])
+
+ # TODO: we shouldn't give up the first time we failed to find a
+ # better position.
+ if old_rect != self._child_rects[collision]:
+ self._detect_collisions(collision)
+ self.emit('child-changed', collision)
+ if weight > 0:
+ self._collisions.append(collision)
+
+ if not self._collisions:
+ self._collisions_sid = 0
+ return False
+
+ return True
+
+ def _detect_collisions(self, child):
+ collision_found = False
+ child_rect = self._child_rects[child]
+ for c in self._children:
+ intersection = child_rect.intersect(self._child_rects[c])
+ if c != child and intersection.width > 0:
+ if c not in self._locked_children and c not in self._collisions:
+ collision_found = True
+ self._collisions.append(c)
+
+ if collision_found:
+ if child not in self._collisions:
+ self._collisions.append(child)
+
+ if len(self._collisions) and not self._collisions_sid:
+ self._collisions_sid = gobject.timeout_add(_REFRESH_RATE,
+ self.__solve_collisions_cb, priority=gobject.PRIORITY_LOW)
+
+ def _add_weight(self, rect):
+ for i in range(rect.x, rect.x + rect.width):
+ for j in range(rect.y, rect.y + rect.height):
+ self[j, i] += 1
+
+ def _remove_weight(self, rect):
+ for i in range(rect.x, rect.x + rect.width):
+ for j in range(rect.y, rect.y + rect.height):
+ self[j, i] -= 1
+
+ def _compute_weight(self, rect):
+ weight = 0
+
+ for i in range(rect.x, rect.x + rect.width):
+ for j in range(rect.y, rect.y + rect.height):
+ weight += self[j, i]
+
+ return weight
+
+ def __getitem__(self, (row, col)):
+ return self._array[col + row * self.width]
+
+ def __setitem__(self, (row, col), value):
+ self._array[col + row * self.width] = value
+
+ def get_child_rect(self, child):
+ return self._child_rects[child]
diff --git a/src/view/home/launchbox.py b/src/view/home/launchbox.py
index a70cb61..dfbec40 100644
--- a/src/view/home/launchbox.py
+++ b/src/view/home/launchbox.py
@@ -25,10 +25,9 @@ from sugar.graphics.xocolor import XoColor
from model import shellmodel
from view.pulsingicon import CanvasPulsingIcon
-class LaunchBox(hippo.CanvasBox):
+class LaunchBox(hippo.Canvas):
def __init__(self):
- gobject.GObject.__init__(
- self, background_color=style.COLOR_WHITE.get_int())
+ gobject.GObject.__init__(self)
self._activity_icon = CanvasPulsingIcon()
@@ -38,8 +37,9 @@ class LaunchBox(hippo.CanvasBox):
style.COLOR_TRANSPARENT.get_svg()))
vbox = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL)
+ vbox.props.background_color = style.COLOR_WHITE.get_int()
vbox.append(self._activity_icon, hippo.PACK_EXPAND)
- self.append(vbox, hippo.PACK_EXPAND)
+ self.set_root(vbox)
self._animator = animator.Animator(1.0)
diff --git a/src/view/home/spreadlayout.py b/src/view/home/spreadlayout.py
index 0ad350a..f7584ab 100644
--- a/src/view/home/spreadlayout.py
+++ b/src/view/home/spreadlayout.py
@@ -14,159 +14,15 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-from numpy import array
-from random import random
-
import hippo
import gobject
import gtk
from sugar.graphics import style
-_PLACE_TRIALS = 20
-_MAX_WEIGHT = 255
-_CELL_SIZE = 4
-
-class _Grid(gobject.GObject):
- __gsignals__ = {
- 'child-changed' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT]))
- }
- def __init__(self, width, height):
- gobject.GObject.__init__(self)
-
- self.width = width
- self.height = height
- self._children = []
- self._collisions = []
- self._collisions_sid = 0
-
- self._array = array([0], dtype='b')
- self._array.resize(width * height)
-
- def add(self, child, width, height):
- trials = _PLACE_TRIALS
- weight = _MAX_WEIGHT
- while trials > 0 and weight:
- x = int(random() * (self.width - width))
- y = int(random() * (self.height - height))
-
- rect = gtk.gdk.Rectangle(x, y, width, height)
- new_weight = self._compute_weight(rect)
- if weight > new_weight:
- weight = new_weight
-
- trials -= 1
-
- child.grid_rect = rect
- child.locked = False
-
- self._add_child(child)
-
- if weight > 0:
- self._detect_collisions(child)
-
- def remove(self, child):
- self._children.remove(child)
- self._remove_weight(child.grid_rect)
- child.grid_rect = None
-
- def _add_child(self, child):
- self._children.append(child)
- self.add_weight(child.grid_rect)
-
- def _move_child(self, child, new_rect):
- self._remove_weight(child.grid_rect)
- self.add_weight(new_rect)
-
- child.grid_rect = new_rect
-
- self.emit('child-changed', child)
-
- def _shift_child(self, child):
- rect = child.grid_rect
- weight = self._compute_weight(rect)
- new_rects = []
-
- if (rect.x + rect.width < self.width - 1):
- new_rects.append(gtk.gdk.Rectangle(rect.x + 1, rect.y,
- rect.width, rect.height))
-
- if (rect.x - 1 > 0):
- new_rects.append(gtk.gdk.Rectangle(rect.x - 1, rect.y,
- rect.width, rect.height))
-
- if (rect.y + rect.height < self.height - 1):
- new_rects.append(gtk.gdk.Rectangle(rect.x, rect.y + 1,
- rect.width, rect.height))
-
- if (rect.y - 1 > 0):
- new_rects.append(gtk.gdk.Rectangle(rect.x, rect.y - 1,
- rect.width, rect.height))
-
- best_rect = None
- for new_rect in new_rects:
- new_weight = self._compute_weight(new_rect)
- if new_weight < weight:
- best_rect = new_rect
- weight = new_weight
-
- if best_rect:
- self._move_child(child, best_rect)
-
- return weight
-
-
- def _solve_collisions(self):
- for collision in self._collisions[:]:
- weight = self._shift_child(collision)
- if not weight:
- self._collisions.remove(collision)
-
- return (len(self._collisions) > 0)
-
- def _detect_collisions(self, child):
- collision_found = False
- for c in self._children:
- intersection = child.grid_rect.intersect(c.grid_rect)
- if c != child and intersection.width > 0:
- if c not in self._collisions:
- collision_found = True
- self._collisions.append(c)
-
- if collision_found:
- if child not in self._collisions:
- self._collisions.append(child)
-
-# if len(self._collisions) and not self._collisions_sid:
-# self._collisions_sid = gobject.idle_add(self._solve_collisions)
-
- def add_weight(self, rect):
- for i in range(rect.x, rect.x + rect.width):
- for j in range(rect.y, rect.y + rect.height):
- self[j, i] += 1
-
- def _remove_weight(self, rect):
- for i in range(rect.x, rect.x + rect.width):
- for j in range(rect.y, rect.y + rect.height):
- self[j, i] -= 1
-
- def _compute_weight(self, rect):
- weight = 0
-
- for i in range(rect.x, rect.x + rect.width):
- for j in range(rect.y, rect.y + rect.height):
- weight += self[j, i]
-
- return weight
-
- def __getitem__(self, (row, col)):
- return self._array[col + row * self.width]
-
- def __setitem__(self, (row, col), value):
- self._array[col + row * self.width] = value
+from view.home.grid import Grid
+_CELL_SIZE = 4
class SpreadLayout(gobject.GObject, hippo.CanvasLayout):
__gtype_name__ = 'SugarSpreadLayout'
@@ -177,35 +33,22 @@ class SpreadLayout(gobject.GObject, hippo.CanvasLayout):
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 = Grid(width / _CELL_SIZE, height / _CELL_SIZE)
self._grid.connect('child-changed', self._grid_child_changed_cb)
- def add_center(self, child, vertical_offset=0):
- self._box.append(child)
-
- width, height = self._get_child_grid_size(child)
- rect = gtk.gdk.Rectangle(int((self._grid.width - width) / 2),
- int((self._grid.height - height) / 2),
- width + 1, height + 1)
- self._grid.add_weight(rect)
-
- box_child = self._box.find_box_child(child)
- box_child.grid_rect = None
- box_child.vertical_offset = vertical_offset
-
def add(self, child):
self._box.append(child)
width, height = self._get_child_grid_size(child)
- box_child = self._box.find_box_child(child)
- self._grid.add(box_child, width, height)
+ self._grid.add(child, width, height)
def remove(self, child):
- box_child = self._box.find_box_child(child)
- self._grid.remove(box_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
@@ -222,19 +65,12 @@ class SpreadLayout(gobject.GObject, hippo.CanvasLayout):
min_w, child_width = child.get_width_request()
min_h, child_height = child.get_height_request(child_width)
- rect = child.grid_rect
- if child.grid_rect:
- child.allocate(rect.x * _CELL_SIZE,
- rect.y * _CELL_SIZE,
- rect.width * _CELL_SIZE,
- rect.height * _CELL_SIZE,
- origin_changed)
- else:
- vertical_offset = child.vertical_offset
- child_x = x + (width - child_width) / 2
- child_y = y + (height - child_height + vertical_offset) / 2
- child.allocate(child_x, child_y, child_width, child_height,
- origin_changed)
+ rect = self._grid.get_child_rect(child.item)
+ child.allocate(rect.x * _CELL_SIZE,
+ rect.y * _CELL_SIZE,
+ rect.width * _CELL_SIZE,
+ rect.height * _CELL_SIZE,
+ origin_changed)
def _get_child_grid_size(self, child):
min_width, width = child.get_width_request()
@@ -242,5 +78,6 @@ class SpreadLayout(gobject.GObject, hippo.CanvasLayout):
return int(width / _CELL_SIZE), int(height / _CELL_SIZE)
- def _grid_child_changed_cb(self, grid, box_child):
- box_child.item.emit_request_changed()
+ def _grid_child_changed_cb(self, grid, child):
+ child.emit_request_changed()
+
diff --git a/src/view/home/transitionbox.py b/src/view/home/transitionbox.py
index c4ef6ca..fb351f8 100644
--- a/src/view/home/transitionbox.py
+++ b/src/view/home/transitionbox.py
@@ -59,7 +59,7 @@ class _Layout(gobject.GObject, hippo.CanvasLayout):
y + (height - child_height) / 2,
child_width, child_height, origin_changed)
-class TransitionBox(hippo.CanvasBox):
+class TransitionBox(hippo.Canvas):
__gtype_name__ = 'SugarTransitionBox'
__gsignals__ = {
@@ -68,16 +68,19 @@ class TransitionBox(hippo.CanvasBox):
}
def __init__(self):
- hippo.CanvasBox.__init__(self,
- background_color=style.COLOR_WHITE.get_int())
+ gobject.GObject.__init__(self)
+
+ self._box = hippo.CanvasBox()
+ self._box.props.background_color = style.COLOR_WHITE.get_int()
+ self.set_root(self._box)
self._size = style.XLARGE_ICON_SIZE
self._layout = _Layout()
- self.set_layout(self._layout)
+ self._box.set_layout(self._layout)
self._my_icon = MyIcon(self._size)
- self.append(self._my_icon)
+ self._box.append(self._my_icon)
self._animator = animator.Animator(0.3)
self._animator.connect('completed', self._animation_completed_cb)
diff --git a/src/view/keyhandler.py b/src/view/keyhandler.py
index d9feda1..c7d3001 100644
--- a/src/view/keyhandler.py
+++ b/src/view/keyhandler.py
@@ -43,26 +43,25 @@ _actions_table = {
'F4' : 'zoom_activity',
'F9' : 'brightness_down',
'F10' : 'brightness_up',
- '<ctrl>F9' : 'brightness_min',
- '<ctrl>F10' : 'brightness_max',
+ '<alt>F9' : 'brightness_min',
+ '<alt>F10' : 'brightness_max',
'F11' : 'volume_down',
'F12' : 'volume_up',
- '<ctrl>F11' : 'volume_min',
- '<ctrl>F12' : 'volume_max',
+ '<alt>F11' : 'volume_min',
+ '<alt>F12' : 'volume_max',
'<alt>1' : 'screenshot',
- '<alt>f' : 'frame',
'0x93' : 'frame',
'0xEB' : 'rotate',
- '<alt>r' : 'rotate',
- '<alt>q' : 'quit_emulator',
'<alt>Tab' : 'next_window',
- '<alt>n' : 'next_window',
'<alt><shift>Tab': 'previous_window',
- '<alt>p' : 'previous_window',
- '<ctrl>Escape' : 'close_window',
- '<ctrl>q' : 'close_window',
+ '<alt>Escape' : 'close_window',
'0xDC' : 'open_search',
- '<alt>s' : 'say_text'
+# the following are intended for emulator users
+ '<alt><shift>f' : 'frame',
+ '<alt><shift>q' : 'quit_emulator',
+ '<alt><shift>o' : 'open_search',
+ '<alt><shift>r' : 'rotate',
+ '<alt><shift>s' : 'say_text',
}
J_DBUS_SERVICE = 'org.laptop.Journal'
diff --git a/src/view/palettes.py b/src/view/palettes.py
index bda7f95..8406fd0 100644
--- a/src/view/palettes.py
+++ b/src/view/palettes.py
@@ -116,11 +116,11 @@ class ActivityPalette(Palette):
def _update_favorite_item(self):
label = self._favorite_item.child
if self._favorite:
- label.set_text(_('Remove from ring'))
+ label.set_text(_('Remove favorite'))
xo_color = XoColor('%s,%s' % (style.COLOR_WHITE.get_svg(),
style.COLOR_TRANSPARENT.get_svg()))
else:
- label.set_text(_('Add to ring'))
+ label.set_text(_('Make favorite'))
xo_color = profile.get_color()
self._favorite_icon.props.xo_color = xo_color