# # Copyright (C) 2006, 2007, 2008 One Laptop Per Child # Copyright (C) 2009 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 gi.repository import Gtk from gi.repository import Gdk from gi.repository import GObject from os.path import join, basename import shutil from gettext import gettext as _ import svgcard import logging from sugar3.activity import activity from sugar3.graphics import style from sugar3.graphics.toolbutton import ToolButton from sugar3.graphics.icon import Icon from sugar3.graphics.palette import Palette from sugar3.graphics.toggletoolbutton import ToggleToolButton from sugar3.graphics.toolcombobox import ToolComboBox from fontcombobox import FontButton from port import chooser import speak.espeak import speak.widgets import speak.face PAIR_SIZE = min(Gdk.Screen.width() / 4, Gdk.Screen.height() / 3) class CreateCardPanel(Gtk.EventBox): __gsignals__ = { 'add-pair': (GObject.SignalFlags.RUN_FIRST, None, 10 * [GObject.TYPE_PYOBJECT]), 'update-pair': (GObject.SignalFlags.RUN_FIRST, None, 8 * [GObject.TYPE_PYOBJECT]), 'change-font': (GObject.SignalFlags.RUN_FIRST, None, 2 * [GObject.TYPE_PYOBJECT]), 'pair-closed': (GObject.SignalFlags.RUN_FIRST, None, []), } def __init__(self, game): def make_label(icon_name, label): label_box = Gtk.VBox() icon = Icon(icon_name=icon_name, icon_size=Gtk.IconSize.LARGE_TOOLBAR) label_box.pack_start(icon, False, False, 0) label = Gtk.Label(label=label) label.modify_fg(Gtk.StateType.NORMAL, style.COLOR_TOOLBAR_GREY.get_gdk_color()) label_box.pack_start(label, True, True, 0) label_box.show_all() return label_box Gtk.EventBox.__init__(self) self._game = game self.equal_pairs = False self._updatebutton_sensitive = False self._card1_has_sound = False self._card2_has_sound = False # save buttons self._portrait = Gdk.Screen.width() < Gdk.Screen.height() if self._portrait: buttons_bar_orientation = Gtk.Orientation.HORIZONTAL else: buttons_bar_orientation = Gtk.Orientation.VERTICAL self._buttons_bar = Gtk.Box(orientation=buttons_bar_orientation) self._buttons_bar.props.border_width = 10 self._buttons_bar.set_valign(Gtk.Align.CENTER) self._buttons_bar.set_halign(Gtk.Align.CENTER) self._addbutton = ToolButton(tooltip=_('Add as new pair'), sensitive=False) self._addbutton.set_icon_widget( make_label('pair-add', ' ' + _('Add'))) self._addbutton.connect('clicked', self.emit_add_pair) self._buttons_bar.pack_start(self._addbutton, False, False, 0) self._updatebutton = ToolButton(tooltip=_('Update seleted pair'), sensitive=False) self._updatebutton.set_icon_widget( make_label('pair-update', ' ' + _('Update'))) self._updatebutton.connect('clicked', self.emit_update_pair) self._buttons_bar.pack_start(self._updatebutton, False, False, 0) self._removebutton = ToolButton(tooltip=_('Remove seleted pair'), sensitive=False) self._removebutton.set_icon_widget( make_label('remove', ' ' + _('Remove'))) self._removebutton.connect('clicked', self.emit_close) self._buttons_bar.pack_start(self._removebutton, False, False, 0) # Set card editors self.cardeditor1 = CardEditor(self._game, 1) self.cardeditor2 = CardEditor(self._game, 2) self.clean(None) self.cardeditor1.connect('has-text', self.receive_text_signals) self.cardeditor2.connect('has-text', self.receive_text_signals) self.cardeditor1.connect('has-picture', self.receive_picture_signals) self.cardeditor2.connect('has-picture', self.receive_picture_signals) self.cardeditor1.connect('has-sound', self.receive_sound_signals) self.cardeditor2.connect('has-sound', self.receive_sound_signals) self.cardeditor1.connect('change-font', self.receive_font_signals) self.cardeditor2.connect('change-font', self.receive_font_signals) # edit panel self.card_box = Gtk.HBox() self.card_box.set_homogeneous(True) self.cardeditor1.set_halign(Gtk.Align.CENTER) self.cardeditor1.set_valign(Gtk.Align.CENTER) self.cardeditor2.set_halign(Gtk.Align.CENTER) self.cardeditor2.set_valign(Gtk.Align.CENTER) self.card_box.pack_start(self.cardeditor1, True, True, 0) self.card_box.pack_start(self.cardeditor2, True, True, 0) if self._portrait: main_box_orientation = Gtk.Orientation.VERTICAL else: main_box_orientation = Gtk.Orientation.HORIZONTAL self._main_box = Gtk.Box(orientation=main_box_orientation) self._main_box.pack_start(self.card_box, True, True, 0) self._main_box.pack_start(self._buttons_bar, True, True, 0) self.add(self._main_box) self.connect('size-allocate', self._allocate_cb) self.show_all() def _allocate_cb(self, widget, allocation): GObject.idle_add(self.update_orientation) def update_orientation(self): logging.error('width %s height %s', Gdk.Screen.width(), Gdk.Screen.height()) self._portrait = Gdk.Screen.width() < Gdk.Screen.height() logging.error('cretecardpanel allocate portrait %s', self._portrait) if self._portrait: self._buttons_bar.props.orientation = Gtk.Orientation.HORIZONTAL self._main_box.props.orientation = Gtk.Orientation.VERTICAL else: self._buttons_bar.props.orientation = Gtk.Orientation.VERTICAL self._main_box.props.orientation = Gtk.Orientation.HORIZONTAL def update_font_combos(self, widget, data, grid): logging.error('update font %s', data) if 'font_name1' in data: self.cardeditor1.set_font_name(data['font_name1']) self.cardeditor1.card.change_font(data['font_name1']) if 'font_name2' in data: self.cardeditor2.set_font_name(data['font_name2']) self.cardeditor2.card.change_font(data['font_name2']) def emit_close(self, widget): self.emit('pair-closed') def emit_add_pair(self, widget): self._addbutton.set_sensitive(False) if self.equal_pairs: self.emit('add-pair', self.cardeditor1.get_text(), self.cardeditor1.get_text(), self.cardeditor1.get_image_path(), self.cardeditor1.get_image_path(), self.cardeditor1.get_snd(), self.cardeditor1.get_snd(), self.cardeditor1.get_speak(), self.cardeditor1.get_speak(), self.cardeditor1.get_font_name(), self.cardeditor1.get_font_name()) else: self.emit('add-pair', self.cardeditor1.get_text(), self.cardeditor2.get_text(), self.cardeditor1.get_image_path(), self.cardeditor2.get_image_path(), self.cardeditor1.get_snd(), self.cardeditor2.get_snd(), self.cardeditor1.get_speak(), self.cardeditor2.get_speak(), self.cardeditor1.get_font_name(), self.cardeditor2.get_font_name()) self.clean(None) def emit_update_pair(self, widget): self._addbutton.set_sensitive(False) if self.equal_pairs: self.emit('update-pair', self.cardeditor1.get_text(), self.cardeditor1.get_text(), self.cardeditor1.get_image_path(), self.cardeditor1.get_image_path(), self.cardeditor1.get_snd(), self.cardeditor1.get_snd(), self.cardeditor1.get_speak(), self.cardeditor1.get_speak()) else: self.emit('update-pair', self.cardeditor1.get_text(), self.cardeditor2.get_text(), self.cardeditor1.get_image_path(), self.cardeditor2.get_image_path(), self.cardeditor1.get_snd(), self.cardeditor2.get_snd(), self.cardeditor1.get_speak(), self.cardeditor2.get_speak()) self.clean(None) def pair_selected(self, widget, selected, newtext1, newtext2, aimage_path, bimage_path, asnd, bsnd, aspeak, bspeak): if selected: self.cardeditor1.set_text(newtext1) self.cardeditor2.set_text(newtext2) self.cardeditor1.set_image_path(aimage_path) self.cardeditor2.set_image_path(bimage_path) self.cardeditor1.set_snd(asnd) self.cardeditor2.set_snd(bsnd) self.cardeditor1.set_speak(aspeak) self.cardeditor2.set_speak(bspeak) self._addbutton.set_sensitive(True) self._updatebutton.set_sensitive(selected) self._updatebutton_sensitive = selected self._removebutton.set_sensitive(selected) def change_equal_pairs(self, widget, state): self.equal_pairs = state self.clean(None) if self.equal_pairs: if self.cardeditor2.get_parent(): self.card_box.remove(self.cardeditor2) else: if not self.cardeditor2.get_parent(): self.card_box.pack_start(self.cardeditor2, True, True, 0) def clean(self, widget): self.cardeditor1.clean() self.cardeditor2.clean() self._addbutton.set_sensitive(False) self._card1_has_text = False self._card2_has_text = False self._card1_has_picture = False self._card2_has_picture = False def receive_text_signals(self, widget, has_text): if widget == self.cardeditor1: self._card1_has_text = has_text if widget == self.cardeditor2: self._card2_has_text = has_text self._update_buttom_status() def receive_picture_signals(self, widget, has_picture): if widget == self.cardeditor1: self._card1_has_picture = has_picture if widget == self.cardeditor2: self._card2_has_picture = has_picture self._update_buttom_status() def receive_sound_signals(self, widget, has_sound): if widget == self.cardeditor1: self._card1_has_sound = has_sound if widget == self.cardeditor2: self._card2_has_sound = has_sound self._update_buttom_status() def receive_font_signals(self, widget, font_name): if self.equal_pairs: self.emit('change-font', 1, font_name) self.emit('change-font', 2, font_name) else: if widget == self.cardeditor1: self.emit('change-font', 1, font_name) if widget == self.cardeditor2: self.emit('change-font', 2, font_name) def _update_buttom_status(self): if not self.equal_pairs: if (self._card1_has_text or self._card1_has_picture or self._card1_has_sound) and (self._card2_has_text or self._card2_has_picture or self._card2_has_sound): self._addbutton.set_sensitive(True) self._updatebutton.set_sensitive(self._updatebutton_sensitive) else: self._addbutton.set_sensitive(False) self._updatebutton.set_sensitive(False) else: if (self._card1_has_text or self._card1_has_picture or self._card1_has_sound): self._addbutton.set_sensitive(True) self._updatebutton.set_sensitive(self._updatebutton_sensitive) else: self._addbutton.set_sensitive(False) self._updatebutton.set_sensitive(False) def set_temp_folder(self, temp_folder): self.cardeditor1.temp_folder = temp_folder self.cardeditor2.temp_folder = temp_folder class CardEditor(Gtk.EventBox): __gsignals__ = { 'has-text': (GObject.SignalFlags.RUN_FIRST, None, [GObject.TYPE_PYOBJECT]), 'has-picture': (GObject.SignalFlags.RUN_FIRST, None, [GObject.TYPE_PYOBJECT]), 'has-sound': (GObject.SignalFlags.RUN_FIRST, None, [GObject.TYPE_PYOBJECT]), 'change-font': (GObject.SignalFlags.RUN_FIRST, None, [GObject.TYPE_PYOBJECT]), } def __init__(self, game, editor_index): Gtk.EventBox.__init__(self) self._game = game self.snd = None self.editor_index = editor_index self.temp_folder = None box = Gtk.Grid() box.set_column_spacing(style.DEFAULT_SPACING) box.set_row_spacing(style.DEFAULT_SPACING) box.props.margin = style.DEFAULT_SPACING self.card = svgcard.SvgCard( -1, {'front_text': {'card_text': '', 'text_color': style.Color('#ffffff')}}, None, PAIR_SIZE, '#c0c0c0') self.card.flip() card_align = Gtk.Alignment.new(.5, .5, 0, 0) card_align.add(self.card) box.attach(card_align, 0, 0, 1, 1) self.textentry = Gtk.Entry() self.textentry.connect('changed', self.update_text) self.textentry.set_valign(Gtk.Align.START) box.attach(self.textentry, 0, 1, 1, 1) toolbar = Gtk.VBox() toolbar.set_valign(Gtk.Align.CENTER) browsepicture = ToolButton(icon_name='import_picture', tooltip=_('Insert picture')) toolbar.add(browsepicture) browsesound = ToolButton(icon_name='import_sound', tooltip=_('Insert sound')) toolbar.add(browsesound) browsepicture.connect('clicked', self._load_image) browsesound.connect('clicked', self._load_audio) if speak.espeak.supported: self.usespeak = ToggleToolButton(icon_name='speak') self.usespeak.set_palette(SpeakPalette(self)) toolbar.add(self.usespeak) self.usespeak.connect('toggled', self._usespeak_cb) else: self.usespeak = None self.fontbutton = FontButton() toolbar.add(self.fontbutton) self.id_font_changed = self.fontbutton.connect( 'changed', self.__font_changed_cb) box.attach(toolbar, 1, 0, 1, 2) self.add(box) def __font_changed_cb(self, widget): font = widget.get_font_name() logging.error('Selected font %s', font) if font: self.card.change_font(font) self.emit('change-font', font) def set_font_name(self, font_name): self.fontbutton.handler_block(self.id_font_changed) self.fontbutton.set_font_name(font_name) self.fontbutton.handler_unblock(self.id_font_changed) def update_text(self, entry): self.card.change_text(entry.get_text()) if len(entry.get_text()) == 0: self.emit('has-text', False) else: self.emit('has-text', True) def get_text(self): return self.textentry.get_text() def set_text(self, newtext): if newtext is None: newtext = '' self.textentry.set_text(newtext) def get_speak(self): if self.usespeak is None: return None if self.usespeak.props.active: return self.usespeak.palette.face.status.voice.language def set_speak(self, value): if self.usespeak is None: return if value is None: self.usespeak.props.active = False else: try: self.usespeak.handler_block_by_func(self._usespeak_cb) self.usespeak.props.active = True finally: self.usespeak.handler_unblock_by_func(self._usespeak_cb) self.usespeak.palette.voices.resume(value) def get_image_path(self): return self.card.get_image_path() def set_image_path(self, image_path): self.card.set_image_path(image_path) self.emit('has-picture', True) def _load_image(self, widget): def load(jobject): origin_path = jobject.file_path self._game.model.mark_modified() self._game.model.create_temp_directories() destination_path = join(self._game.model.data['pathimg'], basename(origin_path)) shutil.copy(origin_path, destination_path) self.set_speak(None) self.card.set_image_path(destination_path) logging.debug('Picture Loaded: %s', destination_path) self.emit('has-picture', True) chooser.pick(parent=self.get_toplevel(), what=chooser.IMAGE, cb=load) def _load_audio(self, widget): def load(jobject): origin_path = jobject.file_path self.set_speak(None) self._game.model.mark_modified() self._game.model.create_temp_directories() destination_path = join(self._game.model.data['pathsnd'], basename(origin_path)) shutil.copy(origin_path, destination_path) self.set_snd(destination_path) logging.debug('Audio Loaded: %s', destination_path) # add a icon too sound_icon_path = join(activity.get_bundle_path(), 'icons/sound.svg') destination_path = join(self._game.model.data['pathimg'], 'sound.svg') shutil.copy(sound_icon_path, destination_path) self.card.set_image_path(destination_path) self.emit('has-sound', True) chooser.pick(parent=self.get_toplevel(), what=chooser.AUDIO, cb=load) def _usespeak_cb(self, button): self.card.change_speak(button.props.active) if not button.props.active: self.usespeak.palette.face.shut_up() return self.snd = None self.emit('has-sound', False) self.emit('has-picture', False) button.palette.face.say(self.get_text()) def set_snd(self, snd): self.snd = snd def get_snd(self): return self.snd def get_font_name(self): return self.fontbutton.get_font_name() def clean(self): self.textentry.set_text('') self.card.set_image_path(None) self.snd = None self.emit('has-text', False) self.emit('has-picture', False) self.emit('has-sound', False) if self.usespeak is not None and self.usespeak.palette is not None: self.usespeak.props.active = False self.usespeak.palette.face.shut_up() class SpeakPalette(Palette): def __init__(self, editor): Palette.__init__(self, _('Pronounce text during tile flip')) self.face = speak.face.View() toolbar = Gtk.HBox() toolbar.modify_bg(Gtk.StateType.NORMAL, style.COLOR_BLACK.get_gdk_color()) usespeak_play = ToolButton(icon_name='media-playback-start') usespeak_play.connect('clicked', lambda button: self.face.say(editor.get_text())) toolbar.pack_start(usespeak_play, False, False, 0) self.voices = speak.widgets.Voices(self.face) toolbar.pack_start(ToolComboBox(self.voices), True, True, 0) toolbar.show_all() self.set_content(toolbar)