* doc/tmpl/*: updated

	* src/glade-fixed.[ch], src/glade-widget.[ch]:
	  Improved, now made replace_child a vfunc... fixed some
	  event related bugs.

	* src/glade-gtk.c, widgets/gtk+.xml.in: Added free-form placement
	  for GtkTable widget.

	* src/glade-utils.c: added glade_util_deep_fixed_event(), used to
	  propagate events down to fixed containers that dont have windows.
This commit is contained in:
Tristan Van Berkom 2006-06-11 10:35:47 +00:00
parent 70b5c89a75
commit 020446e22f
14 changed files with 798 additions and 169 deletions

View File

@ -1,3 +1,17 @@
2006-06-11 Tristan Van Berkom <tvb@gnome.org>
* doc/tmpl/*: updated
* src/glade-fixed.[ch], src/glade-widget.[ch]:
Improved, now made replace_child a vfunc... fixed some
event related bugs.
* src/glade-gtk.c, widgets/gtk+.xml.in: Added free-form placement
for GtkTable widget.
* src/glade-utils.c: added glade_util_deep_fixed_event(), used to
propagate events down to fixed containers that dont have windows.
2006-06-10 Tristan Van Berkom <tvb@gnome.org>
* src/glade-fixed.c:

View File

@ -452,6 +452,7 @@ GladeSupportedChild
<FILE>glade-widget</FILE>
<TITLE>GladeWidget</TITLE>
GladeWidget
glade_widget_get_from_gobject
glade_widget_add_child
glade_widget_remove_child
glade_widget_set_name
@ -496,7 +497,6 @@ glade_widget_write
glade_widget_read
glade_widget_has_launcher
glade_widget_launch_editor
glade_widget_get_from_gobject
glade_widget_get_parent
glade_widget_set_parent
glade_widget_is_dupping

90
doc/tmpl/glade-fixed.sgml Normal file
View File

@ -0,0 +1,90 @@
<!-- ##### SECTION Title ##### -->
GladeFixed
<!-- ##### SECTION Short_Description ##### -->
An object wrapper for free-form placement container widgets.
<!-- ##### SECTION Long_Description ##### -->
<para>
#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.
</para>
<para>
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.
</para>
<para>
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.
</para>
<!-- ##### SECTION See_Also ##### -->
<para>
</para>
<!-- ##### SECTION Stability_Level ##### -->
<!-- ##### STRUCT GladeFixed ##### -->
<para>
</para>
<!-- ##### SIGNAL GladeFixed::configure-begin ##### -->
<para>
</para>
@gladefixed: the object which received the signal.
@arg1:
@Returns:
<!-- ##### SIGNAL GladeFixed::configure-child ##### -->
<para>
</para>
@gladefixed: the object which received the signal.
@arg1:
@arg2:
@Returns:
<!-- ##### SIGNAL GladeFixed::configure-end ##### -->
<para>
</para>
@gladefixed: the object which received the signal.
@arg1:
@Returns:
<!-- ##### ARG GladeFixed:height-prop ##### -->
<para>
</para>
<!-- ##### ARG GladeFixed:width-prop ##### -->
<para>
</para>
<!-- ##### ARG GladeFixed:x-prop ##### -->
<para>
</para>
<!-- ##### ARG GladeFixed:y-prop ##### -->
<para>
</para>

View File

@ -110,6 +110,17 @@ convenience api for getting and setting properties (mostly from the plugin).
</para>
<!-- ##### FUNCTION glade_widget_get_from_gobject ##### -->
<para>
</para>
@object:
@Returns:
<!-- # Unused Parameters # -->
@w:
<!-- ##### FUNCTION glade_widget_add_child ##### -->
<para>
@ -535,14 +546,6 @@ convenience api for getting and setting properties (mostly from the plugin).
@widget:
<!-- ##### MACRO glade_widget_get_from_gobject ##### -->
<para>
</para>
@w:
<!-- ##### FUNCTION glade_widget_get_parent ##### -->
<para>

View File

@ -62,28 +62,6 @@ typedef struct {
gulong enter_id;
} GFSigData;
/* Convenience macros used in pointer events.
*/
#define GLADE_FIXED_CURSOR_TOP(type) \
((type) == GLADE_CURSOR_RESIZE_TOP_RIGHT || \
(type) == GLADE_CURSOR_RESIZE_TOP_LEFT || \
(type) == GLADE_CURSOR_RESIZE_TOP)
#define GLADE_FIXED_CURSOR_BOTTOM(type) \
((type) == GLADE_CURSOR_RESIZE_BOTTOM_RIGHT || \
(type) == GLADE_CURSOR_RESIZE_BOTTOM_LEFT || \
(type) == GLADE_CURSOR_RESIZE_BOTTOM)
#define GLADE_FIXED_CURSOR_RIGHT(type) \
((type) == GLADE_CURSOR_RESIZE_TOP_RIGHT || \
(type) == GLADE_CURSOR_RESIZE_BOTTOM_RIGHT || \
(type) == GLADE_CURSOR_RESIZE_RIGHT)
#define GLADE_FIXED_CURSOR_LEFT(type) \
((type) == GLADE_CURSOR_RESIZE_TOP_LEFT || \
(type) == GLADE_CURSOR_RESIZE_BOTTOM_LEFT || \
(type) == GLADE_CURSOR_RESIZE_LEFT)
#define CHILD_WIDTH_MIN 20
#define CHILD_HEIGHT_MIN 20
#define CHILD_WIDTH_DEF 100
@ -237,13 +215,7 @@ glade_fixed_configure_widget (GladeFixed *fixed,
gdk_window_get_pointer
(GTK_WIDGET (gwidget->object)->window, &x, &y, NULL);
/* I think its safe here to skip the glade-property API */
new_area.x = GTK_WIDGET (child->object)->allocation.x;
new_area.y = GTK_WIDGET (child->object)->allocation.y;
new_area.width = GTK_WIDGET (child->object)->allocation.width;
new_area.height = GTK_WIDGET (child->object)->allocation.height;
right = GLADE_FIXED_CURSOR_RIGHT (fixed->operation);
left = GLADE_FIXED_CURSOR_LEFT (fixed->operation);
top = GLADE_FIXED_CURSOR_TOP (fixed->operation);
@ -252,6 +224,11 @@ glade_fixed_configure_widget (GladeFixed *fixed,
/* 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)
@ -261,6 +238,7 @@ glade_fixed_configure_widget (GladeFixed *fixed,
x - fixed->pointer_x_origin;
new_area.y = fixed->child_y_origin +
y - fixed->pointer_y_origin;
} else {
if (bottom)
@ -359,7 +337,6 @@ glade_fixed_connect_child (GladeFixed *fixed,
data, g_free);
}
/*******************************************************************************
GladeFixedClass
*******************************************************************************/
@ -382,7 +359,7 @@ glade_fixed_configure_child_impl (GladeFixed *fixed,
}
static void
static gboolean
glade_fixed_configure_end_impl (GladeFixed *fixed,
GladeWidget *child)
{
@ -396,16 +373,15 @@ glade_fixed_configure_end_impl (GladeFixed *fixed,
GValue new_height_value = { 0, };
GladeProperty *x_prop, *y_prop, *width_prop, *height_prop;
/* XXX Well... this can be simplified now ... heh */
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_if_fail (GLADE_IS_PROPERTY (x_prop));
g_return_if_fail (GLADE_IS_PROPERTY (y_prop));
g_return_if_fail (GLADE_IS_PROPERTY (width_prop));
g_return_if_fail (GLADE_IS_PROPERTY (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);
@ -437,6 +413,8 @@ glade_fixed_configure_end_impl (GladeFixed *fixed,
g_value_unset (&new_y_value);
g_value_unset (&new_width_value);
g_value_unset (&new_height_value);
return TRUE;
}
static gboolean
@ -445,9 +423,8 @@ glade_fixed_handle_child_event (GladeFixed *fixed,
GtkWidget *event_widget,
GdkEvent *event)
{
GladeWidget *gwidget = GLADE_WIDGET (fixed);
gboolean handled = FALSE;
gint parent_x, parent_y, child_x, child_y, x, y;
gboolean handled = FALSE, sig_handled;
gint parent_x, parent_y, x, y;
GladeCursorType operation;
/* Get relative mouse position
@ -473,7 +450,7 @@ glade_fixed_handle_child_event (GladeFixed *fixed,
{
glade_fixed_configure_widget (fixed, child);
glade_cursor_set (((GdkEventAny *)event)->window,
operation);
fixed->operation);
handled = TRUE;
}
gdk_window_get_pointer (GTK_WIDGET (child->object)->window, NULL, NULL, NULL);
@ -490,7 +467,7 @@ glade_fixed_handle_child_event (GladeFixed *fixed,
g_signal_emit (G_OBJECT (fixed),
glade_fixed_signals[CONFIGURE_BEGIN],
0, child);
0, child, &sig_handled);
handled = TRUE;
}
@ -505,7 +482,7 @@ glade_fixed_handle_child_event (GladeFixed *fixed,
g_signal_emit (G_OBJECT (fixed),
glade_fixed_signals[CONFIGURE_END],
0, child);
0, child, &sig_handled);
fixed->configuring = NULL;
handled = TRUE;
@ -523,12 +500,14 @@ glade_fixed_child_event (GtkWidget *widget,
GdkEvent *event,
GladeFixed *fixed)
{
gdouble x, y;
GtkWidget *event_widget;
GladeWidget *search, *event_gwidget,
*gwidget = glade_widget_get_from_gobject (widget);
/* Get the basic event info... */
gdk_window_get_user_data (((GdkEventAny *)event)->window, (gpointer)&event_widget);
event_gwidget = glade_widget_event_widget ();
/* Skip all this choosyness if we're already in a drag/resize
*/
@ -536,14 +515,6 @@ glade_fixed_child_event (GtkWidget *widget,
return glade_fixed_handle_child_event
(fixed, fixed->configuring, event_widget, event);
/* carefull to use the event widget and not the signal widget
* to feed to retrieve_from_position
*/
gdk_event_get_coords (event, &x, &y);
event_gwidget =
GLADE_WIDGET_GET_KLASS (fixed)->retrieve_from_position
(event_widget, (int) (x + 0.5), (int) (y + 0.5));
g_return_val_if_fail (GLADE_IS_WIDGET (gwidget), FALSE);
g_return_val_if_fail (GLADE_IS_WIDGET (event_gwidget), FALSE);
@ -581,7 +552,9 @@ glade_fixed_child_event (GtkWidget *widget,
}
}
return glade_fixed_handle_child_event (fixed, gwidget, event_widget, event);
return glade_fixed_handle_child_event
(fixed, gwidget, event_widget, event);
}
/*******************************************************************************
@ -663,6 +636,24 @@ glade_fixed_remove_child_impl (GladeWidget *fixed,
(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_KLASS (parent_class)->replace_child
(GLADE_WIDGET (fixed), old_object, new_object);
if (gnew_widget)
glade_fixed_connect_child (GLADE_FIXED (fixed), gnew_widget);
}
static gboolean
glade_fixed_popup_menu (GtkWidget *widget, gpointer unused_data)
@ -701,45 +692,35 @@ glade_fixed_event (GtkWidget *widget,
GladeWidgetClass *add_class, *alt_class;
GtkWidget *event_widget;
gboolean handled = FALSE;
GladeWidget *gwidget, *search;
gdouble x, y;
GladeWidget *event_gwidget, *search;
add_class = glade_app_get_add_class ();
alt_class = glade_app_get_alt_class ();
gdk_window_get_pointer (widget->window, NULL, NULL, NULL);
/* Get the event widget and the deep widget */
gdk_window_get_user_data (((GdkEventAny *)event)->window, (gpointer)&event_widget);
event_gwidget = glade_widget_event_widget ();
/* Currently ignoring the return value and acting... even if
* this was a selection click
*/
if (GLADE_WIDGET_KLASS (parent_class)->event (widget, event, gwidget_fixed))
return TRUE;
/* Paranoid assertion
*/
g_assert (glade_widget_get_from_gobject (widget) == gwidget_fixed);
/* carefull to use the event widget and not the signal widget
* to feed to retrieve_from_position
*/
gdk_event_get_coords (event, &x, &y);
gdk_window_get_user_data (((GdkEventAny *)event)->window, (gpointer)&event_widget);
gwidget =
GLADE_WIDGET_GET_KLASS (fixed)->retrieve_from_position
(event_widget, (int) (x + 0.5), (int) (y + 0.5));
g_return_val_if_fail (GLADE_IS_WIDGET (gwidget), FALSE);
g_return_val_if_fail (GLADE_IS_WIDGET (event_gwidget), FALSE);
/* Get the gwidget that is a direct child of 'fixed' */
for (search = gwidget;
search && search->parent != gwidget_fixed;
for (search = event_gwidget;
search && GLADE_IS_FIXED (search->parent) == FALSE;
search = search->parent);
/* Igore events that come from other fixed or thier children
* deeper in the hierarchy (it happens when they all return
* FALSE from thier event handlers).
*/
if (gwidget != gwidget_fixed &&
if (event_gwidget != gwidget_fixed &&
(search && search->parent != gwidget_fixed))
return FALSE;
@ -776,7 +757,7 @@ glade_fixed_event (GtkWidget *widget,
case GDK_ENTER_NOTIFY:
case GDK_MOTION_NOTIFY:
case GDK_BUTTON_RELEASE:
if (gwidget_fixed == gwidget)
if (gwidget_fixed == event_gwidget)
{
fixed->mouse_x = ((GdkEventButton *)event)->x;
fixed->mouse_y = ((GdkEventButton *)event)->y;
@ -788,9 +769,9 @@ glade_fixed_event (GtkWidget *widget,
(fixed, fixed->configuring,
event_widget, event);
}
else if (gwidget_fixed != gwidget)
else if (gwidget_fixed != event_gwidget)
{
if (search && search == gwidget)
if (search && search == event_gwidget)
return glade_fixed_handle_child_event
(fixed, search, event_widget, event);
}
@ -850,28 +831,6 @@ glade_fixed_event (GtkWidget *widget,
/*******************************************************************************
GObjectClass
*******************************************************************************/
static GObject *
glade_fixed_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GObject *obj;
GladeWidget *gwidget;
obj = G_OBJECT_CLASS (parent_class)->constructor
(type, n_construct_properties, construct_properties);
gwidget = GLADE_WIDGET (obj);
/* This is needed at least to set a backing pixmaps
* and handle events more consistantly (lets hope all
* free-form containers support being created this way).
*/
GTK_WIDGET_UNSET_FLAGS(gwidget->object, GTK_NO_WINDOW);
return obj;
}
static void
glade_fixed_finalize (GObject *object)
{
@ -961,7 +920,6 @@ glade_fixed_class_init (GladeFixedClass *fixed_class)
G_OBJECT_CLASS
(g_type_class_peek_parent (gobject_class));
gobject_class->constructor = glade_fixed_constructor;
gobject_class->finalize = glade_fixed_finalize;
gobject_class->set_property = glade_fixed_set_property;
gobject_class->get_property = glade_fixed_get_property;
@ -970,6 +928,7 @@ glade_fixed_class_init (GladeFixedClass *fixed_class)
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;
@ -1032,16 +991,19 @@ glade_fixed_class_init (GladeFixedClass *fixed_class)
* @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_FIRST,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET
(GladeFixedClass, configure_begin),
NULL, NULL,
glade_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, G_TYPE_OBJECT);
glade_boolean_handled_accumulator, NULL,
glade_marshal_BOOLEAN__OBJECT,
G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT);
/**
* GladeFixed::configure-end:
@ -1049,16 +1011,19 @@ glade_fixed_class_init (GladeFixedClass *fixed_class)
* @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_FIRST,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET
(GladeFixedClass, configure_end),
NULL, NULL,
glade_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, G_TYPE_OBJECT);
glade_boolean_handled_accumulator, NULL,
glade_marshal_BOOLEAN__OBJECT,
G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT);
}

View File

@ -17,6 +17,27 @@ G_BEGIN_DECLS
#define GLADE_IS_FIXED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_FIXED))
#define GLADE_FIXED_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_FIXED, GladeFixedClass))
/* Convenience macros used in pointer events.
*/
#define GLADE_FIXED_CURSOR_TOP(type) \
((type) == GLADE_CURSOR_RESIZE_TOP_RIGHT || \
(type) == GLADE_CURSOR_RESIZE_TOP_LEFT || \
(type) == GLADE_CURSOR_RESIZE_TOP)
#define GLADE_FIXED_CURSOR_BOTTOM(type) \
((type) == GLADE_CURSOR_RESIZE_BOTTOM_RIGHT || \
(type) == GLADE_CURSOR_RESIZE_BOTTOM_LEFT || \
(type) == GLADE_CURSOR_RESIZE_BOTTOM)
#define GLADE_FIXED_CURSOR_RIGHT(type) \
((type) == GLADE_CURSOR_RESIZE_TOP_RIGHT || \
(type) == GLADE_CURSOR_RESIZE_BOTTOM_RIGHT || \
(type) == GLADE_CURSOR_RESIZE_RIGHT)
#define GLADE_FIXED_CURSOR_LEFT(type) \
((type) == GLADE_CURSOR_RESIZE_TOP_LEFT || \
(type) == GLADE_CURSOR_RESIZE_BOTTOM_LEFT || \
(type) == GLADE_CURSOR_RESIZE_LEFT)
typedef struct _GladeFixed GladeFixed;
typedef struct _GladeFixedClass GladeFixedClass;
@ -55,8 +76,8 @@ struct _GladeFixedClass {
GladeWidgetKlass parent_class;
gboolean (* configure_child) (GladeFixed *, GladeWidget *, GdkRectangle *);
void (* configure_begin) (GladeFixed *, GladeWidget *);
void (* configure_end) (GladeFixed *, GladeWidget *);
gboolean (* configure_begin) (GladeFixed *, GladeWidget *);
gboolean (* configure_end) (GladeFixed *, GladeWidget *);
/* Signal handler for child widgets
*/

View File

@ -574,6 +574,417 @@ glade_gtk_box_remove_child (GObject *object, GObject *child)
/* ----------------------------- GtkTable ------------------------------ */
typedef struct {
/* comparable part: */
GladeWidget *widget;
gint left_attach;
gint right_attach;
gint top_attach;
gint bottom_attach;
gboolean reset;
GdkRectangle rect;
} GladeGtkTableChild;
typedef enum {
DIR_UP,
DIR_DOWN,
DIR_LEFT,
DIR_RIGHT
} GladeTableDir;
#define TABLE_CHILD_CMP_SIZE (sizeof (GladeWidget *) + (sizeof (gint) * 4))
static GladeGtkTableChild table_edit = { 0, };
static GladeGtkTableChild table_cur_attach = { 0, };
/* Takes a point (x or y depending on 'row') relative to
* table, and returns the row or column in which the point
* was found.
*/
static gint
glade_gtk_table_get_row_col_from_point (GtkTable *table,
gboolean row,
gint point)
{
GtkTableChild *tchild;
GList *list;
gint span, trans_point, size, base, end;
for (list = table->children; list; list = list->next)
{
tchild = list->data;
if (row)
gtk_widget_translate_coordinates
(GTK_WIDGET (table), tchild->widget,
0, point, NULL, &trans_point);
else
gtk_widget_translate_coordinates
(GTK_WIDGET (table), tchild->widget,
point, 0, &trans_point, NULL);
/* Find any widget in our row/column
*/
end = row ?
tchild->widget->allocation.height :
tchild->widget->allocation.width;
if (trans_point >= 0 &&
trans_point <= end)
{
base = row ? tchild->top_attach : tchild->left_attach;
size = row ? (tchild->widget->allocation.height) :
(tchild->widget->allocation.width);
span = row ? (tchild->bottom_attach - tchild->top_attach) :
(tchild->right_attach - tchild->left_attach);
return base + (trans_point * span / size);
}
}
/* g_print ("Failed to find a widget (point %d row %d)! (length %d)\n", */
/* point, row, g_list_length (table->children)); */
return -1;
}
static gboolean
glade_gtk_table_point_crosses_threshold (GtkTable *table,
gboolean row,
gint num,
GladeTableDir dir,
gint point)
{
GtkTableChild *tchild;
GList *list;
gint span, trans_point, size;
for (list = table->children; list; list = list->next)
{
tchild = list->data;
/* Find any widget in our row/column
*/
if ((row && num >= tchild->top_attach && num < tchild->bottom_attach) ||
(!row && num >= tchild->left_attach && num < tchild->right_attach))
{
if (row)
gtk_widget_translate_coordinates
(GTK_WIDGET (table), tchild->widget,
0, point, NULL, &trans_point);
else
gtk_widget_translate_coordinates
(GTK_WIDGET (table), tchild->widget,
point, 0, &trans_point, NULL);
span = row ? (tchild->bottom_attach - tchild->top_attach) :
(tchild->right_attach - tchild->left_attach);
size = row ? (tchild->widget->allocation.height) :
(tchild->widget->allocation.width);
size /= span;
switch (dir)
{
case DIR_UP:
case DIR_LEFT:
return trans_point <= size - ((size / 3) * 2);
case DIR_DOWN:
case DIR_RIGHT:
return trans_point >= ((size / 3) * 2);
default:
break;
}
}
}
/* g_print ("Failed to find a widget (point %d row %d)! (length %d)\n", */
/* point, row, g_list_length (table->children)); */
return FALSE;
}
static gboolean
glade_gtk_table_get_attachments (GladeFixed *fixed,
GtkTable *table,
GdkRectangle *rect,
GladeGtkTableChild *configure)
{
gint center_x, center_y, row, column;
center_x = rect->x + (rect->width / 2);
center_y = rect->y + (rect->height / 2);
column = glade_gtk_table_get_row_col_from_point
(table, FALSE, center_x);
row = glade_gtk_table_get_row_col_from_point
(table, TRUE, center_y);
/* its a start, now try to grow when the rect extents
* reach at least half way into the next row/column
*/
configure->left_attach = column;
configure->right_attach = column + 1;
configure->top_attach = row;
configure->bottom_attach = row +1;
if (column >= 0 && row >= 0)
{
/* Check and expand left
*/
while (configure->left_attach > 0)
{
if (rect->x < fixed->child_x_origin &&
GLADE_FIXED_CURSOR_LEFT (fixed->operation) == FALSE)
break;
if (glade_gtk_table_point_crosses_threshold
(table, FALSE, configure->left_attach -1,
DIR_LEFT, rect->x) == FALSE)
break;
configure->left_attach--;
}
/* Check and expand right
*/
while (configure->right_attach < (table->ncols))
{
if (rect->x + rect->width >
fixed->child_x_origin + fixed->child_width_origin &&
GLADE_FIXED_CURSOR_RIGHT (fixed->operation) == FALSE)
break;
if (glade_gtk_table_point_crosses_threshold
(table, FALSE, configure->right_attach,
DIR_RIGHT, rect->x + rect->width) == FALSE)
break;
configure->right_attach++;
}
/* Check and expand top
*/
while (configure->top_attach > 0)
{
if (rect->y < fixed->child_y_origin &&
GLADE_FIXED_CURSOR_TOP (fixed->operation) == FALSE)
break;
if (glade_gtk_table_point_crosses_threshold
(table, TRUE, configure->top_attach -1,
DIR_UP, rect->y) == FALSE)
break;
configure->top_attach--;
}
/* Check and expand bottom
*/
while (configure->bottom_attach < (table->nrows))
{
if (rect->y + rect->height >
fixed->child_y_origin + fixed->child_height_origin &&
GLADE_FIXED_CURSOR_BOTTOM (fixed->operation) == FALSE)
break;
if (glade_gtk_table_point_crosses_threshold
(table, TRUE, configure->bottom_attach,
DIR_DOWN, rect->y + rect->height) == FALSE)
break;
configure->bottom_attach++;
}
}
return column >= 0 && row >= 0;
}
static gboolean
glade_gtk_table_configure_child (GladeFixed *fixed,
GladeWidget *child,
GdkRectangle *rect,
gpointer unused)
{
GtkWidget *table = GTK_WIDGET (GLADE_WIDGET (fixed)->object);
GladeGtkTableChild configure = { child, };
if (table_edit.reset)
{
memcpy (&table_edit.rect, rect, sizeof (GdkRectangle));
table_edit.reset = FALSE;
}
/* Sometimes we are unable to find a widget in the appropriate column,
* usually because a placeholder hasnt had its size allocation yet.
*/
if (glade_gtk_table_get_attachments (fixed, GTK_TABLE (table), rect, &configure))
{
if (memcmp (&configure, &table_cur_attach, TABLE_CHILD_CMP_SIZE) != 0)
{
if (configure.left_attach < table_cur_attach.left_attach)
{
glade_widget_pack_property_set (child, "left-attach",
configure.left_attach);
glade_widget_pack_property_set (child, "right-attach",
configure.right_attach);
}
else
{
glade_widget_pack_property_set (child, "right-attach",
configure.right_attach);
glade_widget_pack_property_set (child, "left-attach",
configure.left_attach);
}
if (configure.top_attach < table_cur_attach.top_attach)
{
glade_widget_pack_property_set (child, "top-attach",
configure.top_attach);
glade_widget_pack_property_set (child, "bottom-attach",
configure.bottom_attach);
}
else
{
glade_widget_pack_property_set (child, "bottom-attach",
configure.bottom_attach);
glade_widget_pack_property_set (child, "top-attach",
configure.top_attach);
}
memcpy (&table_cur_attach, &configure, TABLE_CHILD_CMP_SIZE);
}
}
return TRUE;
}
static gboolean
glade_gtk_table_configure_begin (GladeFixed *fixed,
GladeWidget *child,
gpointer unused)
{
table_edit.widget = child;
table_edit.reset = TRUE;
glade_widget_pack_property_get (child, "left-attach",
&table_edit.left_attach);
glade_widget_pack_property_get (child, "right-attach",
&table_edit.right_attach);
glade_widget_pack_property_get (child, "top-attach",
&table_edit.top_attach);
glade_widget_pack_property_get (child, "bottom-attach",
&table_edit.bottom_attach);
memcpy (&table_cur_attach, &table_edit, TABLE_CHILD_CMP_SIZE);
return TRUE;
}
static gboolean
glade_gtk_table_configure_end (GladeFixed *fixed,
GladeWidget *child,
gpointer unused)
{
GladeGtkTableChild new = { child, };
glade_widget_pack_property_get (child, "left-attach",
&new.left_attach);
glade_widget_pack_property_get (child, "right-attach",
&new.right_attach);
glade_widget_pack_property_get (child, "top-attach",
&new.top_attach);
glade_widget_pack_property_get (child, "bottom-attach",
&new.bottom_attach);
/* Compare the meaningfull part of the current edit. */
if (memcmp (&new, &table_edit, TABLE_CHILD_CMP_SIZE) != 0)
{
GValue left_attach_value = { 0, };
GValue right_attach_value = { 0, };
GValue top_attach_value = { 0, };
GValue bottom_attach_value = { 0, };
GValue new_left_attach_value = { 0, };
GValue new_right_attach_value = { 0, };
GValue new_top_attach_value = { 0, };
GValue new_bottom_attach_value = { 0, };
GladeProperty *left_attach_prop, *right_attach_prop,
*top_attach_prop, *bottom_attach_prop;
left_attach_prop = glade_widget_get_pack_property (child, "left-attach");
right_attach_prop = glade_widget_get_pack_property (child, "right-attach");
top_attach_prop = glade_widget_get_pack_property (child, "top-attach");
bottom_attach_prop = glade_widget_get_pack_property (child, "bottom-attach");
g_return_val_if_fail (GLADE_IS_PROPERTY (left_attach_prop), FALSE);
g_return_val_if_fail (GLADE_IS_PROPERTY (right_attach_prop), FALSE);
g_return_val_if_fail (GLADE_IS_PROPERTY (top_attach_prop), FALSE);
g_return_val_if_fail (GLADE_IS_PROPERTY (bottom_attach_prop), FALSE);
glade_property_get_value (left_attach_prop, &new_left_attach_value);
glade_property_get_value (right_attach_prop, &new_right_attach_value);
glade_property_get_value (top_attach_prop, &new_top_attach_value);
glade_property_get_value (bottom_attach_prop, &new_bottom_attach_value);
g_value_init (&left_attach_value, G_TYPE_UINT);
g_value_init (&right_attach_value, G_TYPE_UINT);
g_value_init (&top_attach_value, G_TYPE_UINT);
g_value_init (&bottom_attach_value, G_TYPE_UINT);
g_value_set_uint (&left_attach_value, table_edit.left_attach);
g_value_set_uint (&right_attach_value, table_edit.right_attach);
g_value_set_uint (&top_attach_value, table_edit.top_attach);
g_value_set_uint (&bottom_attach_value, table_edit.bottom_attach);
/* whew, all that for this call !
*/
glade_command_set_properties
(left_attach_prop, &left_attach_value, &new_left_attach_value,
right_attach_prop, &right_attach_value, &new_right_attach_value,
top_attach_prop, &top_attach_value, &new_top_attach_value,
bottom_attach_prop, &bottom_attach_value, &new_bottom_attach_value,
NULL);
g_value_unset (&left_attach_value);
g_value_unset (&right_attach_value);
g_value_unset (&top_attach_value);
g_value_unset (&bottom_attach_value);
g_value_unset (&new_left_attach_value);
g_value_unset (&new_right_attach_value);
g_value_unset (&new_top_attach_value);
g_value_unset (&new_bottom_attach_value);
}
return TRUE;
}
void
glade_gtk_table_post_create (GObject *container, GladeCreateReason reason)
{
GladeWidget *gwidget =
glade_widget_get_from_gobject (container);
g_signal_connect (G_OBJECT (gwidget), "configure-child",
G_CALLBACK (glade_gtk_table_configure_child), NULL);
g_signal_connect (G_OBJECT (gwidget), "configure-begin",
G_CALLBACK (glade_gtk_table_configure_begin), NULL);
g_signal_connect (G_OBJECT (gwidget), "configure-end",
G_CALLBACK (glade_gtk_table_configure_end), NULL);
}
static gboolean
glade_gtk_table_has_child (GtkTable *table, guint left_attach, guint top_attach)
{
@ -610,7 +1021,7 @@ glade_gtk_table_refresh_placeholders (GtkTable *table)
{
GList *list, *toremove = NULL;
gint i, j;
for (list = table->children; list && list->data; list = list->next)
{
GtkTableChild *child = list->data;
@ -633,8 +1044,11 @@ glade_gtk_table_refresh_placeholders (GtkTable *table)
gtk_table_attach_defaults (table,
glade_placeholder_new (),
i, i + 1, j, j + 1);
gtk_container_check_resize (GTK_CONTAINER (table));
}
void GLADEGTK_API
glade_gtk_table_add_child (GObject *object, GObject *child)
{
@ -642,6 +1056,7 @@ glade_gtk_table_add_child (GObject *object, GObject *child)
g_return_if_fail (GTK_IS_WIDGET (child));
gtk_container_add (GTK_CONTAINER (object), GTK_WIDGET (child));
glade_gtk_table_refresh_placeholders (GTK_TABLE (object));
}
@ -864,7 +1279,8 @@ glade_gtk_table_verify_left_top_attach (GObject *object,
parent_prop, &parent_val))
return FALSE;
if (val >= parent_val || val >= prop_val) return FALSE;
if (val >= parent_val || val >= prop_val)
return FALSE;
return TRUE;
}
@ -882,7 +1298,8 @@ glade_gtk_table_verify_right_bottom_attach (GObject *object,
parent_prop, &parent_val))
return FALSE;
if (val <= prop_val || val > parent_val) return FALSE;
if (val <= prop_val || val > parent_val)
return FALSE;
return TRUE;
}

View File

@ -42,6 +42,7 @@
#include "glade-property.h"
#include "glade-property-class.h"
#include "glade-clipboard.h"
#include "glade-fixed.h"
#define GLADE_UTIL_SELECTION_NODE_SIZE 7
#define GLADE_UTIL_COPY_BUFFSIZE 1024
@ -1695,10 +1696,6 @@ glade_util_search_devhelp (const gchar *book,
const gchar *page,
const gchar *search)
{
/* XXX
* g_spawn_command_line_somethingorother.
*/
GError *error = NULL;
gchar *book_comm = NULL, *page_comm = NULL;
gchar *string;
@ -1723,3 +1720,24 @@ glade_util_search_devhelp (const gchar *book,
if (book_comm) g_free (book_comm);
if (page_comm) g_free (page_comm);
}
gboolean
glade_util_deep_fixed_event (GtkWidget *widget,
GdkEvent *event,
GladeWidget *gwidget)
{
GladeWidget *event_widget, *search;
event_widget = glade_widget_event_widget ();
/* Look for a child GladeFixed
*/
for (search = event_widget;
search && search != gwidget && GLADE_IS_FIXED (search) == FALSE;
search = search->parent);
if (search && GLADE_IS_FIXED (search) && search != gwidget)
return GLADE_WIDGET_GET_KLASS (search)->event (widget, event, search);
return FALSE;
}

View File

@ -138,6 +138,12 @@ LIBGLADEUI_API
void glade_util_search_devhelp (const gchar *book,
const gchar *page,
const gchar *search);
LIBGLADEUI_API
gboolean glade_util_deep_fixed_event (GtkWidget *widget,
GdkEvent *event,
GladeWidget *gwidget);
G_END_DECLS
#endif /* __GLADE_UTILS_H__ */

View File

@ -1604,7 +1604,8 @@ glade_widget_class_create_widget_real (gboolean query,
{
g_critical ("No class found in glade_widget_class_create_widget_real args");
va_end (vl_copy);
}
return NULL;
}
if (widget_class->fixed)
gwidget_type = GLADE_TYPE_FIXED;
@ -1612,9 +1613,9 @@ glade_widget_class_create_widget_real (gboolean query,
gwidget_type = GLADE_TYPE_WIDGET;
gwidget = g_object_new_valist (gwidget_type,
first_property,
(va_list) vl_copy);
gwidget = (GladeWidget *)g_object_new_valist (gwidget_type,
first_property,
(va_list) vl_copy);
va_end (vl_copy);
if (query && glade_widget_class_query (widget_class))

View File

@ -86,14 +86,20 @@ enum
PROP_REASON
};
static guint glade_widget_signals[LAST_SIGNAL] = {0};
static guint glade_widget_signals[LAST_SIGNAL] = {0};
static GObjectClass *parent_class = NULL;
/* Sometimes we need to use the project deep in the loading code,
* this is just a shortcut way to get the project.
*/
static GladeProject *loading_project = NULL;
static gboolean glade_widget_dupping = FALSE;
static gboolean glade_widget_dupping = FALSE;
static GQuark glade_widget_name_quark = 0;
/* An optimization to avoid looking up the deepest
* widget more than once in an event.
*/
static GladeWidget *deep_event_widget = NULL;
/*******************************************************************************
GladeWidget class methods
@ -123,6 +129,25 @@ glade_widget_remove_child_impl (GladeWidget *widget,
(widget->widget_class, widget->object, child->object);
}
static void
glade_widget_replace_child_impl (GladeWidget *widget,
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 (gnew_widget) gnew_widget->parent = widget;
if (gold_widget) gold_widget->parent = NULL;
glade_widget_class_container_replace_child
(widget->widget_class, widget->object,
old_object, new_object);
if (gnew_widget)
glade_widget_set_packing_properties (gnew_widget, widget);
}
static void
glade_widget_add_signal_handler_impl (GladeWidget *widget, GladeSignal *signal_handler)
{
@ -296,8 +321,8 @@ glade_widget_find_deepest_child_at_position (GtkContainer *toplevel,
* event and returns the real #GladeWidget that was clicked
*
*/
static GladeWidget *
glade_widget_retrieve_from_position_impl (GtkWidget *base, int x, int y)
GladeWidget *
glade_widget_retrieve_from_position (GtkWidget *base, int x, int y)
{
GladeWidget *lookup;
GtkWidget *widget;
@ -323,17 +348,12 @@ glade_widget_button_press (GtkWidget *widget,
{
GladeWidget *glade_widget;
GtkWidget *event_widget;
gint x = (gint) (event->x + 0.5);
gint y = (gint) (event->y + 0.5);
gboolean handled = FALSE;
/* Carefull to use the event widget and not the signal widget
* to feed to retrieve_from_position
/* Get event widget and event glade_widget
*/
gdk_window_get_user_data (event->window, (gpointer)&event_widget);
if ((glade_widget =
GLADE_WIDGET_GET_KLASS
(gwidget)->retrieve_from_position (event_widget, x, y)) == NULL)
if ((glade_widget = deep_event_widget) == NULL)
return FALSE;
/* make sure to grab focus, since we may stop default handlers */
@ -402,8 +422,11 @@ glade_widget_setup_events (GladeWidget *gwidget,
GtkWidget *widget)
{
gtk_widget_add_events (widget,
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK);
GDK_POINTER_MOTION_MASK |
GDK_POINTER_MOTION_HINT_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_ENTER_NOTIFY_MASK);
if (GTK_WIDGET_TOPLEVEL (widget))
g_signal_connect (G_OBJECT (widget), "delete_event",
@ -425,6 +448,7 @@ glade_widget_event (GtkWidget *widget,
case GDK_BUTTON_PRESS:
return glade_widget_button_press (widget, (GdkEventButton*) event, gwidget);
case GDK_EXPOSE:
case GDK_CONFIGURE:
glade_util_queue_draw_nodes (((GdkEventExpose*) event)->window);
break;
default:
@ -434,6 +458,61 @@ glade_widget_event (GtkWidget *widget,
return FALSE;
}
static gboolean
glade_widget_event_private (GtkWidget *widget,
GdkEvent *event,
GladeWidget *gwidget)
{
GtkWidget *event_widget;
gdouble x, y;
/* Get the widget at moust position before anything else
*/
gdk_event_get_coords (event, &x, &y);
gdk_window_get_user_data (((GdkEventAny *)event)->window, (gpointer)&event_widget);
gdk_window_get_pointer (((GdkEventAny *)event)->window, NULL, NULL, NULL);
deep_event_widget =
glade_widget_retrieve_from_position
(event_widget, (int) (x + 0.5), (int) (y + 0.5));
/* Check if there are deep fixed widgets without windows
* that need to be processed first.
*/
if (glade_util_deep_fixed_event (widget, event, gwidget) == FALSE)
{
gboolean handled;
/* Run the real class handler now.
*/
handled = GLADE_WIDGET_GET_KLASS (gwidget)->event (widget, event, gwidget);
#if 0
g_print ("event widget '%s' handled '%d'\n",
deep_event_widget->name, handled);
#endif
return handled;
}
else
{
switch (event->type)
{
case GDK_BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
#if 0
g_print ("Forwarded the button event to a fixed widget "
"(event widget '%s')\n",
deep_event_widget->name);
#endif
break;
default:
break;
}
}
return FALSE;
}
/*******************************************************************************
GObjectClass & Object Construction
*******************************************************************************/
@ -874,6 +953,10 @@ glade_widget_class_init (GladeWidgetKlass *klass)
{
GObjectClass *object_class;
if (glade_widget_name_quark == 0)
glade_widget_name_quark =
g_quark_from_static_string ("GladeWidgetDataTag");
object_class = G_OBJECT_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
@ -885,12 +968,11 @@ glade_widget_class_init (GladeWidgetKlass *klass)
klass->add_child = glade_widget_add_child_impl;
klass->remove_child = glade_widget_remove_child_impl;
klass->replace_child = glade_widget_replace_child_impl;
klass->add_signal_handler = glade_widget_add_signal_handler_impl;
klass->remove_signal_handler = glade_widget_remove_signal_handler_impl;
klass->change_signal_handler = glade_widget_change_signal_handler_impl;
klass->retrieve_from_position = glade_widget_retrieve_from_position_impl;
klass->setup_events = glade_widget_setup_events;
klass->event = glade_widget_event;
@ -1795,6 +1877,14 @@ glade_widget_info_params (GladeWidgetClass *widget_class,
/*******************************************************************************
API
*******************************************************************************/
GladeWidget *
glade_widget_get_from_gobject (gpointer object)
{
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
return g_object_get_qdata (G_OBJECT (object), glade_widget_name_quark);
}
static void
glade_widget_debug_real (GladeWidget *widget, int indent)
{
@ -2764,8 +2854,7 @@ glade_widget_set_object (GladeWidget *gwidget, GObject *new_object)
glade_widget_connect_signal_handlers
(GTK_WIDGET(new_object),
G_CALLBACK
(GLADE_WIDGET_GET_KLASS (gwidget)->event),
G_CALLBACK (glade_widget_event_private),
gwidget);
}
@ -2931,6 +3020,25 @@ glade_widget_has_decendant (GladeWidget *widget, GType type)
}
/**
* glade_widget_event_widget:
*
* During events, this function returns the deepest
* project widget at moust position, or %NULL if it is
* not a mouse event.
*
* Handle with care, you must be in an event for
* the return value to be meaningfull
*
* Returns a #GladeWidget
*/
GladeWidget *
glade_widget_event_widget (void)
{
return deep_event_widget;
}
/**
* glade_widget_replace:
* @old_object: a #GObject
@ -2944,24 +3052,10 @@ glade_widget_has_decendant (GladeWidget *widget, GType type)
void
glade_widget_replace (GladeWidget *parent, GObject *old_object, GObject *new_object)
{
GladeWidget *gnew_widget = NULL;
GladeWidget *gold_widget = NULL;
g_return_if_fail (G_IS_OBJECT (old_object));
g_return_if_fail (G_IS_OBJECT (new_object));
gnew_widget = glade_widget_get_from_gobject (new_object);
gold_widget = glade_widget_get_from_gobject (old_object);
if (gnew_widget) gnew_widget->parent = parent;
if (gold_widget) gold_widget->parent = NULL;
glade_widget_class_container_replace_child
(parent->widget_class, parent->object,
old_object, new_object);
if (gnew_widget)
glade_widget_set_packing_properties (gnew_widget, parent);
GLADE_WIDGET_GET_KLASS (parent)->replace_child (parent, old_object, new_object);
}
/* XML Serialization */

View File

@ -18,8 +18,6 @@ G_BEGIN_DECLS
#define GLADE_IS_WIDGET_KLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_WIDGET))
#define GLADE_WIDGET_GET_KLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GLADE_TYPE_WIDGET, GladeWidgetKlass))
#define glade_widget_get_from_gobject(w) g_object_get_data (G_OBJECT (w), "GladeWidgetDataTag")
typedef struct _GladeWidgetKlass GladeWidgetKlass;
struct _GladeWidget
@ -103,6 +101,7 @@ struct _GladeWidgetKlass
void (*add_child) (GladeWidget *, GladeWidget *, gboolean);
void (*remove_child) (GladeWidget *, GladeWidget *);
void (*replace_child) (GladeWidget *, GObject *, GObject *);
void (*add_signal_handler) (GladeWidget *, GladeSignal *);
void (*remove_signal_handler) (GladeWidget *, GladeSignal *);
@ -111,8 +110,6 @@ struct _GladeWidgetKlass
void (*setup_events) (GladeWidget *, GtkWidget *);
gboolean (*event) (GtkWidget *, GdkEvent *, GladeWidget *);
GladeWidget *(*retrieve_from_position) (GtkWidget *, int, int);
};
/*******************************************************************************
@ -121,6 +118,8 @@ struct _GladeWidgetKlass
LIBGLADEUI_API
GType glade_widget_get_type (void);
LIBGLADEUI_API
GladeWidget *glade_widget_get_from_gobject (gpointer object);
LIBGLADEUI_API
void glade_widget_add_child (GladeWidget *parent,
GladeWidget *child,
gboolean at_mouse);
@ -179,9 +178,10 @@ LIBGLADEUI_API
void glade_widget_launch_editor (GladeWidget *widget);
LIBGLADEUI_API
gboolean glade_widget_has_decendant (GladeWidget *widget,
GType type);
gboolean glade_widget_has_decendant (GladeWidget *widget,
GType type);
LIBGLADEUI_API
GladeWidget *glade_widget_event_widget (void);
/*******************************************************************************
Project, object property references
*******************************************************************************/

View File

@ -44,7 +44,7 @@ typedef struct _GladeProject GladeProject;
#include "glade-utils.h"
#include "glade-builtins.h"
#include "glade-xml-utils.h"
#include "glade-fixed.h"
#define GLADE_TAG_FALSE "False"
#define GLADE_TAG_TRUE "True"

View File

@ -748,8 +748,8 @@
<glade-widget-class name="GtkHBox" generic-name="hbox" _title="Horizontal Box"/>
<glade-widget-class name="GtkVBox" generic-name="vbox" _title="Vertical Box"/>
<glade-widget-class name="GtkTable" generic-name="table" _title="Table">
<post-create-function>empty</post-create-function>
<glade-widget-class name="GtkTable" generic-name="table" _title="Table" fixed="True">
<post-create-function>glade_gtk_table_post_create</post-create-function>
<properties>
<property id="n-rows" default="3" query="True">
<set-function>glade_gtk_table_set_n_rows</set-function>