gtkcelllayout.c 30.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* gtkcelllayout.c
 * Copyright (C) 2003  Kristian Rietveld  <kris@gtk.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16
17
 */

18
/**
Matthias Clasen's avatar
Matthias Clasen committed
19
20
21
 * GtkCellLayout:
 *
 * An interface for packing cells
22
 *
Matthias Clasen's avatar
Matthias Clasen committed
23
24
 * `GtkCellLayout` is an interface to be implemented by all objects which
 * want to provide a `GtkTreeViewColumn` like API for packing cells,
Matthias Clasen's avatar
Matthias Clasen committed
25
 * setting attributes and data funcs.
26
 *
Matthias Clasen's avatar
Matthias Clasen committed
27
 * One of the notable features provided by implementations of 
Matthias Clasen's avatar
Matthias Clasen committed
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
31
 * properties. But they can also be mapped to a column of the underlying
 * tree model with gtk_cell_layout_set_attributes(), which means that the value
Matthias Clasen's avatar
Matthias Clasen committed
32
33
34
35
36
 * of the attribute can change from cell to cell as they are rendered by
 * the cell renderer. Finally, it is possible to specify a function with
 * gtk_cell_layout_set_cell_data_func() that is called to determine the
 * value of the attribute for each cell that is rendered.
 *
37
 * # GtkCellLayouts as GtkBuildable
38
 *
39
 * Implementations of GtkCellLayout which also implement the GtkBuildable
Matthias Clasen's avatar
Matthias Clasen committed
40
41
 * interface (`GtkCellView`, `GtkIconView`, `GtkComboBox`,
 * `GtkEntryCompletion`, `GtkTreeViewColumn`) accept `GtkCellRenderer` objects
42
43
44
 * as `<child>` elements in UI definitions. They support a custom `<attributes>`
 * element for their children, which can contain multiple `<attribute>`
 * elements. Each `<attribute>` element has a name attribute which specifies
45
46
 * a property of the cell renderer; the content of the element is the
 * attribute value.
47
 *
Matthias Clasen's avatar
Matthias Clasen committed
48
 * This is an example of a UI definition fragment specifying attributes:
49
50
 *
 * ```xml
51
52
53
54
55
56
 * <object class="GtkCellView">
 *   <child>
 *     <object class="GtkCellRendererText"/>
 *     <attributes>
 *       <attribute name="text">0</attribute>
 *     </attributes>
57
 *   </child>
58
 * </object>
59
 * ```
60
 *
61
62
63
64
65
 * Furthermore for implementations of `GtkCellLayout` that use a `GtkCellArea`
 * to lay out cells (all `GtkCellLayout`s in GTK use a `GtkCellArea`)
 * [cell properties](class.CellArea.html#cell-properties) can also be defined
 * in the format by specifying the custom `<cell-packing>` attribute which can
 * contain multiple `<property>` elements.
Matthias Clasen's avatar
Matthias Clasen committed
66
67
 *
 * Here is a UI definition fragment specifying cell properties:
68
 *
69
 * ```xml
70
71
72
73
74
75
76
 * <object class="GtkTreeViewColumn">
 *   <child>
 *     <object class="GtkCellRendererText"/>
 *     <cell-packing>
 *       <property name="align">True</property>
 *       <property name="expand">False</property>
 *     </cell-packing>
77
 *   </child>
78
 * </object>
79
 * ```
80
 *
81
 * # Subclassing GtkCellLayout implementations
Matthias Clasen's avatar
Matthias Clasen committed
82
 *
Matthias Clasen's avatar
Matthias Clasen committed
83
84
85
 * When subclassing a widget that implements `GtkCellLayout` like
 * `GtkIconView` or `GtkComboBox`, there are some considerations related
 * to the fact that these widgets internally use a `GtkCellArea`.
86
87
 * The cell area is exposed as a construct-only property by these
 * widgets. This means that it is possible to e.g. do
88
 *
89
90
91
92
 * ```c
 * GtkWIdget *combo =
 *   g_object_new (GTK_TYPE_COMBO_BOX, "cell-area", my_cell_area, NULL);
 * ```
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
99
100
 * cause the default cell area to be instantiated. In this case, a provided
 * construct property value will be ignored (with a warning, to alert
 * you to the problem).
101
 *
102
 * ```c
103
104
105
106
107
108
 * static void
 * my_combo_box_init (MyComboBox *b)
 * {
 *   GtkCellRenderer *cell;
 *
 *   cell = gtk_cell_renderer_pixbuf_new ();
109
 *
110
111
 *   // The following call causes the default cell area for combo boxes,
 *   // a GtkCellAreaBox, to be instantiated
112
113
114
115
116
117
118
 *   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (b), cell, FALSE);
 *   ...
 * }
 *
 * GtkWidget *
 * my_combo_box_new (GtkCellArea *area)
 * {
119
 *   // This call is going to cause a warning about area being ignored
120
121
 *   return g_object_new (MY_TYPE_COMBO_BOX, "cell-area", area, NULL);
 * }
122
 * ```
123
 *
124
125
126
 * If supporting alternative cell areas with your derived widget is
 * not important, then this does not have to concern you. If you want
 * 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"
Johan Dahlin's avatar
Johan Dahlin committed
132
133
#include <string.h>
#include <stdlib.h>
134
#include <errno.h>
Kristian Rietveld's avatar
Kristian Rietveld committed
135
#include "gtkcelllayout.h"
136
#include "gtkbuilderprivate.h"
Matthias Clasen's avatar
Matthias Clasen committed
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

142
143
typedef GtkCellLayoutIface GtkCellLayoutInterface;
G_DEFINE_INTERFACE (GtkCellLayout, gtk_cell_layout, G_TYPE_OBJECT);
144

145
146
147
148
149
150
151
152
153
static void   gtk_cell_layout_default_pack_start         (GtkCellLayout         *cell_layout,
							  GtkCellRenderer       *cell,
							  gboolean               expand);
static void   gtk_cell_layout_default_pack_end           (GtkCellLayout         *cell_layout,
							  GtkCellRenderer       *cell,
							  gboolean               expand);
static void   gtk_cell_layout_default_clear              (GtkCellLayout         *cell_layout);
static void   gtk_cell_layout_default_add_attribute      (GtkCellLayout         *cell_layout,
							  GtkCellRenderer       *cell,
Benjamin Otte's avatar
Benjamin Otte committed
154
							  const char            *attribute,
Benjamin Otte's avatar
Benjamin Otte committed
155
							  int                    column);
156
157
158
159
160
161
162
163
164
static void   gtk_cell_layout_default_set_cell_data_func (GtkCellLayout         *cell_layout,
							  GtkCellRenderer       *cell,
							  GtkCellLayoutDataFunc  func,
							  gpointer               func_data,
							  GDestroyNotify         destroy);
static void   gtk_cell_layout_default_clear_attributes   (GtkCellLayout         *cell_layout,
							  GtkCellRenderer       *cell);
static void   gtk_cell_layout_default_reorder            (GtkCellLayout         *cell_layout,
							  GtkCellRenderer       *cell,
Benjamin Otte's avatar
Benjamin Otte committed
165
							  int                    position);
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
static GList *gtk_cell_layout_default_get_cells          (GtkCellLayout         *cell_layout);


static void
gtk_cell_layout_default_init (GtkCellLayoutIface *iface)
{
  iface->pack_start         = gtk_cell_layout_default_pack_start;
  iface->pack_end           = gtk_cell_layout_default_pack_end;
  iface->clear              = gtk_cell_layout_default_clear;
  iface->add_attribute      = gtk_cell_layout_default_add_attribute;
  iface->set_cell_data_func = gtk_cell_layout_default_set_cell_data_func;
  iface->clear_attributes   = gtk_cell_layout_default_clear_attributes;
  iface->reorder            = gtk_cell_layout_default_reorder;
  iface->get_cells          = gtk_cell_layout_default_get_cells;
}

/* Default implementation is to fall back on an underlying cell area */
static void
gtk_cell_layout_default_pack_start (GtkCellLayout         *cell_layout,
				    GtkCellRenderer       *cell,
				    gboolean               expand)
{
  GtkCellLayoutIface *iface;
  GtkCellArea        *area;

  iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);

  if (iface->get_area)
    {
      area = iface->get_area (cell_layout);

197
198
199
200
      if (area)
	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (area), cell, expand);
      else
	warn_no_cell_area ("GtkCellLayoutIface->pack_start()");
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
    }
}

static void
gtk_cell_layout_default_pack_end (GtkCellLayout         *cell_layout,
				  GtkCellRenderer       *cell,
				  gboolean               expand)
{
  GtkCellLayoutIface *iface;
  GtkCellArea        *area;

  iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);

  if (iface->get_area)
    {
      area = iface->get_area (cell_layout);

218
219
220
221
      if (area)
	gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (area), cell, expand);
      else
	warn_no_cell_area ("GtkCellLayoutIface->pack_end()");
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
    }
}

static void
gtk_cell_layout_default_clear (GtkCellLayout *cell_layout)
{
  GtkCellLayoutIface *iface;
  GtkCellArea        *area;

  iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);

  if (iface->get_area)
    {
      area = iface->get_area (cell_layout);

237
238
239
240
      if (area)
	gtk_cell_layout_clear (GTK_CELL_LAYOUT (area));
      else
	warn_no_cell_area ("GtkCellLayoutIface->clear()");
241
242
    }
}
243

244
static void
245
246
gtk_cell_layout_default_add_attribute (GtkCellLayout         *cell_layout,
				       GtkCellRenderer       *cell,
Benjamin Otte's avatar
Benjamin Otte committed
247
				       const char            *attribute,
Benjamin Otte's avatar
Benjamin Otte committed
248
				       int                    column)
249
{
250
251
252
253
254
255
256
257
258
  GtkCellLayoutIface *iface;
  GtkCellArea        *area;

  iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);

  if (iface->get_area)
    {
      area = iface->get_area (cell_layout);

259
260
261
262
      if (area)
	gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (area), cell, attribute, column);
      else
	warn_no_cell_area ("GtkCellLayoutIface->add_attribute()");
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
    }
}

static void
gtk_cell_layout_default_set_cell_data_func (GtkCellLayout         *cell_layout,
					    GtkCellRenderer       *cell,
					    GtkCellLayoutDataFunc  func,
					    gpointer               func_data,
					    GDestroyNotify         destroy)
{
  GtkCellLayoutIface *iface;
  GtkCellArea        *area;

  iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);

  if (iface->get_area)
    {
      area = iface->get_area (cell_layout);

282
283
284
285
286
287
      if (area)
	_gtk_cell_area_set_cell_data_func_with_proxy (area, cell, 
						      (GFunc)func, func_data, destroy, 
						      cell_layout);
      else
	warn_no_cell_area ("GtkCellLayoutIface->set_cell_data_func()");
288
    }
289
290
}

291
292
293
294
295
296
297
298
299
300
301
302
303
static void
gtk_cell_layout_default_clear_attributes (GtkCellLayout         *cell_layout,
					  GtkCellRenderer       *cell)
{
  GtkCellLayoutIface *iface;
  GtkCellArea        *area;

  iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);

  if (iface->get_area)
    {
      area = iface->get_area (cell_layout);

304
305
306
307
      if (area)
	gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (area), cell);
      else
	warn_no_cell_area ("GtkCellLayoutIface->clear_attributes()");
308
309
310
311
312
313
    }
}

static void
gtk_cell_layout_default_reorder (GtkCellLayout         *cell_layout,
				 GtkCellRenderer       *cell,
Benjamin Otte's avatar
Benjamin Otte committed
314
				 int                    position)
315
316
317
318
319
320
321
322
323
324
{
  GtkCellLayoutIface *iface;
  GtkCellArea        *area;

  iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);

  if (iface->get_area)
    {
      area = iface->get_area (cell_layout);

325
326
327
328
      if (area)
	gtk_cell_layout_reorder (GTK_CELL_LAYOUT (area), cell, position);
      else
	warn_no_cell_area ("GtkCellLayoutIface->reorder()");
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
    }
}

static GList *
gtk_cell_layout_default_get_cells (GtkCellLayout *cell_layout)
{
  GtkCellLayoutIface *iface;
  GtkCellArea        *area;

  iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);

  if (iface->get_area)
    {
      area = iface->get_area (cell_layout);

344
345
346
347
      if (area)
	return gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
      else
	warn_no_cell_area ("GtkCellLayoutIface->get_cells()");
348
349
350
351
352
    }
  return NULL;
}


353
354
/**
 * gtk_cell_layout_pack_start:
Matthias Clasen's avatar
Matthias Clasen committed
355
356
 * @cell_layout: a `GtkCellLayout`
 * @cell: a `GtkCellRenderer`
357
 * @expand: %TRUE if @cell is to be given extra space allocated to @cell_layout
358
359
360
361
362
 *
 * Packs the @cell into the beginning of @cell_layout. If @expand is %FALSE,
 * then the @cell is allocated no more space than it needs. Any unused space
 * is divided evenly between cells for which @expand is %TRUE.
 *
363
 * Note that reusing the same cell renderer is not supported.
364
 */
365
366
367
368
369
370
371
372
void
gtk_cell_layout_pack_start (GtkCellLayout   *cell_layout,
                            GtkCellRenderer *cell,
                            gboolean         expand)
{
  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));

373
  GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->pack_start (cell_layout, cell, expand);
374
375
}

376
377
/**
 * gtk_cell_layout_pack_end:
Matthias Clasen's avatar
Matthias Clasen committed
378
379
 * @cell_layout: a `GtkCellLayout`
 * @cell: a `GtkCellRenderer`
380
 * @expand: %TRUE if @cell is to be given extra space allocated to @cell_layout
381
382
383
384
385
 *
 * Adds the @cell to the end of @cell_layout. If @expand is %FALSE, then the
 * @cell is allocated no more space than it needs. Any unused space is
 * divided evenly between cells for which @expand is %TRUE.
 *
386
 * Note that reusing the same cell renderer is not supported.
387
 */
388
389
390
391
392
393
394
395
void
gtk_cell_layout_pack_end (GtkCellLayout   *cell_layout,
                          GtkCellRenderer *cell,
                          gboolean         expand)
{
  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));

396
  GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->pack_end (cell_layout, cell, expand);
397
398
}

399
400
/**
 * gtk_cell_layout_clear:
Matthias Clasen's avatar
Matthias Clasen committed
401
 * @cell_layout: a `GtkCellLayout`
402
 *
403
404
 * Unsets all the mappings on all renderers on @cell_layout and
 * removes all renderers from @cell_layout.
405
 */
406
407
408
409
410
void
gtk_cell_layout_clear (GtkCellLayout *cell_layout)
{
  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));

411
  GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->clear (cell_layout);
412
413
414
415
416
417
418
}

static void
gtk_cell_layout_set_attributesv (GtkCellLayout   *cell_layout,
                                 GtkCellRenderer *cell,
                                 va_list          args)
{
Benjamin Otte's avatar
Benjamin Otte committed
419
  char *attribute;
Benjamin Otte's avatar
Benjamin Otte committed
420
  int column;
421

Benjamin Otte's avatar
Benjamin Otte committed
422
  attribute = va_arg (args, char *);
423

424
  gtk_cell_layout_clear_attributes (cell_layout, cell);
425
426
427

  while (attribute != NULL)
    {
Benjamin Otte's avatar
Benjamin Otte committed
428
      column = va_arg (args, int);
429
430

      gtk_cell_layout_add_attribute (cell_layout, cell, attribute, column);
431

Benjamin Otte's avatar
Benjamin Otte committed
432
      attribute = va_arg (args, char *);
433
434
435
    }
}

436
437
/**
 * gtk_cell_layout_set_attributes:
Matthias Clasen's avatar
Matthias Clasen committed
438
439
 * @cell_layout: a `GtkCellLayout`
 * @cell: a `GtkCellRenderer`
Matthias Clasen's avatar
Matthias Clasen committed
440
 * @...: a %NULL-terminated list of attributes
441
 *
442
443
444
445
 * Sets the attributes in the parameter list as the attributes 
 * of @cell_layout.
 *
 * See [method@Gtk.CellLayout.add_attribute] for more details.
446
 *
447
448
449
 * The attributes should be in attribute/column order, as in
 * gtk_cell_layout_add_attribute(). All existing attributes are
 * removed, and replaced with the new attributes.
450
 */
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
void
gtk_cell_layout_set_attributes (GtkCellLayout   *cell_layout,
                                GtkCellRenderer *cell,
                                ...)
{
  va_list args;

  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));

  va_start (args, cell);
  gtk_cell_layout_set_attributesv (cell_layout, cell, args);
  va_end (args);
}

466
467
/**
 * gtk_cell_layout_add_attribute:
Matthias Clasen's avatar
Matthias Clasen committed
468
469
 * @cell_layout: a `GtkCellLayout`
 * @cell: a `GtkCellRenderer`
470
 * @attribute: a property on the renderer
471
472
473
 * @column: the column position on the model to get the attribute from
 *
 * 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
Matthias Clasen's avatar
Matthias Clasen committed
478
 * “text” attribute of a `GtkCellRendererText` get its values from column 2.
479
 * In this context "attribute" and "property" are used interchangeably.
480
 */
481
482
483
void
gtk_cell_layout_add_attribute (GtkCellLayout   *cell_layout,
                               GtkCellRenderer *cell,
Benjamin Otte's avatar
Benjamin Otte committed
484
                               const char      *attribute,
Benjamin Otte's avatar
Benjamin Otte committed
485
                               int              column)
486
487
488
489
490
491
{
  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
  g_return_if_fail (attribute != NULL);
  g_return_if_fail (column >= 0);

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:
Matthias Clasen's avatar
Matthias Clasen committed
497
498
499
 * @cell_layout: a `GtkCellLayout`
 * @cell: a `GtkCellRenderer`
 * @func: (nullable): the `GtkCellLayout`DataFunc to use
500
 * @func_data: (closure): user data for @func
501
502
 * @destroy: destroy notify for @func_data
 *
Matthias Clasen's avatar
Matthias Clasen committed
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
508
509
 * cell renderer(s) as appropriate.
 *
 * @func may be %NULL to remove a previously set function.
510
 */
511
512
513
514
515
516
517
518
519
520
void
gtk_cell_layout_set_cell_data_func (GtkCellLayout         *cell_layout,
                                    GtkCellRenderer       *cell,
                                    GtkCellLayoutDataFunc  func,
                                    gpointer               func_data,
                                    GDestroyNotify         destroy)
{
  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));

521
522
  GTK_CELL_LAYOUT_GET_IFACE 
    (cell_layout)->set_cell_data_func (cell_layout, cell, func, func_data, destroy);
523
524
}

525
526
/**
 * gtk_cell_layout_clear_attributes:
Matthias Clasen's avatar
Matthias Clasen committed
527
528
 * @cell_layout: a `GtkCellLayout`
 * @cell: a `GtkCellRenderer` to clear the attribute mapping on
529
530
531
532
 *
 * Clears all existing attributes previously set with
 * gtk_cell_layout_set_attributes().
 */
533
534
535
536
537
538
539
void
gtk_cell_layout_clear_attributes (GtkCellLayout   *cell_layout,
                                  GtkCellRenderer *cell)
{
  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));

540
  GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->clear_attributes (cell_layout, cell);
541
}
Kristian Rietveld's avatar
Kristian Rietveld committed
542
543
544

/**
 * gtk_cell_layout_reorder:
Matthias Clasen's avatar
Matthias Clasen committed
545
546
 * @cell_layout: a `GtkCellLayout`
 * @cell: a `GtkCellRenderer` to reorder
547
548
549
 * @position: new position to insert @cell at
 *
 * Re-inserts @cell at @position.
Kristian Rietveld's avatar
Kristian Rietveld committed
550
 *
551
552
 * Note that @cell has already to be packed into @cell_layout
 * for this to function properly.
Kristian Rietveld's avatar
Kristian Rietveld committed
553
554
555
556
 */
void
gtk_cell_layout_reorder (GtkCellLayout   *cell_layout,
                         GtkCellRenderer *cell,
Benjamin Otte's avatar
Benjamin Otte committed
557
                         int              position)
Kristian Rietveld's avatar
Kristian Rietveld committed
558
559
560
561
{
  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));

562
  GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->reorder (cell_layout, cell, position);
Kristian Rietveld's avatar
Kristian Rietveld committed
563
}
564

565
566
/**
 * gtk_cell_layout_get_cells:
Matthias Clasen's avatar
Matthias Clasen committed
567
 * @cell_layout: a `GtkCellLayout`
568
 *
569
570
 * Returns the cell renderers which have been added to @cell_layout.
 *
571
 * Returns: (element-type GtkCellRenderer) (transfer container):
Matthias Clasen's avatar
Matthias Clasen committed
572
573
574
 *   a list of cell renderers. The list, but not the renderers has
 *   been newly allocated and should be freed with g_list_free()
 *   when no longer needed.
575
576
577
578
579
580
 */
GList *
gtk_cell_layout_get_cells (GtkCellLayout *cell_layout)
{
  g_return_val_if_fail (GTK_IS_CELL_LAYOUT (cell_layout), NULL);

581
  return GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->get_cells (cell_layout);
582
583
}

584
585
/**
 * gtk_cell_layout_get_area:
Matthias Clasen's avatar
Matthias Clasen committed
586
 * @cell_layout: a `GtkCellLayout`
587
 *
Matthias Clasen's avatar
Matthias Clasen committed
588
589
 * Returns the underlying `GtkCellArea` which might be @cell_layout
 * if called on a `GtkCellArea` or might be %NULL if no `GtkCellArea`
590
591
 * is used by @cell_layout.
 *
Matthias Clasen's avatar
Matthias Clasen committed
592
 * Returns: (transfer none) (nullable): the cell area used by @cell_layout
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
 */
GtkCellArea *
gtk_cell_layout_get_area (GtkCellLayout *cell_layout)
{
  GtkCellLayoutIface *iface;

  g_return_val_if_fail (GTK_IS_CELL_LAYOUT (cell_layout), NULL);

  iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);  
  if (iface->get_area)
    return iface->get_area (cell_layout);

  return NULL;
}

/* Attribute parsing */
Johan Dahlin's avatar
Johan Dahlin committed
609
610
611
typedef struct {
  GtkCellLayout   *cell_layout;
  GtkCellRenderer *renderer;
612
  GtkBuilder      *builder;
Benjamin Otte's avatar
Benjamin Otte committed
613
  char            *attr_name;
614
  GString         *string;
Johan Dahlin's avatar
Johan Dahlin committed
615
616
617
} AttributesSubParserData;

static void
618
attributes_start_element (GtkBuildableParseContext *context,
Benjamin Otte's avatar
Benjamin Otte committed
619
620
621
                          const char               *element_name,
                          const char              **names,
                          const char              **values,
622
623
                          gpointer                  user_data,
                          GError                  **error)
Johan Dahlin's avatar
Johan Dahlin committed
624
{
625
  AttributesSubParserData *data = (AttributesSubParserData*)user_data;
Johan Dahlin's avatar
Johan Dahlin committed
626
627

  if (strcmp (element_name, "attribute") == 0)
Matthias Clasen's avatar
Matthias Clasen committed
628
    {
Benjamin Otte's avatar
Benjamin Otte committed
629
      const char *name;
630
631
632
633
634
635
636
637
638
639
640
641
642

      if (!_gtk_builder_check_parent (data->builder, context, "attributes", error))
        return;

      if (!g_markup_collect_attributes (element_name, names, values, error,
                                        G_MARKUP_COLLECT_STRING, "name", &name,
                                        G_MARKUP_COLLECT_INVALID))
        {
          _gtk_builder_prefix_error (data->builder, context, error);
          return;
        }

      data->attr_name = g_strdup (name);
Matthias Clasen's avatar
Matthias Clasen committed
643
    }
Johan Dahlin's avatar
Johan Dahlin committed
644
  else if (strcmp (element_name, "attributes") == 0)
645
646
647
648
649
650
651
652
653
    {
      if (!_gtk_builder_check_parent (data->builder, context, "child", error))
        return;

      if (!g_markup_collect_attributes (element_name, names, values, error,
                                        G_MARKUP_COLLECT_INVALID, NULL, NULL,
                                        G_MARKUP_COLLECT_INVALID))
        _gtk_builder_prefix_error (data->builder, context, error);
    }
Johan Dahlin's avatar
Johan Dahlin committed
654
  else
655
656
657
658
659
660
661
662
    {
      _gtk_builder_error_unhandled_tag (data->builder, context,
                                        "GtkCellLayout", element_name,
                                        error);
    }
}

static void
663
attributes_text_element (GtkBuildableParseContext  *context,
Benjamin Otte's avatar
Benjamin Otte committed
664
                         const char                *text,
665
666
667
                         gsize                      text_len,
                         gpointer                   user_data,
                         GError                   **error)
668
669
670
671
672
{
  AttributesSubParserData *data = (AttributesSubParserData*)user_data;

  if (data->attr_name)
    g_string_append_len (data->string, text, text_len);
Johan Dahlin's avatar
Johan Dahlin committed
673
674
675
}

static void
676
attributes_end_element (GtkBuildableParseContext  *context,
Benjamin Otte's avatar
Benjamin Otte committed
677
                        const char                *element_name,
678
679
                        gpointer                   user_data,
                        GError                   **error)
Johan Dahlin's avatar
Johan Dahlin committed
680
{
681
682
683
684
  AttributesSubParserData *data = (AttributesSubParserData*)user_data;
  GValue val = G_VALUE_INIT;

  if (!data->attr_name)
Johan Dahlin's avatar
Johan Dahlin committed
685
    return;
686

687
  if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, data->string->str, &val, error))
688
    {
689
690
      _gtk_builder_prefix_error (data->builder, context, error);
       return;
691
692
    }

693
694
695
696
697
698
699
700
701
  gtk_cell_layout_add_attribute (data->cell_layout,
				 data->renderer,
				 data->attr_name,
                                 g_value_get_int (&val));

  g_free (data->attr_name);
  data->attr_name = NULL;

  g_string_set_size (data->string, 0);
Johan Dahlin's avatar
Johan Dahlin committed
702
703
}

704
static const GtkBuildableParser attributes_parser =
Johan Dahlin's avatar
Johan Dahlin committed
705
706
  {
    attributes_start_element,
707
708
    attributes_end_element,
    attributes_text_element
Johan Dahlin's avatar
Johan Dahlin committed
709
710
  };

711
712
713
714
715
716

/* Cell packing parsing */
static void
gtk_cell_layout_buildable_set_cell_property (GtkCellArea     *area,
					     GtkBuilder      *builder,
					     GtkCellRenderer *cell,
Benjamin Otte's avatar
Benjamin Otte committed
717
718
					     char            *name,
					     const char      *value)
719
720
{
  GParamSpec *pspec;
Javier Jardón's avatar
Javier Jardón committed
721
  GValue gvalue = G_VALUE_INIT;
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
  GError *error = NULL;

  pspec = gtk_cell_area_class_find_cell_property (GTK_CELL_AREA_GET_CLASS (area), name);
  if (!pspec)
    {
      g_warning ("%s does not have a property called %s",
		 g_type_name (G_OBJECT_TYPE (area)), name);
      return;
    }

  if (!gtk_builder_value_from_string (builder, pspec, value, &gvalue, &error))
    {
      g_warning ("Could not read property %s:%s with value %s of type %s: %s",
		 g_type_name (G_OBJECT_TYPE (area)),
		 name,
		 value,
		 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
		 error->message);
      g_error_free (error);
      return;
    }

  gtk_cell_area_cell_set_property (area, cell, name, &gvalue);
  g_value_unset (&gvalue);
}

typedef struct {
  GtkBuilder      *builder;
  GtkCellLayout   *cell_layout;
  GtkCellRenderer *renderer;
752
  GString         *string;
Benjamin Otte's avatar
Benjamin Otte committed
753
754
  char            *cell_prop_name;
  char            *context;
755
756
757
758
  gboolean         translatable;
} CellPackingSubParserData;

static void
759
cell_packing_start_element (GtkBuildableParseContext *context,
Benjamin Otte's avatar
Benjamin Otte committed
760
761
762
                            const char               *element_name,
                            const char              **names,
                            const char              **values,
763
764
                            gpointer                  user_data,
                            GError                  **error)
765
{
766
  CellPackingSubParserData *data = (CellPackingSubParserData*)user_data;
767
768
769

  if (strcmp (element_name, "property") == 0)
    {
Benjamin Otte's avatar
Benjamin Otte committed
770
      const char *name;
771
      gboolean translatable = FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
772
      const char *ctx = NULL;
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790

      if (!_gtk_builder_check_parent (data->builder, context, "cell-packing", error))
        return;

      if (!g_markup_collect_attributes (element_name, names, values, error,
                                        G_MARKUP_COLLECT_STRING, "name", &name,
                                        G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable,
                                        G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL,
                                        G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &ctx,
                                        G_MARKUP_COLLECT_INVALID))
       {
         _gtk_builder_prefix_error (data->builder, context, error);
         return;
       }

      data->cell_prop_name = g_strdup (name);
      data->translatable = translatable;
      data->context = g_strdup (ctx);
791
792
    }
  else if (strcmp (element_name, "cell-packing") == 0)
793
794
795
796
797
798
799
800
801
    {
      if (!_gtk_builder_check_parent (data->builder, context, "child", error))
        return;

      if (!g_markup_collect_attributes (element_name, names, values, error,
                                        G_MARKUP_COLLECT_INVALID, NULL, NULL,
                                        G_MARKUP_COLLECT_INVALID))
        _gtk_builder_prefix_error (data->builder, context, error);
    }
802
  else
803
804
805
806
807
    {
      _gtk_builder_error_unhandled_tag (data->builder, context,
                                        "GtkCellLayout", element_name,
                                        error);
    }
808
809
810
}

static void
811
cell_packing_text_element (GtkBuildableParseContext *context,
Benjamin Otte's avatar
Benjamin Otte committed
812
                           const char               *text,
813
814
815
                           gsize                     text_len,
                           gpointer                  user_data,
                           GError                  **error)
816
{
817
  CellPackingSubParserData *data = (CellPackingSubParserData*)user_data;
818

819
820
  if (data->cell_prop_name)
    g_string_append_len (data->string, text, text_len);
821
}
822

823
static void
824
cell_packing_end_element (GtkBuildableParseContext *context,
Benjamin Otte's avatar
Benjamin Otte committed
825
                          const char               *element_name,
826
827
                          gpointer                  user_data,
                          GError                  **error)
828
{
829
  CellPackingSubParserData *data = (CellPackingSubParserData*)user_data;
830
  GtkCellArea *area;
831

832
  area = gtk_cell_layout_get_area (data->cell_layout);
833

834
835
836
  if (area)
    {
      /* translate the string */
837
      if (data->string->len && data->translatable)
838
	{
Benjamin Otte's avatar
Benjamin Otte committed
839
840
	  const char *translated;
	  const char * domain;
841

842
	  domain = gtk_builder_get_translation_domain (data->builder);
843
844

	  translated = _gtk_builder_parser_translate (domain,
845
846
847
						      data->context,
						      data->string->str);
	  g_string_assign (data->string, translated);
848
	}
849

850
851
852
853
854
855
      if (data->cell_prop_name)
	gtk_cell_layout_buildable_set_cell_property (area,
						     data->builder,
						     data->renderer,
						     data->cell_prop_name,
						     data->string->str);
856
    }
857
858
  else
    g_warning ("%s does not have an internal GtkCellArea class and cannot apply child cell properties",
859
860
861
862
863
864
865
866
	       g_type_name (G_OBJECT_TYPE (data->cell_layout)));

  g_string_set_size (data->string, 0);
  g_free (data->cell_prop_name);
  g_free (data->context);
  data->cell_prop_name = NULL;
  data->context = NULL;
  data->translatable = FALSE;
867
868
}

869

870
static const GtkBuildableParser cell_packing_parser =
871
872
  {
    cell_packing_start_element,
873
    cell_packing_end_element,
874
    cell_packing_text_element
875
876
  };

Johan Dahlin's avatar
Johan Dahlin committed
877
gboolean
878
879
880
_gtk_cell_layout_buildable_custom_tag_start (GtkBuildable       *buildable,
                                             GtkBuilder         *builder,
                                             GObject            *child,
Benjamin Otte's avatar
Benjamin Otte committed
881
                                             const char         *tagname,
882
883
                                             GtkBuildableParser *parser,
                                             gpointer           *data)
Johan Dahlin's avatar
Johan Dahlin committed
884
{
885
886
  AttributesSubParserData  *attr_data;
  CellPackingSubParserData *packing_data;
Johan Dahlin's avatar
Johan Dahlin committed
887
888
889
890
891
892

  if (!child)
    return FALSE;

  if (strcmp (tagname, "attributes") == 0)
    {
893
894
895
      attr_data = g_slice_new0 (AttributesSubParserData);
      attr_data->cell_layout = GTK_CELL_LAYOUT (buildable);
      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 ("");
Johan Dahlin's avatar
Johan Dahlin committed
899
900

      *parser = attributes_parser;
901
      *data = attr_data;
902

903
904
905
906
907
      return TRUE;
    }
  else if (strcmp (tagname, "cell-packing") == 0)
    {
      packing_data = g_slice_new0 (CellPackingSubParserData);
908
      packing_data->string = g_string_new ("");
909
910
911
912
913
914
      packing_data->builder = builder;
      packing_data->cell_layout = GTK_CELL_LAYOUT (buildable);
      packing_data->renderer = GTK_CELL_RENDERER (child);

      *parser = cell_packing_parser;
      *data = packing_data;
915

Johan Dahlin's avatar
Johan Dahlin committed
916
917
918
919
920
921
      return TRUE;
    }

  return FALSE;
}

922
gboolean
Johan Dahlin's avatar
Johan Dahlin committed
923
924
925
_gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable,
					   GtkBuilder   *builder,
					   GObject      *child,
Benjamin Otte's avatar
Benjamin Otte committed
926
					   const char   *tagname,
Johan Dahlin's avatar
Johan Dahlin committed
927
928
					   gpointer     *data)
{
929
  AttributesSubParserData *attr_data;
930
  CellPackingSubParserData *packing_data;
Johan Dahlin's avatar
Johan Dahlin committed
931

932
933
934
935
  if (strcmp (tagname, "attributes") == 0)
    {
      attr_data = (AttributesSubParserData*)data;
      g_assert (!attr_data->attr_name);
936
      g_string_free (attr_data->string, TRUE);
937
      g_slice_free (AttributesSubParserData, attr_data);
938

939
      return TRUE;
940
941
942
    }
  else if (strcmp (tagname, "cell-packing") == 0)
    {
943
944
945
      packing_data = (CellPackingSubParserData *)data;
      g_string_free (packing_data->string, TRUE);
      g_slice_free (CellPackingSubParserData, packing_data);
946

947
      return TRUE;
948
    }
949
  return FALSE;
Johan Dahlin's avatar
Johan Dahlin committed
950
951
952
}

void
953
954
955
_gtk_cell_layout_buildable_add_child (GtkBuildable      *buildable,
				      GtkBuilder        *builder,
				      GObject           *child,
Benjamin Otte's avatar
Benjamin Otte committed
956
				      const char        *type)
Johan Dahlin's avatar
Johan Dahlin committed
957
958
959
960
{
  g_return_if_fail (GTK_IS_CELL_LAYOUT (buildable));
  g_return_if_fail (GTK_IS_CELL_RENDERER (child));

961
  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (buildable), GTK_CELL_RENDERER (child), FALSE);
Johan Dahlin's avatar
Johan Dahlin committed
962
}