# Copyright (C) 2006, 2007, 2008 One Laptop Per Child
#
# 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
import svgcard
import logging
import os
from os import environ
from os.path import join
import model
import zipfile
import tempfile
from gobject import SIGNAL_RUN_FIRST, TYPE_PYOBJECT
from sugar.graphics import style
from sugar.graphics.icon import Icon
import theme
_logger = logging.getLogger('memorize-activity')
class CardList(gtk.EventBox):
__gsignals__ = {
'pair-selected': (SIGNAL_RUN_FIRST, None, 9 * [TYPE_PYOBJECT]),
'update-create-toolbar': (SIGNAL_RUN_FIRST, None, 3 * [TYPE_PYOBJECT]),
'update-create-buttons': (SIGNAL_RUN_FIRST, None, 2 * [TYPE_PYOBJECT]),
}
def __init__(self):
gtk.EventBox.__init__(self)
self.model = model.Model()
self.pairs = []
self.current_pair = None
self.current_game_key = None
self.vbox = gtk.VBox(False)
fill_box = gtk.Label()
fill_box.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#000000'))
fill_box.show()
self.vbox.pack_end(fill_box, True, True)
scroll = gtk.ScrolledWindow()
scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scroll.add_with_viewport(self.vbox)
scroll.set_border_width(0)
scroll.get_child().modify_bg(gtk.STATE_NORMAL,
gtk.gdk.color_parse('#000000'))
self.add(scroll)
self.show_all()
def load_game(self, widget, game_name):
self.model.read(game_name)
self.current_game_key = self.model.data['game_file']
self.emit('update-create-toolbar', self.model.data['name'],
self.model.data.get('equal_pairs', 'False'),
self.model.data.get('divided', '0'))
game_pairs = self.model.pairs
game_data = self.model.data
self.clean_list()
for key in game_pairs:
if game_pairs[key].props.aimg != None:
aimg = gtk.gdk.pixbuf_new_from_file( \
join(self.model.data['pathimg'],
game_pairs[key].props.aimg))
else:
aimg = None
if game_pairs[key].props.bimg != None:
bimg = gtk.gdk.pixbuf_new_from_file( \
join(self.model.data['pathimg'],
game_pairs[key].props.bimg))
else:
bimg = None
if game_pairs[key].props.asnd != None:
asnd = join(self.model.data['pathsnd'],
game_pairs[key].props.asnd)
else:
asnd = None
if game_pairs[key].props.bsnd != None:
bsnd = join(self.model.data['pathsnd'],
game_pairs[key].props.bsnd)
else:
bsnd = None
self.add_pair(None, game_pairs[key].props.achar,
game_pairs[key].props.bchar, aimg, bimg, asnd, bsnd,
game_pairs[key].props.aspeak, game_pairs[key].props.bspeak,
False)
def save_game(self, widget, game_name, equal_pairs, grouped):
tmp_root = join(environ['SUGAR_ACTIVITY_ROOT'], 'instance')
temp_folder = tempfile.mkdtemp(dir=tmp_root)
os.chmod(temp_folder, 0777)
temp_img_folder = join(temp_folder, 'images')
temp_snd_folder = join(temp_folder, 'sounds')
os.makedirs(temp_img_folder)
os.makedirs(temp_snd_folder)
game_zip = zipfile.ZipFile(join(temp_folder, 'game.zip'), 'w')
game_model = model.Model(temp_folder)
game_model.data['name'] = game_name
for pair in range(len(self.pairs)):
pair_card = model.Pair()
# achar
achar = self.pairs[pair].get_text(1)
if achar != '':
pair_card.set_property('achar', achar)
# bchar
bchar = self.pairs[pair].get_text(2)
if bchar != '':
pair_card.set_property('bchar', bchar)
# aspeak
aspeak = self.pairs[pair].get_speak(1)
pair_card.set_property('aspeak', aspeak)
# bspeak
bspeak = self.pairs[pair].get_speak(2)
pair_card.set_property('bspeak', bspeak)
# aimg
aimg = self.pairs[pair].get_pixbuf(1)
if aimg != None:
if equal_pairs:
aimgfile = 'img' + str(pair) + '.jpg'
else:
aimgfile = 'aimg' + str(pair) + '.jpg'
aimg.save(join(temp_img_folder, aimgfile), 'jpeg',
{'quality':'85'})
game_zip.write(join(temp_img_folder, aimgfile),
join('images', aimgfile))
pair_card.set_property('aimg', aimgfile)
# bimg
bimg = self.pairs[pair].get_pixbuf(2)
if bimg != None:
if equal_pairs:
bimgfile = 'img' + str(pair) + '.jpg'
else:
bimgfile = 'bimg' + str(pair) + '.jpg'
bimg.save(join(temp_img_folder, bimgfile), 'jpeg',
{'quality':'85'})
game_zip.write(join(temp_img_folder, bimgfile),
join('images', bimgfile))
pair_card.set_property('bimg', bimgfile)
# asnd
asnd = self.pairs[pair].get_sound(1)
if asnd != None:
if equal_pairs:
asndfile = 'snd' + str(pair) + '.ogg'
else:
asndfile = 'asnd' + str(pair) + '.ogg'
_logger.error(asndfile + ': ' + asnd)
game_zip.write(asnd, join('sounds', asndfile))
pair_card.set_property('asnd', asndfile)
# bsnd
bsnd = self.pairs[pair].get_sound(2)
if bsnd != None:
if equal_pairs:
bsndfile = 'snd'+str(pair)+'.ogg'
else:
bsndfile = 'bsnd' + str(pair) + '.ogg'
game_zip.write(bsnd, join('sounds', bsndfile))
_logger.error(bsndfile + ': ' + bsnd)
pair_card.set_property('bsnd', bsndfile)
game_model.pairs[pair] = pair_card
game_model.write(equal_pairs, grouped)
game_zip.write(join(temp_folder, 'game.xml'), 'game.xml')
game_zip.close()
game_model.save_byte_array(join(temp_folder, 'game.zip'), game_name)
def clean_list(self, button = None):
if button != None:
self.current_game_key = None
map(lambda x: self.vbox.remove(x), self.pairs)
del self.pairs
self.pairs = []
def clean_tmp_folder(self, path):
for root, dirs, files in os.walk(path, topdown=False):
for name in files:
os.remove(join(root, name))
for name in dirs:
os.rmdir(join(root, name))
os.rmdir(path)
def add_pair(self, widget, achar, bchar, aimg, bimg, asnd, bsnd,
aspeak, bspeak, show = True):
pair = Pair(achar, bchar, aimg, bimg, asnd, bsnd, aspeak, bspeak)
self.vbox.pack_end(pair, False, True)
self.pairs.append(pair)
pair.connect('pair-selected', self.set_selected)
pair.connect('pair-closed', self.rem_pair)
self.emit('update-create-buttons', True, True)
if show:
self.show_all()
def rem_pair(self, widget, event):
self.vbox.remove(widget)
self.pairs.remove(widget)
del widget
self.emit('update-create-buttons', True, True)
self.emit('pair-selected', False, None, None, None, None, None, None,
False, False)
def set_selected(self, widget, event):
if self.current_pair is not None:
current_pair = self.current_pair
current_pair.set_selected(False)
self.current_pair = widget
widget.set_selected(True)
self.emit('pair-selected', True,
self.current_pair.get_text(1), self.current_pair.get_text(2),
self.current_pair.get_pixbuf(1), self.current_pair.get_pixbuf(2),
self.current_pair.get_sound(1), self.current_pair.get_sound(2),
self.current_pair.get_speak(1), self.current_pair.get_speak(2))
def update_selected(self, widget, newtext1, newtext2, aimg, bimg,
asnd, bsnd, aspeak, bspeak):
self.current_pair.change_text(newtext1, newtext2)
self.current_pair.change_pixbuf(aimg, bimg)
self.current_pair.change_sound(asnd, bsnd)
self.current_pair.change_speak(aspeak, bspeak)
self.emit('update-create-buttons', True, True)
class Pair(gtk.EventBox):
__gsignals__ = {
'pair-selected': (SIGNAL_RUN_FIRST, None, [TYPE_PYOBJECT]),
'pair-closed': (SIGNAL_RUN_FIRST, None, [TYPE_PYOBJECT]),
}
def __init__(self, text1, text2 = None, aimg = None, bimg = None,
asnd = None, bsnd = None, aspeak=None, bspeak=None):
gtk.EventBox.__init__(self)
self.bg_color = '#000000'
self.asnd = asnd
self.bsnd = bsnd
self.current_game_key = None
row = gtk.HBox()
row.props.border_width = 10
row.props.spacing = 10
self.bcard1 = svgcard.SvgCard(-1,
{ 'front_text' : { 'card_text' : text1,
'speak' : aspeak,
'text_color' : '#ffffff' },
'front' : { 'fill_color' : '#4c4d4f',
'stroke_color' : '#ffffff',
'opacity' : '1' } },
None, theme.PAIR_SIZE, 1, self.bg_color)
self.bcard1.flip()
self.bcard1.set_pixbuf(aimg)
align = gtk.Alignment(.5, .5, 0, 0)
align.add(self.bcard1)
row.pack_start(align)
self.bcard2 = svgcard.SvgCard(-1,
{ 'front_text' : { 'card_text' : text2,
'speak' : bspeak,
'text_color' : '#ffffff' },
'front' : { 'fill_color' : '#4c4d4f',
'stroke_color' : '#ffffff',
'opacity' : '1' } },
None, theme.PAIR_SIZE, 1, self.bg_color)
self.bcard2.flip()
self.bcard2.set_pixbuf(bimg)
align = gtk.Alignment(.5, .5, 0, 0)
align.add(self.bcard2)
row.pack_start(align)
close_image = Icon(
icon_name='remove',
icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
align = gtk.Alignment(.5, .5)
align.add(close_image)
close_button = gtk.ToolButton()
close_button.set_icon_widget(align)
close_button.connect('clicked', self.emit_close)
close_button.set_size_request(style.STANDARD_ICON_SIZE,
style.STANDARD_ICON_SIZE)
align = gtk.Alignment(.5, 0, 0, 0)
align.add(close_button)
row.pack_start(align, False)
self.connect('button-press-event', self.emit_selected)
self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.bg_color))
self.add(row)
self.show_all()
def emit_selected(self, widget, event):
self.emit('pair-selected', self)
def emit_close(self, widget):
self.emit('pair-closed', self)
def set_selected(self, status):
if not status:
self.bg_color = '#000000'
else:
self.bg_color = '#b2b3b7'
self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.bg_color))
self.bcard1.set_background(self.bg_color)
self.bcard2.set_background(self.bg_color)
def change_pixbuf(self, aimg, bimg):
self.bcard1.set_pixbuf(aimg)
self.bcard2.set_pixbuf(bimg)
def change_text(self, text1, text2):
self.bcard1.change_text(text1)
self.bcard2.change_text(text2)
def change_sound(self, asnd, bsnd):
self.asnd = asnd
self.bsnd = bsnd
def get_text(self, card):
if card == 1:
return self.bcard1.get_text()
else:
return self.bcard2.get_text()
def change_speak(self, aspeak, bspeak):
self.bcard1.change_speak(aspeak)
self.bcard2.change_speak(bspeak)
def get_speak(self, card):
if card == 1:
return self.bcard1.get_speak()
else:
return self.bcard2.get_speak()
def get_pixbuf(self, card):
if card == 1:
return self.bcard1.get_pixbuf()
else:
return self.bcard2.get_pixbuf()
def get_sound(self, card):
if card == 1:
return self.asnd
else:
return self.bsnd