Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--PathsActivity.py139
-rw-r--r--activity/activity-paths.svg37
-rw-r--r--activity/activity.info8
-rw-r--r--card.py69
-rw-r--r--deck.py140
-rw-r--r--game.py256
-rwxr-xr-xgenpieces.py208
-rw-r--r--grid.py147
-rwxr-xr-xpath.py68
-rw-r--r--sprites.py453
10 files changed, 1525 insertions, 0 deletions
diff --git a/PathsActivity.py b/PathsActivity.py
new file mode 100644
index 0000000..3e97940
--- /dev/null
+++ b/PathsActivity.py
@@ -0,0 +1,139 @@
+#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 sugar
+from sugar.activity import activity
+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 sugar.graphics.icon import Icon
+from sugar.datastore import datastore
+
+from gettext import gettext as _
+import locale
+import os.path
+
+from game import Game
+
+SERVICE = 'org.sugarlabs.PathsActivity'
+IFACE = SERVICE
+PATH = '/org/augarlabs/PathsActivity'
+
+
+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 PathsActivity(activity.Activity):
+ """ Path puzzle game """
+
+ def __init__(self, handle):
+ """ Initialize the toolbars and the game board """
+ super(PathsActivity,self).__init__(handle)
+
+ self._setup_toolbars(_have_toolbox)
+
+ # Create a canvas
+ canvas = gtk.DrawingArea()
+ canvas.set_size_request(gtk.gdk.screen_width(), \
+ gtk.gdk.screen_height())
+ self.set_canvas(canvas)
+ canvas.show()
+ self.show_all()
+
+ self.game = Game(canvas, self)
+ self.game.new_game()
+
+ def _setup_toolbars(self, have_toolbox):
+ """ Setup the toolbars.. """
+
+ if have_toolbox:
+ toolbox = ToolbarBox()
+
+ # Activity toolbar
+ activity_button = ActivityToolbarButton(self)
+
+ toolbox.toolbar.insert(activity_button, 0)
+ activity_button.show()
+
+ self.set_toolbar_box(toolbox)
+ toolbox.show()
+ toolbar = toolbox.toolbar
+
+ else:
+ # Use pre-0.86 toolbar design
+ games_toolbar = gtk.Toolbar()
+ toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(toolbox)
+ toolbox.add_toolbar(_('Game'), games_toolbar)
+ toolbox.show()
+ toolbox.set_current_toolbar(1)
+ toolbar = games_toolbar
+
+ 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()
diff --git a/activity/activity-paths.svg b/activity/activity-paths.svg
new file mode 100644
index 0000000..0ce63ea
--- /dev/null
+++ b/activity/activity-paths.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(-133.34375,-475.4375)">
+ <path
+ d="m 138.21806,502.9375 45.25138,0"
+ fill="none;" stroke="&stroke_color;" stroke-width="10" stroke-linecap="square" />
+ <path
+ d="m 160.84375,525.57355 0,-45.2721"
+ fill="none;" stroke="&stroke_color;" stroke-width="10" stroke-linecap="square" />
+ <path
+ d="m 138.21806,503.00075 45.25138,0"
+ fill="none;" stroke="&stroke_color;" stroke-width="10" stroke-linecap="square" />
+ <g
+ transform="matrix(1.0,0,0,1.0,-0.62570935,0.77971155)"
+ stroke="&stroke_color;">
+ <path
+ d="m 138.90476,502.66172 21.98482,0"
+ fill="none;" stroke="&stroke_color;" stroke-width="10" stroke-linecap="square" />
+ <path
+ d="m 160.88958,524.66172 0,-22"
+ fill="none;" stroke="&stroke_color;" stroke-width="10" stroke-linecap="square" />
+ </g>
+ <path
+ d="m 138.34375,502.93751 18.46999,0"
+ fill="none;" stroke="&stroke_color;" stroke-width="10" stroke-linecap="square" />
+ <path
+ d="M 0,27.5 27.5,0"
+ transform="translate(133.34375,475.4375)"
+ fill="none;" stroke="&fill_color;" stroke-width="10" stroke-linecap="square" />
+ <path
+ d="m 160.84375,530.4375 27.5,-27.5"
+ fill="none;" stroke="&fill_color;" stroke-width="10" stroke-linecap="square" />
+ </g>
+</svg>
diff --git a/activity/activity.info b/activity/activity.info
new file mode 100644
index 0000000..c498a46
--- /dev/null
+++ b/activity/activity.info
@@ -0,0 +1,8 @@
+[Activity]
+name = Paths
+activity_version = 1
+license = GPLv3
+bundle_id = org.sugarlabs.PathsActivity
+exec = sugar-activity PathsActivity.PathsActivity
+icon = activity-paths
+show_launcher = yes
diff --git a/card.py b/card.py
new file mode 100644
index 0000000..d506225
--- /dev/null
+++ b/card.py
@@ -0,0 +1,69 @@
+#Copyright (c) 2009-11 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
+from sprites import Sprite
+
+N = 0
+E = N + 1
+S = E + 1
+W = S + 1
+
+
+class Card:
+
+ def __init__(self, sprites, svg_string, card_type='tile'):
+ self.spr = Sprite(sprites, 0, 0, svg_str_to_pixbuf(svg_string))
+ self.connections = [] # [N, E, S, W]
+ self.card_type = card_type
+
+ def set_connections(self, connections):
+ self.connections = connections[:]
+
+ def get_connections(self):
+ return self.connections
+
+ def rotate_clockwise(self):
+ """ rotate the card and its connections """
+ west = self.connections[W]
+ self.connections[W] = self.connections[S]
+ self.connections[S] = self.connections[E]
+ self.connections[E] = self.connections[N]
+ self.connections[N] = west
+ self.spr.images[0] = self.spr.images[0].rotate_simple(270)
+ self.spr.draw()
+
+ def show_card(self):
+ self.spr.set_layer(2000)
+ self.spr.draw()
+
+ def hide_card(self):
+ self.spr.hide()
+
+
+#
+# Load pixbuf from SVG string
+#
+def svg_str_to_pixbuf(svg_string):
+ pl = gtk.gdk.PixbufLoader('svg')
+ pl.write(svg_string)
+ pl.close()
+ pixbuf = pl.get_pixbuf()
+ return pixbuf
+
+#
+# Create an error card
+#
+from genpieces import generate_x
+
+def error_card(sprites):
+ return Sprite(sprites, 0, 0, svg_str_to_pixbuf(generate_x(0.5)))
diff --git a/deck.py b/deck.py
new file mode 100644
index 0000000..21039f1
--- /dev/null
+++ b/deck.py
@@ -0,0 +1,140 @@
+#Copyright (c) 2009-11 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
+from random import randrange
+
+from card import Card
+from genpieces import generate_tile_1_line, generate_tile_2_lines, \
+ generate_board
+
+
+class Deck:
+ ''' Class for defining deck of card '''
+
+ def __init__(self, sprites, scale):
+ ''' Create the deck of cards. '''
+ self.cards = []
+ for a in range(16):
+ self.cards.append(Card(sprites, generate_tile_1_line(
+ -1, 0, 0, 0, scale)))
+ self.cards[-1].set_connections([0, 0, 0, 1])
+ for a in range(4):
+ self.cards.append(Card(sprites, generate_tile_1_line(
+ -1, 0, 1, 0, scale)))
+ self.cards[-1].set_connections([0, 1, 0, 1])
+ for a in range(12):
+ self.cards.append(Card(sprites, generate_tile_2_lines(
+ -1, 0, 1, 0, 0, 0, 0, 1, scale)))
+ self.cards[-1].set_connections([0, 1, 1, 1])
+ for a in range(16):
+ self.cards.append(Card(sprites, generate_tile_2_lines(
+ -1, 0, 0, 0, 0, -1, 0, 0, scale)))
+ self.cards[-1].set_connections([1, 0, 0, 1])
+ for a in range(4):
+ self.cards.append(Card(sprites, generate_tile_2_lines(
+ -1, 0, 1, 0, 0, -1, 0, 1, scale)))
+ self.cards[-1].set_connections([1, 1, 1, 1])
+ for a in range(8):
+ self.cards.append(Card(sprites, generate_tile_2_lines(
+ -1, 0, 0, 1, 0, -1, 1, 0, scale)))
+ self.cards[-1].set_connections([1, 1, 1, 1])
+ for a in range(4):
+ self.cards.append(Card(sprites, generate_tile_2_lines(
+ -1, 0, 0, 0, 0, -1, 1, 0, scale)))
+ self.cards[-1].set_connections([1, 1, 0, 1])
+ # Remember the current position in the deck.
+ self.index = 0
+
+ # And a playing surface
+ self.board = Card(sprites, generate_board(scale), card_type='board')
+ self.board.spr.set_layer(1)
+
+ def shuffle(self):
+ ''' Shuffle the deck (Knuth algorithm). '''
+ decksize = self.count()
+ # Hide all the cards.
+ for c in self.cards:
+ c.hide_card()
+ # Randomize the card order.
+ for n in range(decksize):
+ i = randrange(decksize - n)
+ self.swap_cards(n, decksize - 1 - i)
+ # Reset the index to the beginning of the deck after a shuffle,
+ self.index = 0
+ self.hide()
+ return
+
+ def restore(self, saved_deck_indices):
+ ''' Restore the deck upon resume. '''
+ decksize = len(saved_deck_indices)
+ # If we have a short deck, then we need to abort.
+ if self.count() < decksize:
+ return False
+ _deck = []
+ for i in saved_deck_indices:
+ _deck.append(self.index_to_card(i))
+ for i in range(decksize):
+ self.cards[i] = _deck[i]
+ return True
+
+ def swap_cards(self,i,j):
+ ''' Swap the position of two cards in the deck. '''
+ tmp = self.cards[j]
+ self.cards[j] = self.cards[i]
+ self.cards[i] = tmp
+ return
+
+ def spr_to_card(self, spr):
+ ''' Given a sprite, find the corresponding card in the deck. '''
+ if spr == self.board.spr:
+ return self.board
+ for c in self.cards:
+ if c.spr == spr:
+ return c
+ return None
+
+ def index_to_card(self, i):
+ ''' Given a card index, find the corresponding card in the deck. '''
+ for c in self.cards:
+ if c.index == i:
+ return c
+ return None
+
+ def deal_next_card(self):
+ ''' Return the next card from the deck. '''
+ if self.empty():
+ return None
+ next_card = self.cards[self.index]
+ self.index += 1
+ return next_card
+
+ def empty(self):
+ ''' Is the deck empty? '''
+ if self.cards_remaining() > 0:
+ return False
+ else:
+ return True
+
+ def cards_remaining(self):
+ ''' Return how many cards are remaining in the deck. '''
+ return(self.count()-self.index)
+
+ def hide(self):
+ ''' Hide the deck. '''
+ for c in self.cards:
+ if c is not None:
+ c.hide_card()
+
+ def count(self):
+ ''' Return the length of the deck. '''
+ return len(self.cards)
diff --git a/game.py b/game.py
new file mode 100644
index 0000000..8f339b8
--- /dev/null
+++ b/game.py
@@ -0,0 +1,256 @@
+# -*- 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
+from gettext import gettext as _
+import logging
+_logger = logging.getLogger('paths-activity')
+
+try:
+ from sugar.graphics import style
+ GRID_CELL_SIZE = style.GRID_CELL_SIZE
+except ImportError:
+ GRID_CELL_SIZE = 0
+
+from grid import Grid
+from deck import Deck
+from card import error_card
+from sprites import Sprites
+
+N = 0
+E = N + 1
+S = E + 1
+W = S + 1
+CARD_WIDTH = 55
+CARD_HEIGHT = 55
+ROW = 8
+COL = 8
+
+
+class Game():
+
+ def __init__(self, canvas, parent=None):
+ self.activity = parent
+
+ # Starting from command line
+ if parent is None:
+ self.sugar = False
+ self.canvas = canvas
+ # Starting from Sugar
+ else:
+ self.sugar = True
+ self.canvas = canvas
+ parent.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.connect("expose-event", self._expose_cb)
+ self.canvas.connect("button-press-event", self._button_press_cb)
+ self.canvas.connect("button-release-event", self._button_release_cb)
+ self.canvas.connect("key_press_event", self._keypress_cb)
+ self.width = gtk.gdk.screen_width()
+ self.height = gtk.gdk.screen_height()-GRID_CELL_SIZE
+ self.scale = self.height / (8.0 * CARD_HEIGHT)
+ self.card_width = CARD_WIDTH * self.scale
+ self.card_height = CARD_HEIGHT * self.scale
+ self.sprites = Sprites(self.canvas)
+ self.last_spr_moved = []
+ self.errormsg = []
+
+ for i in range(4):
+ self.errormsg.append(error_card(self.sprites))
+
+ def new_game(self, saved_state=None, deck_index=0):
+ ''' Start a new game. '''
+
+ # If there is already a deck, hide it.
+ if hasattr(self, 'deck'):
+ self.deck.hide()
+
+ # Initialize the grid and create a deck of cards.
+ if not hasattr(self, 'grid'):
+ self.grid = Grid(self.width, self.height, self.card_width,
+ self.card_height)
+
+ if not hasattr(self, 'deck'):
+ self.deck = Deck(self.sprites, self.scale)
+ self.deck.board.spr.move((self.grid.left, self.grid.top))
+
+ # Shuffle the deck and deal a hand of tiles.
+ self.deck.shuffle()
+ self.grid.deal(self.deck)
+ self.last_spr_move = [None]
+ self._hide_errormsgs()
+
+ def _button_press_cb(self, win, event):
+ win.grab_focus()
+ x, y = map(int, event.get_coords())
+ self.start_drag = [x, y]
+
+ self._hide_errormsgs()
+
+ spr = self.sprites.find_sprite((x, y))
+ if spr is None or spr == self.deck.board.spr:
+ self.press = None
+ self.release = None
+ return True
+ if self.grid.spr_to_hand(spr) is not None:
+ self.last_spr_moved.append(spr)
+ if spr != self.last_spr_moved[-1]:
+ self.press = None
+ self.release = None
+ return True
+ self.press = spr
+ return True
+
+ def _button_release_cb(self, win, event):
+ win.grab_focus()
+
+ if self.press is None:
+ return
+
+ x, y = map(int, event.get_coords())
+ spr = self.sprites.find_sprite((x, y))
+
+ if spr is None: # Returning tile to hand
+ i = self.grid.find_empty_slot()
+ if i is not None:
+ card = self.deck.spr_to_card(self.press)
+ card.spr.move(self.grid.hand_to_xy(i))
+ self.grid.hand[i] = card
+ if self.grid.spr_to_grid(self.press) is not None:
+ self.grid.grid[self.grid.spr_to_grid(self.press)] = None
+ if spr in self.last_spr_moved:
+ self.last_spr_moved.remove(spr)
+ self.press = None
+ self.release = None
+ return True
+
+ self.release = spr
+ if self.press == self.release:
+
+ card = self.deck.spr_to_card(spr)
+ card.rotate_clockwise()
+
+ if self.last_spr_moved[-1] != card.spr:
+ self.last_spr_moved.append(card.spr)
+
+ elif self.release == self.deck.board.spr:
+ card = self.deck.spr_to_card(self.press)
+ card.spr.move(self.grid.grid_to_xy(self.grid.xy_to_grid(x, y)))
+
+ i = self.grid.spr_to_grid(self.press)
+ if i is not None:
+ self.grid.grid[i] = None
+ self.grid.grid[self.grid.xy_to_grid(x, y)] = card
+
+ i = self.grid.spr_to_hand(self.press)
+ if i is not None:
+ self.grid.hand[i] = None
+
+ if self.last_spr_moved[-1] != card.spr:
+ self.last_spr_moved.append(card.spr)
+
+ self._test_for_bad_paths()
+ self.press = None
+ self.release = None
+
+ if self.grid.cards_in_hand() == 0:
+ self.grid.redeal(self.deck)
+ return True
+
+ def _game_over(self):
+ pass
+
+ def _test_for_bad_paths(self):
+ ''' Is there a path to no where? '''
+ i = self.grid.spr_to_grid(self.press)
+ if i is not None:
+ self._check_north(i)
+ self._check_east(i)
+ self._check_south(i)
+ self._check_west(i)
+
+ def _check_north(self, i):
+ # Is it in the top row?
+ if int(i / COL) == 0:
+ if self.grid.grid[i].connections[N] == 1:
+ self._error(i, N)
+ else:
+ if self.grid.grid[i-COL] is not None:
+ if self.grid.grid[i].connections[N] != \
+ self.grid.grid[i-COL].connections[S]:
+ self._error(i, N)
+
+ def _check_east(self, i):
+ # Is it in the right column?
+ if int(i % ROW) == ROW - 1:
+ if self.grid.grid[i].connections[E] == 1:
+ self._error(i, E)
+ else:
+ if self.grid.grid[i+1] is not None:
+ if self.grid.grid[i].connections[E] != \
+ self.grid.grid[i+1].connections[W]:
+ self._error(i, E)
+
+ def _check_south(self, i):
+ # Is it in the bottom row?
+ if int(i / COL) == COL - 1:
+ if self.grid.grid[i].connections[S] == 1:
+ self._error(i, S)
+ else:
+ if self.grid.grid[i+COL] is not None:
+ if self.grid.grid[i].connections[S] != \
+ self.grid.grid[i+COL].connections[N]:
+ self._error(i, S)
+
+ def _check_west(self, i):
+ # Is it in the left column?
+ if int(i % ROW) == 0:
+ if self.grid.grid[i].connections[W] == 1:
+ self._error(i, W)
+ else:
+ if self.grid.grid[i-1] is not None:
+ if self.grid.grid[i].connections[W] != \
+ self.grid.grid[i-1].connections[E]:
+ self._error(i, W)
+
+ def _error(self, i, direction):
+ ''' Display an error message where and when appropriate. '''
+ offsets = [[0.375, -0.125], [0.875, 0.375], [0.375, 0.875],
+ [-0.125, 0.375]]
+ x, y = self.press.get_xy()
+ self.errormsg[direction].move(
+ (x + offsets[direction][0] * self.card_width,
+ y + offsets[direction][1] * self.card_height))
+ self.errormsg[direction].set_layer(3000)
+
+ def _hide_errormsgs(self):
+ ''' Hide all the error messages. '''
+ for i in range(4):
+ self.errormsg[i].move((self.grid.left, self.grid.top))
+ self.errormsg[i].set_layer(0)
+
+ #
+ # Callbacks
+ #
+ def _keypress_cb(self, area, event):
+ 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/genpieces.py b/genpieces.py
new file mode 100755
index 0000000..d365e78
--- /dev/null
+++ b/genpieces.py
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#Copyright (c) 2009,10 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 os
+
+#
+# SVG generators
+#
+class SVG:
+ def __init__(self):
+ self._scale = 1
+ self._stroke_width = 1
+ self._fill = "#FF0000"
+ self._stroke = "A00000"
+
+ def _svg_style(self, extras=""):
+ return "%s%s%s%s%s%f%s%s%s" % ("style=\"fill:", self._fill, ";stroke:",
+ self._stroke, ";stroke-width:",
+ self._stroke_width, ";", extras,
+ "\" />\n")
+
+ def _svg_line(self, x1, y1, x2, y2):
+ svg_string = "<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\"\n" % \
+ (x1, y1, x2, y2)
+ svg_string += self._svg_style("stroke-linecap:square;")
+ return svg_string
+
+ def _svg_rect(self, w, h, rx, ry, x, y):
+ svg_string = " <rect\n"
+ svg_string += " width=\"%f\"\n" % (w)
+ svg_string += " height=\"%f\"\n" % (h)
+ svg_string += " rx=\"%f\"\n" % (rx)
+ svg_string += " ry=\"%f\"\n" % (ry)
+ svg_string += " x=\"%f\"\n" % (x)
+ svg_string += " y=\"%f\"\n" % (y)
+ self.set_stroke_width(1.0)
+ svg_string += self._svg_style()
+ return svg_string
+
+ def _svg_x(self, w, h):
+ self.set_stroke_width(10.0)
+ svg_string = self._svg_line(0, 0, w, h)
+ svg_string += self._svg_line(0, h, w, 0)
+ return svg_string
+
+ def _background(self, scale):
+ return self._svg_rect(54.5 * scale, 54.5 * scale, 4, 4, 0.25, 0.25)
+
+ def header(self, scale=1, background=True):
+ svg_string = "<?xml version=\"1.0\" encoding=\"UTF-8\""
+ svg_string += " standalone=\"no\"?>\n"
+ svg_string += "<!-- Created with Emacs -->\n"
+ svg_string += "<svg\n"
+ svg_string += " xmlns:svg=\"http://www.w3.org/2000/svg\"\n"
+ svg_string += " xmlns=\"http://www.w3.org/2000/svg\"\n"
+ svg_string += " version=\"1.0\"\n"
+ svg_string += "%s%f%s" % (" width=\"", scale * 55 * self._scale,
+ "\"\n")
+ svg_string += "%s%f%s" % (" height=\"", scale * 55 * self._scale,
+ "\">\n")
+ svg_string += "%s%f%s%f%s" % ("<g\n transform=\"matrix(",
+ self._scale, ",0,0,", self._scale,
+ ",0,0)\">\n")
+ if background:
+ svg_string += self._background(scale)
+ return svg_string
+
+ def footer(self):
+ svg_string = "</g>\n"
+ svg_string += "</svg>\n"
+ return svg_string
+
+ #
+ # Utility functions
+ #
+ def set_scale(self, scale=1.0):
+ self._scale = scale
+
+ def set_colors(self, colors):
+ self._stroke = colors[0]
+ self._fill = colors[1]
+
+ def set_stroke_width(self, stroke_width=1.0):
+ self._stroke_width = stroke_width
+
+ #
+ # Card pattern generators
+ #
+
+ def path(self, a, b, c, d):
+ x1 = a * 27.5 + 27.5
+ y1 = b * 27.5 + 27.5
+ x2 = c * 27.5 + 27.5
+ y2 = d * 27.5 + 27.5
+ self.set_stroke_width(10)
+ svg_string = self._svg_line(x1, y1, x2, y2)
+ return svg_string
+
+#
+# Card generators
+#
+def generate_x(scale=1):
+ svg = SVG()
+ svg.set_scale(scale)
+ svg.set_colors(["#FF0000", "#FF0000"])
+ svg_string = svg.header(background=False)
+ svg_string += svg._svg_x(55, 55)
+ svg_string += svg.footer()
+ return svg_string
+
+def generate_board(scale=1):
+ svg = SVG()
+ svg.set_scale(scale)
+ svg.set_colors(["#000000", "#FFFFFF"])
+ svg_string = svg.header(scale=8) # board is 8x8 tiles
+ svg_string += svg.footer()
+ return svg_string
+
+def generate_tile_1_line(a, b, c, d, scale=1):
+ svg = SVG()
+ svg.set_scale(scale)
+ svg.set_colors(["#000000", "#FFFFFF"])
+ svg_string = svg.header()
+ svg_string += svg.path(a, b, c, d)
+ svg_string += svg.footer()
+ return svg_string
+
+def generate_tile_2_lines(a, b, c, d, e, f, g, h, scale=1):
+ svg = SVG()
+ svg.set_scale(scale)
+ svg.set_colors(["#000000", "#FFFFFF"])
+ svg_string = svg.header()
+ svg_string += svg.path(a, b, c, d)
+ svg_string += svg.path(e, f, g, h)
+ svg_string += svg.footer()
+ return svg_string
+
+#
+# Command line utilities used for testing purposed only
+#
+def open_file(datapath, filename):
+ return file(os.path.join(datapath, filename), "w")
+
+def close_file(f):
+ f.close()
+
+def generator(datapath):
+ i = 0
+ filename = "tile-%d.svg" % (i)
+ f = open_file(datapath, filename)
+ f.write(generate_tile_1_line(-1, 0, 0, 0))
+ close_file(f)
+ i += 1
+ filename = "tile-%d.svg" % (i)
+ f = open_file(datapath, filename)
+ f.write(generate_tile_1_line(-1, 0, 1, 0))
+ i += 1
+ close_file(f)
+ filename = "tile-%d.svg" % (i)
+ f = open_file(datapath, filename)
+ f.write(generate_tile_2_lines(-1, 0, 1, 0, 0, 0, 0, 1))
+ i += 1
+ close_file(f)
+ filename = "tile-%d.svg" % (i)
+ f = open_file(datapath, filename)
+ f.write(generate_tile_2_lines(-1, 0, 0, 0, 0, -1, 0, 0))
+ i += 1
+ close_file(f)
+ filename = "tile-%d.svg" % (i)
+ f = open_file(datapath, filename)
+ f.write(generate_tile_2_lines(-1, 0, 1, 0, 0, -1, 0, 1))
+ i += 1
+ close_file(f)
+ filename = "tile-%d.svg" % (i)
+ f = open_file(datapath, filename)
+ f.write(generate_tile_2_lines(-1, 0, 0, 1, 0, -1, 1, 0))
+ i += 1
+ close_file(f)
+ filename = "tile-%d.svg" % (i)
+ f = open_file(datapath, filename)
+ f.write(generate_tile_2_lines(-1, 0, 0, 0, 0, -1, 1, 0))
+ i += 1
+ close_file(f)
+ f = open_file(datapath, 'x.svg')
+ f.write(generate_x())
+ i += 1
+ close_file(f)
+
+def main():
+ return 0
+
+if __name__ == "__main__":
+ if not os.path.exists(os.path.join(os.path.abspath('.'), 'images')):
+ os.mkdir(os.path.join(os.path.abspath('.'), 'images'))
+ generator(os.path.join(os.path.abspath('.'), 'images'))
+ main()
diff --git a/grid.py b/grid.py
new file mode 100644
index 0000000..abf08d7
--- /dev/null
+++ b/grid.py
@@ -0,0 +1,147 @@
+#Copyright (c) 2009-11 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
+
+from deck import Deck
+
+ROW = 8
+COL = 8
+
+
+class Grid:
+ ''' Class for managing ROWxCOL matrix of cards '''
+
+ def __init__(self, width, height, card_width, card_height):
+ # the playing surface
+ self.grid = []
+
+ # the tiles in your hand
+ self.hand = []
+
+ for i in range(ROW * COL):
+ self.grid.append(None)
+
+ for i in range(COL):
+ self.hand.append(None)
+
+ # card spacing
+ self.left_hand = int(card_width / 2)
+ self.left = int((width - (card_width * COL)) / 2 + card_width)
+ self.xinc = int(card_width)
+ self.top = 0
+ self.yinc = int(card_height)
+
+ def deal(self, deck):
+ ''' Deal an initial set of cards to the hand '''
+ for i in range(COL):
+ self.hand[i] = deck.deal_next_card()
+ self.place_a_card(self.hand[i], self.hand_to_xy(i)[0],
+ self.hand_to_xy(i)[1])
+
+ # and empty the grid
+ for i in range(ROW * COL):
+ self.grid[i] = None
+
+ def redeal(self, deck):
+ ''' Deal another set of cards to the hand '''
+ for i in range(COL):
+ self.hand[i] = deck.deal_next_card()
+ self.place_a_card(self.hand[i], self.hand_to_xy(i)[0],
+ self.hand_to_xy(i)[1])
+
+ def find_empty_slot(self):
+ ''' Is there an empty slot in the hand? '''
+ for i in range(COL):
+ if self.hand[i] == None:
+ return i
+ return None
+
+ def cards_in_hand(self):
+ ''' How many cards are in the hand? '''
+ return COL - self.hand.count(None)
+
+ def cards_in_grid(self):
+ ''' How many cards are on the grid? '''
+ return ROW * COL - self.grid.count(None)
+
+ def restore(self, deck, saved_card_index):
+ ''' Restore cards to grid upon resume or share. '''
+ # TODO: restore hand too
+ self.hide()
+ j = 0
+ for i in saved_card_index:
+ if i is None:
+ self.grid[j] = None
+ else:
+ self.grid[j] = deck.index_to_card(i)
+ j += 1
+ self.show()
+
+ def place_a_card(self, c, x, y):
+ ''' Place a card at position x,y and display it. '''
+ if c is not None:
+ c.spr.move((x, y))
+ c.spr.set_layer(2000)
+
+ def xy_to_grid(self, x, y):
+ ''' Convert from sprite x,y to grid index. '''
+ return COL * int((y - self.top) / self.yinc) + \
+ int((x - self.left) / self.xinc)
+
+ def xy_to_hand(self, x, y):
+ ''' Convert from sprite x,y to hand index. '''
+ return int((y - self.top) / self.yinc)
+
+ def grid_to_xy(self, i):
+ ''' Convert from grid index to sprite x,y. '''
+ return (int((self.left + i % COL * self.xinc)),
+ int((self.top + (i / COL) * self.yinc)))
+
+ def hand_to_xy(self, i):
+ ''' Convert from hand index to sprite x,y. '''
+ return ((self.left_hand, (self.top + i * self.yinc)))
+
+ def grid_to_spr(self, i):
+ ''' Return the sprite in grid-position i. '''
+ return self.grid[i].spr
+
+ def hand_to_spr(self, i):
+ ''' Return the sprite in hand-position i. '''
+ return self.hand[i].spr
+
+ def spr_to_grid(self, spr):
+ ''' Return the index of a sprite in grid. '''
+ for i in range(ROW * COL):
+ if self.grid[i] is not None and self.grid[i].spr == spr:
+ return(i)
+ return None
+
+ def spr_to_hand(self, spr):
+ ''' Return the index of a sprite in hand. '''
+ for i in range(COL):
+ if self.hand[i] is not None and self.hand[i].spr == spr:
+ return(i)
+ return None
+
+ def hide(self):
+ ''' Hide all of the cards on the grid. '''
+ for i in range(ROW * COL):
+ if self.grid[i] is not None:
+ self.grid[i].hide_card()
+
+ def show(self):
+ ''' Restore all card on the grid to their x,y positions. '''
+ for i in range(ROW * COL):
+ self.place_a_card(self.grid[i],self.grid_to_xy(i)[0],
+ self.grid_to_xy(i)[1])
+
diff --git a/path.py b/path.py
new file mode 100755
index 0000000..f498ab3
--- /dev/null
+++ b/path.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+
+#Copyright (c) 2009,10 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
+
+from gettext import gettext as _
+import os
+
+from game import Game
+
+
+class PathMain:
+ def __init__(self):
+ self.r = 0
+ self.tw = None
+
+ # create a new window
+ self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ self.win.maximize()
+ self.win.set_title("%s: %s" % (_("Paths"),
+ _("Move tiles to make a path.")))
+ self.win.connect("delete_event", lambda w,e: gtk.main_quit())
+
+ # A vbox to put a menu and the canvas in
+ vbox = gtk.VBox(False, 0)
+ self.win.add(vbox)
+ vbox.show()
+
+ canvas = gtk.DrawingArea()
+ vbox.pack_end(canvas, True, True)
+ canvas.show()
+
+ self.win.show_all()
+
+ # Join the activity
+ self.vmw = Game(canvas, os.path.join(os.path.abspath('.'),
+ 'images/'))
+ self.vmw.win = self.win
+ self.vmw.activity = self
+ self.vmw.level = 12
+
+ self.vmw.new_game()
+
+ def set_title(self, title):
+ self.win.set_title(title)
+
+ def _new_game_cb(self, widget, game):
+ self.vmw.new_game()
+ return True
+
+def main():
+ gtk.main()
+ return 0
+
+if __name__ == "__main__":
+ PathMain()
+ main()
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)