Merge branch 'master' of git+ssh://j5@dev.laptop.org/git/sugar

This commit is contained in:
John (J5) Palmieri 2007-08-27 15:51:55 -04:00
commit f5f95f4d7b
46 changed files with 973 additions and 708 deletions

1
NEWS
View File

@ -1,3 +1,4 @@
* Update arabic translation. (khaled)
* Restore Icon's ability to load absolute file paths. (tomeu) * Restore Icon's ability to load absolute file paths. (tomeu)
* #722 Show "charging" badge on battery. (danw) * #722 Show "charging" badge on battery. (danw)
* #2010 Remember state when scrubbing. (marco) * #2010 Remember state when scrubbing. (marco)

View File

@ -3,4 +3,8 @@
export SUGAR_PREFIX=@prefix@ export SUGAR_PREFIX=@prefix@
export SUGAR_PATH=@prefix@/share/sugar export SUGAR_PATH=@prefix@/share/sugar
export GTK2_RC_FILES=@prefix@/share/sugar/data/sugar-xo.gtkrc export GTK2_RC_FILES=@prefix@/share/sugar/data/sugar-xo.gtkrc
if [ -f /etc/olpc-security ] ; then
dbus-launch --exit-with-session --config-file=/etc/dbus-1/session-olpc.conf sugar-shell
else
dbus-launch --exit-with-session sugar-shell dbus-launch --exit-with-session sugar-shell
fi

153
po/ar.po
View File

@ -7,82 +7,155 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: olpc-sugar.master\n" "Project-Id-Version: olpc-sugar.master\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-06-28 10:44-0700\n" "POT-Creation-Date: 2007-08-13 17:41-0700\n"
"PO-Revision-Date: 2007-06-28 21:36+0300\n" "PO-Revision-Date: 2007-08-16 00:17+0300\n"
"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n" "Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
"Language-Team: Arabic <doc@arabeyes.org>\n" "Language-Team: Arabic <doc@arabeyes.org>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: KBabel 1.11.4\n" "X-Generator: KBabel 1.11.4\n"
"Plural-Forms: nplurals=4; plural=n==1 ? 0 : n==2 ? 1 : n>=3 && n<=10 ? 2 : 3\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\nnplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
#: ../shell/intro/intro.py:77 #: ../shell/intro/intro.py:63
msgid "Pick a buddy picture" msgid "Name:"
msgstr "اختر صورة صاحب" msgstr "الاسم:"
#: ../shell/intro/intro.py:100 #: ../shell/intro/intro.py:93
msgid "My Picture:" msgid "Click to change color:"
msgstr "صورتي:" msgstr "انقر لتغيير اللون:"
#: ../shell/intro/intro.py:180 #: ../shell/intro/intro.py:147
msgid "My Name:" msgid "Back"
msgstr "اسمي:" msgstr "السابق"
#: ../shell/intro/intro.py:204 #: ../shell/intro/intro.py:155
msgid "My Color:" msgid "Done"
msgstr "لوني:" msgstr "تمّ"
#: ../shell/intro/intro.py:158
msgid "Next"
msgstr "التالي"
#: ../shell/view/BuddyMenu.py:83 #: ../shell/view/BuddyMenu.py:83
msgid "Remove friend" msgid "Remove friend"
msgstr "أزل صديق" msgstr "أزل صديق"
#: ../shell/view/BuddyMenu.py:87 #: ../shell/view/BuddyMenu.py:86
msgid "Make friend" msgid "Make friend"
msgstr "اصنع صديق" msgstr "اصنع صديق"
#: ../shell/view/BuddyMenu.py:97 #. FIXME check that the buddy is not in the activity already
#: ../shell/view/BuddyMenu.py:98
msgid "Invite" msgid "Invite"
msgstr "ادعُ" msgstr "ادعُ"
#: ../shell/view/clipboardmenu.py:103 #: ../shell/view/clipboardmenu.py:65
msgid "Remove" msgid "Remove"
msgstr "أزل" msgstr "أزل"
#: ../shell/view/clipboardmenu.py:110 #: ../shell/view/clipboardmenu.py:70
msgid "Open" msgid "Open"
msgstr "افتح" msgstr "افتح"
#: ../shell/view/clipboardmenu.py:117 #. self._stop_item = MenuItem(_('Stop download'), 'stock-close')
msgid "Stop download" #. TODO: Implement stopping downloads
msgstr "أوقف التنزيل" #. self._stop_item.connect('activate', self._stop_item_activate_cb)
#. self.append_menu_item(self._stop_item)
#: ../shell/view/clipboardmenu.py:124 #: ../shell/view/clipboardmenu.py:80
msgid "Add to journal" msgid "Add to journal"
msgstr "أضف جرنال" msgstr "أضف يوميّة"
#: ../services/clipboard/objecttypeservice.py:32 #: ../shell/view/clipboardmenu.py:158
msgid "Text"
msgstr "نص"
#: ../services/clipboard/objecttypeservice.py:35
msgid "Image"
msgstr "صورة"
#: ../shell/view/Shell.py:227
msgid "Screenshot"
msgstr "لقطة شاشة"
#: ../shell/view/clipboardicon.py:211
#, python-format #, python-format
msgid "Clipboard object: %s." msgid "Clipboard object: %s."
msgstr "عنصر الحافظة: %s." msgstr "عنصر الحافظة: %s."
#: ../shell/view/home/MeshBox.py:122 #: ../shell/view/frame/zoombox.py:39
msgid "Neighborhood"
msgstr "الجِوَار"
#: ../shell/view/frame/zoombox.py:50
msgid "Group"
msgstr "مجموعة"
#: ../shell/view/frame/zoombox.py:61
msgid "Home"
msgstr "منزل"
#: ../shell/view/frame/zoombox.py:72
msgid "Activity"
msgstr "النشاط"
#: ../services/shell/objecttypeservice.py:32
msgid "Text"
msgstr "نص"
#: ../services/shell/objecttypeservice.py:36
msgid "Image"
msgstr "صورة"
#: ../shell/hardware/keydialog.py:113
msgid "Authentication Type:"
msgstr "نوع الاستيثاق:"
#: ../shell/hardware/keydialog.py:158
msgid "Encryption Type:"
msgstr "نوع التعمية:"
#: ../shell/view/home/activitiesdonut.py:75
msgid "Starting..."
msgstr "يبدأ..."
#: ../shell/view/home/activitiesdonut.py:89
msgid "Resume"
msgstr "استكمل"
#: ../shell/view/home/activitiesdonut.py:96 ../sugar/activity/activity.py:89
msgid "Stop"
msgstr "قف"
#: ../shell/view/Shell.py:214
msgid "Screenshot"
msgstr "لقطة شاشة"
#: ../shell/view/home/HomeBox.py:131
msgid "Shutdown"
msgstr "أطفيء"
#: ../shell/view/home/MeshBox.py:126
msgid "Mesh Network" msgid "Mesh Network"
msgstr "شبكة عُروِيّة" msgstr "شبكة عُروِيّة"
#: ../sugar/activity/activity.py:232 #: ../shell/view/devices/battery.py:34
msgid "My Battery life"
msgstr "عمر بطاريتي"
#: ../shell/view/devices/battery.py:87
msgid "Battery charging"
msgstr "شحن البطاريّة"
#: ../shell/view/devices/battery.py:89
msgid "Battery discharging"
msgstr "تفريغ البطاريّة"
#: ../shell/view/devices/battery.py:91
msgid "Battery fully charged"
msgstr "البطارية مشحونة بالكامل"
#: ../sugar/activity/activity.py:73
msgid "Private"
msgstr "خاص"
#: ../sugar/activity/activity.py:75
msgid "My Neighborhood"
msgstr "جِوارِي"
#: ../sugar/activity/activity.py:83
msgid "Keep"
msgstr "ابقِ"
#: ../sugar/activity/activity.py:262
#, python-format #, python-format
msgid "%s Activity" msgid "%s Activity"
msgstr "نشاط %s" msgstr "نشاط %s"

View File

@ -1,13 +1,9 @@
#!/bin/sh #!/bin/sh
TODO="C0111,C0301,C0322,W0311,C0324,W0331,W0212,W0611,W0613,W0201,W0106,W0622,W0403,W0612,W0102,W0404,W0704,W0402,W0702,W0401,E0602,E1111,W0101,W0105,W0601,W0602,W0703,W0701,W0312,W0231,W0233" TODO="C0111,C0301,C0322,W0311,C0324,W0331,W0212,W0611,W0613,W0201,W0106,W0622,W0403,W0102,W0404,W0704,W0402,W0702,W0401,E0602,E1102,C0321,E0611,E1103,W1001,E0213,W0107,R0921,R0401,E1111,W0101,W0105,W0601,W0602,W0703,W0701,W0312,W0231,W0233,F0401,W0612"
BROKEN="C0103,E1101" BROKEN="C0103,E1101"
DISABLE="W0142,R0913,W0621,R0903,R0201,R0904,W0511,W0232,R0902,W0603,R0914,C0302,C0102,I0011,R0911,R0912,R0901,R0801,R0923,R0915" DISABLE="W0142,R0913,W0621,R0903,R0201,R0904,W0511,W0232,R0902,W0603,R0914,C0302,C0102,I0011,R0911,R0912,R0901,R0801,R0923,R0915"
PYTHONPATH=.:./shell:$SUGAR_PREFIX/lib/python2.4/site-packages/gtk-2.0:$PYTHONPATH \ pylint --include-ids=y --disable-msg=$TODO,$BROKEN,$DISABLE shell sugar
pylint \
--include-ids=y \
--disable-msg=$TODO,$BROKEN,$DISABLE \
shell sugar

View File

@ -30,27 +30,27 @@ class ObjectTypeRegistry(dbus.service.Object):
self._types = {} self._types = {}
self._add_primitive('Text', _('Text'), 'theme:text-x-generic', self._add_primitive('Text', _('Text'), 'text-x-generic',
['text/plain', 'text/rtf', 'application/pdf', ['text/plain', 'text/rtf', 'application/pdf',
'application/x-pdf', 'text/html', 'application/x-pdf', 'text/html',
'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.text',
'application/rtf', 'text/rtf']) 'application/rtf', 'text/rtf'])
self._add_primitive('Image', _('Image'), 'theme:image-x-generic', self._add_primitive('Image', _('Image'), 'image-x-generic',
['image/png', 'image/gif', 'image/jpeg']) ['image/png', 'image/gif', 'image/jpeg'])
self._add_primitive('Audio', _('Audio'), 'theme:audio-x-generic', self._add_primitive('Audio', _('Audio'), 'audio-x-generic',
['audio/ogg']) ['audio/ogg'])
self._add_primitive('Video', _('Video'), 'theme:video-x-generic', self._add_primitive('Video', _('Video'), 'video-x-generic',
['video/ogg', 'application/ogg']) ['video/ogg', 'application/ogg'])
self._add_primitive('Etoys project', _('Etoys project'), self._add_primitive('Etoys project', _('Etoys project'),
'theme:application-x-squeak-project', 'application-x-squeak-project',
['application/x-squeak-project']) ['application/x-squeak-project'])
self._add_primitive('Link', _('Link'), self._add_primitive('Link', _('Link'),
'theme:text-uri-list', 'text-uri-list',
['text/x-moz-url', 'text/uri-list']) ['text/x-moz-url', 'text/uri-list'])
def _add_primitive(self, type_id, name, icon, mime_types): def _add_primitive(self, type_id, name, icon, mime_types):

View File

@ -16,7 +16,7 @@
import hippo import hippo
from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics.icon import CanvasIcon
from sugar.graphics import style from sugar.graphics import style
from sugar.graphics.xocolor import XoColor from sugar.graphics.xocolor import XoColor
@ -26,7 +26,7 @@ class ColorPicker(hippo.CanvasBox, hippo.CanvasItem):
self.props.orientation = hippo.ORIENTATION_HORIZONTAL self.props.orientation = hippo.ORIENTATION_HORIZONTAL
self._xo = CanvasIcon(size=style.XLARGE_ICON_SIZE, self._xo = CanvasIcon(size=style.XLARGE_ICON_SIZE,
icon_name='theme:computer-xo') icon_name='computer-xo')
self._set_random_colors() self._set_random_colors()
self._xo.connect('activated', self._xo_activated_cb) self._xo.connect('activated', self._xo_activated_cb)
self.append(self._xo) self.append(self._xo)

View File

@ -26,8 +26,8 @@ import logging
from sugar import env from sugar import env
from sugar.graphics import style from sugar.graphics import style
from sugar.graphics.canvasbutton import CanvasButton from sugar.graphics.button import CanvasButton
from sugar.graphics.canvasentry import CanvasEntry from sugar.graphics.entry import CanvasEntry
import colorpicker import colorpicker

View File

@ -103,7 +103,7 @@ class HomeActivity(gobject.GObject):
if self._activity_info: if self._activity_info:
return self._activity_info.icon return self._activity_info.icon
else: else:
return 'theme:image-missing' return 'image-missing'
def get_icon_color(self): def get_icon_color(self):
"""Retrieve the appropriate icon colour for this activity """Retrieve the appropriate icon colour for this activity

View File

@ -16,6 +16,7 @@
"""D-bus service providing access to the shell's functionality""" """D-bus service providing access to the shell's functionality"""
import dbus import dbus
import os
_DBUS_SERVICE = "org.laptop.Shell" _DBUS_SERVICE = "org.laptop.Shell"
_DBUS_SHELL_IFACE = "org.laptop.Shell" _DBUS_SHELL_IFACE = "org.laptop.Shell"
@ -40,6 +41,9 @@ class ShellService(dbus.service.Object):
XXX At the moment the d-bus service methods do not appear to do XXX At the moment the d-bus service methods do not appear to do
anything other than add_bundle anything other than add_bundle
""" """
_rainbow = None
def __init__(self, shell): def __init__(self, shell):
self._shell = shell self._shell = shell
self._shell_model = shell.get_model() self._shell_model = shell.get_model()
@ -98,9 +102,20 @@ class ShellService(dbus.service.Object):
def _owner_icon_changed_cb(self, new_icon): def _owner_icon_changed_cb(self, new_icon):
self.IconChanged(dbus.ByteArray(new_icon)) self.IconChanged(dbus.ByteArray(new_icon))
def _get_rainbow_service(self):
"""Lazily initializes an interface to the Rainbow security daemon."""
if self._rainbow is None:
service = iface = 'org.laptop.security.Rainbow'
system_bus = dbus.SystemBus()
object = system_bus.get_object(service, '/')
self._rainbow = dbus.Interface(object, dbus_interface=iface,
follow_name_owner_change=True)
return self._rainbow
@dbus.service.signal(_DBUS_OWNER_IFACE, signature="s") @dbus.service.signal(_DBUS_OWNER_IFACE, signature="s")
def CurrentActivityChanged(self, activity_id): def CurrentActivityChanged(self, activity_id):
pass if os.path.exists('/etc/olpc-security'):
self._get_rainbow_service().ChangeActivity(activity_id, dbus_interface=iface)
def _cur_activity_changed_cb(self, owner, new_activity): def _cur_activity_changed_cb(self, owner, new_activity):
new_id = "" new_id = ""

View File

@ -14,13 +14,13 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics.icon import CanvasIcon
from sugar.graphics.palette import Palette from sugar.graphics.palette import Palette
from view.BuddyMenu import BuddyMenu from view.BuddyMenu import BuddyMenu
class BuddyIcon(CanvasIcon): class BuddyIcon(CanvasIcon):
def __init__(self, shell, buddy): def __init__(self, shell, buddy):
CanvasIcon.__init__(self, icon_name='theme:computer-xo', CanvasIcon.__init__(self, icon_name='computer-xo',
xo_color=buddy.get_color()) xo_color=buddy.get_color())
self._shell = shell self._shell = shell

View File

@ -20,7 +20,7 @@ from gettext import gettext as _
import gobject import gobject
from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics.icon import CanvasIcon
from view.clipboardmenu import ClipboardMenu from view.clipboardmenu import ClipboardMenu
from sugar.graphics.xocolor import XoColor from sugar.graphics.xocolor import XoColor
from sugar.graphics import style from sugar.graphics import style
@ -88,7 +88,7 @@ class ClipboardIcon(CanvasIcon):
if icon_name: if icon_name:
self.props.icon_name = icon_name self.props.icon_name = icon_name
else: else:
self.props.icon_name = 'theme:application-octet-stream' self.props.icon_name = 'application-octet-stream'
self._name = name self._name = name
self._percent = percent self._percent = percent

View File

@ -14,10 +14,13 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from gettext import gettext as _
import gtk import gtk
from gettext import gettext as _ from sugar import profile
from sugar.graphics import canvasicon from sugar.graphics.icon import CanvasIcon
from sugar.graphics.icon import get_icon_state
from sugar.graphics import style from sugar.graphics import style
from sugar.graphics.palette import Palette from sugar.graphics.palette import Palette
@ -27,9 +30,10 @@ _STATUS_CHARGING = 0
_STATUS_DISCHARGING = 1 _STATUS_DISCHARGING = 1
_STATUS_FULLY_CHARGED = 2 _STATUS_FULLY_CHARGED = 2
class DeviceView(canvasicon.CanvasIcon): class DeviceView(CanvasIcon):
def __init__(self, model): def __init__(self, model):
canvasicon.CanvasIcon.__init__(self, size=style.MEDIUM_ICON_SIZE) CanvasIcon.__init__(self, size=style.MEDIUM_ICON_SIZE,
xo_color=profile.get_color())
self._model = model self._model = model
self._palette = BatteryPalette(_('My Battery life')) self._palette = BatteryPalette(_('My Battery life'))
self.set_palette(self._palette) self.set_palette(self._palette)
@ -40,13 +44,13 @@ class DeviceView(canvasicon.CanvasIcon):
self._update_info() self._update_info()
def _update_info(self): def _update_info(self):
self.props.icon_name = canvasicon.get_icon_state( name = get_icon_state(_ICON_NAME, self._model.props.level)
_ICON_NAME, self._model.props.level) self.props.icon_name = name
# Update palette # Update palette
if self._model.props.charging: if self._model.props.charging:
status = _STATUS_CHARGING status = _STATUS_CHARGING
self.props.badge_name = 'theme:badge-charging' self.props.badge_name = 'badge-charging'
elif self._model.props.discharging: elif self._model.props.discharging:
status = _STATUS_DISCHARGING status = _STATUS_DISCHARGING
self.props.badge_name = None self.props.badge_name = None

View File

@ -14,7 +14,7 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics.icon import CanvasIcon
def create(model): def create(model):
name = 'view.devices.' + model.get_type() name = 'view.devices.' + model.get_type()

View File

@ -15,6 +15,7 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from sugar import profile
from sugar.graphics import canvasicon from sugar.graphics import canvasicon
from sugar.graphics import style from sugar.graphics import style
from model.devices import device from model.devices import device
@ -22,7 +23,7 @@ from model.devices import device
class DeviceView(canvasicon.CanvasIcon): class DeviceView(canvasicon.CanvasIcon):
def __init__(self, model): def __init__(self, model):
canvasicon.CanvasIcon.__init__(self, size=style.MEDIUM_ICON_SIZE, canvasicon.CanvasIcon.__init__(self, size=style.MEDIUM_ICON_SIZE,
icon_name='theme:network-mesh') icon_name='network-mesh')
self._model = model self._model = model
model.connect('notify::state', self._state_changed_cb) model.connect('notify::state', self._state_changed_cb)
@ -35,11 +36,10 @@ class DeviceView(canvasicon.CanvasIcon):
# FIXME Change icon colors once we have real icons # FIXME Change icon colors once we have real icons
state = self._model.props.state state = self._model.props.state
if state == device.STATE_ACTIVATING: if state == device.STATE_ACTIVATING:
self.props.fill_color = style.COLOR_INACTIVE_FILL self.props.fill_color = style.COLOR_INACTIVE_FILL.get_svg()
self.props.stroke_color = style.COLOR_INACTIVE_STROKE self.props.stroke_color = style.COLOR_INACTIVE_STROKE.get_svg()
elif state == device.STATE_ACTIVATED: elif state == device.STATE_ACTIVATED:
self.props.fill_color = None self.props.xo_color = profile.get_color()
self.props.stroke_color = None
elif state == device.STATE_INACTIVE: elif state == device.STATE_INACTIVE:
self.props.fill_color = style.COLOR_INACTIVE_FILL self.props.fill_color = style.COLOR_INACTIVE_FILL.get_svg()
self.props.stroke_color = style.COLOR_INACTIVE_STROKE self.props.stroke_color = style.COLOR_INACTIVE_STROKE.get_svg()

View File

@ -19,4 +19,4 @@ from view.devices import deviceview
class DeviceView(deviceview.DeviceView): class DeviceView(deviceview.DeviceView):
def __init__(self, model): def __init__(self, model):
deviceview.DeviceView.__init__(self, model) deviceview.DeviceView.__init__(self, model)
self.props.icon_name = 'theme:network-wired' self.props.icon_name = 'network-wired'

View File

@ -15,11 +15,11 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from sugar.graphics import canvasicon from sugar.graphics.icon import get_icon_state
from sugar.graphics import style from sugar.graphics.icon import CanvasIcon
from sugar.graphics import style from sugar.graphics import style
from model.devices.network import wireless from model.devices.network import wireless
from sugar.graphics.canvasicon import CanvasIcon
from model.devices import device from model.devices import device
_ICON_NAME = 'network-wireless' _ICON_NAME = 'network-wireless'
@ -47,8 +47,7 @@ class DeviceView(CanvasIcon):
self._update_state() self._update_state()
def _update_icon(self): def _update_icon(self):
icon_name = canvasicon.get_icon_state( icon_name = get_icon_state(_ICON_NAME, self._model.props.strength)
_ICON_NAME, self._model.props.strength)
if icon_name: if icon_name:
self.props.icon_name = icon_name self.props.icon_name = icon_name
@ -56,11 +55,11 @@ class DeviceView(CanvasIcon):
# FIXME Change icon colors once we have real icons # FIXME Change icon colors once we have real icons
state = self._model.props.state state = self._model.props.state
if state == device.STATE_ACTIVATING: if state == device.STATE_ACTIVATING:
self.props.fill_color = style.COLOR_INACTIVE_FILL self.props.fill_color = style.COLOR_INACTIVE_FILL.get_svg()
self.props.stroke_color = style.COLOR_INACTIVE_STROKE self.props.stroke_color = style.COLOR_INACTIVE_STROKE.get_svg()
elif state == device.STATE_ACTIVATED: elif state == device.STATE_ACTIVATED:
self.props.fill_color = None self.props.fill_color = None
self.props.stroke_color = None self.props.stroke_color = None
elif state == device.STATE_INACTIVE: elif state == device.STATE_INACTIVE:
self.props.fill_color = style.COLOR_INACTIVE_FILL self.props.fill_color = style.COLOR_INACTIVE_FILL.get_svg()
self.props.stroke_color = style.COLOR_INACTIVE_STROKE self.props.stroke_color = style.COLOR_INACTIVE_STROKE.get_svg()

View File

@ -28,9 +28,9 @@ from frameinvoker import FrameCanvasInvoker
class ActivityButton(IconButton): class ActivityButton(IconButton):
def __init__(self, activity_info): def __init__(self, activity_info):
IconButton.__init__(self, icon_name=activity_info.icon, IconButton.__init__(self, file_name=activity_info.icon,
stroke_color=style.COLOR_WHITE, stroke_color=style.COLOR_WHITE.get_svg(),
fill_color=style.COLOR_TRANSPARENT) fill_color=style.COLOR_TRANSPARENT.get_svg())
palette = Palette(activity_info.name) palette = Palette(activity_info.name)
palette.props.invoker = FrameCanvasInvoker(self) palette.props.invoker = FrameCanvasInvoker(self)
@ -44,7 +44,7 @@ class ActivityButton(IconButton):
class InviteButton(IconButton): class InviteButton(IconButton):
def __init__(self, activity_model, invite): def __init__(self, activity_model, invite):
IconButton.__init__(self, icon_name=activity_model.get_color()) IconButton.__init__(self, file_name=activity_model.get_icon())
self.props.xo_color = activity_model.get_color() self.props.xo_color = activity_model.get_color()
self._invite = invite self._invite = invite

View File

@ -17,7 +17,7 @@
import hippo import hippo
from sugar.graphics.palette import Palette from sugar.graphics.palette import Palette
from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics.icon import CanvasIcon
from sugar.graphics import style from sugar.graphics import style
from sugar.presence import presenceservice from sugar.presence import presenceservice

View File

@ -24,7 +24,7 @@ class OverlayBox(hippo.CanvasBox):
self._shell = shell self._shell = shell
icon = IconButton(icon_name='theme:stock-chat') icon = IconButton(icon_name='stock-chat')
icon.connect('activated', self._overlay_clicked_cb) icon.connect('activated', self._overlay_clicked_cb)
self.append(icon) self.append(icon)

View File

@ -30,7 +30,7 @@ class ZoomBox(hippo.CanvasBox):
self._shell = shell self._shell = shell
icon = IconButton(icon_name='theme:zoom-mesh') icon = IconButton(icon_name='zoom-mesh')
icon.connect('activated', icon.connect('activated',
self._level_clicked_cb, self._level_clicked_cb,
ShellModel.ZOOM_MESH) ShellModel.ZOOM_MESH)
@ -41,7 +41,7 @@ class ZoomBox(hippo.CanvasBox):
palette.set_group_id('frame') palette.set_group_id('frame')
icon.set_palette(palette) icon.set_palette(palette)
icon = IconButton(icon_name='theme:zoom-friends') icon = IconButton(icon_name='zoom-friends')
icon.connect('activated', icon.connect('activated',
self._level_clicked_cb, self._level_clicked_cb,
ShellModel.ZOOM_FRIENDS) ShellModel.ZOOM_FRIENDS)
@ -52,7 +52,7 @@ class ZoomBox(hippo.CanvasBox):
palette.set_group_id('frame') palette.set_group_id('frame')
icon.set_palette(palette) icon.set_palette(palette)
icon = IconButton(icon_name='theme:zoom-home') icon = IconButton(icon_name='zoom-home')
icon.connect('activated', icon.connect('activated',
self._level_clicked_cb, self._level_clicked_cb,
ShellModel.ZOOM_HOME) ShellModel.ZOOM_HOME)
@ -63,7 +63,7 @@ class ZoomBox(hippo.CanvasBox):
palette.set_group_id('frame') palette.set_group_id('frame')
icon.set_palette(palette) icon.set_palette(palette)
icon = IconButton(icon_name='theme:zoom-activity') icon = IconButton(icon_name='zoom-activity')
icon.connect('activated', icon.connect('activated',
self._level_clicked_cb, self._level_clicked_cb,
ShellModel.ZOOM_ACTIVITY) ShellModel.ZOOM_ACTIVITY)

View File

@ -17,7 +17,7 @@
import hippo import hippo
import gobject import gobject
from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics.icon import CanvasIcon
from sugar.graphics import style from sugar.graphics import style
from sugar.presence import presenceservice from sugar.presence import presenceservice
from sugar import activity from sugar import activity
@ -67,7 +67,7 @@ class FriendView(hippo.CanvasBox):
# than hiding the icon? # than hiding the icon?
name = self._get_new_icon_name(home_activity) name = self._get_new_icon_name(home_activity)
if name: if name:
self._activity_icon.props.icon_name = name self._activity_icon.props.file_name = name
self._activity_icon.props.xo_color = buddy.get_color() self._activity_icon.props.xo_color = buddy.get_color()
if not self._activity_icon_visible: if not self._activity_icon_visible:
self.append(self._activity_icon, hippo.PACK_EXPAND) self.append(self._activity_icon, hippo.PACK_EXPAND)

View File

@ -21,10 +21,10 @@ import gobject
from gettext import gettext as _ from gettext import gettext as _
from sugar.graphics.spreadlayout import SpreadLayout from sugar.graphics.spreadlayout import SpreadLayout
from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics.icon import CanvasIcon
from sugar.graphics import style from sugar.graphics import style
from sugar.graphics import xocolor from sugar.graphics import xocolor
from sugar.graphics import canvasicon from sugar.graphics.icon import get_icon_state
from sugar.graphics import style from sugar.graphics import style
from sugar import profile from sugar import profile
@ -86,8 +86,7 @@ class AccessPointView(PulsingIcon):
self.set_tooltip(self._model.props.name) self.set_tooltip(self._model.props.name)
def _update_icon(self): def _update_icon(self):
icon_name = canvasicon.get_icon_state( icon_name = get_icon_state(_ICON_NAME, self._model.props.strength)
_ICON_NAME, self._model.props.strength)
if icon_name: if icon_name:
self.props.icon_name = icon_name self.props.icon_name = icon_name
@ -95,28 +94,28 @@ class AccessPointView(PulsingIcon):
if self._model.props.state == accesspointmodel.STATE_CONNECTING: if self._model.props.state == accesspointmodel.STATE_CONNECTING:
self.props.pulse_time = 1.0 self.props.pulse_time = 1.0
self.props.colors = [ self.props.colors = [
[ style.Color(self._device_stroke), [ style.Color(self._device_stroke).get_svg(),
style.Color(self._device_fill) ], style.Color(self._device_fill).get_svg() ],
[ style.Color(self._device_stroke), [ style.Color(self._device_stroke).get_svg(),
style.Color('#e2e2e2') ] '#e2e2e2' ]
] ]
elif self._model.props.state == accesspointmodel.STATE_CONNECTED: elif self._model.props.state == accesspointmodel.STATE_CONNECTED:
self.props.pulse_time = 2.0 self.props.pulse_time = 2.0
self.props.colors = [ self.props.colors = [
[ style.Color(self._device_stroke), [ style.Color(self._device_stroke).get_svg(),
style.Color(self._device_fill) ], style.Color(self._device_fill).get_svg() ],
[ style.Color('#ffffff'), [ '#ffffff',
style.Color(self._device_fill) ] style.Color(self._device_fill).get_svg() ]
] ]
elif self._model.props.state == accesspointmodel.STATE_NOTCONNECTED: elif self._model.props.state == accesspointmodel.STATE_NOTCONNECTED:
self.props.pulse_time = 0.0 self.props.pulse_time = 0.0
self.props.colors = [ self.props.colors = [
[ style.Color(self._device_stroke), [ style.Color(self._device_stroke).get_svg(),
style.Color(self._device_fill) ] style.Color(self._device_fill).get_svg() ]
] ]
_MESH_ICON_NAME = 'theme:network-mesh' _MESH_ICON_NAME = 'network-mesh'
class MeshDeviceView(PulsingIcon): class MeshDeviceView(PulsingIcon):
def __init__(self, nm_device): def __init__(self, nm_device):
@ -177,7 +176,7 @@ class ActivityView(hippo.CanvasBox):
self._layout = SnowflakeLayout() self._layout = SnowflakeLayout()
self.set_layout(self._layout) self.set_layout(self._layout)
self._icon = CanvasIcon(icon_name=model.get_icon_name(), self._icon = CanvasIcon(file_name=model.get_icon_name(),
xo_color=model.get_color(), box_width=80) xo_color=model.get_color(), box_width=80)
self._icon.connect('activated', self._clicked_cb) self._icon.connect('activated', self._clicked_cb)
self._icon.set_tooltip(self._model.get_title()) self._icon.set_tooltip(self._model.get_title())

View File

@ -14,11 +14,11 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics.icon import CanvasIcon
from sugar import profile from sugar import profile
class MyIcon(CanvasIcon): class MyIcon(CanvasIcon):
def __init__(self, size): def __init__(self, size):
CanvasIcon.__init__(self, size=size, CanvasIcon.__init__(self, size=size,
icon_name='theme:computer-xo', icon_name='computer-xo',
xo_color=profile.get_color()) xo_color=profile.get_color())

View File

@ -24,7 +24,7 @@ import hippo
import gobject import gobject
import gtk import gtk
from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics.icon import CanvasIcon
from sugar.graphics.menuitem import MenuItem from sugar.graphics.menuitem import MenuItem
from sugar.graphics.palette import Palette from sugar.graphics.palette import Palette
from sugar.graphics import style from sugar.graphics import style
@ -73,8 +73,8 @@ class ActivityIcon(CanvasIcon):
self._level = self._level_max self._level = self._level_max
color = self._icon_colors[self._level] color = self._icon_colors[self._level]
CanvasIcon.__init__(self, icon_name=icon_name, xo_color=color, CanvasIcon.__init__(self, file_name=icon_name, xo_color=color,
size=style.MEDIUM_ICON_SIZE, cache=True) size=style.MEDIUM_ICON_SIZE)
self._activity = activity self._activity = activity
self._pulse_id = 0 self._pulse_id = 0
@ -119,8 +119,6 @@ class ActivityIcon(CanvasIcon):
if self._pulse_id: if self._pulse_id:
gobject.source_remove(self._pulse_id) gobject.source_remove(self._pulse_id)
self._pulse_id = 0 self._pulse_id = 0
# dispose of all rendered icons from launch feedback
self._clear_buffers()
def _compute_icon_colors(self): def _compute_icon_colors(self):
_LEVEL_MAX = 1.6 _LEVEL_MAX = 1.6
@ -161,6 +159,7 @@ class ActivityIcon(CanvasIcon):
if self._pulse_id: if self._pulse_id:
return return
self.props.cache_size = self._level_max
self._pulse_id = gobject.timeout_add(self._INTERVAL, self._pulse_cb) self._pulse_id = gobject.timeout_add(self._INTERVAL, self._pulse_cb)
def _stop_pulsing(self): def _stop_pulsing(self):
@ -169,6 +168,7 @@ class ActivityIcon(CanvasIcon):
self._cleanup() self._cleanup()
self._level = 100.0 self._level = 100.0
self.props.cache_size = 1
self.props.xo_color = self._orig_color self.props.xo_color = self._orig_color
def _resume_activate_cb(self, menuitem): def _resume_activate_cb(self, menuitem):
@ -317,7 +317,7 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
try: try:
smaps = ProcSmaps(pid) smaps = ProcSmaps(pid)
_subtract_mappings(smaps, shell_mappings) self._subtract_mappings(smaps, shell_mappings)
for mapping in smaps.mappings: for mapping in smaps.mappings:
if mapping.shared_clean > 0 or mapping.shared_dirty > 0: if mapping.shared_clean > 0 or mapping.shared_dirty > 0:
if num_mappings.has_key(mapping.name): if num_mappings.has_key(mapping.name):
@ -408,7 +408,7 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
if icon.size > _MIN_WEDGE_SIZE: if icon.size > _MIN_WEDGE_SIZE:
icon.size -= (icon.size - _MIN_WEDGE_SIZE) * reduction icon.size -= (icon.size - _MIN_WEDGE_SIZE) * reduction
def _subtract_mappings(smaps, mappings_to_remove): def _subtract_mappings(self, smaps, mappings_to_remove):
for mapping in smaps.mappings: for mapping in smaps.mappings:
if mappings_to_remove.has_key(mapping.name): if mappings_to_remove.has_key(mapping.name):
mapping.shared_clean = 0 mapping.shared_clean = 0

View File

@ -16,7 +16,7 @@
import gobject import gobject
from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics.icon import CanvasIcon
class PulsingIcon(CanvasIcon): class PulsingIcon(CanvasIcon):
__gproperties__ = { __gproperties__ = {

View File

@ -75,9 +75,9 @@ class ActivityToolbar(gtk.Toolbar):
self.share = ToolComboBox(label_text='Share with:') self.share = ToolComboBox(label_text='Share with:')
self.share.combo.connect('changed', self._share_changed_cb) self.share.combo.connect('changed', self._share_changed_cb)
self.share.combo.append_item(None, _('Private'), self.share.combo.append_item(None, _('Private'),
'theme:zoom-home-mini') 'zoom-home-mini')
self.share.combo.append_item(None, _('My Neighborhood'), self.share.combo.append_item(None, _('My Neighborhood'),
'theme:zoom-neighborhood-mini') 'zoom-neighborhood-mini')
self.insert(self.share, -1) self.insert(self.share, -1)
self.share.show() self.share.show()
@ -469,13 +469,22 @@ class Activity(Window, gtk.Container):
self._shared_activity = activity self._shared_activity = activity
self.emit('shared') self.emit('shared')
def share(self): def share(self, private=False):
"""Request that the activity be shared on the network.""" """Request that the activity be shared on the network.
private -- bool: True to share by invitation only,
False to advertise as shared to everyone.
"""
# FIXME: Make private=True to turn on the by-invitation-only scope
if self._shared_activity and self._shared_activity.props.joined: if self._shared_activity and self._shared_activity.props.joined:
raise RuntimeError("Activity %s already shared." % self._activity_id) raise RuntimeError("Activity %s already shared." %
logging.debug('Requesting share of activity %s.' % self._activity_id) self._activity_id)
self._share_id = self._pservice.connect("activity-shared", self._internal_share_cb) verb = private and 'private' or 'public'
self._pservice.share_activity(self) logging.debug('Requesting %s share of activity %s.' %
(verb, self._activity_id))
self._share_id = self._pservice.connect("activity-shared",
self._internal_share_cb)
self._pservice.share_activity(self, private=private)
def _realize_cb(self, window): def _realize_cb(self, window):
wm.set_bundle_id(window.window, self.get_service_name()) wm.set_bundle_id(window.window, self.get_service_name())

View File

@ -26,12 +26,18 @@ from sugar.presence import presenceservice
from sugar.activity.activityhandle import ActivityHandle from sugar.activity.activityhandle import ActivityHandle
from sugar import util from sugar import util
import os
_SHELL_SERVICE = "org.laptop.Shell" _SHELL_SERVICE = "org.laptop.Shell"
_SHELL_PATH = "/org/laptop/Shell" _SHELL_PATH = "/org/laptop/Shell"
_SHELL_IFACE = "org.laptop.Shell" _SHELL_IFACE = "org.laptop.Shell"
_ACTIVITY_FACTORY_INTERFACE = "org.laptop.ActivityFactory" _ACTIVITY_FACTORY_INTERFACE = "org.laptop.ActivityFactory"
_RAINBOW_SERVICE_NAME = "org.laptop.security.Rainbow"
_RAINBOW_ACTIVITY_FACTORY_PATH = "/"
_RAINBOW_ACTIVITY_FACTORY_INTERFACE = "org.laptop.security.Rainbow"
def create_activity_id(): def create_activity_id():
"""Generate a new, unique ID for this activity""" """Generate a new, unique ID for this activity"""
pservice = presenceservice.get_instance() pservice = presenceservice.get_instance()
@ -84,6 +90,9 @@ class ActivityCreationHandler(gobject.GObject):
particular type of activity is created during the activity particular type of activity is created during the activity
registration process in shell bundle registry which creates registration process in shell bundle registry which creates
service definition files for each registered bundle type. service definition files for each registered bundle type.
If the file '/etc/olpc-security' exists, then activity launching
will be delegated to the prototype 'Rainbow' security service.
""" """
gobject.GObject.__init__(self) gobject.GObject.__init__(self)
self._service_name = service_name self._service_name = service_name
@ -112,10 +121,22 @@ class ActivityCreationHandler(gobject.GObject):
reply_handler=self._no_reply_handler, reply_handler=self._no_reply_handler,
error_handler=self._notify_launch_error_handler) error_handler=self._notify_launch_error_handler)
if not os.path.exists('/etc/olpc-security'):
self._factory.create(self._activity_handle.get_dict(), self._factory.create(self._activity_handle.get_dict(),
timeout=120 * 1000, timeout=120 * 1000,
reply_handler=self._no_reply_handler, reply_handler=self._no_reply_handler,
error_handler=self._create_error_handler) error_handler=self._create_error_handler)
else:
system_bus = dbus.SystemBus()
factory = system_bus.get_object(_RAINBOW_SERVICE_NAME,
_RAINBOW_ACTIVITY_FACTORY_PATH)
factory.CreateActivity(
self._service_name,
self._activity_handle.get_dict(),
timeout=120 * 1000,
reply_handler=self._create_reply_handler,
error_handler=self._create_error_handler,
dbus_interface=_RAINBOW_ACTIVITY_FACTORY_INTERFACE)
def get_activity_id(self): def get_activity_id(self):
"""Retrieve the unique identity for this activity""" """Retrieve the unique identity for this activity"""

View File

@ -63,6 +63,9 @@ class DSMetadata(gobject.GObject):
def get_dictionary(self): def get_dictionary(self):
return self._props return self._props
def copy(self):
return DSMetadata(self._props.copy())
class DSObject(object): class DSObject(object):
def __init__(self, object_id, metadata=None, file_path=None): def __init__(self, object_id, metadata=None, file_path=None):
self.object_id = object_id self.object_id = object_id
@ -161,6 +164,9 @@ class DSObject(object):
'Please call DSObject.destroy() before disposing it.') 'Please call DSObject.destroy() before disposing it.')
self.destroy() self.destroy()
def copy(self):
return DSObject(None, self._metadata.copy(), self._file_path)
def get(object_id): def get(object_id):
logging.debug('datastore.get') logging.debug('datastore.get')
metadata = dbus_helpers.get_properties(object_id) metadata = dbus_helpers.get_properties(object_id)
@ -224,6 +230,16 @@ def find(query, sorting=None, limit=None, offset=None, reply_handler=None,
return objects, total_count return objects, total_count
def copy(jobject, mount_point):
new_jobject = jobject.copy()
new_jobject.metadata['mountpoint'] = mount_point
# this will cause the file be retrieved from the DS
new_jobject.file_path = jobject.file_path
write(new_jobject)
def mount(uri, options): def mount(uri, options):
return dbus_helpers.mount(uri, options) return dbus_helpers.mount(uri, options)

View File

@ -2,11 +2,9 @@ sugardir = $(pythondir)/sugar/graphics
sugar_PYTHON = \ sugar_PYTHON = \
__init__.py \ __init__.py \
animator.py \ animator.py \
canvasbutton.py \ button.py \
canvasicon.py \
canvasentry.py \
canvasroundbox.py \
combobox.py \ combobox.py \
entry.py \
icon.py \ icon.py \
iconbutton.py \ iconbutton.py \
iconentry.py \ iconentry.py \
@ -17,6 +15,7 @@ sugar_PYTHON = \
palette.py \ palette.py \
palettegroup.py \ palettegroup.py \
panel.py \ panel.py \
roundbox.py \
spreadlayout.py \ spreadlayout.py \
style.py \ style.py \
toggletoolbutton.py \ toggletoolbutton.py \

View File

@ -25,7 +25,7 @@ class CanvasButton(hippo.CanvasButton):
hippo.CanvasButton.__init__(self, text=label) hippo.CanvasButton.__init__(self, text=label)
if icon_name: if icon_name:
icon = Icon(icon_name,icon_size=gtk.ICON_SIZE_BUTTON) icon = Icon(icon_name=icon_name, icon_size=gtk.ICON_SIZE_BUTTON)
self.props.widget.set_image(icon) self.props.widget.set_image(icon)
icon.show() icon.show()

View File

@ -1,433 +0,0 @@
# Copyright (C) 2006-2007 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import logging
import re
import gobject
import gtk
import hippo
import rsvg
import cairo
import time
from sugar.graphics.xocolor import XoColor
from sugar.graphics import style
from sugar.graphics.palette import Palette, CanvasInvoker
_ICON_REQUEST_SIZE = 50
_BADGE_SIZE = 0.45
class _IconCacheIcon:
def __init__(self, name, fill_color, stroke_color, now):
self.last_used = now
self.usage_count = 1
self.badge_x = 1.0 - _BADGE_SIZE / 2
self.badge_y = 1.0 - _BADGE_SIZE / 2
if name[0:6] == "theme:":
info = gtk.icon_theme_get_default().lookup_icon(
name[6:], _ICON_REQUEST_SIZE, 0)
if not info:
raise ValueError("Icon '" + name + "' not found.")
fname = info.get_filename()
attach_points = info.get_attach_points()
if attach_points is not None:
self.badge_x = float(attach_points[0][0]) / _ICON_REQUEST_SIZE
self.badge_y = float(attach_points[0][1]) / _ICON_REQUEST_SIZE
del info
else:
fname = name
self.handle = self._read_icon_data(fname, fill_color, stroke_color)
def _read_icon_data(self, filename, fill_color, stroke_color):
icon_file = open(filename, 'r')
data = icon_file.read()
icon_file.close()
if fill_color:
entity = '<!ENTITY fill_color "%s">' % fill_color
data = re.sub('<!ENTITY fill_color .*>', entity, data)
if stroke_color:
entity = '<!ENTITY stroke_color "%s">' % stroke_color
data = re.sub('<!ENTITY stroke_color .*>', entity, data)
self.data_size = len(data)
return rsvg.Handle(data=data)
class _IconCache:
_CACHE_MAX = 50000 # in bytes
def __init__(self):
self._icons = {}
self._cache_size = 0
def _cache_cleanup(self, key, now):
while self._cache_size > self._CACHE_MAX:
evict_key = None
oldest_key = None
oldest_time = now
for icon_key, icon in self._icons.items():
# Don't evict the icon we are about to use if it's in the cache
if icon_key == key:
continue
# evict large icons first
if icon.data_size > self._CACHE_MAX:
evict_key = icon_key
break
# evict older icons next; those used over 2 minutes ago
if icon.last_used < now - 120:
evict_key = icon_key
break
# otherwise, evict the oldest
if oldest_time > icon.last_used:
oldest_time = icon.last_used
oldest_key = icon_key
# If there's nothing specific to evict, try evicting
# the oldest thing
if not evict_key:
if not oldest_key:
break
evict_key = oldest_key
self._cache_size -= self._icons[evict_key].data_size
del self._icons[evict_key]
def get_icon(self, name, fill_color, stroke_color):
if not name:
return None
if fill_color or stroke_color:
key = (name, fill_color, stroke_color)
else:
key = name
# If we're over the cache limit, evict something from the cache
now = time.time()
self._cache_cleanup(key, now)
if self._icons.has_key(key):
icon = self._icons[key]
icon.usage_count += 1
icon.last_used = now
else:
icon = _IconCacheIcon(name, fill_color, stroke_color, now)
self._icons[key] = icon
self._cache_size += icon.data_size
return icon
class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
__gtype_name__ = 'CanvasIcon'
__gproperties__ = {
'icon-name' : (str, None, None, None,
gobject.PARAM_READWRITE),
'xo-color' : (object, None, None,
gobject.PARAM_WRITABLE),
'fill-color' : (object, None, None,
gobject.PARAM_READWRITE),
'stroke-color' : (object, None, None,
gobject.PARAM_READWRITE),
'size' : (int, None, None, 0, 1024, 0,
gobject.PARAM_READWRITE),
'scale' : (int, None, None, 0, 1024, 0,
gobject.PARAM_READWRITE),
'cache' : (bool, None, None, False,
gobject.PARAM_READWRITE),
'active' : (bool, None, None, True,
gobject.PARAM_READWRITE),
'badge-name' : (str, None, None, None,
gobject.PARAM_READWRITE)
}
_cache = _IconCache()
def __init__(self, **kwargs):
self._buffers = {}
self._cur_buffer = None
self._size = 0
self._scale = 0
self._fill_color = None
self._stroke_color = None
self._icon_name = None
self._cache = False
self._icon = None
self._active = True
self._palette = None
self._badge_name = None
self._badge_icon = None
hippo.CanvasBox.__init__(self, **kwargs)
self.connect_after('motion-notify-event', self._motion_notify_event_cb)
def _clear_buffers(self):
icon_key = self._get_current_buffer_key(self._icon_name)
badge_key = None
if self._badge_name:
badge_key = self._get_current_buffer_key(self._badge_name)
for key in self._buffers.keys():
if key != icon_key:
if not badge_key or (key != badge_key):
del self._buffers[key]
self._buffers = {}
def do_set_property(self, pspec, value):
if pspec.name == 'icon-name':
if self._icon_name != value and not self._cache:
self._clear_buffers()
self._icon_name = value
self._icon = None
self.emit_paint_needed(0, 0, -1, -1)
elif pspec.name == 'xo-color':
self.props.fill_color = style.Color(value.get_fill_color())
self.props.stroke_color = style.Color(value.get_stroke_color())
elif pspec.name == 'fill-color':
if self._fill_color != value:
if not self._cache:
self._clear_buffers()
self._fill_color = value
self._icon = None
self._badge_icon = None
self.emit_paint_needed(0, 0, -1, -1)
elif pspec.name == 'stroke-color':
if self._stroke_color != value:
if not self._cache:
self._clear_buffers()
self._stroke_color = value
self._icon = None
self._badge_icon = None
self.emit_paint_needed(0, 0, -1, -1)
elif pspec.name == 'size':
if self._size != value and not self._cache:
self._clear_buffers()
self._size = value
self.emit_request_changed()
elif pspec.name == 'scale':
if self._scale != value and not self._cache:
self._clear_buffers()
self._scale = value
self.emit_request_changed()
elif pspec.name == 'cache':
self._cache = value
elif pspec.name == 'active':
if self._active != value:
if not self._cache:
self._clear_buffers()
self._active = value
self._icon = None
self.emit_paint_needed(0, 0, -1, -1)
elif pspec.name == 'badge-name':
if self._badge_name != value and not self._cache:
self._clear_buffers()
self._badge_name = value
self._badge_icon = None
self.emit_paint_needed(0, 0, -1, -1)
def _choose_colors(self):
fill_color = None
stroke_color = None
if self._active:
if self._fill_color:
fill_color = self._fill_color.get_svg()
if self._stroke_color:
stroke_color = self._stroke_color.get_svg()
else:
stroke_color = color.ICON_STROKE_INACTIVE.get_svg()
if self._fill_color:
fill_color = self._fill_color.get_svg()
return [fill_color, stroke_color]
def _get_icon_from_cache(self, name, icon):
if not icon:
cache = CanvasIcon._cache
[fill_color, stroke_color] = self._choose_colors()
icon = cache.get_icon(name, fill_color, stroke_color)
return icon
def _get_icon(self):
self._icon = self._get_icon_from_cache(self._icon_name, self._icon)
return self._icon
def _get_badge_icon(self):
self._badge_icon = self._get_icon_from_cache(self._badge_name,
self._badge_icon)
return self._badge_icon
def _get_current_buffer_key(self, name):
[fill_color, stroke_color] = self._choose_colors()
return (name, fill_color, stroke_color, self._size)
def do_get_property(self, pspec):
if pspec.name == 'size':
return self._size
elif pspec.name == 'icon-name':
return self._icon_name
elif pspec.name == 'fill-color':
return self._fill_color
elif pspec.name == 'stroke-color':
return self._stroke_color
elif pspec.name == 'cache':
return self._cache
elif pspec.name == 'active':
return self._active
elif pspec.name == 'badge-name':
return self._badge_name
elif pspec.name == 'scale':
return self._scale
def _get_icon_size(self, icon):
if icon:
dimensions = icon.handle.get_dimension_data()
return int(dimensions[0]), int(dimensions[1])
else:
return [0, 0]
def _get_size(self, icon):
width, height = self._get_icon_size(icon)
if self._scale != 0:
width = int(width * self._scale)
height = int(height * self._scale)
elif self._size != 0:
width = height = self._size
return [width, height]
def _get_buffer(self, cr, name, icon, scale_factor=None):
"""Return a cached cairo surface for the SVG icon, or if none exists,
create a new cairo surface with the right size."""
buf = None
key = self._get_current_buffer_key(name)
if self._buffers.has_key(key):
buf = self._buffers[key]
else:
[icon_w, icon_h] = self._get_icon_size(icon)
[target_w, target_h] = self._get_size(icon)
if scale_factor:
target_w = int(target_w * scale_factor)
target_h = int(target_h * scale_factor)
target = cr.get_target()
buf = target.create_similar(cairo.CONTENT_COLOR_ALPHA,
target_w, target_h)
ctx = cairo.Context(buf)
ctx.scale(float(target_w) / float(icon_w),
float(target_h) / float(icon_h))
icon.handle.render_cairo(ctx)
del ctx
self._buffers[key] = buf
return buf
def do_paint_below_children(self, cr, damaged_box):
icon = self._get_icon()
if icon is None:
return
icon_buf = self._get_buffer(cr, self._icon_name, icon)
[width, height] = self.get_allocation()
icon_x = (width - icon_buf.get_width()) / 2
icon_y = (height - icon_buf.get_height()) / 2
cr.set_source_surface(icon_buf, icon_x, icon_y)
cr.paint()
if self._badge_name:
badge_icon = self._get_badge_icon()
if badge_icon:
badge_buf = self._get_buffer(cr, self._badge_name, badge_icon, _BADGE_SIZE)
badge_x = (icon_x + icon.badge_x * icon_buf.get_width() -
badge_buf.get_width() / 2)
badge_y = (icon_y + icon.badge_y * icon_buf.get_height() -
badge_buf.get_height() / 2)
cr.set_source_surface(badge_buf, badge_x, badge_y)
cr.paint()
def do_get_content_width_request(self):
icon = self._get_icon()
[width, height] = self._get_size(icon)
if self._badge_name is not None:
# If the badge goes outside the bounding box, add space
# on *both* sides (to keep the main icon centered)
if icon.badge_x < 0.0:
width = int(width * 2 * (1.0 - icon.badge_x))
elif icon.badge_x + _BADGE_SIZE > 1.0:
width = int(width * 2 * (icon.badge_x + _BADGE_SIZE - 1.0))
return (width, width)
def do_get_content_height_request(self, for_width):
icon = self._get_icon()
[width, height] = self._get_size(icon)
if self._badge_name is not None:
if icon.badge_y < 0.0:
height = int(height * 2 * (1.0 - icon.badge_y))
elif icon.badge_y + _BADGE_SIZE > 1.0:
height = int(height * 2 * (icon.badge_y + _BADGE_SIZE - 1.0))
return (height, height)
def do_button_press_event(self, event):
self.emit_activated()
return True
def _motion_notify_event_cb(self, button, event):
if event.detail == hippo.MOTION_DETAIL_ENTER:
self.prelight(True)
elif event.detail == hippo.MOTION_DETAIL_LEAVE:
self.prelight(False)
return False
def prelight(self, enter):
"""
Override this method for adding prelighting behavior.
"""
pass
def get_palette(self):
return self._palette
def set_palette(self, palette):
self._palette = palette
if not self._palette.props.invoker:
self._palette.props.invoker = CanvasInvoker(self)
def set_tooltip(self, text):
self.set_palette(Palette(text))
palette = property(get_palette, set_palette)
def get_icon_state(base_name, perc):
step = 5
strength = round(perc / step) * step
icon_theme = gtk.icon_theme_get_default()
while strength <= 100:
icon_name = '%s-%03d' % (base_name, strength)
if icon_theme.has_icon(icon_name):
return 'theme:' + icon_name
strength = strength + step

View File

@ -61,8 +61,8 @@ class ComboBox(gtk.ComboBox):
del info del info
return fname return fname
def append_item(self, action_id, text, icon_name=None): def append_item(self, action_id, text, icon_name=None, file_name=None):
if not self._icon_renderer and icon_name: if not self._icon_renderer and (icon_name or file_name):
self._icon_renderer = gtk.CellRendererPixbuf() self._icon_renderer = gtk.CellRendererPixbuf()
settings = self.get_settings() settings = self.get_settings()
@ -77,16 +77,17 @@ class ComboBox(gtk.ComboBox):
self.pack_end(self._text_renderer, True) self.pack_end(self._text_renderer, True)
self.add_attribute(self._text_renderer, 'text', 1) self.add_attribute(self._text_renderer, 'text', 1)
if icon_name: if icon_name or file_name:
if text: if text:
size = gtk.ICON_SIZE_MENU size = gtk.ICON_SIZE_MENU
else: else:
size = gtk.ICON_SIZE_LARGE_TOOLBAR size = gtk.ICON_SIZE_LARGE_TOOLBAR
width, height = gtk.icon_size_lookup(size) width, height = gtk.icon_size_lookup(size)
if icon_name[0:6] == "theme:":
icon_name = self._get_real_name_from_theme(icon_name[6:], size) if icon_name:
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(icon_name, width, height) file_name = self._get_real_name_from_theme(icon_name, size)
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(file_name, width, height)
else: else:
pixbuf = None pixbuf = None

View File

@ -16,11 +16,280 @@
# Boston, MA 02111-1307, USA. # Boston, MA 02111-1307, USA.
import os import os
import re
import time
import logging
import gobject import gobject
import gtk import gtk
import re import hippo
import rsvg
import cairo
from sugar.graphics.style import Color from sugar.graphics.style import Color
from sugar.graphics.xocolor import XoColor
from sugar.graphics import style
from sugar.graphics.palette import Palette, CanvasInvoker
_BADGE_SIZE = 0.45
_svg_loader = None
def _get_svg_loader():
global _svg_loader
if _svg_loader == None:
_svg_loader = _SVGLoader()
return _svg_loader
class _SVGIcon(object):
def __init__(self, data):
self.data = data
self.data_size = len(data)
self.last_used = time.time()
class _SVGLoader(object):
CACHE_MAX_SIZE = 50000
CACHE_MAX_ICON_SIZE = 5000
def __init__(self):
self._cache = {}
self._cache_size = 0
def load(self, file_name, entities):
icon = self._get_icon(file_name)
for entity, value in entities.items():
xml = '<!ENTITY %s "%s">' % (entity, value)
icon.data = re.sub('<!ENTITY %s .*>' % entity, xml, icon.data)
return rsvg.Handle(data=icon.data)
def _get_icon(self, file_name):
if self._cache.has_key(file_name):
data = self._cache[file_name]
data.last_used = time.time()
return data
icon_file = open(file_name, 'r')
icon = _SVGIcon(icon_file.read())
icon_file.close()
self._cache[file_name] = icon
self._cleanup_cache()
return icon
def _cleanup_cache(self):
now = time.time()
while self._cache_size > self.CACHE_MAX_SIZE:
evict_key = None
oldest_key = None
oldest_time = now
for icon_key, icon in self._cache.items():
# evict large icons first
if icon.data_size > self.CACHE_MAX_ICON_SIZE:
evict_key = icon_key
break
# evict older icons next; those used over 2 minutes ago
if icon.last_used < now - 120:
evict_key = icon_key
break
# otherwise, evict the oldest
if oldest_time > icon.last_used:
oldest_time = icon.last_used
oldest_key = icon_key
# If there's nothing specific to evict, try evicting
# the oldest thing
if not evict_key:
if not oldest_key:
break
evict_key = oldest_key
self._cache_size -= self._cache[evict_key].data_size
del self._cache[evict_key]
class _IconInfo(object):
def __init__(self):
self.file_name = None
self.attach_x = 0
self.attach_y = 0
class _BadgeInfo(object):
def __init__(self):
self.attach_x = 0
self.attach_y = 0
self.size = 0
self.icon_padding = 0
class _IconBuffer(object):
def __init__(self):
self._svg_loader = _get_svg_loader()
self._surface_cache = {}
self._cache_size = 1
self.icon_name = None
self.file_name = None
self.fill_color = None
self.stroke_color = None
self.badge_name = None
self.width = None
self.height = None
def _get_cache_key(self):
return (self.icon_name, self.file_name, self.fill_color,
self.stroke_color, self.badge_name, self.width, self.height)
def _load_svg(self, file_name):
entities = {}
if self.fill_color:
entities['fill_color'] = self.fill_color
if self.stroke_color:
entities['stroke_color'] = self.stroke_color
return self._svg_loader.load(file_name, entities)
def _get_attach_points(self, info, size_request):
attach_points = info.get_attach_points()
if attach_points:
attach_x = float(attach_points[0][0]) / size_request
attach_y = float(attach_points[0][1]) / size_request
else:
attach_x = attach_y = 0
return attach_x, attach_y
def _get_icon_info(self):
icon_info = _IconInfo()
if self.file_name:
icon_info.file_name = self.file_name
elif self.icon_name:
theme = gtk.icon_theme_get_default()
size = 50
if self.width != None:
size = self.width
info = theme.lookup_icon(self.icon_name, size, 0)
if info:
attach_x, attach_y = self._get_attach_points(info, size)
icon_info.file_name = info.get_filename()
icon_info.attach_x = attach_x
icon_info.attach_y = attach_y
else:
logging.warning('No icon with the name %s was found in the theme.' % self.icon_name)
return icon_info
def _draw_badge(self, context, size):
theme = gtk.icon_theme_get_default()
badge_info = theme.lookup_icon(self.badge_name, size, 0)
if badge_info:
badge_file_name = badge_info.get_filename()
if badge_file_name.endswith('.svg'):
handle = self._svg_loader.load(badge_file_name, {})
handle.render_cairo(context)
else:
pixbuf = gtk.gdk.pixbuf_new_from_file(badge_file_name)
surface = hippo.cairo_surface_from_gdk_pixbuf(pixbuf)
context.set_source_surface(surface, 0, 0)
context.paint()
def _get_size(self, icon_width, icon_height):
if self.width is not None and self.height is not None:
width = self.width
height = self.height
else:
width = icon_width
height = icon_height
return width, height
def _get_badge_info(self, icon_info, icon_width, icon_height):
info = _BadgeInfo()
if self.badge_name is None:
return info
info.size = int(_BADGE_SIZE * icon_width)
info.attach_x = int(icon_info.attach_x * icon_width - info.size / 2)
info.attach_y = int(icon_info.attach_y * icon_height - info.size / 2)
if info.attach_x < 0 or info.attach_y < 0:
info.icon_padding = max(-info.attach_x, -info.attach_y)
elif info.attach_x + info.size > icon_width or \
info.attach_y + info.size > icon_height:
x_padding = info.attach_x + info.size - icon_width
y_padding = info.attach_y + info.size - icon_height
info.icon_padding = max(x_padding, y_padding)
return info
def get_surface(self):
cache_key = self._get_cache_key()
if self._surface_cache.has_key(cache_key):
return self._surface_cache[cache_key]
icon_info = self._get_icon_info()
if icon_info.file_name is None:
return None
is_svg = icon_info.file_name.endswith('.svg')
if is_svg:
handle = self._load_svg(icon_info.file_name)
dimensions = handle.get_dimension_data()
icon_width = int(dimensions[0])
icon_height = int(dimensions[1])
else:
pixbuf = gtk.gdk.pixbuf_new_from_file(icon_info.file_name)
icon_width = pixbuf.get_width()
icon_height = pixbuf.get_height()
badge_info = self._get_badge_info(icon_info, icon_width, icon_height)
width, height = self._get_size(icon_width, icon_height)
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
context = cairo.Context(surface)
padding = badge_info.icon_padding
context.scale(float(width) / (icon_width + padding * 2),
float(height) / (icon_height + padding * 2))
context.save()
context.translate(padding, padding)
if is_svg:
handle.render_cairo(context)
else:
pixbuf_surface = hippo.cairo_surface_from_gdk_pixbuf(pixbuf)
context.set_source_surface(pixbuf_surface, 0, 0)
context.paint()
if self.badge_name:
context.restore()
context.translate(badge_info.attach_x, badge_info.attach_y)
self._draw_badge(context, badge_info.size)
if (len(self._surface_cache) == self._cache_size):
self._surface_cache.popitem()
self._surface_cache[cache_key] = surface
return surface
def get_cache_size(self):
return self._cache_size
def set_cache_size(self, cache_size):
while len(self._surface_cache) > cache_size:
self._surface_cache.popitem()
self._cache_size = cache_size
cache_size = property(get_cache_size, set_cache_size)
class Icon(gtk.Image): class Icon(gtk.Image):
__gtype_name__ = 'SugarIcon' __gtype_name__ = 'SugarIcon'
@ -31,124 +300,219 @@ class Icon(gtk.Image):
'fill-color' : (object, None, None, 'fill-color' : (object, None, None,
gobject.PARAM_READWRITE), gobject.PARAM_READWRITE),
'stroke-color' : (object, None, None, 'stroke-color' : (object, None, None,
gobject.PARAM_READWRITE),
'badge-name' : (str, None, None, None,
gobject.PARAM_READWRITE) gobject.PARAM_READWRITE)
} }
def __init__(self, name, **kwargs): def __init__(self, **kwargs):
self._constructed = False self._buffer = _IconBuffer()
self._fill_color = None
self._stroke_color = None
self._icon_name = name
self._theme = gtk.icon_theme_get_default()
self._data = None
gobject.GObject.__init__(self, **kwargs) gobject.GObject.__init__(self, **kwargs)
self._constructed = True def _sync_image_properties(self):
self._update_icon() if self._buffer.icon_name != self.props.icon_name:
self._buffer.icon_name = self.props.icon_name
def _get_pixbuf(self, data, width, height): if self._buffer.file_name != self.props.file:
loader = gtk.gdk.PixbufLoader('svg') self._buffer.file_name = self.props.file
loader.set_size(width, height)
loader.write(data, len(data))
loader.close()
return loader.get_pixbuf()
def _read_icon_data(self, icon_name): width, height = gtk.icon_size_lookup(self.props.icon_size)
filename = self._get_real_name(icon_name) if self._buffer.width != width and self._buffer.height != height:
icon_file = open(filename, 'r') self._buffer.width = width
data = icon_file.read() self._buffer.height = height
icon_file.close()
return data def _icon_size_changed_cb(self, image, pspec):
self._buffer.icon_size = self.props.icon_size
def _update_normal_icon(self): def _icon_name_changed_cb(self, image, pspec):
icon_theme = gtk.icon_theme_get_for_screen(self.get_screen()) self._buffer.icon_name = self.props.icon_name
icon_set = gtk.IconSet()
if icon_theme.has_icon(self._icon_name) or os.path.exists(self._icon_name): def _file_changed_cb(self, image, pspec):
source = gtk.IconSource() self._buffer.file_name = self.props.file
if os.path.exists(self._icon_name): def _update_buffer_size(self):
source.set_filename(self._icon_name) width, height = gtk.icon_size_lookup(self.props.icon_size)
else:
source.set_icon_name(self._icon_name)
icon_set.add_source(source) self._buffer.width = width
self._buffer.height = height
inactive_name = self._icon_name + '-inactive' def do_expose_event(self, event):
if icon_theme.has_icon(inactive_name) or os.path.exists(inactive_name): self._sync_image_properties()
source = gtk.IconSource()
if os.path.exists(inactive_name): surface = self._buffer.get_surface()
source.set_filename(inactive_name) if surface is not None:
else: cr = self.window.cairo_create()
source.set_icon_name(inactive_name)
source.set_state(gtk.STATE_INSENSITIVE) x = self.allocation.x
icon_set.add_source(source) y = self.allocation.y
self.props.icon_set = icon_set cr.set_source_surface(surface, x, y)
cr.paint()
def _update_icon(self):
if not self._constructed:
return
if not self._fill_color and not self._stroke_color:
self._update_normal_icon()
return
if not self._data:
data = self._read_icon_data(self._icon_name)
else:
data = self._data
if self._fill_color:
entity = '<!ENTITY fill_color "%s">' % self._fill_color
data = re.sub('<!ENTITY fill_color .*>', entity, data)
if self._stroke_color:
entity = '<!ENTITY stroke_color "%s">' % self._stroke_color
data = re.sub('<!ENTITY stroke_color .*>', entity, data)
self._data = data
# Redraw pixbuf
[w, h] = gtk.icon_size_lookup(self.props.icon_size)
pixbuf = self._get_pixbuf(self._data, w, h)
self.set_from_pixbuf(pixbuf)
def _get_real_name(self, name):
if os.path.exists(name):
return name
info = self._theme.lookup_icon(name, self.props.icon_size, 0)
if not info:
raise ValueError("Icon '" + name + "' not found.")
fname = info.get_filename()
del info
return fname
def do_set_property(self, pspec, value): def do_set_property(self, pspec, value):
if pspec.name == 'xo-color': if pspec.name == 'xo-color':
self.props.fill_color = value.get_fill_color() self.props.fill_color = value.get_fill_color()
self.props.stroke_color = value.get_stroke_color() self.props.stroke_color = value.get_stroke_color()
elif pspec.name == 'fill-color': elif pspec.name == 'fill-color':
self._fill_color = value self._buffer.fill_color = value
self._update_icon()
elif pspec.name == 'stroke-color': elif pspec.name == 'stroke-color':
self._stroke_color = value self._buffer.stroke_color = value
self._update_icon() elif pspec.name == 'badge-name':
self._buffer.badge_name = value
else: else:
gtk.Image.do_set_property(self, pspec, value) gtk.Image.do_set_property(self, pspec, value)
if pspec.name == 'icon-size':
self._update_icon()
def do_get_property(self, pspec): def do_get_property(self, pspec):
if pspec.name == 'fill-color': if pspec.name == 'fill-color':
return self._fill_color return self._buffer.fill_color
elif pspec.name == 'stroke-color': elif pspec.name == 'stroke-color':
return self._stroke_color return self._buffer.stroke_color
elif pspec.name == 'badge-name':
return self._buffer.badge_name
else: else:
return gtk.Image.do_get_property(self, pspec) return gtk.Image.do_get_property(self, pspec)
class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
__gtype_name__ = 'CanvasIcon'
__gproperties__ = {
'file-name' : (str, None, None, None,
gobject.PARAM_READWRITE),
'icon-name' : (str, None, None, None,
gobject.PARAM_READWRITE),
'xo-color' : (object, None, None,
gobject.PARAM_WRITABLE),
'fill-color' : (object, None, None,
gobject.PARAM_READWRITE),
'stroke-color' : (object, None, None,
gobject.PARAM_READWRITE),
'size' : (int, None, None, 0, 1024, 0,
gobject.PARAM_READWRITE),
'scale' : (float, None, None, -1024.0, 1024.0, 1.0,
gobject.PARAM_READWRITE),
'cache-size' : (int, None, None, 0, 1024, 0,
gobject.PARAM_READWRITE),
'badge-name' : (str, None, None, None,
gobject.PARAM_READWRITE)
}
def __init__(self, **kwargs):
self._buffer = _IconBuffer()
hippo.CanvasBox.__init__(self, **kwargs)
self._palette = None
def do_set_property(self, pspec, value):
if pspec.name == 'file-name':
self._buffer.file_name = value
self.emit_paint_needed(0, 0, -1, -1)
elif pspec.name == 'icon-name':
self._buffer.icon_name = value
self.emit_paint_needed(0, 0, -1, -1)
elif pspec.name == 'xo-color':
self.props.fill_color = value.get_fill_color()
self.props.stroke_color = value.get_stroke_color()
elif pspec.name == 'fill-color':
if not isinstance(value, basestring) and value is not None:
raise TypeError('fill-color must be a string, not %r' % type(value))
self._buffer.fill_color = value
self.emit_paint_needed(0, 0, -1, -1)
elif pspec.name == 'stroke-color':
if not isinstance(value, basestring) and value is not None:
raise TypeError('stroke-color must be a string, not %r' % type(value))
self._buffer.stroke_color = value
self.emit_paint_needed(0, 0, -1, -1)
elif pspec.name == 'size':
self._buffer.width = value
self._buffer.height = value
self.emit_request_changed()
elif pspec.name == 'scale':
self._buffer.scale = value
self.emit_request_changed()
elif pspec.name == 'cache-size':
self._buffer.cache_size = value
elif pspec.name == 'badge-name':
self._buffer.badge_name = value
self.emit_paint_needed(0, 0, -1, -1)
def do_get_property(self, pspec):
if pspec.name == 'size':
return self._buffer.width
elif pspec.name == 'file-name':
return self._buffer.file_name
elif pspec.name == 'icon-name':
return self._buffer.icon_name
elif pspec.name == 'fill-color':
return self._buffer.fill_color
elif pspec.name == 'stroke-color':
return self._buffer.stroke_color
elif pspec.name == 'cache-size':
return self._buffer.cache_size
elif pspec.name == 'badge-name':
return self._buffer.badge_name
elif pspec.name == 'scale':
return self._buffer.scale
def do_paint_below_children(self, cr, damaged_box):
surface = self._buffer.get_surface()
if surface:
width, height = self.get_allocation()
x = (width - surface.get_width()) / 2
y = (height - surface.get_height()) / 2
cr.set_source_surface(surface, x, y)
cr.paint()
def do_get_content_width_request(self):
surface = self._buffer.get_surface()
if surface:
size = surface.get_width()
elif self._buffer.width:
size = self._buffer.width
else:
size = 0
return size, size
def do_get_content_height_request(self, for_width):
surface = self._buffer.get_surface()
if surface:
size = surface.get_height()
elif self._buffer.height:
size = self._buffer.height
else:
size = 0
return size, size
def do_button_press_event(self, event):
self.emit_activated()
return True
def get_palette(self):
return self._palette
def set_palette(self, palette):
self._palette = palette
if not self._palette.props.invoker:
self._palette.props.invoker = CanvasInvoker(self)
def set_tooltip(self, text):
self.set_palette(Palette(text))
palette = property(get_palette, set_palette)
def get_icon_state(base_name, perc):
step = 5
strength = round(perc / step) * step
icon_theme = gtk.icon_theme_get_default()
while strength <= 100:
icon_name = '%s-%03d' % (base_name, strength)
if icon_theme.has_icon(icon_name):
return icon_name
strength = strength + step

View File

@ -24,18 +24,18 @@ import sys
import gobject import gobject
import hippo import hippo
from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics.icon import CanvasIcon
from sugar.graphics import style from sugar.graphics import style
class IconButton(CanvasIcon, hippo.CanvasItem): class IconButton(CanvasIcon, hippo.CanvasItem):
__gtype_name__ = 'SugarIconButton' __gtype_name__ = 'SugarIconButton'
def __init__(self, **kwargs): def __init__(self, **kwargs):
CanvasIcon.__init__(self, cache=True, **kwargs) CanvasIcon.__init__(self, **kwargs)
if not self.props.fill_color and not self.props.stroke_color: if not self.props.fill_color and not self.props.stroke_color:
self.props.fill_color = style.Color("#404040") self.props.fill_color = style.Color("#404040").get_svg()
self.props.stroke_color = style.Color("#FFFFFF") self.props.stroke_color = style.Color("#FFFFFF").get_svg()
self.connect('activated', self._icon_clicked_cb) self.connect('activated', self._icon_clicked_cb)
@ -43,18 +43,6 @@ class IconButton(CanvasIcon, hippo.CanvasItem):
self.props.box_height = style.GRID_CELL_SIZE self.props.box_height = style.GRID_CELL_SIZE
self.props.size = style.STANDARD_ICON_SIZE self.props.size = style.STANDARD_ICON_SIZE
def do_button_press_event(self, event):
if self._active:
self.emit_activated()
return True
def prelight(self, enter):
if enter:
if self.props.active:
self.props.background_color = 0x000000FF
else:
self.props.background_color = 0x00000000
def _icon_clicked_cb(self, button): def _icon_clicked_cb(self, button):
if self._palette: if self._palette:
self._palette.popdown(True) self._palette.popdown(True)

View File

@ -22,7 +22,7 @@ class MenuItem(gtk.ImageMenuItem):
def __init__(self, text_label, icon_name=None): def __init__(self, text_label, icon_name=None):
gtk.ImageMenuItem.__init__(self, text_label) gtk.ImageMenuItem.__init__(self, text_label)
if icon_name: if icon_name:
icon = Icon(icon_name, icon_size=gtk.ICON_SIZE_MENU) icon = Icon(icon_name=icon_name, icon_size=gtk.ICON_SIZE_MENU)
self.set_image(icon) self.set_image(icon)
icon.show() icon.show()

View File

@ -24,9 +24,9 @@ import hippo
from sugar.activity.bundle import Bundle from sugar.activity.bundle import Bundle
from sugar.date import Date from sugar.date import Date
from sugar.graphics import style from sugar.graphics import style
from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics.icon import CanvasIcon
from sugar.graphics.xocolor import XoColor from sugar.graphics.xocolor import XoColor
from sugar.graphics.canvasroundbox import CanvasRoundBox from sugar.graphics.roundbox import CanvasRoundBox
from sugar.datastore import datastore from sugar.datastore import datastore
from sugar import activity from sugar import activity
from sugar.objects import objecttype from sugar.objects import objecttype
@ -150,7 +150,7 @@ class CollapsedEntry(CanvasRoundBox):
self._icon_name = type.icon self._icon_name = type.icon
if not self._icon_name: if not self._icon_name:
self._icon_name = 'theme:image-missing' self._icon_name = 'image-missing'
return self._icon_name return self._icon_name

View File

@ -31,7 +31,7 @@ class RadioToolButton(gtk.RadioToolButton):
self.set_named_icon(named_icon) self.set_named_icon(named_icon)
def set_named_icon(self, named_icon): def set_named_icon(self, named_icon):
icon = Icon(named_icon, icon = Icon(icon_name=named_icon,
xo_color=self._xo_color, xo_color=self._xo_color,
icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR) icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
self.set_icon_widget(icon) self.set_icon_widget(icon)

View File

@ -29,7 +29,7 @@ class ToggleToolButton(gtk.ToggleToolButton):
self.set_named_icon(named_icon) self.set_named_icon(named_icon)
def set_named_icon(self, named_icon): def set_named_icon(self, named_icon):
icon = Icon(named_icon) icon = Icon(icon_name=named_icon)
self.set_icon_widget(icon) self.set_icon_widget(icon)
icon.show() icon.show()

View File

@ -33,7 +33,7 @@ class ToolButton(gtk.ToolButton):
self.connect('clicked', self._button_clicked_cb) self.connect('clicked', self._button_clicked_cb)
def set_icon(self, icon_name): def set_icon(self, icon_name):
icon = Icon(icon_name) icon = Icon(icon_name=icon_name)
self.set_icon_widget(icon) self.set_icon_widget(icon)
icon.show() icon.show()

89
sugar/graphics/tray.py Normal file
View File

@ -0,0 +1,89 @@
# Copyright (C) 2007, One Laptop Per Child
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import gobject
import gtk
from sugar.graphics.toolbutton import ToolButton
from sugar.graphics.icon import Icon
class _TrayViewport(gtk.Viewport):
def __init__(self):
gobject.GObject.__init__(self)
self.set_shadow_type(gtk.SHADOW_NONE)
self.traybar = gtk.Toolbar()
self.traybar.set_show_arrow(False)
self.add(self.traybar)
self.traybar.show()
def scroll_right(self):
adj = self.get_hadjustment()
new_value = adj.value + self.allocation.width
adj.value = min(new_value, adj.upper - self.allocation.width)
def scroll_left(self):
adj = self.get_hadjustment()
new_value = adj.value - self.allocation.width
adj.value = max(adj.lower, new_value)
class HTray(gtk.HBox):
def __init__(self, **kwargs):
gobject.GObject.__init__(self, **kwargs)
self._scroll_left = gtk.Button()
self._scroll_left.set_relief(gtk.RELIEF_NONE)
self._scroll_left.connect('clicked', self._scroll_left_cb)
icon = Icon(icon_name='go-left', icon_size=gtk.ICON_SIZE_MENU)
self._scroll_left.set_image(icon)
icon.show()
self.pack_start(self._scroll_left, False)
self._scroll_left.show()
self._viewport = _TrayViewport()
self.pack_start(self._viewport)
self._viewport.show()
self._scroll_right = gtk.Button()
self._scroll_right.set_relief(gtk.RELIEF_NONE)
self._scroll_right.connect('clicked', self._scroll_right_cb)
icon = Icon(icon_name='go-right', icon_size=gtk.ICON_SIZE_MENU)
self._scroll_right.set_image(icon)
icon.show()
self.pack_start(self._scroll_right, False)
self._scroll_right.show()
def _scroll_left_cb(self, button):
self._viewport.scroll_left()
def _scroll_right_cb(self, button):
self._viewport.scroll_right()
def add_item(self, item, index=-1):
self._viewport.traybar.insert(item, index)
def remove_item(self, index):
self._viewport.traybar.remove(item)
class TrayButton(ToolButton):
def __init__(self, **kwargs):
ToolButton.__init__(self, **kwargs)

View File

@ -167,16 +167,17 @@ class Activity(gobject.GObject):
return bus_name, connection, channels return bus_name, connection, channels
def _leave_cb(self): def _leave_cb(self):
# XXX Is this the right thing to do? """Callback for async action of leaving shared activity."""
self.emit("joined", False, "left activity") self.emit("joined", False, "left activity")
def _leave_error_cb(self, err): def _leave_error_cb(self, err):
# XXX We are closing down anyway """Callback for error in async leaving of shared activity.
XXX Add logging!"""
pass pass
def leave(self): def leave(self):
"""Leave this shared activity""" """Leave this shared activity"""
# FIXME
self._joined = False self._joined = False
self._activity.Leave(reply_handler=self._leave_cb, self._activity.Leave(reply_handler=self._leave_cb,
error_handler=self._leave_error_cb) error_handler=self._leave_error_cb)

View File

@ -421,7 +421,11 @@ class PresenceService(gobject.GObject):
return self._new_object(owner_op) return self._new_object(owner_op)
def _share_activity_cb(self, activity, op): def _share_activity_cb(self, activity, op):
"""Notify with GObject event of successful sharing of activity""" """Notify with GObject event of successful sharing of activity
op -- full dbus path of the new object, must be
prefixed with either of _PS_BUDDY_OP or _PS_ACTIVITY_OP
"""
psact = self._new_object(op) psact = self._new_object(op)
psact._joined = True psact._joined = True
self.emit("activity-shared", True, psact, None) self.emit("activity-shared", True, psact, None)
@ -431,10 +435,10 @@ class PresenceService(gobject.GObject):
_logger.debug("Error sharing activity %s: %s" % (activity.get_id(), err)) _logger.debug("Error sharing activity %s: %s" % (activity.get_id(), err))
self.emit("activity-shared", False, None, err) self.emit("activity-shared", False, None, err)
def share_activity(self, activity, properties={}): def share_activity(self, activity, properties={}, private=True):
"""Ask presence service to ask the activity to share itself """Ask presence service to ask the activity to share itself publicly.
Uses the ShareActivity method on the service to ask for the Uses the AdvertiseActivity method on the service to ask for the
sharing of the given activity. Arranges to emit activity-shared sharing of the given activity. Arranges to emit activity-shared
event with: event with:
@ -445,19 +449,33 @@ class PresenceService(gobject.GObject):
returns None returns None
""" """
actid = activity.get_id() actid = activity.get_id()
_logger.debug('XXXX in share_activity')
# Ensure the activity is not already shared/joined # Ensure the activity is not already shared/joined
for obj in self._objcache.values(): for obj in self._objcache.values():
if not isinstance(object, Activity): if not isinstance(object, Activity):
continue continue
if obj.props.id == actid or obj.props.joined: if obj.props.id == actid or obj.props.joined:
raise RuntimeError("Activity %s is already shared." % actid) raise RuntimeError("Activity %s is already shared." %
actid)
atype = activity.get_service_name() atype = activity.get_service_name()
name = activity.props.title name = activity.props.title
if private:
_logger.debug('XXXX private, so calling InviteActivity')
self._ps.InviteActivity(actid, atype, name, properties,
reply_handler=lambda *args: \
self._share_activity_cb(activity, *args),
error_handler=lambda *args: \
self._share_activity_error_cb(activity, *args))
else:
# FIXME: Test, then make this AdvertiseActivity:
_logger.debug('XXXX not private, so calling ShareActivity')
self._ps.ShareActivity(actid, atype, name, properties, self._ps.ShareActivity(actid, atype, name, properties,
reply_handler=lambda *args: self._share_activity_cb(activity, *args), reply_handler=lambda *args: \
error_handler=lambda *args: self._share_activity_error_cb(activity, *args)) self._share_activity_cb(activity, *args),
error_handler=lambda *args: \
self._share_activity_error_cb(activity, *args))
def get_preferred_connection(self): def get_preferred_connection(self):
"""Gets the preferred telepathy connection object that an activity """Gets the preferred telepathy connection object that an activity

View File

@ -0,0 +1,51 @@
# Copyright (C) 2007, Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
"""
Test the sugar.graphics.icon.Icon widget.
"""
import gtk
from sugar.graphics.icon import Icon
from sugar.graphics.xocolor import XoColor
import common
test = common.Test()
icon = Icon(icon_name='go-previous')
icon.props.icon_size = gtk.ICON_SIZE_LARGE_TOOLBAR
test.pack_start(icon)
icon.show()
icon = Icon(icon_name='computer-xo',
icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR,
xo_color=XoColor())
test.pack_start(icon)
icon.show()
icon = Icon(icon_name='battery-000',
icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR,
badge_name='badge-busy')
test.pack_start(icon)
icon.show()
test.show()
if __name__ == "__main__":
common.main(test)

50
tests/graphics/tray.py Normal file
View File

@ -0,0 +1,50 @@
# Copyright (C) 2007, Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
"""
Test the sugar.graphics.icon.Icon widget.
"""
import gtk
from sugar.graphics.tray import HTray
from sugar.graphics.tray import TrayButton
import common
test = common.Test()
box = gtk.VBox()
tray = HTray()
box.pack_start(tray, False)
tray.show()
theme_icons = gtk.icon_theme_get_default().list_icons()
for i in range(0, 100):
button = TrayButton(icon_name=theme_icons[i])
tray.add_item(button)
button.show()
test.pack_start(box)
box.show()
test.show()
if __name__ == "__main__":
common.main(test)