Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/shell/model
diff options
context:
space:
mode:
authorDan Williams <dcbw@localhost.localdomain>2006-09-15 20:50:06 (GMT)
committer Dan Williams <dcbw@localhost.localdomain>2006-09-15 20:50:06 (GMT)
commit922b7238b9db8307f6b9df9b3a4e36f9a46e81a0 (patch)
tree90aa62656494be7b5740c43549adbbcfaab49ac8 /shell/model
parentb39eff33655590565aaac3d514df640f1ad3f2a8 (diff)
parente79a5f5e989fa062366295c3f236692fc3c889e5 (diff)
Merge
Diffstat (limited to 'shell/model')
-rw-r--r--shell/model/BuddyInfo.py24
-rw-r--r--shell/model/Friends.py65
-rw-r--r--shell/model/Invites.py54
-rw-r--r--shell/model/Makefile.am8
-rw-r--r--shell/model/Owner.py99
-rw-r--r--shell/model/ShellModel.py64
-rw-r--r--shell/model/__init__.py0
7 files changed, 314 insertions, 0 deletions
diff --git a/shell/model/BuddyInfo.py b/shell/model/BuddyInfo.py
new file mode 100644
index 0000000..68f0a44
--- /dev/null
+++ b/shell/model/BuddyInfo.py
@@ -0,0 +1,24 @@
+from sugar.presence import PresenceService
+from sugar.canvas.IconColor import IconColor
+
+class BuddyInfo:
+ def __init__(self, buddy=None):
+ if buddy:
+ self.set_name(buddy.get_name())
+ self.set_color(buddy.get_color())
+
+ def set_name(self, name):
+ self._name = name
+
+ def set_color(self, color_string):
+ self._color = IconColor(color_string)
+
+ def get_name(self):
+ return self._name
+
+ def get_color(self):
+ return self._color
+
+ def get_buddy(self):
+ pservice = PresenceService.get_instance()
+ return pservice.get_buddy_by_name(self._name)
diff --git a/shell/model/Friends.py b/shell/model/Friends.py
new file mode 100644
index 0000000..a11acc0
--- /dev/null
+++ b/shell/model/Friends.py
@@ -0,0 +1,65 @@
+import os
+from ConfigParser import ConfigParser
+
+import gobject
+
+from model.BuddyInfo import BuddyInfo
+from sugar import env
+
+class Friends(gobject.GObject):
+ __gsignals__ = {
+ 'friend-added': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([object])),
+ 'friend-removed': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([str])),
+ }
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+
+ self._friends = {}
+ self._path = os.path.join(env.get_profile_path(), 'friends')
+
+ self.load()
+
+ def has_buddy(self, buddy):
+ return self._friends.has_key(buddy.get_name())
+
+ def add_friend(self, buddy_info):
+ self._friends[buddy_info.get_name()] = buddy_info
+ self.emit('friend-added', buddy_info)
+
+ def make_friend(self, buddy):
+ if not self.has_buddy(buddy):
+ self.add_friend(BuddyInfo(buddy))
+ self.save()
+
+ def remove(self, buddy_info):
+ del self._friends[buddy_info.get_name()]
+ self.save()
+ self.emit('friend-removed', buddy_info.get_name())
+
+ def __iter__(self):
+ return self._friends.values().__iter__()
+
+ def load(self):
+ cp = ConfigParser()
+
+ if cp.read([self._path]):
+ for name in cp.sections():
+ buddy = BuddyInfo()
+ buddy.set_name(name)
+ buddy.set_color(cp.get(name, 'color'))
+ self.add_friend(buddy)
+
+ def save(self):
+ cp = ConfigParser()
+
+ for friend in self:
+ section = friend.get_name()
+ cp.add_section(section)
+ cp.set(section, 'color', friend.get_color().to_string())
+
+ fileobject = open(self._path, 'w')
+ cp.write(fileobject)
+ fileobject.close()
diff --git a/shell/model/Invites.py b/shell/model/Invites.py
new file mode 100644
index 0000000..24f9913
--- /dev/null
+++ b/shell/model/Invites.py
@@ -0,0 +1,54 @@
+import gobject
+
+import conf
+from sugar.presence import PresenceService
+from sugar.canvas.IconColor import IconColor
+
+class Invite:
+ def __init__(self, issuer, bundle_id, activity_id):
+ self._issuer = issuer
+ self._activity_id = activity_id
+ self._bundle_id = bundle_id
+
+ def get_icon(self):
+ reg = conf.get_activity_registry()
+ return reg.get_activity(self._bundle_id).get_icon()
+
+ def get_color(self):
+ pservice = PresenceService.get_instance()
+ buddy = pservice.get_buddy_by_name(self._issuer)
+ if buddy != None:
+ return IconColor(buddy.get_color())
+ else:
+ return IconColor('white')
+
+ def get_activity_id(self):
+ return self._activity_id
+
+ def get_bundle_id(self):
+ return self._bundle_id
+
+class Invites(gobject.GObject):
+ __gsignals__ = {
+ 'invite-added': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([object])),
+ 'invite-removed': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([object])),
+ }
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+
+ self._list = []
+
+ def add_invite(self, issuer, bundle_id, activity_id):
+ invite = Invite(issuer, bundle_id, activity_id)
+ self._list.append(invite)
+ self.emit('invite-added', invite)
+
+ def remove_invite(self, invite):
+ self._list.remove(invite)
+ self.emit('invite-removed', invite)
+
+ def __iter__(self):
+ return self._list.__iter__()
diff --git a/shell/model/Makefile.am b/shell/model/Makefile.am
new file mode 100644
index 0000000..ce24668
--- /dev/null
+++ b/shell/model/Makefile.am
@@ -0,0 +1,8 @@
+sugardir = $(pkgdatadir)/shell/model
+sugar_PYTHON = \
+ __init__.py \
+ BuddyInfo.py \
+ Friends.py \
+ Invites.py \
+ Owner.py \
+ ShellModel.py
diff --git a/shell/model/Owner.py b/shell/model/Owner.py
new file mode 100644
index 0000000..24004f0
--- /dev/null
+++ b/shell/model/Owner.py
@@ -0,0 +1,99 @@
+import os
+import random
+import base64
+import time
+
+import conf
+from sugar import env
+import logging
+from sugar.p2p import Stream
+from sugar.presence import PresenceService
+from model.Invites import Invites
+
+PRESENCE_SERVICE_TYPE = "_presence_olpc._tcp"
+
+class ShellOwner(object):
+ """Class representing the owner of this machine/instance. This class
+ runs in the shell and serves up the buddy icon and other stuff. It's the
+ server portion of the Owner, paired with the client portion in Buddy.py."""
+ def __init__(self, shell):
+ profile = conf.get_profile()
+
+ self._nick = profile.get_nick_name()
+ user_dir = profile.get_path()
+
+ self._icon = None
+ for fname in os.listdir(user_dir):
+ if not fname.startswith("buddy-icon."):
+ continue
+ fd = open(os.path.join(user_dir, fname), "r")
+ self._icon = fd.read()
+ fd.close()
+ break
+
+ self._pservice = PresenceService.get_instance()
+
+ self._invites = Invites()
+
+ self._shell = shell
+ self._shell.connect('activity-changed', self.__activity_changed_cb)
+ self._last_activity_update = time.time()
+ self._pending_activity_update_timer = None
+ self._pending_activity_update = None
+
+ def get_invites(self):
+ return self._invites
+
+ def announce(self):
+ # Create and announce our presence
+ color = conf.get_profile().get_color()
+ props = {'color':color.to_string()}
+ activity = self._shell.get_current_activity()
+ if activity is not None:
+ props['cur_activity':activity.get_id()]
+ self._last_activity_update = time.time()
+ self._service = self._pservice.register_service(self._nick,
+ PRESENCE_SERVICE_TYPE, properties=props)
+ logging.debug("Owner '%s' using port %d" % (self._nick, self._service.get_port()))
+ self._icon_stream = Stream.Stream.new_from_service(self._service)
+ self._icon_stream.register_reader_handler(self._handle_buddy_icon_request, "get_buddy_icon")
+ self._icon_stream.register_reader_handler(self._handle_invite, "invite")
+
+ def _handle_buddy_icon_request(self):
+ """XMLRPC method, return the owner's icon encoded with base64."""
+ if self._icon:
+ return base64.b64encode(self._icon)
+ return ""
+
+ def _handle_invite(self, issuer, bundle_id, activity_id):
+ """XMLRPC method, called when the owner is invited to an activity."""
+ self._invites.add_invite(issuer, bundle_id, activity_id)
+ return ''
+
+ def __update_advertised_current_activity_cb(self):
+ self._last_activity_update = time.time()
+ self._pending_activity_update_timer = None
+ logging.debug("*** Updating current activity to %s" % self._pending_activity_update)
+ return False
+
+ def __activity_changed_cb(self, shell, activity):
+ """Update our presence service with the latest activity, but no
+ more frequently than every 30 seconds"""
+ self._pending_activity_update = activity.get_id()
+ # If there's no pending update, we must not have updated it in the
+ # last 30 seconds (except for the initial update, hence we also check
+ # for the last update)
+ if not self._pending_activity_update_timer or time.time() - self._last_activity_update > 30:
+ self.__update_advertised_current_activity_cb()
+ return
+
+ # If we have a pending update already, we have nothing left to do
+ if self._pending_activity_update_timer:
+ return
+
+ # Otherwise, we start a timer to update the activity at the next
+ # interval, which should be 30 seconds from the last update, or if that
+ # is in the past already, then now
+ next = 30 - max(30, time.time() - self._last_activity_update)
+ self._pending_activity_update_timer = gobject.timeout_add(next * 1000,
+ self.__update_advertised_current_activity_cb)
diff --git a/shell/model/ShellModel.py b/shell/model/ShellModel.py
new file mode 100644
index 0000000..0820b44
--- /dev/null
+++ b/shell/model/ShellModel.py
@@ -0,0 +1,64 @@
+import gobject
+
+from sugar.presence import PresenceService
+from model.Friends import Friends
+from model.Owner import ShellOwner
+
+class ShellModel(gobject.GObject):
+ __gsignals__ = {
+ 'activity-opened': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
+ 'activity-changed': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
+ 'activity-closed': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT]))
+ }
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+
+ self._hosts = {}
+ self._current_activity = None
+
+ PresenceService.start()
+ self._pservice = PresenceService.get_instance()
+
+ self._owner = ShellOwner(self)
+ self._owner.announce()
+ self._friends = Friends()
+
+ def get_friends(self):
+ return self._friends
+
+ def get_invites(self):
+ return self._owner.get_invites()
+
+ def get_owner(self):
+ return self._owner
+
+ def add_activity(self, activity_host):
+ self._hosts[activity_host.get_xid()] = activity_host
+ self.emit('activity-opened', activity_host)
+
+ def set_current_activity(self, activity_xid):
+ activity_host = self._hosts[activity_xid]
+ if self._current_activity == activity_host:
+ return
+
+ self._current_activity = activity_host
+ self.emit('activity-changed', activity_host)
+
+ def remove_activity(self, activity_xid):
+ if self._hosts.has_key(activity_xid):
+ host = self._hosts[activity_xid]
+ self.emit('activity-closed', host)
+ del self._hosts[activity_xid]
+
+ def get_activity(self, activity_id):
+ for host in self._hosts.values():
+ if host.get_id() == activity_id:
+ return host
+ return None
+
+ def get_current_activity(self):
+ return self._current_activity
diff --git a/shell/model/__init__.py b/shell/model/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/shell/model/__init__.py