#!/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 _
from gi.repository import Gtk
from gi.repository import GObject
from gi.repository import Pango
# Import Sugar UI modules.
import sugar3.activity.activity
import sugar3.graphics.style
import sugar3.graphics.icon
# Import lessonbuilder functions.
import lessonbuilder
class EditLessonScreen(Gtk.VBox):
def __init__(self, activity, lesson):
GObject.GObject.__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(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.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
self.scroll.add(self.vp)
self.pack_start(titlebox, False, False, 10)
self.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL), 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(label=_('New keys'))
knownlabel = Gtk.Label(label=_('Known keys'))
lengthlabel = Gtk.Label(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, False, True, 0)
box.pack_start(generatebox.newkeysent, False, True, 0)
box.pack_start(knownlabel, False, True, 0)
box.pack_start(generatebox.knownkeysent, False, True, 0)
box.pack_start(lengthlabel, False, True, 0)
box.pack_start(generatebox.lengthent, False, True, 0)
box.pack_end(okbtn, False, True, 0)
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, True, True, 0)
generatebox.pack_start(wordsbtn, expand=False, fill=False, padding=0)
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.ComboBoxText()
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(sugar3.graphics.icon.Icon(icon_name='list-remove'))
delstepbtn.connect('clicked', self.del_step_clicked_cb, idx)
addstepbtn = Gtk.Button()
addstepbtn.add(sugar3.graphics.icon.Icon(icon_name='list-add'))
addstepbtn.connect('clicked', self.add_step_clicked_cb, idx)
moveupbtn = Gtk.Button()
moveupbtn.add(sugar3.graphics.icon.Icon(icon_name='go-up'))
moveupbtn.connect('clicked', self.move_step_up_clicked_cb, idx)
movedownbtn = Gtk.Button()
movedownbtn.add(sugar3.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, 0)
btnbox.pack_start(stepbox.typecombo, expand=False,
fill=False, padding=10)
btnbox.pack_end(addstepbtn, False, False, 0)
btnbox.pack_end(delstepbtn, False, False, 0)
btnbox.pack_end(moveupbtn, False, False, 0)
btnbox.pack_end(movedownbtn, False, False, 0)
# 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()
stepbox.insttext.set_buffer(Gtk.TextBuffer())
stepbox.insttext.props.wrap_mode = Gtk.WrapMode.WORD
stepbox.insttext.modify_font(Pango.FontDescription('Monospace'))
instscroll = Gtk.ScrolledWindow()
instscroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.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, 0)
instbox.pack_start(instscroll, True, True, 0)
# 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()
stepbox.texttext.set_buffer(Gtk.TextBuffer())
stepbox.texttext.props.wrap_mode = Gtk.WrapMode.WORD
stepbox.texttext.modify_font(Pango.FontDescription('monospace'))
textscroll = Gtk.ScrolledWindow()
textscroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.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, False, True, 0)
textbox.pack_start(textscroll, True, True, 0)
sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
sizegroup.add_widget(instlabel)
sizegroup.add_widget(textlabel)
stepbox.pack_start(btnbox, False, True, 0)
stepbox.pack_start(instbox, False, True, 0)
stepbox.pack_start(textbox, False, True, 0)
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, 0)
if self.lesson['type'] == 'normal':
acclabel = Gtk.Label(label=_('Accuracy'))
wpmlabel = Gtk.Label(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, 0)
box.pack_start(wpmlabel, False, False, 10)
box.pack_start(box.wpment, False, False, 0)
elif self.lesson['type'] == 'balloon':
scorelabel = Gtk.Label(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, 0)
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.SizeGroupMode.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, False, True, 0)
namebox.pack_start(self.nameent, True, True, 0)
typelabel = Gtk.Label()
typelabel.set_markup("" + _('Type') + "")
typelabel.set_alignment(0.0, 0.5)
typelabel.set_padding(20, 0)
self.textradio = Gtk.RadioButton(_('Normal Lesson'))
self.textradio.connect('toggled', self.type_toggled_cb)
self.balloonradio = Gtk.RadioButton.new_with_label_from_widget(
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, False, True, 0)
typebox.pack_start(self.textradio, False, True, 0)
typebox.pack_start(self.balloonradio, False, True, 0)
desclabel = Gtk.Label()
desclabel.set_markup("" + _('Description') + "")
desclabel.set_alignment(0.0, 0.5)
desclabel.set_padding(20, 0)
self.desctext = Gtk.TextView()
self.desctext.set_buffer(Gtk.TextBuffer())
self.desctext.props.wrap_mode = Gtk.WrapMode.WORD
descscroll = Gtk.ScrolledWindow()
descscroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.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, False, True, 0)
descbox.pack_start(descscroll, True, True, 0)
# 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, False, True, 0)
optsbox.pack_start(self.backspacecheck, False, True, 0)
optsbox.pack_start(self.mistakescheck, False, True, 0)
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, False, True, 0)
self.vbox.pack_start(namebox, False, True, 0)
self.vbox.pack_start(typebox, False, True, 0)
self.vbox.pack_start(descbox, False, True, 0)
self.vbox.pack_start(optsbox, False, True, 0)
# 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,
fill=False, padding=10)
self.vbox.pack_start(generatebox, False, True, 0)
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, False, True, 0)
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()
self.wordstext.set_buffer(Gtk.TextBuffer())
self.wordstext.props.wrap_mode = Gtk.WrapMode.WORD
self.wordstext.modify_font(Pango.FontDescription('Monospace'))
textscroll = Gtk.ScrolledWindow()
textscroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.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, False, True, 0)
textbox.pack_start(textscroll, True, True, 0)
self.vbox.pack_start(textbox, False, True, 0)
# 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,
fill=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], False, True, 0)
self.vbox.pack_start(self.medalboxes[1], False, True, 0)
self.vbox.pack_start(self.medalboxes[2], False, True, 0)
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(), False)
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(),
False)
buf = sb.texttext.get_buffer()
step['text'] = buf.get_text(buf.get_start_iter(),
buf.get_end_iter(), False)
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(), False)
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):
GObject.GObject.__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(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()
self.wordlisttext.set_buffer(Gtk.TextBuffer())
self.wordlisttext.props.wrap_mode = Gtk.WrapMode.WORD
wordlistscroll = Gtk.ScrolledWindow()
wordlistscroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.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, False, True, 0)
self.pack_start(subtitle, False, True, 0)
separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
self.pack_start(separator, expand=False, fill=False, padding=0)
self.pack_start(wordlistscroll, True, True, 0)
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(), False)
self.activity.wordlist = wordstext.split()
self.activity.pop_screen()