~ubuntu-branches/ubuntu/maverick/sugar-memorize-activity/maverick

« back to all changes in this revision

Viewing changes to createcardpanel.py

  • Committer: Bazaar Package Importer
  • Author(s): Luke Faraone, Kandarp Kaushik, Luke Faraone
  • Date: 2010-07-27 09:47:05 UTC
  • mfrom: (0.1.2 sid) (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20100727094705-w79mzcs64wgn4grt
Tags: 34+git20091021.837e6c6b-1
[ Kandarp Kaushik ]
* Initial release. (Closes: #588674)

[ Luke Faraone ]
* Fix dependencies, clean up description.  

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
#    Copyright (C) 2006, 2007, 2008 One Laptop Per Child
 
3
#    Copyright (C) 2009 Simon Schampijer
 
4
#
 
5
#    This program is free software; you can redistribute it and/or modify
 
6
#    it under the terms of the GNU General Public License as published by
 
7
#    the Free Software Foundation; either version 2 of the License, or
 
8
#    (at your option) any later version.
 
9
#
 
10
#    This program is distributed in the hope that it will be useful,
 
11
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
#    GNU General Public License for more details.
 
14
#
 
15
#    You should have received a copy of the GNU General Public License
 
16
#    along with this program; if not, write to the Free Software
 
17
#    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
18
#
 
19
 
 
20
import gtk
 
21
from os import environ
 
22
from os.path import join, basename
 
23
import hippo
 
24
 
 
25
import shutil
 
26
import tempfile
 
27
from gettext import gettext as _
 
28
import svgcard
 
29
import logging
 
30
from gobject import SIGNAL_RUN_FIRST, TYPE_PYOBJECT
 
31
from sugar.graphics import style
 
32
from sugar.graphics.toolbutton import ToolButton
 
33
from sugar.graphics.icon import Icon
 
34
from sugar.graphics.palette import Palette
 
35
from port.widgets import ToggleToolButton
 
36
from port.widgets import CanvasRoundBox, ToolComboBox
 
37
from port import chooser
 
38
 
 
39
import theme
 
40
import speak.espeak
 
41
import speak.widgets
 
42
import speak.face
 
43
 
 
44
_logger = logging.getLogger('memorize-activity')
 
45
 
 
46
class CreateCardPanel(gtk.EventBox):
 
47
    __gsignals__ = {
 
48
        'add-pair': (SIGNAL_RUN_FIRST, None, 8 * [TYPE_PYOBJECT]),
 
49
        'update-pair': (SIGNAL_RUN_FIRST, None, 8 * [TYPE_PYOBJECT]),
 
50
    }
 
51
 
 
52
    def __init__(self):
 
53
        def make_label(icon_name, label):
 
54
            label_box = gtk.HBox()
 
55
            icon = Icon(
 
56
                    icon_name=icon_name,
 
57
                    icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
 
58
            label_box.pack_start(icon, False)
 
59
            label = gtk.Label(label)
 
60
            label.modify_fg(gtk.STATE_NORMAL,
 
61
                    style.COLOR_TOOLBAR_GREY.get_gdk_color())
 
62
            label_box.pack_start(label)
 
63
            label_box.show_all()
 
64
            return label_box
 
65
 
 
66
        gtk.EventBox.__init__(self)
 
67
 
 
68
        self.equal_pairs = False
 
69
        self._updatebutton_sensitive = False
 
70
        self._card1_has_sound = False
 
71
        self._card2_has_sound = False
 
72
 
 
73
        # save buttons
 
74
 
 
75
        buttons_bar = gtk.HBox()
 
76
        buttons_bar.props.border_width = 10
 
77
 
 
78
        self._addbutton = ToolButton(
 
79
                tooltip=_('Add as new pair'),
 
80
                sensitive=False)
 
81
        self._addbutton.set_icon_widget(
 
82
                make_label('pair-add', ' ' + _('Add')))
 
83
        self._addbutton.connect('clicked', self.emit_add_pair)
 
84
        buttons_bar.pack_start(self._addbutton, False)
 
85
 
 
86
        self._updatebutton = ToolButton(
 
87
                tooltip=_('Update selected pair'),
 
88
                sensitive=False)
 
89
        self._updatebutton.set_icon_widget(
 
90
                make_label('pair-update', ' ' + _('Update')))
 
91
        self._updatebutton.connect('clicked', self.emit_update_pair)
 
92
        buttons_bar.pack_start(self._updatebutton, False)
 
93
 
 
94
        # Set card editors
 
95
 
 
96
        self.cardeditor1 = CardEditor()
 
97
        self.cardeditor2 = CardEditor()
 
98
        self.clean(None)
 
99
        self.cardeditor1.connect('has-text', self.receive_text_signals)
 
100
        self.cardeditor2.connect('has-text', self.receive_text_signals)
 
101
        self.cardeditor1.connect('has-picture', self.receive_picture_signals)
 
102
        self.cardeditor2.connect('has-picture', self.receive_picture_signals)
 
103
        self.cardeditor1.connect('has-sound', self.receive_sound_signals)
 
104
        self.cardeditor2.connect('has-sound', self.receive_sound_signals)
 
105
 
 
106
        # edit panel
 
107
 
 
108
        self.card_box = gtk.HBox()
 
109
        self.card_box.pack_start(self.cardeditor1)
 
110
        self.card_box.pack_start(self.cardeditor2)
 
111
 
 
112
        box = gtk.VBox()
 
113
        box.pack_start(self.card_box, False)
 
114
        box.pack_start(buttons_bar, False)
 
115
        self.add(box)
 
116
 
 
117
        self.show_all()
 
118
 
 
119
    def emit_add_pair(self, widget):
 
120
        self._addbutton.set_sensitive(False)
 
121
        if self.equal_pairs:
 
122
            self.emit('add-pair', self.cardeditor1.get_text(), 
 
123
                      self.cardeditor1.get_text(), 
 
124
                      self.cardeditor1.get_pixbuf(), 
 
125
                      self.cardeditor1.get_pixbuf(), 
 
126
                      self.cardeditor1.get_snd(), self.cardeditor1.get_snd(),
 
127
                      self.cardeditor1.get_speak(), self.cardeditor1.get_speak()
 
128
                      )
 
129
        else:
 
130
            self.emit('add-pair', self.cardeditor1.get_text(), 
 
131
                      self.cardeditor2.get_text(), 
 
132
                      self.cardeditor1.get_pixbuf(), 
 
133
                      self.cardeditor2.get_pixbuf(), 
 
134
                      self.cardeditor1.get_snd(), self.cardeditor2.get_snd(),
 
135
                      self.cardeditor1.get_speak(), self.cardeditor2.get_speak()
 
136
                      )
 
137
        self.clean(None)
 
138
 
 
139
    def emit_update_pair(self, widget):
 
140
        self._addbutton.set_sensitive(False)
 
141
        if self.equal_pairs:
 
142
            self.emit('update-pair', self.cardeditor1.get_text(), 
 
143
                      self.cardeditor1.get_text(), 
 
144
                      self.cardeditor1.get_pixbuf(), 
 
145
                      self.cardeditor1.get_pixbuf(), 
 
146
                      self.cardeditor1.get_snd(), self.cardeditor1.get_snd(),
 
147
                      self.cardeditor1.get_speak(), self.cardeditor1.get_speak()
 
148
                      )
 
149
        else:
 
150
            self.emit('update-pair', self.cardeditor1.get_text(), 
 
151
                      self.cardeditor2.get_text(), 
 
152
                      self.cardeditor1.get_pixbuf(), 
 
153
                      self.cardeditor2.get_pixbuf(), 
 
154
                      self.cardeditor1.get_snd(), self.cardeditor2.get_snd(),
 
155
                      self.cardeditor1.get_speak(), self.cardeditor2.get_speak()
 
156
                      )
 
157
        self.clean(None)
 
158
 
 
159
    def pair_selected(self, widget, selected, newtext1, newtext2, aimg, bimg,
 
160
            asnd, bsnd, aspeak, bspeak):
 
161
        if selected:
 
162
            self.cardeditor1.set_text(newtext1)
 
163
            self.cardeditor2.set_text(newtext2)
 
164
            self.cardeditor1.set_pixbuf(aimg)
 
165
            self.cardeditor2.set_pixbuf(bimg)
 
166
            self.cardeditor1.set_snd(asnd)
 
167
            self.cardeditor2.set_snd(bsnd)
 
168
            self.cardeditor1.set_speak(aspeak)
 
169
            self.cardeditor2.set_speak(bspeak)
 
170
            self._addbutton.set_sensitive(True)
 
171
        self._updatebutton.set_sensitive(selected)
 
172
        self._updatebutton_sensitive = selected
 
173
 
 
174
    def change_equal_pairs(self, widget, state):
 
175
        self.equal_pairs = state
 
176
        self.clean(None)
 
177
 
 
178
        if self.equal_pairs:
 
179
            if self.cardeditor2.parent:
 
180
                self.card_box.remove(self.cardeditor2)
 
181
        else:
 
182
            if not self.cardeditor2.parent:
 
183
                self.card_box.pack_start(self.cardeditor2)
 
184
 
 
185
    def clean(self, widget):
 
186
        self.cardeditor1.clean()
 
187
        self.cardeditor2.clean()
 
188
        self._addbutton.set_sensitive(False)
 
189
        self._card1_has_text = False
 
190
        self._card2_has_text = False
 
191
        self._card1_has_picture = False
 
192
        self._card2_has_picture = False
 
193
 
 
194
    def receive_text_signals(self, widget, has_text):
 
195
        if widget == self.cardeditor1:
 
196
            self._card1_has_text = has_text
 
197
        if widget == self.cardeditor2:
 
198
            self._card2_has_text = has_text
 
199
        self._update_buttom_status()
 
200
 
 
201
    def receive_picture_signals(self, widget, has_picture):
 
202
        if widget == self.cardeditor1:
 
203
            self._card1_has_picture = has_picture
 
204
        if widget == self.cardeditor2:
 
205
            self._card2_has_picture = has_picture
 
206
        self._update_buttom_status()
 
207
 
 
208
    def receive_sound_signals(self, widget, has_sound):
 
209
        if widget == self.cardeditor1:
 
210
            self._card1_has_sound = has_sound
 
211
        if widget == self.cardeditor2:
 
212
            self._card2_has_sound = has_sound
 
213
        self._update_buttom_status()
 
214
 
 
215
    def _update_buttom_status(self):
 
216
        if not self.equal_pairs:
 
217
            if (self._card1_has_text or self._card1_has_picture \
 
218
                    or self._card1_has_sound) and (self._card2_has_text 
 
219
                                                   or self._card2_has_picture 
 
220
                                                   or self._card2_has_sound):
 
221
                self._addbutton.set_sensitive(True)
 
222
                self._updatebutton.set_sensitive(self._updatebutton_sensitive)
 
223
            else:
 
224
                self._addbutton.set_sensitive(False)
 
225
                self._updatebutton.set_sensitive(False)
 
226
        else:
 
227
            if (self._card1_has_text or self._card1_has_picture \
 
228
                    or self._card1_has_sound):
 
229
                self._addbutton.set_sensitive(True)
 
230
                self._updatebutton.set_sensitive(self._updatebutton_sensitive)
 
231
            else:
 
232
                self._addbutton.set_sensitive(False)
 
233
                self._updatebutton.set_sensitive(False)
 
234
 
 
235
class CardEditor(gtk.EventBox):
 
236
 
 
237
    __gsignals__ = {
 
238
        'has-text': (SIGNAL_RUN_FIRST, None, [TYPE_PYOBJECT]),
 
239
        'has-picture': (SIGNAL_RUN_FIRST, None, [TYPE_PYOBJECT]),
 
240
        'has-sound': (SIGNAL_RUN_FIRST, None, [TYPE_PYOBJECT]),
 
241
    }
 
242
 
 
243
    def __init__(self):
 
244
        gtk.EventBox.__init__(self)
 
245
 
 
246
        self.snd = None
 
247
 
 
248
        tmp_root = join(environ['SUGAR_ACTIVITY_ROOT'], 'instance')
 
249
        self.temp_folder = tempfile.mkdtemp(dir=tmp_root)
 
250
 
 
251
        box = gtk.VBox()
 
252
        box.props.spacing = theme.PAD
 
253
        box.props.border_width = theme.PAD
 
254
 
 
255
        self.previewlabel = gtk.Label(_('Preview:'))
 
256
        self.previewlabel.set_alignment(0, 1)
 
257
        box.pack_start(self.previewlabel, False)
 
258
 
 
259
        self.card = svgcard.SvgCard(-1,
 
260
                { 'front_text'  : { 'card_text'     : '',
 
261
                                    'text_color'    : '#ffffff' },
 
262
                  'front_border': { 'fill_color'    : '#4c4d4f',
 
263
                                    'stroke_color'  : '#ffffff',
 
264
                                    'opacity'       : '1' } },
 
265
                None, theme.PAIR_SIZE, 1, '#c0c0c0')
 
266
        self.card.flip()
 
267
        card_align = gtk.Alignment(.5, .5, 0, 0)
 
268
        card_align.add(self.card)
 
269
        box.pack_start(card_align, False)
 
270
 
 
271
        textlabel = gtk.Label(_('Text:'))
 
272
        textlabel.set_alignment(0, 1)
 
273
        box.pack_start(textlabel, False)
 
274
 
 
275
        self.textentry = gtk.Entry()
 
276
        self.textentry.connect('changed', self.update_text)
 
277
        box.pack_start(self.textentry, False)
 
278
 
 
279
        toolbar = gtk.HBox()
 
280
 
 
281
        browsepicture = ToolButton(
 
282
                icon_name='import_picture',
 
283
                tooltip=_('Insert picture'))
 
284
        toolbar.pack_start(browsepicture, False)
 
285
 
 
286
        browsesound = ToolButton(
 
287
                icon_name='import_sound',
 
288
                tooltip=_('Insert sound'))
 
289
        toolbar.pack_start(browsesound, False)
 
290
 
 
291
        browsepicture.connect('clicked', self._load_image)
 
292
        browsesound.connect('clicked', self._load_audio)
 
293
 
 
294
        if speak.espeak.supported:
 
295
            self.usespeak = ToggleToolButton(
 
296
                    named_icon='speak',
 
297
                    palette=SpeakPalette(self))
 
298
            toolbar.pack_start(self.usespeak, False)
 
299
            self.usespeak.connect('toggled', self._usespeak_cb)
 
300
        else:
 
301
            self.usespeak = None
 
302
 
 
303
        toolbar_box = CanvasRoundBox(
 
304
                radius=8,
 
305
                border=2,
 
306
                border_color=style.COLOR_BUTTON_GREY.get_int(),
 
307
                background_color=style.COLOR_PANEL_GREY.get_int())
 
308
        toolbar_box.append(hippo.CanvasWidget(widget=toolbar),
 
309
                hippo.PACK_EXPAND)
 
310
        toolbar_canvas = hippo.Canvas()
 
311
        toolbar_canvas.set_root(toolbar_box)
 
312
        box.pack_start(toolbar_canvas, False)
 
313
 
 
314
        self.add(box)
 
315
 
 
316
    def update_text(self, entry):
 
317
        self.card.change_text(entry.get_text())
 
318
        if len(entry.get_text()) == 0:
 
319
            self.emit('has-text', False)
 
320
        else:
 
321
            self.emit('has-text', True)
 
322
 
 
323
    def get_text(self):
 
324
        return self.textentry.get_text()
 
325
 
 
326
    def set_text(self, newtext):
 
327
        if newtext == None:
 
328
            newtext = ''
 
329
        self.textentry.set_text(newtext)
 
330
 
 
331
    def get_speak(self):
 
332
        if self.usespeak is None:
 
333
            return None
 
334
        if self.usespeak.props.active:
 
335
            return self.usespeak.palette.face.status.voice.friendlyname
 
336
 
 
337
    def set_speak(self, value):
 
338
        if self.usespeak is None:
 
339
            return
 
340
        if value is None:
 
341
            self.usespeak.props.active = False
 
342
        else:
 
343
            try:
 
344
                self.usespeak.handler_block_by_func(self._usespeak_cb)
 
345
                self.usespeak.props.active = True
 
346
            finally:
 
347
                self.usespeak.handler_unblock_by_func(self._usespeak_cb)
 
348
            self.usespeak.palette.voices.resume(value)
 
349
 
 
350
    def get_pixbuf(self):
 
351
        return self.card.get_pixbuf()
 
352
 
 
353
    def set_pixbuf(self, pixbuf):
 
354
        self.card.set_pixbuf(pixbuf)
 
355
 
 
356
    def _load_image(self, widget):
 
357
        def load(jobject):
 
358
            index = jobject.file_path
 
359
 
 
360
            self.set_speak(None)
 
361
 
 
362
            pixbuf_t = gtk.gdk.pixbuf_new_from_file_at_size(
 
363
                    index, theme.PAIR_SIZE - theme.PAD*2,
 
364
                    theme.PAIR_SIZE - theme.PAD*2)
 
365
            size = max(pixbuf_t.get_width(), pixbuf_t.get_height())
 
366
            pixbuf_z = gtk.gdk.pixbuf_new_from_file_at_size(
 
367
                'images/white.png', size, size)
 
368
            pixbuf_t.composite(pixbuf_z, 0, 0, pixbuf_t.get_width(),
 
369
                               pixbuf_t.get_height(), 0, 0, 1, 1,
 
370
                               gtk.gdk.INTERP_BILINEAR, 255)
 
371
            self.card.set_pixbuf(pixbuf_z)
 
372
            _logger.debug('Picture Loaded: '+index)
 
373
            self.emit('has-picture', True)
 
374
            del pixbuf_t
 
375
            del pixbuf_z
 
376
 
 
377
        chooser.pick(parent=self.get_toplevel(),
 
378
                     what=chooser.IMAGE,
 
379
                     cb=load)
 
380
 
 
381
    def _load_audio(self, widget):
 
382
        def load(jobject):
 
383
            index = jobject.file_path
 
384
 
 
385
            self.set_speak(None)
 
386
 
 
387
            dst = join(self.temp_folder, basename(index))
 
388
            shutil.copy(index, dst)
 
389
            self.set_snd(dst)
 
390
            icon_theme = gtk.icon_theme_get_default()
 
391
            pixbuf_t = icon_theme.load_icon("audio-x-generic",
 
392
                                            style.XLARGE_ICON_SIZE, 0)
 
393
            self.card.set_pixbuf(pixbuf_t)
 
394
            self.emit('has-sound', True)
 
395
            _logger.debug('Audio Loaded: '+dst)
 
396
 
 
397
        chooser.pick(parent=self.get_toplevel(),
 
398
                     what=chooser.AUDIO,
 
399
                     cb=load)
 
400
 
 
401
    def _usespeak_cb(self, button):
 
402
        self.card.change_speak(button.props.active)
 
403
 
 
404
        if not button.props.active:
 
405
            self.usespeak.palette.face.shut_up()
 
406
            return
 
407
 
 
408
        self.snd = None
 
409
        self.card.set_pixbuf(None)
 
410
        self.emit('has-sound', False)
 
411
        self.emit('has-picture', False)
 
412
 
 
413
        button.palette.face.say(self.get_text())
 
414
 
 
415
    def set_snd(self, snd):
 
416
        self.snd = snd
 
417
 
 
418
    def get_snd(self):
 
419
        return self.snd
 
420
 
 
421
    def clean(self):
 
422
        self.textentry.set_text('')
 
423
        self.card.set_pixbuf(None)
 
424
        self.snd = None
 
425
        self.emit('has-text', False)
 
426
        self.emit('has-picture', False)
 
427
        self.emit('has-sound', False)
 
428
        if self.usespeak is not None:
 
429
            self.usespeak.props.active = False
 
430
            self.usespeak.palette.face.shut_up()
 
431
 
 
432
class SpeakPalette(Palette):
 
433
    def __init__(self, editor):
 
434
        Palette.__init__(self, _('Pronounce text during tile flip'))
 
435
 
 
436
        self.face = speak.face.View()
 
437
 
 
438
        toolbar = gtk.HBox()
 
439
        toolbar.modify_bg(gtk.STATE_NORMAL, style.COLOR_BLACK.get_gdk_color())
 
440
 
 
441
        usespeak_play = ToolButton(icon_name='media-playback-start')
 
442
        usespeak_play.connect('clicked', lambda button:
 
443
                self.face.say(editor.get_text()))
 
444
        toolbar.pack_start(usespeak_play, False)
 
445
 
 
446
        self.voices = speak.widgets.Voices(self.face)
 
447
        toolbar.pack_start(ToolComboBox(self.voices))
 
448
 
 
449
        toolbar.show_all()
 
450
        self.set_content(toolbar)