3 * Copyright (C) 2010 Openismus GmbH
6 * Tristan Van Berkom <tristanvb@openismus.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 #include "gtkcelllayout.h"
25 #include "gtkcellarea.h"
28 static void gtk_cell_area_dispose (GObject *object);
29 static void gtk_cell_area_finalize (GObject *object);
31 /* GtkCellAreaClass */
32 static void gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
36 gint *natural_height);
37 static void gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
43 /* GtkCellLayoutIface */
44 static void gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface);
45 static void gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
46 GtkCellRenderer *renderer,
48 static void gtk_cell_area_clear (GtkCellLayout *cell_layout);
49 static void gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
50 GtkCellRenderer *renderer,
51 const gchar *attribute,
53 static void gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
54 GtkCellRenderer *cell,
55 GtkCellLayoutDataFunc func,
57 GDestroyNotify destroy);
58 static void gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
59 GtkCellRenderer *renderer);
60 static void gtk_cell_area_reorder (GtkCellLayout *cell_layout,
61 GtkCellRenderer *cell,
63 static GList *gtk_cell_area_get_cells (GtkCellLayout *cell_layout);
65 /* GtkCellLayoutDataFunc handling */
67 GtkCellLayoutDataFunc func;
69 GDestroyNotify destroy;
72 static CustomCellData *custom_cell_data_new (GtkCellLayoutDataFunc func,
74 GDestroyNotify destroy);
75 static void custom_cell_data_free (CustomCellData *custom);
77 /* Struct to pass data while looping over
78 * cell renderer attributes
86 struct _GtkCellAreaPrivate
88 GHashTable *custom_cell_data;
91 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED,
92 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
93 gtk_cell_area_cell_layout_init));
96 gtk_cell_area_init (GtkCellArea *area)
98 GtkCellAreaPrivate *priv;
100 area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area,
105 priv->custom_cell_data = g_hash_table_new_full (g_direct_hash,
108 (GDestroyNotify)custom_cell_data_free);
112 gtk_cell_area_class_init (GtkCellAreaClass *class)
114 GObjectClass *object_class = G_OBJECT_CLASS (class);
117 object_class->dispose = gtk_cell_area_dispose;
118 object_class->finalize = gtk_cell_area_finalize;
122 class->remove = NULL;
123 class->forall = NULL;
125 class->render = NULL;
128 class->attribute_connect = NULL;
129 class->attribute_disconnect = NULL;
130 class->attribute_forall = NULL;
133 class->get_request_mode = NULL;
134 class->get_preferred_width = NULL;
135 class->get_preferred_height = NULL;
136 class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width;
137 class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height;
139 g_type_class_add_private (object_class, sizeof (GtkCellAreaPrivate));
143 /*************************************************************
145 *************************************************************/
147 gtk_cell_area_finalize (GObject *object)
149 GtkCellArea *area = GTK_CELL_AREA (object);
150 GtkCellAreaPrivate *priv = area->priv;
152 /* All cell renderers should already be removed at this point,
153 * just kill our hash table here.
155 g_hash_table_destroy (priv->custom_cell_data);
157 G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object);
162 gtk_cell_area_dispose (GObject *object)
164 /* This removes every cell renderer that may be added to the GtkCellArea,
165 * subclasses should be breaking references to the GtkCellRenderers
168 gtk_cell_layout_clear (GTK_CELL_LAYOUT (object));
170 G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
174 /*************************************************************
176 *************************************************************/
178 gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
181 gint *minimum_height,
182 gint *natural_height)
184 /* If the area doesnt do height-for-width, fallback on base preferred height */
185 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, widget, minimum_height, natural_height);
189 gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
195 /* If the area doesnt do width-for-height, fallback on base preferred width */
196 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, widget, minimum_width, natural_width);
199 /*************************************************************
200 * GtkCellLayoutIface *
201 *************************************************************/
202 static CustomCellData *
203 custom_cell_data_new (GtkCellLayoutDataFunc func,
205 GDestroyNotify destroy)
207 CustomCellData *custom = g_slice_new (CustomCellData);
211 custom->destroy = destroy;
217 custom_cell_data_free (CustomCellData *custom)
220 custom->destroy (custom->data);
222 g_slice_free (CustomCellData, custom);
226 gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface)
228 iface->pack_start = gtk_cell_area_pack_default;
229 iface->pack_end = gtk_cell_area_pack_default;
230 iface->clear = gtk_cell_area_clear;
231 iface->add_attribute = gtk_cell_area_add_attribute;
232 iface->set_cell_data_func = gtk_cell_area_set_cell_data_func;
233 iface->clear_attributes = gtk_cell_area_clear_attributes;
234 iface->reorder = gtk_cell_area_reorder;
235 iface->get_cells = gtk_cell_area_get_cells;
239 gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
240 GtkCellRenderer *renderer,
243 gtk_cell_area_add (GTK_CELL_AREA (cell_layout), renderer);
247 gtk_cell_area_clear (GtkCellLayout *cell_layout)
249 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
251 gtk_cell_layout_get_cells (cell_layout);
253 for (l = cells; l; l = l->next)
255 GtkCellRenderer *renderer = l->data;
256 gtk_cell_area_remove (area, renderer);
263 gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
264 GtkCellRenderer *renderer,
265 const gchar *attribute,
268 gtk_cell_area_attribute_connect (GTK_CELL_AREA (cell_layout),
269 renderer, attribute, id);
273 const gchar *attribute;
278 accum_attributes (GtkCellArea *area,
279 GtkCellRenderer *renderer,
280 const gchar *attribute,
284 CellAttribute *attrib = g_slice_new (CellAttribute);
286 attrib->attribute = attribute;
289 *accum = g_list_prepend (*accum, attrib);
293 gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
294 GtkCellRenderer *cell,
295 GtkCellLayoutDataFunc func,
297 GDestroyNotify destroy)
299 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
300 GtkCellAreaPrivate *priv = area->priv;
301 CustomCellData *custom;
305 custom = custom_cell_data_new (func, func_data, destroy);
306 g_hash_table_insert (priv->custom_cell_data, cell, custom);
309 g_hash_table_remove (priv->custom_cell_data, cell);
314 gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
315 GtkCellRenderer *renderer)
317 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
318 GList *l, *attributes = NULL;
320 /* Get a list of attributes so we dont modify the list inline */
321 gtk_cell_area_attribute_forall (area, renderer,
322 (GtkCellAttributeCallback)accum_attributes,
325 for (l = attributes; l; l = l->next)
327 CellAttribute *attrib = l->data;
329 gtk_cell_area_attribute_disconnect (area, renderer,
330 attrib->attribute, attrib->id);
332 g_slice_free (CellAttribute, attrib);
335 g_list_free (attributes);
339 gtk_cell_area_reorder (GtkCellLayout *cell_layout,
340 GtkCellRenderer *cell,
343 g_warning ("GtkCellLayout::reorder not implemented for `%s'",
344 g_type_name (G_TYPE_FROM_INSTANCE (cell_layout)));
348 accum_cells (GtkCellRenderer *renderer,
351 *accum = g_list_prepend (*accum, renderer);
355 gtk_cell_area_get_cells (GtkCellLayout *cell_layout)
359 gtk_cell_area_forall (GTK_CELL_AREA (cell_layout),
360 (GtkCellCallback)accum_cells,
363 return g_list_reverse (cells);
367 /*************************************************************
369 *************************************************************/
373 gtk_cell_area_add (GtkCellArea *area,
374 GtkCellRenderer *renderer)
376 GtkCellAreaClass *class;
378 g_return_if_fail (GTK_IS_CELL_AREA (area));
379 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
381 class = GTK_CELL_AREA_GET_CLASS (area);
384 class->add (area, renderer);
386 g_warning ("GtkCellAreaClass::add not implemented for `%s'",
387 g_type_name (G_TYPE_FROM_INSTANCE (area)));
391 gtk_cell_area_remove (GtkCellArea *area,
392 GtkCellRenderer *renderer)
394 GtkCellAreaClass *class;
396 g_return_if_fail (GTK_IS_CELL_AREA (area));
397 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
399 class = GTK_CELL_AREA_GET_CLASS (area);
401 /* Remove any custom cell data func we have for this renderer */
402 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (area),
403 renderer, NULL, NULL, NULL);
406 class->remove (area, renderer);
408 g_warning ("GtkCellAreaClass::remove not implemented for `%s'",
409 g_type_name (G_TYPE_FROM_INSTANCE (area)));
413 gtk_cell_area_forall (GtkCellArea *area,
414 GtkCellCallback callback,
415 gpointer callback_data)
417 GtkCellAreaClass *class;
419 g_return_if_fail (GTK_IS_CELL_AREA (area));
420 g_return_if_fail (callback != NULL);
422 class = GTK_CELL_AREA_GET_CLASS (area);
425 class->forall (area, callback, callback_data);
427 g_warning ("GtkCellAreaClass::forall not implemented for `%s'",
428 g_type_name (G_TYPE_FROM_INSTANCE (area)));
432 gtk_cell_area_event (GtkCellArea *area,
435 const GdkRectangle *cell_area)
437 GtkCellAreaClass *class;
439 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
440 g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
441 g_return_val_if_fail (event != NULL, 0);
442 g_return_val_if_fail (cell_area != NULL, 0);
444 class = GTK_CELL_AREA_GET_CLASS (area);
447 return class->event (area, widget, event, cell_area);
449 g_warning ("GtkCellAreaClass::event not implemented for `%s'",
450 g_type_name (G_TYPE_FROM_INSTANCE (area)));
455 gtk_cell_area_render (GtkCellArea *area,
458 const GdkRectangle *cell_area)
460 GtkCellAreaClass *class;
462 g_return_if_fail (GTK_IS_CELL_AREA (area));
463 g_return_if_fail (cr != NULL);
464 g_return_if_fail (GTK_IS_WIDGET (widget));
465 g_return_if_fail (cell_area != NULL);
467 class = GTK_CELL_AREA_GET_CLASS (area);
470 class->render (area, cr, widget, cell_area);
472 g_warning ("GtkCellAreaClass::render not implemented for `%s'",
473 g_type_name (G_TYPE_FROM_INSTANCE (area)));
478 gtk_cell_area_attribute_connect (GtkCellArea *area,
479 GtkCellRenderer *renderer,
480 const gchar *attribute,
483 GtkCellAreaClass *class;
485 g_return_if_fail (GTK_IS_CELL_AREA (area));
486 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
487 g_return_if_fail (attribute != NULL);
489 class = GTK_CELL_AREA_GET_CLASS (area);
491 if (class->attribute_connect)
492 class->attribute_connect (area, renderer, attribute, id);
494 g_warning ("GtkCellAreaClass::attribute_connect not implemented for `%s'",
495 g_type_name (G_TYPE_FROM_INSTANCE (area)));
499 gtk_cell_area_attribute_disconnect (GtkCellArea *area,
500 GtkCellRenderer *renderer,
501 const gchar *attribute,
504 GtkCellAreaClass *class;
506 g_return_if_fail (GTK_IS_CELL_AREA (area));
507 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
508 g_return_if_fail (attribute != NULL);
510 class = GTK_CELL_AREA_GET_CLASS (area);
512 if (class->attribute_disconnect)
513 class->attribute_disconnect (area, renderer, attribute, id);
515 g_warning ("GtkCellAreaClass::attribute_disconnect not implemented for `%s'",
516 g_type_name (G_TYPE_FROM_INSTANCE (area)));
520 gtk_cell_area_attribute_forall (GtkCellArea *area,
521 GtkCellRenderer *renderer,
522 GtkCellAttributeCallback callback,
525 GtkCellAreaClass *class;
527 g_return_if_fail (GTK_IS_CELL_AREA (area));
528 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
529 g_return_if_fail (callback != NULL);
531 class = GTK_CELL_AREA_GET_CLASS (area);
533 if (class->attribute_forall)
534 class->attribute_forall (area, renderer, callback, user_data);
536 g_warning ("GtkCellAreaClass::attribute_forall not implemented for `%s'",
537 g_type_name (G_TYPE_FROM_INSTANCE (area)));
543 gtk_cell_area_get_request_mode (GtkCellArea *area)
545 GtkCellAreaClass *class;
547 g_return_val_if_fail (GTK_IS_CELL_AREA (area),
548 GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH);
550 class = GTK_CELL_AREA_GET_CLASS (area);
552 if (class->get_request_mode)
553 return class->get_request_mode (area);
555 g_warning ("GtkCellAreaClass::get_request_mode not implemented for `%s'",
556 g_type_name (G_TYPE_FROM_INSTANCE (area)));
558 return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
562 gtk_cell_area_get_preferred_width (GtkCellArea *area,
567 GtkCellAreaClass *class;
569 g_return_if_fail (GTK_IS_CELL_AREA (area));
570 g_return_if_fail (GTK_IS_WIDGET (widget));
572 class = GTK_CELL_AREA_GET_CLASS (area);
574 if (class->get_preferred_width)
575 class->get_preferred_width (area, widget, minimum_size, natural_size);
577 g_warning ("GtkCellAreaClass::get_preferred_width not implemented for `%s'",
578 g_type_name (G_TYPE_FROM_INSTANCE (area)));
582 gtk_cell_area_get_preferred_height_for_width (GtkCellArea *area,
585 gint *minimum_height,
586 gint *natural_height)
588 GtkCellAreaClass *class;
590 g_return_if_fail (GTK_IS_CELL_AREA (area));
591 g_return_if_fail (GTK_IS_WIDGET (widget));
593 class = GTK_CELL_AREA_GET_CLASS (area);
594 class->get_preferred_height_for_width (area, widget, width, minimum_height, natural_height);
598 gtk_cell_area_get_preferred_height (GtkCellArea *area,
603 GtkCellAreaClass *class;
605 g_return_if_fail (GTK_IS_CELL_AREA (area));
606 g_return_if_fail (GTK_IS_WIDGET (widget));
608 class = GTK_CELL_AREA_GET_CLASS (area);
610 if (class->get_preferred_height)
611 class->get_preferred_height (area, widget, minimum_size, natural_size);
613 g_warning ("GtkCellAreaClass::get_preferred_height not implemented for `%s'",
614 g_type_name (G_TYPE_FROM_INSTANCE (area)));
618 gtk_cell_area_get_preferred_width_for_height (GtkCellArea *area,
624 GtkCellAreaClass *class;
626 g_return_if_fail (GTK_IS_CELL_AREA (area));
627 g_return_if_fail (GTK_IS_WIDGET (widget));
629 class = GTK_CELL_AREA_GET_CLASS (area);
630 class->get_preferred_width_for_height (area, widget, height, minimum_width, natural_width);
635 apply_attributes (GtkCellRenderer *renderer,
636 const gchar *attribute,
640 GValue value = { 0, };
642 /* For each attribute of each renderer we apply the value
643 * from the model to the renderer here
645 gtk_tree_model_get_value (data->model, data->iter, id, &value);
646 g_object_set_property (G_OBJECT (renderer), attribute, &value);
647 g_value_unset (&value);
651 apply_render_attributes (GtkCellRenderer *renderer,
654 gtk_cell_area_attribute_forall (data->area, renderer,
655 (GtkCellAttributeCallback)apply_attributes,
660 apply_custom_cell_data (GtkCellRenderer *renderer,
661 CustomCellData *custom,
664 g_assert (custom->func);
666 /* For each renderer that has a GtkCellLayoutDataFunc set,
667 * go ahead and envoke it to apply the data from the model
669 custom->func (GTK_CELL_LAYOUT (data->area), renderer,
670 data->model, data->iter, custom->data);
674 gtk_cell_area_apply_attributes (GtkCellArea *area,
675 GtkTreeModel *tree_model,
678 GtkCellAreaPrivate *priv;
681 g_return_if_fail (GTK_IS_CELL_AREA (area));
682 g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
683 g_return_if_fail (iter != NULL);
687 /* For every cell renderer, for every attribute, apply the attribute */
689 data.model = tree_model;
691 gtk_cell_area_forall (area, (GtkCellCallback)apply_render_attributes, &data);
693 /* Now go over any custom cell data functions */
694 g_hash_table_foreach (priv->custom_cell_data, (GHFunc)apply_custom_cell_data, &data);