glade-fixed.c 32.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * glade-fixed.c - A GladeWidget derivative object wrapper designed to
 *                 handle free-form child placement for containers such as 
 *                 GtkFixed and GtkLayout.
 *
 * Copyright (C) 2006 The GNOME Foundation.
 *
 * Author(s):
 *      Tristan Van Berkom <tvb@gnome.org>
 *
 * 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.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
/**
 * SECTION:glade-fixed
 * @Short_Description: An object wrapper for free-form placement container widgets.
 *
 * #GladeFixed is a specialized #GladeWidget to handle free-form child
 * placements in containers that support this, it is designed with properties
 * and signals with flexable integration in mind.
 *
 * If you set the x-prop/y-prop/width-prop/height-prop properties and
 * leave the signals alone, #GladeFixed will assume you are like a 
 * GtkFixed/GtkLayout widget and will use pixel counts as units for
 * these properties.
 *
 * If you handle the configure-child/configure-end[/configure-begin] signals
 * and dont let them propagate to the GladeFixed, then the x-prop/y-prop/width-prop/height-prop
 * properties will be completely ignored and it is up to the implementor to play
 * with whatever child packing properties are available to make a closest match
 * for the values passed to configure-child via the #GdkRectangle.
 */

52 53
#include <glib-object.h>
#include <glib/gi18n-lib.h>
54
#include <gdk/gdkkeysyms.h>
55 56 57 58 59 60 61 62 63 64 65 66 67

#include "glade.h"
#include "glade-accumulators.h"
#include "glade-marshallers.h"
#include "glade-popup.h"
#include "glade-fixed.h"

/* properties */
enum {
	PROP_0,
	PROP_X_PROP,
	PROP_Y_PROP,
	PROP_WIDTH_PROP,
68
	PROP_HEIGHT_PROP,
69
	PROP_CAN_RESIZE
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
};

/* signals */
enum {
	CONFIGURE_CHILD,
	CONFIGURE_BEGIN,
	CONFIGURE_END,
	FIXED_SIGNALS
};

typedef struct {
	gulong press_id;
	gulong release_id;
	gulong motion_id;
} GFSigData;

#define CHILD_WIDTH_MIN    20
#define CHILD_HEIGHT_MIN   20
#define CHILD_WIDTH_DEF    100
#define CHILD_HEIGHT_DEF   80

91 92
#define GRAB_BORDER_WIDTH  7
#define GRAB_CORNER_WIDTH  7
93 94 95 96 97 98 99 100 101 102 103 104 105 106

static GObjectClass *parent_class;
static guint         glade_fixed_signals[FIXED_SIGNALS];

/*******************************************************************************
                             Static helper routines
 *******************************************************************************/
static GladeCursorType
glade_fixed_get_operation (GtkWidget       *widget,
			   gint             x,
			   gint             y)
{
	GladeCursorType operation = GLADE_CURSOR_DRAG;

107 108 109 110 111 112 113
#if 0
	g_print ("%s called (width %d height %d x %d y %d)\n",
		 __FUNCTION__,
		 widget->allocation.width,
		 widget->allocation.height, x, y);
#endif

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
	if (x < GRAB_BORDER_WIDTH) {
		if (y < GRAB_BORDER_WIDTH)
			operation = GLADE_CURSOR_RESIZE_TOP_LEFT;
		else if (y > widget->allocation.height - GRAB_BORDER_WIDTH)
			operation = GLADE_CURSOR_RESIZE_BOTTOM_LEFT;
		else
			operation = GLADE_CURSOR_RESIZE_LEFT;
	} else if (x > widget->allocation.width - GRAB_BORDER_WIDTH) {
		if (y < GRAB_BORDER_WIDTH)
			operation = GLADE_CURSOR_RESIZE_TOP_RIGHT;
		else if (y > widget->allocation.height - GRAB_BORDER_WIDTH)
			operation = GLADE_CURSOR_RESIZE_BOTTOM_RIGHT;
		else
			operation = GLADE_CURSOR_RESIZE_RIGHT;
	} else if (y < GRAB_BORDER_WIDTH) {
		if (x < GRAB_BORDER_WIDTH)
			operation = GLADE_CURSOR_RESIZE_TOP_LEFT;
		else if (x > widget->allocation.width - GRAB_BORDER_WIDTH)
			operation = GLADE_CURSOR_RESIZE_TOP_RIGHT;
		else
			operation = GLADE_CURSOR_RESIZE_TOP;
	} else if (y > widget->allocation.height - GRAB_BORDER_WIDTH) {
		if (x < GRAB_BORDER_WIDTH)
			operation = GLADE_CURSOR_RESIZE_BOTTOM_LEFT;
		else if (x > widget->allocation.width - GRAB_BORDER_WIDTH)
			operation = GLADE_CURSOR_RESIZE_BOTTOM_RIGHT;
		else
			operation = GLADE_CURSOR_RESIZE_BOTTOM;
	}
	return operation;
}

static void
glade_fixed_save_state (GladeFixed  *fixed,
			GladeWidget *child)
{
150
	gtk_widget_get_pointer (GTK_WIDGET (GLADE_WIDGET (fixed)->object), 
151
				&(GLADE_FIXED (fixed)->pointer_x_origin), 
152
				&(GLADE_FIXED (fixed)->pointer_y_origin));
153 154 155 156 157 158 159 160 161

	gtk_widget_translate_coordinates (GTK_WIDGET (child->object),
					  GTK_WIDGET (GLADE_WIDGET (fixed)->object),
					  0, 0,
					  &(fixed->child_x_origin), 
					  &(fixed->child_y_origin));

	fixed->child_width_origin  = GTK_WIDGET (child->object)->allocation.width;
	fixed->child_height_origin = GTK_WIDGET (child->object)->allocation.height;
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233

	fixed->pointer_x_child_origin = 
		fixed->pointer_x_origin - fixed->child_x_origin;
	fixed->pointer_y_child_origin = 
		fixed->pointer_y_origin - fixed->child_y_origin;
}


static void 
glade_fixed_filter_event (GladeFixed *fixed,
			  gint       *x,
			  gint       *y,
			  gint        left,
			  gint        right,
			  gint        top,
			  gint        bottom)
{
	gint cont_width, cont_height;

	g_return_if_fail (x && y);

	cont_width  = GTK_WIDGET (GLADE_WIDGET (fixed)->object)->allocation.width;
	cont_height = GTK_WIDGET (GLADE_WIDGET (fixed)->object)->allocation.height;

	/* Clip out mouse events that are outside the window.
	 */
	if ((left || fixed->operation == GLADE_CURSOR_DRAG) &&
	    *x - fixed->pointer_x_child_origin < 0)
		*x = fixed->pointer_x_child_origin;
	if ((top || fixed->operation == GLADE_CURSOR_DRAG) && 
	    *y - fixed->pointer_y_child_origin < 0)
		*y = fixed->pointer_y_child_origin;
	
	if ((right || fixed->operation == GLADE_CURSOR_DRAG) && 
	    *x + (fixed->child_width_origin - 
			  fixed->pointer_x_child_origin) > cont_width)
		*x = cont_width - (fixed->child_width_origin - 
				  fixed->pointer_x_child_origin);
	if ((bottom || fixed->operation == GLADE_CURSOR_DRAG) && 
	    *y + (fixed->child_height_origin - 
			   fixed->pointer_y_child_origin) > cont_height)
		*y = cont_height - (fixed->child_height_origin - 
				   fixed->pointer_y_child_origin);
	
	/* Clip out mouse events that mean shrinking to less than 0.
	 */
	if (left && 
	    (*x - fixed->pointer_x_child_origin) > 
	    (fixed->child_x_origin + (fixed->child_width_origin - CHILD_WIDTH_MIN))) {
		*x = (fixed->child_x_origin + (fixed->child_width_origin - CHILD_WIDTH_MIN)) -
			fixed->pointer_x_child_origin;
	    } else if (right &&
		       (*x - fixed->pointer_x_child_origin) < 
		       fixed->child_x_origin - (fixed->child_width_origin + CHILD_WIDTH_MIN)) {
		*x = (fixed->child_x_origin - (fixed->child_width_origin + CHILD_WIDTH_MIN)) +
			fixed->pointer_x_child_origin;
	}


	if (top && 
	    (*y - fixed->pointer_y_child_origin) > 
	    (fixed->child_y_origin + (fixed->child_height_origin - CHILD_HEIGHT_MIN))) {
		*y = (fixed->child_y_origin + (fixed->child_height_origin - CHILD_HEIGHT_MIN)) - 
			fixed->pointer_y_child_origin;
	} else if (bottom &&
		   (*y - fixed->pointer_y_child_origin) < 
		   fixed->child_y_origin - (fixed->child_height_origin + CHILD_HEIGHT_MIN)) {
		*y = (fixed->child_y_origin - (fixed->child_height_origin + CHILD_HEIGHT_MIN)) + 
			fixed->pointer_y_child_origin;
	}
}

234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
static void
glade_fixed_handle_swindow (GladeFixed   *fixed,
			    GdkRectangle *area)
{
	GtkWidget     *fixed_widget = GTK_WIDGET (GLADE_WIDGET (fixed)->object);
	GtkWidget     *swindow = NULL, *swindow_child = NULL;
	GtkAdjustment *hadj, *vadj;
	gint           x, y;

	swindow_child = swindow = fixed_widget;
	while (swindow && !GTK_IS_SCROLLED_WINDOW (swindow))
	{
		if (!GTK_IS_VIEWPORT (swindow))
			swindow_child = swindow;

		swindow = swindow->parent;
	}

	if (swindow)
	{
		/* Set the adjustments to use appropriate pixel units and then
		 * square in on the target drop area.
		 */
		hadj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (swindow));
		vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (swindow));

		g_object_set (G_OBJECT (hadj),
			      "lower", 0.0F,
			      "upper", (gdouble) swindow_child->allocation.width + 0.0,
			      NULL);

		g_object_set (G_OBJECT (vadj),
			      "lower", 0.0F,
			      "upper", (gdouble) swindow_child->allocation.height + 0.0,
			      NULL);

		gtk_widget_translate_coordinates (fixed_widget,
						  swindow_child,
						  area->x, area->y, &x, &y);

		gtk_adjustment_clamp_page (hadj, x, x + area->width);
		gtk_adjustment_clamp_page (vadj, y, y + area->height);
	}
}

279 280 281 282 283 284 285 286 287
static void
glade_fixed_configure_widget (GladeFixed   *fixed,
			      GladeWidget  *child)
{
	GladeWidget    *gwidget = GLADE_WIDGET (fixed);
	GdkRectangle    new_area;
	gboolean        handled, right, left, top, bottom;
	gint            x, y;

288
	gtk_widget_get_pointer (GTK_WIDGET (gwidget->object), &x, &y);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
289
	
290 291 292 293 294 295 296 297
	right  = GLADE_FIXED_CURSOR_RIGHT  (fixed->operation);
	left   = GLADE_FIXED_CURSOR_LEFT   (fixed->operation);
	top    = GLADE_FIXED_CURSOR_TOP    (fixed->operation);
	bottom = GLADE_FIXED_CURSOR_BOTTOM (fixed->operation);

	/* Filter out events that make your widget go out of bounds */
	glade_fixed_filter_event (fixed, &x, &y, left, right, top, bottom);

Tristan Van Berkom's avatar
Tristan Van Berkom committed
298 299 300 301 302
	new_area.x      = fixed->child_x_origin;
	new_area.y      = fixed->child_y_origin;
	new_area.width  = fixed->child_width_origin;
	new_area.height = fixed->child_height_origin;

303 304 305 306 307 308 309 310 311
	/* Modify current size.
	 */
	if (fixed->operation == GLADE_CURSOR_DRAG)
	{
		/* Move widget */
		new_area.x = fixed->child_x_origin + 
			x - fixed->pointer_x_origin;
		new_area.y = fixed->child_y_origin + 
			y - fixed->pointer_y_origin;
Tristan Van Berkom's avatar
Tristan Van Berkom committed
312

313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
	} else {

		if (bottom) 
		{
			new_area.height =
				fixed->child_height_origin +
				(y - fixed->pointer_y_origin);
		} else if (top)
		{

			new_area.height =
				fixed->child_height_origin -
				(y - fixed->pointer_y_origin);
			new_area.y =
				fixed->child_y_origin +
				(y - fixed->pointer_y_origin);
		}

		if (right) 
		{
			new_area.width =
				fixed->child_width_origin +
				(x - fixed->pointer_x_origin);
		} else if (left)
		{
			new_area.width =
				fixed->child_width_origin -
				(x - fixed->pointer_x_origin);
			new_area.x =
				fixed->child_x_origin + 
				(x - fixed->pointer_x_origin);
		}
	}

	/* Trim */
	if (new_area.width < CHILD_WIDTH_MIN)
		new_area.width = CHILD_WIDTH_MIN;
	if (new_area.height < CHILD_WIDTH_MIN)
		new_area.height = CHILD_HEIGHT_MIN;

353 354 355 356 357 358
	/* before configuring the child widget, make make sure the target child
	 * widget area is visible if the GladeFixed is placed inside a scrolled 
	 * window or a viewport inside a scrolled window.
	 */
	glade_fixed_handle_swindow (fixed, &new_area);

359 360 361 362
	/* Apply new rectangle to the object */
	g_signal_emit (G_OBJECT (fixed), 
		       glade_fixed_signals[CONFIGURE_CHILD],
		       0, child, &new_area, &handled);
363

364 365 366 367 368 369
	/* Correct glitches when some widgets are draged over others */
	gtk_widget_queue_draw (GTK_WIDGET (GLADE_WIDGET (fixed)->object));
}

static void
glade_fixed_disconnect_child (GladeFixed   *fixed,
370
			      GladeWidget  *child)
371 372 373
{
	GFSigData *data;

374
	if (GTK_IS_WIDGET (child->object) == FALSE)
Tristan Van Berkom's avatar
Tristan Van Berkom committed
375 376
		return;

377 378
	if ((data = g_object_get_data (G_OBJECT (child), "glade-fixed-signal-data")) != NULL)
	{
Tristan Van Berkom's avatar
Tristan Van Berkom committed
379 380 381
		g_signal_handler_disconnect (child, data->press_id);
		g_signal_handler_disconnect (child, data->release_id);
		g_signal_handler_disconnect (child, data->motion_id);
382
                    
383 384 385 386 387 388
		g_object_set_data (G_OBJECT (child), "glade-fixed-signal-data", NULL);
	}
}

static void
glade_fixed_connect_child (GladeFixed   *fixed,
389
			   GladeWidget  *child)
390 391 392
{
	GFSigData *data;

393
	if (GTK_IS_WIDGET (child->object) == FALSE)
Tristan Van Berkom's avatar
Tristan Van Berkom committed
394 395
		return;

396 397 398 399 400
	if ((data = g_object_get_data (G_OBJECT (child), "glade-fixed-signal-data")) != NULL)
		glade_fixed_disconnect_child (fixed, child);
	
	data = g_new (GFSigData, 1);

401 402
	/* Connect-after here... leave a chance for selection
	 */
403
	data->press_id =
404
		g_signal_connect_after
Tristan Van Berkom's avatar
Tristan Van Berkom committed
405
		(child, "button-press-event", G_CALLBACK
406 407 408
		 (GLADE_FIXED_GET_CLASS(fixed)->child_event), fixed);
	data->release_id =
		g_signal_connect
Tristan Van Berkom's avatar
Tristan Van Berkom committed
409
		(child, "button-release-event", G_CALLBACK
410 411 412
		 (GLADE_FIXED_GET_CLASS(fixed)->child_event), fixed);
	data->motion_id = 
		g_signal_connect
Tristan Van Berkom's avatar
Tristan Van Berkom committed
413
		(child, "motion-notify-event", G_CALLBACK
414 415
		 (GLADE_FIXED_GET_CLASS(fixed)->child_event), fixed);

416

417 418 419 420 421 422 423 424 425 426 427 428
	g_object_set_data_full (G_OBJECT (child), "glade-fixed-signal-data", 
				data, g_free);
}

/*******************************************************************************
                               GladeFixedClass
 *******************************************************************************/
static gboolean
glade_fixed_configure_child_impl (GladeFixed   *fixed,
				  GladeWidget  *child,
				  GdkRectangle *rect)
{
429 430 431 432 433 434
	/* Make sure we can modify these properties */
	glade_widget_pack_property_set_enabled (child, fixed->x_prop, TRUE);
	glade_widget_pack_property_set_enabled (child, fixed->y_prop, TRUE);
	glade_widget_property_set_enabled (child, fixed->width_prop, TRUE);
	glade_widget_property_set_enabled (child, fixed->height_prop, TRUE);

435 436 437 438 439 440 441 442
 	glade_widget_pack_property_set (child, fixed->x_prop, rect->x);
 	glade_widget_pack_property_set (child, fixed->y_prop, rect->y);
 	glade_widget_property_set (child, fixed->width_prop, rect->width);
 	glade_widget_property_set (child, fixed->height_prop, rect->height);
	return TRUE;
}


Tristan Van Berkom's avatar
Tristan Van Berkom committed
443
static gboolean
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
glade_fixed_configure_end_impl (GladeFixed  *fixed,
				GladeWidget *child)
{
	GValue x_value = { 0, };
	GValue y_value = { 0, };
	GValue width_value = { 0, };
	GValue height_value = { 0, };
	GValue new_x_value = { 0, };
	GValue new_y_value = { 0, };
	GValue new_width_value = { 0, };
	GValue new_height_value = { 0, };
	GladeProperty *x_prop, *y_prop, *width_prop, *height_prop;

	x_prop      = glade_widget_get_pack_property (child, fixed->x_prop);
	y_prop      = glade_widget_get_pack_property (child, fixed->y_prop);
	width_prop  = glade_widget_get_property      (child, fixed->width_prop);
	height_prop = glade_widget_get_property      (child, fixed->height_prop);

Tristan Van Berkom's avatar
Tristan Van Berkom committed
462 463 464 465
	g_return_val_if_fail (GLADE_IS_PROPERTY (x_prop), FALSE);
	g_return_val_if_fail (GLADE_IS_PROPERTY (y_prop), FALSE);
	g_return_val_if_fail (GLADE_IS_PROPERTY (width_prop), FALSE);
	g_return_val_if_fail (GLADE_IS_PROPERTY (height_prop), FALSE);
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481

	g_value_init (&x_value, G_TYPE_INT);
	g_value_init (&y_value, G_TYPE_INT);
	g_value_init (&width_value, G_TYPE_INT);
	g_value_init (&height_value, G_TYPE_INT);

	glade_property_get_value (x_prop, &new_x_value);
	glade_property_get_value (y_prop, &new_y_value);
	glade_property_get_value (width_prop, &new_width_value);
	glade_property_get_value (height_prop, &new_height_value);

	g_value_set_int (&x_value, fixed->child_x_origin);
	g_value_set_int (&y_value, fixed->child_y_origin);
	g_value_set_int (&width_value, fixed->child_width_origin);
	g_value_set_int (&height_value, fixed->child_height_origin);

482 483 484 485
	glade_command_push_group (_("Placing %s inside %s"), 
				  child->name,
				  GLADE_WIDGET (fixed)->name);

486 487 488 489 490 491 492
	/* whew, all that for this call !
	 */
	glade_command_set_properties (x_prop, &x_value, &new_x_value,
				      y_prop, &y_value, &new_y_value,
				      width_prop, &width_value, &new_width_value,
				      height_prop, &height_value, &new_height_value,
				      NULL);
493 494 495

	glade_command_pop_group ();

496 497 498 499 500 501 502 503
	g_value_unset (&x_value);
	g_value_unset (&y_value);
	g_value_unset (&width_value);
	g_value_unset (&height_value);
	g_value_unset (&new_x_value);
	g_value_unset (&new_y_value);
	g_value_unset (&new_width_value);
	g_value_unset (&new_height_value);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
504 505

	return TRUE;
506 507
}

508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525

static void
glade_fixed_cancel_operation (GladeFixed      *fixed,
			      GladeCursorType  new_operation)
{
	gboolean handled;
	
	g_signal_emit (G_OBJECT (fixed),
		       glade_fixed_signals[CONFIGURE_END],
		       0, fixed->configuring, &handled);

	/* Leave the machine state intact untill after
	 * the user handled signal. 
	 */
	fixed->operation   = new_operation;
	fixed->configuring = NULL;
}			      

526 527 528
static gboolean
glade_fixed_handle_child_event (GladeFixed  *fixed,
				GladeWidget *child,
529
				GtkWidget   *event_widget,
530 531
				GdkEvent    *event)
{
532
	GladeCursorType  operation;
533
	GdkModifierType  event_state = 0;
534
	GladePointerMode pointer_mode;
535
	GtkWidget       *fixed_widget, *child_widget;
536
	gint             fixed_x, fixed_y, child_x, child_y;
537
	gboolean         handled = FALSE, sig_handled;
538

Tristan Van Berkom's avatar
Tristan Van Berkom committed
539
	fixed_widget = GTK_WIDGET (GLADE_WIDGET (fixed)->object);
540
	child_widget = GTK_WIDGET (child->object);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
541

542 543
	pointer_mode = glade_app_get_pointer_mode ();

544 545 546 547
	/* when widget->window points to a parent window, these calculations
	 * would be wrong if we based them on the GTK_WIDGET (fixed)->window,
	 * so we must always consult the event widget's window
	 */
548 549 550 551 552 553 554 555 556
	gtk_widget_get_pointer (fixed_widget, &fixed_x, &fixed_y);
	
	/* Container widgets are trustable to have widget->window occupying
	 * the entire widget allocation (gtk_widget_get_pointer broken on GtkEntry).
	 */
	gtk_widget_translate_coordinates (fixed_widget,
					  child_widget,
					  fixed_x, fixed_y,
					  &child_x, &child_y);
557

558 559
	if (fixed->can_resize)
		operation = glade_fixed_get_operation (GTK_WIDGET (child->object), 
560
						       child_x, child_y);
561 562
	else
		operation = GLADE_CURSOR_DRAG;
563

564 565
	gdk_event_get_state (event, &event_state);

566 567 568 569 570
	switch (event->type)
	{
	case GDK_MOTION_NOTIFY:
		if (fixed->configuring == NULL)
		{
571 572
			if ((event_state & GDK_SHIFT_MASK) ||
			    pointer_mode == GLADE_POINTER_DRAG_RESIZE)
573 574
				glade_cursor_set (((GdkEventAny *)event)->window, 
						  operation);
575
			else if (pointer_mode == GLADE_POINTER_SELECT)
576 577 578 579
				glade_cursor_set (((GdkEventAny *)event)->window, 
						  GLADE_CURSOR_SELECTOR);

		} else if (fixed->configuring && !(event_state & GDK_BUTTON1_MASK))
580 581 582 583 584 585 586
		{
			/* Cancel drags that no longer have mouse down */
			glade_cursor_set (((GdkEventAny *)event)->window, 
					  operation);

			glade_fixed_cancel_operation (fixed, operation);
			handled = TRUE;
587
		} else if (fixed->configuring)
588
		{
589 590 591 592
			/* Need to update mouse for configures. */
			gtk_widget_get_pointer (fixed_widget,
						&fixed->mouse_x, &fixed->mouse_y);

593
			glade_fixed_configure_widget (fixed, child);
594
			glade_cursor_set (((GdkEventAny *)event)->window, 
Tristan Van Berkom's avatar
Tristan Van Berkom committed
595
					  fixed->operation);
596 597
			handled = TRUE;
		}
598

599 600 601
		gdk_window_get_pointer (GTK_WIDGET (child->object)->window, NULL, NULL, NULL);
		break;
	case GDK_BUTTON_PRESS:
602 603 604
		/* We cant rely on GDK_BUTTON1_MASK since event->state isnt yet updated
		 * by the current event itself 
		 */
605 606 607
		if (((GdkEventButton *)event)->button == 1 && 
		    ((event_state & GDK_SHIFT_MASK) ||
		     pointer_mode == GLADE_POINTER_DRAG_RESIZE))
608 609 610 611 612 613 614 615 616 617
		{
			fixed->configuring = child;
			/* Save widget allocation and pointer pos */
			glade_fixed_save_state (fixed, child);
			
			fixed->operation = operation;
			glade_cursor_set (((GdkEventAny *)event)->window, fixed->operation);

			g_signal_emit (G_OBJECT (fixed),
				       glade_fixed_signals[CONFIGURE_BEGIN],
Tristan Van Berkom's avatar
Tristan Van Berkom committed
618
				       0, child, &sig_handled);
619 620 621 622 623

			handled = TRUE;
		}
		break;
	case GDK_BUTTON_RELEASE:
624
		if (((GdkEventButton *)event)->button == 1 && fixed->configuring)
625
		{
626

627 628
			if ((event_state & GDK_SHIFT_MASK) ||
			    pointer_mode == GLADE_POINTER_DRAG_RESIZE)
629 630 631 632 633
				glade_cursor_set (((GdkEventAny *)event)->window,
						  operation);
			else
				glade_cursor_set (((GdkEventAny *)event)->window,
						  GLADE_CURSOR_SELECTOR);
634

635
			glade_fixed_cancel_operation (fixed, operation);
636 637 638 639 640 641 642 643 644 645
			handled = TRUE;
		}
		break;
	default:
		break;
	}
	return handled;
}

static gint
646
glade_fixed_child_event (GladeWidget *gwidget, 
647 648 649
			 GdkEvent    *event,
			 GladeFixed  *fixed)
{
650
	GtkWidget *event_widget;
651

Tristan Van Berkom's avatar
Tristan Van Berkom committed
652
	/* Get the basic event info... */
653 654
	gdk_window_get_user_data (((GdkEventAny *)event)->window, (gpointer)&event_widget);

655 656 657
	/* Skip all this choosyness if we're already in a drag/resize
	 */
	if (fixed->configuring)
658
	{
659
		return glade_fixed_handle_child_event
660
			(fixed, fixed->configuring, event_widget, event);
661
	}
662 663 664

	g_return_val_if_fail (GLADE_IS_WIDGET (gwidget), FALSE);

665
	/* Early return for fixed children with selection in
666 667
	 * the palette.
	 */
668
	if (GLADE_IS_FIXED (gwidget) &&
669
	    glade_app_get_pointer_mode() == GLADE_POINTER_ADD_WIDGET)
670 671 672 673
	{
		glade_cursor_set (((GdkEventAny *)event)->window, 
				  GLADE_CURSOR_ADD_WIDGET);
		return FALSE;
674 675
	}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
676 677 678
	return glade_fixed_handle_child_event
		(fixed, gwidget, event_widget, event);

679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
}

/*******************************************************************************
                               GladeWidgetClass
 *******************************************************************************/
static void
glade_fixed_add_child_impl (GladeWidget *gwidget_fixed,
			    GladeWidget *child,
			    gboolean     at_mouse)
{
	GladeFixed   *fixed = GLADE_FIXED (gwidget_fixed);
	GdkRectangle  rect;
	gboolean      handled;

	g_return_if_fail (GLADE_IS_FIXED (fixed));
	g_return_if_fail (GLADE_IS_WIDGET (child));

	/* Chain up for the basic parenting */
697
	GLADE_WIDGET_CLASS (parent_class)->add_child
698
		(GLADE_WIDGET (fixed), child, at_mouse);
699 700 701 702 703

	/* We only operate on widgets here */
	if (!GTK_IS_WIDGET (child->object))
		return;
		
704 705 706 707 708 709 710
	gtk_widget_add_events (GTK_WIDGET (child->object),
			       GDK_POINTER_MOTION_MASK      |
			       GDK_POINTER_MOTION_HINT_MASK |
			       GDK_BUTTON_PRESS_MASK        |
			       GDK_BUTTON_RELEASE_MASK      |
			       GDK_ENTER_NOTIFY_MASK);

711
	glade_fixed_connect_child (fixed, child);
712 713 714 715 716

	/* Setup rect and send configure
	 */
	if (fixed->creating)
	{
Tristan Van Berkom's avatar
Tristan Van Berkom committed
717 718
		rect.x      = fixed->mouse_x;
		rect.y      = fixed->mouse_y;
719 720
		rect.width  = CHILD_WIDTH_DEF;
		rect.height = CHILD_HEIGHT_DEF;
721 722 723 724 725 726

		g_signal_emit (G_OBJECT (fixed),
			       glade_fixed_signals[CONFIGURE_CHILD],
			       0, child, &rect, &handled);
	} 
	else if (at_mouse)
727 728 729
	{
		rect.x      = fixed->mouse_x;
		rect.y      = fixed->mouse_y;
730 731
		rect.width  = GTK_WIDGET (child->object)->allocation.width;
		rect.height = GTK_WIDGET (child->object)->allocation.height;
732

733 734
		if (rect.width < CHILD_WIDTH_DEF)
			rect.width = CHILD_WIDTH_DEF;
735

736
		if (rect.height < CHILD_HEIGHT_DEF)
737
			rect.height = CHILD_HEIGHT_DEF;
738 739 740 741

		g_signal_emit (G_OBJECT (fixed),
			       glade_fixed_signals[CONFIGURE_CHILD],
			       0, child, &rect, &handled);
742 743 744 745
	}
	return;
}

746 747
static void
glade_fixed_remove_child_impl (GladeWidget *fixed,
748
			       GladeWidget *child)
749
{
750
	glade_fixed_disconnect_child (GLADE_FIXED (fixed), child);
751 752

	/* Chain up for the basic unparenting */
753
	GLADE_WIDGET_CLASS (parent_class)->remove_child
754 755 756
		(GLADE_WIDGET (fixed), child);
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
757 758 759 760 761 762
static void
glade_fixed_replace_child_impl (GladeWidget *fixed,
				GObject     *old_object,
				GObject     *new_object)
{
	GladeWidget *gnew_widget = glade_widget_get_from_gobject (new_object);
763
	GladeWidget *gold_widget = glade_widget_get_from_gobject (old_object);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
764

765 766
	if (gold_widget)
		glade_fixed_disconnect_child (GLADE_FIXED (fixed), gold_widget);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
767 768

	/* Chain up for the basic reparenting */
769
	GLADE_WIDGET_CLASS (parent_class)->replace_child
Tristan Van Berkom's avatar
Tristan Van Berkom committed
770 771 772
		(GLADE_WIDGET (fixed), old_object, new_object);

	if (gnew_widget)
773
		glade_fixed_connect_child (GLADE_FIXED (fixed), gnew_widget);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
774
}
775

776
static gint
777 778
glade_fixed_event (GladeWidget *gwidget_fixed,
		   GdkEvent    *event)
779
{
780 781 782 783
	GladeFixed         *fixed = GLADE_FIXED (gwidget_fixed);
	GladeWidgetAdaptor *adaptor;
	GtkWidget          *event_widget;
	gboolean            handled = FALSE;
784

785
	adaptor = glade_palette_get_current_item (glade_app_get_palette ());
786

Tristan Van Berkom's avatar
Tristan Van Berkom committed
787 788 789
	/* Get the event widget and the deep widget */
	gdk_window_get_user_data (((GdkEventAny *)event)->window, (gpointer)&event_widget);

790
	/* If the GladeWidget used this event... let it slide.
791
	 */
792
	if (GLADE_WIDGET_CLASS (parent_class)->event (gwidget_fixed, event))
793 794
		return TRUE;

795 796 797 798 799 800
	switch (event->type)
	{

	case GDK_BUTTON_PRESS:
	case GDK_MOTION_NOTIFY:
	case GDK_BUTTON_RELEASE:
801 802
		gtk_widget_get_pointer (GTK_WIDGET (gwidget_fixed->object),
					&fixed->mouse_x, &fixed->mouse_y);
803 804
		if (fixed->configuring)
		{
805 806 807
			return glade_fixed_handle_child_event
				(fixed, fixed->configuring, 
				 event_widget, event);
808 809 810 811 812 813 814 815
		} 
		break;
	default:
		break;
	}

	switch (event->type)
	{
816
	case GDK_BUTTON_PRESS: /* add widget */
817
		if (((GdkEventButton *) event)->button == 1)
818 819
		{

820
			if (adaptor != NULL)
821 822 823 824 825
			{
				/* A widget type is selected in the palette.
				 * Add a new widget of that type.
				 */
				fixed->creating = TRUE;
826
				glade_command_create (adaptor, 
827 828
						      GLADE_WIDGET (fixed), NULL, 
						      GLADE_WIDGET (fixed)->project);
829 830
				fixed->creating = FALSE;
				
831 832
				glade_palette_deselect_current_item (glade_app_get_palette(), TRUE);
					
833 834 835 836 837
				handled = TRUE;
			}
		}
		break;
	case GDK_MOTION_NOTIFY:
838
		if (glade_app_get_pointer_mode() == GLADE_POINTER_ADD_WIDGET)
839
		{
840 841
			glade_cursor_set (((GdkEventAny *)event)->window, 
					  GLADE_CURSOR_ADD_WIDGET);
842

843 844
			handled = TRUE;
		}
845 846
		else if (GLADE_IS_FIXED (gwidget_fixed->parent) == FALSE &&
			 glade_app_get_pointer_mode() == GLADE_POINTER_SELECT)
847
			glade_cursor_set (((GdkEventAny *)event)->window,
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
					  GLADE_CURSOR_SELECTOR);
		break;
	default:
		break;
	}
	return handled;
}

/*******************************************************************************
                                   GObjectClass
 *******************************************************************************/
static void
glade_fixed_finalize (GObject *object)
{
	GladeFixed *fixed = GLADE_FIXED (object);

	/* A GladeFixed should be finalized as a result of its
	 * GtkContainer being destroyed, so we shouldn't need to bother
	 * about disconnecting all the child signals.
	 */
	g_free (fixed->x_prop);
	g_free (fixed->y_prop);
	g_free (fixed->width_prop);
	g_free (fixed->height_prop);

	G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
glade_fixed_set_property (GObject      *object,
			  guint         prop_id,
			  const GValue *value,
			  GParamSpec   *pspec)
{
	GladeFixed *fixed = GLADE_FIXED (object);

	switch (prop_id)
	{
	case PROP_X_PROP:
		g_free (fixed->x_prop);
		fixed->x_prop = g_value_dup_string (value);
		break;
	case PROP_Y_PROP:
		g_free (fixed->y_prop);
		fixed->y_prop = g_value_dup_string (value);
		break;
	case PROP_WIDTH_PROP:
		g_free (fixed->width_prop);
		fixed->width_prop = g_value_dup_string (value);
		break;
	case PROP_HEIGHT_PROP:
		g_free (fixed->height_prop);
		fixed->height_prop = g_value_dup_string (value);
		break;
902 903 904
	case PROP_CAN_RESIZE:
		fixed->can_resize = g_value_get_boolean (value);
		break;
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

static void
glade_fixed_get_property (GObject    *object,
			  guint       prop_id,
			  GValue     *value,
			  GParamSpec *pspec)
{
	GladeFixed *fixed = GLADE_FIXED (object);

	switch (prop_id)
	{
921 922 923 924 925
	case PROP_X_PROP:           g_value_set_string  (value, fixed->x_prop);           break;
	case PROP_Y_PROP:           g_value_set_string  (value, fixed->y_prop);           break;
	case PROP_WIDTH_PROP:       g_value_set_string  (value, fixed->width_prop);       break;
	case PROP_HEIGHT_PROP:      g_value_set_string  (value, fixed->height_prop);      break;
	case PROP_CAN_RESIZE:       g_value_set_boolean (value, fixed->can_resize);       break;
926 927 928 929 930 931 932 933 934 935 936 937 938 939
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

static void
glade_fixed_init (GladeFixed *fixed)
{
	/* Set defaults */
	fixed->x_prop      = g_strdup ("x");
	fixed->y_prop      = g_strdup ("y");
	fixed->width_prop  = g_strdup ("width");
	fixed->height_prop = g_strdup ("height");
940
	fixed->can_resize  = TRUE;
941 942 943 944 945 946
}

static void
glade_fixed_class_init (GladeFixedClass *fixed_class)
{
	GObjectClass     *gobject_class = G_OBJECT_CLASS (fixed_class);
947
	GladeWidgetClass *gwidget_class = GLADE_WIDGET_CLASS (fixed_class);
948 949 950 951 952 953 954 955 956 957 958

	parent_class = 
		G_OBJECT_CLASS
		(g_type_class_peek_parent (gobject_class));

	gobject_class->finalize         = glade_fixed_finalize;
	gobject_class->set_property     = glade_fixed_set_property;
	gobject_class->get_property     = glade_fixed_get_property;
	
	gwidget_class->event            = glade_fixed_event;
	gwidget_class->add_child        = glade_fixed_add_child_impl;
959
	gwidget_class->remove_child     = glade_fixed_remove_child_impl;
Tristan Van Berkom's avatar
Tristan Van Berkom committed
960
	gwidget_class->replace_child    = glade_fixed_replace_child_impl;
961 962 963 964 965 966 967 968 969 970 971 972

	fixed_class->configure_child  = glade_fixed_configure_child_impl;
	fixed_class->configure_begin  = NULL;
	fixed_class->configure_end    = glade_fixed_configure_end_impl;
	fixed_class->child_event      = glade_fixed_child_event;

	/* Properties */
	g_object_class_install_property 
		(gobject_class, PROP_X_PROP,
		 g_param_spec_string 
		 ("x_prop", _("X position property"),
		  _("The property used to set the X position of a child object"),
973
		  "x", G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
974 975 976 977 978 979

	g_object_class_install_property 
		(gobject_class, PROP_Y_PROP,
		 g_param_spec_string 
		 ("y_prop", _("Y position property"),
		  _("The property used to set the Y position of a child object"),
980
		  "y", G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
981 982 983 984 985 986

	g_object_class_install_property 
		(gobject_class, PROP_WIDTH_PROP,
		 g_param_spec_string 
		 ("width_prop", _("Width property"),
		  _("The property used to set the width of a child object"),
987
		  "width-request", G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
988 989 990 991 992 993

	g_object_class_install_property 
		(gobject_class, PROP_HEIGHT_PROP,
		 g_param_spec_string 
		 ("height_prop", _("Height property"),
		  _("The property used to set the height of a child object"),
994
		  "height-request", G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
995

996 997 998 999 1000 1001 1002
	g_object_class_install_property 
		(gobject_class, PROP_CAN_RESIZE,
		 g_param_spec_boolean 
		 ("can_resize", _("Can resize"),
		  _("Whether this container supports resizes of child widgets"),
		  TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));

1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
	/**
	 * GladeFixed::configure-child:
	 * @gladewidget: the #GladeFixed which received the signal.
	 * @arg1: the child #GladeWidget
	 * @arg2: a pointer to a #GdkRectange describing the new size.
	 *
	 * Delegates the Drag/Resize job.
	 *
	 * Returns: %TRUE means you have handled the event and cancels the
	 *          default handler from being triggered.
	 */
	glade_fixed_signals[CONFIGURE_CHILD] =
		g_signal_new ("configure-child",
			      G_TYPE_FROM_CLASS (gobject_class),
			      G_SIGNAL_RUN_LAST,
			      G_STRUCT_OFFSET
			      (GladeFixedClass, configure_child),
			      glade_boolean_handled_accumulator, NULL,
			      glade_marshal_BOOLEAN__OBJECT_POINTER,
			      G_TYPE_BOOLEAN, 2, G_TYPE_OBJECT, G_TYPE_POINTER);

	/**
	 * GladeFixed::configure-begin:
	 * @gladewidget: the #GladeFixed which received the signal.
	 * @arg1: the child #GladeWidget
	 *
	 * Signals the beginning of a Drag/Resize
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1030 1031 1032
	 *
	 * Returns: %TRUE means you have handled the event and cancels the
	 *          default handler from being triggered.
1033 1034 1035 1036
	 */
	glade_fixed_signals[CONFIGURE_BEGIN] =
		g_signal_new ("configure-begin",
			      G_TYPE_FROM_CLASS (gobject_class),
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1037
			      G_SIGNAL_RUN_LAST,
1038 1039
			      G_STRUCT_OFFSET
			      (GladeFixedClass, configure_begin),
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1040 1041 1042
			      glade_boolean_handled_accumulator, NULL,
			      glade_marshal_BOOLEAN__OBJECT,
			      G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT);
1043 1044 1045 1046 1047 1048 1049

	/**
	 * GladeFixed::configure-end:
	 * @gladewidget: the #GladeFixed which received the signal.
	 * @arg1: the child #GladeWidget
	 *
	 * Signals the end of a Drag/Resize
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1050 1051 1052
	 *
	 * Returns: %TRUE means you have handled the event and cancels the
	 *          default handler from being triggered.
1053 1054 1055 1056
	 */
	glade_fixed_signals[CONFIGURE_END] =
		g_signal_new ("configure-end",
			      G_TYPE_FROM_CLASS (gobject_class),
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1057
			      G_SIGNAL_RUN_LAST,
1058 1059
			      G_STRUCT_OFFSET
			      (GladeFixedClass, configure_end),
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1060 1061 1062
			      glade_boolean_handled_accumulator, NULL,
			      glade_marshal_BOOLEAN__OBJECT,
			      G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT);
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
}


/*******************************************************************************
                                      API
*******************************************************************************/

GType
glade_fixed_get_type (void)
{
	static GType fixed_type = 0;
	
	if (!fixed_type)
	{
		static const GTypeInfo fixed_info = 
		{
			sizeof (GladeFixedClass),
			(GBaseInitFunc) NULL,
			(GBaseFinalizeFunc) NULL,
			(GClassInitFunc) glade_fixed_class_init,
			(GClassFinalizeFunc) NULL,
			NULL,           /* class_data */
			sizeof (GladeFixed),
			0,              /* n_preallocs */
			(GInstanceInitFunc) glade_fixed_init,
		};
		fixed_type = 
			g_type_register_static (GLADE_TYPE_WIDGET,
						"GladeFixed",
						&fixed_info, 0);
	}
	return fixed_type;
}