Ejemplo n.º 1
0
class ImplodeActivity(Activity):
    def __init__(self, handle):
        Activity.__init__(self, handle)

        self._joining_hide = False
        self._game = ImplodeGame()
        self._collab = CollabWrapper(self)
        self._collab.connect('message', self._message_cb)

        game_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        game_box.pack_start(self._game, True, True, 0)
        self._stuck_strip = _StuckStrip()

        self._configure_toolbars()

        self.set_canvas(game_box)

        # Show everything except the stuck strip.
        self.show_all()
        self._configure_cb()

        game_box.pack_end(self._stuck_strip,
                          expand=False,
                          fill=False,
                          padding=0)

        self._game.connect('show-stuck', self._show_stuck_cb)
        self._game.connect('piece-selected', self._piece_selected_cb)
        self._game.connect('undo-key-pressed', self._undo_key_pressed_cb)
        self._game.connect('redo-key-pressed', self._redo_key_pressed_cb)
        self._game.connect('new-key-pressed', self._new_key_pressed_cb)
        self._stuck_strip.connect('undo-clicked', self._stuck_undo_cb)
        game_box.connect('key-press-event', self._key_press_event_cb)

        self._game.grab_focus()

        last_game_path = self._get_last_game_path()
        if os.path.exists(last_game_path):
            self.read_file(last_game_path)

        self._collab.setup()

        # Hide the canvas when joining a shared activity
        if self.shared_activity:
            if not self.get_shared():
                self.get_canvas().hide()
                self.busy()
                self._joining_hide = True

    def _get_last_game_path(self):
        return os.path.join(self.get_activity_root(), 'data', 'last_game')

    def get_data(self):
        return self._game.get_game_state()

    def set_data(self, data):
        if not data['win_draw_flag']:
            self._game.set_game_state(data)
        # Ensure that the visual display matches the game state.
        self._levels_buttons[data['difficulty']].props.active = True
        # Release the cork
        if self._joining_hide:
            self.get_canvas().show()
            self.unbusy()

    def read_file(self, file_path):
        # Loads the game state from a file.
        f = open(file_path, 'r')
        file_data = json.loads(f.read())
        f.close()

        (file_type, version, game_data) = file_data
        if file_type == 'Implode save game' and version <= [1, 0]:
            self.set_data(game_data)

    def write_file(self, file_path):
        # Writes the game state to a file.
        data = self.get_data()
        file_data = ['Implode save game', [1, 0], data]
        last_game_path = self._get_last_game_path()
        for path in (file_path, last_game_path):
            f = open(path, 'w')
            f.write(json.dumps(file_data))
            f.close()

    def _show_stuck_cb(self, state, data=None):
        if self.shared_activity:
            return
        if self.metadata:
            share_scope = self.metadata.get('share-scope', SCOPE_PRIVATE)
            if share_scope != SCOPE_PRIVATE:
                return
        if data:
            self._stuck_strip.show_all()
        else:
            if self._stuck_strip.get_focus_child():
                self._game.grab_focus()
            self._stuck_strip.hide()

    def _stuck_undo_cb(self, state, data=None):
        self._game.undo_to_solvable_state()

    def _key_press_event_cb(self, source, event):
        # Make the game navigable by keypad controls.
        action = KEY_MAP.get(event.keyval, None)
        if action is None:
            return False
        if not self._stuck_strip.get_state_flags() & Gtk.AccelFlags.VISIBLE:
            return True
        if self._game.get_focus_child():
            if action == 'down':
                self._stuck_strip.button.grab_focus()
            return True
        elif self._stuck_strip.get_focus_child():
            if action == 'up':
                self._game.grab_focus()
            elif action == 'select':
                self._stuck_strip.button.activate()
            return True
        return True

    def _configure_toolbars(self):
        """Create, set, and show a toolbar box with an activity button, game
        controls, difficulty selector, help button, and stop button. All
        callbacks are locally defined."""

        self._seps = []

        toolbar_box = ToolbarBox()
        toolbar = toolbar_box.toolbar

        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        self._add_separator(toolbar)

        def add_button(icon_name, tooltip, callback):
            button = ToolButton(icon_name)
            toolbar.add(button)
            button.connect('clicked', callback)
            button.set_tooltip(tooltip)
            return button

        add_button('new-game', _("New"), self._new_game_cb)
        add_button('replay-game', _("Replay"), self._replay_game_cb)

        self._add_separator(toolbar)

        add_button('edit-undo', _("Undo"), self._undo_cb)
        add_button('edit-redo', _("Redo"), self._redo_cb)

        self._add_separator(toolbar)

        self._levels_buttons = []

        def add_level_button(icon_name, tooltip, numeric_level):
            if self._levels_buttons:
                button = RadioToolButton(icon_name=icon_name,
                                         group=self._levels_buttons[0])
            else:
                button = RadioToolButton(icon_name=icon_name)
            self._levels_buttons.append(button)
            toolbar.add(button)

            def callback(source):
                if source.get_active():
                    self._collab.post({'action': icon_name})
                    self._game.set_level(numeric_level)
                    self._game.new_game()

            button.connect('toggled', callback)
            button.set_tooltip(tooltip)

        add_level_button('easy-level', _("Easy"), 0)
        add_level_button('medium-level', _("Medium"), 1)
        add_level_button('hard-level', _("Hard"), 2)

        self._add_separator(toolbar)

        def _help_clicked_cb(button):
            help_window = _HelpWindow()
            help_window.set_transient_for(self.get_toplevel())
            help_window.show_all()

        help_button = add_button('toolbar-help', _("Help"), _help_clicked_cb)

        def _help_disable_cb(collab, buddy):
            if help_button.props.sensitive:
                help_button.props.sensitive = False

        self._collab.connect('buddy-joined', _help_disable_cb)

        self._add_expander(toolbar)

        stop_button = StopButton(self)
        toolbar_box.toolbar.insert(stop_button, -1)
        stop_button.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show()

        Gdk.Screen.get_default().connect('size-changed', self._configure_cb)

    def _add_separator(self, toolbar):
        self._seps.append(Gtk.SeparatorToolItem())
        toolbar.add(self._seps[-1])
        self._seps[-1].show()

    def _add_expander(self, toolbar, expand=True):
        """Insert a toolbar item which will expand to fill the available
        space."""
        self._seps.append(Gtk.SeparatorToolItem())
        self._seps[-1].props.draw = False
        self._seps[-1].set_expand(expand)
        toolbar.insert(self._seps[-1], -1)
        self._seps[-1].show()

    def _configure_cb(self, event=None):
        if Gdk.Screen.width() < Gdk.Screen.height():
            hide = True
        else:
            hide = False
        for sep in self._seps:
            if hide:
                sep.hide()
            else:
                sep.show()

    def _new_game_cb(self, button):
        self._game.reseed()
        self._collab.post({
            'action': 'new-game',
            'seed': self._game.get_seed()
        })
        self._game.new_game()

    def _replay_game_cb(self, button):
        self._collab.post({'action': 'replay-game'})
        self._game.replay_game()

    def _undo_cb(self, button):
        self._collab.post({'action': 'edit-undo'})
        self._game.undo()

    def _redo_cb(self, button):
        self._collab.post({'action': 'edit-redo'})
        self._game.redo()

    def _message_cb(self, collab, buddy, msg):
        action = msg.get('action')
        if action == 'new-game':
            self._game.set_seed(msg.get('seed'))
            self._game.new_game()
        elif action == 'replay-game':
            self._game.replay_game()
        elif action == 'edit-undo':
            self._game.undo()
        elif action == 'edit-redo':
            self._game.redo()
        elif action == 'easy-level':
            self._game.set_level(0)
            self._game.new_game()
        elif action == 'medium-level':
            self._game.set_level(1)
            self._game.new_game()
        elif action == 'hard-level':
            self._game.set_level(2)
            self._game.new_game()
        elif action == 'piece-selected':
            x = msg.get('x')
            y = msg.get('y')
            self._game.piece_selected(x, y)

    def _piece_selected_cb(self, game, x, y):
        self._collab.post({'action': 'piece-selected', 'x': x, 'y': y})

    def _undo_key_pressed_cb(self, game, dummy):
        self._collab.post({'action': 'edit-undo'})

    def _redo_key_pressed_cb(self, game, dummy):
        self._collab.post({'action': 'edit-redo'})

    def _new_key_pressed_cb(self, game, seed):
        self._collab.post({'action': 'new-game', 'seed': seed})
Ejemplo n.º 2
0
class FlipActivity(activity.Activity):
    """ Flip puzzle game """
    def __init__(self, handle):
        """ Initialize the toolbars and the game board """
        super(FlipActivity, self).__init__(handle)

        self.nick = profile.get_nick_name()
        if profile.get_color() is not None:
            self.colors = profile.get_color().to_string().split(',')
        else:
            self.colors = ['#A0FFA0', '#FF8080']

        self._setup_toolbars()
        self._setup_dispatch_table()

        # 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._game = Game(canvas, parent=self, colors=self.colors)
        self.connect('shared', self._shared_cb)
        self.connect('joined', self._joined_cb)

        self._collab = CollabWrapper(self)
        self._collab.connect('message', self._message_cb)
        self._collab.connect('joined', self._joined_cb)
        self._collab.setup()

        if 'dotlist' in self.metadata:
            self._restore()
        else:
            self._game.new_game()

    def _setup_toolbars(self):
        """ Setup the toolbars. """

        self.max_participants = 4

        toolbox = ToolbarBox()

        # Activity toolbar
        activity_button = ActivityToolbarButton(self)

        toolbox.toolbar.insert(activity_button, 0)
        activity_button.show()

        self.set_toolbar_box(toolbox)
        toolbox.show()
        self.toolbar = toolbox.toolbar

        self._new_game_button_h = button_factory('new-game',
                                                 self.toolbar,
                                                 self._new_game_cb,
                                                 tooltip=_('Start a game.'))

        self.status = label_factory(self.toolbar, '')

        separator_factory(toolbox.toolbar, True, False)

        self.solver = button_factory('help-toolbar',
                                     self.toolbar,
                                     self._solve_cb,
                                     tooltip=_('Solve the puzzle'))

        stop_button = StopButton(self)
        stop_button.props.accelerator = '<Ctrl>q'
        toolbox.toolbar.insert(stop_button, -1)
        stop_button.show()

    def _new_game_cb(self, button=None):
        ''' Start a new game. '''
        self._game.new_game()

    def _solve_cb(self, button=None):
        ''' Solve the puzzle '''
        self._game.solve()

    def write_file(self, file_path):
        """ Write the grid status to the Journal """
        (dot_list, move_list) = self._game.save_game()
        self.metadata['dotlist'] = ''
        for dot in dot_list:
            self.metadata['dotlist'] += str(dot)
            if dot_list.index(dot) < len(dot_list) - 1:
                self.metadata['dotlist'] += ' '
        self.metadata['movelist'] = ''
        for move in move_list:
            self.metadata['movelist'] += str(move)
            if move_list.index(move) < len(move_list) - 1:
                self.metadata['movelist'] += ' '
        _logger.debug(self.metadata['movelist'])

    def _restore(self):
        """ Restore the game state from metadata """
        dot_list = []
        dots = self.metadata['dotlist'].split()
        for dot in dots:
            dot_list.append(int(dot))
        if 'movelist' in self.metadata:
            move_list = []
            moves = self.metadata['movelist'].split()
            for move in moves:
                move_list.append(int(move))
        else:
            move_list = None
        _logger.debug(move_list)
        self._game.restore_game(dot_list, move_list)

    # Collaboration-related methods

    def set_data(self, data):
        pass

    def get_data(self):
        return None

    def _shared_cb(self, activity):
        """ Either set up initial share..."""
        self.after_share_join(True)

    def _joined_cb(self, activity):
        """ ...or join an exisiting share. """
        self.after_share_join(False)

    def after_share_join(self, sharer):
        self.waiting_for_hand = not sharer
        self._game.set_sharing(True)

    def _setup_dispatch_table(self):
        ''' Associate tokens with commands. '''
        self._processing_methods = {
            'n': [self._receive_new_game, 'get a new game grid'],
            'p': [self._receive_dot_click, 'get a dot click'],
        }

    def _message_cb(self, collab, buddy, msg):
        ''' Data from a tube has arrived. '''
        command = msg.get('command')
        payload = msg.get('payload')
        self._processing_methods[command][0](payload)

    def send_new_game(self):
        ''' Send a new grid to all players '''
        self.send_event('n', self._game.save_game())

    def _receive_new_game(self, payload):
        ''' Sharer can start a new game. '''
        (dot_list, move_list) = payload
        self._game.restore_game(dot_list, move_list)

    def send_dot_click(self, dot):
        ''' Send a dot click to all the players '''
        self.send_event('p', dot)

    def _receive_dot_click(self, payload):
        ''' When a dot is clicked, everyone should change its color. '''
        dot = payload
        self._game.remote_button_press(dot)

    def send_event(self, command, payload):
        """ Send event through the tube. """
        self._collab.post({'command': command, 'payload': payload})
Ejemplo n.º 3
0
class FractionBounceActivity(activity.Activity):
    def __init__(self, handle):
        ''' Initiate activity. '''
        super(FractionBounceActivity, self).__init__(handle)

        self.nick = profile.get_nick_name()
        if profile.get_color() is not None:
            self._colors = profile.get_color().to_string().split(',')
        else:
            self._colors = ['#A0FFA0', '#FF8080']

        self.max_participants = 4  # sharing
        self._playing = True

        self._setup_toolbars()
        canvas = self._setup_canvas()

        # Read any custom fractions from the project metadata
        if 'custom' in self.metadata:
            custom = self.metadata['custom']
        else:
            custom = None

        self._current_ball = 'soccerball'

        self._toolbar_was_expanded = False

        # Initialize the canvas
        self._bounce_window = Bounce(canvas, activity.get_bundle_path(), self)

        Gdk.Screen.get_default().connect('size-changed', self._configure_cb)

        # Restore any custom fractions
        if custom is not None:
            fractions = custom.split(',')
            for f in fractions:
                self._bounce_window.add_fraction(f)

        self._bounce_window.buddies.append(self.nick)
        self._player_colors = [self._colors]
        self._player_pixbufs = [
            svg_str_to_pixbuf(generate_xo_svg(scale=0.8, colors=self._colors))
        ]

        def on_activity_joined_cb(me):
            logging.debug('activity joined')
            self._player.set_from_pixbuf(self._player_pixbufs[0])

        self.connect('joined', on_activity_joined_cb)

        def on_activity_shared_cb(me):
            logging.debug('activity shared')
            self._player.set_from_pixbuf(self._player_pixbufs[0])
            self._label.set_label(_('Wait for others to join.'))

        self.connect('shared', on_activity_shared_cb)

        self._collab = CollabWrapper(self)

        if self.shared_activity:
            # We're joining
            if not self.get_shared():
                self._label.set_label(_('Wait for the sharer to start.'))

        actions = {
            'j': self._new_joiner,
            'b': self._buddy_list,
            'f': self._receive_a_fraction,
            't': self._take_a_turn,
            'l': self._buddy_left,
        }

        def on_message_cb(collab, buddy, msg):
            logging.debug('on_message_cb buddy %r msg %r' % (buddy, msg))
            if self._playing:
                actions[msg.get('action')](msg.get('data'))

        self._collab.connect('message', on_message_cb)

        def on_joined_cb(collab, msg):
            logging.debug('joined')
            self.send_event('j', [self.nick, self._colors])

        self._collab.connect('joined', on_joined_cb, 'joined')

        def on_buddy_joined_cb(collab, buddy, msg):
            logging.debug('on_buddy_joined_cb buddy %r' % (buddy.props.nick))

        self._collab.connect('buddy_joined', on_buddy_joined_cb,
                             'buddy_joined')

        def on_buddy_left_cb(collab, buddy, msg):
            logging.debug('on_buddy_left_cb buddy %r' % (buddy.props.nick))

        self._collab.connect('buddy_left', on_buddy_left_cb, 'buddy_left')

        self._collab.setup()

    def set_data(self, blob):
        pass

    def get_data(self):
        return None

    def close(self, **kwargs):
        aplay.close()
        activity.Activity.close(self, **kwargs)

    def _configure_cb(self, event):
        if Gdk.Screen.width() < 1024:
            self._label.set_size_request(275, -1)
            self._label.set_label('')
            self._separator.set_expand(False)
        else:
            self._label.set_size_request(500, -1)
            self._separator.set_expand(True)

        self._bounce_window.configure_cb(event)
        if self._toolbar_expanded():
            self._bounce_window.bar.bump_bars('up')
            self._bounce_window.ball.ball.move_relative(
                (0, -style.GRID_CELL_SIZE))

    def _toolbar_expanded(self):
        if self._activity_button.is_expanded():
            return True
        elif self._custom_toolbar_button.is_expanded():
            return True
        return False

    def _update_graphics(self, widget):
        # We need to catch opening and closing of toolbars and ignore
        # switching between open toolbars.
        if self._toolbar_expanded():
            if not self._toolbar_was_expanded:
                self._bounce_window.bar.bump_bars('up')
                self._bounce_window.ball.ball.move_relative(
                    (0, -style.GRID_CELL_SIZE))
                self._toolbar_was_expanded = True
        else:
            if self._toolbar_was_expanded:
                self._bounce_window.bar.bump_bars('down')
                self._bounce_window.ball.ball.move_relative(
                    (0, style.GRID_CELL_SIZE))
                self._toolbar_was_expanded = False

    def _setup_toolbars(self):
        custom_toolbar = Gtk.Toolbar()
        toolbox = ToolbarBox()
        self._toolbar = toolbox.toolbar
        self._activity_button = ActivityToolbarButton(self)
        self._activity_button.connect('clicked', self._update_graphics)
        self._toolbar.insert(self._activity_button, 0)
        self._activity_button.show()

        self._custom_toolbar_button = ToolbarButton(label=_('Custom'),
                                                    page=custom_toolbar,
                                                    icon_name='view-source')
        self._custom_toolbar_button.connect('clicked', self._update_graphics)
        custom_toolbar.show()
        self._toolbar.insert(self._custom_toolbar_button, -1)
        self._custom_toolbar_button.show()

        self._load_standard_buttons(self._toolbar)

        self._separator = Gtk.SeparatorToolItem()
        self._separator.props.draw = False
        self._separator.set_expand(True)
        self._toolbar.insert(self._separator, -1)
        self._separator.show()

        stop_button = StopButton(self)
        stop_button.props.accelerator = _('<Ctrl>Q')
        self._toolbar.insert(stop_button, -1)
        stop_button.show()
        self.set_toolbar_box(toolbox)
        toolbox.show()

        self._load_custom_buttons(custom_toolbar)

    def _load_standard_buttons(self, toolbar):
        fraction_button = RadioToolButton(group=None)
        fraction_button.set_icon_name('fraction')
        fraction_button.set_tooltip(_('fractions'))
        fraction_button.connect('clicked', self._fraction_cb)
        toolbar.insert(fraction_button, -1)
        fraction_button.show()

        sector_button = RadioToolButton(group=fraction_button)
        sector_button.set_icon_name('sector')
        sector_button.set_tooltip(_('sectors'))
        sector_button.connect('clicked', self._sector_cb)
        toolbar.insert(sector_button, -1)
        sector_button.show()

        percent_button = RadioToolButton(group=fraction_button)
        percent_button.set_icon_name('percent')
        percent_button.set_tooltip(_('percents'))
        percent_button.connect('clicked', self._percent_cb)
        toolbar.insert(percent_button, -1)
        percent_button.show()

        self._player = Gtk.Image()
        self._player.set_from_pixbuf(
            svg_str_to_pixbuf(
                generate_xo_svg(scale=0.8, colors=['#282828', '#282828'])))
        self._player.set_tooltip_text(self.nick)
        toolitem = Gtk.ToolItem()
        toolitem.add(self._player)
        self._player.show()
        toolbar.insert(toolitem, -1)
        toolitem.show()

        self._label = Gtk.Label(_("Click the ball to start."))
        self._label.set_line_wrap(True)
        if Gdk.Screen.width() < 1024:
            self._label.set_size_request(275, -1)
        else:
            self._label.set_size_request(500, -1)
        self.toolitem = Gtk.ToolItem()
        self.toolitem.add(self._label)
        self._label.show()
        toolbar.insert(self.toolitem, -1)
        self.toolitem.show()

    def _load_custom_buttons(self, toolbar):
        self.numerator = Gtk.Entry()
        self.numerator.set_text('')
        self.numerator.set_tooltip_text(_('numerator'))
        self.numerator.set_width_chars(3)
        toolitem = Gtk.ToolItem()
        toolitem.add(self.numerator)
        self.numerator.show()
        toolbar.insert(toolitem, -1)
        toolitem.show()

        label = Gtk.Label('   /   ')
        toolitem = Gtk.ToolItem()
        toolitem.add(label)
        label.show()
        toolbar.insert(toolitem, -1)
        toolitem.show()

        self.denominator = Gtk.Entry()
        self.denominator.set_text('')
        self.denominator.set_tooltip_text(_('denominator'))
        self.denominator.set_width_chars(3)
        toolitem = Gtk.ToolItem()
        toolitem.add(self.denominator)
        self.denominator.show()
        toolbar.insert(toolitem, -1)
        toolitem.show()

        button = ToolButton('list-add')
        button.set_tooltip(_('add new fraction'))
        button.props.sensitive = True
        button.props.accelerator = 'Return'
        button.connect('clicked', self._add_fraction_cb)
        toolbar.insert(button, -1)
        button.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(False)
        toolbar.insert(separator, -1)
        separator.show()

        button = ToolButton('soccerball')
        button.set_tooltip(_('choose a ball'))
        button.props.sensitive = True
        button.connect('clicked', self._button_palette_cb)
        toolbar.insert(button, -1)
        button.show()
        self._ball_palette = button.get_palette()
        button_grid = Gtk.Grid()
        row = 0
        for ball in BALLDICT.keys():
            if ball == 'custom':
                button = ToolButton('view-source')
            else:
                button = ToolButton(ball)
            button.connect('clicked', self._load_ball_cb, None, ball)
            eventbox = Gtk.EventBox()
            eventbox.connect('button_press_event', self._load_ball_cb, ball)
            label = Gtk.Label(BALLDICT[ball][0])
            eventbox.add(label)
            label.show()
            button_grid.attach(button, 0, row, 1, 1)
            button.show()
            button_grid.attach(eventbox, 1, row, 1, 1)
            eventbox.show()
            row += 1
        self._ball_palette.set_content(button_grid)
        button_grid.show()

        button = ToolButton('insert-picture')
        button.set_tooltip(_('choose a background'))
        button.props.sensitive = True
        button.connect('clicked', self._button_palette_cb)
        toolbar.insert(button, -1)
        button.show()
        self._bg_palette = button.get_palette()
        button_grid = Gtk.Grid()
        row = 0
        for bg in BGDICT.keys():
            if bg == 'custom':
                button = ToolButton('view-source')
            else:
                button = ToolButton(bg)
            button.connect('clicked', self._load_bg_cb, None, bg)
            eventbox = Gtk.EventBox()
            eventbox.connect('button_press_event', self._load_bg_cb, bg)
            label = Gtk.Label(BGDICT[bg][0])
            eventbox.add(label)
            label.show()
            button_grid.attach(button, 0, row, 1, 1)
            button.show()
            button_grid.attach(eventbox, 1, row, 1, 1)
            eventbox.show()
            row += 1
        self._bg_palette.set_content(button_grid)
        button_grid.show()

    def _button_palette_cb(self, button):
        palette = button.get_palette()
        if palette:
            if not palette.is_up():
                palette.popup(immediate=True)
            else:
                palette.popdown(immediate=True)

    def can_close(self):
        # Let everyone know we are leaving...
        if hasattr(self, '_bounce_window') and \
           self._bounce_window.we_are_sharing():
            self._playing = False
            self.send_event('l', self.nick)
        return True

    def _setup_canvas(self):
        canvas = Gtk.DrawingArea()
        canvas.set_size_request(Gdk.Screen.width(), Gdk.Screen.height())
        self.set_canvas(canvas)
        canvas.show()
        return canvas

    def _load_bg_cb(self, widget, event, bg):
        if bg == 'custom':
            chooser(self, 'Image', self._new_background_from_journal)
        else:
            self._bounce_window.set_background(BGDICT[bg][1])

    def _load_ball_cb(self, widget, event, ball):
        if ball == 'custom':
            chooser(self, 'Image', self._new_ball_from_journal)
        else:
            self._bounce_window.ball.new_ball(
                os.path.join(activity.get_bundle_path(), 'images',
                             ball + '.svg'))
            self._bounce_window.set_background(BGDICT[BALLDICT[ball][1]][1])
        self._current_ball = ball

    def _reset_ball(self):
        ''' If we switch back from sector mode, we need to restore the ball '''
        if self._bounce_window.mode != 'sectors':
            return

        if self._current_ball == 'custom':  # TODO: Reload custom ball
            self._current_ball = 'soccerball'
        self._bounce_window.ball.new_ball(
            os.path.join(activity.get_bundle_path(), 'images',
                         self._current_ball + '.svg'))

    def _new_ball_from_journal(self, dsobject):
        ''' Load an image from the Journal. '''
        self._bounce_window.ball.new_ball_from_image(
            dsobject.file_path,
            os.path.join(activity.get_activity_root(), 'tmp', 'custom.png'))

    def _new_background_from_journal(self, dsobject):
        ''' Load an image from the Journal. '''
        self._bounce_window.new_background_from_image(None, dsobject=dsobject)

    def _fraction_cb(self, arg=None):
        ''' Set fraction mode '''
        self._reset_ball()
        self._bounce_window.mode = 'fractions'

    def _percent_cb(self, arg=None):
        ''' Set percent mode '''
        self._reset_ball()
        self._bounce_window.mode = 'percents'

    def _sector_cb(self, arg=None):
        ''' Set sector mode '''
        self._bounce_window.mode = 'sectors'

    def _add_fraction_cb(self, arg=None):
        ''' Read entries and add a fraction to the list '''
        try:
            numerator = int(self.numerator.get_text().strip())
        except ValueError:
            self.numerator.set_text('NAN')
            numerator = 0
        try:
            denominator = int(self.denominator.get_text().strip())
        except ValueError:
            self.denominator.set_text('NAN')
            denominator = 1
        if denominator == 0:
            self.denominator.set_text('ZDE')
        if numerator > denominator:
            numerator = 0
        if numerator > 0 and denominator > 1:
            fraction = '%d/%d' % (numerator, denominator)
            self._bounce_window.add_fraction(fraction)
            if 'custom' in self.metadata:  # Save to Journal
                self.metadata['custom'] = '%s,%s' % (self.metadata['custom'],
                                                     fraction)
            else:
                self.metadata['custom'] = fraction

            self.alert(
                _('New fraction'),
                _('Your fraction, %s, has been added to the program' %
                  (fraction)))

    def reset_label(self, label):
        ''' update the challenge label '''
        self._label.set_label(label)

    def alert(self, title, text=None):
        alert = NotifyAlert(timeout=5)
        alert.props.title = title
        alert.props.msg = text
        self.add_alert(alert)
        alert.connect('response', self._alert_cancel_cb)
        alert.show()

    def _alert_cancel_cb(self, alert, response_id):
        self.remove_alert(alert)

    # Collaboration-related methods

    def _buddy_left(self, nick):
        self._label.set_label(nick + ' ' + _('has left.'))
        if self._collab.props.leader:
            self._remove_player(nick)
            self.send_event('b',
                            [self._bounce_window.buddies, self._player_colors])
            # Restart from sharer's turn
            self._bounce_window.its_my_turn()

    def _new_joiner(self, payload):
        ''' Someone has joined; sharer adds them to the buddy list. '''
        [nick, colors] = payload
        self._label.set_label(nick + ' ' + _('has joined.'))
        if self._collab.props.leader:
            self._append_player(nick, colors)
            self.send_event('b',
                            [self._bounce_window.buddies, self._player_colors])
            if self._bounce_window.count == 0:  # Haven't started yet...
                self._bounce_window.its_my_turn()

    def _remove_player(self, nick):
        if nick in self._bounce_window.buddies:
            i = self._bounce_window.buddies.index(nick)
            self._bounce_window.buddies.remove(nick)
            self._player_colors.remove(self._player_colors[i])
            self._player_pixbufs.remove(self._player_pixbufs[i])

    def _append_player(self, nick, colors):
        ''' Keep a list of players, their colors, and an XO pixbuf '''
        if nick not in self._bounce_window.buddies:
            _logger.debug('appending %s to the buddy list', nick)
            self._bounce_window.buddies.append(nick)
            self._player_colors.append([str(colors[0]), str(colors[1])])
            self._player_pixbufs.append(
                svg_str_to_pixbuf(
                    generate_xo_svg(scale=0.8,
                                    colors=self._player_colors[-1])))

    def _buddy_list(self, payload):
        '''Sharer sent the updated buddy list, so regenerate internal lists'''
        if not self._collab.props.leader:
            [buddies, colors] = payload
            self._bounce_window.buddies = buddies[:]
            self._player_colors = colors[:]
            self._player_pixbufs = []
            for colors in self._player_colors:
                self._player_pixbufs.append(
                    svg_str_to_pixbuf(
                        generate_xo_svg(
                            scale=0.8, colors=[str(colors[0]),
                                               str(colors[1])])))

    def send_a_fraction(self, fraction):
        ''' Send a fraction to other players. '''
        self.send_event('f', fraction)

    def _receive_a_fraction(self, payload):
        ''' Receive a fraction from another player. '''
        self._bounce_window.play_a_fraction(payload)

    def _take_a_turn(self, nick):
        ''' If it is your turn, take it, otherwise, wait. '''
        if nick == self.nick:  # TODO: disambiguate
            self._bounce_window.its_my_turn()
        else:
            self._bounce_window.its_their_turn(nick)

    def send_event(self, action, data):
        ''' Send event through the tube. '''
        _logger.debug('send_event action=%r data=%r' % (action, data))
        self._collab.post({'action': action, 'data': data})

    def set_player_on_toolbar(self, nick):
        ''' Display the XO icon of the player whose turn it is. '''
        self._player.set_from_pixbuf(
            self._player_pixbufs[self._bounce_window.buddies.index(nick)])
        self._player.set_tooltip_text(nick)
Ejemplo n.º 4
0
class CollabWrapperTestActivity(activity.Activity):
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)

        self._make_toolbar_box()
        self._make_canvas()
        self._make_collaboration()
        self._make_automatic_restart()

    def set_data(self, data):
        if data is not 'no specific data':
            logging.error('get_data, set_data: unexpected data %r' % data)

    def get_data(self):
        return 'no specific data'

    def _make_toolbar_box(self):
        toolbar_box = ToolbarBox()

        def tool(callable, pos):
            widget = callable(self)
            toolbar_box.toolbar.insert(widget, pos)
            widget.show()
            return widget

        def bar():
            separator = Gtk.SeparatorToolItem()
            toolbar_box.toolbar.insert(separator, -1)
            separator.show()

        def gap():
            separator = Gtk.SeparatorToolItem()
            separator.props.draw = False
            separator.set_expand(True)
            toolbar_box.toolbar.insert(separator, -1)
            separator.show()

        tool(ActivityButton, 0)
        tool(TitleEntry, -1)
        tool(DescriptionItem, -1)
        tool(ShareButton, -1)
        bar()
        button = tool(TestButton, -1)
        button.props.accelerator = '<ctrl>p'
        button.connect('clicked', self._test_clicked_cb)
        button = tool(SendButton, -1)
        button.connect('clicked', self._send_clicked_cb)
        button = tool(ClearButton, -1)
        button.connect('clicked', self._clear_clicked_cb)
        gap()
        tool(StopButton, -1)

        toolbar_box.show()

        self.set_toolbar_box(toolbar_box)

    def _test_clicked_cb(self, widget):
        now = time.time()
        self._collab.post({'action': 'echo-request', 'text': now})
        self._say('%.6f send echo-request %r\n' % (now, now))

    def _send_clicked_cb(self, widget):
        now = time.time()
        data = 'One Two Three'
        desc = 'Test Data'
        self._collab.send_file_memory(self._last_buddy, data, desc)
        self._say('%.6f send data %r\n' % (now, (data, desc)))

    def _clear_clicked_cb(self, widget):
        self._textbuffer.props.text = ''

    def _make_canvas(self):
        self._textview = Gtk.TextView()
        self._textview.props.editable = False
        self._textbuffer = self._textview.get_buffer()

        sw = Gtk.ScrolledWindow()
        sw.add(self._textview)

        entry = Gtk.Entry()
        entry.connect('activate', self._entry_activate_cb)

        def focus_timer_cb():
            entry.grab_focus()
            return False

        GLib.timeout_add(1500, focus_timer_cb)

        box = Gtk.VBox()
        box.pack_start(sw, True, True, 10)
        box.pack_end(entry, False, False, 10)
        box.show_all()

        self.set_canvas(box)

    def _say(self, string):
        self._textbuffer.begin_user_action()
        self._textbuffer.insert(self._textbuffer.get_end_iter(), string,
                                len(string))
        self._textview.scroll_mark_onscreen(
            self._textbuffer.create_mark(None,
                                         self._textbuffer.get_end_iter()))
        self._textbuffer.end_user_action()

    def _entry_activate_cb(self, widget):
        text = widget.props.text
        widget.props.text = ''
        self._collab.post({'action': 'chat', 'text': text})
        self._say('%.6f send chat %r\n' % (time.time(), text))

    def _make_collaboration(self):
        def on_activity_joined_cb(me):
            self._say('%.6f activity joined\n' % (time.time()))

        self.connect('joined', on_activity_joined_cb)

        def on_activity_shared_cb(me):
            self._say('%.6f activity shared\n' % (time.time()))

        self.connect('shared', on_activity_shared_cb)

        self._collab = CollabWrapper(self)
        self._collab.connect('message', self._message_cb)

        def on_joined_cb(collab, msg):
            self._say('%.6f joined\n' % (time.time()))

        self._collab.connect('joined', on_joined_cb, 'joined')

        def on_buddy_joined_cb(collab, buddy, msg):
            self._say('%.6f buddy-joined %s@%s\n' %
                      (time.time(), buddy.props.nick, buddy.props.ip4_address))

        self._collab.connect('buddy_joined', on_buddy_joined_cb,
                             'buddy_joined')

        def on_buddy_left_cb(collab, buddy, msg):
            self._say('%.6f buddy-left %s@%s\n' %
                      (time.time(), buddy.props.nick, buddy.props.ip4_address))

        self._collab.connect('buddy_left', on_buddy_left_cb, 'buddy_left')

        def on_incoming_file_cb(collab, ft, desc):
            self._say('%.6f incoming-file %r\n' % (time.time(), desc))

            def on_ready_cb(ft, stream):
                stream.close(None)
                gbytes = stream.steal_as_bytes()
                data = gbytes.get_data()

                self._say('%.6f data %r\n' % (time.time(), data))

            ft.connect('ready', on_ready_cb)
            ft.accept_to_memory()

        self._collab.connect('incoming_file', on_incoming_file_cb)

        self._collab.setup()
        self._last_buddy = None

    def _message_cb(self, collab, buddy, msg):
        def say(string):
            self._say('%.6f recv from %s@%s ' %
                      (time.time(), buddy.props.nick, buddy.props.ip4_address))
            self._say(string)

        self._last_buddy = buddy

        action = msg.get('action')
        if action == 'echo-reply':
            text = msg.get('text')
            latency = (time.time() - float(text)) * 1000.0
            say('%s %r latency=%.3f ms\n' % (action, text, latency))
            return
        if action == 'echo-request':
            text = msg.get('text')
            self._collab.post({'action': 'echo-reply', 'text': text})
            say('%s %r\n' % (action, text))
            return
        if action == 'chat':
            text = msg.get('text')
            say('%s %r\n' % (action, text))
            return
        say('%s\n' % (action))

    def _make_automatic_restart(self):
        ct = os.stat('activity.py').st_ctime

        def restarter():
            if os.stat('activity.py').st_ctime != ct:
                GLib.timeout_add(233, self.close)
                logging.error('-- restart --')
                return False
            return True

        GLib.timeout_add(233, restarter)
Ejemplo n.º 5
0
class DeductoActivity(activity.Activity):
    """ Logic puzzle game """
    def __init__(self, handle):
        """ Initialize the toolbars and the game board """

        activity.Activity.__init__(self, handle)

        self.nick = profile.get_nick_name()
        if profile.get_color() is not None:
            self.colors = profile.get_color().to_string().split(',')
        else:
            self.colors = ['#A0FFA0', '#FF8080']

        self.level = 0
        self._correct = 0
        self._playing = True
        self._game_over = False

        self._python_code = None

        self._setup_toolbars()
        self._setup_dispatch_table()

        # 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._game = Game(canvas, parent=self, colors=self.colors)

        self._sharing = False
        self._initiating = False
        self.connect('shared', self._shared_cb)

        self._collab = CollabWrapper(self)
        self._collab.connect('message', self._message_cb)
        self._collab.connect('joined', self._joined_cb)
        self._collab.setup()

        if 'level' in self.metadata:
            self.level = int(self.metadata['level'])
            self.status.set_label(_('Resuming level %d') % (self.level + 1))
            self._game.show_random()
        else:
            self._game.new_game()

    def _setup_toolbars(self):
        """ Setup the toolbars. """

        self.max_participants = 4

        toolbox = ToolbarBox()

        # Activity toolbar
        activity_button = ActivityToolbarButton(self)

        toolbox.toolbar.insert(activity_button, 0)
        activity_button.show()

        self.set_toolbar_box(toolbox)
        toolbox.show()
        self.toolbar = toolbox.toolbar

        self._new_game_button = button_factory('new-game',
                                               self.toolbar,
                                               self._new_game_cb,
                                               tooltip=_('Start a new game.'))

        separator_factory(toolbox.toolbar, False, True)

        self._true_button = button_factory(
            'true',
            self.toolbar,
            self._true_cb,
            tooltip=_('The pattern matches the rule.'))

        self._false_button = button_factory(
            'false',
            self.toolbar,
            self._false_cb,
            tooltip=_('The pattern does not match the rule.'))

        separator_factory(toolbox.toolbar, False, True)

        self._example_button = button_factory(
            'example',
            self.toolbar,
            self._example_cb,
            tooltip=_('Explore some examples.'))

        self.status = label_factory(self.toolbar, '', width=300)

        separator_factory(toolbox.toolbar, True, False)

        self._gear_button = button_factory('view-source',
                                           self.toolbar,
                                           self._gear_cb,
                                           tooltip=_('Load a custom level.'))

        stop_button = StopButton(self)
        stop_button.props.accelerator = '<Ctrl>q'
        toolbox.toolbar.insert(stop_button, -1)
        stop_button.show()

    def _new_game_cb(self, button=None):
        ''' Start a new game. '''
        self._game_over = False
        self._correct = 0
        self.level = 0
        if not self._playing:
            self._example_cb()
        self._game.new_game()
        if self._initiating:
            _logger.debug('sending new game and new grid')
            self._send_new_game()
            self._send_new_grid()
        self.status.set_label(_('Playing level %d') % (self.level + 1))

    def _test_for_game_over(self):
        ''' If we are at maximum levels, the game is over '''
        if self.level == self._game.max_levels:
            self.level = 0
            self._game_over = True
            self.status.set_label(_('Game over.'))
        else:
            self.status.set_label(_('Playing level %d') % (self.level + 1))
            self._correct = 0
            if (not self._sharing) or self._initiating:
                self._game.show_random()
                if self._initiating:
                    self._send_new_grid()

    def _true_cb(self, button=None):
        ''' Declare pattern true or show an example of a true pattern. '''
        if self._game_over:
            if (not self._sharing) or self._initiating:
                self.status.set_label(_('Click on new game button to begin.'))
            else:
                self.status.set_label(
                    _('Wait for sharer to start ' + 'a new game.'))
            return
        if self._playing:
            if self._game.this_pattern:
                self._correct += 1
                if self._correct == 5:
                    self.level += 1
                    self._test_for_game_over()
                    self.metadata['level'] = str(self.level)
                else:
                    self.status.set_label(
                        _('%d correct answers.') % (self._correct))
                    if (not self._sharing) or self._initiating:
                        self._game.show_random()
                        if self._initiating:
                            self._send_new_grid()
            else:
                self.status.set_label(_('Pattern was false.'))
                self._correct = 0
            if (button is not None) and self._sharing:
                self._send_true_button_click()
        else:
            self._game.show_true()

    def _false_cb(self, button=None):
        ''' Declare pattern false or show an example of a false pattern. '''
        if self._game_over:
            if (not self._sharing) or self._initiating:
                self.status.set_label(_('Click on new game button to begin.'))
            else:
                self.status.set_label(
                    _('Wait for sharer to start ' + 'a new game.'))
            return
        if self._playing:
            if not self._game.this_pattern:
                self._correct += 1
                if self._correct == 5:
                    self.level += 1
                    self._test_for_game_over()
                else:
                    self.status.set_label(
                        _('%d correct answers.') % (self._correct))
                    if (not self._sharing) or self._initiating:
                        self._game.show_random()
                        if self._initiating:
                            self._send_new_grid()
            else:
                self.status.set_label(_('Pattern was true.'))
                self._correct = 0
            if (button is not None) and self._sharing:
                self._send_false_button_click()
        else:
            self._game.show_false()

    def _example_cb(self, button=None):
        ''' Show examples or resume play of current level. '''
        if self._playing:
            self._example_button.set_icon_name('resume-play')
            self._example_button.set_tooltip(_('Resume play'))
            self._true_button.set_tooltip(
                _('Show a pattern that matches the rule.'))
            self._false_button.set_tooltip(
                _('Show a pattern that does not match the rule.'))
            Button1 = '☑'
            Button2 = '☒'
            self.status.set_label(
                _("Explore patterns with the {} and {} buttons.".format(
                    Button1, Button2)))
            self._playing = False
        else:
            self._example_button.set_icon_name('example')
            self._example_button.set_tooltip(_('Explore some examples.'))
            self._true_button.set_tooltip(_('The pattern matches the rule.'))
            self._false_button.set_tooltip(
                _('The pattern does not match the rule.'))
            self.status.set_label(_('Playing level %d') % (self.level + 1))
            self._playing = True
            self._correct = 0

    def _gear_cb(self, button=None):
        ''' Load a custom level. '''
        self.status.set_text(
            _('Load a "True" pattern generator from the journal'))
        self._chooser('org.laptop.Pippy', self._load_python_code_from_journal)
        if self._python_code is None:
            return
        LEVELS_TRUE.append(self._python_code)
        self.status.set_text(
            _('Load a "False" pattern generator from the journal'))
        self._chooser('org.laptop.Pippy', self._load_python_code_from_journal)
        LEVELS_FALSE.append(self._python_code)
        if self._python_code is None:
            return
        self.status.set_text(_('New level added'))
        self._game.max_levels += 1

    def _load_python_code_from_journal(self, dsobject):
        ''' Read the Python code from the Journal object '''
        self._python_code = None
        try:
            _logger.debug("opening %s " % dsobject.file_path)
            file_handle = open(dsobject.file_path, "r")
            self._python_code = file_handle.read()
            file_handle.close()
        except IOError:
            _logger.debug("couldn't open %s" % dsobject.file_path)

    def _chooser(self, filter, action):
        ''' Choose an object from the datastore and take some action '''
        chooser = None
        try:
            chooser = ObjectChooser(parent=self, what_filter=filter)
        except TypeError:
            chooser = ObjectChooser(
                None, self,
                Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT)
        if chooser is not None:
            try:
                result = chooser.run()
                if result == Gtk.ResponseType.ACCEPT:
                    dsobject = chooser.get_selected_object()
                    action(dsobject)
                    dsobject.destroy()
            finally:
                chooser.destroy()
                del chooser

    # Collaboration-related methods

    # The sharer sends patterns and everyone shares whatever vote is
    # cast first among all the sharer and joiners.

    def set_data(self, data):
        pass

    def get_data(self):
        return None

    def _shared_cb(self, activity):
        ''' Either set up initial share...'''
        self.after_share_join(True)

    def _joined_cb(self, activity):
        ''' ...or join an exisiting share. '''
        self.after_share_join(True)

    def after_share_join(self, sharer):

        self.waiting_for_hand = not sharer
        self._initiating = sharer
        self._sharing = True

    def _setup_dispatch_table(self):
        ''' Associate tokens with commands. '''
        self._processing_methods = {
            'new_game': [self._receive_new_game, 'new game'],
            'new_grid': [self._receive_new_grid, 'get a new grid'],
            'true':
            [self._receive_true_button_click, 'get a true button press'],
            'false': [
                self._receive_false_button_click,
                'get a false ' + 'button press'
            ],
        }

    def _message_cb(self, collab, buddy, msg):

        command = msg.get('command')
        payload = msg.get('payload')
        self._processing_methods[command][0](payload)

    def _send_new_game(self):
        '''
        Send a new game message to all players
        '''
        self._send_event('new_game', ' ')

    def _receive_new_game(self, payload):
        ''' Receive a new game notification from the sharer. '''
        self._game_over = False
        self._correct = 0
        self.level = 0
        if not self._playing:
            self._example_cb()
        self.status.set_label(_('Playing level %d') % (self.level + 1))

    def _send_new_grid(self):
        ''' Send a new grid to all players '''
        self._send_event('new_grid', self._game.save_grid())

    def _receive_new_grid(self, payload):
        ''' Receive a grid from the sharer. '''
        (dot_list, boolean, colors) = payload
        self._game.restore_grid(dot_list, boolean, colors)

    def _send_true_button_click(self):
        ''' Send a true click to all the players '''
        self._send_event('true')

    def _receive_true_button_click(self, payload):
        ''' When a button is clicked, everyone should react. '''
        self._playing = True
        self._true_cb()

    def _send_false_button_click(self):
        ''' Send a false click to all the players '''
        self._send_event('false')

    def _receive_false_button_click(self, payload):
        ''' When a button is clicked, everyone should react. '''
        self._playing = True
        self._false_cb()

    def _send_event(self, command, payload=None):
        self._collab.post({'command': command, 'payload': payload})
Ejemplo n.º 6
0
class YupanaActivity(activity.Activity):
    """ Yupana counting device """
    def __init__(self, handle):
        """ Initialize the toolbars and the yupana """
        activity.Activity.__init__(self, handle)

        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()

        # 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._yupana = Yupana(canvas, parent=self, colors=self.colors)
        self._setup_collab()

        if 'dotlist' in self.metadata:
            self._restore()
        else:
            self._yupana.new_yupana(mode='ten')

        self._make_custom_toolbar()
        if self._reload_custom:
            self._custom_cb()

    def _setup_toolbars(self):
        """ Setup the toolbars. """

        self.max_participants = 4

        yupana_toolbar = Gtk.Toolbar()
        self.custom_toolbar = Gtk.Toolbar()
        toolbox = ToolbarBox()

        # Activity toolbar
        activity_button = ActivityToolbarButton(self)

        toolbox.toolbar.insert(activity_button, 0)
        activity_button.show()

        yupana_toolbar_button = ToolbarButton(label=_("Mode"),
                                              page=yupana_toolbar,
                                              icon_name='preferences-system')
        yupana_toolbar.show()
        toolbox.toolbar.insert(yupana_toolbar_button, -1)
        yupana_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

        self._new_yupana_button = button_factory(
            'edit-delete',
            self.toolbar,
            self._new_yupana_cb,
            tooltip=_('Clear the yupana.'))

        separator_factory(yupana_toolbar, False, True)

        self.ten_button = radio_factory('ten',
                                        yupana_toolbar,
                                        self._ten_cb,
                                        tooltip=_('decimal mode'),
                                        group=None)
        self.twenty_button = radio_factory('twenty',
                                           yupana_toolbar,
                                           self._twenty_cb,
                                           tooltip=_('base-twenty mode'),
                                           group=self.ten_button)
        self.factor_button = radio_factory('factor',
                                           yupana_toolbar,
                                           self._factor_cb,
                                           tooltip=_('prime-factor mode'),
                                           group=self.ten_button)
        self.fibonacci_button = radio_factory('fibonacci',
                                              yupana_toolbar,
                                              self._fibonacci_cb,
                                              tooltip=_('Fibonacci mode'),
                                              group=self.ten_button)
        self.custom_button = radio_factory('view-source',
                                           yupana_toolbar,
                                           self._custom_cb,
                                           tooltip=_('custom mode'),
                                           group=self.ten_button)

        separator_factory(self.toolbar, False, False)
        self.status = label_factory(self.toolbar, '', width=200)
        self.status.set_label(_('decimal mode'))

        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 _make_custom_toolbar(self):
        self._ones = entry_factory(str(self._yupana.custom[0]),
                                   self.custom_toolbar,
                                   tooltip=_('one row'))
        self._twos = entry_factory(str(self._yupana.custom[1]),
                                   self.custom_toolbar,
                                   tooltip=_('two row'))
        self._threes = entry_factory(str(self._yupana.custom[2]),
                                     self.custom_toolbar,
                                     tooltip=_('three row'))
        self._fives = entry_factory(str(self._yupana.custom[3]),
                                    self.custom_toolbar,
                                    tooltip=_('five row'))

        separator_factory(self.custom_toolbar, False, True)
        self._base = entry_factory(str(self._yupana.custom[4]),
                                   self.custom_toolbar,
                                   tooltip=_('base'))

        separator_factory(self.custom_toolbar, False, True)
        button_factory('view-refresh',
                       self.custom_toolbar,
                       self._custom_cb,
                       tooltip=_('Reload custom values.'))

    def _new_yupana_cb(self, button=None):
        ''' Start a new yupana. '''
        self._yupana.new_yupana()

    def _ten_cb(self, button=None):
        self._yupana.new_yupana(mode='ten')
        self.status.set_label(_('decimal mode'))

    def _twenty_cb(self, button=None):
        self._yupana.new_yupana(mode='twenty')
        self.status.set_label(_('base-twenty mode'))

    def _factor_cb(self, button=None):
        self._yupana.new_yupana(mode='factor')
        self.status.set_label(_('prime-factor mode'))

    def _fibonacci_cb(self, button=None):
        self._yupana.new_yupana(mode='fibonacci')
        self.status.set_label(_('Fibonacci mode'))

    def _custom_cb(self, button=None):
        if hasattr(self, '_ones'):
            self._yupana.custom[0] = int(self._ones.get_text())
            self._yupana.custom[1] = int(self._twos.get_text())
            self._yupana.custom[2] = int(self._threes.get_text())
            self._yupana.custom[3] = int(self._fives.get_text())
            self._yupana.custom[4] = int(self._base.get_text())
            self._reload_custom = False
        else:
            self._reload_custom = True
        self._yupana.new_yupana(mode='custom')
        self.status.set_label(_('custom mode'))

    def write_file(self, file_path):
        """ Write the grid status to the Journal """
        [mode, dot_list] = self._yupana.save_yupana()
        self.metadata['mode'] = mode
        self.metadata['custom'] = ''
        for i in range(5):
            self.metadata['custom'] += str(self._yupana.custom[i])
            self.metadata['custom'] += ' '
        self.metadata['dotlist'] = ''
        for dot in dot_list:
            self.metadata['dotlist'] += str(dot)
            if dot_list.index(dot) < len(dot_list) - 1:
                self.metadata['dotlist'] += ' '
        self.metadata['label'] = self._yupana.get_label()

    def _restore(self):
        """ Restore the yupana state from metadata """
        if 'custom' in self.metadata:
            values = self.metadata['custom'].split()
            for i in range(5):
                self._yupana.custom[i] = int(values[i])
        if 'mode' in self.metadata:
            if self.metadata['mode'] == 'ten':
                self.ten_button.set_active(True)
            elif self.metadata['mode'] == 'twenty':
                self.twenty_button.set_active(True)
            elif self.metadata['mode'] == 'factor':
                self.factor_button.set_active(True)
            elif self.metadata['mode'] == 'fibonacci':
                self.fibonacci_button.set_active(True)
            else:
                self.custom_button.set_active(True)
        if 'dotlist' in self.metadata:
            dot_list = []
            dots = self.metadata['dotlist'].split()
            for dot in dots:
                dot_list.append(int(dot))
            self._yupana.restore_yupana(self.metadata['mode'], dot_list)
        self._yupana.set_label(self.metadata['label'])

    # Collaboration-related methods

    def _setup_collab(self):
        """ Setup the Presence Service. """
        self.initiating = None  # sharing (True) or joining (False)

        self.connect('shared', self._shared_cb)
        self.connect('joined', self._joined_cb)
        self._collab = CollabWrapper(self)
        self._collab.connect('message', self._message_cb)
        self._collab.connect('joined', self._joined_cb)
        self._collab.setup()

    def set_data(self, data):
        pass

    def get_data(self):
        return None

    def _shared_cb(self, activity):
        """ Either set up initial share..."""
        self.after_share_join(True)

    def _joined_cb(self, activity):
        """ ...or join an exisiting share. """
        self.after_share_join(False)

    def after_share_join(self, sharer):
        """ Joining and sharing are mostly the same... """
        self.initiating = sharer
        self.waiting_for_hand = not sharer

        self._yupana.set_sharing(True)

    def _message_cb(self, collab, buddy, msg):
        command = msg.get('command')
        payload = msg.get('payload')
        if command == 'new_game':
            '''Get a new yupana grid'''
            self._receive_new_yupana(payload)
        elif command == 'played':
            '''Get a dot click'''
            self._receive_dot_click(payload)
        elif command == 'label':
            self._yupana.set_label(payload)

    def send_new_yupana(self):
        ''' Send a new orientation, grid to all players '''
        self._collab.post(
            dict(command='new_game',
                 payload=json_dump(self._yupana.save_yupana())))

    def _receive_new_yupana(self, payload):
        ''' Sharer can start a new yupana. '''
        mode, dot_list = json_load(payload)
        self._yupana.restore_yupana(mode, dot_list)

    def send_dot_click(self, dot, color):
        ''' Send a dot click to all the players '''
        self._collab.post(
            dict(command='played', payload=json_dump([dot, color])))

    def _receive_dot_click(self, payload):
        ''' When a dot is clicked, everyone should change its color. '''
        (dot, color) = json_load(payload)
        self._yupana.remote_button_press(dot, color)

    def send_label(self, label):
        self._collab.post(dict(command='label', payload=label))
Ejemplo n.º 7
0
class MemorizeActivity(Activity):
    def __init__(self, handle):
        Activity.__init__(self, handle)

        self.play_mode = None

        toolbar_box = ToolbarBox()
        self.set_toolbar_box(toolbar_box)

        self.activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(self.activity_button, -1)

        self._memorizeToolbarBuilder = \
            memorizetoolbar.MemorizeToolbarBuilder(self)

        toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        self._edit_button = ToggleToolButton('view-source')
        self._edit_button.set_tooltip(_('Edit game'))
        self._edit_button.set_active(False)
        toolbar_box.toolbar.insert(self._edit_button, -1)

        self._createToolbarBuilder = \
            createtoolbar.CreateToolbarBuilder(self)

        separator = Gtk.SeparatorToolItem()
        separator.set_expand(True)
        separator.set_draw(False)
        separator.set_size_request(0, -1)
        toolbar_box.toolbar.insert(separator, -1)

        toolbar_box.toolbar.insert(StopButton(self), -1)

        self.game = game.MemorizeGame()
        # Play game mode
        self.table = cardtable.CardTable()
        self.scoreboard = scoreboard.Scoreboard()
        self.cardlist = cardlist.CardList()
        self.createcardpanel = createcardpanel.CreateCardPanel(self.game)
        self.cardlist.connect('pair-selected',
                              self.createcardpanel.pair_selected)
        self.cardlist.connect('update-create-toolbar',
                              self._createToolbarBuilder.update_create_toolbar)
        self.createcardpanel.connect('add-pair', self.cardlist.add_pair)
        self.createcardpanel.connect('update-pair',
                                     self.cardlist.update_selected)
        self.createcardpanel.connect('change-font', self.cardlist.change_font)
        self.createcardpanel.connect('pair-closed',
                                     self.cardlist.rem_current_pair)

        self._createToolbarBuilder.connect('create_new_game',
                                           self.cardlist.clean_list)
        self._createToolbarBuilder.connect('create_new_game',
                                           self.createcardpanel.clean)
        self._createToolbarBuilder.connect('create_new_game',
                                           self._memorizeToolbarBuilder.reset)
        self._createToolbarBuilder.connect('create_equal_pairs',
                                           self.change_equal_pairs)

        self._edit_button.connect('toggled', self._change_mode_bt)

        self.connect('key-press-event', self.table.key_press_event)
        self.table.connect('card-flipped', self.game.card_flipped)
        self.table.connect('card-highlighted', self.game.card_highlighted)

        self.game.connect('set-border', self.table.set_border)
        self.game.connect('flop-card', self.table.flop_card)
        self.game.connect('flip-card', self.table.flip_card)
        self.game.connect('cement-card', self.table.cement_card)
        self.game.connect('highlight-card', self.table.highlight_card)
        self.game.connect('load_mode', self.table.load_msg)

        self.game.connect('msg_buddy', self.scoreboard.set_buddy_message)
        self.game.connect('add_buddy', self.scoreboard.add_buddy)
        self.game.connect('rem_buddy', self.scoreboard.rem_buddy)
        self.game.connect('increase-score', self.scoreboard.increase_score)
        self.game.connect('wait_mode_buddy', self.scoreboard.set_wait_mode)
        self.game.connect('change-turn', self.scoreboard.set_selected)
        self.game.connect('change_game', self.scoreboard.change_game)

        self.game.connect('reset_scoreboard', self.scoreboard.reset)
        self.game.connect('reset_table', self.table.reset)

        self.game.connect('load_game', self.table.load_game)
        self.game.connect('change_game', self.table.change_game)
        self.game.connect('load_game',
                          self._memorizeToolbarBuilder.update_toolbar)
        self.game.connect('change_game',
                          self._memorizeToolbarBuilder.update_toolbar)
        self.game.connect('change_game',
                          self.createcardpanel.update_font_combos)

        self._memorizeToolbarBuilder.connect('game_changed', self.change_game)

        self.box = Gtk.HBox(orientation=Gtk.Orientation.VERTICAL,
                            homogeneous=False)

        width = Gdk.Screen.width()
        height = Gdk.Screen.height() - style.GRID_CELL_SIZE
        self.table.resize(width, height - style.GRID_CELL_SIZE)
        self.scoreboard.set_size_request(-1, style.GRID_CELL_SIZE)
        self.set_canvas(self.box)

        # connect to the in/out events of the memorize activity
        self.connect('focus_in_event', self._focus_in)
        self.connect('focus_out_event', self._focus_out)
        self.connect('destroy', self._cleanup_cb)

        self.add_events(Gdk.EventMask.POINTER_MOTION_MASK)
        self.connect('motion_notify_event',
                     lambda widget, event: face.look_at())

        Gdk.Screen.get_default().connect('size-changed', self.__configure_cb)

        # start on the game toolbar, might change this
        # to the create toolbar later
        self._change_mode(_MODE_PLAY)

        def on_activity_joined_cb(me):
            logging.debug('activity joined')
            self.game.add_buddy(self._collab.props.owner)

        self.connect('joined', on_activity_joined_cb)

        def on_activity_shared_cb(me):
            logging.debug('activity shared')

        self.connect('shared', on_activity_shared_cb)

        self._collab = CollabWrapper(self)
        self.game.set_myself(self._collab.props.owner)

        def on_message_cb(collab, buddy, msg):
            logging.debug('on_message_cb buddy %r msg %r' % (buddy, msg))
            action = msg.get('action')
            if action == 'flip':
                n = msg.get('n')
                self.game.card_flipped(None, n, True)
            elif action == 'change':
                self.get_canvas().hide()

                def momentary_blank_timeout_cb():
                    self.set_data(msg)
                    self.get_canvas().show()

                GLib.timeout_add(100, momentary_blank_timeout_cb)

        self._collab.connect('message', on_message_cb)

        def on_joined_cb(collab, msg):
            logging.debug('joined')

        self._collab.connect('joined', on_joined_cb, 'joined')

        def on_buddy_joined_cb(collab, buddy, msg):
            logging.debug('on_buddy_joined_cb buddy %r msg %r' % (buddy, msg))
            self.game.add_buddy(buddy)

        self._collab.connect('buddy_joined', on_buddy_joined_cb,
                             'buddy_joined')

        def on_buddy_left_cb(collab, buddy, msg):
            logging.debug('on_buddy_left_cb buddy %r msg %r' % (buddy, msg))
            self.game.rem_buddy(buddy)

        self._collab.connect('buddy_left', on_buddy_left_cb, 'buddy_left')

        self._files = {}  # local temporary copies of shared games

        self._collab.setup()

        def on_flip_card_cb(game, n):
            logging.debug('on_flip_card_cb n %r' % (n))
            self._collab.post({'action': 'flip', 'n': n})

        self.game.connect('flip-card-signal', on_flip_card_cb)

        def on_change_game_cb(sender, mode, grid, data, waiting_list, zip):
            logging.debug('on_change_game_cb')
            blob = self.get_data()
            blob['action'] = 'change'

            self._collab.post(blob)

        self.game.connect('change_game_signal', on_change_game_cb)

        if self._collab.props.leader:
            logging.debug('is leader')
            game_file = os.path.join(os.path.dirname(__file__), 'demos',
                                     'addition.zip')
            self.game.load_game(game_file, 4, 'demo')
            self.game.add_buddy(self._collab.props.owner)

        self.show_all()

    def __configure_cb(self, event):
        ''' Screen size has changed '''
        width = Gdk.Screen.width()
        height = Gdk.Screen.height() - style.GRID_CELL_SIZE
        self.box.set_size_request(width, height)
        self.scoreboard.set_size_request(-1, style.GRID_CELL_SIZE)
        self.table.resize(width, height - style.GRID_CELL_SIZE)
        self.show_all()

    def _change_mode_bt(self, button):
        if button.get_active():
            self._change_mode(_MODE_CREATE)
            button.set_icon_name('player_play')
            button.set_tooltip(_('Play game'))
        else:
            self._change_mode(_MODE_PLAY)
            button.set_icon_name('view-source')
            button.set_tooltip(_('Edit game'))

    def _change_game_receiver(self, mode, grid, data, path):
        if mode == 'demo':
            game_name = os.path.basename(data.get('game_file', 'debug-demo'))
            game_file = os.path.join(os.path.dirname(__file__), 'demos',
                                     game_name).encode('ascii')
            self.game.model.read(game_file)

        if mode == 'art4apps':
            game_file = data['game_file']
            category = game_file[:game_file.find('_')]
            language = data['language']
            self.game.model.is_demo = True
            self.game.model.read_art4apps(category, language)

        if mode == 'file':
            self.game.model.read(self._files[path])

        if 'path' in self.game.model.data:
            data['path'] = self.game.model.data['path']
            data['pathimg'] = self.game.model.data['pathimg']
            data['pathsnd'] = self.game.model.data['pathsnd']
        self.game.load_remote(grid, data, mode, True)

    def set_data(self, blob):
        logging.debug("set_data %r" % list(blob.keys()))
        grid = blob['grid']
        data = blob['data']
        current_player = blob['current']
        path = blob['path']

        if 'zip' in blob:
            tmp_root = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],
                                    'instance')
            temp_dir = tempfile.mkdtemp(dir=tmp_root)
            os.chmod(temp_dir, 0o777)
            temp_file = os.path.join(temp_dir, 'game.zip')
            self._files[path] = temp_file
            f = open(temp_file, 'wb')

            f.write(base64.b64decode(blob['zip']))
            f.close()

        self._change_game_receiver(data['mode'], grid, data, path)

        for i in range(len(self.game.players)):
            self.game.increase_point(self.game.players[i],
                                     int(data.get(str(i), '0')))

        self.game.current_player = self.game.players[current_player]
        self.game.update_turn()

    def get_data(self):
        data = self.game.collect_data()
        path = data['game_file']

        blob = {
            "grid": self.game.get_grid(),
            "data": data,
            "current": self.game.players.index(self.game.current_player),
            "path": path
        }

        if data['mode'] == 'file':
            blob['zip'] = base64.b64encode(open(path, 'rb').read()).decode()

        logging.debug("get_data %r" % list(blob.keys()))
        return blob

    def read_file(self, file_path):
        if 'icon-color' in self.metadata:
            color = self.metadata['icon-color']
        else:
            color = profile.get_color().to_string()
        self.change_game(None, file_path, 4, 'file', self.metadata['title'],
                         color)

    def write_file(self, file_path):
        logging.debug('WRITE_FILE is_demo %s', self.game.model.is_demo)
        if self.game.model.is_demo:
            # if is a demo game only want keep the metadata
            self._jobject.set_file_path(None)
            raise NotImplementedError
            return
        if self.cardlist.pair_list_modified:
            self.cardlist.update_model(self.game.model)

        temp_img_folder = self.game.model.data['pathimg']
        temp_snd_folder = self.game.model.data['pathsnd']
        self.game.model.create_temp_directories()
        game_zip = zipfile.ZipFile(file_path, 'w')
        save_image_and_sound = True
        if 'origin' in self.game.model.data:
            if self.game.model.data['origin'] == 'art4apps':
                # we don't need save images and audio files
                # for art4apps games
                save_image_and_sound = False

        if save_image_and_sound:
            for pair in self.game.model.pairs:
                # aimg
                aimg = self.game.model.pairs[pair].get_property('aimg')
                if aimg is not None:
                    game_zip.write(os.path.join(temp_img_folder, aimg),
                                   os.path.join('images', aimg))

                # bimg
                bimg = self.game.model.pairs[pair].get_property('bimg')
                if bimg is not None:
                    game_zip.write(os.path.join(temp_img_folder, bimg),
                                   os.path.join('images', bimg))

                # asnd
                asnd = self.game.model.pairs[pair].get_property('asnd')
                if asnd is not None:
                    if os.path.exists(os.path.join(temp_snd_folder, asnd)):
                        game_zip.write(os.path.join(temp_snd_folder, asnd),
                                       os.path.join('sounds', asnd))

                # bsnd
                bsnd = self.game.model.pairs[pair].get_property('bsnd')
                if bsnd is not None:
                    if os.path.exists(os.path.join(temp_snd_folder, bsnd)):
                        game_zip.write(os.path.join(temp_snd_folder, bsnd),
                                       os.path.join('sounds', bsnd))

        self.game.model.game_path = self.game.model.temp_folder
        self.game.model.data['name'] = str(self.get_title())
        self.game.model.write()
        game_zip.write(os.path.join(self.game.model.temp_folder, 'game.xml'),
                       'game.xml')
        game_zip.close()
        self.metadata['mime_type'] = 'application/x-memorize-project'

    def _complete_close(self):
        self._remove_temp_files()
        Activity._complete_close(self)

    def _remove_temp_files(self):
        tmp_root = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'], 'instance')
        for root, dirs, files in os.walk(tmp_root, topdown=False):
            for name in files:
                os.remove(os.path.join(root, name))
            for name in dirs:
                os.rmdir(os.path.join(root, name))

    def _change_mode(self, mode):
        logging.debug("Change mode %s" % mode)
        if mode == _MODE_CREATE:
            if self.play_mode:

                self.box.remove(self.scoreboard)
                self.box.remove(self.table)
                self.createcardpanel.update_orientation()
                self.box.pack_start(self.createcardpanel, True, True, 0)
                self.box.pack_start(self.cardlist, False, False, 0)
                self.cardlist.load_game(self.game)
                self.game.model.create_temp_directories()
                self.createcardpanel.set_temp_folder(
                    self.game.model.temp_folder)
            self.play_mode = False
        else:
            if self.game.model.modified:
                self.cardlist.update_model(self.game.model)
                self.save()
                self.game.reset_game()
                self.table.change_game(None, self.game.model.data,
                                       self.game.model.grid)
                self.game.model.modified = False

            if not self.play_mode:
                self.box.remove(self.createcardpanel)
                self.box.remove(self.cardlist)

            if self.play_mode in (False, None):
                self.box.pack_start(self.table, True, True, 0)
                self.box.pack_start(self.scoreboard, False, False, 0)
            self.play_mode = True
        self._memorizeToolbarBuilder.update_controls(mode == _MODE_PLAY)
        self._createToolbarBuilder.update_controls(mode == _MODE_CREATE)

    def change_game(self,
                    widget,
                    game_name,
                    size,
                    mode,
                    title=None,
                    color=None):
        logging.debug('Change game %s', game_name)
        self.game.change_game(widget, game_name, size, mode, title, color)
        self.cardlist.game_loaded = False

    def change_equal_pairs(self, widget, state):
        self.cardlist.update_model(self.game.model)
        self.createcardpanel.change_equal_pairs(widget, state)

    def _focus_in(self, event, data=None):
        self.game.audio.play()

    def _focus_out(self, event, data=None):
        self.game.audio.pause()

    def _cleanup_cb(self, data=None):
        self.game.audio.stop()