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