1 /* $Id: exo-wrap-table.c 22884 2006-08-26 12:40:43Z benny $ */
3 * Copyright (c) 2000 Ramiro Estrugo <ramiro@eazel.com>
4 * Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
26 #include "exo-wrap-table.h"
30 #define EXO_WRAP_TABLE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EXO_TYPE_WRAP_TABLE, ExoWrapTablePrivate))
34 /* Property identifiers */
45 static void exo_wrap_table_class_init (ExoWrapTableClass
*klass
);
46 static void exo_wrap_table_init (ExoWrapTable
*table
);
47 static void exo_wrap_table_get_property (GObject
*object
,
51 static void exo_wrap_table_set_property (GObject
*object
,
55 static void exo_wrap_table_size_request (GtkWidget
*widget
,
56 GtkRequisition
*requisition
);
57 static void exo_wrap_table_size_allocate (GtkWidget
*widget
,
58 GtkAllocation
*allocation
);
59 static void exo_wrap_table_add (GtkContainer
*container
,
61 static void exo_wrap_table_remove (GtkContainer
*container
,
63 static void exo_wrap_table_forall (GtkContainer
*container
,
64 gboolean include_internals
,
66 gpointer callback_data
);
67 static void exo_wrap_table_layout (ExoWrapTable
*table
);
68 static gint
exo_wrap_table_get_max_child_size (const ExoWrapTable
*table
,
69 gint
*max_width_return
,
70 gint
*max_height_return
);
71 static gint
exo_wrap_table_get_num_fitting (gint available
,
77 struct _ExoWrapTablePrivate
79 /* the list of child widgets */
82 /* configurable parameters */
85 guint homogeneous
: 1;
87 /* the estimated number of columns */
93 static GObjectClass
*exo_wrap_table_parent_class
;
95 _exo_g_type_register_simple (GType type_parent
,
96 const gchar
*type_name_static
,
100 gpointer instance_init
)
102 /* generate the type info (on the stack) */
117 /* register the static type */
118 return g_type_register_static (type_parent
, type_name_static
, &info
, 0);
124 exo_wrap_table_get_type (void)
126 static GType type
= G_TYPE_INVALID
;
128 if (G_UNLIKELY (type
== G_TYPE_INVALID
))
130 type
= _exo_g_type_register_simple (GTK_TYPE_CONTAINER
,
132 sizeof (ExoWrapTableClass
),
133 exo_wrap_table_class_init
,
134 sizeof (ExoWrapTable
),
135 exo_wrap_table_init
);
144 exo_wrap_table_class_init (ExoWrapTableClass
*klass
)
146 GtkContainerClass
*gtkcontainer_class
;
147 GtkWidgetClass
*gtkwidget_class
;
148 GObjectClass
*gobject_class
;
150 /* add our private data to the class */
151 g_type_class_add_private (klass
, sizeof (ExoWrapTablePrivate
));
153 /* determine our parent type class */
154 exo_wrap_table_parent_class
= g_type_class_peek_parent (klass
);
156 gobject_class
= G_OBJECT_CLASS (klass
);
157 gobject_class
->get_property
= exo_wrap_table_get_property
;
158 gobject_class
->set_property
= exo_wrap_table_set_property
;
160 gtkwidget_class
= GTK_WIDGET_CLASS (klass
);
161 gtkwidget_class
->size_request
= exo_wrap_table_size_request
;
162 gtkwidget_class
->size_allocate
= exo_wrap_table_size_allocate
;
164 gtkcontainer_class
= GTK_CONTAINER_CLASS (klass
);
165 gtkcontainer_class
->add
= exo_wrap_table_add
;
166 gtkcontainer_class
->remove
= exo_wrap_table_remove
;
167 gtkcontainer_class
->forall
= exo_wrap_table_forall
;
170 * ExoWrapTable::col-spacing:
172 * The amount of space between two consecutive columns.
176 #define EXO_PARAM_READWRITE (G_PARAM_READWRITE \
177 | G_PARAM_STATIC_NAME \
178 | G_PARAM_STATIC_NICK \
179 | G_PARAM_STATIC_BLURB)
181 g_object_class_install_property (gobject_class
,
183 g_param_spec_uint ("col-spacing",
185 "The amount of space between two consecutive columns",
187 EXO_PARAM_READWRITE
));
190 * ExoWrapTable::row-spacing:
192 * The amount of space between two consecutive rows.
196 g_object_class_install_property (gobject_class
,
198 g_param_spec_uint ("row-spacing",
200 "The amount of space between two consecutive rows",
202 EXO_PARAM_READWRITE
));
205 * ExoWrapTable::homogeneous:
207 * Whether the children should be all the same size.
211 g_object_class_install_property (gobject_class
,
213 g_param_spec_boolean ("homogeneous",
215 ("Whether the children should be all the same size"),
217 EXO_PARAM_READWRITE
));
223 exo_wrap_table_init (ExoWrapTable
*table
)
225 /* grab a pointer on the private data */
226 table
->priv
= EXO_WRAP_TABLE_GET_PRIVATE (table
);
228 /* we don't provide our own window */
229 GTK_WIDGET_SET_FLAGS (table
, GTK_NO_WINDOW
);
235 exo_wrap_table_get_property (GObject
*object
,
240 ExoWrapTable
*table
= EXO_WRAP_TABLE (object
);
244 case PROP_COL_SPACING
:
245 g_value_set_uint (value
, exo_wrap_table_get_col_spacing (table
));
248 case PROP_ROW_SPACING
:
249 g_value_set_uint (value
, exo_wrap_table_get_row_spacing (table
));
252 case PROP_HOMOGENEOUS
:
253 g_value_set_boolean (value
, exo_wrap_table_get_homogeneous (table
));
257 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
265 exo_wrap_table_set_property (GObject
*object
,
270 ExoWrapTable
*table
= EXO_WRAP_TABLE (object
);
274 case PROP_COL_SPACING
:
275 exo_wrap_table_set_col_spacing (table
, g_value_get_uint (value
));
278 case PROP_ROW_SPACING
:
279 exo_wrap_table_set_row_spacing (table
, g_value_get_uint (value
));
282 case PROP_HOMOGENEOUS
:
283 exo_wrap_table_set_homogeneous (table
, g_value_get_boolean (value
));
287 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
295 exo_wrap_table_size_request (GtkWidget
*widget
,
296 GtkRequisition
*requisition
)
298 ExoWrapTable
*table
= EXO_WRAP_TABLE (widget
);
305 /* determine the max size request */
306 num_children
= exo_wrap_table_get_max_child_size (table
, &max_width
, &max_height
);
308 /* check if we have any visible children */
309 if (G_LIKELY (num_children
> 0))
311 num_cols
= exo_wrap_table_get_num_fitting (widget
->allocation
.width
312 - GTK_CONTAINER (widget
)->border_width
* 2,
313 table
->priv
->col_spacing
, max_width
);
314 num_rows
= num_children
/ num_cols
;
315 num_rows
= MAX (num_rows
, 1);
317 if ((num_children
% num_rows
) > 0)
320 requisition
->width
= -1;
321 requisition
->height
= (num_rows
* max_height
)
322 + (num_rows
- 1) * table
->priv
->col_spacing
323 + GTK_CONTAINER (widget
)->border_width
* 2;
327 requisition
->width
= 0;
328 requisition
->height
= 0;
335 exo_wrap_table_size_allocate (GtkWidget
*widget
,
336 GtkAllocation
*allocation
)
338 ExoWrapTable
*table
= EXO_WRAP_TABLE (widget
);
340 /* setup the new allocation */
341 widget
->allocation
= *allocation
;
343 /* layout the children */
344 exo_wrap_table_layout (table
);
350 exo_wrap_table_add (GtkContainer
*container
,
353 ExoWrapTable
*table
= EXO_WRAP_TABLE (container
);
355 /* take over ownership */
356 gtk_widget_set_parent (widget
, GTK_WIDGET (table
));
358 /* add the child to our internal list */
359 table
->priv
->children
= g_list_append (table
->priv
->children
, widget
);
361 /* realize the widget if required */
362 if (GTK_WIDGET_REALIZED (container
))
363 gtk_widget_realize (widget
);
365 /* map the widget if required */
366 if (GTK_WIDGET_VISIBLE (container
) && GTK_WIDGET_VISIBLE (widget
))
368 if (GTK_WIDGET_MAPPED (container
))
369 gtk_widget_map (widget
);
372 /* queue a resize on the table */
373 gtk_widget_queue_resize (GTK_WIDGET (container
));
379 exo_wrap_table_remove (GtkContainer
*container
,
382 ExoWrapTable
*table
= EXO_WRAP_TABLE (container
);
383 gboolean widget_was_visible
;
385 /* check if the widget was visible */
386 widget_was_visible
= GTK_WIDGET_VISIBLE (widget
);
388 /* unparent and remove the widget */
389 gtk_widget_unparent (widget
);
390 table
->priv
->children
= g_list_remove (table
->priv
->children
, widget
);
392 /* schedule a resize if the widget was visible */
393 if (G_LIKELY (widget_was_visible
))
394 gtk_widget_queue_resize (GTK_WIDGET (table
));
400 exo_wrap_table_forall (GtkContainer
*container
,
401 gboolean include_internals
,
402 GtkCallback callback
,
403 gpointer callback_data
)
405 ExoWrapTable
*table
= EXO_WRAP_TABLE (container
);
409 for (node
= table
->priv
->children
; node
!= NULL
; node
= next
)
411 /* verify that we have a valid widget for the node */
412 g_assert (GTK_IS_WIDGET (node
->data
));
414 /* remember a pointer to the next node */
417 /* invoke the callback for this widget */
418 (*callback
) (GTK_WIDGET (node
->data
), callback_data
);
425 exo_wrap_table_layout (ExoWrapTable
*table
)
427 GtkRequisition child_requisition
;
428 GtkAllocation child_allocation
;
437 /* determine the number of visible children and the max size */
438 num_children
= exo_wrap_table_get_max_child_size (table
, &max_width
, &max_height
);
439 if (G_UNLIKELY (num_children
<= 0))
442 /* determine the number of columns */
443 num_cols
= exo_wrap_table_get_num_fitting (GTK_WIDGET (table
)->allocation
.width
444 - GTK_CONTAINER (table
)->border_width
* 2,
445 table
->priv
->col_spacing
, max_width
);
447 /* verify that the number of columns match */
448 if (G_UNLIKELY (num_cols
!= table
->priv
->num_cols
))
450 table
->priv
->num_cols
= num_cols
;
451 gtk_widget_queue_resize (GTK_WIDGET (table
));
455 /* determine the horizontal bounds */
456 x0
= GTK_WIDGET (table
)->allocation
.x
+ GTK_CONTAINER (table
)->border_width
;
457 x1
= x0
+ GTK_WIDGET (table
)->allocation
.width
- GTK_CONTAINER (table
)->border_width
;
459 /* initialize the position */
461 y
= GTK_WIDGET (table
)->allocation
.y
+ GTK_CONTAINER (table
)->border_width
;
463 /* allocate space to all visible children */
464 for (lp
= table
->priv
->children
; lp
!= NULL
; lp
= lp
->next
)
466 /* allocate space only for visible children */
467 child
= GTK_WIDGET (lp
->data
);
468 if (G_UNLIKELY (!GTK_WIDGET_VISIBLE (child
)))
471 /* initialize the child position */
472 child_allocation
.x
= x
;
473 child_allocation
.y
= y
;
475 /* check if we should layout the children homogeneously */
476 if (G_LIKELY (table
->priv
->homogeneous
))
478 child_allocation
.width
= max_width
;
479 child_allocation
.height
= max_height
;
481 /* check if we're wrapping */
482 if (G_UNLIKELY ((x
+ max_width
) > x1
))
484 x
= x0
+ table
->priv
->col_spacing
+ max_width
;
485 y
+= table
->priv
->row_spacing
+ max_height
;
486 child_allocation
.x
= x0
;
487 child_allocation
.y
= y
;
491 x
+= table
->priv
->col_spacing
+ max_width
;
496 gtk_widget_size_request (child
, &child_requisition
);
498 child_allocation
.width
= child_requisition
.width
;
499 child_allocation
.height
= child_requisition
.height
;
501 g_assert (child_allocation
.width
<= max_width
);
502 g_assert (child_allocation
.height
<= max_height
);
504 if (G_UNLIKELY ((x
+ max_width
) > x1
))
506 x
= x0
+ table
->priv
->col_spacing
+ max_width
;
507 y
+= table
->priv
->row_spacing
+ max_height
;
508 child_allocation
.x
= x0
;
509 child_allocation
.y
= y
;
513 x
+= table
->priv
->col_spacing
+ max_width
;
517 /* allocate the space to the child */
518 gtk_widget_size_allocate (child
, &child_allocation
);
525 exo_wrap_table_get_max_child_size (const ExoWrapTable
*table
,
526 gint
*max_width_return
,
527 gint
*max_height_return
)
529 GtkRequisition child_requisition
;
534 gint num_children
= 0;
536 for (lp
= table
->priv
->children
; lp
!= NULL
; lp
= lp
->next
)
538 child
= GTK_WIDGET (lp
->data
);
539 if (GTK_WIDGET_VISIBLE (child
))
541 gtk_widget_size_request (child
, &child_requisition
);
542 if (child_requisition
.width
> max_width
)
543 max_width
= child_requisition
.width
;
544 if (child_requisition
.height
> max_height
)
545 max_height
= child_requisition
.height
;
547 /* we count only visible children */
552 /* use atleast one pixel if we have visible childrens */
553 if (G_LIKELY (num_children
> 0))
555 if (G_UNLIKELY (max_width
< 1))
557 if (G_UNLIKELY (max_height
< 1))
561 /* return the determined values */
562 if (G_LIKELY (max_width_return
!= NULL
))
563 *max_width_return
= max_width
;
564 if (G_LIKELY (max_height_return
!= NULL
))
565 *max_height_return
= max_height
;
573 exo_wrap_table_get_num_fitting (gint available
,
579 g_return_val_if_fail (spacing
>= 0, 0);
580 g_return_val_if_fail (max_child_size
> 0, 0);
582 /* verify that available is atleast 0 */
583 if (G_UNLIKELY (available
< 0))
586 /* determine the num */
587 num
= (available
+ spacing
) / (max_child_size
+ spacing
);
589 /* verify that num is atleast 1 */
590 if (G_UNLIKELY (num
< 1))
599 * exo_wrap_table_new:
600 * @homogeneous : %TRUE if all children are to be given equal space allotments.
602 * Allocates a new #ExoWrapTable.
604 * Return value: the newly allocated #ExoWrapTable.
609 exo_wrap_table_new (gboolean homogeneous
)
611 return g_object_new (EXO_TYPE_WRAP_TABLE
,
612 "homogeneous", homogeneous
,
619 * exo_wrap_table_get_col_spacing:
620 * @table : an #ExoWrapTable.
622 * Returns the amount of space between consecutive
625 * Return value: the amount of space between
626 * consecutive columns.
631 exo_wrap_table_get_col_spacing (const ExoWrapTable
*table
)
633 g_return_val_if_fail (EXO_IS_WRAP_TABLE (table
), 0);
634 return table
->priv
->col_spacing
;
640 * exo_wrap_table_set_col_spacing:
641 * @table : an #ExoWrapTable.
642 * @col_spacing : the new column spacing.
644 * Sets the amount of space between consecutive
645 * columns in @table to @col_spacing.
650 exo_wrap_table_set_col_spacing (ExoWrapTable
*table
,
653 g_return_if_fail (EXO_IS_WRAP_TABLE (table
));
655 if (G_LIKELY (table
->priv
->col_spacing
!= col_spacing
))
657 table
->priv
->col_spacing
= col_spacing
;
658 gtk_widget_queue_resize (GTK_WIDGET (table
));
659 g_object_notify (G_OBJECT (table
), "col-spacing");
666 * exo_wrap_table_get_row_spacing:
667 * @table : an #ExoWrapTable.
669 * Returns the amount of space between consecutive
672 * Return value: the amount of space between
673 * consecutive rows in @table.
678 exo_wrap_table_get_row_spacing (const ExoWrapTable
*table
)
680 g_return_val_if_fail (EXO_IS_WRAP_TABLE (table
), 0);
681 return table
->priv
->row_spacing
;
687 * exo_wrap_table_set_row_spacing:
688 * @table : an #ExoWrapTable.
689 * @row_spacing : the new row spacing.
691 * Sets the amount of spacing between consecutive
692 * rows in @table to @row_spacing.
697 exo_wrap_table_set_row_spacing (ExoWrapTable
*table
,
700 g_return_if_fail (EXO_IS_WRAP_TABLE (table
));
702 if (G_LIKELY (table
->priv
->row_spacing
!= row_spacing
))
704 table
->priv
->row_spacing
= row_spacing
;
705 gtk_widget_queue_resize (GTK_WIDGET (table
));
706 g_object_notify (G_OBJECT (table
), "row-spacing");
713 * exo_wrap_table_get_homogeneous:
714 * @table : an #ExoWrapTable.
716 * Returns whether the table cells are all constrained
717 * to the same width and height.
719 * Return value: %TRUE if the cells are all constrained
725 exo_wrap_table_get_homogeneous (const ExoWrapTable
*table
)
727 g_return_val_if_fail (EXO_IS_WRAP_TABLE (table
), FALSE
);
728 return table
->priv
->homogeneous
;
734 * exo_wrap_table_set_homogeneous:
735 * @table : an #ExoWrapTable.
736 * @homogeneous : Set to %TRUE to ensure all @table cells are the same size.
737 * Set to %FALSE if this is not your desired behaviour.
739 * Changes the homogenous property of @table cells, ie. whether all cells
740 * are an equal size or not.
745 exo_wrap_table_set_homogeneous (ExoWrapTable
*table
,
746 gboolean homogeneous
)
748 g_return_if_fail (EXO_IS_WRAP_TABLE (table
));
750 if (G_LIKELY (table
->priv
->homogeneous
!= homogeneous
))
752 table
->priv
->homogeneous
= homogeneous
;
753 gtk_widget_queue_resize (GTK_WIDGET (table
));
754 g_object_notify (G_OBJECT (table
), "homogeneous");
760 #define __EXO_WRAP_TABLE_C__