glom r1626 - in trunk/glom/utility_widgets/egg: toolpalette util



Author: jhs
Date: Mon Jun 16 10:23:15 2008
New Revision: 1626
URL: http://svn.gnome.org/viewvc/glom?rev=1626&view=rev

Log:
Really add egg directories

Added:
   trunk/glom/utility_widgets/egg/toolpalette/
   trunk/glom/utility_widgets/egg/toolpalette/Makefile.am
   trunk/glom/utility_widgets/egg/toolpalette/TODO
   trunk/glom/utility_widgets/egg/toolpalette/eggenumaction.c
   trunk/glom/utility_widgets/egg/toolpalette/eggenumaction.h
   trunk/glom/utility_widgets/egg/toolpalette/eggtoolitemgroup.c
   trunk/glom/utility_widgets/egg/toolpalette/eggtoolitemgroup.h
   trunk/glom/utility_widgets/egg/toolpalette/eggtoolpalette.c
   trunk/glom/utility_widgets/egg/toolpalette/eggtoolpalette.h
   trunk/glom/utility_widgets/egg/toolpalette/eggtoolpaletteprivate.h
   trunk/glom/utility_widgets/egg/toolpalette/testtoolpalette.c
   trunk/glom/utility_widgets/egg/util/
   trunk/glom/utility_widgets/egg/util/Makefile.am
   trunk/glom/utility_widgets/egg/util/egg-macros.h
   trunk/glom/utility_widgets/egg/util/egg-marshal.c
   trunk/glom/utility_widgets/egg/util/eggintl.h
   trunk/glom/utility_widgets/egg/util/eggmarshalers.list

Added: trunk/glom/utility_widgets/egg/toolpalette/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/glom/utility_widgets/egg/toolpalette/Makefile.am	Mon Jun 16 10:23:15 2008
@@ -0,0 +1,18 @@
+INCLUDES = \
+  $(EGG_CFLAGS) \
+  -I../util \
+  -DEGG_COMPILATION \
+  -DGTK_DISABLE_DEPRECATED \
+  -DGDK_DISABLE_DEPRECATED \
+  -DG_DISABLE_DEPRECATED
+
+noinst_LTLIBRARIES = libeggtoolpalette.la
+noinst_PROGRAMS = testtoolpalette
+
+libeggtoolpalette_la_SOURCES = eggenumaction.c eggtoolpalette.c eggtoolitemgroup.c
+
+testtoolpalette_SOURCES = testtoolpalette.c
+testtoolpalette_LDFLAGS = libeggtoolpalette.la ../util/libeggutil.la $(EGG_LIBS)
+testtoolpalette_DEPENDENCIES = libeggtoolpalette.la ../util/libeggutil.la
+
+-include Makefile.local

Added: trunk/glom/utility_widgets/egg/toolpalette/TODO
==============================================================================
--- (empty file)
+++ trunk/glom/utility_widgets/egg/toolpalette/TODO	Mon Jun 16 10:23:15 2008
@@ -0,0 +1,32 @@
+EggToolPalette
+==============
+
+* support horizontal orientation for other styles but icon only:
+  - GTK_TOOLBAR_ICONS: done
+  - GTK_TOOLBAR_TEXT: rotate the text labels. currently can be done
+    by poking into the tool items internal's. would be better if
+    GtkToolShell would announce the required text orientation.
+  - GTK_TOOLBAR_BOTH: place items similar to Nautilus' icon view.
+  - GTK_TOOLBAR_BOTH_HORIZ: place items similar to Nautilus' compact view.
+    guess it's only in trunk. Manny blogged about it. Don't know right now
+    how the Windows Explorer calls that view.
+* left-align labels (and center icons) in both-horiz mode:
+  - would require poking into the tool item right now. would be better
+    if GtkToolShell would announce the required text alignment.
+* apply ellipses to text item labels:
+  - would require poking into the tool item right now. would be better
+    if GtkToolShell would announce the preferred ellipsize mode.
+* consider scrollbar size in size-request
+  - is that even possible?
+* implement natural-size interface:
+  - requires GtkNaturalSize to be pushed to GTK+
+
+GtkToolShell/GtkToolItem
+========================
+
+* enforce "expand" and "homogeneous" child property. don't know if GObject can
+  enforce implementation of child properties.
+* inform tool items about the desired text orientation, alignment and ellipsize
+  mode by implementing gtk_tool_shell_get_text_orientation(),
+  gtk_tool_shell_get_text_alignment() and gtk_tool_shell_get_ellipsize_mode().
+  change GtkToolItem to use that information.

Added: trunk/glom/utility_widgets/egg/toolpalette/eggenumaction.c
==============================================================================
--- (empty file)
+++ trunk/glom/utility_widgets/egg/toolpalette/eggenumaction.c	Mon Jun 16 10:23:15 2008
@@ -0,0 +1,552 @@
+/* EggEnumAction -- An action that creates combo boxes for enums
+ * Copyright (C) 2008  Openismus GmbH
+ *
+ * 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.1 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
+ *
+ * Authors:
+ *      Mathias Hasselmann
+ */
+
+#include "eggenumaction.h"
+#include <gtk/gtk.h>
+
+#define P_(msgid) (msgid)
+
+enum
+{
+  PROP_NONE,
+  PROP_ENUM_TYPE,
+};
+
+struct _EggEnumActionPrivate
+{
+  GType         enum_type;
+  GEnumClass   *enum_class;
+  GSList       *bindings;
+  GSList       *callbacks;
+  GtkListStore *model;
+
+  EggEnumActionFilterFunc filter_func;
+  GDestroyNotify          filter_destroy;
+  gpointer                filter_data;
+};
+
+static GQuark egg_enum_action_child_quark;
+static GQuark egg_enum_action_value_quark;
+
+G_DEFINE_TYPE (EggEnumAction, egg_enum_action, GTK_TYPE_ACTION);
+
+static void
+egg_enum_action_init (EggEnumAction *action)
+{
+  action->priv = G_TYPE_INSTANCE_GET_PRIVATE (action,
+                                              EGG_TYPE_ENUM_ACTION,
+                                              EggEnumActionPrivate);
+}
+
+static void
+egg_enum_action_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  EggEnumAction *action = EGG_ENUM_ACTION (object);
+  GType enum_type;
+
+  switch (prop_id)
+    {
+      case PROP_ENUM_TYPE:
+        enum_type = g_value_get_gtype (value);
+
+        if (enum_type == action->priv->enum_type)
+          break;
+
+        if (action->priv->enum_class)
+          {
+            g_type_class_unref (action->priv->enum_class);
+            action->priv->enum_class = NULL;
+          }
+
+        action->priv->enum_type = enum_type;
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+egg_enum_action_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  EggEnumAction *action = EGG_ENUM_ACTION (object);
+
+  switch (prop_id)
+    {
+      case PROP_ENUM_TYPE:
+        g_value_set_gtype (value, action->priv->enum_type);
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+egg_enum_action_dispose (GObject *object)
+{
+  EggEnumAction *action = EGG_ENUM_ACTION (object);
+
+  if (action->priv->enum_class)
+    {
+      g_type_class_unref (action->priv->enum_class);
+      action->priv->enum_class = NULL;
+    }
+
+  if (action->priv->model)
+    {
+      g_type_class_unref (action->priv->model);
+      action->priv->model = NULL;
+    }
+
+  while (action->priv->bindings)
+    {
+      g_param_spec_unref (action->priv->bindings->data);
+      g_object_unref (action->priv->bindings->next->data);
+
+      action->priv->bindings->data = g_slist_delete_link (action->priv->bindings->data,
+                                                          action->priv->bindings->data);
+      action->priv->bindings->data = g_slist_delete_link (action->priv->bindings->data,
+                                                          action->priv->bindings->data);
+    }
+
+  if (action->priv->callbacks)
+    {
+      g_slist_free (action->priv->callbacks);
+      action->priv->callbacks = NULL;
+    }
+
+  if (action->priv->filter_destroy)
+    {
+      action->priv->filter_destroy (action->priv->filter_data);
+      action->priv->filter_destroy = NULL;
+    }
+
+  action->priv->filter_func = NULL;
+  action->priv->filter_data = NULL;
+
+  G_OBJECT_CLASS (egg_enum_action_parent_class)->dispose (object);
+}
+
+static GtkTreeModel*
+egg_enum_action_get_model (EggEnumAction *action)
+{
+  if (!action->priv->enum_class)
+    action->priv->enum_class = g_type_class_ref (action->priv->enum_type);
+
+  if (!action->priv->model)
+    {
+      GtkTreeIter iter;
+      guint i;
+
+      action->priv->model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
+
+      for (i = 0; i < action->priv->enum_class->n_values; ++i)
+        {
+          GEnumValue *enum_value = &action->priv->enum_class->values[i];
+
+          if (action->priv->filter_func &&
+             !action->priv->filter_func (enum_value, action->priv->filter_data))
+            continue;
+
+          gtk_list_store_append (action->priv->model, &iter);
+          gtk_list_store_set (action->priv->model, &iter,
+                              0, enum_value->value_nick,
+                              1, enum_value, -1);
+        }
+    }
+
+  return GTK_TREE_MODEL (action->priv->model);
+}
+
+static gboolean
+egg_enum_action_get_iter (EggEnumAction *action,
+                          GtkTreeIter   *iter,
+                          GObject       *object,
+                          GParamSpec    *property)
+{
+  GtkTreeModel *model = egg_enum_action_get_model (action);
+  GEnumValue *enum_value;
+  gint current_value;
+
+  if (!gtk_tree_model_get_iter_first (model, iter))
+    return FALSE;
+
+  if (!G_IS_OBJECT (object) || !G_IS_PARAM_SPEC_ENUM (property))
+    return TRUE;
+
+  g_object_get (object, property->name, &current_value, NULL);
+
+  do
+    {
+      gtk_tree_model_get (model, iter, 1, &enum_value, -1);
+
+      if (enum_value->value == current_value)
+        return TRUE;
+    }
+  while (gtk_tree_model_iter_next (model, iter));
+
+  return FALSE;
+}
+
+static gboolean
+egg_enum_action_get_active_iter (EggEnumAction *action,
+                                 GtkTreeIter   *iter)
+{
+  GParamSpec *property = NULL;
+  GObject *object = NULL;
+
+  if (action->priv->bindings)
+    {
+      property = action->priv->bindings->data;
+      object = action->priv->bindings->next->data;
+    }
+
+  return egg_enum_action_get_iter (action, iter, object, property);
+}
+
+static void
+egg_enum_action_set_value (EggEnumAction *action,
+                           GEnumValue    *enum_value)
+{
+  GSList *iter;
+
+  for (iter = action->priv->bindings; iter; iter = iter->next->next)
+    {
+      GParamSpec *property = iter->data;
+      GObject *object = iter->next->data;
+
+      g_object_set (object, property->name, enum_value->value, NULL);
+    }
+
+  for (iter = action->priv->callbacks; iter; iter = iter->next->next)
+    {
+      EggEnumActionCallback callback = iter->next->data;
+      gpointer user_data = iter->data;
+
+      callback (enum_value, user_data);
+    }
+}
+
+static void
+egg_enum_action_combo_changed (GtkComboBox *combo,
+                               gpointer     data)
+{
+  EggEnumAction *action = EGG_ENUM_ACTION (data);
+  GEnumValue *enum_value;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  if (gtk_combo_box_get_active_iter (combo, &iter))
+    {
+      model = egg_enum_action_get_model (action);
+      gtk_tree_model_get (model, &iter, 1, &enum_value, -1);
+      egg_enum_action_set_value (action, enum_value);
+    }
+}
+
+static void
+egg_enum_action_toolbar_reconfigured (GtkToolItem *item,
+                                      gpointer     data)
+{
+  gboolean important = gtk_tool_item_get_is_important (item);
+  GtkToolbarStyle style = gtk_tool_item_get_toolbar_style (item);
+  EggEnumAction *action = EGG_ENUM_ACTION (data);
+  gchar *text, *tmp;
+
+  GtkWidget *align, *box = NULL;
+  GtkWidget *combo, *label;
+  GtkCellRenderer *cell;
+  GtkTreeIter iter;
+
+  align = gtk_bin_get_child (GTK_BIN (item));
+  box = gtk_bin_get_child (GTK_BIN (align));
+
+  if (box)
+    gtk_container_remove (GTK_CONTAINER (align), box);
+
+  g_object_get (action, "label", &text, NULL);
+  box = NULL;
+
+  combo = gtk_combo_box_new_with_model (egg_enum_action_get_model (action));
+  g_signal_connect (combo, "changed", G_CALLBACK (egg_enum_action_combo_changed), action);
+
+  cell = gtk_cell_renderer_text_new ();
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
+  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell, "text", 0, NULL);
+  g_object_set_qdata (G_OBJECT (item), egg_enum_action_child_quark, combo);
+
+  if (egg_enum_action_get_active_iter (action, &iter))
+    gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
+
+  if (GTK_TOOLBAR_BOTH == style)
+    {
+      label = gtk_label_new (text);
+
+      box = gtk_vbox_new (FALSE, 0);
+      gtk_box_pack_end (GTK_BOX (box), label, FALSE, TRUE, 0);
+    }
+  else if (GTK_TOOLBAR_ICONS != style || important)
+    {
+      tmp = g_strconcat (text, ":", NULL);
+      label = gtk_label_new (tmp);
+      g_free (tmp);
+
+      gtk_misc_set_padding (GTK_MISC (label), 3, 0);
+
+      box = gtk_hbox_new (FALSE, 0);
+      gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
+    }
+
+  if (box)
+    {
+      gtk_box_pack_start (GTK_BOX (box), combo, TRUE, TRUE, 0);
+      gtk_container_add (GTK_CONTAINER (align), box);
+    }
+  else
+    gtk_container_add (GTK_CONTAINER (align), combo);
+
+  gtk_widget_show_all (align);
+
+  g_free (text);
+}
+
+static void
+egg_enum_action_select_menu_item (EggEnumAction *action,
+                                  GtkTreeIter   *iter,
+                                  GtkWidget     *menu)
+{
+  GList *items = gtk_container_get_children (GTK_CONTAINER (menu));
+  GtkTreeModel *model = egg_enum_action_get_model (action);
+  GtkTreePath *path = gtk_tree_model_get_path (model, iter);
+  gint item_index = gtk_tree_path_get_indices (path)[0];
+  GtkWidget *child = g_list_nth_data (items, item_index);
+
+  if (child)
+    gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (child), TRUE);
+
+  gtk_tree_path_free (path);
+  g_list_free (items);
+}
+
+static void
+egg_enum_action_menu_item_toggled (GtkCheckMenuItem *item,
+                                   gpointer          data)
+{
+  GEnumValue *enum_value;
+
+  enum_value = g_object_get_qdata (G_OBJECT (item), egg_enum_action_value_quark);
+  egg_enum_action_set_value (EGG_ENUM_ACTION (data), enum_value);
+}
+
+static GtkWidget*
+egg_enum_action_create_menu_item (GtkAction *action)
+{
+  GtkTreeModel *model = egg_enum_action_get_model (EGG_ENUM_ACTION (action));
+  GEnumValue *enum_value;
+  GtkTreeIter iter;
+  gchar *label;
+
+  GtkWidget *item;
+  GtkWidget *menu = gtk_menu_new ();
+  GSList *group = NULL;
+
+  if (gtk_tree_model_get_iter_first (model, &iter))
+    do
+      {
+        gtk_tree_model_get (model, &iter, 0, &label, 1, &enum_value, -1);
+
+        item = gtk_radio_menu_item_new_with_label (group, label);
+        group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item));
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+        g_object_set_qdata (G_OBJECT (item),
+                            egg_enum_action_value_quark,
+                            enum_value);
+
+        g_signal_connect (item, "toggled",
+                          G_CALLBACK (egg_enum_action_menu_item_toggled),
+                          action);
+      }
+    while (gtk_tree_model_iter_next (model, &iter));
+
+  gtk_widget_show_all (menu);
+
+  if (egg_enum_action_get_active_iter (EGG_ENUM_ACTION (action), &iter))
+    egg_enum_action_select_menu_item (EGG_ENUM_ACTION (action), &iter, menu);
+
+  item = GTK_ACTION_CLASS (egg_enum_action_parent_class)->create_menu_item (action);
+  g_object_set_qdata (G_OBJECT (item), egg_enum_action_child_quark, menu);
+  gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
+
+  return item;
+}
+
+static GtkWidget*
+egg_enum_action_create_tool_item (GtkAction *action)
+{
+  GtkToolItem *item = gtk_tool_item_new ();
+  GtkWidget *align = gtk_alignment_new (0.5, 0.5, 1, 0);
+
+  gtk_container_add (GTK_CONTAINER (item), align);
+
+  g_signal_connect (item, "toolbar-reconfigured",
+                    G_CALLBACK (egg_enum_action_toolbar_reconfigured),
+                    action);
+
+  return GTK_WIDGET (item);
+}
+
+static void
+egg_enum_action_class_init (EggEnumActionClass *cls)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (cls);
+  GtkActionClass *aclass = GTK_ACTION_CLASS (cls);
+
+  oclass->set_property     = egg_enum_action_set_property;
+  oclass->get_property     = egg_enum_action_get_property;
+  oclass->dispose          = egg_enum_action_dispose;
+
+  aclass->create_menu_item = egg_enum_action_create_menu_item;
+  aclass->create_tool_item = egg_enum_action_create_tool_item;
+
+  g_object_class_install_property (oclass, PROP_ENUM_TYPE,
+                                   g_param_spec_gtype ("enum-type",
+                                                       P_("Enum Type"),
+                                                       P_("Type of the enumeration"),
+                                                       G_TYPE_ENUM,
+                                                       G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
+                                                       G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  g_type_class_add_private (cls, sizeof (EggEnumActionPrivate));
+
+  egg_enum_action_child_quark = g_quark_from_static_string ("egg-enum-action-child-quark");
+  egg_enum_action_value_quark = g_quark_from_static_string ("egg-enum-action-value-quark");
+}
+
+GtkAction*
+egg_enum_action_new (const gchar *name,
+                     const gchar *label,
+                     const gchar *tooltip,
+                     GType        enum_type)
+{
+  g_return_val_if_fail (NULL != name, NULL);
+  g_return_val_if_fail (g_type_is_a (enum_type, G_TYPE_ENUM), NULL);
+
+  return g_object_new (EGG_TYPE_ENUM_ACTION, "name", name, "label", label,
+                       "tooltip", tooltip, "enum-type", enum_type, NULL);
+}
+
+static void
+egg_enum_action_notify (GObject    *object,
+                        GParamSpec *property,
+                        gpointer    data)
+{
+  EggEnumAction *action = EGG_ENUM_ACTION (data);
+  GtkTreeIter active_iter;
+  GtkWidget *child;
+  GSList *proxies;
+
+  if (egg_enum_action_get_iter (action, &active_iter, object, property))
+    {
+      proxies = gtk_action_get_proxies (GTK_ACTION (action));
+
+      while (proxies)
+        {
+          child = g_object_get_qdata (proxies->data, egg_enum_action_child_quark);
+
+          if (GTK_IS_COMBO_BOX (child))
+            gtk_combo_box_set_active_iter (GTK_COMBO_BOX (child), &active_iter);
+          else if (GTK_IS_MENU (child))
+            egg_enum_action_select_menu_item (action, &active_iter, child);
+
+          proxies = proxies->next;
+        }
+    }
+}
+
+void
+egg_enum_action_bind (EggEnumAction *action,
+                      GObject       *object,
+                      const gchar   *property_name)
+{
+  gchar *signal_name;
+  GParamSpec *property;
+
+  g_return_if_fail (EGG_IS_ENUM_ACTION (action));
+  g_return_if_fail (GTK_IS_OBJECT (object));
+  g_return_if_fail (NULL != property_name);
+
+  property = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
+                                           property_name);
+
+  g_return_if_fail (NULL != property);
+  g_return_if_fail (g_type_is_a (property->value_type, action->priv->enum_type));
+
+  signal_name = g_strconcat ("notify::", property_name, NULL);
+
+  g_signal_connect (object, signal_name,
+                    G_CALLBACK (egg_enum_action_notify),
+                    action);
+
+  g_free (signal_name);
+
+  action->priv->bindings = g_slist_prepend (action->priv->bindings, g_object_ref (object));
+  action->priv->bindings = g_slist_prepend (action->priv->bindings, g_param_spec_ref (property));
+
+  egg_enum_action_notify (object, property, action);
+}
+
+void
+egg_enum_action_connect (EggEnumAction         *action,
+                         EggEnumActionCallback  callback,
+                         gpointer               data)
+{
+  g_return_if_fail (EGG_IS_ENUM_ACTION (action));
+  g_return_if_fail (NULL != callback);
+
+  action->priv->callbacks = g_slist_prepend (action->priv->callbacks, callback);
+  action->priv->callbacks = g_slist_prepend (action->priv->callbacks, data);
+}
+
+void
+egg_enum_action_set_filter (EggEnumAction           *action,
+                            EggEnumActionFilterFunc  filter,
+                            gpointer                 user_data,
+                            GDestroyNotify           destroy_data)
+{
+  g_return_if_fail (EGG_IS_ENUM_ACTION (action));
+
+  if (action->priv->filter_destroy)
+    action->priv->filter_destroy (action->priv->filter_data);
+
+  action->priv->filter_func = filter;
+  action->priv->filter_data = user_data;
+  action->priv->filter_destroy = destroy_data;
+}

Added: trunk/glom/utility_widgets/egg/toolpalette/eggenumaction.h
==============================================================================
--- (empty file)
+++ trunk/glom/utility_widgets/egg/toolpalette/eggenumaction.h	Mon Jun 16 10:23:15 2008
@@ -0,0 +1,76 @@
+/* EggEnumAction -- An action that creates combo boxes for enums
+ * Copyright (C) 2008  Openismus GmbH
+ *
+ * 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.1 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
+ *
+ * Authors:
+ *      Mathias Hasselmann
+ */
+
+#ifndef __EGG_ENUM_ACTION_H__
+#define __EGG_ENUM_ACTION_H__
+
+#include <gtk/gtkaction.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_ENUM_ACTION           (egg_enum_action_get_type())
+#define EGG_ENUM_ACTION(obj)           (G_TYPE_CHECK_INSTANCE_CAST(obj, EGG_TYPE_ENUM_ACTION, EggEnumAction))
+#define EGG_ENUM_ACTION_CLASS(cls)     (G_TYPE_CHECK_CLASS_CAST(cls, EGG_TYPE_ENUM_ACTION, EggEnumActionClass))
+#define EGG_IS_ENUM_ACTION(obj)        (G_TYPE_CHECK_INSTANCE_TYPE(obj, EGG_TYPE_ENUM_ACTION))
+#define EGG_IS_ENUM_ACTION_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE(obj, EGG_TYPE_ENUM_ACTION))
+#define EGG_ENUM_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EGG_TYPE_ENUM_ACTION, EggEnumActionClass))
+
+typedef struct _EggEnumAction        EggEnumAction;
+typedef struct _EggEnumActionClass   EggEnumActionClass;
+typedef struct _EggEnumActionPrivate EggEnumActionPrivate;
+
+typedef void     (*EggEnumActionCallback)   (GEnumValue *enum_value,
+                                             gpointer    user_data);
+typedef gboolean (*EggEnumActionFilterFunc) (GEnumValue *enum_value,
+                                             gpointer    user_data);
+
+struct _EggEnumAction
+{
+  GtkAction parent_instance;
+  EggEnumActionPrivate *priv;
+};
+
+struct _EggEnumActionClass
+{
+  GtkActionClass parent_class;
+};
+
+GType      egg_enum_action_get_type   (void) G_GNUC_CONST;
+GtkAction* egg_enum_action_new        (const gchar             *name,
+                                       const gchar             *label,
+                                       const gchar             *tooltip,
+                                       GType                    enum_type);
+
+void       egg_enum_action_bind       (EggEnumAction           *action,
+                                       GObject                 *object,
+                                       const gchar             *property_name);
+void       egg_enum_action_connect    (EggEnumAction           *action,
+                                       EggEnumActionCallback    callback,
+                                       gpointer                 data);
+
+void       egg_enum_action_set_filter (EggEnumAction           *action,
+                                       EggEnumActionFilterFunc  filter,
+                                       gpointer                 user_data,
+                                       GDestroyNotify           destroy_data);
+
+G_END_DECLS
+
+#endif /* __EGG_ENUM_ACTION_H__ */ 

Added: trunk/glom/utility_widgets/egg/toolpalette/eggtoolitemgroup.c
==============================================================================
--- (empty file)
+++ trunk/glom/utility_widgets/egg/toolpalette/eggtoolitemgroup.c	Mon Jun 16 10:23:15 2008
@@ -0,0 +1,1692 @@
+/* EggToolPalette -- A tool palette with categories and DnD support
+ * Copyright (C) 2008  Openismus GmbH
+ *
+ * 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.1 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
+ *
+ * Authors:
+ *      Mathias Hasselmann
+ *      Jan Arne Petersen
+ */
+
+#include "eggtoolitemgroup.h"
+#include "eggtoolpaletteprivate.h"
+
+#include <gtk/gtk.h>
+#include <math.h>
+#include <string.h>
+
+#define P_(msgid)               (msgid)
+
+#define ANIMATION_TIMEOUT        50
+#define ANIMATION_DURATION      (ANIMATION_TIMEOUT * 4)
+#define DEFAULT_EXPANDER_SIZE    16
+#define DEFAULT_HEADER_SPACING   2
+
+#define DEFAULT_NAME             NULL
+#define DEFAULT_COLLAPSED        FALSE
+#define DEFAULT_ELLIPSIZE        PANGO_ELLIPSIZE_NONE
+
+enum
+{
+  PROP_NONE,
+  PROP_NAME,
+  PROP_COLLAPSED,
+  PROP_ELLIPSIZE,
+};
+
+enum
+{
+  CHILD_PROP_NONE,
+  CHILD_PROP_HOMOGENEOUS,
+  CHILD_PROP_EXPAND,
+  CHILD_PROP_FILL,
+  CHILD_PROP_NEW_ROW,
+  CHILD_PROP_POSITION,
+};
+
+typedef struct _EggToolItemGroupChild EggToolItemGroupChild;
+
+struct _EggToolItemGroupPrivate
+{
+  GtkWidget         *header;
+
+  GList             *children;
+
+  gint64             animation_start;
+  GSource           *animation_timeout;
+  GtkExpanderStyle   expander_style;
+  gint               expander_size;
+  gint               header_spacing;
+  PangoEllipsizeMode ellipsize;
+
+  guint              collapsed : 1;
+
+};
+
+struct _EggToolItemGroupChild
+{
+  GtkToolItem *item;
+
+  guint        homogeneous : 1;
+  guint        expand : 1;
+  guint        fill : 1;
+  guint        new_row : 1;
+};
+
+#ifdef GTK_TYPE_TOOL_SHELL
+
+static void egg_tool_item_group_tool_shell_init (GtkToolShellIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EggToolItemGroup, egg_tool_item_group, GTK_TYPE_CONTAINER,
+G_IMPLEMENT_INTERFACE (GTK_TYPE_TOOL_SHELL, egg_tool_item_group_tool_shell_init));
+
+#else /* GTK_TYPE_TOOL_SHELL */
+
+#define GTK_TOOL_SHELL(obj)             EGG_TOOL_ITEM_GROUP((obj))
+
+#define GtkToolShell                    EggToolItemGroup
+
+#define gtk_tool_shell_get_orientation  egg_tool_item_group_get_orientation
+#define gtk_tool_shell_get_style        egg_tool_item_group_get_style
+
+G_DEFINE_TYPE (EggToolItemGroup, egg_tool_item_group, GTK_TYPE_CONTAINER);
+
+#endif /* GTK_TYPE_TOOL_SHELL */
+
+static GtkWidget*
+egg_tool_item_group_get_alignment (EggToolItemGroup *group)
+{
+  return gtk_bin_get_child (GTK_BIN (group->priv->header));
+}
+
+static GtkWidget*
+egg_tool_item_group_get_label (EggToolItemGroup *group)
+{
+  GtkWidget *alignment = egg_tool_item_group_get_alignment (group);
+  return gtk_bin_get_child (GTK_BIN (alignment));
+}
+
+static GtkOrientation
+egg_tool_item_group_get_orientation (GtkToolShell *shell)
+{
+  GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell));
+
+  if (EGG_IS_TOOL_PALETTE (parent))
+    return egg_tool_palette_get_orientation (EGG_TOOL_PALETTE (parent));
+
+  return GTK_ORIENTATION_VERTICAL;
+}
+
+static GtkToolbarStyle
+egg_tool_item_group_get_style (GtkToolShell *shell)
+{
+  GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell));
+
+  if (EGG_IS_TOOL_PALETTE (parent))
+    return egg_tool_palette_get_style (EGG_TOOL_PALETTE (parent));
+
+  return GTK_TOOLBAR_ICONS;
+}
+
+#ifdef GTK_TYPE_TOOL_SHELL
+
+static GtkIconSize
+egg_tool_item_group_get_icon_size (GtkToolShell *shell)
+{
+  GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell));
+
+  if (EGG_IS_TOOL_PALETTE (parent))
+    return egg_tool_palette_get_icon_size (EGG_TOOL_PALETTE (parent));
+
+  return GTK_ICON_SIZE_SMALL_TOOLBAR;
+}
+
+#ifdef HAVE_EXTENDED_TOOL_SHELL_SUPPORT_BUG_535090
+
+static PangoEllipsizeMode
+egg_tool_item_group_get_ellipsize_mode (GtkToolShell *shell)
+{
+  return EGG_TOOL_ITEM_GROUP (shell)->priv->ellipsize;
+}
+
+static gfloat
+egg_tool_item_group_get_text_alignment (GtkToolShell *shell)
+{
+  if (GTK_TOOLBAR_TEXT == egg_tool_item_group_get_style (shell) ||
+      GTK_TOOLBAR_BOTH_HORIZ == egg_tool_item_group_get_style (shell))
+    return 0.0;
+
+  return 0.5;
+}
+
+static GtkOrientation
+egg_tool_item_group_get_text_orientation (GtkToolShell *shell)
+{
+  GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell));
+
+  if (EGG_IS_TOOL_PALETTE (parent))
+    {
+      GtkOrientation orientation = egg_tool_palette_get_orientation (EGG_TOOL_PALETTE (parent));
+      if (GTK_ORIENTATION_HORIZONTAL == orientation &&
+          (GTK_TOOLBAR_TEXT == egg_tool_item_group_get_style (shell)/* ||
+           GTK_TOOLBAR_BOTH_HORIZ == egg_tool_item_group_get_style (shell)*/))
+        return GTK_ORIENTATION_VERTICAL;
+    }
+
+  return GTK_ORIENTATION_HORIZONTAL;
+}
+
+static GtkSizeGroup *
+egg_tool_item_group_get_text_size_group (GtkToolShell *shell)
+{
+  GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell));
+
+  if (EGG_IS_TOOL_PALETTE (parent))
+    return _egg_tool_palette_get_size_group (EGG_TOOL_PALETTE (parent));
+
+  return NULL;
+}
+
+#endif
+
+static void
+egg_tool_item_group_tool_shell_init (GtkToolShellIface *iface)
+{
+  iface->get_icon_size = egg_tool_item_group_get_icon_size;
+  iface->get_orientation = egg_tool_item_group_get_orientation;
+  iface->get_style = egg_tool_item_group_get_style;
+#ifdef HAVE_EXTENDED_TOOL_SHELL_SUPPORT_BUG_535090
+  iface->get_text_alignment = egg_tool_item_group_get_text_alignment;
+  iface->get_text_orientation = egg_tool_item_group_get_text_orientation;
+  iface->get_text_size_group = egg_tool_item_group_get_text_size_group;
+  iface->get_ellipsize_mode = egg_tool_item_group_get_ellipsize_mode;
+#endif
+}
+
+#endif /* GTK_TYPE_TOOL_SHELL */
+
+static gboolean
+egg_tool_item_group_header_expose_event_cb (GtkWidget      *widget,
+                                            GdkEventExpose *event,
+                                            gpointer        data)
+{
+  EggToolItemGroup *group = EGG_TOOL_ITEM_GROUP (data);
+  GtkExpanderStyle expander_style;
+  GtkOrientation orientation;
+  gint x, y;
+  GtkTextDirection direction;
+
+  orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
+  expander_style = group->priv->expander_style;
+  direction = gtk_widget_get_direction (widget);
+
+  if (GTK_ORIENTATION_VERTICAL == orientation)
+    {
+      if (GTK_TEXT_DIR_RTL == direction)
+        x = widget->allocation.x + widget->allocation.width - group->priv->expander_size / 2;
+      else
+        x = widget->allocation.x + group->priv->expander_size / 2;
+      y = widget->allocation.y + widget->allocation.height / 2;
+    }
+  else
+    {
+      x = widget->allocation.x + widget->allocation.width / 2;
+      y = widget->allocation.y + group->priv->expander_size / 2;
+
+      /* Unfortunatly gtk_paint_expander() doesn't support rotated drawing
+       * modes. Luckily the following shady arithmetics produce the desired
+       * result. */
+      expander_style = GTK_EXPANDER_EXPANDED - expander_style; /* XXX */
+    }
+
+  gtk_paint_expander (widget->style, widget->window,
+                      group->priv->header->state,
+                      &event->area, GTK_WIDGET (group),
+                      "tool-palette-header", x, y,
+                      expander_style);
+
+  return FALSE;
+}
+
+static void
+egg_tool_item_group_header_size_request_cb (GtkWidget      *widget G_GNUC_UNUSED,
+                                            GtkRequisition *requisition,
+                                            gpointer        data)
+{
+  EggToolItemGroup *group = EGG_TOOL_ITEM_GROUP (data);
+  requisition->height = MAX (requisition->height, group->priv->expander_size);
+}
+
+static void
+egg_tool_item_group_header_clicked_cb (GtkButton *button G_GNUC_UNUSED,
+                                       gpointer   data)
+{
+  EggToolItemGroup *group = EGG_TOOL_ITEM_GROUP (data);
+  GtkWidget *parent = gtk_widget_get_parent (data);
+
+  if (group->priv->collapsed ||
+      !EGG_IS_TOOL_PALETTE (parent) ||
+      !egg_tool_palette_get_exclusive (EGG_TOOL_PALETTE (parent), data))
+    egg_tool_item_group_set_collapsed (group, !group->priv->collapsed);
+}
+
+static void
+egg_tool_item_group_header_adjust_style (EggToolItemGroup *group)
+{
+  GtkWidget *alignment = egg_tool_item_group_get_alignment (group);
+  GtkWidget *label = gtk_bin_get_child (GTK_BIN (alignment));
+  GtkWidget *widget = GTK_WIDGET (group);
+  gint dx = 0, dy = 0;
+  GtkTextDirection direction = gtk_widget_get_direction (widget);
+
+  gtk_widget_style_get (widget,
+                        "header-spacing", &group->priv->header_spacing,
+                        "expander-size", &group->priv->expander_size,
+                        NULL);
+
+  switch (gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group)))
+    {
+      case GTK_ORIENTATION_HORIZONTAL:
+        dy = group->priv->header_spacing + group->priv->expander_size;
+        gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_NONE);
+        if (GTK_TEXT_DIR_RTL == direction)
+          gtk_label_set_angle (GTK_LABEL (label), -90);
+        else
+          gtk_label_set_angle (GTK_LABEL (label), 90);
+        break;
+
+      case GTK_ORIENTATION_VERTICAL:
+        dx = group->priv->header_spacing + group->priv->expander_size;
+        gtk_label_set_ellipsize (GTK_LABEL (label), group->priv->ellipsize);
+        gtk_label_set_angle (GTK_LABEL (label), 0);
+        break;
+    }
+
+  gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), dy, 0, dx, 0);
+}
+
+static void
+egg_tool_item_group_init (EggToolItemGroup *group)
+{
+  GtkWidget *alignment;
+  GtkWidget *label;
+
+  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (group), FALSE);
+
+  group->priv = G_TYPE_INSTANCE_GET_PRIVATE (group,
+                                             EGG_TYPE_TOOL_ITEM_GROUP,
+                                             EggToolItemGroupPrivate);
+
+  group->priv->children = NULL;
+  group->priv->header_spacing = DEFAULT_HEADER_SPACING;
+  group->priv->expander_size = DEFAULT_EXPANDER_SIZE;
+  group->priv->expander_style = GTK_EXPANDER_EXPANDED;
+
+  label = gtk_label_new (NULL);
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+  alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+  gtk_container_add (GTK_CONTAINER (alignment), label);
+  gtk_widget_show_all (alignment);
+
+  gtk_widget_push_composite_child ();
+  group->priv->header = gtk_button_new ();
+  gtk_widget_set_composite_name (group->priv->header, "header");
+  gtk_widget_pop_composite_child ();
+
+  g_object_ref_sink (group->priv->header);
+  gtk_button_set_focus_on_click (GTK_BUTTON (group->priv->header), FALSE);
+  gtk_container_add (GTK_CONTAINER (group->priv->header), alignment);
+  gtk_widget_set_parent (group->priv->header, GTK_WIDGET (group));
+
+  egg_tool_item_group_header_adjust_style (group);
+
+  g_signal_connect_after (alignment, "expose-event",
+                          G_CALLBACK (egg_tool_item_group_header_expose_event_cb),
+                          group);
+  g_signal_connect_after (alignment, "size-request",
+                          G_CALLBACK (egg_tool_item_group_header_size_request_cb),
+                          group);
+
+  g_signal_connect (group->priv->header, "clicked",
+                    G_CALLBACK (egg_tool_item_group_header_clicked_cb),
+                    group);
+}
+
+static void
+egg_tool_item_group_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  EggToolItemGroup *group = EGG_TOOL_ITEM_GROUP (object);
+
+  switch (prop_id)
+    {
+      case PROP_NAME:
+        egg_tool_item_group_set_name (group, g_value_get_string (value));
+        break;
+
+      case PROP_COLLAPSED:
+        egg_tool_item_group_set_collapsed (group, g_value_get_boolean (value));
+        break;
+
+      case PROP_ELLIPSIZE:
+        egg_tool_item_group_set_ellipsize (group, g_value_get_enum (value));
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+egg_tool_item_group_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  EggToolItemGroup *group = EGG_TOOL_ITEM_GROUP (object);
+
+  switch (prop_id)
+    {
+      case PROP_NAME:
+        g_value_set_string (value, egg_tool_item_group_get_name (group));
+        break;
+
+      case PROP_COLLAPSED:
+        g_value_set_boolean (value, egg_tool_item_group_get_collapsed (group));
+        break;
+
+      case PROP_ELLIPSIZE:
+        g_value_set_enum (value, egg_tool_item_group_get_ellipsize (group));
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+egg_tool_item_group_finalize (GObject *object)
+{
+  EggToolItemGroup *group = EGG_TOOL_ITEM_GROUP (object);
+
+  if (group->priv->children)
+    {
+      g_list_free (group->priv->children);
+      group->priv->children = NULL;
+    }
+
+  G_OBJECT_CLASS (egg_tool_item_group_parent_class)->finalize (object);
+}
+
+static void
+egg_tool_item_group_get_item_size (EggToolItemGroup *group,
+                                   GtkRequisition   *item_size)
+{
+  GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (group));
+
+  if (EGG_IS_TOOL_PALETTE (parent))
+    _egg_tool_palette_get_item_size (EGG_TOOL_PALETTE (parent), item_size);
+  else
+    _egg_tool_item_group_item_size_request (group, item_size);
+}
+
+static void
+egg_tool_item_group_size_request (GtkWidget      *widget,
+                                  GtkRequisition *requisition)
+{
+  const gint border_width = GTK_CONTAINER (widget)->border_width;
+  EggToolItemGroup *group = EGG_TOOL_ITEM_GROUP (widget);
+  GtkOrientation orientation;
+  GtkRequisition item_size;
+
+  if (group->priv->children && egg_tool_item_group_get_name (group))
+    {
+      gtk_widget_size_request (group->priv->header, requisition);
+      gtk_widget_show (group->priv->header);
+    }
+  else
+    {
+      requisition->width = requisition->height = 0;
+      gtk_widget_hide (group->priv->header);
+    }
+
+  egg_tool_item_group_get_item_size (group, &item_size);
+
+  orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
+
+  if (GTK_ORIENTATION_VERTICAL == orientation)
+    requisition->width = MAX (requisition->width, item_size.width);
+  else
+    requisition->height = MAX (requisition->height, item_size.height);
+
+  requisition->width += border_width * 2;
+  requisition->height += border_width * 2;
+}
+
+static gboolean
+egg_tool_item_group_is_item_visible (GtkToolItem    *item,
+                                     GtkOrientation  orientation)
+{
+  return
+    (GTK_WIDGET_VISIBLE (item)) &&
+    (GTK_ORIENTATION_VERTICAL == orientation ?
+     gtk_tool_item_get_visible_vertical (item) :
+     gtk_tool_item_get_visible_horizontal (item));
+}
+
+static void
+egg_tool_item_group_real_size_allocate (GtkWidget      *widget,
+                                        GtkAllocation  *allocation,
+                                        GtkRequisition *inquery)
+{
+  const gint border_width = GTK_CONTAINER (widget)->border_width;
+  EggToolItemGroup *group = EGG_TOOL_ITEM_GROUP (widget);
+  GtkRequisition child_requisition;
+  GtkAllocation child_allocation;
+
+  GtkRequisition item_size;
+  GtkAllocation item_area;
+
+  GtkOrientation orientation;
+  GtkToolbarStyle style;
+
+  guint n_visible_items;
+
+  GList *it;
+
+  guint header_width = 0;
+
+  guint n_columns, n_rows;
+
+  GtkTextDirection direction = gtk_widget_get_direction (widget);
+
+  if (!inquery)
+    GTK_WIDGET_CLASS (egg_tool_item_group_parent_class)->size_allocate (widget, allocation);
+
+  orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
+  style = gtk_tool_shell_get_style (GTK_TOOL_SHELL (group));
+
+  /* figure out header size */
+
+  if (GTK_WIDGET_VISIBLE (group->priv->header))
+    gtk_widget_size_request (group->priv->header, &child_requisition);
+  else
+    child_requisition.width = child_requisition.height = 0;
+
+  /* figure out item size */
+
+  egg_tool_item_group_get_item_size (group, &item_size);
+
+  if (GTK_ORIENTATION_VERTICAL == orientation)
+    item_size.width = MIN (item_size.width, allocation->width);
+  else
+    item_size.height = MIN (item_size.height, allocation->height);
+
+  n_visible_items = 0;
+
+  for (it = group->priv->children; it != NULL; it = it->next)
+    {
+      EggToolItemGroupChild *child = it->data;
+
+      if (egg_tool_item_group_is_item_visible (child->item, orientation))
+        n_visible_items += 1;
+    }
+
+  if (GTK_ORIENTATION_VERTICAL == orientation)
+    {
+      item_area.width = allocation->width - 2 * border_width;
+      n_columns = MAX (item_area.width / item_size.width, 1);
+      n_rows = (n_visible_items + n_columns - 1) / n_columns;
+    }
+  else if (inquery)
+    {
+      item_area.height = allocation->height - 2 * border_width;
+      n_rows = MAX (item_area.height / item_size.height, 1);
+      n_columns = (n_visible_items + n_rows - 1) / n_rows;
+    }
+  else
+    {
+      item_area.width = allocation->width - 2 * border_width;
+
+      if (child_requisition.width > 0)
+        item_area.width -= child_requisition.width;
+
+      n_columns = MAX (item_area.width / item_size.width, 1);
+      n_rows = (n_visible_items + n_columns - 1) / n_columns;
+    }
+
+  item_area.width = item_size.width * n_columns;
+  item_area.height = item_size.height * n_rows;
+
+  /* place the header widget */
+
+  child_allocation.x = border_width;
+  child_allocation.y = border_width;
+
+  if (GTK_WIDGET_VISIBLE (group->priv->header))
+    {
+      if (GTK_ORIENTATION_VERTICAL == orientation)
+        {
+          child_allocation.width = allocation->width;
+          child_allocation.height = child_requisition.height;
+        }
+      else
+        {
+          child_allocation.width = child_requisition.width;
+	  header_width = child_allocation.width;
+          child_allocation.height = allocation->height;
+
+	  if (GTK_TEXT_DIR_RTL == direction)
+            child_allocation.x = allocation->width - border_width - header_width;
+        }
+
+      if (!inquery)
+        gtk_widget_size_allocate (group->priv->header, &child_allocation);
+
+      if (GTK_ORIENTATION_VERTICAL == orientation)
+        child_allocation.y += child_allocation.height;
+      else if (GTK_TEXT_DIR_RTL != direction)
+        child_allocation.x += child_allocation.width;
+      else
+        child_allocation.x = border_width;
+    }
+
+  item_area.x = child_allocation.x;
+  item_area.y = child_allocation.y;
+
+  if (!inquery)
+    {
+      if (GTK_ORIENTATION_VERTICAL == orientation)
+        {
+          item_area.width = allocation->width - 2 * border_width - header_width;
+          item_size.width = item_area.width / n_columns;
+        }
+      else
+        {
+          item_area.height = allocation->height - 2 * border_width;
+          item_size.height = item_area.height / n_rows;
+        }
+    }
+
+  /* otherwise, when expanded or in transition, place the tool items */
+  if (!group->priv->collapsed || group->priv->animation_timeout)
+    {
+      guint col = 0, row = 0;
+
+      for (it = group->priv->children; it != NULL; it = it->next)
+        {
+          EggToolItemGroupChild *child = it->data;
+
+          if (!egg_tool_item_group_is_item_visible (child->item, orientation))
+            {
+              if (!inquery)
+                gtk_widget_set_child_visible (GTK_WIDGET (child->item), FALSE);
+
+              continue;
+            }
+
+          child_requisition.width = 0;
+	  if (!child->homogeneous)
+            {
+              if (GTK_ORIENTATION_HORIZONTAL == orientation && GTK_TOOLBAR_TEXT == style)
+                {
+                  /* in horizontal text mode non homogneneous items are not supported */
+                  gtk_widget_set_child_visible (GTK_WIDGET (child->item), FALSE);
+                  continue;
+                }
+
+              gtk_widget_size_request (GTK_WIDGET (child->item), &child_requisition);
+              child_requisition.width = MIN (child_requisition.width, item_area.width);
+            }
+
+          if (col > 0 && (child->new_row || (col * item_size.width) + MAX (child_requisition.width, item_size.width) > item_area.width))
+            {
+              row++;
+              col = 0;
+              child_allocation.y += child_allocation.height;
+            }
+
+          if (!child->homogeneous)
+            {
+              guint col_width;
+              guint width;
+
+              if (child->expand)
+                col_width = n_columns - col;
+              else
+                col_width = (guint) ceil (1.0 * child_requisition.width / item_size.width);
+
+              width = col_width * item_size.width;
+
+              if (child->fill)
+                {
+                  child_allocation.x = item_area.x + 
+			  (((GTK_TEXT_DIR_RTL == direction) ? (n_columns - col - col_width) : col) * item_size.width);
+                  child_allocation.width = width;
+                }
+              else
+                {
+                  child_allocation.x = item_area.x + 
+			  (((GTK_TEXT_DIR_RTL == direction) ? (n_columns - col - col_width) : col) * item_size.width) + 
+			  (width - child_requisition.width) / 2;
+                  child_allocation.width = child_requisition.width;
+                }
+
+              col += col_width;
+            }
+          else
+            {
+              child_allocation.x = item_area.x + 
+		      (((GTK_TEXT_DIR_RTL == direction) ? (n_columns - col - 1) : col) * item_size.width);
+              child_allocation.width = item_size.width;
+
+              col++;
+            }
+
+          child_allocation.height = item_size.height;
+
+          if (!inquery)
+            {
+              gtk_widget_size_allocate (GTK_WIDGET (child->item), &child_allocation);
+              gtk_widget_set_child_visible (GTK_WIDGET (child->item), TRUE);
+            }
+        }
+
+      child_allocation.y += item_size.height;
+    }
+
+  /* or just hide all items, when collapsed */
+
+  else if (!inquery)
+    {
+      for (it = group->priv->children; it != NULL; it = it->next)
+        {
+          EggToolItemGroupChild *child = it->data;
+
+          gtk_widget_set_child_visible (GTK_WIDGET (child->item), FALSE);
+        }
+    }
+
+  /* report effective widget size */
+
+  if (inquery)
+    {
+      inquery->width = item_area.width + header_width + 2 * border_width;
+      inquery->height = child_allocation.y + border_width;
+    }
+}
+
+static void
+egg_tool_item_group_size_allocate (GtkWidget     *widget,
+                                   GtkAllocation *allocation)
+{
+  egg_tool_item_group_real_size_allocate (widget, allocation, NULL);
+
+  if (GTK_WIDGET_MAPPED (widget))
+    gdk_window_invalidate_rect (widget->window, NULL, FALSE);
+}
+
+static void
+egg_tool_item_group_realize (GtkWidget *widget)
+{
+  const gint border_width = GTK_CONTAINER (widget)->border_width;
+  gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+  GdkWindowAttr attributes;
+  GdkDisplay *display;
+
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.x = widget->allocation.x + border_width;
+  attributes.y = widget->allocation.y + border_width;
+  attributes.width = widget->allocation.width - border_width * 2;
+  attributes.height = widget->allocation.height - border_width * 2;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = gtk_widget_get_colormap (widget);
+  attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK
+                        | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+                        | GDK_BUTTON_MOTION_MASK;
+
+  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                   &attributes, attributes_mask);
+
+  display = gdk_drawable_get_display (widget->window);
+
+  if (gdk_display_supports_composite (display))
+    gdk_window_set_composited (widget->window, TRUE);
+
+  gdk_window_set_user_data (widget->window, widget);
+  widget->style = gtk_style_attach (widget->style, widget->window);
+  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+  gtk_container_forall (GTK_CONTAINER (widget),
+                        (GtkCallback) gtk_widget_set_parent_window,
+                        widget->window);
+
+  gtk_widget_queue_resize_no_redraw (widget);
+}
+
+static void
+egg_tool_item_group_style_set (GtkWidget *widget,
+                               GtkStyle  *previous_style)
+{
+  egg_tool_item_group_header_adjust_style (EGG_TOOL_ITEM_GROUP (widget));
+  GTK_WIDGET_CLASS (egg_tool_item_group_parent_class)->style_set (widget, previous_style);
+}
+
+static void
+egg_tool_item_group_add (GtkContainer *container,
+                         GtkWidget    *widget)
+{
+  g_return_if_fail (EGG_IS_TOOL_ITEM_GROUP (container));
+  g_return_if_fail (GTK_IS_TOOL_ITEM (widget));
+
+  egg_tool_item_group_insert (EGG_TOOL_ITEM_GROUP (container),
+                              GTK_TOOL_ITEM (widget), -1);
+}
+
+static void
+egg_tool_item_group_remove (GtkContainer *container,
+                            GtkWidget    *child)
+{
+  EggToolItemGroup *group;
+  GList *it;
+
+  g_return_if_fail (EGG_IS_TOOL_ITEM_GROUP (container));
+  group = EGG_TOOL_ITEM_GROUP (container);
+
+  for (it = group->priv->children; it != NULL; it = it->next)
+    {
+      EggToolItemGroupChild *child_info = it->data;
+
+      if ((GtkWidget *)child_info->item == child)
+        {
+          g_object_unref (child);
+          gtk_widget_unparent (child);
+
+          g_free (child_info);
+          group->priv->children = g_list_delete_link (group->priv->children, it);
+
+          gtk_widget_queue_resize (GTK_WIDGET (container));
+          break;
+        }
+    }
+}
+
+static void
+egg_tool_item_group_forall (GtkContainer *container,
+                            gboolean      internals,
+                            GtkCallback   callback,
+                            gpointer      callback_data)
+{
+  EggToolItemGroup *group = EGG_TOOL_ITEM_GROUP (container);
+  GList *children;
+
+  if (internals && group->priv->header)
+    callback (group->priv->header, callback_data);
+
+  children = group->priv->children;
+  while (children)
+    {
+      EggToolItemGroupChild *child = children->data;
+      children = children->next; /* store pointer before call to callback
+				    because the child pointer is invalid if the
+				    child->item is removed from the item group 
+				    in callback */
+
+      callback (GTK_WIDGET (child->item), callback_data);
+    }
+}
+
+static GType
+egg_tool_item_group_child_type (GtkContainer *container G_GNUC_UNUSED)
+{
+  return GTK_TYPE_TOOL_ITEM;
+}
+
+static EggToolItemGroupChild *
+egg_tool_item_group_get_child (EggToolItemGroup  *group,
+                               GtkToolItem       *item,
+                               gint              *position,
+                               GList            **link)
+{
+  guint i;
+  GList *it;
+
+  g_return_val_if_fail (EGG_IS_TOOL_ITEM_GROUP (group), NULL);
+  g_return_val_if_fail (GTK_IS_TOOL_ITEM (item), NULL);
+
+  for (it = group->priv->children, i = 0; it != NULL; it = it->next, ++i)
+    {
+      EggToolItemGroupChild *child = it->data;
+
+      if (child->item == item)
+        {
+          if (position)
+            *position = i;
+
+          if (link)
+            *link = it;
+
+          return child;
+        }
+    }
+
+  return NULL;
+}
+
+static void
+egg_tool_item_group_get_item_packing (EggToolItemGroup *group,
+                                      GtkToolItem      *item,
+                                      gboolean         *homogeneous,
+                                      gboolean         *expand,
+                                      gboolean         *fill,
+                                      gboolean         *new_row)
+{
+  EggToolItemGroupChild *child;
+
+  g_return_if_fail (EGG_IS_TOOL_ITEM_GROUP (group));
+  g_return_if_fail (GTK_IS_TOOL_ITEM (item));
+
+  child = egg_tool_item_group_get_child (group, item, NULL, NULL);
+  if (!child)
+    return;
+
+  if (expand)
+    *expand = child->expand;
+
+  if (homogeneous)
+    *homogeneous = child->homogeneous;
+
+  if (fill)
+    *fill = child->fill;
+
+  if (new_row)
+    *new_row = child->new_row;
+}
+
+static void
+egg_tool_item_group_set_item_packing (EggToolItemGroup *group,
+                                      GtkToolItem      *item,
+                                      gboolean          homogeneous,
+                                      gboolean          expand,
+                                      gboolean          fill,
+                                      gboolean          new_row)
+{
+  EggToolItemGroupChild *child;
+  gboolean changed = FALSE;
+
+  g_return_if_fail (EGG_IS_TOOL_ITEM_GROUP (group));
+  g_return_if_fail (GTK_IS_TOOL_ITEM (item));
+
+  child = egg_tool_item_group_get_child (group, item, NULL, NULL);
+  if (!child)
+    return;
+
+  gtk_widget_freeze_child_notify (GTK_WIDGET (item));
+
+  if (child->homogeneous != homogeneous)
+    {
+      child->homogeneous = homogeneous;
+      changed = TRUE;
+      gtk_widget_child_notify (GTK_WIDGET (item), "homogeneous");
+    }
+  if (child->expand != expand)
+    {
+      child->expand = expand;
+      changed = TRUE;
+      gtk_widget_child_notify (GTK_WIDGET (item), "expand");
+    }
+  if (child->fill != fill)
+    {
+      child->fill = fill;
+      changed = TRUE;
+      gtk_widget_child_notify (GTK_WIDGET (item), "fill");
+    }
+  if (child->new_row != new_row)
+    {
+      child->new_row = new_row;
+      changed = TRUE;
+      gtk_widget_child_notify (GTK_WIDGET (item), "new-row");
+    }
+
+  gtk_widget_thaw_child_notify (GTK_WIDGET (item));
+
+  if (changed && GTK_WIDGET_VISIBLE (group) && GTK_WIDGET_VISIBLE (item))
+    gtk_widget_queue_resize (GTK_WIDGET (group));
+}
+
+static void
+egg_tool_item_group_set_child_property (GtkContainer *container,
+                                        GtkWidget    *child,
+                                        guint         prop_id,
+                                        const GValue *value,
+                                        GParamSpec   *pspec)
+{
+  EggToolItemGroup *group = EGG_TOOL_ITEM_GROUP (container);
+  GtkToolItem *item = GTK_TOOL_ITEM (child);
+  gboolean homogeneous, expand, fill, new_row;
+
+  if (prop_id != CHILD_PROP_POSITION)
+    egg_tool_item_group_get_item_packing (group, item,
+                                          &homogeneous,
+                                          &expand,
+                                          &fill,
+                                          &new_row);
+
+  switch (prop_id)
+    {
+      case CHILD_PROP_HOMOGENEOUS:
+        egg_tool_item_group_set_item_packing (group, item,
+                                              g_value_get_boolean (value),
+                                              expand,
+                                              fill,
+                                              new_row);
+        break;
+
+      case CHILD_PROP_EXPAND:
+        egg_tool_item_group_set_item_packing (group, item,
+                                              homogeneous,
+                                              g_value_get_boolean (value),
+                                              fill,
+                                              new_row);
+        break;
+
+      case CHILD_PROP_FILL:
+        egg_tool_item_group_set_item_packing (group, item,
+                                              homogeneous,
+                                              expand,
+                                              g_value_get_boolean (value),
+                                              new_row);
+        break;
+
+      case CHILD_PROP_NEW_ROW:
+        egg_tool_item_group_set_item_packing (group, item,
+                                              homogeneous,
+                                              expand,
+                                              fill,
+                                              g_value_get_boolean (value));
+        break;
+
+      case CHILD_PROP_POSITION:
+        egg_tool_item_group_set_item_position (group, item, g_value_get_int (value));
+        break;
+
+      default:
+        GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+egg_tool_item_group_get_child_property (GtkContainer *container,
+                                        GtkWidget    *child,
+                                        guint         prop_id,
+                                        GValue       *value,
+                                        GParamSpec   *pspec)
+{
+  EggToolItemGroup *group = EGG_TOOL_ITEM_GROUP (container);
+  GtkToolItem *item = GTK_TOOL_ITEM (child);
+  gboolean homogeneous, expand, fill, new_row;
+
+  if (prop_id != CHILD_PROP_POSITION)
+    egg_tool_item_group_get_item_packing (group, item,
+                                          &homogeneous,
+                                          &expand,
+                                          &fill,
+                                          &new_row);
+
+  switch (prop_id)
+    {
+      case CHILD_PROP_HOMOGENEOUS:
+        g_value_set_boolean (value, homogeneous);
+        break;
+
+       case CHILD_PROP_EXPAND:
+        g_value_set_boolean (value, expand);
+        break;
+
+       case CHILD_PROP_FILL:
+        g_value_set_boolean (value, fill);
+        break;
+
+       case CHILD_PROP_NEW_ROW:
+        g_value_set_boolean (value, new_row);
+        break;
+
+     case CHILD_PROP_POSITION:
+        g_value_set_int (value, egg_tool_item_group_get_item_position (group, item));
+        break;
+
+      default:
+        GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+egg_tool_item_group_class_init (EggToolItemGroupClass *cls)
+{
+  GObjectClass       *oclass = G_OBJECT_CLASS (cls);
+  GtkWidgetClass     *wclass = GTK_WIDGET_CLASS (cls);
+  GtkContainerClass  *cclass = GTK_CONTAINER_CLASS (cls);
+
+  oclass->set_property       = egg_tool_item_group_set_property;
+  oclass->get_property       = egg_tool_item_group_get_property;
+  oclass->finalize           = egg_tool_item_group_finalize;
+
+  wclass->size_request       = egg_tool_item_group_size_request;
+  wclass->size_allocate      = egg_tool_item_group_size_allocate;
+  wclass->realize            = egg_tool_item_group_realize;
+  wclass->style_set          = egg_tool_item_group_style_set;
+
+  cclass->add                = egg_tool_item_group_add;
+  cclass->remove             = egg_tool_item_group_remove;
+  cclass->forall             = egg_tool_item_group_forall;
+  cclass->child_type         = egg_tool_item_group_child_type;
+  cclass->set_child_property = egg_tool_item_group_set_child_property;
+  cclass->get_child_property = egg_tool_item_group_get_child_property;
+
+  g_object_class_install_property (oclass, PROP_NAME,
+                                   g_param_spec_string ("name",
+                                                        P_("Name"),
+                                                        P_("The name of this item group"),
+                                                        DEFAULT_NAME,
+                                                        G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  g_object_class_install_property (oclass, PROP_COLLAPSED,
+                                   g_param_spec_boolean ("collapsed",
+                                                         P_("Collapsed"),
+                                                         P_("Wether the group has been collapsed and items are hidden"),
+                                                         DEFAULT_COLLAPSED,
+                                                         G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
+                                                         G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  g_object_class_install_property (oclass, PROP_ELLIPSIZE,
+                                   g_param_spec_enum ("ellipsize",
+                                                      P_("ellipsize"),
+                                                      P_("Ellipsize for item group headers"),
+                                                      PANGO_TYPE_ELLIPSIZE_MODE, DEFAULT_ELLIPSIZE,
+                                                      G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
+                                                      G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  gtk_widget_class_install_style_property (wclass,
+                                           g_param_spec_int ("expander-size",
+                                                             P_("Expander Size"),
+                                                             P_("Size of the expander arrow"),
+                                                             0,
+                                                             G_MAXINT,
+                                                             DEFAULT_EXPANDER_SIZE,
+                                                             G_PARAM_READABLE | G_PARAM_STATIC_NAME |
+                                                             G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  gtk_widget_class_install_style_property (wclass,
+                                           g_param_spec_int ("header-spacing",
+                                                             P_("Header Spacing"),
+                                                             P_("Spacing between expander arrow and caption"),
+                                                             0,
+                                                             G_MAXINT,
+                                                             DEFAULT_HEADER_SPACING,
+                                                             G_PARAM_READABLE | G_PARAM_STATIC_NAME |
+                                                             G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  gtk_container_class_install_child_property (cclass, CHILD_PROP_HOMOGENEOUS,
+                                              g_param_spec_boolean ("homogeneous",
+                                                                    P_("Homogeneous"),
+                                                                    P_("Whether the item should be the same size as other homogeneous items"),
+                                                                    TRUE,
+                                                                    G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
+                                                                    G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  gtk_container_class_install_child_property (cclass, CHILD_PROP_EXPAND,
+                                              g_param_spec_boolean ("expand",
+                                                                    P_("Expand"),
+                                                                    P_("Whether the item should receive extra space when the toolbar grows"),
+                                                                    FALSE,
+                                                                    G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
+                                                                    G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  gtk_container_class_install_child_property (cclass, CHILD_PROP_FILL,
+                                              g_param_spec_boolean ("fill",
+                                                                    P_("Fill"),
+                                                                    P_("Whether the item should fill the avaiable space"),
+                                                                    TRUE,
+                                                                    G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
+                                                                    G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  gtk_container_class_install_child_property (cclass, CHILD_PROP_NEW_ROW,
+                                              g_param_spec_boolean ("new-row",
+                                                                    P_("New Row"),
+                                                                    P_("Whether the item should start a new row"),
+                                                                    FALSE,
+                                                                    G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
+                                                                    G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  gtk_container_class_install_child_property (cclass, CHILD_PROP_POSITION,
+                                              g_param_spec_int ("position",
+                                                                P_("Position"),
+                                                                P_("Position of the item within this group"),
+                                                                0,
+                                                                G_MAXINT,
+                                                                0,
+                                                                G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
+                                                                G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  g_type_class_add_private (cls, sizeof (EggToolItemGroupPrivate));
+}
+
+GtkWidget*
+egg_tool_item_group_new (const gchar *name)
+{
+  return g_object_new (EGG_TYPE_TOOL_ITEM_GROUP, "name", name, NULL);
+}
+
+void
+egg_tool_item_group_set_name (EggToolItemGroup *group,
+                              const gchar      *name)
+{
+  const gchar *current_name;
+  GtkWidget *label;
+
+  g_return_if_fail (EGG_IS_TOOL_ITEM_GROUP (group));
+  current_name = egg_tool_item_group_get_name (group);
+
+  if (current_name != name && (!current_name || !name || strcmp (current_name, name)))
+    {
+      label = egg_tool_item_group_get_label (group);
+      gtk_label_set_text (GTK_LABEL (label), name);
+
+      if (name && group->priv->children)
+        gtk_widget_show (group->priv->header);
+      else
+        gtk_widget_hide (group->priv->header);
+
+      g_object_notify (G_OBJECT (group), "name");
+    }
+}
+
+static gint64
+egg_tool_item_group_get_animation_timestamp (EggToolItemGroup *group)
+{
+  GTimeVal now;
+  g_source_get_current_time (group->priv->animation_timeout, &now);
+  return (now.tv_sec * G_USEC_PER_SEC + now.tv_usec - group->priv->animation_start) / 1000;
+}
+
+static gboolean
+egg_tool_item_group_animation_cb (gpointer data)
+{
+  EggToolItemGroup *group = EGG_TOOL_ITEM_GROUP (data);
+  gint64 timestamp = egg_tool_item_group_get_animation_timestamp (group);
+
+  /* enque this early to reduce number of expose events */
+  gtk_widget_queue_resize_no_redraw (GTK_WIDGET (group));
+
+  if (GTK_WIDGET_REALIZED (group->priv->header))
+    {
+      GtkWidget *alignment = egg_tool_item_group_get_alignment (group);
+      GdkRectangle area;
+
+      area.x = alignment->allocation.x;
+      area.y = alignment->allocation.y + (alignment->allocation.height - group->priv->expander_size) / 2;
+      area.height = group->priv->expander_size;
+      area.width = group->priv->expander_size;
+
+      gdk_window_invalidate_rect (group->priv->header->window, &area, TRUE);
+    }
+
+  if (GTK_WIDGET_REALIZED (group))
+    {
+      GtkWidget *widget = GTK_WIDGET (group);
+      GtkWidget *parent = gtk_widget_get_parent (widget);
+
+      gint width = widget->allocation.width;
+      gint height = widget->allocation.height;
+      gint x, y;
+
+      gtk_widget_translate_coordinates (widget, parent, 0, 0, &x, &y);
+
+      if (GTK_WIDGET_VISIBLE (group->priv->header))
+        {
+          height -= group->priv->header->allocation.height;
+          y += group->priv->header->allocation.height;
+        }
+
+      gtk_widget_queue_draw_area (parent, x, y, width, height);
+    }
+
+  if (group->priv->collapsed)
+    {
+      if (group->priv->expander_style == GTK_EXPANDER_EXPANDED)
+        group->priv->expander_style = GTK_EXPANDER_SEMI_COLLAPSED;
+      else
+        group->priv->expander_style = GTK_EXPANDER_COLLAPSED;
+    }
+  else
+    {
+      if (group->priv->expander_style == GTK_EXPANDER_COLLAPSED)
+        group->priv->expander_style = GTK_EXPANDER_SEMI_EXPANDED;
+      else
+        group->priv->expander_style = GTK_EXPANDER_EXPANDED;
+    }
+
+  if (timestamp >= ANIMATION_DURATION)
+    group->priv->animation_timeout = NULL;
+
+  /* Ensure that all composited windows and child windows are repainted, before
+   * the parent widget gets its expose-event. This is needed to avoid heavy
+   * rendering artifacts. GTK+ should take care about this issue by itself I
+   * guess, but currently it doesn't. Also I don't understand the parameters
+   * of this issue well enough yet, to file a bug report.
+   */
+  gdk_window_process_updates (GTK_WIDGET (group)->window, TRUE);
+
+  return (group->priv->animation_timeout != NULL);
+}
+
+void
+egg_tool_item_group_set_collapsed (EggToolItemGroup *group,
+                                   gboolean          collapsed)
+{
+  g_return_if_fail (EGG_IS_TOOL_ITEM_GROUP (group));
+
+  if (collapsed != group->priv->collapsed)
+    {
+      GTimeVal now;
+      GtkWidget *parent;
+
+      g_get_current_time (&now);
+
+      if (group->priv->animation_timeout)
+        g_source_destroy (group->priv->animation_timeout);
+
+      group->priv->collapsed = collapsed;
+      group->priv->animation_start = (now.tv_sec * G_USEC_PER_SEC + now.tv_usec);
+      group->priv->animation_timeout = g_timeout_source_new (ANIMATION_TIMEOUT);
+
+      parent = gtk_widget_get_parent (GTK_WIDGET (group));
+      if (EGG_IS_TOOL_PALETTE (parent) && !collapsed)
+        _egg_tool_palette_set_expanding_child (EGG_TOOL_PALETTE (parent), GTK_WIDGET (group));
+
+      g_source_set_callback (group->priv->animation_timeout,
+                             egg_tool_item_group_animation_cb,
+                             group, NULL);
+
+      g_source_attach (group->priv->animation_timeout, NULL);
+      g_object_notify (G_OBJECT (group), "collapsed");
+    }
+}
+
+void
+egg_tool_item_group_set_ellipsize (EggToolItemGroup   *group,
+                                   PangoEllipsizeMode  ellipsize)
+{
+  g_return_if_fail (EGG_IS_TOOL_ITEM_GROUP (group));
+
+  if (ellipsize != group->priv->ellipsize)
+    {
+      group->priv->ellipsize = ellipsize;
+      egg_tool_item_group_header_adjust_style (group);
+      g_object_notify (G_OBJECT (group), "ellipsize");
+#ifdef HAVE_EXTENDED_TOOL_SHELL_SUPPORT_BUG_535090
+      _egg_tool_item_group_palette_reconfigured (group);
+#endif
+    }
+}
+
+G_CONST_RETURN gchar*
+egg_tool_item_group_get_name (EggToolItemGroup *group)
+{
+  GtkWidget *label;
+
+  g_return_val_if_fail (EGG_IS_TOOL_ITEM_GROUP (group), DEFAULT_NAME);
+
+  label = egg_tool_item_group_get_label (group);
+  return gtk_label_get_text (GTK_LABEL (label));
+}
+
+gboolean
+egg_tool_item_group_get_collapsed (EggToolItemGroup *group)
+{
+  g_return_val_if_fail (EGG_IS_TOOL_ITEM_GROUP (group), DEFAULT_COLLAPSED);
+  return group->priv->collapsed;
+}
+
+PangoEllipsizeMode
+egg_tool_item_group_get_ellipsize (EggToolItemGroup *group)
+{
+  g_return_val_if_fail (EGG_IS_TOOL_ITEM_GROUP (group), DEFAULT_ELLIPSIZE);
+  return group->priv->ellipsize;
+}
+
+void
+egg_tool_item_group_insert (EggToolItemGroup *group,
+                            GtkToolItem      *item,
+                            gint              position)
+{
+  GtkWidget *parent;
+  EggToolItemGroupChild *child;
+
+  g_return_if_fail (EGG_IS_TOOL_ITEM_GROUP (group));
+  g_return_if_fail (GTK_IS_TOOL_ITEM (item));
+  g_return_if_fail (position >= -1);
+
+  parent = gtk_widget_get_parent (GTK_WIDGET (group));
+
+  child = g_new (EggToolItemGroupChild, 1);
+  child->item = g_object_ref_sink (item);
+  child->homogeneous = TRUE;
+  child->expand = FALSE;
+  child->fill = TRUE;
+  child->new_row = FALSE;
+
+  group->priv->children = g_list_insert (group->priv->children, child, position);
+
+  if (EGG_IS_TOOL_PALETTE (parent))
+    _egg_tool_palette_child_set_drag_source (GTK_WIDGET (item), parent);
+
+  gtk_widget_set_parent (GTK_WIDGET (item), GTK_WIDGET (group));
+}
+
+void
+egg_tool_item_group_set_item_position (EggToolItemGroup *group,
+                                       GtkToolItem      *item,
+                                       gint              position)
+{
+  gint old_position;
+  GList *link;
+  EggToolItemGroupChild *child;
+
+  g_return_if_fail (EGG_IS_TOOL_ITEM_GROUP (group));
+  g_return_if_fail (GTK_IS_TOOL_ITEM (item));
+
+  g_return_if_fail (position >= -1);
+
+  child = egg_tool_item_group_get_child (group, item, &old_position, &link);
+
+  g_return_if_fail (child != NULL);
+
+  if (position == old_position)
+    return;
+
+  group->priv->children = g_list_delete_link (group->priv->children, link);
+  group->priv->children = g_list_insert (group->priv->children, child, position);
+
+  gtk_widget_child_notify (GTK_WIDGET (item), "position");
+  if (GTK_WIDGET_VISIBLE (group) && GTK_WIDGET_VISIBLE (item))
+    gtk_widget_queue_resize (GTK_WIDGET (group));
+}
+
+gint
+egg_tool_item_group_get_item_position (EggToolItemGroup *group,
+                                       GtkToolItem      *item)
+{
+  gint position;
+
+  g_return_val_if_fail (EGG_IS_TOOL_ITEM_GROUP (group), -1);
+  g_return_val_if_fail (GTK_IS_TOOL_ITEM (item), -1);
+
+  if (egg_tool_item_group_get_child (group, item, &position, NULL))
+    return position;
+
+  return -1;
+}
+
+guint
+egg_tool_item_group_get_n_items (EggToolItemGroup *group)
+{
+  g_return_val_if_fail (EGG_IS_TOOL_ITEM_GROUP (group), 0);
+
+  return g_list_length (group->priv->children);
+}
+
+GtkToolItem*
+egg_tool_item_group_get_nth_item (EggToolItemGroup *group,
+                                  guint             index)
+{
+  EggToolItemGroupChild *child;
+
+  g_return_val_if_fail (EGG_IS_TOOL_ITEM_GROUP (group), NULL);
+
+  child = g_list_nth_data (group->priv->children, index);
+
+  return child != NULL ? child->item : NULL;
+}
+
+GtkToolItem*
+egg_tool_item_group_get_drop_item (EggToolItemGroup *group,
+                                   gint              x,
+                                   gint              y)
+{
+  GtkAllocation *allocation;
+  GtkOrientation orientation;
+  GList *it;
+
+  g_return_val_if_fail (EGG_IS_TOOL_ITEM_GROUP (group), NULL);
+
+  allocation = &GTK_WIDGET (group)->allocation;
+  orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
+
+  g_return_val_if_fail (x >= 0 && x < allocation->width, NULL);
+  g_return_val_if_fail (y >= 0 && y < allocation->height, NULL);
+
+  for (it = group->priv->children; it != NULL; it = it->next)
+    {
+      EggToolItemGroupChild *child = it->data;
+      GtkToolItem *item = child->item;
+      gint x0, y0;
+
+      if (!item || !egg_tool_item_group_is_item_visible (item, orientation))
+        continue;
+
+      allocation = &GTK_WIDGET (item)->allocation;
+
+      x0 = x - allocation->x;
+      y0 = y - allocation->y;
+
+      if (x0 >= 0 && x0 < allocation->width &&
+          y0 >= 0 && y0 < allocation->height)
+        return item;
+    }
+
+  return NULL;
+}
+
+void
+_egg_tool_item_group_item_size_request (EggToolItemGroup *group,
+                                        GtkRequisition   *item_size)
+{
+  GtkRequisition child_requisition;
+  GList *it;
+
+  g_return_if_fail (EGG_IS_TOOL_ITEM_GROUP (group));
+  g_return_if_fail (NULL != item_size);
+
+  item_size->width = item_size->height = 0;
+
+  for (it = group->priv->children; it != NULL; it = it->next)
+    {
+      EggToolItemGroupChild *child = it->data;
+
+      gtk_widget_size_request (GTK_WIDGET (child->item), &child_requisition);
+
+      if (child->homogeneous)
+        item_size->width = MAX (item_size->width, child_requisition.width);
+      item_size->height = MAX (item_size->height, child_requisition.height);
+    }
+}
+
+void
+_egg_tool_item_group_paint (EggToolItemGroup *group,
+                            cairo_t          *cr)
+{
+  GtkWidget *widget = GTK_WIDGET (group);
+
+  gdk_cairo_set_source_pixmap (cr, widget->window,
+                               widget->allocation.x,
+                               widget->allocation.y);
+
+  if (group->priv->animation_timeout)
+    {
+      GtkOrientation orientation = egg_tool_item_group_get_orientation (GTK_TOOL_SHELL (group));
+      cairo_pattern_t *mask;
+      gdouble v0, v1;
+
+      if (GTK_ORIENTATION_VERTICAL == orientation)
+        v1 = widget->allocation.height;
+      else
+        v1 = widget->allocation.width;
+
+      v0 = v1 - 256;
+
+      if (!GTK_WIDGET_VISIBLE (group->priv->header))
+        v0 = MAX (v0, 0);
+      else if (GTK_ORIENTATION_VERTICAL == orientation)
+        v0 = MAX (v0, group->priv->header->allocation.height);
+      else
+        v0 = MAX (v0, group->priv->header->allocation.width);
+
+      v1 = MIN (v0 + 256, v1);
+
+      if (GTK_ORIENTATION_VERTICAL == orientation)
+        {
+          v0 += widget->allocation.y;
+          v1 += widget->allocation.y;
+
+          mask = cairo_pattern_create_linear (0.0, v0, 0.0, v1);
+        }
+      else
+        {
+          v0 += widget->allocation.x;
+          v1 += widget->allocation.x;
+
+          mask = cairo_pattern_create_linear (v0, 0.0, v1, 0.0);
+        }
+
+      cairo_pattern_add_color_stop_rgba (mask, 0.00, 0.0, 0.0, 0.0, 1.00);
+      cairo_pattern_add_color_stop_rgba (mask, 0.25, 0.0, 0.0, 0.0, 0.25);
+      cairo_pattern_add_color_stop_rgba (mask, 0.50, 0.0, 0.0, 0.0, 0.10);
+      cairo_pattern_add_color_stop_rgba (mask, 0.75, 0.0, 0.0, 0.0, 0.01);
+      cairo_pattern_add_color_stop_rgba (mask, 1.00, 0.0, 0.0, 0.0, 0.00);
+
+      cairo_mask (cr, mask);
+      cairo_pattern_destroy (mask);
+    }
+  else
+    cairo_paint (cr);
+}
+
+gint
+_egg_tool_item_group_get_size_for_limit (EggToolItemGroup *group,
+                                         gint              limit,
+                                         gboolean          vertical,
+                                         gboolean          animation)
+{
+  GtkRequisition requisition;
+
+  gtk_widget_size_request (GTK_WIDGET (group), &requisition);
+
+  if (!group->priv->collapsed || group->priv->animation_timeout)
+    {
+      GtkAllocation allocation = { 0, 0, requisition.width, requisition.height };
+      GtkRequisition inquery;
+
+      if (vertical)
+        allocation.width = limit;
+      else
+        allocation.height = limit;
+
+      egg_tool_item_group_real_size_allocate (GTK_WIDGET (group),
+                                              &allocation, &inquery);
+
+      if (vertical)
+        inquery.height -= requisition.height;
+      else
+        inquery.width -= requisition.width;
+
+      if (group->priv->animation_timeout && animation)
+        {
+          gint64 timestamp = egg_tool_item_group_get_animation_timestamp (group);
+
+          timestamp = MIN (timestamp, ANIMATION_DURATION);
+
+          if (group->priv->collapsed)
+            timestamp = ANIMATION_DURATION - timestamp;
+
+          if (vertical)
+            {
+              inquery.height *= timestamp;
+              inquery.height /= ANIMATION_DURATION;
+            }
+          else
+            {
+              inquery.width *= timestamp;
+              inquery.width /= ANIMATION_DURATION;
+            }
+        }
+
+      if (vertical)
+        requisition.height += inquery.height;
+      else
+        requisition.width += inquery.width;
+    }
+
+  return (vertical ? requisition.height : requisition.width);
+}
+
+gint
+_egg_tool_item_group_get_height_for_width (EggToolItemGroup *group,
+                                           gint              width)
+{
+  return _egg_tool_item_group_get_size_for_limit (group, width, TRUE, TRUE);
+}
+
+gint
+_egg_tool_item_group_get_width_for_height (EggToolItemGroup *group,
+                                           gint              height)
+{
+  return _egg_tool_item_group_get_size_for_limit (group, height, FALSE, TRUE);
+}
+
+#ifdef GTK_TYPE_TOOL_SHELL
+
+static void
+egg_tool_palette_reconfigured_foreach_item (GtkWidget *child,
+                                            gpointer   data G_GNUC_UNUSED)
+{
+  if (GTK_IS_TOOL_ITEM (child))
+    gtk_tool_item_toolbar_reconfigured (GTK_TOOL_ITEM (child));
+}
+
+#endif /* GTK_TYPE_TOOL_SHELL */
+
+void
+_egg_tool_item_group_palette_reconfigured (EggToolItemGroup *group)
+{
+#ifdef GTK_TYPE_TOOL_SHELL
+
+  gtk_container_foreach (GTK_CONTAINER (group),
+                         egg_tool_palette_reconfigured_foreach_item,
+                         NULL);
+
+#endif /* GTK_TYPE_TOOL_SHELL */
+
+  egg_tool_item_group_header_adjust_style (group);
+}

Added: trunk/glom/utility_widgets/egg/toolpalette/eggtoolitemgroup.h
==============================================================================
--- (empty file)
+++ trunk/glom/utility_widgets/egg/toolpalette/eggtoolitemgroup.h	Mon Jun 16 10:23:15 2008
@@ -0,0 +1,84 @@
+/* EggToolPalette -- A tool palette with categories and DnD support
+ * Copyright (C) 2008  Openismus GmbH
+ *
+ * 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.1 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
+ *
+ * Authors:
+ *      Mathias Hasselmann
+ */
+
+#ifndef __EGG_TOOL_ITEM_GROUP_H__
+#define __EGG_TOOL_ITEM_GROUP_H__
+
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtktoolitem.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_TOOL_ITEM_GROUP           (egg_tool_item_group_get_type())
+#define EGG_TOOL_ITEM_GROUP(obj)           (G_TYPE_CHECK_INSTANCE_CAST(obj, EGG_TYPE_TOOL_ITEM_GROUP, EggToolItemGroup))
+#define EGG_TOOL_ITEM_GROUP_CLASS(cls)     (G_TYPE_CHECK_CLASS_CAST(cls, EGG_TYPE_TOOL_ITEM_GROUP, EggToolItemGroupClass))
+#define EGG_IS_TOOL_ITEM_GROUP(obj)        (G_TYPE_CHECK_INSTANCE_TYPE(obj, EGG_TYPE_TOOL_ITEM_GROUP))
+#define EGG_IS_TOOL_ITEM_GROUP_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE(obj, EGG_TYPE_TOOL_ITEM_GROUP))
+#define EGG_TOOL_ITEM_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EGG_TYPE_TOOL_ITEM_GROUP, EggToolItemGroupClass))
+
+typedef struct _EggToolItemGroup        EggToolItemGroup;
+typedef struct _EggToolItemGroupClass   EggToolItemGroupClass;
+typedef struct _EggToolItemGroupPrivate EggToolItemGroupPrivate;
+
+struct _EggToolItemGroup
+{
+  GtkContainer parent_instance;
+  EggToolItemGroupPrivate *priv;
+};
+
+struct _EggToolItemGroupClass
+{
+  GtkContainerClass parent_class;
+};
+
+GType                 egg_tool_item_group_get_type          (void) G_GNUC_CONST;
+GtkWidget*            egg_tool_item_group_new               (const gchar        *name);
+
+void                  egg_tool_item_group_set_name          (EggToolItemGroup   *group,
+                                                             const gchar        *name);
+void                  egg_tool_item_group_set_collapsed      (EggToolItemGroup  *group,
+                                                             gboolean            collapsed);
+void                  egg_tool_item_group_set_ellipsize     (EggToolItemGroup   *group,
+                                                             PangoEllipsizeMode  ellipsize);
+
+G_CONST_RETURN gchar* egg_tool_item_group_get_name          (EggToolItemGroup   *group);
+gboolean              egg_tool_item_group_get_collapsed     (EggToolItemGroup   *group);
+PangoEllipsizeMode    egg_tool_item_group_get_ellipsize     (EggToolItemGroup   *group);
+
+void                  egg_tool_item_group_insert            (EggToolItemGroup   *group,
+                                                             GtkToolItem        *item,
+                                                             gint                position);
+void                  egg_tool_item_group_set_item_position (EggToolItemGroup   *group,
+                                                             GtkToolItem        *item,
+                                                             gint                position);
+gint                  egg_tool_item_group_get_item_position (EggToolItemGroup   *group,
+                                                             GtkToolItem        *item);
+
+guint                 egg_tool_item_group_get_n_items       (EggToolItemGroup   *group);
+GtkToolItem*          egg_tool_item_group_get_nth_item      (EggToolItemGroup   *group,
+                                                             guint               index);
+GtkToolItem*          egg_tool_item_group_get_drop_item     (EggToolItemGroup   *group,
+                                                             gint                x,
+                                                             gint                y);
+
+G_END_DECLS
+
+#endif /* __EGG_TOOL_ITEM_GROUP_H__ */ 

Added: trunk/glom/utility_widgets/egg/toolpalette/eggtoolpalette.c
==============================================================================
--- (empty file)
+++ trunk/glom/utility_widgets/egg/toolpalette/eggtoolpalette.c	Mon Jun 16 10:23:15 2008
@@ -0,0 +1,1356 @@
+/* EggToolPalette -- A tool palette with categories and DnD support
+ * Copyright (C) 2008  Openismus GmbH
+ *
+ * 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.1 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
+ *
+ * Authors:
+ *      Mathias Hasselmann
+ */
+
+#include "eggtoolpalette.h"
+#include "eggtoolpaletteprivate.h"
+#include "eggtoolitemgroup.h"
+#include "eggmarshalers.h"
+
+#include <gtk/gtk.h>
+#include <string.h>
+
+#define DEFAULT_ICON_SIZE       GTK_ICON_SIZE_SMALL_TOOLBAR
+#define DEFAULT_ORIENTATION     GTK_ORIENTATION_VERTICAL
+#define DEFAULT_TOOLBAR_STYLE   GTK_TOOLBAR_ICONS
+
+#define DEFAULT_CHILD_EXCLUSIVE FALSE
+#define DEFAULT_CHILD_EXPAND    FALSE
+
+#define P_(msgid) (msgid)
+
+typedef struct _EggToolItemGroupInfo   EggToolItemGroupInfo;
+typedef struct _EggToolPaletteDragData EggToolPaletteDragData;
+
+enum
+{
+  PROP_NONE,
+  PROP_ICON_SIZE,
+  PROP_ORIENTATION,
+  PROP_TOOLBAR_STYLE,
+};
+
+enum
+{
+  CHILD_PROP_NONE,
+  CHILD_PROP_EXCLUSIVE,
+  CHILD_PROP_EXPAND,
+};
+
+struct _EggToolItemGroupInfo
+{
+  EggToolItemGroup *widget;
+
+  guint             notify_collapsed;
+  guint             exclusive : 1;
+  guint             expand : 1;
+};
+
+struct _EggToolPalettePrivate
+{
+  EggToolItemGroupInfo *groups;
+  gsize                 groups_size;
+  gsize                 groups_length;
+
+  GtkAdjustment        *hadjustment;
+  GtkAdjustment        *vadjustment;
+
+  GtkRequisition        item_size;
+  GtkIconSize           icon_size;
+  GtkOrientation        orientation;
+  GtkToolbarStyle       style;
+
+  GtkWidget            *expanding_child;
+
+#ifdef HAVE_EXTENDED_TOOL_SHELL_SUPPORT_BUG_535090
+  GtkSizeGroup         *text_size_group;
+#endif
+
+  guint                 sparse_groups : 1;
+  guint                 drag_source : 1;
+};
+
+struct _EggToolPaletteDragData
+{
+  EggToolPalette *palette;
+  GtkWidget      *item;
+};
+
+static GdkAtom dnd_target_atom_item = GDK_NONE;
+static GdkAtom dnd_target_atom_group = GDK_NONE;
+
+static const GtkTargetEntry dnd_targets[] =
+{
+  { "application/x-egg-tool-palette-item", GTK_TARGET_SAME_APP, 0 },
+  { "application/x-egg-tool-palette-group", GTK_TARGET_SAME_APP, 0 },
+};
+
+G_DEFINE_TYPE (EggToolPalette,
+               egg_tool_palette,
+               GTK_TYPE_CONTAINER);
+
+static void
+egg_tool_palette_init (EggToolPalette *palette)
+{
+  palette->priv = G_TYPE_INSTANCE_GET_PRIVATE (palette,
+                                               EGG_TYPE_TOOL_PALETTE,
+                                               EggToolPalettePrivate);
+
+  palette->priv->groups_size = 4;
+  palette->priv->groups_length = 0;
+  palette->priv->groups = g_new0 (EggToolItemGroupInfo, palette->priv->groups_size);
+
+  palette->priv->icon_size = DEFAULT_ICON_SIZE;
+  palette->priv->orientation = DEFAULT_ORIENTATION;
+  palette->priv->style = DEFAULT_TOOLBAR_STYLE;
+
+#ifdef HAVE_EXTENDED_TOOL_SHELL_SUPPORT_BUG_535090
+  palette->priv->text_size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
+#endif
+}
+
+static void
+egg_tool_palette_reconfigured (EggToolPalette *palette)
+{
+  guint i;
+
+  for (i = 0; i < palette->priv->groups_length; ++i)
+    {
+      if (palette->priv->groups[i].widget)
+        _egg_tool_item_group_palette_reconfigured (palette->priv->groups[i].widget);
+    }
+
+  gtk_widget_queue_resize_no_redraw (GTK_WIDGET (palette));
+}
+
+static void
+egg_tool_palette_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  EggToolPalette *palette = EGG_TOOL_PALETTE (object);
+
+  switch (prop_id)
+    {
+      case PROP_ICON_SIZE:
+        if ((guint) g_value_get_enum (value) != palette->priv->icon_size)
+          {
+            palette->priv->icon_size = g_value_get_enum (value);
+            egg_tool_palette_reconfigured (palette);
+          }
+        break;
+
+      case PROP_ORIENTATION:
+        if ((guint) g_value_get_enum (value) != palette->priv->orientation)
+          {
+            palette->priv->orientation = g_value_get_enum (value);
+            egg_tool_palette_reconfigured (palette);
+          }
+        break;
+
+      case PROP_TOOLBAR_STYLE:
+        if ((guint) g_value_get_enum (value) != palette->priv->style)
+          {
+            palette->priv->style = g_value_get_enum (value);
+            egg_tool_palette_reconfigured (palette);
+          }
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+egg_tool_palette_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  EggToolPalette *palette = EGG_TOOL_PALETTE (object);
+
+  switch (prop_id)
+    {
+      case PROP_ICON_SIZE:
+        g_value_set_enum (value, egg_tool_palette_get_icon_size (palette));
+        break;
+
+      case PROP_ORIENTATION:
+        g_value_set_enum (value, egg_tool_palette_get_orientation (palette));
+        break;
+
+      case PROP_TOOLBAR_STYLE:
+        g_value_set_enum (value, egg_tool_palette_get_style (palette));
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+egg_tool_palette_dispose (GObject *object)
+{
+  EggToolPalette *palette = EGG_TOOL_PALETTE (object);
+  guint i;
+
+  if (palette->priv->hadjustment)
+    {
+      g_object_unref (palette->priv->hadjustment);
+      palette->priv->hadjustment = NULL;
+    }
+
+  if (palette->priv->vadjustment)
+    {
+      g_object_unref (palette->priv->vadjustment);
+      palette->priv->vadjustment = NULL;
+    }
+
+  for (i = 0; i < palette->priv->groups_size; ++i)
+    {
+      EggToolItemGroupInfo *group = &palette->priv->groups[i];
+
+      if (group->notify_collapsed)
+        {
+          g_signal_handler_disconnect (group->widget, group->notify_collapsed);
+          group->notify_collapsed = 0;
+        }
+    }
+
+#ifdef HAVE_EXTENDED_TOOL_SHELL_SUPPORT_BUG_535090
+  if (palette->priv->text_size_group)
+    {
+      g_object_unref (palette->priv->text_size_group);
+      palette->priv->text_size_group = NULL;
+    }
+#endif
+
+  G_OBJECT_CLASS (egg_tool_palette_parent_class)->dispose (object);
+}
+
+static void
+egg_tool_palette_finalize (GObject *object)
+{
+  EggToolPalette *palette = EGG_TOOL_PALETTE (object);
+
+  if (palette->priv->groups)
+    {
+      palette->priv->groups_length = 0;
+      g_free (palette->priv->groups);
+      palette->priv->groups = NULL;
+    }
+
+  G_OBJECT_CLASS (egg_tool_palette_parent_class)->finalize (object);
+}
+
+static void
+egg_tool_palette_size_request (GtkWidget      *widget,
+                               GtkRequisition *requisition)
+{
+  const gint border_width = GTK_CONTAINER (widget)->border_width;
+  EggToolPalette *palette = EGG_TOOL_PALETTE (widget);
+  GtkRequisition child_requisition;
+  guint i;
+
+  requisition->width = 0;
+  requisition->height = 0;
+
+  palette->priv->item_size.width = 0;
+  palette->priv->item_size.height = 0;
+
+  for (i = 0; i < palette->priv->groups_length; ++i)
+    {
+      EggToolItemGroupInfo *group = &palette->priv->groups[i];
+
+      if (!group->widget)
+        continue;
+
+      gtk_widget_size_request (GTK_WIDGET (group->widget), &child_requisition);
+
+      if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
+        {
+          requisition->width = MAX (requisition->width, child_requisition.width);
+          requisition->height += child_requisition.height;
+        }
+      else
+        {
+          requisition->width += child_requisition.width;
+          requisition->height = MAX (requisition->height, child_requisition.height);
+        }
+
+      _egg_tool_item_group_item_size_request (group->widget, &child_requisition);
+
+      palette->priv->item_size.width = MAX (palette->priv->item_size.width,
+                                            child_requisition.width);
+      palette->priv->item_size.height = MAX (palette->priv->item_size.height,
+                                             child_requisition.height);
+    }
+
+  requisition->width += border_width * 2;
+  requisition->height += border_width * 2;
+}
+
+static void
+egg_tool_palette_size_allocate (GtkWidget     *widget,
+                                GtkAllocation *allocation)
+{
+  const gint border_width = GTK_CONTAINER (widget)->border_width;
+  EggToolPalette *palette = EGG_TOOL_PALETTE (widget);
+  GtkAdjustment *adjustment = NULL;
+  GtkAllocation child_allocation;
+
+  gint n_expand_groups = 0;
+  gint remaining_space = 0;
+  gint expand_space = 0;
+
+  gint page_start, page_size = 0;
+  gint offset = 0;
+  guint i;
+
+  gint min_offset = -1, max_offset = -1;
+
+  gint x;
+
+  gint *group_sizes = g_newa(gint, palette->priv->groups_length);
+
+  GtkTextDirection direction = gtk_widget_get_direction (widget);
+
+  GTK_WIDGET_CLASS (egg_tool_palette_parent_class)->size_allocate (widget, allocation);
+
+  if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
+    {
+      adjustment = palette->priv->vadjustment;
+      page_size = allocation->height;
+    }
+  else
+    {
+      adjustment = palette->priv->hadjustment;
+      page_size = allocation->width;
+    }
+
+  if (adjustment)
+    offset = gtk_adjustment_get_value (adjustment);
+  if (GTK_ORIENTATION_HORIZONTAL == palette->priv->orientation &&
+      GTK_TEXT_DIR_RTL == direction)
+    offset = -offset;
+
+  if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
+    child_allocation.width = allocation->width - border_width * 2;
+  else
+    child_allocation.height = allocation->height - border_width * 2;
+
+  if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
+    remaining_space = allocation->height;
+  else
+    remaining_space = allocation->width;
+
+  for (i = 0; i < palette->priv->groups_length; ++i)
+    {
+      EggToolItemGroupInfo *group = &palette->priv->groups[i];
+      gint size;
+
+      if (!group->widget)
+        continue;
+
+      widget = GTK_WIDGET (group->widget);
+
+      if (egg_tool_item_group_get_n_items (group->widget))
+        {
+          if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
+            size = _egg_tool_item_group_get_height_for_width (group->widget, child_allocation.width);
+          else
+            size = _egg_tool_item_group_get_width_for_height (group->widget, child_allocation.height);
+
+          if (group->expand && !egg_tool_item_group_get_collapsed (group->widget))
+            n_expand_groups += 1;
+        }
+      else
+        size = 0;
+
+      remaining_space -= size;
+      group_sizes[i] = size;
+
+      if (widget == palette->priv->expanding_child)
+        {
+          gint j, real_size;
+          gint limit = GTK_ORIENTATION_VERTICAL == palette->priv->orientation ? child_allocation.width : child_allocation.height;
+
+          min_offset = 0;
+          for (j = 0; j < i; ++j)
+            {
+              min_offset += group_sizes[j];
+            }
+          max_offset = min_offset + group_sizes[i];
+
+          real_size = _egg_tool_item_group_get_size_for_limit (EGG_TOOL_ITEM_GROUP (widget),
+                                                               limit,
+                                                               GTK_ORIENTATION_VERTICAL == palette->priv->orientation,
+                                                               FALSE);
+          if (size == real_size)
+            palette->priv->expanding_child = NULL;
+        }
+    }
+
+  if (n_expand_groups > 0)
+    {
+      remaining_space = MAX (0, remaining_space);
+      expand_space = remaining_space / n_expand_groups;
+    }
+
+  if (max_offset != -1)
+    {
+      gint limit = GTK_ORIENTATION_VERTICAL == palette->priv->orientation ? allocation->height : allocation->width;
+
+      offset = MIN(MAX (offset, max_offset - limit), min_offset);
+    }
+
+  if (remaining_space > 0)
+    offset = 0;
+
+  x = border_width;
+  child_allocation.y = border_width;
+
+  if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
+    child_allocation.y -= offset;
+  else
+    x -= offset;
+
+  for (i = 0; i < palette->priv->groups_length; ++i)
+    {
+      EggToolItemGroupInfo *group = &palette->priv->groups[i];
+      GtkWidget *widget;
+
+      if (!group->widget)
+        continue;
+
+      widget = GTK_WIDGET (group->widget);
+
+      if (egg_tool_item_group_get_n_items (group->widget))
+        {
+          gint size = group_sizes[i];
+
+          if (group->expand && !egg_tool_item_group_get_collapsed (group->widget))
+            {
+              size += MIN (expand_space, remaining_space);
+              remaining_space -= expand_space;
+            }
+
+          if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
+            child_allocation.height = size;
+          else
+            child_allocation.width = size;
+
+          if (GTK_ORIENTATION_HORIZONTAL == palette->priv->orientation &&
+              GTK_TEXT_DIR_RTL == direction)
+            child_allocation.x = allocation->width - x - child_allocation.width;
+          else
+            child_allocation.x = x;
+
+          gtk_widget_size_allocate (widget, &child_allocation);
+          gtk_widget_show (widget);
+
+          if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
+            child_allocation.y += child_allocation.height;
+          else
+            x += child_allocation.width;
+        }
+      else
+        gtk_widget_hide (widget);
+    }
+
+  if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
+    {
+      child_allocation.y += border_width;
+      child_allocation.y += offset;
+
+      page_start = child_allocation.y;
+    }
+  else
+    {
+      x += border_width;
+      x += offset;
+
+      page_start = x;
+    }
+
+  if (adjustment)
+    {
+      gdouble value;
+
+      adjustment->page_increment = page_size * 0.9;
+      adjustment->step_increment = page_size * 0.1;
+      adjustment->page_size = page_size;
+      if (GTK_ORIENTATION_HORIZONTAL == palette->priv->orientation &&
+          GTK_TEXT_DIR_RTL == direction)
+        {
+          adjustment->lower = page_size - MAX (0, page_start);
+          adjustment->upper = page_size;
+
+          offset = -offset;
+
+          value = MAX(offset, adjustment->lower);
+          gtk_adjustment_clamp_page (adjustment, offset, value + page_size);
+        }
+      else
+        {
+          adjustment->lower = 0;
+          adjustment->upper = MAX (0, page_start);
+
+          value = MIN (offset, adjustment->upper - adjustment->page_size);
+          gtk_adjustment_clamp_page (adjustment, value, offset + page_size);
+        }
+
+      gtk_adjustment_changed (adjustment);
+    }
+}
+
+static gboolean
+egg_tool_palette_expose_event (GtkWidget      *widget,
+                               GdkEventExpose *event)
+{
+  EggToolPalette *palette = EGG_TOOL_PALETTE (widget);
+  GdkDisplay *display;
+  cairo_t *cr;
+  guint i;
+
+  display = gdk_drawable_get_display (widget->window);
+
+  if (!gdk_display_supports_composite (display))
+    return FALSE;
+
+  cr = gdk_cairo_create (widget->window);
+  gdk_cairo_region (cr, event->region);
+  cairo_clip (cr);
+
+  cairo_push_group (cr);
+
+  for (i = 0; i < palette->priv->groups_length; ++i)
+    if (palette->priv->groups[i].widget)
+      _egg_tool_item_group_paint (palette->priv->groups[i].widget, cr);
+
+  cairo_pop_group_to_source (cr);
+
+  cairo_paint (cr);
+  cairo_destroy (cr);
+
+  return FALSE;
+}
+
+static void
+egg_tool_palette_realize (GtkWidget *widget)
+{
+  const gint border_width = GTK_CONTAINER (widget)->border_width;
+  gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+  GdkWindowAttr attributes;
+
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.x = widget->allocation.x + border_width;
+  attributes.y = widget->allocation.y + border_width;
+  attributes.width = widget->allocation.width - border_width * 2;
+  attributes.height = widget->allocation.height - border_width * 2;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = gtk_widget_get_colormap (widget);
+  attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK
+                        | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+                        | GDK_BUTTON_MOTION_MASK;
+
+  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                   &attributes, attributes_mask);
+
+  gdk_window_set_user_data (widget->window, widget);
+  widget->style = gtk_style_attach (widget->style, widget->window);
+  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+  gtk_container_forall (GTK_CONTAINER (widget),
+                        (GtkCallback) gtk_widget_set_parent_window,
+                        widget->window);
+
+  gtk_widget_queue_resize_no_redraw (widget);
+}
+
+static void
+egg_tool_palette_adjustment_value_changed (GtkAdjustment *adjustment G_GNUC_UNUSED,
+                                           gpointer       data)
+{
+  GtkWidget *widget = GTK_WIDGET (data);
+  egg_tool_palette_size_allocate (widget, &widget->allocation);
+}
+
+static void
+egg_tool_palette_set_scroll_adjustments (GtkWidget     *widget,
+                                         GtkAdjustment *hadjustment,
+                                         GtkAdjustment *vadjustment)
+{
+  EggToolPalette *palette = EGG_TOOL_PALETTE (widget);
+
+  if (palette->priv->hadjustment)
+    g_object_unref (palette->priv->hadjustment);
+  if (palette->priv->vadjustment)
+    g_object_unref (palette->priv->vadjustment);
+
+  if (hadjustment)
+    g_object_ref_sink (hadjustment);
+  if (vadjustment)
+    g_object_ref_sink (vadjustment);
+
+  palette->priv->hadjustment = hadjustment;
+  palette->priv->vadjustment = vadjustment;
+
+  if (palette->priv->hadjustment)
+    g_signal_connect (palette->priv->hadjustment, "value-changed",
+                      G_CALLBACK (egg_tool_palette_adjustment_value_changed),
+                      palette);
+  if (palette->priv->vadjustment)
+    g_signal_connect (palette->priv->vadjustment, "value-changed",
+                      G_CALLBACK (egg_tool_palette_adjustment_value_changed),
+                      palette);
+}
+
+static void
+egg_tool_palette_repack (EggToolPalette *palette)
+{
+  guint si, di;
+
+  for (si = di = 0; di < palette->priv->groups_length; ++si)
+    {
+      if (palette->priv->groups[si].widget)
+        {
+          palette->priv->groups[di] = palette->priv->groups[si];
+          ++di;
+        }
+      else
+        --palette->priv->groups_length;
+    }
+
+  palette->priv->sparse_groups = FALSE;
+}
+
+static void
+egg_tool_palette_add (GtkContainer *container,
+                      GtkWidget    *child)
+{
+  EggToolPalette *palette;
+
+  g_return_if_fail (EGG_IS_TOOL_PALETTE (container));
+  g_return_if_fail (EGG_IS_TOOL_ITEM_GROUP (child));
+
+  palette = EGG_TOOL_PALETTE (container);
+
+  if (palette->priv->groups_length == palette->priv->groups_size)
+    egg_tool_palette_repack (palette);
+
+  if (palette->priv->groups_length == palette->priv->groups_size)
+    {
+      gsize old_size = palette->priv->groups_size;
+      gsize new_size = old_size * 2;
+
+      palette->priv->groups = g_renew (EggToolItemGroupInfo,
+                                       palette->priv->groups,
+                                       new_size);
+
+      memset (palette->priv->groups + old_size, 0,
+              sizeof (EggToolItemGroupInfo) * old_size);
+
+      palette->priv->groups_size = new_size;
+    }
+
+  palette->priv->groups[palette->priv->groups_length].widget = g_object_ref_sink (child);
+  palette->priv->groups_length += 1;
+
+  gtk_widget_set_parent (child, GTK_WIDGET (palette));
+}
+
+static void
+egg_tool_palette_remove (GtkContainer *container,
+                         GtkWidget    *child)
+{
+  EggToolPalette *palette;
+  guint i;
+
+  g_return_if_fail (EGG_IS_TOOL_PALETTE (container));
+  palette = EGG_TOOL_PALETTE (container);
+
+  for (i = 0; i < palette->priv->groups_length; ++i)
+    if ((GtkWidget*) palette->priv->groups[i].widget == child)
+      {
+        g_object_unref (child);
+        gtk_widget_unparent (child);
+
+        memset (&palette->priv->groups[i], 0, sizeof (EggToolItemGroupInfo));
+        palette->priv->sparse_groups = TRUE;
+      }
+}
+
+static void
+egg_tool_palette_forall (GtkContainer *container,
+                         gboolean      internals G_GNUC_UNUSED,
+                         GtkCallback   callback,
+                         gpointer      callback_data)
+{
+  EggToolPalette *palette = EGG_TOOL_PALETTE (container);
+  guint i;
+
+  if (palette->priv->groups)
+    {
+      for (i = 0; i < palette->priv->groups_length; ++i)
+        if (palette->priv->groups[i].widget)
+          callback (GTK_WIDGET (palette->priv->groups[i].widget),
+                    callback_data);
+    }
+}
+
+static GType
+egg_tool_palette_child_type (GtkContainer *container G_GNUC_UNUSED)
+{
+  return EGG_TYPE_TOOL_ITEM_GROUP;
+}
+
+static void
+egg_tool_palette_set_child_property (GtkContainer *container,
+                                     GtkWidget    *child,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+  EggToolPalette *palette = EGG_TOOL_PALETTE (container);
+
+  switch (prop_id)
+    {
+      case CHILD_PROP_EXCLUSIVE:
+        egg_tool_palette_set_exclusive (palette, child, g_value_get_boolean (value));
+        break;
+
+      case CHILD_PROP_EXPAND:
+        egg_tool_palette_set_expand (palette, child, g_value_get_boolean (value));
+        break;
+
+      default:
+        GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+egg_tool_palette_get_child_property (GtkContainer *container,
+                                     GtkWidget    *child,
+                                     guint         prop_id,
+                                     GValue       *value,
+                                     GParamSpec   *pspec)
+{
+  EggToolPalette *palette = EGG_TOOL_PALETTE (container);
+
+  switch (prop_id)
+    {
+      case CHILD_PROP_EXCLUSIVE:
+        g_value_set_boolean (value, egg_tool_palette_get_exclusive (palette, child));
+        break;
+
+      case CHILD_PROP_EXPAND:
+        g_value_set_boolean (value, egg_tool_palette_get_expand (palette, child));
+        break;
+
+      default:
+        GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+egg_tool_palette_class_init (EggToolPaletteClass *cls)
+{
+  GObjectClass      *oclass   = G_OBJECT_CLASS (cls);
+  GtkWidgetClass    *wclass   = GTK_WIDGET_CLASS (cls);
+  GtkContainerClass *cclass   = GTK_CONTAINER_CLASS (cls);
+
+  oclass->set_property        = egg_tool_palette_set_property;
+  oclass->get_property        = egg_tool_palette_get_property;
+  oclass->dispose             = egg_tool_palette_dispose;
+  oclass->finalize            = egg_tool_palette_finalize;
+
+  wclass->size_request        = egg_tool_palette_size_request;
+  wclass->size_allocate       = egg_tool_palette_size_allocate;
+  wclass->expose_event        = egg_tool_palette_expose_event;
+  wclass->realize             = egg_tool_palette_realize;
+
+  cclass->add                 = egg_tool_palette_add;
+  cclass->remove              = egg_tool_palette_remove;
+  cclass->forall              = egg_tool_palette_forall;
+  cclass->child_type          = egg_tool_palette_child_type;
+  cclass->set_child_property  = egg_tool_palette_set_child_property;
+  cclass->get_child_property  = egg_tool_palette_get_child_property;
+
+  cls->set_scroll_adjustments = egg_tool_palette_set_scroll_adjustments;
+
+  wclass->set_scroll_adjustments_signal =
+    g_signal_new ("set-scroll-adjustments",
+                  G_TYPE_FROM_CLASS (oclass),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  G_STRUCT_OFFSET (EggToolPaletteClass, set_scroll_adjustments),
+                  NULL, NULL,
+                  _egg_marshal_VOID__OBJECT_OBJECT,
+                  G_TYPE_NONE, 2,
+                  GTK_TYPE_ADJUSTMENT,
+                  GTK_TYPE_ADJUSTMENT);
+
+  g_object_class_install_property (oclass, PROP_ICON_SIZE,
+                                   g_param_spec_enum ("icon-size",
+                                                      P_("Icon Size"),
+                                                      P_("The size of palette icons"),
+                                                      GTK_TYPE_ICON_SIZE,
+                                                      DEFAULT_ICON_SIZE,
+                                                      G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
+                                                      G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  g_object_class_install_property (oclass, PROP_ORIENTATION,
+                                   g_param_spec_enum ("orientation",
+                                                      P_("Orientation"),
+                                                      P_("Orientation of the tool palette"),
+                                                      GTK_TYPE_ORIENTATION,
+                                                      DEFAULT_ORIENTATION,
+                                                      G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
+                                                      G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  g_object_class_install_property (oclass, PROP_TOOLBAR_STYLE,
+                                   g_param_spec_enum ("toolbar-style",
+                                                      P_("Toolbar Style"),
+                                                      P_("Style of items in the tool palette"),
+                                                      GTK_TYPE_TOOLBAR_STYLE,
+                                                      DEFAULT_TOOLBAR_STYLE,
+                                                      G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
+                                                      G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  gtk_container_class_install_child_property (cclass, CHILD_PROP_EXCLUSIVE,
+                                              g_param_spec_boolean ("exclusive",
+                                                                    P_("Exclusive"),
+                                                                    P_("Whether the item group should be the only expanded at a given time"),
+                                                                    DEFAULT_CHILD_EXCLUSIVE,
+                                                                    G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
+                                                                    G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  gtk_container_class_install_child_property (cclass, CHILD_PROP_EXPAND,
+                                              g_param_spec_boolean ("expand",
+                                                                    P_("Expand"),
+                                                                    P_("Whether the item group should receive extra space when the palette grows"),
+                                                                    DEFAULT_CHILD_EXPAND,
+                                                                    G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
+                                                                    G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  g_type_class_add_private (cls, sizeof (EggToolPalettePrivate));
+
+  dnd_target_atom_item = gdk_atom_intern_static_string (dnd_targets[0].target);
+  dnd_target_atom_group = gdk_atom_intern_static_string (dnd_targets[1].target);
+}
+
+GtkWidget*
+egg_tool_palette_new (void)
+{
+  return g_object_new (EGG_TYPE_TOOL_PALETTE, NULL);
+}
+
+void
+egg_tool_palette_set_icon_size (EggToolPalette *palette,
+                                GtkIconSize     icon_size)
+{
+  g_return_if_fail (EGG_IS_TOOL_PALETTE (palette));
+
+  if (icon_size != palette->priv->icon_size)
+    g_object_set (palette, "icon-size", icon_size, NULL);
+}
+
+void
+egg_tool_palette_set_orientation (EggToolPalette *palette,
+                                  GtkOrientation  orientation)
+{
+  g_return_if_fail (EGG_IS_TOOL_PALETTE (palette));
+
+  if (orientation != palette->priv->orientation)
+    g_object_set (palette, "orientation", orientation, NULL);
+}
+
+void
+egg_tool_palette_set_style (EggToolPalette  *palette,
+                            GtkToolbarStyle  style)
+{
+  g_return_if_fail (EGG_IS_TOOL_PALETTE (palette));
+
+  if (style != palette->priv->style)
+    g_object_set (palette, "style", style, NULL);
+}
+
+GtkIconSize
+egg_tool_palette_get_icon_size (EggToolPalette *palette)
+{
+  g_return_val_if_fail (EGG_IS_TOOL_PALETTE (palette), DEFAULT_ICON_SIZE);
+  return palette->priv->icon_size;
+}
+
+GtkOrientation
+egg_tool_palette_get_orientation (EggToolPalette *palette)
+{
+  g_return_val_if_fail (EGG_IS_TOOL_PALETTE (palette), DEFAULT_ORIENTATION);
+  return palette->priv->orientation;
+}
+
+GtkToolbarStyle
+egg_tool_palette_get_style (EggToolPalette *palette)
+{
+  g_return_val_if_fail (EGG_IS_TOOL_PALETTE (palette), DEFAULT_TOOLBAR_STYLE);
+  return palette->priv->style;
+}
+
+void
+egg_tool_palette_set_group_position (EggToolPalette *palette,
+                                     GtkWidget      *group,
+                                     gint            position)
+{
+  EggToolItemGroupInfo group_info;
+  gint old_position;
+  gpointer src, dst;
+  gsize len;
+
+  g_return_if_fail (EGG_IS_TOOL_PALETTE (palette));
+  g_return_if_fail (EGG_IS_TOOL_ITEM_GROUP (group));
+
+  egg_tool_palette_repack (palette);
+
+  g_return_if_fail (position >= -1);
+
+  if (-1 == position)
+    position = palette->priv->groups_length - 1;
+
+  g_return_if_fail ((guint) position < palette->priv->groups_length);
+
+  if (EGG_TOOL_ITEM_GROUP (group) == palette->priv->groups[position].widget)
+    return;
+
+  old_position = egg_tool_palette_get_group_position (palette, group);
+  g_return_if_fail (old_position >= 0);
+
+  group_info = palette->priv->groups[old_position];
+
+  if (position < old_position)
+    {
+      dst = palette->priv->groups + position + 1;
+      src = palette->priv->groups + position;
+      len = old_position - position;
+    }
+  else
+    {
+      dst = palette->priv->groups + old_position;
+      src = palette->priv->groups + old_position + 1;
+      len = position - old_position;
+    }
+
+  memmove (dst, src, len * sizeof (*palette->priv->groups));
+  palette->priv->groups[position] = group_info;
+
+  gtk_widget_queue_resize (GTK_WIDGET (palette));
+}
+
+static void
+egg_tool_palette_group_notify_collapsed (EggToolItemGroup *group,
+                                         GParamSpec       *pspec G_GNUC_UNUSED,
+                                         gpointer          data)
+{
+  EggToolPalette *palette = EGG_TOOL_PALETTE (data);
+  guint i;
+
+  if (egg_tool_item_group_get_collapsed (group))
+    return;
+
+  for (i = 0; i < palette->priv->groups_size; ++i)
+    {
+      EggToolItemGroup *current_group = palette->priv->groups[i].widget;
+
+      if (current_group && current_group != group)
+        egg_tool_item_group_set_collapsed (palette->priv->groups[i].widget, TRUE);
+    }
+}
+
+void
+egg_tool_palette_set_exclusive (EggToolPalette *palette,
+                                GtkWidget      *group,
+                                gboolean        exclusive)
+{
+  EggToolItemGroupInfo *group_info;
+  gint position;
+
+  g_return_if_fail (EGG_IS_TOOL_PALETTE (palette));
+  g_return_if_fail (EGG_IS_TOOL_ITEM_GROUP (group));
+
+  position = egg_tool_palette_get_group_position (palette, group);
+  g_return_if_fail (position >= 0);
+
+  group_info = &palette->priv->groups[position];
+
+  if (exclusive == group_info->exclusive)
+    return;
+
+  group_info->exclusive = exclusive;
+
+  if (group_info->exclusive != (0 != group_info->notify_collapsed))
+    {
+      if (group_info->exclusive)
+        {
+          group_info->notify_collapsed =
+            g_signal_connect (group, "notify::collapsed",
+                              G_CALLBACK (egg_tool_palette_group_notify_collapsed),
+                              palette);
+        }
+      else
+        {
+          g_signal_handler_disconnect (group, group_info->notify_collapsed);
+          group_info->notify_collapsed = 0;
+        }
+    }
+
+  egg_tool_palette_group_notify_collapsed (group_info->widget, NULL, palette);
+  gtk_widget_child_notify (group, "exclusive");
+}
+
+void
+egg_tool_palette_set_expand (EggToolPalette *palette,
+                             GtkWidget      *group,
+                             gboolean        expand G_GNUC_UNUSED)
+{
+  EggToolItemGroupInfo *group_info;
+  gint position;
+
+  g_return_if_fail (EGG_IS_TOOL_PALETTE (palette));
+  g_return_if_fail (EGG_IS_TOOL_ITEM_GROUP (group));
+
+  position = egg_tool_palette_get_group_position (palette, group);
+  g_return_if_fail (position >= 0);
+
+  group_info = &palette->priv->groups[position];
+
+  if (expand != group_info->expand)
+    {
+      group_info->expand = expand;
+      gtk_widget_queue_resize (GTK_WIDGET (palette));
+      gtk_widget_child_notify (group, "expand");
+    }
+}
+
+gint
+egg_tool_palette_get_group_position (EggToolPalette *palette,
+                                     GtkWidget      *group)
+{
+  guint i;
+
+  g_return_val_if_fail (EGG_IS_TOOL_PALETTE (palette), -1);
+  g_return_val_if_fail (EGG_IS_TOOL_ITEM_GROUP (group), -1);
+
+  for (i = 0; i < palette->priv->groups_length; ++i)
+    if ((gpointer) group == palette->priv->groups[i].widget)
+      return i;
+
+  return -1;
+}
+
+gboolean
+egg_tool_palette_get_exclusive (EggToolPalette *palette G_GNUC_UNUSED,
+                                GtkWidget      *group G_GNUC_UNUSED)
+{
+  gint position;
+
+  g_return_val_if_fail (EGG_IS_TOOL_PALETTE (palette), DEFAULT_CHILD_EXCLUSIVE);
+  g_return_val_if_fail (EGG_IS_TOOL_ITEM_GROUP (group), DEFAULT_CHILD_EXCLUSIVE);
+
+  position = egg_tool_palette_get_group_position (palette, group);
+  g_return_val_if_fail (position >= 0, DEFAULT_CHILD_EXCLUSIVE);
+
+  return palette->priv->groups[position].exclusive;
+}
+
+gboolean
+egg_tool_palette_get_expand (EggToolPalette *palette,
+                             GtkWidget      *group)
+{
+  gint position;
+
+  g_return_val_if_fail (EGG_IS_TOOL_PALETTE (palette), DEFAULT_CHILD_EXPAND);
+  g_return_val_if_fail (EGG_IS_TOOL_ITEM_GROUP (group), DEFAULT_CHILD_EXPAND);
+
+  position = egg_tool_palette_get_group_position (palette, group);
+  g_return_val_if_fail (position >= 0, DEFAULT_CHILD_EXPAND);
+
+  return palette->priv->groups[position].expand;
+}
+
+GtkToolItem*
+egg_tool_palette_get_drop_item (EggToolPalette *palette,
+                                gint            x,
+                                gint            y)
+{
+  GtkWidget *group = egg_tool_palette_get_drop_group (palette, x, y);
+
+  if (group)
+    return egg_tool_item_group_get_drop_item (EGG_TOOL_ITEM_GROUP (group),
+                                              x - group->allocation.x,
+                                              y - group->allocation.y);
+
+  return NULL;
+}
+
+GtkWidget*
+egg_tool_palette_get_drop_group (EggToolPalette *palette,
+                                 gint            x,
+                                 gint            y)
+{
+  GtkAllocation *allocation;
+  guint i;
+
+  g_return_val_if_fail (EGG_IS_TOOL_PALETTE (palette), NULL);
+
+  allocation = &GTK_WIDGET (palette)->allocation;
+
+  g_return_val_if_fail (x >= 0 && x < allocation->width, NULL);
+  g_return_val_if_fail (y >= 0 && y < allocation->height, NULL);
+
+  for (i = 0; i < palette->priv->groups_length; ++i)
+    {
+      EggToolItemGroupInfo *group = &palette->priv->groups[i];
+      GtkWidget *widget;
+      gint x0, y0;
+
+      if (!group->widget)
+        continue;
+
+      widget = GTK_WIDGET (group->widget);
+
+      x0 = x - widget->allocation.x;
+      y0 = y - widget->allocation.y;
+
+      if (x0 >= 0 && x0 < widget->allocation.width &&
+          y0 >= 0 && y0 < widget->allocation.height)
+        return widget;
+    }
+
+  return NULL;
+}
+
+GtkWidget*
+egg_tool_palette_get_drag_item (EggToolPalette         *palette,
+                                const GtkSelectionData *selection)
+{
+  EggToolPaletteDragData *data;
+
+  g_return_val_if_fail (EGG_IS_TOOL_PALETTE (palette), NULL);
+  g_return_val_if_fail (NULL != selection, NULL);
+
+  g_return_val_if_fail (selection->format == 8, NULL);
+  g_return_val_if_fail (selection->length == sizeof (EggToolPaletteDragData), NULL);
+  g_return_val_if_fail (selection->target == dnd_target_atom_item ||
+                        selection->target == dnd_target_atom_group,
+                        NULL);
+
+  data = (EggToolPaletteDragData*) selection->data;
+
+  g_return_val_if_fail (data->palette == palette, NULL);
+
+  if (dnd_target_atom_item == selection->target)
+    g_return_val_if_fail (GTK_IS_TOOL_ITEM (data->item), NULL);
+  else if (dnd_target_atom_group == selection->target)
+    g_return_val_if_fail (EGG_IS_TOOL_ITEM_GROUP (data->item), NULL);
+
+  return data->item;
+}
+
+void
+egg_tool_palette_set_drag_source (EggToolPalette *palette)
+{
+  guint i;
+
+  g_return_if_fail (EGG_IS_TOOL_PALETTE (palette));
+
+  if (palette->priv->drag_source)
+    return;
+
+  palette->priv->drag_source = TRUE;
+
+  for (i = 0; i < palette->priv->groups_length; ++i)
+    {
+      if (palette->priv->groups[i].widget)
+        gtk_container_forall (GTK_CONTAINER (palette->priv->groups[i].widget),
+                              _egg_tool_palette_child_set_drag_source,
+                              palette);
+    }
+}
+
+void
+egg_tool_palette_add_drag_dest (EggToolPalette            *palette,
+                                GtkWidget                 *widget,
+                                GtkDestDefaults            flags,
+                                EggToolPaletteDragTargets  targets,
+                                GdkDragAction              actions)
+{
+  GtkTargetEntry entries[G_N_ELEMENTS (dnd_targets)];
+  gint n_entries = 0;
+
+  g_return_if_fail (EGG_IS_TOOL_PALETTE (palette));
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  egg_tool_palette_set_drag_source (palette);
+
+  if (targets & EGG_TOOL_PALETTE_DRAG_ITEMS)
+    entries[n_entries++] = dnd_targets[0];
+  if (targets & EGG_TOOL_PALETTE_DRAG_GROUPS)
+    entries[n_entries++] = dnd_targets[1];
+
+  gtk_drag_dest_set (widget, flags, entries, n_entries, actions);
+}
+
+void
+_egg_tool_palette_get_item_size (EggToolPalette *palette,
+                                 GtkRequisition *item_size)
+{
+  g_return_if_fail (EGG_IS_TOOL_PALETTE (palette));
+  g_return_if_fail (NULL != item_size);
+
+  *item_size = palette->priv->item_size;
+}
+
+static GtkWidget*
+egg_tool_palette_find_anchestor (GtkWidget *widget,
+                                 GType      type)
+{
+  while (widget)
+    {
+      if (G_TYPE_CHECK_INSTANCE_TYPE (widget, type))
+        return widget;
+
+      widget = gtk_widget_get_parent (widget);
+    }
+
+  return NULL;
+}
+
+static void
+egg_tool_palette_item_drag_data_get (GtkWidget        *widget,
+                                     GdkDragContext   *context G_GNUC_UNUSED,
+                                     GtkSelectionData *selection,
+                                     guint             info G_GNUC_UNUSED,
+                                     guint             time G_GNUC_UNUSED,
+                                     gpointer          data)
+{
+  EggToolPaletteDragData drag_data = { EGG_TOOL_PALETTE (data), NULL };
+
+  if (selection->target == dnd_target_atom_item)
+    drag_data.item = egg_tool_palette_find_anchestor (widget, GTK_TYPE_TOOL_ITEM);
+
+  if (drag_data.item)
+    gtk_selection_data_set (selection, selection->target, 8,
+                            (guchar*) &drag_data, sizeof (drag_data));
+}
+
+static void
+egg_tool_palette_child_drag_data_get (GtkWidget        *widget,
+                                      GdkDragContext   *context G_GNUC_UNUSED,
+                                      GtkSelectionData *selection,
+                                      guint             info G_GNUC_UNUSED,
+                                      guint             time G_GNUC_UNUSED,
+                                      gpointer          data)
+{
+  EggToolPaletteDragData drag_data = { EGG_TOOL_PALETTE (data), NULL };
+
+  if (selection->target == dnd_target_atom_group)
+    drag_data.item = egg_tool_palette_find_anchestor (widget, EGG_TYPE_TOOL_ITEM_GROUP);
+
+  if (drag_data.item)
+    gtk_selection_data_set (selection, selection->target, 8,
+                            (guchar*) &drag_data, sizeof (drag_data));
+}
+
+void
+_egg_tool_palette_child_set_drag_source (GtkWidget *child,
+                                         gpointer   data)
+{
+  EggToolPalette *palette = EGG_TOOL_PALETTE (data);
+
+  /* Check drag_source,
+   * to work properly when called from egg_tool_item_group_insert().
+   */
+  if (!palette->priv->drag_source)
+    return;
+
+  if (GTK_IS_TOOL_ITEM (child))
+    {
+      /* Connect to child instead of the item itself,
+       * to work arround bug 510377.
+       */
+      child = gtk_bin_get_child (GTK_BIN (child));
+
+      if (!child)
+        return;
+
+      gtk_drag_source_set (child, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
+                           &dnd_targets[0], 1, GDK_ACTION_COPY | GDK_ACTION_MOVE);
+
+      g_signal_connect (child, "drag-data-get",
+                        G_CALLBACK (egg_tool_palette_item_drag_data_get),
+                        palette);
+    }
+  else if (GTK_IS_BUTTON (child))
+    {
+      gtk_drag_source_set (child, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
+                           &dnd_targets[1], 1, GDK_ACTION_COPY | GDK_ACTION_MOVE);
+
+      g_signal_connect (child, "drag-data-get",
+                        G_CALLBACK (egg_tool_palette_child_drag_data_get),
+                        palette);
+    }
+}
+
+G_CONST_RETURN GtkTargetEntry*
+egg_tool_palette_get_drag_target_item (void)
+{
+  return &dnd_targets[0];
+}
+
+G_CONST_RETURN GtkTargetEntry*
+egg_tool_palette_get_drag_target_group (void)
+{
+  return &dnd_targets[1];
+}
+
+void
+_egg_tool_palette_set_expanding_child (EggToolPalette   *palette,
+                                       GtkWidget        *widget)
+{
+  g_return_if_fail (EGG_IS_TOOL_PALETTE (palette));
+
+  palette->priv->expanding_child = widget;
+}
+
+#ifdef HAVE_EXTENDED_TOOL_SHELL_SUPPORT_BUG_535090
+GtkSizeGroup *
+_egg_tool_palette_get_size_group (EggToolPalette *palette)
+{
+  g_return_val_if_fail (EGG_IS_TOOL_PALETTE (palette), NULL);
+
+  return palette->priv->text_size_group;
+}
+#endif

Added: trunk/glom/utility_widgets/egg/toolpalette/eggtoolpalette.h
==============================================================================
--- (empty file)
+++ trunk/glom/utility_widgets/egg/toolpalette/eggtoolpalette.h	Mon Jun 16 10:23:15 2008
@@ -0,0 +1,117 @@
+/* EggToolPalette -- A tool palette with categories and DnD support
+ * Copyright (C) 2008  Openismus GmbH
+ *
+ * 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.1 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
+ *
+ * Authors:
+ *      Mathias Hasselmann
+ */
+
+#ifndef __EGG_TOOL_PALETTE_H__
+#define __EGG_TOOL_PALETTE_H__
+
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtkdnd.h>
+#include <gtk/gtktoolitem.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_TOOL_PALETTE           (egg_tool_palette_get_type())
+#define EGG_TOOL_PALETTE(obj)           (G_TYPE_CHECK_INSTANCE_CAST(obj, EGG_TYPE_TOOL_PALETTE, EggToolPalette))
+#define EGG_TOOL_PALETTE_CLASS(cls)     (G_TYPE_CHECK_CLASS_CAST(cls, EGG_TYPE_TOOL_PALETTE, EggToolPaletteClass))
+#define EGG_IS_TOOL_PALETTE(obj)        (G_TYPE_CHECK_INSTANCE_TYPE(obj, EGG_TYPE_TOOL_PALETTE))
+#define EGG_IS_TOOL_PALETTE_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE(obj, EGG_TYPE_TOOL_PALETTE))
+#define EGG_TOOL_PALETTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EGG_TYPE_TOOL_PALETTE, EggToolPaletteClass))
+
+typedef struct _EggToolPalette           EggToolPalette;
+typedef struct _EggToolPaletteClass      EggToolPaletteClass;
+typedef struct _EggToolPalettePrivate    EggToolPalettePrivate;
+
+typedef enum /*< flags >*/
+{
+  EGG_TOOL_PALETTE_DRAG_ITEMS = (1 << 0),
+  EGG_TOOL_PALETTE_DRAG_GROUPS = (1 << 1),
+}
+EggToolPaletteDragTargets;
+
+struct _EggToolPalette
+{
+  GtkContainer parent_instance;
+  EggToolPalettePrivate *priv;
+};
+
+struct _EggToolPaletteClass
+{
+  GtkContainerClass parent_class;
+
+  void (*set_scroll_adjustments) (GtkWidget     *widget,
+                                  GtkAdjustment *hadjustment,
+                                  GtkAdjustment *vadjustment);
+};
+
+GType                          egg_tool_palette_get_type              (void) G_GNUC_CONST;
+GtkWidget*                     egg_tool_palette_new                   (void);
+
+void                           egg_tool_palette_set_group_position    (EggToolPalette            *palette,
+                                                                       GtkWidget                 *group,
+                                                                       gint                       position);
+void                           egg_tool_palette_set_exclusive         (EggToolPalette            *palette,
+                                                                       GtkWidget                 *group,
+                                                                       gboolean                   exclusive);
+void                           egg_tool_palette_set_expand            (EggToolPalette            *palette,
+                                                                       GtkWidget                 *group,
+                                                                       gboolean                   expand);
+
+gint                           egg_tool_palette_get_group_position    (EggToolPalette            *palette,
+                                                                       GtkWidget                 *group);
+gboolean                       egg_tool_palette_get_exclusive         (EggToolPalette            *palette,
+                                                                       GtkWidget                 *group);
+gboolean                       egg_tool_palette_get_expand            (EggToolPalette            *palette,
+                                                                       GtkWidget                 *group);
+
+void                           egg_tool_palette_set_icon_size         (EggToolPalette            *palette,
+                                                                       GtkIconSize                icon_size);
+void                           egg_tool_palette_set_orientation       (EggToolPalette            *palette,
+                                                                       GtkOrientation             orientation);
+void                           egg_tool_palette_set_style             (EggToolPalette            *palette,
+                                                                       GtkToolbarStyle            style);
+
+GtkIconSize                    egg_tool_palette_get_icon_size         (EggToolPalette            *palette);
+GtkOrientation                 egg_tool_palette_get_orientation       (EggToolPalette            *palette);
+GtkToolbarStyle                egg_tool_palette_get_style             (EggToolPalette            *palette);
+
+GtkToolItem*                   egg_tool_palette_get_drop_item         (EggToolPalette            *palette,
+                                                                       gint                       x,
+                                                                       gint                       y);
+GtkWidget*                     egg_tool_palette_get_drop_group        (EggToolPalette            *palette,
+                                                                       gint                       x,
+                                                                       gint                       y);
+GtkWidget*                     egg_tool_palette_get_drag_item         (EggToolPalette            *palette,
+                                                                       const GtkSelectionData    *selection);
+
+void                           egg_tool_palette_set_drag_source       (EggToolPalette            *palette);
+void                           egg_tool_palette_add_drag_dest         (EggToolPalette            *palette,
+                                                                       GtkWidget                 *widget,
+                                                                       GtkDestDefaults            flags,
+                                                                       EggToolPaletteDragTargets  targets,
+                                                                       GdkDragAction              actions);
+
+G_CONST_RETURN GtkTargetEntry* egg_tool_palette_get_drag_target_item  (void) G_GNUC_CONST;
+G_CONST_RETURN GtkTargetEntry* egg_tool_palette_get_drag_target_group (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif /* __EGG_TOOL_PALETTE_H__ */ 

Added: trunk/glom/utility_widgets/egg/toolpalette/eggtoolpaletteprivate.h
==============================================================================
--- (empty file)
+++ trunk/glom/utility_widgets/egg/toolpalette/eggtoolpaletteprivate.h	Mon Jun 16 10:23:15 2008
@@ -0,0 +1,57 @@
+/* EggToolPalette -- A tool palette with categories and DnD support
+ * Copyright (C) 2008  Openismus GmbH
+ *
+ * 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.1 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
+ *
+ * Authors:
+ *      Mathias Hasselmann
+ */
+
+#ifndef __EGG_TOOL_PALETTE_PRIVATE_H__
+#define __EGG_TOOL_PALETTE_PRIVATE_H__
+
+#include "eggtoolpalette.h"
+#include "eggtoolitemgroup.h"
+#include <gtk/gtk.h>
+
+void _egg_tool_palette_get_item_size           (EggToolPalette   *palette,
+                                                GtkRequisition   *item_size);
+void _egg_tool_palette_child_set_drag_source   (GtkWidget        *widget,
+                                                gpointer          data);
+void _egg_tool_palette_set_expanding_child     (EggToolPalette   *palette,
+                                                GtkWidget        *widget);
+
+void _egg_tool_item_group_palette_reconfigured (EggToolItemGroup *group);
+void _egg_tool_item_group_item_size_request    (EggToolItemGroup *group,
+                                                GtkRequisition   *item_size);
+gint _egg_tool_item_group_get_height_for_width (EggToolItemGroup *group,
+                                                gint              width);
+gint _egg_tool_item_group_get_width_for_height (EggToolItemGroup *group,
+                                                gint              height);
+void _egg_tool_item_group_paint                (EggToolItemGroup *group,
+                                                cairo_t          *cr);
+gint _egg_tool_item_group_get_size_for_limit   (EggToolItemGroup *group,
+                                                gint              limit,
+                                                gboolean          vertical,
+                                                gboolean          animation);
+
+#undef HAVE_EXTENDED_TOOL_SHELL_SUPPORT_BUG_535090
+/* #define HAVE_EXTENDED_TOOL_SHELL_SUPPORT_BUG_535090 */
+
+#ifdef HAVE_EXTENDED_TOOL_SHELL_SUPPORT_BUG_535090
+GtkSizeGroup *_egg_tool_palette_get_size_group (EggToolPalette   *palette);
+#endif
+
+#endif /* __EGG_TOOL_PALETTE_PRIVATE_H__ */

Added: trunk/glom/utility_widgets/egg/toolpalette/testtoolpalette.c
==============================================================================
--- (empty file)
+++ trunk/glom/utility_widgets/egg/toolpalette/testtoolpalette.c	Mon Jun 16 10:23:15 2008
@@ -0,0 +1,887 @@
+#include "eggtoolpalette.h"
+#include "eggtoolitemgroup.h"
+#include "eggenumaction.h"
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+typedef struct _CanvasItem CanvasItem;
+
+struct _CanvasItem
+{
+  GdkPixbuf *pixbuf;
+  gdouble x, y;
+};
+
+static CanvasItem *drop_item = NULL;
+static GList *canvas_items = NULL;
+
+/********************************/
+/* ====== Canvas drawing ====== */
+/********************************/
+
+static CanvasItem*
+canvas_item_new (GtkWidget     *widget,
+                 GtkToolButton *button,
+                 gdouble        x,
+                 gdouble        y)
+{
+  CanvasItem *item = NULL;
+  const gchar *stock_id;
+  GdkPixbuf *pixbuf;
+
+  stock_id = gtk_tool_button_get_stock_id (button);
+  pixbuf = gtk_widget_render_icon (widget, stock_id, GTK_ICON_SIZE_DIALOG, NULL);
+
+  if (pixbuf)
+    {
+      item = g_slice_new0 (CanvasItem);
+      item->pixbuf = pixbuf;
+      item->x = x;
+      item->y = y;
+    }
+
+  return item;
+}
+
+static void
+canvas_item_free (CanvasItem *item)
+{
+  g_object_unref (item->pixbuf);
+  g_slice_free (CanvasItem, item);
+}
+
+static void
+canvas_item_draw (const CanvasItem *item,
+                  cairo_t          *cr,
+                  gboolean          preview)
+{
+  gdouble cx = gdk_pixbuf_get_width (item->pixbuf);
+  gdouble cy = gdk_pixbuf_get_height (item->pixbuf);
+
+  gdk_cairo_set_source_pixbuf (cr,
+                               item->pixbuf,
+                               item->x - cx * 0.5,
+                               item->y - cy * 0.5);
+
+  if (preview)
+    cairo_paint_with_alpha (cr, 0.6);
+  else
+    cairo_paint (cr);
+}
+
+static gboolean
+canvas_expose_event (GtkWidget      *widget,
+                     GdkEventExpose *event)
+{
+  cairo_t *cr;
+  GList *iter;
+
+  cr = gdk_cairo_create (widget->window);
+  gdk_cairo_region (cr, event->region);
+  cairo_clip (cr);
+
+  cairo_set_source_rgb (cr, 1, 1, 1);
+  cairo_rectangle (cr, 0, 0, widget->allocation.width, widget->allocation.height);
+  cairo_fill (cr);
+
+  for (iter = canvas_items; iter; iter = iter->next)
+    canvas_item_draw (iter->data, cr, FALSE);
+
+  if (drop_item)
+    canvas_item_draw (drop_item, cr, TRUE);
+
+  cairo_destroy (cr);
+
+  return TRUE;
+}
+
+/*****************************/
+/* ====== Palette DnD ====== */
+/*****************************/
+
+static void
+palette_drop_item (GtkToolItem      *drag_item,
+                   EggToolItemGroup *drop_group,
+                   gint              x,
+                   gint              y)
+{
+  GtkWidget *drag_group = gtk_widget_get_parent (GTK_WIDGET (drag_item));
+  GtkToolItem *drop_item = egg_tool_item_group_get_drop_item (drop_group, x, y);
+  gint drop_position = -1;
+
+  if (drop_item)
+    drop_position = egg_tool_item_group_get_item_position (EGG_TOOL_ITEM_GROUP (drop_group), drop_item);
+
+  if (EGG_TOOL_ITEM_GROUP (drag_group) != drop_group)
+    {
+      gboolean homogeneous, expand, fill, new_row;
+      
+      g_object_ref (drag_item);
+      gtk_container_child_get (GTK_CONTAINER (drag_group), GTK_WIDGET (drag_item),
+                               "homogeneous", &homogeneous,
+                               "expand", &expand,
+                               "fill", &fill,
+                               "new-row", &new_row,
+                               NULL);
+      gtk_container_remove (GTK_CONTAINER (drag_group), GTK_WIDGET (drag_item));
+      egg_tool_item_group_insert (EGG_TOOL_ITEM_GROUP (drop_group),
+                                  drag_item, drop_position);
+      gtk_container_child_set (GTK_CONTAINER (drop_group), GTK_WIDGET (drag_item),
+                               "homogeneous", homogeneous,
+                               "expand", expand,
+                               "fill", fill,
+                               "new-row", new_row,
+                               NULL);
+      g_object_unref (drag_item);
+    }
+  else
+    egg_tool_item_group_set_item_position (EGG_TOOL_ITEM_GROUP (drop_group),
+                                           drag_item, drop_position);
+}
+
+static void
+palette_drop_group (EggToolPalette *palette,
+                    GtkWidget      *drag_group,
+                    GtkWidget      *drop_group)
+{
+  gint drop_position = -1;
+
+  if (drop_group)
+    drop_position = egg_tool_palette_get_group_position (palette, drop_group);
+
+  egg_tool_palette_set_group_position (palette, drag_group, drop_position);
+}
+
+static void
+palette_drag_data_received (GtkWidget        *widget,
+                            GdkDragContext   *context,
+                            gint              x,
+                            gint              y,
+                            GtkSelectionData *selection,
+                            guint             info G_GNUC_UNUSED,
+                            guint             time G_GNUC_UNUSED,
+                            gpointer          data G_GNUC_UNUSED)
+{
+  GtkWidget *drag_palette = gtk_drag_get_source_widget (context);
+  GtkWidget *drag_item = NULL, *drop_group = NULL;
+
+  while (drag_palette && !EGG_IS_TOOL_PALETTE (drag_palette))
+    drag_palette = gtk_widget_get_parent (drag_palette);
+
+  if (drag_palette)
+    {
+      drag_item = egg_tool_palette_get_drag_item (EGG_TOOL_PALETTE (drag_palette), selection);
+      drop_group = egg_tool_palette_get_drop_group (EGG_TOOL_PALETTE (widget), x, y);
+    }
+
+  if (EGG_IS_TOOL_ITEM_GROUP (drag_item))
+    palette_drop_group (EGG_TOOL_PALETTE (drag_palette), drag_item, drop_group);
+  else if (GTK_IS_TOOL_ITEM (drag_item) && drop_group)
+    palette_drop_item (GTK_TOOL_ITEM (drag_item),
+                       EGG_TOOL_ITEM_GROUP (drop_group),
+                       x - GTK_WIDGET (drop_group)->allocation.x,
+                       y - GTK_WIDGET (drop_group)->allocation.y);
+}
+
+/********************************/
+/* ====== Passive Canvas ====== */
+/********************************/
+
+static void
+passive_canvas_drag_data_received (GtkWidget        *widget,
+                                   GdkDragContext   *context,
+                                   gint              x,
+                                   gint              y,
+                                   GtkSelectionData *selection,
+                                   guint             info G_GNUC_UNUSED,
+                                   guint             time G_GNUC_UNUSED,
+                                   gpointer          data G_GNUC_UNUSED)
+{
+  /* find the tool button, which is the source of this DnD operation */
+
+  GtkWidget *palette = gtk_drag_get_source_widget (context);
+  CanvasItem *canvas_item = NULL;
+  GtkWidget *tool_item = NULL;
+
+  while (palette && !EGG_IS_TOOL_PALETTE (palette))
+    palette = gtk_widget_get_parent (palette);
+
+  if (palette)
+    tool_item = egg_tool_palette_get_drag_item (EGG_TOOL_PALETTE (palette), selection);
+
+  g_assert (NULL == drop_item);
+
+  /* append a new canvas item when a tool button was found */
+
+  if (GTK_IS_TOOL_ITEM (tool_item))
+    canvas_item = canvas_item_new (widget, GTK_TOOL_BUTTON (tool_item), x, y);
+
+  if (canvas_item)
+    {
+      canvas_items = g_list_append (canvas_items, canvas_item);
+      gtk_widget_queue_draw (widget);
+    }
+}
+
+/************************************/
+/* ====== Interactive Canvas ====== */
+/************************************/
+
+static gboolean
+interactive_canvas_drag_motion (GtkWidget        *widget,
+                                GdkDragContext   *context,
+                                gint              x,
+                                gint              y,
+                                guint             time,
+                                gpointer          data G_GNUC_UNUSED)
+{
+  if (drop_item)
+    {
+      /* already have a drop indicator - just update position */
+
+      drop_item->x = x;
+      drop_item->y = y;
+
+      gtk_widget_queue_draw (widget);
+      gdk_drag_status (context, GDK_ACTION_COPY, time);
+    }
+  else
+    {
+      /* request DnD data for creating a drop indicator */
+
+      GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
+
+      if (!target)
+        return FALSE;
+
+      gtk_drag_get_data (widget, context, target, time);
+    }
+
+  return TRUE;
+}
+
+static void
+interactive_canvas_drag_data_received (GtkWidget        *widget,
+                                       GdkDragContext   *context,
+                                       gint              x,
+                                       gint              y,
+                                       GtkSelectionData *selection,
+                                       guint             info G_GNUC_UNUSED,
+                                       guint             time G_GNUC_UNUSED,
+                                       gpointer          data G_GNUC_UNUSED)
+
+{
+  /* find the tool button, which is the source of this DnD operation */
+
+  GtkWidget *palette = gtk_drag_get_source_widget (context);
+  GtkWidget *tool_item = NULL;
+
+  while (palette && !EGG_IS_TOOL_PALETTE (palette))
+    palette = gtk_widget_get_parent (palette);
+
+  if (palette)
+    tool_item = egg_tool_palette_get_drag_item (EGG_TOOL_PALETTE (palette), selection);
+
+  /* create a drop indicator when a tool button was found */
+
+  g_assert (NULL == drop_item);
+
+  if (GTK_IS_TOOL_ITEM (tool_item))
+    {
+      drop_item = canvas_item_new (widget, GTK_TOOL_BUTTON (tool_item), x, y);
+      gdk_drag_status (context, GDK_ACTION_COPY, time);
+      gtk_widget_queue_draw (widget);
+    }
+}
+
+static gboolean
+interactive_canvas_drag_drop (GtkWidget        *widget,
+                              GdkDragContext   *context G_GNUC_UNUSED,
+                              gint              x,
+                              gint              y,
+                              guint             time,
+                              gpointer          data    G_GNUC_UNUSED)
+{
+  if (drop_item)
+    {
+      /* turn the drop indicator into a real canvas item */
+
+      drop_item->x = x;
+      drop_item->y = y;
+
+      canvas_items = g_list_append (canvas_items, drop_item);
+      drop_item = NULL;
+
+      /* signal the item was accepted and redraw */
+
+      gtk_drag_finish (context, TRUE, FALSE, time);
+      gtk_widget_queue_draw (widget);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+interactive_canvas_real_drag_leave (gpointer data)
+{
+  if (drop_item)
+    {
+      GtkWidget *widget = GTK_WIDGET (data);
+
+      canvas_item_free (drop_item);
+      drop_item = NULL;
+
+      gtk_widget_queue_draw (widget);
+    }
+
+  return FALSE;
+}
+
+static void
+interactive_canvas_drag_leave (GtkWidget        *widget,
+                               GdkDragContext   *context G_GNUC_UNUSED,
+                               guint             time    G_GNUC_UNUSED,
+                               gpointer          data    G_GNUC_UNUSED)
+{
+  /* defer cleanup until a potential "drag-drop" signal was received */
+  g_idle_add (interactive_canvas_real_drag_leave, widget);
+}
+
+/*******************************/
+/* ====== Setup Test UI ====== */
+/*******************************/
+
+static void
+not_implemented (GtkAction *action,
+                 GtkWindow *parent)
+{
+  GtkWidget *dialog = gtk_message_dialog_new (parent,
+                                              GTK_DIALOG_MODAL |
+                                              GTK_DIALOG_DESTROY_WITH_PARENT,
+                                              GTK_MESSAGE_INFO,
+                                              GTK_BUTTONS_CLOSE,
+                                              _("Not implemented yet."));
+  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                            _("Sorry, the '%s' action is not implemented."),
+                                            gtk_action_get_name (action));
+
+  gtk_dialog_run (GTK_DIALOG (dialog));
+  gtk_widget_destroy (dialog);
+}
+
+static void
+load_stock_items (EggToolPalette *palette)
+{
+  GtkWidget *group_af = egg_tool_item_group_new (_("Stock Icons (A-F)"));
+  GtkWidget *group_gn = egg_tool_item_group_new (_("Stock Icons (G-N)"));
+  GtkWidget *group_or = egg_tool_item_group_new (_("Stock Icons (O-R)"));
+  GtkWidget *group_sz = egg_tool_item_group_new (_("Stock Icons (S-Z)"));
+  GtkWidget *group = NULL;
+
+  GtkToolItem *item;
+  GSList *stock_ids;
+  GSList *iter;
+
+  stock_ids = gtk_stock_list_ids ();
+  stock_ids = g_slist_sort (stock_ids, (GCompareFunc) strcmp);
+
+  gtk_container_add (GTK_CONTAINER (palette), group_af);
+  gtk_container_add (GTK_CONTAINER (palette), group_gn);
+  gtk_container_add (GTK_CONTAINER (palette), group_or);
+  gtk_container_add (GTK_CONTAINER (palette), group_sz);
+
+  for (iter = stock_ids; iter; iter = g_slist_next (iter))
+    {
+      GtkStockItem stock_item;
+      gchar *id = iter->data;
+
+      switch (id[4])
+        {
+          case 'a':
+            group = group_af;
+            break;
+
+          case 'g':
+            group = group_gn;
+            break;
+
+          case 'o':
+            group = group_or;
+            break;
+
+          case 's':
+            group = group_sz;
+            break;
+        }
+
+      item = gtk_tool_button_new_from_stock (id);
+      gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (item), id);
+      gtk_tool_item_set_is_important (GTK_TOOL_ITEM (item), TRUE);
+      egg_tool_item_group_insert (EGG_TOOL_ITEM_GROUP (group), item, -1);
+
+      if (!gtk_stock_lookup (id, &stock_item) || !stock_item.label)
+        gtk_tool_button_set_label (GTK_TOOL_BUTTON (item), id);
+
+      g_free (id);
+    }
+
+  g_slist_free (stock_ids);
+}
+
+static void
+load_special_items (EggToolPalette *palette)
+{
+  GtkToolItem *item;
+  GtkWidget *group;
+
+  group = egg_tool_item_group_new (_("Advanced Features"));
+  gtk_container_add (GTK_CONTAINER (palette), group);
+
+  item = gtk_tool_item_new ();
+  gtk_container_add (GTK_CONTAINER (item), gtk_entry_new ());
+  gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (item))), "homogeneous=FALSE");
+  egg_tool_item_group_insert (EGG_TOOL_ITEM_GROUP (group), item, -1);
+  gtk_container_child_set (GTK_CONTAINER (group), GTK_WIDGET (item),
+                           "homogeneous", FALSE,
+                           NULL);
+
+  item = gtk_tool_item_new ();
+  gtk_container_add (GTK_CONTAINER (item), gtk_entry_new ());
+  gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (item))), "homogeneous=FALSE, expand=TRUE");
+  egg_tool_item_group_insert (EGG_TOOL_ITEM_GROUP (group), item, -1);
+  gtk_container_child_set (GTK_CONTAINER (group), GTK_WIDGET (item),
+                           "homogeneous", FALSE,
+                           "expand", TRUE,
+                           NULL);
+
+  item = gtk_tool_item_new ();
+  gtk_container_add (GTK_CONTAINER (item), gtk_entry_new ());
+  gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (item))), "homogeneous=FALSE, expand=TRUE, fill=FALSE");
+  egg_tool_item_group_insert (EGG_TOOL_ITEM_GROUP (group), item, -1);
+  gtk_container_child_set (GTK_CONTAINER (group), GTK_WIDGET (item),
+                           "homogeneous", FALSE,
+                           "expand", TRUE,
+                           "fill", FALSE,
+                           NULL);
+
+  item = gtk_tool_item_new ();
+  gtk_container_add (GTK_CONTAINER (item), gtk_entry_new ());
+  gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (item))), "homogeneous=FALSE, expand=TRUE, new-row=TRUE");
+  egg_tool_item_group_insert (EGG_TOOL_ITEM_GROUP (group), item, -1);
+  gtk_container_child_set (GTK_CONTAINER (group), GTK_WIDGET (item),
+                           "homogeneous", FALSE,
+                           "expand", TRUE,
+                           "new-row", TRUE,
+                           NULL);
+ 
+  item = gtk_tool_button_new_from_stock (GTK_STOCK_GO_UP);
+  gtk_tool_item_set_tooltip_text (item, "Show on vertical palettes only");
+  egg_tool_item_group_insert (EGG_TOOL_ITEM_GROUP (group), item, -1);
+  gtk_tool_item_set_visible_horizontal (item, FALSE);
+
+  item = gtk_tool_button_new_from_stock (GTK_STOCK_GO_FORWARD);
+  gtk_tool_item_set_tooltip_text (item, "Show on horizontal palettes only");
+  egg_tool_item_group_insert (EGG_TOOL_ITEM_GROUP (group), item, -1);
+  gtk_tool_item_set_visible_vertical (item, FALSE);
+
+  item = gtk_tool_button_new_from_stock (GTK_STOCK_DELETE);
+  gtk_tool_item_set_tooltip_text (item, "Do not show at all");
+  egg_tool_item_group_insert (EGG_TOOL_ITEM_GROUP (group), item, -1);
+  gtk_widget_set_no_show_all (GTK_WIDGET (item), TRUE);
+
+  item = gtk_tool_button_new_from_stock (GTK_STOCK_FULLSCREEN);
+  gtk_tool_item_set_tooltip_text (item, "Expanded this item");
+  egg_tool_item_group_insert (EGG_TOOL_ITEM_GROUP (group), item, -1);
+  gtk_container_child_set (GTK_CONTAINER (group), GTK_WIDGET (item),
+                           "homogeneous", FALSE,
+                           "expand", TRUE,
+                           NULL);
+
+  item = gtk_tool_button_new_from_stock (GTK_STOCK_HELP);
+  gtk_tool_item_set_tooltip_text (item, "A regular item");
+  egg_tool_item_group_insert (EGG_TOOL_ITEM_GROUP (group), item, -1);
+}
+
+static gboolean
+drop_invalid_icon_size (GEnumValue *enum_value,
+                        gpointer    user_data G_GNUC_UNUSED)
+{
+  return (enum_value->value != GTK_ICON_SIZE_INVALID);
+}
+
+static void
+palette_notify_orientation (GObject    *object,
+                            GParamSpec *pspec G_GNUC_UNUSED,
+                            gpointer    data  G_GNUC_UNUSED)
+{
+  GtkWidget *scroller = gtk_widget_get_parent (GTK_WIDGET (object));
+  GtkWidget *parent = gtk_widget_get_parent (scroller);
+
+  GtkWidget *hpaned = g_object_get_data (object, "hpaned");
+  GtkWidget *vpaned = g_object_get_data (object, "vpaned");
+
+  g_object_ref (scroller);
+
+  if (parent)
+    gtk_container_remove (GTK_CONTAINER (parent), scroller);
+
+  switch (egg_tool_palette_get_orientation (EGG_TOOL_PALETTE (object)))
+    {
+      case GTK_ORIENTATION_VERTICAL:
+        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
+                                        GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+        gtk_paned_pack1 (GTK_PANED (hpaned), scroller, FALSE, FALSE);
+        break;
+
+      case GTK_ORIENTATION_HORIZONTAL:
+        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
+                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
+        gtk_paned_pack1 (GTK_PANED (vpaned), scroller, FALSE, FALSE);
+        break;
+    }
+
+  g_object_unref (scroller);
+}
+
+static void
+view_ellipsize_changed_cb (GtkWidget *widget,
+                      gpointer   data)
+{
+  GEnumValue *ellipsize = data;
+
+  egg_tool_item_group_set_ellipsize (EGG_TOOL_ITEM_GROUP (widget),
+                                     ellipsize->value);
+}
+
+static void
+view_ellipsize_changed (GEnumValue *value,
+                        gpointer    data)
+{
+  gtk_container_foreach (data, view_ellipsize_changed_cb, value);
+}
+
+static void
+view_exclusive_toggled_cb (GtkWidget *widget,
+                           gpointer   data)
+{
+  gboolean value = gtk_toggle_action_get_active (data);
+  GtkWidget *palette = gtk_widget_get_parent (widget);
+
+  egg_tool_palette_set_exclusive (EGG_TOOL_PALETTE (palette), widget, value);
+}
+
+static void
+view_exclusive_toggled (GtkToggleAction *action,
+                        gpointer         data)
+{
+  gtk_container_foreach (data, view_exclusive_toggled_cb, action);
+}
+
+static void
+view_expand_toggled_cb (GtkWidget *widget,
+                        gpointer   data)
+{
+  gboolean value = gtk_toggle_action_get_active (data);
+  GtkWidget *palette = gtk_widget_get_parent (widget);
+
+  egg_tool_palette_set_expand (EGG_TOOL_PALETTE (palette), widget, value);
+}
+
+static void
+view_expand_toggled (GtkToggleAction *action,
+                     gpointer         data)
+{
+  gtk_container_foreach (data, view_expand_toggled_cb, action);
+}
+
+static GtkWidget*
+create_ui (void)
+{
+  static const gchar ui_spec[] = "              \
+    <ui>                                        \
+      <menubar>                                 \
+        <menu action='FileMenu'>                \
+          <menuitem action='FileNew' />         \
+          <menuitem action='FileOpen' />        \
+          <separator />                         \
+          <menuitem action='FileSave' />        \
+          <menuitem action='FileSaveAs' />      \
+          <separator />                         \
+          <menuitem action='FileClose' />       \
+          <menuitem action='FileQuit' />        \
+        </menu>                                 \
+                                                \
+        <menu action='ViewMenu'>                \
+          <menuitem action='ViewIconSize' />    \
+          <menuitem action='ViewOrientation' /> \
+          <menuitem action='ViewStyle' />       \
+          <separator />                         \
+          <menuitem action='ViewEllipsize' />   \
+          <menuitem action='ViewExclusive' />   \
+          <menuitem action='ViewExpand' />      \
+        </menu>                                 \
+                                                \
+        <menu action='HelpMenu'>                \
+          <menuitem action='HelpAbout' />       \
+        </menu>                                 \
+      </menubar>                                \
+                                                \
+      <toolbar>                                 \
+        <toolitem action='FileNew' />           \
+        <toolitem action='FileOpen' />          \
+        <toolitem action='FileSave' />          \
+        <separator />                           \
+        <toolitem action='ViewIconSize' />      \
+        <toolitem action='ViewOrientation' />   \
+        <toolitem action='ViewStyle' />         \
+        <separator />                           \
+        <toolitem action='ViewEllipsize' />     \
+        <toolitem action='ViewExclusive' />     \
+        <toolitem action='ViewExpand' />        \
+        <separator />                           \
+        <toolitem action='HelpAbout' />         \
+      </toolbar>                                \
+    </ui>";
+
+  static GtkActionEntry actions[] = {
+    { "FileMenu",   NULL, N_("_File"),       NULL, NULL, NULL },
+    { "FileNew",    GTK_STOCK_NEW, NULL,     NULL, NULL, G_CALLBACK (not_implemented) },
+    { "FileOpen",   GTK_STOCK_OPEN, NULL,    NULL, NULL, G_CALLBACK (not_implemented) },
+    { "FileSave",   GTK_STOCK_SAVE, NULL,    NULL, NULL, G_CALLBACK (not_implemented) },
+    { "FileSaveAs", GTK_STOCK_SAVE_AS, NULL, NULL, NULL, G_CALLBACK (not_implemented) },
+    { "FileClose",  GTK_STOCK_CLOSE, NULL,   NULL, NULL, G_CALLBACK (not_implemented) },
+    { "FileQuit",   GTK_STOCK_QUIT, NULL,    NULL, NULL, G_CALLBACK (gtk_main_quit) },
+    { "ViewMenu",   NULL, N_("_View"),       NULL, NULL, NULL },
+    { "HelpMenu",   NULL, N_("_Help"),       NULL, NULL, NULL },
+    { "HelpAbout",  GTK_STOCK_ABOUT, NULL,   NULL, NULL, G_CALLBACK (not_implemented) },
+  };
+
+  GtkActionGroup *group;
+  GError *error = NULL;
+  GtkUIManager *ui;
+
+  GtkWidget *window, *vbox, *hpaned, *vpaned;
+  GtkWidget *menubar, *toolbar, *notebook;
+  GtkWidget *palette, *palette_scroller;
+  GtkWidget *contents, *contents_scroller;
+  GtkAction *action;
+
+  /* ===== menubar/toolbar ===== */
+
+  palette = egg_tool_palette_new ();
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  group = gtk_action_group_new ("");
+  ui = gtk_ui_manager_new ();
+
+  gtk_action_group_add_actions (group, actions, G_N_ELEMENTS (actions), window);
+
+  action = egg_enum_action_new ("ViewIconSize", _("Icon Size"), NULL, GTK_TYPE_ICON_SIZE);
+  egg_enum_action_set_filter (EGG_ENUM_ACTION (action), drop_invalid_icon_size, NULL, NULL);
+  egg_enum_action_bind (EGG_ENUM_ACTION (action), G_OBJECT (palette), "icon-size");
+  gtk_action_group_add_action (group, action);
+
+  action = egg_enum_action_new ("ViewOrientation", _("Orientation"), NULL, GTK_TYPE_ORIENTATION);
+  egg_enum_action_bind (EGG_ENUM_ACTION (action), G_OBJECT (palette), "orientation");
+  gtk_action_group_add_action (group, action);
+
+  action = egg_enum_action_new ("ViewStyle", _("Style"), NULL, GTK_TYPE_TOOLBAR_STYLE);
+  egg_enum_action_bind (EGG_ENUM_ACTION (action), G_OBJECT (palette), "toolbar-style");
+  gtk_action_group_add_action (group, action);
+
+  action = egg_enum_action_new ("ViewEllipsize", _("Ellipsize Headers"), NULL, PANGO_TYPE_ELLIPSIZE_MODE);
+  egg_enum_action_connect (EGG_ENUM_ACTION (action), view_ellipsize_changed, palette);
+  gtk_action_group_add_action (group, action);
+
+  action = GTK_ACTION (gtk_toggle_action_new ("ViewExclusive", _("Exclusive Groups"), NULL, NULL));
+  g_signal_connect (action, "toggled", G_CALLBACK (view_exclusive_toggled), palette);
+  gtk_action_group_add_action (group, action);
+
+  action = GTK_ACTION (gtk_toggle_action_new ("ViewExpand", _("Expand Groups"), NULL, NULL));
+  g_signal_connect (action, "toggled", G_CALLBACK (view_expand_toggled), palette);
+  gtk_action_group_add_action (group, action);
+
+  gtk_ui_manager_insert_action_group (ui, group, -1);
+
+  if (!gtk_ui_manager_add_ui_from_string (ui, ui_spec, sizeof ui_spec - 1, &error))
+    {
+      g_message ("building ui_spec failed: %s", error->message);
+      g_clear_error (&error);
+    }
+
+  menubar = gtk_ui_manager_get_widget (ui, "/menubar");
+  toolbar = gtk_ui_manager_get_widget (ui, "/toolbar");
+
+  gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), FALSE);
+
+  /* ===== palette ===== */
+
+  load_stock_items (EGG_TOOL_PALETTE (palette));
+  load_special_items (EGG_TOOL_PALETTE (palette));
+
+  g_signal_connect (palette, "notify::orientation",
+                    G_CALLBACK (palette_notify_orientation),
+                    NULL);
+
+  palette_scroller = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (palette_scroller),
+                                  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+  gtk_container_set_border_width (GTK_CONTAINER (palette_scroller), 6);
+  gtk_container_add (GTK_CONTAINER (palette_scroller), palette);
+  gtk_widget_show_all (palette_scroller);
+
+  /* ===== notebook ===== */
+
+  notebook = gtk_notebook_new ();
+  gtk_container_set_border_width (GTK_CONTAINER (notebook), 6);
+
+  /* ===== DnD for tool items ===== */
+
+  g_signal_connect (palette, "drag-data-received",
+                    G_CALLBACK (palette_drag_data_received),
+                    NULL);
+
+  egg_tool_palette_add_drag_dest (EGG_TOOL_PALETTE (palette),
+                                  palette, GTK_DEST_DEFAULT_ALL,
+                                  EGG_TOOL_PALETTE_DRAG_ITEMS |
+                                  EGG_TOOL_PALETTE_DRAG_GROUPS,
+                                  GDK_ACTION_MOVE);
+
+  /* ===== passive DnD dest ===== */
+
+  contents = gtk_drawing_area_new ();
+  gtk_widget_set_app_paintable (contents, TRUE);
+
+  g_object_connect (contents,
+                    "signal::expose-event",       canvas_expose_event, NULL,
+                    "signal::drag-data-received", passive_canvas_drag_data_received, NULL,
+                    NULL);
+
+  egg_tool_palette_add_drag_dest (EGG_TOOL_PALETTE (palette),
+                                  contents, GTK_DEST_DEFAULT_ALL,
+                                  EGG_TOOL_PALETTE_DRAG_ITEMS,
+                                  GDK_ACTION_COPY);
+
+  contents_scroller = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (contents_scroller),
+                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (contents_scroller), contents);
+  gtk_container_set_border_width (GTK_CONTAINER (contents_scroller), 6);
+
+  gtk_notebook_append_page (GTK_NOTEBOOK (notebook), contents_scroller,
+                            gtk_label_new ("Passive DnD Mode"));
+
+  /* ===== interactive DnD dest ===== */
+
+  contents = gtk_drawing_area_new ();
+  gtk_widget_set_app_paintable (contents, TRUE);
+
+  g_object_connect (contents,
+                    "signal::expose-event",       canvas_expose_event, NULL,
+                    "signal::drag-motion",        interactive_canvas_drag_motion, NULL,
+                    "signal::drag-data-received", interactive_canvas_drag_data_received, NULL,
+                    "signal::drag-leave",         interactive_canvas_drag_leave, NULL,
+                    "signal::drag-drop",          interactive_canvas_drag_drop, NULL,
+                    NULL);
+
+  egg_tool_palette_add_drag_dest (EGG_TOOL_PALETTE (palette),
+                                  contents, GTK_DEST_DEFAULT_HIGHLIGHT,
+                                  EGG_TOOL_PALETTE_DRAG_ITEMS,
+                                  GDK_ACTION_COPY);
+
+  contents_scroller = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (contents_scroller),
+                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (contents_scroller), contents);
+  gtk_container_set_border_width (GTK_CONTAINER (contents_scroller), 6);
+
+  gtk_notebook_append_page (GTK_NOTEBOOK (notebook), contents_scroller,
+                            gtk_label_new ("Interactive DnD Mode"));
+
+  /* ===== hpaned ===== */
+
+  hpaned = gtk_hpaned_new ();
+  gtk_paned_pack2 (GTK_PANED (hpaned), notebook, TRUE, FALSE);
+
+  g_object_set_data_full (G_OBJECT (palette), "hpaned",
+                          g_object_ref (hpaned),
+                          g_object_unref);
+
+  /* ===== vpaned ===== */
+
+  vpaned = gtk_vpaned_new ();
+  gtk_paned_pack2 (GTK_PANED (vpaned), hpaned, TRUE, FALSE);
+
+  g_object_set_data_full (G_OBJECT (palette), "vpaned",
+                          g_object_ref (vpaned),
+                          g_object_unref);
+
+  /* ===== vbox ===== */
+
+  vbox = gtk_vbox_new (FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (vbox), vpaned, TRUE, TRUE, 0);
+  gtk_widget_show_all (vbox);
+
+  /* ===== window ===== */
+
+  gtk_container_add (GTK_CONTAINER (window), vbox);
+  gtk_window_set_default_size (GTK_WINDOW (window), 600, 500);
+  gtk_window_add_accel_group (GTK_WINDOW (window), gtk_ui_manager_get_accel_group (ui));
+  g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
+
+  /* ===== final fixup ===== */
+
+  g_object_unref (ui);
+  return window;
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  GtkWidget *ui;
+
+  gtk_init (&argc, &argv);
+
+  gtk_rc_parse_string ("                                \
+    style 'egg-tool-item-group' {                       \
+      EggToolItemGroup::expander-size = 10              \
+    }                                                   \
+                                                        \
+    style 'egg-tool-item-group-header' {                \
+      bg[NORMAL] = @selected_bg_color                   \
+      fg[NORMAL] = @selected_fg_color                   \
+      bg[PRELIGHT] = shade(1.04, @selected_bg_color)    \
+      fg[PRELIGHT] = @selected_fg_color                 \
+      bg[ACTIVE] = shade(0.9, @selected_bg_color)       \
+      fg[ACTIVE] = shade(0.9, @selected_fg_color)       \
+                                                        \
+      font_name = 'Sans Serif Bold 10.'                 \
+      GtkButton::inner_border = { 0, 3, 0, 0 }          \
+    }                                                   \
+                                                        \
+    style 'egg-tool-item-group-button' {                \
+      GtkToolButton::icon-spacing = 12                  \
+    }                                                   \
+                                                        \
+    class 'EggToolItemGroup'                            \
+    style 'egg-tool-item-group'                         \
+                                                        \
+    widget_class '*<EggToolItemGroup>.GtkButton*'       \
+    style 'egg-tool-item-group-header'                  \
+                                                        \
+    widget_class '*<EggToolItemGroup>.GtkToolButton'    \
+    style 'egg-tool-item-group-button'                  \
+    ");
+
+  ui = create_ui ();
+  gtk_widget_show (ui);
+  gtk_main ();
+
+  return 0;
+}

Added: trunk/glom/utility_widgets/egg/util/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/glom/utility_widgets/egg/util/Makefile.am	Mon Jun 16 10:23:15 2008
@@ -0,0 +1,29 @@
+INCLUDES = \
+	$(EGG_CFLAGS)
+
+noinst_LTLIBRARIES=libeggutil.la
+
+eggmarshalers.h: eggmarshalers.list
+	cd $(srcdir) \
+	&& $(GLIB_GENMARSHAL) --prefix=_egg_marshal eggmarshalers.list --header > xgen-emh \
+	&& cp xgen-emh eggmarshalers.h \
+	&& rm -f xgen-emh xgen-emh~
+
+eggmarshalers.c: eggmarshalers.list
+	cd $(srcdir) \
+	&& $(GLIB_GENMARSHAL) --prefix=_egg_marshal eggmarshalers.list --body > xgen-emc \
+	&& cp xgen-emc eggmarshalers.c \
+	&& rm -f xgen-emc xgen-emc~
+
+egg-marshal.c: eggmarshalers.h eggmarshalers.c
+
+libeggutil_la_SOURCES =	\
+	egg-marshal.c
+
+noinst_HEADERS = \
+	eggmarshalers.h	\
+	eggintl.h \
+	egg-macros.h
+
+EXTRA_DIST=			\
+	eggmarshalers.list

Added: trunk/glom/utility_widgets/egg/util/egg-macros.h
==============================================================================
--- (empty file)
+++ trunk/glom/utility_widgets/egg/util/egg-macros.h	Mon Jun 16 10:23:15 2008
@@ -0,0 +1,154 @@
+/**
+ * Useful macros.
+ *
+ * Author:
+ *   Darin Adler <darin bentspoon com>
+ *
+ * Copyright 2001 Ben Tea Spoons, Inc.
+ */
+#ifndef _EGG_MACROS_H_
+#define _EGG_MACROS_H_
+
+#include <glib/gmacros.h>
+
+G_BEGIN_DECLS
+
+/* Macros for defining classes.  Ideas taken from Nautilus and GOB. */
+
+/* Define the boilerplate type stuff to reduce typos and code size.  Defines
+ * the get_type method and the parent_class static variable. */
+
+#define EGG_BOILERPLATE(type, type_as_function, corba_type,		\
+			   parent_type, parent_type_macro,		\
+			   register_type_macro)				\
+static void type_as_function ## _class_init    (type ## Class *klass);	\
+static void type_as_function ## _instance_init (type          *object);	\
+static parent_type ## Class *parent_class = NULL;			\
+static void								\
+type_as_function ## _class_init_trampoline (gpointer klass,		\
+					    gpointer data)		\
+{									\
+	parent_class = (parent_type ## Class *)g_type_class_ref (	\
+		parent_type_macro);					\
+	type_as_function ## _class_init ((type ## Class *)klass);	\
+}									\
+GType									\
+type_as_function ## _get_type (void)					\
+{									\
+	static GType object_type = 0;					\
+	if (object_type == 0) {						\
+		static const GTypeInfo object_info = {			\
+		    sizeof (type ## Class),				\
+		    NULL,		/* base_init */			\
+		    NULL,		/* base_finalize */		\
+		    type_as_function ## _class_init_trampoline,		\
+		    NULL,		/* class_finalize */		\
+		    NULL,               /* class_data */		\
+		    sizeof (type),					\
+		    0,                  /* n_preallocs */		\
+		    (GInstanceInitFunc) type_as_function ## _instance_init \
+		};							\
+		object_type = register_type_macro			\
+			(type, type_as_function, corba_type,		\
+			 parent_type, parent_type_macro);		\
+	}								\
+	return object_type;						\
+}
+
+/* Just call the parent handler.  This assumes that there is a variable
+ * named parent_class that points to the (duh!) parent class.  Note that
+ * this macro is not to be used with things that return something, use
+ * the _WITH_DEFAULT version for that */
+#define EGG_CALL_PARENT(parent_class_cast, name, args)		\
+	((parent_class_cast(parent_class)->name != NULL) ?		\
+	 parent_class_cast(parent_class)->name args : (void)0)
+
+/* Same as above, but in case there is no implementation, it evaluates
+ * to def_return */
+#define EGG_CALL_PARENT_WITH_DEFAULT(parent_class_cast,		\
+					name, args, def_return)		\
+	((parent_class_cast(parent_class)->name != NULL) ?		\
+	 parent_class_cast(parent_class)->name args : def_return)
+
+/* Call a virtual method */
+#define EGG_CALL_VIRTUAL(object, get_class_cast, method, args) \
+    (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : (void)0)
+
+/* Call a virtual method with default */
+#define EGG_CALL_VIRTUAL_WITH_DEFAULT(object, get_class_cast, method, args, default) \
+    (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : default)
+
+#define EGG_CLASS_BOILERPLATE(type, type_as_function,		\
+				 parent_type, parent_type_macro)	\
+	EGG_BOILERPLATE(type, type_as_function, type,		\
+			   parent_type, parent_type_macro,		\
+			   EGG_REGISTER_TYPE)
+
+#define EGG_REGISTER_TYPE(type, type_as_function, corba_type,		\
+			    parent_type, parent_type_macro)		\
+	g_type_register_static (parent_type_macro, #type, &object_info, 0)
+
+
+#define EGG_DEFINE_BOXED_TYPE(TN, t_n) \
+EGG_DEFINE_BOXED_TYPE_WITH_CODE(TN, t_n, {});
+
+#define EGG_DEFINE_BOXED_TYPE_WITH_CODE(TN, t_n, _C_) \
+\
+static gpointer t_n##_copy (gpointer boxed); \
+static void t_n##_free (gpointer boxed); \
+\
+EGG_DEFINE_BOXED_TYPE_EXTENDED(TN, t_n, t_n##_copy, t_n##_free, _C_);
+
+#define EGG_DEFINE_BOXED_TYPE_EXTENDED(TN, t_n, b_c, b_f, _C_) \
+\
+_EGG_DEFINE_BOXED_TYPE_EXTENDED_BEGIN(TN, t_n, b_c, b_f) {_C_;} \
+_EGG_DEFINE_BOXED_TYPE_EXTENDED_END()
+
+#define _EGG_DEFINE_BOXED_TYPE_EXTENDED_BEGIN(TypeName, type_name, boxed_copy, boxed_free) \
+\
+GType \
+type_name##_get_type (void) \
+{ \
+  static volatile gsize g_define_type_id__volatile = 0; \
+  if (g_once_init_enter (&g_define_type_id__volatile)) \
+    { \
+      GType g_define_type_id = \
+        g_boxed_type_register_static (g_intern_static_string (#TypeName), \
+                                      boxed_copy, boxed_free); \
+      { /* custom code follows */
+#define _EGG_DEFINE_BOXED_TYPE_EXTENDED_END() \
+        /* following custom code */ \
+      }	\
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); \
+    } \
+  return g_define_type_id__volatile; \
+} /* closes type_name##_get_type() */
+
+#define EGG_DEFINE_QUARK(QN, q_n) \
+\
+GQuark \
+q_n##_quark (void) \
+{ \
+  static volatile gsize g_define_quark__volatile = 0; \
+  if (g_once_init_enter (&g_define_quark__volatile)) \
+    { \
+      GQuark g_define_quark = g_quark_from_string (#QN); \
+      g_once_init_leave (&g_define_quark__volatile, g_define_quark); \
+    } \
+  return g_define_quark__volatile; \
+}
+
+#define EGG_IS_POSITIVE_RESPONSE(response_id) \
+  ((response_id) == GTK_RESPONSE_ACCEPT || \
+   (response_id) == GTK_RESPONSE_OK     || \
+   (response_id) == GTK_RESPONSE_YES    || \
+   (response_id) == GTK_RESPONSE_APPLY)
+
+#define EGG_IS_NEGATIVE_RESPONSE(response_id) \
+  ((response_id) == GTK_RESPONSE_REJECT || \
+   (response_id) == GTK_RESPONSE_CANCEL || \
+   (response_id) == GTK_RESPONSE_NO)
+
+G_END_DECLS
+
+#endif /* _EGG_MACROS_H_ */

Added: trunk/glom/utility_widgets/egg/util/egg-marshal.c
==============================================================================
--- (empty file)
+++ trunk/glom/utility_widgets/egg/util/egg-marshal.c	Mon Jun 16 10:23:15 2008
@@ -0,0 +1,2 @@
+#include "eggmarshalers.h"
+#include "eggmarshalers.c"

Added: trunk/glom/utility_widgets/egg/util/eggintl.h
==============================================================================
--- (empty file)
+++ trunk/glom/utility_widgets/egg/util/eggintl.h	Mon Jun 16 10:23:15 2008
@@ -0,0 +1,8 @@
+#ifndef __EGG_INTL_H__
+#define __EGG_INTL_H__
+
+/* We don't support gettext yet, dunno if we should /Anders */
+#define _(x) (x)
+#define N_(x) (x)
+
+#endif /* __EGG_INTL_H__ */

Added: trunk/glom/utility_widgets/egg/util/eggmarshalers.list
==============================================================================
--- (empty file)
+++ trunk/glom/utility_widgets/egg/util/eggmarshalers.list	Mon Jun 16 10:23:15 2008
@@ -0,0 +1,20 @@
+VOID:OBJECT,OBJECT
+VOID:OBJECT,STRING,LONG,LONG
+VOID:OBJECT,LONG
+VOID:OBJECT,STRING,STRING
+VOID:UINT,UINT
+BOOLEAN:INT
+BOOLEAN:ENUM
+BOOLEAN:VOID
+OBJECT:VOID
+VOID:VOID
+VOID:INT,INT
+VOID:UINT,UINT
+VOID:BOOLEAN
+VOID:OBJECT,ENUM,BOXED
+VOID:BOXED
+BOOLEAN:BOOLEAN
+BOOLEAN:OBJECT,STRING,STRING
+BOOLEAN:ENUM,INT
+STRING:POINTER
+STRING:STRING,STRING



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