]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellview.c
cellrenderer: Merge GtkCellSizeRequest into GtkCellRenderer
[~andy/gtk] / gtk / gtkcellview.c
1 /* gtkellview.c
2  * Copyright (C) 2002, 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 #include "config.h"
21 #include <string.h>
22 #include "gtkcellview.h"
23 #include "gtkcelllayout.h"
24 #include "gtkintl.h"
25 #include "gtkcellrenderertext.h"
26 #include "gtkcellrendererpixbuf.h"
27 #include "gtkprivate.h"
28 #include "gtksizerequest.h"
29 #include <gobject/gmarshal.h>
30 #include "gtkbuildable.h"
31
32
33 typedef struct _GtkCellViewCellInfo GtkCellViewCellInfo;
34 struct _GtkCellViewCellInfo
35 {
36   GtkCellRenderer *cell;
37
38   gint requested_width;
39   gint natural_width;
40   gint real_width;
41   guint expand : 1;
42   guint pack : 1;
43
44   GSList *attributes;
45
46   GtkCellLayoutDataFunc func;
47   gpointer func_data;
48   GDestroyNotify destroy;
49 };
50
51 struct _GtkCellViewPrivate
52 {
53   GtkTreeModel *model;
54   GtkTreeRowReference *displayed_row;
55   GList *cell_list;
56   gint spacing;
57
58   GdkColor background;
59   gboolean background_set;
60 };
61
62
63 static void        gtk_cell_view_cell_layout_init         (GtkCellLayoutIface *iface);
64 static void        gtk_cell_view_get_property             (GObject           *object,
65                                                            guint             param_id,
66                                                            GValue           *value,
67                                                            GParamSpec       *pspec);
68 static void        gtk_cell_view_set_property             (GObject          *object,
69                                                            guint             param_id,
70                                                            const GValue     *value,
71                                                            GParamSpec       *pspec);
72 static void        gtk_cell_view_finalize                 (GObject          *object);
73 static void        gtk_cell_view_size_allocate            (GtkWidget        *widget,
74                                                            GtkAllocation    *allocation);
75 static gboolean    gtk_cell_view_draw                     (GtkWidget        *widget,
76                                                            cairo_t          *cr);
77 static void        gtk_cell_view_set_value                (GtkCellView     *cell_view,
78                                                            GtkCellRenderer *renderer,
79                                                            gchar           *property,
80                                                            GValue          *value);
81 static GtkCellViewCellInfo *gtk_cell_view_get_cell_info   (GtkCellView      *cellview,
82                                                            GtkCellRenderer  *renderer);
83 static void        gtk_cell_view_set_cell_data            (GtkCellView      *cell_view);
84
85
86 static void        gtk_cell_view_cell_layout_pack_start        (GtkCellLayout         *layout,
87                                                                 GtkCellRenderer       *renderer,
88                                                                 gboolean               expand);
89 static void        gtk_cell_view_cell_layout_pack_end          (GtkCellLayout         *layout,
90                                                                 GtkCellRenderer       *renderer,
91                                                                 gboolean               expand);
92 static void        gtk_cell_view_cell_layout_add_attribute     (GtkCellLayout         *layout,
93                                                                 GtkCellRenderer       *renderer,
94                                                                 const gchar           *attribute,
95                                                                 gint                   column);
96 static void       gtk_cell_view_cell_layout_clear              (GtkCellLayout         *layout);
97 static void       gtk_cell_view_cell_layout_clear_attributes   (GtkCellLayout         *layout,
98                                                                 GtkCellRenderer       *renderer);
99 static void       gtk_cell_view_cell_layout_set_cell_data_func (GtkCellLayout         *layout,
100                                                                 GtkCellRenderer       *cell,
101                                                                 GtkCellLayoutDataFunc  func,
102                                                                 gpointer               func_data,
103                                                                 GDestroyNotify         destroy);
104 static void       gtk_cell_view_cell_layout_reorder            (GtkCellLayout         *layout,
105                                                                 GtkCellRenderer       *cell,
106                                                                 gint                   position);
107 static GList *    gtk_cell_view_cell_layout_get_cells          (GtkCellLayout         *layout);
108
109 /* buildable */
110 static void       gtk_cell_view_buildable_init                 (GtkBuildableIface     *iface);
111 static gboolean   gtk_cell_view_buildable_custom_tag_start     (GtkBuildable          *buildable,
112                                                                 GtkBuilder            *builder,
113                                                                 GObject               *child,
114                                                                 const gchar           *tagname,
115                                                                 GMarkupParser         *parser,
116                                                                 gpointer              *data);
117 static void       gtk_cell_view_buildable_custom_tag_end       (GtkBuildable          *buildable,
118                                                                 GtkBuilder            *builder,
119                                                                 GObject               *child,
120                                                                 const gchar           *tagname,
121                                                                 gpointer              *data);
122
123 static void       gtk_cell_view_get_preferred_width            (GtkWidget             *widget,
124                                                                 gint                  *minimum_size,
125                                                                 gint                  *natural_size);
126 static void       gtk_cell_view_get_preferred_height           (GtkWidget             *widget,
127                                                                 gint                  *minimum_size,
128                                                                 gint                  *natural_size);
129 static void       gtk_cell_view_get_preferred_width_for_height (GtkWidget             *widget,
130                                                                 gint                   avail_size,
131                                                                 gint                  *minimum_size,
132                                                                 gint                  *natural_size);
133 static void       gtk_cell_view_get_preferred_height_for_width (GtkWidget             *widget,
134                                                                 gint                   avail_size,
135                                                                 gint                  *minimum_size,
136                                                                 gint                  *natural_size);
137
138 static GtkBuildableIface *parent_buildable_iface;
139
140
141 enum
142 {
143   PROP_0,
144   PROP_BACKGROUND,
145   PROP_BACKGROUND_GDK,
146   PROP_BACKGROUND_SET,
147   PROP_MODEL
148 };
149
150 G_DEFINE_TYPE_WITH_CODE (GtkCellView, gtk_cell_view, GTK_TYPE_WIDGET, 
151                          G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
152                                                 gtk_cell_view_cell_layout_init)
153                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
154                                                 gtk_cell_view_buildable_init))
155
156
157 static void
158 gtk_cell_view_class_init (GtkCellViewClass *klass)
159 {
160   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
161   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
162
163   gobject_class->get_property = gtk_cell_view_get_property;
164   gobject_class->set_property = gtk_cell_view_set_property;
165   gobject_class->finalize = gtk_cell_view_finalize;
166
167   widget_class->draw                           = gtk_cell_view_draw;
168   widget_class->size_allocate                  = gtk_cell_view_size_allocate;
169   widget_class->get_preferred_width            = gtk_cell_view_get_preferred_width;
170   widget_class->get_preferred_height           = gtk_cell_view_get_preferred_height;
171   widget_class->get_preferred_width_for_height = gtk_cell_view_get_preferred_width_for_height;
172   widget_class->get_preferred_height_for_width = gtk_cell_view_get_preferred_height_for_width;
173
174   /* properties */
175   g_object_class_install_property (gobject_class,
176                                    PROP_BACKGROUND,
177                                    g_param_spec_string ("background",
178                                                         P_("Background color name"),
179                                                         P_("Background color as a string"),
180                                                         NULL,
181                                                         GTK_PARAM_WRITABLE));
182   g_object_class_install_property (gobject_class,
183                                    PROP_BACKGROUND_GDK,
184                                    g_param_spec_boxed ("background-gdk",
185                                                       P_("Background color"),
186                                                       P_("Background color as a GdkColor"),
187                                                       GDK_TYPE_COLOR,
188                                                       GTK_PARAM_READWRITE));
189
190   /**
191    * GtkCellView:model
192    *
193    * The model for cell view
194    *
195    * since 2.10
196    */
197   g_object_class_install_property (gobject_class,
198                                    PROP_MODEL,
199                                    g_param_spec_object  ("model",
200                                                          P_("CellView model"),
201                                                          P_("The model for cell view"),
202                                                          GTK_TYPE_TREE_MODEL,
203                                                          GTK_PARAM_READWRITE));
204   
205 #define ADD_SET_PROP(propname, propval, nick, blurb) g_object_class_install_property (gobject_class, propval, g_param_spec_boolean (propname, nick, blurb, FALSE, GTK_PARAM_READWRITE))
206
207   ADD_SET_PROP ("background-set", PROP_BACKGROUND_SET,
208                 P_("Background set"),
209                 P_("Whether this tag affects the background color"));
210
211   g_type_class_add_private (gobject_class, sizeof (GtkCellViewPrivate));
212 }
213
214 static void
215 gtk_cell_view_buildable_init (GtkBuildableIface *iface)
216 {
217   parent_buildable_iface = g_type_interface_peek_parent (iface);
218   iface->add_child = _gtk_cell_layout_buildable_add_child;
219   iface->custom_tag_start = gtk_cell_view_buildable_custom_tag_start;
220   iface->custom_tag_end = gtk_cell_view_buildable_custom_tag_end;
221 }
222
223 static void
224 gtk_cell_view_cell_layout_init (GtkCellLayoutIface *iface)
225 {
226   iface->pack_start = gtk_cell_view_cell_layout_pack_start;
227   iface->pack_end = gtk_cell_view_cell_layout_pack_end;
228   iface->clear = gtk_cell_view_cell_layout_clear;
229   iface->add_attribute = gtk_cell_view_cell_layout_add_attribute;
230   iface->set_cell_data_func = gtk_cell_view_cell_layout_set_cell_data_func;
231   iface->clear_attributes = gtk_cell_view_cell_layout_clear_attributes;
232   iface->reorder = gtk_cell_view_cell_layout_reorder;
233   iface->get_cells = gtk_cell_view_cell_layout_get_cells;
234 }
235
236 static void
237 gtk_cell_view_get_property (GObject    *object,
238                             guint       param_id,
239                             GValue     *value,
240                             GParamSpec *pspec)
241 {
242   GtkCellView *view = GTK_CELL_VIEW (object);
243
244   switch (param_id)
245     {
246       case PROP_BACKGROUND_GDK:
247         {
248           GdkColor color;
249
250           color = view->priv->background;
251
252           g_value_set_boxed (value, &color);
253         }
254         break;
255       case PROP_BACKGROUND_SET:
256         g_value_set_boolean (value, view->priv->background_set);
257         break;
258       case PROP_MODEL:
259         g_value_set_object (value, view->priv->model);
260         break;
261       default:
262         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
263         break;
264     }
265 }
266
267 static void
268 gtk_cell_view_set_property (GObject      *object,
269                             guint         param_id,
270                             const GValue *value,
271                             GParamSpec   *pspec)
272 {
273   GtkCellView *view = GTK_CELL_VIEW (object);
274
275   switch (param_id)
276     {
277       case PROP_BACKGROUND:
278         {
279           GdkColor color;
280
281           if (!g_value_get_string (value))
282             gtk_cell_view_set_background_color (view, NULL);
283           else if (gdk_color_parse (g_value_get_string (value), &color))
284             gtk_cell_view_set_background_color (view, &color);
285           else
286             g_warning ("Don't know color `%s'", g_value_get_string (value));
287
288           g_object_notify (object, "background-gdk");
289         }
290         break;
291       case PROP_BACKGROUND_GDK:
292         gtk_cell_view_set_background_color (view, g_value_get_boxed (value));
293         break;
294       case PROP_BACKGROUND_SET:
295         view->priv->background_set = g_value_get_boolean (value);
296         break;
297       case PROP_MODEL:
298         gtk_cell_view_set_model (view, g_value_get_object (value));
299         break;
300     default:
301         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
302         break;
303     }
304 }
305
306 static void
307 gtk_cell_view_init (GtkCellView *cellview)
308 {
309   GtkCellViewPrivate *priv;
310
311   cellview->priv = G_TYPE_INSTANCE_GET_PRIVATE (cellview,
312                                                 GTK_TYPE_CELL_VIEW,
313                                                 GtkCellViewPrivate);
314   priv = cellview->priv;
315
316   gtk_widget_set_has_window (GTK_WIDGET (cellview), FALSE);
317 }
318
319 static void
320 gtk_cell_view_finalize (GObject *object)
321 {
322   GtkCellView *cellview = GTK_CELL_VIEW (object);
323
324   gtk_cell_view_cell_layout_clear (GTK_CELL_LAYOUT (cellview));
325
326   if (cellview->priv->model)
327      g_object_unref (cellview->priv->model);
328
329   if (cellview->priv->displayed_row)
330      gtk_tree_row_reference_free (cellview->priv->displayed_row);
331
332   G_OBJECT_CLASS (gtk_cell_view_parent_class)->finalize (object);
333 }
334
335 static void
336 gtk_cell_view_size_allocate (GtkWidget     *widget,
337                              GtkAllocation *allocation)
338 {
339   GtkCellView      *cellview;
340   GtkRequestedSize *sizes;
341   GList            *list;
342   gint              n_visible_cells, n_expand_cells;
343   gint              avail_width = 0;
344   gint              extra_per_cell, extra_extra, i;
345   gboolean          first_cell = TRUE;
346
347   gtk_widget_set_allocation (widget, allocation);
348
349   cellview = GTK_CELL_VIEW (widget);
350
351   avail_width = allocation->width;
352
353   /* Count visible/expand children */
354   for (n_visible_cells = 0, n_expand_cells = 0, list = cellview->priv->cell_list; 
355        list; list = list->next)
356     {
357       GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
358
359       n_visible_cells++;
360
361       if (info->expand)
362         n_expand_cells++;
363     }
364
365   sizes = g_new0 (GtkRequestedSize, n_visible_cells);
366
367   /* checking how much extra space we have */
368   for (i = 0, list = cellview->priv->cell_list; list; list = list->next)
369     {
370       GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
371
372       if (!gtk_cell_renderer_get_visible (info->cell))
373         continue;
374
375       sizes[i].data = info;
376       sizes[i].minimum_size = info->requested_width;
377       sizes[i].natural_size = info->natural_width;
378
379       if (!first_cell)
380         avail_width -= cellview->priv->spacing;
381
382       avail_width -= sizes[i].minimum_size;
383
384       first_cell = FALSE;
385
386       i++;
387     }
388
389   avail_width = gtk_distribute_natural_allocation (MAX (0, avail_width), n_visible_cells, sizes);
390
391   /* Deal with any expand space... */
392   if (n_expand_cells > 0)
393     {
394       extra_per_cell = avail_width / n_expand_cells;
395       extra_extra    = avail_width % n_expand_cells;
396     }
397   else
398     /* Everything just left-aligned if no cells expand */
399     extra_per_cell = extra_extra = 0;
400
401   for (i = 0, list = cellview->priv->cell_list; list; list = list->next)
402     {
403       GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
404
405       if (!gtk_cell_renderer_get_visible (info->cell))
406         continue;
407
408       info->real_width = sizes[i].minimum_size;
409
410       if (info->expand)
411         {
412           info->real_width += extra_per_cell;
413
414           if (extra_extra)
415             {
416               info->real_width++;
417               extra_extra--;
418             }
419         }
420       
421       /* increment index into sizes for visible children */
422       i++;
423     }
424
425   g_free (sizes);
426 }
427
428 static gboolean
429 gtk_cell_view_draw (GtkWidget *widget,
430                     cairo_t   *cr)
431 {
432   GList *list;
433   GtkCellView *cellview;
434   GdkRectangle area;
435   GtkCellRendererState state;
436   gboolean rtl = (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL);
437   GtkPackType packing;
438   int width;
439
440   cellview = GTK_CELL_VIEW (widget);
441
442   /* render cells */
443   area.x = 0;
444   area.y = 0;
445   area.width = width = gtk_widget_get_allocated_width (widget);
446   area.height = gtk_widget_get_allocated_height (widget);
447
448   /* "blank" background */
449   if (cellview->priv->background_set)
450     {
451       gdk_cairo_rectangle (cr, &area);
452       gdk_cairo_set_source_color (cr, &cellview->priv->background);
453       cairo_fill (cr);
454     }
455
456   /* set cell data (if available) */
457   if (cellview->priv->displayed_row)
458     gtk_cell_view_set_cell_data (cellview);
459   else if (cellview->priv->model)
460     return FALSE;
461
462   if (gtk_widget_get_state (widget) == GTK_STATE_PRELIGHT)
463     state = GTK_CELL_RENDERER_PRELIT;
464   else if (gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE)
465     state = GTK_CELL_RENDERER_INSENSITIVE;
466   else
467     state = 0;
468       
469   for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
470     {
471       if (packing == GTK_PACK_START)
472         area.x = rtl ? width : 0;
473       else
474         area.x = rtl ? 0 : width;
475
476       for (list = cellview->priv->cell_list; list; list = list->next)
477         {
478           GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
479
480           if (info->pack != packing)
481             continue;
482
483           if (!gtk_cell_renderer_get_visible (info->cell))
484             continue;
485
486           area.width = info->real_width;
487
488           if ((packing == GTK_PACK_START && rtl) ||
489               (packing == GTK_PACK_END && !rtl))
490             area.x -= area.width;
491
492           gtk_cell_renderer_render (info->cell,
493                                     cr,
494                                     widget,
495                                     /* FIXME! */
496                                     &area, &area, state);
497
498           if ((packing == GTK_PACK_START && !rtl) ||
499               (packing == GTK_PACK_END && rtl))
500             {
501               area.x += area.width;
502               area.x += cellview->priv->spacing;
503             }
504           else
505             area.x -= cellview->priv->spacing;
506         }
507     }
508
509   return FALSE;
510 }
511
512 static GtkCellViewCellInfo *
513 gtk_cell_view_get_cell_info (GtkCellView     *cellview,
514                              GtkCellRenderer *renderer)
515 {
516   GList *i;
517
518   for (i = cellview->priv->cell_list; i; i = i->next)
519     {
520       GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
521
522       if (info->cell == renderer)
523         return info;
524     }
525
526   return NULL;
527 }
528
529 static void
530 gtk_cell_view_set_cell_data (GtkCellView *cell_view)
531 {
532   GList *i;
533   GtkTreeIter iter;
534   GtkTreePath *path;
535
536   g_return_if_fail (cell_view->priv->displayed_row != NULL);
537
538   path = gtk_tree_row_reference_get_path (cell_view->priv->displayed_row);
539   if (!path)
540     return;
541
542   gtk_tree_model_get_iter (cell_view->priv->model, &iter, path);
543   gtk_tree_path_free (path);
544
545   for (i = cell_view->priv->cell_list; i; i = i->next)
546     {
547       GSList *j;
548       GtkCellViewCellInfo *info = i->data;
549
550       g_object_freeze_notify (G_OBJECT (info->cell));
551
552       for (j = info->attributes; j && j->next; j = j->next->next)
553         {
554           gchar *property = j->data;
555           gint column = GPOINTER_TO_INT (j->next->data);
556           GValue value = {0, };
557
558           gtk_tree_model_get_value (cell_view->priv->model, &iter,
559                                     column, &value);
560           g_object_set_property (G_OBJECT (info->cell),
561                                  property, &value);
562           g_value_unset (&value);
563         }
564
565       if (info->func)
566         (* info->func) (GTK_CELL_LAYOUT (cell_view),
567                         info->cell,
568                         cell_view->priv->model,
569                         &iter,
570                         info->func_data);
571
572       g_object_thaw_notify (G_OBJECT (info->cell));
573     }
574 }
575
576 /* GtkCellLayout implementation */
577 static void
578 gtk_cell_view_cell_layout_pack_start (GtkCellLayout   *layout,
579                                       GtkCellRenderer *renderer,
580                                       gboolean         expand)
581 {
582   GtkCellViewCellInfo *info;
583   GtkCellView *cellview = GTK_CELL_VIEW (layout);
584
585   g_return_if_fail (!gtk_cell_view_get_cell_info (cellview, renderer));
586
587   g_object_ref_sink (renderer);
588
589   info = g_slice_new0 (GtkCellViewCellInfo);
590   info->cell = renderer;
591   info->expand = expand ? TRUE : FALSE;
592   info->pack = GTK_PACK_START;
593
594   cellview->priv->cell_list = g_list_append (cellview->priv->cell_list, info);
595
596   gtk_widget_queue_resize (GTK_WIDGET (cellview));
597 }
598
599 static void
600 gtk_cell_view_cell_layout_pack_end (GtkCellLayout   *layout,
601                                     GtkCellRenderer *renderer,
602                                     gboolean         expand)
603 {
604   GtkCellViewCellInfo *info;
605   GtkCellView *cellview = GTK_CELL_VIEW (layout);
606
607   g_return_if_fail (!gtk_cell_view_get_cell_info (cellview, renderer));
608
609   g_object_ref_sink (renderer);
610
611   info = g_slice_new0 (GtkCellViewCellInfo);
612   info->cell = renderer;
613   info->expand = expand ? TRUE : FALSE;
614   info->pack = GTK_PACK_END;
615
616   cellview->priv->cell_list = g_list_append (cellview->priv->cell_list, info);
617
618   gtk_widget_queue_resize (GTK_WIDGET (cellview));
619 }
620
621 static void
622 gtk_cell_view_cell_layout_add_attribute (GtkCellLayout   *layout,
623                                          GtkCellRenderer *renderer,
624                                          const gchar     *attribute,
625                                          gint             column)
626 {
627   GtkCellViewCellInfo *info;
628   GtkCellView *cellview = GTK_CELL_VIEW (layout);
629
630   info = gtk_cell_view_get_cell_info (cellview, renderer);
631   g_return_if_fail (info != NULL);
632
633   info->attributes = g_slist_prepend (info->attributes,
634                                       GINT_TO_POINTER (column));
635   info->attributes = g_slist_prepend (info->attributes,
636                                       g_strdup (attribute));
637 }
638
639 static void
640 gtk_cell_view_cell_layout_clear (GtkCellLayout *layout)
641 {
642   GtkCellView *cellview = GTK_CELL_VIEW (layout);
643
644   while (cellview->priv->cell_list)
645     {
646       GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)cellview->priv->cell_list->data;
647
648       gtk_cell_view_cell_layout_clear_attributes (layout, info->cell);
649       g_object_unref (info->cell);
650       g_slice_free (GtkCellViewCellInfo, info);
651       cellview->priv->cell_list = g_list_delete_link (cellview->priv->cell_list, 
652                                                       cellview->priv->cell_list);
653     }
654 }
655
656 static void
657 gtk_cell_view_cell_layout_set_cell_data_func (GtkCellLayout         *layout,
658                                               GtkCellRenderer       *cell,
659                                               GtkCellLayoutDataFunc  func,
660                                               gpointer               func_data,
661                                               GDestroyNotify         destroy)
662 {
663   GtkCellView *cellview = GTK_CELL_VIEW (layout);
664   GtkCellViewCellInfo *info;
665
666   info = gtk_cell_view_get_cell_info (cellview, cell);
667   g_return_if_fail (info != NULL);
668
669   if (info->destroy)
670     {
671       GDestroyNotify d = info->destroy;
672
673       info->destroy = NULL;
674       d (info->func_data);
675     }
676
677   info->func = func;
678   info->func_data = func_data;
679   info->destroy = destroy;
680 }
681
682 static void
683 gtk_cell_view_cell_layout_clear_attributes (GtkCellLayout   *layout,
684                                             GtkCellRenderer *renderer)
685 {
686   GtkCellView *cellview = GTK_CELL_VIEW (layout);
687   GtkCellViewCellInfo *info;
688   GSList *list;
689
690   info = gtk_cell_view_get_cell_info (cellview, renderer);
691   if (info != NULL)
692     {
693       list = info->attributes;
694       while (list && list->next)
695         {
696           g_free (list->data);
697           list = list->next->next;
698         }
699       
700       g_slist_free (info->attributes);
701       info->attributes = NULL;
702     }
703 }
704
705 static void
706 gtk_cell_view_cell_layout_reorder (GtkCellLayout   *layout,
707                                    GtkCellRenderer *cell,
708                                    gint             position)
709 {
710   GtkCellView *cellview = GTK_CELL_VIEW (layout);
711   GtkCellViewCellInfo *info;
712   GList *link;
713
714   info = gtk_cell_view_get_cell_info (cellview, cell);
715
716   g_return_if_fail (info != NULL);
717   g_return_if_fail (position >= 0);
718
719   link = g_list_find (cellview->priv->cell_list, info);
720
721   g_return_if_fail (link != NULL);
722
723   cellview->priv->cell_list = g_list_delete_link (cellview->priv->cell_list,
724                                                   link);
725   cellview->priv->cell_list = g_list_insert (cellview->priv->cell_list,
726                                              info, position);
727
728   gtk_widget_queue_draw (GTK_WIDGET (cellview));
729 }
730
731 /**
732  * gtk_cell_view_new:
733  *
734  * Creates a new #GtkCellView widget.
735  *
736  * Return value: A newly created #GtkCellView widget.
737  *
738  * Since: 2.6
739  */
740 GtkWidget *
741 gtk_cell_view_new (void)
742 {
743   GtkCellView *cellview;
744
745   cellview = g_object_new (gtk_cell_view_get_type (), NULL);
746
747   return GTK_WIDGET (cellview);
748 }
749
750 /**
751  * gtk_cell_view_new_with_text:
752  * @text: the text to display in the cell view
753  *
754  * Creates a new #GtkCellView widget, adds a #GtkCellRendererText 
755  * to it, and makes its show @text.
756  *
757  * Return value: A newly created #GtkCellView widget.
758  *
759  * Since: 2.6
760  */
761 GtkWidget *
762 gtk_cell_view_new_with_text (const gchar *text)
763 {
764   GtkCellView *cellview;
765   GtkCellRenderer *renderer;
766   GValue value = {0, };
767
768   cellview = GTK_CELL_VIEW (gtk_cell_view_new ());
769
770   renderer = gtk_cell_renderer_text_new ();
771   gtk_cell_view_cell_layout_pack_start (GTK_CELL_LAYOUT (cellview),
772                                         renderer, TRUE);
773
774   g_value_init (&value, G_TYPE_STRING);
775   g_value_set_string (&value, text);
776   gtk_cell_view_set_value (cellview, renderer, "text", &value);
777   g_value_unset (&value);
778
779   return GTK_WIDGET (cellview);
780 }
781
782 /**
783  * gtk_cell_view_new_with_markup:
784  * @markup: the text to display in the cell view
785  *
786  * Creates a new #GtkCellView widget, adds a #GtkCellRendererText 
787  * to it, and makes it show @markup. The text can be
788  * marked up with the <link linkend="PangoMarkupFormat">Pango text 
789  * markup language</link>.
790  *
791  * Return value: A newly created #GtkCellView widget.
792  *
793  * Since: 2.6
794  */
795 GtkWidget *
796 gtk_cell_view_new_with_markup (const gchar *markup)
797 {
798   GtkCellView *cellview;
799   GtkCellRenderer *renderer;
800   GValue value = {0, };
801
802   cellview = GTK_CELL_VIEW (gtk_cell_view_new ());
803
804   renderer = gtk_cell_renderer_text_new ();
805   gtk_cell_view_cell_layout_pack_start (GTK_CELL_LAYOUT (cellview),
806                                         renderer, TRUE);
807
808   g_value_init (&value, G_TYPE_STRING);
809   g_value_set_string (&value, markup);
810   gtk_cell_view_set_value (cellview, renderer, "markup", &value);
811   g_value_unset (&value);
812
813   return GTK_WIDGET (cellview);
814 }
815
816 /**
817  * gtk_cell_view_new_with_pixbuf:
818  * @pixbuf: the image to display in the cell view
819  *
820  * Creates a new #GtkCellView widget, adds a #GtkCellRendererPixbuf 
821  * to it, and makes its show @pixbuf. 
822  *
823  * Return value: A newly created #GtkCellView widget.
824  *
825  * Since: 2.6
826  */
827 GtkWidget *
828 gtk_cell_view_new_with_pixbuf (GdkPixbuf *pixbuf)
829 {
830   GtkCellView *cellview;
831   GtkCellRenderer *renderer;
832   GValue value = {0, };
833
834   cellview = GTK_CELL_VIEW (gtk_cell_view_new ());
835
836   renderer = gtk_cell_renderer_pixbuf_new ();
837   gtk_cell_view_cell_layout_pack_start (GTK_CELL_LAYOUT (cellview),
838                                         renderer, TRUE);
839
840   g_value_init (&value, GDK_TYPE_PIXBUF);
841   g_value_set_object (&value, pixbuf);
842   gtk_cell_view_set_value (cellview, renderer, "pixbuf", &value);
843   g_value_unset (&value);
844
845   return GTK_WIDGET (cellview);
846 }
847
848 /**
849  * gtk_cell_view_set_value:
850  * @cell_view: a #GtkCellView widget
851  * @renderer: one of the renderers of @cell_view
852  * @property: the name of the property of @renderer to set
853  * @value: the new value to set the property to
854  * 
855  * Sets a property of a cell renderer of @cell_view, and
856  * makes sure the display of @cell_view is updated.
857  *
858  * Since: 2.6
859  */
860 static void
861 gtk_cell_view_set_value (GtkCellView     *cell_view,
862                          GtkCellRenderer *renderer,
863                          gchar           *property,
864                          GValue          *value)
865 {
866   g_object_set_property (G_OBJECT (renderer), property, value);
867
868   /* force resize and redraw */
869   gtk_widget_queue_resize (GTK_WIDGET (cell_view));
870   gtk_widget_queue_draw (GTK_WIDGET (cell_view));
871 }
872
873 /**
874  * gtk_cell_view_set_model:
875  * @cell_view: a #GtkCellView
876  * @model: (allow-none): a #GtkTreeModel
877  *
878  * Sets the model for @cell_view.  If @cell_view already has a model
879  * set, it will remove it before setting the new model.  If @model is
880  * %NULL, then it will unset the old model.
881  *
882  * Since: 2.6
883  */
884 void
885 gtk_cell_view_set_model (GtkCellView  *cell_view,
886                          GtkTreeModel *model)
887 {
888   g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
889   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
890
891   if (cell_view->priv->model)
892     {
893       if (cell_view->priv->displayed_row)
894         gtk_tree_row_reference_free (cell_view->priv->displayed_row);
895       cell_view->priv->displayed_row = NULL;
896
897       g_object_unref (cell_view->priv->model);
898       cell_view->priv->model = NULL;
899     }
900
901   cell_view->priv->model = model;
902
903   if (cell_view->priv->model)
904     g_object_ref (cell_view->priv->model);
905 }
906
907 /**
908  * gtk_cell_view_get_model:
909  * @cell_view: a #GtkCellView
910  *
911  * Returns the model for @cell_view. If no model is used %NULL is
912  * returned.
913  *
914  * Returns: (transfer none): a #GtkTreeModel used or %NULL
915  *
916  * Since: 2.16
917  **/
918 GtkTreeModel *
919 gtk_cell_view_get_model (GtkCellView *cell_view)
920 {
921   g_return_val_if_fail (GTK_IS_CELL_VIEW (cell_view), NULL);
922
923   return cell_view->priv->model;
924 }
925
926 /**
927  * gtk_cell_view_set_displayed_row:
928  * @cell_view: a #GtkCellView
929  * @path: (allow-none): a #GtkTreePath or %NULL to unset.
930  *
931  * Sets the row of the model that is currently displayed
932  * by the #GtkCellView. If the path is unset, then the
933  * contents of the cellview "stick" at their last value;
934  * this is not normally a desired result, but may be
935  * a needed intermediate state if say, the model for
936  * the #GtkCellView becomes temporarily empty.
937  *
938  * Since: 2.6
939  **/
940 void
941 gtk_cell_view_set_displayed_row (GtkCellView *cell_view,
942                                  GtkTreePath *path)
943 {
944   g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
945   g_return_if_fail (GTK_IS_TREE_MODEL (cell_view->priv->model));
946
947   if (cell_view->priv->displayed_row)
948     gtk_tree_row_reference_free (cell_view->priv->displayed_row);
949
950   if (path)
951     {
952       cell_view->priv->displayed_row =
953         gtk_tree_row_reference_new (cell_view->priv->model, path);
954     }
955   else
956     cell_view->priv->displayed_row = NULL;
957
958   /* force resize and redraw */
959   gtk_widget_queue_resize (GTK_WIDGET (cell_view));
960   gtk_widget_queue_draw (GTK_WIDGET (cell_view));
961 }
962
963 /**
964  * gtk_cell_view_get_displayed_row:
965  * @cell_view: a #GtkCellView
966  *
967  * Returns a #GtkTreePath referring to the currently 
968  * displayed row. If no row is currently displayed, 
969  * %NULL is returned.
970  *
971  * Returns: the currently displayed row or %NULL
972  *
973  * Since: 2.6
974  */
975 GtkTreePath *
976 gtk_cell_view_get_displayed_row (GtkCellView *cell_view)
977 {
978   g_return_val_if_fail (GTK_IS_CELL_VIEW (cell_view), NULL);
979
980   if (!cell_view->priv->displayed_row)
981     return NULL;
982
983   return gtk_tree_row_reference_get_path (cell_view->priv->displayed_row);
984 }
985
986 /**
987  * gtk_cell_view_get_size_of_row:
988  * @cell_view: a #GtkCellView
989  * @path: a #GtkTreePath 
990  * @requisition: return location for the size 
991  *
992  * Sets @requisition to the size needed by @cell_view to display 
993  * the model row pointed to by @path.
994  * 
995  * Return value: %TRUE
996  *
997  * Since: 2.6
998  * 
999  * Deprecated: 3.0: Use gtk_cell_view_get_desired_width_of_row() and
1000  * gtk_cell_view_get_desired_height_for_width_of_row() instead.
1001  */
1002 gboolean
1003 gtk_cell_view_get_size_of_row (GtkCellView    *cell_view,
1004                                GtkTreePath    *path,
1005                                GtkRequisition *requisition)
1006 {
1007   GtkRequisition req;
1008
1009   g_return_val_if_fail (GTK_IS_CELL_VIEW (cell_view), FALSE);
1010   g_return_val_if_fail (path != NULL, FALSE);
1011
1012   /* Return the minimum height for the minimum width */
1013   gtk_cell_view_get_desired_width_of_row (cell_view, path, &req.width, NULL);
1014   gtk_cell_view_get_desired_height_for_width_of_row (cell_view, path, req.width, &req.height, NULL);
1015
1016   if (requisition)
1017     *requisition = req;
1018
1019   return TRUE;
1020 }
1021
1022
1023 /**
1024  * gtk_cell_view_get_desired_width_of_row:
1025  * @cell_view: a #GtkCellView
1026  * @path: a #GtkTreePath 
1027  * @minimum_size: location to store the minimum size 
1028  * @natural_size: location to store the natural size 
1029  *
1030  * Sets @minimum_size and @natural_size to the width desired by @cell_view 
1031  * to display the model row pointed to by @path.
1032  * 
1033  * Since: 3.0
1034  */
1035 void
1036 gtk_cell_view_get_desired_width_of_row (GtkCellView     *cell_view,
1037                                         GtkTreePath     *path,
1038                                         gint            *minimum_size,
1039                                         gint            *natural_size)
1040 {
1041   GtkTreeRowReference *tmp;
1042
1043   g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
1044   g_return_if_fail (path != NULL);
1045   g_return_if_fail (minimum_size != NULL || natural_size != NULL);
1046
1047   tmp = cell_view->priv->displayed_row;
1048   cell_view->priv->displayed_row =
1049     gtk_tree_row_reference_new (cell_view->priv->model, path);
1050
1051   gtk_cell_view_get_preferred_width (GTK_WIDGET (cell_view), minimum_size, natural_size);
1052
1053   gtk_tree_row_reference_free (cell_view->priv->displayed_row);
1054   cell_view->priv->displayed_row = tmp;
1055
1056   /* Restore active size (this will restore the cellrenderer info->width/requested_width's) */
1057   gtk_cell_view_get_preferred_width (GTK_WIDGET (cell_view), NULL, NULL);
1058 }
1059
1060
1061 /**
1062  * gtk_cell_view_get_desired_height_for_width_of_row:
1063  * @cell_view: a #GtkCellView
1064  * @path: a #GtkTreePath 
1065  * @avail_size: available width
1066  * @minimum_size: location to store the minimum height 
1067  * @natural_size: location to store the natural height
1068  *
1069  * Sets @minimum_size and @natural_size to the height desired by @cell_view 
1070  * if it were allocated a width of @avail_size to display the model row 
1071  * pointed to by @path.
1072  * 
1073  * Since: 3.0
1074  */
1075 void
1076 gtk_cell_view_get_desired_height_for_width_of_row (GtkCellView     *cell_view,
1077                                                    GtkTreePath     *path,
1078                                                    gint             avail_size,
1079                                                    gint            *minimum_size,
1080                                                    gint            *natural_size)
1081 {
1082   GtkTreeRowReference *tmp;
1083
1084   g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
1085   g_return_if_fail (path != NULL);
1086   g_return_if_fail (minimum_size != NULL || natural_size != NULL);
1087
1088   tmp = cell_view->priv->displayed_row;
1089   cell_view->priv->displayed_row =
1090     gtk_tree_row_reference_new (cell_view->priv->model, path);
1091
1092   /* Then get the collective height_for_width based on the cached values */
1093   gtk_cell_view_get_preferred_height_for_width (GTK_WIDGET (cell_view), avail_size, minimum_size, natural_size);
1094
1095   gtk_tree_row_reference_free (cell_view->priv->displayed_row);
1096   cell_view->priv->displayed_row = tmp;
1097
1098   /* Restore active size (this will restore the cellrenderer info->width/requested_width's) */
1099   gtk_cell_view_get_preferred_width (GTK_WIDGET (cell_view), NULL, NULL);
1100 }
1101
1102 /**
1103  * gtk_cell_view_set_background_color:
1104  * @cell_view: a #GtkCellView
1105  * @color: the new background color
1106  *
1107  * Sets the background color of @view.
1108  *
1109  * Since: 2.6
1110  */
1111 void
1112 gtk_cell_view_set_background_color (GtkCellView    *cell_view,
1113                                     const GdkColor *color)
1114 {
1115   g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
1116
1117   if (color)
1118     {
1119       if (!cell_view->priv->background_set)
1120         {
1121           cell_view->priv->background_set = TRUE;
1122           g_object_notify (G_OBJECT (cell_view), "background-set");
1123         }
1124
1125       cell_view->priv->background = *color;
1126     }
1127   else
1128     {
1129       if (cell_view->priv->background_set)
1130         {
1131           cell_view->priv->background_set = FALSE;
1132           g_object_notify (G_OBJECT (cell_view), "background-set");
1133         }
1134     }
1135
1136   gtk_widget_queue_draw (GTK_WIDGET (cell_view));
1137 }
1138
1139 static GList *
1140 gtk_cell_view_cell_layout_get_cells (GtkCellLayout *layout)
1141 {
1142   GtkCellView *cell_view = GTK_CELL_VIEW (layout);
1143   GList *retval = NULL, *list;
1144
1145   g_return_val_if_fail (cell_view != NULL, NULL);
1146
1147   gtk_cell_view_set_cell_data (cell_view);
1148
1149   for (list = cell_view->priv->cell_list; list; list = list->next)
1150     {
1151       GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
1152
1153       retval = g_list_prepend (retval, info->cell);
1154     }
1155
1156   return g_list_reverse (retval);
1157 }
1158
1159 static gboolean
1160 gtk_cell_view_buildable_custom_tag_start (GtkBuildable  *buildable,
1161                                           GtkBuilder    *builder,
1162                                           GObject       *child,
1163                                           const gchar   *tagname,
1164                                           GMarkupParser *parser,
1165                                           gpointer      *data)
1166 {
1167   if (parent_buildable_iface->custom_tag_start &&
1168       parent_buildable_iface->custom_tag_start (buildable, builder, child,
1169                                                 tagname, parser, data))
1170     return TRUE;
1171
1172   return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child,
1173                                                       tagname, parser, data);
1174 }
1175
1176 static void
1177 gtk_cell_view_buildable_custom_tag_end (GtkBuildable *buildable,
1178                                         GtkBuilder   *builder,
1179                                         GObject      *child,
1180                                         const gchar  *tagname,
1181                                         gpointer     *data)
1182 {
1183   if (strcmp (tagname, "attributes") == 0)
1184     _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname,
1185                                                data);
1186   else if (parent_buildable_iface->custom_tag_end)
1187     parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname,
1188                                             data);
1189 }
1190
1191 static void
1192 gtk_cell_view_get_preferred_width  (GtkWidget *widget,
1193                                     gint      *minimum_size,
1194                                     gint      *natural_size)
1195 {
1196   GList *list;
1197   gint cell_min, cell_nat;
1198   gboolean first_cell = TRUE;
1199   GtkCellView *cellview = GTK_CELL_VIEW (widget);
1200   gint minimum, natural;
1201
1202   minimum = natural = 0;
1203
1204   if (cellview->priv->displayed_row)
1205     gtk_cell_view_set_cell_data (cellview);
1206
1207   for (list = cellview->priv->cell_list; list; list = list->next)
1208     {
1209       GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
1210
1211       if (gtk_cell_renderer_get_visible (info->cell))
1212         {
1213           
1214           if (!first_cell)
1215             {
1216               minimum += cellview->priv->spacing;
1217               natural += cellview->priv->spacing;
1218             }
1219
1220           gtk_cell_renderer_get_preferred_width (info->cell,
1221                                                  GTK_WIDGET (cellview), &cell_min, &cell_nat);
1222           
1223           info->requested_width = cell_min;
1224           info->natural_width   = cell_nat;
1225           
1226           minimum += info->requested_width;
1227           natural += info->natural_width;
1228
1229           first_cell = FALSE;
1230         }
1231     }
1232
1233   if (minimum_size)
1234     *minimum_size = minimum;
1235
1236   if (natural_size)
1237     *natural_size = natural;
1238 }
1239
1240 static void       
1241 gtk_cell_view_get_preferred_height (GtkWidget *widget,
1242                                     gint      *minimum_size,
1243                                     gint      *natural_size)
1244 {
1245   gint minimum_width;
1246
1247   /* CellViews only need to respond to height-for-width mode (cellview is pretty much
1248    * an implementation detail of GtkComboBox) */
1249   gtk_cell_view_get_preferred_width (widget, &minimum_width, NULL);
1250   gtk_cell_view_get_preferred_height_for_width (widget, minimum_width, minimum_size, natural_size);
1251 }
1252
1253 static void       
1254 gtk_cell_view_get_preferred_width_for_height (GtkWidget *widget,
1255                                               gint       for_size,
1256                                               gint      *minimum_size,
1257                                               gint      *natural_size)
1258 {
1259   /* CellViews only need to respond to height-for-width mode (cellview is pretty much
1260    * an implementation detail of GtkComboBox) */
1261   gtk_cell_view_get_preferred_width (widget, minimum_size, natural_size);
1262 }
1263
1264 static void       
1265 gtk_cell_view_get_preferred_height_for_width (GtkWidget *widget,
1266                                               gint       for_size,
1267                                               gint      *minimum_size,
1268                                               gint      *natural_size)
1269 {
1270   GtkCellView      *cellview = GTK_CELL_VIEW (widget);
1271   GList            *list;
1272   GtkRequestedSize *sizes;
1273   GArray           *array;
1274   gint              minimum, natural, avail_size;
1275   gboolean          first_cell = TRUE;
1276   gint              n_expand_cells = 0;
1277   gint              extra_per_cell, extra_extra, i;
1278
1279   minimum = natural = 0;
1280   avail_size = for_size;
1281
1282   array = g_array_new (0, TRUE, sizeof (GtkRequestedSize));
1283
1284   if (cellview->priv->displayed_row)
1285     gtk_cell_view_set_cell_data (cellview);
1286
1287   /* First allocate the right width to all cells */
1288   for (list = cellview->priv->cell_list; list; list = list->next)
1289     {
1290       GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
1291
1292       if (gtk_cell_renderer_get_visible (info->cell))
1293         {
1294           GtkRequestedSize requested;
1295
1296           gtk_cell_renderer_get_preferred_width (GTK_CELL_RENDERER (info->cell),
1297                                                  GTK_WIDGET (cellview), 
1298                                                  &requested.minimum_size, 
1299                                                  &requested.natural_size);
1300
1301           requested.data = info;
1302           g_array_append_val (array, requested);
1303
1304           avail_size -= requested.minimum_size;
1305
1306           if (!first_cell)
1307             avail_size -= cellview->priv->spacing;
1308
1309           first_cell = FALSE;
1310
1311           if (info->expand)
1312             n_expand_cells++;
1313         }
1314     }
1315
1316   sizes      = (GtkRequestedSize *)array->data;
1317   avail_size = gtk_distribute_natural_allocation (MAX (0, avail_size), array->len, sizes);
1318
1319   /* Deal with any expand space... */
1320   if (n_expand_cells > 0)
1321     {
1322       extra_per_cell = avail_size / n_expand_cells;
1323       extra_extra    = avail_size % n_expand_cells;
1324     }
1325   else
1326     /* Everything just left-aligned if no cells expand */
1327     extra_per_cell = extra_extra = 0;
1328
1329   /* Now get the height for the real width of each cell */
1330   for (i = 0, list = cellview->priv->cell_list; list; list = list->next)
1331     {
1332       GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
1333       gint cell_minimum, cell_natural;
1334
1335       if (gtk_cell_renderer_get_visible (info->cell))
1336         {
1337           gint cell_width = sizes[i].minimum_size;
1338
1339           g_assert (sizes[i].data == info);
1340
1341           if (info->expand)
1342             {
1343               cell_width += extra_per_cell;
1344               if (extra_extra)
1345                 {
1346                   cell_width++;
1347                   extra_extra--;
1348                 }
1349             }
1350
1351           /* Get the height for the real width of this cell */
1352           gtk_cell_renderer_get_preferred_height_for_width (GTK_CELL_RENDERER (info->cell),
1353                                                             GTK_WIDGET (widget),
1354                                                             cell_width, &cell_minimum, &cell_natural);
1355
1356           minimum = MAX (minimum, cell_minimum);
1357           natural = MAX (natural, cell_natural);
1358
1359           /* increment sizes[] index for visible cells */
1360           i++;
1361         }
1362     }
1363
1364   g_array_free (array, TRUE);
1365
1366   if (minimum_size)
1367     *minimum_size = minimum;
1368   if (natural_size)
1369     *natural_size = natural;
1370 }