Trying to improve speed
[gmpc-albumview.git] / src / exo-wrap-table.c
blob9b98c18b1ab89100b7adb38cd91b971fa5ef96b7
1 /* $Id: exo-wrap-table.c 22884 2006-08-26 12:40:43Z benny $ */
2 /*-
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.
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
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 */
35 enum
37 PROP_0,
38 PROP_COL_SPACING,
39 PROP_ROW_SPACING,
40 PROP_HOMOGENEOUS,
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,
48 guint prop_id,
49 GValue *value,
50 GParamSpec *pspec);
51 static void exo_wrap_table_set_property (GObject *object,
52 guint prop_id,
53 const GValue *value,
54 GParamSpec *pspec);
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,
60 GtkWidget *widget);
61 static void exo_wrap_table_remove (GtkContainer *container,
62 GtkWidget *widget);
63 static void exo_wrap_table_forall (GtkContainer *container,
64 gboolean include_internals,
65 GtkCallback callback,
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,
72 gint spacing,
73 gint max_child_size);
77 struct _ExoWrapTablePrivate
79 /* the list of child widgets */
80 GList *children;
82 /* configurable parameters */
83 guint col_spacing;
84 guint row_spacing;
85 guint homogeneous : 1;
87 /* the estimated number of columns */
88 gint num_cols;
93 static GObjectClass *exo_wrap_table_parent_class;
94 GType
95 _exo_g_type_register_simple (GType type_parent,
96 const gchar *type_name_static,
97 guint class_size,
98 gpointer class_init,
99 guint instance_size,
100 gpointer instance_init)
102 /* generate the type info (on the stack) */
103 GTypeInfo info =
105 class_size,
106 NULL,
107 NULL,
108 class_init,
109 NULL,
110 NULL,
111 instance_size,
113 instance_init,
114 NULL,
117 /* register the static type */
118 return g_type_register_static (type_parent, type_name_static, &info, 0);
123 GType
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,
131 "ExoWrapTable",
132 sizeof (ExoWrapTableClass),
133 exo_wrap_table_class_init,
134 sizeof (ExoWrapTable),
135 exo_wrap_table_init);
138 return type;
143 static void
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.
174 * Since: 0.3.1
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,
182 PROP_COL_SPACING,
183 g_param_spec_uint ("col-spacing",
184 "Column spacing",
185 "The amount of space between two consecutive columns",
186 0, G_MAXUINT, 0,
187 EXO_PARAM_READWRITE));
190 * ExoWrapTable::row-spacing:
192 * The amount of space between two consecutive rows.
194 * Since: 0.3.1
196 g_object_class_install_property (gobject_class,
197 PROP_ROW_SPACING,
198 g_param_spec_uint ("row-spacing",
199 "Row spacing",
200 "The amount of space between two consecutive rows",
201 0, G_MAXUINT, 0,
202 EXO_PARAM_READWRITE));
205 * ExoWrapTable::homogeneous:
207 * Whether the children should be all the same size.
209 * Since: 0.3.1
211 g_object_class_install_property (gobject_class,
212 PROP_HOMOGENEOUS,
213 g_param_spec_boolean ("homogeneous",
214 ("Homogeneous"),
215 ("Whether the children should be all the same size"),
216 FALSE,
217 EXO_PARAM_READWRITE));
222 static void
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);
234 static void
235 exo_wrap_table_get_property (GObject *object,
236 guint prop_id,
237 GValue *value,
238 GParamSpec *pspec)
240 ExoWrapTable *table = EXO_WRAP_TABLE (object);
242 switch (prop_id)
244 case PROP_COL_SPACING:
245 g_value_set_uint (value, exo_wrap_table_get_col_spacing (table));
246 break;
248 case PROP_ROW_SPACING:
249 g_value_set_uint (value, exo_wrap_table_get_row_spacing (table));
250 break;
252 case PROP_HOMOGENEOUS:
253 g_value_set_boolean (value, exo_wrap_table_get_homogeneous (table));
254 break;
256 default:
257 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
258 break;
264 static void
265 exo_wrap_table_set_property (GObject *object,
266 guint prop_id,
267 const GValue *value,
268 GParamSpec *pspec)
270 ExoWrapTable *table = EXO_WRAP_TABLE (object);
272 switch (prop_id)
274 case PROP_COL_SPACING:
275 exo_wrap_table_set_col_spacing (table, g_value_get_uint (value));
276 break;
278 case PROP_ROW_SPACING:
279 exo_wrap_table_set_row_spacing (table, g_value_get_uint (value));
280 break;
282 case PROP_HOMOGENEOUS:
283 exo_wrap_table_set_homogeneous (table, g_value_get_boolean (value));
284 break;
286 default:
287 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
288 break;
294 static void
295 exo_wrap_table_size_request (GtkWidget *widget,
296 GtkRequisition *requisition)
298 ExoWrapTable *table = EXO_WRAP_TABLE (widget);
299 gint max_width = 0;
300 gint max_height = 0;
301 gint num_children;
302 gint num_cols;
303 gint num_rows;
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)
318 ++num_rows;
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;
325 else
327 requisition->width = 0;
328 requisition->height = 0;
334 static void
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);
349 static void
350 exo_wrap_table_add (GtkContainer *container,
351 GtkWidget *widget)
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));
378 static void
379 exo_wrap_table_remove (GtkContainer *container,
380 GtkWidget *widget)
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));
399 static void
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);
406 GList *next;
407 GList *node;
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 */
415 next = node->next;
417 /* invoke the callback for this widget */
418 (*callback) (GTK_WIDGET (node->data), callback_data);
424 static void
425 exo_wrap_table_layout (ExoWrapTable *table)
427 GtkRequisition child_requisition;
428 GtkAllocation child_allocation;
429 GtkWidget *child;
430 GList *lp;
431 gint x0, x1, x, y;
432 gint num_children;
433 gint num_cols;
434 gint max_height;
435 gint max_width;
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))
440 return;
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));
452 return;
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 */
460 x = x0;
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)))
469 continue;
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;
489 else
491 x += table->priv->col_spacing + max_width;
494 else
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;
511 else
513 x += table->priv->col_spacing + max_width;
517 /* allocate the space to the child */
518 gtk_widget_size_allocate (child, &child_allocation);
524 static gint
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;
530 GtkWidget *child;
531 GList *lp;
532 gint max_width = 0;
533 gint max_height = 0;
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 */
548 ++num_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))
556 max_width = 1;
557 if (G_UNLIKELY (max_height < 1))
558 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;
567 return num_children;
572 static gint
573 exo_wrap_table_get_num_fitting (gint available,
574 gint spacing,
575 gint max_child_size)
577 gint num;
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))
584 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))
591 num = 1;
593 return num;
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.
606 * Since: 0.3.1
608 GtkWidget*
609 exo_wrap_table_new (gboolean homogeneous)
611 return g_object_new (EXO_TYPE_WRAP_TABLE,
612 "homogeneous", homogeneous,
613 NULL);
619 * exo_wrap_table_get_col_spacing:
620 * @table : an #ExoWrapTable.
622 * Returns the amount of space between consecutive
623 * columns in @table.
625 * Return value: the amount of space between
626 * consecutive columns.
628 * Since: 0.3.1
630 guint
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.
647 * Since: 0.3.1
649 void
650 exo_wrap_table_set_col_spacing (ExoWrapTable *table,
651 guint col_spacing)
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
670 * rows in @table.
672 * Return value: the amount of space between
673 * consecutive rows in @table.
675 * Since: 0.3.1
677 guint
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.
694 * Since: 0.3.1
696 void
697 exo_wrap_table_set_row_spacing (ExoWrapTable *table,
698 guint row_spacing)
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
720 * to the same size.
722 * Since: 0.3.1
724 gboolean
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.
742 * Since: 0.3.1
744 void
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__