[libgda] GdaBrowser: allow to zoom some forms



commit a1ad43cd9d8dc765b69e04863d6d4a8a7715c854
Author: Vivien Malerba <malerba gnome-db org>
Date:   Tue Jul 12 20:39:13 2011 +0200

    GdaBrowser: allow to zoom some forms

 tools/browser/common/Makefile.am      |    4 +-
 tools/browser/common/ui-formgrid.c    |   81 ++-
 tools/browser/common/widget-overlay.c | 1045 +++++++++++++++++++++++++++++++++
 tools/browser/common/widget-overlay.h |   62 ++
 4 files changed, 1173 insertions(+), 19 deletions(-)
---
diff --git a/tools/browser/common/Makefile.am b/tools/browser/common/Makefile.am
index 9b0b787..4924ee3 100644
--- a/tools/browser/common/Makefile.am
+++ b/tools/browser/common/Makefile.am
@@ -32,7 +32,9 @@ libcommon_la_SOURCES = \
 	ui-formgrid.c \
 	ui-formgrid.h \
 	fk-declare.c \
-	fk-declare.h
+	fk-declare.h \
+	widget-overlay.c \
+	widget-overlay.h
 
 $(OBJECTS): marshal.c marshal.h
 
diff --git a/tools/browser/common/ui-formgrid.c b/tools/browser/common/ui-formgrid.c
index 66e91c8..d2f4764 100644
--- a/tools/browser/common/ui-formgrid.c
+++ b/tools/browser/common/ui-formgrid.c
@@ -26,6 +26,7 @@
 #include <libgda-ui/gdaui-data-selector.h>
 #include "../support.h"
 #include "../browser-window.h"
+#include "widget-overlay.h"
 #include <libgda/gda-data-model-extra.h>
 #ifdef HAVE_LDAP
 #include "../ldap-browser/ldap-browser-perspective.h"
@@ -52,6 +53,7 @@ struct _UiFormGridPriv
 	GtkWidget   *raw_form;
 	GtkWidget   *raw_grid;
 	GtkWidget   *info;
+	GtkWidget   *overlay;
 	GtkWidget   *autoupdate_toggle;
 	gboolean     autoupdate;
 	gboolean     autoupdate_possible;
@@ -168,25 +170,48 @@ static void
 ui_formgrid_show (GtkWidget *widget)
 {
 	UiFormGrid *formgrid;
+	GtkWidget *ovl, *packed;
 	formgrid = UI_FORMGRID (widget);
 
-	/* finalize packing */
-	if (formgrid->priv->scroll_form) {
-		GtkWidget *sw, *vp;
-		sw = gtk_scrolled_window_new (NULL, NULL);
-		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
-						GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-		gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_NONE);
-		vp = gtk_viewport_new (NULL, NULL);
-		gtk_container_add (GTK_CONTAINER (sw), vp);
-		gtk_viewport_set_shadow_type (GTK_VIEWPORT (vp), GTK_SHADOW_NONE);
-		gtk_container_add (GTK_CONTAINER (vp), formgrid->priv->raw_form);
-		gtk_notebook_append_page (GTK_NOTEBOOK (formgrid->priv->nb), sw, NULL);
-		gtk_widget_show_all (sw);
-	}
-	else {
-		gtk_notebook_append_page (GTK_NOTEBOOK (formgrid->priv->nb), formgrid->priv->raw_form, NULL);
-		gtk_widget_show (formgrid->priv->raw_form);
+	if (! formgrid->priv->overlay) {
+		/* finalize packing */
+		if (formgrid->priv->scroll_form) {
+			GtkWidget *sw, *vp;
+			sw = gtk_scrolled_window_new (NULL, NULL);
+			gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+							GTK_POLICY_AUTOMATIC,
+							GTK_POLICY_AUTOMATIC);
+			gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
+							     GTK_SHADOW_NONE);
+			vp = gtk_viewport_new (NULL, NULL);
+			gtk_container_add (GTK_CONTAINER (sw), vp);
+			gtk_viewport_set_shadow_type (GTK_VIEWPORT (vp), GTK_SHADOW_NONE);
+			gtk_container_add (GTK_CONTAINER (vp), formgrid->priv->raw_form);
+			gtk_widget_show_all (sw);
+			packed = sw;
+		}
+		else {
+			gtk_widget_show (formgrid->priv->raw_form);
+			packed = formgrid->priv->raw_form;
+		}
+		
+		/* overlay */
+		ovl = widget_overlay_new ();
+		formgrid->priv->overlay = ovl;
+		g_object_set (G_OBJECT (ovl), "add-scale", TRUE, NULL);
+		g_object_set (G_OBJECT (ovl), "add-scale", FALSE, NULL);
+		gtk_container_add (GTK_CONTAINER (ovl), packed);
+		widget_overlay_set_child_props (WIDGET_OVERLAY (ovl), packed,
+						WIDGET_OVERLAY_CHILD_HALIGN,
+						WIDGET_OVERLAY_ALIGN_FILL,
+						WIDGET_OVERLAY_CHILD_VALIGN,
+						WIDGET_OVERLAY_ALIGN_FILL,
+						WIDGET_OVERLAY_CHILD_SCALE, 1.,
+						-1);
+		gtk_widget_show (ovl);
+		gtk_notebook_append_page (GTK_NOTEBOOK (formgrid->priv->nb), ovl, NULL);
+		gtk_notebook_set_current_page (GTK_NOTEBOOK (formgrid->priv->nb),
+					       0);
 	}
 
 	((GtkWidgetClass *)parent_class)->show (widget);
@@ -354,9 +379,10 @@ static void execute_action_mitem_cb (GtkMenuItem *menuitem, UiFormGrid *formgrid
 #ifdef HAVE_LDAP
 static void ldap_view_dn_mitem_cb (GtkMenuItem *menuitem, UiFormGrid *formgrid);
 #endif
+static void zoom_mitem_cb (GtkCheckMenuItem *checkmenuitem, UiFormGrid *formgrid);
 
 static void
-form_grid_populate_popup_cb (G_GNUC_UNUSED GtkWidget *wid, GtkMenu *menu, UiFormGrid *formgrid)
+form_grid_populate_popup_cb (GtkWidget *wid, GtkMenu *menu, UiFormGrid *formgrid)
 {
 	/* add actions to execute to menu */
 	GdaDataModelIter *iter;
@@ -432,6 +458,25 @@ form_grid_populate_popup_cb (G_GNUC_UNUSED GtkWidget *wid, GtkMenu *menu, UiForm
 		}
 	}
 #endif
+
+	if (wid == formgrid->priv->raw_form) {
+		GtkWidget *mitem;
+		gboolean add_scale;
+		g_object_get (G_OBJECT (formgrid->priv->overlay), "add-scale", &add_scale, NULL);
+		mitem = gtk_check_menu_item_new_with_label (_("Zoom..."));
+		gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mitem), add_scale);
+		gtk_widget_show (mitem);
+		gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), mitem);
+		g_signal_connect (mitem, "toggled",
+				  G_CALLBACK (zoom_mitem_cb), formgrid);
+	}
+}
+
+static void
+zoom_mitem_cb (GtkCheckMenuItem *checkmenuitem, UiFormGrid *formgrid)
+{
+	g_object_set (G_OBJECT (formgrid->priv->overlay), "add-scale",
+		      gtk_check_menu_item_get_active (checkmenuitem), NULL);
 }
 
 typedef struct {
diff --git a/tools/browser/common/widget-overlay.c b/tools/browser/common/widget-overlay.c
new file mode 100644
index 0000000..9b27203
--- /dev/null
+++ b/tools/browser/common/widget-overlay.c
@@ -0,0 +1,1045 @@
+/*
+ * Copyright (C) 2010 David King <davidk openismus com>
+ * Copyright (C) 2010 - 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This library 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 2 of the License, or (at your option) any later version.
+ *
+ * This library 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 this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#include "widget-overlay.h"
+#include <stdarg.h>
+#include <glib/gi18n-lib.h>
+
+#define SCALE_MIN .6
+#define SCALE_MAX 1.
+#define SCALE_MAX_SCALE 1.5
+#define SCALE_STEP ((SCALE_MAX - SCALE_MIN) / 20.)
+
+static void     widget_overlay_realize       (GtkWidget       *widget);
+static void     widget_overlay_unrealize     (GtkWidget       *widget);
+static void     widget_overlay_get_preferred_width  (GtkWidget *widget,
+						     gint      *minimum,
+						     gint      *natural);
+static void     widget_overlay_get_preferred_height (GtkWidget *widget,
+						     gint      *minimum,
+						     gint      *natural);
+static void     widget_overlay_size_allocate (GtkWidget       *widget,
+					      GtkAllocation   *allocation);
+static gboolean widget_overlay_damage        (GtkWidget       *widget,
+					      GdkEventExpose  *event);
+static gboolean widget_overlay_draw          (GtkWidget       *widget,
+					      cairo_t         *cr);
+static void     widget_overlay_add           (GtkContainer    *container,
+					      GtkWidget       *child);
+static void     widget_overlay_remove        (GtkContainer    *container,
+					      GtkWidget       *widget);
+static void     widget_overlay_forall        (GtkContainer    *container,
+					      gboolean         include_internals,
+					      GtkCallback      callback,
+					      gpointer         callback_data);
+static void     widget_overlay_show (GtkWidget *widget);
+static void     widget_overlay_dispose       (GObject *obj);
+static void     widget_overlay_finalize      (GObject *obj);
+
+static gboolean widget_overlay_event         (GtkWidget *widget, GdkEvent *event);
+
+static void widget_overlay_set_property (GObject *object,
+                                         guint param_id,
+                                         const GValue *value,
+                                         GParamSpec *pspec);
+static void widget_overlay_get_property (GObject *object,
+                                         guint param_id,
+                                         GValue *value,
+                                         GParamSpec *pspec);
+
+static GdkWindow * pick_offscreen_child (GdkWindow *offscreen_window,
+					 double widget_x, double widget_y,
+					 WidgetOverlay *ovl);
+
+
+/* properties */
+enum
+{
+        PROP_0,
+	PROP_ADD_SCALE
+};
+
+G_DEFINE_TYPE (WidgetOverlay, widget_overlay, GTK_TYPE_CONTAINER);
+
+typedef struct  {
+	WidgetOverlay *ovl;
+	GtkWidget *child;
+	GdkWindow *offscreen_window;
+	WidgetOverlayAlign halign;
+	WidgetOverlayAlign valign;
+	gint x, y;
+	gdouble alpha;
+	gboolean ignore_events;
+	gdouble scale;
+
+	gboolean is_tooltip;
+} ChildData;
+
+struct _WidgetOverlayPrivate
+{
+	GList *children;
+	ChildData *scale_child;
+	GtkRange *scale_range;
+
+	guint tooltip_ms;
+	guint idle_timer;
+};
+
+static GObjectClass *parent_class = NULL;
+
+/*
+static gint
+get_child_nb (WidgetOverlay *ovl, ChildData *cd)
+{
+	return g_list_index (ovl->priv->children, cd);
+}
+*/
+
+static void
+to_child (WidgetOverlay *ovl, GdkWindow *offscreen_window, double widget_x, double widget_y,
+          double *x_out, double *y_out)
+{
+	GList *list;
+	for (list = ovl->priv->children; list; list = list->next) {
+		ChildData *lcd = (ChildData*) list->data;
+		if (lcd->offscreen_window == offscreen_window) {
+			*x_out = (widget_x - lcd->x) / lcd->scale;
+			*y_out = (widget_y - lcd->y) / lcd->scale;
+			return;
+		}
+	}
+
+	*x_out = -1;
+	*y_out = -1;
+}
+
+static void
+to_parent (WidgetOverlay *ovl, GdkWindow *offscreen_window, double offscreen_x, double offscreen_y,
+           double *x_out, double *y_out)
+{
+	GList *list;
+
+	for (list = ovl->priv->children; list; list = list->next) {
+		ChildData *lcd = (ChildData*) list->data;
+		if (lcd->offscreen_window == offscreen_window) {
+			*x_out = lcd->x * lcd->scale + offscreen_x;
+			*y_out = lcd->y * lcd->scale + offscreen_y;
+			return;
+		}
+	}
+
+	*x_out = offscreen_x;
+	*y_out = offscreen_y;
+}
+
+static void
+widget_overlay_class_init (WidgetOverlayClass *klass)
+{
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+	GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	widget_class->realize = widget_overlay_realize;
+	widget_class->unrealize = widget_overlay_unrealize;
+	widget_class->get_preferred_width = widget_overlay_get_preferred_width;
+	widget_class->get_preferred_height = widget_overlay_get_preferred_height;
+	widget_class->size_allocate = widget_overlay_size_allocate;
+	widget_class->draw = widget_overlay_draw;
+	widget_class->event = widget_overlay_event;
+	widget_class->show = widget_overlay_show;
+
+	g_signal_override_class_closure (g_signal_lookup ("damage-event", GTK_TYPE_WIDGET),
+					 WIDGET_OVERLAY_TYPE,
+					 g_cclosure_new (G_CALLBACK (widget_overlay_damage),
+							 NULL, NULL));
+
+	container_class->add = widget_overlay_add;
+	container_class->remove = widget_overlay_remove;
+	container_class->forall = widget_overlay_forall;
+	G_OBJECT_CLASS (klass)->dispose = widget_overlay_dispose;
+	G_OBJECT_CLASS (klass)->finalize = widget_overlay_finalize;
+
+	/* Properties */
+        G_OBJECT_CLASS (klass)->set_property = widget_overlay_set_property;
+        G_OBJECT_CLASS (klass)->get_property = widget_overlay_get_property;
+	g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ADD_SCALE,
+                                         g_param_spec_boolean ("add-scale", NULL, NULL,
+                                                               FALSE,
+                                                               (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+}
+
+static void
+change_widget_scale (WidgetOverlay *ovl, ChildData *cd, gdouble new_scale)
+{
+	widget_overlay_set_child_props (ovl, cd->child, WIDGET_OVERLAY_CHILD_SCALE, new_scale, -1);
+	if (ovl->priv->scale_child)
+		gtk_range_set_value (ovl->priv->scale_range, new_scale);
+}
+
+static ChildData *
+get_first_child (WidgetOverlay *ovl)
+{
+	GList *list;
+
+	for (list = ovl->priv->children; list; list = list->next) {
+		ChildData *lcd;
+		lcd = (ChildData*) list->data;
+		if (lcd != ovl->priv->scale_child)
+			return lcd;
+	}
+	return NULL;
+}
+
+static void
+scale_value_changed_cb (GtkRange *range, WidgetOverlay *ovl)
+{
+	ChildData *cd;
+	cd = get_first_child (ovl);
+	if (!cd)
+		return;
+	change_widget_scale (ovl, cd, gtk_range_get_value (range));
+}
+
+static void
+scale_button_clicked_cb (GtkButton *button, WidgetOverlay *ovl)
+{
+	g_object_set (G_OBJECT (ovl), "add-scale", FALSE, NULL);
+}
+
+static void
+widget_overlay_set_property (GObject *object,
+			     guint param_id,
+			     const GValue *value,
+			     GParamSpec *pspec)
+{
+	WidgetOverlay *ovl = WIDGET_OVERLAY (object);
+	if (ovl->priv) {
+                switch (param_id) {
+                case PROP_ADD_SCALE: {
+			gboolean need_scale;
+			need_scale = g_value_get_boolean (value);
+			if (!need_scale && !ovl->priv->scale_child)
+				return;
+
+			if (need_scale && ovl->priv->scale_child) {
+				widget_overlay_set_child_props (ovl, ovl->priv->scale_child->child,
+								WIDGET_OVERLAY_CHILD_ALPHA, .6,
+								-1);
+			}
+			else if (need_scale) {
+				GtkWidget *box, *wid, *button, *image;
+				box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+				wid = gtk_vscale_new_with_range (SCALE_MIN, SCALE_MAX, SCALE_STEP);
+				ovl->priv->scale_range = GTK_RANGE (wid);
+				g_object_set (G_OBJECT (wid), "draw-value", FALSE, NULL);
+				gtk_box_pack_start (GTK_BOX (box), wid, TRUE, TRUE, 0);
+
+				button = gtk_button_new ();
+				image = gtk_image_new_from_stock (GTK_STOCK_CLOSE,
+								  GTK_ICON_SIZE_MENU);
+				gtk_container_add (GTK_CONTAINER (button), image);
+				gtk_container_add (GTK_CONTAINER (box), button);
+				gtk_widget_set_name (button, "browser-tab-close-button");
+				g_signal_connect (button, "clicked",
+						  G_CALLBACK (scale_button_clicked_cb), ovl);
+
+				gtk_container_add (GTK_CONTAINER (ovl), box);
+				gtk_widget_show_all (box);
+
+				GList *list;
+				for (list = ovl->priv->children; list; list = list->next) {
+					ChildData *cd = (ChildData*) list->data;
+					if (cd->child == box) {
+						ovl->priv->scale_child = cd;
+						break;
+					}
+				}
+				g_assert (ovl->priv->scale_child);
+
+				ChildData *cd;
+				cd = get_first_child (ovl);
+				if (cd)
+					gtk_range_set_value (ovl->priv->scale_range, cd->scale);
+				gtk_range_set_inverted (ovl->priv->scale_range, TRUE);
+
+				g_signal_connect (wid, "value-changed",
+						  G_CALLBACK (scale_value_changed_cb), ovl);
+
+				widget_overlay_set_child_props (ovl, box,
+								WIDGET_OVERLAY_CHILD_VALIGN,
+								WIDGET_OVERLAY_ALIGN_FILL,
+								WIDGET_OVERLAY_CHILD_HALIGN,
+								WIDGET_OVERLAY_ALIGN_END,
+								WIDGET_OVERLAY_CHILD_SCALE, 1.,
+								WIDGET_OVERLAY_CHILD_ALPHA, .6,
+								-1);
+			}
+			else {
+				widget_overlay_set_child_props (ovl, ovl->priv->scale_child->child,
+								WIDGET_OVERLAY_CHILD_ALPHA, 0.,
+								-1);
+			}
+			break;
+			}
+		}
+	}
+}
+
+static void
+widget_overlay_get_property (GObject *object,
+			     guint param_id,
+			     GValue *value,
+			     GParamSpec *pspec)
+{
+	WidgetOverlay *ovl = WIDGET_OVERLAY (object);
+	if (ovl->priv) {
+                switch (param_id) {
+                case PROP_ADD_SCALE: {
+			gboolean has_scale = FALSE;
+			if (ovl->priv->scale_child &&
+			    (ovl->priv->scale_child->alpha > 0.))
+				has_scale = TRUE;
+			g_value_set_boolean (value, has_scale);
+			break;
+		}
+		}
+	}
+}
+
+static gboolean
+idle_timer_cb (WidgetOverlay *ovl)
+{
+	GList *list;
+	for (list = ovl->priv->children; list; list = list->next) {
+		ChildData *cd;
+		cd = (ChildData*) list->data;
+		if (cd->is_tooltip) {
+			gtk_widget_show (cd->child);
+			gtk_widget_queue_draw (GTK_WIDGET (ovl));
+			/*
+			widget_overlay_set_child_props (ovl, cd->child,
+							WIDGET_OVERLAY_CHILD_ALPHA, 1.,
+							WIDGET_OVERLAY_CHILD_HAS_EVENTS, TRUE,
+							-1);
+			*/
+		}
+	}
+	ovl->priv->idle_timer = 0;
+	return FALSE; /* remove timer */
+}
+
+static gboolean
+widget_overlay_event (GtkWidget *widget, GdkEvent *event)
+{
+	GdkEventScroll *ev = (GdkEventScroll *) event;
+	WidgetOverlay *ovl = WIDGET_OVERLAY (widget);
+	ChildData *cdevent = NULL;
+
+	/* find child */
+	GList *list;
+	for (list = ovl->priv->children; list; list = list->next) {
+		ChildData *cd;
+		cd = (ChildData*) list->data;
+		if (cd->offscreen_window == ((GdkEventAny*)event)->window) {
+			cdevent = cd;
+			break;
+		}
+	}
+	/*g_print (" CH%d/%d", g_list_index (ovl->priv->children, cdevent), event->type);*/
+
+	/* tooltip widgets handling */
+	gboolean tooltip_event = FALSE;
+	if ((event->type == GDK_BUTTON_PRESS) ||
+	    (event->type == GDK_2BUTTON_PRESS) ||
+	    (event->type == GDK_3BUTTON_PRESS) ||
+	    (event->type == GDK_BUTTON_RELEASE) ||
+	    (event->type == GDK_MOTION_NOTIFY) ||
+	    (event->type == GDK_KEY_PRESS) ||
+	    (event->type == GDK_KEY_RELEASE) ||
+	    (event->type == GDK_ENTER_NOTIFY) ||
+	    (event->type == GDK_LEAVE_NOTIFY) ||
+	    (event->type == GDK_SCROLL))
+		tooltip_event = TRUE;
+
+	if (tooltip_event) {
+		/*g_print (".");*/
+		gboolean need_tooltip = FALSE;
+		GList *list;
+		for (list = ovl->priv->children; list; list = list->next) {
+			ChildData *cd;
+			cd = (ChildData*) list->data;
+			if (cd->is_tooltip) {
+				need_tooltip = TRUE;
+				break;
+			}
+		}
+		
+		if (ovl->priv->idle_timer) {
+			g_source_remove (ovl->priv->idle_timer);
+			ovl->priv->idle_timer = 0;
+		}
+
+		if ((event->type != GDK_ENTER_NOTIFY) &&
+		    (event->type != GDK_LEAVE_NOTIFY)) {
+			gboolean inc_timeout = FALSE;
+			for (list = ovl->priv->children; list; list = list->next) {
+				ChildData *cd;
+				cd = (ChildData*) list->data;
+				if (cd->is_tooltip && gtk_widget_get_visible (cd->child)) {
+					inc_timeout = TRUE;
+					gtk_widget_hide (cd->child);
+				}
+			}
+			if (inc_timeout) {
+				GtkSettings *settings;
+				guint t;
+				settings = gtk_widget_get_settings (widget);
+				g_object_get (settings, "gtk-tooltip-timeout",
+					      &t, NULL);
+				if ((ovl->priv->tooltip_ms * 2) < (t * 32))
+					ovl->priv->tooltip_ms = ovl->priv->tooltip_ms * 2;
+			}
+		}
+					
+		if ((event->type == GDK_LEAVE_NOTIFY) &&
+		    ((GdkEventAny*)event)->window == gtk_widget_get_window (GTK_WIDGET (ovl)))
+			need_tooltip = FALSE;
+
+		if (need_tooltip) {
+			if (ovl->priv->tooltip_ms == 0) {
+				GtkSettings *settings;
+				settings = gtk_widget_get_settings (widget);
+				g_object_get (settings, "gtk-tooltip-timeout",
+					      &(ovl->priv->tooltip_ms), NULL);
+			}
+			ovl->priv->idle_timer = g_timeout_add (ovl->priv->tooltip_ms,
+							       (GSourceFunc) idle_timer_cb, ovl);
+		}
+	}
+
+	if ((event->type != GDK_SCROLL) || !(ev->state & GDK_SHIFT_MASK))
+		return FALSE;
+
+	ChildData *cd = NULL;
+	cd = get_first_child (ovl);
+	if (!cd)
+		return FALSE;
+
+	gdouble scale;
+	scale = cd->scale;
+	if (ev->direction == GDK_SCROLL_UP)
+		scale += SCALE_STEP;
+	else
+		scale -= SCALE_STEP;
+	change_widget_scale (ovl, cd, scale);
+
+	return FALSE;
+}
+
+static void
+widget_overlay_init (WidgetOverlay *ovl)
+{
+	gtk_widget_set_has_window (GTK_WIDGET (ovl), TRUE);
+	ovl->priv = g_new0 (WidgetOverlayPrivate, 1);
+	ovl->priv->children = NULL;
+	ovl->priv->scale_child = NULL;
+	ovl->priv->tooltip_ms = 0;
+}
+
+GtkWidget *
+widget_overlay_new (void)
+{
+	return g_object_new (WIDGET_OVERLAY_TYPE, NULL);
+}
+
+static void
+widget_overlay_dispose (GObject *obj)
+{
+	WidgetOverlay *ovl = WIDGET_OVERLAY (obj);
+	if (ovl->priv) {
+		if (ovl->priv->idle_timer) {
+			g_source_remove (ovl->priv->idle_timer);
+			ovl->priv->idle_timer = 0;
+		}
+	}
+	if (G_OBJECT_CLASS (parent_class)->dispose)
+		G_OBJECT_CLASS (parent_class)->dispose (obj);
+}
+
+static void
+widget_overlay_finalize (GObject *obj)
+{
+	WidgetOverlay *ovl = WIDGET_OVERLAY (obj);
+	if (ovl->priv->children) {
+		GList *list;
+		for (list = ovl->priv->children; list; list = list->next) {
+			ChildData *cd = (ChildData*) list->data;
+			g_free (cd);
+		}
+		g_list_free (ovl->priv->children);
+	}
+	g_free (ovl->priv);
+
+	if (G_OBJECT_CLASS (parent_class)->finalize)
+		G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static void
+widget_overlay_show (GtkWidget *widget)
+{
+	WidgetOverlay *ovl = WIDGET_OVERLAY (widget);
+	GList *list;
+
+	((GtkWidgetClass *)parent_class)->show (widget);
+	for (list = ovl->priv->children; list; list = list->next) {
+		ChildData *cd;
+		cd = (ChildData*) list->data;
+		if (cd->is_tooltip)
+			gtk_widget_hide (cd->child);
+	}
+}
+
+static GdkWindow *
+pick_offscreen_child (G_GNUC_UNUSED GdkWindow *offscreen_window,
+                      double widget_x, double widget_y,
+                      WidgetOverlay *ovl)
+{
+	GList *list;
+	GdkWindow *retval = NULL;
+	gboolean ign_non_tooltip = FALSE;
+
+	//return ((ChildData*)ovl->priv->children)->offscreen_window;
+
+	for (list = ovl->priv->children; list; list = list->next) {
+		ChildData *cd = (ChildData*) list->data;
+		if (cd->is_tooltip && gtk_widget_get_visible (cd->child)) {
+			ign_non_tooltip = TRUE;
+			break;
+		}
+	}
+
+	for (list = g_list_last (ovl->priv->children); list; list = list->prev) {
+		ChildData *cd = (ChildData*) list->data;
+		if (!cd->ignore_events && gtk_widget_get_visible (cd->child) &&
+		    (!ign_non_tooltip || (ign_non_tooltip && cd->is_tooltip))) {
+			GtkAllocation alloc;
+			double x, y;
+			to_child (ovl, cd->offscreen_window, widget_x, widget_y, &x, &y);
+			gtk_widget_get_allocation ((GtkWidget*) cd->child, &alloc);
+			if (x >= 0 && x < alloc.width &&
+			    y >= 0 && y < alloc.height) {
+				retval = cd->offscreen_window;
+				break;
+			}
+		}
+	}
+
+	/*g_print ("%s() => %p\n", __FUNCTION__, retval);*/
+	return retval;
+}
+
+static void
+offscreen_window_to_parent (GdkWindow *offscreen_window,
+                            double offscreen_x, double offscreen_y,
+                            double *parent_x, double *parent_y,
+                            WidgetOverlay *ovl)
+{
+	to_parent (ovl, offscreen_window, offscreen_x, offscreen_y, parent_x, parent_y);
+}
+
+static void
+offscreen_window_from_parent (GdkWindow *offscreen_window,
+                              double parent_x, double parent_y,
+                              double *offscreen_x, double *offscreen_y,
+                              WidgetOverlay *ovl)
+{
+	to_child (ovl, offscreen_window, parent_x, parent_y, offscreen_x, offscreen_y);
+}
+
+static void
+widget_overlay_realize (GtkWidget *widget)
+{
+	WidgetOverlay *ovl = WIDGET_OVERLAY (widget);
+	GdkWindowAttr attributes;
+	gint attributes_mask;
+	gint border_width;
+	GtkRequisition child_requisition;
+	GtkAllocation allocation;
+
+	gtk_widget_set_realized (widget, TRUE);
+
+	border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+
+	gtk_widget_get_allocation (widget, &allocation);
+	attributes.x = allocation.x + border_width;
+	attributes.y = allocation.y + border_width;
+	attributes.width = allocation.width - 2 * border_width;
+	attributes.height = allocation.height - 2 * border_width;
+	attributes.window_type = GDK_WINDOW_CHILD;
+	attributes.event_mask = gtk_widget_get_events (widget)
+		| GDK_EXPOSURE_MASK
+		| GDK_POINTER_MOTION_MASK
+		| GDK_BUTTON_PRESS_MASK
+		| GDK_BUTTON_RELEASE_MASK
+		| GDK_SCROLL_MASK
+		| GDK_ENTER_NOTIFY_MASK
+		| GDK_LEAVE_NOTIFY_MASK;
+
+	attributes.visual = gtk_widget_get_visual (widget);
+	attributes.wclass = GDK_INPUT_OUTPUT;
+
+	attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+
+	GdkWindow *win;
+	win = gdk_window_new (gtk_widget_get_parent_window (widget),
+			      &attributes, attributes_mask);
+	gtk_widget_set_window (widget, win);
+	gdk_window_set_user_data (win, widget);
+	g_signal_connect (win, "pick-embedded-child",
+			  G_CALLBACK (pick_offscreen_child), ovl);
+
+	GtkStyle *style;
+	style = gtk_widget_get_style (widget);
+	style = gtk_style_attach (style, win);
+	gtk_widget_set_style (widget, style);
+	
+	/* offscreen windows */
+	attributes.window_type = GDK_WINDOW_OFFSCREEN;
+
+	GList *list;
+	for (list = ovl->priv->children; list; list = list->next) {
+		ChildData *cd = (ChildData*) list->data;
+		child_requisition.width = child_requisition.height = 0;
+		if (gtk_widget_get_visible (cd->child)) {
+			GtkAllocation allocation;
+			gtk_widget_get_allocation (cd->child, &allocation);
+			attributes.width = allocation.width;
+			attributes.height = allocation.height;
+		}
+
+		cd->offscreen_window = gdk_window_new (gtk_widget_get_root_window (widget),
+						       &attributes, attributes_mask);
+		gdk_window_set_user_data (cd->offscreen_window, widget);
+		gtk_widget_set_parent_window (cd->child, cd->offscreen_window);
+		gdk_offscreen_window_set_embedder (cd->offscreen_window, win);
+		g_signal_connect (cd->offscreen_window, "to-embedder",
+				  G_CALLBACK (offscreen_window_to_parent), ovl);
+		g_signal_connect (cd->offscreen_window, "from-embedder",
+				  G_CALLBACK (offscreen_window_from_parent), ovl);
+
+		//gtk_style_set_background (style, win, GTK_STATE_NORMAL);
+		gtk_style_set_background (style, cd->offscreen_window, GTK_STATE_NORMAL);
+		gdk_window_show (cd->offscreen_window);
+	}
+}
+
+static void
+widget_overlay_unrealize (GtkWidget *widget)
+{
+	WidgetOverlay *ovl = WIDGET_OVERLAY (widget);
+
+	GList *list;
+	for (list = ovl->priv->children; list; list = list->next) {
+		ChildData *cd = (ChildData*) list->data;
+		gdk_window_set_user_data (cd->offscreen_window, NULL);
+		gdk_window_destroy (cd->offscreen_window);
+		cd->offscreen_window = NULL;
+	}
+
+	GTK_WIDGET_CLASS (widget_overlay_parent_class)->unrealize (widget);
+}
+
+static void
+widget_overlay_add (GtkContainer *container, GtkWidget *widget)
+{
+	WidgetOverlay *ovl = WIDGET_OVERLAY (container);
+	ChildData *cd;
+
+	cd = g_new0 (ChildData, 1);
+	gtk_widget_set_parent (widget, GTK_WIDGET (ovl));
+	cd->ovl = ovl;
+	cd->child = widget;
+	cd->halign = WIDGET_OVERLAY_ALIGN_CENTER;
+	cd->valign = WIDGET_OVERLAY_ALIGN_END;
+	cd->alpha = 1.;
+	cd->scale = 1.;
+	cd->ignore_events = FALSE;
+	cd->is_tooltip = FALSE;
+	
+	ovl->priv->children = g_list_append (ovl->priv->children, cd);
+
+	if (ovl->priv->scale_child) {
+		ChildData *fcd;
+		fcd = get_first_child (ovl);
+		if (cd == fcd)
+			gtk_range_set_value (ovl->priv->scale_range, cd->scale);
+
+		ovl->priv->children = g_list_remove (ovl->priv->children, ovl->priv->scale_child);
+		ovl->priv->children = g_list_append (ovl->priv->children, ovl->priv->scale_child);
+	}
+}
+
+static void
+widget_overlay_remove (GtkContainer *container, GtkWidget *widget)
+{
+	WidgetOverlay *ovl = WIDGET_OVERLAY (container);
+	gboolean was_visible;
+
+	was_visible = gtk_widget_get_visible (widget);
+	GList *list;
+	for (list = ovl->priv->children; list; list = list->next) {
+		ChildData *cd = (ChildData*) list->data;
+		if (cd->child == widget) {
+			gtk_widget_unparent (widget);
+			ovl->priv->children = g_list_remove (ovl->priv->children, cd);
+			g_free (cd);
+			if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container)))
+				gtk_widget_queue_resize (GTK_WIDGET (container));
+			break;
+		}
+	}
+}
+
+static void
+widget_overlay_forall (GtkContainer *container,
+		       G_GNUC_UNUSED gboolean include_internals,
+		       GtkCallback   callback,
+		       gpointer      callback_data)
+{
+	WidgetOverlay *ovl = WIDGET_OVERLAY (container);
+
+	g_return_if_fail (callback != NULL);
+	GList *list, *copy;
+	copy = g_list_copy (ovl->priv->children);
+	for (list = copy; list; list = list->next) {
+		ChildData *cd = (ChildData*) list->data;
+		(*callback) (cd->child, callback_data);
+	}
+	g_list_free (copy);
+}
+
+static void
+widget_overlay_size_request (GtkWidget *widget, GtkRequisition *req_min, GtkRequisition *req_nat)
+{
+	WidgetOverlay *ovl = WIDGET_OVERLAY (widget);
+	guint border_width;
+	border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+	req_min->width = 1;
+	req_min->height = 1;
+	req_nat->width = 1;
+	req_nat->height = 1;
+
+	GList *list;
+	for (list = ovl->priv->children; list; list = list->next) {
+		ChildData *cd = (ChildData*) list->data;
+		if (gtk_widget_get_visible (cd->child)) {
+			GtkRequisition child_req_min, child_req_nat;
+			gtk_widget_get_preferred_size (cd->child, &child_req_min, &child_req_nat);
+			if (cd->halign == WIDGET_OVERLAY_ALIGN_FILL) {
+				req_min->width = MAX (border_width * 2 + child_req_min.width,
+						      req_min->width);
+				req_nat->width = MAX (border_width * 2 + child_req_nat.width,
+						      req_nat->width);
+			}
+			else {
+				req_min->width = MAX (border_width * 2 + child_req_min.width * cd->scale,
+						      req_min->width);
+				req_nat->width = MAX (border_width * 2 + child_req_nat.width * cd->scale,
+						      req_nat->width);
+			}
+			
+			if (cd->valign == WIDGET_OVERLAY_ALIGN_FILL) {
+				req_min->height = MAX (border_width * 2 + child_req_min.height,
+						       req_min->height);
+				
+				req_nat->height = MAX (border_width * 2 + child_req_nat.height,
+						       req_nat->height);
+			}
+			else {
+				req_min->height = MAX (border_width * 2 + child_req_min.height * cd->scale,
+						       req_min->height);
+				
+				req_nat->height = MAX (border_width * 2 + child_req_nat.height * cd->scale,
+						       req_nat->height);
+			}
+		}
+	}
+}
+
+static void
+widget_overlay_get_preferred_width (GtkWidget *widget, gint *minimum, gint *natural)
+{
+	GtkRequisition req_min, req_nat;
+	widget_overlay_size_request (widget, &req_min, &req_nat);
+	*minimum = req_min.width;
+	*natural = req_nat.width;
+}
+
+static void
+widget_overlay_get_preferred_height (GtkWidget *widget, gint *minimum, gint *natural)
+{
+	GtkRequisition req_min, req_nat;
+	widget_overlay_size_request (widget, &req_min, &req_nat);
+	*minimum = req_min.height;
+	*natural = req_nat.height;
+}
+
+static void
+widget_overlay_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+	WidgetOverlay *ovl = WIDGET_OVERLAY (widget);
+	gint border_width;
+	gint w, h;
+
+	gtk_widget_set_allocation (widget, allocation);
+
+	border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+
+	w = allocation->width - border_width * 2;
+	h = allocation->height - border_width * 2;
+
+	if (gtk_widget_get_realized (widget)) {
+		GdkWindow *win;
+		win = gtk_widget_get_window (widget);
+		gdk_window_move_resize (win,
+					allocation->x + border_width,
+					allocation->y + border_width,
+					w, h);
+	}
+
+	GList *list;
+	for (list = ovl->priv->children; list; list = list->next) {
+		ChildData *cd = (ChildData*) list->data;
+		if (gtk_widget_get_visible (cd->child)){
+			GtkRequisition child_requisition;
+			GtkAllocation child_allocation;
+			
+			gtk_widget_get_preferred_size (cd->child, &child_requisition, NULL);
+			child_allocation.x = 0;
+			child_allocation.y = 0;
+			child_allocation.height = child_requisition.height;
+			child_allocation.width = child_requisition.width;
+			
+			if (cd->halign == WIDGET_OVERLAY_ALIGN_FILL)
+				child_allocation.width = w / cd->scale;
+			if ((cd->valign == WIDGET_OVERLAY_ALIGN_FILL) ||
+			    (cd == ovl->priv->scale_child))
+				child_allocation.height = h / cd->scale;
+
+			if (gtk_widget_get_realized (widget))
+				gdk_window_move_resize (cd->offscreen_window,
+							child_allocation.x,
+							child_allocation.y,
+							child_allocation.width,
+							child_allocation.height);
+			
+			child_allocation.x = child_allocation.y = 0;
+
+			gtk_widget_size_allocate (cd->child, &child_allocation);
+		}
+	}
+}
+
+static gboolean
+widget_overlay_damage (GtkWidget      *widget,
+		       G_GNUC_UNUSED GdkEventExpose *event)
+{
+	gdk_window_invalidate_rect (gtk_widget_get_window (widget), NULL, FALSE);
+
+	return TRUE;
+}
+
+static gboolean
+widget_overlay_draw (GtkWidget *widget, cairo_t *cr)
+{
+	WidgetOverlay *ovl = WIDGET_OVERLAY (widget);
+	GdkWindow *window;
+
+	GtkAllocation area;
+	gtk_widget_get_allocation (widget, &area);
+
+	window = gtk_widget_get_window (widget);
+	if (gtk_cairo_should_draw_window (cr, window)) {
+		GList *list;
+		for (list = ovl->priv->children; list; list = list->next) {
+			ChildData *cd = (ChildData*) list->data;
+			if (gtk_widget_get_visible (cd->child)) {
+				cairo_surface_t *surface;
+				GtkAllocation child_area;
+				double x, y;
+				
+				gtk_widget_get_allocation (cd->child, &child_area);
+				child_area.width *= cd->scale;
+				child_area.height *= cd->scale;
+				switch (cd->halign) {
+				case WIDGET_OVERLAY_ALIGN_FILL:
+				case WIDGET_OVERLAY_ALIGN_START:
+					x = 0;
+					break;
+				case WIDGET_OVERLAY_ALIGN_END:
+					x = area.width - child_area.width;
+					break;
+				case WIDGET_OVERLAY_ALIGN_CENTER:
+					x = (area.width - child_area.width) / 2.;
+					break;
+				}
+				switch (cd->valign) {
+				case WIDGET_OVERLAY_ALIGN_FILL:
+				case WIDGET_OVERLAY_ALIGN_START:
+					y = 0;
+					break;
+				case WIDGET_OVERLAY_ALIGN_END:
+					y = area.height - child_area.height;
+					break;
+				case WIDGET_OVERLAY_ALIGN_CENTER:
+					y = (area.height - child_area.height) / 2.;
+					break;
+				}
+				
+				surface = gdk_offscreen_window_get_surface (cd->offscreen_window);
+				if (cd->scale == 1.) {
+					cairo_set_source_surface (cr, surface, x, y);
+					cairo_paint_with_alpha (cr, cd->alpha);
+				}
+				else {
+					cairo_save (cr);
+					cairo_scale (cr, cd->scale, cd->scale);
+					cairo_set_source_surface (cr, surface, x/cd->scale, y/cd->scale);
+					cairo_paint_with_alpha (cr, cd->alpha);
+					cairo_restore (cr);
+				}
+
+				cd->x = x;
+				cd->y = y;
+			}
+
+			if (list->next &&
+			    ((ChildData*) list->next->data == ovl->priv->scale_child) &&
+			    (ovl->priv->scale_child->alpha > 0.)) {
+				cairo_set_source_rgba (cr, 0., 0., 0., .3);
+				cairo_rectangle (cr, 0, 0,
+						 area.width, area.height);
+				cairo_fill (cr);
+			}
+		}
+	}
+	else {
+		GList *list;
+		for (list = ovl->priv->children; list; list = list->next) {
+			ChildData *cd = (ChildData*) list->data;
+			if (gtk_cairo_should_draw_window (cr, cd->offscreen_window))
+				gtk_container_propagate_draw (GTK_CONTAINER (widget), cd->child, cr);
+		}
+	}
+
+	return TRUE;
+}
+
+/**
+ * widget_overlay_set_child_props:
+ *
+ * @...: (WidgetOverlayChildProperty, value) terminated by -1
+ */
+void
+widget_overlay_set_child_props (WidgetOverlay *ovl, GtkWidget *child, ...)
+{
+	GList *list;
+	g_return_if_fail (IS_WIDGET_OVERLAY (ovl));
+
+	for (list = ovl->priv->children; list; list = list->next) {
+		ChildData *cd = (ChildData*) list->data;
+		if (cd->child == child) {
+			va_list args;
+			WidgetOverlayChildProperty prop;
+			GtkAllocation area;
+			gtk_widget_get_allocation (GTK_WIDGET (ovl), &area);
+			if (gtk_widget_get_has_window (GTK_WIDGET (ovl)))
+				area.x = area.y = 0;
+
+			va_start (args, child);
+			for (prop = va_arg (args, WidgetOverlayChildProperty); prop != -1;
+			     prop = va_arg (args, WidgetOverlayChildProperty)) {
+				switch (prop) {
+				case WIDGET_OVERLAY_CHILD_VALIGN:
+					cd->valign = va_arg (args, WidgetOverlayAlign);
+					break;
+				case WIDGET_OVERLAY_CHILD_HALIGN:
+					cd->halign = va_arg (args, WidgetOverlayAlign);
+					break;
+				case WIDGET_OVERLAY_CHILD_ALPHA:
+					cd->alpha = va_arg (args, gdouble);
+					if (cd->alpha > 1.)
+						cd->alpha = 1;
+					else if (cd->alpha < 0.)
+						cd->alpha = 0.;
+
+					if (cd != ovl->priv->scale_child) {
+						gtk_widget_get_allocation (cd->child, &area);
+						area.x = cd->x;
+						area.y = cd->y;
+					}
+					break;
+				case WIDGET_OVERLAY_CHILD_HAS_EVENTS:
+					cd->ignore_events = ! (va_arg (args, gboolean));
+					break;
+				case WIDGET_OVERLAY_CHILD_SCALE: {
+					GtkAllocation alloc;
+					gdouble current, max;
+					current = cd->scale;
+					gtk_widget_get_allocation (cd->child, &alloc);
+
+					cd->scale = va_arg (args, gdouble);
+					if (cd->scale < SCALE_MIN)
+						cd->scale = SCALE_MIN;
+					max = SCALE_MAX;
+					if (cd == ovl->priv->scale_child)
+						max = SCALE_MAX_SCALE;
+					if (cd->scale > max)
+						cd->scale = max;
+
+					if (current >= cd->scale) {
+						area.x = cd->x;
+						area.y = cd->y;
+						area.width = alloc.width;
+						area.height = alloc.height;
+					}
+					if ((cd->halign == WIDGET_OVERLAY_ALIGN_FILL) ||
+					    (cd->valign == WIDGET_OVERLAY_ALIGN_FILL)) {
+						/*gtk_widget_queue_resize (GTK_WIDGET (ovl));*/
+						gtk_widget_queue_resize (cd->child);
+					}
+					break;
+				}
+				case WIDGET_OVERLAY_CHILD_TOOLTIP:
+					cd->is_tooltip = va_arg (args, gboolean);
+					break;
+				default:
+					g_assert_not_reached ();
+					break;
+				}
+			}
+			va_end (args);
+			/*g_print ("Queue draw %dx%d %dx%d\n", area.x, area.y, area.width, area.height);*/
+			gtk_widget_queue_draw_area (GTK_WIDGET (ovl), area.x, area.y, area.width, area.height);
+
+			return;
+		}
+	}
+}
diff --git a/tools/browser/common/widget-overlay.h b/tools/browser/common/widget-overlay.h
new file mode 100644
index 0000000..1789885
--- /dev/null
+++ b/tools/browser/common/widget-overlay.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2010 - 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This library 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 2 of the License, or (at your option) any later version.
+ *
+ * This library 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 this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+#include <gtk/gtk.h>
+
+#define WIDGET_OVERLAY_TYPE              (widget_overlay_get_type ())
+#define WIDGET_OVERLAY(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), WIDGET_OVERLAY_TYPE, WidgetOverlay))
+#define WIDGET_OVERLAY_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), WIDGET_OVERLAY_TYPE, WidgetOverlayClass))
+#define IS_WIDGET_OVERLAY(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WIDGET_OVERLAY_TYPE))
+#define IS_WIDGET_OVERLAY_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), WIDGET_OVERLAY_TYPE))
+#define WIDGET_OVERLAY_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), WIDGET_OVERLAY_TYPE, WidgetOverlayClass))
+
+typedef struct _WidgetOverlay WidgetOverlay;
+typedef struct _WidgetOverlayClass WidgetOverlayClass;
+typedef struct _WidgetOverlayPrivate WidgetOverlayPrivate;
+
+struct _WidgetOverlay
+{
+	GtkContainer container;
+	WidgetOverlayPrivate *priv;
+};
+
+struct _WidgetOverlayClass
+{
+	GtkContainerClass parent_class;
+};
+
+typedef enum
+{
+	WIDGET_OVERLAY_ALIGN_FILL,
+	WIDGET_OVERLAY_ALIGN_START,
+	WIDGET_OVERLAY_ALIGN_END,
+	WIDGET_OVERLAY_ALIGN_CENTER
+} WidgetOverlayAlign;
+
+typedef enum {
+	WIDGET_OVERLAY_CHILD_VALIGN, /* WidgetOverlayAlign */
+	WIDGET_OVERLAY_CHILD_HALIGN, /* WidgetOverlayAlign */
+	WIDGET_OVERLAY_CHILD_ALPHA,  /* gdouble */
+	WIDGET_OVERLAY_CHILD_HAS_EVENTS, /* gboolean */
+	WIDGET_OVERLAY_CHILD_SCALE,  /* gdouble */
+	WIDGET_OVERLAY_CHILD_TOOLTIP, /* gboolean */
+} WidgetOverlayChildProperty;
+
+GType      widget_overlay_get_type        (void) G_GNUC_CONST;
+GtkWidget* widget_overlay_new             (void);
+void       widget_overlay_set_child_props (WidgetOverlay *ovl, GtkWidget *child, ...);



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