Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/boardwidget.py
diff options
context:
space:
mode:
Diffstat (limited to 'boardwidget.py')
-rw-r--r--boardwidget.py244
1 files changed, 244 insertions, 0 deletions
diff --git a/boardwidget.py b/boardwidget.py
new file mode 100644
index 0000000..845520e
--- /dev/null
+++ b/boardwidget.py
@@ -0,0 +1,244 @@
+# -*- coding: UTF-8 -*-
+# Copyright 2007-2008 One Laptop Per Child
+# Copyright 2007 Gerard J. Cerchio <www.circlesoft.com>
+# Copyright 2008 Andrés Ambrois <andresambrois@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import gobject
+import gtk
+
+import logging
+
+logger = logging.getLogger('PlayGo.GoBoardWidget')
+
+class GoBoardWidget(gtk.Widget):
+ ''' A Go Board Widget '''
+
+ __gsignals__ = {
+ 'insert-requested': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_INT, gobject.TYPE_INT)),
+ }
+
+ def __init__(self, status, size=19):
+ gtk.Widget.__init__(self)
+
+ self.status = status
+ self.size = size
+
+ self.lastX = -1
+ self.lastY = -1
+
+
+ def do_realize(self):
+ """Called when the widget should create all of its
+ windowing resources. Create our gtk.gdk.Window
+ and load our pixmaps."""
+
+ # First set an internal flag telling that we're realized
+ self.set_flags(self.flags() | gtk.REALIZED)
+
+ # Create our window and set the event masks we need
+ self.window = gtk.gdk.Window(
+ self.get_parent_window(),
+ width=self.allocation.width,
+ height=self.allocation.height,
+ window_type=gtk.gdk.WINDOW_CHILD,
+ wclass=gtk.gdk.INPUT_OUTPUT,
+ event_mask=self.get_events() | gtk.gdk.EXPOSURE_MASK
+ | gtk.gdk.BUTTON1_MOTION_MASK | gtk.gdk.BUTTON_PRESS_MASK
+ | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK
+ | gtk.gdk.BUTTON_RELEASE_MASK)
+
+ # Asociate ourselves with this window
+ self.window.set_user_data(self)
+
+ # Set this window's style
+ self.style.attach(self.window)
+
+ # The default color of the background should be what
+ # the style (theme engine) tells us.
+ self.style.set_background(self.window, gtk.STATE_NORMAL)
+ self.window.move_resize(*self.allocation)
+
+ # Load the board pixmap
+ pixbuf = gtk.gdk.pixbuf_new_from_file("./images/board.gif")
+ self.BoardPixmap, mask = pixbuf.render_pixmap_and_mask()
+ del pixbuf
+
+ # Load the white stone pixmap
+ self.WhitePixbuf = gtk.gdk.pixbuf_new_from_file("./images/white.gif")
+
+ # Load the black stone pixmap
+ self.BlackPixbuf = gtk.gdk.pixbuf_new_from_file("./images/black.gif")
+
+ self.gc = self.style.fg_gc[gtk.STATE_NORMAL]
+
+ self.connect('button-release-event', self.button_release_cb)
+
+ def draw_lines(self):
+ ctx = self.window.cairo_create()
+
+ # Single width black lines
+ ctx.set_line_width(1)
+ ctx.set_source_rgba(0, 0, 0, 1)
+
+ # Horizontal lines
+ for i in xrange(1, self.size + 1):
+ ctx.move_to( self.unit, i * self.unit)
+ ctx.line_to(self.size * self.unit, i * self.unit )
+
+ # Vertical lines
+ for i in xrange(1, self.size + 1):
+ ctx.move_to(i * self.unit, self.unit )
+ ctx.line_to(i * self.unit, self.size * self.unit)
+
+ ctx.stroke()
+
+ # star point coords per board size
+ if self.size == 19 :
+ seq = [ 4, 10, 16 ]
+ elif self.size == 13 :
+ seq = [ 4, 7, 10 ]
+ elif self.size == 9 :
+ seq = [ 3, 7 ]
+ # set the middle singleton
+ ctx.arc( self.unit * 5, self.unit * 5, 3, 0, -1e-10)
+ ctx.fill_preserve()
+ ctx.stroke()
+ else :
+ seq = []
+
+ # stroke in the star points
+ #TODO: adjust size for teeny boards
+ for x in seq :
+ for y in seq :
+ ctx.arc( self.unit * x, self.unit * y, 3, 0, -1e-10)
+ ctx.fill_preserve()
+ ctx.stroke()
+
+
+ def do_unrealize(self):
+ # The do_unrealized method is responsible for freeing the GDK resources
+ # De-associate the window we created in do_realize with ourselves
+ self.window.destroy()
+
+ def do_size_request(self, requisition):
+ """From Widget.py: The do_size_request method Gtk+ is calling
+ on a widget to ask it the widget how large it wishes to be.
+ It's not guaranteed that gtk+ will actually give this size
+ to the widget. So we will send gtk+ an appropiate minimum size"""
+
+ requisition.height = 500
+ requisition.width = 500
+
+ def do_size_allocate(self, allocation):
+ """The do_size_allocate is called by when the actual
+ size is known and the widget is told how much space
+ could actually be allocated Save the allocated space
+ self.allocation = allocation."""
+
+ logger.debug('Allocating %s x %s for widget', allocation.height, allocation.width)
+ self.allocation = allocation
+ if self.flags() & gtk.REALIZED:
+ self.window.move_resize(*allocation)
+
+ def do_expose_event(self, event=None):
+ """This is where the widget must draw itself."""
+
+ #Scale everything
+ self.unit = (min(self.allocation.height, self.allocation.width)+10)/(self.size + 1)
+ if self.unit == 0:
+ return
+ self.BlackPixbuf = self.BlackPixbuf.scale_simple( int(self.unit), int(self.unit), gtk.gdk.INTERP_BILINEAR )
+ self.WhitePixbuf = self.WhitePixbuf.scale_simple( int(self.unit), int(self.unit), gtk.gdk.INTERP_BILINEAR )
+ #Draw the board
+ self.window.draw_drawable(self.gc, self.BoardPixmap, 0, 0, 0, 0, self.allocation.width, self.allocation.height)
+ #Draw the lines
+ self.draw_lines()
+ #Draw the stones
+ self.draw_stones(self.status)
+
+ def get_mouse_event_xy(self, event):
+ """
+ calculate the x and y position on the board given pixel address
+ """
+
+ x0 = 0 #self.get_allocation().x
+ y0 = 0 #self.get_allocation().y
+ x = int(( ( event.x - x0 ) / self.unit ) - 0.5)
+ y = int(( ( event.y - y0 ) / self.unit ) - 0.5)
+ if x > self.size - 1: x = self.size - 1
+ if y > self.size - 1: y = self.size - 1
+ return x, y
+
+ def draw_ghost_stone(self, x, y, color):
+ x, y = self.get_pixel_from_coordinates(x, y)
+ if x == self.lastX and y == self.lastY:
+ return
+
+ if self.lastX is not -1 :
+ self.window.invalidate_rect(gtk.gdk.Rectangle(int(self.lastX - self.unit/2), int(self.lastY - self.unit/2), int(self.unit), int(self.unit)), False)
+
+ self.lastX = x
+ self.lastY = y
+
+ ctx = self.window.cairo_create()
+ if color is 'B':
+ ctx.set_source_rgba(0, 0, 0, .5 )
+ else:
+ ctx.set_source_rgba(0xff, 0xff, 0xff, .5 )
+
+ ctx.arc( self.lastX, self.lastY, self.unit/2 -4, 0, -1e-10)
+ ctx.fill_preserve()
+ ctx.stroke()
+ del ctx
+
+ def button_release_cb(self, widget, event):
+ x, y = self.get_mouse_event_xy(event)
+ self.emit('insert-requested', x, y)
+
+ def draw_stone(self, x, y, color, widget):
+ """
+ paint a single stone on a point
+ """
+ x = x + 1
+ y = y + 1
+ ctx = self.window.cairo_create()
+ ct = gtk.gdk.CairoContext(ctx)
+ if color == 'B':
+ ct.set_source_pixbuf(self.BlackPixbuf, self.unit*x - self.unit/2, self.unit*y - self.unit/2 )
+ else:
+ ct.set_source_pixbuf(self.WhitePixbuf, self.unit*x - self.unit/2, self.unit*y - self.unit/2 )
+ ctx.paint()
+
+ def draw_stones(self, status):
+ for x in status.keys():
+ self.draw_stone(x[0], x[1], status[x], self)
+
+ def redraw_area(self, x, y):
+ x, y = self.get_pixel_from_coordinates(x, y)
+ self.window.invalidate_rect(gtk.gdk.Rectangle(int(x - self.unit/2), int(y - self.unit/2), int(self.unit), int(self.unit)), False)
+
+ def get_pixel_from_coordinates(self, x, y):
+ if x > self.size - 1: x = self.size - 1
+ if y > self.size - 1: y = self.size - 1
+ x = (x+1) * self.unit
+ y = (y+1) * self.unit
+ return x, y
+
+ def clear(self):
+ self.lastX = -1
+ self.lastY = -1
+ self.do_expose_event()