diff options
Diffstat (limited to 'Dimensions.py')
-rw-r--r-- | Dimensions.py | 832 |
1 files changed, 832 insertions, 0 deletions
diff --git a/Dimensions.py b/Dimensions.py new file mode 100644 index 0000000..99f8f0e --- /dev/null +++ b/Dimensions.py @@ -0,0 +1,832 @@ +#Copyright (c) 2009-13 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 + + +from gi.repository import Gtk +from gi.repository import Gdk +from gi.repository import GObject + +from sugar3.activity import activity +from sugar3 import profile +from sugar3.graphics.toolbarbox import ToolbarBox +from sugar3.activity.widgets import ActivityToolbarButton +from sugar3.activity.widgets import StopButton +from sugar3.graphics.toolbarbox import ToolbarButton +from sugar3.graphics.alert import NotifyAlert +from sugar3.graphics import style +from sugar3.datastore import datastore + +import telepathy +from dbus.service import signal +from dbus.gobject_service import ExportedGObject +from sugar3.presence import presenceservice +from sugar3.presence.tubeconn import TubeConnection + +from gettext import gettext as _ +import os.path +import logging +_logger = logging.getLogger('dimensions-activity') +import json +from json import load as jload +from json import dump as jdump + +from StringIO import StringIO + +from toolbar_utils import radio_factory, button_factory, label_factory, \ + spin_factory, separator_factory + +from constants import DECKSIZE, PRODUCT, HASH, ROMAN, WORD, CHINESE, MAYAN, \ + INCAN, DOTS, STAR, DICE, LINES, DEAL +from helpbutton import HelpButton, add_section, add_paragraph, help_windows, \ + help_buttons +from game import Game + + +help_palettes = {} + +BEGINNER = 0 +INTERMEDIATE = 1 +EXPERT = 2 +LEVEL_LABELS = [_('beginner'),_('intermediate'), _('expert')] +LEVEL_DECKSIZE = [DECKSIZE / 3, DECKSIZE, DECKSIZE / 9] + +NUMBER_O_BUTTONS = {} +NUMBER_C_BUTTONS = {} +LEVEL_BUTTONS = {} + +SERVICE = 'org.sugarlabs.Dimensions' +IFACE = SERVICE +PATH = '/org/augarlabs/Dimensions' + +PROMPT_DICT = {'pattern': _('New pattern game'), + 'number': _('New number game'), + 'word': _('New word game'), + 'custom': _('Import custom cards')} + + +class Dimensions(activity.Activity): + ''' Dimension matching game ''' + def __init__(self, handle): + ''' Initialize the Sugar activity ''' + super(Dimensions, self).__init__(handle) + self.ready_to_play = False + self._prompt = '' + self._read_journal_data() + self._setup_toolbars() + canvas = self._setup_canvas() + self._setup_presence_service() + + if not hasattr(self, '_saved_state'): + self._saved_state = None + self.vmw.new_game(self._saved_state, self._deck_index) + self.ready_to_play = True + if self._saved_state == None: + # Launch animated help + self.vmw.help_animation() + + if self._editing_word_list: + self.vmw.editing_word_list = True + self.vmw.edit_word_list() + elif self._editing_custom_cards: + self.vmw.editing_custom_cards = True + self.vmw.edit_custom_card() + + def _select_game_cb(self, button, card_type): + ''' Choose which game we are playing. ''' + if self.vmw.joiner(): # joiner cannot change level + return + self.vmw.card_type = card_type + self._prompt = PROMPT_DICT[card_type] + self._load_new_game(card_type) + + def _load_new_game(self, card_type=None): + if not self.ready_to_play: + return + self._notify_new_game(self._prompt) + # a brief pause to give alert time to load + timeout = GObject.timeout_add(200, self._new_game, card_type) + + def _new_game(self, card_type): + if card_type == 'custom' and self.vmw.custom_paths[0] is None: + self.image_import_cb() + else: + self.vmw.new_game() + + def _robot_cb(self, button=None): + ''' Toggle robot assist on/off ''' + if self.vmw.robot: + self.vmw.robot = False + self.robot_button.set_tooltip(_('Play with the computer.')) + self.robot_button.set_icon('robot-off') + elif not self.vmw.editing_word_list: + self.vmw.robot = True + self.robot_button.set_tooltip( + _('Stop playing with the computer.')) + self.robot_button.set_icon('robot-on') + + def _level_cb(self, button, level): + ''' Cycle between levels ''' + if self.vmw.joiner(): # joiner cannot change level + return + self.vmw.level = level + self.level_label.set_text(self.calc_level_label(self.vmw.low_score, + self.vmw.level)) + self._load_new_game() + + def calc_level_label(self, low_score, play_level): + ''' Show the score. ''' + if low_score[play_level] == -1: + return LEVEL_LABELS[play_level] + else: + return '%s (%d:%02d)' % \ + (LEVEL_LABELS[play_level], + int(low_score[play_level] / 60), + int(low_score[play_level] % 60)) + + def image_import_cb(self, button=None): + ''' Import custom cards from the Journal ''' + self.vmw.editing_custom_cards = True + self.vmw.editing_word_list = False + self.vmw.edit_custom_card() + if self.vmw.robot: + self._robot_cb(button) + + def _number_card_O_cb(self, button, numberO): + ''' Choose between O-card list for numbers game. ''' + if self.vmw.joiner(): # joiner cannot change decks + return + self.vmw.numberO = numberO + self.vmw.card_type = 'number' + self._load_new_game() + + def _number_card_C_cb(self, button, numberC): + ''' Choose between C-card list for numbers game. ''' + if self.vmw.joiner(): # joiner cannot change decks + return + self.vmw.numberC = numberC + self.vmw.card_type = 'number' + self._load_new_game() + + def _robot_time_spin_cb(self, button): + ''' Set delay for robot. ''' + self.vmw.robot_time = self._robot_time_spin.get_value_as_int() + return + + def _edit_words_cb(self, button): + ''' Edit the word list. ''' + self.vmw.editing_word_list = True + self.vmw.editing_custom_cards = False + self.vmw.edit_word_list() + if self.vmw.robot: + self._robot_cb(button) + + def _read_metadata(self, keyword, default_value): + ''' If the keyword is found, return stored value ''' + if keyword in self.metadata: + return(self.metadata[keyword]) + else: + return(default_value) + + def _read_journal_data(self): + ''' There may be data from a previous instance. ''' + self._play_level = int(self._read_metadata('play_level', 2)) + self._robot_time = int(self._read_metadata('robot_time', 60)) + self._card_type = self._read_metadata('cardtype', 'pattern') + self._low_score = [int(self._read_metadata('low_score_beginner', -1)), + int(self._read_metadata('low_score_intermediate', + -1)), + int(self._read_metadata('low_score_expert', -1))] + + self._all_scores = self._data_loader( + self._read_metadata('all_scores', '[]')) + self._numberO = int(self._read_metadata('numberO', PRODUCT)) + self._numberC = int(self._read_metadata('numberC', HASH)) + self._matches = int(self._read_metadata('matches', 0)) + self._robot_matches = int(self._read_metadata('robot_matches', 0)) + self._total_time = int(self._read_metadata('total_time', 0)) + self._deck_index = int(self._read_metadata('deck_index', 0)) + self._word_lists = [[self._read_metadata('mouse', _('mouse')), + self._read_metadata('cat', _('cat')), + self._read_metadata('dog', _('dog'))], + [self._read_metadata('cheese', _('cheese')), + self._read_metadata('apple', _('apple')), + self._read_metadata('bread', _('bread'))], + [self._read_metadata('moon', _('moon')), + self._read_metadata('sun', _('sun')), + self._read_metadata('earth', _('earth'))]] + self._editing_word_list = bool(int(self._read_metadata( + 'editing_word_list', 0))) + self._editing_custom_cards = bool(int(self._read_metadata( + 'editing_custom_cards', 0))) + if self._card_type == 'custom': + self._custom_object = self._read_metadata('custom_object', None) + if self._custom_object == None: + self._card_type = 'pattern' + self._custom_jobject = [] + for i in range(9): + self._custom_jobject.append(self._read_metadata( + 'custom_' + str(i), None)) + + def _write_scores_to_clipboard(self, button=None): + ''' SimpleGraph will plot the cululative results ''' + jscores = '' + for i, s in enumerate(self.vmw.all_scores): + jscores += '%s: %s\n' % (str(i + 1), s) + Gtk.Clipboard().set_text(jscores) + + def _setup_toolbars(self): + ''' Setup the toolbars.. ''' + + games_toolbar = Gtk.Toolbar() + tools_toolbar = Gtk.Toolbar() + numbers_toolbar = Gtk.Toolbar() + toolbox = ToolbarBox() + + self.activity_toolbar_button = ActivityToolbarButton(self) + + toolbox.toolbar.insert(self.activity_toolbar_button, 0) + self.activity_toolbar_button.show() + + self.game_toolbar_button = ToolbarButton( + page=games_toolbar, + icon_name='new-game') + games_toolbar.show() + toolbox.toolbar.insert(self.game_toolbar_button, -1) + self.game_toolbar_button.show() + + self.numbers_toolbar_button = ToolbarButton( + page=numbers_toolbar, + icon_name='number-tools') + # numbers_toolbar.show() + # toolbox.toolbar.insert(self.numbers_toolbar_button, -1) + # self.numbers_toolbar_button.show() + + self.tools_toolbar_button = ToolbarButton( + page=tools_toolbar, + icon_name='view-source') + tools_toolbar.show() + toolbox.toolbar.insert(self.tools_toolbar_button, -1) + self.tools_toolbar_button.show() + + self._set_labels(toolbox.toolbar) + separator_factory(toolbox.toolbar, True, False) + + help_button = HelpButton(self) + toolbox.toolbar.insert(help_button, -1) + help_button.show() + self._setup_toolbar_help() + + stop_button = StopButton(self) + stop_button.props.accelerator = '<Ctrl>q' + toolbox.toolbar.insert(stop_button, -1) + stop_button.show() + + export_scores = button_factory( + 'score-copy', self.activity_toolbar_button, + self._write_scores_to_clipboard, + tooltip=_('Export scores to clipboard')) + + self.set_toolbar_box(toolbox) + toolbox.show() + + self.game_toolbar_button.set_expanded(True) + + self.button_pattern = button_factory( + 'new-pattern-game', games_toolbar, self._select_game_cb, + cb_arg='pattern', tooltip=PROMPT_DICT['pattern']) + ''' + self.button_number = button_factory( + 'new-number-game', games_toolbar, self._select_game_cb, + cb_arg='number', tooltip=PROMPT_DICT['number']) + self.button_word = button_factory( + 'new-word-game', games_toolbar, self._select_game_cb, + cb_arg='word', tooltip=PROMPT_DICT['word']) + ''' + self.button_custom = button_factory( + 'no-custom-game', games_toolbar, self._select_game_cb, + cb_arg='custom', tooltip=PROMPT_DICT['custom']) + + self._set_extras(games_toolbar) + + ''' + self.words_tool_button = button_factory( + 'word-tools', tools_toolbar, self._edit_words_cb, + tooltip=_('Edit word lists.')) + ''' + + self.import_button = button_factory( + 'image-tools', tools_toolbar, self.image_import_cb, + tooltip=_('Import custom cards')) + + self.product_button = radio_factory( + 'product', + numbers_toolbar, + self._number_card_O_cb, + cb_arg=PRODUCT, + tooltip=_('product'), + group=None) + NUMBER_O_BUTTONS[PRODUCT] = self.product_button + self.roman_button = radio_factory( + 'roman', + numbers_toolbar, + self._number_card_O_cb, + cb_arg=ROMAN, + tooltip=_('Roman numerals'), + group=self.product_button) + NUMBER_O_BUTTONS[ROMAN] = self.roman_button + self.word_button = radio_factory( + 'word', + numbers_toolbar, + self._number_card_O_cb, + cb_arg=WORD, + tooltip=_('word'), + group=self.product_button) + NUMBER_O_BUTTONS[WORD] = self.word_button + self.chinese_button = radio_factory( + 'chinese', + numbers_toolbar, + self._number_card_O_cb, + cb_arg=CHINESE, + tooltip=_('Chinese'), + group=self.product_button) + NUMBER_O_BUTTONS[CHINESE] = self.chinese_button + self.mayan_button = radio_factory( + 'mayan', + numbers_toolbar, + self._number_card_O_cb, + cb_arg=MAYAN, + tooltip=_('Mayan'), + group=self.product_button) + NUMBER_O_BUTTONS[MAYAN] = self.mayan_button + self.incan_button = radio_factory( + 'incan', + numbers_toolbar, + self._number_card_O_cb, + cb_arg=INCAN, + tooltip=_('Quipu'), + group=self.product_button) + NUMBER_O_BUTTONS[INCAN] = self.incan_button + + separator_factory(numbers_toolbar, False, True) + + self.hash_button = radio_factory( + 'hash', + numbers_toolbar, + self._number_card_C_cb, + cb_arg=HASH, + tooltip=_('hash marks'), + group=None) + NUMBER_C_BUTTONS[HASH] = self.hash_button + self.dots_button = radio_factory( + 'dots', + numbers_toolbar, + self._number_card_C_cb, + cb_arg=DOTS, + tooltip=_('dots in a circle'), + group=self.hash_button) + NUMBER_C_BUTTONS[DOTS] = self.dots_button + self.star_button = radio_factory( + 'star', + numbers_toolbar, + self._number_card_C_cb, + cb_arg=STAR, + tooltip=_('points on a star'), + group=self.hash_button) + NUMBER_C_BUTTONS[STAR] = self.star_button + self.dice_button = radio_factory( + 'dice', + numbers_toolbar, + self._number_card_C_cb, + cb_arg=DICE, + tooltip=_('dice'), + group=self.hash_button) + NUMBER_C_BUTTONS[DICE] = self.dice_button + self.lines_button = radio_factory( + 'lines', + numbers_toolbar, + self._number_card_C_cb, + cb_arg=LINES, + tooltip=_('dots in a line'), + group=self.hash_button) + NUMBER_C_BUTTONS[LINES] = self.lines_button + + def _set_extras(self, toolbar): + separator_factory(toolbar, False, True) + self.robot_button = button_factory( + 'robot-off', toolbar, self._robot_cb, + tooltip=_('Play with the computer')) + + self._robot_time_spin = spin_factory(self._robot_time, 5, 180, + self._robot_time_spin_cb, + toolbar) + separator_factory(toolbar, False, True) + + self.beginner_button = radio_factory( + 'beginner', + toolbar, + self._level_cb, + cb_arg=BEGINNER, + tooltip=_('beginner'), + group=None) + LEVEL_BUTTONS[BEGINNER] = self.beginner_button + self.intermediate_button = radio_factory( + 'intermediate', + toolbar, + self._level_cb, + cb_arg=INTERMEDIATE, + tooltip=_('intermediate'), + group=self.beginner_button) + LEVEL_BUTTONS[INTERMEDIATE] = self.intermediate_button + self.expert_button = radio_factory( + 'expert', + toolbar, + self._level_cb, + cb_arg=EXPERT, + tooltip=_('expert'), + group=self.beginner_button) + LEVEL_BUTTONS[EXPERT] = self.expert_button + + self.level_label = label_factory(self.calc_level_label( + self._low_score, self._play_level), toolbar) + + def _set_labels(self, toolbar): + ''' Add labels to toolbar toolbar ''' + self.status_label = label_factory(_('Find a match.'), toolbar) + separator_factory(toolbar, False, True) + self.deck_label = label_factory( + '%d %s' % (LEVEL_DECKSIZE[self._play_level] - DEAL, _('cards')), + toolbar) + separator_factory(toolbar, False, True) + self.match_label = label_factory('%d %s' % (0, _('matches')), toolbar) + separator_factory(toolbar, False, True) + self.clock_label = label_factory('-', toolbar) + + def _setup_canvas(self): + ''' Create a canvas.. ''' + canvas = Gtk.DrawingArea() + canvas.set_size_request(Gdk.Screen.width(), Gdk.Screen.height()) + self.set_canvas(canvas) + canvas.show() + self.show_all() + + self.vmw = Game(canvas, self) + self.vmw.level = self._play_level + LEVEL_BUTTONS[self._play_level].set_active(True) + self.vmw.card_type = self._card_type + self.vmw.robot = False + self.vmw.robot_time = self._robot_time + self.vmw.low_score = self._low_score + self.vmw.all_scores = self._all_scores + self.vmw.numberO = self._numberO + NUMBER_O_BUTTONS[self._numberO].set_active(True) + self.vmw.numberC = self._numberC + NUMBER_C_BUTTONS[self._numberC].set_active(True) + self.vmw.matches = self._matches + self.vmw.robot_matches = self._robot_matches + self.vmw.total_time = self._total_time + self.vmw.buddies = [] + self.vmw.word_lists = self._word_lists + self.vmw.editing_word_list = self._editing_word_list + if hasattr(self, '_custom_object') and self._custom_object is not None: + self.vmw._find_custom_paths(datastore.get(self._custom_object)) + for i in range(9): + if hasattr(self, '_custom_jobject') and \ + self._custom_jobject[i] is not None: + self.vmw.custom_paths[i] = datastore.get( + self._custom_jobject[i]) + return canvas + + def write_file(self, file_path): + ''' Write data to the Journal. ''' + if hasattr(self, 'vmw'): + self.metadata['play_level'] = self.vmw.level + self.metadata['low_score_beginner'] = int(self.vmw.low_score[0]) + self.metadata['low_score_intermediate'] = int( + self.vmw.low_score[1]) + self.metadata['low_score_expert'] = int(self.vmw.low_score[2]) + self.metadata['all_scores'] = \ + self._data_dumper(self.vmw.all_scores) + self.metadata['robot_time'] = self.vmw.robot_time + self.metadata['numberO'] = self.vmw.numberO + self.metadata['numberC'] = self.vmw.numberC + self.metadata['cardtype'] = self.vmw.card_type + self.metadata['matches'] = self.vmw.matches + self.metadata['robot_matches'] = self.vmw.robot_matches + self.metadata['total_time'] = int(self.vmw.total_time) + self.metadata['deck_index'] = self.vmw.deck.index + self.metadata['mouse'] = self.vmw.word_lists[0][0] + self.metadata['cat'] = self.vmw.word_lists[0][1] + self.metadata['dog'] = self.vmw.word_lists[0][2] + self.metadata['cheese'] = self.vmw.word_lists[1][0] + self.metadata['apple'] = self.vmw.word_lists[1][1] + self.metadata['bread'] = self.vmw.word_lists[1][2] + self.metadata['moon'] = self.vmw.word_lists[2][0] + self.metadata['sun'] = self.vmw.word_lists[2][1] + self.metadata['earth'] = self.vmw.word_lists[2][2] + self.metadata['editing_word_list'] = self.vmw.editing_word_list + self.metadata['mime_type'] = 'application/x-visualmatch' + f = file(file_path, 'w') + f.write(self._dump()) + f.close() + else: + _logger.debug('Deferring saving to %s' % file_path) + + def _dump(self): + ''' Dump game data to the journal.''' + data = [] + for i in self.vmw.grid.grid: + if i is None or self.vmw.editing_word_list: + data.append(None) + else: + data.append(i.index) + for i in self.vmw.clicked: + if i.spr is None or self.vmw.deck.spr_to_card(i.spr) is None or \ + self.vmw.editing_word_list: + data.append(None) + else: + data.append(self.vmw.deck.spr_to_card(i.spr).index) + for i in self.vmw.deck.cards: + if i is None or self.vmw.editing_word_list: + data.append(None) + else: + data.append(i.index) + for i in self.vmw.match_list: + if self.vmw.deck.spr_to_card(i) is not None: + data.append(self.vmw.deck.spr_to_card(i).index) + for i in self.vmw.word_lists: + for j in i: + data.append(j) + return self._data_dumper(data) + + def _data_dumper(self, data): + io = StringIO() + jdump(data, io) + return io.getvalue() + + def read_file(self, file_path): + ''' Read data from the Journal. ''' + f = open(file_path, 'r') + self._load(f.read()) + f.close() + + def _load(self, data): + ''' Load game data from the journal. ''' + saved_state = self._data_loader(data) + if len(saved_state) > 0: + self._saved_state = saved_state + + def _data_loader(self, data): + io = StringIO(data) + return jload(io) + + def _notify_new_game(self, prompt): + ''' Called from New Game button since loading a new game can + be slooow!! ''' + alert = NotifyAlert(3) + alert.props.title = prompt + alert.props.msg = _('A new game is loading.') + + def _notification_alert_response_cb(alert, response_id, self): + self.remove_alert(alert) + + alert.connect('response', _notification_alert_response_cb, self) + self.add_alert(alert) + alert.show() + + def _new_help_box(self, name, button=None): + help_box = Gtk.VBox() + help_box.set_homogeneous(False) + help_palettes[name] = help_box + if button is not None: + help_buttons[name] = button + help_windows[name] = Gtk.ScrolledWindow() + help_windows[name].set_size_request( + int(Gdk.Screen.width() / 3), + Gdk.Screen.height() - style.GRID_CELL_SIZE * 3) + help_windows[name].set_policy(Gtk.PolicyType.NEVER, + Gtk.PolicyType.AUTOMATIC) + help_windows[name].add_with_viewport(help_palettes[name]) + help_palettes[name].show() + return help_box + + def _setup_toolbar_help(self): + ''' Set up a help palette for the main toolbars ''' + help_box = self._new_help_box('custom-toolbar', + self.tools_toolbar_button) + add_section(help_box, _('Tools'), icon='view-source') + add_section(help_box, _('Edit word lists.'), icon='word-tools') + add_section(help_box, _('Import image cards'), icon='image-tools') + + help_box = self._new_help_box('activity-toolbar', + self.activity_toolbar_button) + add_section(help_box, _('Dimensions'), icon='activity-dimensions') + add_paragraph(help_box, _('Export scores to clipboard'), icon='score-copy') + + help_box = self._new_help_box('game-toolbar', + self.game_toolbar_button) + add_section(help_box, _('Game'), icon='new-game') + add_paragraph(help_box, PROMPT_DICT['pattern'], icon='new-pattern-game') + add_paragraph(help_box, PROMPT_DICT['number'], icon='new-number-game') + add_paragraph(help_box, PROMPT_DICT['word'], icon='new-word-game') + add_paragraph(help_box, PROMPT_DICT['custom'], icon='new-custom-game') + add_paragraph(help_box, _('Play with the computer'), icon='robot-off') + add_paragraph(help_box, _('robot pause time')) + add_paragraph(help_box, _('beginner'), icon='beginner') + add_paragraph(help_box, _('intermediate'), icon='intermediate') + add_paragraph(help_box, _('expert'), icon='expert') + + help_box = self._new_help_box('numbers-toolbar', + self.numbers_toolbar_button) + add_section(help_box, _('Numbers'), icon='number-tools') + add_paragraph(help_box, _('product'), icon='product') + add_paragraph(help_box, _('Roman numerals'), icon='roman') + add_paragraph(help_box, _('word'), icon='word') + add_paragraph(help_box, _('Chinese'), icon='chinese') + add_paragraph(help_box, _('Mayan'), icon='mayan') + add_paragraph(help_box, _('Quipu'), icon='incan') + add_paragraph(help_box, _('hash marks'), icon='hash') + add_paragraph(help_box, _('dots in a circle'), icon='dots') + add_paragraph(help_box, _('points on a star'), icon='star') + add_paragraph(help_box, _('dice'), icon='dice') + add_paragraph(help_box, _('dots in a line'), icon='lines') + + help_box = self._new_help_box('main-toolbar') + add_section(help_box, _('Dimensions'), icon='activity-dimensions') + add_paragraph(help_box, _('Game'), icon='new-game') + add_paragraph(help_box, _('Numbers'), icon='number-tools') + add_paragraph(help_box, _('Tools'), icon='view-source') + add_paragraph(help_box, _('number of cards remaining in deck')) + add_paragraph(help_box, _('number of matches')) + add_paragraph(help_box, _('elapsed time')) + + 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.vmw.buddies.append(self.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...''' + if self._shared_activity is None: + _logger.error('Failed to share or join activity ... \ + _shared_activity is null in _shared_cb()') + return + + self.initiating = True + self.waiting_for_deck = False + _logger.debug('I am sharing...') + + 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) + + _logger.debug('This is my activity: making a tube...') + id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( + SERVICE, {}) + + def _joined_cb(self, activity): + ''' ...or join an exisiting share. ''' + if self._shared_activity is None: + _logger.error('Failed to share or join activity ... \ + _shared_activity is null in _shared_cb()') + return + + self.initiating = False + _logger.debug('I joined a shared activity.') + + 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) + + _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.waiting_for_deck = 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.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) + + if self.waiting_for_deck: + self._send_event('j') + + def event_received_cb(self, text): + ''' Data is passed as tuples: cmd:text ''' + if text[0] == 'B': + e, card_index = text.split(':') + self.vmw.add_to_clicked( + self.vmw.deck.index_to_card(int(card_index)).spr) + elif text[0] == 'r': + self.vmw.clean_up_match() + elif text[0] == 'R': + self.vmw.clean_up_no_match(None) + elif text[0] == 'S': + e, card_index = text.split(':') + i = int(card_index) + self.vmw.process_click(self.vmw.clicked[i].spr) + self.vmw.process_selection(self.vmw.clicked[i].spr) + elif text[0] == 'j': + if self.initiating: # Only the sharer 'shares'. + self._send_event('P:' + str(self.vmw.level)) + self._send_event('X:' + str(self.vmw.deck.index)) + self._send_event('M:' + str(self.vmw.matches)) + self._send_event('C:' + self.vmw.card_type) + self._send_event('D:' + str(self._dump())) + elif text[0] == 'J': # Force a request for current state. + self._send_event('j') + self.waiting_for_deck = True + elif text[0] == 'C': + e, text = text.split(':') + self.vmw.card_type = text + elif text[0] == 'P': + e, text = text.split(':') + self.vmw.level = int(text) + self.level_label.set_text(self.calc_level_label( + self.vmw.low_score, self.vmw.level)) + LEVEL_BUTTONS[self.vmw.level].set_active(True) + elif text[0] == 'X': + e, text = text.split(':') + self.vmw.deck.index = int(text) + elif text[0] == 'M': + e, text = text.split(':') + self.vmw.matches = int(text) + elif text[0] == 'D': + if self.waiting_for_deck: + e, text = text.split(':') + self._load(text) + self.waiting_for_deck = False + self.vmw.new_game(self._saved_state, self.vmw.deck.index) + + 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 |