aboutsummaryrefslogtreecommitdiffstats
path: root/lvc/widgets/gtk/drawing.py
diff options
context:
space:
mode:
Diffstat (limited to 'lvc/widgets/gtk/drawing.py')
-rw-r--r--lvc/widgets/gtk/drawing.py268
1 files changed, 268 insertions, 0 deletions
diff --git a/lvc/widgets/gtk/drawing.py b/lvc/widgets/gtk/drawing.py
new file mode 100644
index 0000000..5888851
--- /dev/null
+++ b/lvc/widgets/gtk/drawing.py
@@ -0,0 +1,268 @@
+# @Base: Miro - an RSS based video player application
+# Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011
+# Participatory Culture Foundation
+#
+# 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
+#
+# In addition, as a special exception, the copyright holders give
+# permission to link the code of portions of this program with the OpenSSL
+# library.
+#
+# You must obey the GNU General Public License in all respects for all of
+# the code used other than OpenSSL. If you modify file(s) with this
+# exception, you may extend this exception to your version of the file(s),
+# but you are not obligated to do so. If you do not wish to do so, delete
+# this exception statement from your version. If you delete this exception
+# statement from all source files in the program, then also delete it here.
+
+""".drawing -- Contains classes used to draw on
+widgets.
+"""
+
+import cairo
+import gobject
+import gtk
+
+import wrappermap
+from .base import Widget, Bin
+from .layoutmanager import LayoutManager
+
+def css_to_color(css_string):
+ parts = (css_string[1:3], css_string[3:5], css_string[5:7])
+ return tuple((int(value, 16) / 255.0) for value in parts)
+
+class ImageSurface:
+ def __init__(self, image):
+ format = cairo.FORMAT_RGB24
+ if image.pixbuf.get_has_alpha():
+ format = cairo.FORMAT_ARGB32
+ self.image = cairo.ImageSurface(
+ format, int(image.width), int(image.height))
+ context = cairo.Context(self.image)
+ gdkcontext = gtk.gdk.CairoContext(context)
+ gdkcontext.set_source_pixbuf(image.pixbuf, 0, 0)
+ gdkcontext.paint()
+ self.pattern = cairo.SurfacePattern(self.image)
+ self.pattern.set_extend(cairo.EXTEND_REPEAT)
+ self.width = image.width
+ self.height = image.height
+
+ def get_size(self):
+ return self.width, self.height
+
+ def _align_pattern(self, x, y):
+ """Line up our image pattern so that it's top-left corner is x, y."""
+ m = cairo.Matrix()
+ m.translate(-x, -y)
+ self.pattern.set_matrix(m)
+
+ def draw(self, context, x, y, width, height, fraction=1.0):
+ self._align_pattern(x, y)
+ cairo_context = context.context
+ cairo_context.save()
+ cairo_context.set_source(self.pattern)
+ cairo_context.new_path()
+ cairo_context.rectangle(x, y, width, height)
+ if fraction >= 1.0:
+ cairo_context.fill()
+ else:
+ cairo_context.clip()
+ cairo_context.paint_with_alpha(fraction)
+ cairo_context.restore()
+
+ def draw_rect(self, context, dest_x, dest_y, source_x, source_y,
+ width, height, fraction=1.0):
+
+ self._align_pattern(dest_x-source_x, dest_y-source_y)
+ cairo_context = context.context
+ cairo_context.save()
+ cairo_context.set_source(self.pattern)
+ cairo_context.new_path()
+ cairo_context.rectangle(dest_x, dest_y, width, height)
+ if fraction >= 1.0:
+ cairo_context.fill()
+ else:
+ cairo_context.clip()
+ cairo_context.paint_with_alpha(fraction)
+ cairo_context.restore()
+
+class DrawingStyle(object):
+ def __init__(self, widget, use_base_color=False, state=None):
+ if state is None:
+ state = widget._widget.state
+ self.use_custom_style = widget.use_custom_style
+ self.style = widget._widget.style
+ self.text_color = widget.convert_gtk_color(self.style.text[state])
+ if use_base_color:
+ self.bg_color = widget.convert_gtk_color(self.style.base[state])
+ else:
+ self.bg_color = widget.convert_gtk_color(self.style.bg[state])
+
+class DrawingContext(object):
+ """DrawingContext. This basically just wraps a Cairo context and adds a
+ couple convenience methods.
+ """
+
+ def __init__(self, window, drawing_area, expose_area):
+ self.window = window
+ self.context = window.cairo_create()
+ self.context.rectangle(expose_area.x, expose_area.y,
+ expose_area.width, expose_area.height)
+ self.context.clip()
+ self.width = drawing_area.width
+ self.height = drawing_area.height
+ self.context.translate(drawing_area.x, drawing_area.y)
+
+ def __getattr__(self, name):
+ return getattr(self.context, name)
+
+ def set_color(self, (red, green, blue), alpha=1.0):
+ self.context.set_source_rgba(red, green, blue, alpha)
+
+ def set_shadow(self, color, opacity, offset, blur_radius):
+ pass
+
+ def gradient_fill(self, gradient):
+ old_source = self.context.get_source()
+ self.context.set_source(gradient.pattern)
+ self.context.fill()
+ self.context.set_source(old_source)
+
+ def gradient_fill_preserve(self, gradient):
+ old_source = self.context.get_source()
+ self.context.set_source(gradient.pattern)
+ self.context.fill_preserve()
+ self.context.set_source(old_source)
+
+class Gradient(object):
+ def __init__(self, x1, y1, x2, y2):
+ self.pattern = cairo.LinearGradient(x1, y1, x2, y2)
+
+ def set_start_color(self, (red, green, blue)):
+ self.pattern.add_color_stop_rgb(0, red, green, blue)
+
+ def set_end_color(self, (red, green, blue)):
+ self.pattern.add_color_stop_rgb(1, red, green, blue)
+
+class CustomDrawingMixin(object):
+ def do_expose_event(self, event):
+ wrapper = wrappermap.wrapper(self)
+ if self.flags() & gtk.NO_WINDOW:
+ drawing_area = self.allocation
+ else:
+ drawing_area = gtk.gdk.Rectangle(0, 0,
+ self.allocation.width, self.allocation.height)
+ context = DrawingContext(event.window, drawing_area, event.area)
+ context.style = DrawingStyle(wrapper)
+ if self.flags() & gtk.CAN_FOCUS:
+ focus_space = (self.style_get_property('focus-padding') +
+ self.style_get_property('focus-line-width'))
+ if not wrapper.squish_width:
+ context.width -= focus_space * 2
+ translate_x = focus_space
+ else:
+ translate_x = 0
+ if not wrapper.squish_height:
+ context.height -= focus_space * 2
+ translate_y = focus_space
+ else:
+ translate_y = 0
+ context.translate(translate_x, translate_y)
+ wrapper.layout_manager.update_cairo_context(context.context)
+ self.draw(wrapper, context)
+
+ def draw(self, wrapper, context):
+ wrapper.layout_manager.reset()
+ wrapper.draw(context, wrapper.layout_manager)
+
+ def do_size_request(self, requesition):
+ wrapper = wrappermap.wrapper(self)
+ width, height = wrapper.size_request(wrapper.layout_manager)
+ requesition.width = width
+ requesition.height = height
+ if self.flags() & gtk.CAN_FOCUS:
+ focus_space = (self.style_get_property('focus-padding') +
+ self.style_get_property('focus-line-width'))
+ if not wrapper.squish_width:
+ requesition.width += focus_space * 2
+ if not wrapper.squish_height:
+ requesition.height += focus_space * 2
+
+class MiroDrawingArea(CustomDrawingMixin, gtk.Widget):
+ def __init__(self):
+ gtk.Widget.__init__(self)
+ CustomDrawingMixin.__init__(self)
+ self.set_flags(gtk.NO_WINDOW)
+
+class BackgroundWidget(CustomDrawingMixin, gtk.Bin):
+ def do_size_request(self, requesition):
+ CustomDrawingMixin.do_size_request(self, requesition)
+ if self.get_child():
+ child_width, child_height = self.get_child().size_request()
+ requesition.width = max(child_width, requesition.width)
+ requesition.height = max(child_height, requesition.height)
+
+ def do_expose_event(self, event):
+ CustomDrawingMixin.do_expose_event(self, event)
+ if self.get_child():
+ self.propagate_expose(self.get_child(), event)
+
+ def do_size_allocate(self, allocation):
+ gtk.Bin.do_size_allocate(self, allocation)
+ if self.get_child():
+ self.get_child().size_allocate(allocation)
+
+gobject.type_register(MiroDrawingArea)
+gobject.type_register(BackgroundWidget)
+
+class Drawable:
+ def __init__(self):
+ self.squish_width = self.squish_height = False
+
+ def set_squish_width(self, setting):
+ self.squish_width = setting
+
+ def set_squish_height(self, setting):
+ self.squish_height = setting
+
+ def set_widget(self, drawing_widget):
+ if self.is_opaque() and 0:
+ box = gtk.EventBox()
+ box.add(drawing_widget)
+ Widget.set_widget(self, box)
+ else:
+ Widget.set_widget(self, drawing_widget)
+ self.layout_manager = LayoutManager(self._widget)
+
+ def size_request(self, layout_manager):
+ return 0, 0
+
+ def draw(self, context, layout_manager):
+ pass
+
+ def is_opaque(self):
+ return False
+
+class DrawingArea(Drawable, Widget):
+ def __init__(self):
+ Widget.__init__(self)
+ Drawable.__init__(self)
+ self.set_widget(MiroDrawingArea())
+
+class Background(Drawable, Bin):
+ def __init__(self):
+ Bin.__init__(self)
+ Drawable.__init__(self)
+ self.set_widget(BackgroundWidget())