#!/usr/bin/env python # vi: sw=4 et # Copyright 2008 by Kate Scheppke and Wade Brainerd. # This file is part of Typing Turtle. # # Typing Turtle 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 3 of the License, or # (at your option) any later version. # # Typing Turtle 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 Typing Turtle. If not, see . # Import standard Python modules. import logging, os, math, time, copy, locale, datetime, random, re from gettext import gettext as _ # Import PyGTK. import gobject, pygtk, gtk, pango # Import Sugar UI modules. import sugar.activity.activity import sugar.graphics.style import sugar.graphics.icon # Import lessonbuilder functions. import lessonbuilder class EditLessonScreen(gtk.VBox): def __init__(self, activity, lesson): gtk.VBox.__init__(self) self.set_border_width(10) self.activity = activity self.lesson = lesson self.in_build = False # Add the header. title = gtk.Label() title.set_markup("" + _("Edit a Lesson") + "") title.set_alignment(1.0, 0.0) stoplabel = gtk.Label(_('Go Back')) stopbtn = gtk.Button() stopbtn.add(stoplabel) stopbtn.connect('clicked', self.stop_clicked_cb) titlebox = gtk.HBox() titlebox.pack_start(stopbtn, False, False, 10) titlebox.pack_end(title, False, False, 10) self.vp = gtk.Viewport() self.scroll = gtk.ScrolledWindow() self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.scroll.add(self.vp) self.pack_start(titlebox, False, False, 10) self.pack_start(gtk.HSeparator(), False, False, 0) self.pack_start(self.scroll, True, True, 0) self.build() self.show_all() def build_generate(self): generatebox = gtk.VBox() generatebox.set_spacing(5) newlabel = gtk.Label(_('New keys')) knownlabel = gtk.Label(_('Known keys')) lengthlabel = gtk.Label(_('Length')) generatebox.newkeysent = gtk.Entry() generatebox.newkeysent.set_width_chars(8) generatebox.knownkeysent = gtk.Entry() generatebox.knownkeysent.set_width_chars(15) generatebox.lengthent = gtk.Entry() generatebox.lengthent.set_width_chars(5) generatebox.lengthent.set_text('60') oklabel = gtk.Label() oklabel.set_markup(_('Generate!')) okbtn = gtk.Button() okbtn.add(oklabel) okbtn.connect('clicked', self.generate_ok_clicked_cb, generatebox) okbtn.set_alignment(0.5, 0.5) box = gtk.HBox() box.set_spacing(10) box.pack_start(newlabel, expand=False) box.pack_start(generatebox.newkeysent, expand=False) box.pack_start(knownlabel, expand=False) box.pack_start(generatebox.knownkeysent, expand=False) box.pack_start(lengthlabel, expand=False) box.pack_start(generatebox.lengthent, expand=False) box.pack_end(okbtn, expand=False) box.show_all() wordslabel = gtk.Label() wordslabel.set_markup(_('Edit Word List')) wordsbtn = gtk.Button() wordsbtn.add(wordslabel) wordsbtn.connect('clicked', self.generate_words_clicked_cb) wordsbtn.set_alignment(0.5, 0.5) generatebox.pack_start(box) generatebox.pack_start(wordsbtn, expand=False, fill=False) return generatebox def build_step(self, step, idx): stepbox = gtk.VBox() stepbox.set_spacing(5) steplabel = gtk.Label() steplabel.set_markup("" + (_('Step #%d') % (idx+1)) + "") steplabel.set_alignment(0.0, 0.5) steplabel.set_padding(10, 0) # Build the step type combo box. stepbox.typecombo = gtk.combo_box_new_text() stepbox.typecombo.append_text(_('Keys')) stepbox.typecombo.append_text(_('Words')) steptype = step.get('mode', 'text') if steptype == 'key': stepbox.typecombo.set_active(0) elif steptype == 'text': stepbox.typecombo.set_active(1) # Build the tool buttons. delstepbtn = gtk.Button() delstepbtn.add(sugar.graphics.icon.Icon(icon_name='list-remove')) delstepbtn.connect('clicked', self.del_step_clicked_cb, idx) addstepbtn = gtk.Button() addstepbtn.add(sugar.graphics.icon.Icon(icon_name='list-add')) addstepbtn.connect('clicked', self.add_step_clicked_cb, idx) moveupbtn = gtk.Button() moveupbtn.add(sugar.graphics.icon.Icon(icon_name='go-up')) moveupbtn.connect('clicked', self.move_step_up_clicked_cb, idx) movedownbtn = gtk.Button() movedownbtn.add(sugar.graphics.icon.Icon(icon_name='go-down')) movedownbtn.connect('clicked', self.move_step_down_clicked_cb, idx) if idx == 0: moveupbtn.set_sensitive(False) if idx == len(self.lesson['steps']) - 1: movedownbtn.set_sensitive(False) btnbox = gtk.HBox() btnbox.pack_start(steplabel, False, False) btnbox.pack_start(stepbox.typecombo, expand=False, padding=10) btnbox.pack_end(addstepbtn, False, False) btnbox.pack_end(delstepbtn, False, False) btnbox.pack_end(moveupbtn, False, False) btnbox.pack_end(movedownbtn, False, False) # Build the instructions entry. instlabel = gtk.Label() instlabel.set_markup("" + _('Instructions') + "") instlabel.set_alignment(0.0, 0.5) instlabel.set_padding(20, 0) self.labelsizegroup.add_widget(instlabel) stepbox.insttext = gtk.TextView(gtk.TextBuffer()) stepbox.insttext.props.wrap_mode = gtk.WRAP_WORD stepbox.insttext.modify_font(pango.FontDescription('Monospace')) instscroll = gtk.ScrolledWindow() instscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) instscroll.add(stepbox.insttext) instscroll.set_size_request(-1, 75) stepbox.insttext.get_buffer().set_text(step['instructions']) instbox = gtk.HBox() instbox.pack_start(instlabel, False, False) instbox.pack_start(instscroll, True, True) # Build the text entry. textlabel = gtk.Label() textlabel.set_markup("" + _('Text') + "") textlabel.set_alignment(0.0, 0.5) textlabel.set_padding(20, 0) self.labelsizegroup.add_widget(textlabel) stepbox.texttext = gtk.TextView(gtk.TextBuffer()) stepbox.texttext.props.wrap_mode = gtk.WRAP_WORD stepbox.texttext.modify_font(pango.FontDescription('monospace')) textscroll = gtk.ScrolledWindow() textscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) textscroll.add(stepbox.texttext) textscroll.set_size_request(-1, 100) stepbox.texttext.get_buffer().set_text(step['text']) textbox = gtk.HBox() textbox.pack_start(textlabel, expand=False) textbox.pack_start(textscroll) sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) sizegroup.add_widget(instlabel) sizegroup.add_widget(textlabel) stepbox.pack_start(btnbox, expand=False) stepbox.pack_start(instbox, expand=False) stepbox.pack_start(textbox, expand=False) return stepbox def build_medal(self, medal, name): box = gtk.HBox() label = gtk.Label() label.set_markup("" + name + "") label.set_alignment(0.0, 0.5) label.set_padding(20, 0) self.labelsizegroup.add_widget(label) box.pack_start(label, False, False) if self.lesson['type'] == 'normal': acclabel = gtk.Label(_('Accuracy')) wpmlabel = gtk.Label(_('WPM')) box.accent = gtk.Entry() box.wpment = gtk.Entry() box.accent.set_text(str(medal['accuracy'])) box.wpment.set_text(str(medal['wpm'])) box.pack_start(acclabel, False, False, 10) box.pack_start(box.accent, False, False) box.pack_start(wpmlabel, False, False, 10) box.pack_start(box.wpment, False, False) elif self.lesson['type'] == 'balloon': scorelabel = gtk.Label(_('Score')) box.scoreent = gtk.Entry() box.scoreent.set_text(str(medal['score'])) box.pack_start(scorelabel, False, False, 10) box.pack_start(box.scoreent, False, False) return box def build(self): self.in_build = True self.vbox = gtk.VBox() self.vbox.set_border_width(20) self.vbox.set_spacing(5) self.labelsizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) # Lesson details widgets. detailslabel = gtk.Label() detailslabel.set_markup("" + _('Lesson Details') + "") detailslabel.set_alignment(0.0, 0.5) detailslabel.set_padding(10, 0) namelabel = gtk.Label() namelabel.set_markup("" + _('Name') + "") namelabel.set_alignment(0.0, 0.5) namelabel.set_padding(20, 0) self.nameent = gtk.Entry() self.nameent.set_text(self.lesson['name']) namebox = gtk.HBox() namebox.pack_start(namelabel, expand=False) namebox.pack_start(self.nameent) typelabel = gtk.Label() typelabel.set_markup("" + _('Type') + "") typelabel.set_alignment(0.0, 0.5) typelabel.set_padding(20, 0) self.textradio = gtk.RadioButton(None, _('Normal Lesson')) self.textradio.connect('toggled', self.type_toggled_cb) self.balloonradio = gtk.RadioButton(self.textradio, _('Balloon Game')) self.balloonradio.connect('toggled', self.type_toggled_cb) self.textradio.set_active(self.lesson['type'] == 'normal') self.balloonradio.set_active(self.lesson['type'] == 'balloon') typebox = gtk.HBox() typebox.pack_start(typelabel, expand=False) typebox.pack_start(self.textradio, expand=False) typebox.pack_start(self.balloonradio, expand=False) desclabel = gtk.Label() desclabel.set_markup("" + _('Description') + "") desclabel.set_alignment(0.0, 0.5) desclabel.set_padding(20, 0) self.desctext = gtk.TextView(gtk.TextBuffer()) self.desctext.props.wrap_mode = gtk.WRAP_WORD descscroll = gtk.ScrolledWindow() descscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) descscroll.add(self.desctext) descscroll.set_size_request(-1, 75) self.desctext.get_buffer().set_text(self.lesson['description']) descbox = gtk.HBox() descbox.pack_start(desclabel, expand=False) descbox.pack_start(descscroll) # Build the options. optslabel = gtk.Label() optslabel.set_markup("" + _('Options') + "") optslabel.set_alignment(0.0, 0.5) optslabel.set_padding(20, 0) self.mistakescheck = gtk.CheckButton(_('Allow Mistakes')) self.mistakescheck.set_active(self.lesson.get('options', {}).get('mistakes', True)) self.backspacecheck = gtk.CheckButton(_('Allow Backspace')) self.backspacecheck.set_active(self.lesson.get('options', {}).get('backspace', True)) optsbox = gtk.HBox() optsbox.pack_start(optslabel, expand=False) optsbox.pack_start(self.backspacecheck, expand=False) optsbox.pack_start(self.mistakescheck, expand=False) self.labelsizegroup.add_widget(namelabel) self.labelsizegroup.add_widget(typelabel) self.labelsizegroup.add_widget(desclabel) self.labelsizegroup.add_widget(optslabel) self.vbox.pack_start(detailslabel, expand=False) self.vbox.pack_start(namebox, expand=False) self.vbox.pack_start(typebox, expand=False) self.vbox.pack_start(descbox, expand=False) self.vbox.pack_start(optsbox, expand=False) # Build the generator. generatelabel = gtk.Label() generatelabel.set_markup("" + _('Automatic Lesson Generator') + "") generatelabel.set_alignment(0.0, 0.5) generatelabel.set_padding(10, 0) generatebox = self.build_generate() self.vbox.pack_start(generatelabel, expand=False, padding=10) self.vbox.pack_start(generatebox, expand=False) self.has_normal_widgets = False self.has_balloon_widgets = False # Steps or words widgets. if self.lesson['type'] == 'normal': self.has_normal_widgets = True if not self.lesson.has_key('steps') or len(self.lesson['steps']) == 0: step = { 'instructions': '', 'text': '' } self.lesson['steps'] = [ step ] self.stepboxes = [] for step in self.lesson['steps']: stepbox = self.build_step(step, len(self.stepboxes)) self.stepboxes.append(stepbox) self.vbox.pack_start(stepbox, expand=False) if self.lesson['type'] == 'balloon': self.has_balloon_widgets = True if not self.lesson.has_key('words') or len(self.lesson['words']) == 0: self.lesson['words'] = [] textlabel = gtk.Label() textlabel.set_markup("" + _('Words') + "") textlabel.set_alignment(0.0, 0.5) textlabel.set_padding(20, 0) self.labelsizegroup.add_widget(textlabel) self.wordstext = gtk.TextView(gtk.TextBuffer()) self.wordstext.props.wrap_mode = gtk.WRAP_WORD self.wordstext.modify_font(pango.FontDescription('Monospace')) textscroll = gtk.ScrolledWindow() textscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) textscroll.add(self.wordstext) textscroll.set_size_request(-1, 200) self.wordstext.get_buffer().set_text(' '.join(self.lesson['words'])) textbox = gtk.HBox() textbox.pack_start(textlabel, expand=False) textbox.pack_start(textscroll) self.vbox.pack_start(textbox, expand=False) # Medal requirements widgets. medalslabel = gtk.Label() medalslabel.set_markup("" + _('Medal Requirements') + "") medalslabel.set_alignment(0.0, 0.5) medalslabel.set_padding(10, 0) self.vbox.pack_start(medalslabel, expand=False, padding=10) self.medalboxes = [] self.medalboxes.append(self.build_medal(self.lesson['medals'][0], _('Bronze'))) self.medalboxes.append(self.build_medal(self.lesson['medals'][1], _('Silver'))) self.medalboxes.append(self.build_medal(self.lesson['medals'][2], _('Gold'))) self.vbox.pack_start(self.medalboxes[0], expand=False) self.vbox.pack_start(self.medalboxes[1], expand=False) self.vbox.pack_start(self.medalboxes[2], expand=False) self.vbox.show_all() self.in_build = False # Remove any existing controls. if self.vp.get_child(): self.vp.remove(self.vp.get_child()) self.vp.add(self.vbox) def stop_clicked_cb(self, btn): self.save() self.activity.pop_screen() def save(self): self.lesson['name'] = self.nameent.get_text() buf = self.desctext.get_buffer() self.lesson['description'] = buf.get_text(buf.get_start_iter(), buf.get_end_iter()) if not self.lesson.has_key('options'): self.lesson['options'] = {} self.lesson['options']['mistakes'] = self.mistakescheck.get_active() self.lesson['options']['backspace'] = self.backspacecheck.get_active() if self.textradio.get_active(): self.lesson['type'] = 'normal' if self.has_normal_widgets: steps = [] for sb in self.stepboxes: step = {} buf = sb.insttext.get_buffer() step['instructions'] = buf.get_text(buf.get_start_iter(), buf.get_end_iter()) buf = sb.texttext.get_buffer() step['text'] = buf.get_text(buf.get_start_iter(), buf.get_end_iter()) if sb.typecombo.get_active() == 0: step['mode'] = 'key' elif sb.typecombo.get_active() == 1: step['mode'] = 'text' steps.append(step) self.lesson['steps'] = steps for i in range(0, 3): self.lesson['medals'][i]['accuracy'] = int(self.medalboxes[i].accent.get_text()) self.lesson['medals'][i]['wpm'] = int(self.medalboxes[i].wpment.get_text()) if self.balloonradio.get_active(): self.lesson['type'] = 'balloon' if self.has_balloon_widgets: buf = self.wordstext.get_buffer() text = buf.get_text(buf.get_start_iter(), buf.get_end_iter()) self.lesson['words'] = text.split(' ') for i in range(0, 3): self.lesson['medals'][i]['score'] = int(self.medalboxes[i].scoreent.get_text()) def add_step_clicked_cb(self, btn, index): self.save() step = { 'instructions': '', 'text': '' } self.lesson['steps'].insert(index, step) self.build() def del_step_clicked_cb(self, btn, index): self.save() self.lesson['steps'].pop(index) self.build() def move_step_up_clicked_cb(self, btn, index): if index > 0: self.save() step = self.lesson['steps'].pop(index) self.lesson['steps'].insert(index-1, step) self.build() def move_step_down_clicked_cb(self, btn, index): if index < len(self.lesson['steps']) - 1: self.save() step = self.lesson['steps'].pop(index) self.lesson['steps'].insert(index+1, step) self.build() def type_toggled_cb(self, btn): # Prevent infinite recursion if self.in_build: return self.save() self.build() def get_wordlist(self): if len(self.activity.wordlist): return self.activity.wordlist # Load the myspell dictionary. # TODO: Find a better way to determine its location. code = locale.getdefaultlocale()[0] or 'en_US' return lessonbuilder.load_wordlist('/usr/share/myspell/%s.dic' % code) def generate_ok_clicked_cb(self, btn, box): self.save() new_keys = box.newkeysent.get_text() known_keys = box.knownkeysent.get_text() length = int(box.lengthent.get_text()) try: words = self.get_wordlist() if self.lesson['type'] == 'normal': self.lesson['steps'] = lessonbuilder.build_key_steps(length, new_keys, known_keys, words, []) if self.lesson['type'] == 'balloon': self.lesson['words'] = lessonbuilder.build_game_words(length, new_keys, known_keys, words, []) except Exception, e: logging.error('Unable to generate lesson: ' + str(e)) self.build() def generate_words_clicked_cb(self, btn): self.activity.push_screen(WordListScreen(self.activity)) class WordListScreen(gtk.VBox): def __init__(self, activity): gtk.VBox.__init__(self) self.set_border_width(10) self.activity = activity # Add the header. title = gtk.Label() title.set_markup("" + _("Edit Word List") + "") title.set_alignment(1.0, 0.0) stoplabel = gtk.Label(_('Go Back')) stopbtn = gtk.Button() stopbtn.add(stoplabel) stopbtn.connect('clicked', self.stop_clicked_cb) titlebox = gtk.HBox() titlebox.pack_start(stopbtn, False, False, 10) titlebox.pack_end(title, False, False, 10) subtitle = gtk.Label() subtitle.set_markup("" + _("Type or paste words here, for the Automatic Lesson Generator. If empty, the dictionary will be used.") + "") subtitle.set_alignment(1.0, 0.0) self.wordlisttext = gtk.TextView(gtk.TextBuffer()) self.wordlisttext.props.wrap_mode = gtk.WRAP_WORD wordlistscroll = gtk.ScrolledWindow() wordlistscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) wordlistscroll.add(self.wordlisttext) wordlistscroll.set_size_request(-1, 75) self.wordlisttext.get_buffer().set_text(' '.join(self.activity.wordlist)) self.pack_start(titlebox, expand=False) self.pack_start(subtitle, expand=False) self.pack_start(gtk.HSeparator(), expand=False) self.pack_start(wordlistscroll) self.show_all() def stop_clicked_cb(self, btn): buf = self.wordlisttext.get_buffer() wordstext = buf.get_text(buf.get_start_iter(), buf.get_end_iter()) self.activity.wordlist = wordstext.split() self.activity.pop_screen()