Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/model
diff options
context:
space:
mode:
Diffstat (limited to 'src/model')
-rw-r--r--src/model/BuddyModel.py164
-rw-r--r--src/model/Friends.py114
-rw-r--r--src/model/Invites.py72
-rw-r--r--src/model/Makefile.am14
-rw-r--r--src/model/MeshModel.py235
-rw-r--r--src/model/Owner.py87
-rw-r--r--src/model/__init__.py16
-rw-r--r--src/model/accesspointmodel.py81
-rw-r--r--src/model/devices/Makefile.am8
-rw-r--r--src/model/devices/__init__.py16
-rw-r--r--src/model/devices/battery.py96
-rw-r--r--src/model/devices/device.py45
-rw-r--r--src/model/devices/devicesmodel.py136
-rw-r--r--src/model/devices/network/Makefile.am6
-rw-r--r--src/model/devices/network/__init__.py16
-rw-r--r--src/model/devices/network/mesh.py75
-rw-r--r--src/model/devices/network/wired.py28
-rw-r--r--src/model/devices/network/wireless.py96
-rw-r--r--src/model/homeactivity.py215
-rw-r--r--src/model/homemodel.py283
-rw-r--r--src/model/shellmodel.py112
21 files changed, 1915 insertions, 0 deletions
diff --git a/src/model/BuddyModel.py b/src/model/BuddyModel.py
new file mode 100644
index 0000000..11c6567
--- /dev/null
+++ b/src/model/BuddyModel.py
@@ -0,0 +1,164 @@
+# Copyright (C) 2006-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 logging
+
+from sugar.presence import presenceservice
+from sugar.graphics.xocolor import XoColor
+import gobject
+
+_NOT_PRESENT_COLOR = "#888888,#BBBBBB"
+
+class BuddyModel(gobject.GObject):
+ __gsignals__ = {
+ 'appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
+ 'disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
+ 'nick-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
+ 'color-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
+ 'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([])),
+ 'current-activity-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT]))
+ }
+
+ def __init__(self, key=None, buddy=None, nick=None):
+ if (key and buddy) or (not key and not buddy):
+ raise RuntimeError("Must specify only _one_ of key or buddy.")
+
+ gobject.GObject.__init__(self)
+
+ self._ba_handler = None
+ self._pc_handler = None
+ self._dis_handler = None
+ self._bic_handler = None
+ self._cac_handler = None
+
+ self._pservice = presenceservice.get_instance()
+
+ self._buddy = None
+
+ if not buddy:
+ self._key = key
+ # connect to the PS's buddy-appeared signal and
+ # wait for the buddy to appear
+ self._ba_handler = self._pservice.connect('buddy-appeared',
+ self._buddy_appeared_cb)
+ # Set color to 'inactive'/'disconnected'
+ self._set_color_from_string(_NOT_PRESENT_COLOR)
+ self._nick = nick
+
+ self._pservice.get_buddies_async(reply_handler=self._get_buddies_cb)
+ else:
+ self._update_buddy(buddy)
+
+ def _get_buddies_cb(self, list):
+ buddy = None
+ for iter_buddy in list:
+ if iter_buddy.props.key == self._key:
+ buddy = iter_buddy
+ break
+
+ if buddy:
+ if self._ba_handler:
+ # Once we have the buddy, we no longer need to
+ # monitor buddy-appeared events
+ self._pservice.disconnect(self._ba_handler)
+ self._ba_handler = None
+
+ self._update_buddy(buddy)
+
+ def _set_color_from_string(self, color_string):
+ self._color = XoColor(color_string)
+
+ def get_key(self):
+ return self._key
+
+ def get_nick(self):
+ return self._nick
+
+ def get_color(self):
+ return self._color
+
+ def get_buddy(self):
+ return self._buddy
+
+ def is_owner(self):
+ if not self._buddy:
+ return False
+ return self._buddy.props.owner
+
+ def is_present(self):
+ if self._buddy:
+ return True
+ return False
+
+ def get_current_activity(self):
+ if self._buddy:
+ return self._buddy.props.current_activity
+ return None
+
+ def _update_buddy(self, buddy):
+ if not buddy:
+ raise ValueError("Buddy cannot be None.")
+
+ self._buddy = buddy
+ self._key = self._buddy.props.key
+ self._nick = self._buddy.props.nick
+ self._set_color_from_string(self._buddy.props.color)
+
+ self._pc_handler = self._buddy.connect('property-changed', self._buddy_property_changed_cb)
+ self._bic_handler = self._buddy.connect('icon-changed', self._buddy_icon_changed_cb)
+
+ def _buddy_appeared_cb(self, pservice, buddy):
+ if self._buddy or buddy.props.key != self._key:
+ return
+
+ if self._ba_handler:
+ # Once we have the buddy, we no longer need to
+ # monitor buddy-appeared events
+ self._pservice.disconnect(self._ba_handler)
+ self._ba_handler = None
+
+ self._update_buddy(buddy)
+ self.emit('appeared')
+
+ def _buddy_property_changed_cb(self, buddy, keys):
+ if not self._buddy:
+ return
+ if 'color' in keys:
+ self._set_color_from_string(self._buddy.props.color)
+ self.emit('color-changed', self.get_color())
+ if 'current-activity' in keys:
+ self.emit('current-activity-changed', buddy.props.current_activity)
+ if 'nick' in keys:
+ self._nick = self._buddy.props.nick
+ self.emit('nick-changed', self.get_nick())
+
+ def _buddy_disappeared_cb(self, buddy):
+ if buddy != self._buddy:
+ return
+ self._buddy.disconnect(self._pc_handler)
+ self._buddy.disconnect(self._dis_handler)
+ self._buddy.disconnect(self._bic_handler)
+ self._buddy.disconnect(self._cac_handler)
+ self._set_color_from_string(_NOT_PRESENT_COLOR)
+ self.emit('disappeared')
+ self._buddy = None
+
+ def _buddy_icon_changed_cb(self, buddy):
+ self.emit('icon-changed')
diff --git a/src/model/Friends.py b/src/model/Friends.py
new file mode 100644
index 0000000..6fc3e97
--- /dev/null
+++ b/src/model/Friends.py
@@ -0,0 +1,114 @@
+# Copyright (C) 2006-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 dbus
+import os
+from ConfigParser import ConfigParser
+
+import gobject
+
+from model.BuddyModel import BuddyModel
+from sugar import env
+import logging
+
+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_key())
+
+ def add_friend(self, buddy_info):
+ self._friends[buddy_info.get_key()] = buddy_info
+ self.emit('friend-added', buddy_info)
+
+ def make_friend(self, buddy):
+ if not self.has_buddy(buddy):
+ self.add_friend(buddy)
+ self.save()
+
+ def remove(self, buddy_info):
+ del self._friends[buddy_info.get_key()]
+ self.save()
+ self.emit('friend-removed', buddy_info.get_key())
+
+ def __iter__(self):
+ return self._friends.values().__iter__()
+
+ def load(self):
+ cp = ConfigParser()
+
+ try:
+ success = cp.read([self._path])
+ if success:
+ for key in cp.sections():
+ # HACK: don't screw up on old friends files
+ if len(key) < 20:
+ continue
+ buddy = BuddyModel(key=key, nick=cp.get(key, 'nick'))
+ self.add_friend(buddy)
+ except Exception, exc:
+ logging.error("Error parsing friends file: %s" % exc)
+
+ def save(self):
+ cp = ConfigParser()
+
+ for friend in self:
+ section = friend.get_key()
+ cp.add_section(section)
+ cp.set(section, 'nick', friend.get_nick())
+ cp.set(section, 'color', friend.get_color().to_string())
+
+ fileobject = open(self._path, 'w')
+ cp.write(fileobject)
+ fileobject.close()
+
+ self._sync_friends()
+
+ def _sync_friends(self):
+ # XXX: temporary hack
+ # remove this when the shell service has a D-Bus API for buddies
+
+ def friends_synced():
+ pass
+
+ def friends_synced_error(e):
+ logging.error("Error asking presence service to sync friends: %s"
+ % e)
+
+ keys = []
+ for friend in self:
+ keys.append(friend.get_key())
+
+ bus = dbus.SessionBus()
+ ps = bus.get_object('org.laptop.Sugar.Presence',
+ '/org/laptop/Sugar/Presence')
+ psi = dbus.Interface(ps, 'org.laptop.Sugar.Presence')
+ psi.SyncFriends(keys,
+ reply_handler=friends_synced,
+ error_handler=friends_synced_error)
diff --git a/src/model/Invites.py b/src/model/Invites.py
new file mode 100644
index 0000000..9ffab44
--- /dev/null
+++ b/src/model/Invites.py
@@ -0,0 +1,72 @@
+# Copyright (C) 2006-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 gobject
+from sugar.presence import presenceservice
+
+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_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._dict = {}
+
+ ps = presenceservice.get_instance()
+ owner = ps.get_owner()
+ owner.connect('joined-activity', self._owner_joined_cb)
+
+ def add_invite(self, issuer, bundle_id, activity_id):
+ if activity_id in self._dict:
+ # there is no point to add more than one time
+ # an invite for the same activity
+ return
+
+ invite = Invite(issuer, bundle_id, activity_id)
+ self._dict[activity_id] = invite
+ self.emit('invite-added', invite)
+
+ def remove_invite(self, invite):
+ self._dict.pop(invite.get_activity_id())
+ self.emit('invite-removed', invite)
+
+ def remove_activity(self, activity_id):
+ invite = self._dict.get(activity_id)
+ if invite is not None:
+ self.remove_invite(invite)
+
+ def _owner_joined_cb(self, owner, activity):
+ self.remove_activity(activity.props.id)
+
+ def __iter__(self):
+ return self._dict.values().__iter__()
diff --git a/src/model/Makefile.am b/src/model/Makefile.am
new file mode 100644
index 0000000..0b7d14c
--- /dev/null
+++ b/src/model/Makefile.am
@@ -0,0 +1,14 @@
+SUBDIRS = devices
+
+sugardir = $(pkgdatadir)/shell/model
+sugar_PYTHON = \
+ __init__.py \
+ accesspointmodel.py \
+ BuddyModel.py \
+ Friends.py \
+ Invites.py \
+ Owner.py \
+ MeshModel.py \
+ shellmodel.py \
+ homeactivity.py \
+ homemodel.py
diff --git a/src/model/MeshModel.py b/src/model/MeshModel.py
new file mode 100644
index 0000000..da5b3c2
--- /dev/null
+++ b/src/model/MeshModel.py
@@ -0,0 +1,235 @@
+# Copyright (C) 2006-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 gobject
+
+from sugar.graphics.xocolor import XoColor
+from sugar.presence import presenceservice
+from sugar import activity
+
+from model.BuddyModel import BuddyModel
+from model.accesspointmodel import AccessPointModel
+from hardware import hardwaremanager
+from hardware import nmclient
+
+class ActivityModel:
+ def __init__(self, activity, bundle):
+ self.activity = activity
+ self.bundle = bundle
+
+ def get_id(self):
+ return self.activity.props.id
+
+ def get_icon_name(self):
+ return self.bundle.icon
+
+ def get_color(self):
+ return XoColor(self.activity.props.color)
+
+ def get_bundle_id(self):
+ return self.bundle.bundle_id
+
+class MeshModel(gobject.GObject):
+ __gsignals__ = {
+ 'activity-added': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
+ 'activity-removed': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
+ 'buddy-added': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
+ 'buddy-moved': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT,
+ gobject.TYPE_PYOBJECT])),
+ 'buddy-removed': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
+ 'access-point-added': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
+ 'access-point-removed': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
+ 'mesh-added': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
+ 'mesh-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([]))
+ }
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+
+ self._activities = {}
+ self._buddies = {}
+ self._access_points = {}
+ self._mesh = None
+
+ self._pservice = presenceservice.get_instance()
+ self._pservice.connect("activity-appeared",
+ self._activity_appeared_cb)
+ self._pservice.connect('activity-disappeared',
+ self._activity_disappeared_cb)
+ self._pservice.connect("buddy-appeared",
+ self._buddy_appeared_cb)
+ self._pservice.connect("buddy-disappeared",
+ self._buddy_disappeared_cb)
+
+ # Add any buddies the PS knows about already
+ self._pservice.get_buddies_async(reply_handler=self._get_buddies_cb)
+
+ self._pservice.get_activities_async(reply_handler=self._get_activities_cb)
+
+ network_manager = hardwaremanager.get_network_manager()
+ if network_manager:
+ for nm_device in network_manager.get_devices():
+ self._add_network_device(nm_device)
+ network_manager.connect('device-added',
+ self._nm_device_added_cb)
+ network_manager.connect('device-removed',
+ self._nm_device_removed_cb)
+
+ def _get_buddies_cb(self, list):
+ for buddy in list:
+ self._buddy_appeared_cb(self._pservice, buddy)
+
+ def _get_activities_cb(self, list):
+ for activity in list:
+ self._check_activity(activity)
+
+ def _nm_device_added_cb(self, manager, nm_device):
+ self._add_network_device(nm_device)
+
+ def _nm_device_removed_cb(self, manager, nm_device):
+ self._remove_network_device(nm_device)
+
+ def _nm_network_appeared_cb(self, nm_device, nm_network):
+ self._add_access_point(nm_device, nm_network)
+
+ def _nm_network_disappeared_cb(self, nm_device, nm_network):
+ if self._access_points.has_key(nm_network.get_op()):
+ ap = self._access_points[nm_network.get_op()]
+ self._remove_access_point(ap)
+
+ def _add_network_device(self, nm_device):
+ dtype = nm_device.get_type()
+ if dtype == nmclient.DEVICE_TYPE_802_11_WIRELESS:
+ for nm_network in nm_device.get_networks():
+ self._add_access_point(nm_device, nm_network)
+
+ nm_device.connect('network-appeared',
+ self._nm_network_appeared_cb)
+ nm_device.connect('network-disappeared',
+ self._nm_network_disappeared_cb)
+ elif dtype == nmclient.DEVICE_TYPE_802_11_MESH_OLPC:
+ self._mesh = nm_device
+ self.emit('mesh-added', self._mesh)
+
+ def _remove_network_device(self, nm_device):
+ if nm_device == self._mesh:
+ self._mesh = None
+ self.emit('mesh-removed')
+ elif nm_device.get_type() == nmclient.DEVICE_TYPE_802_11_WIRELESS:
+ aplist = self._access_points.values()
+ for ap in aplist:
+ if ap.get_nm_device() == nm_device:
+ self._remove_access_point(ap)
+
+ def _add_access_point(self, nm_device, nm_network):
+ model = AccessPointModel(nm_device, nm_network)
+ self._access_points[model.get_id()] = model
+ self.emit('access-point-added', model)
+
+ def _remove_access_point(self, ap):
+ if not self._access_points.has_key(ap.get_id()):
+ return
+ self.emit('access-point-removed', ap)
+ del self._access_points[ap.get_id()]
+
+ def get_mesh(self):
+ return self._mesh
+
+ def get_access_points(self):
+ return self._access_points.values()
+
+ def get_activities(self):
+ return self._activities.values()
+
+ def get_buddies(self):
+ return self._buddies.values()
+
+ def _buddy_activity_changed_cb(self, model, cur_activity):
+ if not self._buddies.has_key(model.get_key()):
+ return
+ if cur_activity and self._activities.has_key(cur_activity.props.id):
+ activity_model = self._activities[cur_activity.props.id]
+ self.emit('buddy-moved', model, activity_model)
+ else:
+ self.emit('buddy-moved', model, None)
+
+ def _buddy_appeared_cb(self, pservice, buddy):
+ if self._buddies.has_key(buddy.props.key):
+ return
+
+ model = BuddyModel(buddy=buddy)
+ model.connect('current-activity-changed',
+ self._buddy_activity_changed_cb)
+ self._buddies[buddy.props.key] = model
+ self.emit('buddy-added', model)
+
+ cur_activity = buddy.props.current_activity
+ if cur_activity:
+ self._buddy_activity_changed_cb(model, cur_activity)
+
+ def _buddy_disappeared_cb(self, pservice, buddy):
+ if not self._buddies.has_key(buddy.props.key):
+ return
+ self.emit('buddy-removed', self._buddies[buddy.props.key])
+ del self._buddies[buddy.props.key]
+
+ def _activity_appeared_cb(self, pservice, activity):
+ self._check_activity(activity)
+
+ def _check_activity(self, presence_activity):
+ registry = activity.get_registry()
+ bundle = registry.get_activity(presence_activity.props.type)
+ if not bundle:
+ return
+ if self.has_activity(presence_activity.props.id):
+ return
+ self.add_activity(bundle, presence_activity)
+
+ def has_activity(self, activity_id):
+ return self._activities.has_key(activity_id)
+
+ def get_activity(self, activity_id):
+ if self.has_activity(activity_id):
+ return self._activities[activity_id]
+ else:
+ return None
+
+ def add_activity(self, bundle, activity):
+ model = ActivityModel(activity, bundle)
+ self._activities[model.get_id()] = model
+ self.emit('activity-added', model)
+
+ for buddy in self._pservice.get_buddies():
+ cur_activity = buddy.props.current_activity
+ key = buddy.props.key
+ if cur_activity == activity and self._buddies.has_key(key):
+ buddy_model = self._buddies[key]
+ self.emit('buddy-moved', buddy_model, model)
+
+ def _activity_disappeared_cb(self, pservice, activity):
+ if self._activities.has_key(activity.props.id):
+ activity_model = self._activities[activity.props.id]
+ self.emit('activity-removed', activity_model)
+ del self._activities[activity.props.id]
diff --git a/src/model/Owner.py b/src/model/Owner.py
new file mode 100644
index 0000000..b06b391
--- /dev/null
+++ b/src/model/Owner.py
@@ -0,0 +1,87 @@
+# Copyright (C) 2006-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 gobject
+import os
+import random
+import base64
+import time
+import logging
+import dbus
+
+from sugar import env
+from sugar import profile
+from sugar.presence import presenceservice
+from sugar import util
+from model.Invites import Invites
+
+class ShellOwner(gobject.GObject):
+ __gtype_name__ = "ShellOwner"
+
+ __gsignals__ = {
+ 'nick-changed' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_STRING])),
+ 'color-changed' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
+ 'icon-changed' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT]))
+ }
+
+ """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):
+ gobject.GObject.__init__(self)
+
+ self._nick = profile.get_nick_name()
+
+ self._icon = None
+ self._icon_hash = ""
+ icon = os.path.join(env.get_profile_path(), "buddy-icon.jpg")
+ if not os.path.exists(icon):
+ raise RuntimeError("missing buddy icon")
+
+ fd = open(icon, "r")
+ self._icon = fd.read()
+ fd.close()
+ if not self._icon:
+ raise RuntimeError("invalid buddy icon")
+
+ # Get the icon's hash
+ import md5
+ digest = md5.new(self._icon).digest()
+ self._icon_hash = util.printable_hash(digest)
+
+ self._pservice = presenceservice.get_instance()
+ self._pservice.connect('activity-invitation',
+ self._activity_invitation_cb)
+ self._pservice.connect('activity-disappeared',
+ self._activity_disappeared_cb)
+
+ self._invites = Invites()
+
+ def get_invites(self):
+ return self._invites
+
+ def get_nick(self):
+ return self._nick
+
+ def _activity_invitation_cb(self, pservice, activity, buddy, message):
+ self._invites.add_invite(buddy, activity.props.type,
+ activity.props.id)
+
+ def _activity_disappeared_cb(self, pservice, activity):
+ self._invites.remove_activity(activity.props.id)
diff --git a/src/model/__init__.py b/src/model/__init__.py
new file mode 100644
index 0000000..a9dd95a
--- /dev/null
+++ b/src/model/__init__.py
@@ -0,0 +1,16 @@
+# Copyright (C) 2006-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
+
diff --git a/src/model/accesspointmodel.py b/src/model/accesspointmodel.py
new file mode 100644
index 0000000..1d4d6cb
--- /dev/null
+++ b/src/model/accesspointmodel.py
@@ -0,0 +1,81 @@
+# Copyright (C) 2006-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 gobject
+import sys
+
+from hardware import nmclient
+
+STATE_CONNECTING = 0
+STATE_CONNECTED = 1
+STATE_NOTCONNECTED = 2
+
+_nm_state_to_state = {
+ nmclient.NETWORK_STATE_CONNECTED : STATE_CONNECTED,
+ nmclient.NETWORK_STATE_CONNECTING : STATE_CONNECTING,
+ nmclient.NETWORK_STATE_NOTCONNECTED : STATE_NOTCONNECTED
+}
+
+class AccessPointModel(gobject.GObject):
+ __gproperties__ = {
+ 'name' : (str, None, None, None,
+ gobject.PARAM_READABLE),
+ 'strength' : (int, None, None, 0, 100, 0,
+ gobject.PARAM_READABLE),
+ 'state' : (int, None, None, STATE_CONNECTING,
+ STATE_NOTCONNECTED, 0, gobject.PARAM_READABLE),
+ 'capabilities' : (int, None, None, 0, 0x7FFFFFFF, 0,
+ gobject.PARAM_READABLE),
+ 'mode' : (int, None, None, 0, 6, 0, gobject.PARAM_READABLE)
+ }
+
+ def __init__(self, nm_device, nm_network):
+ gobject.GObject.__init__(self)
+ self._nm_network = nm_network
+ self._nm_device = nm_device
+
+ self._nm_network.connect('strength-changed',
+ self._strength_changed_cb)
+ self._nm_network.connect('state-changed',
+ self._state_changed_cb)
+
+ def _strength_changed_cb(self, nm_network):
+ self.notify('strength')
+
+ def _state_changed_cb(self, nm_network):
+ self.notify('state')
+
+ def get_id(self):
+ return self._nm_network.get_op()
+
+ def get_nm_device(self):
+ return self._nm_device
+
+ def get_nm_network(self):
+ return self._nm_network
+
+ def do_get_property(self, pspec):
+ if pspec.name == 'strength':
+ return self._nm_network.get_strength()
+ elif pspec.name == 'name':
+ return self._nm_network.get_ssid()
+ elif pspec.name == 'state':
+ nm_state = self._nm_network.get_state()
+ return _nm_state_to_state[nm_state]
+ elif pspec.name == 'capabilities':
+ return self._nm_network.get_caps()
+ elif pspec.name == 'mode':
+ return self._nm_network.get_mode()
diff --git a/src/model/devices/Makefile.am b/src/model/devices/Makefile.am
new file mode 100644
index 0000000..5440eeb
--- /dev/null
+++ b/src/model/devices/Makefile.am
@@ -0,0 +1,8 @@
+SUBDIRS = network
+
+sugardir = $(pkgdatadir)/shell/model/devices
+sugar_PYTHON = \
+ __init__.py \
+ device.py \
+ devicesmodel.py \
+ battery.py
diff --git a/src/model/devices/__init__.py b/src/model/devices/__init__.py
new file mode 100644
index 0000000..a9dd95a
--- /dev/null
+++ b/src/model/devices/__init__.py
@@ -0,0 +1,16 @@
+# Copyright (C) 2006-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
+
diff --git a/src/model/devices/battery.py b/src/model/devices/battery.py
new file mode 100644
index 0000000..853d00e
--- /dev/null
+++ b/src/model/devices/battery.py
@@ -0,0 +1,96 @@
+# Copyright (C) 2006-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 logging
+
+import gobject
+import dbus
+
+from model.devices import device
+
+_LEVEL_PROP = 'battery.charge_level.percentage'
+_CHARGING_PROP = 'battery.rechargeable.is_charging'
+_DISCHARGING_PROP = 'battery.rechargeable.is_discharging'
+
+class Device(device.Device):
+ __gproperties__ = {
+ 'level' : (int, None, None, 0, 100, 0,
+ gobject.PARAM_READABLE),
+ 'charging' : (bool, None, None, False,
+ gobject.PARAM_READABLE),
+ 'discharging' : (bool, None, None, False,
+ gobject.PARAM_READABLE)
+ }
+
+ def __init__(self, udi):
+ device.Device.__init__(self, udi)
+
+ bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
+ proxy = bus.get_object('org.freedesktop.Hal', udi)
+ self._battery = dbus.Interface(proxy, 'org.freedesktop.Hal.Device')
+ bus.add_signal_receiver(self._battery_changed,
+ 'PropertyModified',
+ 'org.freedesktop.Hal.Device',
+ 'org.freedesktop.Hal',
+ udi)
+
+ self._level = self._get_level()
+ self._charging = self._get_charging()
+ self._discharging = self._get_discharging()
+
+ def _get_level(self):
+ try:
+ return self._battery.GetProperty(_LEVEL_PROP)
+ except dbus.DBusException:
+ logging.error('Cannot access %s' % _LEVEL_PROP)
+ return 0
+
+ def _get_charging(self):
+ try:
+ return self._battery.GetProperty(_CHARGING_PROP)
+ except dbus.DBusException:
+ logging.error('Cannot access %s' % _CHARGING_PROP)
+ return False
+
+ def _get_discharging(self):
+ try:
+ return self._battery.GetProperty(_DISCHARGING_PROP)
+ except dbus.DBusException:
+ logging.error('Cannot access %s' % _DISCHARGING_PROP)
+ return False
+
+ def do_get_property(self, pspec):
+ if pspec.name == 'level':
+ return self._level
+ if pspec.name == 'charging':
+ return self._charging
+ if pspec.name == 'discharging':
+ return self._discharging
+
+ def get_type(self):
+ return 'battery'
+
+ def _battery_changed(self, num_changes, changes_list):
+ for change in changes_list:
+ if change[0] == _LEVEL_PROP:
+ self._level = self._get_level()
+ self.notify('level')
+ elif change[0] == _CHARGING_PROP:
+ self._charging = self._get_charging()
+ self.notify('charging')
+ elif change[0] == _DISCHARGING_PROP:
+ self._discharging = self._get_discharging()
+ self.notify('discharging')
diff --git a/src/model/devices/device.py b/src/model/devices/device.py
new file mode 100644
index 0000000..d7105b5
--- /dev/null
+++ b/src/model/devices/device.py
@@ -0,0 +1,45 @@
+#
+# 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 gobject
+from hardware import nmclient
+
+from sugar import util
+
+STATE_ACTIVATING = 0
+STATE_ACTIVATED = 1
+STATE_INACTIVE = 2
+
+_nm_state_to_state = {
+ nmclient.DEVICE_STATE_ACTIVATING : STATE_ACTIVATING,
+ nmclient.DEVICE_STATE_ACTIVATED : STATE_ACTIVATED,
+ nmclient.DEVICE_STATE_INACTIVE : STATE_INACTIVE
+}
+
+class Device(gobject.GObject):
+ def __init__(self, device_id=None):
+ gobject.GObject.__init__(self)
+ if device_id:
+ self._id = device_id
+ else:
+ self._id = util.unique_id()
+
+ def get_type(self):
+ return 'unknown'
+
+ def get_id(self):
+ return self._id
diff --git a/src/model/devices/devicesmodel.py b/src/model/devices/devicesmodel.py
new file mode 100644
index 0000000..fab9fa4
--- /dev/null
+++ b/src/model/devices/devicesmodel.py
@@ -0,0 +1,136 @@
+#
+# 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 logging
+import gobject
+import dbus
+
+from model.devices import device
+from model.devices.network import wired
+from model.devices.network import wireless
+from model.devices.network import mesh
+from model.devices import battery
+from hardware import hardwaremanager
+from hardware import nmclient
+
+class DevicesModel(gobject.GObject):
+ __gsignals__ = {
+ 'device-appeared' : (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
+ 'device-disappeared': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT]))
+ }
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+
+ self._devices = {}
+ self._sigids = {}
+
+ self._observe_hal_manager()
+ self._observe_network_manager()
+
+ def _observe_hal_manager(self):
+ bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
+ proxy = bus.get_object('org.freedesktop.Hal',
+ '/org/freedesktop/Hal/Manager')
+ hal_manager = dbus.Interface(proxy, 'org.freedesktop.Hal.Manager')
+
+ for udi in hal_manager.FindDeviceByCapability('battery'):
+ self.add_device(battery.Device(udi))
+
+ def _observe_network_manager(self):
+ network_manager = hardwaremanager.get_network_manager()
+ if not network_manager:
+ return
+
+ for device in network_manager.get_devices():
+ self._check_network_device(device)
+
+ network_manager.connect('device-added',
+ self._network_device_added_cb)
+ network_manager.connect('device-activating',
+ self._network_device_activating_cb)
+ network_manager.connect('device-activated',
+ self._network_device_activated_cb)
+ network_manager.connect('device-removed',
+ self._network_device_removed_cb)
+
+ def _network_device_added_cb(self, network_manager, nm_device):
+ state = nm_device.get_state()
+ if state == nmclient.DEVICE_STATE_ACTIVATING \
+ or state == nmclient.DEVICE_STATE_ACTIVATED:
+ self._check_network_device(nm_device)
+
+ def _network_device_activating_cb(self, network_manager, nm_device):
+ self._check_network_device(nm_device)
+
+ def _network_device_activated_cb(self, network_manager, nm_device):
+ pass
+
+ def _network_device_removed_cb(self, network_manager, nm_device):
+ if self._devices.has_key(str(nm_device.get_op())):
+ self.remove_device(self._get_network_device(nm_device))
+
+ def _check_network_device(self, nm_device):
+ if not nm_device.is_valid():
+ logging.debug("Device %s not valid" % nm_device.get_op())
+ return
+
+ dtype = nm_device.get_type()
+ if dtype == nmclient.DEVICE_TYPE_802_11_WIRELESS \
+ or dtype == nmclient.DEVICE_TYPE_802_11_MESH_OLPC:
+ self._add_network_device(nm_device)
+
+ def _get_network_device(self, nm_device):
+ return self._devices[str(nm_device.get_op())]
+
+ def _network_device_state_changed_cb(self, dev, param):
+ if dev.props.state == device.STATE_INACTIVE:
+ self.remove_device(dev)
+
+ def _add_network_device(self, nm_device):
+ if self._devices.has_key(str(nm_device.get_op())):
+ logging.debug("Tried to add device %s twice" % nm_device.get_op())
+ return
+
+ dtype = nm_device.get_type()
+ if dtype == nmclient.DEVICE_TYPE_802_11_WIRELESS:
+ dev = wireless.Device(nm_device)
+ self.add_device(dev)
+ sigid = dev.connect('notify::state', self._network_device_state_changed_cb)
+ self._sigids[dev] = sigid
+ if dtype == nmclient.DEVICE_TYPE_802_11_MESH_OLPC:
+ dev = mesh.Device(nm_device)
+ self.add_device(dev)
+ sigid = dev.connect('notify::state', self._network_device_state_changed_cb)
+ self._sigids[dev] = sigid
+
+ def __iter__(self):
+ return iter(self._devices.values())
+
+ def add_device(self, device):
+ self._devices[device.get_id()] = device
+ self.emit('device-appeared', device)
+
+ def remove_device(self, device):
+ self.emit('device-disappeared', self._devices[device.get_id()])
+ device.disconnect(self._sigids[device])
+ del self._sigids[device]
+ del self._devices[device.get_id()]
diff --git a/src/model/devices/network/Makefile.am b/src/model/devices/network/Makefile.am
new file mode 100644
index 0000000..04074e5
--- /dev/null
+++ b/src/model/devices/network/Makefile.am
@@ -0,0 +1,6 @@
+sugardir = $(pkgdatadir)/shell/model/devices/network
+sugar_PYTHON = \
+ __init__.py \
+ mesh.py \
+ wired.py \
+ wireless.py
diff --git a/src/model/devices/network/__init__.py b/src/model/devices/network/__init__.py
new file mode 100644
index 0000000..a9dd95a
--- /dev/null
+++ b/src/model/devices/network/__init__.py
@@ -0,0 +1,16 @@
+# Copyright (C) 2006-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
+
diff --git a/src/model/devices/network/mesh.py b/src/model/devices/network/mesh.py
new file mode 100644
index 0000000..0152d8a
--- /dev/null
+++ b/src/model/devices/network/mesh.py
@@ -0,0 +1,75 @@
+#
+# Copyright (C) 2006-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 gobject
+
+from model.devices import device
+from hardware import nmclient
+
+class Device(device.Device):
+ __gproperties__ = {
+ 'strength' : (int, None, None, 0, 100, 0,
+ gobject.PARAM_READABLE),
+ 'state' : (int, None, None, device.STATE_ACTIVATING,
+ device.STATE_INACTIVE, 0, gobject.PARAM_READABLE),
+ 'activation-stage': (int, None, None, 0, 7, 0, gobject.PARAM_READABLE),
+ 'frequency': (float, None, None, 0, 2.72, 0, gobject.PARAM_READABLE),
+ 'mesh-step': (int, None, None, 0, 4, 0, gobject.PARAM_READABLE),
+ }
+
+ def __init__(self, nm_device):
+ device.Device.__init__(self)
+ self._nm_device = nm_device
+
+ self._nm_device.connect('strength-changed',
+ self._strength_changed_cb)
+ self._nm_device.connect('state-changed',
+ self._state_changed_cb)
+ self._nm_device.connect('activation-stage-changed',
+ self._activation_stage_changed_cb)
+
+ def _strength_changed_cb(self, nm_device):
+ self.notify('strength')
+
+ def _state_changed_cb(self, nm_device):
+ self.notify('state')
+
+ def _activation_stage_changed_cb(self, nm_device):
+ self.notify('activation-stage')
+
+ def do_get_property(self, pspec):
+ if pspec.name == 'strength':
+ return self._nm_device.get_strength()
+ elif pspec.name == 'state':
+ nm_state = self._nm_device.get_state()
+ return device._nm_state_to_state[nm_state]
+ elif pspec.name == 'activation-stage':
+ return self._nm_device.get_activation_stage()
+ elif pspec.name == 'frequency':
+ return self._nm_device.get_frequency()
+ elif pspec.name == 'mesh-step':
+ return self._nm_device.get_mesh_step()
+
+ def get_type(self):
+ return 'network.mesh'
+
+ def get_id(self):
+ return str(self._nm_device.get_op())
+
+ def get_nm_device(self):
+ return self._nm_device
+
diff --git a/src/model/devices/network/wired.py b/src/model/devices/network/wired.py
new file mode 100644
index 0000000..66c5206
--- /dev/null
+++ b/src/model/devices/network/wired.py
@@ -0,0 +1,28 @@
+# Copyright (C) 2006-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
+
+from model.devices import device
+
+class Device(device.Device):
+ def __init__(self, nm_device):
+ device.Device.__init__(self)
+ self._nm_device = device
+
+ def get_id(self):
+ return str(self._nm_device.get_op())
+
+ def get_type(self):
+ return 'network.wired'
diff --git a/src/model/devices/network/wireless.py b/src/model/devices/network/wireless.py
new file mode 100644
index 0000000..c45a08e
--- /dev/null
+++ b/src/model/devices/network/wireless.py
@@ -0,0 +1,96 @@
+#
+# Copyright (C) 2006-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 gobject
+
+from model.devices import device
+from hardware import nmclient
+
+def freq_to_channel(freq):
+ ftoc = { 2.412: 1, 2.417: 2, 2.422: 3, 2.427: 4,
+ 2.432: 5, 2.437: 6, 2.442: 7, 2.447: 8,
+ 2.452: 9, 2.457: 10, 2.462: 11, 2.467: 12,
+ 2.472: 13
+ }
+ return ftoc[freq]
+
+def channel_to_freq(channel):
+ ctof = { 1: 2.412, 2: 2.417, 3: 2.422, 4: 2.427,
+ 5: 2.432, 6: 2.437, 7: 2.442, 8: 2.447,
+ 9: 2.452, 10: 2.457, 11: 2.462, 12: 2.467,
+ 13: 2.472
+ }
+ return ctof[channel]
+
+
+class Device(device.Device):
+ __gproperties__ = {
+ 'name' : (str, None, None, None,
+ gobject.PARAM_READABLE),
+ 'strength' : (int, None, None, 0, 100, 0,
+ gobject.PARAM_READABLE),
+ 'state' : (int, None, None, device.STATE_ACTIVATING,
+ device.STATE_INACTIVE, 0, gobject.PARAM_READABLE),
+ 'frequency': (float, None, None, 0.0, 9999.99, 0.0,
+ gobject.PARAM_READABLE)
+ }
+
+ def __init__(self, nm_device):
+ device.Device.__init__(self)
+ self._nm_device = nm_device
+
+ self._nm_device.connect('strength-changed',
+ self._strength_changed_cb)
+ self._nm_device.connect('ssid-changed',
+ self._ssid_changed_cb)
+ self._nm_device.connect('state-changed',
+ self._state_changed_cb)
+
+ def _strength_changed_cb(self, nm_device):
+ self.notify('strength')
+
+ def _ssid_changed_cb(self, nm_device):
+ self.notify('name')
+
+ def _state_changed_cb(self, nm_device):
+ self.notify('state')
+
+ def do_get_property(self, pspec):
+ if pspec.name == 'strength':
+ return self._nm_device.get_strength()
+ elif pspec.name == 'name':
+ import logging
+ logging.debug('wireless.Device.props.name: %s' % self._nm_device.get_ssid())
+ return self._nm_device.get_ssid()
+ elif pspec.name == 'state':
+ nm_state = self._nm_device.get_state()
+ return device._nm_state_to_state[nm_state]
+ elif pspec.name == 'frequency':
+ return self._nm_device.get_frequency()
+
+ def get_type(self):
+ return 'network.wireless'
+
+ def get_id(self):
+ return str(self._nm_device.get_op())
+
+ def get_active_network_colors(self):
+ net = self._nm_device.get_active_network()
+ if not net:
+ return (None, None)
+ return net.get_colors()
+
diff --git a/src/model/homeactivity.py b/src/model/homeactivity.py
new file mode 100644
index 0000000..7365271
--- /dev/null
+++ b/src/model/homeactivity.py
@@ -0,0 +1,215 @@
+# Copyright (C) 2006-2007 Owen Williams.
+#
+# 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 time
+import logging
+
+import gobject
+import dbus
+
+from sugar.graphics.xocolor import XoColor
+from sugar.presence import presenceservice
+from sugar import profile
+
+_SERVICE_NAME = "org.laptop.Activity"
+_SERVICE_PATH = "/org/laptop/Activity"
+_SERVICE_INTERFACE = "org.laptop.Activity"
+
+class HomeActivity(gobject.GObject):
+ """Activity which appears in the "Home View" of the Sugar shell
+
+ This class stores the Sugar Shell's metadata regarding a
+ given activity/application in the system. It interacts with
+ the sugar.activity.* modules extensively in order to
+ accomplish its tasks.
+ """
+
+ __gtype_name__ = 'SugarHomeActivity'
+
+ __gproperties__ = {
+ 'launching' : (bool, None, None, False,
+ gobject.PARAM_READWRITE),
+ }
+
+ def __init__(self, activity_info, activity_id):
+ """Initialise the HomeActivity
+
+ activity_info -- sugar.activity.registry.ActivityInfo instance,
+ provides the information required to actually
+ create the new instance. This is, in effect,
+ the "type" of activity being created.
+ activity_id -- unique identifier for this instance
+ of the activity type
+ """
+ gobject.GObject.__init__(self)
+
+ self._window = None
+ self._xid = None
+ self._pid = None
+ self._service = None
+ self._activity_id = activity_id
+ self._activity_info = activity_info
+ self._launch_time = time.time()
+ self._launching = False
+
+ self._retrieve_service()
+
+ if not self._service:
+ bus = dbus.SessionBus()
+ bus.add_signal_receiver(self._name_owner_changed_cb,
+ signal_name="NameOwnerChanged",
+ dbus_interface="org.freedesktop.DBus")
+
+ def set_window(self, window):
+ """An activity is 'launched' once we get its window."""
+ if self._window or self._xid:
+ raise RuntimeError("Activity is already launched!")
+ if not window:
+ raise ValueError("window must be valid")
+
+ self._window = window
+ self._xid = window.get_xid()
+ self._pid = window.get_pid()
+
+ def get_service(self):
+ """Get the activity service
+
+ Note that non-native Sugar applications will not have
+ such a service, so the return value will be None in
+ those cases.
+ """
+
+ return self._service
+
+ def get_title(self):
+ """Retrieve the application's root window's suggested title"""
+ if self._window:
+ return self._window.get_name()
+ else:
+ return ''
+
+ def get_icon_path(self):
+ """Retrieve the activity's icon (file) name"""
+ if self._activity_info:
+ return self._activity_info.icon
+ else:
+ return None
+
+ def get_icon_color(self):
+ """Retrieve the appropriate icon colour for this activity
+
+ Uses activity_id to index into the PresenceService's
+ set of activity colours, if the PresenceService does not
+ have an entry (implying that this is not a Sugar-shared application)
+ uses the local user's profile.get_color() to determine the
+ colour for the icon.
+ """
+ pservice = presenceservice.get_instance()
+
+ # HACK to suppress warning in logs when activity isn't found
+ # (if it's locally launched and not shared yet)
+ activity = None
+ for act in pservice.get_activities():
+ if self._activity_id == act.props.id:
+ activity = act
+ break
+
+ if activity != None:
+ return XoColor(activity.props.color)
+ else:
+ return profile.get_color()
+
+ def get_activity_id(self):
+ """Retrieve the "activity_id" passed in to our constructor
+
+ This is a "globally likely unique" identifier generated by
+ sugar.util.unique_id
+ """
+ return self._activity_id
+
+ def get_xid(self):
+ """Retrieve the X-windows ID of our root window"""
+ return self._xid
+
+ def get_window(self):
+ """Retrieve the X-windows root window of this application
+
+ This was stored by the set_window method, which was
+ called by HomeModel._add_activity, which was called
+ via a callback that looks for all 'window-opened'
+ events.
+
+ HomeModel currently uses a dbus service query on the
+ activity to determine to which HomeActivity the newly
+ launched window belongs.
+ """
+ return self._window
+
+ def get_type(self):
+ """Retrieve the activity bundle id for future reference"""
+ if self._activity_info:
+ return self._activity_info.bundle_id
+ else:
+ return None
+
+ def get_launch_time(self):
+ """Return the time at which the activity was first launched
+
+ Format is floating-point time.time() value
+ (seconds since the epoch)
+ """
+ return self._launch_time
+
+ def get_pid(self):
+ """Returns the activity's PID"""
+ return self._pid
+
+ def equals(self, activity):
+ if self._activity_id and activity.get_activity_id():
+ return self._activity_id == activity.get_activity_id()
+ if self._xid and activity.get_xid():
+ return self._xid == activity.get_xid()
+ return False
+
+ def do_set_property(self, pspec, value):
+ if pspec.name == 'launching':
+ self._launching = value
+
+ def do_get_property(self, pspec):
+ if pspec.name == 'launching':
+ return self._launching
+
+ def _get_service_name(self):
+ if self._activity_id:
+ return _SERVICE_NAME + self._activity_id
+ else:
+ return None
+
+ def _retrieve_service(self):
+ if not self._activity_id:
+ return
+
+ try:
+ bus = dbus.SessionBus()
+ proxy = bus.get_object(self._get_service_name(),
+ _SERVICE_PATH + "/" + self._activity_id)
+ self._service = dbus.Interface(proxy, _SERVICE_INTERFACE)
+ except dbus.DBusException:
+ self._service = None
+
+ def _name_owner_changed_cb(self, name, old, new):
+ if name == self._get_service_name():
+ self._retrieve_service()
diff --git a/src/model/homemodel.py b/src/model/homemodel.py
new file mode 100644
index 0000000..44d5417
--- /dev/null
+++ b/src/model/homemodel.py
@@ -0,0 +1,283 @@
+# Copyright (C) 2006-2007 Owen Williams.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import logging
+
+import gobject
+import wnck
+import dbus
+
+from sugar import wm
+from sugar import activity
+
+from model.homeactivity import HomeActivity
+
+class HomeModel(gobject.GObject):
+ """Model of the "Home" view (activity management)
+
+ The HomeModel is basically the point of registration
+ for all running activities within Sugar. It traps
+ events that tell the system there is a new activity
+ being created (generated by the activity factories),
+ or removed, as well as those which tell us that the
+ currently focussed activity has changed.
+
+ The HomeModel tracks a set of HomeActivity instances,
+ which are tracking the window to activity mappings
+ the activity factories have set up.
+ """
+ __gsignals__ = {
+ 'activity-added': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
+ 'activity-started': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
+ 'activity-removed': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
+ 'active-activity-changed': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
+ 'pending-activity-changed': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT]))
+ }
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+
+ self._activities = []
+ self._active_activity = None
+ self._pending_activity = None
+
+ screen = wnck.screen_get_default()
+ screen.connect('window-opened', self._window_opened_cb)
+ screen.connect('window-closed', self._window_closed_cb)
+ screen.connect('active-window-changed',
+ self._active_window_changed_cb)
+
+ def _get_activities_with_window(self):
+ ret = []
+ for i in self._activities:
+ if i.get_window() is not None:
+ ret.append(i)
+ return ret
+
+ def get_previous_activity(self):
+ activities = self._get_activities_with_window()
+ i = activities.index(self._pending_activity)
+ if len(activities) == 0:
+ return None
+ elif i - 1 >= 0:
+ return activities[i - 1]
+ else:
+ return activities[len(activities) - 1]
+
+ def get_next_activity(self):
+ activities = self._get_activities_with_window()
+ i = activities.index(self._pending_activity)
+ if len(activities) == 0:
+ return None
+ elif i + 1 < len(activities):
+ return activities[i + 1]
+ else:
+ return activities[0]
+
+ def get_pending_activity(self):
+ """Returns the activity that would be seen in the Activity zoom level
+
+ In the Home (or Neighborhood or Groups) zoom level, this
+ indicates the activity that would become active if the user
+ switched to the Activity zoom level. (In the Activity zoom
+ level, this just returns the currently-active activity.)
+ Unlike get_active_activity(), this never returns None as long
+ as there is any activity running.
+ """
+ return self._pending_activity
+
+ def _set_pending_activity(self, home_activity):
+ if self._pending_activity == home_activity:
+ return
+
+ self._pending_activity = home_activity
+ self.emit('pending-activity-changed', self._pending_activity)
+
+ def get_active_activity(self):
+ """Returns the activity that the user is currently working in
+
+ In the Activity zoom level, this returns the currently-active
+ activity. In the other zoom levels, it returns the activity
+ that was most-recently active in the Activity zoom level, or
+ None if the most-recently-active activity is no longer
+ running.
+ """
+ return self._active_activity
+
+ def _set_active_activity(self, home_activity):
+ if self._active_activity == home_activity:
+ return
+
+ if self._active_activity:
+ service = self._active_activity.get_service()
+ if service:
+ service.SetActive(False,
+ reply_handler=self._set_active_success,
+ error_handler=self._set_active_error)
+ if home_activity:
+ service = home_activity.get_service()
+ if service:
+ service.SetActive(True,
+ reply_handler=self._set_active_success,
+ error_handler=self._set_active_error)
+
+ self._active_activity = home_activity
+ self.emit('active-activity-changed', self._active_activity)
+
+ def __iter__(self):
+ return iter(self._activities)
+
+ def __len__(self):
+ return len(self._activities)
+
+ def __getitem__(self, i):
+ return self._activities[i]
+
+ def index(self, obj):
+ return self._activities.index(obj)
+
+ def _window_opened_cb(self, screen, window):
+ if window.get_window_type() == wnck.WINDOW_NORMAL:
+ home_activity = None
+
+ activity_id = wm.get_activity_id(window)
+
+ service_name = wm.get_bundle_id(window)
+ if service_name:
+ registry = activity.get_registry()
+ activity_info = registry.get_activity(service_name)
+ else:
+ activity_info = None
+
+ if activity_id:
+ home_activity = self._get_activity_by_id(activity_id)
+
+ if not home_activity:
+ home_activity = HomeActivity(activity_info, activity_id)
+ self._add_activity(home_activity)
+
+ home_activity.set_window(window)
+
+ home_activity.props.launching = False
+ self.emit('activity-started', home_activity)
+
+ if self._pending_activity is None:
+ self._set_pending_activity(home_activity)
+
+ def _window_closed_cb(self, screen, window):
+ if window.get_window_type() == wnck.WINDOW_NORMAL:
+ self._remove_activity_by_xid(window.get_xid())
+
+ def _get_activity_by_xid(self, xid):
+ for home_activity in self._activities:
+ if home_activity.get_xid() == xid:
+ return home_activity
+ return None
+
+ def _get_activity_by_id(self, activity_id):
+ for home_activity in self._activities:
+ if home_activity.get_activity_id() == activity_id:
+ return home_activity
+ return None
+
+ def _set_active_success(self):
+ pass
+
+ def _set_active_error(self, err):
+ logging.error("set_active() failed: %s" % err)
+
+ def _active_window_changed_cb(self, screen, previous_window=None):
+ window = screen.get_active_window()
+ if window is None:
+ return
+
+ if window.get_window_type() != wnck.WINDOW_DIALOG:
+ while window.get_transient() is not None:
+ window = window.get_transient()
+
+ activity = self._get_activity_by_xid(window.get_xid())
+ if activity is not None:
+ self._set_pending_activity(activity)
+ self._set_active_activity(activity)
+
+ def _add_activity(self, home_activity):
+ self._activities.append(home_activity)
+ self.emit('activity-added', home_activity)
+
+ def _remove_activity(self, home_activity):
+ if home_activity == self._active_activity:
+ self._set_active_activity(None)
+
+ if home_activity == self._pending_activity:
+ # Figure out the new _pending_activity
+ windows = wnck.screen_get_default().get_windows_stacked()
+ windows.reverse()
+ for window in windows:
+ new_activity = self._get_activity_by_xid(window.get_xid())
+ if new_activity is not None:
+ self._set_pending_activity(new_activity)
+ break
+ else:
+ logging.error('No activities are running')
+ self._set_pending_activity(None)
+
+ self.emit('activity-removed', home_activity)
+ self._activities.remove(home_activity)
+
+ def _remove_activity_by_xid(self, xid):
+ home_activity = self._get_activity_by_xid(xid)
+ if home_activity:
+ self._remove_activity(home_activity)
+ else:
+ logging.error('Model for window %d does not exist.' % xid)
+
+ def notify_activity_launch(self, activity_id, service_name):
+ registry = activity.get_registry()
+ activity_info = registry.get_activity(service_name)
+ if not activity_info:
+ raise ValueError("Activity service name '%s' was not found in the bundle registry." % service_name)
+ home_activity = HomeActivity(activity_info, activity_id)
+ home_activity.props.launching = True
+ self._add_activity(home_activity)
+
+ # FIXME: better learn about finishing processes by receiving a signal.
+ # Now just check whether an activity has a window after ~90sec
+ gobject.timeout_add(90000, self._check_activity_launched, activity_id)
+
+ def notify_activity_launch_failed(self, activity_id):
+ home_activity = self._get_activity_by_id(activity_id)
+ if home_activity:
+ logging.debug("Activity %s (%s) launch failed" % (activity_id, home_activity.get_type()))
+ self._remove_activity(home_activity)
+ else:
+ logging.error('Model for activity id %s does not exist.' % activity_id)
+
+ def _check_activity_launched(self, activity_id):
+ home_activity = self._get_activity_by_id(activity_id)
+ if home_activity and home_activity.props.launching:
+ logging.debug('Activity %s still launching, assuming it failed...', activity_id)
+ self.notify_activity_launch_failed(activity_id)
+ return False
diff --git a/src/model/shellmodel.py b/src/model/shellmodel.py
new file mode 100644
index 0000000..5462e27
--- /dev/null
+++ b/src/model/shellmodel.py
@@ -0,0 +1,112 @@
+# Copyright (C) 2006-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 os
+
+import wnck
+import gobject
+
+from sugar.presence import presenceservice
+from model.Friends import Friends
+from model.MeshModel import MeshModel
+from model.homemodel import HomeModel
+from model.Owner import ShellOwner
+from model.devices.devicesmodel import DevicesModel
+from sugar import env
+
+class ShellModel(gobject.GObject):
+ STATE_STARTUP = 0
+ STATE_RUNNING = 1
+ STATE_SHUTDOWN = 2
+
+ ZOOM_MESH = 0
+ ZOOM_FRIENDS = 1
+ ZOOM_HOME = 2
+ ZOOM_ACTIVITY = 3
+
+ __gproperties__ = {
+ 'state' : (int, None, None,
+ 0, 2, STATE_RUNNING,
+ gobject.PARAM_READWRITE),
+ 'zoom-level' : (int, None, None,
+ 0, 3, ZOOM_HOME,
+ gobject.PARAM_READABLE)
+ }
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+
+ self._current_activity = None
+ self._state = self.STATE_RUNNING
+ self._zoom_level = self.ZOOM_HOME
+ self._showing_desktop = True
+
+ self._pservice = presenceservice.get_instance()
+
+ self._owner = ShellOwner()
+
+ self._friends = Friends()
+ self._mesh = MeshModel()
+ self._home = HomeModel()
+ self._devices = DevicesModel()
+
+ self._screen = wnck.screen_get_default()
+ self._screen.connect('showing-desktop-changed',
+ self._showing_desktop_changed_cb)
+
+ def set_zoom_level(self, level):
+ self._zoom_level = level
+ self.notify('zoom-level')
+
+ def get_zoom_level(self):
+ if self._screen.get_showing_desktop():
+ return self._zoom_level
+ else:
+ return self.ZOOM_ACTIVITY
+
+ def do_set_property(self, pspec, value):
+ if pspec.name == 'state':
+ self._state = value
+
+ def do_get_property(self, pspec):
+ if pspec.name == 'state':
+ return self._state
+ elif pspec.name == 'zoom-level':
+ return self.get_zoom_level()
+
+ def get_mesh(self):
+ return self._mesh
+
+ def get_friends(self):
+ return self._friends
+
+ def get_invites(self):
+ return self._owner.get_invites()
+
+ def get_home(self):
+ return self._home
+
+ def get_owner(self):
+ return self._owner
+
+ def get_devices(self):
+ return self._devices
+
+ def _showing_desktop_changed_cb(self, screen):
+ showing_desktop = self._screen.get_showing_desktop()
+ if self._showing_desktop != showing_desktop:
+ self._showing_desktop = showing_desktop
+ self.notify('zoom-level')