gtk2/gtk/gtkpaned.c
Owen Taylor ab1deac87f Add patch from Jonathan Blandford and Anders Carlsson to change the Paned
Thu Feb 17 17:10:12 2000  Owen Taylor  <otaylor@redhat.com>

	* gtk/gt{h,k,}vpaned.[ch]: Add patch from Jonathan Blandford
	and Anders Carlsson to change the Paned widgets so that they
	can be dragged from anywhere along the length. Also change
	the way that this is drawn to make this apparent.

	* gtk/gtkoptionmenu.c (gtk_option_menu_get_history): Apply patch
	from George Lebl to check that option_menu->menu is present
	before getting history.
2000-02-17 22:12:25 +00:00

570 lines
15 KiB
C

/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This 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.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#include "gtkpaned.h"
enum {
ARG_0,
ARG_HANDLE_SIZE
};
static void gtk_paned_class_init (GtkPanedClass *klass);
static void gtk_paned_init (GtkPaned *paned);
static void gtk_paned_set_arg (GtkObject *object,
GtkArg *arg,
guint arg_id);
static void gtk_paned_get_arg (GtkObject *object,
GtkArg *arg,
guint arg_id);
static void gtk_paned_realize (GtkWidget *widget);
static void gtk_paned_map (GtkWidget *widget);
static void gtk_paned_unmap (GtkWidget *widget);
static void gtk_paned_unrealize (GtkWidget *widget);
static gint gtk_paned_expose (GtkWidget *widget,
GdkEventExpose *event);
static void gtk_paned_add (GtkContainer *container,
GtkWidget *widget);
static void gtk_paned_remove (GtkContainer *container,
GtkWidget *widget);
static void gtk_paned_forall (GtkContainer *container,
gboolean include_internals,
GtkCallback callback,
gpointer callback_data);
static GtkType gtk_paned_child_type (GtkContainer *container);
static GtkContainerClass *parent_class = NULL;
GtkType
gtk_paned_get_type (void)
{
static GtkType paned_type = 0;
if (!paned_type)
{
static const GtkTypeInfo paned_info =
{
"GtkPaned",
sizeof (GtkPaned),
sizeof (GtkPanedClass),
(GtkClassInitFunc) gtk_paned_class_init,
(GtkObjectInitFunc) gtk_paned_init,
/* reserved_1 */ NULL,
/* reserved_2 */ NULL,
(GtkClassInitFunc) NULL,
};
paned_type = gtk_type_unique (GTK_TYPE_CONTAINER, &paned_info);
}
return paned_type;
}
static void
gtk_paned_class_init (GtkPanedClass *class)
{
GtkObjectClass *object_class;
GtkWidgetClass *widget_class;
GtkContainerClass *container_class;
object_class = (GtkObjectClass *) class;
widget_class = (GtkWidgetClass *) class;
container_class = (GtkContainerClass *) class;
parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
object_class->set_arg = gtk_paned_set_arg;
object_class->get_arg = gtk_paned_get_arg;
widget_class->realize = gtk_paned_realize;
widget_class->map = gtk_paned_map;
widget_class->unmap = gtk_paned_unmap;
widget_class->unrealize = gtk_paned_unrealize;
widget_class->expose_event = gtk_paned_expose;
container_class->add = gtk_paned_add;
container_class->remove = gtk_paned_remove;
container_class->forall = gtk_paned_forall;
container_class->child_type = gtk_paned_child_type;
gtk_object_add_arg_type("GtkPaned::handle_size", GTK_TYPE_UINT,
GTK_ARG_READWRITE, ARG_HANDLE_SIZE);
}
static GtkType
gtk_paned_child_type (GtkContainer *container)
{
if (!GTK_PANED (container)->child1 || !GTK_PANED (container)->child2)
return GTK_TYPE_WIDGET;
else
return GTK_TYPE_NONE;
}
static void
gtk_paned_init (GtkPaned *paned)
{
GTK_WIDGET_UNSET_FLAGS (paned, GTK_NO_WINDOW);
paned->child1 = NULL;
paned->child2 = NULL;
paned->handle = NULL;
paned->xor_gc = NULL;
paned->cursor_type = GDK_CROSS;
paned->handle_width = 5;
paned->handle_height = 5;
paned->handle_size = 5;
paned->position_set = FALSE;
paned->last_allocation = -1;
paned->in_drag = FALSE;
paned->handle_xpos = -1;
paned->handle_ypos = -1;
}
static void
gtk_paned_set_arg (GtkObject *object,
GtkArg *arg,
guint arg_id)
{
GtkPaned *paned = GTK_PANED (object);
switch (arg_id)
{
case ARG_HANDLE_SIZE:
gtk_paned_set_handle_size (paned, GTK_VALUE_UINT (*arg));
break;
default:
break;
}
}
static void
gtk_paned_get_arg (GtkObject *object,
GtkArg *arg,
guint arg_id)
{
GtkPaned *paned = GTK_PANED (object);
switch (arg_id)
{
case ARG_HANDLE_SIZE:
GTK_VALUE_UINT (*arg) = paned->handle_size;
break;
default:
arg->type = GTK_TYPE_INVALID;
break;
}
}
static void
gtk_paned_realize (GtkWidget *widget)
{
GtkPaned *paned;
GdkWindowAttr attributes;
gint attributes_mask;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_PANED (widget));
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
paned = GTK_PANED (widget);
attributes.x = widget->allocation.x;
attributes.y = widget->allocation.y;
attributes.width = widget->allocation.width;
attributes.height = widget->allocation.height;
attributes.window_type = GDK_WINDOW_CHILD;
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) | 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, paned);
attributes.x = paned->handle_xpos;
attributes.y = paned->handle_ypos;
attributes.width = paned->handle_width;
attributes.height = paned->handle_height;
attributes.cursor = gdk_cursor_new (paned->cursor_type);
attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_POINTER_MOTION_MASK |
GDK_POINTER_MOTION_HINT_MASK);
attributes_mask |= GDK_WA_CURSOR;
paned->handle = gdk_window_new (widget->window,
&attributes, attributes_mask);
gdk_window_set_user_data (paned->handle, paned);
gdk_cursor_destroy (attributes.cursor);
widget->style = gtk_style_attach (widget->style, widget->window);
gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
gtk_style_set_background (widget->style, paned->handle, GTK_STATE_NORMAL);
gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
gdk_window_show (paned->handle);
}
static void
gtk_paned_map (GtkWidget *widget)
{
GtkPaned *paned;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_PANED (widget));
GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
paned = GTK_PANED (widget);
if (paned->child1 &&
GTK_WIDGET_VISIBLE (paned->child1) &&
!GTK_WIDGET_MAPPED (paned->child1))
gtk_widget_map (paned->child1);
if (paned->child2 &&
GTK_WIDGET_VISIBLE (paned->child2) &&
!GTK_WIDGET_MAPPED (paned->child2))
gtk_widget_map (paned->child2);
gdk_window_show (widget->window);
}
static void
gtk_paned_unmap (GtkWidget *widget)
{
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_PANED (widget));
GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
gdk_window_hide (widget->window);
}
static void
gtk_paned_unrealize (GtkWidget *widget)
{
GtkPaned *paned;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_PANED (widget));
paned = GTK_PANED (widget);
if (paned->xor_gc)
{
gdk_gc_destroy (paned->xor_gc);
paned->xor_gc = NULL;
}
if (paned->handle)
{
gdk_window_set_user_data (paned->handle, NULL);
gdk_window_destroy (paned->handle);
paned->handle = NULL;
}
if (GTK_WIDGET_CLASS (parent_class)->unrealize)
(* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
}
static gint
gtk_paned_expose (GtkWidget *widget,
GdkEventExpose *event)
{
GtkPaned *paned;
GdkEventExpose child_event;
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_PANED (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
if (GTK_WIDGET_DRAWABLE (widget))
{
paned = GTK_PANED (widget);
if (event->window == paned->handle)
{
child_event = *event;
event->area.x += paned->handle_xpos;
event->area.y += paned->handle_ypos;
gtk_widget_draw (widget, &event->area);
}
else
{
child_event = *event;
if (paned->child1 &&
GTK_WIDGET_NO_WINDOW (paned->child1) &&
gtk_widget_intersect (paned->child1, &event->area, &child_event.area))
gtk_widget_event (paned->child1, (GdkEvent *) &child_event);
if (paned->child2 &&
GTK_WIDGET_NO_WINDOW (paned->child2) &&
gtk_widget_intersect (paned->child2, &event->area, &child_event.area))
gtk_widget_event (paned->child2, (GdkEvent *) &child_event);
}
}
return FALSE;
}
void
gtk_paned_add1 (GtkPaned *paned,
GtkWidget *widget)
{
gtk_paned_pack1 (paned, widget, FALSE, TRUE);
}
void
gtk_paned_add2 (GtkPaned *paned,
GtkWidget *widget)
{
gtk_paned_pack2 (paned, widget, TRUE, TRUE);
}
void
gtk_paned_pack1 (GtkPaned *paned,
GtkWidget *child,
gboolean resize,
gboolean shrink)
{
g_return_if_fail (paned != NULL);
g_return_if_fail (GTK_IS_PANED (paned));
g_return_if_fail (GTK_IS_WIDGET (child));
if (!paned->child1)
{
paned->child1 = child;
paned->child1_resize = resize;
paned->child1_shrink = shrink;
gtk_widget_set_parent (child, GTK_WIDGET (paned));
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);
}
}
}
void
gtk_paned_pack2 (GtkPaned *paned,
GtkWidget *child,
gboolean resize,
gboolean shrink)
{
g_return_if_fail (paned != NULL);
g_return_if_fail (GTK_IS_PANED (paned));
g_return_if_fail (GTK_IS_WIDGET (child));
if (!paned->child2)
{
paned->child2 = child;
paned->child2_resize = resize;
paned->child2_shrink = shrink;
gtk_widget_set_parent (child, GTK_WIDGET (paned));
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);
}
}
}
static void
gtk_paned_add (GtkContainer *container,
GtkWidget *widget)
{
GtkPaned *paned;
g_return_if_fail (container != NULL);
g_return_if_fail (GTK_IS_PANED (container));
g_return_if_fail (widget != NULL);
paned = GTK_PANED (container);
if (!paned->child1)
gtk_paned_add1 (GTK_PANED (container), widget);
else if (!paned->child2)
gtk_paned_add2 (GTK_PANED (container), widget);
}
static void
gtk_paned_remove (GtkContainer *container,
GtkWidget *widget)
{
GtkPaned *paned;
gboolean was_visible;
g_return_if_fail (container != NULL);
g_return_if_fail (GTK_IS_PANED (container));
g_return_if_fail (widget != NULL);
paned = GTK_PANED (container);
was_visible = GTK_WIDGET_VISIBLE (widget);
if (paned->child1 == widget)
{
gtk_widget_unparent (widget);
paned->child1 = NULL;
if (was_visible && GTK_WIDGET_VISIBLE (container))
gtk_widget_queue_resize (GTK_WIDGET (container));
}
else if (paned->child2 == widget)
{
gtk_widget_unparent (widget);
paned->child2 = NULL;
if (was_visible && GTK_WIDGET_VISIBLE (container))
gtk_widget_queue_resize (GTK_WIDGET (container));
}
}
static void
gtk_paned_forall (GtkContainer *container,
gboolean include_internals,
GtkCallback callback,
gpointer callback_data)
{
GtkPaned *paned;
g_return_if_fail (container != NULL);
g_return_if_fail (GTK_IS_PANED (container));
g_return_if_fail (callback != NULL);
paned = GTK_PANED (container);
if (paned->child1)
(*callback) (paned->child1, callback_data);
if (paned->child2)
(*callback) (paned->child2, callback_data);
}
void
gtk_paned_set_position (GtkPaned *paned,
gint position)
{
g_return_if_fail (paned != NULL);
g_return_if_fail (GTK_IS_PANED (paned));
if (position >= 0)
{
/* We don't clamp here - the assumption is that
* if the total allocation changes at the same time
* as the position, the position set is with reference
* to the new total size. If only the position changes,
* then clamping will occur in gtk_paned_compute_position()
*/
paned->child1_size = position;
paned->position_set = TRUE;
}
else
{
paned->position_set = FALSE;
}
gtk_widget_queue_resize (GTK_WIDGET (paned));
}
void
gtk_paned_set_handle_size (GtkPaned *paned,
guint16 size)
{
g_return_if_fail (paned != NULL);
g_return_if_fail (GTK_IS_PANED (paned));
gtk_widget_queue_resize (GTK_WIDGET (paned));
paned->handle_size = size;
}
void
gtk_paned_compute_position(GtkPaned *paned,
gint allocation,
gint child1_req,
gint child2_req)
{
g_return_if_fail (paned != NULL);
g_return_if_fail (GTK_IS_PANED (paned));
paned->min_position = paned->child1_shrink ? 0 : child1_req;
paned->max_position = allocation;
if (!paned->child2_shrink)
paned->max_position -= child2_req;
if (!paned->position_set)
{
if (paned->child1_resize && !paned->child2_resize)
paned->child1_size = allocation - child2_req;
else if (!paned->child1_resize && paned->child2_resize)
paned->child1_size = child1_req;
else
paned->child1_size = allocation * ((gdouble) child1_req /
(child1_req + child2_req));
}
else
{
/* If the position was set before the initial allocation.
* (paned->last_allocation < 0) just clamp it and leave it.
*/
if (paned->last_allocation >= 0)
{
if (paned->child1_resize && !paned->child2_resize)
paned->child1_size += allocation - paned->last_allocation;
else if (!(!paned->child1_resize && paned->child2_resize))
paned->child1_size = allocation * ((gdouble) paned->child1_size / (paned->last_allocation));
}
}
paned->child1_size = CLAMP (paned->child1_size,
paned->min_position,
paned->max_position);
paned->last_allocation = allocation;
}