diff options
author | Walter Bender <walter.bender@gmail.com> | 2012-07-19 20:27:02 (GMT) |
---|---|---|
committer | Walter Bender <walter.bender@gmail.com> | 2012-07-19 20:27:02 (GMT) |
commit | c516ba2970cd757e049eb420f7c30b00464f1606 (patch) | |
tree | 19ad14b00044a5d839c4f37b707b272fa744095e /GNUChessActivity.py |
new project based on gnuchess
Diffstat (limited to 'GNUChessActivity.py')
-rw-r--r-- | GNUChessActivity.py | 469 |
1 files changed, 469 insertions, 0 deletions
diff --git a/GNUChessActivity.py b/GNUChessActivity.py new file mode 100644 index 0000000..a728091 --- /dev/null +++ b/GNUChessActivity.py @@ -0,0 +1,469 @@ +#Copyright (c) 2012 Walter Bender + +# 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 3 of the License, or +# (at your option) any later version. +# +# You should have received a copy of the GNU General Public License +# along with this library; if not, write to the Free Software +# Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA + + +import gtk + +from sugar.activity import activity +from sugar import profile +try: + from sugar.graphics.toolbarbox import ToolbarBox + _have_toolbox = True +except ImportError: + _have_toolbox = False + +if _have_toolbox: + from sugar.activity.widgets import ActivityToolbarButton + from sugar.activity.widgets import StopButton + from sugar.graphics.toolbarbox import ToolbarButton + +from toolbar_utils import button_factory, label_factory, separator_factory, \ + radio_factory, entry_factory +from utils import json_load, json_dump + +import telepathy +import dbus +from dbus.service import signal +from dbus.gobject_service import ExportedGObject +from sugar.presence import presenceservice +from sugar.presence.tubeconn import TubeConnection + +from gettext import gettext as _ + +from chess import Gnuchess + +import logging +_logger = logging.getLogger('gnuchess-activity') + + +SERVICE = 'org.sugarlabs.GNUChessActivity' +IFACE = SERVICE +PATH = '/org/augarlabs/GNUChessActivity' + + +class GNUChessActivity(activity.Activity): + """ Gnuchess interface from Sugar """ + + def __init__(self, handle): + """ Initialize the toolbars and the gnuchess """ + try: + super(GNUChessActivity, self).__init__(handle) + except dbus.exceptions.DBusException, e: + _logger.error(str(e)) + + self.nick = profile.get_nick_name() + self._reload_custom = False + if profile.get_color() is not None: + self.colors = profile.get_color().to_string().split(',') + else: + self.colors = ['#A0FFA0', '#FF8080'] + + self._setup_toolbars(_have_toolbox) + self._setup_dispatch_table() + + self.playing_white = True + self.mode = 'easy' + self.playing_robot = True + + # Create a canvas + canvas = gtk.DrawingArea() + canvas.set_size_request(gtk.gdk.screen_width(), \ + gtk.gdk.screen_height()) + self.set_canvas(canvas) + canvas.show() + self.show_all() + + self._gnuchess = Gnuchess(canvas, + parent=self, + path=activity.get_bundle_path(), + colors=self.colors) + self._setup_presence_service() + + if 'saved_game' in self.metadata: + self._restore() + else: + self._gnuchess.new_game() + + self._make_custom_toolbar() + if self._reload_custom: + self._custom_cb() + + def _setup_toolbars(self, have_toolbox): + """ Setup the toolbars. """ + + self.max_participants = 1 # No sharing to begin with + + self.edit_toolbar = gtk.Toolbar() + self.view_toolbar = gtk.Toolbar() + self.adjust_toolbar = gtk.Toolbar() + self.custom_toolbar = gtk.Toolbar() + if have_toolbox: + toolbox = ToolbarBox() + + # Activity toolbar + activity_button = ActivityToolbarButton(self) + toolbox.toolbar.insert(activity_button, 0) + activity_button.show() + + edit_toolbar_button = ToolbarButton( + label=_("Edit"), page=self.edit_toolbar, + icon_name='toolbar-edit') + self.edit_toolbar.show() + toolbox.toolbar.insert(edit_toolbar_button, -1) + edit_toolbar_button.show() + + view_toolbar_button = ToolbarButton( + label=_("View"), page=self.view_toolbar, + icon_name='toolbar-view') + self.view_toolbar.show() + toolbox.toolbar.insert(view_toolbar_button, -1) + view_toolbar_button.show() + + adjust_toolbar_button = ToolbarButton( + label=_('Adjust'), page=self.adjust_toolbar, + icon_name='preferences-system') + self.adjust_toolbar.show() + toolbox.toolbar.insert(adjust_toolbar_button, -1) + adjust_toolbar_button.show() + + custom_toolbar_button = ToolbarButton( + label=_("Custom"), page=self.custom_toolbar, + icon_name='view-source') + self.custom_toolbar.show() + toolbox.toolbar.insert(custom_toolbar_button, -1) + custom_toolbar_button.show() + + self.set_toolbar_box(toolbox) + toolbox.show() + self.toolbar = toolbox.toolbar + else: + # Use pre-0.86 toolbar design + toolbox = activity.ActivityToolbox(self) + self.set_toolbox(toolbox) + toolbox.add_toolbar(_('Edit'), self.edit_toolbar) + toolbox.add_toolbar(_('View'), self.view_toolbar) + toolbox.add_toolbar(_('Adjust'), self.adjust_toolbar) + toolbox.add_toolbar(_('Custom'), self.custom_toolbar) + toolbox.show() + toolbox.set_current_toolbar(1) + gnuchess_toolbar = gtk.Toolbar() + self.toolbar = gnuchess_toolbar + + button_factory('edit-copy', + self.edit_toolbar, + self._copy_cb, + tooltip=_('Copy'), + accelerator='<Ctrl>c') + + button_factory('edit-paste', + self.edit_toolbar, + self._paste_cb, + tooltip=_('Paste'), + accelerator='<Ctrl>v') + + if not _have_toolbox: + separator_factory(gnuchess_toolbar, False, True) + + button_factory('view-fullscreen', + self.view_toolbar, + self.do_fullscreen_cb, + tooltip=_('Fullscreen'), + accelerator='<Alt>Return') + + label_factory(self.view_toolbar, + _('White: ')) + self.white_entry = entry_factory('', + self.view_toolbar, + tooltip=_("White's move")) + + separator_factory(self.view_toolbar, False, False) + + label_factory(self.view_toolbar, + _('Black: ')) + self.black_entry = entry_factory('', + self.view_toolbar, + tooltip=_("Black's move")) + + play_white_button = radio_factory('white-rook', + self.adjust_toolbar, + self._play_white_cb, + group=None, + tooltip=_('Play White')) + + play_black_button = radio_factory('black-rook', + self.adjust_toolbar, + self._play_black_cb, + group=play_white_button, + tooltip=_('Play Black')) + + separator_factory(self.adjust_toolbar, False, True) + + easy_button = radio_factory('beginner', + self.adjust_toolbar, + self._easy_cb, + group=None, + tooltip=_('Beginner')) + + hard_button = radio_factory('expert', + self.adjust_toolbar, + self._hard_cb, + group=easy_button, + tooltip=_('Expert')) + + separator_factory(self.adjust_toolbar, False, True) + + robot_button = radio_factory('robot', + self.adjust_toolbar, + self._robot_cb, + group=None, + tooltip=_('Play against the computer')) + + human_button = radio_factory('human', + self.adjust_toolbar, + self._human_cb, + group=robot_button, + tooltip=_('Play against a person')) + + button_factory('new-game', + self.toolbar, + self._new_gnuchess_cb, + tooltip=_('New game')) + + button_factory('edit-undo', + self.toolbar, + self._undo_cb, + tooltip=_('Undo')) + + button_factory('edit-undo', + self.toolbar, + self._undo_cb, + tooltip=_('Hint')) + + separator_factory(self.toolbar, False, False) + self.status = label_factory(self.toolbar, '') + self.status.set_label(_("It is White's move.")) + + if _have_toolbox: + separator_factory(toolbox.toolbar, True, False) + stop_button = StopButton(self) + stop_button.props.accelerator = '<Ctrl>q' + toolbox.toolbar.insert(stop_button, -1) + stop_button.show() + + def do_fullscreen_cb(self, button): + ''' Hide the Sugar toolbars. ''' + self.fullscreen() + + def _copy_cb(self, *args): + return + + def _paste_cb(self, *args): + return + + def _undo_cb(self, *args): + self._gnuchess.undo() + + def _play_white_cb(self, *args): + self.playing_white = True + return + + def _play_black_cb(self, *args): + self.playing_white = False + return + + def _easy_cb(self, *args): + self.mode = 'easy' + return + + def _hard_cb(self, *args): + self.mode = 'hard' + return + + def _robot_cb(self, *args): + self.playing_robot = True + return + + def _human_cb(self, *args): + self.playing_robot = False + return + + def _make_custom_toolbar(self): + return + + def _new_gnuchess_cb(self, button=None): + ''' Start a new gnuchess. ''' + self._gnuchess.new_game() + + def _custom_cb(self, button=None): + return + + def write_file(self, file_path): + """ Write the grid status to the Journal """ + self.metadata['saved_game'] = json_dump(self._gnuchess.save_game()) + if self.playing_white: + self.metadata['playing_white'] = 'True' + else: + self.metadata['playing_white'] = 'False' + self.metadata['playing_mode'] = self.mode + if self.playing_robot: + self.metadata['playing_robot'] = 'True' + else: + self.metadata['playing_robot'] = 'False' + _logger.debug(json_dump(self._gnuchess.save_game())) + + def _restore(self): + """ Restore the gnuchess state from metadata """ + if 'playing_white' in self.metadata: + _logger.debug(self.metadata['playing_white']) + if self.metadata['playing_white'] == 'False': + self.playing_white = False + self._gnuchess.reskin() + if 'playing_mode' in self.metadata: + _logger.debug(self.metadata['playing_mode']) + self.playing_white = self.metadata['playing_mode'] + if 'playing_robot' in self.metadata: + _logger.debug(self.metadata['playing_robot']) + if self.metadata['playing_robot'] == 'False': + self.playing_robot = False + _logger.debug(self.metadata['saved_game']) + self._gnuchess.restore_game(json_load(self.metadata['saved_game'])) + return + + # Collaboration-related methods + + # FIXME: share mode is not set up properly + + def _setup_presence_service(self): + """ Setup the Presence Service. """ + self.pservice = presenceservice.get_instance() + self.initiating = None # sharing (True) or joining (False) + + owner = self.pservice.get_owner() + self.owner = owner + self._share = "" + self.connect('shared', self._shared_cb) + self.connect('joined', self._joined_cb) + + def _shared_cb(self, activity): + """ Either set up initial share...""" + self._new_tube_common(True) + + def _joined_cb(self, activity): + """ ...or join an exisiting share. """ + self._new_tube_common(False) + + def _new_tube_common(self, sharer): + """ Joining and sharing are mostly the same... """ + if self._shared_activity is None: + _logger.debug("Error: Failed to share or join activity ... \ + _shared_activity is null in _shared_cb()") + return + + self.initiating = sharer + self.waiting_for_hand = not sharer + + 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) + + if sharer: + _logger.debug('This is my activity: making a tube...') + id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( + SERVICE, {}) + else: + _logger.debug('I am joining an 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) + self._gnuchess.set_sharing(True) + + def _list_tubes_reply_cb(self, tubes): + """ Reply to a list request. """ + for tube_info in tubes: + self._new_tube_cb(*tube_info) + + def _list_tubes_error_cb(self, e): + """ Log errors. """ + _logger.debug('Error: ListTubes() failed: %s' % (e)) + + def _new_tube_cb(self, id, initiator, type, service, params, state): + """ Create a new tube. """ + _logger.debug('New tube: ID=%d initator=%d type=%d service=%s \ +params=%r state=%d' % (id, initiator, type, service, params, state)) + + if (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.chattube = ChatTube(tube_conn, self.initiating, \ + self.event_received_cb) + + def _setup_dispatch_table(self): + ''' Associate tokens with commands. ''' + self._processing_methods = { + 'n': [self._receive_new_gnuchess, 'get a new gnuchess board'], + } + + def event_received_cb(self, event_message): + ''' Data from a tube has arrived. ''' + if len(event_message) == 0: + return + try: + command, payload = event_message.split('|', 2) + except ValueError: + _logger.debug('Could not split event message %s' % (event_message)) + return + self._processing_methods[command][0](payload) + + def send_new_gnuchess(self): + ''' Send a new orientation, grid to all players ''' + self.send_event('n|%s' % (json_dump(self._gnuchess.save_game()))) + + def _receive_new_gnuchess(self, payload): + ''' Sharer can start a new gnuchess. ''' + self._gnuchess.restore_game(json_load(payload)) + + def send_event(self, entry): + """ Send event through the tube. """ + if hasattr(self, 'chattube') and self.chattube is not None: + self.chattube.SendText(entry) + + +class ChatTube(ExportedGObject): + """ Class for setting up tube for sharing """ + + def __init__(self, tube, is_initiator, stack_received_cb): + super(ChatTube, self).__init__(tube, PATH) + self.tube = tube + self.is_initiator = is_initiator # Are we sharing or joining activity? + self.stack_received_cb = stack_received_cb + self.stack = '' + + self.tube.add_signal_receiver(self.send_stack_cb, 'SendText', IFACE, + path=PATH, sender_keyword='sender') + + def send_stack_cb(self, text, sender=None): + if sender == self.tube.get_unique_name(): + return + self.stack = text + self.stack_received_cb(text) + + @signal(dbus_interface=IFACE, signature='s') + def SendText(self, text): + self.stack = text |