You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnucash/lib/goffice/gui-utils/go-dock-band.c

1987 lines
51 KiB

/* File import from bonoboui to gnumeric by import-bonobo. Do not edit. */
/* go-dock-band.c
Copyright (C) 1998 Free Software Foundation
The Gnome Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The Gnome 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the Gnome Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
Author: Ettore Perazzoli <ettore@comm2000.it>
*/
#include <gnumeric-config.h>
#include <glib/gi18n.h>
#include <string.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <libgnome/gnome-macros.h>
#include "go-dock.h"
#include "go-dock-band.h"
#include "go-dock-item.h"
GNOME_CLASS_BOILERPLATE (GoDockBand, go_dock_band,
GtkContainer, GTK_TYPE_CONTAINER);
#define noBONOBO_DOCK_BAND_DEBUG
/* FIXME: To be removed. */
#if defined GO_DOCK_BAND_DEBUG && defined __GNUC__
#define DEBUG(x) \
do \
{ \
printf ("%s.%d: ", __FUNCTION__, __LINE__); \
printf x; \
putchar ('\n'); \
} \
while (0)
#else
#define DEBUG(x)
#endif
static void go_dock_band_size_request (GtkWidget *widget,
GtkRequisition *requisition);
static void go_dock_band_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static void go_dock_band_map (GtkWidget *widget);
static void go_dock_band_unmap (GtkWidget *widget);
static void go_dock_band_add (GtkContainer *container,
GtkWidget *child);
static void go_dock_band_remove (GtkContainer *container,
GtkWidget *widget);
static void go_dock_band_forall (GtkContainer *container,
gboolean include_internals,
GtkCallback callback,
gpointer callback_data);
static void go_dock_band_finalize (GObject *object);
static void size_allocate_child (GoDockBand *band,
GoDockBandChild *child,
guint space,
GtkAllocation *child_allocation);
static void size_allocate_small (GoDockBand *band,
GtkAllocation *allocation,
guint space,
guint requested_space);
static gboolean docking_allowed (GoDockBand *band,
GoDockItem *item);
static GList *find_child (GoDockBand *band,
GtkWidget *child);
static GList *prev_if_floating (GoDockBand *band,
GList *c);
static GList *next_if_floating (GoDockBand *band,
GList *c);
static GList *prev_not_floating (GoDockBand *band,
GList *c);
static GList *next_not_floating (GoDockBand *band,
GList *c);
static void calc_prev_and_foll_space (GoDockBand *band);
static guint attempt_move_backward (GoDockBand *band,
GList *child,
guint amount);
static guint attempt_move_forward (GoDockBand *band,
GList *child,
guint amount);
static gboolean dock_nonempty (GoDockBand *band,
GoDockItem *item,
GList *where,
gint x, gint y);
static gboolean dock_empty (GoDockBand *band,
GoDockItem *item,
GList *where,
gint x, gint y);
static gboolean dock_empty_right (GoDockBand *band,
GoDockItem *item,
GList *where,
gint x, gint y);
static gboolean check_guint_arg (GObject *object,
const gchar *name,
guint *value_return);
static void
go_dock_band_class_init (GoDockBandClass *klass)
{
GObjectClass *gobject_class;
GtkWidgetClass *widget_class;
GtkContainerClass *container_class;
gobject_class = (GObjectClass *) klass;
widget_class = (GtkWidgetClass *) klass;
container_class = (GtkContainerClass *) klass;
gobject_class->finalize = go_dock_band_finalize;
widget_class->map = go_dock_band_map;
widget_class->unmap = go_dock_band_unmap;
widget_class->size_request = go_dock_band_size_request;
widget_class->size_allocate = go_dock_band_size_allocate;
container_class->add = go_dock_band_add;
container_class->remove = go_dock_band_remove;
container_class->forall = go_dock_band_forall;
}
static void
go_dock_band_instance_init (GoDockBand *band)
{
GtkWidget *widget = GTK_WIDGET (band);
GTK_WIDGET_SET_FLAGS (band, GTK_NO_WINDOW);
band->_priv = NULL;
band->orientation = GTK_ORIENTATION_HORIZONTAL;
band->children = NULL;
band->num_children = 0;
band->floating_child = NULL;
band->doing_drag = FALSE;
band->max_space_requisition = 0;
band->tot_offsets = 0;
band->drag_allocation.x = band->drag_allocation.y = -1;
band->drag_allocation.width = band->drag_allocation.height = 0;
band->new_for_drag = FALSE;
if (GTK_WIDGET_VISIBLE (widget))
gtk_widget_queue_resize (widget);
}
static void
go_dock_band_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
GoDockBand *band;
GList *lp;
DEBUG (("entering function"));
band = GO_DOCK_BAND (widget);
band->max_space_requisition = 0;
band->tot_offsets = 0;
requisition->width = 0;
requisition->height = 0;
for (lp = band->children; lp != NULL; lp = lp->next)
{
GoDockBandChild *c = lp->data;
if (GTK_WIDGET_VISIBLE (c->widget))
{
GtkRequisition req;
req.width = req.height = 0;
if (GO_IS_DOCK_ITEM (c->widget))
go_dock_item_handle_size_request(GO_DOCK_ITEM (c->widget),
&req);
else
gtk_widget_size_request (c->widget, &req);
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
{
gboolean has_preferred_width;
guint preferred_width;
has_preferred_width = check_guint_arg (G_OBJECT (c->widget),
"preferred_width",
&preferred_width);
if (has_preferred_width)
c->max_space_requisition = MAX ((int)preferred_width, req.width);
else
c->max_space_requisition = req.width;
}
else
{
gboolean has_preferred_height;
guint preferred_height;
has_preferred_height = check_guint_arg (G_OBJECT (c->widget),
"preferred_height",
&preferred_height);
if (has_preferred_height)
c->max_space_requisition = MAX ((int)preferred_height, req.height);
else
c->max_space_requisition = req.height;
}
band->max_space_requisition += c->max_space_requisition;
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
{
requisition->height = MAX (requisition->height, req.height);
requisition->width += req.width;
}
else
{
requisition->width = MAX (requisition->width, req.width);
requisition->height += req.height;
}
c->widget->requisition = req;
band->tot_offsets += c->offset;
}
}
widget->requisition = *requisition;
}
static void
size_allocate_child (GoDockBand *band,
GoDockBandChild *child,
guint space,
GtkAllocation *child_allocation)
{
GtkWidget *band_widget;
band_widget = GTK_WIDGET (band);
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
{
child_allocation->x += child->real_offset;
child_allocation->width = space;
child_allocation->height = band_widget->allocation.height;
DEBUG (("horizontal %d %d %d %d real_offset %d",
child_allocation->x, child_allocation->y,
child_allocation->width, child_allocation->height,
child->real_offset));
gtk_widget_size_allocate (child->widget, child_allocation);
child_allocation->x += child_allocation->width;
}
else
{
child_allocation->y += child->real_offset;
child_allocation->width = band_widget->allocation.width;
child_allocation->height = space;
DEBUG (("vertical %d %d %d %d real_offset %d",
child_allocation->x, child_allocation->y,
child_allocation->width, child_allocation->height,
child->real_offset));
gtk_widget_size_allocate (child->widget, child_allocation);
child_allocation->y += child_allocation->height;
}
}
/* The allocated space is smaller than the space needed to show all
the items completely. */
static void
size_allocate_small (GoDockBand *band,
GtkAllocation *allocation,
guint space,
guint requested_space)
{
GtkAllocation child_allocation;
GList *lp;
guint max_space_requisition;
DEBUG (("entering function"));
child_allocation.x = allocation->x;
child_allocation.y = allocation->y;
max_space_requisition = band->max_space_requisition;
for (lp = band->children; lp != NULL; lp = lp->next)
{
GoDockBandChild *child;
child = lp->data;
if (GTK_WIDGET_VISIBLE (child->widget))
{
guint child_requested_space;
child->real_offset = 0;
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
child_requested_space = child->widget->requisition.width;
else
child_requested_space = child->widget->requisition.height;
if (space < child->max_space_requisition
|| (space - child->max_space_requisition
< requested_space - child_requested_space))
break;
space -= child->max_space_requisition;
requested_space -= child_requested_space;
max_space_requisition -= child->max_space_requisition;
size_allocate_child (band, child,
child->max_space_requisition,
&child_allocation);
}
}
if (lp != NULL)
{
GoDockBandChild *child;
guint child_space, child_requested_space;
child = lp->data;
if (GTK_WIDGET_VISIBLE (child->widget))
{
child->real_offset = 0;
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
child_requested_space = child->widget->requisition.width;
else
child_requested_space = child->widget->requisition.height;
requested_space -= child_requested_space;
child_space = space - requested_space;
space -= child_space;
size_allocate_child (band, child,
child_space,
&child_allocation);
}
lp = lp->next;
}
for (; lp != NULL; lp = lp->next)
{
GoDockBandChild *child;
child = lp->data;
if (GTK_WIDGET_VISIBLE (child->widget))
{
child->real_offset = 0;
child->real_offset = 0;
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
size_allocate_child (band, child,
child->widget->requisition.width,
&child_allocation);
else
size_allocate_child (band, child,
child->widget->requisition.height,
&child_allocation);
}
}
}
/* The allocation is enough to show all the items completely, but not
to satisfy all the requested offsets. */
static void
size_allocate_medium (GoDockBand *band,
GtkAllocation *allocation,
guint space,
guint requested_space)
{
GtkAllocation child_allocation;
GList *lp;
gfloat factor;
DEBUG (("entering function"));
child_allocation.x = allocation->x;
child_allocation.y = allocation->y;
factor = (1.0 - ((float) (band->max_space_requisition + band->tot_offsets
- space)
/ (float) band->tot_offsets));
/* Shrink the offsets proportionally. */
for (lp = band->children; lp != NULL; lp = lp->next)
{
GoDockBandChild *child;
child = lp->data;
if (GTK_WIDGET_VISIBLE (child->widget))
{
child->real_offset = (guint) ((float) child->offset * factor + .5);
size_allocate_child (band, child,
child->max_space_requisition,
&child_allocation);
}
}
}
/* The allocation is enough to show all the items completely, with the
requested offsets. */
static void
size_allocate_large (GoDockBand *band,
GtkAllocation *allocation,
guint space,
guint requested_space)
{
GtkAllocation child_allocation;
GList *lp;
DEBUG (("entering function"));
child_allocation.x = allocation->x;
child_allocation.y = allocation->y;
for (lp = band->children; lp != NULL; lp = lp->next)
{
GoDockBandChild *child;
child = lp->data;
if (GTK_WIDGET_VISIBLE (child->widget))
{
child->real_offset = child->offset;
size_allocate_child (band, child,
child->max_space_requisition,
&child_allocation);
}
}
}
static void
go_dock_band_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
GoDockBand *band;
guint space, requested_space;
band = GO_DOCK_BAND (widget);
widget->allocation = *allocation;
/* Check if we have a single exclusive item. If so, allocate the
whole space to it. */
if (band->num_children == 1)
{
GoDockBandChild *c;
c = (GoDockBandChild *) band->children->data;
if (GO_IS_DOCK_ITEM (c->widget) && GTK_WIDGET_VISIBLE (c->widget))
{
GoDockItemBehavior behavior;
GoDockItem *item;
item = GO_DOCK_ITEM (c->widget);
behavior = go_dock_item_get_behavior (item);
if (behavior & GO_DOCK_ITEM_BEH_EXCLUSIVE)
{
gtk_widget_size_allocate (c->widget, allocation);
return;
}
}
}
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
{
space = allocation->width;
requested_space = widget->requisition.width;
}
else
{
space = allocation->height;
requested_space = widget->requisition.height;
}
if (space <= band->max_space_requisition)
size_allocate_small (band, allocation, space, requested_space);
else if (space < band->max_space_requisition + band->tot_offsets)
size_allocate_medium (band, allocation, space, requested_space);
else
size_allocate_large (band, allocation, space, requested_space);
calc_prev_and_foll_space (band);
}
static void
go_dock_band_map (GtkWidget *widget)
{
GoDockBand *band = GO_DOCK_BAND (widget);
GList *lp;
g_return_if_fail(widget != NULL);
g_return_if_fail(GO_IS_DOCK_BAND(widget));
GNOME_CALL_PARENT (GTK_WIDGET_CLASS, map, (widget));
for (lp = band->children; lp != NULL; lp = lp->next)
{
GoDockBandChild *c;
c = lp->data;
if (GTK_WIDGET_VISIBLE (c->widget) && ! GTK_WIDGET_MAPPED (c->widget))
gtk_widget_map (c->widget);
}
}
static void
go_dock_band_unmap (GtkWidget *widget)
{
GoDockBand *band = GO_DOCK_BAND (widget);
GList *lp;
g_return_if_fail(widget != NULL);
g_return_if_fail(GO_IS_DOCK_BAND(widget));
GNOME_CALL_PARENT (GTK_WIDGET_CLASS, unmap, (widget));
for (lp = band->children; lp != NULL; lp = lp->next)
{
GoDockBandChild *c;
c = lp->data;
if (GTK_WIDGET_VISIBLE (c->widget) && GTK_WIDGET_MAPPED (c->widget))
gtk_widget_unmap (c->widget);
}
}
/* GtkContainer methods. */
static void
go_dock_band_add (GtkContainer *container, GtkWidget *child)
{
GoDockBand *band = GO_DOCK_BAND (container);
g_return_if_fail (go_dock_band_prepend (band, child, 0));
}
static void
go_dock_band_remove (GtkContainer *container, GtkWidget *widget)
{
GoDockBand *band;
GList *child;
band = GO_DOCK_BAND (container);
if (band->num_children == 0)
return;
child = find_child (band, widget);
if (child != NULL)
{
gboolean was_visible;
if (child == band->floating_child)
band->floating_child = NULL;
was_visible = GTK_WIDGET_VISIBLE (widget);
gtk_widget_unparent (widget);
band->children = g_list_remove_link (band->children, child);
g_free (child->data);
g_list_free (child);
if (band->doing_drag)
{
GList *p;
for (p = band->children; p != NULL; p = p->next)
{
GoDockBandChild *c;
c = (GoDockBandChild *) p->data;
c->offset = c->real_offset = c->drag_offset;
}
}
gtk_widget_queue_resize (GTK_WIDGET (band));
band->num_children--;
DEBUG (("now num_children = %d", band->num_children));
}
}
static void
go_dock_band_forall (GtkContainer *container,
gboolean include_internals,
GtkCallback callback,
gpointer callback_data)
{
GoDockBand *band;
GoDockBandChild *child;
GList *children;
band = GO_DOCK_BAND (container);
children = band->children;
while (children)
{
child = children->data;
children = children->next;
(* callback) (child->widget, callback_data);
}
}
static void
go_dock_band_finalize (GObject *object)
{
GoDockBand *self = GO_DOCK_BAND (object);
g_free (self->_priv);
self->_priv = NULL;
GNOME_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
}
/* Utility functions. */
static gboolean
docking_allowed (GoDockBand *band, GoDockItem *item)
{
GoDockItemBehavior behavior;
GoDockBandChild *c;
if (band->num_children == 0)
return TRUE;
behavior = go_dock_item_get_behavior (item);
if (behavior & GO_DOCK_ITEM_BEH_EXCLUSIVE)
return FALSE;
c = (GoDockBandChild *) band->children->data;
if (GO_IS_DOCK_ITEM (c->widget))
{
behavior = go_dock_item_get_behavior (GO_DOCK_ITEM (c->widget));
if (behavior & GO_DOCK_ITEM_BEH_EXCLUSIVE)
return c->widget == GTK_WIDGET (item);
}
return TRUE;
}
static GList *
find_child (GoDockBand *band, GtkWidget *child)
{
GList *children;
children = band->children;
while (children != NULL)
{
GoDockBandChild *c;
c = (GoDockBandChild *) children->data;
if (c->widget == child)
return children;
children = children->next;
}
return NULL;
}
static GList *
next_if_floating (GoDockBand *band, GList *c)
{
if (c != NULL && c == band->floating_child)
return c->next;
else
return c;
}
static GList *
prev_if_floating (GoDockBand *band, GList *c)
{
if (c != NULL && c == band->floating_child)
return c->prev;
else
return c;
}
static GList *
next_not_floating (GoDockBand *band, GList *c)
{
if (c == NULL)
return NULL;
else
return next_if_floating (band, c->next);
}
static GList *
prev_not_floating (GoDockBand *band, GList *c)
{
if (c == NULL)
return NULL;
else
return prev_if_floating (band, c->prev);
}
static GList *
find_where (GoDockBand *band, gint offset, gboolean *is_empty)
{
guint count; /* FIXME: used for debugging only */
gint offs;
GList *lp;
if (offset < 0)
offset = 0;
offs = 0;
count = 0; /* FIXME */
for (lp = band->children; lp != NULL; lp = lp->next)
{
GoDockBandChild *child;
child = lp->data;
if (lp == band->floating_child)
{
if (lp->next == NULL)
{
DEBUG (("empty last %d", count));
*is_empty = TRUE;
return lp == band->floating_child ? lp->prev : lp;
}
DEBUG (("%d: is floating or dragged.", count++));
continue;
}
DEBUG (("%d: Checking for x %d, width %d, offs %d (%d)",
count, child->drag_allocation.x,
child->drag_allocation.width, offs, offset));
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
{
if (offset >= offs && offset <= child->drag_allocation.x)
{
*is_empty = TRUE;
DEBUG (("empty %d (allocation.x %d)",
count, child->drag_allocation.x));
return prev_if_floating (band, lp->prev);
}
offs = child->drag_allocation.x + child->drag_allocation.width;
if (offset > child->drag_allocation.x && offset < offs)
{
*is_empty = FALSE;
DEBUG (("%d", count));
return lp->prev;
}
}
else
{
if (offset >= offs && offset <= child->drag_allocation.y)
{
*is_empty = TRUE;
DEBUG (("empty %d (allocation.y %d)",
count, child->drag_allocation.y));
return prev_if_floating (band, lp->prev);
}
offs = child->drag_allocation.y + child->drag_allocation.height;
if (offset > child->drag_allocation.y && offset < offs)
{
*is_empty = FALSE;
DEBUG (("%d", count));
return lp->prev;
}
}
if (lp->next == NULL)
{
DEBUG (("empty last %d", count));
*is_empty = TRUE;
return lp;
}
count++; /* FIXME */
}
DEBUG (("nothing done."));
/* Make compiler happy. */
*is_empty = TRUE;
return lp;
}
static void
calc_prev_and_foll_space (GoDockBand *band)
{
GtkWidget *widget;
GList *lp;
if (band->children == NULL)
return;
widget = GTK_WIDGET (band);
lp = next_if_floating (band, band->children);
if (lp != NULL)
{
GoDockBandChild *c;
guint prev_space, foll_space;
prev_space = 0;
while (1)
{
GList *next;
c = lp->data;
prev_space += c->real_offset;
c->prev_space = prev_space;
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
prev_space += (c->widget->allocation.width
- c->widget->requisition.width);
else
prev_space += (c->widget->allocation.height
- c->widget->requisition.height);
next = next_not_floating (band, lp);
if (next == NULL)
break;
lp = next;
}
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
foll_space = (widget->allocation.x + widget->allocation.width
- (c->widget->allocation.x
+ c->widget->requisition.width));
else
foll_space = (widget->allocation.y + widget->allocation.height
- (c->widget->allocation.y
+ c->widget->requisition.height));
DEBUG(("foll_space %d", foll_space));
for (; lp != NULL; lp = prev_not_floating (band, lp))
{
c = lp->data;
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
foll_space += (c->widget->allocation.width
- c->widget->requisition.width);
else
foll_space += (c->widget->allocation.height
- c->widget->requisition.height);
c->foll_space = foll_space;
foll_space += c->real_offset;
}
}
}
static guint
attempt_move_backward (GoDockBand *band, GList *child, guint amount)
{
GList *lp;
guint effective_amount;
effective_amount = 0;
for (lp = prev_if_floating (band, child);
lp != NULL && amount > 0;
lp = prev_not_floating (band, lp))
{
GoDockBandChild *c;
c = lp->data;
if (c->drag_offset > amount)
{
c->real_offset = c->drag_offset - amount;
effective_amount += amount;
amount = 0;
}
else
{
c->real_offset = 0;
effective_amount += c->drag_offset;
amount -= c->drag_offset;
}
c->offset = c->real_offset;
}
return effective_amount;
}
static guint
attempt_move_forward (GoDockBand *band, GList *child, guint requirement)
{
GList *lp;
guint effective_amount;
effective_amount = 0;
for (lp = next_if_floating (band, child);
lp != NULL && requirement > 0;
lp = next_not_floating (band, lp))
{
GoDockBandChild *c;
c = lp->data;
DEBUG (("requirement = %d", requirement));
if (c->drag_offset > requirement)
{
c->real_offset = c->drag_offset - requirement;
effective_amount += requirement;
requirement = 0;
}
else
{
c->real_offset = 0;
effective_amount += c->drag_offset;
requirement -= c->drag_offset;
}
c->offset = c->real_offset;
}
return effective_amount;
}
static void
reparent_if_needed (GoDockBand *band,
GoDockItem *item,
gint x, gint y)
{
if (GTK_WIDGET (item)->parent != GTK_WIDGET (band))
{
go_dock_item_attach (item, GTK_WIDGET (band), x, y);
/* Reparenting causes the new floating child to be the first
item on the child list (see the `remove' method). */
band->floating_child = band->children;
/* Reparenting will remove the grab, so we need to redo it. */
go_dock_item_grab_pointer (item);
}
}
static gboolean
dock_nonempty (GoDockBand *band,
GoDockItem *item,
GList *where,
gint x, gint y)
{
GoDockBandChild *c, *floating_child;
GtkOrientation orig_item_orientation;
GtkRequisition item_requisition;
GList *lp, *next;
gint amount, requirement;
DEBUG (("entering function"));
if (! docking_allowed (band, item))
return FALSE;
if (where == NULL)
lp = band->children;
else
lp = next_not_floating (band, where);
c = lp->data;
orig_item_orientation = go_dock_item_get_orientation (item);
if (orig_item_orientation != band->orientation
&& ! go_dock_item_set_orientation (item, band->orientation))
return FALSE;
go_dock_item_handle_size_request (item, &item_requisition);
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
requirement = item_requisition.width;
else
requirement = item_requisition.height;
if ((c->drag_prev_space + c->drag_foll_space) < requirement)
{
DEBUG (("not enough space %d %d",
c->drag_prev_space + c->drag_foll_space,
requirement));
/* Restore original orientation. */
if (orig_item_orientation != band->orientation)
go_dock_item_set_orientation (item, orig_item_orientation);
return FALSE;
}
gtk_widget_size_request (GTK_WIDGET (item), &item_requisition);
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
requirement = item_requisition.width;
else
requirement = item_requisition.height;
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
amount = c->drag_allocation.x + c->drag_allocation.width - x;
else
amount = c->drag_allocation.y + c->drag_allocation.height - y;
DEBUG (("amount %d requirement %d", amount, requirement));
amount = attempt_move_backward (band, lp, amount);
if (requirement < amount)
requirement = 0;
else
{
requirement -= amount;
next = next_not_floating (band, lp);
if (next != NULL)
attempt_move_forward (band, next, requirement);
}
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
reparent_if_needed (band, item, x, GTK_WIDGET (band)->allocation.y);
else
reparent_if_needed (band, item, GTK_WIDGET (band)->allocation.x, y);
floating_child = band->floating_child->data;
floating_child->offset = floating_child->real_offset = 0;
if (band->floating_child->prev != lp)
{
DEBUG (("moving"));
band->children = g_list_remove_link (band->children,
band->floating_child);
band->floating_child->next = lp->next;
if (band->floating_child->next != NULL)
band->floating_child->next->prev = band->floating_child;
band->floating_child->prev = lp;
lp->next = band->floating_child;
}
gtk_widget_queue_resize (floating_child->widget);
return TRUE;
}
static gboolean
dock_empty (GoDockBand *band,
GoDockItem *item,
GList *where,
gint x, gint y)
{
GoDockBandChild *floating_child;
GoDockBandChild *c1, *c2;
GtkOrientation orig_item_orientation;
GtkRequisition item_requisition;
GList *lp;
guint new_offset;
GtkWidget *item_widget;
DEBUG (("entering function"));
if (! docking_allowed (band, item))
return FALSE;
if (where != NULL)
{
lp = next_not_floating (band, where);
if (lp == NULL)
/* Extreme right is a special case. */
return dock_empty_right (band, item, where, x, y);
c1 = where->data;
}
else
{
c1 = NULL;
lp = next_if_floating (band, band->children);
if (lp == NULL)
{
/* Only one floating element. Easy. */
GoDockBandChild *c;
if (! go_dock_item_set_orientation (item, band->orientation))
return FALSE;
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
reparent_if_needed (band, item,
x, GTK_WIDGET (band)->allocation.y);
else
reparent_if_needed (band, item,
GTK_WIDGET (band)->allocation.x, y);
c = band->floating_child->data;
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
c->real_offset = x - GTK_WIDGET (band)->allocation.x;
else
c->real_offset = y - GTK_WIDGET (band)->allocation.y;
c->offset = c->real_offset;
DEBUG (("simple case offset %d", c->offset));
gtk_widget_queue_resize (c->widget);
return TRUE;
}
}
c2 = lp->data;
item_widget = GTK_WIDGET (item);
orig_item_orientation = go_dock_item_get_orientation (item);
if (! go_dock_item_set_orientation (item, band->orientation))
return FALSE;
/* Check whether there is enough space for the widget. */
{
gint space;
if (c1 != NULL)
space = c1->drag_foll_space;
else
{
space = c2->real_offset + c2->drag_foll_space;
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
space += c2->widget->allocation.width - c2->widget->requisition.width;
else
space += c2->widget->allocation.height - c2->widget->requisition.height;
}
go_dock_item_handle_size_request (item, &item_requisition);
if (space < (band->orientation == GTK_ORIENTATION_HORIZONTAL
? item_requisition.width
: item_requisition.height))
{
DEBUG (("not enough space %d", space));
/* Restore original orientation. */
if (orig_item_orientation != band->orientation)
go_dock_item_set_orientation (item, orig_item_orientation);
return FALSE;
}
}
gtk_widget_size_request (item_widget, &item_requisition);
if (c1 == NULL)
{
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
new_offset = x - GTK_WIDGET (band)->allocation.x;
else
new_offset = y - GTK_WIDGET (band)->allocation.y;
}
else
{
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
new_offset = x - (c1->drag_allocation.x + c1->drag_allocation.width);
else
new_offset = y - (c1->drag_allocation.y + c1->drag_allocation.height);
}
DEBUG (("new_offset %d", new_offset));
if (c2->drag_offset >= (new_offset
+ (band->orientation == GTK_ORIENTATION_HORIZONTAL
? item_requisition.width
: item_requisition.height)))
{
c2->real_offset = (c2->drag_offset
- (new_offset
+ (band->orientation == GTK_ORIENTATION_HORIZONTAL
? item_requisition.width
: item_requisition.height)));
c2->offset = c2->real_offset;
}
else
{
guint requisition;
GList *lp1;
requisition = new_offset + (band->orientation == GTK_ORIENTATION_HORIZONTAL
? item_requisition.width
: item_requisition.height);
DEBUG (("Moving forward %d!", requisition));
for (lp1 = lp; lp1 != NULL && requisition > 0; )
{
GoDockBandChild *tmp = lp1->data;
GList *lp1next;
if (tmp->drag_offset > requisition)
{
tmp->real_offset = tmp->drag_offset - requisition;
requisition = 0;
}
else
{
requisition -= tmp->drag_offset;
tmp->real_offset = 0;
}
tmp->offset = tmp->real_offset;
DEBUG (("Offset %d (drag %d)", tmp->real_offset, tmp->drag_offset));
lp1next = next_not_floating (band, lp1);
if (lp1next == NULL)
{
if (tmp->drag_foll_space > requisition)
requisition = 0;
else
requisition -= tmp->drag_foll_space;
}
lp1 = lp1next;
}
if (requisition > 0)
new_offset -= requisition;
}
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
reparent_if_needed (band, item, x, GTK_WIDGET (band)->allocation.y);
else
reparent_if_needed (band, item, GTK_WIDGET (band)->allocation.x, y);
floating_child = (GoDockBandChild *) band->floating_child->data;
floating_child->real_offset = floating_child->offset = new_offset;
band->children = g_list_remove_link (band->children, band->floating_child);
if (where == NULL)
{
band->floating_child->next = band->children;
band->children->prev = band->floating_child;
band->children = band->floating_child;
}
else
{
band->floating_child->next = where->next;
band->floating_child->prev = where;
if (where->next != NULL)
where->next->prev = band->floating_child;
where->next = band->floating_child;
}
gtk_widget_queue_resize (((GoDockBandChild *) band->floating_child->data)->widget);
return TRUE;
}
static gboolean
dock_empty_right (GoDockBand *band,
GoDockItem *item,
GList *where,
gint x, gint y)
{
GoDockBandChild *c, *floating_child;
GtkOrientation orig_item_orientation;
GtkRequisition item_requisition;
GtkWidget *item_widget;
gint new_offset;
g_return_val_if_fail (next_not_floating (band, where) == NULL, FALSE);
g_return_val_if_fail (band->floating_child != where, FALSE);
DEBUG (("entering function"));
if (! docking_allowed (band, item))
return FALSE;
item_widget = GTK_WIDGET (item);
c = where->data;
orig_item_orientation = go_dock_item_get_orientation (item);
if (orig_item_orientation != band->orientation
&& ! go_dock_item_set_orientation (item, band->orientation))
return FALSE;
go_dock_item_handle_size_request (item, &item_requisition);
if ((c->drag_prev_space + c->drag_foll_space)
< (band->orientation == GTK_ORIENTATION_HORIZONTAL
? item_requisition.width
: item_requisition.height))
{
DEBUG (("not enough space %d ", c->drag_prev_space+ c->drag_foll_space));
/* Restore original orientation. */
if (orig_item_orientation != band->orientation)
go_dock_item_set_orientation (item, orig_item_orientation);
return FALSE;
}
gtk_widget_size_request (item_widget, &item_requisition);
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
new_offset = x - (c->widget->allocation.x + c->widget->allocation.width);
else
new_offset = y - (c->widget->allocation.y + c->widget->allocation.height);
DEBUG (("x %d y %d new_offset %d width %d foll_space %d",
x, y, new_offset, item_widget->allocation.width,
c->drag_foll_space));
if ((guint) (new_offset
+ (band->orientation == GTK_ORIENTATION_HORIZONTAL
? item_requisition.width
: item_requisition.height)) > c->drag_foll_space)
{
gint excess = (new_offset
+ (band->orientation == GTK_ORIENTATION_HORIZONTAL
? item_requisition.width
: item_requisition.height)
- c->drag_foll_space);
DEBUG (("excess %d new_offset %d", excess, new_offset));
if (excess < new_offset)
new_offset -= excess;
else
{
attempt_move_backward (band, where, excess - new_offset);
new_offset = 0;
}
}
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
reparent_if_needed (band, item,
x, GTK_WIDGET (band)->allocation.y);
else
reparent_if_needed (band, item,
GTK_WIDGET (band)->allocation.x, y);
floating_child = band->floating_child->data;
floating_child->offset = floating_child->real_offset = new_offset;
band->children = g_list_remove_link (band->children, band->floating_child);
where->next = band->floating_child;
band->floating_child->prev = where;
gtk_widget_queue_resize (floating_child->widget);
return TRUE;
}
/* Helper function. */
static gboolean
check_guint_arg (GObject *object,
const gchar *name,
guint *value_return)
{
GParamSpec *pspec;
g_return_val_if_fail (object != NULL, FALSE);
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), name);
if (pspec != NULL) {
GValue value = { 0, };
g_value_init (&value, G_TYPE_UINT);
g_object_get_property (G_OBJECT (object), name, &value);
*value_return = g_value_get_uint (&value);
g_value_unset (&value);
return TRUE;
} else
return FALSE;
}
/* Exported interface. */
/**
* go_dock_band_new:
*
* Description: Create a new GoDockBand widget.
*
* Returns: The new GoDockBand widget.
**/
GtkWidget *
go_dock_band_new (void)
{
GoDockBand *band;
band = g_object_new (go_dock_band_get_type (), NULL);
return GTK_WIDGET (band);
}
/**
* go_dock_band_set_orientation:
* @band: A GoDockBand widget
* @orientation: New orientation for @band
*
* Description: Set the orientation for @band.
**/
void
go_dock_band_set_orientation (GoDockBand *band,
GtkOrientation orientation)
{
g_return_if_fail (orientation == GTK_ORIENTATION_HORIZONTAL
|| orientation == GTK_ORIENTATION_VERTICAL);
band->orientation = orientation;
}
/**
* go_dock_band_get_orientation:
* @band: A GoDockBand widget
*
* Description: Retrieve the orientation of the specified @band.
*
* Returns: The orientation of @band.
**/
GtkOrientation
go_dock_band_get_orientation (GoDockBand *band)
{
return band->orientation;
}
/**
* go_dock_band_insert:
* @band: A GoDockBand widget
* @child: The widget to be added to @band
* @offset: Offset from the previous item
* @position: Position within the @band
*
* Description: Add @child to @band at the specified @position, with
* the specified @offset from the previous item (or from the beginning
* of the band, if this is the first item).
*
* Returns: %TRUE if successful, %FALSE if the operation fails.
**/
gboolean
go_dock_band_insert (GoDockBand *band,
GtkWidget *child,
guint offset,
gint position)
{
GoDockBandChild *band_child;
DEBUG (("%08x", (unsigned int) band));
if (GO_IS_DOCK_ITEM (child)
&& !docking_allowed (band, GO_DOCK_ITEM (child)))
return FALSE;
if (GO_IS_DOCK_ITEM (child) &&
!go_dock_item_set_orientation (GO_DOCK_ITEM (child),
band->orientation))
return FALSE;
if (position < 0 || position > (gint) band->num_children)
position = band->num_children;
band_child = g_new (GoDockBandChild, 1);
band_child->widget = child;
band_child->offset = offset;
band_child->real_offset = 0;
if (position == 0)
band->children = g_list_prepend (band->children, band_child);
else if ((guint) position == band->num_children)
band->children = g_list_append (band->children, band_child);
else
{
GList *p;
p = g_list_nth (band->children, position);
g_list_prepend (p, band_child);
}
gtk_widget_set_parent (child, GTK_WIDGET (band));
if (GTK_WIDGET_REALIZED (child->parent))
gtk_widget_realize (child);
if (GTK_WIDGET_VISIBLE (child->parent) && GTK_WIDGET_VISIBLE (child))
{
if (GTK_WIDGET_MAPPED (child->parent))
gtk_widget_map (child);
gtk_widget_queue_resize (child);
}
band->num_children++;
DEBUG (("now num_children = %d", band->num_children));
return TRUE;
}
void
go_dock_band_move_child (GoDockBand *band,
GList *old_child,
guint new_num)
{
GList *children;
GList *lp;
children = band->children;
lp = old_child;
children = g_list_remove_link (children, lp);
children = g_list_insert (children, lp->data, new_num);
g_list_free (lp);
band->children = children;
/* FIXME */
gtk_widget_queue_resize (GTK_WIDGET (band));
}
/**
* go_dock_band_prepend:
* @band: A GoDockBand widget
* @child: A widget to be added to @band
* @offset: Offset (in pixels) from the beginning of the band
*
* Description: Add @child to @band with the specified @offset as the
* first element.
*
* Returns: %TRUE if successful, %FALSE if the operation fails.
**/
gboolean
go_dock_band_prepend (GoDockBand *band,
GtkWidget *child,
guint offset)
{
return go_dock_band_insert (band, child, offset, 0);
}
/**
* go_dock_band_append:
* @band: A GoDockBand widget
* @child: A widget to be added to @band
* @offset: Offset (in pixels) from the last item of the band
*
* Description: Add @child to @band with the specified @offset as the
* last element.
*
* Returns: %TRUE if successful, %FALSE if the operation fails.
**/
gboolean
go_dock_band_append (GoDockBand *band,
GtkWidget *child,
guint offset)
{
return go_dock_band_insert (band, child, offset, -1);
}
/**
* go_dock_band_set_child_offset:
* @band: A GoDockBand widget
* @child: Child of @band whose offset must be changed
* @offset: New offset value for @child
*
* Description: Set the offset for the specified @child of @band.
**/
void
go_dock_band_set_child_offset (GoDockBand *band,
GtkWidget *child,
guint offset)
{
GList *p;
p = find_child (band, child);
if (p != NULL)
{
GoDockBandChild *c;
c = (GoDockBandChild *) p->data;
c->offset = offset;
gtk_widget_queue_resize (c->widget);
}
}
/**
* go_dock_band_get_child_offset:
* @band: A GoDockBand widget
* @child: Child of @band whose offset must be retrieved
*
* Description: Retrieve the offset of @child in @band.
*
* Returns: The offset of @child.
**/
guint
go_dock_band_get_child_offset (GoDockBand *band,
GtkWidget *child)
{
GList *p;
p = find_child (band, child);
if (p != NULL)
{
GoDockBandChild *c;
c = (GoDockBandChild *) p->data;
return c->offset;
}
return 0;
}
/**
* go_dock_band_get_num_children:
* @band: A GoDockBand widget
*
* Description: Retrieve the number of children in @band.
*
* Returns: The number of children in @band.
**/
guint
go_dock_band_get_num_children (GoDockBand *band)
{
return band->num_children;
}
/* Private interface. */
void
go_dock_band_drag_begin (GoDockBand *band, GoDockItem *item)
{
GList *lp;
GtkWidget *floating_widget;
GtkWidget *item_widget;
guint extra_offset = 0;
DEBUG (("entering function"));
item_widget = GTK_WIDGET (item);
floating_widget = NULL;
for (lp = band->children; lp != NULL;)
{
GoDockBandChild *c;
c = lp->data;
c->drag_allocation = c->widget->allocation;
c->drag_offset = c->real_offset + extra_offset;
c->drag_prev_space = c->prev_space;
c->drag_foll_space = c->foll_space;
c->offset = c->real_offset;
if (c->widget == item_widget)
{
band->floating_child = lp;
floating_widget = item_widget;
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
extra_offset = c->widget->allocation.width + c->real_offset;
else
extra_offset = c->widget->allocation.height + c->real_offset;
}
else
extra_offset = 0;
if (lp->next == NULL)
break;
lp = lp->next;
}
if (floating_widget != NULL)
{
for (lp = band->floating_child->prev; lp != NULL; lp = lp->prev)
{
GoDockBandChild *c;
c = lp->data;
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
c->drag_foll_space += item_widget->requisition.width;
else
c->drag_foll_space += item_widget->requisition.height;
}
for (lp = band->floating_child->next; lp != NULL; lp = lp->next)
{
GoDockBandChild *c;
c = lp->data;
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
c->drag_prev_space += item_widget->requisition.width;
else
c->drag_prev_space += item_widget->requisition.height;
}
}
band->doing_drag = TRUE;
band->drag_allocation = GTK_WIDGET (band)->allocation;
}
gboolean
go_dock_band_drag_to (GoDockBand *band,
GoDockItem *item,
gint x, gint y)
{
GtkAllocation *allocation;
GList *where;
gboolean is_empty;
g_return_val_if_fail (band->doing_drag, FALSE);
DEBUG (("%d %d", x, y));
allocation = & GTK_WIDGET (band)->allocation;
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
{
if (x < allocation->x)
x = allocation->x;
if (x >= allocation->x + allocation->width)
x = allocation->x + allocation->width - 1;
where = find_where (band, x, &is_empty);
}
else
{
if (y < allocation->y)
y = allocation->y;
if (y >= allocation->y + allocation->height)
y = allocation->y + allocation->height - 1;
where = find_where (band, y, &is_empty);
}
{
GList *p;
for (p = next_if_floating (band, band->children);
p != NULL;
p = next_not_floating (band, p))
{
GoDockBandChild *c = p->data;
c->real_offset = c->offset = c->drag_offset;
}
}
if (is_empty)
return dock_empty (band, item, where, x, y);
else
return dock_nonempty (band, item, where, x, y);
}
void
go_dock_band_drag_end (GoDockBand *band, GoDockItem *item)
{
g_return_if_fail (band->doing_drag);
DEBUG (("entering function"));
if (band->floating_child != NULL)
{
GoDockBandChild *f;
/* Minimal sanity check. */
f = (GoDockBandChild *) band->floating_child->data;
g_return_if_fail (f->widget == GTK_WIDGET (item));
gtk_widget_queue_resize (f->widget);
band->floating_child = NULL;
}
band->doing_drag = FALSE;
band->new_for_drag = FALSE;
}
/**
* go_dock_band_get_item_by_name:
* @band: A GoDockBand widget
* @name: Name of the child to be retrieved
* @position_return: Pointer to a variable holding the position of
* the named child
* @offset_return: Pointer to a variable holding the offset of the
* named child
*
* Description: Retrieve a named item from @band, and return its
* position and offset in *@position_return and @offset_return.
*
* Return value: The child whose name is @name, or %NULL if no child
* of @band has such name.
**/
GoDockItem *
go_dock_band_get_item_by_name (GoDockBand *band,
const char *name,
guint *position_return,
guint *offset_return)
{
guint pos;
GList *lp;
for (lp = band->children, pos = 0; lp != NULL; lp = lp->next, pos++)
{
GoDockBandChild *c;
c = lp->data;
if (GO_IS_DOCK_ITEM (c->widget))
{
GoDockItem *item;
item = GO_DOCK_ITEM (c->widget);
if (strcmp (item->name, name) == 0)
{
if (position_return != NULL)
*position_return = pos;
if (offset_return != NULL)
*offset_return = c->offset;
return item;
}
}
}
return NULL;
}
void
go_dock_band_layout_add (GoDockBand *band,
GoDockLayout *layout,
GoDockPlacement placement,
guint band_num)
{
guint child_num;
GList *lp;
for (lp = band->children, child_num = 0;
lp != NULL;
lp = lp->next, child_num++)
{
GoDockBandChild *child;
GtkWidget *item;
child = lp->data;
item = child->widget;
if (GO_IS_DOCK_ITEM (item))
go_dock_layout_add_item (layout,
GO_DOCK_ITEM (item),
placement, band_num,
child_num, child->offset);
}
}
static GoDock *
get_dock (GtkWidget *widget)
{
while (widget && !GO_IS_DOCK (widget))
widget = widget->parent;
return (GoDock *) widget;
}
gint
_bonobo_dock_band_handle_key_nav (GoDockBand *band,
GoDockItem *item,
GdkEventKey *event)
{
gboolean handled = FALSE;
g_return_val_if_fail (GO_IS_DOCK_BAND (band), FALSE);
g_return_val_if_fail (GO_IS_DOCK_ITEM (item), FALSE);
if (event->state & GDK_CONTROL_MASK)
{
GList *l;
int cur_idx = 0;
int dest_idx;
int num_children = g_list_length (band->children);
for (l = band->children; l; l = l->next)
{
GoDockBandChild *child = l->data;
if (child->widget == (GtkWidget *)item)
break;
cur_idx++;
}
g_return_val_if_fail (l != NULL, FALSE);
dest_idx = cur_idx;
if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
{
if (event->keyval == GDK_Left)
dest_idx--;
if (event->keyval == GDK_Right)
dest_idx++;
}
else
{
if (event->keyval == GDK_Up)
dest_idx--;
if (event->keyval == GDK_Down)
dest_idx++;
}
if (dest_idx >= num_children)
dest_idx = num_children - 1;
if (dest_idx < 0)
dest_idx = 0;
if (dest_idx != cur_idx)
{
handled = TRUE;
go_dock_band_move_child (band, l, dest_idx);
}
}
if (!handled)
{
GoDock *dock = get_dock (GTK_WIDGET (band));
if (dock)
handled = _bonobo_dock_handle_key_nav (dock, band, item, event);
}
return handled;
}