diff options
Diffstat (limited to 'drawing.py')
-rw-r--r-- | drawing.py | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/drawing.py b/drawing.py new file mode 100644 index 0000000..2850f4e --- /dev/null +++ b/drawing.py @@ -0,0 +1,181 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2012 Manuel QuiƱones +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from gettext import gettext as _ +import logging + +import gtk +from gtk import gdk +import cairo + + +# This prevents the expose callback to draw a large stroke each time: +STROKE_MAX_POINTS = 80 + + +class Drawing(gtk.DrawingArea): + def __init__(self, parent): + super(Drawing, self).__init__() + + # Communication with the activity, for sharing: + self._parent = parent + + # This are temporal points stored while the stroke is being + # made. When the stroke is finished (release event) the + # stroke is applied to the canvas. + self._stroke_points = [] + + # Our drawing canvas: + self._drawing_canvas = None + + # The settings of our brush: + self._settings = { + 'stroke color': (1.0, 0.0, 0.0, 0.3), + 'stroke width': 8, + } + + # Sharing? + self.we_are_sharing = False + + # The masks to capture the events we are interested in: + self.add_events(gdk.EXPOSURE_MASK | gdk.VISIBILITY_NOTIFY_MASK) + self.add_events(gtk.gdk.BUTTON_PRESS_MASK | \ + gtk.gdk.BUTTON_RELEASE_MASK | \ + gtk.gdk.BUTTON1_MOTION_MASK) + + # Connect the callbacks: + self.connect("expose-event", self._expose_cb) + self.connect("button-press-event", self._press_cb) + self.connect("motion-notify-event", self._motion_cb) + self.connect("button-release-event", self._release_cb) + + def setup(self, width, height): + """Setup a blank canvas of specified size.""" + logging.debug("drawing set up") + self._drawing_canvas = cairo.ImageSurface(cairo.FORMAT_ARGB32, + width, height) + context = cairo.Context(self._drawing_canvas) + context.rectangle(0, 0, width, height) + context.set_source_rgb(1.0, 1.0, 1.0) + context.fill() + self.queue_draw() + + def set_sharing(self, share=True): + """Set sharing True or False.""" + self.we_are_sharing = share + + def get_size(self): + """Return the size of the current canvas.""" + width = self._drawing_canvas.get_width() + height = self._drawing_canvas.get_height() + return width, height + + def remote_stroke(self, stroke_points): + """Draw stroke from other player.""" + context = cairo.Context(self._drawing_canvas) + self._set_stroke_context(context) + self._paint_stroke(context, stroke_points) + self.queue_draw() + + def clear_drawing_canvas(self): + width, height = self.get_size() + self.setup(width, height) + if self.we_are_sharing: + self._parent.send_new_drawing() + + def _set_stroke_context(self, context): + """Set the settings of our brush to the Cairo context.""" + context.set_source_rgba(*self._settings['stroke color']) + context.set_line_width(self._settings['stroke width']) + + def _paint_stroke(self, context, stroke_points): + """Draw lines from the list of points to the Cairo context""" + point_x, point_y = stroke_points[0] + context.move_to(point_x, point_y) + + for point_x, point_y in stroke_points[1:]: + context.line_to(point_x, point_y) + + context.stroke() + + def _send_stroke_to_drawing_canvas(self, continue_stroke=False): + """Paint current stroke in the canvas, clean stroke points.""" + + # Return if there is no stroke to paint: + if self._stroke_points == []: + return + + # Get context from canvas and paint: + context = cairo.Context(self._drawing_canvas) + self._set_stroke_context(context) + self._paint_stroke(context, self._stroke_points) + + if self.we_are_sharing: + self._parent.send_stroke(self._stroke_points) + + # Clean the list of points: + if continue_stroke: + self._stroke_points = self._stroke_points[-1:] + else: + self._stroke_points = [] + + def _press_cb(self, widget, event): + mouse_x, mouse_y, state = event.window.get_pointer() + + # We paint pressing the BUTTON1, return otherwise: + if not state and gtk.gdk.BUTTON1_MASK: + return + + self._stroke_points.append((mouse_x, mouse_y)) + + def _motion_cb(self, widget, event): + if event.is_hint: + mouse_x, mouse_y, state = event.window.get_pointer() + else: + mouse_x = event.x + mouse_y = event.y + state = event.state + + # We paint pressing the BUTTON1, return otherwise: + if not state and gtk.gdk.BUTTON1_MASK: + return + + # Paint stroke in canvas if it gets too big: + if len(self._stroke_points) > STROKE_MAX_POINTS: + self._send_stroke_to_drawing_canvas(continue_stroke=True) + + self._stroke_points.append((mouse_x, mouse_y)) + self.queue_draw() + + def _release_cb(self, widget, event): + self._send_stroke_to_drawing_canvas() + + def _expose_cb(self, widget, event): + context = self.window.cairo_create() + + # Paint the canvas in the widget: + context.set_source_surface(self._drawing_canvas) + context.paint() + + # Return if there is no stroke to paint: + if self._stroke_points == []: + return + + # Get context from widget and paint: + self._set_stroke_context(context) + self._paint_stroke(context, self._stroke_points) |