aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/shortcut-bar/e-group-bar.c
diff options
context:
space:
mode:
Diffstat (limited to 'widgets/shortcut-bar/e-group-bar.c')
-rw-r--r--widgets/shortcut-bar/e-group-bar.c1498
1 files changed, 1498 insertions, 0 deletions
diff --git a/widgets/shortcut-bar/e-group-bar.c b/widgets/shortcut-bar/e-group-bar.c
new file mode 100644
index 0000000000..603aa6f78f
--- /dev/null
+++ b/widgets/shortcut-bar/e-group-bar.c
@@ -0,0 +1,1498 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@gtk.org>
+ *
+ * Copyright 1999, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ * ShortcutBar displays a vertical bar with a number of Groups, each of which
+ * contains any number of icons. It is used on the left of the main application
+ * window so users can easily access items such as folders and files.
+ */
+
+#include <math.h>
+
+#include <gnome.h>
+
+#include "e-group-bar.h"
+
+#define E_GROUP_BAR_SCROLL_TIMEOUT 10
+#define E_GROUP_BAR_MIN_STEP_SIZE 4
+
+#define E_GROUP_BAR_AUTO_SHOW_TIMEOUT 300
+
+
+static void e_group_bar_class_init (EGroupBarClass *class);
+static void e_group_bar_init (EGroupBar *group_bar);
+static void e_group_bar_destroy (GtkObject *object);
+static void e_group_bar_realize (GtkWidget *widget);
+static void e_group_bar_unrealize (GtkWidget *widget);
+static void e_group_bar_map (GtkWidget *widget);
+static void e_group_bar_unmap (GtkWidget *widget);
+static void e_group_bar_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void e_group_bar_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gint e_group_bar_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static void e_group_bar_draw (GtkWidget *widget,
+ GdkRectangle *area);
+static void e_group_bar_add (GtkContainer *container,
+ GtkWidget *widget);
+static void e_group_bar_remove (GtkContainer *container,
+ GtkWidget *widget);
+static void e_group_bar_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data);
+
+static void e_group_bar_create_group_button_window (EGroupBar *group_bar,
+ gint group_num);
+static void e_group_bar_create_group_child_window (EGroupBar *group_bar,
+ gint group_num);
+static gint e_group_bar_get_group_button_position (EGroupBar *group_bar,
+ gint group_num);
+static gint e_group_bar_sum_button_heights (EGroupBar *group_bar,
+ gint first,
+ gint last);
+static gint e_group_bar_get_child_height (EGroupBar *group_bar);
+static gint e_group_bar_get_group_child_position (EGroupBar *group_bar,
+ gint group_num);
+
+static void e_group_bar_on_button_clicked (GtkWidget *group_button,
+ EGroupBar *group_bar);
+static gint e_group_bar_find_button (EGroupBar *group_bar,
+ GtkWidget *group_button);
+static void e_group_bar_start_animation (EGroupBar *group_bar,
+ gint group_num);
+static gboolean e_group_bar_timeout_handler (gpointer data);
+static gint e_group_bar_get_increment (EGroupBar *group_bar,
+ gint window_y,
+ gint window_target_y);
+static gboolean e_group_bar_on_button_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time,
+ EGroupBar *group_bar);
+static void e_group_bar_on_button_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time,
+ EGroupBar *group_bar);
+static gboolean e_group_bar_auto_show (gpointer data);
+static void e_group_bar_stop_all_animation (EGroupBar *group_bar);
+
+
+static GtkContainerClass *parent_class;
+
+
+GtkType
+e_group_bar_get_type (void)
+{
+ static GtkType e_group_bar_type = 0;
+
+ if (!e_group_bar_type){
+ GtkTypeInfo e_group_bar_info = {
+ "EGroupBar",
+ sizeof (EGroupBar),
+ sizeof (EGroupBarClass),
+ (GtkClassInitFunc) e_group_bar_class_init,
+ (GtkObjectInitFunc) e_group_bar_init,
+ NULL, /* reserved 1 */
+ NULL, /* reserved 2 */
+ (GtkClassInitFunc) NULL
+ };
+
+ parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
+ e_group_bar_type = gtk_type_unique (GTK_TYPE_CONTAINER,
+ &e_group_bar_info);
+ }
+
+ return e_group_bar_type;
+}
+
+
+static void
+e_group_bar_class_init (EGroupBarClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkContainerClass *container_class;
+
+ object_class = (GtkObjectClass *) class;
+ widget_class = (GtkWidgetClass *) class;
+ container_class = (GtkContainerClass *) class;
+
+ /* Method override */
+ object_class->destroy = e_group_bar_destroy;
+
+ widget_class->realize = e_group_bar_realize;
+ widget_class->unrealize = e_group_bar_unrealize;
+ widget_class->map = e_group_bar_map;
+ widget_class->unmap = e_group_bar_unmap;
+ widget_class->size_request = e_group_bar_size_request;
+ widget_class->size_allocate = e_group_bar_size_allocate;
+ widget_class->expose_event = e_group_bar_expose;
+ widget_class->draw = e_group_bar_draw;
+
+ container_class->add = e_group_bar_add;
+ container_class->remove = e_group_bar_remove;
+ container_class->forall = e_group_bar_forall;
+}
+
+
+static void
+e_group_bar_init (EGroupBar *group_bar)
+{
+
+ GTK_WIDGET_UNSET_FLAGS (group_bar, GTK_NO_WINDOW);
+
+ /* We don't want child resizes to propagate up to the parent. */
+ gtk_container_set_resize_mode (GTK_CONTAINER (group_bar),
+ GTK_RESIZE_QUEUE);
+
+ group_bar->children = g_array_new (FALSE, FALSE,
+ sizeof (EGroupBarChild));
+
+ group_bar->current_group_num = -1;
+ group_bar->buttons_homogeneous = TRUE;
+ group_bar->max_button_height = 0;
+ group_bar->animation_timeout_id = 0;
+}
+
+
+/**
+ * e_group_bar_new:
+ * @Returns: a new #EGroupBar.
+ *
+ * Creates a new #EGroupBar.
+ **/
+GtkWidget *
+e_group_bar_new (void)
+{
+ GtkWidget *group_bar;
+
+ group_bar = GTK_WIDGET (gtk_type_new (e_group_bar_get_type ()));
+
+ return group_bar;
+}
+
+
+static void
+e_group_bar_destroy (GtkObject *object)
+{
+ EGroupBar *group_bar;
+
+ group_bar = E_GROUP_BAR (object);
+
+ e_group_bar_stop_all_animation (group_bar);
+
+ /* The parent GtkContainer class will automatically destroy all the
+ child widgets, but it calls gtk_container_foreach() so we must not
+ destroy our children array until after. */
+ GTK_OBJECT_CLASS (parent_class)->destroy (object);
+
+ g_array_free (group_bar->children, TRUE);
+}
+
+
+static void
+e_group_bar_realize (GtkWidget *widget)
+{
+ EGroupBar *group_bar;
+ EGroupBarChild *group;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+ gint border_width, group_num;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (E_IS_GROUP_BAR (widget));
+
+ group_bar = E_GROUP_BAR (widget);
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+ border_width = GTK_CONTAINER (group_bar)->border_width;
+
+ 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 - 2 * border_width;
+ attributes.height = widget->allocation.height - 2 * border_width;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+ attributes.event_mask = gtk_widget_get_events (widget);
+ attributes.event_mask |= (GDK_EXPOSURE_MASK);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y
+ | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+ 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);
+
+ gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
+
+ /* Create windows for all the buttons & group canvases. */
+ for (group_num = 0;
+ group_num < group_bar->children->len;
+ group_num++) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+ e_group_bar_create_group_button_window (group_bar, group_num);
+ e_group_bar_create_group_child_window (group_bar, group_num);
+ }
+}
+
+
+static void
+e_group_bar_unrealize (GtkWidget *widget)
+{
+ EGroupBar *group_bar;
+ EGroupBarChild *group;
+ gint group_num;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (E_IS_GROUP_BAR (widget));
+
+ group_bar = E_GROUP_BAR (widget);
+
+ /* Destroy the windows for all the buttons & group canvases. */
+ for (group_num = 0;
+ group_num < group_bar->children->len;
+ group_num++) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+
+ if (group->button_window) {
+ gdk_window_set_user_data (group->button_window, NULL);
+ gdk_window_destroy (group->button_window);
+ group->button_window = NULL;
+ }
+ if (group->child_window) {
+ gdk_window_set_user_data (group->child_window, NULL);
+ gdk_window_destroy (group->child_window);
+ group->child_window = NULL;
+ }
+ }
+
+ if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+ (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+
+static void
+e_group_bar_map (GtkWidget *widget)
+{
+ EGroupBar *group_bar;
+ EGroupBarChild *group;
+ gint group_num;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (E_IS_GROUP_BAR (widget));
+
+ group_bar = E_GROUP_BAR (widget);
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
+
+ /* We do this in reverse order, and lower all the child windows, so
+ the stacking order ends up correct. */
+ for (group_num = group_bar->children->len - 1;
+ group_num >= 0;
+ group_num--) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+
+ if (group->button_window) {
+ gdk_window_show (group->button_window);
+ }
+
+ if (group->button
+ && GTK_WIDGET_VISIBLE (group->button)
+ && !GTK_WIDGET_MAPPED (group->button)) {
+ gtk_widget_map (group->button);
+ }
+
+ if (group->child_window) {
+ gdk_window_show (group->child_window);
+ gdk_window_lower (group->child_window);
+ }
+
+ if (group->child
+ && GTK_WIDGET_VISIBLE (group->child)
+ && !GTK_WIDGET_MAPPED (group->child))
+ gtk_widget_map (group->child);
+ }
+
+ gdk_window_show (widget->window);
+}
+
+
+static void
+e_group_bar_unmap (GtkWidget *widget)
+{
+ EGroupBar *group_bar;
+ EGroupBarChild *group;
+ gint group_num;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (E_IS_GROUP_BAR (widget));
+
+ group_bar = E_GROUP_BAR (widget);
+
+ GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
+
+ for (group_num = 0;
+ group_num < group_bar->children->len;
+ group_num++) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+
+ if (group->button_window) {
+ gdk_window_hide (group->button_window);
+ }
+
+ if (group->button
+ && GTK_WIDGET_MAPPED (group->button))
+ gtk_widget_unmap (group->button);
+
+ if (group->child_window) {
+ gdk_window_hide (group->child_window);
+ }
+
+ if (group->child
+ && GTK_WIDGET_MAPPED (group->child))
+ gtk_widget_unmap (group->child);
+ }
+
+ gdk_window_hide (widget->window);
+}
+
+
+static void
+e_group_bar_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ EGroupBar *group_bar;
+ EGroupBarChild *group;
+ gint group_num, max_child_height;
+ GtkRequisition child_requisition;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (E_IS_GROUP_BAR (widget));
+ g_return_if_fail (requisition != NULL);
+
+ group_bar = E_GROUP_BAR (widget);
+
+ /* We set the requisition width to the largest requested width of the
+ child widgets. The requisition height is set to the sum of all the
+ button heights plus the height of the largest child. */
+ requisition->width = 0;
+ requisition->height = 0;
+
+ /* We have to call size_request on all children, even though we don't
+ use the results, since some widgets like GtkLabel depend on it. */
+ group_bar->max_button_height = 0;
+ max_child_height = 0;
+ for (group_num = 0;
+ group_num < group_bar->children->len;
+ group_num++) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+
+ if (group->button) {
+ gtk_widget_size_request (group->button,
+ &child_requisition);
+ group->button_height = child_requisition.height;
+ } else {
+ group->button_height = 0;
+ }
+
+ group_bar->max_button_height = MAX (group_bar->max_button_height, group->button_height);
+ requisition->height += child_requisition.height;
+
+ if (group->child) {
+ gtk_widget_size_request (group->child,
+ &child_requisition);
+ max_child_height = MAX (max_child_height,
+ child_requisition.height);
+ requisition->width = MAX (requisition->width,
+ child_requisition.width);
+ }
+ }
+
+ requisition->height += max_child_height;
+
+ /* Add on the standard container border widths. */
+ requisition->width += GTK_CONTAINER (widget)->border_width * 2;
+ requisition->height += GTK_CONTAINER (widget)->border_width * 2;
+}
+
+
+static void
+e_group_bar_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ EGroupBar *group_bar;
+ EGroupBarChild *group;
+ gint group_num, border_width, width, height, child_height, y;
+ GtkAllocation button_allocation, child_allocation;
+
+ group_bar = E_GROUP_BAR (widget);
+
+ /* All child & button windows and widgets use the same width as the
+ group bar minus the border width. */
+ border_width = GTK_CONTAINER (widget)->border_width;
+ width = allocation->width - border_width * 2;
+ height = allocation->height - border_width * 2;
+
+ widget->allocation = *allocation;
+ if (GTK_WIDGET_REALIZED (widget))
+ gdk_window_move_resize (widget->window,
+ allocation->x + border_width,
+ allocation->y + border_width,
+ width, height);
+
+ /* All the child widgets use the same height. */
+ child_height = e_group_bar_get_child_height (group_bar);
+
+ /* The buttons are always in the top-left of the button windows, and
+ all have the same width. The height is calculated for each group. */
+ button_allocation.x = 0;
+ button_allocation.y = 0;
+ button_allocation.width = width;
+
+ /* The child widgets are always in the top-left of the child windows,
+ and all have the same width and height. */
+ child_allocation.x = 0;
+ child_allocation.y = 0;
+ child_allocation.width = width;
+ child_allocation.height = child_height;
+
+ /* Step through the groups, placing the windows as necessary, and
+ allocating the areas for the child widgets. Note that if a button
+ or child window is in the middle of an animation, we just resize it
+ and update the target position, and let the animation continue. */
+ for (group_num = 0;
+ group_num < group_bar->children->len;
+ group_num++) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+
+ /* Calculate the y position of the button, which depends on
+ the currently selected group and the button heights. */
+ y = e_group_bar_get_group_button_position (group_bar, group_num);
+ button_allocation.height = group_bar->buttons_homogeneous ? group_bar->max_button_height : group->button_height;
+
+ if (GTK_WIDGET_REALIZED (group->button)) {
+ if (group->button_window_in_animation) {
+ gdk_window_resize (group->button_window,
+ width, button_allocation.height);
+ group->button_window_target_y = y;
+ } else {
+ gdk_window_move_resize (group->button_window,
+ 0, y, width, button_allocation.height);
+ }
+ }
+ gtk_widget_size_allocate (group->button, &button_allocation);
+
+ if (GTK_WIDGET_REALIZED (group->child)) {
+ if (group->child_window_in_animation) {
+ gdk_window_resize (group->child_window,
+ width, child_height);
+ group->child_window_target_y = y + button_allocation.height;
+ } else {
+ gdk_window_move_resize (group->child_window,
+ 0, y + button_allocation.height,
+ width, child_height);
+ }
+ }
+ gtk_widget_size_allocate (group->child, &child_allocation);
+ }
+}
+
+
+static gint
+e_group_bar_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ EGroupBar *group_bar;
+ EGroupBarChild *group;
+ GdkEventExpose child_event;
+ gint group_num;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (E_IS_GROUP_BAR (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (GTK_WIDGET_DRAWABLE (widget)) {
+ group_bar = E_GROUP_BAR (widget);
+
+ child_event = *event;
+
+ for (group_num = 0;
+ group_num < group_bar->children->len;
+ group_num++) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+
+ if (event->window == group->button_window
+ && GTK_WIDGET_DRAWABLE (group->button)
+ && GTK_WIDGET_NO_WINDOW (group->button)
+ && gtk_widget_intersect (group->button, &event->area, &child_event.area))
+ gtk_widget_event (group->button, (GdkEvent*) &child_event);
+
+ if (event->window == group->child_window
+ && GTK_WIDGET_DRAWABLE (group->child)
+ && GTK_WIDGET_NO_WINDOW (group->child)
+ && gtk_widget_intersect (group->child, &event->area, &child_event.area))
+ gtk_widget_event (group->child, (GdkEvent*) &child_event);
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void
+e_group_bar_draw (GtkWidget *widget,
+ GdkRectangle *area)
+{
+ EGroupBar *group_bar;
+ EGroupBarChild *group;
+ gint group_num;
+#if 0
+ GdkRectangle child_area;
+#endif
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (E_IS_GROUP_BAR (widget));
+
+ g_print ("In e_group_bar_draw %i,%i %ix%i\n", area->x, area->y,
+ area->width, area->height);
+
+ if (GTK_WIDGET_DRAWABLE (widget)) {
+ group_bar = E_GROUP_BAR (widget);
+
+ for (group_num = 0;
+ group_num < group_bar->children->len;
+ group_num++) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+ /* FIXME */
+#if 0
+ if (GTK_WIDGET_DRAWABLE (child->widget) &&
+ gtk_widget_intersect (child->widget, area, &child_area))
+ gtk_widget_draw (child->widget, &child_area);
+#endif
+ }
+ }
+}
+
+
+static void
+e_group_bar_add (GtkContainer *container,
+ GtkWidget *widget)
+{
+ EGroupBar *group_bar;
+ GtkWidget *button;
+ gchar buffer[32];
+
+ g_return_if_fail (container != NULL);
+ g_return_if_fail (E_IS_GROUP_BAR (container));
+ g_return_if_fail (widget != NULL);
+
+ g_snprintf (buffer, sizeof (buffer), _("Group %i"),
+ group_bar->children->len + 1);
+ button = gtk_button_new_with_label (buffer);
+ gtk_widget_show (button);
+
+ e_group_bar_add_group (group_bar, widget, button, -1);
+}
+
+
+static void
+e_group_bar_remove (GtkContainer *container,
+ GtkWidget *widget)
+{
+ EGroupBar *group_bar;
+ gint group_num;
+
+ g_return_if_fail (container != NULL);
+ g_return_if_fail (E_IS_GROUP_BAR (container));
+ g_return_if_fail (widget != NULL);
+
+ group_bar = E_GROUP_BAR (container);
+
+ group_num = e_group_bar_get_group_num (group_bar, widget);
+ e_group_bar_remove_group (group_bar, group_num);
+}
+
+
+static void
+e_group_bar_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data)
+{
+ EGroupBar *group_bar;
+ EGroupBarChild *group;
+ gint group_num;
+ GList *tmp_list;
+
+ g_return_if_fail (container != NULL);
+ g_return_if_fail (E_IS_GROUP_BAR (container));
+ g_return_if_fail (callback != NULL);
+
+ group_bar = E_GROUP_BAR (container);
+
+ /* Note that drag-and-drop does not check the Z-order of widgets, so
+ we have to iterate through them from top to bottom, or it will
+ not work properly. We also have to use temporary lists so widgets
+ can be safely destroyed while iterating. */
+
+ if (include_internals) {
+ tmp_list = NULL;
+ for (group_num = group_bar->children->len - 1;
+ group_num >= 0;
+ group_num--) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+
+ if (group->button)
+ tmp_list = g_list_prepend (tmp_list,
+ group->button);
+ }
+
+ g_list_foreach (tmp_list, (GFunc) callback, callback_data);
+ g_list_free (tmp_list);
+ }
+
+ tmp_list = NULL;
+ for (group_num = 0;
+ group_num < group_bar->children->len;
+ group_num++) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+
+ if (group->child)
+ tmp_list = g_list_prepend (tmp_list, group->child);
+ }
+ g_list_foreach (tmp_list, (GFunc) callback, callback_data);
+ g_list_free (tmp_list);
+}
+
+
+static void
+e_group_bar_create_group_button_window (EGroupBar *group_bar,
+ gint group_num)
+{
+ EGroupBarChild *group;
+ GtkWidget *widget;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+ gint y, height, border_width;
+
+ widget = GTK_WIDGET (group_bar);
+
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+ y = e_group_bar_get_group_button_position (group_bar, group_num);
+ height = group_bar->buttons_homogeneous ? group_bar->max_button_height
+ : group->button_height;
+ border_width = GTK_CONTAINER (group_bar)->border_width;
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = 0;
+ attributes.y = y;
+ attributes.width = widget->allocation.width - 2 * border_width;
+ attributes.height = height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+ attributes.event_mask = gtk_widget_get_events (widget);
+ attributes.event_mask |= (GDK_EXPOSURE_MASK);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y
+ | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+ group->button_window = gdk_window_new (widget->window, &attributes,
+ attributes_mask);
+ gdk_window_set_user_data (group->button_window, widget);
+
+ gtk_widget_set_parent_window (group->button,
+ group->button_window);
+ gdk_window_set_back_pixmap (group->button_window, NULL, TRUE);
+}
+
+
+static void
+e_group_bar_create_group_child_window (EGroupBar *group_bar,
+ gint group_num)
+{
+ EGroupBarChild *group;
+ GtkWidget *widget;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+ gint y, height, border_width;
+
+ widget = GTK_WIDGET (group_bar);
+
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+ y = e_group_bar_get_group_button_position (group_bar, group_num);
+ y += group_bar->buttons_homogeneous ? group_bar->max_button_height
+ : group->button_height;
+ height = e_group_bar_get_child_height (group_bar);
+ border_width = GTK_CONTAINER (group_bar)->border_width;
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = 0;
+ attributes.y = y;
+ attributes.width = widget->allocation.width - 2 * border_width;
+ attributes.height = height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+ attributes.event_mask = gtk_widget_get_events (widget);
+ attributes.event_mask |= (GDK_EXPOSURE_MASK);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y
+ | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+ group->child_window = gdk_window_new (widget->window, &attributes,
+ attributes_mask);
+ gdk_window_set_user_data (group->child_window, widget);
+
+ gtk_widget_set_parent_window (GTK_WIDGET (group->child),
+ group->child_window);
+ gdk_window_set_back_pixmap (group->child_window, NULL, TRUE);
+}
+
+
+/* This returns the y position of a group's button within the EGroupBar window.
+ */
+static gint
+e_group_bar_get_group_button_position (EGroupBar *group_bar,
+ gint group_num)
+{
+ gint border_width, window_height, y;
+
+ border_width = GTK_CONTAINER (group_bar)->border_width;
+ window_height = GTK_WIDGET (group_bar)->allocation.height - 2 * border_width;
+
+ if (group_num <= group_bar->current_group_num)
+ y = e_group_bar_sum_button_heights (group_bar, 0, group_num - 1);
+ else
+ y = window_height - e_group_bar_sum_button_heights (group_bar, group_num, group_bar->children->len - 1);
+
+ return y;
+}
+
+
+/* This returns the sum of all the buttons from first to last inclusive. */
+static gint
+e_group_bar_sum_button_heights (EGroupBar *group_bar, gint first, gint last)
+{
+ EGroupBarChild *group;
+ gint height, group_num;
+
+ height = 0;
+
+ if (group_bar->buttons_homogeneous)
+ return (last - first + 1) * group_bar->max_button_height;
+
+ for (group_num = first; group_num <= last; group_num++) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+ height += group->button_height;
+ }
+
+ return height;
+}
+
+
+static gint
+e_group_bar_get_group_child_position (EGroupBar *group_bar,
+ gint group_num)
+{
+ EGroupBarChild *group;
+ gint y;
+
+ y = e_group_bar_get_group_button_position (group_bar, group_num);
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+ y += group_bar->buttons_homogeneous ? group_bar->max_button_height
+ : group->button_height;
+
+ return y;
+}
+
+
+static gint
+e_group_bar_get_child_height (EGroupBar *group_bar)
+{
+ EGroupBarChild *group;
+ gint group_num;
+
+ /* Start with the allocated height of the EGroupBar, less the border.*/
+ group_bar->child_height = GTK_WIDGET (group_bar)->allocation.height;
+ group_bar->child_height -= 2 * GTK_CONTAINER (group_bar)->border_width;
+
+ /* Now subtract the heights of all the buttons. */
+ if (group_bar->buttons_homogeneous) {
+ group_bar->child_height -= group_bar->children->len * group_bar->max_button_height;
+ } else {
+ for (group_num = 0;
+ group_num < group_bar->children->len;
+ group_num++) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+ group_bar->child_height -= group->button_height;
+ }
+ }
+
+ return group_bar->child_height;
+}
+
+
+/*
+ * Insertion, reordering and deletion of items.
+ */
+
+/**
+ * e_group_bar_add_group:
+ * @group_bar: an #EGroupBar.
+ * @child: the child widget to add.
+ * @button: the button used to show the child widget.
+ * @position: the new group's position, or -1 to place it last.
+ * @Returns: the position of the new group.
+ *
+ * Adds a new group to a #EGroupBar at the given position.
+ **/
+gint
+e_group_bar_add_group (EGroupBar *group_bar,
+ GtkWidget *child,
+ GtkWidget *button,
+ gint position)
+{
+ EGroupBarChild *group, empty_group, *tmp_group;
+ gint group_num, tmp_group_num;
+
+ g_return_val_if_fail (group_bar != NULL, -1);
+ g_return_val_if_fail (E_IS_GROUP_BAR (group_bar), -1);
+ g_return_val_if_fail (child != NULL, -1);
+ g_return_val_if_fail (button != NULL, -1);
+ g_return_val_if_fail (GTK_IS_BUTTON (button), -1);
+
+ /* Append an empty group to the children array and get a pointer to
+ it, so we can use it like a normal group. */
+ group_num = group_bar->children->len;
+ g_array_append_val (group_bar->children, empty_group);
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+
+ /* Initialize the group. */
+ group->button = button;
+ group->button_window = NULL;
+ group->child = child;
+ group->child_window = NULL;
+ group->button_window_in_animation = FALSE;
+ group->child_window_in_animation = FALSE;
+ group->button_window_target_y = 0;
+ group->child_window_target_y = 0;
+
+ /* If we don't have a current group, set it to the first one. */
+ if (group_bar->current_group_num == -1)
+ group_bar->current_group_num = 0;
+
+ /* If the EGroupBar widget is realize, we need to create the child
+ windows to put the button & child in. */
+ if (GTK_WIDGET_REALIZED (group_bar)) {
+ e_group_bar_create_group_button_window (group_bar, group_num);
+ e_group_bar_create_group_child_window (group_bar, group_num);
+
+ /* We need to lower all the child windows of the previous
+ groups, in reverse order, to keep the stacking order
+ correct. */
+ for (tmp_group_num = group_num - 1;
+ tmp_group_num >= 0;
+ tmp_group_num--) {
+ tmp_group = &g_array_index (group_bar->children,
+ EGroupBarChild,
+ tmp_group_num);
+ gdk_window_lower (group->child_window);
+ }
+ }
+
+ gtk_widget_set_parent (group->button, GTK_WIDGET (group_bar));
+ gtk_widget_set_parent (group->child, GTK_WIDGET (group_bar));
+
+ if (GTK_WIDGET_REALIZED (group_bar)) {
+ gtk_widget_realize (group->button);
+ gtk_widget_realize (group->child);
+ }
+
+ if (GTK_WIDGET_VISIBLE (group_bar)
+ && GTK_WIDGET_MAPPED (group_bar)) {
+ if (group->button
+ && GTK_WIDGET_VISIBLE (group->button)
+ && !GTK_WIDGET_MAPPED (group->button)) {
+ gtk_widget_map (group->button);
+ gtk_widget_queue_resize (group->button);
+ }
+ if (group->child
+ && GTK_WIDGET_VISIBLE (group->child)
+ && !GTK_WIDGET_MAPPED (group->child)) {
+ gtk_widget_map (group->child);
+ gtk_widget_queue_resize (group->child);
+ }
+ }
+
+ gtk_signal_connect (GTK_OBJECT (group->button), "clicked",
+ GTK_SIGNAL_FUNC (e_group_bar_on_button_clicked),
+ group_bar);
+
+ gtk_signal_connect (GTK_OBJECT (group->button), "drag_motion",
+ GTK_SIGNAL_FUNC (e_group_bar_on_button_drag_motion),
+ group_bar);
+ gtk_signal_connect (GTK_OBJECT (group->button), "drag_leave",
+ GTK_SIGNAL_FUNC (e_group_bar_on_button_drag_leave),
+ group_bar);
+
+ return group_num;
+}
+
+
+/**
+ * e_group_bar_reorder_group:
+ * @group_bar: an #EGroupBar.
+ * @group_num: the index of the group to move.
+ * @new_position: the new position of the group.
+ *
+ * Moves a group to a new position within the #EGroupBar.
+ **/
+void
+e_group_bar_reorder_group (EGroupBar *group_bar,
+ gint group_num,
+ gint new_position)
+{
+ g_return_if_fail (E_IS_GROUP_BAR (group_bar));
+
+}
+
+
+/**
+ * e_group_bar_remove_group:
+ * @group_bar: an #EGroupBar.
+ * @group_num: the index of the group to remove.
+ *
+ * Removes a group from an #EGroupBar.
+ **/
+void
+e_group_bar_remove_group (EGroupBar *group_bar,
+ gint group_num)
+{
+ EGroupBarChild *group;
+
+ g_return_if_fail (E_IS_GROUP_BAR (group_bar));
+ g_return_if_fail (group_num >= 0);
+ g_return_if_fail (group_num < group_bar->children->len);
+
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+
+ /* Stop any animation. */
+ e_group_bar_stop_all_animation (group_bar);
+
+ gtk_widget_unparent (group->child);
+ if (group->button)
+ gtk_widget_unparent (group->button);
+
+ if (group->button_window) {
+ gdk_window_set_user_data (group->button_window, NULL);
+ gdk_window_destroy (group->button_window);
+ }
+ if (group->child_window) {
+ gdk_window_set_user_data (group->child_window, NULL);
+ gdk_window_destroy (group->child_window);
+ }
+
+ g_array_remove_index (group_bar->children, group_num);
+
+ /* Make sure the current group is valid. */
+ if (group_bar->current_group_num >= group_bar->children->len)
+ group_bar->current_group_num = group_bar->children->len - 1;
+
+ gtk_widget_queue_resize (GTK_WIDGET (group_bar));
+}
+
+
+/*
+ * Getting & setting the current group.
+ */
+
+/**
+ * e_group_bar_get_current_group_num:
+ * @group_bar: an #EGroupBar.
+ * @Returns: the index of the group currently displayed.
+ *
+ * Returns the index of the group currently displayed.
+ **/
+gint
+e_group_bar_get_current_group_num (EGroupBar *group_bar)
+{
+ g_return_val_if_fail (E_IS_GROUP_BAR (group_bar), -1);
+
+ return group_bar->current_group_num;
+}
+
+
+/**
+ * e_group_bar_set_current_group_num:
+ * @group_bar: an #EGroupBar.
+ * @Returns: the index of the group to display.
+ *
+ * Sets the group to display.
+ **/
+/* FIXME: animate option? May want to set group without animation. */
+void
+e_group_bar_set_current_group_num (EGroupBar *group_bar,
+ gint group_num)
+{
+ g_return_if_fail (E_IS_GROUP_BAR (group_bar));
+
+ /* If that already is the current group, just return. */
+ if (group_bar->current_group_num == group_num)
+ return;
+
+ /* FIXME: Set the target positions of the old current group and the
+ new current group, map the new group's child window, and create the
+ animation timeout, if we haven't already got one. */
+
+ group_bar->current_group_num = group_num;
+
+}
+
+
+/*
+ * Getting groups and group numbers.
+ */
+
+/**
+ * e_group_bar_get_nth_group:
+ * @group_bar: an #EGroupBar.
+ * @group_num: the index of the group to get.
+ * @Returns: the child widget at the given index.
+ *
+ * Returns the child widget at the given index.
+ **/
+GtkWidget*
+e_group_bar_get_nth_group (EGroupBar *group_bar,
+ gint group_num)
+{
+ EGroupBarChild *group;
+
+ g_return_val_if_fail (E_IS_GROUP_BAR (group_bar), NULL);
+ g_return_val_if_fail (group_num >= 0, NULL);
+ g_return_val_if_fail (group_num < group_bar->children->len, NULL);
+
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+ return group->child;
+}
+
+
+/**
+ * e_group_bar_get_group_num:
+ * @group_bar: an #EGroupBar.
+ * @child: the child widget to find.
+ * @Returns: the index of the group containing the given widget.
+ *
+ * Returns the index of the group containing the given child widget.
+ **/
+gint
+e_group_bar_get_group_num (EGroupBar *group_bar,
+ GtkWidget *child)
+{
+ EGroupBarChild *group;
+ gint group_num;
+
+ g_return_val_if_fail (E_IS_GROUP_BAR (group_bar), -1);
+ g_return_val_if_fail (child != NULL, -1);
+
+ for (group_num = 0;
+ group_num < group_bar->children->len;
+ group_num++) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+
+ if (group->child == child)
+ return group_num;
+ }
+
+ return -1;
+}
+
+
+/**
+ * e_group_bar_set_group_button_label:
+ * @group_bar: an #EGroupBar.
+ * @group_num: the index of the group.
+ * @label: the label widget to place in the group's button.
+ *
+ * Sets the label widget for the given group's button, replacing any existing
+ * widget in the button.
+ **/
+void
+e_group_bar_set_group_button_label (EGroupBar *group_bar,
+ gint group_num,
+ GtkWidget *label)
+{
+ EGroupBarChild *group;
+ GtkWidget *button_child;
+
+ g_return_if_fail (E_IS_GROUP_BAR (group_bar));
+ g_return_if_fail (group_num >= 0);
+ g_return_if_fail (group_num < group_bar->children->len);
+
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+
+ button_child = GTK_BIN (group->button)->child;
+ if (button_child) {
+ gtk_container_remove (GTK_CONTAINER (group->button),
+ button_child);
+ }
+
+ if (label)
+ gtk_container_add (GTK_CONTAINER (group->button), label);
+}
+
+
+/*
+ * Getting & setting the EGroupBar options.
+ */
+
+/**
+ * e_group_bar_get_buttons_homogeneous:
+ * @group_bar: an #EGroupBar.
+ * @Returns: TRUE if the buttons are homoegeneous.
+ *
+ * Returns TRUE if the buttons are homogeneous (i.e. all have the same height).
+ **/
+gboolean
+e_group_bar_get_buttons_homogeneous (EGroupBar *group_bar)
+{
+ g_return_val_if_fail (E_IS_GROUP_BAR (group_bar), TRUE);
+
+ return group_bar->buttons_homogeneous;
+}
+
+
+/**
+ * e_group_bar_set_buttons_homogeneous:
+ * @group_bar: an #EGroupBar.
+ * @homogeneous: TRUE if the buttons should be homoegeneous.
+ *
+ * Specifies whether the buttons should be homogeneous. When set to TRUE all
+ * the group buttons will be set to the same height (equal to the largest
+ * requested height). When set to FALSE the buttons will use their own
+ * individual requested heights.
+ **/
+void
+e_group_bar_set_buttons_homogeneous (EGroupBar *group_bar,
+ gboolean homogeneous)
+{
+ g_return_if_fail (E_IS_GROUP_BAR (group_bar));
+
+ /* Just return if the setting hasn't changed. */
+ if (group_bar->buttons_homogeneous == homogeneous)
+ return;
+
+ group_bar->buttons_homogeneous = homogeneous;
+
+ /* Update the position & sizes of the buttons. */
+ gtk_widget_queue_resize (GTK_WIDGET (group_bar));
+}
+
+
+static void
+e_group_bar_on_button_clicked (GtkWidget *group_button,
+ EGroupBar *group_bar)
+{
+ gint group_num;
+
+ /* Determine which group button was clicked. */
+ group_num = e_group_bar_find_button (group_bar, group_button);
+
+ if (group_num != -1)
+ e_group_bar_start_animation (group_bar, group_num);
+}
+
+
+/* This returns the group containing the given button, or -1 if not found. */
+static gint
+e_group_bar_find_button (EGroupBar *group_bar,
+ GtkWidget *group_button)
+{
+ EGroupBarChild *group;
+ gint group_num;
+
+ /* Determine which group button was clicked. */
+ for (group_num = 0;
+ group_num < group_bar->children->len;
+ group_num++) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+ if (group->button == group_button)
+ return group_num;
+ }
+
+ return -1;
+}
+
+
+static void
+e_group_bar_start_animation (EGroupBar *group_bar,
+ gint group_num)
+{
+ EGroupBarChild *group, *old_group;
+ gint old_group_num, step;
+
+ old_group_num = group_bar->current_group_num;
+
+ /* Return if it is already the current group. */
+ if (old_group_num == group_num)
+ return;
+
+ group_bar->current_group_num = group_num;
+
+ /* Calculate the target y position of the new current group button
+ and child, and map the child. */
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+ group->button_window_target_y = e_group_bar_get_group_button_position (group_bar, group_num);
+ group->button_window_in_animation = TRUE;
+
+ group->child_window_target_y = e_group_bar_get_group_child_position (group_bar, group_num);
+ group->child_window_in_animation = TRUE;
+
+ /* Calculate the target y position of the current group button and
+ child. */
+ old_group = &g_array_index (group_bar->children,
+ EGroupBarChild, old_group_num);
+ old_group->button_window_target_y = e_group_bar_get_group_button_position (group_bar, old_group_num);
+ old_group->button_window_in_animation = TRUE;
+
+ old_group->child_window_target_y = e_group_bar_get_group_child_position (group_bar, old_group_num);
+ old_group->child_window_in_animation = TRUE;
+
+ /* We also need to animate the buttons in between the old group and the
+ new group. */
+ step = (old_group_num < group_num) ? 1 : -1;
+ old_group_num += step;
+ while (old_group_num != group_num) {
+ old_group = &g_array_index (group_bar->children,
+ EGroupBarChild, old_group_num);
+ old_group->button_window_target_y = e_group_bar_get_group_button_position (group_bar, old_group_num);
+ old_group->button_window_in_animation = TRUE;
+
+ old_group->child_window_target_y = e_group_bar_get_group_child_position (group_bar, old_group_num);
+ old_group->child_window_in_animation = TRUE;
+
+ old_group_num += step;
+ }
+
+ /* Add a timeout handler if we haven't already got one. */
+ if (group_bar->animation_timeout_id == 0) {
+ group_bar->animation_timeout_id = g_timeout_add (E_GROUP_BAR_SCROLL_TIMEOUT, e_group_bar_timeout_handler, group_bar);
+ }
+}
+
+
+static gboolean
+e_group_bar_timeout_handler (gpointer data)
+{
+ EGroupBar *group_bar;
+ EGroupBarChild *group;
+ gint group_num, button_window_y, child_window_y;
+ gboolean finished = TRUE;
+
+ g_return_val_if_fail (E_IS_GROUP_BAR (data), FALSE);
+
+ group_bar = E_GROUP_BAR (data);
+
+ GDK_THREADS_ENTER ();
+
+ for (group_num = 0;
+ group_num < group_bar->children->len;
+ group_num++) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+
+ if (group->button_window_in_animation) {
+ gdk_window_get_position (group->button_window, NULL,
+ &button_window_y);
+ button_window_y += e_group_bar_get_increment (group_bar, button_window_y, group->button_window_target_y);
+ if (button_window_y == group->button_window_target_y)
+ group->button_window_in_animation = FALSE;
+ else
+ finished = FALSE;
+ gdk_window_move (group->button_window,
+ 0, button_window_y);
+ }
+ if (group->child_window_in_animation) {
+ gdk_window_get_position (group->child_window, NULL,
+ &child_window_y);
+ child_window_y += e_group_bar_get_increment (group_bar, child_window_y, group->child_window_target_y);
+ if (child_window_y == group->child_window_target_y)
+ group->child_window_in_animation = FALSE;
+ else
+ finished = FALSE;
+ gdk_window_move (group->child_window,
+ 0, child_window_y);
+ }
+
+ }
+
+ if (finished)
+ group_bar->animation_timeout_id = 0;
+
+ GDK_THREADS_LEAVE ();
+
+ return !finished;
+}
+
+
+static gint
+e_group_bar_get_increment (EGroupBar *group_bar,
+ gint window_y,
+ gint window_target_y)
+{
+ gdouble percentage;
+ gint distance, total_distance, step;
+
+ total_distance = group_bar->child_height;
+ distance = MIN (abs (window_target_y - window_y), total_distance);
+
+ /* Convert the distance into an angle between -PI/2 and PI/2, so we can
+ then do a cosine of it. */
+ percentage = cos (M_PI * ((gdouble)distance / (gdouble)total_distance) - M_PI / 2);
+
+ /* Now multiply by our maximum step size to get the step size. */
+ step = percentage * total_distance / 6;
+
+ /* Add it to the minimum step size, but don't go too far. */
+ step = step + E_GROUP_BAR_MIN_STEP_SIZE;
+ step = MIN (step, distance);
+
+ if (window_target_y > window_y)
+ return step;
+ else
+ return -step;
+}
+
+
+static gboolean
+e_group_bar_on_button_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time,
+ EGroupBar *group_bar)
+{
+ gint group_num;
+
+ if (!group_bar->auto_show_timeout_id) {
+ group_num = e_group_bar_find_button (group_bar, widget);
+ if (group_num != -1) {
+ group_bar->auto_show_timeout_id = gtk_timeout_add (E_GROUP_BAR_AUTO_SHOW_TIMEOUT, e_group_bar_auto_show, group_bar);
+ group_bar->auto_show_group_num = group_num;
+ }
+ }
+ return TRUE;
+}
+
+
+static void
+e_group_bar_on_button_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time,
+ EGroupBar *group_bar)
+{
+ if (group_bar->auto_show_timeout_id) {
+ gtk_timeout_remove (group_bar->auto_show_timeout_id);
+ group_bar->auto_show_timeout_id = 0;
+ }
+}
+
+
+static gboolean
+e_group_bar_auto_show (gpointer data)
+{
+ EGroupBar *group_bar;
+
+ g_return_val_if_fail (E_IS_GROUP_BAR (data), FALSE);
+
+ group_bar = E_GROUP_BAR (data);
+
+ GDK_THREADS_ENTER ();
+
+ e_group_bar_start_animation (group_bar,
+ group_bar->auto_show_group_num);
+
+ group_bar->auto_show_timeout_id = 0;
+
+ GDK_THREADS_LEAVE ();
+
+ return FALSE;
+}
+
+
+/* This removes all timeouts and sets all 'in_animation' flags to FALSE. */
+static void
+e_group_bar_stop_all_animation (EGroupBar *group_bar)
+{
+ EGroupBarChild *group;
+ gint group_num;
+
+ if (group_bar->animation_timeout_id) {
+ g_source_remove (group_bar->animation_timeout_id);
+ group_bar->animation_timeout_id = 0;
+ }
+ if (group_bar->auto_show_timeout_id) {
+ g_source_remove (group_bar->auto_show_timeout_id);
+ group_bar->auto_show_timeout_id = 0;
+ }
+
+ for (group_num = 0;
+ group_num < group_bar->children->len;
+ group_num++) {
+ group = &g_array_index (group_bar->children,
+ EGroupBarChild, group_num);
+ group->button_window_in_animation = FALSE;
+ group->child_window_in_animation = FALSE;
+ }
+}
+