2
# Copyright (C) 2006, 2007, 2008 One Laptop Per Child
3
# Copyright (C) 2009 Simon Schampijer
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.
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.
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.
21
from os import environ
22
from os.path import join, basename
27
from gettext import gettext as _
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
44
_logger = logging.getLogger('memorize-activity')
46
class CreateCardPanel(gtk.EventBox):
48
'add-pair': (SIGNAL_RUN_FIRST, None, 8 * [TYPE_PYOBJECT]),
49
'update-pair': (SIGNAL_RUN_FIRST, None, 8 * [TYPE_PYOBJECT]),
53
def make_label(icon_name, label):
54
label_box = gtk.HBox()
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)
66
gtk.EventBox.__init__(self)
68
self.equal_pairs = False
69
self._updatebutton_sensitive = False
70
self._card1_has_sound = False
71
self._card2_has_sound = False
75
buttons_bar = gtk.HBox()
76
buttons_bar.props.border_width = 10
78
self._addbutton = ToolButton(
79
tooltip=_('Add as new pair'),
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)
86
self._updatebutton = ToolButton(
87
tooltip=_('Update selected pair'),
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)
96
self.cardeditor1 = CardEditor()
97
self.cardeditor2 = CardEditor()
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)
108
self.card_box = gtk.HBox()
109
self.card_box.pack_start(self.cardeditor1)
110
self.card_box.pack_start(self.cardeditor2)
113
box.pack_start(self.card_box, False)
114
box.pack_start(buttons_bar, False)
119
def emit_add_pair(self, widget):
120
self._addbutton.set_sensitive(False)
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()
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()
139
def emit_update_pair(self, widget):
140
self._addbutton.set_sensitive(False)
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()
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()
159
def pair_selected(self, widget, selected, newtext1, newtext2, aimg, bimg,
160
asnd, bsnd, aspeak, bspeak):
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
174
def change_equal_pairs(self, widget, state):
175
self.equal_pairs = state
179
if self.cardeditor2.parent:
180
self.card_box.remove(self.cardeditor2)
182
if not self.cardeditor2.parent:
183
self.card_box.pack_start(self.cardeditor2)
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
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()
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()
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()
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)
224
self._addbutton.set_sensitive(False)
225
self._updatebutton.set_sensitive(False)
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)
232
self._addbutton.set_sensitive(False)
233
self._updatebutton.set_sensitive(False)
235
class CardEditor(gtk.EventBox):
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]),
244
gtk.EventBox.__init__(self)
248
tmp_root = join(environ['SUGAR_ACTIVITY_ROOT'], 'instance')
249
self.temp_folder = tempfile.mkdtemp(dir=tmp_root)
252
box.props.spacing = theme.PAD
253
box.props.border_width = theme.PAD
255
self.previewlabel = gtk.Label(_('Preview:'))
256
self.previewlabel.set_alignment(0, 1)
257
box.pack_start(self.previewlabel, False)
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',
265
None, theme.PAIR_SIZE, 1, '#c0c0c0')
267
card_align = gtk.Alignment(.5, .5, 0, 0)
268
card_align.add(self.card)
269
box.pack_start(card_align, False)
271
textlabel = gtk.Label(_('Text:'))
272
textlabel.set_alignment(0, 1)
273
box.pack_start(textlabel, False)
275
self.textentry = gtk.Entry()
276
self.textentry.connect('changed', self.update_text)
277
box.pack_start(self.textentry, False)
281
browsepicture = ToolButton(
282
icon_name='import_picture',
283
tooltip=_('Insert picture'))
284
toolbar.pack_start(browsepicture, False)
286
browsesound = ToolButton(
287
icon_name='import_sound',
288
tooltip=_('Insert sound'))
289
toolbar.pack_start(browsesound, False)
291
browsepicture.connect('clicked', self._load_image)
292
browsesound.connect('clicked', self._load_audio)
294
if speak.espeak.supported:
295
self.usespeak = ToggleToolButton(
297
palette=SpeakPalette(self))
298
toolbar.pack_start(self.usespeak, False)
299
self.usespeak.connect('toggled', self._usespeak_cb)
303
toolbar_box = CanvasRoundBox(
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),
310
toolbar_canvas = hippo.Canvas()
311
toolbar_canvas.set_root(toolbar_box)
312
box.pack_start(toolbar_canvas, False)
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)
321
self.emit('has-text', True)
324
return self.textentry.get_text()
326
def set_text(self, newtext):
329
self.textentry.set_text(newtext)
332
if self.usespeak is None:
334
if self.usespeak.props.active:
335
return self.usespeak.palette.face.status.voice.friendlyname
337
def set_speak(self, value):
338
if self.usespeak is None:
341
self.usespeak.props.active = False
344
self.usespeak.handler_block_by_func(self._usespeak_cb)
345
self.usespeak.props.active = True
347
self.usespeak.handler_unblock_by_func(self._usespeak_cb)
348
self.usespeak.palette.voices.resume(value)
350
def get_pixbuf(self):
351
return self.card.get_pixbuf()
353
def set_pixbuf(self, pixbuf):
354
self.card.set_pixbuf(pixbuf)
356
def _load_image(self, widget):
358
index = jobject.file_path
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)
377
chooser.pick(parent=self.get_toplevel(),
381
def _load_audio(self, widget):
383
index = jobject.file_path
387
dst = join(self.temp_folder, basename(index))
388
shutil.copy(index, 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)
397
chooser.pick(parent=self.get_toplevel(),
401
def _usespeak_cb(self, button):
402
self.card.change_speak(button.props.active)
404
if not button.props.active:
405
self.usespeak.palette.face.shut_up()
409
self.card.set_pixbuf(None)
410
self.emit('has-sound', False)
411
self.emit('has-picture', False)
413
button.palette.face.say(self.get_text())
415
def set_snd(self, snd):
422
self.textentry.set_text('')
423
self.card.set_pixbuf(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()
432
class SpeakPalette(Palette):
433
def __init__(self, editor):
434
Palette.__init__(self, _('Pronounce text during tile flip'))
436
self.face = speak.face.View()
439
toolbar.modify_bg(gtk.STATE_NORMAL, style.COLOR_BLACK.get_gdk_color())
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)
446
self.voices = speak.widgets.Voices(self.face)
447
toolbar.pack_start(ToolComboBox(self.voices))
450
self.set_content(toolbar)