diff --git a/configure.ac b/configure.ac
index 0828bda5..e31440d0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -22,5 +22,6 @@ sugar/__installed__.py
sugar/browser/Makefile
sugar/chat/Makefile
sugar/p2p/Makefile
+sugar/p2p/model/Makefile
sugar/shell/Makefile
])
diff --git a/pylint.sh b/pylint.sh
new file mode 100755
index 00000000..4de5a507
--- /dev/null
+++ b/pylint.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# Errors that we may fix in the future
+#
+# C0301 Line length
+# W0201 Attribute defined outside __init__
+# R0201 Method could be a function
+TODO="C0301,W0201,R0201"
+
+# Errors we don't like
+#
+# W0613 Unused argument (glib signals)
+# W0511 - FIXME/TODO/XXX
+DISABLE="W0613,W0511"
+
+MSGS="$TODO,$DISABLE"
+ARG="sugar"
+
+pylint \
+ --disable-all \
+ --include-ids=y \
+ --enable-variables=y \
+ --enable-exceptions=y \
+ --enable-miscellaneous=y \
+ --enable-format=y \
+ --enable-classes=y \
+ --disable-msg=$MSGS \
+ --reports=n \
+ --enable-metrics=n \
+ --indent-string=" " \
+ --ignore="SVGdraw.py" \
+ $ARG
diff --git a/sugar/browser/browser.py b/sugar/browser/browser.py
index 633fa044..fc5ce0a1 100755
--- a/sugar/browser/browser.py
+++ b/sugar/browser/browser.py
@@ -11,6 +11,7 @@ import gtk
import geckoembed
from sugar.shell import activity
+from sugar.p2p.Group import LocalGroup
import sugar.env
class AddressToolbar(gtk.Toolbar):
@@ -77,7 +78,7 @@ class AddressEntry(gtk.HBox):
return self.folded
def set_folded(self, folded):
- self.folded = not self.folded
+ self.folded = folded
self._update_folded_state()
def __button_clicked_cb(self, button):
@@ -88,11 +89,12 @@ class AddressEntry(gtk.HBox):
self.set_folded(True)
class NavigationToolbar(gtk.Toolbar):
- def __init__(self, embed):
+ def __init__(self, browser):
gtk.Toolbar.__init__(self)
- self.embed = embed
+ self._browser = browser
+ self._embed = self._browser.get_embed()
- self.set_style(gtk.TOOLBAR_ICONS)
+ self.set_style(gtk.TOOLBAR_BOTH_HORIZ)
self.back = gtk.ToolButton(gtk.STOCK_GO_BACK)
self.back.connect("clicked", self.__go_back_cb)
@@ -113,7 +115,9 @@ class NavigationToolbar(gtk.Toolbar):
self.insert(separator, -1)
separator.show()
- share = gtk.ToolButton("Share")
+ share = gtk.ToolButton(None, "Share")
+ share.set_icon_name('stock_shared-by-me')
+ share.set_is_important(True)
share.connect("clicked", self.__share_cb)
self.insert(share, -1)
share.show()
@@ -128,34 +132,42 @@ class NavigationToolbar(gtk.Toolbar):
self._update_sensitivity()
- self.embed.connect("location", self.__location_changed)
+ self._embed.connect("location", self.__location_changed)
def _update_sensitivity(self):
- self.back.set_sensitive(self.embed.can_go_back())
- self.forward.set_sensitive(self.embed.can_go_forward())
+ self.back.set_sensitive(self._embed.can_go_back())
+ self.forward.set_sensitive(self._embed.can_go_forward())
def __go_back_cb(self, button):
- self.embed.go_back()
+ self._embed.go_back()
def __go_forward_cb(self, button):
- self.embed.go_forward()
+ self._embed.go_forward()
def __reload_cb(self, button):
- self.embed.reload()
+ self._embed.reload()
def __share_cb(self, button):
- pass
+ self._browser.share()
def __location_changed(self, embed):
self._update_sensitivity()
def __open_address_cb(self, address):
- self.embed.load_address(address)
+ self._embed.load_address(address)
class BrowserActivity(activity.Activity):
- def __init__(self, uri):
+ def __init__(self, group, uri):
activity.Activity.__init__(self)
+
self.uri = uri
+ self._group = group
+
+ def _setup_shared(self, uri):
+ self._model = self._group.get_store().get_model(uri)
+ if self._model:
+ self._load_shared_address()
+ self._model.add_listener(self.__shared_address_changed_cb)
def activity_on_connected_to_shell(self):
self.activity_set_ellipsize_tab(True)
@@ -173,7 +185,7 @@ class BrowserActivity(activity.Activity):
self.embed.show()
self.embed.load_address(self.uri)
- nav_toolbar = NavigationToolbar(self.embed)
+ nav_toolbar = NavigationToolbar(self)
vbox.pack_start(nav_toolbar, False)
nav_toolbar.show()
@@ -182,12 +194,36 @@ class BrowserActivity(activity.Activity):
plug.show()
vbox.show()
+
+ self._setup_shared(uri)
def get_embed(self):
return self.embed
+ def share(self):
+ address = self.embed.get_address()
+ self._model = self._group.get_store().create_model(address)
+ self._model.set_value('current_address', address)
+ self._model.add_listener(self.__shared_address_changed_cb)
+
+ bus = dbus.SessionBus()
+ proxy_obj = bus.get_object('com.redhat.Sugar.Chat', '/com/redhat/Sugar/Chat')
+ chat_shell = dbus.Interface(proxy_obj, 'com.redhat.Sugar.ChatShell')
+ chat_shell.send_message('' +
+ self.embed.get_title() + '')
+
def __title_cb(self, embed):
self.activity_set_tab_text(embed.get_title())
+ # Temporary hack, we need an UI
+ self._model.set_value('current_address', self.embed.get_address())
+
+ def _load_shared_address(self):
+ address = self._model.get_value("current_address")
+ if address != self.embed.get_address():
+ self.embed.load_address(address)
+
+ def __shared_address_changed_cb(self, model, key):
+ self._load_shared_address()
def activity_on_close_from_user(self):
self.activity_shutdown()
@@ -229,7 +265,6 @@ class WebActivity(activity.Activity):
def activity_on_disconnected_from_shell(self):
gtk.main_quit()
- gc.collect()
class BrowserShell(dbus.service.Object):
instance = None
@@ -249,6 +284,7 @@ class BrowserShell(dbus.service.Object):
dbus.service.Object.__init__(self, bus_name, object_path)
self.__browsers = []
+ self._group = LocalGroup()
def open_web_activity(self):
web_activity = WebActivity()
@@ -267,17 +303,13 @@ class BrowserShell(dbus.service.Object):
@dbus.service.method('com.redhat.Sugar.BrowserShell')
def open_browser(self, uri):
- browser = BrowserActivity(uri)
+ browser = BrowserActivity(self._group, uri)
self.__browsers.append(browser)
browser.activity_connect_to_shell()
def main():
BrowserShell.get_instance().open_web_activity()
-
- try:
- gtk.main()
- except KeyboardInterrupt:
- pass
+ gtk.main()
-if __name__=="__main__":
- main()
+if __name__ == "__main__":
+ main()
diff --git a/sugar/chat/BuddyList.py b/sugar/chat/BuddyList.py
deleted file mode 100644
index d35fa847..00000000
--- a/sugar/chat/BuddyList.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# -*- tab-width: 4; indent-tabs-mode: t -*-
-
-import presence
-import avahi
-
-ACTION_BUDDY_ADDED = "added"
-ACTION_BUDDY_REMOVED = "removed"
-
-
-class Buddy(object):
- def __init__(self, nick, realname, servicename, host, address, port, key=None):
- self._nick = nick
- self._realname = realname
- self._servicename = servicename
- self._key = key
- self._host = host
- self._address = str(address)
- self._port = int(port)
- self._chat = None
-
- def set_chat(self, chat):
- self._chat = chat
-
- def chat(self):
- return self._chat
-
- def nick(self):
- return self._nick
-
- def realname(self):
- return self._realname
-
- def servicename(self):
- return self._servicename
-
- def host(self):
- return self._host
-
- def address(self):
- return self._address
-
- def port(self):
- return self._port
-
- def key(self):
- return self._key
-
-class BuddyList(object):
- """ Manage a list of buddies """
-
- def __init__(self, servicename):
- self._listeners = []
- self._buddies = {}
- self._servicename = servicename
- self._pdiscovery = presence.PresenceDiscovery()
- self._pdiscovery.add_service_listener(self._on_service_change)
-
- def start(self):
- self._pdiscovery.start()
-
- def add_buddy_listener(self, listener):
- self._listeners.append(listener)
-
- def _add_buddy(self, host, address, port, servicename, data):
- # Ignore ourselves
- if servicename == self._servicename:
- return
-
- if len(data) > 0 and 'name' in data.keys():
- buddy = self._find_buddy_by_service_name(servicename)
- if not buddy:
- buddy = Buddy(data['name'], data['realname'], servicename, host, address, port)
- self._buddies[data['name']] = buddy
- self._notify_listeners(ACTION_BUDDY_ADDED, buddy)
-
- def _remove_buddy(self, buddy):
- nick = buddy.nick()
- self._notify_listeners(ACTION_BUDDY_REMOVED, buddy)
- del self._buddies[nick]
-
- def _find_buddy_by_service_name(self, servicename):
- for buddy in self._buddies.values():
- if buddy.servicename() == servicename:
- return buddy
- return None
-
- def find_buddy_by_address(self, address):
- for buddy_name in self._buddies.keys():
- buddy = self._buddies[buddy_name]
- if buddy.address() == address:
- return buddy
- return None
-
- def _notify_listeners(self, action, buddy):
- for listener in self._listeners:
- listener(action, buddy)
-
- def _on_service_change(self, action, interface, protocol, name, stype, domain, flags):
- if stype != presence.OLPC_CHAT_SERVICE:
- return
- if action == presence.ACTION_SERVICE_NEW:
- self._pdiscovery.resolve_service(interface, protocol, name, stype, domain, self._on_service_resolved)
- elif action == presence.ACTION_SERVICE_REMOVED:
- buddy = self._find_buddy_by_service_name(name)
- if buddy:
- self._remove_buddy(buddy)
-
- def _pair_to_dict(self, l):
- res = {}
- for el in l:
- tmp = el.split('=', 1)
- if len(tmp) > 1:
- res[tmp[0]] = tmp[1]
- else:
- res[tmp[0]] = ''
- return res
-
- def _on_service_resolved(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags):
- data = self._pair_to_dict(avahi.txt_array_to_string_array(txt))
- self._add_buddy(host, address, port, name, data)
-
diff --git a/sugar/chat/chat.py b/sugar/chat/chat.py
index 2bc72305..fef2c838 100755
--- a/sugar/chat/chat.py
+++ b/sugar/chat/chat.py
@@ -10,9 +10,11 @@ pygtk.require('2.0')
import gtk, gobject
from sugar.shell import activity
-from sugar.p2p.Group import *
-from sugar.p2p.StreamReader import *
-from sugar.p2p.StreamWriter import *
+from sugar.p2p.Group import Group
+from sugar.p2p.Group import LocalGroup
+from sugar.p2p.Service import Service
+from sugar.p2p.StreamReader import StreamReader
+from sugar.p2p.StreamWriter import StreamWriter
import sugar.env
import richtext
@@ -71,14 +73,14 @@ class Chat(activity.Activity):
self._hbox = gtk.HBox(False, 12)
self._hbox.set_border_width(12)
- [chat_vbox, buffer] = self._create_chat()
+ [chat_vbox, buf] = self._create_chat()
self._hbox.pack_start(chat_vbox)
chat_vbox.show()
vbox.pack_start(self._hbox)
self._hbox.show()
- toolbar = self._create_toolbar(buffer)
+ toolbar = self._create_toolbar(buf)
vbox.pack_start(toolbar, False)
toolbar.show()
@@ -130,37 +132,37 @@ class Chat(activity.Activity):
button.set_menu(menu)
def activity_on_close_from_user(self):
- print "act %d: in activity_on_close_from_user"%self.activity_get_id()
+ print "act %d: in activity_on_close_from_user" % self.activity_get_id()
self.activity_shutdown()
def activity_on_lost_focus(self):
- print "act %d: in activity_on_lost_focus"%self.activity_get_id()
+ print "act %d: in activity_on_lost_focus" % self.activity_get_id()
def activity_on_got_focus(self):
- print "act %d: in activity_on_got_focus"%self.activity_get_id()
- self._controller.notify_activate(self)
+ print "act %d: in activity_on_got_focus" % self.activity_get_id()
+ # FIXME self._controller.notify_activate(self)
def recv_message(self, buddy, msg):
self._insert_rich_message(buddy.get_nick_name(), msg)
self._controller.notify_new_message(self, buddy)
def _insert_rich_message(self, nick, msg):
- buffer = self._chat_view.get_buffer()
- aniter = buffer.get_end_iter()
- buffer.insert(aniter, nick + ": ")
+ buf = self._chat_view.get_buffer()
+ aniter = buf.get_end_iter()
+ buf.insert(aniter, nick + ": ")
serializer = richtext.RichTextSerializer()
- serializer.deserialize(msg, buffer)
+ serializer.deserialize(msg, buf)
- aniter = buffer.get_end_iter()
- buffer.insert(aniter, "\n")
+ aniter = buf.get_end_iter()
+ buf.insert(aniter, "\n")
def _local_message(self, success, text):
if not success:
message = "Error: %s\n" % text
- buffer = self._chat_view.get_buffer()
- aniter = buffer.get_end_iter()
- buffer.insert(aniter, message)
+ buf = self._chat_view.get_buffer()
+ aniter = buf.get_end_iter()
+ buf.insert(aniter, message)
else:
owner = self._controller.get_group().get_owner()
self._insert_rich_message(owner.get_nick_name(), text)
@@ -325,11 +327,11 @@ class GroupChat(Chat):
if buddy.get_nick_name() == self._group.get_owner().get_nick_name():
# Do not show ourself in the buddy list
pass
- elif action == BUDDY_JOIN:
+ elif action == Group.BUDDY_JOIN:
aniter = self._buddy_list_model.append(None)
self._buddy_list_model.set(aniter, self._MODEL_COL_NICK, buddy.get_nick_name(),
self._MODEL_COL_ICON, None, self._MODEL_COL_BUDDY, buddy)
- elif action == BUDDY_LEAVE:
+ elif action == Group.BUDDY_LEAVE:
aniter = self._get_iter_for_buddy(buddy)
if aniter:
self._buddy_list_model.remove(aniter)
@@ -346,7 +348,7 @@ class GroupChat(Chat):
aniter = self._get_iter_for_buddy(buddy)
self._buddy_list_model.set(aniter, self._MODEL_COL_ICON, self._pixbuf_new_message)
- def notify_activate(self, chat):
+ def notify_activate(self, chat, buddy):
aniter = self._get_iter_for_buddy(buddy)
self._buddy_list_model.set(aniter, self._MODEL_COL_ICON, self._pixbuf_active_chat)
@@ -387,19 +389,16 @@ class ChatShell(dbus.service.Object):
dbus.service.Object.__init__(self, bus_name, object_path)
def open_group_chat(self):
- group_chat = GroupChat()
- group_chat.activity_connect_to_shell()
+ self._group_chat = GroupChat()
+ self._group_chat.activity_connect_to_shell()
@dbus.service.method('com.redhat.Sugar.ChatShell')
def send_message(self, message):
- pass
+ self._group_chat.send_message(message)
def main():
ChatShell.get_instance().open_group_chat()
- try:
- gtk.main()
- except KeyboardInterrupt:
- pass
+ gtk.main()
if __name__ == "__main__":
main()
diff --git a/sugar/chat/richtext.py b/sugar/chat/richtext.py
index 0ac70b16..ecb15c2e 100644
--- a/sugar/chat/richtext.py
+++ b/sugar/chat/richtext.py
@@ -45,7 +45,7 @@ class RichTextView(gtk.TextView):
def __motion_notify_cb(self, widget, event):
if event.is_hint:
- [x, y, state] = event.window.get_pointer();
+ event.window.get_pointer();
it = self.__get_event_iter(event)
if it:
@@ -134,9 +134,9 @@ class RichTextBuffer(gtk.TextBuffer):
def __insert_text_cb(self, widget, pos, text, length):
for tag in self.active_tags:
- pos_end = pos.copy()
- pos_end.backward_chars(length)
- self.apply_tag_by_name(tag, pos, pos_end)
+ pos_end = pos.copy()
+ pos_end.backward_chars(length)
+ self.apply_tag_by_name(tag, pos, pos_end)
class RichTextToolbar(gtk.Toolbar):
def __init__(self, buf):
@@ -205,6 +205,7 @@ class RichTextToolbar(gtk.Toolbar):
class RichTextHandler(xml.sax.handler.ContentHandler):
def __init__(self, serializer, buf):
+ xml.sax.handler.ContentHandler.__init__(self)
self.buf = buf
self.serializer = serializer
self.tags = []
@@ -286,7 +287,7 @@ class RichTextSerializer:
def serialize(self, buf):
self.buf = buf
- xml = ""
+ res = ""
next_it = buf.get_start_iter()
while not next_it.is_end():
@@ -299,29 +300,29 @@ class RichTextSerializer:
for tag in it.get_toggled_tags(False):
while 1:
open_tag = self._open_tags.pop()
- xml += self.serialize_tag_end(tag)
+ res += self.serialize_tag_end(tag)
if open_tag == tag:
break
tags_to_reopen.append(open_tag)
for tag in tags_to_reopen:
self._open_tags.append(tag)
- xml += self.serialize_tag_start(tag, it)
+ res += self.serialize_tag_start(tag, it)
for tag in it.get_toggled_tags(True):
self._open_tags.append(tag)
- xml += self.serialize_tag_start(tag, it)
+ res += self.serialize_tag_start(tag, it)
- xml += buf.get_text(it, next_it, False)
+ res += buf.get_text(it, next_it, False)
if next_it.is_end():
self._open_tags.reverse()
for tag in self._open_tags:
- xml += self.serialize_tag_end(tag)
+ res += self.serialize_tag_end(tag)
- xml += ""
+ res += ""
- return xml
+ return res
def deserialize(self, xml_string, buf):
parser = xml.sax.make_parser()
@@ -330,11 +331,11 @@ class RichTextSerializer:
parser.feed(xml_string)
parser.close()
-def test_quit(window, rich_buf):
- print RichTextSerializer().serialize(rich_buf)
+def test_quit(w, rb):
+ print RichTextSerializer().serialize(rb)
gtk.main_quit()
-def link_clicked(view, address):
+def link_clicked(v, address):
print "Link clicked " + address
if __name__ == "__main__":
@@ -350,15 +351,15 @@ if __name__ == "__main__":
rich_buf = view.get_buffer()
- xml_string = ""
+ test_xml = ""
- xml_string += "Testone\n"
- xml_string += "Test two"
- xml_string += "Test three"
- xml_string += "Test link"
- xml_string += ""
+ test_xml += "Testone\n"
+ test_xml += "Test two"
+ test_xml += "Test three"
+ test_xml += "Test link"
+ test_xml += ""
- RichTextSerializer().deserialize(xml_string, rich_buf)
+ RichTextSerializer().deserialize(test_xml, rich_buf)
toolbar = RichTextToolbar(rich_buf)
vbox.pack_start(toolbar, False)
diff --git a/sugar/env.py b/sugar/env.py
index e30daa2d..3b812d32 100644
--- a/sugar/env.py
+++ b/sugar/env.py
@@ -1,9 +1,7 @@
-import os
-
try:
- from sugar.__uninstalled__ import *
+ from sugar.__uninstalled__ import internal_get_data_file
except ImportError:
- from sugar.__installed__ import *
+ from sugar.__installed__ import internal_get_data_file
def get_data_file(filename):
return internal_get_data_file(filename)
diff --git a/sugar/p2p/Buddy.py b/sugar/p2p/Buddy.py
index 19d7c0ef..8263a8e3 100644
--- a/sugar/p2p/Buddy.py
+++ b/sugar/p2p/Buddy.py
@@ -1,7 +1,7 @@
import pwd
import os
-from Service import *
+from Service import Service
PRESENCE_SERVICE_TYPE = "_olpc_presence._tcp"
PRESENCE_SERVICE_PORT = 6000
diff --git a/sugar/p2p/Group.py b/sugar/p2p/Group.py
index dedbc1ed..faa6041c 100644
--- a/sugar/p2p/Group.py
+++ b/sugar/p2p/Group.py
@@ -1,21 +1,26 @@
-import avahi
-
+from Buddy import Buddy
+from Buddy import Owner
+from Buddy import PRESENCE_SERVICE_TYPE
+from Service import Service
+from sugar.p2p.model.Store import Store
import presence
-from Buddy import *
-from Service import *
-
-SERVICE_ADDED = "service_added"
-SERVICE_REMOVED = "service_removed"
-
-BUDDY_JOIN = "buddy_join"
-BUDDY_LEAVE = "buddy_leave"
class Group:
+ SERVICE_ADDED = "service_added"
+ SERVICE_REMOVED = "service_removed"
+
+ BUDDY_JOIN = "buddy_join"
+ BUDDY_LEAVE = "buddy_leave"
+
def __init__(self):
self._service_listeners = []
self._presence_listeners = []
+ self._store = Store(self)
- def join(self, buddy):
+ def get_store(self):
+ return self._store
+
+ def join(self):
pass
def add_service_listener(self, listener):
@@ -26,19 +31,19 @@ class Group:
def _notify_service_added(self, service):
for listener in self._service_listeners:
- listener(SERVICE_ADDED, buddy)
+ listener(Group.SERVICE_ADDED, service)
- def _notify_service_removed(self, service):
+ def _notify_service_removed(self, service_id):
for listener in self._service_listeners:
- listener(SERVICE_REMOVED,buddy)
+ listener(Group.SERVICE_REMOVED, service_id)
def _notify_buddy_join(self, buddy):
for listener in self._presence_listeners:
- listener(BUDDY_JOIN, buddy)
+ listener(Group.BUDDY_JOIN, buddy)
def _notify_buddy_leave(self, buddy):
for listener in self._presence_listeners:
- listener(BUDDY_LEAVE, buddy)
+ listener(Group.BUDDY_LEAVE, buddy)
class LocalGroup(Group):
def __init__(self):
@@ -59,16 +64,19 @@ class LocalGroup(Group):
self._services[sid] = service
self._notify_service_added(service)
- def remove_service(self, sid):
- self._notify_service_removed(service)
- del self._services[sid]
+ def remove_service(self, service_id):
+ self._notify_service_removed(service_id)
+ del self._services[service_id]
def join(self):
self._owner = Owner(self)
self._owner.register()
def get_service(self, name, stype):
- return self._services[(name, stype)]
+ if self._services.has_key((name, stype)):
+ return self._services[(name, stype)]
+ else:
+ return None
def get_buddy(self, name):
return self._buddies[name]
@@ -95,8 +103,8 @@ class LocalGroup(Group):
def _on_service_resolved(self, interface, protocol, name, stype, domain,
host, aprotocol, address, port, txt, flags):
- service = Service(name, stype, address, port)
- if stype == PRESENCE_SERVICE_TYPE:
- self._add_buddy(Buddy(service, name))
- elif stype.startswith("_olpc"):
- self.add_service(service)
+ service = Service(name, stype, address, port)
+ if stype == PRESENCE_SERVICE_TYPE:
+ self._add_buddy(Buddy(service, name))
+ elif stype.startswith("_olpc"):
+ self.add_service(service)
diff --git a/sugar/p2p/NotificationListener.py b/sugar/p2p/NotificationListener.py
new file mode 100644
index 00000000..e490f390
--- /dev/null
+++ b/sugar/p2p/NotificationListener.py
@@ -0,0 +1,27 @@
+from Service import Service
+import network
+
+class NotificationListener:
+ TYPE = "_olpc_model_notification._udp"
+ ADDRESS = "224.0.0.222"
+ PORT = 6300
+
+ def __init__(self, group, name):
+ server = network.GroupServer(NotificationListener.TYPE,
+ NotificationListener.PORT,
+ self._recv_multicast)
+ server.start()
+
+ service = Service(name, NotificationListener.TYPE,
+ NotificationListener.ADDRESS,
+ NotificationListener.PORT, True)
+ service.register(group)
+
+ self._listeners = {}
+
+ def add_listener(self, listener):
+ self._listeners.add(listener)
+
+ def _recv_multicast(self, msg):
+ for listener in self._listeners:
+ listener(msg)
diff --git a/sugar/p2p/Notifier.py b/sugar/p2p/Notifier.py
new file mode 100644
index 00000000..017183c9
--- /dev/null
+++ b/sugar/p2p/Notifier.py
@@ -0,0 +1,11 @@
+import network
+
+class Notifier:
+ def __init__(self, group, name):
+ service = group.get_service(name)
+ address = service.get_address()
+ port = service.get_port()
+ self._client = network.GroupClient(address, port)
+
+ def notify(self, msg):
+ self._client.send_msg(msg)
diff --git a/sugar/p2p/StreamReader.py b/sugar/p2p/StreamReader.py
index c108547b..d3dbf83e 100644
--- a/sugar/p2p/StreamReader.py
+++ b/sugar/p2p/StreamReader.py
@@ -1,3 +1,5 @@
+import socket
+
import network
class StreamReaderRequestHandler(object):
@@ -5,7 +7,6 @@ class StreamReaderRequestHandler(object):
self._reader = reader
def message(self, nick_name, message):
- address = network.get_authinfo()
self._reader.recv(nick_name, message)
return True
@@ -37,7 +38,7 @@ class StreamReader:
p2p_server = network.GlibXMLRPCServer(("", port))
p2p_server.register_instance(StreamReaderRequestHandler(self))
started = True
- except:
+ except(socket.error):
port = port + 1
tries = tries - 1
self._service.set_port(port)
diff --git a/sugar/p2p/StreamWriter.py b/sugar/p2p/StreamWriter.py
index f30801ea..6007a934 100644
--- a/sugar/p2p/StreamWriter.py
+++ b/sugar/p2p/StreamWriter.py
@@ -31,7 +31,7 @@ class StreamWriter:
nick_name = self._group.get_owner().get_nick_name()
self._uclient.message(nick_name, data)
return True
- except (socket.error, xmlrpclib.Fault, xmlrpclib.ProtocolError), e:
+ except (socket.error, xmlrpclib.Fault, xmlrpclib.ProtocolError):
traceback.print_exc()
return False
diff --git a/sugar/p2p/model/AbstractModel.py b/sugar/p2p/model/AbstractModel.py
new file mode 100644
index 00000000..a5fbe493
--- /dev/null
+++ b/sugar/p2p/model/AbstractModel.py
@@ -0,0 +1,10 @@
+class AbstractModel:
+ def __init__(self):
+ self._listeners = []
+
+ def add_listener(self, listener):
+ self._listeners.append(listener)
+
+ def _notify_model_change(self, key):
+ for listener in self._listeners:
+ listener(self, key)
diff --git a/sugar/p2p/model/LocalModel.py b/sugar/p2p/model/LocalModel.py
new file mode 100644
index 00000000..89171924
--- /dev/null
+++ b/sugar/p2p/model/LocalModel.py
@@ -0,0 +1,55 @@
+import socket
+
+from sugar.p2p.Service import Service
+from sugar.p2p.model.AbstractModel import AbstractModel
+from sugar.p2p import network
+
+class ModelRequestHandler(object):
+ def __init__(self, model):
+ self._model = model
+
+ def get_value(self, key):
+ return self._model.get_value(key)
+
+ def set_value(self, key, value):
+ return self._model.set_value(key, value)
+
+class LocalModel(AbstractModel):
+ SERVICE_TYPE = "_olpc_model._tcp"
+ SERVICE_PORT = 6300
+
+ def __init__(self, group, model_id):
+ AbstractModel.__init__(self)
+ self._group = group
+ self._model_id = model_id
+ self._values = {}
+
+ self._setup_service()
+
+ def get_value(self, key):
+ return self._values[key]
+
+ def set_value(self, key, value):
+ self._values[key] = value
+ self._notify_model_change(key)
+
+ def _setup_service(self):
+ service = Service(self._model_id, LocalModel.SERVICE_TYPE, '',
+ LocalModel.SERVICE_PORT)
+ self._setup_server(service)
+ service.register(self._group)
+
+ # FIXME this is duplicated with StreamReader
+ def _setup_server(self, service):
+ started = False
+ tries = 10
+ port = service.get_port()
+ while not started and tries > 0:
+ try:
+ p2p_server = network.GlibXMLRPCServer(("", port))
+ p2p_server.register_instance(ModelRequestHandler(self))
+ started = True
+ except(socket.error):
+ port = port + 1
+ tries = tries - 1
+ service.set_port(port)
diff --git a/sugar/p2p/model/Makefile.am b/sugar/p2p/model/Makefile.am
new file mode 100644
index 00000000..c828a086
--- /dev/null
+++ b/sugar/p2p/model/Makefile.am
@@ -0,0 +1,6 @@
+sugardir = $(pythondir)/sugar/p2p/model
+sugar_PYTHON = \
+ __init__.py \
+ LocalModel.py \
+ RemoteModel.py \
+ Store.py
diff --git a/sugar/p2p/model/RemoteModel.py b/sugar/p2p/model/RemoteModel.py
new file mode 100644
index 00000000..b966b435
--- /dev/null
+++ b/sugar/p2p/model/RemoteModel.py
@@ -0,0 +1,26 @@
+import xmlrpclib
+
+from sugar.p2p.NotificationListener import NotificationListener
+from sugar.p2p.model.AbstractModel import AbstractModel
+
+class RemoteModel(AbstractModel):
+ def __init__(self, service):
+ AbstractModel.__init__(self)
+
+ self._service = service
+
+ addr = "http://%s:%d" % (service.get_address(), service.get_port())
+ self._client = xmlrpclib.ServerProxy(addr)
+
+ self._setup_notification_listener()
+
+ def get_value(self, key):
+ return self._client.get_value(key)
+
+ def set_value(self, key, value):
+ self._client.set_value(key, value)
+
+ def _setup_notification_listener(self):
+ name = self._service.get_name()
+ self._notification = NotificationListener(self._group, name)
+ self._notification.add_listener(self._notify_model_change)
diff --git a/sugar/p2p/model/Store.py b/sugar/p2p/model/Store.py
new file mode 100644
index 00000000..d39783f4
--- /dev/null
+++ b/sugar/p2p/model/Store.py
@@ -0,0 +1,22 @@
+from sugar.p2p.model.RemoteModel import RemoteModel
+from sugar.p2p.model.LocalModel import LocalModel
+
+class Store:
+ def __init__(self, group):
+ self._group = group
+ self._local_models = {}
+
+ def create_model(self, model_id):
+ model = LocalModel(self._group, model_id)
+ self._local_models[model_id] = model
+ return model
+
+ def get_model(self, model_id):
+ if self._local_models.has_key(model_id):
+ return self._local_models(model_id)
+ else:
+ service = self._group.get_service(model_id, LocalModel.SERVICE_TYPE)
+ if service:
+ return RemoteModel(service)
+ else:
+ return None
diff --git a/sugar/p2p/network.py b/sugar/p2p/network.py
index c88ede6c..4c054fea 100644
--- a/sugar/p2p/network.py
+++ b/sugar/p2p/network.py
@@ -1,10 +1,6 @@
-# -*- tab-width: 4; indent-tabs-mode: t -*-
-
import socket
import threading
import traceback
-import select
-import time
import xmlrpclib
import sys
@@ -133,10 +129,6 @@ class GroupServer(object):
# Set some options to make it multicast-friendly
self._listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- try:
- self._listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
- except:
- pass
self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20)
self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)
diff --git a/sugar/p2p/presence.py b/sugar/p2p/presence.py
index e16fc928..4c5c086f 100644
--- a/sugar/p2p/presence.py
+++ b/sugar/p2p/presence.py
@@ -65,7 +65,7 @@ class PresenceDiscovery(object):
self._service_type_browsers[(interface, protocol, domain)] = b
- def new_domain(self,interface, protocol, domain, flags):
+ def new_domain(self, interface, protocol, domain, flags):
if domain != "local":
return
self.browse_domain(interface, protocol, domain)
@@ -84,7 +84,7 @@ class PresenceAnnounce(object):
self._hostname = "%s:%s" % (self.server.GetHostName(), rs_port)
rs_name = self._hostname
- info = ["%s=%s" % (k,v) for k,v in kwargs.items()]
+ info = ["%s=%s" % (k, v) for k, v in kwargs.items()]
g.AddService(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, 0, rs_name, rs_service,
"", "", # domain, host (let the system figure it out)
dbus.UInt16(rs_port), info,)
diff --git a/sugar/shell/activity.py b/sugar/shell/activity.py
index 6f1f3fc1..4678a9c0 100644
--- a/sugar/shell/activity.py
+++ b/sugar/shell/activity.py
@@ -1,15 +1,11 @@
# -*- tab-width: 4; indent-tabs-mode: t -*-
-import string
-
-import gc
import dbus
import dbus.service
import dbus.glib
-import gobject
import pygtk
pygtk.require('2.0')
-import gtk,sys
+import gtk
class Activity(dbus.service.Object):
@@ -36,21 +32,21 @@ class Activity(dbus.service.Object):
"com.redhat.Sugar.Shell.ActivityContainer")
self.__activity_id = self.__activity_container.add_activity("")
- self.__object_path = "/com/redhat/Sugar/Shell/Activities/%d"%self.__activity_id
+ self.__object_path = "/com/redhat/Sugar/Shell/Activities/%d" % self.__activity_id
- print "object_path = %s"%self.__object_path
+ print "object_path = %s" % self.__object_path
self.__activity_object = dbus.Interface(self.__bus.get_object("com.redhat.Sugar.Shell", self.__object_path), \
"com.redhat.Sugar.Shell.ActivityHost")
self.__window_id = self.__activity_object.get_host_xembed_id()
- print "XEMBED window_id = %d"%self.__window_id
+ print "XEMBED window_id = %d" % self.__window_id
self.__plug = gtk.Plug(self.__window_id)
# Now let the Activity register a peer service so the Shell can poke it
- self.__peer_service_name = "com.redhat.Sugar.Activity%d"%self.__activity_id
- self.__peer_object_name = "/com/redhat/Sugar/Activity/%d"%self.__activity_id
+ self.__peer_service_name = "com.redhat.Sugar.Activity%d" % self.__activity_id
+ self.__peer_object_name = "/com/redhat/Sugar/Activity/%d" % self.__activity_id
self.__service = dbus.service.BusName(self.__peer_service_name, bus=self.__bus)
dbus.service.Object.__init__(self, self.__service, self.__peer_object_name)
@@ -86,7 +82,7 @@ class Activity(dbus.service.Object):
pixarray = []
pixstr = pixbuf.get_pixels();
for c in pixstr:
- pixarray.append(c)
+ pixarray.append(c)
self.__activity_object.set_tab_icon(pixarray, \
pixbuf.get_colorspace(), \
pixbuf.get_has_alpha(), \
@@ -163,16 +159,16 @@ class Activity(dbus.service.Object):
# pure virtual methods
def activity_on_connected_to_shell(self):
- print "act %d: you need to override activity_on_connected_to_shell"%self.activity_get_id()
+ print "act %d: you need to override activity_on_connected_to_shell" % self.activity_get_id()
def activity_on_disconnected_from_shell(self):
- print "act %d: you need to override activity_on_disconnected_from_shell"%self.activity_get_id()
-
+ print "act %d: you need to override activity_on_disconnected_from_shell" % self.activity_get_id()
+
def activity_on_close_from_user(self):
- print "act %d: you need to override activity_on_close_from_user"%self.activity_get_id()
+ print "act %d: you need to override activity_on_close_from_user" % self.activity_get_id()
def activity_on_lost_focus(self):
- print "act %d: you need to override activity_on_lost_focus"%self.activity_get_id()
+ print "act %d: you need to override activity_on_lost_focus" % self.activity_get_id()
def activity_on_got_focus(self):
- print "act %d: you need to override activity_on_got_focus"%self.activity_get_id()
+ print "act %d: you need to override activity_on_got_focus" % self.activity_get_id()
diff --git a/sugar/shell/shell.py b/sugar/shell/shell.py
index d5423079..0dd3ea95 100755
--- a/sugar/shell/shell.py
+++ b/sugar/shell/shell.py
@@ -1,12 +1,9 @@
#!/usr/bin/python
# -*- tab-width: 4; indent-tabs-mode: t -*-
-import string
-
import dbus
import dbus.service
import dbus.glib
-import gobject
import pygtk
pygtk.require('2.0')
import gtk
@@ -27,7 +24,7 @@ class ActivityHost(dbus.service.Object):
self.activity_id = activity_counter
activity_counter += 1
- self.dbus_object_name = "/com/redhat/Sugar/Shell/Activities/%d"%self.activity_id
+ self.dbus_object_name = "/com/redhat/Sugar/Shell/Activities/%d" % self.activity_id
#print "object name = %s"%self.dbus_object_name
dbus.service.Object.__init__(self, activity_container.service, self.dbus_object_name)
@@ -143,7 +140,7 @@ class ActivityHost(dbus.service.Object):
#print " data = ", data
pixstr = ""
for c in data:
- pixstr += chr(c)
+ pixstr += chr(c)
pixbuf = gtk.gdk.pixbuf_new_from_data(pixstr, colorspace, has_alpha, bits_per_sample, width, height, rowstride)
#print pixbuf
@@ -225,10 +222,11 @@ class ActivityContainer(dbus.service.Object):
self.window.add(self.notebook)
self.window.connect("destroy", lambda w: gtk.main_quit())
- self.window.show()
self.current_activity = None
+ def show(self):
+ self.window.show()
def __focus_reply_cb(self):
pass
@@ -283,10 +281,10 @@ class ActivityContainer(dbus.service.Object):
return activity.get_host_activity_id()
def __print_activities(self):
- print "__print_activities: %d activities registered"%len(self.activities)
+ print "__print_activities: %d activities registered" % len(self.activities)
i = 0
for owner, activity in self.activities:
- print " %d: owner=%s activity_object_name=%s"%(i, owner, activity.dbus_object_name)
+ print " %d: owner=%s activity_object_name=%s" % (i, owner, activity.dbus_object_name)
i += 1
@@ -295,11 +293,9 @@ def main():
service = dbus.service.BusName("com.redhat.Sugar.Shell", bus=session_bus)
activityContainer = ActivityContainer(service, session_bus)
+ activityContainer.show()
- try:
- gtk.main()
- except KeyboardInterrupt:
- pass
+ gtk.main()
-if __name__=="__main__":
- main()
+if __name__ == "__main__":
+ main()