[libgd] Initial GdStack implementation



commit 25911b0068b78cd425f0f405eae9b5654699dd2f
Author: Alexander Larsson <alexl redhat com>
Date:   Fri Feb 8 16:09:46 2013 +0100

    Initial GdStack implementation

 Makefile.am      |   23 ++-
 libgd.m4         |    6 +
 libgd/gd-stack.c |  637 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 libgd/gd-stack.h |   66 ++++++
 libgd/gd.h       |    4 +
 test-stack.c     |   94 ++++++++
 6 files changed, 829 insertions(+), 1 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index c5fd528..12e72b8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -13,6 +13,8 @@ AM_CPPFLAGS =					\
         $(LIBGD_CFLAGS)				\
         $(NULL)
 
+noinst_PROGRAMS =
+
 if LIBGD_STATIC
 noinst_LTLIBRARIES = libgd.la
 else
@@ -121,7 +123,7 @@ revealer_sources =				\
 nodist_libgd_la_SOURCES += $(revealer_sources)
 EXTRA_DIST += $(revealer_sources)
 
-noinst_PROGRAMS = test-revealer
+noinst_PROGRAMS += test-revealer
 test_revealer_SOURCES =				\
 	test-revealer.c				\
 	$(NULL)
@@ -131,6 +133,25 @@ test_revealer_LDADD =				\
 	$(NULL)
 endif
 
+if LIBGD_STACK
+stack_sources =				\
+	libgd/gd-stack.c			\
+	libgd/gd-stack.h			\
+	$(NULL)
+
+nodist_libgd_la_SOURCES += $(stack_sources)
+EXTRA_DIST += $(stack_sources)
+
+noinst_PROGRAMS += test-stack
+test_stack_SOURCES =				\
+	test-stack.c				\
+	$(NULL)
+test_stack_LDADD =				\
+	$(LIBGD_LIBS)				\
+	libgd.la				\
+	$(NULL)
+endif
+
 if LIBGD_TAGGED_ENTRY
 tagged_entry_sources =				\
 	libgd/gd-tagged-entry.c			\
diff --git a/libgd.m4 b/libgd.m4
index 231b147..3b76f01 100644
--- a/libgd.m4
+++ b/libgd.m4
@@ -102,6 +102,12 @@ AC_DEFUN([LIBGD_INIT], [
         AC_DEFINE([LIBGD_REVEALER], [1], [Description])
     ])
 
+    # stack:
+    AM_CONDITIONAL([LIBGD_STACK],[_LIBGD_IF_OPTION_SET([stack],[true],[false])])
+    _LIBGD_IF_OPTION_SET([stack],[
+        AC_DEFINE([LIBGD_STACK], [1], [Description])
+    ])
+
     # tagged-entry: Gtk+ widget
     AM_CONDITIONAL([LIBGD_TAGGED_ENTRY],[_LIBGD_IF_OPTION_SET([tagged-entry],[true],[false])])
     _LIBGD_IF_OPTION_SET([tagged-entry],[
diff --git a/libgd/gd-stack.c b/libgd/gd-stack.c
new file mode 100644
index 0000000..e5370a0
--- /dev/null
+++ b/libgd/gd-stack.c
@@ -0,0 +1,637 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * This program 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 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ *
+ */
+
+#include <gtk/gtk.h>
+#include "gd-stack.h"
+#include <math.h>
+
+enum  {
+  PROP_0,
+  PROP_HOMOGENOUS,
+  PROP_VISIBLE_CHILD,
+  PROP_VISIBLE_CHILD_NAME
+};
+
+struct _GdStackPrivate {
+  GList *children;
+
+  GtkWidget *visible_child;
+
+  gboolean homogenous;
+};
+
+#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+#define GD_STACK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GD_TYPE_STACK, GdStackPrivate))
+
+static void     gd_stack_add                            (GtkContainer  *widget,
+							 GtkWidget     *child);
+static void     gd_stack_remove                         (GtkContainer  *widget,
+							 GtkWidget     *child);
+static void     gd_stack_forall                         (GtkContainer *container,
+							 gboolean      include_internals,
+							 GtkCallback   callback,
+							 gpointer      callback_data);
+static void    gd_stack_compute_expand                  (GtkWidget      *widget,
+							 gboolean       *hexpand,
+							 gboolean       *vexpand);
+static void     gd_stack_size_allocate                  (GtkWidget     *widget,
+							 GtkAllocation *allocation);
+static gboolean gd_stack_draw                           (GtkWidget     *widget,
+							 cairo_t       *cr);
+static void     gd_stack_get_preferred_height           (GtkWidget     *widget,
+							 gint          *minimum_height,
+							 gint          *natural_height);
+static void     gd_stack_get_preferred_height_for_width (GtkWidget     *widget,
+							 gint           width,
+							 gint          *minimum_height,
+							 gint          *natural_height);
+static void     gd_stack_get_preferred_width            (GtkWidget     *widget,
+							 gint          *minimum_width,
+							 gint          *natural_width);
+static void     gd_stack_get_preferred_width_for_height (GtkWidget     *widget,
+							 gint           height,
+							 gint          *minimum_width,
+							 gint          *natural_width);
+static void     gd_stack_finalize                       (GObject       *obj);
+static void     gd_stack_get_property                   (GObject       *object,
+							 guint          property_id,
+							 GValue        *value,
+							 GParamSpec    *pspec);
+static void     gd_stack_set_property                   (GObject       *object,
+							 guint          property_id,
+							 const GValue  *value,
+							 GParamSpec    *pspec);
+
+G_DEFINE_TYPE(GdStack, gd_stack, GTK_TYPE_BIN);
+
+static void
+gd_stack_init (GdStack *stack)
+{
+  GdStackPrivate *priv;
+
+  priv = GD_STACK_GET_PRIVATE (stack);
+  stack->priv = priv;
+
+  gtk_widget_set_has_window ((GtkWidget*) stack, FALSE);
+  gtk_widget_set_redraw_on_allocate ((GtkWidget*) stack, TRUE);
+}
+
+static void
+gd_stack_finalize (GObject* obj)
+{
+  GdStack *stack = GD_STACK (obj);
+  GdStackPrivate *priv = stack->priv;
+
+  G_OBJECT_CLASS (gd_stack_parent_class)->finalize (obj);
+}
+
+static void
+gd_stack_get_property (GObject *object,
+		       guint property_id,
+		       GValue *value,
+		       GParamSpec *pspec)
+{
+  GdStack *stack = GD_STACK (object);
+  GdStackPrivate *priv = stack->priv;
+
+  switch (property_id) {
+    case PROP_HOMOGENOUS:
+      g_value_set_boolean (value, priv->homogenous);
+      break;
+    case PROP_VISIBLE_CHILD:
+      g_value_set_object (value, priv->visible_child);
+      break;
+    case PROP_VISIBLE_CHILD_NAME:
+      g_value_set_string (value, gd_stack_get_visible_child_name (stack));
+      break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    break;
+  }
+}
+
+static void
+gd_stack_set_property (GObject *object,
+		       guint property_id,
+		       const GValue *value,
+		       GParamSpec *pspec)
+{
+  GdStack *stack = GD_STACK (object);
+
+  switch (property_id) {
+    case PROP_HOMOGENOUS:
+      gd_stack_set_homogenous (stack, g_value_get_boolean (value));
+      break;
+    case PROP_VISIBLE_CHILD:
+      gd_stack_set_visible_child (stack, g_value_get_object (value));
+      break;
+    case PROP_VISIBLE_CHILD_NAME:
+      gd_stack_set_visible_child_name (stack, g_value_get_string (value));
+      break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    break;
+  }
+}
+
+static void
+gd_stack_class_init (GdStackClass * klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+  object_class->get_property = gd_stack_get_property;
+  object_class->set_property = gd_stack_set_property;
+  object_class->finalize = gd_stack_finalize;
+
+  widget_class->size_allocate = gd_stack_size_allocate;
+  widget_class->draw = gd_stack_draw;
+  widget_class->get_preferred_height = gd_stack_get_preferred_height;
+  widget_class->get_preferred_height_for_width = gd_stack_get_preferred_height_for_width;
+  widget_class->get_preferred_width = gd_stack_get_preferred_width;
+  widget_class->get_preferred_width_for_height = gd_stack_get_preferred_width_for_height;
+  widget_class->compute_expand = gd_stack_compute_expand;
+
+  container_class->add = gd_stack_add;
+  container_class->remove = gd_stack_remove;
+  container_class->forall = gd_stack_forall;
+  /*container_class->get_path_for_child = gd_stack_get_path_for_child; */
+  gtk_container_class_handle_border_width (container_class);
+
+  g_object_class_install_property (object_class,
+				   PROP_HOMOGENOUS,
+				   g_param_spec_boolean ("homogenous",
+							 "Homogenous",
+							 "Homogenous sizing",
+							 FALSE,
+							 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class,
+				   PROP_VISIBLE_CHILD,
+				   g_param_spec_object ("visible-child",
+							"Visible child",
+							"The widget currently visible in the stack",
+							GTK_TYPE_WIDGET,
+							GTK_PARAM_READWRITE));
+  g_object_class_install_property (object_class,
+				   PROP_VISIBLE_CHILD_NAME,
+				   g_param_spec_string ("visible-child-name",
+							"Name of visible child",
+							"The name of the widget currently visible in the stack",
+							NULL,
+							GTK_PARAM_READWRITE));
+
+  g_type_class_add_private (klass, sizeof (GdStackPrivate));
+}
+
+
+GtkWidget *
+gd_stack_new (void)
+{
+  return g_object_new (GD_TYPE_STACK, NULL);
+}
+
+static void
+set_visible_child (GdStack *stack,
+		   GtkWidget *child)
+{
+  GdStackPrivate *priv = stack->priv;
+  GtkWidget *w;
+  GList *l;
+
+  /* If none, pick first visible */
+  if (child == NULL)
+    {
+      for (l = priv->children; l != NULL; l = l->next)
+	{
+	  w = l->data;
+	  if (gtk_widget_get_visible (w))
+	    {
+	      child = w;
+	      break;
+	    }
+	}
+    }
+
+  if (priv->visible_child)
+    gtk_widget_set_child_visible (priv->visible_child, FALSE);
+
+  priv->visible_child = child;
+
+  if (child)
+    gtk_widget_set_child_visible (child, TRUE);
+
+  gtk_widget_queue_resize (GTK_WIDGET (stack));
+
+  g_object_notify (G_OBJECT (stack), "visible-child");
+  g_object_notify (G_OBJECT (stack), "visible-child-name");
+}
+
+static void
+stack_child_visibility_notify_cb (GObject *obj,
+				  GParamSpec *pspec,
+				  gpointer user_data)
+{
+  GdStack *stack = GD_STACK (user_data);
+  GdStackPrivate *priv = stack->priv;
+  GtkWidget *child = GTK_WIDGET (obj);
+
+  if (priv->visible_child == NULL &&
+      gtk_widget_get_visible (child))
+    set_visible_child (stack, child);
+  else if (priv->visible_child == child &&
+	   gtk_widget_get_visible (child))
+    set_visible_child (stack, NULL);
+}
+
+static void
+gd_stack_add (GtkContainer *container,
+	      GtkWidget *child)
+{
+  GdStack *stack = GD_STACK (container);
+  GdStackPrivate *priv = stack->priv;
+
+  g_return_if_fail (child != NULL);
+
+  priv->children = g_list_append (priv->children, child);
+
+  gtk_widget_set_parent (child, GTK_WIDGET (stack));
+
+  g_signal_connect (child, "notify::visible",
+                    G_CALLBACK (stack_child_visibility_notify_cb), stack);
+
+  if (priv->visible_child == NULL &&
+      gtk_widget_get_visible (child))
+    set_visible_child (stack, child);
+  else
+    gtk_widget_set_child_visible (child, FALSE);
+
+  if (priv->homogenous)
+    gtk_widget_queue_resize (GTK_WIDGET (stack));
+}
+
+static void
+gd_stack_remove (GtkContainer *container,
+		 GtkWidget    *child)
+{
+  GdStack *stack = GD_STACK (container);
+  GdStackPrivate *priv = stack->priv;
+  gboolean was_visible;
+  GList *l;
+
+  l = g_list_find (priv->children, child);
+  if (l == NULL)
+    return;
+
+  priv->children = g_list_delete_link (priv->children, l);
+
+  g_signal_handlers_disconnect_by_func (child,
+					stack_child_visibility_notify_cb,
+					stack);
+
+  was_visible = gtk_widget_get_visible (child);
+  gtk_widget_unparent (child);
+
+  if (priv->visible_child == child)
+    set_visible_child (stack, NULL);
+
+  if (priv->homogenous)
+    gtk_widget_queue_resize (GTK_WIDGET (stack));
+}
+
+void
+gd_stack_set_homogenous (GdStack *stack,
+			 gboolean homogenous)
+{
+  GdStackPrivate *priv;
+
+  g_return_if_fail (stack != NULL);
+
+  priv = stack->priv;
+
+  homogenous = !!homogenous;
+
+  if (priv->homogenous == homogenous)
+    return;
+
+  priv->homogenous = homogenous;
+
+  gtk_widget_queue_resize (GTK_WIDGET (stack));
+
+  g_object_notify (G_OBJECT (stack), "homogenous");
+}
+
+gboolean
+gd_stack_get_homogenous (GdStack *stack)
+{
+  GdStackPrivate *priv;
+
+  g_return_if_fail (stack != NULL);
+
+  return stack->priv->homogenous;
+}
+
+/**
+ * gd_stack_get_visible_child:
+ * @stack: a #GdStack
+ *
+ * Gets the currently visible child of the #GdStack, or %NULL if the
+ * there are no visible children. The returned widget does not have a reference
+ * added, so you do not need to unref it.
+ *
+ * Return value: (transfer none): pointer to child of the #GdStack
+ **/
+GtkWidget *
+gd_stack_get_visible_child (GdStack *stack)
+{
+  GdStackPrivate *priv;
+
+  g_return_if_fail (stack != NULL);
+
+  return stack->priv->visible_child;
+}
+
+const char *
+gd_stack_get_visible_child_name (GdStack *stack)
+{
+  GdStackPrivate *priv;
+
+  g_return_val_if_fail (stack != NULL, NULL);
+
+  if (stack->priv->visible_child)
+    return gtk_widget_get_name (stack->priv->visible_child);
+
+  return NULL;
+}
+
+void
+gd_stack_set_visible_child (GdStack    *stack,
+			    GtkWidget  *child)
+{
+  GdStackPrivate *priv;
+
+  g_return_if_fail (stack != NULL);
+  g_return_if_fail (child != NULL);
+
+  priv = stack->priv;
+
+  if (g_list_find (priv->children, child) == NULL)
+    return;
+
+  if (gtk_widget_get_visible (child))
+    set_visible_child (stack, child);
+}
+
+void
+gd_stack_set_visible_child_name (GdStack    *stack,
+				 const char *name)
+{
+  GdStackPrivate *priv;
+  GtkWidget *child;
+  GList *l;
+
+  g_return_if_fail (stack != NULL);
+  g_return_if_fail (name != NULL);
+
+  priv = stack->priv;
+
+  child = NULL;
+  for (l = priv->children; l != NULL; l = l->next)
+    {
+      GtkWidget *w = l->data;
+      const char *child_name = gtk_widget_get_name (w);
+      if (child_name != NULL &&
+	  strcmp (child_name, name) == 0)
+	{
+	  child = w;
+	  break;
+	}
+    }
+
+  if (child != NULL && gtk_widget_get_visible (child))
+    set_visible_child (stack, child);
+}
+
+static void
+gd_stack_forall (GtkContainer *container,
+		 gboolean      include_internals,
+		 GtkCallback   callback,
+		 gpointer      callback_data)
+{
+  GdStack *stack = GD_STACK (container);
+  GdStackPrivate *priv = stack->priv;
+  GList *l;
+
+  for (l = priv->children; l != NULL; l = l->next)
+    (* callback) (l->data, callback_data);
+}
+
+static void
+gd_stack_compute_expand (GtkWidget      *widget,
+			 gboolean       *hexpand_p,
+			 gboolean       *vexpand_p)
+{
+  GdStack *stack = GD_STACK (widget);
+  GdStackPrivate *priv = stack->priv;
+  gboolean hexpand, vexpand;
+  GList *l;
+
+  hexpand = FALSE;
+  vexpand = FALSE;
+  for (l = priv->children; l != NULL; l = l->next)
+    {
+      GtkWidget *child = l->data;
+
+      if (!hexpand &&
+	  gtk_widget_compute_expand (child, GTK_ORIENTATION_HORIZONTAL))
+	hexpand = TRUE;
+
+      if (!vexpand &&
+	  gtk_widget_compute_expand (child, GTK_ORIENTATION_VERTICAL))
+	vexpand = TRUE;
+
+      if (hexpand && vexpand)
+	break;
+    }
+
+  *hexpand_p = hexpand;
+  *vexpand_p = vexpand;
+}
+
+static gboolean
+gd_stack_draw (GtkWidget *widget,
+	       cairo_t *cr)
+{
+  GdStack *stack = GD_STACK (widget);
+  GdStackPrivate *priv = stack->priv;
+
+  if (priv->visible_child)
+    gtk_container_propagate_draw (GTK_CONTAINER (stack),
+				  priv->visible_child,
+				  cr);
+
+  return TRUE;
+}
+
+static void
+gd_stack_size_allocate (GtkWidget *widget,
+			GtkAllocation *allocation)
+{
+  GdStack *stack = GD_STACK (widget);
+  GdStackPrivate *priv = stack->priv;
+  GtkAllocation child_allocation;
+  GtkWidget *child;
+  gboolean window_visible;
+  int bin_x, bin_y;
+
+  g_return_if_fail (allocation != NULL);
+
+  gtk_widget_set_allocation (widget, allocation);
+
+  if (priv->visible_child)
+    {
+      GtkAllocation child_allocation = *allocation;
+      gtk_widget_size_allocate (priv->visible_child, &child_allocation);
+    }
+}
+
+static void
+gd_stack_get_preferred_height (GtkWidget *widget,
+			       gint *minimum_height,
+			       gint *natural_height)
+{
+  GdStack *stack = GD_STACK (widget);
+  GdStackPrivate *priv = stack->priv;
+  GtkWidget *child;
+  gint child_min, child_nat;
+  GList *l;
+
+  *minimum_height = 0;
+  *natural_height = 0;
+
+  for (l = priv->children; l != NULL; l = l->next)
+    {
+      child = l->data;
+      if (!priv->homogenous &&
+	  priv->visible_child != child)
+	continue;
+      if (gtk_widget_get_visible (child))
+	{
+	  gtk_widget_get_preferred_height (child, &child_min, &child_nat);
+
+	  *minimum_height = MAX (*minimum_height, child_min);
+	  *natural_height = MAX (*natural_height, child_nat);
+	}
+    }
+}
+
+static void
+gd_stack_get_preferred_height_for_width (GtkWidget* widget,
+					 gint width,
+					 gint *minimum_height,
+					 gint *natural_height)
+{
+  GdStack *stack = GD_STACK (widget);
+  GdStackPrivate *priv = stack->priv;
+  GtkWidget *child;
+  gint child_min, child_nat;
+  GList *l;
+
+  *minimum_height = 0;
+  *natural_height = 0;
+
+  for (l = priv->children; l != NULL; l = l->next)
+    {
+      child = l->data;
+      if (!priv->homogenous &&
+	  priv->visible_child != child)
+	continue;
+      if (gtk_widget_get_visible (child))
+	{
+	  gtk_widget_get_preferred_height_for_width (child, width, &child_min, &child_nat);
+
+	  *minimum_height = MAX (*minimum_height, child_min);
+	  *natural_height = MAX (*natural_height, child_nat);
+	}
+    }
+}
+
+static void
+gd_stack_get_preferred_width (GtkWidget *widget,
+			       gint *minimum_width,
+			       gint *natural_width)
+{
+  GdStack *stack = GD_STACK (widget);
+  GdStackPrivate *priv = stack->priv;
+  GtkWidget *child;
+  gint child_min, child_nat;
+  GList *l;
+
+  *minimum_width = 0;
+  *natural_width = 0;
+
+  for (l = priv->children; l != NULL; l = l->next)
+    {
+      child = l->data;
+      if (!priv->homogenous &&
+	  priv->visible_child != child)
+	continue;
+      if (gtk_widget_get_visible (child))
+	{
+	  gtk_widget_get_preferred_width (child, &child_min, &child_nat);
+
+	  *minimum_width = MAX (*minimum_width, child_min);
+	  *natural_width = MAX (*natural_width, child_nat);
+	}
+    }
+}
+
+static void
+gd_stack_get_preferred_width_for_height (GtkWidget* widget,
+					 gint height,
+					 gint *minimum_width,
+					 gint *natural_width)
+{
+  GdStack *stack = GD_STACK (widget);
+  GdStackPrivate *priv = stack->priv;
+  GtkWidget *child;
+  gint child_min, child_nat;
+  GList *l;
+
+  *minimum_width = 0;
+  *natural_width = 0;
+
+  for (l = priv->children; l != NULL; l = l->next)
+    {
+      child = l->data;
+      if (!priv->homogenous &&
+	  priv->visible_child != child)
+	continue;
+      if (gtk_widget_get_visible (child))
+	{
+	  gtk_widget_get_preferred_width_for_height (child, height, &child_min, &child_nat);
+
+	  *minimum_width = MAX (*minimum_width, child_min);
+	  *natural_width = MAX (*natural_width, child_nat);
+	}
+    }
+}
diff --git a/libgd/gd-stack.h b/libgd/gd-stack.h
new file mode 100644
index 0000000..b5a1d90
--- /dev/null
+++ b/libgd/gd-stack.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * This program 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 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ *
+ */
+
+#ifndef __GD_STACK_H__
+#define __GD_STACK_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+
+#define GD_TYPE_STACK (gd_stack_get_type ())
+#define GD_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GD_TYPE_STACK, GdStack))
+#define GD_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GD_TYPE_STACK, GdStackClass))
+#define GD_IS_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GD_TYPE_STACK))
+#define GD_IS_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GD_TYPE_STACK))
+#define GD_STACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GD_TYPE_STACK, GdStackClass))
+
+typedef struct _GdStack GdStack;
+typedef struct _GdStackClass GdStackClass;
+typedef struct _GdStackPrivate GdStackPrivate;
+
+struct _GdStack {
+  GtkContainer parent_instance;
+  GdStackPrivate *priv;
+};
+
+struct _GdStackClass {
+  GtkBinClass parent_class;
+};
+
+GType gd_stack_get_type (void) G_GNUC_CONST;
+
+GtkWidget  *gd_stack_new                    (void);
+void        gd_stack_set_visible_child      (GdStack    *stack,
+					     GtkWidget  *child);
+GtkWidget * gd_stack_get_visible_child      (GdStack    *stack);
+void        gd_stack_set_visible_child_name (GdStack    *stack,
+					     const char *name);
+const char *gd_stack_get_visible_child_name (GdStack    *stack);
+void        gd_stack_set_homogenous         (GdStack    *stack,
+					     gboolean    homogenous);
+gboolean    gd_stack_get_homogenous         (GdStack    *stack);
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgd/gd.h b/libgd/gd.h
index 83cfba5..9ec91a5 100644
--- a/libgd/gd.h
+++ b/libgd/gd.h
@@ -68,6 +68,10 @@ G_BEGIN_DECLS
 # include <libgd/gd-revealer.h>
 #endif
 
+#ifdef LIBGD_STACK
+# include <libgd/gd-stack.h>
+#endif
+
 G_END_DECLS
 
 #endif /* __GD_H__ */
diff --git a/test-stack.c b/test-stack.c
new file mode 100644
index 0000000..041ccab
--- /dev/null
+++ b/test-stack.c
@@ -0,0 +1,94 @@
+#include <gtk/gtk.h>
+#include <libgd/gd-stack.h>
+
+GtkWidget *stack;
+
+static void
+set_visible_child (GtkWidget *button, gpointer data)
+{
+  gd_stack_set_visible_child (GD_STACK (stack), GTK_WIDGET (data));
+}
+
+static void
+set_visible_child_name (GtkWidget *button, gpointer data)
+{
+  gd_stack_set_visible_child_name (GD_STACK (stack), (const char *)data);
+}
+
+static void
+toggle_homogenous (GtkWidget *button, gpointer data)
+{
+  gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+  g_print ("active %d\n", active);
+  gd_stack_set_homogenous (GD_STACK (stack), active);
+}
+
+gint
+main (gint argc,
+      gchar ** argv)
+{
+  GtkWidget *window, *revealer, *box, *widget, *entry, *button, *hbox;
+  GtkWidget *b1, *b2, *b3;
+
+  gtk_init (&argc, &argv);
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_widget_set_size_request (window, 300, 300);
+
+  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+  gtk_container_add (GTK_CONTAINER (window), box);
+
+  stack = gd_stack_new ();
+  gtk_widget_set_halign (stack, GTK_ALIGN_START);
+  gtk_container_add (GTK_CONTAINER (box), stack);
+
+  b1 = gtk_button_new_with_label ("Blah");
+  gtk_container_add (GTK_CONTAINER (stack), b1);
+  gtk_widget_set_name (b1, "1");
+
+  b2 = gtk_button_new_with_label ("Gazoooooooooooooooonk");
+  gtk_container_add (GTK_CONTAINER (stack), b2);
+  gtk_widget_set_name (b2, "2");
+
+  b3 = gtk_button_new_with_label ("Foo\nBar");
+  gtk_container_add (GTK_CONTAINER (stack), b3);
+  gtk_widget_set_name (b3, "3");
+
+  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  gtk_container_add (GTK_CONTAINER (box), hbox);
+
+  button = gtk_button_new_with_label ("1");
+  gtk_container_add (GTK_CONTAINER (hbox), button);
+  g_signal_connect (button, "clicked", (GCallback) set_visible_child, b1);
+
+  button = gtk_button_new_with_label ("2");
+  gtk_container_add (GTK_CONTAINER (hbox), button);
+  g_signal_connect (button, "clicked", (GCallback) set_visible_child, b2);
+
+  button = gtk_button_new_with_label ("3");
+  gtk_container_add (GTK_CONTAINER (hbox), button);
+  g_signal_connect (button, "clicked", (GCallback) set_visible_child, b3);
+
+  button = gtk_button_new_with_label ("1");
+  gtk_container_add (GTK_CONTAINER (hbox), button);
+  g_signal_connect (button, "clicked", (GCallback) set_visible_child_name, "1");
+
+  button = gtk_button_new_with_label ("2");
+  gtk_container_add (GTK_CONTAINER (hbox), button);
+  g_signal_connect (button, "clicked", (GCallback) set_visible_child_name, "2");
+
+  button = gtk_button_new_with_label ("3");
+  gtk_container_add (GTK_CONTAINER (hbox), button);
+  g_signal_connect (button, "clicked", (GCallback) set_visible_child_name, "3");
+
+  button = gtk_check_button_new_with_label ("humongous");
+  gtk_container_add (GTK_CONTAINER (hbox), button);
+  g_signal_connect (button, "clicked", (GCallback) toggle_homogenous, NULL);
+
+  gtk_widget_show_all (window);
+  gtk_main ();
+
+  gtk_widget_destroy (window);
+
+  return 0;
+}


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