Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWalter Bender <walter.bender@gmail.com>2011-04-19 13:02:57 (GMT)
committer Walter Bender <walter.bender@gmail.com>2011-04-19 13:02:57 (GMT)
commit2ac562c0663bd22865eca19933eec9ec2321b7bd (patch)
tree7a59ef057152e9668bf1b235608881bd152e4efb
new project
-rw-r--r--NEWS4
-rw-r--r--NapierActivity.py412
-rw-r--r--activity/activity-napier.svg37
-rw-r--r--activity/activity.info8
-rw-r--r--bones/blank-bone.svg139
-rw-r--r--bones/bones-index.svg139
-rw-r--r--bones/circle.svg32
-rw-r--r--bones/oval.svg38
-rw-r--r--icons/bones.svg45
-rw-r--r--icons/new-game.svg19
-rw-r--r--icons/number-0.svg31
-rw-r--r--icons/number-1.svg31
-rw-r--r--icons/number-2.svg31
-rw-r--r--icons/number-3.svg31
-rw-r--r--icons/number-4.svg31
-rw-r--r--icons/number-5.svg31
-rw-r--r--icons/number-6.svg31
-rw-r--r--icons/number-7.svg31
-rw-r--r--icons/number-8.svg31
-rw-r--r--icons/number-9.svg31
-rw-r--r--po/Napier.pot78
-rw-r--r--sprites.py453
22 files changed, 1714 insertions, 0 deletions
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..5c11c5e
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,4 @@
+1
+
+Napier's bones were invented by John Napier (1550-1617), a Scottish
+mathematician and scientist. They help you to do multiplication.
diff --git a/NapierActivity.py b/NapierActivity.py
new file mode 100644
index 0000000..4954a40
--- /dev/null
+++ b/NapierActivity.py
@@ -0,0 +1,412 @@
+# -*- coding: utf-8 -*-
+#Copyright (c) 2011 Walter Bender
+
+# 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 3 of the License, or
+# (at your option) any later version.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+import gtk
+import gobject
+import os
+
+import sugar
+from sugar.activity import activity
+from sugar import profile
+try:
+ from sugar.graphics.toolbarbox import ToolbarBox
+ _have_toolbox = True
+except ImportError:
+ _have_toolbox = False
+
+if _have_toolbox:
+ from sugar.bundle.activitybundle import ActivityBundle
+ from sugar.activity.widgets import ActivityToolbarButton
+ from sugar.activity.widgets import StopButton
+ from sugar.graphics.toolbarbox import ToolbarButton
+
+from sugar.graphics.toolbutton import ToolButton
+from sugar.graphics.menuitem import MenuItem
+
+from sprites import Sprites, Sprite
+
+from gettext import gettext as _
+
+try:
+ from sugar.graphics import style
+ GRID_CELL_SIZE = style.GRID_CELL_SIZE
+except ImportError:
+ GRID_CELL_SIZE = 0
+
+SERVICE = 'org.sugarlabs.NapierActivity'
+IFACE = SERVICE
+PATH = '/org/augarlabs/NapierActivity'
+BONE_WIDTH = 101
+BONE_HEIGHT = 901
+
+
+def _svg_header(scale=1.0):
+ ''' Return standard header for SVG bones '''
+ return '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\
+<!-- Created with Emacs (http://gnu.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"\
+ version="1.1"\
+ width="%f"\
+ height="%f">' % (101 * scale, 901 * scale)
+
+
+def _svg_footer():
+ ''' Return standard footer for SVG bones '''
+ return '</svg>\
+'
+
+def _svg_single_box(a, scale=1.0):
+ ''' Return standard top-of-column box for SVG bones '''
+ return ' <g>\
+ <rect\
+ width="%f"\
+ height="%f"\
+ x="%f"\
+ y="%f"\
+ style="fill:none;stroke:#000000;stroke-width:%f;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:%f;stroke-opacity:1;stroke-dasharray:none" />\
+ <text\
+ style="font-size:%fpx;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">\
+ <tspan x="%f" y="%f">\
+ %d\
+ </tspan>\
+ </text>\
+ </g>' % (99 * scale, 99 * scale, scale, scale, 2 * scale,
+ 4 * scale, 40 * scale, 55 * scale, 65 * scale, a)
+
+
+def _svg_double_box(a, b, y, scale=1.0):
+ ''' Return standard double-digit box for SVG bones '''
+ return ' <g>\
+ <rect\
+ width="%f"\
+ height="%f"\
+ x="%f"\
+ y="%f"\
+ style="fill:none;stroke:#000000;stroke-width:%f;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:%f;stroke-opacity:1;stroke-dasharray:none" />\
+ <line\
+ x1="%f"\
+ y1="%f"\
+ x2="%f"\
+ y2="%f"\
+ style="fill:none;stroke:#000000;stroke-width:%f;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:%f;stroke-opacity:1;stroke-dasharray:none" />\
+ <text\
+ style="font-size:%fpx;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans">\
+ <tspan x="%f" y="%f">\
+ %d\
+ </tspan>\
+ </text>\
+ <text\
+ style="font-size:%fpx;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans">\
+ <tspan x="%f" y="%f">\
+ %d\
+ </tspan>\
+ </text>\
+ </g>' % (99 * scale, 99 * scale, scale, y * scale, 2 * scale,
+ 4 * scale, scale, (y + 99) * scale, 99 * scale,
+ (y + 1) * scale, 2 * scale, 4 * scale, 40 * scale, 12 * scale,
+ (y + 51) * scale, a, 40 * scale, 52 * scale, (y + 81) * scale, b)
+
+
+def _bone_factory(value, scale=1.0):
+ svg = _svg_header(scale=scale)
+ svg += _svg_single_box(value, scale=scale)
+ for i in range(9):
+ if i > 0:
+ j = (i + 1) * value
+ svg += _svg_double_box(int(j / 10), j % 10, i * 100, scale=scale)
+ return svg + _svg_footer()
+
+
+def _svg_str_to_pixbuf(svg_string):
+ ''' Load pixbuf from SVG string '''
+ pl = gtk.gdk.PixbufLoader('svg')
+ pl.write(svg_string)
+ pl.close()
+ pixbuf = pl.get_pixbuf()
+ return pixbuf
+
+
+def _load_svg_from_file(file_path, width, height):
+ '''Create a pixbuf from SVG in a file. '''
+ return gtk.gdk.pixbuf_new_from_file_at_size(file_path, width, height)
+
+
+def _button_factory(icon_name, tooltip, callback, toolbar, cb_arg=None,
+ accelerator=None):
+ '''Factory for making toolbar buttons'''
+ my_button = ToolButton(icon_name)
+ my_button.set_tooltip(tooltip)
+ my_button.props.sensitive = True
+ if accelerator is not None:
+ my_button.props.accelerator = accelerator
+ if cb_arg is not None:
+ my_button.connect('clicked', callback, cb_arg)
+ else:
+ my_button.connect('clicked', callback)
+ if hasattr(toolbar, 'insert'): # the main toolbar
+ toolbar.insert(my_button, -1)
+ else: # or a secondary toolbar
+ toolbar.props.page.insert(my_button, -1)
+ my_button.show()
+ return my_button
+
+
+def _label_factory(label, toolbar):
+ ''' Factory for adding a label to a toolbar '''
+ my_label = gtk.Label(label)
+ my_label.set_line_wrap(True)
+ my_label.show()
+ toolitem = gtk.ToolItem()
+ toolitem.add(my_label)
+ toolbar.insert(toolitem, -1)
+ toolitem.show()
+ return my_label
+
+
+def _separator_factory(toolbar, visible=True, expand=False):
+ ''' Factory for adding a separator to a toolbar '''
+ separator = gtk.SeparatorToolItem()
+ separator.props.draw = visible
+ separator.set_expand(expand)
+ toolbar.insert(separator, -1)
+ separator.show()
+
+
+class NapierActivity(activity.Activity):
+ ''' Napier's bones: Napier's bones were invented by John Napier
+ (1550-1617), a Scottish mathematician and scientist. They help you
+ to do multiplication. '''
+
+ # TODO: Define your own bone.
+
+ def __init__(self, handle):
+ ''' Initialize the toolbars and the work surface '''
+ super(NapierActivity, self).__init__(handle)
+
+ if os.path.exists(os.path.join('~', 'Activities', 'Napier.activity')):
+ self._bone_path = os.path.join('~', 'Activities', 'Napier.activity',
+ 'bones')
+ else:
+ self._bone_path = os.path.join('.', 'bones')
+
+ self._bones = []
+ self._bone_images = [None, None, None, None, None, None, None, None,
+ None, None]
+ self._blank_image = None
+ self._number = 0
+ self._number_of_bones = 0
+ self._circles = [None, None]
+ self._ovals = []
+
+ self._setup_toolbars(_have_toolbox)
+ self._setup_canvas()
+ self._setup_workspace()
+
+ def _setup_canvas(self):
+ ''' Create a canvas '''
+ self._canvas = gtk.DrawingArea()
+ self._canvas.set_size_request(gtk.gdk.screen_width(),
+ gtk.gdk.screen_height())
+ self.set_canvas(self._canvas)
+ self._canvas.show()
+ self.show_all()
+
+ self._canvas.set_flags(gtk.CAN_FOCUS)
+ self._canvas.add_events(gtk.gdk.BUTTON_PRESS_MASK)
+ self._canvas.add_events(gtk.gdk.BUTTON_RELEASE_MASK)
+ self._canvas.add_events(gtk.gdk.POINTER_MOTION_MASK)
+ self._canvas.connect("expose-event", self._expose_cb)
+ self._canvas.connect("motion-notify-event", self._mouse_move_cb)
+ # self._canvas.connect("key_press_event", self._key_press_cb)
+
+ def _setup_workspace(self):
+ ''' Add the bones. '''
+ self._width = gtk.gdk.screen_width()
+ self._height = int(gtk.gdk.screen_height() - (GRID_CELL_SIZE * 2))
+ self._scale = self._height * 1.0 / BONE_HEIGHT
+ self._bone_width = int(BONE_WIDTH * self._scale)
+ self._bone_height = int(BONE_HEIGHT * self._scale)
+
+ # Generate the sprites we'll need...
+ self._sprites = Sprites(self._canvas)
+ self._bone_index = Sprite(self._sprites, 0, 0, _load_svg_from_file(
+ os.path.join(self._bone_path, 'bones-index.svg'),
+ self._bone_width, self._bone_height))
+ self._max_bones = int(self._width / self._bone_width) - 1
+ self._blank_image = _load_svg_from_file(
+ os.path.join(self._bone_path, 'blank-bone.svg'),
+ self._bone_width, self._bone_height)
+ for bones in range(self._max_bones):
+ self._bones.append(Sprite(self._sprites, bones * self._bone_width,
+ 0, self._blank_image))
+ circle_image = _load_svg_from_file(
+ os.path.join(self._bone_path, 'circle.svg'), int(self._scale * 45),
+ int(self._scale * 45))
+ self._circles[0] = Sprite(self._sprites, 0, -100, circle_image)
+ self._circles[1] = Sprite(self._sprites, 0, -100, circle_image)
+ oval_image = _load_svg_from_file(
+ os.path.join(self._bone_path, 'oval.svg'), int(self._scale * 129),
+ int(self._scale * 92))
+ for bones in range(self._max_bones - 1):
+ self._ovals.append(Sprite(self._sprites, 0, -100, oval_image))
+
+ def _setup_toolbars(self, have_toolbox):
+ ''' Setup the toolbars. '''
+
+ self.max_participants = 1 # no sharing
+
+ if have_toolbox:
+ toolbox = ToolbarBox()
+
+ # Activity toolbar
+ activity_button = ActivityToolbarButton(self)
+
+ toolbox.toolbar.insert(activity_button, 0)
+ activity_button.show()
+
+ self._bones_toolbar = gtk.Toolbar()
+ self._bones_toolbar_button = ToolbarButton(label=_('Select a bone'),
+ page=self._bones_toolbar,
+ icon_name='bones')
+
+ self._bones_toolbar_button.show()
+ toolbox.toolbar.insert(self._bones_toolbar_button, -1)
+ self.set_toolbar_box(toolbox)
+ toolbox.show()
+ self.toolbar = toolbox.toolbar
+
+ else:
+ # Use pre-0.86 toolbar design
+ self._bones_toolbar = gtk.Toolbar()
+ self.toolbar = gtk.Toolbar()
+ toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(toolbox)
+ toolbox.add_toolbar(_('Bones'), self._bones_toolbar)
+ toolbox.add_toolbar(_('Results'), self.toolbar)
+ toolbox.show()
+ toolbox.set_current_toolbar(1)
+
+ _separator_factory(self.toolbar)
+
+ self._new_calc_button = _button_factory(
+ 'new-game', _('Clear'), self._new_calc_cb, self.toolbar)
+
+ self._status = _label_factory('', self.toolbar)
+
+ _button_factory('number-0', _('zero'), self._number_cb,
+ self._bones_toolbar, cb_arg=0)
+
+ _button_factory('number-1', _('one'), self._number_cb,
+ self._bones_toolbar, cb_arg=1)
+
+ _button_factory('number-2', _('two'), self._number_cb,
+ self._bones_toolbar, cb_arg=2)
+
+ _button_factory('number-3', _('three'), self._number_cb,
+ self._bones_toolbar, cb_arg=3)
+
+ _button_factory('number-4', _('four'), self._number_cb,
+ self._bones_toolbar, cb_arg=4)
+
+ _button_factory('number-5', _('five'), self._number_cb,
+ self._bones_toolbar, cb_arg=5)
+
+ _button_factory('number-6', _('six'), self._number_cb,
+ self._bones_toolbar, cb_arg=6)
+
+ _button_factory('number-7', _('seven'), self._number_cb,
+ self._bones_toolbar, cb_arg=7)
+
+ _button_factory('number-8', _('eight'), self._number_cb,
+ self._bones_toolbar, cb_arg=8)
+
+ _button_factory('number-9', _('nine'), self._number_cb,
+ self._bones_toolbar, cb_arg=9)
+
+ if _have_toolbox:
+ _separator_factory(toolbox.toolbar, False, True)
+
+ stop_button = StopButton(self)
+ stop_button.props.accelerator = '<Ctrl>q'
+ toolbox.toolbar.insert(stop_button, -1)
+ stop_button.show()
+ self._bones_toolbar_button.set_expanded(True)
+
+ def _new_calc_cb(self, button=None):
+ ''' Start a new calculation. '''
+ for bone in range(self._max_bones):
+ self._bones[bone].images[0] = self._blank_image
+ self._bones[bone].inval()
+ self._number_of_bones = 0
+ self._number = 0
+ self._status.set_label('')
+ return
+
+ def _number_cb(self, button=None, value=0):
+ ''' Try to add a digit. '''
+ if self._number_of_bones == self._max_bones:
+ return
+ self._number_of_bones += 1
+ if self._bone_images[value] is None:
+ self._bone_images[value] = _svg_str_to_pixbuf(
+ _bone_factory(value, scale=self._scale))
+ self._bones[self._number_of_bones].images[0] = self._bone_images[value]
+ self._bones[self._number_of_bones].inval()
+ self._number = self._number * 10 + value
+
+ def _mouse_move_cb(self, win, event):
+ ''' Determine which row we are in and then calculate the product. '''
+ win.grab_focus()
+ x, y = map(int, event.get_coords())
+ factor = int(y / self._bone_width) # The row determines a factor
+
+ if self._number == 0 or factor == 0:
+ self._status.set_label('')
+ self._circles[0].move((0, -100))
+ self._circles[1].move((0, -100))
+ for number in range(self._max_bones):
+ self._ovals[number].move((0, -100))
+ else:
+ c0dx = int(4 * self._scale)
+ c0dy = int(12 * self._scale)
+ c1dx = int(44 * self._scale)
+ c1dy = int(42 * self._scale)
+ odx = int(42 * self._scale)
+ ody = int(2 * self._scale)
+ self._circles[0].move((self._bone_width + c0dx,
+ factor * self._bone_width + c0dy))
+ self._circles[1].move((
+ self._number_of_bones * self._bone_width + c1dx,
+ factor * self._bone_width + c1dy))
+ for number in range(self._number_of_bones - 1):
+ self._ovals[number].move(((number + 1) * self._bone_width + odx,
+ factor * self._bone_width + ody))
+ self._status.set_label('%d × %d = %d' % (
+ factor + 1, self._number, (factor + 1) * self._number))
+ return True
+
+ def _key_press_cb(self, win, event):
+ ''' TODO: Add bones by typing numbers '''
+ return True
+
+ def _expose_cb(self, win, event):
+ self._sprites.redraw_sprites()
+ return True
+
+ def _destroy_cb(self, win, event):
+ gtk.main_quit()
diff --git a/activity/activity-napier.svg b/activity/activity-napier.svg
new file mode 100644
index 0000000..cbfc4c0
--- /dev/null
+++ b/activity/activity-napier.svg
@@ -0,0 +1,37 @@
+<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#000">
+ <!ENTITY fill_color "#eee">
+]><svg height="55px" viewBox="0 0 55 55" width="55px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5" stroke="&stroke_color;" fill="&fill_color;">
+ <g
+ transform="translate(-164.9317,-522.38248)"
+ id="layer1">
+ <path
+ d="m 172.31971,536.76347 c -0.72411,-2.08754 -3.85535,-2.66829 -4.57016,-4.75911 -0.54398,-1.59114 0.41111,-4.84994 1.17519,-6.34548 0.87849,-1.71948 3.58403,-1.89878 5.48419,-2.11515 2.90308,-0.33058 4.65817,4.0255 5.87592,3.83372 1.55514,-0.1023 2.2545,-2.48793 3.7867,-2.77614 2.1694,-0.40808 4.92014,0.0599 6.39823,1.71856 1.68249,1.88803 1.99295,5.13094 1.17518,7.53525 -0.58154,1.70978 -3.24117,2.02778 -3.91728,3.70153 -1.51719,3.75593 0.59363,8.10493 0.5223,12.16216 -0.0785,4.46388 -2.44397,9.11811 -1.0446,13.35194 0.64576,1.9538 3.46637,2.65253 4.04785,4.62691 0.51535,1.74982 0.13809,3.85051 -0.78345,5.4201 -0.77839,1.32577 -2.19739,2.33161 -3.65613,2.77614 -1.41951,0.43258 -3.07626,0.32499 -4.43959,-0.26439 -1.52949,-0.66121 -2.02542,-3.10643 -3.65612,-3.43714 -1.72831,-0.35049 -2.54848,1.76353 -4.30901,1.85077 -2.0667,0.1024 -5.18389,-0.11769 -6.65938,-1.58637 -1.38558,-1.37919 -1.76507,-3.72751 -1.56691,-5.68449 0.15485,-1.52924 1.08998,-2.94065 2.08922,-4.09812 0.69927,-0.81 2.11477,-0.89977 2.61152,-1.85076 1.58723,-3.03868 -0.0538,-6.87906 0.13057,-10.3114 0.24699,-4.59719 -0.0601,-8.61145 1.30576,-13.74853 z"
+ id="path3060"
+ style="fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:2.4120214;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ x="174.17461"
+ y="554.1217"
+ transform="scale(0.99384853,1.0061895)"
+ id="text3062"
+ xml:space="preserve"
+ style="font-size:21.02147293px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:&stroke_color;;fill-opacity:1;stroke:none;font-family:Sans"><tspan
+ x="174.17461"
+ y="554.1217"
+ id="tspan3064">1</tspan></text>
+ <path
+ d="m 198.96079,536.76347 c -0.72412,-2.08754 -3.85535,-2.66829 -4.57016,-4.75911 -0.54398,-1.59114 0.41111,-4.84994 1.17518,-6.34548 0.8785,-1.71948 3.58403,-1.89878 5.4842,-2.11515 2.90308,-0.33058 4.65816,4.0255 5.87592,3.83372 1.55514,-0.1023 2.25449,-2.48793 3.7867,-2.77614 2.16939,-0.40808 4.92014,0.0599 6.39822,1.71856 1.6825,1.88803 1.99296,5.13094 1.17519,7.53525 -0.58155,1.70978 -3.24118,2.02778 -3.91728,3.70153 -1.5172,3.75593 0.59363,8.10493 0.5223,12.16216 -0.0785,4.46388 -2.44397,9.11811 -1.04461,13.35194 0.64577,1.9538 3.46637,2.65253 4.04786,4.62691 0.51535,1.74982 0.13809,3.85051 -0.78346,5.4201 -0.77839,1.32577 -2.19739,2.33161 -3.65612,2.77614 -1.41951,0.43258 -3.07626,0.32499 -4.43959,-0.26439 -1.5295,-0.66121 -2.02542,-3.10643 -3.65613,-3.43714 -1.72831,-0.35049 -2.54847,1.76353 -4.309,1.85077 -2.0667,0.1024 -5.1839,-0.11769 -6.65938,-1.58637 -1.38558,-1.37919 -1.76507,-3.72751 -1.56691,-5.68449 0.15485,-1.52924 1.08998,-2.94065 2.08921,-4.09812 0.69927,-0.81 2.11478,-0.89977 2.61152,-1.85076 1.58724,-3.03868 -0.0538,-6.87906 0.13058,-10.3114 0.24699,-4.59719 -0.0601,-8.61145 1.30576,-13.74853 z"
+ id="path3060-2"
+ style="fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:2.4120214;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ x="200.98058"
+ y="554.26031"
+ transform="scale(0.99384853,1.0061895)"
+ id="text3062-9"
+ xml:space="preserve"
+ style="font-size:21.02147293px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:&stroke_color;;fill-opacity:1;stroke:none;font-family:Sans"><tspan
+ x="200.98058"
+ y="554.26031"
+ id="tspan3064-9">2</tspan></text>
+ </g>
+</svg>
diff --git a/activity/activity.info b/activity/activity.info
new file mode 100644
index 0000000..5fe2ff2
--- /dev/null
+++ b/activity/activity.info
@@ -0,0 +1,8 @@
+[Activity]
+name = Napier
+activity_version = 1
+license = GPLv3
+bundle_id = org.sugarlabs.NapierActivity
+exec = sugar-activity NapierActivity.NapierActivity
+icon = activity-napier
+show_launcher = yes
diff --git a/bones/blank-bone.svg b/bones/blank-bone.svg
new file mode 100644
index 0000000..e3fee82
--- /dev/null
+++ b/bones/blank-bone.svg
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Emacs (http://gnu.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"
+ version="1.1"
+ width="101"
+ height="901">
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="1"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="65">
+
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="100"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="165">
+
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="200"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="265">
+
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="300"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="365">
+
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="400"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="465">
+
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="500"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="565">
+
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="600"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="665">
+
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="700"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="765">
+
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="800"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="865">
+
+ </tspan>
+ </text>
+ </g>
+</svg>
diff --git a/bones/bones-index.svg b/bones/bones-index.svg
new file mode 100644
index 0000000..cf993ff
--- /dev/null
+++ b/bones/bones-index.svg
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Emacs (http://gnu.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"
+ version="1.1"
+ width="101"
+ height="901">
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="1"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="65">
+
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="100"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="165">
+ 2
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="200"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="265">
+ 3
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="300"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="365">
+ 4
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="400"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="465">
+ 5
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="500"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="565">
+ 6
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="600"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="665">
+ 7
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="700"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="765">
+ 8
+ </tspan>
+ </text>
+ </g>
+ <g>
+ <rect
+ width="99"
+ height="99"
+ x="1"
+ y="800"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ style="font-size:40px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;text-align:center;text-anchor:middle">
+ <tspan x="55" y="865">
+ 9
+ </tspan>
+ </text>
+ </g>
+</svg>
diff --git a/bones/circle.svg b/bones/circle.svg
new file mode 100644
index 0000000..62ac83e
--- /dev/null
+++ b/bones/circle.svg
@@ -0,0 +1,32 @@
+<?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"
+ version="1.1"
+ width="45"
+ height="45"
+ id="svg3918">
+ <metadata
+ id="metadata3927">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs3925" />
+ <path
+ d="m 43.239273,22.514342 a 20.746456,20.746456 0 1 1 -41.4929117,0 20.746456,20.746456 0 1 1 41.4929117,0 z"
+ id="path3921"
+ style="fill:none;stroke:#ff0000;stroke-width:3.5;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+</svg>
diff --git a/bones/oval.svg b/bones/oval.svg
new file mode 100644
index 0000000..24941eb
--- /dev/null
+++ b/bones/oval.svg
@@ -0,0 +1,38 @@
+<?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"
+ version="1.1"
+ width="129"
+ height="92"
+ id="svg2">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <g
+ transform="translate(-548.06803,-551.36311)"
+ id="g4"
+ style="stroke-width:3.5;stroke-miterlimit:4;stroke-dasharray:none">
+ <path
+ d="m 47.575289,236.11583 a 20.083012,20.277992 0 1 1 -40.1660235,0 20.083012,20.277992 0 1 1 40.1660235,0 z"
+ transform="matrix(2.7914157,-1.9284131,1.1535178,0.79689271,263.40099,462.56247)"
+ id="path6"
+ style="fill:none;stroke:#ff0000;stroke-width:1.65936089;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+</svg>
diff --git a/icons/bones.svg b/icons/bones.svg
new file mode 100644
index 0000000..30d64ec
--- /dev/null
+++ b/icons/bones.svg
@@ -0,0 +1,45 @@
+<?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"
+ version="1.1"
+ width="55"
+ height="55">
+ <g
+ transform="translate(-164.9317,-522.38248)"
+ id="layer1">
+ <path
+ d="m 172.31971,536.76347 c -0.72411,-2.08754 -3.85535,-2.66829 -4.57016,-4.75911 -0.54398,-1.59114 0.41111,-4.84994 1.17519,-6.34548 0.87849,-1.71948 3.58403,-1.89878 5.48419,-2.11515 2.90308,-0.33058 4.65817,4.0255 5.87592,3.83372 1.55514,-0.1023 2.2545,-2.48793 3.7867,-2.77614 2.1694,-0.40808 4.92014,0.0599 6.39823,1.71856 1.68249,1.88803 1.99295,5.13094 1.17518,7.53525 -0.58154,1.70978 -3.24117,2.02778 -3.91728,3.70153 -1.51719,3.75593 0.59363,8.10493 0.5223,12.16216 -0.0785,4.46388 -2.44397,9.11811 -1.0446,13.35194 0.64576,1.9538 3.46637,2.65253 4.04785,4.62691 0.51535,1.74982 0.13809,3.85051 -0.78345,5.4201 -0.77839,1.32577 -2.19739,2.33161 -3.65613,2.77614 -1.41951,0.43258 -3.07626,0.32499 -4.43959,-0.26439 -1.52949,-0.66121 -2.02542,-3.10643 -3.65612,-3.43714 -1.72831,-0.35049 -2.54848,1.76353 -4.30901,1.85077 -2.0667,0.1024 -5.18389,-0.11769 -6.65938,-1.58637 -1.38558,-1.37919 -1.76507,-3.72751 -1.56691,-5.68449 0.15485,-1.52924 1.08998,-2.94065 2.08922,-4.09812 0.69927,-0.81 2.11477,-0.89977 2.61152,-1.85076 1.58723,-3.03868 -0.0538,-6.87906 0.13057,-10.3114 0.24699,-4.59719 -0.0601,-8.61145 1.30576,-13.74853 z"
+ id="path3060"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4120214;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ x="174.17461"
+ y="554.1217"
+ transform="scale(0.99384853,1.0061895)"
+ id="text3062"
+ xml:space="preserve"
+ style="font-size:21.02147293px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"><tspan
+ x="174.17461"
+ y="554.1217"
+ id="tspan3064">1</tspan></text>
+ <path
+ d="m 198.96079,536.76347 c -0.72412,-2.08754 -3.85535,-2.66829 -4.57016,-4.75911 -0.54398,-1.59114 0.41111,-4.84994 1.17518,-6.34548 0.8785,-1.71948 3.58403,-1.89878 5.4842,-2.11515 2.90308,-0.33058 4.65816,4.0255 5.87592,3.83372 1.55514,-0.1023 2.25449,-2.48793 3.7867,-2.77614 2.16939,-0.40808 4.92014,0.0599 6.39822,1.71856 1.6825,1.88803 1.99296,5.13094 1.17519,7.53525 -0.58155,1.70978 -3.24118,2.02778 -3.91728,3.70153 -1.5172,3.75593 0.59363,8.10493 0.5223,12.16216 -0.0785,4.46388 -2.44397,9.11811 -1.04461,13.35194 0.64577,1.9538 3.46637,2.65253 4.04786,4.62691 0.51535,1.74982 0.13809,3.85051 -0.78346,5.4201 -0.77839,1.32577 -2.19739,2.33161 -3.65612,2.77614 -1.41951,0.43258 -3.07626,0.32499 -4.43959,-0.26439 -1.5295,-0.66121 -2.02542,-3.10643 -3.65613,-3.43714 -1.72831,-0.35049 -2.54847,1.76353 -4.309,1.85077 -2.0667,0.1024 -5.1839,-0.11769 -6.65938,-1.58637 -1.38558,-1.37919 -1.76507,-3.72751 -1.56691,-5.68449 0.15485,-1.52924 1.08998,-2.94065 2.08921,-4.09812 0.69927,-0.81 2.11478,-0.89977 2.61152,-1.85076 1.58724,-3.03868 -0.0538,-6.87906 0.13058,-10.3114 0.24699,-4.59719 -0.0601,-8.61145 1.30576,-13.74853 z"
+ id="path3060-2"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4120214;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ x="200.98058"
+ y="554.26031"
+ transform="scale(0.99384853,1.0061895)"
+ id="text3062-9"
+ xml:space="preserve"
+ style="font-size:21.02147293px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"><tspan
+ x="200.98058"
+ y="554.26031"
+ id="tspan3064-9">2</tspan></text>
+ </g>
+</svg>
diff --git a/icons/new-game.svg b/icons/new-game.svg
new file mode 100644
index 0000000..7dff35b
--- /dev/null
+++ b/icons/new-game.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Emacs -->
+
+<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.1"
+ width="55"
+ height="55">
+ <path
+ d="m 17,44 4,-13 -11,-8 13,0 5,-13 4,13 14,0 -11,8 4,13 -11,-8 z"
+ id="path6"
+ style="fill:none;stroke:#ffffff;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4" />
+</svg>
diff --git a/icons/number-0.svg b/icons/number-0.svg
new file mode 100644
index 0000000..9ce05a1
--- /dev/null
+++ b/icons/number-0.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <defs
+ id="defs4" />
+ <rect
+ width="50.94099"
+ height="50.940994"
+ rx="8.1506357"
+ ry="7.0263433"
+ x="2.0295048"
+ y="2.0295029"
+ id="rect2817"
+ style="fill:#000000;fill-opacity:1;stroke:#808080;stroke-width:4.05900621;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ x="14.980469"
+ y="42.060547"
+ id="text2394"
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"><tspan
+ x="14.980469"
+ y="42.060547"
+ id="tspan2396">0</tspan></text>
+</svg>
diff --git a/icons/number-1.svg b/icons/number-1.svg
new file mode 100644
index 0000000..bffb371
--- /dev/null
+++ b/icons/number-1.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <defs
+ id="defs4" />
+ <rect
+ width="50.94099"
+ height="50.940994"
+ rx="8.1506357"
+ ry="7.0263433"
+ x="2.0295048"
+ y="2.0295029"
+ id="rect2817"
+ style="fill:#000000;fill-opacity:1;stroke:#808080;stroke-width:4.05900621;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ x="14.980469"
+ y="42.060547"
+ id="text2394"
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"><tspan
+ x="14.980469"
+ y="42.060547"
+ id="tspan2396">1</tspan></text>
+</svg>
diff --git a/icons/number-2.svg b/icons/number-2.svg
new file mode 100644
index 0000000..297ad79
--- /dev/null
+++ b/icons/number-2.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <defs
+ id="defs4" />
+ <rect
+ width="50.94099"
+ height="50.940994"
+ rx="8.1506357"
+ ry="7.0263433"
+ x="2.0295048"
+ y="2.0295029"
+ id="rect2817"
+ style="fill:#000000;fill-opacity:1;stroke:#808080;stroke-width:4.05900621;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ x="14.980469"
+ y="42.060547"
+ id="text2394"
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"><tspan
+ x="14.980469"
+ y="42.060547"
+ id="tspan2396">2</tspan></text>
+</svg>
diff --git a/icons/number-3.svg b/icons/number-3.svg
new file mode 100644
index 0000000..0cde39f
--- /dev/null
+++ b/icons/number-3.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <defs
+ id="defs4" />
+ <rect
+ width="50.94099"
+ height="50.940994"
+ rx="8.1506357"
+ ry="7.0263433"
+ x="2.0295048"
+ y="2.0295029"
+ id="rect2817"
+ style="fill:#000000;fill-opacity:1;stroke:#808080;stroke-width:4.05900621;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ x="14.980469"
+ y="42.060547"
+ id="text2394"
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"><tspan
+ x="14.980469"
+ y="42.060547"
+ id="tspan2396">3</tspan></text>
+</svg>
diff --git a/icons/number-4.svg b/icons/number-4.svg
new file mode 100644
index 0000000..bc27dd2
--- /dev/null
+++ b/icons/number-4.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <defs
+ id="defs4" />
+ <rect
+ width="50.94099"
+ height="50.940994"
+ rx="8.1506357"
+ ry="7.0263433"
+ x="2.0295048"
+ y="2.0295029"
+ id="rect2817"
+ style="fill:#000000;fill-opacity:1;stroke:#808080;stroke-width:4.05900621;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ x="14.980469"
+ y="42.060547"
+ id="text2394"
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"><tspan
+ x="14.980469"
+ y="42.060547"
+ id="tspan2396">4</tspan></text>
+</svg>
diff --git a/icons/number-5.svg b/icons/number-5.svg
new file mode 100644
index 0000000..a47f8d1
--- /dev/null
+++ b/icons/number-5.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <defs
+ id="defs4" />
+ <rect
+ width="50.94099"
+ height="50.940994"
+ rx="8.1506357"
+ ry="7.0263433"
+ x="2.0295048"
+ y="2.0295029"
+ id="rect2817"
+ style="fill:#000000;fill-opacity:1;stroke:#808080;stroke-width:4.05900621;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ x="14.980469"
+ y="42.060547"
+ id="text2394"
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"><tspan
+ x="14.980469"
+ y="42.060547"
+ id="tspan2396">5</tspan></text>
+</svg>
diff --git a/icons/number-6.svg b/icons/number-6.svg
new file mode 100644
index 0000000..19de7ef
--- /dev/null
+++ b/icons/number-6.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <defs
+ id="defs4" />
+ <rect
+ width="50.94099"
+ height="50.940994"
+ rx="8.1506357"
+ ry="7.0263433"
+ x="2.0295048"
+ y="2.0295029"
+ id="rect2817"
+ style="fill:#000000;fill-opacity:1;stroke:#808080;stroke-width:4.05900621;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ x="14.980469"
+ y="42.060547"
+ id="text2394"
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"><tspan
+ x="14.980469"
+ y="42.060547"
+ id="tspan2396">6</tspan></text>
+</svg>
diff --git a/icons/number-7.svg b/icons/number-7.svg
new file mode 100644
index 0000000..581999b
--- /dev/null
+++ b/icons/number-7.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <defs
+ id="defs4" />
+ <rect
+ width="50.94099"
+ height="50.940994"
+ rx="8.1506357"
+ ry="7.0263433"
+ x="2.0295048"
+ y="2.0295029"
+ id="rect2817"
+ style="fill:#000000;fill-opacity:1;stroke:#808080;stroke-width:4.05900621;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ x="14.980469"
+ y="42.060547"
+ id="text2394"
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"><tspan
+ x="14.980469"
+ y="42.060547"
+ id="tspan2396">7</tspan></text>
+</svg>
diff --git a/icons/number-8.svg b/icons/number-8.svg
new file mode 100644
index 0000000..e24a89c
--- /dev/null
+++ b/icons/number-8.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <defs
+ id="defs4" />
+ <rect
+ width="50.94099"
+ height="50.940994"
+ rx="8.1506357"
+ ry="7.0263433"
+ x="2.0295048"
+ y="2.0295029"
+ id="rect2817"
+ style="fill:#000000;fill-opacity:1;stroke:#808080;stroke-width:4.05900621;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ x="14.980469"
+ y="42.060547"
+ id="text2394"
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"><tspan
+ x="14.980469"
+ y="42.060547"
+ id="tspan2396">8</tspan></text>
+</svg>
diff --git a/icons/number-9.svg b/icons/number-9.svg
new file mode 100644
index 0000000..4ff26ff
--- /dev/null
+++ b/icons/number-9.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <defs
+ id="defs4" />
+ <rect
+ width="50.94099"
+ height="50.940994"
+ rx="8.1506357"
+ ry="7.0263433"
+ x="2.0295048"
+ y="2.0295029"
+ id="rect2817"
+ style="fill:#000000;fill-opacity:1;stroke:#808080;stroke-width:4.05900621;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <text
+ x="14.980469"
+ y="42.060547"
+ id="text2394"
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"><tspan
+ x="14.980469"
+ y="42.060547"
+ id="tspan2396">9</tspan></text>
+</svg>
diff --git a/po/Napier.pot b/po/Napier.pot
new file mode 100644
index 0000000..ac23756
--- /dev/null
+++ b/po/Napier.pot
@@ -0,0 +1,78 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-04-19 09:01-0400\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: activity/activity.info:2
+msgid "Napier"
+msgstr ""
+
+#: /home/walter/Desktop/Activities/napier/NapierActivity.py:283
+msgid "Select a bone"
+msgstr ""
+
+#: /home/walter/Desktop/Activities/napier/NapierActivity.py:299
+msgid "Bones"
+msgstr ""
+
+#: /home/walter/Desktop/Activities/napier/NapierActivity.py:300
+msgid "Results"
+msgstr ""
+
+#: /home/walter/Desktop/Activities/napier/NapierActivity.py:307
+msgid "Clear"
+msgstr ""
+
+#: /home/walter/Desktop/Activities/napier/NapierActivity.py:311
+msgid "zero"
+msgstr ""
+
+#: /home/walter/Desktop/Activities/napier/NapierActivity.py:314
+msgid "one"
+msgstr ""
+
+#: /home/walter/Desktop/Activities/napier/NapierActivity.py:317
+msgid "two"
+msgstr ""
+
+#: /home/walter/Desktop/Activities/napier/NapierActivity.py:320
+msgid "three"
+msgstr ""
+
+#: /home/walter/Desktop/Activities/napier/NapierActivity.py:323
+msgid "four"
+msgstr ""
+
+#: /home/walter/Desktop/Activities/napier/NapierActivity.py:326
+msgid "five"
+msgstr ""
+
+#: /home/walter/Desktop/Activities/napier/NapierActivity.py:329
+msgid "six"
+msgstr ""
+
+#: /home/walter/Desktop/Activities/napier/NapierActivity.py:332
+msgid "seven"
+msgstr ""
+
+#: /home/walter/Desktop/Activities/napier/NapierActivity.py:335
+msgid "eight"
+msgstr ""
+
+#: /home/walter/Desktop/Activities/napier/NapierActivity.py:338
+msgid "nine"
+msgstr ""
diff --git a/sprites.py b/sprites.py
new file mode 100644
index 0000000..c633397
--- /dev/null
+++ b/sprites.py
@@ -0,0 +1,453 @@
+# -*- coding: utf-8 -*-
+
+#Copyright (c) 2007-8, Playful Invention Company.
+#Copyright (c) 2008-10 Walter Bender
+
+#Permission is hereby granted, free of charge, to any person obtaining a copy
+#of this software and associated documentation files (the "Software"), to deal
+#in the Software without restriction, including without limitation the rights
+#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+#copies of the Software, and to permit persons to whom the Software is
+#furnished to do so, subject to the following conditions:
+
+#The above copyright notice and this permission notice shall be included in
+#all copies or substantial portions of the Software.
+
+#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+#THE SOFTWARE.
+
+"""
+
+sprites.py is a simple sprites library for managing graphics objects,
+'sprites', on a canvas. It manages multiple sprites with methods such
+as move, hide, set_layer, etc.
+
+There are two classes:
+
+class Sprites maintains a collection of sprites.
+class Sprite manages individual sprites within the collection.
+
+Example usage:
+ # Import the classes into your program.
+ from sprites import Sprites Sprite
+
+ # In your expose callback event handler, call refresh
+ def _expose_cb(self, win, event):
+ self.sprite_list.refresh(event)
+ return True
+
+ # Create a new sprite collection for a gtk Drawing Area.
+ my_drawing_area = gtk.DrawingArea()
+ self.sprite_list = Sprites(my_drawing_area)
+
+ # Create a "pixbuf" (in this example, from SVG).
+ my_pixbuf = svg_str_to_pixbuf("<svg>...some svg code...</svg>")
+
+ # Create a sprite at position x1, y1.
+ my_sprite = sprites.Sprite(self.sprite_list, x1, y1, my_pixbuf)
+
+ # Move the sprite to a new position.
+ my_sprite.move_relative((dx, dy))
+
+ # Create another "pixbuf".
+ your_pixbuf = svg_str_to_pixbuf("<svg>...some svg code...</svg>")
+
+ # Create a sprite at position x2, y2.
+ your_sprite = sprites.Sprite(self.sprite_list, x2, y2, my_pixbuf)
+
+ # Assign the sprites to layers.
+ # In this example, your_sprite will be on top of my_sprite.
+ my_sprite.set_layer(100)
+ your_sprite.set_layer(200)
+
+ # Now put my_sprite on top of your_sprite.
+ my_sprite.set_layer(300)
+
+# method for converting SVG to a gtk pixbuf
+def svg_str_to_pixbuf(svg_string):
+ pl = gtk.gdk.PixbufLoader('svg')
+ pl.write(svg_string)
+ pl.close()
+ pixbuf = pl.get_pixbuf()
+ return pixbuf
+"""
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import pango
+
+
+class Sprites:
+ """ A class for the list of sprites and everything they share in common """
+
+ def __init__(self, canvas, area=None, gc=None):
+ """ Initialize an empty array of sprites """
+ self.canvas = canvas
+ if area == None:
+ self.area = self.canvas.window
+ self.gc = self.area.new_gc()
+ else:
+ self.area = area
+ self.gc = gc
+ self.cm = self.gc.get_colormap()
+ self.list = []
+
+ def get_sprite(self, i):
+ """ Return a sprint from the array """
+ if i < 0 or i > len(self.list) - 1:
+ return(None)
+ else:
+ return(self.list[i])
+
+ def length_of_list(self):
+ """ How many sprites are there? """
+ return(len(self.list))
+
+ def append_to_list(self, spr):
+ """ Append a new sprite to the end of the list. """
+ self.list.append(spr)
+
+ def insert_in_list(self, spr, i):
+ """ Insert a sprite at position i. """
+ if i < 0:
+ self.list.insert(0, spr)
+ elif i > len(self.list) - 1:
+ self.list.append(spr)
+ else:
+ self.list.insert(i, spr)
+
+ def remove_from_list(self, spr):
+ """ Remove a sprite from the list. """
+ if spr in self.list:
+ self.list.remove(spr)
+
+ def find_sprite(self, pos, alpha=True):
+ """ Search based on (x, y) position. Return the 'top/first' one. """
+ list = self.list[:]
+ list.reverse()
+ for spr in list:
+ if spr.hit(pos):
+ if not alpha or spr.get_pixel(pos)[3] == 255:
+ return spr
+ return None
+
+ def refresh(self, event):
+ """ Handle expose event refresh """
+ self.redraw_sprites(event.area)
+
+ def redraw_sprites(self, area=None):
+ """ Redraw the sprites that intersect area. """
+ for spr in self.list:
+ if area == None:
+ spr.draw()
+ else:
+ intersection = spr.rect.intersect(area)
+ if intersection.width > 0 or intersection.height > 0:
+ spr.draw()
+
+
+class Sprite:
+ """ A class for the individual sprites """
+
+ def __init__(self, sprites, x, y, image):
+ """ Initialize an individual sprite """
+ self._sprites = sprites
+ self.rect = gtk.gdk.Rectangle(int(x), int(y), 0, 0)
+ self._scale = [12]
+ self._rescale = [True]
+ self._horiz_align = ["center"]
+ self._vert_align = ["middle"]
+ self._fd = None
+ self._bold = False
+ self._italic = False
+ self._color = None
+ self._margins = [0, 0, 0, 0]
+ self.layer = 100
+ self.labels = []
+ self.images = []
+ self._dx = [] # image offsets
+ self._dy = []
+ self.set_image(image)
+ if self._sprites is not None:
+ self._sprites.append_to_list(self)
+
+ def set_image(self, image, i=0, dx=0, dy=0):
+ """ Add an image to the sprite. """
+ while len(self.images) < i + 1:
+ self.images.append(None)
+ self._dx.append(0)
+ self._dy.append(0)
+ self.images[i] = image
+ self._dx[i] = dx
+ self._dy[i] = dy
+ if isinstance(self.images[i], gtk.gdk.Pixbuf):
+ w = self.images[i].get_width()
+ h = self.images[i].get_height()
+ else:
+ w, h = self.images[i].get_size()
+ if i == 0: # Always reset width and height when base image changes.
+ self.rect.width = w + dx
+ self.rect.height = h + dy
+ else:
+ if w + dx > self.rect.width:
+ self.rect.width = w + dx
+ if h + dy > self.rect.height:
+ self.rect.height = h + dy
+
+ def move(self, pos, visible=True):
+ """ Move to new (x, y) position """
+ if visible:
+ self.inval()
+ self.rect.x, self.rect.y = int(pos[0]), int(pos[1])
+ if visible:
+ self.inval()
+
+ def move_relative(self, pos, visible=True):
+ """ Move to new (x+dx, y+dy) position """
+ if visible:
+ self.inval()
+ self.rect.x += int(pos[0])
+ self.rect.y += int(pos[1])
+ if visible:
+ self.inval()
+
+ def get_xy(self):
+ """ Return current (x, y) position """
+ return (self.rect.x, self.rect.y)
+
+ def get_dimensions(self):
+ """ Return current size """
+ return (self.rect.width, self.rect.height)
+
+ def get_layer(self):
+ """ Return current layer """
+ return self.layer
+
+ def set_shape(self, image, i=0):
+ """ Set the current image associated with the sprite """
+ self.inval()
+ self.set_image(image, i)
+ self.inval()
+
+ def set_layer(self, layer):
+ """ Set the layer for a sprite """
+ if self._sprites is None:
+ return
+ self._sprites.remove_from_list(self)
+ self.layer = layer
+ for i in range(self._sprites.length_of_list()):
+ if layer < self._sprites.get_sprite(i).layer:
+ self._sprites.insert_in_list(self, i)
+ self.inval()
+ return
+ self._sprites.append_to_list(self)
+ self.inval()
+
+ def set_label(self, new_label, i=0):
+ """ Set the label drawn on the sprite """
+ self._extend_labels_array(i)
+ if type(new_label) is str or type(new_label) is unicode:
+ # pango doesn't like nulls
+ self.labels[i] = new_label.replace("\0", " ")
+ else:
+ self.labels[i] = str(new_label)
+ self.inval()
+
+ def set_margins(self, l=0, t=0, r=0, b=0):
+ """ Set the margins for drawing the label """
+ self._margins = [l, t, r, b]
+
+ def _extend_labels_array(self, i):
+ """ Append to the labels attribute list """
+ if self._fd is None:
+ self.set_font('Sans')
+ if self._color is None:
+ self._color = self._sprites.cm.alloc_color('black')
+ while len(self.labels) < i + 1:
+ self.labels.append(" ")
+ self._scale.append(self._scale[0])
+ self._rescale.append(self._rescale[0])
+ self._horiz_align.append(self._horiz_align[0])
+ self._vert_align.append(self._vert_align[0])
+
+ def set_font(self, font):
+ """ Set the font for a label """
+ self._fd = pango.FontDescription(font)
+
+ def set_label_color(self, rgb):
+ """ Set the font color for a label """
+ self._color = self._sprites.cm.alloc_color(rgb)
+
+ def set_label_attributes(self, scale, rescale=True, horiz_align="center",
+ vert_align="middle", i=0):
+ """ Set the various label attributes """
+ self._extend_labels_array(i)
+ self._scale[i] = scale
+ self._rescale[i] = rescale
+ self._horiz_align[i] = horiz_align
+ self._vert_align[i] = vert_align
+
+ def hide(self):
+ """ Hide a sprite """
+ if self._sprites is None:
+ return
+ self.inval()
+ self._sprites.remove_from_list(self)
+
+ def inval(self):
+ """ Force a region redraw by gtk """
+ if self._sprites is None:
+ return
+ self._sprites.area.invalidate_rect(self.rect, False)
+
+ def draw(self):
+ """ Draw the sprite (and label) """
+ if self._sprites is None:
+ return
+ for i, img in enumerate(self.images):
+ if isinstance(img, gtk.gdk.Pixbuf):
+ self._sprites.area.draw_pixbuf(self._sprites.gc, img, 0, 0,
+ self.rect.x + self._dx[i],
+ self.rect.y + self._dy[i])
+ elif img is not None:
+ self._sprites.area.draw_drawable(self._sprites.gc, img, 0, 0,
+ self.rect.x + self._dx[i],
+ self.rect.y + self._dy[i],
+ -1, -1)
+ if len(self.labels) > 0:
+ self.draw_label()
+
+ def hit(self, pos):
+ """ Is (x, y) on top of the sprite? """
+ x, y = pos
+ if x < self.rect.x:
+ return False
+ if x > self.rect.x + self.rect.width:
+ return False
+ if y < self.rect.y:
+ return False
+ if y > self.rect.y + self.rect.height:
+ return False
+ return True
+
+ def draw_label(self):
+ """ Draw the label based on its attributes """
+ if self._sprites is None:
+ return
+ my_width = self.rect.width - self._margins[0] - self._margins[2]
+ if my_width < 0:
+ my_width = 0
+ my_height = self.rect.height - self._margins[1] - self._margins[3]
+ for i in range(len(self.labels)):
+ pl = self._sprites.canvas.create_pango_layout(str(self.labels[i]))
+ self._fd.set_size(int(self._scale[i] * pango.SCALE))
+ pl.set_font_description(self._fd)
+ w = pl.get_size()[0] / pango.SCALE
+ if w > my_width:
+ if self._rescale[i]:
+ self._fd.set_size(
+ int(self._scale[i] * pango.SCALE * my_width / w))
+ pl.set_font_description(self._fd)
+ w = pl.get_size()[0] / pango.SCALE
+ else:
+ j = len(self.labels[i]) - 1
+ while(w > my_width and j > 0):
+ pl = self._sprites.canvas.create_pango_layout(
+ "…" + self.labels[i][len(self.labels[i]) - j:])
+ self._fd.set_size(int(self._scale[i] * pango.SCALE))
+ pl.set_font_description(self._fd)
+ w = pl.get_size()[0] / pango.SCALE
+ j -= 1
+ if self._horiz_align[i] == "center":
+ x = int(self.rect.x + self._margins[0] + (my_width - w) / 2)
+ elif self._horiz_align[i] == 'left':
+ x = int(self.rect.x + self._margins[0])
+ else: # right
+ x = int(self.rect.x + self.rect.width - w - self._margins[2])
+ h = pl.get_size()[1] / pango.SCALE
+ if self._vert_align[i] == "middle":
+ y = int(self.rect.y + self._margins[1] + (my_height - h) / 2)
+ elif self._vert_align[i] == "top":
+ y = int(self.rect.y + self._margins[1])
+ else: # bottom
+ y = int(self.rect.y + self.rect.height - h - self._margins[3])
+ self._sprites.gc.set_foreground(self._color)
+ self._sprites.area.draw_layout(self._sprites.gc, x, y, pl)
+
+ def label_width(self):
+ """ Calculate the width of a label """
+ max = 0
+ for i in range(len(self.labels)):
+ pl = self._sprites.canvas.create_pango_layout(self.labels[i])
+ self._fd.set_size(int(self._scale[i] * pango.SCALE))
+ pl.set_font_description(self._fd)
+ w = pl.get_size()[0] / pango.SCALE
+ if w > max:
+ max = w
+ return max
+
+ def label_safe_width(self):
+ """ Return maximum width for a label """
+ return self.rect.width - self._margins[0] - self._margins[2]
+
+ def label_safe_height(self):
+ """ Return maximum height for a label """
+ return self.rect.height - self._margins[1] - self._margins[3]
+
+ def label_left_top(self):
+ """ Return the upper-left corner of the label safe zone """
+ return(self._margins[0], self._margins[1])
+
+ def get_pixel(self, pos, i=0, mode='888'):
+ """ Return the pixel at (x, y) """
+ x, y = pos
+ x = x - self.rect.x
+ y = y - self.rect.y
+ if isinstance(self.images[i], gtk.gdk.Pixbuf):
+ if y > self.images[i].get_height() - 1:
+ return(-1, -1, -1, -1)
+ array = self.images[i].get_pixels()
+ if array is not None:
+ try:
+ if self.images[i].get_has_alpha():
+ offset = (y * self.images[i].get_width() + x) * 4
+ a = ord(array[offset + 3])
+ else:
+ offset = (y * self.images[i].get_width() + x) * 3
+ a = 255
+ r = ord(array[offset])
+ g = ord(array[offset + 1])
+ b = ord(array[offset + 2])
+ return(r, g, b, a)
+ except IndexError:
+ """
+ print "Index Error: %d %d (%d, %d) (w: %d, h: %d) (%dx%d)"\
+ % (len(array), offset, x, y,
+ self.images[i].get_width(),
+ self.images[i].get_height(),
+ self.rect.width, self.rect.height)
+ """
+ pass
+ return(-1, -1, -1, -1)
+ else:
+ w, h = self.images[i].get_size()
+ if x < 0 or x > (w - 1) or y < 0 or y > (h - 1):
+ return(-1, -1, -1, -1)
+ image = self.images[i].get_image(x, y, 1, 1)
+ pixel = image.get_pixel(0, 0)
+ visual = self.images[i].get_visual()
+ r = int((pixel & visual.red_mask) >> visual.red_shift)
+ g = int((pixel & visual.green_mask) >> visual.green_shift)
+ b = int((pixel & visual.blue_mask) >> visual.blue_shift)
+ # Rescale to 8 bits
+ if mode == '565':
+ r = r << 3
+ g = g << 2
+ b = b << 3
+ return(r, g, b, 0)