/* -*- 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
/**
* 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.
*/
#include <glib-object.h>
#include <glib/gi18n-lib.h>
#include <gdk/gdkkeysyms.h>
#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,
PROP_HEIGHT_PROP,
PROP_CAN_RESIZE
};
/* 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
#define GRAB_BORDER_WIDTH 7
#define GRAB_CORNER_WIDTH 7
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;
#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
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)
{
gtk_widget_get_pointer (GTK_WIDGET (GLADE_WIDGET (fixed)->object),
&(GLADE_FIXED (fixed)->pointer_x_origin),
&(GLADE_FIXED (fixed)->pointer_y_origin));
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;
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;
}
}
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);
}
}
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;
gtk_widget_get_pointer (GTK_WIDGET (gwidget->object), &x, &y);
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);
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;
/* 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;
} 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;
/* 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);
/* Apply new rectangle to the object */
g_signal_emit (G_OBJECT (fixed),
glade_fixed_signals[CONFIGURE_CHILD],
0, child, &new_area, &handled);
/* 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,
GladeWidget *child)
{
GFSigData *data;
if (GTK_IS_WIDGET (child->object) == FALSE)
return;
if ((data = g_object_get_data (G_OBJECT (child), "glade-fixed-signal-data")) != NULL)
{
g_signal_handler_disconnect (child, data->press_id);
g_signal_handler_disconnect (child, data->release_id);
g_signal_handler_disconnect (child, data->motion_id);
g_object_set_data (G_OBJECT (child), "glade-fixed-signal-data", NULL);
}
}
static void
glade_fixed_connect_child (GladeFixed *fixed,
GladeWidget *child)
{
GFSigData *data;
if (GTK_IS_WIDGET (child->object) == FALSE)
return;
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);
/* Connect-after here... leave a chance for selection
*/
data->press_id =
g_signal_connect_after
(child, "button-press-event", G_CALLBACK
(GLADE_FIXED_GET_CLASS(fixed)->child_event), fixed);
data->release_id =
g_signal_connect
(child, "button-release-event", G_CALLBACK
(GLADE_FIXED_GET_CLASS(fixed)->child_event), fixed);
data->motion_id =
g_signal_connect
(child, "motion-notify-event", G_CALLBACK
(GLADE_FIXED_GET_CLASS(fixed)->child_event), fixed);
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)
{
/* 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);
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;
}
static gboolean
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);
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);
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);
glade_command_push_group (_("Placing %s inside %s"),
child->name,
GLADE_WIDGET (fixed)->name);
/* 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);
glade_command_pop_group ();
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);
return TRUE;
}
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;
}
static gboolean
glade_fixed_handle_child_event (GladeFixed *fixed,
GladeWidget *child,
GtkWidget *event_widget,
GdkEvent *event)
{
GladeCursorType operation;
GdkModifierType event_state = 0;
GladePointerMode pointer_mode;
GtkWidget *fixed_widget, *child_widget;
gint fixed_x, fixed_y, child_x, child_y;
gboolean handled = FALSE, sig_handled;
fixed_widget = GTK_WIDGET (GLADE_WIDGET (fixed)->object);
child_widget = GTK_WIDGET (child->object);
pointer_mode = glade_app_get_pointer_mode ();
/* 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
*/
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);
if (fixed->can_resize)
operation = glade_fixed_get_operation (GTK_WIDGET (child->object),
child_x, child_y);
else
operation = GLADE_CURSOR_DRAG;
gdk_event_get_state (event, &event_state);
switch (event->type)
{
case GDK_MOTION_NOTIFY:
if (fixed->configuring == NULL)
{
if ((event_state & GDK_SHIFT_MASK) ||
pointer_mode == GLADE_POINTER_DRAG_RESIZE)
glade_cursor_set (((GdkEventAny *)event)->window,
operation);
else if (pointer_mode == GLADE_POINTER_SELECT)
glade_cursor_set (((GdkEventAny *)event)->window,
GLADE_CURSOR_SELECTOR);
} else if (fixed->configuring && !(event_state & GDK_BUTTON1_MASK))
{
/* Cancel drags that no longer have mouse down */
glade_cursor_set (((GdkEventAny *)event)->window,
operation);
glade_fixed_cancel_operation (fixed, operation);
handled = TRUE;
} else if (fixed->configuring)
{
/* Need to update mouse for configures. */
gtk_widget_get_pointer (fixed_widget,
&fixed->mouse_x, &fixed->mouse_y);
glade_fixed_configure_widget (fixed, child);
glade_cursor_set (((GdkEventAny *)event)->window,
fixed->operation);
handled = TRUE;
}
gdk_window_get_pointer (GTK_WIDGET (child->object)->window, NULL, NULL, NULL);
break;
case GDK_BUTTON_PRESS:
/* We cant rely on GDK_BUTTON1_MASK since event->state isnt yet updated
* by the current event itself
*/
if (((GdkEventButton *)event)->button == 1 &&
((event_state & GDK_SHIFT_MASK) ||
pointer_mode == GLADE_POINTER_DRAG_RESIZE))
{
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],
0, child, &sig_handled);
handled = TRUE;
}
break;
case GDK_BUTTON_RELEASE:
if (((GdkEventButton *)event)->button == 1 && fixed->configuring)
{
if ((event_state & GDK_SHIFT_MASK) ||
pointer_mode == GLADE_POINTER_DRAG_RESIZE)
glade_cursor_set (((GdkEventAny *)event)->window,
operation);
else
glade_cursor_set (((GdkEventAny *)event)->window,
GLADE_CURSOR_SELECTOR);
glade_fixed_cancel_operation (fixed, operation);
handled = TRUE;
}
break;
default:
break;
}
return handled;
}
static gint
glade_fixed_child_event (GladeWidget *gwidget,
GdkEvent *event,
GladeFixed *fixed)
{
GtkWidget *event_widget;
/* Get the basic event info... */
gdk_window_get_user_data (((GdkEventAny *)event)->window, (gpointer)&event_widget);
/* Skip all this choosyness if we're already in a drag/resize
*/
if (fixed->configuring)
{
return glade_fixed_handle_child_event
(fixed, fixed->configuring, event_widget, event);
}
g_return_val_if_fail (GLADE_IS_WIDGET (gwidget), FALSE);
/* Early return for fixed children with selection in
* the palette.
*/
if (GLADE_IS_FIXED (gwidget) &&
glade_app_get_pointer_mode() == GLADE_POINTER_ADD_WIDGET)
{
glade_cursor_set (((GdkEventAny *)event)->window,
GLADE_CURSOR_ADD_WIDGET);
return FALSE;
}
return glade_fixed_handle_child_event
(fixed, gwidget, event_widget, event);
}
/*******************************************************************************
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 */
GLADE_WIDGET_CLASS (parent_class)->add_child
(GLADE_WIDGET (fixed), child, at_mouse);
/* We only operate on widgets here */
if (!GTK_IS_WIDGET (child->object))
return;
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);
glade_fixed_connect_child (fixed, child);
/* Setup rect and send configure
*/
if (fixed->creating)
{
rect.x = fixed->mouse_x;
rect.y = fixed->mouse_y;
rect.width = CHILD_WIDTH_DEF;
rect.height = CHILD_HEIGHT_DEF;
g_signal_emit (G_OBJECT (fixed),
glade_fixed_signals[CONFIGURE_CHILD],
0, child, &rect, &handled);
}
else if (at_mouse)
{
rect.x = fixed->mouse_x;
rect.y = fixed->mouse_y;
rect.width = GTK_WIDGET (child->object)->allocation.width;
rect.height = GTK_WIDGET (child->object)->allocation.height;
if (rect.width < CHILD_WIDTH_DEF)
rect.width = CHILD_WIDTH_DEF;
if (rect.height < CHILD_HEIGHT_DEF)
rect.height = CHILD_HEIGHT_DEF;
g_signal_emit (G_OBJECT (fixed),
glade_fixed_signals[CONFIGURE_CHILD],
0, child, &rect, &handled);
}
return;
}
static void
glade_fixed_remove_child_impl (GladeWidget *fixed,
GladeWidget *child)
{
glade_fixed_disconnect_child (GLADE_FIXED (fixed), child);
/* Chain up for the basic unparenting */
GLADE_WIDGET_CLASS (parent_class)->remove_child
(GLADE_WIDGET (fixed), child);
}
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);
GladeWidget *gold_widget = glade_widget_get_from_gobject (old_object);
if (gold_widget)
glade_fixed_disconnect_child (GLADE_FIXED (fixed), gold_widget);
/* Chain up for the basic reparenting */
GLADE_WIDGET_CLASS (parent_class)->replace_child
(GLADE_WIDGET (fixed), old_object, new_object);
if (gnew_widget)
glade_fixed_connect_child (GLADE_FIXED (fixed), gnew_widget);
}
static gint
glade_fixed_event (GladeWidget *gwidget_fixed,
GdkEvent *event)
{
GladeFixed *fixed = GLADE_FIXED (gwidget_fixed);
GladeWidgetAdaptor *adaptor;
GtkWidget *event_widget;
gboolean handled = FALSE;
adaptor = glade_palette_get_current_item (glade_app_get_palette ());
/* Get the event widget and the deep widget */
gdk_window_get_user_data (((GdkEventAny *)event)->window, (gpointer)&event_widget);
/* If the GladeWidget used this event... let it slide.
*/
if (GLADE_WIDGET_CLASS (parent_class)->event (gwidget_fixed, event))
return TRUE;
switch (event->type)
{
case GDK_BUTTON_PRESS:
case GDK_MOTION_NOTIFY:
case GDK_BUTTON_RELEASE:
gtk_widget_get_pointer (GTK_WIDGET (gwidget_fixed->object),
&fixed->mouse_x, &fixed->mouse_y);
if (fixed->configuring)
{
return glade_fixed_handle_child_event
(fixed, fixed->configuring,
event_widget, event);
}
break;
default:
break;
}
switch (event->type)
{
case GDK_BUTTON_PRESS: /* add widget */
if (((GdkEventButton *) event)->button == 1)
{
if (adaptor != NULL)
{
/* A widget type is selected in the palette.
* Add a new widget of that type.
*/
fixed->creating = TRUE;
glade_command_create (adaptor,
GLADE_WIDGET (fixed), NULL,
GLADE_WIDGET (fixed)->project);
fixed->creating = FALSE;
glade_palette_deselect_current_item (glade_app_get_palette(), TRUE);
handled = TRUE;
}
}
break;
case GDK_MOTION_NOTIFY:
if (glade_app_get_pointer_mode() == GLADE_POINTER_ADD_WIDGET)
{
glade_cursor_set (((GdkEventAny *)event)->window,
GLADE_CURSOR_ADD_WIDGET);
handled = TRUE;
}
else if (GLADE_IS_FIXED (gwidget_fixed->parent) == FALSE &&
glade_app_get_pointer_mode() == GLADE_POINTER_SELECT)
glade_cursor_set (((GdkEventAny *)event)->window,
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;
case PROP_CAN_RESIZE:
fixed->can_resize = g_value_get_boolean (value);
break;
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)
{
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;
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");
fixed->can_resize = TRUE;
}
static void
glade_fixed_class_init (GladeFixedClass *fixed_class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (fixed_class);
GladeWidgetClass *gwidget_class = GLADE_WIDGET_CLASS (fixed_class);
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;
gwidget_class->remove_child = glade_fixed_remove_child_impl;
gwidget_class->replace_child = glade_fixed_replace_child_impl;
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"),
"x", G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
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"),
"y", G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
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"),
"width-request", G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
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"),
"height-request", G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
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));
/**
* 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
*
* Returns: %TRUE means you have handled the event and cancels the
* default handler from being triggered.
*/
glade_fixed_signals[CONFIGURE_BEGIN] =
g_signal_new ("configure-begin",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET
(GladeFixedClass, configure_begin),
glade_boolean_handled_accumulator, NULL,
glade_marshal_BOOLEAN__OBJECT,
G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT);
/**
* GladeFixed::configure-end:
* @gladewidget: the #GladeFixed which received the signal.
* @arg1: the child #GladeWidget
*
* Signals the end of a Drag/Resize
*
* Returns: %TRUE means you have handled the event and cancels the
* default handler from being triggered.
*/
glade_fixed_signals[CONFIGURE_END] =
g_signal_new ("configure-end",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET
(GladeFixedClass, configure_end),
glade_boolean_handled_accumulator, NULL,
glade_marshal_BOOLEAN__OBJECT,
G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT);
}
/*******************************************************************************
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;
}
syntax highlighted by Code2HTML, v. 0.9.1