Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordave <drykod@gmail.com>2009-10-27 00:31:16 (GMT)
committer dave <drykod@gmail.com>2009-10-27 00:31:16 (GMT)
commitd3a006f11993435cf9a9e14b59ff626bfa09e7ba (patch)
tree8c66ad1a1e6138898d4be5e827d15f33d0b485d4
parent926238a2c54daae80d4c561b4cda8546d40173a7 (diff)
added new actions and events
-rw-r--r--addons/changecolor.py66
-rw-r--r--addons/intromessage.py161
-rw-r--r--addons/messagebuttonnext.py158
-rw-r--r--addons/openactivity.py180
-rw-r--r--addons/playsound.py50
-rw-r--r--addons/smiley.py82
-rw-r--r--data/icons/smiley.svg120
-rw-r--r--tutorius/overlayer.py83
8 files changed, 898 insertions, 2 deletions
diff --git a/addons/changecolor.py b/addons/changecolor.py
new file mode 100644
index 0000000..ae20e8a
--- /dev/null
+++ b/addons/changecolor.py
@@ -0,0 +1,66 @@
+# Copyright (C) 2009, Tutorius.org
+#
+# 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
+from sugar.tutorius.actions import Action
+
+class ChangeColor(Action):
+
+ def __init__(self, target, color, state):
+ """Constructor - Change a widget color
+ @param target: the widget for which you want to change the color
+ @param color: the color you want the widget to be colorized with \
+ (string)(ex: "#ffff00000000", or "red")
+ @param: state of the widget when you want a color change \
+ (ex: gtk.STATE_NORMAL)
+ @return : nothing!
+ """
+ Action.__init__(self)
+ self._target = target
+ self._state = state
+ #We have to get the initial color in the sugar rc theme
+ current_style = self._target.rc_get_style()
+
+ self._old_color = current_style.bg[self._state]
+ self._new_color = gtk.gdk.color_parse(color)
+
+ def do(self):
+ """Action do
+ Change the color of the targeted widget with the chosen color
+ """
+ self._target.modify_bg(state,self._new_color)
+
+ def undo(self):
+ """Action undo
+ Go back to the original color
+ """
+ self._target.modify_bg(state,self._old_color)
+
+ def enter_editmode(self, *args):
+ """
+ Enters edit mode. The action should display itself in some way,
+ without affecting the currently running application.
+ """
+ self._target.modify_bg(state,self._new_color)
+
+ def exit_editmode(self, *args):
+ self._target.modify_bg(state,self._old_color)
+
+__action__ = {
+ "name" : "ChangeColor",
+ "display_name" : "Change widget color",
+ "icon" : "message-bubble",
+ "class" : ChangeColor
+}
+
diff --git a/addons/intromessage.py b/addons/intromessage.py
new file mode 100644
index 0000000..f4f19a9
--- /dev/null
+++ b/addons/intromessage.py
@@ -0,0 +1,161 @@
+# Copyright (C) 2009, Tutorius.org
+#
+# 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
+
+import gobject
+
+import gtk, gtk.gdk
+
+from sugar.tutorius.filters import EventFilter
+from sugar.tutorius.properties import TStringProperty, TArrayProperty, TIntProperty
+from sugar.tutorius import overlayer
+from sugar.tutorius.services import ObjectStore
+
+from sugar import profile
+
+# for easy profile access
+xo_line_color = profile.get_color().get_stroke_color()
+xo_fill_color = profile.get_color().get_fill_color()
+
+class IntroMessage(EventFilter):
+ """
+ IntroMessage is a special EventFilter that uses gobject
+ start button to trigger a state change after user click on it.
+ It must be used inside a gobject main loop to work.
+ """
+ message = TStringProperty("Message")
+
+ def __init__(self, message=None):
+ """Constructor.
+
+ @param message message to display
+ """
+ super(IntroMessage,self).__init__()
+
+ if message:
+ self.message = message
+
+ self.overlay = None
+ self.msgnext = None
+
+ def install_handlers(self, callback, **kwargs):
+ """install_handlers creates the introduction message and shows it"""
+ super(IntroMessage,self).install_handlers(callback, **kwargs)
+
+ # get or inject overlayer
+ self.overlay = ObjectStore().activity._overlayer
+
+ if not self.overlay:
+ self.overlay = ObjectStore().activity._overlayer
+
+ self.msgnext = None
+
+ #Create the introduction message
+ if not self.msgnext:
+ # create an eventbox
+ self.msgnext = gtk.EventBox()
+ self.msgnext.set_visible_window(True)
+
+ # create a vbox
+ self.box = gtk.VBox()
+
+ # get position (center of screen)
+ screen = gtk.gdk.Screen()
+ scr_width_half = screen.get_width()/2 #self.overlayer.get_screen().get_width()/2
+ scr_height_half = screen.get_height()/2 #self.overlayer.get_screen().get_height()/2
+
+ # get user name
+ name = profile.get_nick_name()
+ if not name or not len(name):
+ name = "User"
+
+ # create a label
+ self._label = gtk.Label("Hello " + name + "!\n\n" + self.message)
+ self._text = "<b>%s</b>" % self.message
+ self._label.set_markup(self._text)
+ self._label.set_line_wrap(True)
+
+ self._colortext = gtk.gdk.color_parse("white")
+ self._label.modify_fg(gtk.STATE_NORMAL, self._colortext)
+ self._label.modify_fg(gtk.STATE_PRELIGHT, self._colortext)
+ self._label.modify_fg(gtk.STATE_ACTIVE, self._colortext)
+ self._label.modify_fg(gtk.STATE_INSENSITIVE, self._colortext)
+
+ self._label.show()
+
+ # create a hbox (holding button)
+ self._hbox = gtk.HBox()
+
+ # create a button inside hbox
+ self._btnnext = gtk.Button("START")
+ self._btnnext.connect("clicked", self.btnnext_clicked)
+
+ self._colorbtn = gtk.gdk.color_parse(xo_fill_color)
+
+ self._btnnext.modify_bg(gtk.STATE_NORMAL, self._colorbtn)
+ self._btnnext.modify_bg(gtk.STATE_PRELIGHT, self._colorbtn)
+ self._btnnext.modify_bg(gtk.STATE_ACTIVE, self._colorbtn)
+
+ self._btnnext.show()
+
+ self._hbox.pack_end(self._btnnext, expand=False)
+
+ self._hbox.show()
+
+ self.box.pack_start(self._label, expand=True)
+ self.box.pack_start(self._hbox, expand=True)
+
+ self.box.show()
+
+ self.msgnext.add(self.box)
+
+ self._colormsgnext = gtk.gdk.color_parse(xo_fill_color)
+ self.msgnext.modify_bg(gtk.STATE_NORMAL, self._colormsgnext)
+
+ # add space around minimum need size
+ wid_width, wid_height = self.msgnext.size_request()
+ self.msgnext.set_size_request(wid_width+40, wid_height+40)
+
+ self.msgnext.show()
+
+ # set position
+ wid_width, wid_height = self.msgnext.size_request()
+ self.position = (scr_width_half-wid_width/2, scr_height_half-wid_height/2)
+ x, y = self.position
+
+ self.overlay.put(self.msgnext, x, y)
+
+ self.overlay.queue_draw()
+
+ def remove_handlers(self):
+ """remove handler removes the introduction message"""
+ super(IntroMessage,self).remove_handlers()
+
+ if self.msgnext:
+ self.msgnext.destroy()
+ self.msgnext = None
+
+ def btnnext_clicked(self, widget):
+ self.do_callback()
+
+__event__ = {
+ "name" : "IntroMessage",
+ "display_name" : "Introduction message",
+ "icon" : "message-bubble",
+ "class" : IntroMessage,
+ "mandatory_props" : ["message"]
+}
diff --git a/addons/messagebuttonnext.py b/addons/messagebuttonnext.py
new file mode 100644
index 0000000..ed6dd9d
--- /dev/null
+++ b/addons/messagebuttonnext.py
@@ -0,0 +1,158 @@
+# Copyright (C) 2009, Tutorius.org
+#
+# 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
+
+import gobject
+
+import gtk, gtk.gdk
+
+from sugar.tutorius.filters import EventFilter
+from sugar.tutorius.properties import TStringProperty, TArrayProperty, TIntProperty
+from sugar.tutorius import overlayer
+from sugar.tutorius.services import ObjectStore
+
+from sugar import profile
+
+# for easy profile access
+xo_line_color = profile.get_color().get_stroke_color()
+xo_fill_color = profile.get_color().get_fill_color()
+
+class MessageButtonNext(EventFilter):
+ """
+ MessageButtonNext is a special EventFilter that uses gobject
+ next button to trigger a state change after user click on it.
+ It must be used inside a gobject main loop to work.
+ """
+ message = TStringProperty("Message")
+
+ # Create the position as an array of fixed-size 2
+ position = TArrayProperty((0,0), 2, 2)
+
+ def __init__(self, message=None, position=None):
+ """Constructor.
+
+ @param message message to display
+ @param position message position
+ """
+ super(MessageButtonNext,self).__init__()
+
+ if position:
+ self.position = position
+ else:
+ self.position = (300, 200)
+
+ if message:
+ self.message = message
+
+ self.overlay = None
+ self.msgnext = None
+
+ def install_handlers(self, callback, **kwargs):
+ """install_handlers creates the message button next and shows it"""
+ super(MessageButtonNext,self).install_handlers(callback, **kwargs)
+
+ x, y = self.position
+
+ # get or inject overlayer
+ self.overlay = ObjectStore().activity._overlayer
+
+ if not self.overlay:
+ self.overlay = ObjectStore().activity._overlayer
+
+ self.msgnext = None
+
+ #Create the message button next
+ if not self.msgnext:
+ # create an eventbox
+ self.msgnext = gtk.EventBox()
+ self.msgnext.set_visible_window(True)
+
+ # create a vbox
+ self.box = gtk.VBox()
+
+ # create a label
+ self._label = gtk.Label(self.message)
+ self._text = "<b>%s</b>" % self.message
+ self._label.set_markup(self._text)
+ self._label.set_line_wrap(True)
+
+ self._colortext = gtk.gdk.color_parse("white")
+ self._label.modify_fg(gtk.STATE_NORMAL, self._colortext)
+ self._label.modify_fg(gtk.STATE_PRELIGHT, self._colortext)
+ self._label.modify_fg(gtk.STATE_ACTIVE, self._colortext)
+ self._label.modify_fg(gtk.STATE_INSENSITIVE, self._colortext)
+
+ self._label.show()
+
+ # create a hbox (holding button)
+ self._hbox = gtk.HBox()
+
+ # create a button inside hbox
+ self._btnnext = gtk.Button("NEXT")
+ self._btnnext.connect("clicked", self.btnnext_clicked)
+
+ self._colorbtn = gtk.gdk.color_parse(xo_fill_color)
+
+ self._btnnext.modify_bg(gtk.STATE_NORMAL, self._colorbtn)
+ self._btnnext.modify_bg(gtk.STATE_PRELIGHT, self._colorbtn)
+ self._btnnext.modify_bg(gtk.STATE_ACTIVE, self._colorbtn)
+
+ self._btnnext.show()
+
+ self._hbox.pack_end(self._btnnext, expand=False)
+
+ self._hbox.show()
+
+ self.box.pack_start(self._label, expand=True)
+ self.box.pack_start(self._hbox, expand=True)
+
+ self.box.show()
+
+ self.msgnext.add(self.box)
+
+ self._colormsgnext = gtk.gdk.color_parse(xo_fill_color)
+ self.msgnext.modify_bg(gtk.STATE_NORMAL, self._colormsgnext)
+
+ # add space around minimum need size
+ wid_width, wid_height = self.msgnext.size_request()
+
+ self.msgnext.set_size_request(wid_width+40, wid_height+40)
+
+ self.msgnext.show()
+
+ self.overlay.put(self.msgnext, x, y)
+
+ self.overlay.queue_draw()
+
+ def remove_handlers(self):
+ """remove handler removes the message button next"""
+ super(MessageButtonNext,self).remove_handlers()
+
+ if self.msgnext:
+ self.msgnext.destroy()
+ self.msgnext = None
+
+ def btnnext_clicked(self, widget):
+ self.do_callback()
+
+__event__ = {
+ "name" : "MessageButtonNext",
+ "display_name" : "Message button next",
+ "icon" : "message-bubble",
+ "class" : MessageButtonNext,
+ "mandatory_props" : ["message"]
+}
diff --git a/addons/openactivity.py b/addons/openactivity.py
new file mode 100644
index 0000000..1b4d4b7
--- /dev/null
+++ b/addons/openactivity.py
@@ -0,0 +1,180 @@
+# Copyright (C) 2009, Tutorius.org
+#
+# 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
+
+import gobject
+
+import gtk, gtk.gdk
+
+import os
+import dbus
+
+from sugar.tutorius.filters import EventFilter
+from sugar.tutorius.properties import TStringProperty, TArrayProperty, TIntProperty
+from sugar.tutorius import overlayer
+from sugar.tutorius.services import ObjectStore
+
+from sugar import profile
+
+from sugar.activity import activityfactory
+from sugar.bundle.activitybundle import ActivityBundle
+
+# for easy profile access
+xo_line_color = profile.get_color().get_stroke_color()
+xo_fill_color = profile.get_color().get_fill_color()
+
+class OpenActivity(EventFilter):
+ """
+ IntroMessage is a special EventFilter that uses gobject
+ start button to trigger a state change after user click on it.
+ It must be used inside a gobject main loop to work.
+ """
+ message = TStringProperty("Message")
+
+ def __init__(self, message=None):
+ """Constructor.
+
+ @param message message to display
+ """
+ super(OpenActivity,self).__init__()
+
+ if message:
+ self.message = message
+
+ self.overlay = None
+ self.msgnext = None
+
+ def install_handlers(self, callback, **kwargs):
+ """install_handlers creates the open activity message and shows it"""
+ super(OpenActivity,self).install_handlers(callback, **kwargs)
+
+ # get or inject overlayer
+ self.overlay = ObjectStore().activity._overlayer
+
+ if not self.overlay:
+ self.overlay = ObjectStore().activity._overlayer
+
+ self.msgnext = None
+
+ #Create the open activity message
+ if not self.msgnext:
+ # create an eventbox
+ self.msgnext = gtk.EventBox()
+ self.msgnext.set_visible_window(True)
+
+ # create a vbox
+ self.box = gtk.VBox()
+
+ # get position (center of screen)
+ screen = gtk.gdk.Screen()
+ scr_width_half = screen.get_width()/2 #self.overlayer.get_screen().get_width()/2
+ scr_height_half = screen.get_height()/2 #self.overlayer.get_screen().get_height()/2
+
+ # get user name
+ name = profile.get_nick_name()
+ if not name or not len(name):
+ name = "User"
+
+ # create a label
+ self._label = gtk.Label("Hello " + name + "!\n\n" + self.message)
+ self._text = "<b>%s</b>" % self.message
+ self._label.set_markup(self._text)
+ self._label.set_line_wrap(True)
+
+ self._colortext = gtk.gdk.color_parse("white")
+ self._label.modify_fg(gtk.STATE_NORMAL, self._colortext)
+ self._label.modify_fg(gtk.STATE_PRELIGHT, self._colortext)
+ self._label.modify_fg(gtk.STATE_ACTIVE, self._colortext)
+ self._label.modify_fg(gtk.STATE_INSENSITIVE, self._colortext)
+
+ self._label.show()
+
+ # create a hbox (holding button)
+ self._hbox = gtk.HBox()
+
+ # create a button inside hbox
+ self._btnnext = gtk.Button("OPEN")
+ self._btnnext.connect("clicked", self.btnnext_clicked)
+
+ self._colorbtn = gtk.gdk.color_parse(xo_fill_color)
+
+ self._btnnext.modify_bg(gtk.STATE_NORMAL, self._colorbtn)
+ self._btnnext.modify_bg(gtk.STATE_PRELIGHT, self._colorbtn)
+ self._btnnext.modify_bg(gtk.STATE_ACTIVE, self._colorbtn)
+
+ self._btnnext.show()
+
+ self._hbox.pack_end(self._btnnext, expand=False)
+
+ self._hbox.show()
+
+ self.box.pack_start(self._label, expand=True)
+ self.box.pack_start(self._hbox, expand=True)
+
+ self.box.show()
+
+ self.msgnext.add(self.box)
+
+ self._colormsgnext = gtk.gdk.color_parse(xo_fill_color)
+ self.msgnext.modify_bg(gtk.STATE_NORMAL, self._colormsgnext)
+
+ # add space around minimum need size
+ wid_width, wid_height = self.msgnext.size_request()
+ self.msgnext.set_size_request(wid_width+40, wid_height+40)
+
+ self.msgnext.show()
+
+ # set position
+ wid_width, wid_height = self.msgnext.size_request()
+ self.position = (scr_width_half-wid_width/2, scr_height_half-wid_height/2)
+ x, y = self.position
+
+ self.overlay.put(self.msgnext, x, y)
+
+ self.overlay.queue_draw()
+
+ def remove_handlers(self):
+ """remove handler removes the open activity message"""
+ super(OpenActivity,self).remove_handlers()
+
+ if self.msgnext:
+ self.msgnext.destroy()
+ self.msgnext = None
+
+ def btnnext_clicked(self, widget):
+ # temporary activity name hardcoded
+ activity_name = "org.laptop.Terminal"
+
+ bus = dbus.SessionBus()
+ proxy = bus.get_object('org.laptop.Shell', '/org/laptop/Shell')
+ path = dbus.Interface(proxy, 'org.laptop.Shell').GetBundlePath(activity_name)
+ if not path:
+ print 'Cannot find %s bundle.' % activity_name
+ else:
+ activity = ActivityBundle(path)
+ activityfactory.create(activity)
+
+ self.do_callback()
+
+__event__ = {
+ "name" : "OpenActivity",
+ "display_name" : "Open activity message",
+ "icon" : "message-bubble",
+ "class" : OpenActivity,
+ "mandatory_props" : ["message"]
+}
+
diff --git a/addons/playsound.py b/addons/playsound.py
new file mode 100644
index 0000000..b657c8a
--- /dev/null
+++ b/addons/playsound.py
@@ -0,0 +1,50 @@
+# Copyright (C) 2009, Tutorius.org
+#
+# 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
+from sugar.tutorius.actions import Action
+
+class PlaySound(Action):
+
+ def __init__(self, target):
+ """Constructor
+ @param target: name of the sound you want to play
+ """
+ Action.__init__(self)
+ self._target = target
+
+ def do(self):
+ """Action do
+ Play a sound in the mixer
+ """
+ pygame.mixer.init()
+ #We wait for a "place" in the mixer
+ if not pygame.mixer.music.get_busy():
+ pygame.mixer.music.load(self._target)
+ pygame.mixer.music.play()
+
+ def undo(self):
+ """Action undo
+ Quit the mixer
+ """
+ pygame.mixer.music.stop()
+ pygame.mixer.quit()
+
+__action__ = {
+ "name" : "PlaySound",
+ "display_name" : "Play sound",
+ "icon" : "message-bubble",
+ "class" : PlaySound
+}
+
diff --git a/addons/smiley.py b/addons/smiley.py
new file mode 100644
index 0000000..b50de0c
--- /dev/null
+++ b/addons/smiley.py
@@ -0,0 +1,82 @@
+# Copyright (C) 2009, Tutorius.org
+#
+# 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
+from sugar.tutorius.actions import Action
+from sugar.tutorius import overlayer
+from sugar.tutorius.services import ObjectStore
+
+class Smiley(Action):
+
+ def __init__(self):
+ """
+ Draw a big smiley at the center of the screen
+ """
+ Action.__init__(self)
+
+ self.overlay = None
+ self._smiley = None
+
+ def do(self):
+ """
+ Do
+ """
+ # get or inject overlayer
+ self.overlay = ObjectStore().activity._overlayer
+ # FIXME: subwindows, are left to overlap this. This behaviour is
+ # undesirable. subwindows (i.e. child of top level windows) should be
+ # handled either by rendering over them, or by finding different way to
+ # draw the overlay.
+
+ if not self.overlay:
+ self.overlay = ObjectStore().activity._overlayer
+ if not self._smiley:
+ self._smiley = overlayer.Smiley()
+ self._smiley.show()
+ self.overlay.put(self._smiley, 0, 0)
+ self.overlay.queue_draw()
+
+ def undo(self):
+ """
+ Undo
+ """
+ if self._smiley:
+ self._smiley.destroy()
+ self._smiley = None
+
+ def enter_editmode(self, *args):
+ """
+ Enters edit mode. The action should display itself in some way,
+ without affecting the currently running application.
+ """
+ if not self.overlay:
+ self.overlay = ObjectStore().activity._overlayer
+
+ self._smiley = overlayer.Smiley()
+ self.overlay.put(self._smiley, 0, 0)
+ self._smiley.show()
+
+ def exit_editmode(self, *args):
+ if self._smiley:
+ self.overlay.remove(self._smiley)
+ self._smiley = None
+ self.overlay = None
+
+__action__ = {
+ "name" : "Smiley",
+ "display_name" : "Big smiley",
+ "icon" : "message-bubble",
+ "class" : Smiley
+}
+
diff --git a/data/icons/smiley.svg b/data/icons/smiley.svg
new file mode 100644
index 0000000..9756352
--- /dev/null
+++ b/data/icons/smiley.svg
@@ -0,0 +1,120 @@
+<?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"
+ height="55px"
+ id="svg2383"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:version="0.46"
+ sodipodi:docname="smiley.svg"
+ sodipodi:version="0.32"
+ version="1.1"
+ width="55px">
+ <defs
+ id="defs2385">
+ <inkscape:perspective
+ id="perspective2391"
+ inkscape:persp3d-origin="32 : 21.333333 : 1"
+ inkscape:vp_x="0 : 32 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="64 : 32 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ id="base"
+ inkscape:current-layer="layer1"
+ inkscape:cx="36.007299"
+ inkscape:cy="32"
+ inkscape:document-units="px"
+ inkscape:grid-bbox="true"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:window-height="726"
+ inkscape:window-maximized="0"
+ inkscape:window-width="897"
+ inkscape:window-x="145"
+ inkscape:window-y="31"
+ inkscape:zoom="5.5"
+ pagecolor="#ffffff"
+ showgrid="true" />
+ <metadata
+ id="metadata2388">
+ <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>
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1"
+ transform="translate(0,-9)">
+ <path
+ d="m 18.420265,15.588046 c -6.299941,0 -11.3606278,5.091225 -11.3606278,11.391168 l 0,8.52047 c 0,6.299941 5.0606868,11.360627 11.3606278,11.360628 l 4.306045,0 c -3.990137,3.853923 -9.100719,8.55101 -9.100719,8.55101 l 14.90319,-8.55101 8.215077,0 c 6.299941,0 11.360629,-5.060688 11.360628,-11.360628 l 0,-8.52047 c 0,-6.299941 -5.060686,-11.391168 -11.360628,-11.391168 l -18.323593,0 z"
+ id="rect2393"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.93177485;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ d="m 15.024328,38.638614 c 1.955367,-5.091599 3.910736,-10.183195 5.866104,-15.274795 0.725882,0 1.451765,0 2.177647,0 2.083874,5.0916 4.167748,10.183196 6.251621,15.274795 -0.767559,0 -1.535119,0 -2.302681,0 -0.593902,-1.542066 -1.187806,-3.084133 -1.781711,-4.6262 -2.129023,0 -4.258049,0 -6.387073,0 -0.559173,1.542067 -1.118345,3.084134 -1.677518,4.6262 -0.715463,0 -1.430926,0 -2.146389,0 z m 4.407394,-6.272461 c 1.726141,0 3.452282,0 5.178425,0 -0.902841,-2.462093 -1.914567,-4.887008 -2.677778,-7.397751 -0.52677,2.55814 -1.634085,4.944943 -2.500647,7.397751 z m 18.504796,4.907524 c -1.838977,1.733981 -5.157492,2.370029 -7.106335,0.497687 -1.652486,-1.767667 -0.397833,-4.8651 1.93833,-5.228082 1.582053,-0.524808 3.967388,-0.117044 5.019523,-1.242503 0.357796,-2.140519 -2.188156,-2.803677 -3.81609,-2.30789 -1.611203,-0.05002 -1.152189,2.566754 -2.696647,1.856331 -1.660075,0.218606 -0.318477,-1.770333 0.269586,-2.358415 1.812428,-1.412953 4.486925,-1.487977 6.565502,-0.656418 1.932898,0.888601 1.522618,3.214011 1.566166,4.96126 0.09528,1.950742 -0.314033,4.039001 0.593905,5.842967 -0.652949,0 -1.305894,0 -1.958842,0 C 38.099047,38.213411 37.986767,37.7444 37.936518,37.273677 z M 37.780227,33.08509 c -1.623868,0.756746 -3.573656,0.44341 -5.123725,1.341496 -1.689943,1.542703 0.669958,3.650795 2.433084,2.927027 1.729527,-0.198499 2.900173,-1.892395 2.690641,-3.580843 0,-0.229227 0,-0.458454 0,-0.68768 z"
+ id="flowRoot3171"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Arial;-inkscape-font-specification:Arial" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="path2426"
+ sodipodi:cx="26.181818"
+ sodipodi:cy="26.636364"
+ sodipodi:rx="23.272728"
+ sodipodi:ry="22.363636"
+ d="M 49.454546,26.636364 A 23.272728,22.363636 0 1 1 2.90909,26.636364 A 23.272728,22.363636 0 1 1 49.454546,26.636364 z"
+ transform="matrix(1.0988775,0,0,1.1516775,-0.9524328,5.8689525)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="path3207"
+ sodipodi:cx="18.727272"
+ sodipodi:cy="17.09091"
+ sodipodi:rx="3.090909"
+ sodipodi:ry="3"
+ d="M 21.818181,17.09091 A 3.090909,3 0 1 1 15.636363,17.09091 A 3.090909,3 0 1 1 21.818181,17.09091 z"
+ transform="translate(0,9)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="path3209"
+ sodipodi:cx="18.727272"
+ sodipodi:cy="17.09091"
+ sodipodi:rx="3.090909"
+ sodipodi:ry="3"
+ d="M 21.818181,17.09091 A 3.090909,3 0 1 1 15.636363,17.09091 A 3.090909,3 0 1 1 21.818181,17.09091 z"
+ transform="translate(16.363637,8.7272722)" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;fill:#ffff00;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path3211"
+ sodipodi:cx="27.363636"
+ sodipodi:cy="35.363636"
+ sodipodi:rx="10.272727"
+ sodipodi:ry="10"
+ d="M 37.483395,33.644345 A 10.272727,10 0 1 1 17.259675,33.558445"
+ transform="translate(0,9)"
+ sodipodi:start="6.1103977"
+ sodipodi:end="9.6062922"
+ sodipodi:open="true" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="eyes" />
+</svg>
diff --git a/tutorius/overlayer.py b/tutorius/overlayer.py
index 0a3d542..7ebe347 100644
--- a/tutorius/overlayer.py
+++ b/tutorius/overlayer.py
@@ -58,7 +58,7 @@ class Overlayer(gtk.Layout):
@param overlayed widget to be overlayed. Will be resized to full size.
"""
def __init__(self, overlayed=None):
- gtk.Layout.__init__(self)
+ super(Overlayer, self).__init__()
self._overlayed = overlayed
if overlayed:
@@ -83,7 +83,7 @@ class Overlayer(gtk.Layout):
if hasattr(child, "draw_with_context"):
# if the widget has the CanvasDrawable protocol, use it.
child.no_expose = True
- gtk.Layout.put(self, child, x, y)
+ super(Overlayer, self).put(child, x, y)
# be sure to redraw or the overlay may not show
self.queue_draw()
@@ -151,6 +151,85 @@ class Overlayer(gtk.Layout):
# some cases.
self._overlayed.set_size_request(allocation.width, allocation.height)
+class Smiley(gtk.Widget):
+ """
+ Draw a smiley
+ """
+ def __init__(self):
+ """
+ Creates a new Smiley
+ """
+ gtk.Widget.__init__(self)
+
+ self.__realizer = self.connect_after("realize", self.do_realize_init)
+ self.connect("size-allocate", self.do_size_allocate_init)
+
+ def draw_with_context(self, context):
+ # DRAW SMILEY
+
+ scr_width_half = self.get_screen().get_width()/2
+ scr_height_half = self.get_screen().get_height()/2
+
+ # face
+ context.set_source_rgb(1.0, 1.0, 0.0) # rgb
+ context.arc(scr_width_half, scr_height_half, 150, 0, 2 * 3.14159)
+ context.fill()
+
+ # face stroke
+ context.set_source_rgb(0.0, 0.0, 0.0) # rgb
+ context.arc(scr_width_half, scr_height_half, 150, 0, 2 * 3.14159)
+ context.set_line_width(10)
+ context.stroke()
+
+ # eye
+ context.set_source_rgb(0.0, 0.0, 0.0) # rgb
+ context.arc(scr_width_half-50, scr_height_half-40, 25, 0, 2 * 3.14159)
+ context.fill()
+
+ # eye
+ context.set_source_rgb(0.0, 0.0, 0.0) # rgb
+ context.arc(scr_width_half+50, scr_height_half-40, 25, 0, 2 * 3.14159)
+ context.fill()
+
+ # mouth
+ context.set_source_rgb(0.0, 0.0, 0.0) # rgb
+ context.arc(scr_width_half, scr_height_half+40, 60, 0, 3.14159)
+ context.set_line_width(10)
+ context.stroke()
+
+ # END OF DRAW SMILEY
+
+ def do_realize(self):
+ """ Setup gdk window creation. """
+ self.set_flags(gtk.REALIZED | gtk.NO_WINDOW)
+
+ self.window = self.get_parent_window()
+ if not isinstance(self.parent, Overlayer):
+ assert False, "%s should not realize" % type(self).__name__
+ print "Danger, Will Robinson! Rectangle parent is not Overlayer"
+
+ def do_realize_init(self, widget):
+ """ Setup gdk window creation. """
+ self.set_flags(gtk.REALIZED | gtk.NO_WINDOW)
+
+ self.window = self.get_parent_window()
+ if not isinstance(self.parent, Overlayer):
+ assert False, "%s should not realize" % type(self).__name__
+ print "Danger, Will Robinson! Rectangle parent is not Overlayer"
+
+ ctx = self.window.cairo_create()
+
+ self.draw_with_context(ctx)
+
+ def do_size_allocate(self, allocation):
+ """Save zone allocated to the widget."""
+ self.allocation = allocation
+
+ def do_size_allocate_init(self, widget, allocation):
+ """Save zone allocated to the widget."""
+ self.allocation = allocation
+
+gobject.type_register(Smiley)
class TextBubble(gtk.Widget):
"""