1/* gtkcelllayout.c
2 * Copyright (C) 2003 Kristian Rietveld <kris@gtk.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/**
19 * GtkCellLayout:
20 *
21 * An interface for packing cells
22 *
23 * `GtkCellLayout` is an interface to be implemented by all objects which
24 * want to provide a `GtkTreeViewColumn` like API for packing cells,
25 * setting attributes and data funcs.
26 *
27 * One of the notable features provided by implementations of
28 * `GtkCellLayout` are attributes. Attributes let you set the properties
29 * in flexible ways. They can just be set to constant values like regular
30 * properties. But they can also be mapped to a column of the underlying
31 * tree model with gtk_cell_layout_set_attributes(), which means that the value
32 * of the attribute can change from cell to cell as they are rendered by
33 * the cell renderer. Finally, it is possible to specify a function with
34 * gtk_cell_layout_set_cell_data_func() that is called to determine the
35 * value of the attribute for each cell that is rendered.
36 *
37 * # GtkCellLayouts as GtkBuildable
38 *
39 * Implementations of GtkCellLayout which also implement the GtkBuildable
40 * interface (`GtkCellView`, `GtkIconView`, `GtkComboBox`,
41 * `GtkEntryCompletion`, `GtkTreeViewColumn`) accept `GtkCellRenderer` objects
42 * as `<child>` elements in UI definitions. They support a custom `<attributes>`
43 * element for their children, which can contain multiple `<attribute>`
44 * elements. Each `<attribute>` element has a name attribute which specifies
45 * a property of the cell renderer; the content of the element is the
46 * attribute value.
47 *
48 * This is an example of a UI definition fragment specifying attributes:
49 *
50 * ```xml
51 * <object class="GtkCellView">
52 * <child>
53 * <object class="GtkCellRendererText"/>
54 * <attributes>
55 * <attribute name="text">0</attribute>
56 * </attributes>
57 * </child>
58 * </object>
59 * ```
60 *
61 * Furthermore for implementations of `GtkCellLayout` that use a `GtkCellArea`
62 * to lay out cells (all `GtkCellLayout`s in GTK use a `GtkCellArea`)
63 * [cell properties](class.CellArea.html#cell-properties) can also be defined
64 * in the format by specifying the custom `<cell-packing>` attribute which can
65 * contain multiple `<property>` elements.
66 *
67 * Here is a UI definition fragment specifying cell properties:
68 *
69 * ```xml
70 * <object class="GtkTreeViewColumn">
71 * <child>
72 * <object class="GtkCellRendererText"/>
73 * <cell-packing>
74 * <property name="align">True</property>
75 * <property name="expand">False</property>
76 * </cell-packing>
77 * </child>
78 * </object>
79 * ```
80 *
81 * # Subclassing GtkCellLayout implementations
82 *
83 * When subclassing a widget that implements `GtkCellLayout` like
84 * `GtkIconView` or `GtkComboBox`, there are some considerations related
85 * to the fact that these widgets internally use a `GtkCellArea`.
86 * The cell area is exposed as a construct-only property by these
87 * widgets. This means that it is possible to e.g. do
88 *
89 * ```c
90 * GtkWIdget *combo =
91 * g_object_new (GTK_TYPE_COMBO_BOX, "cell-area", my_cell_area, NULL);
92 * ```
93 *
94 * to use a custom cell area with a combo box. But construct properties
95 * are only initialized after instance `init()`
96 * functions have run, which means that using functions which rely on
97 * the existence of the cell area in your subclass `init()` function will
98 * cause the default cell area to be instantiated. In this case, a provided
99 * construct property value will be ignored (with a warning, to alert
100 * you to the problem).
101 *
102 * ```c
103 * static void
104 * my_combo_box_init (MyComboBox *b)
105 * {
106 * GtkCellRenderer *cell;
107 *
108 * cell = gtk_cell_renderer_pixbuf_new ();
109 *
110 * // The following call causes the default cell area for combo boxes,
111 * // a GtkCellAreaBox, to be instantiated
112 * gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (b), cell, FALSE);
113 * ...
114 * }
115 *
116 * GtkWidget *
117 * my_combo_box_new (GtkCellArea *area)
118 * {
119 * // This call is going to cause a warning about area being ignored
120 * return g_object_new (MY_TYPE_COMBO_BOX, "cell-area", area, NULL);
121 * }
122 * ```
123 *
124 * If supporting alternative cell areas with your derived widget is
125 * not important, then this does not have to concern you. If you want
126 * to support alternative cell areas, you can do so by moving the
127 * problematic calls out of `init()` and into a `constructor()`
128 * for your class.
129 */
130
131#include "config.h"
132#include <string.h>
133#include <stdlib.h>
134#include <errno.h>
135#include "gtkcelllayout.h"
136#include "gtkbuilderprivate.h"
137#include "gtkintl.h"
138
139#define warn_no_cell_area(func) \
140 g_critical ("%s: Called but no GtkCellArea is available yet", func)
141
142typedef GtkCellLayoutIface GtkCellLayoutInterface;
143G_DEFINE_INTERFACE (GtkCellLayout, gtk_cell_layout, G_TYPE_OBJECT);
144
145static void gtk_cell_layout_default_pack_start (GtkCellLayout *cell_layout,
146 GtkCellRenderer *cell,
147 gboolean expand);
148static void gtk_cell_layout_default_pack_end (GtkCellLayout *cell_layout,
149 GtkCellRenderer *cell,
150 gboolean expand);
151static void gtk_cell_layout_default_clear (GtkCellLayout *cell_layout);
152static void gtk_cell_layout_default_add_attribute (GtkCellLayout *cell_layout,
153 GtkCellRenderer *cell,
154 const char *attribute,
155 int column);
156static void gtk_cell_layout_default_set_cell_data_func (GtkCellLayout *cell_layout,
157 GtkCellRenderer *cell,
158 GtkCellLayoutDataFunc func,
159 gpointer func_data,
160 GDestroyNotify destroy);
161static void gtk_cell_layout_default_clear_attributes (GtkCellLayout *cell_layout,
162 GtkCellRenderer *cell);
163static void gtk_cell_layout_default_reorder (GtkCellLayout *cell_layout,
164 GtkCellRenderer *cell,
165 int position);
166static GList *gtk_cell_layout_default_get_cells (GtkCellLayout *cell_layout);
167
168
169static void
170gtk_cell_layout_default_init (GtkCellLayoutIface *iface)
171{
172 iface->pack_start = gtk_cell_layout_default_pack_start;
173 iface->pack_end = gtk_cell_layout_default_pack_end;
174 iface->clear = gtk_cell_layout_default_clear;
175 iface->add_attribute = gtk_cell_layout_default_add_attribute;
176 iface->set_cell_data_func = gtk_cell_layout_default_set_cell_data_func;
177 iface->clear_attributes = gtk_cell_layout_default_clear_attributes;
178 iface->reorder = gtk_cell_layout_default_reorder;
179 iface->get_cells = gtk_cell_layout_default_get_cells;
180}
181
182/* Default implementation is to fall back on an underlying cell area */
183static void
184gtk_cell_layout_default_pack_start (GtkCellLayout *cell_layout,
185 GtkCellRenderer *cell,
186 gboolean expand)
187{
188 GtkCellLayoutIface *iface;
189 GtkCellArea *area;
190
191 iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
192
193 if (iface->get_area)
194 {
195 area = iface->get_area (cell_layout);
196
197 if (area)
198 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (area), cell, expand);
199 else
200 warn_no_cell_area ("GtkCellLayoutIface->pack_start()");
201 }
202}
203
204static void
205gtk_cell_layout_default_pack_end (GtkCellLayout *cell_layout,
206 GtkCellRenderer *cell,
207 gboolean expand)
208{
209 GtkCellLayoutIface *iface;
210 GtkCellArea *area;
211
212 iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
213
214 if (iface->get_area)
215 {
216 area = iface->get_area (cell_layout);
217
218 if (area)
219 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (area), cell, expand);
220 else
221 warn_no_cell_area ("GtkCellLayoutIface->pack_end()");
222 }
223}
224
225static void
226gtk_cell_layout_default_clear (GtkCellLayout *cell_layout)
227{
228 GtkCellLayoutIface *iface;
229 GtkCellArea *area;
230
231 iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
232
233 if (iface->get_area)
234 {
235 area = iface->get_area (cell_layout);
236
237 if (area)
238 gtk_cell_layout_clear (GTK_CELL_LAYOUT (area));
239 else
240 warn_no_cell_area ("GtkCellLayoutIface->clear()");
241 }
242}
243
244static void
245gtk_cell_layout_default_add_attribute (GtkCellLayout *cell_layout,
246 GtkCellRenderer *cell,
247 const char *attribute,
248 int column)
249{
250 GtkCellLayoutIface *iface;
251 GtkCellArea *area;
252
253 iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
254
255 if (iface->get_area)
256 {
257 area = iface->get_area (cell_layout);
258
259 if (area)
260 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (area), cell, attribute, column);
261 else
262 warn_no_cell_area ("GtkCellLayoutIface->add_attribute()");
263 }
264}
265
266static void
267gtk_cell_layout_default_set_cell_data_func (GtkCellLayout *cell_layout,
268 GtkCellRenderer *cell,
269 GtkCellLayoutDataFunc func,
270 gpointer func_data,
271 GDestroyNotify destroy)
272{
273 GtkCellLayoutIface *iface;
274 GtkCellArea *area;
275
276 iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
277
278 if (iface->get_area)
279 {
280 area = iface->get_area (cell_layout);
281
282 if (area)
283 _gtk_cell_area_set_cell_data_func_with_proxy (area, cell,
284 func: (GFunc)func, func_data, destroy,
285 proxy: cell_layout);
286 else
287 warn_no_cell_area ("GtkCellLayoutIface->set_cell_data_func()");
288 }
289}
290
291static void
292gtk_cell_layout_default_clear_attributes (GtkCellLayout *cell_layout,
293 GtkCellRenderer *cell)
294{
295 GtkCellLayoutIface *iface;
296 GtkCellArea *area;
297
298 iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
299
300 if (iface->get_area)
301 {
302 area = iface->get_area (cell_layout);
303
304 if (area)
305 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (area), cell);
306 else
307 warn_no_cell_area ("GtkCellLayoutIface->clear_attributes()");
308 }
309}
310
311static void
312gtk_cell_layout_default_reorder (GtkCellLayout *cell_layout,
313 GtkCellRenderer *cell,
314 int position)
315{
316 GtkCellLayoutIface *iface;
317 GtkCellArea *area;
318
319 iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
320
321 if (iface->get_area)
322 {
323 area = iface->get_area (cell_layout);
324
325 if (area)
326 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (area), cell, position);
327 else
328 warn_no_cell_area ("GtkCellLayoutIface->reorder()");
329 }
330}
331
332static GList *
333gtk_cell_layout_default_get_cells (GtkCellLayout *cell_layout)
334{
335 GtkCellLayoutIface *iface;
336 GtkCellArea *area;
337
338 iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
339
340 if (iface->get_area)
341 {
342 area = iface->get_area (cell_layout);
343
344 if (area)
345 return gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
346 else
347 warn_no_cell_area ("GtkCellLayoutIface->get_cells()");
348 }
349 return NULL;
350}
351
352
353/**
354 * gtk_cell_layout_pack_start:
355 * @cell_layout: a `GtkCellLayout`
356 * @cell: a `GtkCellRenderer`
357 * @expand: %TRUE if @cell is to be given extra space allocated to @cell_layout
358 *
359 * Packs the @cell into the beginning of @cell_layout. If @expand is %FALSE,
360 * then the @cell is allocated no more space than it needs. Any unused space
361 * is divided evenly between cells for which @expand is %TRUE.
362 *
363 * Note that reusing the same cell renderer is not supported.
364 */
365void
366gtk_cell_layout_pack_start (GtkCellLayout *cell_layout,
367 GtkCellRenderer *cell,
368 gboolean expand)
369{
370 g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
371 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
372
373 GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->pack_start (cell_layout, cell, expand);
374}
375
376/**
377 * gtk_cell_layout_pack_end:
378 * @cell_layout: a `GtkCellLayout`
379 * @cell: a `GtkCellRenderer`
380 * @expand: %TRUE if @cell is to be given extra space allocated to @cell_layout
381 *
382 * Adds the @cell to the end of @cell_layout. If @expand is %FALSE, then the
383 * @cell is allocated no more space than it needs. Any unused space is
384 * divided evenly between cells for which @expand is %TRUE.
385 *
386 * Note that reusing the same cell renderer is not supported.
387 */
388void
389gtk_cell_layout_pack_end (GtkCellLayout *cell_layout,
390 GtkCellRenderer *cell,
391 gboolean expand)
392{
393 g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
394 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
395
396 GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->pack_end (cell_layout, cell, expand);
397}
398
399/**
400 * gtk_cell_layout_clear:
401 * @cell_layout: a `GtkCellLayout`
402 *
403 * Unsets all the mappings on all renderers on @cell_layout and
404 * removes all renderers from @cell_layout.
405 */
406void
407gtk_cell_layout_clear (GtkCellLayout *cell_layout)
408{
409 g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
410
411 GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->clear (cell_layout);
412}
413
414static void
415gtk_cell_layout_set_attributesv (GtkCellLayout *cell_layout,
416 GtkCellRenderer *cell,
417 va_list args)
418{
419 char *attribute;
420 int column;
421
422 attribute = va_arg (args, char *);
423
424 gtk_cell_layout_clear_attributes (cell_layout, cell);
425
426 while (attribute != NULL)
427 {
428 column = va_arg (args, int);
429
430 gtk_cell_layout_add_attribute (cell_layout, cell, attribute, column);
431
432 attribute = va_arg (args, char *);
433 }
434}
435
436/**
437 * gtk_cell_layout_set_attributes:
438 * @cell_layout: a `GtkCellLayout`
439 * @cell: a `GtkCellRenderer`
440 * @...: a %NULL-terminated list of attributes
441 *
442 * Sets the attributes in the parameter list as the attributes
443 * of @cell_layout.
444 *
445 * See [method@Gtk.CellLayout.add_attribute] for more details.
446 *
447 * The attributes should be in attribute/column order, as in
448 * gtk_cell_layout_add_attribute(). All existing attributes are
449 * removed, and replaced with the new attributes.
450 */
451void
452gtk_cell_layout_set_attributes (GtkCellLayout *cell_layout,
453 GtkCellRenderer *cell,
454 ...)
455{
456 va_list args;
457
458 g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
459 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
460
461 va_start (args, cell);
462 gtk_cell_layout_set_attributesv (cell_layout, cell, args);
463 va_end (args);
464}
465
466/**
467 * gtk_cell_layout_add_attribute:
468 * @cell_layout: a `GtkCellLayout`
469 * @cell: a `GtkCellRenderer`
470 * @attribute: a property on the renderer
471 * @column: the column position on the model to get the attribute from
472 *
473 * Adds an attribute mapping to the list in @cell_layout.
474 *
475 * The @column is the column of the model to get a value from, and the
476 * @attribute is the property on @cell to be set from that value. So for
477 * example if column 2 of the model contains strings, you could have the
478 * “text” attribute of a `GtkCellRendererText` get its values from column 2.
479 * In this context "attribute" and "property" are used interchangeably.
480 */
481void
482gtk_cell_layout_add_attribute (GtkCellLayout *cell_layout,
483 GtkCellRenderer *cell,
484 const char *attribute,
485 int column)
486{
487 g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
488 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
489 g_return_if_fail (attribute != NULL);
490 g_return_if_fail (column >= 0);
491
492 GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->add_attribute (cell_layout, cell, attribute, column);
493}
494
495/**
496 * gtk_cell_layout_set_cell_data_func:
497 * @cell_layout: a `GtkCellLayout`
498 * @cell: a `GtkCellRenderer`
499 * @func: (nullable): the `GtkCellLayout`DataFunc to use
500 * @func_data: (closure): user data for @func
501 * @destroy: destroy notify for @func_data
502 *
503 * Sets the `GtkCellLayout`DataFunc to use for @cell_layout.
504 *
505 * This function is used instead of the standard attributes mapping
506 * for setting the column value, and should set the value of @cell_layout’s
507 * cell renderer(s) as appropriate.
508 *
509 * @func may be %NULL to remove a previously set function.
510 */
511void
512gtk_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout,
513 GtkCellRenderer *cell,
514 GtkCellLayoutDataFunc func,
515 gpointer func_data,
516 GDestroyNotify destroy)
517{
518 g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
519 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
520
521 GTK_CELL_LAYOUT_GET_IFACE
522 (cell_layout)->set_cell_data_func (cell_layout, cell, func, func_data, destroy);
523}
524
525/**
526 * gtk_cell_layout_clear_attributes:
527 * @cell_layout: a `GtkCellLayout`
528 * @cell: a `GtkCellRenderer` to clear the attribute mapping on
529 *
530 * Clears all existing attributes previously set with
531 * gtk_cell_layout_set_attributes().
532 */
533void
534gtk_cell_layout_clear_attributes (GtkCellLayout *cell_layout,
535 GtkCellRenderer *cell)
536{
537 g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
538 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
539
540 GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->clear_attributes (cell_layout, cell);
541}
542
543/**
544 * gtk_cell_layout_reorder:
545 * @cell_layout: a `GtkCellLayout`
546 * @cell: a `GtkCellRenderer` to reorder
547 * @position: new position to insert @cell at
548 *
549 * Re-inserts @cell at @position.
550 *
551 * Note that @cell has already to be packed into @cell_layout
552 * for this to function properly.
553 */
554void
555gtk_cell_layout_reorder (GtkCellLayout *cell_layout,
556 GtkCellRenderer *cell,
557 int position)
558{
559 g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
560 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
561
562 GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->reorder (cell_layout, cell, position);
563}
564
565/**
566 * gtk_cell_layout_get_cells:
567 * @cell_layout: a `GtkCellLayout`
568 *
569 * Returns the cell renderers which have been added to @cell_layout.
570 *
571 * Returns: (element-type GtkCellRenderer) (transfer container):
572 * a list of cell renderers. The list, but not the renderers has
573 * been newly allocated and should be freed with g_list_free()
574 * when no longer needed.
575 */
576GList *
577gtk_cell_layout_get_cells (GtkCellLayout *cell_layout)
578{
579 g_return_val_if_fail (GTK_IS_CELL_LAYOUT (cell_layout), NULL);
580
581 return GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->get_cells (cell_layout);
582}
583
584/**
585 * gtk_cell_layout_get_area:
586 * @cell_layout: a `GtkCellLayout`
587 *
588 * Returns the underlying `GtkCellArea` which might be @cell_layout
589 * if called on a `GtkCellArea` or might be %NULL if no `GtkCellArea`
590 * is used by @cell_layout.
591 *
592 * Returns: (transfer none) (nullable): the cell area used by @cell_layout
593 */
594GtkCellArea *
595gtk_cell_layout_get_area (GtkCellLayout *cell_layout)
596{
597 GtkCellLayoutIface *iface;
598
599 g_return_val_if_fail (GTK_IS_CELL_LAYOUT (cell_layout), NULL);
600
601 iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
602 if (iface->get_area)
603 return iface->get_area (cell_layout);
604
605 return NULL;
606}
607
608/* Attribute parsing */
609typedef struct {
610 GtkCellLayout *cell_layout;
611 GtkCellRenderer *renderer;
612 GtkBuilder *builder;
613 char *attr_name;
614 GString *string;
615} AttributesSubParserData;
616
617static void
618attributes_start_element (GtkBuildableParseContext *context,
619 const char *element_name,
620 const char **names,
621 const char **values,
622 gpointer user_data,
623 GError **error)
624{
625 AttributesSubParserData *data = (AttributesSubParserData*)user_data;
626
627 if (strcmp (s1: element_name, s2: "attribute") == 0)
628 {
629 const char *name;
630
631 if (!_gtk_builder_check_parent (builder: data->builder, context, parent_name: "attributes", error))
632 return;
633
634 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
635 first_type: G_MARKUP_COLLECT_STRING, first_attr: "name", &name,
636 G_MARKUP_COLLECT_INVALID))
637 {
638 _gtk_builder_prefix_error (builder: data->builder, context, error);
639 return;
640 }
641
642 data->attr_name = g_strdup (str: name);
643 }
644 else if (strcmp (s1: element_name, s2: "attributes") == 0)
645 {
646 if (!_gtk_builder_check_parent (builder: data->builder, context, parent_name: "child", error))
647 return;
648
649 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
650 first_type: G_MARKUP_COLLECT_INVALID, NULL, NULL,
651 G_MARKUP_COLLECT_INVALID))
652 _gtk_builder_prefix_error (builder: data->builder, context, error);
653 }
654 else
655 {
656 _gtk_builder_error_unhandled_tag (builder: data->builder, context,
657 object: "GtkCellLayout", element_name,
658 error);
659 }
660}
661
662static void
663attributes_text_element (GtkBuildableParseContext *context,
664 const char *text,
665 gsize text_len,
666 gpointer user_data,
667 GError **error)
668{
669 AttributesSubParserData *data = (AttributesSubParserData*)user_data;
670
671 if (data->attr_name)
672 g_string_append_len (string: data->string, val: text, len: text_len);
673}
674
675static void
676attributes_end_element (GtkBuildableParseContext *context,
677 const char *element_name,
678 gpointer user_data,
679 GError **error)
680{
681 AttributesSubParserData *data = (AttributesSubParserData*)user_data;
682 GValue val = G_VALUE_INIT;
683
684 if (!data->attr_name)
685 return;
686
687 if (!gtk_builder_value_from_string_type (builder: data->builder, G_TYPE_INT, string: data->string->str, value: &val, error))
688 {
689 _gtk_builder_prefix_error (builder: data->builder, context, error);
690 return;
691 }
692
693 gtk_cell_layout_add_attribute (cell_layout: data->cell_layout,
694 cell: data->renderer,
695 attribute: data->attr_name,
696 column: g_value_get_int (value: &val));
697
698 g_free (mem: data->attr_name);
699 data->attr_name = NULL;
700
701 g_string_set_size (string: data->string, len: 0);
702}
703
704static const GtkBuildableParser attributes_parser =
705 {
706 attributes_start_element,
707 attributes_end_element,
708 attributes_text_element
709 };
710
711
712/* Cell packing parsing */
713static void
714gtk_cell_layout_buildable_set_cell_property (GtkCellArea *area,
715 GtkBuilder *builder,
716 GtkCellRenderer *cell,
717 char *name,
718 const char *value)
719{
720 GParamSpec *pspec;
721 GValue gvalue = G_VALUE_INIT;
722 GError *error = NULL;
723
724 pspec = gtk_cell_area_class_find_cell_property (GTK_CELL_AREA_GET_CLASS (area), property_name: name);
725 if (!pspec)
726 {
727 g_warning ("%s does not have a property called %s",
728 g_type_name (G_OBJECT_TYPE (area)), name);
729 return;
730 }
731
732 if (!gtk_builder_value_from_string (builder, pspec, string: value, value: &gvalue, error: &error))
733 {
734 g_warning ("Could not read property %s:%s with value %s of type %s: %s",
735 g_type_name (G_OBJECT_TYPE (area)),
736 name,
737 value,
738 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
739 error->message);
740 g_error_free (error);
741 return;
742 }
743
744 gtk_cell_area_cell_set_property (area, renderer: cell, property_name: name, value: &gvalue);
745 g_value_unset (value: &gvalue);
746}
747
748typedef struct {
749 GtkBuilder *builder;
750 GtkCellLayout *cell_layout;
751 GtkCellRenderer *renderer;
752 GString *string;
753 char *cell_prop_name;
754 char *context;
755 gboolean translatable;
756} CellPackingSubParserData;
757
758static void
759cell_packing_start_element (GtkBuildableParseContext *context,
760 const char *element_name,
761 const char **names,
762 const char **values,
763 gpointer user_data,
764 GError **error)
765{
766 CellPackingSubParserData *data = (CellPackingSubParserData*)user_data;
767
768 if (strcmp (s1: element_name, s2: "property") == 0)
769 {
770 const char *name;
771 gboolean translatable = FALSE;
772 const char *ctx = NULL;
773
774 if (!_gtk_builder_check_parent (builder: data->builder, context, parent_name: "cell-packing", error))
775 return;
776
777 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
778 first_type: G_MARKUP_COLLECT_STRING, first_attr: "name", &name,
779 G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable,
780 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL,
781 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &ctx,
782 G_MARKUP_COLLECT_INVALID))
783 {
784 _gtk_builder_prefix_error (builder: data->builder, context, error);
785 return;
786 }
787
788 data->cell_prop_name = g_strdup (str: name);
789 data->translatable = translatable;
790 data->context = g_strdup (str: ctx);
791 }
792 else if (strcmp (s1: element_name, s2: "cell-packing") == 0)
793 {
794 if (!_gtk_builder_check_parent (builder: data->builder, context, parent_name: "child", error))
795 return;
796
797 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
798 first_type: G_MARKUP_COLLECT_INVALID, NULL, NULL,
799 G_MARKUP_COLLECT_INVALID))
800 _gtk_builder_prefix_error (builder: data->builder, context, error);
801 }
802 else
803 {
804 _gtk_builder_error_unhandled_tag (builder: data->builder, context,
805 object: "GtkCellLayout", element_name,
806 error);
807 }
808}
809
810static void
811cell_packing_text_element (GtkBuildableParseContext *context,
812 const char *text,
813 gsize text_len,
814 gpointer user_data,
815 GError **error)
816{
817 CellPackingSubParserData *data = (CellPackingSubParserData*)user_data;
818
819 if (data->cell_prop_name)
820 g_string_append_len (string: data->string, val: text, len: text_len);
821}
822
823static void
824cell_packing_end_element (GtkBuildableParseContext *context,
825 const char *element_name,
826 gpointer user_data,
827 GError **error)
828{
829 CellPackingSubParserData *data = (CellPackingSubParserData*)user_data;
830 GtkCellArea *area;
831
832 area = gtk_cell_layout_get_area (cell_layout: data->cell_layout);
833
834 if (area)
835 {
836 /* translate the string */
837 if (data->string->len && data->translatable)
838 {
839 const char *translated;
840 const char * domain;
841
842 domain = gtk_builder_get_translation_domain (builder: data->builder);
843
844 translated = _gtk_builder_parser_translate (domain,
845 context: data->context,
846 text: data->string->str);
847 g_string_assign (string: data->string, rval: translated);
848 }
849
850 if (data->cell_prop_name)
851 gtk_cell_layout_buildable_set_cell_property (area,
852 builder: data->builder,
853 cell: data->renderer,
854 name: data->cell_prop_name,
855 value: data->string->str);
856 }
857 else
858 g_warning ("%s does not have an internal GtkCellArea class and cannot apply child cell properties",
859 g_type_name (G_OBJECT_TYPE (data->cell_layout)));
860
861 g_string_set_size (string: data->string, len: 0);
862 g_free (mem: data->cell_prop_name);
863 g_free (mem: data->context);
864 data->cell_prop_name = NULL;
865 data->context = NULL;
866 data->translatable = FALSE;
867}
868
869
870static const GtkBuildableParser cell_packing_parser =
871 {
872 cell_packing_start_element,
873 cell_packing_end_element,
874 cell_packing_text_element
875 };
876
877gboolean
878_gtk_cell_layout_buildable_custom_tag_start (GtkBuildable *buildable,
879 GtkBuilder *builder,
880 GObject *child,
881 const char *tagname,
882 GtkBuildableParser *parser,
883 gpointer *data)
884{
885 AttributesSubParserData *attr_data;
886 CellPackingSubParserData *packing_data;
887
888 if (!child)
889 return FALSE;
890
891 if (strcmp (s1: tagname, s2: "attributes") == 0)
892 {
893 attr_data = g_slice_new0 (AttributesSubParserData);
894 attr_data->cell_layout = GTK_CELL_LAYOUT (buildable);
895 attr_data->renderer = GTK_CELL_RENDERER (child);
896 attr_data->builder = builder;
897 attr_data->attr_name = NULL;
898 attr_data->string = g_string_new (init: "");
899
900 *parser = attributes_parser;
901 *data = attr_data;
902
903 return TRUE;
904 }
905 else if (strcmp (s1: tagname, s2: "cell-packing") == 0)
906 {
907 packing_data = g_slice_new0 (CellPackingSubParserData);
908 packing_data->string = g_string_new (init: "");
909 packing_data->builder = builder;
910 packing_data->cell_layout = GTK_CELL_LAYOUT (buildable);
911 packing_data->renderer = GTK_CELL_RENDERER (child);
912
913 *parser = cell_packing_parser;
914 *data = packing_data;
915
916 return TRUE;
917 }
918
919 return FALSE;
920}
921
922gboolean
923_gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable,
924 GtkBuilder *builder,
925 GObject *child,
926 const char *tagname,
927 gpointer *data)
928{
929 AttributesSubParserData *attr_data;
930 CellPackingSubParserData *packing_data;
931
932 if (strcmp (s1: tagname, s2: "attributes") == 0)
933 {
934 attr_data = (AttributesSubParserData*)data;
935 g_assert (!attr_data->attr_name);
936 g_string_free (string: attr_data->string, TRUE);
937 g_slice_free (AttributesSubParserData, attr_data);
938
939 return TRUE;
940 }
941 else if (strcmp (s1: tagname, s2: "cell-packing") == 0)
942 {
943 packing_data = (CellPackingSubParserData *)data;
944 g_string_free (string: packing_data->string, TRUE);
945 g_slice_free (CellPackingSubParserData, packing_data);
946
947 return TRUE;
948 }
949 return FALSE;
950}
951
952void
953_gtk_cell_layout_buildable_add_child (GtkBuildable *buildable,
954 GtkBuilder *builder,
955 GObject *child,
956 const char *type)
957{
958 g_return_if_fail (GTK_IS_CELL_LAYOUT (buildable));
959 g_return_if_fail (GTK_IS_CELL_RENDERER (child));
960
961 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (buildable), GTK_CELL_RENDERER (child), FALSE);
962}
963

source code of gtk/gtk/gtkcelllayout.c