From 8a2defe196f4f1d1edd7845ca871d58a57c717e1 Mon Sep 17 00:00:00 2001 From: Simon Schampijer Date: Thu, 12 Jul 2007 20:45:24 +0000 Subject: Started to merge the memosono game and the meorize game into one activity. --- diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..ac6f8ff --- /dev/null +++ b/AUTHORS @@ -0,0 +1,2 @@ +Simon Schampijer - simon@schampijer.de +Muriel de Souza Godoi - muriel@laptop.org diff --git a/NEWS b/NEWS index f9acee0..392dffb 100644 --- a/NEWS +++ b/NEWS @@ -1 +1,3 @@ +Starting to merge the memosono game and the memorize game into one activity. (erikos) + First version of the collaborated memosono. (erikos) diff --git a/activity.py b/activity.py new file mode 100755 index 0000000..0eb203c --- /dev/null +++ b/activity.py @@ -0,0 +1,258 @@ +#! /usr/bin/env python +# +# Copyright (C) 2007, One Laptop Per Child +# +# Muriel de Souza Godoi - muriel@laptop.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import logging +from gettext import gettext as _ + +import dbus +import gtk +import pygtk +import telepathy +import telepathy.client + +from sugar.activity.activity import Activity, ActivityToolbox +from sugar.presence import presenceservice +import sugar.logger + +from sugar.graphics.xocolor import XoColor +from sugar import profile +import cardtable +import scoreboard +import game +import messenger +import memorizetoolbar + +# will eventually be imported from telepathy.tubes or something +from tubeconn import TubeConnection + +SERVICE = "org.laptop.Memorize" +IFACE = SERVICE +PATH = "/org/laptop/Memorize" + +_logger = logging.getLogger('memorize-activity') + +class MemorizeActivity(Activity): + def __init__(self, handle): + Activity.__init__(self, handle) + + self.set_title('Memorize Activity') + + self.table = cardtable.CardTable() + self.scoreboard = scoreboard.Scoreboard() + self.game = game.MemorizeGame() + + hbox = gtk.HBox(False) + hbox.pack_start(self.scoreboard, False, False) + hbox.pack_start(self.table) + + toolbox = ActivityToolbox(self) + activity_toolbar = toolbox.get_activity_toolbar() + + self._memorizeToolbar = memorizetoolbar.MemorizeToolbar(self) + toolbox.add_toolbar('Games', self._memorizeToolbar) + self._memorizeToolbar.show() + + self.set_toolbox(toolbox) + toolbox.show() + self.set_canvas(hbox) + + self.table.connect('key-press-event', self.table.key_press_event) + self.connect('shared', self._shared_cb) + + self.table.connect('card-flipped', self.game.card_flipped) + self.table.connect('card-highlighted', self.game.card_highlighted) + + 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('change_game', self._memorizeToolbar.update_toolbar) + 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('highlight-card', self.table.highlight_card) + 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.show_all() + + # Get the Presence Service + self.pservice = presenceservice.get_instance() + bus = dbus.Bus() + name, path = self.pservice.get_preferred_connection() + self.tp_conn_name = name + self.tp_conn_path = path + self.conn = telepathy.client.Connection(name, path) + self.initiating = None + + # Buddy object for you + owner = self.pservice.get_owner() + self.owner = owner + self.current = 0 + self.game.set_myself(self.owner) + # Owner.props.key + if self._shared_activity: + # We are joining the activity + self.connect('joined', self._joined_cb) + if self.get_shared(): + # We've already joined + self._joined_cb() + else: + _logger.debug("buddy joined - __init__: %s", self.owner.props.nick) + self.game.load_game('numbers', 4) + self.game.add_buddy(self.owner) + + def restart(self, widget): + self.game.reset() + + def change_game(self, game_name, size): + self.game.change_game(game_name, size) + + def _shared_cb(self, activity): + _logger.debug('My activity was shared') + self.initiating = True + self._setup() + + for buddy in self._shared_activity.get_joined_buddies(): + pass # Can do stuff with newly acquired buddies here + + self._shared_activity.connect('buddy-joined', self._buddy_joined_cb) + self._shared_activity.connect('buddy-left', self._buddy_left_cb) + + _logger.debug('This is my activity: making a tube...') + id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferTube( + telepathy.TUBE_TYPE_DBUS, SERVICE, {}) + + def _setup(self): + if self._shared_activity is None: + _logger.error('Failed to share or join activity') + return + + bus_name, conn_path, channel_paths =\ + self._shared_activity.get_channels() + + # Work out what our room is called and whether we have Tubes already + room = None + tubes_chan = None + text_chan = None + for channel_path in channel_paths: + channel = telepathy.client.Channel(bus_name, channel_path) + htype, handle = channel.GetHandle() + if htype == telepathy.HANDLE_TYPE_ROOM: + _logger.debug('Found our room: it has handle#%d "%s"', + handle, self.conn.InspectHandles(htype, [handle])[0]) + room = handle + ctype = channel.GetChannelType() + if ctype == telepathy.CHANNEL_TYPE_TUBES: + _logger.debug('Found our Tubes channel at %s', channel_path) + tubes_chan = channel + elif ctype == telepathy.CHANNEL_TYPE_TEXT: + _logger.debug('Found our Text channel at %s', channel_path) + text_chan = channel + + if room is None: + _logger.error("Presence service didn't create a room") + return + if text_chan is None: + _logger.error("Presence service didn't create a text channel") + return + + # Make sure we have a Tubes channel - PS doesn't yet provide one + if tubes_chan is None: + _logger.debug("Didn't find our Tubes channel, requesting one...") + tubes_chan = self.conn.request_channel(telepathy.CHANNEL_TYPE_TUBES, + telepathy.HANDLE_TYPE_ROOM, room, True) + + self.tubes_chan = tubes_chan + self.text_chan = text_chan + + tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube', + self._new_tube_cb) + + def _list_tubes_reply_cb(self, tubes): + for tube_info in tubes: + self._new_tube_cb(*tube_info) + + def _list_tubes_error_cb(self, e): + _logger.error('ListTubes() failed: %s', e) + + def _joined_cb(self, activity): + if not self._shared_activity: + return + + for buddy in self._shared_activity.get_joined_buddies(): + _logger.debug("buddy joined - _joined_cb: %s", buddy.props.nick) + self.game.add_buddy(buddy) + + _logger.debug('Joined an existing shared activity') + self.initiating = False + self._setup() + + self._shared_activity.connect('buddy-joined', self._buddy_joined_cb) + self._shared_activity.connect('buddy-left', self._buddy_left_cb) + + _logger.debug('This is not my activity: waiting for a tube...') + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( + reply_handler=self._list_tubes_reply_cb, + error_handler=self._list_tubes_error_cb) + + def _new_tube_cb(self, id, initiator, type, service, params, state): + _logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' + 'params=%r state=%d', id, initiator, type, service, + params, state) + + if (type == telepathy.TUBE_TYPE_DBUS and + service == SERVICE): + if state == telepathy.TUBE_STATE_LOCAL_PENDING: + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptTube(id) + + self.tube_conn = TubeConnection(self.conn, + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], + id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) + + print 'Tube created' + self.messenger = messenger.Messenger(self.tube_conn, self.initiating, self._get_buddy, self.game) + self.game.connect('flip-card-signal', self.messenger.flip) + self.game.connect('change_game_signal', self.messenger.change_game) + + def _get_buddy(self, cs_handle): + """Get a Buddy from a channel specific handle.""" + group = self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP] + my_csh = group.GetSelfHandle() + if my_csh == cs_handle: + handle = self.conn.GetSelfHandle() + else: + handle = group.GetHandleOwners([cs_handle])[0] + assert handle != 0 + return self.pservice.get_buddy_by_telepathy_handle(self.tp_conn_name, + self.tp_conn_path, handle) + + def _buddy_joined_cb (self, activity, buddy): + if buddy <> self.owner: + _logger.debug("buddy joined - _buddy_joined_cb: %s", buddy.props.nick) + self.game.add_buddy(buddy) + + def _buddy_left_cb (self, activity, buddy): + _logger.debug("buddy left - _buddy_left_cb: %s", buddy.props.nick) + self.game.rem_buddy(buddy) \ No newline at end of file diff --git a/activity/activity.info b/activity/activity.info index 2949e26..10dab42 100644 --- a/activity/activity.info +++ b/activity/activity.info @@ -1,6 +1,7 @@ [Activity] -name = Memosono -service_name = org.laptop.Memosono -class = memosonoactivity.MemosonoActivity -icon = activity-memosono +name = Memorize +service_name = org.laptop.Memorize +class = activity.MemorizeActivity +icon = activity-memorize activity_version = 11 +show_launcher = yes diff --git a/buddiespanel.py b/buddiespanel.py deleted file mode 100644 index e3361ad..0000000 --- a/buddiespanel.py +++ /dev/null @@ -1,197 +0,0 @@ -import gtk -import hippo -import math - -from sugar.graphics.canvasicon import CanvasIcon -from sugar.graphics.xocolor import XoColor -from sugar.graphics import color -from sugar.graphics import style -from sugar.graphics import units - - -class BuddyPlayer(hippo.CanvasBox, hippo.CanvasItem): - __gtype_name__ = 'BuddyPlayer' - _BORDER_DEFAULT = units.points_to_pixels(1.0) - - def __init__(self, buddy, **kargs): - hippo.CanvasBox.__init__(self, **kargs) - - self._radius = units.points_to_pixels(5) - self.props.border_color = 0 - self.props.background_color = 0 - self.props.orientation = hippo.ORIENTATION_VERTICAL - self.props.border = self._BORDER_DEFAULT - self.props.border_left = self._radius - self.props.border_right = self._radius - - buddy_color = buddy.props.color - if not buddy_color: - buddy_color = "#000000,#ffffff" - - self.icon = CanvasIcon( - icon_name='theme:stock-buddy', - xo_color=XoColor(buddy_color)) - - nick = buddy.props.nick - if not nick: - nick = "" - self.name = hippo.CanvasText(text=nick, size_mode=hippo.CANVAS_SIZE_WRAP_WORD, color=color.WHITE.get_int()) - - self.append(self.icon) - self.append(self.name) - - - def do_paint_background(self, cr, damaged_box): - [width, height] = self.get_allocation() - - x = self._BORDER_DEFAULT / 2 - y = self._BORDER_DEFAULT / 2 - width -= self._BORDER_DEFAULT - height -= self._BORDER_DEFAULT - - cr.move_to(x + self._radius, y); - cr.arc(x + width - self._radius, y + self._radius, - self._radius, math.pi * 1.5, math.pi * 2); - cr.arc(x + width - self._radius, x + height - self._radius, - self._radius, 0, math.pi * 0.5); - cr.arc(x + self._radius, y + height - self._radius, - self._radius, math.pi * 0.5, math.pi); - cr.arc(x + self._radius, y + self._radius, self._radius, - math.pi, math.pi * 1.5); - - hippo.cairo_set_source_rgba32(cr, self.props.background_color) - cr.fill() - - -class BuddiesPanel(hippo.CanvasBox): - _COLOR_ACTIVE = 50 - _COLOR_INACTIVE = 0 - - def __init__(self): - hippo.CanvasBox.__init__(self, spacing=4, padding=5, - orientation=hippo.ORIENTATION_VERTICAL) - - self.players_box = hippo.CanvasBox(spacing=4, padding=5, - orientation=hippo.ORIENTATION_VERTICAL) - - self.watchers_box = hippo.CanvasBox(spacing=4, padding=5, - orientation=hippo.ORIENTATION_VERTICAL) - - self.append(self.players_box) - self.append(hippo.CanvasWidget(widget=gtk.HSeparator())) - self.append(self.watchers_box, hippo.PACK_EXPAND) - - self.players = {} - self.watchers = {} - self.last_active = None - - def _create_buddy_vbox (self, buddy): - buddy_color = buddy.props.color - if not buddy_color: - buddy_color = "#000000,#ffffff" - - icon = CanvasIcon( - icon_name='theme:stock-buddy', - xo_color=XoColor(buddy_color)) - - nick = buddy.props.nick - if not nick: - nick = "" - name = hippo.CanvasText(text=nick, color=color.WHITE.get_int()) - - vbox = hippo.CanvasBox(padding=5) - vbox._radius = units.points_to_pixels(5) - vbox.props.border_color = 100 - vbox.props.background_color = 200 - vbox.props.orientation = hippo.ORIENTATION_VERTICAL - vbox.props.border = units.points_to_pixels(1.0) - vbox.props.border_left = vbox._radius - vbox.props.border_right = vbox._radius - - vbox.append(icon) - vbox.append(name) - - return vbox - - def add_watcher(self, buddy): - op = buddy.object_path() - if self.watchers.get(op) is not None: - return - # if the watcher is also a player, don't add them - if self.players.get(op) is not None: - return - - vbox = self._create_buddy_vbox (buddy) - - self.watchers_box.append(vbox) - - self.watchers[op] = vbox - - def add_player(self, buddy): - op = buddy.object_path() - if self.players.get(op) is not None: - return - # if the player is also a watcher, drop them from the watchers - widget = self.watchers.pop(op, None) - if widget is not None: - self.watchers_box.remove(widget) - - assert len(self.players) < 2 - - hbox = hippo.CanvasBox(spacing=4, padding=5, - orientation=hippo.ORIENTATION_HORIZONTAL) - hbox.append(BuddyPlayer(buddy)) - - count_font = style.FONT_BOLD.get_pango_desc() - count_font.set_size(30000) - count = hippo.CanvasText(text="0", color=color.WHITE.get_int(), - font_desc=count_font) - hbox.append(count) - - self.players_box.append(hbox) - - self.players[op] = hbox - - def set_is_playing(self, buddy): - hbox = self.players.get(buddy.object_path()) - bp = hbox.get_children()[0] - bp.props.background_color = self._COLOR_ACTIVE - bp.emit_paint_needed(0, 0, -1, -1) - if self.last_active is not None: - hbox = self.players.get(self.last_active.object_path()) - lbp = hbox.get_children()[0] - lbp.props.background_color = self._COLOR_INACTIVE - lbp.emit_paint_needed(0, 0, -1, -1) - self.last_active = buddy - - def set_count(self, buddy, val): - hbox = self.players.get(buddy.object_path()) - if hbox is None: - return - - count = hbox.get_children()[1] - count.props.text = str(val) - - def remove_watcher(self, buddy): - op = buddy.object_path() - widget = self.watchers[op] - if widget is None: - return - - self.watchers_box.remove(widget) - del self.watchers[op] - - # removing someone from the game entirely should also remove them - # from the players - self.remove_player(buddy) - - def remove_player(self, buddy): - op = buddy.object_path() - widget = self.players.get(op) - if widget is None: - return - - self.players_box.remove(widget) - del self.players[op] - - self.add_watcher(buddy) diff --git a/cardtable.py b/cardtable.py new file mode 100755 index 0000000..b9f96b5 --- /dev/null +++ b/cardtable.py @@ -0,0 +1,192 @@ +#! /usr/bin/env python +# +# Copyright (C) 2007, One Laptop Per Child +# +# Muriel de Souza Godoi - muriel@laptop.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import gtk, pygtk +import svgcard +import os +import time +import gobject +import math +import gc + +class CardTable(gtk.EventBox): + + __gsignals__ = { + 'card-flipped': (gobject.SIGNAL_RUN_FIRST, None, [int, gobject.TYPE_PYOBJECT]), + 'card-highlighted': (gobject.SIGNAL_RUN_FIRST, None, [int, gobject.TYPE_PYOBJECT]), + } + + def __init__(self): + gtk.EventBox.__init__(self) + + # Set table settings + self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#000000')) + self.table = gtk.Table() + self.table.grab_focus() + self.table.set_flags(gtk.CAN_FOCUS) + self.table.set_flags(gtk.CAN_DEFAULT) + self.table.set_row_spacings(11) + self.table.set_col_spacings(11) + self.table.set_border_width(11) + self.table.set_resize_mode(gtk.RESIZE_IMMEDIATE) + self.set_property('child', self.table) + self.fist_load = True + + def load_game(self, widget, data, grid): + self.data = data + self.cards_data = grid + self.size = int(math.ceil(math.sqrt(len(grid)))) + self.table.resize(self.size, self.size) + self.card_size = self.get_card_size(self.size) + self.cards = {} + self.cd2id = {} + self.id2cd = {} + self.dict = {} + self.selected_card = [0, 0] + self.flipped_card = -1 + self.table_positions = {} + + # Build the table + if data['divided']=='True': + text1 = str(self.data['face1']) + text2 = str(self.data['face2']) + else: + text1 = str(self.data['face']) + text2 = str(self.data['face']) + buffer_card_1 = svgcard.SvgCard(-1, {'front_border':{'opacity':'0'}, 'front_h_border':{'opacity':'0.5'}, 'back_text':{'card_text':text1}}, {}, None, self.card_size) + buffer_card_2 = svgcard.SvgCard(-1, {'front_border':{'opacity':'0'}, 'front_h_border':{'opacity':'0.5'}, 'back_text':{'card_text':text2}}, {}, None, self.card_size) + + self.game_dir = os.path.join(os.path.dirname(__file__), 'games') + x = 0 + y = 0 + id = 0 + for card in self.cards_data: + if card[1] <> '': + jpg = os.path.join(self.game_dir, self.data['game_name']+'/images/'+str(card[1])) + else: + jpg = None + props = {} + props['front_border'] = {'opacity':'1'} + props['front_h_border'] ={'opacity':'1'} + props['front_text']= {'card_text':card[3], 'card_line1':card[4], 'card_line2':card[5], 'card_line3':card[6], 'card_line4':card[7]} + + if card[0]== '1': + buffer_card = buffer_card_1 + else: + buffer_card = buffer_card_2 + + card = svgcard.SvgCard(id, props, buffer_card.get_cache(), jpg, self.card_size) + card.connect('enter-notify-event', self.mouse_event, [x, y]) + card.connect("button-press-event", self.flip_card_mouse, id) + self.table_positions[(x, y)]=1 + self.cd2id[card] = id + self.id2cd[id] = card + self.cards[(x, y)] = card + self.dict[id] = (x, y) + self.table.attach(card, x, x+1, y, y+1, gtk.SHRINK, gtk.SHRINK) + #button = gtk.Button('button') + #button.show() + #self.table.attach(button, x, x+1, y, y+1, gtk.SHRINK, gtk.SHRINK) + x += 1 + if x == self.size: + x = 0 + y +=1 + id += 1 + self.fist_load = False + gc.collect() + + def change_game(self, widget, data, grid): + if not self.fist_load: + for card in self.cards.values(): + self.table.remove(card) + del card + self.load_game(None, data, grid) + + def get_card_size(self, size_table): + x = (780 - (11*size_table))/size_table + return x + + def mouse_event(self, widget, event, coord): + #self.table.grab_focus() + card = self.cards[coord[0], coord[1]] + id = self.cd2id.get(card) + self.emit('card-highlighted', id, True) + self.selected_card = (coord[0], coord[1]) + + def key_press_event(self, widget, event): + #self.table.grab_focus() + x= self.selected_card[0] + y= self.selected_card[1] + + if event.keyval in (gtk.keysyms.Left, gtk.keysyms.KP_Left,gtk.keysyms.a): + if self.table_positions.has_key((x-1, y)): + card = self.cards[x-1, y] + id = self.cd2id.get(card) + self.emit('card-highlighted', id, False) + + elif event.keyval in (gtk.keysyms.Right, gtk.keysyms.KP_Right,gtk.keysyms.d): + if self.table_positions.has_key((x+1, y)): + card = self.cards[x+1, y] + id = self.cd2id.get(card) + self.emit('card-highlighted', id, False) + + elif event.keyval in (gtk.keysyms.Up, gtk.keysyms.KP_Up,gtk.keysyms.w): + if self.table_positions.has_key((x, y-1)): + card = self.cards[x, y-1] + id = self.cd2id.get(card) + self.emit('card-highlighted', id, False) + + elif event.keyval in (gtk.keysyms.Down, gtk.keysyms.KP_Down,gtk.keysyms.s): + if self.table_positions.has_key((x, y+1)): + card = self.cards[x, y+1] + id = self.cd2id.get(card) + self.emit('card-highlighted', id, False) + + elif event.keyval in (gtk.keysyms.space,gtk.keysyms.KP_Page_Down): + card = self.cards[x, y] + self.card_flipped(card) + + def flip_card_mouse(self, widget, event, id): + position = self.dict[id] + card = self.cards[position] + self.card_flipped(card) + + def card_flipped(self, card): + if not card.is_flipped(): + id = self.cd2id[card] + self.emit('card-flipped', id, False) + + def set_border(self, widget, id, stroke_color, fill_color): + self.id2cd[id].set_border(stroke_color, fill_color) + + def flop_card(self, widget, id): + self.id2cd.get(id).flop() + + def flip_card(self, widget, id): + self.id2cd.get(id).flip() + + def highlight_card(self, widget, id, status): + self.selected_card = self.dict.get(id) + self.id2cd.get(id).set_highlight(status) + + def reset(self, widget): + for id in self.id2cd.keys(): + self.id2cd[id].reset() diff --git a/controller.py b/controller.py deleted file mode 100644 index 4970d87..0000000 --- a/controller.py +++ /dev/null @@ -1,250 +0,0 @@ -import logging - -import gobject -import gtk -import os - -from dbus import Interface -from dbus.service import method, signal -from dbus.gobject_service import ExportedGObject - -from model import Model -from csound.csoundserver import CsoundServer - -SERVICE = "org.freedesktop.Telepathy.Tube.Memosono" -IFACE = SERVICE -PATH = "/org/freedesktop/Telepathy/Tube/Memosono" - - -GAME_PATH = os.path.join(os.path.dirname(__file__),'games/drumgit') -IMAGES_PATH = os.path.join(os.path.dirname(__file__),'games/drumgit/images') -SOUNDS_PATH = os.path.join(os.path.dirname(__file__),'games/drumgit/sounds') -MAX_NUM_PLAYERS = 2 - -_logger = logging.getLogger('controller') - - -class Controller(ExportedGObject): - ''' Networked Controller which is the core component of the activity. It - handles the communication with the components (model, view) and with the - other players over the network. - ''' - def __init__(self, tube, playview, is_initiator, buddies_panel, info_panel, - owner, get_buddy, activity): - super(Controller, self).__init__(tube, PATH) - self.tube = tube - self.pv = playview - self.is_initiator = is_initiator - self.entered = False - self.buddies_panel = buddies_panel - self.info_panel = info_panel - self.owner = owner - self._get_buddy = get_buddy - self.activity = activity - self.numplayers = 0 - self.turn = 0 - - self.cs = CsoundServer() - gtk.gdk.threads_init() - self.cs.start() - - if self.is_initiator: - self.init_game() - - for tile in self.pv.tiles: - tile.connect('button-press-event', self._button_press_cb, self.pv.tiles.index(tile)) - - - self.tube.watch_participants(self.participant_change_cb) - - - def participant_change_cb(self, added, removed): - - _logger.debug('adding participants: %r', added) - _logger.debug('removing participants: %r', removed) - - for handle, bus_name in added: - buddy = self._get_buddy(handle) - if buddy is not None: - _logger.debug('buddy %r was added', buddy) - if self.numplayers < MAX_NUM_PLAYERS: - self.buddies_panel.add_player(buddy) - self.numplayers+=1 - if self.is_initiator: - self.buddies_panel.set_is_playing(buddy) - self.model.players[self.tube.participants[handle]] = [buddy.props.nick, 0] - _logger.debug('list of players: %s', self.model.players) - else: - self.info_panel.show('we are already two players') - - for handle in removed: - buddy = self._get_buddy(handle) - if buddy is not None: - _logger.debug('-----> buddy %r was removed', buddy) - self.buddies_panel.remove_player(buddy) - self.numplayers-=1 - if self.is_initiator: - try: - del self.model.players[self.tube.participants[handle]] - except ValueError: - # already absent - pass - - if not self.entered: - self.playerid = self.tube.get_unique_name() - self.tube.add_signal_receiver(self.info_cb, 'Info', IFACE, - path=PATH, sender_keyword='sender') - self.tube.add_signal_receiver(self.turn_cb, 'Turn', IFACE, - path=PATH, sender_keyword='sender') - self.tube.add_signal_receiver(self.flip_cb, 'Flip', IFACE, - path=PATH, sender_keyword='sender') - self.tube.add_signal_receiver(self.play_cb, 'Play', IFACE, - path=PATH, sender_keyword='sender') - self.tube.add_signal_receiver(self.points_cb, 'Points', IFACE, - path=PATH, sender_keyword='sender') - - self.entered = True - - if self.is_initiator: - if len(self.model.players) == 2 and self.model.started == 0: - _logger.debug('start the game') - self.Info('start the game') - self.model.started = 1 - self.change_turn() - - def init_game(self): - _logger.debug('I am the initiator, so making myself the leader of the game.') - self.model = Model(GAME_PATH, os.path.dirname(__file__)) - self.model.read('drumgit.mson') - self.model.def_grid() - self.tube.add_signal_receiver(self.selected_cb, 'Selected', IFACE, - path=PATH, sender_keyword='sender') - - - @signal(dbus_interface=IFACE, signature='n') - def Selected(self, tilenum): - """Signal that a tile has been selected""" - - def selected_cb(self, tilenum, sender=None): - _logger.debug('MA: %s flipped tile %d', sender, tilenum) - if self.model.grid[tilenum][2] == 1: - self.Info('selected already') - else: - pairkey, moch, state = self.model.grid[tilenum] - color = self.model.pairs[pairkey].props.color - - if moch == 0: - if self.model.pairs[pairkey].props.aimg != None: - img = os.path.join(IMAGES_PATH, self.model.pairs[pairkey].props.aimg) - self.Flip(tilenum, img, color) - if self.model.pairs[pairkey].props.asnd != None: - snd = os.path.join(SOUNDS_PATH, self.model.pairs[pairkey].props.asnd) - self.Play(tilenum, snd, color) - elif moch == 1: - if self.model.pairs[pairkey].props.bimg != None: - img = os.path.join(IMAGES_PATH, self.model.pairs[pairkey].props.bimg) - self.Flip(tilenum, img, color) - if self.model.pairs[pairkey].props.bsnd != None: - snd = os.path.join(SOUNDS_PATH, self.model.pairs[pairkey].props.bsnd) - self.Play(tilenum, snd, color) - - self.model.count+=1 - if self.model.count == 1: - self.model.selected = tilenum - self.model.grid[tilenum][2] = 1 - return - if self.model.count == 2: - self.model.count = 0 - # evaluate - if( self.model.same(tilenum, self.model.selected) == 1): - _logger.debug('MA: Tile(%d) and (%d) are the same', tilenum, self.model.selected) - self.model.grid[tilenum][2] = 1 - self.model.grid[self.model.selected][2] = 1 - - self.model.players[sender][1]+=1 - self.Points(sender, self.model.players[sender][1]) - self.Info('found pair, one more try') - else: - self.model.grid[tilenum][2] = 0 - self.model.grid[self.model.selected][2] = 0 - self.change_turn() - self.Info('pair does not match, next player') - gobject.timeout_add(2000, self._turn_back, tilenum, self.model.selected) - _logger.debug('Tile(%d) and (%d) are NOT the same', tilenum, self.model.selected) - - def _turn_back(self, tilenuma, tilenumb): - self.Flip(tilenuma, 'images/black.png', 100) - self.Flip(tilenumb, 'images/black.png', 100) - return False - - def change_turn(self): - if self.model.player_active < (len(self.model.players)-1): - self.model.player_active+=1 - else: - self.model.player_active = 0 - - key = self.model.players.keys()[self.model.player_active] - self.Turn(key, self.model.players[key][0]) - - - @signal(dbus_interface=IFACE, signature='nsn') - def Flip(self, tilenum, obj, color): - """Signal that a tile will be flipped""" - - def flip_cb(self, tilenum, obj, color, sender=None): - handle = self.tube.bus_name_to_handle[sender] - _logger.debug('Flipped tile(%d) from %s. Show image.', tilenum, sender) - self.pv.flip(tilenum, os.path.join(os.path.dirname(__file__), obj), color) - - - @signal(dbus_interface=IFACE, signature='nsn') - def Play(self, tilenum, obj, color): - """Signal that a sound will be played""" - - def play_cb(self, tilenum, obj, color, sender=None): - handle = self.tube.bus_name_to_handle[sender] - _logger.debug('Flipped tile(%d) from %s. Play sound.', tilenum, sender) - _logger.debug(' Sound: %s', obj) - self.cs.perform('i 108 0.0 3.0 "%s" 1 0.7 0.5 0'%(obj)) - - - @signal(dbus_interface=IFACE, signature='ss') - def Turn(self, playerid, name): - """Signal that it is the players turn""" - - def turn_cb(self, playerid, name, sender=None): - if self.playerid == playerid: - self.turn = 1 - else: - self.turn = 0 - - buddy = self._get_buddy(self.tube.bus_name_to_handle[playerid]) - self.buddies_panel.set_is_playing(buddy) - self.info_panel.show('hey %s it is your turn'%name) - - @signal(dbus_interface=IFACE, signature='sn') - def Points(self, player, points): - """Signal to update the points""" - - def points_cb(self, player, points, sender=None): - handle = self.tube.bus_name_to_handle[player] - buddy = self._get_buddy(handle) - self.buddies_panel.set_count(buddy, points) - - - @signal(dbus_interface=IFACE, signature='s') - def Info(self, msg): - """Signal that there is some game information""" - - def info_cb(self, msg, sender=None): - self.info_panel.show(msg) - - - def _button_press_cb(self, tile, event, tilenum=None): - if self.turn == 1: - self.Selected(tilenum) - else: - _logger.debug('Not my turn') - self.info_panel.show('Not my turn') - - diff --git a/game.py b/game.py new file mode 100755 index 0000000..493ce64 --- /dev/null +++ b/game.py @@ -0,0 +1,304 @@ +#! /usr/bin/env python +# +# Copyright (C) 2007, One Laptop Per Child +# +# Muriel de Souza Godoi - muriel@laptop.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import os +import random +import logging +import gobject +import time + +from sugar import profile +from dbus.service import method, signal +from dbus.gobject_service import ExportedGObject + +import gobject + +_logger = logging.getLogger('memorize-activity') + +SERVICE = "org.laptop.Memorize" +IFACE = SERVICE +PATH = "/org/laptop/Memorize" + + +class MemorizeGame(gobject.GObject): + + __gsignals__ = { + 'reset_scoreboard': (gobject.SIGNAL_RUN_FIRST, None, []), + 'reset_table': (gobject.SIGNAL_RUN_FIRST, None, []), + 'load_game': (gobject.SIGNAL_RUN_FIRST, None, [gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT]), + 'change_game': (gobject.SIGNAL_RUN_FIRST, None, [gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT]), + 'change_game_signal': (gobject.SIGNAL_RUN_FIRST, None, [gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT]), + 'set-border': (gobject.SIGNAL_RUN_FIRST, None, [int, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT]), + 'flip-card': (gobject.SIGNAL_RUN_FIRST, None, [int]), + 'flip-card-signal': (gobject.SIGNAL_RUN_FIRST, None, [int]), + 'flop-card': (gobject.SIGNAL_RUN_FIRST, None, [int]), + 'highlight-card': (gobject.SIGNAL_RUN_FIRST, None, [int, gobject.TYPE_PYOBJECT]), + 'add_buddy': (gobject.SIGNAL_RUN_FIRST, None, [gobject.TYPE_PYOBJECT, int]), + 'rem_buddy': (gobject.SIGNAL_RUN_FIRST, None, [gobject.TYPE_PYOBJECT]), + 'increase-score': (gobject.SIGNAL_RUN_FIRST, None, [gobject.TYPE_PYOBJECT]), + 'wait_mode_buddy': (gobject.SIGNAL_RUN_FIRST, None, [gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT]), + 'change-turn': (gobject.SIGNAL_RUN_FIRST, None, [gobject.TYPE_PYOBJECT]), + } + + def __init__(self): + gobject.GObject.__init__(self) + self.myself = None + self.players_score = {} + self.players = [] + self.waiting_players = [] + self.current_player = None + self.last_flipped = -1 + self.last_highlight = 1 + self.game_dir = os.path.join(os.path.dirname(__file__), 'games') + self.messenger = None + self.sentitive = True + + def load_game(self, game_name, size): + tuple = self.read_config(game_name, size) + self.data = tuple[0] + self.grid = tuple[1] + self.data['running'] = 'False' + self.emit('load_game', self.data, self.grid) + + def add_buddy(self, buddy, score = 0): + _logger.debug('Buddy %r was added to game', buddy.props.nick) + self.players.append(buddy) + self.players_score[buddy] = score + self.emit('add_buddy', buddy, score) + + if self.current_player == None: + self.current_player = buddy + self.change_turn() + + def rem_buddy(self, buddy): + _logger.debug('Buddy %r was removed to game', buddy.props.nick) + index = self.players.index(buddy) + del self.players[index] + del (self.players_score[buddy]) + if self.current_player == buddy and len(self.players) <> 0: + self.change_turn() + self.emit('rem_buddy', buddy) + + def change_turn(self): + if self.current_player == None: + self.current_player = self.players[0] + elif self.current_player == self.players[-1]: + self.current_player = self.players[0] + else: + self.current_player = self.players[self.players.index(self.current_player)+1] + self.set_sensitive(self.current_player == self.myself) + self.emit('change-turn', self.current_player) + + def card_flipped(self, widget, id, signal = False): + # Check if is my turn + if not self.sentitive and not signal: + return + + # Handle groups if needed + if self.data['divided'] == 'True': + if self.last_flipped == -1 and id >= (len(self.grid)/2): + return + if self.last_flipped <> -1 and id < (len(self.grid)/2): + return + self.data['running'] = 'True' + + # First card case + if self.last_flipped == -1: + self.last_flipped = id + self.grid[id][8] = 1 + self.emit('flip-card', id) + if not signal: + self.emit('flip-card-signal', id) + if self.data['divided'] == 'True': + self.card_highlighted(widget, -1, False) + + # Pair matched + elif self.grid[self.last_flipped][-1] == self.grid[id][-1]: + stroke_color, fill_color = self.current_player.props.color.split(',') + self.emit('set-border', id, stroke_color, fill_color) + self.emit('set-border', self.last_flipped, stroke_color, fill_color) + self.increase_point(self.current_player) + self.grid[id][8] = 1 + self.emit('flip-card', id) + if self.data['divided'] == 'True': + self.card_highlighted(widget, -1, False) + if not signal: + self.emit('flip-card-signal', id) + self.last_flipped = -1 + # Pair don't match + elif self.grid[self.last_flipped][-1] <> self.grid[id][-1]: + self.emit('flip-card', id) + if not signal: + self.emit('flip-card-signal', id) + self.grid[id][8] = 1 + time.sleep(2) + self.emit('flop-card', id) + self.grid[id][8] = 0 + self.emit('flop-card', self.last_flipped) + if self.data['divided'] == 'True': + self.card_highlighted(widget, -1, False) + # self.emit('highlight-card', id, True) + self.grid[self.last_flipped][8] = 0 + self.last_flipped = -1 + self.change_turn() + + def card_highlighted(self, widget, id, mouse): + if id == -1: + self.last_highlight = 1 + self.emit('highlight-card', self.last_highlight, False) + return + + if not self.sentitive: + return + if self.data['divided'] == 'True': + if self.last_flipped == -1 and id >= (len(self.grid)/2): + return + if self.last_flipped <> -1 and id < (len(self.grid)/2): + return + self.emit('highlight-card', self.last_highlight, False) + if mouse and self.grid[id][8]==0: + self.emit('highlight-card', id, True) + if not mouse: + self.emit('highlight-card', id, True) + + self.last_highlight = id + + def increase_point(self, buddy): + self.players_score[buddy] += 1 + self.emit('increase-score', buddy) + + def read_config(self, game_name, size = 100): + filename = os.path.join(self.game_dir, game_name +'/'+game_name+'.mem') + # seed = random.randint(0, 14567) + temp1 = [] + temp2 = [] + grid = [] + data = {} + # set random seed + random.seed() + filecheck = filename.split('.') + if filecheck[2] != 'mem': + logging.error('File format of %s'%filename) + else: + fd = open(filename, 'r') + if fd == None: + logging.error(' Reading setup file %s'%filename) + else:# set random seed + logging.info(' Read setup for memosono from file %s'%filename) + lines = fd.readlines() + index = 0 + + # Load variables + while lines[index][0] != '#': + zw = lines[index].split('=') + zw[1] = zw[1][:-1] + if len(zw) is not 0: + data[zw[0]]=zw[1] + index += 1 + index += 1 + data['size'] = str(size) + + # Load cards data + tile_number = 0 + card_num = len(lines)-index + while tile_number < card_num and tile_number <= int((size*size)/2)-1: + zw = lines[index].split(',') + if len(zw) is not 0: + temp1.append(zw[:8]+[ 0, 0, tile_number]) + temp2.append(zw[8:]+[ 0, 0, tile_number]) + tile_number += 1 + index += 1 + fd.close() + + # Shuffle cards order + if data['divided']=='True': + random.shuffle(temp1) + random.shuffle(temp2) + temp1.extend(temp2) + else: + temp1.extend(temp2) + random.shuffle(temp1) + + return data, temp1 + + def get_grid(self): + return self.grid + + def get_data(self): + return self.data + + def change_game(self, game_name, size): + tuple = self.read_config(game_name, size) + data = tuple[0] + grid = tuple[1] + self.load_remote(grid, data, False) + + + def load_remote(self, grid, data, signal = False): + self.grid = grid + self.data = data + self.emit('reset_scoreboard') + self.emit('change_game', self.get_data(), self.get_grid()) + if not signal: + self.emit('change_game_signal', self.get_grid(), self.get_data(), self.waiting_players) + for buddy in self.players: + self.players_score[buddy] = 0 + self.current_player = None + self.last_flipped = -1 + self.last_highlight = 1 + self.change_turn() + self.data['running'] = 'False' + + def set_messenger(self, messenger): + self.messenger = messenger + + def set_sensitive(self, status): + self.sentitive = status + if not status: + self.emit('highlight-card', self.last_highlight, False) + else: + self.emit('highlight-card', self.last_highlight, True) + def get_sensitive(self): + return self.sentitive + + def get_current_player(self): + return self.current_player + + def set_myself(self, buddy): + self.myself = buddy + + def add_to_waiting_list(self,buddy): + self.players.remove(buddy) + self.waiting_players.append(buddy) + self.emit('wait_mode_buddy',buddy,True) + + def rem_to_waiting_list(self,buddy): + self.waiting_players.remove(buddy) + self.players.append(buddy) + self.emit('wait_mode_buddy',buddy,False) + + def load_waiting_list(self,list): + for buddy in list: + self.add_to_waiting_list(buddy) + + def empty_waiting_list(self): + for buddy in self.waiting_players: + self.rem_to_waiting_list(buddy) \ No newline at end of file diff --git a/games/addition/addition.mem b/games/addition/addition.mem new file mode 100755 index 0000000..9e41bcc --- /dev/null +++ b/games/addition/addition.mem @@ -0,0 +1,24 @@ +game_name=addition +score_sound=score.wav +win_sound=score.wav +divided=false +face= +# Cards +,,,,,,1+1,,,,,2,,,, +,,,,,,1+2,,,,,3,,,, +,,,,,,2+2,,,,,4,,,, +,,,,,,2+3,,,,,5,,,, +,,,,,,3+3,,,,,6,,,, +,,,,,,3+4,,,,,7,,,, +,,,,,,4+4,,,,,8,,,, +,,,,,,4+5,,,,,9,,,, +,,,,,,5+5,,,,,10,,,, +,,,,,,5+6,,,,,11,,,, +,,,,,,6+6,,,,,12,,,, +,,,,,,6+7,,,,,13,,,, +,,,,,,7+7,,,,,14,,,, +,,,,,,7+8,,,,,15,,,, +,,,,,,8+8,,,,,16,,,, +,,,,,,8+9,,,,,17,,,, +,,,,,,9+9,,,,,18,,,, +,,,,,,10+9,,,,,19,,,, \ No newline at end of file diff --git a/games/drumgit/drumgit.mem b/games/drumgit/drumgit.mem new file mode 100755 index 0000000..482005a --- /dev/null +++ b/games/drumgit/drumgit.mem @@ -0,0 +1,30 @@ +game_name=drumgit +score_sound=score.wav +win_sound=score.wav +divided=False +face= +# Cards +,drumkit1_b.jpg,beat1_a.wav,,,,,,,drumkit1_b.jpg,beat1_a.wav,,,,, +,drumkit2_b.jpg,beat1_b.wav,,,,,,,drumkit2_b.jpg,beat1_b.wav,,,,, +,drumkit3_b.jpg,beat1_c.wav,,,,,,,drumkit3_b.jpg,beat1_c.wav,,,,, +,drumkit4_b.jpg,beat8.wav,,,,,,,drumkit4_b.jpg,beat8.wav,,,,, +,drumkit5_b.jpg,beat2.wav,,,,,,,drumkit5_b.jpg,beat2.wav,,,,, +,drumkit6_b.jpg,beat3.wav,,,,,,,drumkit6_b.jpg,beat3.wav,,,,, +,drumkit7_b.jpg,beat4.wav,,,,,,,drumkit7_b.jpg,beat4.wav,,,,, +,drumkit8_b.jpg,beat14.wav,,,,,,,drumkit8_b.jpg,beat14.wav,,,,, +,drumkit9_b.jpg,beat6_2.wav,,,,,,,drumkit9_b.jpg,beat6_2.wav,,,,, +,drumkit10_b.jpg,beat16.wav,,,,,,,drumkit10_b.jpg,beat16.wav,,,,, +,drumkit11_b.jpg,beat17.wav,,,,,,,drumkit11_b.jpg,beat17.wav,,,,, +,drumkit12_b.jpg,beat10.wav,,,,,,,drumkit12_b.jpg,beat10.wav,,,,, +,guitar1_2.jpg,bending_a.wav,,,,,,,guitar1_2.jpg,bending_a.wav,,,,, +,guitar2_2.jpg,bending_b.wav,,,,,,,guitar2_2.jpg,bending_b.wav,,,,, +,guitar3_2.jpg,flashcomp2a.wav,,,,,,,guitar3_2.jpg,flashcomp2a.wav,,,,, +,guitar4_2.jpg,flashcomp2b.wav,,,,,,,guitar4_2.jpg,flashcomp2b.wav,,,,, +,guitar5_2.jpg,gedaempft.wav,,,,,,,guitar5_2.jpg,gedaempft.wav,,,,, +,guitar6_2.jpg,ungedaempft.wav,,,,,,,guitar6_2.jpg,ungedaempft.wav,,,,, +,guitar7_2.jpg,jimi4.wav,,,,,,,guitar7_2.jpg,jimi4.wav,,,,, +,guitar8_2.jpg,git_hit1.wav,,,,,,,guitar8_2.jpg,git_hit1.wav,,,,, +,guitar9_2.jpg,git_hit4.wav,,,,,,,guitar9_2.jpg,git_hit4.wav,,,,, +,guitar10_2.jpg,guitcello.wav,,,,,,,guitar10_2.jpg,guitcello.wav,,,,, +,guitar11_2.jpg,flasholet4.wav,,,,,,,guitar11_2.jpg,flasholet4.wav,,,,, +,guitar12_2.jpg,jimi1.wav,,,,,,,guitar12_2.jpg,jimi1.wav,,,,, \ No newline at end of file diff --git a/games/letters1/letters1.mem b/games/letters1/letters1.mem new file mode 100755 index 0000000..cda6e27 --- /dev/null +++ b/games/letters1/letters1.mem @@ -0,0 +1,25 @@ +game_name=letters1 +score_sound=score.wav +win_sound=score.wav +divided=True +face1=1 +face2=2 +# Cards +1,,,A,,,,,2,,,a,,,, +1,,,E,,,,,2,,,e,,,, +1,,,I,,,,,2,,,i,,,, +1,,,O,,,,,2,,,o,,,, +1,,,U,,,,,2,,,u,,,, +1,,,B,,,,,2,,,b,,,, +1,,,C,,,,,2,,,c,,,, +1,,,D,,,,,2,,,d,,,, +1,,,F,,,,,2,,,f,,,, +1,,,G,,,,,2,,,g,,,, +1,,,H,,,,,2,,,h,,,, +1,,,J,,,,,2,,,j,,,, +1,,,K,,,,,2,,,k,,,, +1,,,L,,,,,2,,,l,,,, +1,,,M,,,,,2,,,m,,,, +1,,,N,,,,,2,,,n,,,, +1,,,P,,,,,2,,,p,,,, +1,,,Q,,,,,2,,,q,,,, \ No newline at end of file diff --git a/games/letters2/letters2.mem b/games/letters2/letters2.mem new file mode 100755 index 0000000..ff806a9 --- /dev/null +++ b/games/letters2/letters2.mem @@ -0,0 +1,25 @@ +game_name=letters2 +score_sound=score.wav +win_sound=score.wav +divided=True +face1=1 +face2=2 +# Cards +1,,,A,,,,,2,,,a,,,, +1,,,E,,,,,2,,,e,,,, +1,,,I,,,,,2,,,i,,,, +1,,,O,,,,,2,,,o,,,, +1,,,U,,,,,2,,,u,,,, +1,,,L,,,,,2,,,l,,,, +1,,,M,,,,,2,,,m,,,, +1,,,N,,,,,2,,,n,,,, +1,,,P,,,,,2,,,p,,,, +1,,,Q,,,,,2,,,q,,,, +1,,,R,,,,,2,,,r,,,, +1,,,S,,,,,2,,,s,,,, +1,,,T,,,,,2,,,t,,,, +1,,,V,,,,,2,,,v,,,, +1,,,W,,,,,2,,,w,,,, +1,,,X,,,,,2,,,x,,,, +1,,,Y,,,,,2,,,y,,,, +1,,,Z,,,,,2,,,z,,,, \ No newline at end of file diff --git a/games/numbers/images/01x.jpg b/games/numbers/images/01x.jpg new file mode 100755 index 0000000..4b8b35a --- /dev/null +++ b/games/numbers/images/01x.jpg Binary files differ diff --git a/games/numbers/images/02x.jpg b/games/numbers/images/02x.jpg new file mode 100755 index 0000000..bfd6f1b --- /dev/null +++ b/games/numbers/images/02x.jpg Binary files differ diff --git a/games/numbers/images/03x.jpg b/games/numbers/images/03x.jpg new file mode 100755 index 0000000..44329a2 --- /dev/null +++ b/games/numbers/images/03x.jpg Binary files differ diff --git a/games/numbers/images/04x.jpg b/games/numbers/images/04x.jpg new file mode 100755 index 0000000..63c1d8f --- /dev/null +++ b/games/numbers/images/04x.jpg Binary files differ diff --git a/games/numbers/images/05x.jpg b/games/numbers/images/05x.jpg new file mode 100755 index 0000000..0e0df29 --- /dev/null +++ b/games/numbers/images/05x.jpg Binary files differ diff --git a/games/numbers/images/06x.jpg b/games/numbers/images/06x.jpg new file mode 100755 index 0000000..be5b5cf --- /dev/null +++ b/games/numbers/images/06x.jpg Binary files differ diff --git a/games/numbers/images/07x.jpg b/games/numbers/images/07x.jpg new file mode 100755 index 0000000..17f09bb --- /dev/null +++ b/games/numbers/images/07x.jpg Binary files differ diff --git a/games/numbers/images/08x.jpg b/games/numbers/images/08x.jpg new file mode 100755 index 0000000..8ce3123 --- /dev/null +++ b/games/numbers/images/08x.jpg Binary files differ diff --git a/games/numbers/images/09x.jpg b/games/numbers/images/09x.jpg new file mode 100755 index 0000000..5120b45 --- /dev/null +++ b/games/numbers/images/09x.jpg Binary files differ diff --git a/games/numbers/images/10x.jpg b/games/numbers/images/10x.jpg new file mode 100755 index 0000000..866446f --- /dev/null +++ b/games/numbers/images/10x.jpg Binary files differ diff --git a/games/numbers/images/11x.jpg b/games/numbers/images/11x.jpg new file mode 100755 index 0000000..93a949c --- /dev/null +++ b/games/numbers/images/11x.jpg Binary files differ diff --git a/games/numbers/images/12x.jpg b/games/numbers/images/12x.jpg new file mode 100755 index 0000000..1e4bf75 --- /dev/null +++ b/games/numbers/images/12x.jpg Binary files differ diff --git a/games/numbers/images/13x.jpg b/games/numbers/images/13x.jpg new file mode 100755 index 0000000..708f705 --- /dev/null +++ b/games/numbers/images/13x.jpg Binary files differ diff --git a/games/numbers/images/14x.jpg b/games/numbers/images/14x.jpg new file mode 100755 index 0000000..9ea6403 --- /dev/null +++ b/games/numbers/images/14x.jpg Binary files differ diff --git a/games/numbers/images/15x.jpg b/games/numbers/images/15x.jpg new file mode 100755 index 0000000..7a57cf8 --- /dev/null +++ b/games/numbers/images/15x.jpg Binary files differ diff --git a/games/numbers/numbers.mem b/games/numbers/numbers.mem new file mode 100755 index 0000000..b1a3839 --- /dev/null +++ b/games/numbers/numbers.mem @@ -0,0 +1,22 @@ +game_name=numbers +score_sound=score.wav +win_sound=score.wav +divided=True +face1=1 +face2=2 +# Cards +1,,01.wav,1,,,,,2,01x.jpg,01.wav,,,,, +1,,02.wav,2,,,,,2,02x.jpg,02.wav,,,,, +1,,03.wav,3,,,,,2,03x.jpg,03.wav,,,,, +1,,04.wav,4,,,,,2,04x.jpg,04.wav,,,,, +1,,05.wav,5,,,,,2,05x.jpg,05.wav,,,,, +1,,06.wav,6,,,,,2,06x.jpg,06.wav,,,,, +1,,07.wav,7,,,,,2,07x.jpg,07.wav,,,,, +1,,08.wav,8,,,,,2,08x.jpg,08.wav,,,,, +1,,09.wav,9,,,,,2,09x.jpg,09.wav,,,,, +1,,10.wav,10,,,,,2,10x.jpg,10.wav,,,,, +1,,11.wav,11,,,,,2,11x.jpg,11.wav,,,,, +1,,12.wav,12,,,,,2,12x.jpg,12.wav,,,,, +1,,13.wav,13,,,,,2,13x.jpg,13.wav,,,,, +1,,14.wav,14,,,,,2,14x.jpg,14.wav,,,,, +1,,15.wav,15,,,,,2,15x.jpg,15.wav,,,,, \ No newline at end of file diff --git a/games/phonemes/images/agua1.jpg b/games/phonemes/images/agua1.jpg new file mode 100755 index 0000000..3175d47 --- /dev/null +++ b/games/phonemes/images/agua1.jpg Binary files differ diff --git a/games/phonemes/images/agua2.jpg b/games/phonemes/images/agua2.jpg new file mode 100755 index 0000000..e680fc1 --- /dev/null +++ b/games/phonemes/images/agua2.jpg Binary files differ diff --git a/games/phonemes/images/bola1.jpg b/games/phonemes/images/bola1.jpg new file mode 100755 index 0000000..8cb1e53 --- /dev/null +++ b/games/phonemes/images/bola1.jpg Binary files differ diff --git a/games/phonemes/images/bola2.jpg b/games/phonemes/images/bola2.jpg new file mode 100755 index 0000000..1531c51 --- /dev/null +++ b/games/phonemes/images/bola2.jpg Binary files differ diff --git a/games/phonemes/images/casa1.jpg b/games/phonemes/images/casa1.jpg new file mode 100755 index 0000000..628fab9 --- /dev/null +++ b/games/phonemes/images/casa1.jpg Binary files differ diff --git a/games/phonemes/images/casa2.jpg b/games/phonemes/images/casa2.jpg new file mode 100755 index 0000000..40892d9 --- /dev/null +++ b/games/phonemes/images/casa2.jpg Binary files differ diff --git a/games/phonemes/images/ema1.jpg b/games/phonemes/images/ema1.jpg new file mode 100755 index 0000000..64c6706 --- /dev/null +++ b/games/phonemes/images/ema1.jpg Binary files differ diff --git a/games/phonemes/images/ema2.jpg b/games/phonemes/images/ema2.jpg new file mode 100755 index 0000000..3e20812 --- /dev/null +++ b/games/phonemes/images/ema2.jpg Binary files differ diff --git a/games/phonemes/images/frutas1.jpg b/games/phonemes/images/frutas1.jpg new file mode 100755 index 0000000..925d8e8 --- /dev/null +++ b/games/phonemes/images/frutas1.jpg Binary files differ diff --git a/games/phonemes/images/frutas2.jpg b/games/phonemes/images/frutas2.jpg new file mode 100755 index 0000000..a7a178c --- /dev/null +++ b/games/phonemes/images/frutas2.jpg Binary files differ diff --git a/games/phonemes/images/homem1.jpg b/games/phonemes/images/homem1.jpg new file mode 100755 index 0000000..b08e612 --- /dev/null +++ b/games/phonemes/images/homem1.jpg Binary files differ diff --git a/games/phonemes/images/homem2.jpg b/games/phonemes/images/homem2.jpg new file mode 100755 index 0000000..0d3d5e1 --- /dev/null +++ b/games/phonemes/images/homem2.jpg Binary files differ diff --git a/games/phonemes/images/ioio1.jpg b/games/phonemes/images/ioio1.jpg new file mode 100755 index 0000000..aa3ba6d --- /dev/null +++ b/games/phonemes/images/ioio1.jpg Binary files differ diff --git a/games/phonemes/images/ioio2.jpg b/games/phonemes/images/ioio2.jpg new file mode 100755 index 0000000..d339f6a --- /dev/null +++ b/games/phonemes/images/ioio2.jpg Binary files differ diff --git a/games/phonemes/images/kiwi1.jpg b/games/phonemes/images/kiwi1.jpg new file mode 100755 index 0000000..cd9e24d --- /dev/null +++ b/games/phonemes/images/kiwi1.jpg Binary files differ diff --git a/games/phonemes/images/kiwi2.jpg b/games/phonemes/images/kiwi2.jpg new file mode 100755 index 0000000..7c7250b --- /dev/null +++ b/games/phonemes/images/kiwi2.jpg Binary files differ diff --git a/games/phonemes/images/ovo1.jpg b/games/phonemes/images/ovo1.jpg new file mode 100755 index 0000000..fc83ec4 --- /dev/null +++ b/games/phonemes/images/ovo1.jpg Binary files differ diff --git a/games/phonemes/images/ovo2.jpg b/games/phonemes/images/ovo2.jpg new file mode 100755 index 0000000..8876749 --- /dev/null +++ b/games/phonemes/images/ovo2.jpg Binary files differ diff --git a/games/phonemes/images/peixe1.jpg b/games/phonemes/images/peixe1.jpg new file mode 100755 index 0000000..0e7c1d0 --- /dev/null +++ b/games/phonemes/images/peixe1.jpg Binary files differ diff --git a/games/phonemes/images/peixe2.jpg b/games/phonemes/images/peixe2.jpg new file mode 100755 index 0000000..daab513 --- /dev/null +++ b/games/phonemes/images/peixe2.jpg Binary files differ diff --git a/games/phonemes/images/queijo1.jpg b/games/phonemes/images/queijo1.jpg new file mode 100755 index 0000000..e23c8c2 --- /dev/null +++ b/games/phonemes/images/queijo1.jpg Binary files differ diff --git a/games/phonemes/images/queijo2.jpg b/games/phonemes/images/queijo2.jpg new file mode 100755 index 0000000..ed744b8 --- /dev/null +++ b/games/phonemes/images/queijo2.jpg Binary files differ diff --git a/games/phonemes/images/roda1.jpg b/games/phonemes/images/roda1.jpg new file mode 100755 index 0000000..8df6d11 --- /dev/null +++ b/games/phonemes/images/roda1.jpg Binary files differ diff --git a/games/phonemes/images/roda2.jpg b/games/phonemes/images/roda2.jpg new file mode 100755 index 0000000..d9a213e --- /dev/null +++ b/games/phonemes/images/roda2.jpg Binary files differ diff --git a/games/phonemes/images/sapo1.jpg b/games/phonemes/images/sapo1.jpg new file mode 100755 index 0000000..4409d47 --- /dev/null +++ b/games/phonemes/images/sapo1.jpg Binary files differ diff --git a/games/phonemes/images/sapo2.jpg b/games/phonemes/images/sapo2.jpg new file mode 100755 index 0000000..632fb6a --- /dev/null +++ b/games/phonemes/images/sapo2.jpg Binary files differ diff --git a/games/phonemes/images/uva1.jpg b/games/phonemes/images/uva1.jpg new file mode 100755 index 0000000..b4f702c --- /dev/null +++ b/games/phonemes/images/uva1.jpg Binary files differ diff --git a/games/phonemes/images/uva2.jpg b/games/phonemes/images/uva2.jpg new file mode 100755 index 0000000..c0242f5 --- /dev/null +++ b/games/phonemes/images/uva2.jpg Binary files differ diff --git a/games/phonemes/images/zebra1.jpg b/games/phonemes/images/zebra1.jpg new file mode 100755 index 0000000..46d2516 --- /dev/null +++ b/games/phonemes/images/zebra1.jpg Binary files differ diff --git a/games/phonemes/images/zebra2.jpg b/games/phonemes/images/zebra2.jpg new file mode 100755 index 0000000..6002fd8 --- /dev/null +++ b/games/phonemes/images/zebra2.jpg Binary files differ diff --git a/games/phonemes/phonemes.mem b/games/phonemes/phonemes.mem new file mode 100755 index 0000000..a28d4ba --- /dev/null +++ b/games/phonemes/phonemes.mem @@ -0,0 +1,22 @@ +game_name=phonemes +score_sound=score.wav +win_sound=score.wav +divided=True +face1=1 +face2=2 +# Cards +1,agua1.jpg,agua1.wav,,,,,Á_ _ _,2,agua2.jpg,agua2.wav,,,,,_GUA +1,bola1.jpg,bola1.wav,,,,,BO_ _,2,bola2.jpg,bola2.wav,,,,,_ _LA +1,casa1.jpg,casa1.wav,,,,,CA_ _,2,casa2.jpg,casa2.wav,,,,,_ _SA +1,ema1.jpg,ema1.wav,,,,,E_ _,2,ema2.jpg,ema2.wav,,,,,_MA +1,frutas1.jpg,frutas1.wav,,,,,FRU_ _ _,2,frutas2.jpg,frutas2.wav,,,,,_ _ _TAS +1,homem1.jpg,homem1.wav,,,,,HO_ _ _,2,homem2.jpg,homem2.wav,,,,,_ _MEM +1,ioio1.jpg,ioio1.wav,,,,,IO_ _,2,ioio2.jpg,ioio2.wav,,,,,_ _IO +1,kiwi1.jpg,kiwi1.wav,,,,,KI_ _,2,kiwi2.jpg,kiwi2.wav,,,,,_ _WI +1,ovo1.jpg,ovo1.wav,,,,,O_ _,2,ovo2.jpg,ovo2.wav,,,,,_VO +1,peixe1.jpg,peixe1.wav,,,,,PEI_ _,2,peixe2.jpg,peixe2.wav,,,,,_ _ _XE +1,queijo1.jpg,queijo1.wav,,,,,QUEI_ _,2,queijo2.jpg,queijo2.wav,,,,,_ _ _ _JO +1,roda1.jpg,roda1.wav,,,,,RO_ _,2,roda2.jpg,roda2.wav,,,,,_ _DA +1,sapo1.jpg,sapo1.wav,,,,,SA_ _,2,sapo2.jpg,sapo2.wav,,,,,_ _PO +1,uva1.jpg,uva1.wav,,,,,U_ _,2,uva2.jpg,uva2.wav,,,,,_VA +1,zebra1.jpg,zebra1.wav,,,,,ZE_ _ _,2,zebra2.jpg,zebra2.wav,,,,,_ _BRA \ No newline at end of file diff --git a/images/card-text.svg b/images/card-text.svg new file mode 100755 index 0000000..f095605 --- /dev/null +++ b/images/card-text.svg @@ -0,0 +1,126 @@ + + + + + +]> + + + + + + + image/svg+xml + + + + + + card_line1 + card_line3 + card_line2 + card_line4 + card_text + + diff --git a/images/card.svg b/images/card.svg new file mode 100755 index 0000000..5db83ce --- /dev/null +++ b/images/card.svg @@ -0,0 +1,79 @@ + + + + + + +]> + + + + + + + image/svg+xml + + + + + + + + + diff --git a/images/score.svg b/images/score.svg new file mode 100755 index 0000000..00a996a --- /dev/null +++ b/images/score.svg @@ -0,0 +1,81 @@ + + + + + +]> + + + + + + + image/svg+xml + + + + + + + + + diff --git a/images/stock-buddy.svg b/images/stock-buddy.svg new file mode 100755 index 0000000..bad856f --- /dev/null +++ b/images/stock-buddy.svg @@ -0,0 +1,18 @@ + + + + + + +]> + + + + + diff --git a/infopanel.py b/infopanel.py deleted file mode 100644 index 95defa6..0000000 --- a/infopanel.py +++ /dev/null @@ -1,20 +0,0 @@ -import hippo -import pango -from sugar.graphics import color - -class InfoPanel(hippo.CanvasBox): - def __init__(self): - hippo.CanvasBox.__init__(self, spacing=4, padding=5, - orientation=hippo.ORIENTATION_VERTICAL) - self.status_box = hippo.CanvasBox(spacing=4, padding=5, - orientation=hippo.ORIENTATION_VERTICAL) - self.append(self.status_box) - - def show(self, text): - textwidget = hippo.CanvasText(text=text, - font_desc=pango.FontDescription('Sans 16'), - color=color.WHITE.get_int(), - xalign=hippo.ALIGNMENT_CENTER) - self.status_box.remove_all() - self.status_box.append(textwidget) - diff --git a/memorize.dtd b/memorize.dtd new file mode 100644 index 0000000..40542e8 --- /dev/null +++ b/memorize.dtd @@ -0,0 +1,21 @@ + + + + + + + diff --git a/memorizetoolbar.py b/memorizetoolbar.py new file mode 100755 index 0000000..9736e3d --- /dev/null +++ b/memorizetoolbar.py @@ -0,0 +1,99 @@ +#! /usr/bin/env python +# +# Copyright (C) 2007, One Laptop Per Child +# +# Muriel de Souza Godoi - muriel@laptop.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import logging +from gettext import gettext as _ + +import gtk +import os + +from sugar.graphics.toolbutton import ToolButton +from sugar.graphics.combobox import ComboBox + +class MemorizeToolbar(gtk.Toolbar): + __gtype_name__ = 'MemoryToolbar' + + def __init__(self, activity): + gtk.Toolbar.__init__(self) + self.activity = activity + self._lock = True + + + # Reset Button + self._reset_button = ToolButton('insert-image') + self._reset_button.connect('clicked', self._game_changed_cb) + self.insert(self._reset_button, -1) + self._reset_button.show() + + # Separator + separator = gtk.SeparatorToolItem() + separator.set_draw(True) + self.insert(separator, -1) + + # Change game combobox + self.games = os.listdir(os.path.join(os.path.dirname(__file__), 'games')) + self.games.sort() + self._game_combo = ComboBox() + for i, f in enumerate(self.games): + self._game_combo.append_item(i, f) + if f == 'numbers': + self._game_combo.set_active(i) + self._game_combo.connect('changed', self._game_changed_cb) + self._add_widget(self._game_combo) + + separator = gtk.SeparatorToolItem() + separator.set_draw(True) + self.insert(separator, -1) + self._lock = False + + # Change size combobox + self._size_combo = ComboBox() + self._sizes = ['4 X 4', '5 X 5', '6 X 6'] + for i, f in enumerate(self._sizes): + self._size_combo.append_item(i, f) + if f == '4 X 4': + self._size_combo.set_active(i) + self._size_combo.connect('changed', self._game_changed_cb) + self._add_widget(self._size_combo) + + def _add_widget(self, widget, expand=False): + tool_item = gtk.ToolItem() + tool_item.set_expand(expand) + tool_item.add(widget) + widget.show() + self.insert(tool_item, -1) + tool_item.show() + + def _game_changed_cb(self, combobox): + if not self._lock: + game_name = self.games[self._game_combo.get_active()] + game_size = int(self._sizes[self._size_combo.get_active()][0]) + self.activity.change_game(game_name, game_size) + + def update_toolbar(self, widget, data, grid): + game = data.get('game_name') + size = data.get('size') + self._lock = True + game_index = self.games.index(game) + self._game_combo.set_active(game_index) + size_index = self._sizes.index(size+' X '+size) + self._size_combo.set_active(int(size_index)) + self._lock = False diff --git a/memosono.dtd b/memosono.dtd deleted file mode 100644 index 713c779..0000000 --- a/memosono.dtd +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - diff --git a/memosonoactivity.py b/memosonoactivity.py deleted file mode 100755 index 48783c9..0000000 --- a/memosonoactivity.py +++ /dev/null @@ -1,290 +0,0 @@ -#! /usr/bin/env python -# -# Copyright (C) 2006, 2007 Simon Schampijer -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -# - -from gettext import gettext as _ - -import gobject -import gtk -import os -import logging -import telepathy -import telepathy.client -import hippo - -from sugar.activity.activity import Activity -from sugar.activity.activity import ActivityToolbox -from sugar.presence import presenceservice - -from tubeconn import TubeConnection -from playview import PlayView -from buddiespanel import BuddiesPanel -from infopanel import InfoPanel -from controller import Controller - - -_logger = logging.getLogger('activity') - -class MemosonoActivity(Activity): - def __init__(self, handle): - Activity.__init__(self, handle) - - _logger.debug('Starting Memosono activity...') - - self.set_title(_('Memsosono Activity')) - - w = self.get_screen().get_width() - h = self.get_screen().get_height() - ### FIXME: do better grid calculation - if w <= 1024: - self.pv = PlayView(600, 600, 32) - else: - self.pv = PlayView(800, 800, 32) - - self.buddies_panel = BuddiesPanel() - - self.info_panel = InfoPanel() - - vbox = hippo.CanvasBox(spacing=4, - orientation=hippo.ORIENTATION_VERTICAL) - - hbox = hippo.CanvasBox(spacing=4, - orientation=hippo.ORIENTATION_HORIZONTAL) - - hbox.append(self.buddies_panel) - hbox.append(self.pv, hippo.PACK_EXPAND) - - vbox.append(hbox, hippo.PACK_EXPAND) - vbox.append(self.info_panel, hippo.PACK_END) - - canvas = hippo.Canvas() - canvas.set_root(vbox) - self.set_canvas(canvas) - self.show_all() - - toolbox = ActivityToolbox(self) - self.set_toolbox(toolbox) - toolbox.show() - - self.pservice = presenceservice.get_instance() - - name, path = self.pservice.get_preferred_connection() - self.tp_conn_name = name - self.tp_conn_path = path - self.conn = telepathy.client.Connection(name, path) - self.initiating = None - - self.ctrl = None - - toolbox = ActivityToolbox(self) - self.set_toolbox(toolbox) - toolbox.show() - - # connect to the in/out events of the memosono activity - self.connect('focus_in_event', self._focus_in) - self.connect('focus_out_event', self._focus_out) - self.connect('destroy', self._cleanup_cb) - - self.info_panel.show('To play, share!') - - self.connect('shared', self._shared_cb) - - owner = self.pservice.get_owner() - self.owner = owner - - if self._shared_activity: - # we are joining the activity - self.buddies_panel.add_watcher(owner) - self.connect('joined', self._joined_cb) - self._shared_activity.connect('buddy-joined', self._buddy_joined_cb) - self._shared_activity.connect('buddy-left', self._buddy_left_cb) - if self.get_shared(): - # oh, OK, we've already joined - self._joined_cb() - else: - # we are creating the activity - self.buddies_panel.add_player(owner) - - def _get_buddy(self, cs_handle): - """Get a Buddy from a channel specific handle.""" - _logger.debug('Trying to find owner of handle %u...', cs_handle) - group = self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP] - my_csh = group.GetSelfHandle() - _logger.debug('My handle in that group is %u', my_csh) - if my_csh == cs_handle: - handle = self.conn.GetSelfHandle() - _logger.debug('CS handle %u belongs to me, %u', cs_handle, handle) - elif group.GetGroupFlags() & telepathy.CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES: - handle = group.GetHandleOwners([cs_handle])[0] - _logger.debug('CS handle %u belongs to %u', cs_handle, handle) - else: - handle = cs_handle - _logger.debug('non-CS handle %u belongs to itself', handle) - - # XXX: deal with failure to get the handle owner - assert handle != 0 - - # XXX: we're assuming that we have Buddy objects for all contacts - - # this might break when the server becomes scalable. - return self.pservice.get_buddy_by_telepathy_handle(self.tp_conn_name, - self.tp_conn_path, handle) - - def _shared_cb(self, activity): - _logger.debug('My Memosono activity was shared') - self.initiating = True - self._setup() - - for buddy in self._shared_activity.get_joined_buddies(): - self.buddies_panel.add_watcher(buddy) - - self._shared_activity.connect('buddy-joined', self._buddy_joined_cb) - self._shared_activity.connect('buddy-left', self._buddy_left_cb) - - _logger.debug('This is my activity: making a tube...') - id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferTube( - telepathy.TUBE_TYPE_DBUS, 'org.fredektop.Telepathy.Tube.Memosono', {}) - _logger.debug('Tube address: %s', self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].GetDBusServerAddress(id)) - self.info_panel.show('Waiting for another player to join') - - # FIXME: presence service should be tubes-aware and give us more help - # with this - def _setup(self): - if self._shared_activity is None: - _logger.error('Failed to share or join activity') - return - - bus_name, conn_path, channel_paths = self._shared_activity.get_channels() - - # Work out what our room is called and whether we have Tubes already - room = None - tubes_chan = None - text_chan = None - for channel_path in channel_paths: - channel = telepathy.client.Channel(bus_name, channel_path) - htype, handle = channel.GetHandle() - if htype == telepathy.HANDLE_TYPE_ROOM: - _logger.debug('Found our room: it has handle#%d "%s"', - handle, self.conn.InspectHandles(htype, [handle])[0]) - room = handle - ctype = channel.GetChannelType() - if ctype == telepathy.CHANNEL_TYPE_TUBES: - _logger.debug('Found our Tubes channel at %s', channel_path) - tubes_chan = channel - elif ctype == telepathy.CHANNEL_TYPE_TEXT: - _logger.debug('Found our Text channel at %s', channel_path) - text_chan = channel - - if room is None: - _logger.error("Presence service didn't create a room") - return - if text_chan is None: - _logger.error("Presence service didn't create a text channel") - return - - # Make sure we have a Tubes channel - PS doesn't yet provide one - if tubes_chan is None: - _logger.debug("Didn't find our Tubes channel, requesting one...") - tubes_chan = self.conn.request_channel(telepathy.CHANNEL_TYPE_TUBES, - telepathy.HANDLE_TYPE_ROOM, room, True) - - self.tubes_chan = tubes_chan - self.text_chan = text_chan - - tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube', - self._new_tube_cb) - - def _list_tubes_reply_cb(self, tubes): - for tube_info in tubes: - self._new_tube_cb(*tube_info) - - def _list_tubes_error_cb(self, e): - _logger.error('ListTubes() failed: %s', e) - - def _joined_cb(self, activity): - if self.ctrl is not None: - return - - if not self._shared_activity: - return - - for buddy in self._shared_activity.get_joined_buddies(): - self.buddies_panel.add_watcher(buddy) - - _logger.debug('Joined an existing Memosono game') - self.info_panel.show('Joined a game. Waiting for my turn...') - self.initiating = False - self._setup() - - _logger.debug('This is not my activity: waiting for a tube...') - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( - reply_handler=self._list_tubes_reply_cb, - error_handler=self._list_tubes_error_cb) - - def _new_tube_cb(self, id, initiator, type, service, params, state): - _logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' - 'params=%r state=%d', id, initiator, type, service, - params, state) - - if (self.ctrl is None and type == telepathy.TUBE_TYPE_DBUS and - service == 'org.fredektop.Telepathy.Tube.Memosono'): - if state == telepathy.TUBE_STATE_LOCAL_PENDING: - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptTube(id) - - tube_conn = TubeConnection(self.conn, - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], - id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) - self.ctrl = Controller(tube_conn, self.pv, self.initiating, - self.buddies_panel, self.info_panel, self.owner, - self._get_buddy, self) - - def _buddy_joined_cb(self, activity, buddy): - _logger.debug('buddy joined') - self.buddies_panel.add_watcher(buddy) - - def _buddy_left_cb(self, activity, buddy): - _logger.debug('buddy left') - self.buddies_panel.remove_watcher(buddy) - - def write_file(self, file_path): - """Store game state in Journal. - - Handling the Journal is provided by Activity - we only need - to define this method. - """ - _logger.debug(" Write game state. ") - f = open(file_path, 'w') - try: - f.write('erikos won the game') - finally: - f.close() - - def _focus_in(self, event, data=None): - if self.ctrl != None: - self.ctrl.cs.start() - _logger.debug(" Memosono is visible: start csound server. ") - - def _focus_out(self, event, data=None): - if self.ctrl != None: - self.ctrl.cs.start() - _logger.debug(" Memosono is invisible: pause csound server. ") - - def _cleanup_cb(self, data=None): - if self.ctrl != None: - self.ctrl.cs.quit() - _logger.debug(" Memosono closes: close csound server. ") - diff --git a/messenger.py b/messenger.py new file mode 100644 index 0000000..2bceab9 --- /dev/null +++ b/messenger.py @@ -0,0 +1,116 @@ +#! /usr/bin/env python +# +# Copyright (C) 2007, One Laptop Per Child +# +# Muriel de Souza Godoi - muriel@laptop.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import logging +import dbus +from dbus.gobject_service import ExportedGObject + +SERVICE = "org.laptop.Memorize" +IFACE = SERVICE +PATH = "/org/laptop/Memorize" + +_logger = logging.getLogger('memorize-activity') + +class Messenger(ExportedGObject): + + def __init__(self, tube, is_initiator, get_buddy, game): + ExportedGObject.__init__(self, tube, PATH) + self._tube = tube + self.is_initiator = is_initiator + self._get_buddy = get_buddy + self.game = game + self.ordered_bus_names = [] + self.entered = False + self._tube.watch_participants(self.participant_change_cb) + + def participant_change_cb(self, added, removed): + if not self.entered: + self._tube.add_signal_receiver(self._flip_receiver, '_flip_signal', IFACE, path=PATH, sender_keyword='sender') + self._tube.add_signal_receiver(self._change_game_receiver, '_change_game_signal', IFACE, path=PATH, sender_keyword='sender') + if self.is_initiator: + _logger.debug('Initialising a new game, I am %s .', self._tube.get_unique_name()) + self.player_id = self._tube.get_unique_name() + self.ordered_bus_names = [self.player_id] + self._tube.add_signal_receiver(self._hello_receiver, '_hello_signal', IFACE, path=PATH, sender_keyword='sender') + else: + self._hello_signal() + self.entered = True + + @dbus.service.signal(IFACE, signature='') + def _hello_signal(self): + ''' Notify current players that you joined ''' + _logger.debug('Sending hello to all') + + def _hello_receiver(self, sender=None): + ''' Someone joined the game, so sync the new player ''' + _logger.debug('The new player %s has joined', sender) + self.ordered_bus_names.append(sender) + self._tube.get_object(sender, PATH).load_game(self.ordered_bus_names, self.game.get_grid(), self.game.get_data(), self.game.waiting_players, dbus_interface=IFACE) + + #@dbus.service.method(dbus_interface=IFACE, in_signature='asss', out_signature='') + @dbus.service.method(dbus_interface=IFACE, in_signature='asa(ssssssssiii)a{ss}av', out_signature='') + def load_game(self, bus_names, grid, data,list): + ''' Sync the game with with players ''' + _logger.debug('Data received to sync game data') + self.ordered_bus_names = bus_names + self.player_id = bus_names.index(self._tube.get_unique_name()) + self._change_game_receiver(grid,data,self.ordered_bus_names[0]) + self.game.load_waiting_list(list) + + def flip(self, widget, id): + ''' Notify other players that you flipped a card ''' + _logger.debug('Sending flip message: '+str(id)) + self._flip_signal(id) + + @dbus.service.signal(IFACE, signature='n') + def _flip_signal(self, card_number): + _logger.debug('Notifing other players that you flipped: %s', str(card_number)) + ''' Notify current players that you flipped a card ''' + + def _flip_receiver(self, card_number, sender=None): + ''' Someone flipped a card ''' + handle = self._tube.bus_name_to_handle[sender] + + if self._tube.self_handle <> handle: + _logger.debug('Other player flipped: %s ', str(card_number)) + self.game.card_flipped(None, card_number, True) + + def change_game(self, sender, grid, data, waiting_list): + ''' Notify other players that you changed the game ''' + _logger.debug('Sending changed game message') + self._change_game_signal(grid, data) + + @dbus.service.signal(IFACE, signature='a(ssssssssiii)a{ss}') + def _change_game_signal(self, grid, data): + _logger.debug('Notifing other players that you changed the game') + ''' Notify current players that you changed the game ''' + + def _change_game_receiver(self, grid, data, sender=None): + ''' Game changed by other player ''' + handle = self._tube.bus_name_to_handle[sender] + + if self._tube.self_handle <> handle: + _logger.debug('Game changed by other player') + new_grid = [] + for card in grid: + new_grid.append(map(lambda x: str(x), card[:8])+[int(card[8]), int(card[9]), int(card[10])]) + self.game.load_remote(new_grid, data, True) + \ No newline at end of file diff --git a/misc/addition.mem b/misc/addition.mem new file mode 100644 index 0000000..9521cb7 --- /dev/null +++ b/misc/addition.mem @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/misc/drumgit.mem b/misc/drumgit.mem new file mode 100644 index 0000000..67efec4 --- /dev/null +++ b/misc/drumgit.mem @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/model.py b/model.py index bb7870a..d85f603 100644 --- a/model.py +++ b/model.py @@ -5,7 +5,8 @@ import random import gobject IMAGES_PATH = 'games/drumgit/images' -GAME_PATH = 'games/drumgit' +SOUNDS_PATH = 'games/drumgit/sounds' +GAME_PATH = '' _logger = logging.getLogger('model') @@ -14,8 +15,10 @@ class Pair(gobject.GObject): __gproperties__ = { 'aimg' : (str, None, None, None, gobject.PARAM_READWRITE), 'asnd' : (str, None, None, None, gobject.PARAM_READWRITE), + 'achar' : (str, None, None, None, gobject.PARAM_READWRITE), 'bimg' : (str, None, None, None, gobject.PARAM_READWRITE), 'bsnd' : (str, None, None, None, gobject.PARAM_READWRITE), + 'bchar' : (str, None, None, None, gobject.PARAM_READWRITE), 'color': (gobject.TYPE_INT, 'Base', 'Base', 0, 10, 0, gobject.PARAM_READWRITE) } @@ -30,10 +33,14 @@ class Pair(gobject.GObject): return self._properties["aimg"] elif pspec.name == "asnd": return self._properties["asnd"] + elif pspec.name == "achar": + return self._properties["achar"] elif pspec.name == "bimg": return self._properties["bimg"] elif pspec.name == "bsnd": return self._properties["bsnd"] + elif pspec.name == "bchar": + return self._properties["bchar"] elif pspec.name == "color": return self._properties["color"] @@ -42,10 +49,14 @@ class Pair(gobject.GObject): self._properties['aimg'] = value elif name == "asnd": self._properties["asnd"] = value + elif name == "achar": + self._properties["achar"] = value elif name == "bimg": self._properties["bimg"] = value elif name == "bsnd": self._properties["bsnd"] = value + elif name == "bchar": + self._properties["bchar"] = value elif name == "color": self._properties["color"] = int(value) ''' @@ -60,19 +71,20 @@ class Model(object): information. ''' def __init__(self, gamepath, dtdpath, name='noname'): - self.name = name + self.data = {} self.gamepath = gamepath self.dtdpath = dtdpath + try: - self.dtd = libxml2.parseDTD(None, os.path.join(self.dtdpath, 'memosono.dtd')) + self.dtd = libxml2.parseDTD(None, os.path.join(self.dtdpath, 'memorize.dtd')) except libxml2.parserError, e: - _logger.error('No memosono.dtd found ' +str(e)) + _logger.error('No memorize.dtd found ' +str(e)) self.dtd = None self.ctxt = libxml2.newValidCtxt() self.pairs = {} self.grid = [] - + # used by the leader of the game to keep track of the game state self.players = {} self.player_active = 0 @@ -81,7 +93,6 @@ class Model(object): self.started = 0 self.count = 0 - def read(self, filename): ''' reades the configuration from an xml file ''' try: @@ -97,18 +108,34 @@ class Model(object): for elem in res: attributes = elem.get_properties() pair = Pair() - for attribute in attributes: - if(attribute.name == 'text'): - pass - else: - pass - pair.set_property(attribute.name, attribute.content) if( elem.name == 'pair' ): + for attribute in attributes: + if(attribute.name == 'text'): + pass + else: + pair.set_property(attribute.name, attribute.content) self.pairs[self.idpair] = pair - self.idpair+=1 - elif( elem.name == 'memosono' ): - self.name = attribute.content - + self.idpair+=1 + elif( elem.name == 'memorize' ): + for attribute in attributes: + if(attribute.name == 'text'): + pass + elif(attribute.name == 'name'): + self.data['game_name'] = attribute.content + elif(attribute.name == 'scoresnd'): + self.data['scoresnd'] = attribute.content + elif(attribute.name == 'winsnd'): + self.data['winsnd'] = attribute.content + elif(attribute.name == 'divided'): + self.data['divided'] = attribute.content + elif(attribute.name == 'divided'): + self.data['divided'] = attribute.content + elif(attribute.name == 'face'): + self.data['face'] = attribute.content + elif(attribute.name == 'face1'): + self.data['face1'] = attribute.content + elif(attribute.name == 'face2'): + self.data['face2'] = attribute.content xpa.xpathFreeContext() else: _logger.error('Error in validation of the file') @@ -120,8 +147,9 @@ class Model(object): def save(self, filename): ''' saves the configuration to an xml file ''' doc = libxml2.newDoc("1.0") - root = doc.newChild(None, "memosono", None) - root.setProp("name", self.name) + root = doc.newChild(None, "memorize", None) + root.setProp("name", self.data['game_name']) + ### Fixme: add other attributes here for key in self.pairs: elem = root.newChild(None, "pair", None) @@ -129,10 +157,14 @@ class Model(object): elem.setProp("aimg", self.pairs[key].props.aimg) if self.pairs[key].props.asnd != None: elem.setProp("asnd", self.pairs[key].props.asnd) + if self.pairs[key].props.achar != None: + elem.setProp("achar", self.pairs[key].props.achar) if self.pairs[key].props.bimg != None: elem.setProp("bimg", self.pairs[key].props.bimg) if self.pairs[key].props.bsnd != None: elem.setProp("bsnd", self.pairs[key].props.bsnd) + if self.pairs[key].props.bchar != None: + elem.setProp("bchar", self.pairs[key].props.bchar) elem.setProp("color", str(self.pairs[key].props.color)) if doc.validateDtd(self.ctxt, self.dtd): @@ -140,15 +172,27 @@ class Model(object): doc.freeDoc() - def def_grid(self): + def def_grid(self, size): ''' create the grid for the play from the pairs information and shuffles the grid so they always appear in a different - place + place + grid [pair_key, a_or_b, flipstatus] ''' _logger.debug(' pairs: %s', self.pairs) + i=0 for key in self.pairs.iterkeys(): - self.grid.append([key, 0, 0]) - self.grid.append([key, 1, 0]) + if i < size: + self.grid.append([key, 0, 0]) + self.grid.append([key, 1, 0]) + i+=1 + else: + break + + numpairs = len(self.pairs) + if numpairs < size: + _logger.debug('We did not have enough pairs. requested=%s had=%s' %(numpairs, size)) + + self.data['size'] = numpairs random.shuffle(self.grid) _logger.debug(' grid: %s', self.grid) @@ -156,10 +200,24 @@ class Model(object): def gettile(self, tilenum): ''' gets the information of an object associated with a tile number ''' - pairkey, moch, state = self.grid[tilenum] - obj = os.path.join(IMAGES_PATH, self.pairs[pairkey][moch]) - color = self.pairs[pairkey][2] - return (obj, color) + img = None + snd = None + char = None + pairkey, moch, state = self.grid[tilenum] + if moch == 0: + if self.pairs[pairkey].props.aimg != None: + img = os.path.join(IMAGES_PATH, self.pairs[pairkey].props.aimg) + if self.pairs[pairkey].props.asnd != None: + snd = os.path.join(SOUNDS_PATH, self.pairs[pairkey].props.asnd) + char = self.pairs[pairkey].props.achar + if moch == 1: + if self.pairs[pairkey].props.bimg != None: + img = os.path.join(IMAGES_PATH, self.pairs[pairkey].props.bimg) + if self.pairs[pairkey].props.bsnd != None: + snd = os.path.join(SOUNDS_PATH, self.pairs[pairkey].props.bsnd) + char = self.pairs[pairkey].props.bchar + color = self.pairs[pairkey].props.color + return (img, snd, char, color) def same(self, a, b): @@ -173,11 +231,41 @@ class Model(object): if __name__ == '__main__': model = Model(GAME_PATH, os.path.dirname(__file__)) - model.read('drumgit.mson') - print '%s' %model.pairs[0].props.color - print '%s' %model.pairs[1]._properties - print '%s' %model.pairs[2]._properties + model.read('drumgit.mem') + print '%s' %model.pairs[0]._properties + print 'name=%s' %model.data['game_name'] + print 'scoresnd=%s' %model.data['scoresnd'] + print 'winsnd=%s' %model.data['winsnd'] + print 'div=%s' %model.data['divided'] + model.def_grid(8) + print 'grid size=%d'%model.data['size'] + print model.grid + + i=0 + while i < model.data['size']: + pairkey, moch, state = model.grid[i] + if moch == 0: + if model.pairs[pairkey].props.aimg != None: + print model.pairs[pairkey].props.aimg + if moch == 1: + if model.pairs[pairkey].props.bimg != None: + print model.pairs[pairkey].props.bimg + i+=1 - model.def_grid() + + ''' + print '\n_______________________________\n' + + model.read('addition.mem') + print '%s' %model.pairs[0]._properties + print 'name=%s' %model.data['game_name'] + print 'scoresnd=%s' %model.data['scoresnd'] + print 'winsnd=%s' %model.data['winsnd'] + print 'div=%s' %model.data['divided'] + + model.def_grid(12) print model.grid - #model.save('/tmp/mod.txt') + print model.gettile(0) + print model.gettile(1) + model.save('/tmp/mod.txt') + ''' diff --git a/osc/__init__.py b/osc/__init__.py deleted file mode 100755 index e69de29..0000000 --- a/osc/__init__.py +++ /dev/null diff --git a/osc/oscapi.py b/osc/oscapi.py deleted file mode 100755 index bc26ddb..0000000 --- a/osc/oscapi.py +++ /dev/null @@ -1,163 +0,0 @@ -""" This file is based on simpleOSC 0.2 by Daniel Holth. -This file has been modified by Simon Schampijer. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -""" - - -import socket -import errno -import logging -import sys - -import osccore - - -class OscApi(object): - def __init__(self, port=None, host=None): - """create the send/receive socket and the callback manager - bind the socket to a name (host and port) - - Keyword arguments: - port -- the port - if no port is specified the socket will not be bound to an address and - will only be used for sending - host -- the host address, can be a dotted decimal address (e.g. '192.168.0.100') - or a hostname (e.g. 'machine', 'localhost') - if you do not specify a hostname the socket will listen on all the - devices for incoming messages - """ - - self.manager = 0 - self.iosock = 0 - self.iosock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.manager = osccore.CallbackManager() - - self.isserver = 0 - - if port is not None: - if host is None: - host = "" - try: - self.iosock.bind((host, port)) - self.isserver = 1 - except socket.error: - if errno.EADDRINUSE: - logging.error("Port " +str(port)+ " in use." + - " Maybe another oscapi is still or already running?" + - " oscapi can only be used for sending.") - self.iosock.close() - self.iosock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.iosock.setblocking(0) - - - def addmethod(self, path, types, func): - '''adds an osc method to the listener - - Keyword arguments: - func -- the callback function which gets called when the appropriate osc message is received - path -- the path (identifier) of the osc message - - ''' - self.manager.add(func, path, types) - - - def handlemsg(self, data, addr): - '''give the information read from the socket to the osc message dispatcher - - Keyword arguments: - data -- the data read from the socket - addr -- address the message is received from - ''' - self.manager.handle(data, addr) - - - def _create_binarymsg(self, path, data): - """create an OSC message in binary format - - Keyword arguments: - path -- the path (identifier) of the osc message - data -- the data of the message - - return: the osc message in binary format - """ - m = osccore.OSCMessage() - m.setAddress(path) - - if len(data) != 0: - for x in data: - m.append(x) - - return m.getBinary() - - - def send(self, to, path, data): - """send an osc message to the address specified - - Keyword arguments: - to -- address of receiver in the form (ipaddr, bundle) - path -- the path of the osc message - data -- the data of the message - """ - ### resolve host? [a,b,host] = socket.gethostbyaddr(to[0]) - msg = self._create_binarymsg(path, data) - try: - self.iosock.sendto(msg, (to[0],to[1])) - except socket.error: - cla, exc, trbk = sys.exc_info() - try: - excArgs = exc.__dict__["args"] - except KeyError: - excArgs = "" - logging.error('error '+str(excArgs)) - - def createbundle(self): - """create the header of a bundle of OSC messages""" - b = osccore.OSCMessage() - b.setAddress("") - b.append("#bundle") - b.append(0) - b.append(0) - return b - - def appendbundle(self, bundle, path, data): - """append osc message to the bundle - - Keyword arguments: - bundle -- the bundle to which you want to append a message - path -- the path of the OSC message - data -- the data of the message - """ - msg = self._create_binarymsg(path, data) - bundle.append(msg, 'b') - - - def sendbundle(self, to, bundle): - """send bundle to the address specified - - Keyword arguments: - to -- address of receiver in the form (ipaddr, bundle) - bundle -- the bundle to send - """ - try: - self.iosock.sendto(bundle.message, to) - except socket.error: - cla, exc, trbk = sys.exc_info() - try: - excArgs = exc.__dict__["args"] - except KeyError: - excArgs = "" - logging.error('error '+str(excArgs)) - diff --git a/osc/osccore.py b/osc/osccore.py deleted file mode 100755 index 4cab0b5..0000000 --- a/osc/osccore.py +++ /dev/null @@ -1,405 +0,0 @@ -#!/usr/bin/python -# -# Open SoundControl for Python -# Copyright (C) 2002 Daniel Holth, Clinton McChesney -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# For questions regarding this module contact -# Daniel Holth or visit -# http://www.stetson.edu/~ProctoLogic/ -# -# Changelog: -# 15 Nov. 2001: -# Removed dependency on Python 2.0 features. -# - dwh -# 13 Feb. 2002: -# Added a generic callback handler. -# - dwh -# 29 Mai 2007: -# Added type checking of messanges. -# - erikos - -import socket -import struct -import math -import sys -import string -import pprint - - -def hexDump(bytes): - """Useful utility; prints the string in hexadecimal""" - for i in range(len(bytes)): - sys.stdout.write("%2x " % (ord(bytes[i]))) - if (i+1) % 8 == 0: - print repr(bytes[i-7:i+1]) - - if(len(bytes) % 8 != 0): - print string.rjust("", 11), repr(bytes[i-len(bytes)%8:i+1]) - - -class OSCMessage: - """Builds typetagged OSC messages.""" - def __init__(self): - self.address = "" - self.typetags = "," - self.message = "" - - def setAddress(self, address): - self.address = address - - def setMessage(self, message): - self.message = message - - def setTypetags(self, typetags): - self.typetags = typetags - - def clear(self): - self.address = "" - self.clearData() - - def clearData(self): - self.typetags = "," - self.message = "" - - def append(self, argument, typehint = None): - """Appends data to the message, - updating the typetags based on - the argument's type. - If the argument is a blob (counted string) - pass in 'b' as typehint.""" - - if typehint == 'b': - binary = OSCBlob(argument) - else: - binary = OSCArgument(argument) - - self.typetags = self.typetags + binary[0] - self.rawAppend(binary[1]) - - def rawAppend(self, data): - """Appends raw data to the message. Use append().""" - self.message = self.message + data - - def getBinary(self): - """Returns the binary message (so far) with typetags.""" - address = OSCArgument(self.address)[1] - typetags = OSCArgument(self.typetags)[1] - return address + typetags + self.message - - def __repr__(self): - return self.getBinary() - -def readString(data): - length = string.find(data,"\0") - nextData = int(math.ceil((length+1) / 4.0) * 4) - return (data[0:length], data[nextData:]) - - -def readBlob(data): - length = struct.unpack(">i", data[0:4])[0] - nextData = int(math.ceil((length) / 4.0) * 4) + 4 - return (data[4:length+4], data[nextData:]) - - -def readInt(data): - if(len(data)<4): - print "Error: too few bytes for int", data, len(data) - rest = data - integer = 0 - else: - integer = struct.unpack(">i", data[0:4])[0] - rest = data[4:] - - return (integer, rest) - - - -def readLong(data): - """Tries to interpret the next 8 bytes of the data - as a 64-bit signed integer.""" - high, low = struct.unpack(">ll", data[0:8]) - big = (long(high) << 32) + low - rest = data[8:] - return (big, rest) - - - -def readFloat(data): - if(len(data)<4): - print "Error: too few bytes for float", data, len(data) - rest = data - float = 0 - else: - float = struct.unpack(">f", data[0:4])[0] - rest = data[4:] - - return (float, rest) - - -def OSCBlob(next): - """Convert a string into an OSC Blob, - returning a (typetag, data) tuple.""" - - if type(next) == type(""): - length = len(next) - padded = math.ceil((len(next)) / 4.0) * 4 - binary = struct.pack(">i%ds" % (padded), length, next) - tag = 'b' - else: - tag = '' - binary = '' - - return (tag, binary) - - -def OSCArgument(next): - """Convert some Python types to their - OSC binary representations, returning a - (typetag, data) tuple.""" - - if type(next) == type(""): - OSCstringLength = math.ceil((len(next)+1) / 4.0) * 4 - binary = struct.pack(">%ds" % (OSCstringLength), next) - tag = "s" - elif type(next) == type(42.5): - binary = struct.pack(">f", next) - tag = "f" - elif type(next) == type(13): - binary = struct.pack(">i", next) - tag = "i" - else: - binary = "" - tag = "" - - return (tag, binary) - - -def parseArgs(args): - """Given a list of strings, produces a list - where those strings have been parsed (where - possible) as floats or integers.""" - parsed = [] - for arg in args: - print arg - arg = arg.strip() - interpretation = None - try: - interpretation = float(arg) - if string.find(arg, ".") == -1: - interpretation = int(interpretation) - except: - # Oh - it was a string. - interpretation = arg - pass - parsed.append(interpretation) - return parsed - - - -def decodeOSC(data): - """Converts a typetagged OSC message to a Python list.""" - table = {"i":readInt, "f":readFloat, "s":readString, "b":readBlob} - decoded = [] - address, rest = readString(data) - typetags = "" - - if address == "#bundle": - time, rest = readLong(rest) -# decoded.append(address) -# decoded.append(time) - while len(rest)>0: - length, rest = readInt(rest) - decoded.append(decodeOSC(rest[:length])) - rest = rest[length:] - - elif len(rest) > 0: - typetags, rest = readString(rest) - decoded.append(address) - decoded.append(typetags) - if typetags[0] == ",": - for tag in typetags[1:]: - value, rest = table[tag](rest) - decoded.append(value) - else: - print "Oops, typetag lacks the magic ," - - return decoded - - -class CallbackManager: - """This utility class maps OSC addresses to callables. - - The CallbackManager calls its callbacks with a list - of decoded OSC arguments, including the address and - the typetags as the first two arguments.""" - - def __init__(self): - self.callbacks = {} - self.add(self.unbundler, "#bundle", '') - - def handle(self, data, source = None): - """Given OSC data, tries to call the callback with the - right address.""" - decoded = decodeOSC(data) - self.dispatch(decoded, source) - - def dispatch(self, message, source = None): - """Sends decoded OSC data to an appropriate callback""" - try: - if type(message[0]) == str : - # got a single message - address = message[0] - - if address in self.callbacks: - types = message[1].split(',')[1] - if(types == self.callbacks[address][1]): - self.callbacks[address][0](message, source) - else: - print '[error] path %s exist on osc server but...'%address - print ' the types received [%s] do not match with [%s].'%(types,self.callbacks[address][1]) - print ' received message: %s'%(message) - else: - print '[error] path %s does not exist on osc server.'%address - - elif type(message[0]) == list : - # smells like nested messages - for msg in message : - self.dispatch(msg, source) - - except KeyError, e: - # address not found - print 'address %s not found ' % address - pprint.pprint(message) - except IndexError, e: - print 'got malformed OSC message' - pass - except None, e: - print "Exception in", address, "callback :", e - - return - - def add(self, callback, name, types): - """Adds a callback to our set of callbacks, - or removes the callback with name if callback - is None.""" - if callback == None: - del self.callbacks[name] - else: - self.callbacks[name] = [callback, types] - - def unbundler(self, messages): - """Dispatch the messages in a decoded bundle.""" - # first two elements are #bundle and the time tag, rest are messages. - for message in messages[2:]: - self.dispatch(message) - - -if __name__ == "__main__": - hexDump("Welcome to the OSC testing program.") - print - message = OSCMessage() - message.setAddress("/foo/play") - message.append(44) - message.append(11) - message.append(4.5) - message.append("the white cliffs of dover") - hexDump(message.getBinary()) - - print "Making and unmaking a message.." - - strings = OSCMessage() - strings.append("Mary had a little lamb") - strings.append("its fleece was white as snow") - strings.append("and everywhere that Mary went,") - strings.append("the lamb was sure to go.") - strings.append(14.5) - strings.append(14.5) - strings.append(-400) - - raw = strings.getBinary() - - hexDump(raw) - - print "Retrieving arguments..." - data = raw - for i in range(6): - text, data = readString(data) - print text - - number, data = readFloat(data) - print number - - number, data = readFloat(data) - print number - - number, data = readInt(data) - print number - - hexDump(raw) - print decodeOSC(raw) - print decodeOSC(message.getBinary()) - - print "Testing Blob types." - - blob = OSCMessage() - blob.append("","b") - blob.append("b","b") - blob.append("bl","b") - blob.append("blo","b") - blob.append("blob","b") - blob.append("blobs","b") - blob.append(42) - - hexDump(blob.getBinary()) - - print decodeOSC(blob.getBinary()) - - def printingCallback(*stuff): - sys.stdout.write("Got: ") - for i in stuff: - sys.stdout.write(str(i) + " ") - sys.stdout.write("\n") - - print "Testing the callback manager." - - c = CallbackManager() - c.add(printingCallback, "/print") - - c.handle(message.getBinary()) - message.setAddress("/print") - c.handle(message.getBinary()) - - print1 = OSCMessage() - print1.setAddress("/print") - print1.append("Hey man, that's cool.") - print1.append(42) - print1.append(3.1415926) - - c.handle(print1.getBinary()) - - bundle = OSCMessage() - bundle.setAddress("") - bundle.append("#bundle") - bundle.append(0) - bundle.append(0) - bundle.append(print1.getBinary(), 'b') - bundle.append(print1.getBinary(), 'b') - - bundlebinary = bundle.message - - print "sending a bundle to the callback manager" - c.handle(bundlebinary) diff --git a/playerscoreboard.py b/playerscoreboard.py new file mode 100755 index 0000000..c6a180a --- /dev/null +++ b/playerscoreboard.py @@ -0,0 +1,131 @@ +#! /usr/bin/env python +# +# Copyright (C) 2007, One Laptop Per Child +# +# Muriel de Souza Godoi - muriel@laptop.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import gtk, pygtk + +import pango +import svglabel +import os +from score import Score + +class PlayerScoreboard(gtk.EventBox): + + def __init__(self, nick, fill_color, stroke_color,score = 0): + gtk.EventBox.__init__(self) + + self.default_color = '#4c4d4f' + self.selected_color = '#818286' + self.current_color = '#4c4d4f' + self.status = False + + self.fill_color = fill_color + self.stroke_color = stroke_color + + # Set table + self.table = gtk.Table(2, 3, True) + self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.current_color)) + self.table.set_row_spacings(0) + self.table.set_col_spacings(5) + self.table.set_border_width(10) + + self.scores = [] + self.current_x = 1 + self.current_y = 1 + status = False + + # Set buddy icon + self.xo_buddy = os.path.join(os.path.dirname(__file__), "images/stock-buddy.svg") + self.icon = svglabel.SvgLabel(self.xo_buddy, fill_color, stroke_color, False, self.current_color, 45, 55) + + # Set waiting buddy icon + self.waiting_icon = svglabel.SvgLabel(self.xo_buddy, self.default_color, '#ffffff', False, self.current_color, 45, 55) + + # Cache the score icon + score_label = Score(fill_color, stroke_color) + self.score_pixbuf_unsel = score_label.get_pixbuf() + self.score_pixbuf_sel = score_label.get_pixbuf_sel() + + # Set nick label + self.nick = gtk.Label(nick) + self.nick.modify_font(pango.FontDescription("12")) + self.nick.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#ffffff')) + self.nick.set_alignment(0, 0.5) + + # Set message label + self.msg = gtk.Label('Waiting for next game...') + self.msg.modify_font(pango.FontDescription("12")) + self.msg.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#ffffff')) + self.msg.set_alignment(0, 0.5) + + self.add(self.table) + self.table.attach(self.icon, 0, 1, 0, 1) + self.table.attach(self.nick, 1, 7, 0, 1) + + if score <> 0: + for i in range(score): + self.increase_score() + + def increase_score(self): + new_score = Score(self.fill_color, self.stroke_color, self.score_pixbuf_sel, self.score_pixbuf_unsel,self.status) + self.scores.append(new_score) + new_score.show() + self.table.attach(new_score, self.current_x , self.current_x+1, self.current_y, self.current_y+1) + self.current_x += 1 + if self.current_x == 7: + self.current_x = 1 + self.current_y += 1 + self.queue_draw() + + def set_selected(self, sel): + self.status = sel + if sel: + self.current_color = self.selected_color + else: + self.current_color = self.default_color + self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.current_color)) + self.icon.set_background(self.current_color) + for score in self.scores: + score.set_selected(sel) + self.queue_draw() + + def reset(self): + for score in self.scores: + self.table.remove(score) + self.current_x = 1 + self.current_y = 1 + del self.scores + self.scores = [] + self.queue_draw() + + def set_wait_mode(self,status): + if status: + self.table.remove(self.icon) + self.table.attach(self.waiting_icon, 0, 1, 0, 1) + if len(self.scores) == 0: + self.table.attach(self.msg, 1, 7, 1, 2) + else: + self.table.remove(self.waiting_icon) + self.table.attach(self.icon, 0, 1, 0, 1) + self.table.remove(self.msg) + if len(self.scores) == 0: + self.table.remove(self.msg) + self.queue_draw() + \ No newline at end of file diff --git a/playtile.py b/playtile.py deleted file mode 100644 index e3c2494..0000000 --- a/playtile.py +++ /dev/null @@ -1,71 +0,0 @@ -import gtk -import hippo -import math -import os - -from sugar.graphics import units - -class PlayTile(hippo.CanvasBox, hippo.CanvasItem): - __gtype_name__ = 'PlayTile' - _BORDER_DEFAULT = units.points_to_pixels(1.0) - - def __init__(self, num, x, y, **kargs): - hippo.CanvasBox.__init__(self, **kargs) - - self.num = num - self.image = os.path.join(os.path.dirname(__file__), 'images/black.png') - self.scale_x = x - self.scale_y = y - - self._radius = units.points_to_pixels(5) - self.props.border_color = 100 - self.props.background_color = 100 - self.props.orientation = hippo.ORIENTATION_VERTICAL - self.props.border = self._BORDER_DEFAULT - self.props.border_left = self._radius - self.props.border_right = self._radius - - self.append(self._build_title_box()) - - - def do_paint_background(self, cr, damaged_box): - [width, height] = self.get_allocation() - - x = self._BORDER_DEFAULT / 2 - y = self._BORDER_DEFAULT / 2 - width -= self._BORDER_DEFAULT - height -= self._BORDER_DEFAULT - - cr.move_to(x + self._radius, y); - cr.arc(x + width - self._radius, y + self._radius, - self._radius, math.pi * 1.5, math.pi * 2); - cr.arc(x + width - self._radius, x + height - self._radius, - self._radius, 0, math.pi * 0.5); - cr.arc(x + self._radius, y + height - self._radius, - self._radius, math.pi * 0.5, math.pi); - cr.arc(x + self._radius, y + self._radius, self._radius, - math.pi, math.pi * 1.5); - - hippo.cairo_set_source_rgba32(cr, self.props.background_color) - cr.fill() - - def _build_title_box(self): - hbox = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL) - hbox.props.spacing = units.points_to_pixels(5) - hbox.props.padding_top = units.points_to_pixels(5) - hbox.props.padding_bottom = units.points_to_pixels(5) - - self.img_widget = gtk.Image() - pixbuf_i = gtk.gdk.pixbuf_new_from_file(self.image) - self.scaledbuf_i = pixbuf_i.scale_simple(self.scale_x, self.scale_y, gtk.gdk.INTERP_BILINEAR) - self.img_widget.set_from_pixbuf(self.scaledbuf_i) - - canvas_widget = hippo.CanvasWidget() - canvas_widget.props.widget = self.img_widget - self.img_widget.show() - hbox.append(canvas_widget) - - return hbox - - - diff --git a/playview.py b/playview.py deleted file mode 100644 index 1a9a15d..0000000 --- a/playview.py +++ /dev/null @@ -1,42 +0,0 @@ -import hippo -import os -import cairo -import gtk -import math - -from sugar.graphics import color - -from playtile import PlayTile - -class PlayView(hippo.CanvasBox, hippo.CanvasItem): - def __init__(self, wx, wy, numtiles, **kargs): - hippo.CanvasBox.__init__(self, **kargs) - - self.props.orientation = hippo.ORIENTATION_VERTICAL - self.tiles = [] - tile_num = 0 - - numx = math.pow(float(numtiles), 0.5) - if numx%1 != 0: - numx = int(numx)+1 - self.y = self.x = (int(wx/numx)-10) - - while tile_num < numtiles: - if tile_num == 0 or ((tile_num)%numx) == 0: - box = hippo.CanvasBox() - box.props.orientation = hippo.ORIENTATION_HORIZONTAL - self.append(box) - - tile = PlayTile(tile_num, self.x, self.y) - self.tiles.append(tile) - box.append(tile) - - tile_num+=1 - - def flip(self, tile_num, obj, color): - tile = self.tiles[tile_num] - tile.img_pixbuf = gtk.gdk.pixbuf_new_from_file(obj) - tile.img_widget.set_from_pixbuf(tile.img_pixbuf.scale_simple(self.x, self.y, gtk.gdk.INTERP_BILINEAR)) - tile.props.background_color = color - tile.emit_paint_needed(0, 0, -1, -1) - diff --git a/score.py b/score.py new file mode 100755 index 0000000..7b6e11e --- /dev/null +++ b/score.py @@ -0,0 +1,67 @@ +#! /usr/bin/env python +# +# Copyright (C) 2007, One Laptop Per Child +# +# Muriel de Souza Godoi - muriel@laptop.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import rsvg +import re +import svglabel +import gtk +import gobject +import os + +class Score(svglabel.SvgLabel): + + selected_color = "#818286" + default_color = "#4c4d4f" + status = False + + def __init__(self, fill_color, stroke_color, pixbuf= None, pixbuf_sel = None,status= False): + filename = os.path.join(os.path.dirname(__file__), "images/score.svg") + self.pixbuf_un = pixbuf + self.pixbuf_sel = pixbuf_sel + self.status = status + if self.pixbuf_un == None: + self.pixbuf_un = svglabel.SvgLabel(filename, fill_color, stroke_color, False, self.default_color).get_pixbuf() + if self.pixbuf_sel == None: + self.pixbuf_sel = svglabel.SvgLabel(filename, fill_color, stroke_color, False, self.selected_color).get_pixbuf() + if status: + self.pixbuf = self.pixbuf_sel + else: + self.pixbuf = self.pixbuf_un + + svglabel.SvgLabel.__init__(self, filename, fill_color, stroke_color, self.pixbuf, self.default_color, 35, 35) + self.set_selected(status) + + def set_selected(self, status): + self.status = status + if status: + self.pixbuf = self.pixbuf_sel + self.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(self.selected_color)) + else: + self.pixbuf = self.pixbuf_un + self.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(self.default_color)) + self.queue_draw() + + def get_pixbuf_un(self): + return self.pixbuf_un + + def get_pixbuf_sel(self): + return self.pixbuf_sel + \ No newline at end of file diff --git a/scoreboard.py b/scoreboard.py new file mode 100755 index 0000000..8c00214 --- /dev/null +++ b/scoreboard.py @@ -0,0 +1,83 @@ +#! /usr/bin/env python +# +# Copyright (C) 2007, One Laptop Per Child +# +# Muriel de Souza Godoi - muriel@laptop.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import gtk +from playerscoreboard import PlayerScoreboard + +class Scoreboard(gtk.EventBox): + def __init__(self): + gtk.EventBox.__init__(self) + + self.players = {} + self.current_buddy = None + + self.set_size_request(400, 150) + self.vbox = gtk.VBox(False) + + fill_box = gtk.EventBox() + fill_box.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#4c4d4f')) + fill_box.show() + self.vbox.pack_end(fill_box, True, True) + + scroll = gtk.ScrolledWindow() + scroll.props.shadow_type = gtk.SHADOW_NONE + scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scroll.add_with_viewport(self.vbox) + scroll.set_border_width(0) + scroll.get_child().set_property('shadow-type', gtk.SHADOW_NONE) + self.add(scroll) + self.show() + + def add_buddy(self, widget, buddy, score): + nick = buddy.props.nick + stroke_color, fill_color = buddy.props.color.split(',') + player = PlayerScoreboard(nick, fill_color, stroke_color, score) + player.show() + self.players[buddy]=player + self.vbox.pack_start(player, False, True) + if score == -1: + player.set_wait_mode(True) + self.show_all() + + + + def rem_buddy(self, widget, buddy): + self.vbox.remove(self.players[buddy]) + del self.players[id] + + def set_selected(self, widget, buddy): + if self.current_buddy <> None: + old = self.players[self.current_buddy] + old.set_selected(False) + self.current_buddy = buddy + player = self.players[buddy] + player.set_selected(True) + + def increase_score(self, widget, buddy): + self.players[buddy].increase_score() + + def reset(self, widget): + for buddy in self.players.keys(): + self.players[buddy].reset() + + def set_wait_mode(self, widget, buddy, status): + self.players[buddy].set_wait_mode(status) + \ No newline at end of file diff --git a/svgcard.py b/svgcard.py new file mode 100755 index 0000000..9062d18 --- /dev/null +++ b/svgcard.py @@ -0,0 +1,215 @@ +#! /usr/bin/env python +# +# Copyright (C) 2007, One Laptop Per Child +# +# Muriel de Souza Godoi - muriel@laptop.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import os +import gc +import rsvg +import re +import svglabel +import gtk +import gobject + +class SvgCard(gtk.DrawingArea): + + border_svg = os.path.join(os.path.dirname(__file__), "images/card.svg") + text_svg = os.path.join(os.path.dirname(__file__), "images/card-text.svg") + + # Default properties + default_props = {} + default_props['back_border'] = {'filename':border_svg, 'fill_color':'#b2b3b7', 'stroke_color':'#b2b3b7', 'opacity':'1'} + default_props['back_h_border'] = {'filename':border_svg, 'fill_color':'#b2b3b7', 'stroke_color':'#ffffff', 'opacity':'1'} + default_props['back_text'] = {'filename':text_svg, 'text_color':'#c7c8cc'} + default_props['front_border'] = {'filename':border_svg, 'fill_color':'#4c4d4f', 'stroke_color':'#ffffff', 'opacity':'0'} + default_props['front_h_border'] = {'filename':border_svg, 'fill_color':'#555555', 'stroke_color':'#888888', 'opacity':'0.5'} + default_props['front_text'] = {'filename':text_svg, 'text_color':'#ffffff'} + + + def __init__(self, id, pprops, pcache, jpeg, size): + gtk.DrawingArea.__init__(self) + self.set_size_request(size, size) + self.bg_color = '#000000' + self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.bg_color)) + self.connect('expose-event', self._expose_cb) + self.flipped = False + self.id = id + self.jpeg = jpeg + self.size = size + self.set_flags(gtk.CAN_FOCUS) + + # Views properties + views = ['back_border','back_h_border','back_text','front_border','front_h_border','front_text'] + self.pprops = pprops + self.props = {} + for view in views: + self.props[view] = {} + self.props[view].update(self.default_props[view]) + self.props[view].update(pprops.get(view, {})) + + # Cache + self.cache = {} + self.cache.update(pcache) + + if len(self.cache) == 0: + build_all = True + else: + build_all = False + + self.build_all = build_all + + if build_all or pprops.has_key('back_border'): + self.cache['back_border']= self._read_icon_data(self.props['back_border']) + if build_all or pprops.has_key('back_h_border'): + self.cache['back_h_border']= self._read_icon_data(self.props['back_h_border']) + if build_all or pprops.has_key('back_text'): + text = self._read_icon_data(self.props['back_text']) + self.cache['back_text'] = text.scale_simple(self.size-14, self.size-14, gtk.gdk.INTERP_BILINEAR) + del text + if build_all or self.pprops.has_key('back_border') or self.pprops.has_key('back_text'): + self.cache['back'] = self.build_face('back') + if build_all or self.pprops.has_key('back_h_border') or self.pprops.has_key('back_text'): + self.cache['back_h'] = self.build_face('back_h') + + self.current_pixbuf = self.cache['back'] + # Set events and listeners + self.set_events(gtk.gdk.ALL_EVENTS_MASK) + gc.collect() + self.show() + + def _expose_cb(self, widget, event): + self.window.draw_pixbuf(None, self.current_pixbuf, 0, 0, 0, 0) + return False + + def _read_icon_data(self, dict): + icon_file = open(dict.get('filename', 'card.svg'), 'r') + data = icon_file.read() + icon_file.close() + + # Replace borders parameters + entity = '' % dict.get('fill_color', '') + data = re.sub('', entity, data) + + entity = '' % dict.get('stroke_color', '') + data = re.sub('', entity, data) + + entity = '' % dict.get('opacity', '') + data = re.sub('', entity, data) + + data = re.sub('size_card1', str(self.size), data) + data = re.sub('size_card2', str(self.size-6), data) + data = re.sub('size_card3', str(self.size-17), data) + + # Replace text parameters + entity = '' % dict.get('text_color', '#ffffff') + data = re.sub('', entity, data) + + data = re.sub('card_text', dict.get('card_text', ''), data) + data = re.sub('card_line1', dict.get('card_line1', ''), data) + data = re.sub('card_line2', dict.get('card_line2', ''), data) + data = re.sub('card_line3', dict.get('card_line3', ''), data) + data = re.sub('card_line4', dict.get('card_line4', ''), data) + + self.data_size = len(data) + return rsvg.Handle(data=data).get_pixbuf() + + def build_face(self, face): + if face.endswith('_h'): + text = face[:-2] + else: + text = face + pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, self.size, self.size) + pixbuf.fill(0x00000000) + self.cache[face + '_border'].composite(pixbuf, 0, 0, self.size, self.size, 0, 0, 1, 1, gtk.gdk.INTERP_NEAREST, 255) + if face.startswith('front') and self.jpeg <> None: + self.cache['jpeg'].composite(pixbuf, 11, 11, self.size-22, self.size-22, 11, 11, 1, 1, gtk.gdk.INTERP_NEAREST, 255) + #self.cache[face + '_border'].composite(pixbuf, 0, 0, self.size, self.size, 0, 0, 1, 1, gtk.gdk.INTERP_BILINEAR, 255) + self.cache[text + '_text'].composite(pixbuf, 11, 11, self.size-22, self.size-22, 11, 11, 1, 1, gtk.gdk.INTERP_NEAREST, 255) + return pixbuf + + def set_border(self, stroke_color, fill_color): + self.props['front_border'].update({'fill_color':fill_color, 'stroke_color':stroke_color}) + self.cache['front_border'] = self._read_icon_data(self.props['front_border']) + self.cache['front'] = self.build_face('front') + self.current_pixbuf = self.cache['front'] + self.queue_draw() + + def set_highlight(self, status, mouse = False): + if self.flipped: + if mouse: + return + if status: + self.current_pixbuf = self.cache['front_h'] + else: + self.current_pixbuf = self.cache['front'] + else: + if status: + self.current_pixbuf = self.cache['back_h'] + else: + self.current_pixbuf = self.cache['back'] + self.queue_draw() + + def flip(self): + if self.build_all or self.pprops.has_key('front_border'): + self.cache['front_border']= self._read_icon_data(self.props['front_border']) + if self.build_all or self.pprops.has_key('front_h_border'): + self.cache['front_h_border']= self._read_icon_data(self.props['front_h_border']) + if self.build_all or self.pprops.has_key('front_text'): + text = self._read_icon_data(self.props['front_text']) + self.cache['front_text'] = text.scale_simple(self.size-22, self.size-22, gtk.gdk.INTERP_BILINEAR) + del text + + if self.jpeg <> None: + pixbuf_t = gtk.gdk.pixbuf_new_from_file(self.jpeg) + # pixbuf_t = pixbuf_t.add_alpha(True,chr(255),chr(255),chr(255)) + self.cache['jpeg']= pixbuf_t.scale_simple(self.size-22, self.size-22, gtk.gdk.INTERP_BILINEAR) + del pixbuf_t + + if self.cache.has_key('front_border') or self.cache.has_key('front_text'): + self.cache['front'] = self.build_face('front') + if self.cache.has_key('front_h_border') or self.cache.has_key('front_text'): + self.cache['front_h'] = self.build_face('front_h') + + if not self.flipped: + self.current_pixbuf = self.build_face('front') + self.flipped = True + self.queue_draw() + while gtk.events_pending(): + gtk.main_iteration() + + def flop(self): + self.current_pixbuf = self.build_face('back') + self.flipped = False + self.queue_draw() + + def is_flipped(self): + return self.flipped + + def get_id(self): + return self.id + + def get_cache(self): + return self.cache + + def reset(self): + if self.flipped: + fill_color = self.default_props.get('front_border').get('fill_color') + stroke_color = self.default_props.get('front_border').get('stroke_color') + self.set_border(fill_color, stroke_color) + self.flop() \ No newline at end of file diff --git a/svglabel.py b/svglabel.py new file mode 100755 index 0000000..64c7f44 --- /dev/null +++ b/svglabel.py @@ -0,0 +1,103 @@ +#! /usr/bin/env python +# +# Copyright (C) 2007, One Laptop Per Child +# +# Muriel de Souza Godoi - muriel@laptop.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import gtk, pygtk +import rsvg +import cairo +import re + +class SvgLabel(gtk.DrawingArea): + + filename = '' + fill_color = '' + stroke_color = '' + background_color = '' + + + def __init__(self, filename, fill_color, stroke_color, pixbuf = False, background_color = '', request_x = 45, request_y = 45): + gtk.DrawingArea.__init__(self) + self.set_size_request(request_x, request_y) + self.filename = filename + self.background_color = background_color + self.fill_color = fill_color + self.stroke_color = stroke_color + self.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(background_color)) + if pixbuf: + self.pixbuf = pixbuf + else: + self.pixbuf = self._read_icon_data(self.filename, self.fill_color, self.stroke_color) + + self.connect('expose-event', self._expose_cb) + + def _expose_cb(self, widget, event): + widget.window.draw_pixbuf(None,self.pixbuf, 0, 0, 0, 0) + return False + + def _read_icon_data(self, filename, fill_color, stroke_color): + icon_file = open(filename, 'r') + data = icon_file.read() + icon_file.close() + + if fill_color: + entity = '' % fill_color + data = re.sub('', entity, data) + + if stroke_color: + entity = '' % stroke_color + data = re.sub('', entity, data) + + self.data_size = len(data) + return rsvg.Handle(data=data).get_pixbuf() + + def set_color(self, fill_color, stroke_color): + self.fill_color = fill_color + self.stroke_color = stroke_color + self.pixmap = self._read_icon_data(self.filename, self.fill_color, self.stroke_color) + self.queue_draw() + + def set_fill_color(self, fill_color): + self.fill_color = fill_color + self.pixmap = self._read_icon_data(self.filename, self.fill_color, self.stroke_color) + self.queue_draw() + + def get_fill_color(self): + return self.fill_color + + def set_stroke_color(self, stroke_color): + self.stroke_color = stroke_color + self.pixmap = self._read_icon_data(self.filename, self.fill_color, self.stroke_color) + self.queue_draw() + + def get_stroke_color(self): + return self.stroke_color + + def get_pixbuf(self): + return self.pixbuf + + def set_pixbuf(self, pixbuf): + self.pixbuf = pixbuf + self.queue_draw() + + def set_background(self, background_color): + self.background_color = background_color + self.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse(self.background_color)) + self.queue_draw() + \ No newline at end of file -- cgit v0.9.1