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