[gnome-photos] Absorb gegl-gtk



commit 79844cfaa7181f3985713b4cccb7e197ecbb2f9e
Author: Debarshi Ray <debarshir gnome org>
Date:   Mon Mar 25 02:41:42 2013 +0100

    Absorb gegl-gtk
    
    It appears to be unmaintained and not widely distributed. Lets carry
    our own copy until the situation changes.
    
    Like other elements of the GEGL stack, gegl-gtk is LGPLv3+.

 configure.ac               |    9 +-
 src/Makefile.am            |   50 ++++-
 src/gegl-gtk-enums.h       |   43 +++
 src/gegl-gtk-marshal.list  |    1 +
 src/gegl-gtk-view-helper.c |  466 ++++++++++++++++++++++++++++++++
 src/gegl-gtk-view-helper.h |   90 +++++++
 src/gegl-gtk-view.c        |  638 ++++++++++++++++++++++++++++++++++++++++++++
 src/gegl-gtk-view.h        |   79 ++++++
 src/photos-preview-view.c  |    2 +-
 9 files changed, 1373 insertions(+), 5 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 123b2c9..5e13eb1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -73,10 +73,13 @@ if test x$have_zlib = xno; then
        AC_MSG_ERROR([No sufficient zlib library found on your system.])
 fi
 
-PKG_CHECK_MODULES(CAIRO, [cairo])
+PKG_CHECK_MODULES(BABL, [babl])
+
+PKG_CHECK_MODULES(CAIRO, [cairo cairo-gobject])
+AC_DEFINE([HAVE_CAIRO_GOBJECT], [], [We want gegl-gtk to use the cairo-gobject code path])
+
 PKG_CHECK_MODULES(EXEMPI, [exempi-2.0 >= $EXEMPI_MIN_VERSION])
 PKG_CHECK_MODULES(GEGL, [gegl-0.2])
-PKG_CHECK_MODULES(GEGL_GTK, [gegl-gtk3-0.1])
 PKG_CHECK_MODULES(GDK_PIXBUF, [gdk-pixbuf-2.0])
 PKG_CHECK_MODULES(GLIB, [glib-2.0 >= $GLIB_MIN_VERSION])
 PKG_CHECK_MODULES(GIO, [gio-2.0])
@@ -88,6 +91,8 @@ PKG_CHECK_MODULES(GOA, [goa-1.0])
 AC_DEFINE([GOA_API_IS_SUBJECT_TO_CHANGE], [], [We are aware that GOA's API can change])
 
 PKG_CHECK_MODULES(GTK, [gtk+-3.0])
+AC_DEFINE([HAVE_GTK3], [], [We want gegl-gtk to use the GTK+ 3.x code path])
+
 PKG_CHECK_MODULES(GTK_UNIX_PRINT, [gtk+-unix-print-3.0])
 PKG_CHECK_MODULES(LCMS, [lcms2])
 PKG_CHECK_MODULES(LIBEXIF, [libexif >= $LIBEXIF_MIN_VERSION])
diff --git a/src/Makefile.am b/src/Makefile.am
index f9fd269..e0798ab 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,9 @@
 bin_PROGRAMS = gnome-photos
 
 gnome_photos_built_sources = \
+       gegl-gtk-enums.c \
+       gegl-gtk-marshal.c \
+       gegl-gtk-marshal.h \
        photos-about-data.c \
        photos-about-data.h \
        photos-enums.c \
@@ -50,6 +53,11 @@ gnome_photos_SOURCES = \
        eog-uri-converter.h \
        eog-util.c \
        eog-util.h \
+       gegl-gtk-enums.h \
+       gegl-gtk-view.c \
+       gegl-gtk-view.h \
+       gegl-gtk-view-helper.c \
+       gegl-gtk-view-helper.h \
        photos-application.c \
        photos-application.h \
        photos-base-manager.c \
@@ -148,6 +156,7 @@ BUILT_SOURCES = \
        $(NULL)
 
 EXTRA_DIST = \
+       gegl-gtk-marshal.list \
        photos-enums.c.template \
        photos-enums.h.template \
        photos-generate-about \
@@ -161,11 +170,11 @@ EXTRA_DIST = \
 AM_CPPFLAGS = \
        -DPACKAGE_ICONS_DIR=\""${pkgdatadir}/icons"\" \
        -DPACKAGE_LOCALE_DIR=\""${datadir}/locale"\" \
+       $(BABL_CFLAGS) \
        $(CAIRO_CFLAGS) \
        $(EXEMPI_CFLAGS) \
        $(GDK_PIXBUF_CFLAGS) \
        $(GEGL_CFLAGS) \
-       $(GEGL_GTK_CFLAGS) \
        $(GIO_CFLAGS) \
        $(GLIB_CFLAGS) \
        $(GNOME_DESKTOP_CFLAGS) \
@@ -182,11 +191,11 @@ AM_CPPFLAGS = \
 gnome_photos_LDFLAGS = 
 
 gnome_photos_LDADD = \
+       $(BABL_LIBS) \
        $(CAIRO_LIBS) \
        $(EXEMPI_LIBS) \
        $(GDK_PIXBUF_LIBS) \
        $(GEGL_LIBS) \
-       $(GEGL_GTK_LIBS) \
        $(GIO_LIBS) \
        $(GLIB_LIBS) \
        $(GNOME_DESKTOP_LIBS) \
@@ -203,6 +212,7 @@ gnome_photos_LDADD = \
 
 CLEANFILES = \
        $(gnome_photos_built_sources) \
+       stamp-gegl-gtk-marshal.h \
        stamp-photos-about-data.h \
        stamp-photos-enums.h \
        stamp-photos-marshalers.h \
@@ -210,10 +220,46 @@ CLEANFILES = \
        xgen-ah \
        xgen-etbc \
        xgen-etbh \
+       xgen-gmc \
+       xgen-gmh \
        xgen-tmc \
        xgen-tmh \
        $(NULL)
 
+gegl-gtk-enums.c: $(srcdir)/gegl-gtk-enums.h
+       glib-mkenums \
+          --fhead "/* This is a generated file, do not edit directly */\n\n#include \"config.h\"\n#include 
<glib-object.h>\n#include \"gegl-gtk-enums.h\"" \
+          --fprod "\n/* enumerations from \"@filename \" */" \
+          --vhead "GType\n enum_name@_get_type (void)\n{\n  static GType etype = 0;\n  if (etype == 0) {\n   
  static const G Type@Value values[] = {" \
+          --vprod "      { @VALUENAME@, \"@valuenick \", \"@valuenick \" }," \
+          --vtail "      { 0, NULL, NULL }\n   };\n    etype = g_ type@_register_static (\"@EnumName \", 
values);\n  }\n  return etype;\n}\n\n" \
+          $(srcdir)/gegl-gtk-enums.h > gegl-gtk-enums.c
+
+gegl-gtk-marshal.h: stamp-gegl-gtk-marshal.h
+       @true
+
+stamp-gegl-gtk-marshal.h: gegl-gtk-marshal.list Makefile
+       $(AM_V_GEN) ( \
+               ( glib-genmarshal \
+                       --prefix=gegl_gtk_marshal \
+                       --header $(srcdir)/gegl-gtk-marshal.list \
+               ) >> xgen-gmh \
+               && ( cmp -s xgen-gmh gegl-gtk-marshal.h || cp xgen-gmh gegl-gtk-marshal.h ) \
+               && rm -f xgen-gmh \
+               && echo timestamp > $(@F) \
+       )
+
+gegl-gtk-marshal.c: gegl-gtk-marshal.list gegl-gtk-marshal.h Makefile
+       $(AM_V_GEN) ( \
+               echo "#include \"gegl-gtk-marshal.h\"" > xgen-gmc \
+               && ( glib-genmarshal \
+                       --prefix=gegl_gtk_marshal \
+                       --body $(srcdir)/gegl-gtk-marshal.list \
+                  ) >> xgen-gmc \
+               && cp xgen-gmc gegl-gtk-marshal.c \
+               && rm -f xgen-gmc \
+       )
+
 photos-about-data.h: stamp-photos-about-data.h
        @true
 
diff --git a/src/gegl-gtk-enums.h b/src/gegl-gtk-enums.h
new file mode 100644
index 0000000..ba0da5b
--- /dev/null
+++ b/src/gegl-gtk-enums.h
@@ -0,0 +1,43 @@
+/* This file is part of GEGL-GTK
+ *
+ * GEGL-GTK is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL-GTK 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL-GTK; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2011 Jon Nordby <jononor gmail com>
+ */
+
+#ifndef __GEGL_GTK_ENUMS_H__
+#define __GEGL_GTK_ENUMS_H__
+
+G_BEGIN_DECLS
+
+/**
+ * GeglGtkViewAutoscale:
+ * @GEGL_GTK_VIEW_AUTOSCALE_DISABLED: Do not autoscale
+ * @GEGL_GTK_VIEW_AUTOSCALE_WIDGET: Automatically scale the widget size
+ * @GEGL_GTK_VIEW_AUTOSCALE_CONTENT: Automatically scale content to fit widget
+ *
+ * Specifies the autoscaling behavior of #GeglGtkView.
+ **/
+typedef enum {
+    GEGL_GTK_VIEW_AUTOSCALE_DISABLED = 0,
+    GEGL_GTK_VIEW_AUTOSCALE_WIDGET,
+    GEGL_GTK_VIEW_AUTOSCALE_CONTENT
+} GeglGtkViewAutoscale;
+
+GType gegl_gtk_view_autoscale_get_type(void) G_GNUC_CONST;
+#define GEGL_GTK_TYPE_VIEW_AUTOSCALE (gegl_gtk_view_autoscale_get_type())
+
+G_END_DECLS
+
+#endif /* __GEGL_GTK_ENUMS_H__ */
diff --git a/src/gegl-gtk-marshal.list b/src/gegl-gtk-marshal.list
new file mode 100644
index 0000000..73ccad0
--- /dev/null
+++ b/src/gegl-gtk-marshal.list
@@ -0,0 +1 @@
+VOID:BOXED,BOXED
diff --git a/src/gegl-gtk-view-helper.c b/src/gegl-gtk-view-helper.c
new file mode 100644
index 0000000..2042893
--- /dev/null
+++ b/src/gegl-gtk-view-helper.c
@@ -0,0 +1,466 @@
+/* This file is part of GEGL-GTK
+ *
+ * GEGL-GTK is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL-GTK 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL-GTK; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2003, 2004, 2006 Øyvind Kolås <pippin gimp org>
+ * Copyright (C) 2011 Jon Nordby <jononor gmail com>
+ */
+
+#include "gegl-gtk-view-helper.h"
+
+#include <math.h>
+#include <babl/babl.h>
+
+
+G_DEFINE_TYPE(ViewHelper, view_helper, G_TYPE_OBJECT)
+
+
+enum {
+    SIGNAL_REDRAW_NEEDED,
+    SIGNAL_SIZE_CHANGED,
+    N_SIGNALS
+};
+
+static guint view_helper_signals[N_SIGNALS] = { 0 };
+
+
+static void
+finalize(GObject *gobject);
+void
+trigger_processing(ViewHelper *self, GeglRectangle roi);
+void
+trigger_redraw(ViewHelper *self, GeglRectangle *redraw_rect);
+
+
+static void
+view_helper_class_init(ViewHelperClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+
+    gobject_class->finalize = finalize;
+
+    /* Emitted when a redraw is needed, with the area that needs redrawing. */
+    view_helper_signals[SIGNAL_REDRAW_NEEDED] = g_signal_new("redraw-needed",
+            G_TYPE_FROM_CLASS(klass),
+            G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+            0,
+            NULL, NULL,
+            g_cclosure_marshal_VOID__BOXED,
+            G_TYPE_NONE, 1,
+            GEGL_TYPE_RECTANGLE);
+
+    /* Emitted when the size of the view changes, with the new size. */
+    view_helper_signals[SIGNAL_SIZE_CHANGED] = g_signal_new("size-changed",
+            G_TYPE_FROM_CLASS(klass),
+            G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+            0,
+            NULL, NULL,
+            g_cclosure_marshal_VOID__BOXED,
+            G_TYPE_NONE, 1,
+            GEGL_TYPE_RECTANGLE);
+}
+
+static void
+view_helper_init(ViewHelper *self)
+{
+    GdkRectangle invalid_gdkrect = {0, 0, -1, -1};
+
+    self->node        = NULL;
+    self->x           = 0;
+    self->y           = 0;
+    self->scale       = 1.0;
+    self->autoscale_policy = GEGL_GTK_VIEW_AUTOSCALE_CONTENT;
+    self->block = FALSE;
+
+    self->monitor_id  = 0;
+    self->processor = NULL;
+    self->processing_queue = g_queue_new();
+    self->currently_processed_rect = NULL;
+
+    self->widget_allocation = invalid_gdkrect;
+}
+
+static void
+finalize(GObject *gobject)
+{
+    ViewHelper *self = VIEW_HELPER(gobject);
+
+    if (self->monitor_id) {
+        g_source_remove(self->monitor_id);
+        self->monitor_id = 0;
+    }
+
+    if (self->node)
+        g_object_unref(self->node);
+
+    if (self->processor)
+        g_object_unref(self->processor);
+
+    g_queue_free_full(self->processing_queue, g_free);
+
+    if (self->currently_processed_rect) {
+        g_free(self->currently_processed_rect);
+    }
+}
+
+/* Transform a rectangle from model to view coordinates. */
+static void
+model_rect_to_view_rect(ViewHelper *self, GeglRectangle *rect)
+{
+    GeglRectangle temp;
+
+    temp.x = self->scale * (rect->x) - self->x;
+    temp.y = self->scale * (rect->y) - self->y;
+    temp.width = ceil(self->scale * rect->width);
+    temp.height = ceil(self->scale * rect->height);
+
+    *rect = temp;
+}
+
+static void
+update_autoscale(ViewHelper *self)
+{
+    GdkRectangle viewport = self->widget_allocation;
+    GeglRectangle bbox = gegl_node_get_bounding_box(self->node);
+    model_rect_to_view_rect(self, &bbox);
+
+    if (!self->node || viewport.width < 0 || viewport.height < 0
+            || bbox.width < 0 || bbox.height < 0)
+        return;
+
+    if (self->autoscale_policy == GEGL_GTK_VIEW_AUTOSCALE_WIDGET) {
+        /* Request widget size change */
+        /* XXX: Should we reset scale/x/y here? */
+        g_signal_emit(self, view_helper_signals[SIGNAL_SIZE_CHANGED],
+                      0, &bbox, NULL);
+
+    } else if (self->autoscale_policy == GEGL_GTK_VIEW_AUTOSCALE_CONTENT) {
+        /* Calculate and set scaling factor to make the content fit inside */
+        float width_ratio = bbox.width / (float)viewport.width;
+        float height_ratio = bbox.height / (float)viewport.height;
+        float max_ratio = width_ratio >= height_ratio ? width_ratio : height_ratio;
+
+        float current_scale = view_helper_get_scale(self);
+        view_helper_set_scale(self, current_scale * (1.0 / max_ratio));
+    }
+
+}
+
+static void
+invalidated_event(GeglNode      *node,
+                  GeglRectangle *rect,
+                  ViewHelper    *self)
+{
+    trigger_processing(self, *rect);
+}
+
+static gboolean
+task_monitor(ViewHelper *self)
+{
+    if (!self->processor || !self->node) {
+        return FALSE;
+    }
+
+    // PERFORMANCE: combine all the rects added to the queue during a single
+    // iteration of the main loop somehow
+
+    if (!self->currently_processed_rect) {
+
+        if (g_queue_is_empty(self->processing_queue)) {
+            // Unregister worker
+            self->monitor_id = 0;
+            return FALSE;
+        }
+        else {
+            // Fetch next rect to process
+            self->currently_processed_rect = (GeglRectangle *)g_queue_pop_tail(self->processing_queue);
+            g_assert(self->currently_processed_rect);
+            gegl_processor_set_rectangle(self->processor, self->currently_processed_rect);
+        }
+    }
+
+    gboolean processing_done = !gegl_processor_work(self->processor, NULL);
+
+    if (processing_done) {
+        // Go to next region
+        if (self->currently_processed_rect) {
+            g_free(self->currently_processed_rect);
+        }
+        self->currently_processed_rect = NULL;
+    }
+
+    return TRUE;
+}
+
+
+/* When the GeglNode has been computed,
+ * find out if the size of the vie changed and
+ * emit the "size-changed" signal to notify view
+ * find out which area in the view was computed and emit the
+ * "redraw-needed" signal to notify it that a redraw is needed */
+static void
+computed_event(GeglNode      *node,
+               GeglRectangle *rect,
+               ViewHelper    *self)
+{
+    update_autoscale(self);
+
+    /* Emit redraw-needed */
+    GeglRectangle redraw_rect = *rect;
+    model_rect_to_view_rect(self, &redraw_rect);
+
+    trigger_redraw(self, &redraw_rect);
+}
+
+ViewHelper *
+view_helper_new(void)
+{
+    return VIEW_HELPER(g_object_new(VIEW_HELPER_TYPE, NULL));
+}
+
+/* Draw the view of the GeglNode to the provided cairo context,
+ * taking into account transformations et.c.
+ * @rect the bounding box of the area to draw in view coordinates
+ *
+ * For instance called by widget during the draw/expose */
+void
+view_helper_draw(ViewHelper *self, cairo_t *cr, GdkRectangle *rect)
+{
+    cairo_surface_t *surface = NULL;
+    guchar          *buf = NULL;
+    GeglRectangle   roi;
+
+    roi.x = self->x + rect->x;
+    roi.y = self->y + rect->y;
+    roi.width  = rect->width;
+    roi.height = rect->height;
+
+    buf = g_malloc((roi.width) * (roi.height) * 4);
+
+    gegl_node_blit(self->node,
+                   self->scale,
+                   &roi,
+                   babl_format("B'aG'aR'aA u8"),
+                   (gpointer)buf,
+                   GEGL_AUTO_ROWSTRIDE,
+                   GEGL_BLIT_CACHE | (self->block ? 0 : GEGL_BLIT_DIRTY));
+
+    surface = cairo_image_surface_create_for_data(buf,
+              CAIRO_FORMAT_ARGB32,
+              roi.width, roi.height,
+              roi.width * 4);
+    cairo_set_source_surface(cr, surface, rect->x, rect->y);
+    cairo_paint(cr);
+
+    cairo_surface_destroy(surface);
+    g_free(buf);
+
+}
+
+void
+view_helper_set_allocation(ViewHelper *self, GdkRectangle *allocation)
+{
+    self->widget_allocation = *allocation;
+    update_autoscale(self);
+}
+
+/* Trigger processing of the GeglNode */
+void
+trigger_processing(ViewHelper *self, GeglRectangle roi)
+{
+    //GeglRectangle    roi;
+
+    // PERFORMANCE: determine the area that the view widget is interested in,
+    // and calculate the intersection with the invalidated rect
+    // and only pass this value as the ROI
+    // Would then also have to follow changes in view transformation
+
+    if (!self->node)
+        return;
+
+//    roi.x = self->x / self->scale;
+//    roi.y = self->y / self->scale;
+
+//    roi.width = ceil(self->widget_allocation.width / self->scale + 1);
+//    roi.height = ceil(self->widget_allocation.height / self->scale + 1);
+
+    if (self->monitor_id == 0) {
+        self->monitor_id = g_idle_add_full(G_PRIORITY_LOW,
+                                           (GSourceFunc) task_monitor, self,
+                                           NULL);
+    }
+
+    // Add the invalidated region to the dirty
+    GeglRectangle *rect = g_new(GeglRectangle, 1);
+    rect->x = roi.x;
+    rect->y = roi.y;
+    rect->width = roi.width;
+    rect->height = roi.height;
+    g_queue_push_head(self->processing_queue, rect);
+}
+
+void
+trigger_redraw(ViewHelper *self, GeglRectangle *redraw_rect)
+{
+    if (!redraw_rect) {
+        GeglRectangle invalid_rect = {0, 0, -1, -1}; /* Indicates full redraw */
+        redraw_rect = &invalid_rect;
+    }
+
+    g_signal_emit(self, view_helper_signals[SIGNAL_REDRAW_NEEDED],
+                  0, redraw_rect, NULL);
+}
+
+void
+view_helper_set_node(ViewHelper *self, GeglNode *node)
+{
+    if (self->node == node)
+        return;
+
+    if (self->node)
+        g_object_unref(self->node);
+
+    if (node) {
+        g_object_ref(node);
+        self->node = node;
+
+        g_signal_connect_object(self->node, "computed",
+                                G_CALLBACK(computed_event),
+                                self, 0);
+        g_signal_connect_object(self->node, "invalidated",
+                                G_CALLBACK(invalidated_event),
+                                self, 0);
+
+        if (self->processor)
+            g_object_unref(self->processor);
+
+        GeglRectangle bbox = gegl_node_get_bounding_box(self->node);
+        self->processor = gegl_node_new_processor(self->node, &bbox);
+
+        update_autoscale(self);
+        trigger_processing(self, bbox);
+
+    } else
+        self->node = NULL;
+}
+
+GeglNode *
+view_helper_get_node(ViewHelper *self)
+{
+    return self->node;
+}
+
+void
+view_helper_set_scale(ViewHelper *self, float scale)
+{
+    if (self->scale == scale)
+        return;
+
+    self->scale = scale;
+    update_autoscale(self);
+    trigger_redraw(self, NULL);
+}
+
+float
+view_helper_get_scale(ViewHelper *self)
+{
+    return self->scale;
+}
+
+void
+view_helper_set_x(ViewHelper *self, float x)
+{
+    if (self->x == x)
+        return;
+
+    self->x = x;
+    update_autoscale(self);
+    trigger_redraw(self, NULL);
+}
+
+float
+view_helper_get_x(ViewHelper *self)
+{
+    return self->x;
+}
+
+void
+view_helper_set_y(ViewHelper *self, float y)
+{
+    if (self->y == y)
+        return;
+
+    self->y = y;
+    update_autoscale(self);
+    trigger_redraw(self, NULL);
+}
+
+float
+view_helper_get_y(ViewHelper *self)
+{
+    return self->y;
+}
+
+void view_helper_get_transformation(ViewHelper *self, GeglMatrix3 *matrix)
+{
+    /* XXX: Below gives the right result, but is it really the
+     * way we want transformations to work? */
+
+    /* API change in GEGL 1.7 (1.6+git):
+     * GeglMatrix3 changed from float[3][3] to
+     * struct with a float[3][3] coeff member */
+
+#if GEGL_MINOR_VERSION == 1 && GEGL_MICRO_VERSION >= 7 || GEGL_MINOR_VERSION >= 2
+    matrix->coeff [0][0] = self->scale; /* xx */
+    matrix->coeff [0][1] = 0.0; /* xy */
+    matrix->coeff [0][2] = -self->x; /* x0 */
+
+    matrix->coeff [1][0] = 0.0; /* yx */
+    matrix->coeff [1][1] = self->scale; /* yy */
+    matrix->coeff [1][2] = -self->y; /* y0 */
+
+    matrix->coeff [2][0] = 0.0;
+    matrix->coeff [2][1] = 0.0;
+    matrix->coeff [2][2] = 1.0;
+#else
+    (*matrix) [0][0] = self->scale;
+    (*matrix) [0][1] = 0.0;
+    (*matrix) [0][2] = -self->x;
+
+    (*matrix) [1][0] = 0.0;
+    (*matrix) [1][1] = self->scale;
+    (*matrix) [1][2] = -self->y;
+
+    (*matrix) [2][0] = 0.0;
+    (*matrix) [2][1] = 0.0;
+    (*matrix) [2][2] = 1.0;
+#endif
+
+}
+
+void
+view_helper_set_autoscale_policy(ViewHelper *self, GeglGtkViewAutoscale autoscale)
+{
+    if (self->autoscale_policy == autoscale)
+        return;
+
+    self->autoscale_policy = autoscale;
+    update_autoscale(self);
+}
+
+GeglGtkViewAutoscale
+view_helper_get_autoscale_policy(ViewHelper *self)
+{
+    return self->autoscale_policy;
+}
diff --git a/src/gegl-gtk-view-helper.h b/src/gegl-gtk-view-helper.h
new file mode 100644
index 0000000..022ac5e
--- /dev/null
+++ b/src/gegl-gtk-view-helper.h
@@ -0,0 +1,90 @@
+/* This file is part of GEGL-GTK
+ *
+ * GEGL-GTK is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL-GTK 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL-GTK; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2003, 2004, 2006 Øyvind Kolås <pippin gimp org>
+ * Copyright (C) 2011 Jon Nordby <jononor gmail com>
+ */
+
+#ifndef __VIEW_HELPER_H__
+#define __VIEW_HELPER_H__
+
+#include <glib-object.h>
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include <gegl-gtk-enums.h>
+
+G_BEGIN_DECLS
+
+#define VIEW_HELPER_TYPE            (view_helper_get_type ())
+#define VIEW_HELPER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIEW_HELPER_TYPE, ViewHelper))
+#define VIEW_HELPER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  VIEW_HELPER_TYPE, ViewHelperClass))
+#define IS_VIEW_HELPER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIEW_HELPER_TYPE))
+#define IS_VIEW_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  VIEW_HELPER_TYPE))
+#define VIEW_HELPER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  VIEW_HELPER_TYPE, ViewHelperClass))
+
+typedef struct _ViewHelper        ViewHelper;
+typedef struct _ViewHelperClass   ViewHelperClass;
+
+struct _ViewHelper {
+    GObject parent_instance;
+
+    GeglNode      *node;
+    gfloat         x;
+    gfloat         y;
+    gdouble        scale;
+    gboolean       block;    /* blocking render */
+    GeglGtkViewAutoscale autoscale_policy;
+
+    guint          monitor_id;
+    GeglProcessor *processor;
+    GQueue        *processing_queue; /* Queue of rectangles that needs to be processed */
+    GeglRectangle *currently_processed_rect;
+
+    GdkRectangle   widget_allocation; /* The allocated size of the widget */
+};
+
+struct _ViewHelperClass {
+    GObjectClass parent_class;
+};
+
+
+GType view_helper_get_type(void) G_GNUC_CONST;
+
+ViewHelper *view_helper_new(void);
+
+void view_helper_draw(ViewHelper *self, cairo_t *cr, GdkRectangle *rect);
+void view_helper_set_allocation(ViewHelper *self, GdkRectangle *allocation);
+
+void view_helper_set_node(ViewHelper *self, GeglNode *node);
+GeglNode *view_helper_get_node(ViewHelper *self);
+
+void view_helper_set_scale(ViewHelper *self, float scale);
+float view_helper_get_scale(ViewHelper *self);
+
+void view_helper_set_x(ViewHelper *self, float x);
+float view_helper_get_x(ViewHelper *self);
+
+void view_helper_set_y(ViewHelper *self, float y);
+float view_helper_get_y(ViewHelper *self);
+
+void view_helper_get_transformation(ViewHelper *self, GeglMatrix3 *matrix);
+
+void view_helper_set_autoscale_policy(ViewHelper *self, GeglGtkViewAutoscale autoscale);
+GeglGtkViewAutoscale view_helper_get_autoscale_policy(ViewHelper *self);
+
+G_END_DECLS
+
+#endif /* __VIEW_HELPER_H__ */
diff --git a/src/gegl-gtk-view.c b/src/gegl-gtk-view.c
new file mode 100644
index 0000000..74a3737
--- /dev/null
+++ b/src/gegl-gtk-view.c
@@ -0,0 +1,638 @@
+/* This file is part of GEGL-GTK
+ *
+ * GEGL-GTK is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL-GTK 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL-GTK; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2003, 2004, 2006 Øyvind Kolås <pippin gimp org>
+ * Copyright (C) 2011 Jon Nordby <jononor gmail com>
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <gegl.h>
+
+#ifdef HAVE_CAIRO_GOBJECT
+#include <cairo-gobject.h>
+#endif
+
+#include "gegl-gtk-view.h"
+#include "gegl-gtk-view-helper.h"
+#include "gegl-gtk-marshal.h"
+
+/**
+ * SECTION:gegl-gtk-view
+ * @short_description: Widget for displaying a #GeglNode
+ * @stability: Unstable
+ * @include: gegl-gtk.h
+ *
+ * The view widget displays the output of a node in a GEGL graph.
+ * It will tracks changes in the node, and will therefore automatically
+ * show the correct content when the GEGL graph is changed.
+ *
+ * For setting which #GeglNode to display, use gegl_gtk_view_set_node(),
+ * or use the gegl_gtk_view_new_for_node() convenience constructor.
+ *
+ * Transformations:
+ *
+ * The widget can show a transformed view of the GeglNode. Scaling and
+ * transformations are supported, as well as autoscaling.
+ * For manual control over the transformation see
+ * methods gegl_gtk_view_set_scale(), gegl_gtk_view_set_x() and
+ * gegl_gtk_view_set_y(), or use the corresponding properties.
+ * For changing the autoscaling behavior, see
+ * gegl_gtk_view_set_autoscale_policy()
+ * For getting the effective affine transformation applied, use
+ * gegl_gtk_view_get_transformation()
+ *
+ * Examples:
+ *
+ * In the GEGL-GTK example directories, you can find code examples for
+ * how to use #GeglGtkView in files with names starting with gegl-gtk-view
+ **/
+
+/*
+ * This class is responsible for providing the public interface
+ * consumers expect of the view widget, and for rendering onto the widget.
+ * Tracking changes in the GeglNode, dealing with model<->view transformations
+ * et.c. is delegated to the internal/private class ViewHelper.
+ *
+ * This separation of concerns keeps the classes small and "stupid", and
+ * allows to test a lot of functionality without having to instantiate
+ * a widget and rely on the presence and behaviour of a windowing system.
+ */
+
+/*
+ * TODO: Emit a transformation-changed signal whenever the tranformation changes
+ */
+
+G_DEFINE_TYPE(GeglGtkView, gegl_gtk_view, GTK_TYPE_DRAWING_AREA)
+
+
+enum {
+    PROP_0,
+    PROP_NODE,
+    PROP_X,
+    PROP_Y,
+    PROP_SCALE,
+    PROP_BLOCK,
+    PROP_AUTOSCALE_POLICY
+};
+
+#ifdef HAVE_CAIRO_GOBJECT
+enum {
+    SIGNAL_DRAW_BACKGROUND,
+    SIGNAL_DRAW_OVERLAY,
+    N_SIGNALS
+};
+
+static guint gegl_view_signals[N_SIGNALS];
+#endif
+
+static ViewHelper *
+get_private(GeglGtkView *self)
+{
+    return VIEW_HELPER(self->priv);
+}
+
+#define GET_PRIVATE(self) (get_private(self))
+
+
+static void      gegl_gtk_view_class_init(GeglGtkViewClass  *klass);
+static void      gegl_gtk_view_init(GeglGtkView       *self);
+static void      finalize(GObject        *gobject);
+static void      set_property(GObject        *gobject,
+                              guint           prop_id,
+                              const GValue   *value,
+                              GParamSpec     *pspec);
+static void      get_property(GObject        *gobject,
+                              guint           prop_id,
+                              GValue         *value,
+                              GParamSpec     *pspec);
+
+#ifdef HAVE_GTK2
+static gboolean  expose_event(GtkWidget      *widget,
+                              GdkEventExpose *event);
+#endif
+#ifdef HAVE_GTK3
+static gboolean  draw(GtkWidget *widget,
+                      cairo_t *cr);
+#endif
+
+
+static void
+trigger_redraw(ViewHelper *priv, GeglRectangle *rect, GeglGtkView *view);
+static void
+size_allocate(GtkWidget *widget, GdkRectangle *allocation, gpointer user_data);
+
+static void
+view_size_changed(ViewHelper *priv, GeglRectangle *rect, GeglGtkView *view);
+
+static void
+gegl_gtk_view_class_init(GeglGtkViewClass *klass)
+{
+    GObjectClass   *gobject_class = G_OBJECT_CLASS(klass);
+    GtkWidgetClass *widget_class  = GTK_WIDGET_CLASS(klass);
+
+    gobject_class->finalize     = finalize;
+    gobject_class->set_property = set_property;
+    gobject_class->get_property = get_property;
+
+#ifdef HAVE_GTK2
+    widget_class->expose_event        = expose_event;
+#endif
+
+#ifdef HAVE_GTK3
+    widget_class->draw                = draw;
+#endif
+
+    g_object_class_install_property(gobject_class, PROP_X,
+                                    g_param_spec_float("x",
+                                            "X",
+                                            "X origin",
+                                            -G_MAXFLOAT, G_MAXFLOAT, 0.0,
+                                            G_PARAM_CONSTRUCT |
+                                            G_PARAM_READWRITE));
+    g_object_class_install_property(gobject_class, PROP_Y,
+                                    g_param_spec_float("y",
+                                            "Y",
+                                            "Y origin",
+                                            -G_MAXFLOAT, G_MAXFLOAT, 0.0,
+                                            G_PARAM_CONSTRUCT |
+                                            G_PARAM_READWRITE));
+    g_object_class_install_property(gobject_class, PROP_SCALE,
+                                    g_param_spec_double("scale",
+                                            "Scale",
+                                            "Zoom factor",
+                                            0.0, 100.0, 1.00,
+                                            G_PARAM_CONSTRUCT |
+                                            G_PARAM_READWRITE));
+    g_object_class_install_property(gobject_class, PROP_NODE,
+                                    g_param_spec_object("node",
+                                            "Node",
+                                            "The node to render",
+                                            G_TYPE_OBJECT,
+                                            G_PARAM_CONSTRUCT |
+                                            G_PARAM_READWRITE));
+    g_object_class_install_property(gobject_class, PROP_BLOCK,
+                                    g_param_spec_boolean("block",
+                                            "Blocking render",
+                                            "Make sure all data requested to blit is generated.",
+                                            FALSE,
+                                            G_PARAM_READWRITE));
+    g_object_class_install_property(gobject_class, PROP_AUTOSCALE_POLICY,
+                                    g_param_spec_enum("autoscale-policy",
+                                            "Autoscale policy", "The autoscaling behavior used",
+                                            GEGL_GTK_TYPE_VIEW_AUTOSCALE,
+                                            GEGL_GTK_VIEW_AUTOSCALE_CONTENT,
+                                            G_PARAM_READWRITE |
+                                            G_PARAM_CONSTRUCT));
+
+
+/* XXX: maybe we should just allow a second GeglNode to be specified for background? */
+
+/**
+ * GeglGtkView::draw-background:
+ * @widget: the #GeglGtkView widget that emitted the signal
+ * @cr: the #CairoContext to render to
+ * @rect: the area that was updated, view coordinates
+ *
+ * Emitted during painting, before the node contents has been rendered.
+ * Allows consumers to draw a custom background for the widget.
+ *
+ * Note:
+ * Manipulating the view widget in the signal handler is not supported.
+ * This signal is only available if GEGL-GTK was build with Cairo GObject support.
+ **/
+
+/**
+* GeglGtkView::draw-overlay:
+* @widget: the #GeglGtkView widget that emitted the signal
+* @cr: the #CairoContext to render to
+* @rect: the area that was updated, in view coordinates
+*
+* Emitted during painting, before the node contents has been rendered.
+*
+* Allows consumers to draw an overlay for the widget, for instance
+* for simple user interface elements.
+*
+* Note:
+* Manipulating the view widget in the signal handler is not supported.
+* This signal is only available if GEGL-GTK was build with Cairo GObject support.
+**/
+#ifdef HAVE_CAIRO_GOBJECT
+    gegl_view_signals[SIGNAL_DRAW_BACKGROUND] =
+        g_signal_new("draw-background",
+                     G_TYPE_FROM_CLASS(klass),
+                     0,
+                     0,
+                     NULL,
+                     NULL,
+                     gegl_gtk_marshal_VOID__BOXED_BOXED,
+                     G_TYPE_NONE, 2, CAIRO_GOBJECT_TYPE_CONTEXT, GDK_TYPE_RECTANGLE);
+
+    gegl_view_signals[SIGNAL_DRAW_OVERLAY] =
+        g_signal_new("draw-overlay",
+                     G_TYPE_FROM_CLASS(klass),
+                     0,
+                     0,
+                     NULL,
+                     NULL,
+                     gegl_gtk_marshal_VOID__BOXED_BOXED,
+                     G_TYPE_NONE, 2, CAIRO_GOBJECT_TYPE_CONTEXT, GDK_TYPE_RECTANGLE);
+#endif
+
+}
+
+static void
+gegl_gtk_view_init(GeglGtkView *self)
+{
+    self->priv = (GeglGtkViewPrivate *)view_helper_new();
+
+    g_signal_connect(self->priv, "redraw-needed", G_CALLBACK(trigger_redraw), (gpointer)self);
+    g_signal_connect(self->priv, "size-changed", G_CALLBACK(view_size_changed), (gpointer)self);
+
+    g_signal_connect(self, "size-allocate", G_CALLBACK(size_allocate), NULL);
+}
+
+static void
+finalize(GObject *gobject)
+{
+    GeglGtkView *self = GEGL_GTK_VIEW(gobject);
+
+    g_object_unref(G_OBJECT(self->priv));
+
+    G_OBJECT_CLASS(gegl_gtk_view_parent_class)->finalize(gobject);
+}
+
+static void
+set_property(GObject      *gobject,
+             guint         property_id,
+             const GValue *value,
+             GParamSpec   *pspec)
+{
+    GeglGtkView *self = GEGL_GTK_VIEW(gobject);
+    ViewHelper *priv = GET_PRIVATE(self);
+
+    switch (property_id) {
+    case PROP_NODE:
+        gegl_gtk_view_set_node(self, GEGL_NODE(g_value_get_object(value)));
+        break;
+    case PROP_X:
+        gegl_gtk_view_set_x(self, g_value_get_float(value));
+        break;
+    case PROP_BLOCK:
+        priv->block = g_value_get_boolean(value);
+        break;
+    case PROP_Y:
+        gegl_gtk_view_set_y(self, g_value_get_float(value));
+        break;
+    case PROP_SCALE:
+        gegl_gtk_view_set_scale(self, g_value_get_double(value));
+        break;
+    case PROP_AUTOSCALE_POLICY:
+        gegl_gtk_view_set_autoscale_policy(self, g_value_get_enum(value));
+        break;
+    default:
+
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
+        break;
+    }
+}
+
+static void
+get_property(GObject      *gobject,
+             guint         property_id,
+             GValue       *value,
+             GParamSpec   *pspec)
+{
+    GeglGtkView *self = GEGL_GTK_VIEW(gobject);
+    ViewHelper *priv = GET_PRIVATE(self);
+
+    switch (property_id) {
+    case PROP_NODE:
+        g_value_set_object(value, gegl_gtk_view_get_node(self));
+        break;
+    case PROP_X:
+        g_value_set_float(value, gegl_gtk_view_get_x(self));
+        break;
+    case PROP_BLOCK:
+        g_value_set_boolean(value, priv->block);
+        break;
+    case PROP_Y:
+        g_value_set_float(value, gegl_gtk_view_get_y(self));
+        break;
+    case PROP_SCALE:
+        g_value_set_double(value, gegl_gtk_view_get_scale(self));
+        break;
+    case PROP_AUTOSCALE_POLICY:
+        g_value_set_enum(value, gegl_gtk_view_get_autoscale_policy(self));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
+        break;
+    }
+}
+
+/* Trigger a redraw */
+static void
+trigger_redraw(ViewHelper *priv,
+               GeglRectangle *rect,
+               GeglGtkView *view)
+{
+    if (rect->width < 0 || rect->height < 0)
+        gtk_widget_queue_draw(GTK_WIDGET(view));
+    else
+        gtk_widget_queue_draw_area(GTK_WIDGET(view),
+                                   rect->x, rect->y, rect->width, rect->height);
+}
+
+/* Bounding box of the node view changed */
+static void
+view_size_changed(ViewHelper *priv, GeglRectangle *rect, GeglGtkView *view)
+{
+    /* Resize the widget to fit the entire view bounding box
+     * TODO: implement a policy for this
+     * consumers should be able to have the view not autoscale at all
+     * or to have it autoscale the content to fit the size of widget */
+    gtk_widget_set_size_request(GTK_WIDGET(view), rect->width, rect->height);
+}
+
+static void
+size_allocate(GtkWidget *widget, GdkRectangle *allocation, gpointer user_data)
+{
+    GeglGtkView *self = GEGL_GTK_VIEW(widget);
+    view_helper_set_allocation(GET_PRIVATE(self), allocation);
+}
+
+static void
+draw_implementation(GeglGtkView *self, cairo_t *cr, GdkRectangle *rect)
+{
+    ViewHelper *priv = GET_PRIVATE(self);
+
+#ifdef HAVE_CAIRO_GOBJECT
+    /* Draw background */
+    cairo_save(cr);
+    g_signal_emit(G_OBJECT(self), gegl_view_signals[SIGNAL_DRAW_BACKGROUND],
+                  0, cr, rect, NULL);
+    cairo_restore(cr);
+#endif
+
+    /* Draw the gegl node */
+    cairo_save(cr);
+    view_helper_draw(priv, cr, rect);
+    cairo_restore(cr);
+
+#ifdef HAVE_CAIRO_GOBJECT
+    /* Draw overlay */
+    cairo_save(cr);
+    g_signal_emit(G_OBJECT(self), gegl_view_signals[SIGNAL_DRAW_OVERLAY],
+                  0, cr, rect, NULL);
+    cairo_restore(cr);
+#endif
+}
+
+#ifdef HAVE_GTK3
+static gboolean
+draw(GtkWidget *widget, cairo_t *cr)
+{
+    GeglGtkView *self = GEGL_GTK_VIEW(widget);
+    ViewHelper *priv = GET_PRIVATE(self);
+    GdkRectangle rect;
+
+    if (!priv->node)
+        return FALSE;
+
+    gdk_cairo_get_clip_rectangle(cr, &rect);
+
+    draw_implementation(self, cr, &rect);
+
+    return FALSE;
+}
+#endif
+
+#ifdef HAVE_GTK2
+static gboolean
+expose_event(GtkWidget      *widget,
+             GdkEventExpose *event)
+{
+    GeglGtkView *self = GEGL_GTK_VIEW(widget);
+    ViewHelper *priv = GET_PRIVATE(self);
+    cairo_t      *cr;
+    GdkRectangle rect;
+
+    if (!priv->node)
+        return FALSE;
+
+    cr = gdk_cairo_create(widget->window);
+    gdk_cairo_region(cr, event->region);
+    cairo_clip(cr);
+    gdk_region_get_clipbox(event->region, &rect);
+
+    draw_implementation(self, cr, &rect);
+
+    cairo_destroy(cr);
+
+    return FALSE;
+}
+#endif
+
+
+/**
+ * gegl_gtk_view_new:
+ *
+ * Create a new #GeglGtkView
+ *
+ * Returns: New #GeglGtkView
+ **/
+GeglGtkView *
+gegl_gtk_view_new()
+{
+    return GEGL_GTK_VIEW(g_object_new(GEGL_GTK_TYPE_VIEW, NULL));
+}
+
+GeglGtkView *
+gegl_gtk_view_new_for_buffer(GeglBuffer *buffer)
+{
+    GeglNode *node = gegl_node("gegl:buffer-source",
+                     "buffer", buffer, NULL);
+    return gegl_gtk_view_new_for_node(node);
+}
+
+/**
+ * gegl_gtk_view_new_for_node:
+ * @node: The #GeglNode to display
+ *
+ * Create a new #GeglGtkView for a given #GeglNode
+ *
+ * Returns: New #GeglGtkView displaying @node
+ **/
+GeglGtkView *
+gegl_gtk_view_new_for_node(GeglNode *node)
+{
+    GeglGtkView *view = gegl_gtk_view_new();
+    gegl_gtk_view_set_node(view, node);
+    return view;
+}
+
+/**
+ * gegl_gtk_view_set_node:
+ * @self: A #GeglGtkView
+ * @node: (transfer full)(allow-none): a #GeglNode instance or %NULL
+ *
+ * Change the #GeglNode to display
+ **/
+void
+gegl_gtk_view_set_node(GeglGtkView *self, GeglNode *node)
+{
+    view_helper_set_node(GET_PRIVATE(self), node);
+}
+
+/**
+ * gegl_gtk_view_get_node:
+ * @self: A #GeglGtkView
+ * Returns: (transfer none): The #GeglNode this widget displays
+ *
+ * Get the displayed #GeglNode
+ **/
+GeglNode *
+gegl_gtk_view_get_node(GeglGtkView *self)
+{
+    return view_helper_get_node(GET_PRIVATE(self));
+}
+
+/**
+ * gegl_gtk_view_set_scale:
+ * @self: A #GeglGtkView
+ * @scale:
+ *
+ * Setter for the :scale property
+ **/
+void
+gegl_gtk_view_set_scale(GeglGtkView *self, float scale)
+{
+    view_helper_set_scale(GET_PRIVATE(self), scale);
+}
+
+/**
+ * gegl_gtk_view_get_scale:
+ * @self: A #GeglGtkView
+ *
+ * Getter for the :scale property
+ *
+ * Returns:
+ **/
+float
+gegl_gtk_view_get_scale(GeglGtkView *self)
+{
+    return view_helper_get_scale(GET_PRIVATE(self));
+}
+
+/**
+ * gegl_gtk_view_set_x:
+ * @self: A #GeglGtkView
+ * @x:
+ *
+ * Setter for the :x property
+ **/
+void
+gegl_gtk_view_set_x(GeglGtkView *self, float x)
+{
+    view_helper_set_x(GET_PRIVATE(self), x);
+}
+
+/**
+ * gegl_gtk_view_get_x:
+ * @self: A #GeglGtkView
+ *
+ * Getter for the :x property
+ *
+ * Returns:
+ **/
+float
+gegl_gtk_view_get_x(GeglGtkView *self)
+{
+    return view_helper_get_x(GET_PRIVATE(self));
+}
+
+/**
+ * gegl_gtk_view_set_y:
+ * @self: A #GeglGtkView
+ * @y:
+ *
+ * Setter for the :y property
+ **/
+void
+gegl_gtk_view_set_y(GeglGtkView *self, float y)
+{
+    view_helper_set_y(GET_PRIVATE(self), y);
+}
+
+/**
+ * gegl_gtk_view_get_y:
+ * @self: A #GeglGtkView
+ *
+ * Getter for the :y property
+ *
+ * Returns:
+ **/
+float
+gegl_gtk_view_get_y(GeglGtkView *self)
+{
+    return view_helper_get_y(GET_PRIVATE(self));
+}
+
+/**
+ * gegl_gtk_view_get_transformation:
+ * @self: A #GeglGtkView
+ * @matrix: (out caller-allocates): Pointer to location for transformation matrix
+ *
+ * Get the model->view transformation
+ *
+ * The transformation matrix describes the transformation between the
+ * model (the output of the GeglNode) and the view (the display in the widget).
+ * To transform coordinates use gegl_matrix3_transform_point().
+ * To get a matrix representing the view->model space transformation, use gegl_matrix3_invert()
+ **/
+void gegl_gtk_view_get_transformation(GeglGtkView *self, GeglMatrix3 *matrix)
+{
+    view_helper_get_transformation(GET_PRIVATE(self), matrix);
+}
+
+/**
+ * gegl_gtk_view_set_autoscale_policy:
+ * @self: A #GeglGtkView
+ * @autoscale: #GeglGtkViewAutoscale policy to use
+ *
+ * Set the autoscaling policy
+ **/
+void
+gegl_gtk_view_set_autoscale_policy(GeglGtkView *self, GeglGtkViewAutoscale autoscale)
+{
+    view_helper_set_autoscale_policy(GET_PRIVATE(self), autoscale);
+}
+
+/**
+ * gegl_gtk_view_get_autoscale_policy:
+ * @self: A #GeglGtkView
+ *
+ * Get the autoscaling policy
+ *
+ * Returns: Current #GeglGtkViewAutoscale policy in use
+ **/
+GeglGtkViewAutoscale
+gegl_gtk_view_get_autoscale_policy(GeglGtkView *self)
+{
+    return view_helper_get_autoscale_policy(GET_PRIVATE(self));
+}
diff --git a/src/gegl-gtk-view.h b/src/gegl-gtk-view.h
new file mode 100644
index 0000000..d6611c7
--- /dev/null
+++ b/src/gegl-gtk-view.h
@@ -0,0 +1,79 @@
+/* This file is part of GEGL-GTK
+ *
+ * GEGL-GTK is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL-GTK 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL-GTK; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2003, 2004, 2006 Øyvind Kolås <pippin gimp org>
+ * Copyright (C) 2011 Jon Nordby <jononor gmail com>
+ */
+
+#ifndef __GEGL_GTK_VIEW_H__
+#define __GEGL_GTK_VIEW_H__
+
+#include <gtk/gtk.h>
+#include <gegl.h>
+
+#include "gegl-gtk-enums.h"
+
+G_BEGIN_DECLS
+
+#define GEGL_GTK_TYPE_VIEW            (gegl_gtk_view_get_type ())
+#define GEGL_GTK_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_GTK_TYPE_VIEW, GeglGtkView))
+#define GEGL_GTK_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GEGL_GTK_TYPE_VIEW, 
GeglGtkViewClass))
+#define GEGL_GTK_IS_VIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_GTK_TYPE_VIEW))
+#define GEGL_GTK_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GEGL_GTK_TYPE_VIEW))
+#define GEGL_GTK_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GEGL_GTK_TYPE_VIEW, 
GeglGtkViewClass))
+
+typedef struct _GeglGtkView        GeglGtkView;
+typedef struct _GeglGtkViewClass   GeglGtkViewClass;
+
+typedef struct _ViewHelper GeglGtkViewPrivate;
+
+struct _GeglGtkView {
+    /*< private >*/
+    GtkDrawingArea parent_instance;
+    GeglGtkViewPrivate *priv; /* Can't use the GType private mechanism for GObjects */
+};
+
+struct _GeglGtkViewClass {
+    /*< private >*/
+    GtkDrawingAreaClass parent_class;
+};
+
+GType           gegl_gtk_view_get_type(void) G_GNUC_CONST;
+
+
+GeglGtkView *gegl_gtk_view_new(void);
+GeglGtkView *gegl_gtk_view_new_for_node(GeglNode *node);
+GeglGtkView *gegl_gtk_view_new_for_buffer(GeglBuffer *buffer);
+
+void gegl_gtk_view_set_node(GeglGtkView *self, GeglNode *node);
+GeglNode *gegl_gtk_view_get_node(GeglGtkView *self);
+
+void gegl_gtk_view_set_scale(GeglGtkView *self, float scale);
+float gegl_gtk_view_get_scale(GeglGtkView *self);
+
+void gegl_gtk_view_set_x(GeglGtkView *self, float x);
+float gegl_gtk_view_get_x(GeglGtkView *self);
+
+void gegl_gtk_view_set_y(GeglGtkView *self, float y);
+float gegl_gtk_view_get_y(GeglGtkView *self);
+
+void gegl_gtk_view_get_transformation(GeglGtkView *self, GeglMatrix3 *matrix);
+
+void gegl_gtk_view_set_autoscale_policy(GeglGtkView *self, GeglGtkViewAutoscale autoscale);
+GeglGtkViewAutoscale gegl_gtk_view_get_autoscale_policy(GeglGtkView *self);
+
+G_END_DECLS
+
+#endif /* __GEGL_GTK_VIEW_H__ */
diff --git a/src/photos-preview-view.c b/src/photos-preview-view.c
index 8c7815b..1b1183a 100644
--- a/src/photos-preview-view.c
+++ b/src/photos-preview-view.c
@@ -25,11 +25,11 @@
 
 #include "config.h"
 
-#include <gegl-gtk.h>
 #include <glib.h>
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
 
+#include "gegl-gtk-view.h"
 #include "photos-preview-view.h"
 
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]