cardlist.py 12.2 KB
Newer Older
James Cameron's avatar
James Cameron committed
1
# Copyright (C) 2006, 2007, 2008 One Laptop per Child
2
#
James Cameron's avatar
James Cameron committed
3 4 5 6
# 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.
7
#
James Cameron's avatar
James Cameron committed
8 9 10 11
# 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.
12
#
James Cameron's avatar
James Cameron committed
13 14 15
# 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.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16

17
from gi.repository import GObject
James Cameron's avatar
James Cameron committed
18 19
from gi.repository import Gdk
from gi.repository import Gtk
20

James Cameron's avatar
James Cameron committed
21
from card import Card
22
import logging
23
from os.path import join, basename
24

25
from model import Pair
26

27
from sugar3.graphics import style
28
from sugar3 import profile
29

30
PAIR_SIZE = min(Gdk.Screen.width() / 7, Gdk.Screen.height() / 5)
31
user_color = profile.get_color()
32

Gonzalo Odiard's avatar
Gonzalo Odiard committed
33

34
class CardList(Gtk.EventBox):
Gonzalo Odiard's avatar
Gonzalo Odiard committed
35

36
    __gsignals__ = {
37
        'pair-selected': (GObject.SignalFlags.RUN_FIRST,
38
                          None, 9 * [GObject.TYPE_PYOBJECT]),
39
        'update-create-toolbar': (GObject.SignalFlags.RUN_FIRST,
40
                                  None, 3 * [GObject.TYPE_PYOBJECT]),
41
    }
42

43
    def __init__(self):
44
        Gtk.EventBox.__init__(self)
45 46
        self.pairs = []
        self.current_pair = None
Simon Schampijer's avatar
Simon Schampijer committed
47
        self.current_game_key = None
Gonzalo Odiard's avatar
Gonzalo Odiard committed
48
        self.model = None
49
        self.pair_list_modified = False
50
        self.game_loaded = False
51

52
        self.hbox = Gtk.HBox(False)
53

54
        fill_box = Gtk.Label()
55
        fill_box.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse('#d7d7d7'))
56
        fill_box.show()
57
        self.hbox.pack_end(fill_box, True, True, 0)
Gonzalo Odiard's avatar
Gonzalo Odiard committed
58

59 60 61
        self._scroll = Gtk.ScrolledWindow()
        self._scroll.set_policy(Gtk.PolicyType.AUTOMATIC,
                                Gtk.PolicyType.AUTOMATIC)
62
        self._scroll.add_with_viewport(self.hbox)
63 64
        self._scroll.set_border_width(0)
        self._scroll.get_child().modify_bg(Gtk.StateType.NORMAL,
65
                                           Gdk.color_parse('#d7d7d7'))
66
        self.add(self._scroll)
67
        self.set_size_request(-1, PAIR_SIZE * 2 + style.DEFAULT_SPACING * 4)
68
        self.show_all()
Gonzalo Odiard's avatar
Gonzalo Odiard committed
69

70
    def load_game(self, game):
71 72 73
        if self.game_loaded:
            return
        self.get_window().freeze_updates()
74
        self.model = game.model
75
        self.current_game_key = self.model.data['game_file']
76 77
        font_name1 = self.model.data['font_name1']
        font_name2 = self.model.data['font_name2']
78
        game_pairs = self.model.pairs
79
        self.clean_list(load=True)
80
        for key in game_pairs:
81
            if game_pairs[key].props.aimg is not None:
82 83
                aimg_path = join(self.model.data['pathimg'],
                                 game_pairs[key].props.aimg)
84
            else:
85
                aimg_path = None
Gonzalo Odiard's avatar
Gonzalo Odiard committed
86

87
            if game_pairs[key].props.bimg is not None:
88 89
                bimg_path = join(self.model.data['pathimg'],
                                 game_pairs[key].props.bimg)
90
            else:
91
                bimg_path = None
Gonzalo Odiard's avatar
Gonzalo Odiard committed
92

93
            if game_pairs[key].props.asnd is not None:
Simon Schampijer's avatar
Simon Schampijer committed
94 95
                asnd = join(self.model.data['pathsnd'],
                            game_pairs[key].props.asnd)
96 97
            else:
                asnd = None
Gonzalo Odiard's avatar
Gonzalo Odiard committed
98

99
            if game_pairs[key].props.bsnd is not None:
Simon Schampijer's avatar
Simon Schampijer committed
100 101
                bsnd = join(self.model.data['pathsnd'],
                            game_pairs[key].props.bsnd)
102 103
            else:
                bsnd = None
Gonzalo Odiard's avatar
Gonzalo Odiard committed
104

105 106
            self.add_pair(
                None, game_pairs[key].props.achar,
107
                game_pairs[key].props.bchar, aimg_path, bimg_path, asnd, bsnd,
108
                game_pairs[key].props.aspeak, game_pairs[key].props.bspeak,
109
                font_name1, font_name2, False, load=True)
110
        self.get_window().thaw_updates()
111 112 113
        self.emit('update-create-toolbar', self.model.data['name'],
                  self.model.data['equal_pairs'],
                  self.model.data['divided'])
114
        self.game_loaded = True
115 116 117 118

    def update_model(self, game_model):
        game_model.pairs = {}

119
        for pair in range(len(self.pairs)):
120
            pair_card = Pair()
121 122 123 124
            # achar
            achar = self.pairs[pair].get_text(1)
            if achar != '':
                pair_card.set_property('achar', achar)
125

126 127 128 129
            # bchar
            bchar = self.pairs[pair].get_text(2)
            if bchar != '':
                pair_card.set_property('bchar', bchar)
130

131 132 133 134 135 136 137 138
            # 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)

139
            # aimg
140
            aimg = self.pairs[pair].get_image_path(1)
141
            if aimg is not None:
142 143
                aimgfile = self.pairs[pair].get_image_path(1)
                pair_card.set_property('aimg', basename(aimgfile))
144
            # bimg
145
            bimg = self.pairs[pair].get_image_path(2)
146
            if bimg is not None:
147 148
                bimgfile = self.pairs[pair].get_image_path(2)
                pair_card.set_property('bimg', basename(bimgfile))
149 150
            # asnd
            asnd = self.pairs[pair].get_sound(1)
Gonzalo Odiard's avatar
Gonzalo Odiard committed
151
            logging.debug('update_model asnd %s', asnd)
152
            if asnd is not None:
153
                pair_card.set_property('asnd', basename(asnd))
154 155
            # bsnd
            bsnd = self.pairs[pair].get_sound(2)
Gonzalo Odiard's avatar
Gonzalo Odiard committed
156
            logging.debug('update_model bsnd %s', bsnd)
157
            if bsnd is not None:
158
                pair_card.set_property('bsnd', basename(bsnd))
Gonzalo Odiard's avatar
Gonzalo Odiard committed
159

160
            game_model.pairs[pair] = pair_card
161
        self.pair_list_modified = False
Simon Schampijer's avatar
Simon Schampijer committed
162

Gonzalo Odiard's avatar
Gonzalo Odiard committed
163
    def clean_list(self, button=None, load=False):
164
        if button is not None:
165
            self.current_game_key = None
166
        map(lambda x: self.hbox.remove(x), self.pairs)
167 168
        del self.pairs
        self.pairs = []
169
        if not load:
170
            self.pair_list_modified = True
171
            self.model.mark_modified()
Gonzalo Odiard's avatar
Gonzalo Odiard committed
172

173
    def add_pair(self, widget, achar, bchar, aimg_path, bimg_path, asnd, bsnd,
174
                 aspeak, bspeak, font_name1, font_name2,
175 176 177
                 show=True, load=False):
        pair = CardPair(achar, bchar, aimg_path, bimg_path, asnd, bsnd,
                        aspeak, bspeak, font_name1, font_name2)
178
        self.hbox.pack_end(pair, False, True, 0)
179 180
        self.pairs.append(pair)
        pair.connect('pair-selected', self.set_selected)
181 182
        if not load:
            self.model.mark_modified()
183
            self.pair_list_modified = True
184 185
            adj = self._scroll.get_vadjustment()
            adj.set_value(adj.get_lower())
186 187
        if show:
            self.show_all()
Gonzalo Odiard's avatar
Gonzalo Odiard committed
188

189 190 191 192 193 194 195 196 197
    def change_font(self, widget, group, font_name):
        for pair in self.pairs:
            pair.change_font(group, font_name)
        if group == 1:
            self.model.data['font_name1'] = font_name
        if group == 2:
            self.model.data['font_name2'] = font_name
        self.model.mark_modified()

198 199 200 201
    def rem_current_pair(self, widget):
        self.hbox.remove(self.current_pair)
        self.pairs.remove(self.current_pair)
        self.current_pair = None
202
        self.model.mark_modified()
203
        self.emit('pair-selected', False, None, None, None, None, None, None,
204
                  False, False)
Simon Schampijer's avatar
Simon Schampijer committed
205

206
    def set_selected(self, widget, event):
Simon Schampijer's avatar
Simon Schampijer committed
207 208 209
        if self.current_pair is not None:
            current_pair = self.current_pair
            current_pair.set_selected(False)
Gonzalo Odiard's avatar
Gonzalo Odiard committed
210
        self.current_pair = widget
211
        widget.set_selected(True)
212
        self.emit('pair-selected', True,
213 214
                  self.current_pair.get_text(1),
                  self.current_pair.get_text(2),
215 216
                  self.current_pair.get_image_path(1),
                  self.current_pair.get_image_path(2),
217 218 219 220
                  self.current_pair.get_sound(1),
                  self.current_pair.get_sound(2),
                  self.current_pair.get_speak(1),
                  self.current_pair.get_speak(2))
221

222
    def update_selected(self, widget, newtext1, newtext2, aimg_path, bimg_path,
223
                        asnd, bsnd, aspeak, bspeak):
224
        self.current_pair.change_text(newtext1, newtext2)
225
        self.current_pair.change_image_path(aimg_path, bimg_path)
226
        self.current_pair.change_sound(asnd, bsnd)
227
        self.current_pair.change_speak(aspeak, bspeak)
228
        self.model.mark_modified()
229
        self.pair_list_modified = False
230

Gonzalo Odiard's avatar
Gonzalo Odiard committed
231

232
class CardPair(Gtk.EventBox):
233

234
    __gsignals__ = {
235 236
        'pair-selected': (GObject.SignalFlags.RUN_FIRST,
                          None, [GObject.TYPE_PYOBJECT]),
237
    }
238

239
    def __init__(self, text1, text2=None, aimg_path=None, bimg_path=None,
240
                 asnd=None, bsnd=None, aspeak=None, bspeak=None,
241
                 font_name1=None, font_name2=None):
242
        Gtk.EventBox.__init__(self)
243
        self.bg_color = '#d7d7d7'
244 245
        self._stroke_color = '#ffffff'
        self._fill_color = '#4c4d4f'
246

247 248
        self.asnd = asnd
        self.bsnd = bsnd
249

250
        self.current_game_key = None
251

252
        row = Gtk.VBox()
253 254
        row.props.margin_left = style.DEFAULT_SPACING
        row.props.margin_right = style.DEFAULT_SPACING
255

James Cameron's avatar
James Cameron committed
256
        self.bcard1 = Card(
257
            -1, {'front_text': {'card_text': text1,
Gonzalo Odiard's avatar
Gonzalo Odiard committed
258
                                'speak': aspeak,
259
                                'text_color': style.Color('#ffffff')},
260 261
                 'front': {'fill_color': style.Color(self._fill_color),
                           'stroke_color': style.Color(self._stroke_color)}},
262
            aimg_path, PAIR_SIZE, self.bg_color, font_name1)
263
        self.bcard1.flip()
264 265
        self.bcard1.set_valign(Gtk.Align.CENTER)
        row.pack_start(self.bcard1, True, True, 0)
266

James Cameron's avatar
James Cameron committed
267
        self.bcard2 = Card(
268
            -1, {'front_text': {'card_text': text2,
Gonzalo Odiard's avatar
Gonzalo Odiard committed
269
                                'speak': bspeak,
270
                                'text_color': style.Color('#ffffff')},
271 272
                 'front': {'fill_color': style.Color(self._fill_color),
                           'stroke_color': style.Color(self._stroke_color)}},
273
            bimg_path, PAIR_SIZE, self.bg_color, font_name2)
274
        self.bcard2.flip()
275 276
        self.bcard2.set_valign(Gtk.Align.CENTER)
        row.pack_start(self.bcard2, True, True, 0)
277
        self.connect('button-press-event', self.emit_selected)
278
        self.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(self.bg_color))
279
        self.add(row)
280
        self.show_all()
281

282 283
    def emit_selected(self, widget, event):
        self.emit('pair-selected', self)
284

285 286
    def set_selected(self, status):
        if not status:
287 288
            stroke_color = self._stroke_color
            fill_color = self._fill_color
289
        else:
290 291
            stroke_color = user_color.get_stroke_color()
            fill_color = user_color.get_fill_color()
Gonzalo Odiard's avatar
Gonzalo Odiard committed
292

293 294
        self.bcard1.set_border(stroke_color, fill_color)
        self.bcard2.set_border(stroke_color, fill_color)
Gonzalo Odiard's avatar
Gonzalo Odiard committed
295

296 297 298
    def change_image_path(self, aimage_path, bimage_path):
        self.bcard1.set_image_path(aimage_path)
        self.bcard2.set_image_path(bimage_path)
Gonzalo Odiard's avatar
Gonzalo Odiard committed
299

300 301 302
    def change_text(self, text1, text2):
        self.bcard1.change_text(text1)
        self.bcard2.change_text(text2)
303

304 305 306
    def change_sound(self, asnd, bsnd):
        self.asnd = asnd
        self.bsnd = bsnd
307

308 309 310 311 312 313
    def change_font(self, card, font_name):
        if card == 1:
            self.bcard1.change_font(font_name)
        else:
            self.bcard2.change_font(font_name)

314 315 316 317
    def get_text(self, card):
        if card == 1:
            return self.bcard1.get_text()
        else:
318 319 320 321 322 323 324 325 326 327 328 329
            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()

330
    def get_image_path(self, card):
331
        if card == 1:
332
            return self.bcard1.get_image_path()
333
        else:
334
            return self.bcard2.get_image_path()
335

336 337 338 339
    def get_sound(self, card):
        if card == 1:
            return self.asnd
        else:
Simon Schampijer's avatar
Simon Schampijer committed
340
            return self.bsnd