Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksey Lim <alsroot@member.fsf.org>2009-12-25 12:22:35 (GMT)
committer Aleksey Lim <alsroot@member.fsf.org>2009-12-25 12:22:35 (GMT)
commit070882bcfecee3d17a909c4072da7b2f160d41d3 (patch)
tree5f364b26912dc6904c307ae49df4bd9cdd739a93
parentea7c199885e7295cfa6b6038f3a9865fee9e7f7b (diff)
Switch to new toolbar design
-rw-r--r--Speak.activity/activity.py330
-rw-r--r--Speak.activity/activity/activity-hablarconsara.svg134
-rw-r--r--Speak.activity/brain.py110
-rw-r--r--Speak.activity/chat.py31
-rw-r--r--Speak.activity/eye.py11
-rw-r--r--Speak.activity/face.py25
-rw-r--r--Speak.activity/glasses.py4
-rw-r--r--Speak.activity/icons/face.svg103
-rw-r--r--Speak.activity/icons/mode-chat.svg128
-rw-r--r--Speak.activity/icons/mode-robot.svg119
-rw-r--r--Speak.activity/icons/mode-type.svg82
-rw-r--r--Speak.activity/icons/voice.svg66
-rw-r--r--Speak.activity/port/COPYING340
-rw-r--r--Speak.activity/port/README17
-rw-r--r--Speak.activity/port/__init__.py0
-rw-r--r--Speak.activity/port/activity.py317
-rw-r--r--Speak.activity/port/widgets.py403
-rw-r--r--Speak.activity/widgets.py48
18 files changed, 766 insertions, 1502 deletions
diff --git a/Speak.activity/activity.py b/Speak.activity/activity.py
index 68746ca..1f13761 100644
--- a/Speak.activity/activity.py
+++ b/Speak.activity/activity.py
@@ -7,24 +7,24 @@
#
# Parts of Speak.activity are based on code from Measure.activity
# Copyright (C) 2007 Arjun Sarwal - arjun@laptop.org
-#
+#
# Speak.activity 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.
-#
+#
# Speak.activity 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 Speak.activity. If not, see <http://www.gnu.org/licenses/>.
from sugar.activity import activity
from sugar.presence import presenceservice
-import logging
+import logging
import gtk
import gobject
import pango
@@ -32,8 +32,14 @@ import cjson
from gettext import gettext as _
from sugar.graphics.toolbutton import ToolButton
-from port.widgets import ComboBox, ToolComboBox
-from port.activity import SharedActivity
+from sugar.graphics.toggletoolbutton import ToggleToolButton
+from sugar.graphics.radiotoolbutton import RadioToolButton
+
+from toolkit.toolitem import ToolWidget
+from toolkit.combobox import ComboBox
+from toolkit.toolbarbox import ToolbarBox
+from toolkit.activity import SharedActivity
+from toolkit.activity_widgets import *
import eye
import glasses
@@ -45,20 +51,23 @@ import face
import brain
import chat
import espeak
-import widgets
from messenger import Messenger, SERVICE
logger = logging.getLogger('speak')
-BOT_TOOLBAR = 3
-CHAT_TOOLBAR = 4
+MODE_TYPE = 1
+MODE_BOT = 2
+MODE_CHAT = 3
+
class SpeakActivity(SharedActivity):
def __init__(self, handle):
self.notebook = gtk.Notebook()
-
+
SharedActivity.__init__(self, self.notebook, SERVICE, handle)
- bounds = self.get_allocation()
+
+ self._mode = MODE_TYPE
+ self.numeyesadj = None
# make an audio device for playing back and rendering audio
self.connect( "notify::active", self._activeCb )
@@ -81,9 +90,11 @@ class SpeakActivity(SharedActivity):
box.pack_start(self.face)
box.pack_start(self.entrycombo, expand=False)
- box.add_events(gtk.gdk.BUTTON_PRESS_MASK |
- gtk.gdk.POINTER_MOTION_MASK)
- box.connect("motion_notify_event", self._mouse_moved_cb)
+ self.add_events(gtk.gdk.POINTER_MOTION_HINT_MASK
+ | gtk.gdk.POINTER_MOTION_MASK)
+ self.connect("motion_notify_event", self._mouse_moved_cb)
+
+ box.add_events(gtk.gdk.BUTTON_PRESS_MASK)
box.connect("button_press_event", self._mouse_clicked_cb)
# desktop
@@ -98,62 +109,80 @@ class SpeakActivity(SharedActivity):
self.chat.show_all()
self.notebook.append_page(self.chat)
- # make some toolbars
- self.toolbox = activity.ActivityToolbox(self)
- self.set_toolbox(self.toolbox)
- self.toolbox.show()
- #activitybar = toolbox.get_activity_toolbar()
- self.toolbox.connect('current-toolbar-changed', self._toolbar_changed_cb)
-
- voicebar = self.make_voice_bar()
- self.toolbox.add_toolbar(_('Voice'), voicebar)
- voicebar.show()
-
- facebar = self.make_face_bar()
- self.toolbox.add_toolbar(_('Face'), facebar)
- facebar.show()
-
- self.bot = brain.Toolbar(self)
- self.toolbox.add_toolbar(_('Robot'), self.bot)
- self.bot.show()
-
- chatbar = chat.Toolbar(self.chat)
- self.toolbox.add_toolbar(_('Chat'), chatbar)
- chatbar.show()
-
# make the text box active right away
self.entry.grab_focus()
-
+
self.entry.connect("move-cursor", self._cursor_moved_cb)
self.entry.connect("changed", self._cursor_moved_cb)
- # try to catch all mouse-moved events so the eyes will track wherever you go
- # this doesn't work for some reason I don't understand
- # it gets mouse motion over lots of stuff, but not sliders or comboboxes
- # import time
- # self.window.set_events(self.window.get_events() | gtk.gdk.POINTER_MOTION_MASK)
- # def event_filter(event, user_data=None):
- # map(lambda w: w.queue_draw(), self.eyes)
- # print time.asctime(), time.time(), event.get_coords(), event.get_root_coords()
- # return gtk.gdk.FILTER_CONTINUE
- # self.window.add_filter(event_filter)
- # map(lambda c: c.forall(lambda w: w.add_events(gtk.gdk.POINTER_MOTION_MASK)), self.window.get_children())
-
- # start polling for mouse movement
- # self.mouseX = None
- # self.mouseY = None
- # def poll_mouse():
- # display = gtk.gdk.display_get_default()
- # screen, mouseX, mouseY, modifiers = display.get_pointer()
- # if self.mouseX != mouseX or self.mouseY != mouseY:
- # self.mouseX = mouseX
- # self.mouseY = mouseY
- # map(lambda w: w.queue_draw(), self.eyes)
- # return True
- # gobject.timeout_add(100, poll_mouse)
+ # toolbar
+
+ toolbox = ToolbarBox()
+
+ toolbox.toolbar.insert(ActivityToolbarButton(self), -1)
+
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(False)
+ toolbox.toolbar.insert(separator, -1)
+
+ self.voices = ComboBox()
+ for name in sorted(voice.allVoices().keys()):
+ self.voices.append_item(voice.allVoices()[name], name)
+ self.voices.select(voice.defaultVoice())
+ all_voices = self.voices.get_model()
+ brain_voices = brain.get_voices()
+
+ mode_type = RadioToolButton(
+ named_icon='mode-type',
+ tooltip=_('Lessons'))
+ mode_type.connect('toggled', self.__toggled_mode_type_cb, all_voices)
+ toolbox.toolbar.insert(mode_type, -1)
+
+ mode_robot = RadioToolButton(
+ named_icon='mode-robot',
+ group=mode_type,
+ tooltip=_('Robot'))
+ mode_robot.connect('toggled', self.__toggled_mode_robot_cb,
+ brain_voices)
+ toolbox.toolbar.insert(mode_robot, -1)
+
+ mode_chat = RadioToolButton(
+ named_icon='mode-chat',
+ group=mode_type,
+ tooltip=_('Chat'))
+ mode_chat.connect('toggled', self.__toggled_mode_chat_cb, all_voices)
+ toolbox.toolbar.insert(mode_chat, -1)
+
+ separator = gtk.SeparatorToolItem()
+ toolbox.toolbar.insert(separator, -1)
+
+ voices_toolitem = ToolWidget(widget=self.voices)
+ toolbox.toolbar.insert(voices_toolitem, -1)
+
+ voice_button = ToolbarButton(
+ page=self.make_voice_bar(),
+ label=_('Voice'),
+ icon_name='voice')
+ toolbox.toolbar.insert(voice_button, -1)
+
+ face_button = ToolbarButton(
+ page=self.make_face_bar(),
+ label=_('Face'),
+ icon_name='face')
+ toolbox.toolbar.insert(face_button, -1)
+
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(False)
+ separator.set_expand(True)
+ toolbox.toolbar.insert(separator, -1)
+
+ toolbox.toolbar.insert(StopButton(self), -1)
+
+ toolbox.show_all()
+ self.toolbar_box = toolbox
def new_instance(self):
- self.voice_combo.connect('changed', self.voice_changed_cb)
+ self.voices.connect('changed', self.__changed_voices_cb)
self.pitchadj.connect("value_changed", self.pitch_adjusted_cb, self.pitchadj)
self.rateadj.connect("value_changed", self.rate_adjusted_cb, self.rateadj)
self.mouth_shape_combo.connect('changed', self.mouth_changed_cb, False)
@@ -203,7 +232,7 @@ class SpeakActivity(SharedActivity):
pos = layout.get_cursor_pos(index)
x = pos[0][0] / pango.SCALE - entry.props.scroll_offset
y = entry.get_allocation().y
- self.face.look_at(x, y)
+ self.face.look_at(pos=(x, y))
def get_mouse(self):
display = gtk.gdk.display_get_default()
@@ -212,8 +241,8 @@ class SpeakActivity(SharedActivity):
def _mouse_moved_cb(self, widget, event):
# make the eyes track the motion of the mouse cursor
- x,y = self.get_mouse()
- self.face.look_at(x, y)
+ self.face.look_at()
+ self.chat.look_at()
def _mouse_clicked_cb(self, widget, event):
pass
@@ -221,18 +250,6 @@ class SpeakActivity(SharedActivity):
def make_voice_bar(self):
voicebar = gtk.Toolbar()
- # button = ToolButton('change-voice')
- # button.set_tooltip("Change Voice")
- # button.connect('clicked', self.change_voice_cb)
- # voicebar.insert(button, -1)
- # button.show()
-
- self.voice_combo = widgets.Voices(self.face)
- self.voice_combo.select(name=self.face.status.voice.friendlyname)
- combotool = ToolComboBox(self.voice_combo)
- voicebar.insert(combotool, -1)
- combotool.show()
-
self.pitchadj = gtk.Adjustment(self.face.status.pitch, 0,
espeak.PITCH_MAX, 1, espeak.PITCH_MAX/10, 0)
pitchbar = gtk.HScale(self.pitchadj)
@@ -240,11 +257,11 @@ class SpeakActivity(SharedActivity):
#pitchbar.set_inverted(True)
pitchbar.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
pitchbar.set_size_request(240,15)
- pitchtool = gtk.ToolItem()
- pitchtool.add(pitchbar)
- pitchtool.show()
- voicebar.insert(pitchtool, -1)
- pitchbar.show()
+
+ pitchbar_toolitem = ToolWidget(
+ widget=pitchbar,
+ label_text=_('Pitch:'))
+ voicebar.insert(pitchbar_toolitem, -1)
self.rateadj = gtk.Adjustment(self.face.status.rate, 0, espeak.RATE_MAX,
1, espeak.RATE_MAX/10, 0)
@@ -253,17 +270,14 @@ class SpeakActivity(SharedActivity):
#ratebar.set_inverted(True)
ratebar.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
ratebar.set_size_request(240,15)
- ratetool = gtk.ToolItem()
- ratetool.add(ratebar)
- ratetool.show()
- voicebar.insert(ratetool, -1)
- ratebar.show()
-
- return voicebar
- def voice_changed_cb(self, combo):
- self.face.status.voice = combo.props.value
- self.face.say_notification(self.face.status.voice.friendlyname)
+ ratebar_toolitem = ToolWidget(
+ widget=ratebar,
+ label_text=_('Rate:'))
+ voicebar.insert(ratebar_toolitem, -1)
+
+ voicebar.show_all()
+ return voicebar
def pitch_adjusted_cb(self, get, data=None):
self.face.status.pitch = get.value
@@ -276,42 +290,44 @@ class SpeakActivity(SharedActivity):
def make_face_bar(self):
facebar = gtk.Toolbar()
- self.numeyesadj = None
-
self.mouth_shape_combo = ComboBox()
self.mouth_shape_combo.append_item(mouth.Mouth, _("Simple"))
self.mouth_shape_combo.append_item(waveform_mouth.WaveformMouth, _("Waveform"))
self.mouth_shape_combo.append_item(fft_mouth.FFTMouth, _("Frequency"))
self.mouth_shape_combo.set_active(0)
- combotool = ToolComboBox(self.mouth_shape_combo)
- facebar.insert(combotool, -1)
- combotool.show()
+
+ mouth_shape_toolitem = ToolWidget(
+ widget=self.mouth_shape_combo,
+ label_text=_('Mouth:'))
+ facebar.insert(mouth_shape_toolitem, -1)
self.eye_shape_combo = ComboBox()
self.eye_shape_combo.append_item(eye.Eye, _("Round"))
self.eye_shape_combo.append_item(glasses.Glasses, _("Glasses"))
- combotool = ToolComboBox(self.eye_shape_combo)
- facebar.insert(combotool, -1)
- combotool.show()
+ self.eye_shape_combo.set_active(0)
+
+ eye_shape_toolitem = ToolWidget(
+ widget=self.eye_shape_combo,
+ label_text=_('Eyes:'))
+ facebar.insert(eye_shape_toolitem, -1)
self.numeyesadj = gtk.Adjustment(2, 1, 5, 1, 1, 0)
numeyesbar = gtk.HScale(self.numeyesadj)
numeyesbar.set_draw_value(False)
numeyesbar.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
numeyesbar.set_size_request(240,15)
- numeyestool = gtk.ToolItem()
- numeyestool.add(numeyesbar)
- numeyestool.show()
- facebar.insert(numeyestool, -1)
- numeyesbar.show()
- self.eye_shape_combo.set_active(0)
-
+ numeyesbar_toolitem = ToolWidget(
+ widget=numeyesbar,
+ label_text=_('Eyes number:'))
+ facebar.insert(numeyesbar_toolitem, -1)
+
+ facebar.show_all()
return facebar
def mouth_changed_cb(self, combo, quiet):
self.face.status.mouth = combo.props.value
- self.face.update()
+ self._update_face()
# this SegFaults: self.face.say(combo.get_active_text())
if not quiet:
@@ -323,12 +339,16 @@ class SpeakActivity(SharedActivity):
self.face.status.eyes = [self.eye_shape_combo.props.value] \
* int(self.numeyesadj.value)
- self.face.update()
+ self._update_face()
# this SegFaults: self.face.say(self.eye_shape_combo.get_active_text())
if not quiet:
self.face.say_notification(_("eyes changed"))
-
+
+ def _update_face(self):
+ self.face.update()
+ self.chat.update(self.face.status)
+
def _combo_changed_cb(self, combo):
# when a new item is chosen, make sure the text is selected
if not self.entry.is_focus():
@@ -359,13 +379,14 @@ class SpeakActivity(SharedActivity):
text = entry.props.text
if text:
self.face.look_ahead()
-
+
# speak the text
- if self.toolbox.get_current_toolbar() == BOT_TOOLBAR:
- self.face.say(self.bot.respond(text))
+ if self._mode == MODE_BOT:
+ self.face.say(
+ brain.respond(self.voices.props.value, text))
else:
self.face.say(text)
-
+
# add this text to our history unless it is the same as the last item
history = self.entrycombo.get_model()
if len(history)==0 or history[-1][0] != text:
@@ -378,25 +399,80 @@ class SpeakActivity(SharedActivity):
# select the whole text
entry.select_region(0,-1)
- def _synth_cb(self, callback_type, index_mark=None):
- print "synth callback:", callback_type, index_mark
-
def _activeCb( self, widget, pspec ):
# only generate sound when this activity is active
if not self.props.active:
self.face.shut_up()
self.chat.shut_up()
- def _toolbar_changed_cb(self, widget, index):
- if index == CHAT_TOOLBAR:
- self.face.shut_up()
- self.chat.update(self.face.status)
- self.notebook.set_current_page(1)
+ def _set_voice(self, new_voice):
+ try:
+ self.voices.handler_block_by_func(self.__changed_voices_cb)
+ self.voices.select(new_voice)
+ finally:
+ self.voices.handler_unblock_by_func(self.__changed_voices_cb)
+
+ def __toggled_mode_type_cb(self, button, voices_model):
+ if not button.props.active:
+ return
+
+ self._mode = MODE_TYPE
+ self.chat.shut_up()
+ self.face.shut_up()
+ self.notebook.set_current_page(0)
+
+ old_voice = self.voices.props.value
+ self.voices.set_model(voices_model)
+ self._set_voice(old_voice)
+
+ def __toggled_mode_robot_cb(self, button, voices_model):
+ if not button.props.active:
+ return
+
+ self._mode = MODE_BOT
+ self.chat.shut_up()
+ self.face.shut_up()
+ self.notebook.set_current_page(0)
+
+ old_voice = self.voices.props.value
+ self.voices.set_model(voices_model)
+
+ if not [i for i in voices_model if i[0] == old_voice]:
+ new_voice = brain.get_default_voice()
+ sorry = _("Sorry, I can't speak %s, let's speak %s instead.") % \
+ (old_voice.friendlyname, new_voice.friendlyname)
else:
- self.chat.shut_up()
- self.notebook.set_current_page(0)
- if index == BOT_TOOLBAR:
- self.bot.update_voice()
+ new_voice = old_voice
+ sorry = None
+
+ self._set_voice(new_voice)
+
+ if not brain.load(self, self.voices.props.value, sorry):
+ if sorry:
+ self.face.say_notification(sorry)
+
+ def __toggled_mode_chat_cb(self, button, voices_model):
+ if not button.props.active:
+ return
+
+ self._mode = MODE_CHAT
+ self.face.shut_up()
+ self.notebook.set_current_page(1)
+
+ old_voice = self.voices.props.value
+ self.voices.set_model(voices_model)
+ self._set_voice(old_voice)
+
+ def __changed_voices_cb(self, combo):
+ voice = combo.props.value
+
+ if self._mode == MODE_BOT:
+ self.face.set_voice(voice)
+ brain.load(self, voice)
+ else:
+ self.face.set_voice(voice)
+ self.face.say_notification(voice.friendlyname)
+
# activate gtk threads when this module loads
gtk.gdk.threads_init()
diff --git a/Speak.activity/activity/activity-hablarconsara.svg b/Speak.activity/activity/activity-hablarconsara.svg
deleted file mode 100644
index dcd31cc..0000000
--- a/Speak.activity/activity/activity-hablarconsara.svg
+++ /dev/null
@@ -1,134 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
- <!ENTITY stroke_color "#010101">
- <!ENTITY fill_color "#FFFFFF">
-]>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="50"
- height="50"
- id="svg2"
- sodipodi:version="0.32"
- inkscape:version="0.46"
- sodipodi:docname="Activity-HablaConSara.svg"
- inkscape:output_extension="org.inkscape.output.svg.inkscape">
- <metadata
- id="metadata17">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs15"><inkscape:perspective
- sodipodi:type="inkscape:persp3d"
- inkscape:vp_x="0 : 25 : 1"
- inkscape:vp_y="0 : 1000 : 0"
- inkscape:vp_z="50 : 25 : 1"
- inkscape:persp3d-origin="25 : 16.666667 : 1"
- id="perspective19" />
-<inkscape:perspective
- id="perspective2475"
- inkscape:persp3d-origin="27.5 : 18.333333 : 1"
- inkscape:vp_z="55 : 27.5 : 1"
- inkscape:vp_y="0 : 1000 : 0"
- inkscape:vp_x="0 : 27.5 : 1"
- sodipodi:type="inkscape:persp3d" />
-
-
-</defs>
- <sodipodi:namedview
- inkscape:window-height="722"
- inkscape:window-width="1280"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0.0"
- guidetolerance="10.0"
- gridtolerance="10.0"
- objecttolerance="10.0"
- borderopacity="1.0"
- id="base"
- showgrid="false"
- inkscape:zoom="2.33"
- inkscape:cx="26.863795"
- inkscape:cy="20.573512"
- inkscape:window-x="0"
- inkscape:window-y="26"
- inkscape:current-layer="svg2" />
- <g
- id="g3429">
- <ellipse
- transform="matrix(0.6477914,0,0,0.6477914,10.158787,8.6181926)"
- id="ellipse4"
- ry="10"
- rx="10"
- cy="12"
- cx="12"
- sodipodi:cx="12"
- sodipodi:cy="12"
- sodipodi:rx="10"
- sodipodi:ry="10"
- style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:3.70489636;stroke-miterlimit:4;stroke-dasharray:none" />
- <ellipse
- transform="matrix(0.6477914,0,0,0.6477914,10.158787,8.6181926)"
- id="ellipse6"
- ry="10"
- rx="10"
- cy="12"
- cx="36"
- sodipodi:cx="36"
- sodipodi:cy="12"
- sodipodi:rx="10"
- sodipodi:ry="10"
- style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:3.70489636;stroke-miterlimit:4;stroke-dasharray:none" />
- <circle
- transform="matrix(0.6477914,0,0,0.6477914,9.7794379,8.6181926)"
- id="circle8"
- r="2"
- cy="10"
- cx="16"
- sodipodi:cx="16"
- sodipodi:cy="10"
- sodipodi:rx="2"
- sodipodi:ry="2"
- style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:3.396155;stroke-miterlimit:4;stroke-dasharray:none" />
- <circle
- transform="matrix(0.6477914,0,0,0.6477914,9.8553077,8.6940624)"
- id="circle10"
- r="2"
- cy="10"
- cx="40"
- sodipodi:cx="40"
- sodipodi:cy="10"
- sodipodi:rx="2"
- sodipodi:ry="2"
- style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:3.396155;stroke-miterlimit:4;stroke-dasharray:none" />
- <path
- sodipodi:type="arc"
- style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:2.96432175;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- id="path3304"
- sodipodi:cx="16.309013"
- sodipodi:cy="-42.060085"
- sodipodi:rx="27.896996"
- sodipodi:ry="19.957081"
- d="M 42.271638,-34.758097 A 27.896996,19.957081 0 0 1 -9.0684098,-33.771844"
- transform="matrix(0.6789713,0,0,0.9654291,13.305764,57.674017)"
- sodipodi:start="0.37458313"
- sodipodi:end="2.7133165"
- sodipodi:open="true" />
- <path
- id="path2468"
- display="inline"
- d="M 7.7871374,45.936639 C 8.4431762,47.180327 13.584711,46.000335 15.719242,44.357198 C 17.741226,42.811224 18.804163,41.55458 19.890186,41.55458 C 20.980057,41.55458 31.952834,42.547803 40.028267,35.384717 C 46.978233,29.223491 49.429243,20.499317 44.376202,12.241526 C 39.324132,3.9891322 31.070741,2.4388396 24.292956,3.0002269 C 15.043003,3.7742937 2.9399537,11.944637 3.3757097,24.255214 C 3.6296602,31.500349 6.604921,34.125914 8.1103471,35.810076 C 9.6128874,37.500715 12.482335,37.500715 10.988453,41.842831 C 10.401673,43.547504 7.2965512,44.993076 7.7871374,45.936639 z"
- style="fill:none;stroke:&stroke_color;;stroke-width:3.28907108;display:inline" />
- </g>
-</svg>
diff --git a/Speak.activity/brain.py b/Speak.activity/brain.py
index b53f030..6edfe94 100644
--- a/Speak.activity/brain.py
+++ b/Speak.activity/brain.py
@@ -8,12 +8,12 @@
# 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.
-#
+#
# HablarConSara.activity 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 HablarConSara.activity. If not, see <http://www.gnu.org/licenses/>.
@@ -24,7 +24,7 @@ from gettext import gettext as _
import logging
logger = logging.getLogger('speak')
-from port.widgets import ToolComboBox
+from toolkit.combobox import ComboBox
import bot.aiml
import voice
@@ -39,82 +39,58 @@ BOTS = {
'predicates': { 'name': 'Alice',
'master': 'the Sugar Community' } } }
-DEFAULT = voice.defaultVoice()
-if not BOTS.has_key(DEFAULT):
- DEFAULT = voice.allVoices()[_('English')]
-
-class Toolbar(gtk.Toolbar):
- def __init__(self, activity):
- gtk.Toolbar.__init__(self)
- self.activity = activity
- self._kernels = {}
-
- self.voices = ToolComboBox()
+_kernels = {}
- for lang in sorted(BOTS.keys()):
- self.voices.combo.append_item(lang, lang)
- self.voices.combo.set_active(0)
- self.voices.combo.connect('changed', self._changed_cb)
- self.voices.show()
+def get_default_voice():
+ default_voice = voice.defaultVoice()
+ if default_voice.friendlyname not in BOTS:
+ return voice.allVoices()[_('English')]
+ else:
+ return default_voice
- self.insert(self.voices, -1)
- def _load_brain(self, voice, sorry=''):
- def load_brain(old_cursor):
- is_first_session = (len(self._kernels) == 0)
+def get_voices():
+ voices = ComboBox()
+ for lang in sorted(BOTS.keys()):
+ voices.append_item(voice.allVoices()[lang], lang)
+ return voices.get_model()
- try:
- brain = BOTS[voice]
- logger.debug('Load bot: %s' % brain)
- kernel = bot.aiml.Kernel()
- kernel.loadBrain(brain['brain'])
- for name, value in brain['predicates'].items():
- kernel.setBotPredicate(name, value)
- self._kernels[voice] = kernel
- finally:
- self.activity.set_cursor(old_cursor)
+def respond(voice, text):
+ return _kernels[voice].respond(text)
- if is_first_session:
- hello = _("Hello, I'am a robot \"%s\". Ask me any question.") \
- % BOTS[voice]['name']
- hello += ' ' + sorry
- self.activity.face.say_notification(hello)
- elif sorry:
- self.activity.face.say_notification(sorry)
-
- old_cursor = self.activity.get_cursor()
- self.activity.set_cursor(gtk.gdk.WATCH)
- gobject.idle_add(load_brain, old_cursor)
- def _changed_cb(self, combo):
- voice = combo.props.value
- self.activity.change_voice(voice, False)
- if not self._kernels.has_key(voice):
- self._load_brain(voice)
+def load(activity, voice, sorry=None):
+ if voice in _kernels:
+ return False
- def update_voice(self):
- voice = self.activity.voice_combo.props.value.friendlyname
- new_voice = BOTS.has_key(voice) and voice or DEFAULT.friendlyname
+ old_cursor = activity.get_cursor()
+ activity.set_cursor(gtk.gdk.WATCH)
- if voice != new_voice:
- self.activity.change_voice(new_voice, True)
+ def load_brain():
+ is_first_session = (len(_kernels) == 0)
try:
- self.voices.combo.handler_block_by_func(self._changed_cb)
- self.voices.combo.select(new_voice)
+ brain = BOTS[voice.friendlyname]
+ logger.debug('Load bot: %s' % brain)
+
+ kernel = bot.aiml.Kernel()
+ kernel.loadBrain(brain['brain'])
+ for name, value in brain['predicates'].items():
+ kernel.setBotPredicate(name, value)
+ _kernels[voice] = kernel
finally:
- self.voices.combo.handler_unblock_by_func(self._changed_cb)
+ activity.set_cursor(old_cursor)
- sorry = _("Sorry, I can speak %s, let's speak %s instead.") \
- % (voice, new_voice)
-
- if not self._kernels.has_key(new_voice):
- self._load_brain(new_voice, (voice != new_voice) and sorry or '')
- elif voice != new_voice:
- self.activity.face.say_notification(sorry)
+ if is_first_session:
+ hello = _("Hello, I'am a robot \"%s\". Ask me any question.") \
+ % BOTS[voice.friendlyname]['name']
+ if sorry:
+ hello += ' ' + sorry
+ activity.face.say_notification(hello)
+ elif sorry:
+ activity.face.say_notification(sorry)
- def respond(self, text):
- voice = self.voices.combo.props.value
- return self._kernels[voice].respond(text)
+ gobject.idle_add(load_brain)
+ return True
diff --git a/Speak.activity/chat.py b/Speak.activity/chat.py
index 0b132f1..251628e 100644
--- a/Speak.activity/chat.py
+++ b/Speak.activity/chat.py
@@ -43,23 +43,6 @@ ENTRY_COLOR = style.COLOR_PANEL_GREY
ENTRY_XPAD = 0
ENTRY_YPAD = 7
-class Toolbar(gtk.Toolbar):
- def __init__(self, chat):
- gtk.Toolbar.__init__(self)
- self.chat = chat
-
- mute = ToggleToolButton('stock_volume-mute')
- mute.set_tooltip(_('Mute'))
- mute.connect('toggled', self._toggled_cb)
- mute.show()
- self.insert(mute, -1)
-
- def _toggled_cb(self, widget):
- if widget.get_active():
- self.chat.quiet = True
- self.chat.shut_up()
- else:
- self.chat.quiet = False
class View(hippo.Canvas):
def __init__(self):
@@ -70,7 +53,6 @@ class View(hippo.Canvas):
self.quiet = False
self._buddies = {}
- self.connect('motion_notify_event', self._motion_notify_cb)
# buddies box
@@ -190,7 +172,7 @@ class View(hippo.Canvas):
)
buddy_face, buddy_widget = self._new_face(buddy, BUDDIES_COLOR)
-
+
char_box = hippo.CanvasBox(
orientation = hippo.ORIENTATION_VERTICAL,
)
@@ -268,12 +250,7 @@ class View(hippo.Canvas):
return (buddy_face, outer)
- def _look_at(self, x, y):
- self.me.look_at(x, y)
+ def look_at(self):
+ self.me.look_at()
for i in self._buddies.values():
- i['face'].look_at(x, y)
-
- def _motion_notify_cb(self, widget, event):
- display = gtk.gdk.display_get_default()
- screen, x, y, modifiers = display.get_pointer()
- self._look_at(x,y)
+ i['face'].look_at()
diff --git a/Speak.activity/eye.py b/Speak.activity/eye.py
index 1fe23f5..72f058e 100644
--- a/Speak.activity/eye.py
+++ b/Speak.activity/eye.py
@@ -43,17 +43,6 @@ class Eye(gtk.DrawingArea):
self.connect("button_press_event", self._mouse_pressed_cb)
self.connect("button_release_event", self._mouse_released_cb)
- # Instead of listening for mouse move events we could poll to see if the mouse has moved
- # would let us react to the mouse even when it isn't directly over this widget.
- # Unfortunately that would cause a lot of CPU usage. So instead we rely on our parent to
- # tell us to redraw when the mouse has moved. We still need to call add_events so that
- # our parent will get mouse motion events, but we don't connect the callback for them ourselves.
- self.add_events(gtk.gdk.POINTER_MOTION_MASK)
- # self.connect("motion_notify_event", self._mouse_moved_cb)
-
- def _mouse_moved_cb(self, widget, event):
- self.queue_draw()
-
def _mouse_pressed_cb(self, widget, event):
self.blink = True
self.queue_draw()
diff --git a/Speak.activity/face.py b/Speak.activity/face.py
index 5ad632c..a3b5d7b 100644
--- a/Speak.activity/face.py
+++ b/Speak.activity/face.py
@@ -110,7 +110,7 @@ class View(gtk.EventBox):
self._mouth = None
self._mouthbox = gtk.HBox()
self._mouthbox.show()
-
+
# layout the screen
box = gtk.VBox(homogeneous=False)
box.pack_start(self._eyebox)
@@ -119,14 +119,12 @@ class View(gtk.EventBox):
self.modify_bg(gtk.STATE_NORMAL, self.fill_color.get_gdk_color())
self.add(box)
- self._mapped = False
self._peding = None
- self.connect("map_event",self._map_event)
+ self.connect('map', self.__map_cb)
self.update()
-
- def _map_event(self, widget, event):
- self._mapped = True
+
+ def __map_cb(self, widget):
if self._peding:
self.update(self._peding)
self._peding = None
@@ -135,15 +133,20 @@ class View(gtk.EventBox):
if self._eyes:
map(lambda e: e.look_ahead(), self._eyes)
- def look_at(self, x, y):
+ def look_at(self, pos=None):
if self._eyes:
- map(lambda e, x=x, y=y: e.look_at(x,y), self._eyes)
+ if pos is None:
+ display = gtk.gdk.display_get_default()
+ screen_, x, y, modifiers_ = display.get_pointer()
+ else:
+ x, y = pos
+ map(lambda e, x=x, y=y: e.look_at(x, y), self._eyes)
def update(self, status = None):
if not status:
status = self.status
else:
- if not self._mapped:
+ if not self.flags() & gtk.MAPPED:
self._peding = status
return
self.status = status
@@ -169,6 +172,10 @@ class View(gtk.EventBox):
# enable mouse move events so we can track the eyes while the mouse is over the mouth
#self._mouth.add_events(gtk.gdk.POINTER_MOTION_MASK)
+ def set_voice(self, voice):
+ self.status.voice = voice
+ self.say_notification(voice.friendlyname)
+
def say(self, something):
self._audio.speak(self._peding or self.status, something)
diff --git a/Speak.activity/glasses.py b/Speak.activity/glasses.py
index fb64bbd..dd3daf5 100644
--- a/Speak.activity/glasses.py
+++ b/Speak.activity/glasses.py
@@ -29,11 +29,11 @@ class Glasses(Eye):
def expose(self, widget, event):
bounds = self.get_allocation()
-
+
eyeSize = min(bounds.width, bounds.height)
outlineWidth = eyeSize/20.0
pupilSize = eyeSize/10.0
- pupilX, pupilY = self.pupil_position()
+ pupilX, pupilY = self.computePupil()
dX = pupilX - bounds.width/2.
dY = pupilY - bounds.height/2.
distance = math.sqrt(dX*dX + dY*dY)
diff --git a/Speak.activity/icons/face.svg b/Speak.activity/icons/face.svg
new file mode 100644
index 0000000..6e2503f
--- /dev/null
+++ b/Speak.activity/icons/face.svg
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="50"
+ height="50"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="face.svg">
+ <metadata
+ id="metadata18">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs16">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 25 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="50 : 25 : 1"
+ inkscape:persp3d-origin="25 : 16.666667 : 1"
+ id="perspective20" />
+ </defs>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1278"
+ inkscape:window-height="735"
+ id="namedview14"
+ showgrid="false"
+ inkscape:zoom="5.22"
+ inkscape:cx="-8.5249042"
+ inkscape:cy="25"
+ inkscape:window-x="0"
+ inkscape:window-y="16"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <ellipse
+ fill="#FFFFFF"
+ stroke="#000000"
+ stroke-width="3.5"
+ cx="13"
+ cy="17"
+ rx="10"
+ ry="10"
+ id="ellipse4"
+ style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none" />
+ <ellipse
+ fill="#FFFFFF"
+ stroke="#000000"
+ stroke-width="3.5"
+ cx="37"
+ cy="17"
+ rx="10"
+ ry="10"
+ id="ellipse6"
+ style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none" />
+ <circle
+ fill="#FFFFFF"
+ stroke="#000000"
+ stroke-width="3.5"
+ cx="17"
+ cy="15"
+ r="2"
+ id="circle8" />
+ <circle
+ fill="#FFFFFF"
+ stroke="#000000"
+ stroke-width="3.5"
+ cx="41"
+ cy="15"
+ r="2"
+ id="circle10" />
+ <ellipse
+ fill="#FFFFFF"
+ stroke="#000000"
+ stroke-width="3.5"
+ cx="26"
+ cy="39"
+ rx="20"
+ ry="4"
+ id="ellipse12"
+ style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none" />
+</svg>
diff --git a/Speak.activity/icons/mode-chat.svg b/Speak.activity/icons/mode-chat.svg
new file mode 100644
index 0000000..9b38013
--- /dev/null
+++ b/Speak.activity/icons/mode-chat.svg
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="50"
+ height="50"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="mode-chat.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.1">
+ <metadata
+ id="metadata17">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs15">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 25 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="50 : 25 : 1"
+ inkscape:persp3d-origin="25 : 16.666667 : 1"
+ id="perspective19" />
+ <inkscape:perspective
+ id="perspective2475"
+ inkscape:persp3d-origin="27.5 : 18.333333 : 1"
+ inkscape:vp_z="55 : 27.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 27.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <sodipodi:namedview
+ inkscape:window-height="735"
+ inkscape:window-width="1278"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ id="base"
+ showgrid="false"
+ inkscape:zoom="2.33"
+ inkscape:cx="-48.243501"
+ inkscape:cy="20.573512"
+ inkscape:window-x="0"
+ inkscape:window-y="16"
+ inkscape:current-layer="svg2"
+ inkscape:window-maximized="0" />
+ <path
+ style="fill:#fff2f2;fill-opacity:1;stroke:#010101;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;display:inline"
+ d="m 7.7871373,45.936639 c 0.656039,1.243688 5.7975727,0.0637 7.9321037,-1.579441 2.021984,-1.545974 3.084921,-2.802618 4.170944,-2.802618 1.089871,0 12.062648,0.993223 20.138081,-6.169863 6.949967,-6.161226 9.400977,-14.8854 4.347936,-23.143191 C 39.324131,3.9891325 31.07074,2.4388395 24.292955,3.0002275 15.043002,3.7742935 2.9399533,11.944637 3.3757093,24.255214 c 0.253951,7.245135 3.229211,9.8707 4.734637,11.554862 1.502541,1.690639 4.3719877,1.690639 2.8781057,6.032755 -0.58678,1.704673 -3.6919007,3.150245 -3.2013147,4.093808 z"
+ display="inline"
+ id="path2468" />
+ <ellipse
+ style="fill:#ffffff;stroke:#010101;stroke-width:3.08741363;stroke-miterlimit:4;stroke-dasharray:none"
+ sodipodi:ry="10"
+ sodipodi:rx="10"
+ sodipodi:cy="12"
+ sodipodi:cx="12"
+ cx="12"
+ cy="12"
+ rx="10"
+ ry="10"
+ id="ellipse4"
+ transform="matrix(0.6477914,0,0,0.6477914,10.158787,8.6181926)" />
+ <ellipse
+ style="fill:#ffffff;stroke:#010101;stroke-width:3.08741363;stroke-miterlimit:4;stroke-dasharray:none"
+ sodipodi:ry="10"
+ sodipodi:rx="10"
+ sodipodi:cy="12"
+ sodipodi:cx="36"
+ cx="36"
+ cy="12"
+ rx="10"
+ ry="10"
+ id="ellipse6"
+ transform="matrix(0.6477914,0,0,0.6477914,10.158787,8.6181926)" />
+ <circle
+ style="fill:#ffffff;stroke:#010101;stroke-width:3.39615512;stroke-miterlimit:4;stroke-dasharray:none"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="10"
+ sodipodi:cx="16"
+ cx="16"
+ cy="10"
+ r="2"
+ id="circle8"
+ transform="matrix(0.6477914,0,0,0.6477914,9.7794379,8.6181926)" />
+ <circle
+ style="fill:#ffffff;stroke:#010101;stroke-width:3.39615512;stroke-miterlimit:4;stroke-dasharray:none"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="10"
+ sodipodi:cx="40"
+ cx="40"
+ cy="10"
+ r="2"
+ id="circle10"
+ transform="matrix(0.6477914,0,0,0.6477914,9.8553077,8.6940624)" />
+ <path
+ sodipodi:open="true"
+ sodipodi:end="2.7133165"
+ sodipodi:start="0.37458313"
+ transform="matrix(0.6789713,0,0,0.9654291,13.305764,57.674017)"
+ d="m 42.271638,-34.758097 a 27.896996,19.957081 0 0 1 -51.3400478,0.986253"
+ sodipodi:ry="19.957081"
+ sodipodi:rx="27.896996"
+ sodipodi:cy="-42.060085"
+ sodipodi:cx="16.309013"
+ id="path3304"
+ style="fill:none;stroke:#010101;stroke-width:2.47026812;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ sodipodi:type="arc" />
+</svg>
diff --git a/Speak.activity/icons/mode-robot.svg b/Speak.activity/icons/mode-robot.svg
new file mode 100644
index 0000000..420e922
--- /dev/null
+++ b/Speak.activity/icons/mode-robot.svg
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.0"
+ width="55"
+ height="55"
+ viewBox="0 0 50 50"
+ id="Layer_1"
+ xml:space="preserve"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="activity-x2o.svg"><metadata
+ id="metadata29"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1278"
+ inkscape:window-height="735"
+ id="namedview27"
+ showgrid="false"
+ inkscape:zoom="4.7454545"
+ inkscape:cx="-9.3773946"
+ inkscape:cy="27.5"
+ inkscape:window-x="0"
+ inkscape:window-y="16"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="Layer_1" /><defs
+ id="defs6"><inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 27.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="55 : 27.5 : 1"
+ inkscape:persp3d-origin="27.5 : 18.333333 : 1"
+ id="perspective31" /></defs>
+
+<path
+ d="M 16.723374,46.806292 C 16.855649,43.252225 16.987924,39.723192 17.120199,35.827457 C 18.481703,31.220865 14.052534,27.950574 12.887395,23.658144 C 11.742257,15.774242 14.12395,9.9649462 24.924432,9.1078802 C 31.587506,10.578246 39.012306,11.18182 35.770993,23.922695 C 33.371992,27.717361 29.197982,29.558066 29.421786,35.166081 C 29.131102,39.169447 28.433422,43.376312 27.437659,47.732218 C 24.508943,48.617185 22.012584,50.3051 16.723374,46.806292 z"
+ id="path3190"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 16.975898,44.822165 C 17.216398,44.902332 17.456899,44.982498 17.697399,45.062665 C 18.795738,45.428778 19.981952,45.513553 21.064402,45.784166 C 21.980159,46.013105 23.234291,45.784166 24.190905,45.784166 C 25.361118,45.784166 26.304322,45.626976 27.437659,45.303165 C 27.697639,45.228885 27.902688,45.22566 28.159159,45.182915"
+ id="path3198"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 17.133479,42.262299 C 17.446278,42.340499 18.01959,42.784415 18.493954,42.942537 C 19.169057,43.167571 19.817619,43.326596 20.492151,43.495229 C 21.270392,43.689789 22.112653,43.581905 22.872982,43.835348 C 23.350903,43.994655 24.015112,43.920378 24.531061,43.920378 C 25.193603,43.920378 25.919587,43.489619 26.529258,43.367685 C 27.078211,43.257894 27.52687,42.992633 28.059792,42.814992 C 28.4145,42.696756 28.384141,42.500073 28.655,42.432359"
+ id="path3208"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 17.04845,39.243746 C 17.281316,39.156421 18.824413,39.738142 19.174191,39.796439 C 21.305683,40.151687 23.560017,40.051528 25.721476,40.051528 C 26.543207,40.051528 28.142494,40.244009 28.867574,39.881468"
+ id="path3212"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 16.835875,36.480281 C 17.629072,36.480281 19.916025,37.250319 21.257418,37.585667 C 22.381947,37.866799 23.7113,37.415608 24.828665,37.415608 C 25.529754,37.415608 26.609622,37.21247 27.294525,37.075489 C 27.935027,36.947389 28.643047,36.97219 29.250208,36.8204"
+ id="path3214"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 18.536469,13.607299 C 18.431764,14.02612 17.651319,13.812211 17.346053,14.117477 C 15.831697,15.631833 15.846313,16.88531 15.390371,18.709079"
+ id="path3224"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 30.440623,14.202506 C 31.28182,15.6045 32.362472,14.492321 33.246603,18.028842 C 33.50682,19.069712 33.181971,20.263448 32.906484,21.08991"
+ id="path3232"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 19.726884,35.885074 C 19.736112,34.55356 19.556825,33.306212 19.556825,31.973709 C 19.556825,30.810705 19.488084,29.690611 19.301736,28.572522 C 19.167181,27.765193 19.046647,26.926717 19.046647,26.106661 C 19.046647,25.440348 18.706528,24.823394 18.706528,24.150979"
+ id="path3238"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 26.274169,34.09945 C 26.274169,34.042764 26.274169,33.986078 26.274169,33.929391 C 26.274169,32.646146 27.391115,30.652024 27.719674,29.337789 C 27.77636,29.111043 27.833047,28.884297 27.889733,28.657551 C 28.108768,27.781413 28.43707,27.052701 28.82506,26.27672 C 29.050142,25.826556 29.032745,25.360951 29.165178,24.831216 C 29.283218,24.359058 29.420267,24.188534 29.420267,23.72583"
+ id="path3252"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 20.662211,24.576127 C 22.797072,25.109842 24.88686,23.067902 25.338843,21.25997 C 25.569635,20.336803 25.260119,19.624725 24.318487,19.389317 C 23.614169,19.213237 23.492585,19.692489 23.38316,20.239614 C 23.190462,21.203106 23.569982,22.268338 23.893338,23.130623 C 24.189988,23.921688 24.21253,24.294549 24.998724,24.491097 C 25.602008,24.641918 26.15078,24.498609 26.614288,24.150979"
+ id="path3258"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 13.264629,29.762937 C 10.707218,28.739973 7.3550018,30.448514 5.1017805,31.293472"
+ id="path3268"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 9.0131455,23.640801 C 7.534669,23.04941 5.5299153,22.960563 3.911365,22.960563"
+ id="path3270"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 9.8634423,17.858783 C 7.4097472,16.877305 4.8810938,14.092164 2.5508902,12.927062"
+ id="path3272"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 15.64546,9.0156967 C 15.577739,8.9886084 14.270342,4.936068 14.114926,4.5941536"
+ id="path3276"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 20.067003,7.4851625 C 19.434985,7.2323553 20.407122,3.0721788 20.407122,1.873204"
+ id="path3278"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 27.379555,8.3354593 C 27.158812,8.2471618 28.229852,3.6895085 28.229852,2.7235007"
+ id="path3280"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 33.161573,9.5258747 C 32.98088,9.4535975 35.089459,5.8216307 35.372345,5.4444503"
+ id="path3282"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 37.072938,11.736646 C 35.91726,11.274375 39.824038,9.6778439 42.344778,8.1653999"
+ id="path3284"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 39.11365,16.498308 C 37.874839,16.002784 41.690775,15.648011 43.025016,15.648011"
+ id="path3286"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 37.242998,26.871928 C 38.346419,27.239736 38.801386,28.600377 39.623829,29.422819"
+ id="path3290"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 34.01187,30.443175 C 35.488246,30.9353 37.740219,33.713713 38.603472,34.864718"
+ id="path3292"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 11.223917,11.566587 C 10.939233,10.903218 8.5659634,8.8015698 7.8227301,7.3151032"
+ id="path3294"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
+ d="M 37.923235,21.089911 C 38.834054,20.372818 40.830076,21.430029 42.514837,21.430029"
+ id="path3296"
+ style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81818182;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /></svg> \ No newline at end of file
diff --git a/Speak.activity/icons/mode-type.svg b/Speak.activity/icons/mode-type.svg
new file mode 100644
index 0000000..ddf7dbc
--- /dev/null
+++ b/Speak.activity/icons/mode-type.svg
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.0"
+ width="55"
+ height="55"
+ viewBox="0 0 50 50"
+ id="Layer_1"
+ xml:space="preserve"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="mode-type.svg"><metadata
+ id="metadata29"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><sodipodi:namedview
+ pagecolor="#878787"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0.42352941"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1278"
+ inkscape:window-height="735"
+ id="namedview27"
+ showgrid="false"
+ inkscape:zoom="4.7454545"
+ inkscape:cx="-5.7247776"
+ inkscape:cy="38.193871"
+ inkscape:window-x="0"
+ inkscape:window-y="16"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="Layer_1"
+ inkscape:snap-grids="true" /><defs
+ id="defs6"><inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 27.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="55 : 27.5 : 1"
+ inkscape:persp3d-origin="27.5 : 18.333333 : 1"
+ id="perspective31" /><inkscape:perspective
+ id="perspective3618"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" /><inkscape:perspective
+ id="perspective3643"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" /><inkscape:perspective
+ id="perspective3668"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" /></defs>
+
+<text
+ xml:space="preserve"
+ style="font-size:23.4211235px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:URW Gothic L;-inkscape-font-specification:URW Gothic L Semi-Bold"
+ x="1.8150109"
+ y="36.040466"
+ id="text2834-5"
+ sodipodi:linespacing="125%"
+ transform="scale(0.94064439,1.063101)"><tspan
+ sodipodi:role="line"
+ id="tspan2836-48"
+ x="1.8150109"
+ y="36.040466"
+ style="font-size:23.4211235px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:URW Gothic L;-inkscape-font-specification:URW Gothic L Semi-Bold">ABC</tspan></text>
+</svg> \ No newline at end of file
diff --git a/Speak.activity/icons/voice.svg b/Speak.activity/icons/voice.svg
new file mode 100644
index 0000000..d32f370
--- /dev/null
+++ b/Speak.activity/icons/voice.svg
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ enable-background="new 0 0 55 55"
+ height="55px"
+ version="1.1"
+ viewBox="0 0 55 55"
+ width="55px"
+ x="0px"
+ xml:space="preserve"
+ y="0px"
+ id="svg2"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="voice.svg"><metadata
+ id="metadata17"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs15"><inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 27.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="55 : 27.5 : 1"
+ inkscape:persp3d-origin="27.5 : 18.333333 : 1"
+ id="perspective19" />
+
+
+</defs><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1278"
+ inkscape:window-height="735"
+ id="namedview13"
+ showgrid="false"
+ inkscape:zoom="4.7454545"
+ inkscape:cx="-9.3773946"
+ inkscape:cy="27.5"
+ inkscape:window-x="0"
+ inkscape:window-y="16"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" /><g
+ id="g5"
+ style="stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none">
+ <path
+ style="fill:#ffffff;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none"
+ id="path7"
+ d="m 48.143,36.983 c 0,3.3 -2.7,6 -6,6 H 12.705 c -3.3,0 -6,-2.7 -6,-6 v -18.77 c 0,-3.3 2.7,-6 6,-6 h 29.438 c 3.3,0 6,2.7 6,6 v 18.77 z" />
+ <path
+ style="fill:none;stroke:#010101;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
+ id="path9"
+ d="m 48.143,36.983 c 0,3.3 -2.7,6 -6,6 H 12.705 c -3.3,0 -6,-2.7 -6,-6 v -18.77 c 0,-3.3 2.7,-6 6,-6 h 29.438 c 3.3,0 6,2.7 6,6 v 18.77 z" />
+ </g><path
+ style="fill:#ffffff;stroke:#010101;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
+ id="path11"
+ d="m 8.007,27.598 c 10.118,0 6.243,9.431 11.213,9.431 4.375,0 2.154,-19.154 6.279,-19.154 4.875,0 1.719,19.154 6.719,19.154 4.406,0 1.781,-11.529 5.156,-11.529 3.375,0 2,4.875 4.25,4.875 2.375,0 0.25,-3.375 5.75,-3.375" /></svg> \ No newline at end of file
diff --git a/Speak.activity/port/COPYING b/Speak.activity/port/COPYING
deleted file mode 100644
index 623b625..0000000
--- a/Speak.activity/port/COPYING
+++ /dev/null
@@ -1,340 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
diff --git a/Speak.activity/port/README b/Speak.activity/port/README
deleted file mode 100644
index b6d738f..0000000
--- a/Speak.activity/port/README
+++ /dev/null
@@ -1,17 +0,0 @@
-About
------
-
-A set of sugar components/libraries/etc to simplify writing activities.
-
-Cornerstone purposes for this project:
-* Total backwards compatibility for sugar-port API
-* Run on all sugar platforms beginning from 0.82
-
-In most cases sugar-port could be embedded to activity's directory tree.
-There is no need to include the whole sugar-port project only the files
-you are using directly - sugar-port's files don't import each other.
-
-Get it
-------
-
-http://wiki.sugarlabs.org/go/Development_Team/sugar-port
diff --git a/Speak.activity/port/__init__.py b/Speak.activity/port/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/Speak.activity/port/__init__.py
+++ /dev/null
diff --git a/Speak.activity/port/activity.py b/Speak.activity/port/activity.py
deleted file mode 100644
index 0392fef..0000000
--- a/Speak.activity/port/activity.py
+++ /dev/null
@@ -1,317 +0,0 @@
-# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-"""Extend sugar-toolkit activity class"""
-
-import gtk
-import logging
-import telepathy
-import gobject
-
-import sugar.activity.activity as toolkit
-from sugar.presence.sugartubeconn import SugarTubeConnection
-from sugar.graphics.alert import ConfirmationAlert, NotifyAlert
-from sugar.activity import activity
-
-_NEW_INSTANCE = 0
-_NEW_INSTANCE = 1
-_PRE_INSTANCE = 2
-_POST_INSTANCE = 3
-
-class CursorFactory:
- __shared_state = {"cursors": {}}
-
- def __init__(self):
- self.__dict__ = self.__shared_state
-
- def get_cursor(self, cur_type):
- if not self.cursors.has_key(cur_type):
- cur = gtk.gdk.Cursor(cur_type)
- self.cursors[cur_type] = cur
- return self.cursors[cur_type]
-
-class Activity(toolkit.Activity):
- """Basic activity class"""
-
- def new_instance(self):
- """
- New instance was created.
-
- Will be invoked after __init__() instead of resume_instance().
- Subclass should implement this method to catch creation stage.
- """
- pass
-
- def resume_instance(self, filepath):
- """
- Instance was resumed.
-
- Will be invoked after __init__() instead of new_instance().
- Subclass should implement this method to catch resuming stage.
-
- """
- pass
-
- def save_instance(self, filepath):
- """
- Save activity instance.
-
- Subclass should implement this method to save activity data.
- """
- raise NotImplementedError
-
- def on_save_instance(self, cb, *args):
- """ Register callback which will be invoked before save_instance """
- self.__on_save_instance.append((cb, args))
-
- def share_instance(self, connection, is_initiator):
- """
- Activity was shared/joined.
-
- connection -- SugarTubeConnection object
- wich represents telepathy connection
-
- is_initiator -- boolean
- if True activity was shared and
- (current activity is an initiator of sharing)
- otherwise activity was joined(to existed sharing session)
-
- Will be invoked after __init__() and {new,resume}_instance().
- Subclass should implement this method to catch sharing stage.
- """
- pass
-
- def notify_alert(self, title, msg):
- """Raise standard notify alert"""
- alert = NotifyAlert(title=title, msg=msg)
-
- def response(alert, response_id, self):
- self.remove_alert(alert)
-
- alert.connect('response', response, self)
- alert.show_all()
- self.add_alert(alert)
-
- def confirmation_alert(self, title, msg, cb, *cb_args):
- """Raise standard confirmation alert"""
- alert = ConfirmationAlert(title=title, msg=msg)
-
- def response(alert, response_id, self, cb, *cb_args):
- self.remove_alert(alert)
- if response_id is gtk.RESPONSE_OK:
- cb(*cb_args)
-
- alert.connect('response', response, self, cb, *cb_args)
- alert.show_all()
- self.add_alert(alert)
-
- def get_cursor(self):
- return self._cursor
-
- def set_cursor(self, cursor):
- if not isinstance(cursor, gtk.gdk.Cursor):
- cursor = CursorFactory().get_cursor(cursor)
-
- if self._cursor != cursor:
- self._cursor = cursor
- self.window.set_cursor(self._cursor)
-
- def __init__(self, canvas, handle):
- """
- Initialise the Activity.
-
- canvas -- gtk.Widget
- root widget for activity content
-
- handle -- sugar.activity.activityhandle.ActivityHandle
- instance providing the activity id and access to the
- presence service which *may* provide sharing for this
- application
-
- """
- toolkit.Activity.__init__(self, handle)
-
- if handle.object_id:
- self.__state = _NEW_INSTANCE
- else:
- self.__state = _NEW_INSTANCE
-
- self.__resume_filename = None
- self.__postponed_share = []
- self.__on_save_instance = []
-
- self._cursor = None
- self.set_cursor(gtk.gdk.LEFT_PTR)
-
- # XXX do it after(possible) read_file() invoking
- # have to rely on calling read_file() from map_cb in sugar-toolkit
- canvas.connect_after('map', self.__map_canvasactivity_cb)
- self.set_canvas(canvas)
-
- def __instance(self):
- logging.debug('Activity.__instance')
-
- if self.__resume_filename:
- self.resume_instance(self.__resume_filename)
- else:
- self.new_instance()
-
- for i in self.__postponed_share:
- self.share_instance(*i)
- self.__postponed_share = []
-
- self.__state = _POST_INSTANCE
-
- def read_file(self, filepath):
- """Subclass should not override this method"""
- logging.debug('Activity.read_file state=%s' % self.__state)
-
- self.__resume_filename = filepath
-
- if self.__state == _NEW_INSTANCE:
- self.__state = _PRE_INSTANCE
- elif self.__state == _PRE_INSTANCE:
- self.__instance();
-
- def write_file(self, filepath):
- """Subclass should not override this method"""
- for cb, args in self.__on_save_instance:
- cb(*args)
- self.save_instance(filepath)
-
- def __map_canvasactivity_cb(self, widget):
- logging.debug('Activity.__map_canvasactivity_cb state=%s' % \
- self.__state)
-
- if self.__state == _NEW_INSTANCE:
- self.__instance()
- elif self.__state == _NEW_INSTANCE:
- self.__state = _PRE_INSTANCE
- elif self.__state == _PRE_INSTANCE:
- self.__instance();
-
- return False
-
- def _share(self, tube_conn, initiator):
- logging.debug('Activity._share state=%s' % self.__state)
-
- if self.__state == _NEW_INSTANCE:
- self.__postponed_share.append((tube_conn, initiator))
- self.__state = _PRE_INSTANCE
- elif self.__state == _PRE_INSTANCE:
- self.__postponed_share.append((tube_conn, initiator))
- self.__instance();
- elif self.__state == _POST_INSTANCE:
- self.share_instance(tube_conn, initiator)
-
-class SharedActivity(Activity):
- """Basic activity class with sharing features"""
-
- def __init__(self, canvas, service, handle):
- """
- Initialise the Activity.
-
- canvas -- gtk.Widget
- root widget for activity content
-
- service -- string
- dbus service for activity
-
- handle -- sugar.activity.activityhandle.ActivityHandle
- instance providing the activity id and access to the
- presence service which *may* provide sharing for this
- application
-
- """
- Activity.__init__(self, canvas, handle)
- self.service = service
-
- self.connect('shared', self._shared_cb)
-
- # Owner.props.key
- if self._shared_activity:
- # We are joining the activity
- self.connect('joined', self._joined_cb)
- if self.get_shared():
- # We've already joined
- self._joined_cb()
-
- def _shared_cb(self, activity):
- logging.debug('My activity was shared')
- self.__initiator = True
- self._sharing_setup()
-
- logging.debug('This is my activity: making a tube...')
- id = self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
- self.service, {})
-
- def _joined_cb(self, activity):
- if not self._shared_activity:
- return
-
- logging.debug('Joined an existing shared activity')
-
- self.__initiator = False
- self._sharing_setup()
-
- logging.debug('This is not my activity: waiting for a tube...')
- self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(
- reply_handler=self._list_tubes_reply_cb,
- error_handler=self._list_tubes_error_cb)
-
- def _sharing_setup(self):
- if self._shared_activity is None:
- logging.error('Failed to share or join activity')
- return
- self._conn = self._shared_activity.telepathy_conn
- self._tubes_chan = self._shared_activity.telepathy_tubes_chan
- self._text_chan = self._shared_activity.telepathy_text_chan
-
- self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
- 'NewTube', self._new_tube_cb)
-
- def _list_tubes_reply_cb(self, tubes):
- for tube_info in tubes:
- self._new_tube_cb(*tube_info)
-
- def _list_tubes_error_cb(self, e):
- logging.error('ListTubes() failed: %s', e)
-
- def _new_tube_cb(self, id, initiator, type, service, params, state):
- logging.debug('New tube: ID=%d initator=%d type=%d service=%s '
- 'params=%r state=%d', id, initiator, type, service,
- params, state)
-
- if (type == telepathy.TUBE_TYPE_DBUS and
- service == self.service):
- if state == telepathy.TUBE_STATE_LOCAL_PENDING:
- self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES] \
- .AcceptDBusTube(id)
-
- tube_conn = SugarTubeConnection(self._conn,
- self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id,
- group_iface=self._text_chan[telepathy.CHANNEL_INTERFACE_GROUP])
-
- self._share(tube_conn, self.__initiator)
-
-class ActivityToolbox(activity.ActivityToolbox):
- def __init__(self, activity_object):
- activity.ActivityToolbox.__init__(self, activity_object)
-
- def get_bars(self):
- nb = self._notebook
- return [nb.get_nth_page(i).get_child().get_child() \
- for i in range(nb.get_n_pages())]
-
- bars = property(get_bars)
diff --git a/Speak.activity/port/widgets.py b/Speak.activity/port/widgets.py
deleted file mode 100644
index 9531c97..0000000
--- a/Speak.activity/port/widgets.py
+++ /dev/null
@@ -1,403 +0,0 @@
-# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import gtk
-import hippo
-import gobject
-import logging
-import math
-
-from sugar.graphics import style
-from sugar.graphics import palette
-from sugar.graphics import toolbutton
-from sugar.graphics import radiotoolbutton
-from sugar.graphics import icon
-from sugar.graphics import toggletoolbutton
-from sugar.graphics import combobox
-from sugar.graphics import roundbox
-
-def labelize(text, widget):
- box = hippo.CanvasBox()
- box.props.spacing = style.DEFAULT_SPACING
-
- text = hippo.CanvasText(text=text)
- text.props.color = style.COLOR_SELECTION_GREY.get_int()
- if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL:
- text.props.xalign = hippo.ALIGNMENT_END
- else:
- text.props.xalign = hippo.ALIGNMENT_START
- box.append(text)
-
- box.append(widget, hippo.PACK_EXPAND)
-
- return box
-
-class Entry(hippo.CanvasWidget):
- def __init__(self, text=None, frame_color=style.COLOR_WHITE.get_gdk_color(),
- **kwargs):
- hippo.CanvasWidget.__init__(self, **kwargs)
-
- self.entry = gtk.Entry()
- self.entry.modify_bg(gtk.STATE_INSENSITIVE, frame_color)
-
- self.props.widget = self.entry
-
- if text:
- self.text = text
-
- def get_text(self):
- return self.entry.props.text
-
- def set_text(self, value):
- self.entry.props.text = value
-
- text = gobject.property(type=str, setter=set_text, getter=get_text)
- text = property(get_text, set_text)
-
-class TextView(hippo.CanvasWidget):
- def __init__(self, text=None, **kwargs):
- hippo.CanvasWidget.__init__(self, **kwargs)
-
- self.view = gtk.TextView()
- self.view.props.left_margin = style.DEFAULT_SPACING
- self.view.props.right_margin = style.DEFAULT_SPACING
- self.view.props.wrap_mode = gtk.WRAP_WORD
- self.view.props.accepts_tab = False
- self.view.show()
-
- scrolled_window = gtk.ScrolledWindow()
- scrolled_window.set_shadow_type(gtk.SHADOW_OUT)
- scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
- scrolled_window.add(self.view)
-
- self.props.widget = scrolled_window
-
- if text:
- self.text = text
-
- def get_text(self):
- return self.view.props.buffer.props.text
-
- def set_text(self, value):
- self.view.props.buffer.props.text = value or ''
-
- text = gobject.property(type=str, setter=set_text, getter=get_text)
- text = property(get_text, set_text)
-
-class Image(hippo.CanvasWidget):
- def __init__(self, pal=None, tooltip=None, pixbuf=None, **kwargs):
- self.image = gtk.Image()
- self._invoker = palette.CanvasInvoker()
-
- hippo.CanvasBox.__init__(self, widget=self.image)
-
- self._invoker._position_hint = self._invoker.AT_CURSOR
- self._invoker.attach(self)
-
- self.palette_class = None
-
- self.connect('destroy', self._destroy_cb)
-
- if pal:
- self.palette = pal
- if tooltip:
- self.tooltip = tooltip
- if pixbuf:
- self.pixbuf = pixbuf
-
- def _destroy_cb(self, widget):
- if self._invoker is not None:
- self._invoker.detach()
-
- def create_palette(self):
- if self.palette_class is None:
- return None
- if isinstance(self.palette_class, tuple):
- return self.palette_class[0](*self.palette_class[1:])
- else:
- return self.palette_class()
-
- def get_palette(self):
- return self._invoker.palette
-
- def set_palette(self, palette):
- self._invoker.palette = palette
-
- palette = gobject.property(type=object,
- setter=set_palette, getter=get_palette)
- palette = property(get_palette, set_palette)
-
- def get_tooltip(self):
- return self._invoker.palette and self._invoker.palette.primary_text
-
- def set_tooltip(self, text):
- self.set_palette(palette.Palette(text))
-
- tooltip = gobject.property(type=str, setter=set_tooltip, getter=get_tooltip)
- tooltip = property(get_tooltip, set_tooltip)
-
- def set_pixbuf(self, value):
- self.image.set_from_pixbuf(value)
- self.props.box_width = value.get_width()
- self.props.box_height = value.get_height()
-
- pixbuf = gobject.property(type=object, setter=set_pixbuf, getter=None)
- pixbuf = property(None, set_pixbuf)
-
-class ToolButton(toolbutton.ToolButton):
- def __init__(self,
- icon_name,
- size=gtk.ICON_SIZE_SMALL_TOOLBAR,
- padding=None,
- **kwargs):
-
- toolbutton.ToolButton.__init__(self, **kwargs)
-
- image = icon.Icon(icon_name=icon_name, icon_size=size)
- image.show()
-
- # The alignment is a hack to work around gtk.ToolButton code
- # that sets the icon_size when the icon_widget is a gtk.Image
- alignment = gtk.Alignment(0.5, 0.5)
- alignment.show()
- alignment.add(image)
-
- self.set_icon_widget(alignment)
-
- sizes = { gtk.ICON_SIZE_SMALL_TOOLBAR: style.SMALL_ICON_SIZE,
- gtk.ICON_SIZE_LARGE_TOOLBAR: style.STANDARD_ICON_SIZE }
-
- if padding is not None and sizes.has_key(size):
- button_size = sizes[size] + style.DEFAULT_SPACING + padding
- self.set_size_request(button_size, button_size)
-
-class RadioToolButton(radiotoolbutton.RadioToolButton):
- def __init__(self,
- icon_name,
- size=gtk.ICON_SIZE_SMALL_TOOLBAR,
- padding=None,
- **kwargs):
-
- radiotoolbutton.RadioToolButton.__init__(self, **kwargs)
-
- image = icon.Icon(icon_name=icon_name, icon_size=size)
- image.show()
-
- # The alignment is a hack to work around gtk.ToolButton code
- # that sets the icon_size when the icon_widget is a gtk.Image
- alignment = gtk.Alignment(0.5, 0.5)
- alignment.show()
- alignment.add(image)
-
- self.set_icon_widget(alignment)
-
- sizes = { gtk.ICON_SIZE_SMALL_TOOLBAR: style.SMALL_ICON_SIZE,
- gtk.ICON_SIZE_LARGE_TOOLBAR: style.STANDARD_ICON_SIZE }
-
- if padding is not None and sizes.has_key(size):
- button_size = sizes[size] + style.DEFAULT_SPACING + padding
- self.set_size_request(button_size, button_size)
-
-class ToolWidget(gtk.ToolItem):
- def __init__(self, widget):
- gtk.ToolItem.__init__(self)
- self.add(widget)
- widget.show()
-
-class ToggleToolButton(toggletoolbutton.ToggleToolButton):
- def __init__(self, named_icon=None, tooltip=None, palette=None, **kwargs):
- toggletoolbutton.ToggleToolButton.__init__(self, named_icon, **kwargs)
-
- if tooltip:
- self.set_tooltip(tooltip)
- if palette:
- self.set_palette(palette)
-
-class Palette(palette.Palette):
- def __init__(self, **kwargs):
- palette.Palette.__init__(self, **kwargs)
-
- def popup(self, immediate=False, state=None):
- if not self.props.invoker:
- if _none_invoker.palette:
- _none_invoker.palette.popdown(immediate=True)
- _none_invoker.palette = self
- self.props.invoker = _none_invoker
- palette.Palette.popup(self, immediate, state)
-
-class _NoneInvoker(palette.Invoker):
- def __init__(self):
- palette.Invoker.__init__(self)
- self._position_hint = palette.Invoker.AT_CURSOR
-
- def get_rect(self):
- return gtk.gdk.Rectangle(0, 0, 0, 0)
-
- def get_toplevel(self):
- return None
-
-_none_invoker = _NoneInvoker()
-
-class ComboBox(combobox.ComboBox):
- def __init__(self, **kwargs):
- combobox.ComboBox.__init__(self, **kwargs)
-
- def select(self, id=None, name=None):
- if id is not None:
- column = 0
- value = id
- elif name is not None:
- column = 1
- value = name
- else:
- return
-
- for i, item in enumerate(self.get_model()):
- if item[column] != value:
- continue
- self.set_active(i)
- break
-
- def append_item(self, action_id, text, icon_name=None, file_name=None):
- item = self._item_new(action_id, text, icon_name, file_name)
- self.get_model().append(item)
-
- def set_item(self, action_id, text=None, icon_name=None, file_name=None):
- for i, value in enumerate(self.get_model()):
- if value[0] == action_id:
- item = self._item_new(action_id, text, icon_name, file_name)
- iter = self.get_model().iter_nth_child(None, i)
- if text is not None:
- self.get_model().set(iter, 1, item[1])
- if icon_name is not None or file_name is not None:
- self.get_model().set(iter, 2, item[2])
- return True
- return False
-
- def _item_new(self, action_id, text, icon_name, file_name):
- if not self._icon_renderer and (icon_name or file_name):
- self._icon_renderer = gtk.CellRendererPixbuf()
-
- settings = self.get_settings()
- w, h = gtk.icon_size_lookup_for_settings(
- settings, gtk.ICON_SIZE_MENU)
- self._icon_renderer.props.stock_size = max(w, h)
-
- self.pack_start(self._icon_renderer, False)
- self.add_attribute(self._icon_renderer, 'pixbuf', 2)
-
- if not self._text_renderer and text:
- self._text_renderer = gtk.CellRendererText()
- self.pack_end(self._text_renderer, True)
- self.add_attribute(self._text_renderer, 'text', 1)
-
- if icon_name or file_name:
- if text:
- size = gtk.ICON_SIZE_MENU
- else:
- size = gtk.ICON_SIZE_LARGE_TOOLBAR
- width, height = gtk.icon_size_lookup(size)
-
- if icon_name:
- file_name = self._get_real_name_from_theme(icon_name, size)
-
- pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(
- file_name, width, height)
- else:
- pixbuf = None
-
- return (action_id, text, pixbuf, False)
-
-class ToolComboBox(gtk.ToolItem):
- __gproperties__ = {
- 'label-text' : (str, None, None, None,
- gobject.PARAM_WRITABLE),
- }
-
- def __init__(self, combo=None, **kwargs):
- self.label = None
- self._label_text = ''
-
- gobject.GObject.__init__(self, **kwargs)
-
- self.set_border_width(style.DEFAULT_PADDING)
-
- hbox = gtk.HBox(False, style.DEFAULT_SPACING)
-
- self.label = gtk.Label(self._label_text)
- hbox.pack_start(self.label, False)
- self.label.show()
-
- if combo:
- self.combo = combo
- else:
- self.combo = ComboBox()
-
- hbox.pack_start(self.combo)
- self.combo.show()
-
- self.add(hbox)
- hbox.show()
-
- def do_set_property(self, pspec, value):
- if pspec.name == 'label-text':
- self._label_text = value
- if self.label:
- self.label.set_text(self._label_text)
-
-class CanvasRoundBox(roundbox.CanvasRoundBox):
- def __init__(self, radius=style.zoom(10), **kwargs):
- hippo.CanvasBox.__init__(self, **kwargs)
- self.set_radius(radius)
-
- def get_radius(self):
- return self._radius
-
- def set_radius(self, radius):
- self._radius = radius
- self.props.border_left = radius
- self.props.border_right = radius
-
- radius = property(get_radius, set_radius)
-
- def do_paint_background(self, cr, damaged_box):
- [width, height] = self.get_allocation()
-
- cr.rectangle(0, 0, width, height)
- hippo.cairo_set_source_rgba32(cr, self.props.background_color)
- cr.fill()
-
- x = self.props.border_top/2
- y = self.props.border_top/2
- width -= self.props.border_top
- height -= self.props.border_top
-
- cr.move_to(x + self._radius, y)
- cr.arc(x + width - self._radius, y + self._radius,
- self._radius, math.pi * 1.5, math.pi * 2)
- cr.arc(x + width - self._radius, x + height - self._radius,
- self._radius, 0, math.pi * 0.5)
- cr.arc(x + self._radius, y + height - self._radius,
- self._radius, math.pi * 0.5, math.pi)
- cr.arc(x + self._radius, y + self._radius, self._radius,
- math.pi, math.pi * 1.5)
-
- hippo.cairo_set_source_rgba32(cr, self.props.background_color)
- cr.fill_preserve()
-
- # TODO: we should be more consistent here with the border properties.
- if self.props.border_color:
- hippo.cairo_set_source_rgba32(cr, self.props.border_color)
- cr.set_line_width(self.props.border_top)
- cr.stroke()
diff --git a/Speak.activity/widgets.py b/Speak.activity/widgets.py
deleted file mode 100644
index e20ae35..0000000
--- a/Speak.activity/widgets.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import logging
-
-from port.widgets import ComboBox
-
-import voice
-
-class Voices(ComboBox):
- def __init__(self, face, **kwargs):
- ComboBox.__init__(self, **kwargs)
-
- self.face = face
-
- voices = voice.allVoices()
- voicenames = voices.keys()
- voicenames.sort()
-
- for name in voicenames:
- self.append_item(voices[name], name)
-
- self.select(voice.defaultVoice())
-
- self.connect('changed', self._changed_cb)
-
- def _changed_cb(self, widget):
- self.face.status.voice = widget.props.value
- self.face.say_notification(self.face.status.voice.friendlyname)
-
- def resume(self, value):
- try:
- self.handler_block_by_func(self._changed_cb)
- self.select(name=value)
- self.face.status.voice = self.props.value
- finally:
- self.handler_unblock_by_func(self._changed_cb)