]> Pileus Git - ~andy/gtk/blob - tests/cellareascaffold.c
Added tests to reflect proper treatment of background area.
[~andy/gtk] / tests / cellareascaffold.c
1 /* cellareascaffold.c
2  *
3  * Copyright (C) 2010 Openismus GmbH
4  *
5  * Authors:
6  *      Tristan Van Berkom <tristanvb@openismus.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include <string.h>
25 #include "cellareascaffold.h"
26
27 /* GObjectClass */
28 static void      cell_area_scaffold_finalize                       (GObject              *object);
29 static void      cell_area_scaffold_dispose                        (GObject              *object);
30 static void      cell_area_scaffold_set_property                   (GObject              *object,
31                                                                     guint                 prop_id,
32                                                                     const GValue         *value,
33                                                                     GParamSpec           *pspec);
34 static void      cell_area_scaffold_get_property                   (GObject              *object,
35                                                                     guint                 prop_id,
36                                                                     GValue               *value,
37                                                                     GParamSpec           *pspec);
38
39 /* GtkWidgetClass */
40 static void      cell_area_scaffold_realize                        (GtkWidget       *widget);
41 static void      cell_area_scaffold_unrealize                      (GtkWidget       *widget);
42 static gboolean  cell_area_scaffold_draw                           (GtkWidget       *widget,
43                                                                     cairo_t         *cr);
44 static void      cell_area_scaffold_size_allocate                  (GtkWidget       *widget,
45                                                                     GtkAllocation   *allocation);
46 static void      cell_area_scaffold_get_preferred_width            (GtkWidget       *widget,
47                                                                     gint            *minimum_size,
48                                                                     gint            *natural_size);
49 static void      cell_area_scaffold_get_preferred_height_for_width (GtkWidget       *widget,
50                                                                     gint             for_size,
51                                                                     gint            *minimum_size,
52                                                                     gint            *natural_size);
53 static void      cell_area_scaffold_get_preferred_height           (GtkWidget       *widget,
54                                                                     gint            *minimum_size,
55                                                                     gint            *natural_size);
56 static void      cell_area_scaffold_get_preferred_width_for_height (GtkWidget       *widget,
57                                                                     gint             for_size,
58                                                                     gint            *minimum_size,
59                                                                     gint            *natural_size);
60 static void      cell_area_scaffold_map                            (GtkWidget       *widget);
61 static void      cell_area_scaffold_unmap                          (GtkWidget       *widget);
62 static gint      cell_area_scaffold_focus                          (GtkWidget       *widget,
63                                                                     GtkDirectionType direction);
64 static gboolean  cell_area_scaffold_button_press                   (GtkWidget       *widget,
65                                                                     GdkEventButton  *event);
66
67 /* GtkContainerClass */
68 static void      cell_area_scaffold_forall                         (GtkContainer    *container,
69                                                                     gboolean         include_internals,
70                                                                     GtkCallback      callback,
71                                                                     gpointer         callback_data);
72 static void      cell_area_scaffold_remove                         (GtkContainer    *container,
73                                                                     GtkWidget       *child);
74 static void      cell_area_scaffold_put_edit_widget                (CellAreaScaffold *scaffold,
75                                                                     GtkWidget        *edit_widget,
76                                                                     gint              x,
77                                                                     gint              y,
78                                                                     gint              width,
79                                                                     gint              height);
80
81 /* CellAreaScaffoldClass */
82 static void      cell_area_scaffold_activate                       (CellAreaScaffold *scaffold);
83
84 /* CellArea/GtkTreeModel callbacks */
85 static void      size_changed_cb                                   (GtkCellAreaIter *iter,
86                                                                     GParamSpec       *pspec,
87                                                                     CellAreaScaffold *scaffold);
88 static void      focus_changed_cb                                  (GtkCellArea      *area,
89                                                                     GtkCellRenderer  *renderer,
90                                                                     const gchar      *path,
91                                                                     CellAreaScaffold *scaffold);
92 static void      editing_started_cb                                (GtkCellArea      *area,
93                                                                     GtkCellRenderer  *renderer,
94                                                                     GtkCellEditable  *edit_widget,
95                                                                     GdkRectangle     *cell_area,
96                                                                     const gchar      *path,
97                                                                     CellAreaScaffold *scaffold);
98 static void      remove_editable_cb                                (GtkCellArea      *area,
99                                                                     GtkCellRenderer  *renderer,
100                                                                     GtkCellEditable  *edit_widget,
101                                                                     CellAreaScaffold *scaffold);
102 static void      row_changed_cb                                    (GtkTreeModel     *model,
103                                                                     GtkTreePath      *path,
104                                                                     GtkTreeIter      *iter,
105                                                                     CellAreaScaffold *scaffold);
106 static void      row_inserted_cb                                   (GtkTreeModel     *model,
107                                                                     GtkTreePath      *path,
108                                                                     GtkTreeIter      *iter,
109                                                                     CellAreaScaffold *scaffold);
110 static void      row_deleted_cb                                    (GtkTreeModel     *model,
111                                                                     GtkTreePath      *path,
112                                                                     CellAreaScaffold *scaffold);
113 static void      rows_reordered_cb                                 (GtkTreeModel     *model,
114                                                                     GtkTreePath      *parent,
115                                                                     GtkTreeIter      *iter,
116                                                                     gint             *new_order,
117                                                                     CellAreaScaffold *scaffold);
118
119 typedef struct {
120   gint    size; /* The size of the row in the scaffold's opposing orientation */
121 } RowData;
122
123 struct _CellAreaScaffoldPrivate {
124
125   /* Window for catching events and dispatching them to the cell area */
126   GdkWindow       *event_window;
127
128   /* The model we're showing data for */
129   GtkTreeModel    *model;
130   gulong           row_changed_id;
131   gulong           row_inserted_id;
132   gulong           row_deleted_id;
133   gulong           rows_reordered_id;
134
135   /* The area rendering the data and a global iter */
136   GtkCellArea     *area;
137   GtkCellAreaIter *iter;
138
139   /* Cache some info about rows (hieghts etc) */
140   GArray          *row_data;
141
142   /* Focus handling */
143   gint             focus_row;
144   gulong           focus_changed_id;
145
146   /* Check when the underlying area changes the size and
147    * we need to queue a redraw */
148   gulong           size_changed_id;
149
150   /* Currently edited widget */
151   GtkWidget       *edit_widget;
152   GdkRectangle     edit_rect;
153   gulong           editing_started_id;
154   gulong           remove_editable_id;
155
156
157   gint             row_spacing;
158   gint             indent;
159 };
160
161 enum {
162   PROP_0,
163   PROP_ORIENTATION
164 };
165
166 enum {
167   ACTIVATE,
168   N_SIGNALS
169 };
170
171 static guint scaffold_signals[N_SIGNALS] = { 0 };
172
173 #define DIRECTION_STR(dir)                              \
174   ((dir) == GTK_DIR_TAB_FORWARD  ? "tab forward" :      \
175    (dir) == GTK_DIR_TAB_BACKWARD ? "tab backward" :     \
176    (dir) == GTK_DIR_UP           ? "up" :               \
177    (dir) == GTK_DIR_DOWN         ? "down" :             \
178    (dir) == GTK_DIR_LEFT         ? "left" :             \
179    (dir) == GTK_DIR_RIGHT        ? "right" : "invalid")
180
181 G_DEFINE_TYPE_WITH_CODE (CellAreaScaffold, cell_area_scaffold, GTK_TYPE_CONTAINER,
182                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL));
183
184
185 static void
186 cell_area_scaffold_init (CellAreaScaffold *scaffold)
187 {
188   CellAreaScaffoldPrivate *priv;
189
190   scaffold->priv = G_TYPE_INSTANCE_GET_PRIVATE (scaffold,
191                                                 TYPE_CELL_AREA_SCAFFOLD,
192                                                 CellAreaScaffoldPrivate);
193   priv = scaffold->priv;
194
195   priv->area = gtk_cell_area_box_new ();
196   priv->iter = gtk_cell_area_create_iter (priv->area);
197
198   priv->row_data = g_array_new (FALSE, FALSE, sizeof (RowData));
199
200   gtk_widget_set_has_window (GTK_WIDGET (scaffold), FALSE);
201   gtk_widget_set_can_focus (GTK_WIDGET (scaffold), TRUE);
202
203   priv->size_changed_id = 
204     g_signal_connect (priv->iter, "notify",
205                       G_CALLBACK (size_changed_cb), scaffold);
206
207   priv->focus_changed_id =
208     g_signal_connect (priv->area, "focus-changed",
209                       G_CALLBACK (focus_changed_cb), scaffold);
210
211   priv->editing_started_id =
212     g_signal_connect (priv->area, "editing-started",
213                       G_CALLBACK (editing_started_cb), scaffold);
214
215   priv->remove_editable_id =
216     g_signal_connect (priv->area, "remove-editable",
217                       G_CALLBACK (remove_editable_cb), scaffold);
218 }
219
220 static void
221 cell_area_scaffold_class_init (CellAreaScaffoldClass *class)
222 {
223   GObjectClass      *gobject_class;
224   GtkWidgetClass    *widget_class;
225   GtkContainerClass *container_class;
226
227   gobject_class = G_OBJECT_CLASS (class);
228   gobject_class->dispose = cell_area_scaffold_dispose;
229   gobject_class->finalize = cell_area_scaffold_finalize;
230   gobject_class->get_property = cell_area_scaffold_get_property;
231   gobject_class->set_property = cell_area_scaffold_set_property;
232
233   widget_class = GTK_WIDGET_CLASS (class);
234   widget_class->realize = cell_area_scaffold_realize;
235   widget_class->unrealize = cell_area_scaffold_unrealize;
236   widget_class->draw = cell_area_scaffold_draw;
237   widget_class->size_allocate = cell_area_scaffold_size_allocate;
238   widget_class->get_preferred_width = cell_area_scaffold_get_preferred_width;
239   widget_class->get_preferred_height_for_width = cell_area_scaffold_get_preferred_height_for_width;
240   widget_class->get_preferred_height = cell_area_scaffold_get_preferred_height;
241   widget_class->get_preferred_width_for_height = cell_area_scaffold_get_preferred_width_for_height;
242   widget_class->map = cell_area_scaffold_map;
243   widget_class->unmap = cell_area_scaffold_unmap;
244   widget_class->focus = cell_area_scaffold_focus;
245   widget_class->button_press_event = cell_area_scaffold_button_press;
246
247   container_class = GTK_CONTAINER_CLASS (class);
248   container_class->forall = cell_area_scaffold_forall;
249   container_class->remove = cell_area_scaffold_remove;
250
251   class->activate = cell_area_scaffold_activate;
252
253   g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation");
254
255   scaffold_signals[ACTIVATE] =
256     g_signal_new ("activate",
257                   G_OBJECT_CLASS_TYPE (gobject_class),
258                   G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
259                   G_STRUCT_OFFSET (CellAreaScaffoldClass, activate),
260                   NULL, NULL,
261                   g_cclosure_marshal_VOID__VOID,
262                   G_TYPE_NONE, 0);
263   widget_class->activate_signal = scaffold_signals[ACTIVATE];
264
265
266   g_type_class_add_private (gobject_class, sizeof (CellAreaScaffoldPrivate));
267 }
268
269 /*********************************************************
270  *                    GObjectClass                       *
271  *********************************************************/
272 static void
273 cell_area_scaffold_finalize (GObject *object)
274 {
275   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (object);
276   CellAreaScaffoldPrivate *priv;
277
278   priv = scaffold->priv;
279
280   g_array_free (priv->row_data, TRUE);
281
282   G_OBJECT_CLASS (cell_area_scaffold_parent_class)->finalize (object);
283 }
284
285 static void
286 cell_area_scaffold_dispose (GObject *object)
287 {
288   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (object);
289   CellAreaScaffoldPrivate *priv;
290
291   priv = scaffold->priv;
292
293   cell_area_scaffold_set_model (scaffold, NULL);
294
295   if (priv->iter)
296     {
297       /* Disconnect signals */
298       g_signal_handler_disconnect (priv->iter, priv->size_changed_id);
299
300       g_object_unref (priv->iter);
301       priv->iter = NULL;
302       priv->size_changed_id = 0;
303     }
304
305   if (priv->area)
306     {
307       /* Disconnect signals */
308       g_signal_handler_disconnect (priv->area, priv->focus_changed_id);
309       g_signal_handler_disconnect (priv->area, priv->editing_started_id);
310       g_signal_handler_disconnect (priv->area, priv->remove_editable_id);
311
312       g_object_unref (priv->area);
313       priv->area = NULL;
314       priv->focus_changed_id = 0;
315     }
316
317   G_OBJECT_CLASS (cell_area_scaffold_parent_class)->dispose (object);  
318 }
319
320 static void
321 cell_area_scaffold_set_property (GObject      *object,
322                                  guint         prop_id,
323                                  const GValue *value,
324                                  GParamSpec   *pspec)
325 {
326   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (object);
327   CellAreaScaffoldPrivate *priv;
328
329   priv = scaffold->priv;
330
331   switch (prop_id)
332     {
333     case PROP_ORIENTATION:
334       gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->area), 
335                                       g_value_get_enum (value));
336       break;
337     default:
338       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
339       break;
340     }
341 }
342
343 static void      
344 cell_area_scaffold_get_property (GObject    *object,
345                                  guint       prop_id,
346                                  GValue     *value,
347                                  GParamSpec *pspec)
348 {
349   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (object);
350   CellAreaScaffoldPrivate *priv;
351
352   priv = scaffold->priv;
353
354   switch (prop_id)
355     {
356     case PROP_ORIENTATION:
357       g_value_set_enum (value, 
358                         gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area)));
359       break;
360     default:
361       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
362       break;
363     }
364 }
365
366 /*********************************************************
367  *                    GtkWidgetClass                     *
368  *********************************************************/
369 static void
370 cell_area_scaffold_realize (GtkWidget *widget)
371 {
372   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
373   CellAreaScaffoldPrivate *priv     = scaffold->priv;
374   GtkAllocation            allocation;
375   GdkWindow               *window;
376   GdkWindowAttr            attributes;
377   gint                     attributes_mask;
378
379   gtk_widget_get_allocation (widget, &allocation);
380
381   gtk_widget_set_realized (widget, TRUE);
382
383   attributes.window_type = GDK_WINDOW_CHILD;
384   attributes.x = allocation.x;
385   attributes.y = allocation.y;
386   attributes.width = allocation.width;
387   attributes.height = allocation.height;
388   attributes.wclass = GDK_INPUT_ONLY;
389   attributes.event_mask = gtk_widget_get_events (widget);
390   attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
391                             GDK_BUTTON_RELEASE_MASK |
392                             GDK_KEY_PRESS_MASK |
393                             GDK_KEY_RELEASE_MASK);
394
395   attributes_mask = GDK_WA_X | GDK_WA_Y;
396
397   window = gtk_widget_get_parent_window (widget);
398   gtk_widget_set_window (widget, window);
399   g_object_ref (window);
400
401   priv->event_window = gdk_window_new (window, &attributes, attributes_mask);
402   gdk_window_set_user_data (priv->event_window, widget);
403
404   gtk_widget_style_attach (widget);
405 }
406
407 static void
408 cell_area_scaffold_unrealize (GtkWidget *widget)
409 {
410   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
411   CellAreaScaffoldPrivate *priv     = scaffold->priv;
412
413   if (priv->event_window)
414     {
415       gdk_window_set_user_data (priv->event_window, NULL);
416       gdk_window_destroy (priv->event_window);
417       priv->event_window = NULL;
418     }
419   
420   GTK_WIDGET_CLASS (cell_area_scaffold_parent_class)->unrealize (widget);
421 }
422
423 static gboolean
424 cell_area_scaffold_draw (GtkWidget       *widget,
425                          cairo_t         *cr)
426 {
427   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
428   CellAreaScaffoldPrivate *priv     = scaffold->priv;
429   GtkOrientation           orientation;
430   GtkTreeIter              iter;
431   gboolean                 valid;
432   GdkRectangle             background_area;
433   GdkRectangle             render_area;
434   GtkAllocation            allocation;
435   gint                     i = 0;
436   gboolean                 have_focus;
437   GtkCellRendererState     flags;
438
439   if (!priv->model)
440     return FALSE;
441
442   have_focus  = gtk_widget_has_focus (widget);
443   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
444
445   gtk_widget_get_allocation (widget, &allocation);
446
447   render_area.x      = 0;
448   render_area.y      = 0;
449   render_area.width  = allocation.width;
450   render_area.height = allocation.height;
451
452   background_area = render_area;
453
454   if (orientation == GTK_ORIENTATION_HORIZONTAL)
455     {
456       render_area.x      = priv->indent;
457       render_area.width -= priv->indent;
458     }
459   else
460     {
461       render_area.y       = priv->indent;
462       render_area.height -= priv->indent;
463     }
464
465   valid = gtk_tree_model_get_iter_first (priv->model, &iter);
466   while (valid)
467     {
468       RowData *data = &g_array_index (priv->row_data, RowData, i);
469
470       if (have_focus && i == priv->focus_row)
471         flags = GTK_CELL_RENDERER_FOCUSED;
472       else
473         flags = 0;
474
475       if (orientation == GTK_ORIENTATION_HORIZONTAL)
476         {
477           render_area.height     = data->size;
478
479           background_area.height = render_area.height;
480           background_area.y      = render_area.y;
481
482           if (i == 0)
483             {
484               background_area.height += priv->row_spacing / 2;
485               background_area.height += priv->row_spacing % 2;
486             }
487           else if (i == priv->row_data->len - 1)
488             {
489               background_area.y      -= priv->row_spacing / 2;
490               background_area.height += priv->row_spacing / 2;
491             }
492           else
493             {
494               background_area.y      -= priv->row_spacing / 2;
495               background_area.height += priv->row_spacing;
496             }
497         }
498       else /* GTK_ORIENTATION_VERTICAL */
499         {
500           render_area.width     = data->size;
501
502           background_area.width = render_area.height;
503           background_area.x     = render_area.x;
504
505           if (i == 0)
506             {
507               background_area.width += priv->row_spacing / 2;
508               background_area.width += priv->row_spacing % 2;
509             }
510           else if (i == priv->row_data->len - 1)
511             {
512               background_area.x     -= priv->row_spacing / 2;
513               background_area.width += priv->row_spacing / 2;
514             }
515           else
516             {
517               background_area.x     -= priv->row_spacing / 2;
518               background_area.width += priv->row_spacing;
519             }
520         }
521
522       gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
523       gtk_cell_area_render (priv->area, priv->iter, widget, cr, 
524                             &background_area, &render_area, flags,
525                             (have_focus && i == priv->focus_row));
526
527       if (orientation == GTK_ORIENTATION_HORIZONTAL)
528         {
529           render_area.y += data->size;
530           render_area.y += priv->row_spacing;
531         }
532       else
533         {
534           render_area.x += data->size;
535           render_area.x += priv->row_spacing;
536         }
537
538       i++;
539       valid = gtk_tree_model_iter_next (priv->model, &iter);
540     }
541
542   /* Draw the edit widget after drawing everything else */
543   GTK_WIDGET_CLASS (cell_area_scaffold_parent_class)->draw (widget, cr);
544
545   return FALSE;
546 }
547
548 static void 
549 request_all_base (CellAreaScaffold *scaffold)
550 {
551   CellAreaScaffoldPrivate *priv = scaffold->priv;
552   GtkWidget               *widget = GTK_WIDGET (scaffold);
553   GtkOrientation           orientation;
554   GtkTreeIter              iter;
555   gboolean                 valid;
556
557   if (!priv->model)
558     return;
559
560   g_signal_handler_block (priv->iter, priv->size_changed_id);
561
562   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
563
564   valid = gtk_tree_model_get_iter_first (priv->model, &iter);
565   while (valid)
566     {
567       gint min, nat;
568
569       gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
570
571       if (orientation == GTK_ORIENTATION_HORIZONTAL)
572         gtk_cell_area_get_preferred_width (priv->area, priv->iter, widget, &min, &nat);
573       else
574         gtk_cell_area_get_preferred_height (priv->area, priv->iter, widget, &min, &nat);
575
576       valid = gtk_tree_model_iter_next (priv->model, &iter);
577     }
578
579   if (orientation == GTK_ORIENTATION_HORIZONTAL)
580     gtk_cell_area_iter_sum_preferred_width (priv->iter);
581   else
582     gtk_cell_area_iter_sum_preferred_height (priv->iter);
583
584   g_signal_handler_unblock (priv->iter, priv->size_changed_id);
585 }
586
587 static void 
588 get_row_sizes (CellAreaScaffold *scaffold, 
589                GArray           *array,
590                gint              for_size)
591 {
592   CellAreaScaffoldPrivate *priv = scaffold->priv;
593   GtkWidget               *widget = GTK_WIDGET (scaffold);
594   GtkOrientation           orientation;
595   GtkTreeIter              iter;
596   gboolean                 valid;
597   gint                     i = 0;
598
599   if (!priv->model)
600     return;
601
602   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
603
604   valid = gtk_tree_model_get_iter_first (priv->model, &iter);
605   while (valid)
606     {
607       RowData *data = &g_array_index (array, RowData, i);
608
609       gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
610
611       if (orientation == GTK_ORIENTATION_HORIZONTAL)
612         gtk_cell_area_get_preferred_height_for_width (priv->area, priv->iter, widget, 
613                                                       for_size, &data->size, NULL);
614       else
615         gtk_cell_area_get_preferred_width_for_height (priv->area, priv->iter, widget, 
616                                                       for_size, &data->size, NULL);
617
618       i++;
619       valid = gtk_tree_model_iter_next (priv->model, &iter);
620     }
621 }
622
623 static void
624 cell_area_scaffold_size_allocate (GtkWidget           *widget,
625                                   GtkAllocation       *allocation)
626 {
627   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
628   CellAreaScaffoldPrivate *priv     = scaffold->priv;
629   GtkOrientation           orientation;
630
631   gtk_widget_set_allocation (widget, allocation);
632
633   if (gtk_widget_get_realized (widget))
634     gdk_window_move_resize (priv->event_window,
635                             allocation->x,
636                             allocation->y,
637                             allocation->width,
638                             allocation->height);
639
640   /* Allocate the child GtkCellEditable widget if one is currently editing a row */
641   if (priv->edit_widget)
642     gtk_widget_size_allocate (priv->edit_widget, &priv->edit_rect);
643
644   if (!priv->model)
645     return;
646
647   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
648
649   /* Cache the per-row sizes and allocate the iter */
650   if (orientation == GTK_ORIENTATION_HORIZONTAL)
651     {
652       gtk_cell_area_iter_allocate_width (priv->iter, allocation->width);
653       get_row_sizes (scaffold, priv->row_data, allocation->width);
654     }
655   else
656     {
657       gtk_cell_area_iter_allocate_height (priv->iter, allocation->height);
658       get_row_sizes (scaffold, priv->row_data, allocation->height);
659     }
660 }
661
662
663 static void
664 cell_area_scaffold_get_preferred_width (GtkWidget       *widget,
665                                         gint            *minimum_size,
666                                         gint            *natural_size)
667 {
668   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
669   CellAreaScaffoldPrivate *priv     = scaffold->priv;
670   GtkOrientation           orientation;
671
672   if (!priv->model)
673     return;
674
675   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
676
677   if (orientation == GTK_ORIENTATION_HORIZONTAL)
678     {
679       request_all_base (scaffold);
680
681       gtk_cell_area_iter_get_preferred_width (priv->iter, minimum_size, natural_size);
682
683       *minimum_size += priv->indent;
684       *natural_size += priv->indent;
685     }
686   else
687     {
688       gint min_size, nat_size;
689
690       GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_size, &nat_size);
691       GTK_WIDGET_GET_CLASS (widget)->get_preferred_width_for_height (widget, min_size, 
692                                                                      minimum_size, natural_size);
693     }
694 }
695
696 static void
697 cell_area_scaffold_get_preferred_height_for_width (GtkWidget       *widget,
698                                                    gint             for_size,
699                                                    gint            *minimum_size,
700                                                    gint            *natural_size)
701 {
702   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
703   CellAreaScaffoldPrivate *priv     = scaffold->priv;
704   GtkOrientation           orientation;
705
706   if (!priv->model)
707     return;
708
709   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
710
711   if (orientation == GTK_ORIENTATION_HORIZONTAL)
712     {
713       GArray *request_array;
714       gint    n_rows, i, full_size = 0;
715
716       n_rows = gtk_tree_model_iter_n_children (priv->model, NULL);
717
718       /* Get an array for the contextual request */
719       request_array = g_array_new (FALSE, FALSE, sizeof (RowData));
720       g_array_set_size (request_array, n_rows);
721       memset (request_array->data, 0x0, n_rows * sizeof (RowData));
722
723       /* Gather each contextual size into the request array */
724       get_row_sizes (scaffold, request_array, for_size - priv->indent);
725
726       /* Sum up the size and add some row spacing */
727       for (i = 0; i < n_rows; i++)
728         {
729           RowData *data = &g_array_index (request_array, RowData, i);
730
731           full_size += data->size;
732         }
733
734       full_size += MAX (0, n_rows -1) * priv->row_spacing;
735
736       g_array_free (request_array, TRUE);
737
738       *minimum_size = full_size;
739       *natural_size = full_size;
740     }
741   else
742     {
743       GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_size, natural_size);
744     }
745 }
746
747 static void
748 cell_area_scaffold_get_preferred_height (GtkWidget       *widget,
749                                          gint            *minimum_size,
750                                          gint            *natural_size)
751 {
752   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
753   CellAreaScaffoldPrivate *priv     = scaffold->priv;
754   GtkOrientation           orientation;
755
756   if (!priv->model)
757     return;
758
759   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
760
761   if (orientation == GTK_ORIENTATION_VERTICAL)
762     {
763       request_all_base (scaffold);
764
765       gtk_cell_area_iter_get_preferred_height (priv->iter, minimum_size, natural_size);
766
767       *minimum_size += priv->indent;
768       *natural_size += priv->indent;
769     }
770   else
771     {
772       gint min_size, nat_size;
773
774       GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_size, &nat_size);
775       GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_size, 
776                                                                      minimum_size, natural_size);
777     }
778 }
779
780 static void
781 cell_area_scaffold_get_preferred_width_for_height (GtkWidget       *widget,
782                                                    gint             for_size,
783                                                    gint            *minimum_size,
784                                                    gint            *natural_size)
785 {
786   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
787   CellAreaScaffoldPrivate *priv     = scaffold->priv;
788   GtkOrientation           orientation;
789
790   if (!priv->model)
791     return;
792
793   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
794
795   if (orientation == GTK_ORIENTATION_VERTICAL)
796     {
797       GArray *request_array;
798       gint    n_rows, i, full_size = 0;
799
800       n_rows = gtk_tree_model_iter_n_children (priv->model, NULL);
801
802       /* Get an array for the contextual request */
803       request_array = g_array_new (FALSE, FALSE, sizeof (RowData));
804       g_array_set_size (request_array, n_rows);
805       memset (request_array->data, 0x0, n_rows * sizeof (RowData));
806
807       /* Gather each contextual size into the request array */
808       get_row_sizes (scaffold, request_array, for_size - priv->indent);
809
810       /* Sum up the size and add some row spacing */
811       for (i = 0; i < n_rows; i++)
812         {
813           RowData *data = &g_array_index (request_array, RowData, i);
814
815           full_size += data->size;
816         }
817
818       full_size += MAX (0, n_rows -1) * priv->row_spacing;
819
820       g_array_free (request_array, TRUE);
821
822       *minimum_size = full_size;
823       *natural_size = full_size;
824     }
825   else
826     {
827       GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_size, natural_size);
828     }
829 }
830
831 static void
832 cell_area_scaffold_map (GtkWidget       *widget)
833 {
834   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
835   CellAreaScaffoldPrivate *priv     = scaffold->priv;
836   
837   GTK_WIDGET_CLASS (cell_area_scaffold_parent_class)->map (widget);
838
839   if (priv->event_window)
840     gdk_window_show (priv->event_window);
841 }
842
843 static void
844 cell_area_scaffold_unmap (GtkWidget       *widget)
845 {
846   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
847   CellAreaScaffoldPrivate *priv     = scaffold->priv;
848   
849   GTK_WIDGET_CLASS (cell_area_scaffold_parent_class)->unmap (widget);
850
851   if (priv->event_window)
852     gdk_window_hide (priv->event_window);
853 }
854
855
856 static gint
857 cell_area_scaffold_focus (GtkWidget       *widget,
858                           GtkDirectionType direction)
859 {
860   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
861   CellAreaScaffoldPrivate *priv     = scaffold->priv;
862   GtkTreeIter              iter;
863   gboolean                 valid;
864   gint                     focus_row;
865   GtkOrientation           orientation;
866   gboolean                 changed = FALSE;
867
868   /* Grab focus on ourself if we dont already have focus */
869   if (!gtk_widget_has_focus (widget))
870     gtk_widget_grab_focus (widget);
871
872   /* Move focus from cell to cell and row to row */
873   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
874   
875   focus_row = priv->focus_row;
876
877   g_signal_handler_block (priv->area, priv->focus_changed_id);
878   
879   valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, priv->focus_row);
880   while (valid)
881     {
882       gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
883       
884       /* If focus stays in the area we dont need to do any more */
885       if (gtk_cell_area_focus (priv->area, direction))
886         {
887           priv->focus_row = focus_row;
888
889           /* XXX A smarter implementation would only invalidate the rectangles where
890            * focus was removed from and new focus was placed */
891           gtk_widget_queue_draw (widget);
892           changed = TRUE;
893           break;
894         }
895       else
896         {
897           if (orientation == GTK_ORIENTATION_HORIZONTAL)
898             {
899               if (direction == GTK_DIR_RIGHT ||
900                   direction == GTK_DIR_LEFT)
901                 break;
902               else if (direction == GTK_DIR_UP ||
903                        direction == GTK_DIR_TAB_BACKWARD)
904                 {
905                   if (focus_row == 0)
906                     break;
907                   else
908                     {
909                       /* XXX A real implementation should check if the
910                        * previous row can focus with it's attributes setup */
911                       focus_row--;
912                       valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, focus_row);
913                     }
914                 }
915               else /* direction == GTK_DIR_DOWN || GTK_DIR_TAB_FORWARD */
916                 {
917                   if (focus_row == priv->row_data->len - 1)
918                     break;
919                   else
920                     {
921                       /* XXX A real implementation should check if the
922                        * previous row can focus with it's attributes setup */
923                       focus_row++;
924                       valid = gtk_tree_model_iter_next (priv->model, &iter);
925                     }
926                 }
927             }
928           else  /* (orientation == GTK_ORIENTATION_HORIZONTAL) */
929             {
930               if (direction == GTK_DIR_UP ||
931                   direction == GTK_DIR_DOWN)
932                 break;
933               else if (direction == GTK_DIR_LEFT ||
934                        direction == GTK_DIR_TAB_BACKWARD)
935                 {
936                   if (focus_row == 0)
937                     break;
938                   else
939                     {
940                       /* XXX A real implementation should check if the
941                        * previous row can focus with it's attributes setup */
942                       focus_row--;
943                       valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, focus_row);
944                     }
945                 }
946               else /* direction == GTK_DIR_RIGHT || GTK_DIR_TAB_FORWARD */
947                 {
948                   if (focus_row == priv->row_data->len - 1)
949                     break;
950                   else
951                     {
952                       /* XXX A real implementation should check if the
953                        * previous row can focus with it's attributes setup */
954                       focus_row++;
955                       valid = gtk_tree_model_iter_next (priv->model, &iter);
956                     }
957                 }
958             }
959         }
960     }
961
962   g_signal_handler_unblock (priv->area, priv->focus_changed_id);
963
964   /* XXX A smarter implementation would only invalidate the rectangles where
965    * focus was removed from and new focus was placed */
966   gtk_widget_queue_draw (widget);
967
968   return changed;
969 }
970
971 static gboolean
972 cell_area_scaffold_button_press (GtkWidget       *widget,
973                                  GdkEventButton  *event)
974 {
975   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
976   CellAreaScaffoldPrivate *priv     = scaffold->priv;
977   GtkTreeIter              iter;
978   gboolean                 valid;
979   GtkOrientation           orientation;
980   gint                     i = 0;
981   GdkRectangle             event_area;
982   GtkAllocation            allocation;
983   gboolean                 handled = FALSE;
984
985   /* Move focus from cell to cell and row to row */
986   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
987
988   gtk_widget_get_allocation (widget, &allocation);
989
990   event_area.x      = 0;
991   event_area.y      = 0;
992   event_area.width  = allocation.width;
993   event_area.height = allocation.height;
994
995   if (orientation == GTK_ORIENTATION_HORIZONTAL)
996     {
997       event_area.x      = priv->indent;
998       event_area.width -= priv->indent;
999     }
1000   else
1001     {
1002       event_area.y       = priv->indent;
1003       event_area.height -= priv->indent;
1004     }
1005
1006   valid = gtk_tree_model_get_iter_first (priv->model, &iter);
1007   while (valid)
1008     {
1009       RowData *data = &g_array_index (priv->row_data, RowData, i);
1010
1011       if (orientation == GTK_ORIENTATION_HORIZONTAL)
1012         {
1013           event_area.height = data->size;
1014
1015           if (event->y >= event_area.y && 
1016               event->y <= event_area.y + event_area.height)
1017             {
1018               /* XXX A real implementation would assemble GtkCellRendererState flags here */
1019               gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
1020               handled = gtk_cell_area_event (priv->area, priv->iter, GTK_WIDGET (scaffold),
1021                                              (GdkEvent *)event, &event_area, 0);
1022               break;
1023             }
1024
1025           event_area.y += data->size;
1026           event_area.y += priv->row_spacing;
1027         }
1028       else
1029         {
1030           event_area.width = data->size;
1031
1032           if (event->x >= event_area.x && 
1033               event->x <= event_area.x + event_area.width)
1034             {
1035               /* XXX A real implementation would assemble GtkCellRendererState flags here */
1036               gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
1037               handled = gtk_cell_area_event (priv->area, priv->iter, GTK_WIDGET (scaffold),
1038                                              (GdkEvent *)event, &event_area, 0);
1039               break;
1040             }
1041
1042           event_area.x += data->size;
1043           event_area.x += priv->row_spacing;
1044         }
1045
1046       i++;
1047       valid = gtk_tree_model_iter_next (priv->model, &iter);
1048     }
1049
1050   return handled;
1051 }
1052
1053
1054 /*********************************************************
1055  *                   GtkContainerClass                   *
1056  *********************************************************/
1057 static void
1058 cell_area_scaffold_put_edit_widget (CellAreaScaffold *scaffold,
1059                                     GtkWidget        *edit_widget,
1060                                     gint              x,
1061                                     gint              y,
1062                                     gint              width,
1063                                     gint              height)
1064 {
1065   CellAreaScaffoldPrivate *priv = scaffold->priv;
1066
1067   priv->edit_rect.x      = x;
1068   priv->edit_rect.y      = y;
1069   priv->edit_rect.width  = width;
1070   priv->edit_rect.height = height;
1071   priv->edit_widget      = edit_widget;
1072
1073   gtk_widget_set_parent (edit_widget, GTK_WIDGET (scaffold));
1074 }
1075
1076 static void
1077 cell_area_scaffold_forall (GtkContainer    *container,
1078                            gboolean         include_internals,
1079                            GtkCallback      callback,
1080                            gpointer         callback_data)
1081 {
1082   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (container);
1083   CellAreaScaffoldPrivate *priv     = scaffold->priv;
1084
1085   if (priv->edit_widget)
1086     (* callback) (priv->edit_widget, callback_data);
1087 }
1088
1089 static void
1090 cell_area_scaffold_remove (GtkContainer    *container,
1091                            GtkWidget       *child)
1092 {
1093   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (container);
1094   CellAreaScaffoldPrivate *priv     = scaffold->priv;
1095
1096   g_return_if_fail (child == priv->edit_widget);
1097
1098   gtk_widget_unparent (priv->edit_widget);
1099   priv->edit_widget = NULL;
1100 }
1101
1102 /*********************************************************
1103  *                CellAreaScaffoldClass                  *
1104  *********************************************************/
1105 static void
1106 cell_area_scaffold_activate (CellAreaScaffold *scaffold)
1107 {
1108   CellAreaScaffoldPrivate *priv     = scaffold->priv;
1109   GtkWidget               *widget   = GTK_WIDGET (scaffold);
1110   GtkAllocation            allocation;
1111   GtkOrientation           orientation;
1112   GdkRectangle             cell_area;
1113   GtkTreeIter              iter;
1114   gboolean                 valid;
1115   gint                     i = 0;
1116
1117   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
1118   gtk_widget_get_allocation (widget, &allocation);
1119
1120   cell_area.x = 0;
1121   cell_area.y = 0;
1122   cell_area.width  = allocation.width;
1123   cell_area.height = allocation.height;
1124
1125   if (orientation == GTK_ORIENTATION_HORIZONTAL)
1126     {
1127       cell_area.x      = priv->indent;
1128       cell_area.width -= priv->indent;
1129     }
1130   else
1131     {
1132       cell_area.y       = priv->indent;
1133       cell_area.height -= priv->indent;
1134     }
1135
1136   valid = gtk_tree_model_get_iter_first (priv->model, &iter);
1137   while (valid)
1138     {
1139       RowData *data = &g_array_index (priv->row_data, RowData, i);
1140
1141       if (i == priv->focus_row)
1142         {
1143           if (orientation == GTK_ORIENTATION_HORIZONTAL)
1144             cell_area.height = data->size;
1145           else
1146             cell_area.width = data->size;
1147
1148           gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
1149           gtk_cell_area_activate (priv->area, priv->iter, widget, &cell_area, GTK_CELL_RENDERER_FOCUSED);
1150
1151           break;
1152         }
1153
1154       if (orientation == GTK_ORIENTATION_HORIZONTAL)
1155         cell_area.y += data->size + priv->row_spacing;
1156       else
1157         cell_area.x += data->size + priv->row_spacing;
1158
1159       i++;
1160       valid = gtk_tree_model_iter_next (priv->model, &iter);
1161     }
1162 }
1163
1164 /*********************************************************
1165  *           CellArea/GtkTreeModel callbacks             *
1166  *********************************************************/
1167 static void
1168 size_changed_cb (GtkCellAreaIter  *iter,
1169                  GParamSpec       *pspec,
1170                  CellAreaScaffold *scaffold)
1171 {
1172   if (!strcmp (pspec->name, "minimum-width") ||
1173       !strcmp (pspec->name, "natural-width") ||
1174       !strcmp (pspec->name, "minimum-height") ||
1175       !strcmp (pspec->name, "natural-height"))
1176     gtk_widget_queue_resize (GTK_WIDGET (scaffold));
1177 }
1178
1179 static void
1180 focus_changed_cb (GtkCellArea      *area,
1181                   GtkCellRenderer  *renderer,
1182                   const gchar      *path,
1183                   CellAreaScaffold *scaffold)
1184 {
1185   CellAreaScaffoldPrivate *priv = scaffold->priv;
1186   GtkWidget               *widget = GTK_WIDGET (scaffold);
1187   GtkTreePath             *treepath;
1188   gint                    *indices;
1189
1190   if (!priv->model)
1191     return;
1192
1193   /* We can be signaled that a renderer lost focus, here
1194    * we dont care */
1195   if (!renderer)
1196     return;
1197   
1198   treepath = gtk_tree_path_new_from_string (path);
1199   indices = gtk_tree_path_get_indices (treepath);
1200
1201   priv->focus_row = indices[0];
1202
1203   gtk_tree_path_free (treepath);
1204
1205   /* Make sure we have focus now */
1206   if (!gtk_widget_has_focus (widget))
1207     gtk_widget_grab_focus (widget);
1208
1209   gtk_widget_queue_draw (widget);
1210 }
1211
1212 static void
1213 editing_started_cb (GtkCellArea      *area,
1214                     GtkCellRenderer  *renderer,
1215                     GtkCellEditable  *edit_widget,
1216                     GdkRectangle     *cell_area,
1217                     const gchar      *path,
1218                     CellAreaScaffold *scaffold)
1219 {
1220   GtkAllocation allocation;
1221
1222   gtk_widget_get_allocation (GTK_WIDGET (scaffold), &allocation);
1223
1224   cell_area_scaffold_put_edit_widget (scaffold, GTK_WIDGET (edit_widget),
1225                                       allocation.x + cell_area->x, 
1226                                       allocation.y + cell_area->y, 
1227                                       cell_area->width, cell_area->height);
1228
1229   gtk_cell_editable_start_editing (edit_widget, NULL);
1230   gtk_widget_grab_focus (GTK_WIDGET (edit_widget));
1231 }
1232
1233 static void
1234 remove_editable_cb (GtkCellArea      *area,
1235                     GtkCellRenderer  *renderer,
1236                     GtkCellEditable  *edit_widget,
1237                     CellAreaScaffold *scaffold)
1238 {
1239   gtk_container_remove (GTK_CONTAINER (scaffold), GTK_WIDGET (edit_widget));
1240 }
1241
1242 static void 
1243 rebuild_and_flush_internals (CellAreaScaffold *scaffold)
1244 {
1245   CellAreaScaffoldPrivate *priv = scaffold->priv;
1246   gint n_rows;
1247
1248   if (priv->model)
1249     {
1250       n_rows = gtk_tree_model_iter_n_children (priv->model, NULL);
1251
1252       /* Clear/reset the array */
1253       g_array_set_size (priv->row_data, n_rows);
1254       memset (priv->row_data->data, 0x0, n_rows * sizeof (RowData));
1255     }
1256   else
1257     g_array_set_size (priv->row_data, 0);
1258
1259   /* Data changed, lets flush the iter and consequently queue resize and
1260    * start everything over again (note this is definitly far from optimized) */
1261   gtk_cell_area_iter_flush (priv->iter);
1262 }
1263
1264 static void
1265 row_changed_cb (GtkTreeModel     *model,
1266                 GtkTreePath      *path,
1267                 GtkTreeIter      *iter,
1268                 CellAreaScaffold *scaffold)
1269 {
1270   rebuild_and_flush_internals (scaffold);
1271 }
1272
1273 static void
1274 row_inserted_cb (GtkTreeModel     *model,
1275                  GtkTreePath      *path,
1276                  GtkTreeIter      *iter,
1277                  CellAreaScaffold *scaffold)
1278 {
1279   rebuild_and_flush_internals (scaffold);
1280 }
1281
1282 static void
1283 row_deleted_cb (GtkTreeModel     *model,
1284                 GtkTreePath      *path,
1285                 CellAreaScaffold *scaffold)
1286 {
1287   rebuild_and_flush_internals (scaffold);
1288 }
1289
1290 static void
1291 rows_reordered_cb (GtkTreeModel     *model,
1292                    GtkTreePath      *parent,
1293                    GtkTreeIter      *iter,
1294                    gint             *new_order,
1295                    CellAreaScaffold *scaffold)
1296 {
1297   rebuild_and_flush_internals (scaffold);
1298 }
1299
1300 /*********************************************************
1301  *                         API                           *
1302  *********************************************************/
1303 GtkWidget *
1304 cell_area_scaffold_new (void)
1305 {
1306   return (GtkWidget *)g_object_new (TYPE_CELL_AREA_SCAFFOLD, NULL);
1307 }
1308
1309 GtkCellArea  *
1310 cell_area_scaffold_get_area (CellAreaScaffold *scaffold)
1311 {
1312   CellAreaScaffoldPrivate *priv;
1313   
1314   g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), NULL);
1315
1316   priv = scaffold->priv;
1317
1318   return priv->area;
1319 }
1320
1321 void
1322 cell_area_scaffold_set_model (CellAreaScaffold *scaffold,
1323                               GtkTreeModel     *model)
1324 {
1325   CellAreaScaffoldPrivate *priv;
1326   
1327   g_return_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold));
1328
1329   priv = scaffold->priv;
1330
1331   if (priv->model != model)
1332     {
1333       if (priv->model)
1334         {
1335           g_signal_handler_disconnect (priv->model, priv->row_changed_id);
1336           g_signal_handler_disconnect (priv->model, priv->row_inserted_id);
1337           g_signal_handler_disconnect (priv->model, priv->row_deleted_id);
1338           g_signal_handler_disconnect (priv->model, priv->rows_reordered_id);
1339
1340           g_object_unref (priv->model);
1341         }
1342
1343       priv->model = model;
1344
1345       if (priv->model)
1346         {
1347           g_object_ref (priv->model);
1348
1349           priv->row_changed_id = 
1350             g_signal_connect (priv->model, "row-changed",
1351                               G_CALLBACK (row_changed_cb), scaffold);
1352
1353           priv->row_inserted_id = 
1354             g_signal_connect (priv->model, "row-inserted",
1355                               G_CALLBACK (row_inserted_cb), scaffold);
1356
1357           priv->row_deleted_id = 
1358             g_signal_connect (priv->model, "row-deleted",
1359                               G_CALLBACK (row_deleted_cb), scaffold);
1360
1361           priv->rows_reordered_id = 
1362             g_signal_connect (priv->model, "rows-reordered",
1363                               G_CALLBACK (rows_reordered_cb), scaffold);
1364         }
1365
1366       rebuild_and_flush_internals (scaffold);
1367     }
1368 }
1369
1370 GtkTreeModel *
1371 cell_area_scaffold_get_model (CellAreaScaffold *scaffold)
1372 {
1373   CellAreaScaffoldPrivate *priv;
1374   
1375   g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), NULL);
1376
1377   priv = scaffold->priv;
1378
1379   return priv->model;
1380 }
1381
1382
1383 void
1384 cell_area_scaffold_set_row_spacing (CellAreaScaffold *scaffold,
1385                                     gint              spacing)
1386 {
1387   CellAreaScaffoldPrivate *priv;
1388   
1389   g_return_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold));
1390
1391   priv = scaffold->priv;
1392
1393   if (priv->row_spacing != spacing)
1394     {
1395       priv->row_spacing = spacing;
1396       gtk_widget_queue_resize (GTK_WIDGET (scaffold));
1397     }
1398 }
1399
1400 gint
1401 cell_area_scaffold_get_row_spacing (CellAreaScaffold *scaffold)
1402 {
1403   CellAreaScaffoldPrivate *priv;
1404   
1405   g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), 0);
1406
1407   priv = scaffold->priv;
1408
1409   return priv->row_spacing;
1410 }
1411
1412 void
1413 cell_area_scaffold_set_indentation (CellAreaScaffold *scaffold,
1414                                     gint              indent)
1415 {
1416   CellAreaScaffoldPrivate *priv;
1417   
1418   g_return_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold));
1419
1420   priv = scaffold->priv;
1421
1422   if (priv->indent != indent)
1423     {
1424       priv->indent = indent;
1425       gtk_widget_queue_resize (GTK_WIDGET (scaffold));
1426     }
1427 }
1428
1429 gint
1430 cell_area_scaffold_get_indentation (CellAreaScaffold *scaffold)
1431 {
1432   CellAreaScaffoldPrivate *priv;
1433   
1434   g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), 0);
1435
1436   priv = scaffold->priv;
1437
1438   return priv->indent;
1439 }
1440
1441