Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGerard J. Cerchio <gjpc@circlesoft.com>2007-11-28 00:54:37 (GMT)
committer Gerard J. Cerchio <gjpc@circlesoft.com>2007-11-28 00:54:37 (GMT)
commit958f0fcb437be76c2b420e84b138c944ae47847a (patch)
tree16fc6c92f2c139974bb0c83b59ae3044e0087d46
Initial import
-rw-r--r--MANIFEST25
-rw-r--r--NEWS77
-rw-r--r--PlayGo-1.xobin0 -> 218530 bytes
-rwxr-xr-xactivity.py199
-rw-r--r--activity.pycbin0 -> 7415 bytes
-rw-r--r--activity/activity-go.svg118
-rw-r--r--activity/activity-helloworld.svg8
-rw-r--r--activity/activity.info7
-rwxr-xr-xboardwidget.py231
-rw-r--r--boardwidget.pycbin0 -> 7399 bytes
-rwxr-xr-xbuddiespanel.py132
-rw-r--r--buddiespanel.pycbin0 -> 4529 bytes
-rwxr-xr-xgame.py305
-rw-r--r--game.pycbin0 -> 10728 bytes
-rw-r--r--images/BsTurn.gifbin0 -> 691 bytes
-rw-r--r--images/README7
-rw-r--r--images/WsTurn.gifbin0 -> 728 bytes
-rw-r--r--images/b.gifbin0 -> 107 bytes
-rw-r--r--images/black.gifbin0 -> 1343 bytes
-rw-r--r--images/board.gifbin0 -> 197707 bytes
-rw-r--r--images/bw.gifbin0 -> 141 bytes
-rw-r--r--images/w.gifbin0 -> 105 bytes
-rw-r--r--images/white.gifbin0 -> 1250 bytes
-rwxr-xr-xinfopanel.py20
-rw-r--r--infopanel.pycbin0 -> 1270 bytes
-rw-r--r--locale/ar/LC_MESSAGES/org.laptop.Connect.mobin0 -> 1084 bytes
-rw-r--r--locale/ar/LC_MESSAGES/org.laptop.Playg.mobin0 -> 1084 bytes
-rw-r--r--locale/ar/activity.linfo2
-rw-r--r--locale/de/LC_MESSAGES/org.laptop.Connect.mobin0 -> 845 bytes
-rw-r--r--locale/de/LC_MESSAGES/org.laptop.Playg.mobin0 -> 845 bytes
-rw-r--r--locale/de/activity.linfo2
-rw-r--r--locale/el/LC_MESSAGES/org.laptop.Connect.mobin0 -> 967 bytes
-rw-r--r--locale/el/LC_MESSAGES/org.laptop.Playg.mobin0 -> 967 bytes
-rw-r--r--locale/el/activity.linfo2
-rw-r--r--locale/es/LC_MESSAGES/org.laptop.Connect.mobin0 -> 967 bytes
-rw-r--r--locale/es/LC_MESSAGES/org.laptop.Playg.mobin0 -> 967 bytes
-rw-r--r--locale/es/activity.linfo2
-rw-r--r--po/Connect.pot49
-rw-r--r--po/ar.po63
-rw-r--r--po/de.po58
-rw-r--r--po/el.po58
-rw-r--r--po/es.po58
-rwxr-xr-xsetup.py22
-rwxr-xr-xwidgettest.py62
44 files changed, 1507 insertions, 0 deletions
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..8df329a
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,25 @@
+activity.py
+boardwidget.py
+buddiespanel.py
+game.py
+gridwidget.py
+infopanel.py
+activity/activity-go.svg
+activity/activity.info
+images/b.gif
+images/black.gif
+images/board.gif
+images/BsTurn.gif
+images/bw.gif
+images/w.gif
+images/white.gif
+images/WsTurn.gif
+locale/ar
+locale/de
+locale/el
+locale/es
+po/ar.po
+po/Connect.pot
+po/de.po
+po/el.po
+po/es.po
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..166cb58
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,77 @@
+20
+* morph Connect into Go most of the boardwidget is complete,
+* you can click play on a single machine (sortof)
+
+19
+
+* #4663: fix and use _get_buddy again to convert handles into buddies
+ (morgs)
+
+18
+
+* Fixes in usage of sugar.presence API (morgs)
+
+17
+
+* Remove Tubes boilerplate and rely on #4503 having been fixed in
+ sugar.presence (smcv)
+ -- this depends on sugar snapshot 8c89bfaed7
+
+16
+
+* Remove self.set_title() (morgs)
+* #3492: Don't quit on escape (cassidy)
+
+15
+
+* #3557: Updated spanish translation (morgs)
+
+14
+
+* Updated spanish translation (morgs)
+* Add spanish translation (xavi)
+
+13
+
+* #2349 Suggests sharing or inviting someone to play (morgs)
+* #3194 Detect win with more than 4 in a row (morgs)
+
+12
+
+* New activity icon, Fix for #2829 (erikos)
+
+11
+
+* Port to new tubes API (cassidy)
+
+10
+
+* Fix sugar.graphics change to CanvasIcon (morgs)
+* Add greek translation. (simosx)
+* Add arabic translation. (khaled)
+
+9
+
+* Fix buddy icon (morgs)
+* Use tubeconn from sugar.presence (morgs)
+* Use sugar.graphics.style instead of sugar.graphics.color (danw)
+* #2579 German translation (morgs)
+
+8
+
+* Add gettext for i18n (morgs)
+* #1625, #2420: Fix activity icon (danw, cassidy)
+
+7
+
+* 1960, #1624: Show the activity toolbox and sidebar when PS not connected
+ (morgs)
+
+6
+
+* Adapt to sugar API change (marco)
+
+5
+
+* Fix buddy handles for Salut (Link Local) channels (smcv)
+* UI improvement to show whose turn it is, below grid (morgs)
diff --git a/PlayGo-1.xo b/PlayGo-1.xo
new file mode 100644
index 0000000..d973f99
--- /dev/null
+++ b/PlayGo-1.xo
Binary files differ
diff --git a/activity.py b/activity.py
new file mode 100755
index 0000000..756a712
--- /dev/null
+++ b/activity.py
@@ -0,0 +1,199 @@
+import logging
+from gettext import gettext as _
+
+import dbus
+import gtk
+import hippo
+import telepathy
+import telepathy.client
+
+from sugar.activity.activity import Activity, ActivityToolbox
+from sugar.presence import presenceservice
+import sugar.logger
+
+import boardwidget
+import game
+
+from sugar.presence.tubeconn import TubeConnection
+from buddiespanel import BuddiesPanel
+from infopanel import InfoPanel
+
+
+logger = logging.getLogger('connect-activity')
+
+
+class PlayGo(Activity):
+ def __init__(self, handle):
+ Activity.__init__(self, handle)
+
+ logger.debug('Starting Connect activity...')
+
+ board = game.GoBoard( 19 )
+ self.grid = boardwidget.BoardWidget( board )
+
+ self.buddies_panel = BuddiesPanel()
+
+ self.info_panel = InfoPanel()
+
+ vbox = hippo.CanvasBox(spacing=4,
+ orientation=hippo.ORIENTATION_VERTICAL)
+
+ hbox = hippo.CanvasBox(spacing=4,
+ orientation=hippo.ORIENTATION_HORIZONTAL)
+
+ hbox.append(self.buddies_panel)
+ hbox.append(hippo.CanvasWidget(widget=self.grid), hippo.PACK_EXPAND)
+
+ vbox.append(hbox, hippo.PACK_EXPAND)
+ vbox.append(self.info_panel, hippo.PACK_END)
+
+ canvas = hippo.Canvas()
+ canvas.set_root(vbox)
+ self.set_canvas(canvas)
+ self.show_all()
+
+ toolbox = ActivityToolbox(self)
+ self.set_toolbox(toolbox)
+ toolbox.show()
+
+ self.pservice = presenceservice.get_instance()
+ owner = self.pservice.get_owner()
+ self.owner = owner
+
+ # This displays the buddies_panel even if we fail to connect:
+ self.buddies_panel.add_watcher(owner)
+ self.info_panel.show(_('To play, share or invite someone.'))
+
+ self.initiating = None
+
+ self.game = None
+
+ self.connect('shared', self._shared_cb)
+
+ if self._shared_activity:
+ # we are joining the activity
+ self.buddies_panel.add_watcher(owner)
+ self.connect('joined', self._joined_cb)
+ self._shared_activity.connect('buddy-joined', self._buddy_joined_cb)
+ self._shared_activity.connect('buddy-left', self._buddy_left_cb)
+ if self.get_shared():
+ # oh, OK, we've already joined
+ self._joined_cb()
+ else:
+ # we are creating the activity
+ self.buddies_panel.remove_watcher(owner)
+ self.buddies_panel.add_player(owner)
+ #self.buddies_panel.set_is_playing(owner)
+ #self.buddies_panel.set_count(owner, 69)
+
+ self.connect('key-press-event', self.key_press_cb)
+
+ def _get_buddy(self, cs_handle):
+ """Get a Buddy from a channel specific handle."""
+ # FIXME: After Update.1, design a better API for sugar.presence
+ # to track handles of buddies, and use that instead
+ logger.debug('Trying to find owner of handle %u...', cs_handle)
+ group = self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]
+ my_csh = group.GetSelfHandle()
+ logger.debug('My handle in that group is %u', my_csh)
+ if my_csh == cs_handle:
+ handle = self.conn.GetSelfHandle()
+ logger.debug('CS handle %u belongs to me, %u', cs_handle, handle)
+ elif group.GetGroupFlags() & telepathy.CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES:
+ handle = group.GetHandleOwners([cs_handle])[0]
+ logger.debug('CS handle %u belongs to %u', cs_handle, handle)
+ else:
+ handle = cs_handle
+ logger.debug('non-CS handle %u belongs to itself', handle)
+
+ # XXX: deal with failure to get the handle owner
+ assert handle != 0
+
+ return self.pservice.get_buddy_by_telepathy_handle(
+ self.conn.service_name, self.conn.object_path, handle)
+
+ def key_press_cb(self, widget, event):
+ logger.debug('Keypress: %r, %r', widget, event)
+
+ if self.game is not None:
+ self.game.key_press_event(widget, event)
+
+ def _shared_cb(self, activity):
+ logger.debug('My Connect activity was shared')
+ self.initiating = True
+ self._setup()
+
+ for buddy in self._shared_activity.get_joined_buddies():
+ self.buddies_panel.add_watcher(buddy)
+
+ self._shared_activity.connect('buddy-joined', self._buddy_joined_cb)
+ self._shared_activity.connect('buddy-left', self._buddy_left_cb)
+
+ logger.debug('This is my activity: making a tube...')
+ id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
+ SERVICE, {})
+ self.info_panel.show(_('Waiting for another player to join.'))
+
+ def _setup(self):
+ if self._shared_activity is None:
+ logger.error('Failed to share or join activity')
+ return
+
+ self.conn = self._shared_activity.telepathy_conn
+ self.tubes_chan = self._shared_activity.telepathy_tubes_chan
+ self.text_chan = self._shared_activity.telepathy_text_chan
+
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
+ 'NewTube', self._new_tube_cb)
+
+ def _list_tubes_reply_cb(self, tubes):
+ for tube_info in tubes:
+ self._new_tube_cb(*tube_info)
+
+ def _list_tubes_error_cb(self, e):
+ logger.error('ListTubes() failed: %s', e)
+
+ def _joined_cb(self, activity):
+ if self.game is not None:
+ return
+
+ if not self._shared_activity:
+ return
+
+ for buddy in self._shared_activity.get_joined_buddies():
+ self.buddies_panel.add_watcher(buddy)
+
+ logger.debug('Joined an existing Connect game')
+ self.info_panel.show(_('Joined a game. Waiting for my turn...'))
+ self.initiating = False
+ self._setup()
+
+ logger.debug('This is not my activity: waiting for a tube...')
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(
+ reply_handler=self._list_tubes_reply_cb,
+ error_handler=self._list_tubes_error_cb)
+
+ def _new_tube_cb(self, id, initiator, type, service, params, state):
+ logger.debug('New tube: ID=%d initator=%d type=%d service=%s '
+ 'params=%r state=%d', id, initiator, type, service,
+ params, state)
+
+ if (self.game is None and type == telepathy.TUBE_TYPE_DBUS and
+ service == SERVICE):
+ if state == telepathy.TUBE_STATE_LOCAL_PENDING:
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)
+
+ tube_conn = TubeConnection(self.conn,
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES],
+ id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP])
+ self.game = GoGame(tube_conn, self.grid, self.initiating,
+ self.buddies_panel, self.info_panel, self.owner,
+ self._get_buddy, self)
+
+ def _buddy_joined_cb (self, activity, buddy):
+ logger.debug("buddy joined")
+ self.buddies_panel.add_watcher(buddy)
+
+ def _buddy_left_cb (self, activity, buddy):
+ logger.debug("buddy left")
+ self.buddies_panel.remove_watcher(buddy)
diff --git a/activity.pyc b/activity.pyc
new file mode 100644
index 0000000..6020793
--- /dev/null
+++ b/activity.pyc
Binary files differ
diff --git a/activity/activity-go.svg b/activity/activity-go.svg
new file mode 100644
index 0000000..c40cafa
--- /dev/null
+++ b/activity/activity-go.svg
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+ <!ENTITY ns_svg "http://www.w3.org/2000/svg">
+ <!ENTITY ns_xlink " http://www.w3.org/1999/xlink">
+ <!ENTITY stroke_color "#000000">
+ <!ENTITY fill_color "#AAAAAA">
+ ]>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ enable-background="new 0 0 55 55"
+ height="55px"
+ version="1.1"
+ viewBox="0 0 55 55"
+ width="55px"
+ x="0px"
+ xml:space="preserve"
+ y="0px"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docname="activity-go.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ sodipodi:docbase="/home/gjpc/PlayGo/PlayGo/src/activity"><metadata
+ id="metadata26"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs24" /><sodipodi:namedview
+ inkscape:window-height="619"
+ inkscape:window-width="872"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#ffffff"
+ id="base"
+ inkscape:zoom="7.3818182"
+ inkscape:cx="27.5"
+ inkscape:cy="27.5"
+ inkscape:window-x="26"
+ inkscape:window-y="59"
+ inkscape:current-layer="svg2" /><g
+ display="block"
+ id="activity-connect"
+ transform="translate(-0.5418719,-1.2192118)"
+ style="display:block">
+ <g
+ display="inline"
+ id="g5"
+ style="display:inline">
+ <path
+ d="M 14.118,9.535 C 14.119,11.967 12.148,13.94 9.716,13.941 C 7.283,13.942 5.311,11.971 5.31,9.539 C 5.31,9.538 5.31,9.536 5.31,9.535 C 5.308,7.103 7.279,5.13 9.711,5.128 C 12.143,5.127 14.116,7.098 14.117,9.53 C 14.118,9.532 14.118,9.534 14.118,9.535 z "
+ id="path2160"
+ style="fill:&fill_color;stroke:&stroke_color;stroke-width:2.25;stroke-linejoin:bevel" />
+ <path
+ d="M 51.188,9.535 C 51.19,11.967 49.219,13.94 46.786,13.941 C 44.354,13.942 42.382,11.971 42.38,9.539 C 42.38,9.538 42.38,9.536 42.38,9.535 C 42.38,7.102 44.351,5.13 46.782,5.128 C 49.216,5.127 51.188,7.098 51.188,9.53 C 51.188,9.532 51.188,9.534 51.188,9.535 z "
+ id="path2162"
+ style="fill:#010101;stroke:&stroke_color;stroke-width:2.25;stroke-linejoin:bevel" />
+ <path
+ d="M 26.475,9.535 C 26.475,11.967 24.504,13.94 22.072,13.941 C 19.639,13.942 17.667,11.971 17.665,9.539 C 17.665,9.538 17.665,9.536 17.665,9.535 C 17.664,7.102 19.635,5.13 22.067,5.128 C 24.5,5.127 26.472,7.098 26.474,9.53 C 26.475,9.532 26.475,9.534 26.475,9.535 z "
+ id="path3134"
+ style="fill:&fill_color;stroke:&stroke_color;stroke-width:2.25;stroke-linejoin:bevel" />
+ <path
+ d="M 38.832,9.535 C 38.832,11.967 36.861,13.94 34.43,13.941 C 31.996,13.942 30.024,11.971 30.022,9.539 C 30.022,9.538 30.022,9.536 30.022,9.535 C 30.022,7.102 31.993,5.13 34.424,5.128 C 36.858,5.127 38.83,7.098 38.832,9.53 C 38.832,9.532 38.832,9.534 38.832,9.535 z "
+ id="path3136"
+ style="fill:&fill_color;stroke:&stroke_color;stroke-width:2.25;stroke-linejoin:bevel" />
+ <path
+ d="M 26.475,21.892 C 26.475,24.325 24.504,26.297 22.072,26.298 C 19.639,26.299 17.667,24.328 17.665,21.896 C 17.665,21.895 17.665,21.893 17.665,21.892 C 17.664,19.459 19.635,17.487 22.067,17.485 C 24.5,17.484 26.472,19.455 26.474,21.887 C 26.475,21.889 26.475,21.891 26.475,21.892 z "
+ id="path3138"
+ style="fill:&fill_color;stroke:&stroke_color;stroke-width:2.25;stroke-linejoin:bevel" />
+ <path
+ d="M 38.832,21.892 C 38.832,24.325 36.861,26.297 34.43,26.298 C 31.996,26.299 30.024,24.328 30.022,21.896 C 30.022,21.895 30.022,21.893 30.022,21.892 C 30.022,19.459 31.993,17.487 34.424,17.485 C 36.858,17.484 38.83,19.455 38.832,21.887 C 38.832,21.889 38.832,21.891 38.832,21.892 z "
+ id="path3140"
+ style="fill:#010101;stroke:&stroke_color;stroke-width:2.25;stroke-linejoin:bevel" />
+ <path
+ d="M 14.118,21.892 C 14.119,24.325 12.148,26.297 9.716,26.298 C 7.284,26.299 5.311,24.328 5.31,21.896 C 5.31,21.895 5.31,21.893 5.31,21.892 C 5.309,19.459 7.28,17.487 9.712,17.485 C 12.144,17.484 14.117,19.455 14.118,21.887 C 14.118,21.889 14.118,21.891 14.118,21.892 z "
+ id="path3142"
+ style="fill:&fill_color;stroke:&stroke_color;stroke-width:2.25;stroke-linejoin:bevel" />
+ <path
+ d="M 51.188,21.892 C 51.19,24.325 49.219,26.297 46.786,26.298 C 44.354,26.299 42.382,24.328 42.38,21.896 C 42.38,21.895 42.38,21.893 42.38,21.892 C 42.38,19.459 44.351,17.487 46.782,17.485 C 49.216,17.484 51.188,19.455 51.188,21.887 C 51.188,21.889 51.188,21.891 51.188,21.892 z "
+ id="path3144"
+ style="fill:&fill_color;stroke:&stroke_color;stroke-width:2.25;stroke-linejoin:bevel" />
+ <path
+ d="M 14.118,34.248 C 14.119,36.68 12.148,38.652 9.716,38.654 C 7.283,38.655 5.311,36.684 5.31,34.252 C 5.31,34.25 5.31,34.249 5.31,34.248 C 5.309,31.815 7.28,29.843 9.712,29.842 C 12.144,29.84 14.117,31.811 14.118,34.244 C 14.118,34.245 14.118,34.246 14.118,34.248 z "
+ id="path3146"
+ style="fill:&fill_color;stroke:&stroke_color;stroke-width:2.25;stroke-linejoin:bevel" />
+
+ <path
+ d="M 38.832,34.248 C 38.832,36.68 36.861,38.652 34.43,38.654 C 31.996,38.655 30.024,36.684 30.022,34.252 C 30.022,34.25 30.022,34.249 30.022,34.248 C 30.022,31.815 31.993,29.843 34.424,29.842 C 36.858,29.84 38.83,31.811 38.832,34.244 C 38.832,34.245 38.832,34.246 38.832,34.248 z "
+ id="path3152"
+ style="fill:&fill_color;stroke:&stroke_color;stroke-width:2.25;stroke-linejoin:bevel" />
+ <path
+ d="M 38.832,46.604 C 38.832,49.038 36.861,51.01 34.43,51.011 C 31.996,51.012 30.024,49.041 30.022,46.609 C 30.022,46.608 30.022,46.606 30.022,46.604 C 30.022,44.172 31.993,42.2 34.424,42.198 C 36.858,42.198 38.83,44.169 38.832,46.6 C 38.832,46.602 38.832,46.603 38.832,46.604 z "
+ id="path3154"
+ style="fill:&fill_color;stroke:&stroke_color;stroke-width:2.25;stroke-linejoin:bevel" />
+ <path
+ d="M 14.118,46.604 C 14.119,49.038 12.148,51.01 9.716,51.011 C 7.284,51.012 5.311,49.041 5.31,46.609 C 5.31,46.608 5.31,46.606 5.31,46.604 C 5.309,44.172 7.28,42.2 9.712,42.198 C 12.144,42.198 14.117,44.169 14.118,46.6 C 14.118,46.602 14.118,46.603 14.118,46.604 z "
+ id="path3156"
+ style="fill:#010101;stroke:&stroke_color;stroke-width:2.25;stroke-linejoin:bevel" />
+ <path
+ d="M 26.475,46.604 C 26.475,49.038 24.504,51.01 22.072,51.011 C 19.639,51.012 17.667,49.041 17.665,46.609 C 17.665,46.608 17.665,46.606 17.665,46.604 C 17.664,44.172 19.635,42.2 22.067,42.198 C 24.5,42.198 26.472,44.169 26.474,46.6 C 26.475,46.602 26.475,46.603 26.475,46.604 z "
+ id="path3158"
+ style="fill:&fill_color;stroke:&stroke_color;stroke-width:2.25;stroke-linejoin:bevel" />
+ <path
+ d="M 51.188,46.604 C 51.19,49.038 49.219,51.01 46.786,51.011 C 44.354,51.012 42.382,49.041 42.38,46.609 C 42.38,46.608 42.38,46.606 42.38,46.604 C 42.38,44.172 44.351,42.2 46.782,42.198 C 49.216,42.198 51.188,44.169 51.188,46.6 C 51.188,46.602 51.188,46.603 51.188,46.604 z "
+ id="path3160"
+ style="fill:&fill_color;stroke:&stroke_color;stroke-width:2.25;stroke-linejoin:bevel" />
+ </g>
+</g></svg> \ No newline at end of file
diff --git a/activity/activity-helloworld.svg b/activity/activity-helloworld.svg
new file mode 100644
index 0000000..b9278b0
--- /dev/null
+++ b/activity/activity-helloworld.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+ <!ENTITY fill_color "#FFFFFF">
+ <!ENTITY stroke_color "#000000">
+]>
+<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
+<rect x="1" y="1" width="48" height="48" style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:2"/>
+</svg>
diff --git a/activity/activity.info b/activity/activity.info
new file mode 100644
index 0000000..f5ed3b4
--- /dev/null
+++ b/activity/activity.info
@@ -0,0 +1,7 @@
+[Activity]
+name = PlayGo
+service_name = org.laptop.Playg
+class = activity.PlayGo
+icon = activity-go
+activity_version = 1
+show_launcher = yes
diff --git a/boardwidget.py b/boardwidget.py
new file mode 100755
index 0000000..373a898
--- /dev/null
+++ b/boardwidget.py
@@ -0,0 +1,231 @@
+import logging
+import cairo
+import gobject
+import gtk
+
+import game
+
+
+logger = logging.getLogger('PlayGo-activity.gridwidget')
+
+
+class BoardWidget(gtk.EventBox):
+ "Gtk widget for drawing the graphical board."""
+
+ __gsignals__ = {
+ 'insert-requested': (gobject.SIGNAL_RUN_FIRST, None, [int]),
+ }
+
+ def __init__( self, aBoard ):
+
+ gtk.EventBox.__init__( self )
+
+ self.output = gtk.DrawingArea()
+ self.set_property('child', self.output)
+
+ self.output.connect('expose-event', self.expose_cb)
+ self.add_events(gtk.gdk.POINTER_MOTION_MASK)
+
+ self.connect('button-release-event', self.__class__.button_release_cb)
+ self.connect('motion-notify-event', self.__class__.motion_cb)
+
+ self.drawCoords = 1
+ self.columns = aBoard.size
+ self.rows = aBoard.size
+ self.lastUnit = 0
+ self.myBoard = aBoard
+
+ self.lastColor = 1
+
+ # get the bitmap for genuine simulated wooden board
+ input = open("./images/board.gif")
+ imagebuf = input.read()
+ pixbufloader = gtk.gdk.PixbufLoader()
+ pixbufloader.write(imagebuf)
+ pixbufloader.close()
+ self.pixBoard = pixbufloader.get_pixbuf()
+
+ # get the bitmap for genuine simulated white stone
+ input = open("./images/white.gif")
+ imagebuf = input.read()
+ pixbufloader = gtk.gdk.PixbufLoader()
+ pixbufloader.write(imagebuf)
+ pixbufloader.close()
+ self.pixWhite = pixbufloader.get_pixbuf()
+
+ # get the bitmap for genuine simulated black stone
+ input = open("./images/black.gif")
+ imagebuf = input.read()
+ pixbufloader = gtk.gdk.PixbufLoader()
+ pixbufloader.write(imagebuf)
+ pixbufloader.close()
+ self.pixBlack = pixbufloader.get_pixbuf()
+
+ def check_coord (self, i, j):
+ return i >= 0 and i < self.rows and j >= 0 and j < self.columns
+
+ def insert(self, column, value):
+ """Return:
+ None : no winner
+ 0, 1: player 0/1 wins the game
+ """
+ discs = [row[column] for row in self.grid]
+
+ if -1 not in discs:
+ raise ValueError('Column is full')
+
+ row = self.rows - list(reversed(discs)).index(-1) - 1
+ self.grid[row][column] = value
+
+ return self.check_winner(row, column, value)
+
+
+ def draw_background(self, rect, unit, ctx):
+
+ ct = gtk.gdk.CairoContext(ctx)
+ ct.set_source_pixbuf(self.pixBoard,0,0)
+ ctx.paint()
+ ctx.stroke()
+
+
+ def draw_lines(self, rect, unit, ctx):
+
+ # single width balck lines
+ ctx.set_line_width(1)
+ ctx.set_source_rgba(0, 0, 0, 1)
+
+ for i in xrange(self.rows + 1):
+ ctx.move_to( unit, i * unit)
+ ctx.line_to(self.columns * unit, i * unit )
+
+ for i in xrange(self.columns + 1):
+ ctx.move_to(i * unit, unit )
+ ctx.line_to(i * unit, self.rows * unit)
+
+ ctx.stroke()
+
+ # star point coords per board size
+ if self.columns == 19 :
+ seq = [ 4, 10, 16 ]
+ elif self.columns == 13 :
+ seq = [ 4, 7, 10 ]
+ elif self.columns == 9 :
+ seq = [ 3, 7 ]
+ # set the middle singleton
+ ctx.arc( unit * 5, unit * 5, 3, 0, -1e-10)
+ ctx.fill_preserve()
+ ctx.stroke()
+
+ # stroke in the star points
+ #TODO: adjust size for teeny boards
+ for x in seq :
+ for y in seq :
+ ctx.arc( unit * x, unit * y, 3, 0, -1e-10)
+ ctx.fill_preserve()
+ ctx.stroke()
+
+
+
+ def draw_stone(self, x, y, color, unit, ctx):
+
+ x = x + 1
+ y = y + 1
+ ct = gtk.gdk.CairoContext(ctx)
+ if color == 0 :
+ ct.set_source_pixbuf(self.pixBlackSized, unit*x - unit/2, unit*y - unit/2, )
+ else :
+ ct.set_source_pixbuf(self.pixWhiteSized, unit*x - unit/2, unit*y - unit/2, )
+
+ ctx.paint()
+
+ def draw_stones( self, ctx ):
+
+ for x in xrange(self.rows):
+ for y in xrange(self.columns):
+
+ point = self.myBoard.getPoint( x, y )
+
+ if ( point == 1 ) :
+ self.draw_stone( x, y, 1, self.lastUnit, ctx )
+ elif ( point == 2 ) :
+ self.draw_stone( x, y, 0, self.lastUnit, ctx )
+
+ ctx.stroke()
+
+ def get_mouse_event_col(self, event):
+
+ unit, x0, y0 = self.get_coordinates(self.get_allocation())
+ col = ( event.x - x0 ) / unit
+ row = ( event.y - y0 ) / unit
+ return int(row), int(col)
+
+ def motion_cb(self, event):
+
+ col = self.get_mouse_event_col(event)
+
+ def button_release_cb(self, event):
+
+ self.motion_cb(event)
+ row, col = self.get_mouse_event_col(event)
+
+ self.myBoard.setPointi( col, row, self.lastColor )
+ if self.lastColor == 1:
+ self.lastColor = 2
+ else :
+ self.lastColor = 1
+
+ self.window.invalidate_rect(self.get_allocation(), True)
+ #self.emit('insert-requested', col)
+
+ def queue_draw(self):
+ self.output.queue_draw()
+
+ def get_coordinates(self, rect):
+ """Returns tuple (unit size, origin x, origin y) suitable for drawing
+ a grid within @rect."""
+
+ if rect.height / float(self.rows) < rect.width / float(self.columns):
+ # wide
+ unit = rect.height / float(self.rows)
+ x0 = rect.x + (rect.width - self.columns * unit) / 2.0
+ y0 = rect.y
+ else:
+ # narrow
+ unit = rect.width / float(self.columns)
+ x0 = rect.x
+ y0 = rect.y + (rect.height - self.rows * unit) / 2.0
+
+ # now shrink the size for a 1 unit boarder
+ unit = unit - unit / self.rows
+
+ return unit, x0, y0
+
+ def draw(self, rect, ctx):
+ """Draw a grid using the cairo context @ctx within the rectangle
+ @rect."""
+
+ ctx.save()
+ ctx.set_line_cap(cairo.LINE_CAP_ROUND)
+ unit, x0, y0 = self.get_coordinates(rect)
+
+ # I could not find the resize event so...
+ if self.lastUnit != unit :
+ self.pixBlackSized = self.pixBlack.scale_simple( int(unit), int(unit), gtk.gdk.INTERP_BILINEAR )
+ self.pixWhiteSized = self.pixWhite.scale_simple( int(unit), int(unit), gtk.gdk.INTERP_BILINEAR )
+ self.lastUnit = unit
+# bx, by = self.pixBoard.get_size()
+# if rect.height > by :
+# self.pixBoard = self.pixBoard.scaleSimple( bx, by, gtk.gdk.INTERP_BILINEAR )
+
+ ctx.translate( x0, y0 )
+ self.draw_background( rect, unit, ctx )
+ self.draw_lines( rect, unit, ctx )
+ self.draw_stones( ctx )
+ ctx.restore()
+
+
+ def expose_cb(self, widget, event):
+
+ ctx = widget.window.cairo_create()
+ rect = self.get_allocation()
+ self.draw(rect, ctx)
diff --git a/boardwidget.pyc b/boardwidget.pyc
new file mode 100644
index 0000000..d6bc0ca
--- /dev/null
+++ b/boardwidget.pyc
Binary files differ
diff --git a/buddiespanel.py b/buddiespanel.py
new file mode 100755
index 0000000..5787f5c
--- /dev/null
+++ b/buddiespanel.py
@@ -0,0 +1,132 @@
+import gtk
+import hippo
+import logging
+from sugar.graphics.icon import CanvasIcon
+from sugar.graphics.xocolor import XoColor
+from sugar.graphics import style
+
+logger = logging.getLogger('PlayGo.buddiespanel')
+
+class BuddiesPanel(hippo.CanvasBox):
+ def __init__(self):
+ hippo.CanvasBox.__init__(self, spacing=4, padding=5,
+ orientation=hippo.ORIENTATION_VERTICAL)
+
+ self.players_box = hippo.CanvasBox(spacing=4, padding=5,
+ orientation=hippo.ORIENTATION_VERTICAL)
+
+ self.watchers_box = hippo.CanvasBox(spacing=4, padding=5,
+ orientation=hippo.ORIENTATION_VERTICAL)
+
+ self.append(self.players_box)
+ self.append(hippo.CanvasWidget(widget=gtk.HSeparator()))
+ self.append(self.watchers_box, hippo.PACK_EXPAND)
+
+ self.players = {}
+ self.watchers = {}
+
+ def _create_buddy_vbox (self, buddy):
+ buddy_color = buddy.props.color
+ if not buddy_color:
+ buddy_color = "#000000,#ffffff"
+
+ icon = CanvasIcon(
+ icon_name='computer-xo',
+ xo_color=XoColor(buddy_color))
+
+ nick = buddy.props.nick
+ if not nick:
+ nick = ""
+ name = hippo.CanvasText(text=nick, color=style.COLOR_WHITE.get_int())
+
+ vbox = hippo.CanvasBox(padding=5)
+ vbox.append(icon)
+ vbox.append(name)
+
+ return vbox
+
+ def add_watcher(self, buddy):
+ op = buddy.object_path()
+ if self.watchers.get(op) is not None:
+ return
+ # if the watcher is also a player, don't add them
+ if self.players.get(op) is not None:
+ return
+
+ vbox = self._create_buddy_vbox (buddy)
+
+ logger.debug("add watcher %s" % op)
+ self.watchers_box.append(vbox)
+
+ self.watchers[op] = vbox
+
+ def add_player(self, buddy):
+ op = buddy.object_path()
+ if self.players.get(op) is not None:
+ return
+ # if the player is also a watcher, drop them from the watchers
+ widget = self.watchers.pop(op, None)
+ if widget is not None:
+ self.watchers_box.remove(widget)
+
+ logger.debug("add player %s" % op)
+
+ assert len(self.players) < 2
+
+ hbox = hippo.CanvasBox(spacing=4, padding=5,
+ orientation=hippo.ORIENTATION_HORIZONTAL)
+
+ vbox = self._create_buddy_vbox(buddy)
+ hbox.append(vbox)
+
+ count_font = style.FONT_BOLD.get_pango_desc()
+ count_font.set_size(30000)
+ count = hippo.CanvasText(text="0", color=style.COLOR_WHITE.get_int(),
+ font_desc=count_font)
+ hbox.append(count)
+
+ self.players_box.append(hbox)
+
+ self.players[op] = hbox
+
+ def set_is_playing(self, buddy):
+ op = buddy.object_path()
+ for player, hbox in self.players.items():
+ vbox = hbox.get_children()[0]
+ icon, name = vbox.get_children()
+ if player == op:
+ name.props.font_desc = style.FONT_BOLD.get_pango_desc()
+ else:
+ name.props.font_desc = style.FONT_NORMAL.get_pango_desc()
+
+ def set_count(self, buddy, val):
+ hbox = self.players.get(buddy.object_path())
+ if hbox is None:
+ return
+
+ count = hbox.get_children()[1]
+ count.props.text = str(val)
+
+ def remove_watcher(self, buddy):
+ op = buddy.object_path()
+ widget = self.watchers[op]
+ if widget is None:
+ return
+
+ self.watchers_box.remove(widget)
+ del self.watchers[op]
+
+ # removing someone from the game entirely should also remove them
+ # from the players
+ self.remove_player(buddy)
+
+ def remove_player(self, buddy):
+ op = buddy.object_path()
+ widget = self.players.get(op)
+ if widget is None:
+ return
+
+ self.players_box.remove(widget)
+ del self.players[op]
+
+ self.add_watcher(buddy)
diff --git a/buddiespanel.pyc b/buddiespanel.pyc
new file mode 100644
index 0000000..28f6d8f
--- /dev/null
+++ b/buddiespanel.pyc
Binary files differ
diff --git a/game.py b/game.py
new file mode 100755
index 0000000..1af1040
--- /dev/null
+++ b/game.py
@@ -0,0 +1,305 @@
+import logging
+
+from Numeric import *
+from gettext import gettext as _
+import gtk
+
+from dbus import Interface
+from dbus.service import method, signal
+from dbus.gobject_service import ExportedGObject
+
+
+# XXX: I'm not convinced this is in the right namespace
+SERVICE = "org.freedesktop.Telepathy.Tube.Connect"
+IFACE = SERVICE
+PATH = "/org/freedesktop/Telepathy/Tube/Connect"
+
+
+_logger = logging.getLogger('PlayGo.game')
+
+
+def redraw(grid):
+ """Utility function to force a redraw of a Gtk widget."""
+ grid.queue_draw()
+
+
+def dump_grid(seq):
+ grid = ''
+ for row in seq:
+ row_str = ''
+ for col in row:
+ if col == -1:
+ row_str += ' |'
+ else:
+ row_str += '%d|' % col
+ grid = '%s%s\n' % (grid, row_str)
+ _logger.debug('Grid state is now:\n%s', grid)
+
+class GoBoard( object ) :
+
+ def __init__( self, size ):
+
+ self.size = size
+ self.board = zeros( [ size, size ], Int )
+ self.playNumber = 0;
+
+
+ def getPoint( self, x, y ):
+
+ assert( x < self.size )
+ assert( y < self.size )
+ return self.board[x][y]
+
+ def setPoint( self, x, y, value ):
+
+ if value is 'Empty' :
+ n = 0;
+ elif value is 'White' :
+ n = 1;
+ elif value is 'Black' :
+ n = 2;
+ elif value is 'WhiteKo' :
+ n = 3;
+ elif value is 'BlackKo' :
+ n = 4;
+
+ self.board[x][y] = n
+
+ def setPointi( self, x, y, value ):
+ self.board[x][y] = value
+
+ def clear(self):
+ for x in range( self.size ):
+ for y in range( self.size ):
+ self.board[x][y] = 0
+
+ def CopyBoard( self ) :
+ copy = GoBoard( self.size )
+ copy.board = self.board
+ copy.playNumber = self.playNumber
+ return copy
+
+
+
+class GoGame(ExportedGObject):
+
+ def __init__(self, tube, grid, is_initiator, buddies_panel, info_panel,
+ owner, get_buddy, activity):
+ super(GoGame, self).__init__(tube, PATH)
+ self.tube = tube
+ self.grid = grid
+ self.is_initiator = is_initiator
+ self.entered = False
+ self.player_id = None
+ self.buddies_panel = buddies_panel
+ self.info_panel = info_panel
+ self.owner = owner
+ self._get_buddy = get_buddy
+ self.activity = activity
+
+ # list indexed by player ID
+ # 0, 1 are players 0, 1
+ # 2+ are the spectator queue, 2 is to play next
+ self.ordered_bus_names = []
+
+ self.tube.watch_participants(self.participant_change_cb)
+ self.grid.connect('insert-requested', self.insert_requested_cb)
+
+ def participant_change_cb(self, added, removed):
+ # Initiator is player 0, other player is player 1.
+
+ _logger.debug('adding participants: %r', added)
+ _logger.debug('removing participants: %r', removed)
+
+ for handle, bus_name in added:
+ buddy = self._get_buddy(handle)
+ _logger.debug('Buddy %r was added', buddy)
+ if buddy is not None:
+ self.buddies_panel.add_watcher(buddy)
+
+ for handle in removed:
+ buddy = self._get_buddy(handle)
+ _logger.debug('Buddy %r was removed', buddy)
+ if buddy is not None:
+ self.buddies_panel.remove_watcher(buddy)
+ try:
+ self.ordered_bus_names.remove(self.tube.participants[handle])
+ except ValueError:
+ # already absent
+ pass
+
+ if not self.entered:
+ self.tube.add_signal_receiver(self.insert_cb, 'Insert', IFACE,
+ path=PATH, sender_keyword='sender')
+ if self.is_initiator:
+ _logger.debug('I am the initiator, so making myself player 0')
+ self.add_hello_handler()
+ self.ordered_bus_names = [self.tube.get_unique_name()]
+ self.player_id = 0
+ self.buddies_panel.add_player(self.owner)
+ else:
+ _logger.debug('Hello, everyone! What did I miss?')
+ self.Hello()
+ self.entered = True
+
+ @signal(dbus_interface=IFACE, signature='')
+ def Hello(self):
+ """Request that this player's Welcome method is called to bring it
+ up to date with the game state.
+ """
+
+ @method(dbus_interface=IFACE, in_signature='aanas', out_signature='')
+ def Welcome(self, grid, bus_names):
+ """To be called on the incoming player by the other players to
+ inform them of the game state.
+
+ FIXME: nominate a "referee" (initially the initiator) responsible
+ for saying Welcome, elect a new referee when the current referee
+ leaves? This could also be used to make the protocol robust against
+ cheating/bugs
+ """
+ if self.player_id is None:
+ _logger.debug('Welcomed to the game. Player bus names are %r',
+ bus_names)
+ self.grid.grid = grid
+ dump_grid(grid)
+ self.ordered_bus_names = bus_names
+ self.player_id = bus_names.index(self.tube.get_unique_name())
+ # OK, now I'm synched with the game, I can welcome others
+ self.add_hello_handler()
+
+ buddy = self._get_buddy(self.tube.bus_name_to_handle[bus_names[0]])
+ self.buddies_panel.add_player(buddy)
+ buddy = self._get_buddy(self.tube.bus_name_to_handle[bus_names[1]])
+ self.buddies_panel.add_player(buddy)
+
+ if self.get_active_player() == self.player_id:
+ _logger.debug("It's my turn already!")
+ self.change_turn()
+
+ redraw(self.grid)
+ else:
+ _logger.debug("I've already been welcomed, doing nothing")
+
+ def add_hello_handler(self):
+ self.tube.add_signal_receiver(self.hello_cb, 'Hello', IFACE,
+ path=PATH, sender_keyword='sender')
+
+ @signal(dbus_interface=IFACE, signature='n')
+ def Insert(self, column):
+ """Signal that the local player has placed a disc."""
+ assert column >= 0
+ assert column < 7
+
+ def hello_cb(self, sender=None):
+ """Tell the newcomer what's going on."""
+ _logger.debug('Newcomer %s has joined', sender)
+ self.ordered_bus_names.append(sender)
+ if len(self.ordered_bus_names) == 2:
+ buddy = self._get_buddy(self.tube.bus_name_to_handle[sender])
+ self.buddies_panel.add_player(buddy)
+ _logger.debug('Bus names are now: %r', self.ordered_bus_names)
+ _logger.debug('Welcoming newcomer and sending them the game state')
+ self.tube.get_object(sender, PATH).Welcome(self.grid.grid,
+ self.ordered_bus_names,
+ dbus_interface=IFACE)
+ if (self.player_id == 0 and len(self.ordered_bus_names) == 2):
+ _logger.debug("This is my game and an opponent has joined. "
+ "I go first")
+ self.change_turn()
+
+ def insert_cb(self, column, sender=None):
+ # Someone placed a disc
+ handle = self.tube.bus_name_to_handle[sender]
+ _logger.debug('Insert(%d) from %s', column, sender)
+
+ if self.tube.self_handle == handle:
+ _logger.debug('Ignoring Insert signal from myself: %d', column)
+ return
+
+ try:
+ winner = self.grid.insert(column, self.get_active_player())
+ except ValueError:
+ return
+
+ dump_grid(self.grid.grid)
+
+ if winner is not None:
+ _logger.debug('Player with handle %d wins', handle)
+ self.info_panel.show(_('The other player wins!'))
+ redraw(self.grid)
+ return
+
+ self.change_turn()
+
+ def change_turn(self):
+ try:
+ bus_name = self.ordered_bus_names[self.get_active_player()]
+ buddy = self._get_buddy(self.tube.bus_name_to_handle[bus_name])
+ self.buddies_panel.set_is_playing(buddy)
+ except:
+ _logger.error('argh!', exc_info=1)
+ raise
+
+ if self.get_active_player() == self.player_id:
+ _logger.debug('It\'s my turn now')
+ self.info_panel.show(_('Your turn'))
+ self.grid.selected_column = 3
+ self.activity.grab_focus()
+ else:
+ _logger.debug('It\'s not my turn')
+ self.grid.selected_column = None
+
+ redraw(self.grid)
+
+ def get_active_player(self):
+ count = {}
+
+ for row in self.grid.grid:
+ for player in row:
+ if player > -1:
+ count[player] = count.get(player, 0) + 1
+
+ if count.get(0, 0) > count.get(1, 0):
+ return 1
+ else:
+ return 0
+
+ def key_press_event(self, widget, event):
+ if self.grid.selected_column is None:
+ _logger.debug('Ignoring keypress - not my turn')
+ return
+
+ _logger.debug('Keypress: keyval %s', event.keyval)
+
+ if event.keyval in (gtk.keysyms.Left,):
+ _logger.debug('<--')
+ if self.grid.selected_column > 0:
+ self.grid.selected_column -= 1
+ redraw(self.grid)
+ elif event.keyval in (gtk.keysyms.Right,):
+ _logger.debug('-->')
+ if self.grid.selected_column < 6:
+ self.grid.selected_column += 1
+ redraw(self.grid)
+ elif event.keyval in (gtk.keysyms.Down, gtk.keysyms.space):
+ _logger.debug('v')
+ self.insert_requested_cb(self.grid, self.grid.selected_column)
+
+ def insert_requested_cb(self, grid, col):
+ winner = grid.insert(col, self.player_id)
+ if winner == -1:
+ return
+
+ _logger.debug('Inserting at %d', col)
+ dump_grid(grid.grid)
+ redraw(grid)
+ self.Insert(col)
+
+ self.change_turn()
+
+ if winner is not None:
+ _logger.debug("I win")
+ self.info_panel.show(_('You win!'))
+ else:
+ self.info_panel.show(_('Other player\'s turn'))
diff --git a/game.pyc b/game.pyc
new file mode 100644
index 0000000..a098753
--- /dev/null
+++ b/game.pyc
Binary files differ
diff --git a/images/BsTurn.gif b/images/BsTurn.gif
new file mode 100644
index 0000000..52edae7
--- /dev/null
+++ b/images/BsTurn.gif
Binary files differ
diff --git a/images/README b/images/README
new file mode 100644
index 0000000..cca19e0
--- /dev/null
+++ b/images/README
@@ -0,0 +1,7 @@
+OLPC PlayGo Activity image library
+
+These images are from Ulrich Goertz's (u@g0ertz.de) uliGo 0.3
+
+They are published under the GNU GENERAL PUBLIC LICENSE Version 2, June 1991.
+
+(C) Ulrich Goertz (u@g0ertz.de), 2001-2003. \ No newline at end of file
diff --git a/images/WsTurn.gif b/images/WsTurn.gif
new file mode 100644
index 0000000..ab00f50
--- /dev/null
+++ b/images/WsTurn.gif
Binary files differ
diff --git a/images/b.gif b/images/b.gif
new file mode 100644
index 0000000..e56bc98
--- /dev/null
+++ b/images/b.gif
Binary files differ
diff --git a/images/black.gif b/images/black.gif
new file mode 100644
index 0000000..1719614
--- /dev/null
+++ b/images/black.gif
Binary files differ
diff --git a/images/board.gif b/images/board.gif
new file mode 100644
index 0000000..c253991
--- /dev/null
+++ b/images/board.gif
Binary files differ
diff --git a/images/bw.gif b/images/bw.gif
new file mode 100644
index 0000000..eb069a0
--- /dev/null
+++ b/images/bw.gif
Binary files differ
diff --git a/images/w.gif b/images/w.gif
new file mode 100644
index 0000000..1374cba
--- /dev/null
+++ b/images/w.gif
Binary files differ
diff --git a/images/white.gif b/images/white.gif
new file mode 100644
index 0000000..03e3bf7
--- /dev/null
+++ b/images/white.gif
Binary files differ
diff --git a/infopanel.py b/infopanel.py
new file mode 100755
index 0000000..e608889
--- /dev/null
+++ b/infopanel.py
@@ -0,0 +1,20 @@
+import hippo
+import pango
+from sugar.graphics import style
+
+class InfoPanel(hippo.CanvasBox):
+ def __init__(self):
+ hippo.CanvasBox.__init__(self, spacing=4, padding=5,
+ orientation=hippo.ORIENTATION_VERTICAL)
+ self.status_box = hippo.CanvasBox(spacing=4, padding=5,
+ orientation=hippo.ORIENTATION_VERTICAL)
+ self.append(self.status_box)
+
+ def show(self, text):
+ textwidget = hippo.CanvasText(text=text,
+ font_desc=pango.FontDescription('Sans 16'),
+ color=style.COLOR_WHITE.get_int(),
+ xalign=hippo.ALIGNMENT_CENTER)
+ self.status_box.remove_all()
+ self.status_box.append(textwidget)
+
diff --git a/infopanel.pyc b/infopanel.pyc
new file mode 100644
index 0000000..003f43e
--- /dev/null
+++ b/infopanel.pyc
Binary files differ
diff --git a/locale/ar/LC_MESSAGES/org.laptop.Connect.mo b/locale/ar/LC_MESSAGES/org.laptop.Connect.mo
new file mode 100644
index 0000000..24d15c3
--- /dev/null
+++ b/locale/ar/LC_MESSAGES/org.laptop.Connect.mo
Binary files differ
diff --git a/locale/ar/LC_MESSAGES/org.laptop.Playg.mo b/locale/ar/LC_MESSAGES/org.laptop.Playg.mo
new file mode 100644
index 0000000..24d15c3
--- /dev/null
+++ b/locale/ar/LC_MESSAGES/org.laptop.Playg.mo
Binary files differ
diff --git a/locale/ar/activity.linfo b/locale/ar/activity.linfo
new file mode 100644
index 0000000..97bb718
--- /dev/null
+++ b/locale/ar/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = PlayGo
diff --git a/locale/de/LC_MESSAGES/org.laptop.Connect.mo b/locale/de/LC_MESSAGES/org.laptop.Connect.mo
new file mode 100644
index 0000000..79cbe9f
--- /dev/null
+++ b/locale/de/LC_MESSAGES/org.laptop.Connect.mo
Binary files differ
diff --git a/locale/de/LC_MESSAGES/org.laptop.Playg.mo b/locale/de/LC_MESSAGES/org.laptop.Playg.mo
new file mode 100644
index 0000000..79cbe9f
--- /dev/null
+++ b/locale/de/LC_MESSAGES/org.laptop.Playg.mo
Binary files differ
diff --git a/locale/de/activity.linfo b/locale/de/activity.linfo
new file mode 100644
index 0000000..97bb718
--- /dev/null
+++ b/locale/de/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = PlayGo
diff --git a/locale/el/LC_MESSAGES/org.laptop.Connect.mo b/locale/el/LC_MESSAGES/org.laptop.Connect.mo
new file mode 100644
index 0000000..6493eb9
--- /dev/null
+++ b/locale/el/LC_MESSAGES/org.laptop.Connect.mo
Binary files differ
diff --git a/locale/el/LC_MESSAGES/org.laptop.Playg.mo b/locale/el/LC_MESSAGES/org.laptop.Playg.mo
new file mode 100644
index 0000000..6493eb9
--- /dev/null
+++ b/locale/el/LC_MESSAGES/org.laptop.Playg.mo
Binary files differ
diff --git a/locale/el/activity.linfo b/locale/el/activity.linfo
new file mode 100644
index 0000000..97bb718
--- /dev/null
+++ b/locale/el/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = PlayGo
diff --git a/locale/es/LC_MESSAGES/org.laptop.Connect.mo b/locale/es/LC_MESSAGES/org.laptop.Connect.mo
new file mode 100644
index 0000000..a0fae11
--- /dev/null
+++ b/locale/es/LC_MESSAGES/org.laptop.Connect.mo
Binary files differ
diff --git a/locale/es/LC_MESSAGES/org.laptop.Playg.mo b/locale/es/LC_MESSAGES/org.laptop.Playg.mo
new file mode 100644
index 0000000..a0fae11
--- /dev/null
+++ b/locale/es/LC_MESSAGES/org.laptop.Playg.mo
Binary files differ
diff --git a/locale/es/activity.linfo b/locale/es/activity.linfo
new file mode 100644
index 0000000..97bb718
--- /dev/null
+++ b/locale/es/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = PlayGo
diff --git a/po/Connect.pot b/po/Connect.pot
new file mode 100644
index 0000000..562cb13
--- /dev/null
+++ b/po/Connect.pot
@@ -0,0 +1,49 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-10-31 13:04+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: activity/activity.info:2
+msgid "Go"
+msgstr ""
+
+#: activity.py:63
+msgid "To play the game of Go, share or invite someone."
+msgstr ""
+
+#: activity.py:133
+msgid "Waiting for another player to join."
+msgstr ""
+
+#: activity.py:164
+msgid "Joined a game. Waiting for my turn..."
+msgstr ""
+
+#: game.py:183
+msgid "The other player wins!"
+msgstr ""
+
+#: game.py:200
+msgid "Your turn"
+msgstr ""
+
+#: game.py:257
+msgid "You win!"
+msgstr ""
+
+#: game.py:259
+msgid "Other player's turn"
+msgstr ""
diff --git a/po/ar.po b/po/ar.po
new file mode 100644
index 0000000..ace3d98
--- /dev/null
+++ b/po/ar.po
@@ -0,0 +1,63 @@
+# translation of Connect.master.po to Arabic
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Khaled Hosny <khaledhosny@eglug.org>, 2007.
+msgid ""
+msgstr ""
+"Project-Id-Version: Connect.master\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-10-31 13:04+0200\n"
+"PO-Revision-Date: 2007-08-16 01:03+0300\n"
+"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
+"Language-Team: Arabic <doc@arabeyes.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
+"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
+"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? "
+"3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
+
+#: activity/activity.info:2
+#, fuzzy
+msgid "Connect"
+msgstr "نشاط التوصيل"
+
+#: activity.py:63
+msgid "To play, share or invite someone."
+msgstr ""
+
+#: activity.py:133
+msgid "Waiting for another player to join."
+msgstr "بانتظار انضمام لاعب آخر."
+
+#: activity.py:164
+msgid "Joined a game. Waiting for my turn..."
+msgstr "انضممت للعبة. أنتظر دوري..."
+
+#: game.py:183
+msgid "The other player wins!"
+msgstr "فاز اللاعب الآخر!"
+
+#: game.py:200
+msgid "Your turn"
+msgstr "دورك"
+
+#: game.py:257
+msgid "You win!"
+msgstr "فزت!"
+
+#: game.py:259
+msgid "Other player's turn"
+msgstr "دور اللاعب الآخر"
+
+#~ msgid "Connect Activity"
+#~ msgstr "نشاط التوصيل"
+
+#~ msgid "Offline"
+#~ msgstr "بدون اتصال"
+
+#~ msgid "To play, share!"
+#~ msgstr "لتلعب، شارِك!"
diff --git a/po/de.po b/po/de.po
new file mode 100644
index 0000000..09988ac
--- /dev/null
+++ b/po/de.po
@@ -0,0 +1,58 @@
+# This file is distributed under the same license as the PACKAGE package.
+# Fabian Affolter <fab@fedoraproject.org>, 2007.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: connect\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-10-31 13:04+0200\n"
+"PO-Revision-Date: 2007-07-30 10:44+0100\n"
+"Last-Translator: Fabian Affolter <fab@fedoraproject.org>\n"
+"Language-Team: German <fedora-trans-de@redhat.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: German\n"
+
+#: activity/activity.info:2
+#, fuzzy
+msgid "Connect"
+msgstr "Verbindungs-Aktivität"
+
+#: activity.py:63
+msgid "To play, share or invite someone."
+msgstr ""
+
+#: activity.py:133
+msgid "Waiting for another player to join."
+msgstr "Warte auf andere Spieleum beizutreten."
+
+#: activity.py:164
+msgid "Joined a game. Waiting for my turn..."
+msgstr "Einem Spiel begetreten. Warte auf meinen Zug..."
+
+#: game.py:183
+msgid "The other player wins!"
+msgstr "Der andere Spieler gewinnt!"
+
+#: game.py:200
+msgid "Your turn"
+msgstr "Dein Zug"
+
+#: game.py:257
+msgid "You win!"
+msgstr "Du gewinnst!"
+
+#: game.py:259
+msgid "Other player's turn"
+msgstr "Der andere Spieler ist am Zug"
+
+#~ msgid "Connect Activity"
+#~ msgstr "Verbindungs-Aktivität"
+
+#~ msgid "Offline"
+#~ msgstr "Offline"
+
+#~ msgid "To play, share!"
+#~ msgstr "Zum Spielen, teile!"
diff --git a/po/el.po b/po/el.po
new file mode 100644
index 0000000..a15c023
--- /dev/null
+++ b/po/el.po
@@ -0,0 +1,58 @@
+# Greek translation of Connect activity.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Simos Xenitellis <simos.lists@googlemail.com>, 2007.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Connect activity\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-10-31 13:04+0200\n"
+"PO-Revision-Date: 2007-08-20 17:08+0100\n"
+"Last-Translator: Simos Xenitellis <simos.lists@googlemail.com>\n"
+"Language-Team: Greek <olpc-l10n-el@googlegroups.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: activity/activity.info:2
+#, fuzzy
+msgid "Connect"
+msgstr "Δραστηριότητα Συνδεθείτε"
+
+#: activity.py:63
+msgid "To play, share or invite someone."
+msgstr ""
+
+#: activity.py:133
+msgid "Waiting for another player to join."
+msgstr "Σε αναμονή για τη σύνδεση άλλου παίκτη."
+
+#: activity.py:164
+msgid "Joined a game. Waiting for my turn..."
+msgstr "Σε σύνδεση σε παιχνίδι. Αναμένουμε για τη σειρά μας..."
+
+#: game.py:183
+msgid "The other player wins!"
+msgstr "Η άλλη πλευρά κερδίζει!"
+
+#: game.py:200
+msgid "Your turn"
+msgstr "Η σειρά σου"
+
+#: game.py:257
+msgid "You win!"
+msgstr "Κέρδισες!"
+
+#: game.py:259
+msgid "Other player's turn"
+msgstr "Η σειρά της άλλης πλευράς"
+
+#~ msgid "Connect Activity"
+#~ msgstr "Δραστηριότητα Συνδεθείτε"
+
+#~ msgid "Offline"
+#~ msgstr "Εκτός σύνδεσης"
+
+#~ msgid "To play, share!"
+#~ msgstr "Για να παίξετε, να μοιραστείτε!"
diff --git a/po/es.po b/po/es.po
new file mode 100644
index 0000000..eb6fcea
--- /dev/null
+++ b/po/es.po
@@ -0,0 +1,58 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Actividad Conectar\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-10-31 13:04+0200\n"
+"PO-Revision-Date: 2007-09-18 09:24-0300\n"
+"Last-Translator: Domingo Becker <domingobecker@gmail.com>\n"
+"Language-Team: Fedora Spanish <fedora-trans-es@redhat.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Spanish\n"
+
+#: activity/activity.info:2
+msgid "Connect"
+msgstr "Conectar"
+
+#: activity.py:63
+msgid "To play, share or invite someone."
+msgstr "Para jugar, compartir o invitar a alguien."
+
+#: activity.py:133
+msgid "Waiting for another player to join."
+msgstr "Esperando que otro jugador se conecte."
+
+#: activity.py:164
+msgid "Joined a game. Waiting for my turn..."
+msgstr "Jugando. Esperando mi turno..."
+
+#: game.py:183
+msgid "The other player wins!"
+msgstr "El otro jugador ganó!"
+
+#: game.py:200
+msgid "Your turn"
+msgstr "Tu turno"
+
+#: game.py:257
+msgid "You win!"
+msgstr "Ganaste!"
+
+#: game.py:259
+msgid "Other player's turn"
+msgstr "Turno del otro jugador"
+
+#~ msgid "Connect Activity"
+#~ msgstr "Actividad Conectar"
+
+#~ msgid "Offline"
+#~ msgstr "Fuera de línea"
+
+#~ msgid "To play, share!"
+#~ msgstr "Para jugar, comparte!"
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..d07fb3d
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2006, 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 sugar.activity import bundlebuilder
+
+if __name__ == "__main__":
+ bundlebuilder.start('PlayGo')
diff --git a/widgettest.py b/widgettest.py
new file mode 100755
index 0000000..7803dab
--- /dev/null
+++ b/widgettest.py
@@ -0,0 +1,62 @@
+
+import gtk
+import random
+
+import game
+import boardwidget
+
+board = game.GoBoard( 19 )
+
+def redraw(grid):
+ """Utility function to force a redraw of a Gtk widget."""
+ grid.window.invalidate_rect(grid.get_allocation(), True)
+
+def key_press_cb(window, event, grid, player):
+
+ key = gtk.gdk.keyval_name(event.keyval)
+
+ if key in ('Left',):
+ for x in range( 19 ):
+ board.setPoint(x, 3, 'Black' )
+ redraw(grid)
+
+ elif key in ('Right',):
+ for x in range( 19 ):
+ board.setPoint(x, 3, 'Empty' )
+ redraw(grid)
+
+ elif key in ( 'r', ):
+ for x in range( 19 ):
+ for y in range( 19 ):
+ board.setPointi(x, y, random.randint( 0, 3 ) )
+
+ redraw(grid)
+
+ elif key in ( 'c', ):
+ board.clear()
+ redraw(grid)
+
+ elif gtk.gdk.keyval_name(event.keyval) in ('Escape', 'q'):
+ gtk.main_quit()
+
+def main():
+
+ for x in range( 19 ):
+ board.setPoint(x, 0, 'White' )
+
+ grid = boardwidget.BoardWidget( board )
+
+ window = gtk.Window()
+ window.connect('destroy', gtk.main_quit)
+ window.connect('key-press-event', key_press_cb, grid, [1])
+ window.add(grid)
+ window.show_all()
+
+ try:
+ gtk.main()
+ except KeyboardInterrupt:
+ pass
+
+if __name__ == '__main__':
+ main()
+