Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomeu Vizoso <tomeu@tomeuvizoso.net>2008-04-02 12:43:08 (GMT)
committer Tomeu Vizoso <tomeu@tomeuvizoso.net>2008-04-02 12:43:08 (GMT)
commite79aea7e6bb84d5a24c2566592fa17693e042c4b (patch)
tree009d72ab9c20c140079dc3d7c7cb221d5779a3ba
parentce379c6e75e78b62ad6a45cdc2dd03b32bb0a62d (diff)
New activities ring.
-rw-r--r--data/Makefile.am1
-rw-r--r--data/activities.defaults19
-rw-r--r--service/activityregistryservice.py20
-rw-r--r--service/bundleregistry.py44
-rw-r--r--src/model/homemodel.py8
-rw-r--r--src/view/Makefile.am1
-rw-r--r--src/view/frame/activitiestray.py127
-rw-r--r--src/view/home/HomeBox.py3
-rw-r--r--src/view/home/MeshBox.py82
-rw-r--r--src/view/home/activitieslist.py60
-rw-r--r--src/view/home/activitiesring.py272
-rw-r--r--src/view/pulsingicon.py178
12 files changed, 518 insertions, 297 deletions
diff --git a/data/Makefile.am b/data/Makefile.am
index 698721d..7771ce9 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -8,7 +8,6 @@ sugar-xo.gtkrc: gtkrc.em
sugardir = $(pkgdatadir)/data
sugar_DATA = \
- activities.defaults \
kbdconfig \
mime.defaults \
$(GTKRC_FILES)
diff --git a/data/activities.defaults b/data/activities.defaults
deleted file mode 100644
index af368f0..0000000
--- a/data/activities.defaults
+++ /dev/null
@@ -1,19 +0,0 @@
-# Ordered list of the activities displayed in the frame
-
-org.laptop.Chat
-org.laptop.WebActivity
-org.laptop.AbiWordActivity
-org.laptop.RecordActivity
-org.laptop.Oficina
-org.laptop.TamTamMini
-org.vpri.EtoysActivity
-org.laptop.TurtleArtActivity
-org.laptop.Pippy
-org.laptop.Calculate
-org.laptop.Terminal
-org.laptop.MeasureActivity
-org.laptop.AcousticMeasure
-org.laptop.Memorize
-org.laptop.TamTamJam
-org.laptop.TamTamEdit
-org.laptop.TamTamSynthLab
diff --git a/service/activityregistryservice.py b/service/activityregistryservice.py
index 9c2dda7..9ead767 100644
--- a/service/activityregistryservice.py
+++ b/service/activityregistryservice.py
@@ -33,6 +33,7 @@ class ActivityRegistry(dbus.service.Object):
bundle_registry = bundleregistry.get_registry()
bundle_registry.connect('bundle-added', self._bundle_added_cb)
bundle_registry.connect('bundle-removed', self._bundle_removed_cb)
+ bundle_registry.connect('bundle-changed', self._bundle_changed_cb)
@dbus.service.method(_ACTIVITY_REGISTRY_IFACE,
in_signature='s', out_signature='b')
@@ -101,6 +102,12 @@ class ActivityRegistry(dbus.service.Object):
result.append(self._bundle_to_dict(bundle))
return result
+ @dbus.service.method(_ACTIVITY_REGISTRY_IFACE,
+ in_signature='sib', out_signature='')
+ def SetActivityFavorite(self, bundle_id, version, favorite):
+ registry = bundleregistry.get_registry()
+ registry.set_bundle_favorite(bundle_id, version, favorite)
+
@dbus.service.signal(_ACTIVITY_REGISTRY_IFACE, signature='a{sv}')
def ActivityAdded(self, activity_info):
pass
@@ -109,14 +116,22 @@ class ActivityRegistry(dbus.service.Object):
def ActivityRemoved(self, activity_info):
pass
+ @dbus.service.signal(_ACTIVITY_REGISTRY_IFACE, signature='a{sv}')
+ def ActivityChanged(self, activity_info):
+ pass
+
def _bundle_to_dict(self, bundle):
+ registry = bundleregistry.get_registry()
+ favorite = registry.is_bundle_favorite(bundle.get_bundle_id(),
+ bundle.get_activity_version())
return {'name': bundle.get_name(),
'icon': bundle.get_icon(),
'bundle_id': bundle.get_bundle_id(),
'version': bundle.get_activity_version(),
'path': bundle.get_path(),
'command': bundle.get_command(),
- 'show_launcher': bundle.get_show_launcher()}
+ 'show_launcher': bundle.get_show_launcher(),
+ 'favorite': favorite}
def _bundle_added_cb(self, bundle_registry, bundle):
self.ActivityAdded(self._bundle_to_dict(bundle))
@@ -124,6 +139,9 @@ class ActivityRegistry(dbus.service.Object):
def _bundle_removed_cb(self, bundle_registry, bundle):
self.ActivityRemoved(self._bundle_to_dict(bundle))
+ def _bundle_changed_cb(self, bundle_registry, bundle):
+ self.ActivityChanged(self._bundle_to_dict(bundle))
+
_instance = None
def get_instance():
diff --git a/service/bundleregistry.py b/service/bundleregistry.py
index 25a3440..9cf81cc 100644
--- a/service/bundleregistry.py
+++ b/service/bundleregistry.py
@@ -18,6 +18,7 @@ import os
import logging
import gobject
+import simplejson
from sugar.bundle.activitybundle import ActivityBundle
from sugar.bundle.bundle import MalformedBundleException
@@ -51,9 +52,11 @@ class BundleRegistry(gobject.GObject):
"""Service that tracks the available activity bundles"""
__gsignals__ = {
- 'bundle-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT])),
+ 'bundle-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
'bundle-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
+ 'bundle-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT]))
}
@@ -64,6 +67,17 @@ class BundleRegistry(gobject.GObject):
self._search_path = []
self._mime_defaults = _load_mime_defaults()
+ path = env.get_profile_path('favorite_activities')
+ if os.path.exists(path):
+ try:
+ self._favorite_bundles = simplejson.load(open(path))
+ print 'loaded %r' % self._favorite_bundles
+ except ValueError, e:
+ logging.error('Error while loading favorite_activities: %r.' % e)
+ self._favorite_bundles = []
+ else:
+ self._favorite_bundles = []
+
def get_bundle(self, bundle_id):
"""Returns an bundle given his service name"""
for bundle in self._bundles:
@@ -139,6 +153,32 @@ class BundleRegistry(gobject.GObject):
else:
return None
+ def _find_bundle(self, bundle_id, version):
+ for bundle in self._bundles:
+ if bundle.get_bundle_id() == bundle_id and \
+ bundle.get_activity_version() == version:
+ return bundle
+ raise ValueError('No bundle %r with version %r exists.' % \
+ (bundle_id, version))
+
+ def set_bundle_favorite(self, bundle_id, version, favorite):
+ 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()
+
+ def is_bundle_favorite(self, bundle_id, version):
+ return [bundle_id, version] in self._favorite_bundles
+
+ def _write_favorites_file(self):
+ path = env.get_profile_path('favorite_activities')
+ simplejson.dump(self._favorite_bundles, open(path, 'w'))
+
def get_registry():
return _bundle_registry
diff --git a/src/model/homemodel.py b/src/model/homemodel.py
index 44d5417..8de989c 100644
--- a/src/model/homemodel.py
+++ b/src/model/homemodel.py
@@ -18,7 +18,6 @@ import logging
import gobject
import wnck
-import dbus
from sugar import wm
from sugar import activity
@@ -270,7 +269,9 @@ class HomeModel(gobject.GObject):
def notify_activity_launch_failed(self, activity_id):
home_activity = self._get_activity_by_id(activity_id)
if home_activity:
- logging.debug("Activity %s (%s) launch failed" % (activity_id, home_activity.get_type()))
+ logging.debug("Activity %s (%s) launch failed" % \
+ (activity_id, home_activity.get_type()))
+ home_activity.props.launching = False
self._remove_activity(home_activity)
else:
logging.error('Model for activity id %s does not exist.' % activity_id)
@@ -278,6 +279,7 @@ class HomeModel(gobject.GObject):
def _check_activity_launched(self, activity_id):
home_activity = self._get_activity_by_id(activity_id)
if home_activity and home_activity.props.launching:
- logging.debug('Activity %s still launching, assuming it failed...', activity_id)
+ logging.debug('Activity %s still launching, assuming it failed...',
+ activity_id)
self.notify_activity_launch_failed(activity_id)
return False
diff --git a/src/view/Makefile.am b/src/view/Makefile.am
index abbb230..b09d965 100644
--- a/src/view/Makefile.am
+++ b/src/view/Makefile.am
@@ -11,4 +11,5 @@ sugar_PYTHON = \
keyhandler.py \
pulsingicon.py \
OverlayWindow.py \
+ palettes.py \
Shell.py
diff --git a/src/view/frame/activitiestray.py b/src/view/frame/activitiestray.py
index 2060783..e2a40cc 100644
--- a/src/view/frame/activitiestray.py
+++ b/src/view/frame/activitiestray.py
@@ -14,140 +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
-import os
-import statvfs
import logging
from gettext import gettext as _
import gtk
-from sugar import env
-from sugar import profile
from sugar.graphics import style
from sugar.graphics.tray import HTray
from sugar.graphics.radiotoolbutton import RadioToolButton
from sugar.graphics.icon import Icon
-from sugar.graphics.palette import Palette
-from sugar.graphics.menuitem import MenuItem
+from view.palettes import JournalPalette, CurrentActivityPalette
from view.frame.frameinvoker import FrameWidgetInvoker
-class _Palette(Palette):
- def __init__(self, widget, home_activity):
- Palette.__init__(self, '', menu_after_content=True)
-
- self.props.invoker = FrameWidgetInvoker(widget)
- self.set_group_id('frame')
-
- if home_activity.props.launching:
- home_activity.connect('notify::launching', self._launching_changed_cb)
- self.set_primary_text(_('Starting...'))
- else:
- self.setup_palette()
-
- def _launching_changed_cb(self, home_activity, pspec):
- if not home_activity.props.launching:
- self.setup_palette()
-
- def setup_palette(self):
- raise NotImplementedError
-
-class ActivityPalette(_Palette):
- def __init__(self, widget, home_activity):
- _Palette.__init__(self, widget, home_activity)
- self._home_activity = home_activity
-
- def setup_palette(self):
- self.set_primary_text(self._home_activity.get_title())
-
- menu_item = MenuItem(_('Resume'), 'activity-start')
- menu_item.connect('activate', self.__resume_activate_cb)
- self.menu.append(menu_item)
- menu_item.show()
-
- """ Not implemented yet
- menu_item = MenuItem(_('Share with'), 'zoom-neighborhood')
- #menu_item.connect('activate', self.__share_activate_cb)
- self.menu.append(menu_item)
- menu_item.show()
-
- menu_item = MenuItem(_('Keep'))
- icon = Icon(icon_name='document-save', icon_size=gtk.ICON_SIZE_MENU,
- xo_color=profile.get_color())
- menu_item.set_image(icon)
- icon.show()
- #menu_item.connect('activate', self.__keep_activate_cb)
- self.menu.append(menu_item)
- menu_item.show()
- """
-
- separator = gtk.SeparatorMenuItem()
- self.menu.append(separator)
- separator.show()
-
- menu_item = MenuItem(_('Stop'), 'activity-stop')
- menu_item.connect('activate', self.__stop_activate_cb)
- self.menu.append(menu_item)
- menu_item.show()
-
- def __resume_activate_cb(self, menu_item):
- self._home_activity.get_window().activate(1)
-
- def __stop_activate_cb(self, menu_item):
- self._home_activity.get_window().close(1)
-
-class JournalPalette(_Palette):
- def __init__(self, widget, home_activity):
- _Palette.__init__(self, widget, home_activity)
- self._home_activity = home_activity
- self._progress_bar = None
- self._free_space_label = None
-
- def setup_palette(self):
- self.set_primary_text(self._home_activity.get_title())
-
- vbox = gtk.VBox()
- self.set_content(vbox)
- vbox.show()
-
- self._progress_bar = gtk.ProgressBar()
- vbox.add(self._progress_bar)
- self._progress_bar.show()
-
- self._free_space_label = gtk.Label()
- self._free_space_label.set_alignment(0.5, 0.5)
- vbox.add(self._free_space_label)
- self._free_space_label.show()
-
- self.connect('popup', self.__popup_cb)
-
- menu_item = MenuItem(_('Show contents'))
-
- icon = Icon(file=self._home_activity.get_icon_path(),
- icon_size=gtk.ICON_SIZE_MENU,
- xo_color=self._home_activity.get_icon_color())
- menu_item.set_image(icon)
- icon.show()
-
- menu_item.connect('activate', self.__open_activate_cb)
- self.menu.append(menu_item)
- menu_item.show()
-
- def __open_activate_cb(self, menu_item):
- self._home_activity.get_window().activate(1)
-
- def __popup_cb(self, palette):
- # TODO: we should be able to ask the datastore this info, as that's the
- # component that knows about mount points.
- stat = os.statvfs(env.get_profile_path())
- free_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL]
- total_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BLOCKS]
-
- fraction = (total_space - free_space) / float(total_space)
- self._progress_bar.props.fraction = fraction
- self._free_space_label.props.label = _('%(free_space)d MB Free') % \
- {'free_space': free_space / (1024 * 1024)}
-
class ActivityButton(RadioToolButton):
def __init__(self, home_activity, group):
RadioToolButton.__init__(self, group=group)
@@ -165,7 +44,9 @@ class ActivityButton(RadioToolButton):
if self._home_activity.get_type() == "org.laptop.JournalActivity":
palette = JournalPalette(self, self._home_activity)
else:
- palette = ActivityPalette(self, self._home_activity)
+ palette = CurrentActivityPalette(self, self._home_activity)
+ palette.props.invoker = FrameWidgetInvoker(self)
+ palette.set_group_id('frame')
self.set_palette(palette)
class ActivitiesTray(HTray):
diff --git a/src/view/home/HomeBox.py b/src/view/home/HomeBox.py
index 95ddc78..76fc9b1 100644
--- a/src/view/home/HomeBox.py
+++ b/src/view/home/HomeBox.py
@@ -46,7 +46,7 @@ class HomeBox(hippo.CanvasBox, hippo.CanvasItem):
self._toolbar.connect('view-changed', self.__toolbar_view_changed_cb)
self.append(hippo.CanvasWidget(widget=self._toolbar))
- self._set_view(_LIST_VIEW)
+ self._set_view(_RING_VIEW)
def __toolbar_view_changed_cb(self, toolbar, view):
self._set_view(view)
@@ -153,7 +153,6 @@ class HomeToolbar(gtk.Toolbar):
list_button.connect('toggled', self.__view_button_toggled_cb, _LIST_VIEW)
self.insert(list_button, -1)
list_button.show()
- list_button.props.active = True
self._add_separator()
diff --git a/src/view/home/MeshBox.py b/src/view/home/MeshBox.py
index 7d78b41..40e39c6 100644
--- a/src/view/home/MeshBox.py
+++ b/src/view/home/MeshBox.py
@@ -23,6 +23,7 @@ import gobject
import gtk
from sugar.graphics.icon import CanvasIcon
+from sugar.graphics.xocolor import XoColor
from sugar.graphics import style
from sugar.graphics.icon import get_icon_state
from sugar.graphics import style
@@ -37,7 +38,7 @@ from model.devices.network import wireless
from hardware import hardwaremanager
from hardware import nmclient
from view.BuddyIcon import BuddyIcon
-from view.pulsingicon import PulsingIcon
+from view.pulsingicon import CanvasPulsingIcon
from view.home.snowflakelayout import SnowflakeLayout
from view.home.spreadlayout import SpreadLayout
@@ -46,9 +47,9 @@ from hardware.nmclient import NM_802_11_CAP_PROTO_WEP, NM_802_11_CAP_PROTO_WPA,
_ICON_NAME = 'network-wireless'
-class AccessPointView(PulsingIcon):
+class AccessPointView(CanvasPulsingIcon):
def __init__(self, model, mesh_device=None):
- PulsingIcon.__init__(self, size=style.STANDARD_ICON_SIZE, cache=True)
+ CanvasPulsingIcon.__init__(self, size=style.STANDARD_ICON_SIZE, cache=True)
self._model = model
self._meshdev = mesh_device
self._disconnect_item = None
@@ -60,9 +61,9 @@ class AccessPointView(PulsingIcon):
model.connect('notify::name', self._name_changed_cb)
model.connect('notify::state', self._state_changed_cb)
- (stroke, fill) = model.get_nm_network().get_colors()
- self._device_stroke = stroke
- self._device_fill = fill
+ pulse_color = XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
+ style.COLOR_TRANSPARENT.get_svg()))
+ self.props.pulse_color = pulse_color
self._palette = self._create_palette()
self.set_palette(self._palette)
@@ -128,35 +129,22 @@ class AccessPointView(PulsingIcon):
if self._model.props.state == accesspointmodel.STATE_CONNECTING:
if self._disconnect_item:
self._disconnect_item.hide()
- self.props.pulse_time = 1.0
- self.props.colors = [
- [ style.Color(self._device_stroke).get_svg(),
- style.Color(self._device_fill).get_svg() ],
- [ style.Color(self._device_stroke).get_svg(),
- '#e2e2e2' ]
- ]
+ self.props.pulsing = True
elif self._model.props.state == accesspointmodel.STATE_CONNECTED:
if self._disconnect_item:
self._disconnect_item.show()
- self.props.pulse_time = 0.0
- self.props.colors = [
- [ '#ffffff',
- style.Color(self._device_fill).get_svg() ],
- [ '#ffffff',
- style.Color(self._device_fill).get_svg() ]
- ]
+ self.props.pulsing = False
elif self._model.props.state == accesspointmodel.STATE_NOTCONNECTED:
if self._disconnect_item:
self._disconnect_item.hide()
- self.props.pulse_time = 0.0
- self.props.colors = [
- [ style.Color(self._device_stroke).get_svg(),
- style.Color(self._device_fill).get_svg() ]
- ]
+ self.props.pulsing = False
if self._greyed_out:
- self.props.pulse_time = 0.0
- self.props.colors = [['#D5D5D5', '#D5D5D5']]
+ self.props.pulsing = False
+ self.props.base_color = XoColor('#D5D5D5,#D5D5D5')
+ else:
+ self.props.base_color = XoColor('%s,%s' % \
+ self._model.get_nm_network().get_colors())
def set_filter(self, query):
self._greyed_out = self._model.props.name.lower().find(query) == -1
@@ -164,7 +152,7 @@ class AccessPointView(PulsingIcon):
_MESH_ICON_NAME = 'network-mesh'
-class MeshDeviceView(PulsingIcon):
+class MeshDeviceView(CanvasPulsingIcon):
def __init__(self, nm_device, channel):
if not channel in [1, 6, 11]:
raise ValueError("Invalid channel %d" % channel)
@@ -181,9 +169,9 @@ class MeshDeviceView(PulsingIcon):
self._palette = self._create_palette()
self.set_palette(self._palette)
- mycolor = profile.get_color()
- self._device_fill = mycolor.get_fill_color()
- self._device_stroke = mycolor.get_stroke_color()
+ pulse_color = XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
+ style.COLOR_TRANSPARENT.get_svg()))
+ self.props.pulse_color = pulse_color
self.connect('activated', self._activate_cb)
@@ -221,35 +209,21 @@ class MeshDeviceView(PulsingIcon):
def _update_state(self):
state = self._nm_device.get_state()
chan = wireless.freq_to_channel(self._nm_device.get_frequency())
- if self._greyed_out:
- self.props.colors = [['#D5D5D5', '#D5D5D5']]
- elif state == nmclient.DEVICE_STATE_ACTIVATING and chan == self.channel:
+ if state == nmclient.DEVICE_STATE_ACTIVATING and chan == self.channel:
self._disconnect_item.hide()
- self.props.pulse_time = 0.75
- self.props.colors = [
- [ style.Color(self._device_stroke).get_svg(),
- style.Color(self._device_fill).get_svg() ],
- [ style.Color(self._device_stroke).get_svg(),
- '#e2e2e2' ]
- ]
+ self.props.pulsing = True
elif state == nmclient.DEVICE_STATE_ACTIVATED and chan == self.channel:
self._disconnect_item.show()
- self.props.pulse_time = 0.0
- self.props.colors = [
- [ '#ffffff',
- style.Color(self._device_fill).get_svg() ],
- [ '#ffffff',
- style.Color(self._device_fill).get_svg() ]
- ]
+ self.props.pulsing = False
elif state == nmclient.DEVICE_STATE_INACTIVE or chan != self.channel:
self._disconnect_item.hide()
- self.props.pulse_time = 0.0
- self.props.colors = [
- [ style.Color(self._device_stroke).get_svg(),
- style.Color(self._device_fill).get_svg() ]
- ]
+ self.props.pulsing = False
+
+ if self._greyed_out:
+ self.props.pulsing = False
+ self.props.base_color = XoColor('#D5D5D5,#D5D5D5')
else:
- raise RuntimeError("Shouldn't get here")
+ self.props.base_color = profile.get_color()
def set_filter(self, query):
self._greyed_out = (query != '')
diff --git a/src/view/home/activitieslist.py b/src/view/home/activitieslist.py
index 5ccfe57..2fa404d 100644
--- a/src/view/home/activitieslist.py
+++ b/src/view/home/activitieslist.py
@@ -17,7 +17,6 @@
import logging
import gobject
-import gtk
import hippo
from sugar import profile
@@ -38,33 +37,25 @@ class ActivitiesList(hippo.CanvasScrollbars):
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-added', self.__activity_added_cb)
+ registry.connect('activity-removed', self.__activity_removed_cb)
def _get_activities_cb(self, activity_list):
for info in activity_list:
if info.bundle_id != 'org.laptop.JournalActivity':
self._add_activity(info)
- def _activity_added_cb(self, activity_registry, activity_info):
+ def __activity_added_cb(self, activity_registry, activity_info):
self._add_activity(activity_info)
- def _activity_removed_cb(self, activity_registry, activity_info):
- """
- for item in self._tray.get_children():
- if item.get_bundle_id() == activity_info.bundle_id:
- self._tray.remove_item(item)
+ def __activity_removed_cb(self, activity_registry, activity_info):
+ for entry in self.get_children():
+ if entry.get_bundle_id() == activity_info.bundle_id:
+ self.remove(entry)
return
- """
- # TODO: Implement activity removal.
- pass
def _add_activity(self, activity_info):
- entry = ActivityEntry(self._shell, activity_info)
- #item.connect('clicked', self._activity_clicked_cb)
- #item.connect('remove_activity', self._remove_activity_cb)
- self._box.append(entry)
+ self._box.append(ActivityEntry(self._shell, activity_info))
class ActivityEntry(hippo.CanvasBox, hippo.CanvasItem):
__gtype_name__ = 'SugarActivityEntry'
@@ -85,18 +76,20 @@ class ActivityEntry(hippo.CanvasBox, hippo.CanvasItem):
self._shell = shell
self._activity_info = activity_info
- favorite_icon = FavoriteIcon(False)
- #favorite_icon.connect('button-release-event',
- # self._favorite_icon_button_release_event_cb)
+ favorite_icon = FavoriteIcon(self._activity_info.favorite)
+ favorite_icon.connect('notify::favorite', self.__favorite_changed_cb)
self.append(favorite_icon)
- icon = CanvasIcon(size=style.STANDARD_ICON_SIZE, cache=True,
+ self.icon = CanvasIcon(size=style.STANDARD_ICON_SIZE, cache=True,
file_name=activity_info.icon,
stroke_color=style.COLOR_BUTTON_GREY.get_svg(),
fill_color=style.COLOR_TRANSPARENT.get_svg())
- icon.connect_after('button-release-event',
- self.__icon_button_release_event_cb)
- self.append(icon)
+ self.icon.connect('hovering-changed',
+ self.__icon_hovering_changed_event_cb)
+ self.icon.connect('button-release-event',
+ self.__icon_button_release_event_cb)
+
+ self.append(self.icon)
title = hippo.CanvasText(text=activity_info.name,
xalign=hippo.ALIGNMENT_START,
@@ -119,9 +112,24 @@ class ActivityEntry(hippo.CanvasBox, hippo.CanvasItem):
box_width=ActivityEntry._DATE_COL_WIDTH)
self.append(date)
+ def __favorite_changed_cb(self, favorite_icon, pspec):
+ registry = activity.get_registry()
+ registry.set_activity_favorite(self._activity_info.bundle_id,
+ self._activity_info.version, favorite_icon.props.favorite)
+
+ def __icon_hovering_changed_event_cb(self, icon, event):
+ if event:
+ self.icon.props.xo_color = profile.get_color()
+ else:
+ self.icon.props.stroke_color = style.COLOR_BUTTON_GREY.get_svg()
+ self.icon.props.fill_color = style.COLOR_TRANSPARENT.get_svg()
+
def __icon_button_release_event_cb(self, icon, event):
self._shell.start_activity(self._activity_info.bundle_id)
+ def get_bundle_id(self):
+ return self._activity_info.bundle_id
+
class FavoriteIcon(CanvasIcon):
__gproperties__ = {
'favorite' : (bool, None, None, False,
@@ -134,6 +142,7 @@ class FavoriteIcon(CanvasIcon):
size=style.SMALL_ICON_SIZE)
self._favorite = None
self._set_favorite(favorite)
+ self.connect('button-release-event', self.__release_event_cb)
def _set_favorite(self, favorite):
if favorite == self._favorite:
@@ -158,3 +167,6 @@ class FavoriteIcon(CanvasIcon):
else:
return CanvasIcon.do_get_property(self, pspec)
+ def __release_event_cb(self, icon, event):
+ self.props.favorite = not self.props.favorite
+
diff --git a/src/view/home/activitiesring.py b/src/view/home/activitiesring.py
index 5967623..4566823 100644
--- a/src/view/home/activitiesring.py
+++ b/src/view/home/activitiesring.py
@@ -20,6 +20,7 @@ import logging
import signal
from gettext import gettext as _
import re
+import math
import gobject
import gtk
@@ -29,9 +30,14 @@ import dbus
from hardware import hardwaremanager
from sugar.graphics import style
from sugar.graphics.palette import Palette
+from sugar.graphics.icon import Icon, CanvasIcon
+from sugar.graphics.xocolor import XoColor
+from sugar.graphics.menuitem import MenuItem
from sugar.profile import get_profile
from sugar import env
+from sugar import activity
+from view.palettes import JournalPalette, CurrentActivityPalette
from view.home.MyIcon import MyIcon
from model.shellmodel import ShellModel
from hardware import schoolserver
@@ -42,15 +48,57 @@ class ActivitiesRing(hippo.CanvasBox, hippo.CanvasItem):
__gtype_name__ = 'SugarActivitiesRing'
def __init__(self, shell):
- hippo.CanvasBox.__init__(self, background_color=0xe2e2e2ff)
+ hippo.CanvasBox.__init__(self, background_color=style.COLOR_WHITE.get_int())
- shell_model = shell.get_model()
+ self._shell = shell
+ shell.get_model().connect('notify::state', self._shell_state_changed_cb)
self._my_icon = _MyIcon(shell, style.XLARGE_ICON_SIZE)
self.append(self._my_icon, hippo.PACK_FIXED)
- shell_model.connect('notify::state',
- self._shell_state_changed_cb)
+ self._current_activity = CurrentActivityIcon(shell)
+ self.append(self._current_activity, hippo.PACK_FIXED)
+
+ self.set_layout(RingLayout())
+
+ 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 _get_activities_cb(self, activity_list):
+ for info in activity_list:
+ if info.favorite and info.bundle_id != "org.laptop.JournalActivity":
+ self.append(ActivityIcon(self._shell, info))
+
+ def __activity_added_cb(self, activity_registry, activity_info):
+ if activity_info.favorite and \
+ activity_info.bundle_id != "org.laptop.JournalActivity":
+ self.append(ActivityIcon(self._shell, activity_info))
+
+ 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
+
+ def __activity_removed_cb(self, activity_registry, activity_info):
+ icon = self._find_activity_icon(activity_info.bundle_id,
+ activity_info.version)
+ if icon is not None:
+ self.remove(icon)
+
+ def __activity_changed_cb(self, activity_registry, activity_info):
+ 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:
+ self.append(ActivityIcon(self._shell, activity_info))
def _shell_state_changed_cb(self, model, pspec):
# FIXME implement this
@@ -60,13 +108,194 @@ class ActivitiesRing(hippo.CanvasBox, hippo.CanvasItem):
def do_allocate(self, width, height, origin_changed):
hippo.CanvasBox.do_allocate(self, width, height, origin_changed)
- [icon_width, icon_height] = self._my_icon.get_allocation()
- self.set_position(self._my_icon, (width - icon_width) / 2,
- (height - icon_height) / 2)
+ [my_icon_width, my_icon_height] = self._my_icon.get_allocation()
+ x = (width - my_icon_width) / 2
+ y = (height - my_icon_height - style.GRID_CELL_SIZE) / 2
+ self.set_position(self._my_icon, x, y)
+
+ [icon_width, icon_height] = self._current_activity.get_allocation()
+ 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)
def enable_xo_palette(self):
self._my_icon.enable_palette()
+class ActivityIcon(CanvasIcon):
+ def __init__(self, shell, activity_info):
+ CanvasIcon.__init__(self, cache=True, file_name=activity_info.icon)
+ self._shell = shell
+ self._activity_info = activity_info
+ self.set_palette(ActivityPalette(shell, activity_info))
+ self.connect('hovering-changed', self.__hovering_changed_event_cb)
+ self.connect('button-release-event', self.__button_release_event_cb)
+
+ self.props.stroke_color = style.COLOR_BUTTON_GREY.get_svg()
+ self.props.fill_color = style.COLOR_TRANSPARENT.get_svg()
+
+ def __hovering_changed_event_cb(self, icon, event):
+ if event:
+ self.props.xo_color = get_profile().color
+ else:
+ self.props.stroke_color = style.COLOR_BUTTON_GREY.get_svg()
+ self.props.fill_color = style.COLOR_TRANSPARENT.get_svg()
+
+ def __button_release_event_cb(self, icon, event):
+ self._shell.start_activity(self._activity_info.bundle_id)
+
+ def get_bundle_id(self):
+ return self._activity_info.bundle_id
+ bundle_id = property(get_bundle_id, None)
+
+ def get_version(self):
+ return self._activity_info.version
+ version = property(get_version, None)
+
+class ActivityPalette(Palette):
+ def __init__(self, shell, activity_info):
+ activity_icon = Icon(file=activity_info.icon,
+ xo_color=get_profile().color,
+ icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
+
+ Palette.__init__(self, None, None, primary_text=activity_info.name,
+ icon=activity_icon)
+
+ self._shell = shell
+ self._activity_info = activity_info
+
+ menu_item = MenuItem(_('Start'), 'activity-start')
+ menu_item.connect('activate', self.__start_activate_cb)
+ self.menu.append(menu_item)
+ menu_item.show()
+
+ menu_item = MenuItem(_('Start with'), 'activity-start')
+ menu_item.props.sensitive = False
+ #menu_item.connect('activate', self.__start_with_activate_cb)
+ self.menu.append(menu_item)
+ menu_item.show()
+
+ def __start_activate_cb(self, menu_item):
+ self._shell.start_activity(self._activity_info.bundle_id)
+
+class CurrentActivityIcon(CanvasIcon, hippo.CanvasItem):
+ def __init__(self, shell):
+ CanvasIcon.__init__(self, cache=True)
+ self._home_model = shell.get_model().get_home()
+
+ if self._home_model.get_pending_activity() is not None:
+ self._update(self._home_model.get_pending_activity())
+
+ self._home_model.connect('pending-activity-changed',
+ self.__pending_activity_changed_cb)
+
+ self.connect('button-release-event', self.__button_release_event_cb)
+
+ def __button_release_event_cb(self, icon, event):
+ self._home_model.get_pending_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":
+ palette = JournalPalette(self, home_activity)
+ else:
+ palette = CurrentActivityPalette(self, home_activity)
+ self.set_palette(palette)
+
+ def __pending_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, shell, scale):
MyIcon.__init__(self, scale)
@@ -76,29 +305,40 @@ class _MyIcon(MyIcon):
self._profile = get_profile()
def enable_palette(self):
- palette = Palette(self._profile.nick_name)
+ palette_icon = Icon(icon_name='computer-xo',
+ icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR,
+ xo_color=self._profile.color)
+ palette = Palette(self._profile.nick_name,
+ #secondary_text='Sample secondary label',
+ icon=palette_icon)
+
+ item = MenuItem(_('About this XO'))
+
+ icon = Icon(icon_name='computer-xo', icon_size=gtk.ICON_SIZE_MENU,
+ xo_color=self._profile.color)
+ item.set_image(icon)
+ icon.show()
+
+ item.connect('activate', self._about_activate_cb)
+ palette.menu.append(item)
+ item.show()
- item = gtk.MenuItem(_('Reboot'))
+ item = MenuItem(_('Restart'), 'system-restart')
item.connect('activate', self._reboot_activate_cb)
palette.menu.append(item)
item.show()
- item = gtk.MenuItem(_('Shutdown'))
+ item = MenuItem(_('Shutdown'), 'system-shutdown')
item.connect('activate', self._shutdown_activate_cb)
palette.menu.append(item)
item.show()
if not self._profile.is_registered():
- item = gtk.MenuItem(_('Register'))
+ item = MenuItem(_('Register'), 'media-record')
item.connect('activate', self._register_activate_cb)
palette.menu.append(item)
item.show()
- item = gtk.MenuItem(_('About this XO'))
- item.connect('activate', self._about_activate_cb)
- palette.menu.append(item)
- item.show()
-
self.set_palette(palette)
def _reboot_activate_cb(self, menuitem):
diff --git a/src/view/pulsingicon.py b/src/view/pulsingicon.py
index 9e7b3d9..b937816 100644
--- a/src/view/pulsingicon.py
+++ b/src/view/pulsingicon.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2006-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
@@ -15,76 +15,150 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import gobject
+import math
from sugar.graphics.icon import CanvasIcon
+from sugar.graphics.style import Color
+from sugar.graphics.xocolor import XoColor
+
+class CanvasPulsingIcon(CanvasIcon):
+ __gtype_name__ = 'SugarCanvasPulsingIcon'
+
+ _INTERVAL = 100
+ _STEP = math.pi / 10 # must be a fraction of pi, for clean caching
-class PulsingIcon(CanvasIcon):
__gproperties__ = {
- 'paused' : (bool, None, None, False,
- gobject.PARAM_READWRITE),
- 'colors' : (object, None, None,
- gobject.PARAM_READWRITE),
- 'pulse-time' : (float, None, None,
- 0.0, 500.0, 0.0,
- gobject.PARAM_READWRITE),
+ 'base-color' : (object, None, None, gobject.PARAM_READWRITE),
+ 'pulse-color' : (object, None, None, gobject.PARAM_READWRITE),
+ 'pulsing' : (bool, None, None, False, gobject.PARAM_READWRITE),
+ 'paused' : (bool, None, None, False, gobject.PARAM_READWRITE)
}
def __init__(self, **kwargs):
+ self._base_color = None
+ self._pulse_color = None
+ self._pulse_hid = None
self._paused = False
- self._pulse_time = 0.0
- self._colors = None
- self._pulse_sid = 0
- self._pos = 0
+ self._pulsing = False
+ self._level = 0
+ self._phase = 0
CanvasIcon.__init__(self, **kwargs)
+ def _start_pulsing(self, restart=False):
+ if restart:
+ self._phase = 0
+ self._pulse_hid = gobject.timeout_add(self._INTERVAL, self.__pulse_cb)
+
+ def _stop_pulsing(self):
+ if self._pulse_hid is not None:
+ gobject.source_remove(self._pulse_hid)
+ self._pulse_hid = None
+ self.props.xo_color = self._base_color
+
+ def _get_color(self, orig_color, target_color):
+ next_point = (orig_color[0] + self._level * (target_color[0] - orig_color[0]),
+ orig_color[1] + self._level * (target_color[1] - orig_color[1]),
+ orig_color[2] + self._level * (target_color[2] - orig_color[2]))
+ return Color('#%02x%02x%02x' % (int(next_point[0] * 255),
+ int(next_point[1] * 255),
+ int(next_point[2] * 255)))
+
+ def __pulse_cb(self):
+ self._phase += self._STEP
+ self._level = (math.sin(self._phase) + 1) / 2
+ self._update_colors()
+
+ return True
+
+ def _get_as_rgba(self, html_color):
+ if html_color == 'none':
+ return Color('#FFFFFF', alpha=1.0).get_rgba()
+ else:
+ return Color(html_color).get_rgba()
+
+ def _update_colors(self):
+ if self._pulsing:
+ base_stroke = self._get_as_rgba(self._base_color.get_stroke_color())
+ pulse_stroke = self._get_as_rgba(self._pulse_color.get_stroke_color())
+ base_fill = self._get_as_rgba(self._base_color.get_fill_color())
+ pulse_fill = self._get_as_rgba(self._pulse_color.get_fill_color())
+
+ self.props.stroke_color = \
+ self._get_color(base_stroke, pulse_stroke).get_svg()
+ self.props.fill_color = \
+ self._get_color(base_fill, pulse_fill).get_svg()
+ else:
+ self.props.xo_color = self._base_color
+
def do_set_property(self, pspec, value):
- CanvasIcon.do_set_property(self, pspec, value)
-
- if pspec.name == 'pulse-time':
- self._pulse_time = value
- self._stop()
- if not self._paused and self._pulse_time > 0.0:
- self._start()
- elif pspec.name == 'colors':
- self._colors = value
- self._pos = 0
- self._update_colors()
+ if pspec.name == 'base-color':
+ if self._base_color != value:
+ self._base_color = value
+ self._update_colors()
+ elif pspec.name == 'pulse-color':
+ if self._pulse_color != value:
+ self._pulse_color = value
+ self._update_colors()
+ elif pspec.name == 'pulsing':
+ if self._pulsing != value:
+ self._pulsing = value
+ if self._pulsing:
+ self._start_pulsing(restart=True)
+ else:
+ self._stop_pulsing()
elif pspec.name == 'paused':
- self._paused = value
- if not self._paused and self._pulse_time > 0.0:
- self._start()
- else:
- self._stop()
+ if self._paused != value:
+ self._paused = value
+ if self._paused:
+ self._stop_pulsing()
+ else:
+ self._start_pulsing(restart=False)
+ else:
+ CanvasIcon.do_set_property(self, pspec, value)
def do_get_property(self, pspec):
- CanvasIcon.do_get_property(self, pspec)
+ if pspec.name == 'base-color':
+ return self._base_color
+ elif pspec.name == 'pulse-color':
+ return self._pulse_color
+ elif pspec.name == 'pulsing':
+ return self._pulsing
+ elif pspec.name == 'paused':
+ return self._paused
+ else:
+ return CanvasIcon.do_get_property(self, pspec)
- if pspec.name == 'pulse-time':
- return self._pulse_time
- elif pspec.name == 'colors':
- return self._colors
+ """
+ def set_base_color(self, base_color):
+ self._base_color = base_color
- def _update_colors(self):
- self.props.stroke_color = self._colors[self._pos][0]
- self.props.fill_color = self._colors[self._pos][1]
+ def get_base_color(self):
+ return self._base_color
+ base_color = gobject.property(type=object, setter=set_base_color,
+ getter=get_base_color)
- def _pulse_timeout(self):
- if self._colors:
- self._update_colors()
+ def set_pulse_color(self, pulse_color):
+ self._pulse_color = pulse_color
- self._pos += 1
- if self._pos == len(self._colors):
- self._pos = 0
+ def get_pulse_color(self):
+ return self._pulse_color
+ pulse_color = gobject.property(type=object, setter=set_pulse_color,
+ getter=get_pulse_color)
- return True
+ def set_pulsing(self, pulsing):
+ pass
+
+ def get_pulsing(self):
+ return self._pulse_hid != None
+ pulsing = gobject.property(type=bool, default=False, setter=set_pulsing,
+ getter=get_pulsing)
- def _start(self):
- if self._pulse_sid == 0:
- self._pulse_sid = gobject.timeout_add(
- int(self._pulse_time * 1000), self._pulse_timeout)
+ def set_paused(self, paused):
+ self._paused = paused
- def _stop(self):
- if self._pulse_sid:
- gobject.source_remove(self._pulse_sid)
- self._pulse_sid = 0
+ def get_paused(self):
+ return self._paused
+ paused = gobject.property(type=bool, default=False, setter=set_paused,
+ getter=get_paused)
+ """