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

This commit is contained in:
Marco Pesenti Gritti 2007-02-26 12:26:23 +01:00
commit dd74d030c3
17 changed files with 1001 additions and 251 deletions

View File

@ -115,6 +115,7 @@ services/presence2/Makefile
services/clipboard/Makefile
services/datastore/Makefile
shell/Makefile
shell/intro/Makefile
shell/data/Makefile
shell/hardware/Makefile
shell/view/Makefile

View File

@ -4,7 +4,9 @@ sugar_PYTHON = \
activity.py \
buddy.py \
buddyiconcache.py \
presenceservice.py
linklocal_plugin.py \
presenceservice.py \
server_plugin.py
bin_SCRIPTS = sugar-presence-service2

View File

@ -32,7 +32,7 @@ class Buddy(dbus.service.Object):
"""Represents another person on the network and keeps track of the
activities and resources they make available for sharing."""
def __init__(self, bus_name, object_id, icon_cache):
def __init__(self, bus_name, object_id, icon_cache, handle=None):
if not bus_name:
raise ValueError("DBus bus name must be valid")
if not object_id or not isinstance(object_id, int):
@ -48,6 +48,8 @@ class Buddy(dbus.service.Object):
self._icon_cache = icon_cache
self._handle = handle
self._nick_name = None
self._color = None
self._key = None

View File

@ -0,0 +1,23 @@
# Copyright (C) 2007, Red Hat, Inc.
# Copyright (C) 2007, Collabora Ltd.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import gobject
class LinkLocalPlugin(gobject.GObject):
def __init__(self, registry):
gobject.GObject.__init__(self)
self._registry = registry

View File

@ -21,11 +21,12 @@ from telepathy.interfaces import (CONN_MGR_INTERFACE, CONN_INTERFACE)
from telepathy.constants import (CONNECTION_STATUS_CONNECTING, CONNECTION_STATUS_CONNECTED,
CONNECTION_STATUS_DISCONNECTED, CONNECTION_HANDLE_TYPE_CONTACT)
import telepathyclient
from server_plugin import ServerPlugin
from linklocal_plugin import LinkLocalPlugin
from buddy import Buddy, Owner
from activity import Activity
import buddyiconcache
from sugar import profile
_PRESENCE_SERVICE = "org.laptop.Sugar.Presence"
@ -60,52 +61,23 @@ class PresenceService(dbus.service.Object):
self._registry = ManagerRegistry()
self._registry.LoadManagers()
account = {
'account': 'olpc@collabora.co.uk',
'password': 'learn',
'server': 'light.bluelinux.co.uk'
}
self._server_client = self._connect_to_connection_manager("jabber", account)
self._handles[self._server_client] = {}
# Set up the server connection
self._server_plugin = ServerPlugin(self._registry)
self._handles[self._server_plugin] = {}
# Telepathy link local connection
self._ll_client = None
self._server_plugin.connect('status', self._server_status_cb)
self._server_plugin.connect('contact-online', self._contact_online)
self._server_plugin.connect('contact-offline', self._contact_offline)
self._server_plugin.start()
self._server_client.connect('contact-online', self._contact_online)
self._server_client.connect('contact-offline', self._contact_offline)
self._server_client.run()
# Set up the link local connection
self._ll_plugin = LinkLocalPlugin(self._registry)
self._handles[self._ll_plugin] = {}
dbus.service.Object.__init__(self, self._bus_name, _PRESENCE_PATH)
def _connect_to_connection_manager(self, protocol, account):
if protocol == "jabber":
cm = "gabble"
else:
return
mgr = self._registry.GetManager(cm)
conn = None
# Search existing connections, if any, that we might be able to use
connections = Connection.get_connections()
for item in connections:
if item[CONN_INTERFACE].GetProtocol() != protocol:
continue
if not item.object_path.startswith("/org/freedesktop/Telepathy/Connection/%s/%s/" % (cm, protocol)):
continue
if item[CONN_INTERFACE].GetStatus() == CONNECTION_STATUS_CONNECTED:
self_name = account['account']
test_handle = item[CONN_INTERFACE].RequestHandles(CONNECTION_HANDLE_TYPE_CONTACT, [self_name])[0]
if item[CONN_INTERFACE].GetSelfHandle() != test_handle:
continue
conn = item
if not conn:
conn_bus_name, conn_object_path = \
mgr[CONN_MGR_INTERFACE].RequestConnection(protocol, account)
conn = Connection(conn_bus_name, conn_object_path)
return telepathyclient.TelepathyClient(conn)
def _server_status_cb(self, plugin, status, reason):
pass
def _contact_online(self, tp, handle, key):
buddy = self._buddies.get(key)
@ -113,7 +85,7 @@ class PresenceService(dbus.service.Object):
if not buddy:
# we don't know yet this buddy
objid = self._get_next_object_id()
buddy = Buddy(self._bus_name, objid, self._icon_cache)
buddy = Buddy(self._bus_name, objid, self._icon_cache, handle=handle)
buddy.set_key(key)
print "create buddy"
self._buddies[key] = buddy

View File

@ -0,0 +1,244 @@
# Copyright (C) 2007, Red Hat, Inc.
# Copyright (C) 2007, Collabora Ltd.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import gobject
from sugar import profile
from sugar import util
import logging
from telepathy.client import ConnectionManager, ManagerRegistry, Connection, Channel
from telepathy.interfaces import (
CONN_MGR_INTERFACE, CONN_INTERFACE, CHANNEL_TYPE_CONTACT_LIST, CHANNEL_INTERFACE_GROUP, CONN_INTERFACE_ALIASING,
CONN_INTERFACE_AVATARS, CONN_INTERFACE_PRESENCE)
from telepathy.constants import (
CONNECTION_HANDLE_TYPE_NONE, CONNECTION_HANDLE_TYPE_CONTACT,
CONNECTION_STATUS_CONNECTED, CONNECTION_STATUS_DISCONNECTED, CONNECTION_STATUS_CONNECTING,
CONNECTION_HANDLE_TYPE_LIST, CONNECTION_HANDLE_TYPE_CONTACT,
CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED)
class ServerPlugin(gobject.GObject):
__gsignals__ = {
'contact-online': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])),
'contact-offline': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'status': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_INT, gobject.TYPE_INT]))
}
def __init__(self, registry):
gobject.GObject.__init__(self)
self._registry = registry
self._online_contacts = set()
self._account = self._get_account_info()
self._ever_connected = False
self._conn = self._init_connection()
def _get_account_info(self):
account_info = {'server': 'olpc.collabora.co.uk'}
pubkey = profile.get_pubkey()
khash = util.printable_hash(util._sha_data(pubkey))
account_info['account'] = "%s@%s" % (khash, account_info['server'])
account_info['password'] = profile.get_private_key_hash()
return account_info
def _init_connection(self, register=False):
protocol = 'jabber'
mgr = self._registry.GetManager('gabble')
# Search existing connections, if any, that we might be able to use
connections = Connection.get_connections()
conn = None
for item in connections:
if not item.object_path.startswith("/org/freedesktop/Telepathy/Connection/gabble/jabber/"):
continue
if item[CONN_INTERFACE].GetStatus() == CONNECTION_STATUS_DISCONNECTED:
item[CONN_INTERFACE].Disconnect()
continue
if item[CONN_INTERFACE].GetProtocol() != protocol:
continue
if item[CONN_INTERFACE].GetStatus() == CONNECTION_STATUS_CONNECTED:
self_name = self._account['account']
test_handle = item[CONN_INTERFACE].RequestHandles(CONNECTION_HANDLE_TYPE_CONTACT, [self_name])[0]
if item[CONN_INTERFACE].GetSelfHandle() != test_handle:
continue
conn = item
break
if not conn:
acct = self._account.copy()
if register:
acct['register'] = True
# Create a new connection
print acct
name, path = mgr[CONN_MGR_INTERFACE].RequestConnection(protocol, acct)
conn = Connection(name, path)
conn[CONN_INTERFACE].connect_to_signal('StatusChanged', self._status_changed_cb)
# hack
conn._valid_interfaces.add(CONN_INTERFACE_PRESENCE)
conn[CONN_INTERFACE_PRESENCE].connect_to_signal('PresenceUpdate',
self._presence_update_cb)
return conn
def _request_list_channel(self, name):
handle = self._conn[CONN_INTERFACE].RequestHandles(
CONNECTION_HANDLE_TYPE_LIST, [name])[0]
chan_path = self._conn[CONN_INTERFACE].RequestChannel(
CHANNEL_TYPE_CONTACT_LIST, CONNECTION_HANDLE_TYPE_LIST,
handle, True)
channel = Channel(self._conn._dbus_object._named_service, chan_path)
# hack
channel._valid_interfaces.add(CHANNEL_INTERFACE_GROUP)
return channel
def _connected_cb(self):
self._ever_connected = True
# the group of contacts who may receive your presence
publish = self._request_list_channel('publish')
publish_handles, local_pending, remote_pending = publish[CHANNEL_INTERFACE_GROUP].GetAllMembers()
# the group of contacts for whom you wish to receive presence
subscribe = self._request_list_channel('subscribe')
subscribe_handles = subscribe[CHANNEL_INTERFACE_GROUP].GetMembers()
if local_pending:
# accept pending subscriptions
#print 'pending: %r' % local_pending
publish[CHANNEL_INTERFACE_GROUP].AddMembers(local_pending, '')
not_subscribed = list(set(publish_handles) - set(subscribe_handles))
self_handle = self._conn[CONN_INTERFACE].GetSelfHandle()
self._online_contacts.add(self_handle)
for handle in not_subscribed:
# request subscriptions from people subscribed to us if we're not subscribed to them
subscribe[CHANNEL_INTERFACE_GROUP].AddMembers([self_handle], '')
# hack
self._conn._valid_interfaces.add(CONN_INTERFACE_ALIASING)
if CONN_INTERFACE_ALIASING in self._conn:
aliases = self._conn[CONN_INTERFACE_ALIASING].RequestAliases(subscribe_handles)
else:
aliases = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, subscribe_handles)
#for handle, alias in zip(subscribe_handles, aliases):
# print alias
# self.buddies[handle].alias = alias
# hack
self._conn._valid_interfaces.add(CONN_INTERFACE_AVATARS)
#if CONN_INTERFACE_AVATARS in self._conn:
# #tokens = self._conn[CONN_INTERFACE_AVATARS].RequestAvatarTokens(subscribe_handles)
# #for handle, token in zip(subscribe_handles, tokens):
# for handle in subscribe_handles:
# avatar, mime_type = self._conn[CONN_INTERFACE_AVATARS].RequestAvatar(handle)
# self.buddies[handle].avatar = ''.join(map(chr, avatar))
# import gtk
# window = gtk.Window()
# window.set_title(self.buddies[handle].alias)
# loader = gtk.gdk.PixbufLoader()
# loader.write(self.buddies[handle].avatar)
# loader.close()
# image = gtk.Image()
# image.set_from_pixbuf(loader.get_pixbuf())
# window.add(image)
# window.show_all()
def _status_changed_cb(self, state, reason):
gobject.idle_add(self._status_changed_cb2, state, reason)
def _status_changed_cb2(self, state, reason):
if state == CONNECTION_STATUS_CONNECTING:
print 'connecting: %r' % reason
elif state == CONNECTION_STATUS_CONNECTED:
print 'connected: %r' % reason
self.emit('status', state, int(reason))
self._connected_cb()
elif state == CONNECTION_STATUS_DISCONNECTED:
print 'disconnected: %r' % reason
self.emit('status', state, int(reason))
if reason == CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED and \
not self._ever_connected:
# Hmm; probably aren't registered on the server, try reconnecting
# and registering
self.disconnect()
del self._conn
self._conn = self._init_connection(register=True)
self.start()
return False
def start(self):
# If the connection is already connected query initial contacts
conn_status = self._conn[CONN_INTERFACE].GetStatus()
if conn_status == CONNECTION_STATUS_CONNECTED:
self._connected_cb()
subscribe = self._request_list_channel('subscribe')
subscribe_handles = subscribe[CHANNEL_INTERFACE_GROUP].GetMembers()
self._conn[CONN_INTERFACE_PRESENCE].RequestPresence(subscribe_handles)
elif conn_status == CONNECTION_STATUS_CONNECTING:
pass
else:
self._conn[CONN_INTERFACE].Connect()
def disconnect(self):
self._conn[CONN_INTERFACE].Disconnect()
def _contact_go_offline(self, handle):
name = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
print name, "offline"
self._online_contacts.remove(handle)
self.emit("contact-offline", handle)
def _contact_go_online(self, handle):
name = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
print name, "online"
# TODO: use the OLPC interface to get the key
key = handle
self._online_contacts.add(handle)
self.emit("contact-online", handle, key)
def _presence_update_cb(self, presence):
for handle in presence:
timestamp, statuses = presence[handle]
name = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
online = handle in self._online_contacts
for status, params in statuses.items():
if not online and status in ["available", "away", "brb", "busy", "dnd", "xa"]:
self._contact_go_online(handle)
elif online and status in ["offline", "invisible"]:
self._contact_go_offline(handle)

View File

@ -1,194 +0,0 @@
import dbus.glib
import gobject
from telepathy.client import ConnectionManager, ManagerRegistry, Connection, Channel
from telepathy.interfaces import (
CONN_MGR_INTERFACE, CONN_INTERFACE, CHANNEL_TYPE_CONTACT_LIST, CHANNEL_INTERFACE_GROUP, CONN_INTERFACE_ALIASING,
CONN_INTERFACE_AVATARS, CONN_INTERFACE_PRESENCE)
from telepathy.constants import (
CONNECTION_HANDLE_TYPE_NONE, CONNECTION_HANDLE_TYPE_CONTACT,
CONNECTION_STATUS_CONNECTED, CONNECTION_STATUS_DISCONNECTED, CONNECTION_STATUS_CONNECTING,
CONNECTION_HANDLE_TYPE_LIST, CONNECTION_HANDLE_TYPE_CONTACT)
loop = None
import buddy
class TelepathyClient(gobject.GObject):
__gsignals__ = {
'contact-online':(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])),
'contact-offline':(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
}
def __init__(self, conn):
gobject.GObject.__init__(self)
self._online_contacts = set()
conn[CONN_INTERFACE].connect_to_signal('StatusChanged',
self._status_changed_cb)
# hack
conn._valid_interfaces.add(CONN_INTERFACE_PRESENCE)
conn[CONN_INTERFACE_PRESENCE].connect_to_signal('PresenceUpdate',
self._presence_update_cb)
self.conn = conn
def _request_list_channel(self, name):
handle = self.conn[CONN_INTERFACE].RequestHandles(
CONNECTION_HANDLE_TYPE_LIST, [name])[0]
chan_path = self.conn[CONN_INTERFACE].RequestChannel(
CHANNEL_TYPE_CONTACT_LIST, CONNECTION_HANDLE_TYPE_LIST,
handle, True)
channel = Channel(self.conn._dbus_object._named_service, chan_path)
# hack
channel._valid_interfaces.add(CHANNEL_INTERFACE_GROUP)
return channel
def _connected_cb(self):
# the group of contacts who may receive your presence
publish = self._request_list_channel('publish')
publish_handles, local_pending, remote_pending = publish[CHANNEL_INTERFACE_GROUP].GetAllMembers()
# the group of contacts for whom you wish to receive presence
subscribe = self._request_list_channel('subscribe')
subscribe_handles = subscribe[CHANNEL_INTERFACE_GROUP].GetMembers()
if local_pending:
# accept pending subscriptions
#print 'pending: %r' % local_pending
publish[CHANNEL_INTERFACE_GROUP].AddMembers(local_pending, '')
not_subscribed = list(set(publish_handles) - set(subscribe_handles))
self_handle = self.conn[CONN_INTERFACE].GetSelfHandle()
self._online_contacts.add(self_handle)
for handle in not_subscribed:
# request subscriptions from people subscribed to us if we're not subscribed to them
subscribe[CHANNEL_INTERFACE_GROUP].AddMembers([self_handle], '')
# hack
self.conn._valid_interfaces.add(CONN_INTERFACE_ALIASING)
if CONN_INTERFACE_ALIASING in self.conn:
aliases = self.conn[CONN_INTERFACE_ALIASING].RequestAliases(subscribe_handles)
else:
aliases = self.conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, subscribe_handles)
#for handle, alias in zip(subscribe_handles, aliases):
# print alias
# self.buddies[handle].alias = alias
# hack
self.conn._valid_interfaces.add(CONN_INTERFACE_AVATARS)
#if CONN_INTERFACE_AVATARS in self.conn:
# #tokens = self.conn[CONN_INTERFACE_AVATARS].RequestAvatarTokens(subscribe_handles)
# #for handle, token in zip(subscribe_handles, tokens):
# for handle in subscribe_handles:
# avatar, mime_type = self.conn[CONN_INTERFACE_AVATARS].RequestAvatar(handle)
# self.buddies[handle].avatar = ''.join(map(chr, avatar))
# import gtk
# window = gtk.Window()
# window.set_title(self.buddies[handle].alias)
# loader = gtk.gdk.PixbufLoader()
# loader.write(self.buddies[handle].avatar)
# loader.close()
# image = gtk.Image()
# image.set_from_pixbuf(loader.get_pixbuf())
# window.add(image)
# window.show_all()
def _status_changed_cb(self, state, reason):
if state == CONNECTION_STATUS_CONNECTING:
print 'connecting'
elif state == CONNECTION_STATUS_CONNECTED:
print 'connected'
self._connected_cb()
elif state == CONNECTION_STATUS_DISCONNECTED:
print 'disconnected'
loop.quit()
def run(self):
# If the connection is already connected query initial contacts
conn_status = self.conn[CONN_INTERFACE].GetStatus()
if conn_status == CONNECTION_STATUS_CONNECTED:
self._connected_cb()
subscribe = self._request_list_channel('subscribe')
subscribe_handles = subscribe[CHANNEL_INTERFACE_GROUP].GetMembers()
self.conn[CONN_INTERFACE_PRESENCE].RequestPresence(subscribe_handles)
elif conn_status == CONNECTION_STATUS_CONNECTING:
pass
else:
self.conn[CONN_INTERFACE].Connect()
def disconnect(self):
self.conn[CONN_INTERFACE].Disconnect()
def _contact_go_offline(self, handle):
name = self.conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
print name, "offline"
self._online_contacts.remove(handle)
self.emit("contact-offline", handle)
def _contact_go_online(self, handle):
name = self.conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
print name, "online"
# TODO: use the OLPC interface to get the key
key = handle
self._online_contacts.add(handle)
self.emit("contact-online", handle, key)
def _presence_update_cb(self, presence):
for handle in presence:
timestamp, statuses = presence[handle]
name = self.conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
online = handle in self._online_contacts
for status, params in statuses.items():
if not online and status in ["available", "away", "brb", "busy", "dnd", "xa"]:
self._contact_go_online(handle)
elif online and status in ["offline", "invisible"]:
self._contact_go_offline(handle)
if __name__ == '__main__':
import logging
logging.basicConfig()
registry = ManagerRegistry()
registry.LoadManagers()
mgr = registry.GetManager('gabble')
protocol = 'jabber'
account = {
'account': 'olpc@collabora.co.uk',
'password': 'learn',
'server': 'light.bluelinux.co.uk'
}
loop = gobject.MainLoop()
conn_bus_name, conn_object_path = \
mgr[CONN_MGR_INTERFACE].RequestConnection(protocol, account)
print conn_bus_name
print conn_object_path
conn = Connection(conn_bus_name, conn_object_path)
client = TelepathyClient(conn)
try:
loop.run()
finally:
try:
#conn[CONN_INTERFACE].Disconnect()
client.disconnect()
except:
pass

View File

@ -1,4 +1,4 @@
SUBDIRS = data hardware model view
SUBDIRS = data hardware model view intro
bin_SCRIPTS = \
sugar-activity \

View File

@ -363,7 +363,7 @@ class NMClient(gobject.GObject):
self._get_initial_devices()
def get_devices(self):
return self._devices
return self._devices.values()
def _get_initial_devices_reply_cb(self, ops):
for op in ops:

6
shell/intro/Makefile.am Normal file
View File

@ -0,0 +1,6 @@
sugardir = $(pkgdatadir)/shell/intro
sugar_PYTHON = \
__init__.py \
colorpicker.py \
intro.py \
glive.py

0
shell/intro/__init__.py Normal file
View File

213
shell/intro/colorpicker.py Normal file
View File

@ -0,0 +1,213 @@
# Copyright (C) 2007, Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import hippo
import random, math
import gobject
from sugar.graphics.canvasicon import CanvasIcon
from sugar.graphics import color
from sugar.graphics import units
from sugar.graphics.xocolor import XoColor
# Ported from a JavaScript implementation (C) 2007 Jacob Rus
# http://www.hcs.harvard.edu/~jrus/olpc/colorpicker.svg
# An array of arrays with value 1-9 for each of 40 different hues,
# starting with 5R, and going all the way around to 2.5R.
munsell_colors = [
# 1 2 3 4 5 6 7 8 9
["#40011d","#640d28","#8e172e","#bf1837","#f50141","#fc5f68","#f49599","#f1bdc3","#f8dfe9"], # 5 R
["#410117","#630f1f","#8d1c21","#bd2024","#f21d22","#fc6252","#f5968d","#f2bdbe","#f9dfe7"], # 7.5 R
["#410210","#631214","#940c00","#b03716","#d84b18","#f96735","#f49781","#f3bdb8","#fadfe5"], # 10 R
["#410405","#611601","#7c3111","#a14614","#c85b15","#f07015","#fd9465","#f2beb2","#fbdfe1"], # 2.5 YR
["#381001","#542305","#763601","#944f1f","#ba651f","#e07b1d","#f69955","#febc98","#fcdfdc"], # 5 YR
["#2b180e","#492a14","#6b3e14","#8e5313","#b26a0a","#d1832b","#f69b26","#f9be91","#fbe0d7"], # 7.5 YR
["#2a190b","#462c0f","#66400c","#885703","#a67020","#c9881a","#eba004","#fcbf6f","#f9e1d3"], # 10 YR
["#271a09","#422e09","#614303","#7c5c1e","#9e7415","#bb8e32","#dda72c","#ffc01e","#f6e2d0"], # 2.5 Y
["#251b09","#3f2f06","#574621","#775e19","#967709","#b2922a","#d2ac1d","#edc73f","#fae3b2"], # 5 Y
["#221c0a","#3b3105","#534820","#706116","#8b7b2e","#a99525","#c8af13","#e3ca3a","#fde676"], # 7.5 Y
["#1f1d0d","#363207","#4e4920","#6a6316","#857d2f","#a19825","#beb30e","#dacd39","#f8ea20"], # 10 Y
["#1c1e11","#31340e","#474c02","#61651c","#7b810b","#959b2b","#b0b71b","#ccd23f","#e8ee25"], # 2.5 GY
["#1a1e13","#2b3515","#3e4e0f","#576824","#6e841e","#85a007","#a1ba30","#b9d719","#d6f337"], # 5 GY
["#082205","#18390a","#255301","#3e6d19","#508914","#61a704","#7ec22e","#90e01a","#abfc39"], # 7.5 GY
["#01230d","#0b3a18","#0a541b","#2a6f2d","#048f1e","#0bad21","#15cb23","#0cea1c","#91fe81"], # 10 GY
["#141f1a","#183728","#115336","#2b6d4c","#2e8b5b","#2ba96a","#20c877","#4be48e","#83feaf"], # 2.5 G
["#131f1b","#15382c","#285041","#226d54","#138c68","#3ea681","#33c695","#54e2ae","#8afbcc"], # 5 G
["#121f1c","#11382f","#245045","#176e5a","#3c8874","#33a78a","#1ac6a0","#46e2b9","#81fbd6"], # 7.5 G
["#111f1e","#0d3832","#205049","#0a6e60","#38887a","#26a792","#4ec2ac","#38e2c4","#79fbe0"], # 10 G
["#0f1f1f","#083836","#1c504d","#366a66","#338880","#13a79a","#45c2b4","#24e2ce","#72fbe9"], # 2.5 BG
["#0e1f22","#03383b","#185053","#34696c","#2d8788","#49a3a2","#3cc2be","#5addd9","#6afbf4"], # 5 BG
["#0c1f24","#00373e","#155057","#336970","#28878f","#46a2aa","#37c1c8","#55dde4","#92f5fb"], # 7.5 BG
["#0c1f26","#23343b","#154f5c","#336975","#278697","#46a1b1","#34c0d2","#52dced","#b5effd"], # 10 BG
["#0c1f27","#23343c","#154f60","#356879","#2b859d","#49a0b8","#3bbedb","#55dbf7","#d5e8f8"], # 2.5 B
["#0c1f29","#023649","#1a4e64","#076988","#3584a2","#19a1ca","#49bce4","#88d4f5","#d8e7f9"], # 5 B
["#0e1e2a","#0a354c","#224d66","#21688b","#0284b3","#3a9fce","#58baea","#90d2f9","#dbe6fa"], # 7.5 B
["#101e2c","#13344d","#294b68","#2f668d","#2d82b6","#1f9edf","#43b9fe","#9bd0fd","#dee5fa"], # 10 B
["#021e38","#1c334f","#1d4b77","#23649e","#277fc7","#159bf3","#7eb3f0","#b8cbee","#e2e4fa"], # 2.5 PB
["#0d1c38","#14315d","#1a4885","#2661ac","#2e7cd6","#4f96f4","#8eb0f1","#bec9ef","#e4e4fa"], # 5 PB
["#1c0560","#2c089c","#3b0ddf","#3f45f7","#566ff1","#7d8ef2","#a2abf2","#c7c7ee","#e8e3fa"], # 7.5 PB
["#290652","#45018a","#5f0fbf","#7d16fe","#8261f3","#9784fb","#b0a4fe","#ccc3fe","#eae2f9"], # 10 PB
["#2e0945","#510079","#7103ab","#911ddb","#a945fd","#b577fd","#c1a0f7","#d5c1fa","#ece1f9"], # 2.5 P
["#340541","#54086b","#79079a","#9d18c8","#c226f9","#ca6bfc","#d397fc","#dcbff5","#ede1f8"], # 5 P
["#37023f","#55115e","#7e0e8a","#a519b3","#d11ee0","#f045ff","#ee8af6","#f2b6f7","#f1e0f5"], # 7.5 P
["#340a36","#5a0c59","#7f177a","#a9229e","#d822c6","#f749e2","#fa86e8","#fab4ee","#f2e0f3"], # 10 P
["#360833","#600450","#880c6d","#b3128e","#e401b2","#fa4fc6","#fa8ad2","#f4b9de","#f4e0f2"], # 2.5 RP
["#39062f","#5b1344","#8d0060","#b32078","#e71994","#fb56aa","#f68fbd","#f9b8d5","#f5dff0"], # 5 RP
["#3b052b","#5e113e","#881951","#b81a69","#ec0c82","#f56099","#fa8eb3","#fcb7cf","#f6dfee"], # 7.5 RP
["#3d0427","#600f38","#8c1645","#bd145a","#e72a6f","#f95f88","#fe8ea7","#ffb7c8","#f7dfed"], # 10 RP
["#3f0222","#620d31","#8d153a","#bf124b","#e92a5e","#fb5f79","#f295a1","#efbdc8","#f6dfeb"], # 2.5 R
]
# neutral values from 0 to 10. Not sure these are completely
# accurate; my method was a bit of a guesstimate.
munsell_neutrals = [
'#000000', '#1d1d1d', '#323232', '#494949', '#626262', '#7c7c7c',
'#969696', '#b1b1b1', '#cbcbcb', '#e7e7e7', '#ffffff'
]
def _hex_color(hue, value):
# hue ranges from 0 (5R) to 39 (2.5R). value ranges from 1 to 9
return munsell_colors[hue][value-1]
def rand(n):
return int(math.floor(random.random() * n))
class ColorPicker(hippo.CanvasBox, hippo.CanvasItem):
__gsignals__ = {
'color': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
}
def __init__(self, **kwargs):
hippo.CanvasBox.__init__(self, **kwargs)
self.props.orientation = hippo.ORIENTATION_HORIZONTAL
# 5YR7: #f69955
# 5BP3: #1a4885
self._fg_hue = 4
self._fg_value = 7
self._bg_hue = 28
self._bg_value = 3
self._fg_hex = _hex_color(self._fg_hue, self._fg_value)
self._bg_hex = _hex_color(self._bg_hue, self._bg_value)
self._pie_hues = 10
self._slider_values = 9
self._xo = CanvasIcon(scale=units.XLARGE_ICON_SCALE,
icon_name='theme:stock-buddy',
stroke_color=color.HTMLColor(self._fg_hex),
fill_color=color.HTMLColor(self._bg_hex))
self._set_random_colors()
self._emit_color()
self._xo.connect('activated', self._xo_activated_cb)
self.append(self._xo)
def _xo_activated_cb(self, item):
self._set_random_colors()
self._emit_color()
def _emit_color(self):
xo_color = XoColor('%s,%s' % (self._xo.props.stroke_color.get_html(),
self._xo.props.fill_color.get_html()))
self.emit('color', xo_color)
def _update_xo_hex(self, fg=None, bg=None):
"""set the colors of the XO man"""
if fg:
self._xo.props.stroke_color = color.HTMLColor(fg)
if bg:
self._xo.props.fill_color = color.HTMLColor(bg)
def _update_fg_hue(self, pie_fg_hue):
"""change foreground (fill) hue"""
self._fg_hue = pie_fg_hue * (40 / self._pie_hues)
self._fg_hex = _hex_color(self._fg_hue, self._fg_value)
self._update_xo_hex(fg=self._fg_hex)
# set value slider
#for i in range(1, 10):
# setFill("fgv" + i, _hex_color(self._fg_hue, i))
# rotate selection dingus
#svgDocument.getElementById("fgHueSelect").setAttribute(
# "transform",
# "rotate(" + (360/self._pie_hues) * pie_fg_hue + ")"
#)
def _update_bg_hue(self, pie_bg_hue):
"""change background (stroke) hue"""
self._bg_hue = pie_bg_hue * (40 / self._pie_hues)
self._bg_hex = _hex_color(self._bg_hue, self._bg_value)
self._update_xo_hex(bg=self._bg_hex)
# set value slider
#for i in range(1, self._slider_values + 1):
# setFill("bgv" + i, _hex_color(self._bg_hue, i))
# rotate selection dingus
#svgDocument.getElementById("bgHueSelect").setAttribute(
# "transform",
# "rotate(" + (360/self._pie_hues) * pie_bg_hue + ")"
#)
def _update_fg_value(self, slider_fg_value):
self._fg_value = slider_fg_value
self._fg_hex = _hex_color(self._fg_hue, self._fg_value)
self._update_xo_hex(fg=self._fg_hex)
# set hue pie
#for i in range(0, self._pie_hues):
# cur_hue = i * (40 / self._pie_hues)
# setFill("fgh" + i, _hex_color(cur_hue, self._fg_value))
# move selection dingus
#svgDocument.getElementById("fgValueSelect").setAttribute(
# "transform",
# "translate(0 -" + 22 * slider_fg_value + ")"
#)
def _update_bg_value(self, slider_bg_value):
self._bg_value = slider_bg_value
self._bg_hex = _hex_color(self._bg_hue, self._bg_value)
self._update_xo_hex(bg=self._bg_hex)
# set hue pie
#for i in range(0, self._pie_hues):
# cur_hue = i * (40 / self._pie_hues)
# setFill("bgh" + i, _hex_color(cur_hue, self._bg_value))
# move selection dingus
#svgDocument.getElementById("bgValueSelect").setAttribute(
# "transform",
# "translate(0 -" + 22 * slider_bg_value + ")"
#)
def _set_random_colors(self):
self._update_fg_hue(rand(self._pie_hues))
self._update_fg_value(rand(self._slider_values)+1)
self._update_bg_hue(rand(self._pie_hues))
self._update_bg_value(rand(self._slider_values)+1)

186
shell/intro/glive.py Normal file
View File

@ -0,0 +1,186 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import gtk
import pygtk
pygtk.require('2.0')
import sys
import pygst
pygst.require('0.10')
import gst
import gst.interfaces
import gobject
gobject.threads_init()
class Glive(gobject.GObject):
__gsignals__ = {
'new-picture': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
'sink': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT]))
}
def __init__(self, parent, width, height):
self._parent = parent
#check out the halfpipe, d00d.
self.pipeline = gst.Pipeline()
self.v4l2src = gst.element_factory_make("v4l2src", "v4l2src")
self.t = gst.element_factory_make("tee", "tee")
self.t_src_pad = self.t.get_request_pad( "src%d" )
self.vscale = gst.element_factory_make("videoscale", "videoscale")
self.ximagesink = gst.element_factory_make("ximagesink", "ximagesink")
self.pipeline.add(self.v4l2src)
self.pipeline.add(self.t)
self.pipeline.add(self.vscale)
self.pipeline.add(self.ximagesink)
self.v4l2src.link(self.t)
videoscale_structure = gst.Structure("video/x-raw-rgb")
videoscale_structure['width'] = width
videoscale_structure['height'] = height
videoscale_structure['bpp'] = 16
videoscale_structure['depth'] = 16
videoscale_caps = gst.Caps(videoscale_structure)
self.t_src_pad.link(self.vscale.get_pad("sink"))
self.vscale.link(self.ximagesink, videoscale_caps)
#self.vscale.link(self.ximagesink)
self.queue = gst.element_factory_make("queue", "queue")
self.queue.set_property("leaky", True)
self.queue.set_property("max-size-buffers", 1)
self.qsrc = self.queue.get_pad( "src" )
self.qsink = self.queue.get_pad("sink")
self.ffmpeg = gst.element_factory_make("ffmpegcolorspace", "ffmpegcolorspace")
self.jpgenc = gst.element_factory_make("jpegenc", "jpegenc")
self.filesink = gst.element_factory_make("fakesink", "fakesink")
self.filesink.connect( "handoff", self.copyframe )
self.filesink.set_property("signal-handoffs", True)
self.pipeline.add(self.queue, self.ffmpeg, self.jpgenc, self.filesink)
#only link at snapshot time
#self.t.link(self.queue)
self.queue.link(self.ffmpeg)
self.ffmpeg.link(self.jpgenc)
self.jpgenc.link(self.filesink)
self.exposureOpen = False
self._bus = self.pipeline.get_bus()
self._CONNECT_SYNC = -1
self._CONNECT_MSG = -1
self.doPostBusStuff()
def copyframe(self, fsink, buffer, pad, user_data=None):
#for some reason, we get two back to back buffers, even though we
#ask for only one.
if (self.exposureOpen):
self.exposureOpen = False
piccy = gtk.gdk.pixbuf_loader_new_with_mime_type("image/jpeg")
piccy.write( buffer )
piccy.close()
pixbuf = piccy.get_pixbuf()
del piccy
self.t.unlink(self.queue)
self.queue.set_property("leaky", True)
gobject.idle_add(self.loadPic, pixbuf)
def loadPic( self, pixbuf ):
self.emit('new-picture', pixbuf)
def takeSnapshot( self ):
if (self.exposureOpen):
return
else:
self.exposureOpen = True
self.t.link(self.queue)
def doPostBusStuff(self):
self._bus.enable_sync_message_emission()
self._bus.add_signal_watch()
self._CONNECT_SYNC = self._bus.connect('sync-message::element', self.on_sync_message)
self._CONNECT_MSG = self._bus.connect('message', self.on_message)
def on_sync_message(self, bus, message):
if message.structure is None:
return
if message.structure.get_name() == 'prepare-xwindow-id':
self.emit('sink', message.src)
message.src.set_property('force-aspect-ratio', True)
def on_message(self, bus, message):
t = message.type
if (t == gst.MESSAGE_ERROR):
err, debug = message.parse_error()
if (self.on_eos):
self.on_eos()
self._playing = False
elif (t == gst.MESSAGE_EOS):
if (self.on_eos):
self.on_eos()
self._playing = False
def on_eos( self ):
pass
def stop(self):
self.pipeline.set_state(gst.STATE_NULL)
def play(self):
self.pipeline.set_state(gst.STATE_PLAYING)
def pause(self):
self.pipeline.set_state(gst.STATE_PAUSED)
class LiveVideoSlot(gtk.EventBox):
__gsignals__ = {
'pixbuf': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
}
def __init__(self, width, height):
gtk.EventBox.__init__(self)
self.imagesink = None
self.unset_flags(gtk.DOUBLE_BUFFERED)
self.connect('focus-in-event', self.focus_in)
self.connect('focus-out-event', self.focus_out)
self.connect("button-press-event", self.button_press)
self.playa = Glive(self, width, height)
self.playa.connect('new-picture', self._new_picture_cb)
self.playa.connect('sink', self._new_sink_cb)
def _new_picture_cb(self, playa, pixbuf):
self.emit('pixbuf', pixbuf)
def _new_sink_sb(self, playa, sink):
if (self.imagesink != None):
assert self.window.xid
self.imagesink = None
del self.imagesink
self.imagesink = sink
self.imagesink.set_xwindow_id(self.window.xid)
def focus_in(self, widget, event, args=None):
self.play()
def focus_out(self, widget, event, args=None):
self.stop()
def play( self ):
self.playa.play()
def pause( self ):
self.playa.pause()
def stop( self ):
self.playa.stop()
def takeSnapshot( self ):
self.playa.takeSnapshot()

238
shell/intro/intro.py Normal file
View File

@ -0,0 +1,238 @@
# Copyright (C) 2007, Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import gtk, gobject
import hippo
import logging
from gettext import gettext as _
import os
from ConfigParser import ConfigParser
from sugar import env
from sugar.graphics import entry
from sugar.graphics import units
from sugar.graphics import font
from sugar.graphics import color
from sugar.graphics import iconbutton
import colorpicker
_VIDEO_WIDTH = 320
_VIDEO_HEIGHT = 240
class IntroFallbackVideo(gtk.EventBox):
__gtype_name__ = "IntroFallbackVideo"
__gsignals__ = {
'pixbuf': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
}
def __init__(self, **kwargs):
gtk.EventBox.__init__(self, **kwargs)
self._image = gtk.Image()
self._image.set_from_stock(gtk.STOCK_OPEN, -1)
self.add(self._image)
self.connect('button-press-event', self._button_press_cb)
def _button_press_cb(self, widget, event):
filt = gtk.FileFilter()
filt.add_pixbuf_formats()
chooser = gtk.FileChooserDialog(_("Pick a buddy picture"), \
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
chooser.set_filter(filt)
resp = chooser.run()
if resp == gtk.RESPONSE_ACCEPT:
fname = chooser.get_filename()
pixbuf = gtk.gdk.pixbuf_new_from_file(fname)
(w, h) = self.get_size_request()
img_pixbuf = pixbuf.scale_simple(w, h, gtk.gdk.INTERP_BILINEAR)
self._image.set_from_pixbuf(img_pixbuf)
self.emit('pixbuf', pixbuf)
chooser.hide()
chooser.destroy()
return True
class VideoBox(hippo.CanvasBox, hippo.CanvasItem):
__gtype_name__ = "SugarVideoBox"
def __init__(self, **kwargs):
hippo.CanvasBox.__init__(self, **kwargs)
self.props.orientation = hippo.ORIENTATION_HORIZONTAL
self._pixbuf = None
self._label = hippo.CanvasText(text=_("My Picture:"),
xalign=hippo.ALIGNMENT_START,
padding_right=units.grid_to_pixels(0.5))
self._label.props.color = color.LABEL_TEXT.get_int()
self._label.props.font_desc = font.DEFAULT.get_pango_desc()
self.append(self._label)
try:
import glive
self._video = glive.LiveVideoSlot(_VIDEO_WIDTH, _VIDEO_HEIGHT)
except ImportError:
self._video = IntroFallbackVideo()
self._video.set_size_request(_VIDEO_WIDTH, _VIDEO_HEIGHT)
self._video.connect('pixbuf', self._new_pixbuf_cb)
self._video_widget = hippo.CanvasWidget()
self._video_widget.props.widget = self._video
self.append(self._video_widget)
def _new_pixbuf_cb(self, widget, pixbuf):
if self._pixbuf:
del self._pixbuf
self._pixbuf = pixbuf
def get_pixbuf(self):
return self._pixbuf
class EntryBox(hippo.CanvasBox, hippo.CanvasItem):
__gtype_name__ = "SugarEntryBox"
def __init__(self, **kwargs):
hippo.CanvasBox.__init__(self, **kwargs)
self.props.orientation = hippo.ORIENTATION_HORIZONTAL
self._label = hippo.CanvasText(text=_("My Name:"),
xalign=hippo.ALIGNMENT_START,
padding_right=units.grid_to_pixels(0.5))
self._label.props.color = color.LABEL_TEXT.get_int()
self._label.props.font_desc = font.DEFAULT.get_pango_desc()
self.append(self._label)
self._entry = entry.Entry()
self.append(self._entry)
def get_text(self):
return self._entry.props.text
class ColorBox(hippo.CanvasBox, hippo.CanvasItem):
__gtype_name__ = "SugarColorBox"
def __init__(self, **kwargs):
hippo.CanvasBox.__init__(self, **kwargs)
self.props.orientation = hippo.ORIENTATION_HORIZONTAL
self._color = None
self._label = hippo.CanvasText(text=_("My Color:"),
xalign=hippo.ALIGNMENT_START,
padding_right=units.grid_to_pixels(0.5))
self._label.props.color = color.LABEL_TEXT.get_int()
self._label.props.font_desc = font.DEFAULT.get_pango_desc()
self.append(self._label)
self._cp = colorpicker.ColorPicker()
self._cp.connect('color', self._new_color_cb)
self.append(self._cp)
def _new_color_cb(self, widget, color):
self._color = color
def get_color(self):
return self._color
class IntroBox(hippo.CanvasBox, hippo.CanvasItem):
__gtype_name__ = 'SugarIntroBox'
def __init__(self, **kwargs):
hippo.CanvasBox.__init__(self, **kwargs)
self._pixbuf = None
self._video_box = VideoBox(xalign=hippo.ALIGNMENT_CENTER,
yalign=hippo.ALIGNMENT_START,
padding_bottom=units.grid_to_pixels(1))
self.append(self._video_box)
self._entry_box = EntryBox(xalign=hippo.ALIGNMENT_CENTER,
padding_bottom=units.grid_to_pixels(1))
self.append(self._entry_box)
self._color_box = ColorBox(xalign=hippo.ALIGNMENT_CENTER,
padding_bottom=units.grid_to_pixels(1))
self.append(self._color_box)
self._ok = iconbutton.IconButton(icon_name="theme:stock-forward",
padding_bottom=units.grid_to_pixels(0.5))
self._ok.connect('activated', self._ok_activated)
self.append(self._ok)
def _ok_activated(self, item):
pixbuf = self._video_box.get_pixbuf()
name = self._entry_box.get_text()
color = self._color_box.get_color()
if not pixbuf or not name or not color:
return
self._create_profile(pixbuf, name, color)
gtk.main_quit()
def _create_profile(self, pixbuf, name, color):
# Save the buddy icon
icon_path = os.path.join(env.get_profile_path(), "buddy-icon.jpg")
scaled = pixbuf.scale_simple(200, 200, gtk.gdk.INTERP_BILINEAR)
pixbuf.save(icon_path, "jpeg", {"quality":"85"})
cp = ConfigParser()
section = 'Buddy'
cp.add_section(section)
cp.set(section, 'NickName', name)
cp.set(section, 'Color', color.to_string())
config_path = os.path.join(env.get_profile_path(), 'config')
f = open(config_path, 'w')
cp.write(f)
f.close()
# Generate keypair
import commands
keypath = os.path.join(env.get_profile_path(), "owner.key")
cmd = "ssh-keygen -q -t dsa -f %s -C '' -N ''" % keypath
(s, o) = commands.getstatusoutput(cmd)
if s != 0:
logging.error("Could not generate key pair: %d" % s)
class IntroWindow(gtk.Window):
def __init__(self):
gtk.Window.__init__(self)
self.set_default_size(gtk.gdk.screen_width(),
gtk.gdk.screen_height())
self.realize()
self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DESKTOP)
self._canvas = hippo.Canvas()
self._intro_box = IntroBox(background_color=0x000000ff,
yalign=hippo.ALIGNMENT_START,
padding_top=units.grid_to_pixels(2),
padding_left=units.grid_to_pixels(3),
padding_right=units.grid_to_pixels(3))
self._canvas.set_root(self._intro_box)
self.add(self._canvas)
self._canvas.show()
if __name__ == "__main__":
w = IntroWindow()
w.show_all()
w.connect('destroy', gtk.main_quit)
gtk.main()

View File

@ -38,15 +38,17 @@ logger.start('shell')
if len(sys.argv) == 1:
sys.path.insert(0, os.path.join(env.get_data_dir(), 'shell'))
from view.FirstTimeDialog import FirstTimeDialog
from view.Shell import Shell
from model.ShellModel import ShellModel
from shellservice import ShellService
from intro import intro
name = profile.get_nick_name()
if not name or not len(name):
dialog = FirstTimeDialog()
dialog.run()
# Do initial setup if needed
key = profile.get_pubkey()
if not key or not len(key):
win = intro.IntroWindow()
win.show_all()
gtk.main()
profile.update()
# Save our DBus Session Bus address somewhere it can be found

View File

@ -62,7 +62,7 @@ class RGBColor(object):
int(self._b * 65535))
def get_html(self):
return '#%x%x%x' % (self._r * 255, self._g * 255, self._b * 255)
return '#%02x%02x%02x' % (self._r * 255, self._g * 255, self._b * 255)
class HTMLColor(RGBColor):
def __init__(self, html_color):

View File

@ -15,9 +15,11 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os
import logging
from ConfigParser import ConfigParser
from sugar import env
from sugar import util
from sugar.graphics.xocolor import XoColor
class _Profile(object):
@ -25,6 +27,7 @@ class _Profile(object):
self.name = None
self.color = None
self.pubkey = None
self.privkey_hash = None
self._load()
def update(self):
@ -41,11 +44,60 @@ class _Profile(object):
if cp.has_option('Buddy', 'Color'):
self.color = XoColor(cp.get('Buddy', 'Color'))
if cp.has_option('Buddy', 'PublicKey'):
self.pubkey = cp.get('Buddy', 'PublicKey')
del cp
self._load_pubkey()
self._hash_private_key()
def _load_pubkey(self):
self.pubkey = None
key_path = os.path.join(env.get_profile_path(), 'owner.key.pub')
try:
f = open(key_path, "r")
lines = f.readlines()
f.close()
except IOError, e:
logging.error("Error reading public key: %s" % e)
return
magic = "ssh-dss "
for l in lines:
l = l.strip()
if not l.startswith(magic):
continue
self.pubkey = l[len(magic):]
break
if not self.pubkey:
logging.error("Error parsing public key.")
def _hash_private_key(self):
self.privkey_hash = None
key_path = os.path.join(env.get_profile_path(), 'owner.key')
try:
f = open(key_path, "r")
lines = f.readlines()
f.close()
except IOError, e:
logging.error("Error reading private key: %s" % e)
return
key = ""
for l in lines:
l = l.strip()
if l.startswith("-----BEGIN DSA PRIVATE KEY-----"):
continue
if l.startswith("-----END DSA PRIVATE KEY-----"):
continue
key += l
if not len(key):
logging.error("Error parsing public key.")
# hash it
key_hash = util._sha_data(key)
self.privkey_hash = util.printable_hash(key_hash)
def get_nick_name():
return _profile.name
@ -55,6 +107,9 @@ def get_color():
def get_pubkey():
return _profile.pubkey
def get_private_key_hash():
return _profile.privkey_hash
def update():
_profile.update()