[gimp] Add new widget GimpOverlayBox which is a GtkContainer subclass



commit 3d354c9434dbbc0bd0c7c671823e968f046fc6ba
Author: Michael Natterer <mitch gimp org>
Date:   Sun Oct 18 21:57:34 2009 +0200

    Add new widget GimpOverlayBox which is a GtkContainer subclass
    
    It keeps around its children as offscreen widgets and renders them
    using a (potantially) arbitrary cairo_matrix_t (the actual API allows
    for arbitrary alignment wihin the container and rotating).

 app/widgets/Makefile.am        |    4 +
 app/widgets/gimpoverlaybox.c   |  466 +++++++++++++++++++++++++++++++++++++++
 app/widgets/gimpoverlaybox.h   |   72 ++++++
 app/widgets/gimpoverlaychild.c |  474 ++++++++++++++++++++++++++++++++++++++++
 app/widgets/gimpoverlaychild.h |   77 +++++++
 app/widgets/widgets-types.h    |    1 +
 6 files changed, 1094 insertions(+), 0 deletions(-)
---
diff --git a/app/widgets/Makefile.am b/app/widgets/Makefile.am
index e126c85..b1926e2 100644
--- a/app/widgets/Makefile.am
+++ b/app/widgets/Makefile.am
@@ -217,6 +217,10 @@ libappwidgets_a_sources = \
 	gimpmessagedialog.h		\
 	gimpnavigationview.c		\
 	gimpnavigationview.h		\
+	gimpoverlaybox.c		\
+	gimpoverlaybox.h		\
+	gimpoverlaychild.c		\
+	gimpoverlaychild.h		\
 	gimppaletteeditor.c		\
 	gimppaletteeditor.h		\
 	gimppaletteselect.c		\
diff --git a/app/widgets/gimpoverlaybox.c b/app/widgets/gimpoverlaybox.c
new file mode 100644
index 0000000..8848e06
--- /dev/null
+++ b/app/widgets/gimpoverlaybox.c
@@ -0,0 +1,466 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpOverlayBox
+ * Copyright (C) 2009 Michael Natterer <mitch gimp org>
+ *
+ * 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#undef GSEAL_ENABLE
+
+#include <gtk/gtk.h>
+
+#include "widgets-types.h"
+
+#include "gimpoverlaybox.h"
+#include "gimpoverlaychild.h"
+
+
+/*  local function prototypes  */
+
+static void        gimp_overlay_box_set_property        (GObject        *object,
+                                                         guint           property_id,
+                                                         const GValue   *value,
+                                                         GParamSpec     *pspec);
+static void        gimp_overlay_box_get_property        (GObject        *object,
+                                                         guint           property_id,
+                                                         GValue         *value,
+                                                         GParamSpec     *pspec);
+
+static void        gimp_overlay_box_realize             (GtkWidget      *widget);
+static void        gimp_overlay_box_unrealize           (GtkWidget      *widget);
+static void        gimp_overlay_box_size_request        (GtkWidget      *widget,
+                                                         GtkRequisition *requisition);
+static void        gimp_overlay_box_size_allocate       (GtkWidget      *widget,
+                                                         GtkAllocation  *allocation);
+static gboolean    gimp_overlay_box_expose              (GtkWidget      *widget,
+                                                         GdkEventExpose *event);
+static gboolean    gimp_overlay_box_damage              (GtkWidget      *widget,
+                                                         GdkEventExpose *event);
+
+static void        gimp_overlay_box_add                 (GtkContainer   *container,
+                                                         GtkWidget      *widget);
+static void        gimp_overlay_box_remove              (GtkContainer   *container,
+                                                         GtkWidget      *widget);
+static void        gimp_overlay_box_forall              (GtkContainer   *container,
+                                                         gboolean        include_internals,
+                                                         GtkCallback     callback,
+                                                         gpointer        callback_data);
+static GType       gimp_overlay_box_child_type          (GtkContainer   *container);
+
+static GdkWindow * gimp_overlay_box_pick_embedded_child (GdkWindow      *window,
+                                                         gdouble         x,
+                                                         gdouble         y,
+                                                         GimpOverlayBox *box);
+
+
+G_DEFINE_TYPE (GimpOverlayBox, gimp_overlay_box, GTK_TYPE_CONTAINER)
+
+#define parent_class gimp_overlay_box_parent_class
+
+
+static void
+gimp_overlay_box_class_init (GimpOverlayBoxClass *klass)
+{
+  GObjectClass      *object_class    = G_OBJECT_CLASS (klass);
+  GtkWidgetClass    *widget_class    = GTK_WIDGET_CLASS (klass);
+  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+  object_class->set_property  = gimp_overlay_box_set_property;
+  object_class->get_property  = gimp_overlay_box_get_property;
+
+  widget_class->realize       = gimp_overlay_box_realize;
+  widget_class->unrealize     = gimp_overlay_box_unrealize;
+  widget_class->size_request  = gimp_overlay_box_size_request;
+  widget_class->size_allocate = gimp_overlay_box_size_allocate;
+  widget_class->expose_event  = gimp_overlay_box_expose;
+
+  g_signal_override_class_handler ("damage-event",
+                                   GIMP_TYPE_OVERLAY_BOX,
+                                   G_CALLBACK (gimp_overlay_box_damage));
+
+  container_class->add        = gimp_overlay_box_add;
+  container_class->remove     = gimp_overlay_box_remove;
+  container_class->forall     = gimp_overlay_box_forall;
+  container_class->child_type = gimp_overlay_box_child_type;
+}
+
+static void
+gimp_overlay_box_init (GimpOverlayBox *box)
+{
+}
+
+static void
+gimp_overlay_box_set_property (GObject      *object,
+                               guint         property_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  switch (property_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_overlay_box_get_property (GObject    *object,
+                               guint       property_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  switch (property_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_overlay_box_realize (GtkWidget *widget)
+{
+  GimpOverlayBox *box = GIMP_OVERLAY_BOX (widget);
+  GtkAllocation   allocation;
+  GdkWindowAttr   attributes;
+  gint            attributes_mask;
+  GList          *list;
+
+  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+  gtk_widget_get_allocation (widget, &allocation);
+
+  attributes.x           = allocation.x;
+  attributes.y           = allocation.y;
+  attributes.width       = allocation.width;
+  attributes.height      = allocation.height;
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.wclass      = GDK_INPUT_OUTPUT;
+  attributes.visual      = gtk_widget_get_visual (widget);
+  attributes.colormap    = gtk_widget_get_colormap (widget);
+  attributes.event_mask  = gtk_widget_get_events (widget);
+
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+  gtk_widget_set_window (widget,
+                         gdk_window_new (gtk_widget_get_parent_window (widget),
+                                         &attributes, attributes_mask));
+  gdk_window_set_user_data (gtk_widget_get_window (widget), widget);
+
+  g_signal_connect (gtk_widget_get_window (widget), "pick-embedded-child",
+                    G_CALLBACK (gimp_overlay_box_pick_embedded_child),
+                    widget);
+
+  widget->style = gtk_style_attach (gtk_widget_get_style (widget),
+                                    gtk_widget_get_window (widget));
+  gtk_style_set_background (gtk_widget_get_style (widget),
+                            gtk_widget_get_window (widget),
+                            GTK_STATE_NORMAL);
+
+  for (list = box->children; list; list = g_list_next (list))
+    gimp_overlay_child_realize (box, list->data);
+}
+
+static void
+gimp_overlay_box_unrealize (GtkWidget *widget)
+{
+  GimpOverlayBox *box = GIMP_OVERLAY_BOX (widget);
+  GList          *list;
+
+  for (list = box->children; list; list = g_list_next (list))
+    gimp_overlay_child_unrealize (box, list->data);
+
+  GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
+}
+
+static void
+gimp_overlay_box_size_request (GtkWidget      *widget,
+                               GtkRequisition *requisition)
+{
+  GimpOverlayBox *box = GIMP_OVERLAY_BOX (widget);
+  GList          *list;
+  gint            border_width;
+
+  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+
+  requisition->width  = 1 + 2 * border_width;
+  requisition->height = 1 + 2 * border_width;
+
+  for (list = box->children; list; list = g_list_next (list))
+    gimp_overlay_child_size_request (box, list->data);
+}
+
+static void
+gimp_overlay_box_size_allocate (GtkWidget     *widget,
+                                GtkAllocation *allocation)
+{
+  GimpOverlayBox *box = GIMP_OVERLAY_BOX (widget);
+  GList          *list;
+
+  gtk_widget_set_allocation (widget, allocation);
+
+  if (GTK_WIDGET_REALIZED (widget))
+    gdk_window_move_resize (gtk_widget_get_window (widget),
+                            allocation->x,
+                            allocation->y,
+                            allocation->width,
+                            allocation->height);
+
+  for (list = box->children; list; list = g_list_next (list))
+    gimp_overlay_child_size_allocate (box, list->data);
+}
+
+static gboolean
+gimp_overlay_box_expose (GtkWidget      *widget,
+                         GdkEventExpose *event)
+{
+  if (gtk_widget_is_drawable (widget))
+    {
+      GimpOverlayBox *box = GIMP_OVERLAY_BOX (widget);
+      GList          *list;
+
+      for (list = box->children; list; list = g_list_next (list))
+        {
+          if (gimp_overlay_child_expose (box, list->data, event))
+            return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+static gboolean
+gimp_overlay_box_damage (GtkWidget      *widget,
+                         GdkEventExpose *event)
+{
+  GimpOverlayBox *box = GIMP_OVERLAY_BOX (widget);
+  GList          *list;
+
+  for (list = box->children; list; list = g_list_next (list))
+    {
+      if (gimp_overlay_child_damage (box, list->data, event))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+gimp_overlay_box_add (GtkContainer *container,
+                      GtkWidget    *widget)
+{
+  gimp_overlay_box_add_child (GIMP_OVERLAY_BOX (container), widget, 0.5, 0.5);
+}
+
+static void
+gimp_overlay_box_remove (GtkContainer *container,
+                         GtkWidget    *widget)
+{
+  GimpOverlayBox   *box   = GIMP_OVERLAY_BOX (container);
+  GimpOverlayChild *child = gimp_overlay_child_find (box, widget);
+
+  if (child)
+    {
+      if (gtk_widget_get_visible (widget))
+        gimp_overlay_child_invalidate (box, child);
+
+      box->children = g_list_remove (box->children, child);
+
+      gimp_overlay_child_free (box, child);
+    }
+}
+
+static void
+gimp_overlay_box_forall (GtkContainer *container,
+                         gboolean      include_internals,
+                         GtkCallback   callback,
+                         gpointer      callback_data)
+{
+  GimpOverlayBox *box = GIMP_OVERLAY_BOX (container);
+  GList          *list;
+
+  list = box->children;
+  while (list)
+    {
+      GimpOverlayChild *child = list->data;
+
+      list = list->next;
+
+      (* callback) (child->widget, callback_data);
+    }
+}
+
+static GType
+gimp_overlay_box_child_type (GtkContainer *container)
+{
+  return GTK_TYPE_WIDGET;
+}
+
+static GdkWindow *
+gimp_overlay_box_pick_embedded_child (GdkWindow      *parent,
+                                      gdouble         parent_x,
+                                      gdouble         parent_y,
+                                      GimpOverlayBox *box)
+{
+  GList *list;
+
+  for (list = box->children; list; list = g_list_next (list))
+    {
+      GimpOverlayChild *child = list->data;
+
+      if (gimp_overlay_child_pick (box, child, parent_x, parent_y))
+        return child->window;
+    }
+
+  return NULL;
+}
+
+
+/*  public functions  */
+
+/**
+ * gimp_overlay_box_new:
+ *
+ * Creates a new #GimpOverlayBox widget.
+ *
+ * Return value: a new #GimpOverlayBox widget
+ **/
+GtkWidget *
+gimp_overlay_box_new (void)
+{
+  return g_object_new (GIMP_TYPE_OVERLAY_BOX, NULL);
+}
+
+void
+gimp_overlay_box_add_child (GimpOverlayBox *box,
+                            GtkWidget      *widget,
+                            gdouble         xalign,
+                            gdouble         yalign)
+{
+  GimpOverlayChild *child;
+
+  g_return_if_fail (GIMP_IS_OVERLAY_BOX (box));
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  child = gimp_overlay_child_new (box, widget, xalign, yalign, 0.0, 0.7);
+
+  box->children = g_list_append (box->children, child);
+}
+
+void
+gimp_overlay_box_set_child_packing (GimpOverlayBox *box,
+                                    GtkWidget      *widget,
+                                    gdouble         xalign,
+                                    gdouble         yalign)
+{
+  GimpOverlayChild *child = gimp_overlay_child_find (box, widget);
+
+  if (child)
+    {
+      xalign = CLAMP (xalign, 0.0, 1.0);
+      yalign = CLAMP (yalign, 0.0, 1.0);
+
+      if (child->xalign != xalign ||
+          child->yalign != yalign)
+        {
+          gimp_overlay_child_invalidate (box, child);
+
+          child->xalign = xalign;
+          child->yalign = yalign;
+
+          gtk_widget_queue_resize (widget);
+        }
+    }
+}
+
+void
+gimp_overlay_box_set_child_angle (GimpOverlayBox *box,
+                                  GtkWidget      *widget,
+                                  gdouble         angle)
+{
+  GimpOverlayChild *child = gimp_overlay_child_find (box, widget);
+
+  if (child)
+    {
+      if (child->angle != angle)
+        {
+          gimp_overlay_child_invalidate (box, child);
+
+          child->angle = angle;
+
+          gtk_widget_queue_draw (widget);
+        }
+    }
+}
+
+void
+gimp_overlay_box_set_child_opacity (GimpOverlayBox *box,
+                                    GtkWidget      *widget,
+                                    gdouble         opacity)
+{
+  GimpOverlayChild *child = gimp_overlay_child_find (box, widget);
+
+  if (child)
+    {
+      opacity = CLAMP (opacity, 0.0, 1.0);
+
+      if (child->opacity != opacity)
+        {
+          child->opacity = opacity;
+
+          gtk_widget_queue_draw (widget);
+        }
+    }
+}
+
+/**
+ * gimp_overlay_box_scroll:
+ * @box: the #GimpOverlayBox widget to scroll.
+ * @offset_x: the x scroll amount.
+ * @offset_y: the y scroll amount.
+ *
+ * Scrolls the box using gdk_window_scroll() and makes sure the result
+ * is displayed immediately by calling gdk_window_process_updates().
+ **/
+void
+gimp_overlay_box_scroll (GimpOverlayBox *box,
+                         gint            offset_x,
+                         gint            offset_y)
+{
+  GtkWidget *widget;
+  GdkWindow *window;
+  GList     *list;
+
+  g_return_if_fail (GIMP_IS_OVERLAY_BOX (box));
+
+  widget = GTK_WIDGET (box);
+  window = gtk_widget_get_window (widget);
+
+  /*  Undraw all overlays  */
+  for (list = box->children; list; list = g_list_next (list))
+    gimp_overlay_child_invalidate (box, list->data);
+
+  gdk_window_scroll (window, offset_x, offset_y);
+
+  /*  Re-draw all overlays  */
+  for (list = box->children; list; list = g_list_next (list))
+    gimp_overlay_child_invalidate (box, list->data);
+
+  /*  Make sure expose events are processed before scrolling again  */
+  gdk_window_process_updates (window, FALSE);
+}
diff --git a/app/widgets/gimpoverlaybox.h b/app/widgets/gimpoverlaybox.h
new file mode 100644
index 0000000..dbdc44b
--- /dev/null
+++ b/app/widgets/gimpoverlaybox.h
@@ -0,0 +1,72 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpOverlayBox
+ * Copyright (C) 2009 Michael Natterer <mitch gimp org>
+ *
+ * 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_OVERLAY_BOX_H__
+#define __GIMP_OVERLAY_BOX_H__
+
+
+#define GIMP_TYPE_OVERLAY_BOX            (gimp_overlay_box_get_type ())
+#define GIMP_OVERLAY_BOX(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OVERLAY_BOX, GimpOverlayBox))
+#define GIMP_OVERLAY_BOX_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OVERLAY_BOX, GimpOverlayBoxClass))
+#define GIMP_IS_OVERLAY_BOX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OVERLAY_BOX))
+#define GIMP_IS_OVERLAY_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OVERLAY_BOX))
+#define GIMP_OVERLAY_BOX_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OVERLAY_BOX, GimpOverlayBoxClass))
+
+
+typedef struct _GimpOverlayBoxClass GimpOverlayBoxClass;
+
+struct _GimpOverlayBox
+{
+  GtkContainer  parent_instance;
+
+  GList        *children;
+};
+
+struct _GimpOverlayBoxClass
+{
+  GtkContainerClass  parent_class;
+};
+
+
+GType       gimp_overlay_box_get_type          (void) G_GNUC_CONST;
+
+GtkWidget * gimp_overlay_box_new               (void);
+
+void        gimp_overlay_box_add_child         (GimpOverlayBox *canvas,
+                                                GtkWidget      *child,
+                                                gdouble         xalign,
+                                                gdouble         yalign);
+void        gimp_overlay_box_set_child_packing (GimpOverlayBox *canvas,
+                                                GtkWidget      *child,
+                                                gdouble         xalign,
+                                                gdouble         yalign);
+void        gimp_overlay_box_set_child_angle   (GimpOverlayBox *canvas,
+                                                GtkWidget      *child,
+                                                gdouble         angle);
+void        gimp_overlay_box_set_child_opacity (GimpOverlayBox *canvas,
+                                                GtkWidget      *child,
+                                                gdouble         opacity);
+
+void        gimp_overlay_box_scroll            (GimpOverlayBox *canvas,
+                                                gint            offset_x,
+                                                gint            offset_y);
+
+
+#endif /*  __GIMP_OVERLAY_BOX_H__  */
diff --git a/app/widgets/gimpoverlaychild.c b/app/widgets/gimpoverlaychild.c
new file mode 100644
index 0000000..a198fe2
--- /dev/null
+++ b/app/widgets/gimpoverlaychild.c
@@ -0,0 +1,474 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoverlaychild.c
+ * Copyright (C) 2009 Michael Natterer <mitch gimp org>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#undef GSEAL_ENABLE
+
+#include <gtk/gtk.h>
+
+#include <libgimpmath/gimpmath.h>
+
+#include "widgets-types.h"
+
+#include "gimpoverlaybox.h"
+#include "gimpoverlaychild.h"
+
+
+/*  local function prototypes  */
+
+static void   gimp_overlay_child_transform_bounds (GimpOverlayChild *child,
+                                                   GdkRectangle     *bounds_child,
+                                                   GdkRectangle     *bounds_box);
+static void   gimp_overlay_child_from_embedder    (GdkWindow        *child_window,
+                                                   gdouble           box_x,
+                                                   gdouble           box_y,
+                                                   gdouble          *child_x,
+                                                   gdouble          *child_y,
+                                                   GimpOverlayChild *child);
+static void   gimp_overlay_child_to_embedder      (GdkWindow        *child_window,
+                                                   gdouble           child_x,
+                                                   gdouble           child_y,
+                                                   gdouble          *box_x,
+                                                   gdouble          *box_y,
+                                                   GimpOverlayChild *child);
+
+
+/*  public functions  */
+
+GimpOverlayChild *
+gimp_overlay_child_new (GimpOverlayBox *box,
+                        GtkWidget      *widget,
+                        gdouble         xalign,
+                        gdouble         yalign,
+                        gdouble         angle,
+                        gdouble         opacity)
+{
+  GimpOverlayChild *child;
+
+  g_return_val_if_fail (GIMP_IS_OVERLAY_BOX (box), NULL);
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+  child = g_slice_new0 (GimpOverlayChild);
+
+  child->widget  = widget;
+  child->xalign  = CLAMP (xalign, 0.0, 1.0);
+  child->yalign  = CLAMP (yalign, 0.0, 1.0);
+  child->angle   = angle;
+  child->opacity = CLAMP (opacity, 0.0, 1.0);
+
+  cairo_matrix_init_identity (&child->matrix);
+
+  if (GTK_WIDGET_REALIZED (box))
+    gimp_overlay_child_realize (box, child);
+
+  gtk_widget_set_parent (widget, GTK_WIDGET (box));
+
+  return child;
+}
+
+void
+gimp_overlay_child_free (GimpOverlayBox   *box,
+                         GimpOverlayChild *child)
+{
+  g_return_if_fail (GIMP_IS_OVERLAY_BOX (box));
+  g_return_if_fail (child != NULL);
+
+  gtk_widget_unparent (child->widget);
+
+  if (GTK_WIDGET_REALIZED (box))
+    gimp_overlay_child_unrealize (box, child);
+
+  g_slice_free (GimpOverlayChild, child);
+}
+
+GimpOverlayChild *
+gimp_overlay_child_find (GimpOverlayBox *box,
+                         GtkWidget      *widget)
+{
+  GList *list;
+
+  g_return_val_if_fail (GIMP_IS_OVERLAY_BOX (box), NULL);
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+  g_return_val_if_fail (gtk_widget_get_parent (widget) == GTK_WIDGET (box),
+                        NULL);
+
+  for (list = box->children; list; list = g_list_next (list))
+    {
+      GimpOverlayChild *child = list->data;
+
+      if (child->widget == widget)
+        return child;
+    }
+
+  return NULL;
+}
+
+void
+gimp_overlay_child_realize (GimpOverlayBox   *box,
+                            GimpOverlayChild *child)
+{
+  GtkWidget     *widget;
+  GtkAllocation  child_allocation;
+  GdkWindowAttr  attributes;
+  gint           attributes_mask;
+
+  g_return_if_fail (GIMP_IS_OVERLAY_BOX (box));
+  g_return_if_fail (child != NULL);
+
+  widget = GTK_WIDGET (box);
+
+  gtk_widget_get_allocation (child->widget, &child_allocation);
+
+  if (gtk_widget_get_visible (child->widget))
+    {
+      attributes.width  = child_allocation.width;
+      attributes.height = child_allocation.height;
+    }
+  else
+    {
+      attributes.width  = 1;
+      attributes.height = 1;
+    }
+
+  attributes.x           = child_allocation.x;
+  attributes.y           = child_allocation.y;
+  attributes.window_type = GDK_WINDOW_OFFSCREEN;
+  attributes.wclass      = GDK_INPUT_OUTPUT;
+  attributes.visual      = gtk_widget_get_visual (widget);
+  attributes.colormap    = gtk_widget_get_colormap (widget);
+  attributes.event_mask  = gtk_widget_get_events (widget);
+
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+  child->window = gdk_window_new (gtk_widget_get_root_window (widget),
+                                  &attributes, attributes_mask);
+  gdk_window_set_user_data (child->window, widget);
+  gtk_widget_set_parent_window (child->widget, child->window);
+  gdk_offscreen_window_set_embedder (child->window,
+                                     gtk_widget_get_window (widget));
+
+  g_signal_connect (child->window, "from-embedder",
+                    G_CALLBACK (gimp_overlay_child_from_embedder),
+                    child);
+  g_signal_connect (child->window, "to-embedder",
+                    G_CALLBACK (gimp_overlay_child_to_embedder),
+                    child);
+
+  gtk_style_set_background (gtk_widget_get_style (widget),
+                            child->window, GTK_STATE_NORMAL);
+  gdk_window_show (child->window);
+}
+
+void
+gimp_overlay_child_unrealize (GimpOverlayBox   *box,
+                              GimpOverlayChild *child)
+{
+  g_return_if_fail (GIMP_IS_OVERLAY_BOX (box));
+  g_return_if_fail (child != NULL);
+
+  gdk_window_set_user_data (child->window, NULL);
+  gdk_window_destroy (child->window);
+  child->window = NULL;
+}
+
+void
+gimp_overlay_child_size_request (GimpOverlayBox   *box,
+                                 GimpOverlayChild *child)
+{
+  GtkRequisition child_requisition;
+
+  g_return_if_fail (GIMP_IS_OVERLAY_BOX (box));
+  g_return_if_fail (child != NULL);
+
+  gtk_widget_size_request (child->widget, &child_requisition);
+}
+
+void
+gimp_overlay_child_size_allocate (GimpOverlayBox   *box,
+                                  GimpOverlayChild *child)
+{
+  GtkWidget      *widget;
+  GtkAllocation   allocation;
+  gint            border;
+  GtkRequisition  child_requisition;
+  GtkAllocation   child_allocation;
+  GdkRectangle    bounds;
+  gint            available_width;
+  gint            available_height;
+  gint            x;
+  gint            y;
+
+  g_return_if_fail (GIMP_IS_OVERLAY_BOX (box));
+  g_return_if_fail (child != NULL);
+
+  widget = GTK_WIDGET (box);
+
+  gtk_widget_get_allocation (widget, &allocation);
+
+  gtk_widget_get_child_requisition (child->widget, &child_requisition);
+
+  child_allocation.x      = 0;
+  child_allocation.y      = 0;
+  child_allocation.width  = child_requisition.width;
+  child_allocation.height = child_requisition.height;
+
+  gtk_widget_size_allocate (child->widget, &child_allocation);
+
+  gtk_widget_get_allocation (child->widget, &child_allocation);
+
+  if (GTK_WIDGET_REALIZED (widget))
+    gdk_window_move_resize (child->window,
+                            child_allocation.x,
+                            child_allocation.y,
+                            child_allocation.width,
+                            child_allocation.height);
+
+  cairo_matrix_init_identity (&child->matrix);
+
+  /* local transform */
+  cairo_matrix_rotate (&child->matrix, child->angle);
+
+  gimp_overlay_child_transform_bounds (child, &child_allocation, &bounds);
+
+  border = gtk_container_get_border_width (GTK_CONTAINER (box));
+
+  available_width  = allocation.width  - 2 * border;
+  available_height = allocation.height - 2 * border;
+
+  x = border;
+  y = border;
+
+  if (available_width > bounds.width)
+    x += child->xalign * (available_width - bounds.width) - bounds.x;
+
+  if (available_height > bounds.height)
+    y += child->yalign * (available_height - bounds.height) - bounds.y;
+
+  cairo_matrix_init_translate (&child->matrix, x, y);
+
+  /* local transform */
+  cairo_matrix_rotate (&child->matrix, child->angle);
+}
+
+gboolean
+gimp_overlay_child_expose (GimpOverlayBox   *box,
+                           GimpOverlayChild *child,
+                           GdkEventExpose   *event)
+{
+  GtkWidget *widget;
+
+  g_return_val_if_fail (GIMP_IS_OVERLAY_BOX (box), FALSE);
+  g_return_val_if_fail (child != NULL, FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  widget = GTK_WIDGET (box);
+
+  if (event->window == gtk_widget_get_window (widget))
+    {
+      GtkAllocation child_allocation;
+      GdkRectangle  bounds;
+
+      gtk_widget_get_allocation (child->widget, &child_allocation);
+
+      gimp_overlay_child_transform_bounds (child, &child_allocation, &bounds);
+
+      if (gtk_widget_get_visible (child->widget) &&
+          gdk_rectangle_intersect (&event->area, &bounds, NULL))
+        {
+          GdkPixmap *pixmap = gdk_offscreen_window_get_pixmap (child->window);
+          cairo_t   *cr     = gdk_cairo_create (gtk_widget_get_window (widget));
+
+          cairo_transform (cr, &child->matrix);
+          gdk_cairo_set_source_pixmap (cr, pixmap, 0, 0);
+          cairo_paint_with_alpha (cr, child->opacity);
+          cairo_destroy (cr);
+        }
+    }
+  else if (event->window == child->window)
+    {
+      gtk_paint_flat_box (gtk_widget_get_style (widget),
+                          event->window,
+                          GTK_STATE_NORMAL, GTK_SHADOW_NONE,
+                          &event->area, widget, NULL,
+                          0, 0, -1, -1);
+
+      gtk_container_propagate_expose (GTK_CONTAINER (widget),
+                                      child->widget,
+                                      event);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+gboolean
+gimp_overlay_child_damage (GimpOverlayBox   *box,
+                           GimpOverlayChild *child,
+                           GdkEventExpose   *event)
+{
+  GtkWidget *widget;
+
+  g_return_val_if_fail (GIMP_IS_OVERLAY_BOX (box), FALSE);
+  g_return_val_if_fail (child != NULL, FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  widget = GTK_WIDGET (box);
+
+  if (event->window == child->window)
+    {
+      GdkRectangle bounds;
+
+      gimp_overlay_child_transform_bounds (child, &event->area, &bounds);
+
+      gdk_window_invalidate_rect (gtk_widget_get_window (widget),
+                                  &bounds, FALSE);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+void
+gimp_overlay_child_invalidate (GimpOverlayBox   *box,
+                               GimpOverlayChild *child)
+{
+  GdkWindow *window;
+
+  g_return_if_fail (GIMP_IS_OVERLAY_BOX (box));
+  g_return_if_fail (child != NULL);
+
+  window = gtk_widget_get_window (GTK_WIDGET (box));
+
+  if (window && gtk_widget_get_visible (child->widget))
+    {
+      GtkAllocation child_allocation;
+      GdkRectangle  bounds;
+
+      gtk_widget_get_allocation (child->widget, &child_allocation);
+
+      gimp_overlay_child_transform_bounds (child, &child_allocation,
+                                           &bounds);
+
+      gdk_window_invalidate_rect (window, &bounds, FALSE);
+    }
+}
+
+gboolean
+gimp_overlay_child_pick (GimpOverlayBox   *box,
+                         GimpOverlayChild *child,
+                         gdouble           box_x,
+                         gdouble           box_y)
+{
+  GtkAllocation child_allocation;
+  gdouble       child_x;
+  gdouble       child_y;
+
+  g_return_val_if_fail (GIMP_IS_OVERLAY_BOX (box), FALSE);
+  g_return_val_if_fail (child != NULL, FALSE);
+
+  gimp_overlay_child_from_embedder (child->window,
+                                    box_x, box_y,
+                                    &child_x, &child_y,
+                                    child);
+
+  gtk_widget_get_allocation (child->widget, &child_allocation);
+
+  if (child_x >= 0                      &&
+      child_x <  child_allocation.width &&
+      child_y >= 0                      &&
+      child_y <  child_allocation.height)
+    {
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+
+/*  private functions  */
+
+static void
+gimp_overlay_child_transform_bounds (GimpOverlayChild *child,
+                                     GdkRectangle     *bounds_child,
+                                     GdkRectangle     *bounds_box)
+{
+  gdouble x1, x2, x3, x4;
+  gdouble y1, y2, y3, y4;
+
+  x1 = bounds_child->x;
+  y1 = bounds_child->y;
+
+  x2 = bounds_child->x + bounds_child->width;
+  y2 = bounds_child->y;
+
+  x3 = bounds_child->x;
+  y3 = bounds_child->y + bounds_child->height;
+
+  x4 = bounds_child->x + bounds_child->width;
+  y4 = bounds_child->y + bounds_child->height;
+
+  cairo_matrix_transform_point (&child->matrix, &x1, &y1);
+  cairo_matrix_transform_point (&child->matrix, &x2, &y2);
+  cairo_matrix_transform_point (&child->matrix, &x3, &y3);
+  cairo_matrix_transform_point (&child->matrix, &x4, &y4);
+
+#define MIN4(a,b,c,d) MIN(MIN((a),(b)),MIN((c),(d)))
+#define MAX4(a,b,c,d) MAX(MAX((a),(b)),MAX((c),(d)))
+
+  bounds_box->x      = (gint) floor (MIN4 (x1, x2, x3, x4));
+  bounds_box->y      = (gint) floor (MIN4 (y1, y2, y3, y4));
+  bounds_box->width  = (gint) ceil (MAX4 (x1, x2, x3, x4)) - bounds_box->x;
+  bounds_box->height = (gint) ceil (MAX4 (y1, y2, y3, y4)) - bounds_box->y;
+}
+
+static void
+gimp_overlay_child_from_embedder (GdkWindow        *child_window,
+                                  gdouble           box_x,
+                                  gdouble           box_y,
+                                  gdouble          *child_x,
+                                  gdouble          *child_y,
+                                  GimpOverlayChild *child)
+{
+  cairo_matrix_t inverse = child->matrix;
+
+  *child_x = box_x;
+  *child_y = box_y;
+
+  cairo_matrix_invert (&inverse);
+  cairo_matrix_transform_point (&inverse, child_x, child_y);
+}
+
+static void
+gimp_overlay_child_to_embedder (GdkWindow        *child_window,
+                                gdouble           child_x,
+                                gdouble           child_y,
+                                gdouble          *box_x,
+                                gdouble          *box_y,
+                                GimpOverlayChild *child)
+{
+  *box_x = child_x;
+  *box_y = child_y;
+
+  cairo_matrix_transform_point (&child->matrix, box_x, box_y);
+}
diff --git a/app/widgets/gimpoverlaychild.h b/app/widgets/gimpoverlaychild.h
new file mode 100644
index 0000000..9182927
--- /dev/null
+++ b/app/widgets/gimpoverlaychild.h
@@ -0,0 +1,77 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoverlaychild.h
+ * Copyright (C) 2009 Michael Natterer <mitch gimp org>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GIMP_OVERLAY_CHILD_H__
+#define __GIMP_OVERLAY_CHILD_H__
+
+
+typedef struct _GimpOverlayChild GimpOverlayChild;
+
+struct _GimpOverlayChild
+{
+  GtkWidget      *widget;
+  GdkWindow      *window;
+  gdouble         xalign;
+  gdouble         yalign;
+  gdouble         angle;
+  gdouble         opacity;
+
+  /* updated in size_allocate */
+  cairo_matrix_t  matrix;
+};
+
+
+GimpOverlayChild * gimp_overlay_child_new           (GimpOverlayBox  *box,
+                                                     GtkWidget       *widget,
+                                                     gdouble          xalign,
+                                                     gdouble          yalign,
+                                                     gdouble          angle,
+                                                     gdouble          opacity);
+void               gimp_overlay_child_free          (GimpOverlayBox   *box,
+                                                     GimpOverlayChild *child);
+
+GimpOverlayChild * gimp_overlay_child_find          (GimpOverlayBox   *box,
+                                                     GtkWidget        *widget);
+
+void               gimp_overlay_child_realize       (GimpOverlayBox   *box,
+                                                     GimpOverlayChild *child);
+void               gimp_overlay_child_unrealize     (GimpOverlayBox   *box,
+                                                     GimpOverlayChild *child);
+void               gimp_overlay_child_size_request  (GimpOverlayBox   *box,
+                                                     GimpOverlayChild *child);
+void               gimp_overlay_child_size_allocate (GimpOverlayBox   *box,
+                                                     GimpOverlayChild *child);
+gboolean           gimp_overlay_child_expose        (GimpOverlayBox   *box,
+                                                     GimpOverlayChild *child,
+                                                     GdkEventExpose   *event);
+gboolean           gimp_overlay_child_damage        (GimpOverlayBox   *box,
+                                                     GimpOverlayChild *child,
+                                                     GdkEventExpose   *event);
+
+void               gimp_overlay_child_invalidate    (GimpOverlayBox   *box,
+                                                     GimpOverlayChild *child);
+gboolean           gimp_overlay_child_pick          (GimpOverlayBox   *box,
+                                                     GimpOverlayChild *child,
+                                                     gdouble           box_x,
+                                                     gdouble           box_y);
+
+
+#endif /* __GIMP_OVERLAY_CHILD_H__ */
diff --git a/app/widgets/widgets-types.h b/app/widgets/widgets-types.h
index 50adcae..997fb42 100644
--- a/app/widgets/widgets-types.h
+++ b/app/widgets/widgets-types.h
@@ -176,6 +176,7 @@ typedef struct _GimpImageProfileView         GimpImageProfileView;
 typedef struct _GimpLanguageStore            GimpLanguageStore;
 typedef struct _GimpLanguageEntry            GimpLanguageEntry;
 typedef struct _GimpMessageBox               GimpMessageBox;
+typedef struct _GimpOverlayBox               GimpOverlayBox;
 typedef struct _GimpProgressBox              GimpProgressBox;
 typedef struct _GimpScaleButton              GimpScaleButton;
 typedef struct _GimpSettingsBox              GimpSettingsBox;



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