gtk3/gtk/gtkvpaned.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

452 lines
13 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-1999. 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 "gtkvpaned.h"
static void gtk_vpaned_class_init (GtkVPanedClass *klass);
static void gtk_vpaned_init (GtkVPaned *vpaned);
static void gtk_vpaned_size_request (GtkWidget *widget,
GtkRequisition *requisition);
static void gtk_vpaned_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static void gtk_vpaned_draw (GtkWidget *widget,
GdkRectangle *area);
static void gtk_vpaned_xor_line (GtkPaned *paned);
static gboolean gtk_vpaned_button_press (GtkWidget *widget,
GdkEventButton *event);
static gboolean gtk_vpaned_button_release (GtkWidget *widget,
GdkEventButton *event);
static gboolean gtk_vpaned_motion (GtkWidget *widget,
GdkEventMotion *event);
GtkType
gtk_vpaned_get_type (void)
{
static GtkType vpaned_type = 0;
if (!vpaned_type)
{
static const GtkTypeInfo vpaned_info =
{
"GtkVPaned",
sizeof (GtkVPaned),
sizeof (GtkVPanedClass),
(GtkClassInitFunc) gtk_vpaned_class_init,
(GtkObjectInitFunc) gtk_vpaned_init,
/* reserved_1 */ NULL,
/* reserved_2 */ NULL,
(GtkClassInitFunc) NULL,
};
vpaned_type = gtk_type_unique(gtk_paned_get_type(), &vpaned_info);
}
return vpaned_type;
}
static void
gtk_vpaned_class_init (GtkVPanedClass *class)
{
GtkWidgetClass *widget_class;
widget_class = (GtkWidgetClass *) class;
widget_class->size_request = gtk_vpaned_size_request;
widget_class->size_allocate = gtk_vpaned_size_allocate;
widget_class->draw = gtk_vpaned_draw;
widget_class->button_press_event = gtk_vpaned_button_press;
widget_class->button_release_event = gtk_vpaned_button_release;
widget_class->motion_notify_event = gtk_vpaned_motion;
}
static void
gtk_vpaned_init (GtkVPaned *vpaned)
{
GtkPaned *paned;
g_return_if_fail (vpaned != NULL);
g_return_if_fail (GTK_IS_PANED (vpaned));
paned = GTK_PANED (vpaned);
paned->cursor_type = GDK_SB_V_DOUBLE_ARROW;
}
GtkWidget *
gtk_vpaned_new (void)
{
GtkVPaned *vpaned;
vpaned = gtk_type_new (GTK_TYPE_VPANED);
return GTK_WIDGET (vpaned);
}
static void
gtk_vpaned_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
GtkPaned *paned;
GtkRequisition child_requisition;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_VPANED (widget));
g_return_if_fail (requisition != NULL);
paned = GTK_PANED (widget);
requisition->width = 0;
requisition->height = 0;
if (paned->child1 && GTK_WIDGET_VISIBLE (paned->child1))
{
gtk_widget_size_request (paned->child1, &child_requisition);
requisition->height = child_requisition.height;
requisition->width = child_requisition.width;
}
if (paned->child2 && GTK_WIDGET_VISIBLE (paned->child2))
{
gtk_widget_size_request (paned->child2, &child_requisition);
requisition->width = MAX (requisition->width, child_requisition.width);
requisition->height += child_requisition.height;
}
requisition->height += GTK_CONTAINER (paned)->border_width * 2 + paned->handle_size;
requisition->width += GTK_CONTAINER (paned)->border_width * 2;
}
static void
gtk_vpaned_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
GtkPaned *paned;
GtkRequisition child1_requisition;
GtkRequisition child2_requisition;
GtkAllocation child1_allocation;
GtkAllocation child2_allocation;
guint16 border_width;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_VPANED (widget));
g_return_if_fail (allocation != NULL);
widget->allocation = *allocation;
paned = GTK_PANED (widget);
border_width = GTK_CONTAINER (widget)->border_width;
if (paned->child1)
gtk_widget_get_child_requisition (paned->child1, &child1_requisition);
else
child1_requisition.height = 0;
if (paned->child2)
gtk_widget_get_child_requisition (paned->child2, &child2_requisition);
else
child2_requisition.height = 0;
gtk_paned_compute_position(paned,
widget->allocation.height
- paned->handle_size
- 2 * border_width,
child1_requisition.height,
child2_requisition.height);
/* Move the handle before the children so we don't get extra expose events */
paned->handle_xpos = 0;
paned->handle_ypos = paned->child1_size + border_width;
paned->handle_width = widget->allocation.width;
paned->handle_height = paned->handle_size;
if (GTK_WIDGET_REALIZED(widget))
{
gdk_window_move_resize (widget->window,
allocation->x, allocation->y,
allocation->width,
allocation->height);
gdk_window_move_resize (paned->handle,
paned->handle_xpos,
paned->handle_ypos,
paned->handle_width,
paned->handle_size);
}
child1_allocation.width = child2_allocation.width = MAX(1, (gint) allocation->width - border_width * 2);
child1_allocation.height = paned->child1_size;
child1_allocation.x = child2_allocation.x = border_width;
child1_allocation.y = border_width;
child2_allocation.y = child1_allocation.y + child1_allocation.height + paned->handle_height;
child2_allocation.height = MAX(1, (gint) allocation->height - child2_allocation.y - border_width);
/* Now allocate the childen, making sure, when resizing not to
* overlap the windows */
if (GTK_WIDGET_MAPPED (widget) &&
paned->child1 && GTK_WIDGET_VISIBLE (paned->child1) &&
paned->child1->allocation.height < child1_allocation.height)
{
if (paned->child2 && GTK_WIDGET_VISIBLE (paned->child2))
gtk_widget_size_allocate(paned->child2, &child2_allocation);
gtk_widget_size_allocate(paned->child1, &child1_allocation);
}
else
{
if (paned->child1 && GTK_WIDGET_VISIBLE (paned->child1))
gtk_widget_size_allocate(paned->child1, &child1_allocation);
if (paned->child2 && GTK_WIDGET_VISIBLE (paned->child2))
gtk_widget_size_allocate(paned->child2, &child2_allocation);
}
}
static void
gtk_vpaned_draw (GtkWidget *widget,
GdkRectangle *area)
{
GtkPaned *paned;
GdkRectangle handle_area, child_area;
guint16 border_width;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_PANED (widget));
if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget))
{
paned = GTK_PANED (widget);
border_width = GTK_CONTAINER (paned)->border_width;
gdk_window_clear_area (widget->window,
area->x, area->y, area->width,
area->height);
handle_area.x = paned->handle_xpos;
handle_area.y = paned->handle_ypos;
handle_area.width = paned->handle_height;
handle_area.height = paned->handle_size;
if (gdk_rectangle_intersect (&handle_area, area, &child_area))
{
if (widget->allocation.height > 2)
{
gdk_draw_point (paned->handle,
widget->style->fg_gc[GTK_STATE_NORMAL],
paned->handle_width/2,
paned->handle_size/2);
gdk_draw_point (paned->handle,
widget->style->bg_gc[GTK_STATE_PRELIGHT],
paned->handle_width/2 - 1,
paned->handle_size/2 - 1);
}
if (widget->allocation.height > 11)
{
gdk_draw_point (paned->handle,
widget->style->fg_gc[GTK_STATE_NORMAL],
paned->handle_width/2 + 5,
paned->handle_size/2);
gdk_draw_point (paned->handle,
widget->style->bg_gc[GTK_STATE_PRELIGHT],
paned->handle_width/2 + 4,
paned->handle_size/2 - 1);
gdk_draw_point (paned->handle,
widget->style->fg_gc[GTK_STATE_NORMAL],
paned->handle_width/2 - 5,
paned->handle_size/2);
gdk_draw_point (paned->handle,
widget->style->bg_gc[GTK_STATE_PRELIGHT],
paned->handle_width/2 - 6,
paned->handle_size/2 - 1);
}
if (widget->allocation.height > 20)
{
gdk_draw_point (paned->handle,
widget->style->fg_gc[GTK_STATE_NORMAL],
paned->handle_width/2 - 10,
paned->handle_size/2);
gdk_draw_point (paned->handle,
widget->style->bg_gc[GTK_STATE_PRELIGHT],
paned->handle_width/2 - 11,
paned->handle_size/2 - 1);
gdk_draw_point (paned->handle,
widget->style->fg_gc[GTK_STATE_NORMAL],
paned->handle_width/2 + 10,
paned->handle_size/2);
gdk_draw_point (paned->handle,
widget->style->bg_gc[GTK_STATE_PRELIGHT],
paned->handle_width/2 + 9,
paned->handle_size/2 - 1);
}
if (widget->allocation.height > 30)
{
gdk_draw_point (paned->handle,
widget->style->fg_gc[GTK_STATE_NORMAL],
paned->handle_width/2 - 15,
paned->handle_size/2);
gdk_draw_point (paned->handle,
widget->style->bg_gc[GTK_STATE_PRELIGHT],
paned->handle_width/2 - 16,
paned->handle_size/2 - 1);
gdk_draw_point (paned->handle,
widget->style->fg_gc[GTK_STATE_NORMAL],
paned->handle_width/2 + 15,
paned->handle_size/2);
gdk_draw_point (paned->handle,
widget->style->bg_gc[GTK_STATE_PRELIGHT],
paned->handle_width/2 + 14,
paned->handle_size/2 - 1);
}
}
/* Redraw the children
*/
if (paned->child1 && gtk_widget_intersect (paned->child1, area, &child_area))
gtk_widget_draw (paned->child1, &child_area);
if (paned->child2 && gtk_widget_intersect (paned->child2, area, &child_area))
gtk_widget_draw (paned->child2, &child_area);
}
}
static void
gtk_vpaned_xor_line (GtkPaned *paned)
{
GtkWidget *widget;
GdkGCValues values;
guint16 ypos;
widget = GTK_WIDGET (paned);
if (!paned->xor_gc)
{
values.function = GDK_INVERT;
values.subwindow_mode = GDK_INCLUDE_INFERIORS;
paned->xor_gc = gdk_gc_new_with_values (widget->window,
&values,
GDK_GC_FUNCTION | GDK_GC_SUBWINDOW);
}
gdk_gc_set_line_attributes (paned->xor_gc, 2, GDK_LINE_SOLID,
GDK_CAP_NOT_LAST, GDK_JOIN_BEVEL);
ypos = paned->child1_size
+ GTK_CONTAINER (paned)->border_width + paned->handle_size / 2;
gdk_draw_line (widget->window, paned->xor_gc,
0,
ypos,
widget->allocation.width - 1,
ypos);
}
static gboolean
gtk_vpaned_button_press (GtkWidget *widget,
GdkEventButton *event)
{
GtkPaned *paned;
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_PANED (widget), FALSE);
paned = GTK_PANED (widget);
if (!paned->in_drag &&
(event->window == paned->handle) && (event->button == 1))
{
paned->in_drag = TRUE;
/* We need a server grab here, not gtk_grab_add(), since
* we don't want to pass events on to the widget's children */
gdk_pointer_grab (paned->handle, FALSE,
GDK_POINTER_MOTION_HINT_MASK
| GDK_BUTTON1_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK, NULL, NULL,
event->time);
paned->child1_size += event->y - paned->handle_size / 2;
paned->child1_size = CLAMP (paned->child1_size, 0,
widget->allocation.height -
paned->handle_size -
2 * GTK_CONTAINER (paned)->border_width);
gtk_vpaned_xor_line(paned);
return TRUE;
}
return FALSE;
}
static gboolean
gtk_vpaned_button_release (GtkWidget *widget,
GdkEventButton *event)
{
GtkPaned *paned;
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_PANED (widget), FALSE);
paned = GTK_PANED (widget);
if (paned->in_drag && (event->button == 1))
{
gtk_vpaned_xor_line (paned);
paned->in_drag = FALSE;
paned->position_set = TRUE;
gdk_pointer_ungrab (event->time);
gtk_widget_queue_resize (GTK_WIDGET (paned));
return TRUE;
}
return FALSE;
}
static gboolean
gtk_vpaned_motion (GtkWidget *widget,
GdkEventMotion *event)
{
GtkPaned *paned;
gint y;
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_PANED (widget), FALSE);
if (event->is_hint || event->window != widget->window)
gtk_widget_get_pointer (widget, NULL, &y);
else
y = event->y;
paned = GTK_PANED (widget);
if (paned->in_drag)
{
gint size = y - GTK_CONTAINER(paned)->border_width - paned->handle_size / 2;
gtk_vpaned_xor_line (paned);
paned->child1_size = CLAMP (size, paned->min_position, paned->max_position);
gtk_vpaned_xor_line(paned);
}
return TRUE;
}