]> Pileus Git - ~andy/gtk/blob - tests/cellareascaffold.c
Removed gtk_cell_area_context_sum_preferred_width/height apis.
[~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                                   (GtkCellAreaContext *context,
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      add_editable_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 context */
136   GtkCellArea        *area;
137   GtkCellAreaContext *context;
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           add_editable_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->context = gtk_cell_area_create_context (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->context, "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->add_editable_id =
212     g_signal_connect (priv->area, "add-editable",
213                       G_CALLBACK (add_editable_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->context)
296     {
297       /* Disconnect signals */
298       g_signal_handler_disconnect (priv->context, priv->size_changed_id);
299
300       g_object_unref (priv->context);
301       priv->context = 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->add_editable_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.width;
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->context, 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->context, 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->context, widget, &min, &nat);
573       else
574         gtk_cell_area_get_preferred_height (priv->area, priv->context, widget, &min, &nat);
575
576       valid = gtk_tree_model_iter_next (priv->model, &iter);
577     }
578
579   g_signal_handler_unblock (priv->context, priv->size_changed_id);
580 }
581
582 static void 
583 get_row_sizes (CellAreaScaffold *scaffold, 
584                GArray           *array,
585                gint              for_size)
586 {
587   CellAreaScaffoldPrivate *priv = scaffold->priv;
588   GtkWidget               *widget = GTK_WIDGET (scaffold);
589   GtkOrientation           orientation;
590   GtkTreeIter              iter;
591   gboolean                 valid;
592   gint                     i = 0;
593
594   if (!priv->model)
595     return;
596
597   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
598
599   valid = gtk_tree_model_get_iter_first (priv->model, &iter);
600   while (valid)
601     {
602       RowData *data = &g_array_index (array, RowData, i);
603
604       gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
605
606       if (orientation == GTK_ORIENTATION_HORIZONTAL)
607         gtk_cell_area_get_preferred_height_for_width (priv->area, priv->context, widget, 
608                                                       for_size, &data->size, NULL);
609       else
610         gtk_cell_area_get_preferred_width_for_height (priv->area, priv->context, widget, 
611                                                       for_size, &data->size, NULL);
612
613       i++;
614       valid = gtk_tree_model_iter_next (priv->model, &iter);
615     }
616 }
617
618 static void
619 cell_area_scaffold_size_allocate (GtkWidget           *widget,
620                                   GtkAllocation       *allocation)
621 {
622   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
623   CellAreaScaffoldPrivate *priv     = scaffold->priv;
624   GtkOrientation           orientation;
625
626   gtk_widget_set_allocation (widget, allocation);
627
628   if (gtk_widget_get_realized (widget))
629     gdk_window_move_resize (priv->event_window,
630                             allocation->x,
631                             allocation->y,
632                             allocation->width,
633                             allocation->height);
634
635   /* Allocate the child GtkCellEditable widget if one is currently editing a row */
636   if (priv->edit_widget)
637     gtk_widget_size_allocate (priv->edit_widget, &priv->edit_rect);
638
639   if (!priv->model)
640     return;
641
642   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
643
644   /* Cache the per-row sizes and allocate the context */
645   if (orientation == GTK_ORIENTATION_HORIZONTAL)
646     {
647       gtk_cell_area_context_allocate (priv->context, allocation->width - priv->indent, -1);
648       get_row_sizes (scaffold, priv->row_data, allocation->width - priv->indent);
649     }
650   else
651     {
652       gtk_cell_area_context_allocate (priv->context, -1, allocation->height - priv->indent);
653       get_row_sizes (scaffold, priv->row_data, allocation->height - priv->indent);
654     }
655 }
656
657
658 static void
659 cell_area_scaffold_get_preferred_width (GtkWidget       *widget,
660                                         gint            *minimum_size,
661                                         gint            *natural_size)
662 {
663   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
664   CellAreaScaffoldPrivate *priv     = scaffold->priv;
665   GtkOrientation           orientation;
666
667   if (!priv->model)
668     return;
669
670   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
671
672   if (orientation == GTK_ORIENTATION_HORIZONTAL)
673     {
674       request_all_base (scaffold);
675
676       gtk_cell_area_context_get_preferred_width (priv->context, minimum_size, natural_size);
677
678       *minimum_size += priv->indent;
679       *natural_size += priv->indent;
680     }
681   else
682     {
683       gint min_size, nat_size;
684
685       GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_size, &nat_size);
686       GTK_WIDGET_GET_CLASS (widget)->get_preferred_width_for_height (widget, min_size, 
687                                                                      minimum_size, natural_size);
688     }
689 }
690
691 static void
692 cell_area_scaffold_get_preferred_height_for_width (GtkWidget       *widget,
693                                                    gint             for_size,
694                                                    gint            *minimum_size,
695                                                    gint            *natural_size)
696 {
697   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
698   CellAreaScaffoldPrivate *priv     = scaffold->priv;
699   GtkOrientation           orientation;
700
701   if (!priv->model)
702     return;
703
704   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
705
706   if (orientation == GTK_ORIENTATION_HORIZONTAL)
707     {
708       GArray *request_array;
709       gint    n_rows, i, full_size = 0;
710
711       n_rows = gtk_tree_model_iter_n_children (priv->model, NULL);
712
713       /* Get an array for the contextual request */
714       request_array = g_array_new (FALSE, FALSE, sizeof (RowData));
715       g_array_set_size (request_array, n_rows);
716       memset (request_array->data, 0x0, n_rows * sizeof (RowData));
717
718       /* Gather each contextual size into the request array */
719       get_row_sizes (scaffold, request_array, for_size - priv->indent);
720
721       /* Sum up the size and add some row spacing */
722       for (i = 0; i < n_rows; i++)
723         {
724           RowData *data = &g_array_index (request_array, RowData, i);
725
726           full_size += data->size;
727         }
728
729       full_size += MAX (0, n_rows -1) * priv->row_spacing;
730
731       g_array_free (request_array, TRUE);
732
733       *minimum_size = full_size;
734       *natural_size = full_size;
735     }
736   else
737     {
738       GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_size, natural_size);
739     }
740 }
741
742 static void
743 cell_area_scaffold_get_preferred_height (GtkWidget       *widget,
744                                          gint            *minimum_size,
745                                          gint            *natural_size)
746 {
747   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
748   CellAreaScaffoldPrivate *priv     = scaffold->priv;
749   GtkOrientation           orientation;
750
751   if (!priv->model)
752     return;
753
754   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
755
756   if (orientation == GTK_ORIENTATION_VERTICAL)
757     {
758       request_all_base (scaffold);
759
760       gtk_cell_area_context_get_preferred_height (priv->context, minimum_size, natural_size);
761
762       *minimum_size += priv->indent;
763       *natural_size += priv->indent;
764     }
765   else
766     {
767       gint min_size, nat_size;
768
769       GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_size, &nat_size);
770       GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_size, 
771                                                                      minimum_size, natural_size);
772     }
773 }
774
775 static void
776 cell_area_scaffold_get_preferred_width_for_height (GtkWidget       *widget,
777                                                    gint             for_size,
778                                                    gint            *minimum_size,
779                                                    gint            *natural_size)
780 {
781   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
782   CellAreaScaffoldPrivate *priv     = scaffold->priv;
783   GtkOrientation           orientation;
784
785   if (!priv->model)
786     return;
787
788   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
789
790   if (orientation == GTK_ORIENTATION_VERTICAL)
791     {
792       GArray *request_array;
793       gint    n_rows, i, full_size = 0;
794
795       n_rows = gtk_tree_model_iter_n_children (priv->model, NULL);
796
797       /* Get an array for the contextual request */
798       request_array = g_array_new (FALSE, FALSE, sizeof (RowData));
799       g_array_set_size (request_array, n_rows);
800       memset (request_array->data, 0x0, n_rows * sizeof (RowData));
801
802       /* Gather each contextual size into the request array */
803       get_row_sizes (scaffold, request_array, for_size - priv->indent);
804
805       /* Sum up the size and add some row spacing */
806       for (i = 0; i < n_rows; i++)
807         {
808           RowData *data = &g_array_index (request_array, RowData, i);
809
810           full_size += data->size;
811         }
812
813       full_size += MAX (0, n_rows -1) * priv->row_spacing;
814
815       g_array_free (request_array, TRUE);
816
817       *minimum_size = full_size;
818       *natural_size = full_size;
819     }
820   else
821     {
822       GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_size, natural_size);
823     }
824 }
825
826 static void
827 cell_area_scaffold_map (GtkWidget       *widget)
828 {
829   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
830   CellAreaScaffoldPrivate *priv     = scaffold->priv;
831   
832   GTK_WIDGET_CLASS (cell_area_scaffold_parent_class)->map (widget);
833
834   if (priv->event_window)
835     gdk_window_show (priv->event_window);
836 }
837
838 static void
839 cell_area_scaffold_unmap (GtkWidget       *widget)
840 {
841   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
842   CellAreaScaffoldPrivate *priv     = scaffold->priv;
843   
844   GTK_WIDGET_CLASS (cell_area_scaffold_parent_class)->unmap (widget);
845
846   if (priv->event_window)
847     gdk_window_hide (priv->event_window);
848 }
849
850
851 static gint
852 cell_area_scaffold_focus (GtkWidget       *widget,
853                           GtkDirectionType direction)
854 {
855   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
856   CellAreaScaffoldPrivate *priv     = scaffold->priv;
857   GtkTreeIter              iter;
858   gboolean                 valid;
859   gint                     focus_row;
860   GtkOrientation           orientation;
861   gboolean                 changed = FALSE;
862
863   /* Grab focus on ourself if we dont already have focus */
864   if (!gtk_widget_has_focus (widget))
865     gtk_widget_grab_focus (widget);
866
867   /* Move focus from cell to cell and row to row */
868   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
869   
870   focus_row = priv->focus_row;
871
872   g_signal_handler_block (priv->area, priv->focus_changed_id);
873   
874   valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, priv->focus_row);
875   while (valid)
876     {
877       gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
878       
879       /* If focus stays in the area we dont need to do any more */
880       if (gtk_cell_area_focus (priv->area, direction))
881         {
882           priv->focus_row = focus_row;
883
884           /* XXX A smarter implementation would only invalidate the rectangles where
885            * focus was removed from and new focus was placed */
886           gtk_widget_queue_draw (widget);
887           changed = TRUE;
888           break;
889         }
890       else
891         {
892           if (orientation == GTK_ORIENTATION_HORIZONTAL)
893             {
894               if (direction == GTK_DIR_RIGHT ||
895                   direction == GTK_DIR_LEFT)
896                 break;
897               else if (direction == GTK_DIR_UP ||
898                        direction == GTK_DIR_TAB_BACKWARD)
899                 {
900                   if (focus_row == 0)
901                     break;
902                   else
903                     {
904                       /* XXX A real implementation should check if the
905                        * previous row can focus with it's attributes setup */
906                       focus_row--;
907                       valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, focus_row);
908                     }
909                 }
910               else /* direction == GTK_DIR_DOWN || GTK_DIR_TAB_FORWARD */
911                 {
912                   if (focus_row == priv->row_data->len - 1)
913                     break;
914                   else
915                     {
916                       /* XXX A real implementation should check if the
917                        * previous row can focus with it's attributes setup */
918                       focus_row++;
919                       valid = gtk_tree_model_iter_next (priv->model, &iter);
920                     }
921                 }
922             }
923           else  /* (orientation == GTK_ORIENTATION_HORIZONTAL) */
924             {
925               if (direction == GTK_DIR_UP ||
926                   direction == GTK_DIR_DOWN)
927                 break;
928               else if (direction == GTK_DIR_LEFT ||
929                        direction == GTK_DIR_TAB_BACKWARD)
930                 {
931                   if (focus_row == 0)
932                     break;
933                   else
934                     {
935                       /* XXX A real implementation should check if the
936                        * previous row can focus with it's attributes setup */
937                       focus_row--;
938                       valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, focus_row);
939                     }
940                 }
941               else /* direction == GTK_DIR_RIGHT || GTK_DIR_TAB_FORWARD */
942                 {
943                   if (focus_row == priv->row_data->len - 1)
944                     break;
945                   else
946                     {
947                       /* XXX A real implementation should check if the
948                        * previous row can focus with it's attributes setup */
949                       focus_row++;
950                       valid = gtk_tree_model_iter_next (priv->model, &iter);
951                     }
952                 }
953             }
954         }
955     }
956
957   g_signal_handler_unblock (priv->area, priv->focus_changed_id);
958
959   /* XXX A smarter implementation would only invalidate the rectangles where
960    * focus was removed from and new focus was placed */
961   gtk_widget_queue_draw (widget);
962
963   return changed;
964 }
965
966 static gboolean
967 cell_area_scaffold_button_press (GtkWidget       *widget,
968                                  GdkEventButton  *event)
969 {
970   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
971   CellAreaScaffoldPrivate *priv     = scaffold->priv;
972   GtkTreeIter              iter;
973   gboolean                 valid;
974   GtkOrientation           orientation;
975   gint                     i = 0;
976   GdkRectangle             event_area;
977   GtkAllocation            allocation;
978   gboolean                 handled = FALSE;
979
980   /* Move focus from cell to cell and row to row */
981   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
982
983   gtk_widget_get_allocation (widget, &allocation);
984
985   event_area.x      = 0;
986   event_area.y      = 0;
987   event_area.width  = allocation.width;
988   event_area.height = allocation.height;
989
990   if (orientation == GTK_ORIENTATION_HORIZONTAL)
991     {
992       event_area.x      = priv->indent;
993       event_area.width -= priv->indent;
994     }
995   else
996     {
997       event_area.y       = priv->indent;
998       event_area.height -= priv->indent;
999     }
1000
1001   valid = gtk_tree_model_get_iter_first (priv->model, &iter);
1002   while (valid)
1003     {
1004       RowData *data = &g_array_index (priv->row_data, RowData, i);
1005
1006       if (orientation == GTK_ORIENTATION_HORIZONTAL)
1007         {
1008           event_area.height = data->size;
1009
1010           if (event->y >= event_area.y && 
1011               event->y <= event_area.y + event_area.height)
1012             {
1013               /* XXX A real implementation would assemble GtkCellRendererState flags here */
1014               gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
1015               handled = gtk_cell_area_event (priv->area, priv->context, GTK_WIDGET (scaffold),
1016                                              (GdkEvent *)event, &event_area, 0);
1017               break;
1018             }
1019
1020           event_area.y += data->size;
1021           event_area.y += priv->row_spacing;
1022         }
1023       else
1024         {
1025           event_area.width = data->size;
1026
1027           if (event->x >= event_area.x && 
1028               event->x <= event_area.x + event_area.width)
1029             {
1030               /* XXX A real implementation would assemble GtkCellRendererState flags here */
1031               gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
1032               handled = gtk_cell_area_event (priv->area, priv->context, GTK_WIDGET (scaffold),
1033                                              (GdkEvent *)event, &event_area, 0);
1034               break;
1035             }
1036
1037           event_area.x += data->size;
1038           event_area.x += priv->row_spacing;
1039         }
1040
1041       i++;
1042       valid = gtk_tree_model_iter_next (priv->model, &iter);
1043     }
1044
1045   return handled;
1046 }
1047
1048
1049 /*********************************************************
1050  *                   GtkContainerClass                   *
1051  *********************************************************/
1052 static void
1053 cell_area_scaffold_put_edit_widget (CellAreaScaffold *scaffold,
1054                                     GtkWidget        *edit_widget,
1055                                     gint              x,
1056                                     gint              y,
1057                                     gint              width,
1058                                     gint              height)
1059 {
1060   CellAreaScaffoldPrivate *priv = scaffold->priv;
1061
1062   priv->edit_rect.x      = x;
1063   priv->edit_rect.y      = y;
1064   priv->edit_rect.width  = width;
1065   priv->edit_rect.height = height;
1066   priv->edit_widget      = edit_widget;
1067
1068   gtk_widget_set_parent (edit_widget, GTK_WIDGET (scaffold));
1069 }
1070
1071 static void
1072 cell_area_scaffold_forall (GtkContainer    *container,
1073                            gboolean         include_internals,
1074                            GtkCallback      callback,
1075                            gpointer         callback_data)
1076 {
1077   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (container);
1078   CellAreaScaffoldPrivate *priv     = scaffold->priv;
1079
1080   if (priv->edit_widget)
1081     (* callback) (priv->edit_widget, callback_data);
1082 }
1083
1084 static void
1085 cell_area_scaffold_remove (GtkContainer    *container,
1086                            GtkWidget       *child)
1087 {
1088   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (container);
1089   CellAreaScaffoldPrivate *priv     = scaffold->priv;
1090
1091   g_return_if_fail (child == priv->edit_widget);
1092
1093   gtk_widget_unparent (priv->edit_widget);
1094   priv->edit_widget = NULL;
1095 }
1096
1097 /*********************************************************
1098  *                CellAreaScaffoldClass                  *
1099  *********************************************************/
1100 static void
1101 cell_area_scaffold_activate (CellAreaScaffold *scaffold)
1102 {
1103   CellAreaScaffoldPrivate *priv     = scaffold->priv;
1104   GtkWidget               *widget   = GTK_WIDGET (scaffold);
1105   GtkAllocation            allocation;
1106   GtkOrientation           orientation;
1107   GdkRectangle             cell_area;
1108   GtkTreeIter              iter;
1109   gboolean                 valid;
1110   gint                     i = 0;
1111
1112   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (priv->area));
1113   gtk_widget_get_allocation (widget, &allocation);
1114
1115   cell_area.x = 0;
1116   cell_area.y = 0;
1117   cell_area.width  = allocation.width;
1118   cell_area.height = allocation.height;
1119
1120   if (orientation == GTK_ORIENTATION_HORIZONTAL)
1121     {
1122       cell_area.x      = priv->indent;
1123       cell_area.width -= priv->indent;
1124     }
1125   else
1126     {
1127       cell_area.y       = priv->indent;
1128       cell_area.height -= priv->indent;
1129     }
1130
1131   valid = gtk_tree_model_get_iter_first (priv->model, &iter);
1132   while (valid)
1133     {
1134       RowData *data = &g_array_index (priv->row_data, RowData, i);
1135
1136       if (i == priv->focus_row)
1137         {
1138           if (orientation == GTK_ORIENTATION_HORIZONTAL)
1139             cell_area.height = data->size;
1140           else
1141             cell_area.width = data->size;
1142
1143           gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
1144           gtk_cell_area_activate (priv->area, priv->context, widget, &cell_area, GTK_CELL_RENDERER_FOCUSED);
1145
1146           break;
1147         }
1148
1149       if (orientation == GTK_ORIENTATION_HORIZONTAL)
1150         cell_area.y += data->size + priv->row_spacing;
1151       else
1152         cell_area.x += data->size + priv->row_spacing;
1153
1154       i++;
1155       valid = gtk_tree_model_iter_next (priv->model, &iter);
1156     }
1157 }
1158
1159 /*********************************************************
1160  *           CellArea/GtkTreeModel callbacks             *
1161  *********************************************************/
1162 static void
1163 size_changed_cb (GtkCellAreaContext  *context,
1164                  GParamSpec          *pspec,
1165                  CellAreaScaffold    *scaffold)
1166 {
1167   if (!strcmp (pspec->name, "minimum-width") ||
1168       !strcmp (pspec->name, "natural-width") ||
1169       !strcmp (pspec->name, "minimum-height") ||
1170       !strcmp (pspec->name, "natural-height"))
1171     gtk_widget_queue_resize (GTK_WIDGET (scaffold));
1172 }
1173
1174 static void
1175 focus_changed_cb (GtkCellArea      *area,
1176                   GtkCellRenderer  *renderer,
1177                   const gchar      *path,
1178                   CellAreaScaffold *scaffold)
1179 {
1180   CellAreaScaffoldPrivate *priv = scaffold->priv;
1181   GtkWidget               *widget = GTK_WIDGET (scaffold);
1182   GtkTreePath             *treepath;
1183   gint                    *indices;
1184
1185   if (!priv->model)
1186     return;
1187
1188   /* We can be signaled that a renderer lost focus, here
1189    * we dont care */
1190   if (!renderer)
1191     return;
1192   
1193   treepath = gtk_tree_path_new_from_string (path);
1194   indices = gtk_tree_path_get_indices (treepath);
1195
1196   priv->focus_row = indices[0];
1197
1198   gtk_tree_path_free (treepath);
1199
1200   /* Make sure we have focus now */
1201   if (!gtk_widget_has_focus (widget))
1202     gtk_widget_grab_focus (widget);
1203
1204   gtk_widget_queue_draw (widget);
1205 }
1206
1207 static void
1208 add_editable_cb (GtkCellArea      *area,
1209                  GtkCellRenderer  *renderer,
1210                  GtkCellEditable  *edit_widget,
1211                  GdkRectangle     *cell_area,
1212                  const gchar      *path,
1213                  CellAreaScaffold *scaffold)
1214 {
1215   GtkAllocation allocation;
1216
1217   gtk_widget_get_allocation (GTK_WIDGET (scaffold), &allocation);
1218
1219   cell_area_scaffold_put_edit_widget (scaffold, GTK_WIDGET (edit_widget),
1220                                       allocation.x + cell_area->x, 
1221                                       allocation.y + cell_area->y, 
1222                                       cell_area->width, cell_area->height);
1223 }
1224
1225 static void
1226 remove_editable_cb (GtkCellArea      *area,
1227                     GtkCellRenderer  *renderer,
1228                     GtkCellEditable  *edit_widget,
1229                     CellAreaScaffold *scaffold)
1230 {
1231   gtk_container_remove (GTK_CONTAINER (scaffold), GTK_WIDGET (edit_widget));
1232
1233   gtk_widget_grab_focus (GTK_WIDGET (scaffold));
1234 }
1235
1236 static void 
1237 rebuild_and_reset_internals (CellAreaScaffold *scaffold)
1238 {
1239   CellAreaScaffoldPrivate *priv = scaffold->priv;
1240   gint n_rows;
1241
1242   if (priv->model)
1243     {
1244       n_rows = gtk_tree_model_iter_n_children (priv->model, NULL);
1245
1246       /* Clear/reset the array */
1247       g_array_set_size (priv->row_data, n_rows);
1248       memset (priv->row_data->data, 0x0, n_rows * sizeof (RowData));
1249     }
1250   else
1251     g_array_set_size (priv->row_data, 0);
1252
1253   /* Data changed, lets reset the context and consequently queue resize and
1254    * start everything over again (note this is definitly far from optimized) */
1255   gtk_cell_area_context_reset (priv->context);
1256 }
1257
1258 static void
1259 row_changed_cb (GtkTreeModel     *model,
1260                 GtkTreePath      *path,
1261                 GtkTreeIter      *iter,
1262                 CellAreaScaffold *scaffold)
1263 {
1264   rebuild_and_reset_internals (scaffold);
1265 }
1266
1267 static void
1268 row_inserted_cb (GtkTreeModel     *model,
1269                  GtkTreePath      *path,
1270                  GtkTreeIter      *iter,
1271                  CellAreaScaffold *scaffold)
1272 {
1273   rebuild_and_reset_internals (scaffold);
1274 }
1275
1276 static void
1277 row_deleted_cb (GtkTreeModel     *model,
1278                 GtkTreePath      *path,
1279                 CellAreaScaffold *scaffold)
1280 {
1281   rebuild_and_reset_internals (scaffold);
1282 }
1283
1284 static void
1285 rows_reordered_cb (GtkTreeModel     *model,
1286                    GtkTreePath      *parent,
1287                    GtkTreeIter      *iter,
1288                    gint             *new_order,
1289                    CellAreaScaffold *scaffold)
1290 {
1291   rebuild_and_reset_internals (scaffold);
1292 }
1293
1294 /*********************************************************
1295  *                         API                           *
1296  *********************************************************/
1297 GtkWidget *
1298 cell_area_scaffold_new (void)
1299 {
1300   return (GtkWidget *)g_object_new (TYPE_CELL_AREA_SCAFFOLD, NULL);
1301 }
1302
1303 GtkCellArea  *
1304 cell_area_scaffold_get_area (CellAreaScaffold *scaffold)
1305 {
1306   CellAreaScaffoldPrivate *priv;
1307   
1308   g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), NULL);
1309
1310   priv = scaffold->priv;
1311
1312   return priv->area;
1313 }
1314
1315 void
1316 cell_area_scaffold_set_model (CellAreaScaffold *scaffold,
1317                               GtkTreeModel     *model)
1318 {
1319   CellAreaScaffoldPrivate *priv;
1320   
1321   g_return_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold));
1322
1323   priv = scaffold->priv;
1324
1325   if (priv->model != model)
1326     {
1327       if (priv->model)
1328         {
1329           g_signal_handler_disconnect (priv->model, priv->row_changed_id);
1330           g_signal_handler_disconnect (priv->model, priv->row_inserted_id);
1331           g_signal_handler_disconnect (priv->model, priv->row_deleted_id);
1332           g_signal_handler_disconnect (priv->model, priv->rows_reordered_id);
1333
1334           g_object_unref (priv->model);
1335         }
1336
1337       priv->model = model;
1338
1339       if (priv->model)
1340         {
1341           g_object_ref (priv->model);
1342
1343           priv->row_changed_id = 
1344             g_signal_connect (priv->model, "row-changed",
1345                               G_CALLBACK (row_changed_cb), scaffold);
1346
1347           priv->row_inserted_id = 
1348             g_signal_connect (priv->model, "row-inserted",
1349                               G_CALLBACK (row_inserted_cb), scaffold);
1350
1351           priv->row_deleted_id = 
1352             g_signal_connect (priv->model, "row-deleted",
1353                               G_CALLBACK (row_deleted_cb), scaffold);
1354
1355           priv->rows_reordered_id = 
1356             g_signal_connect (priv->model, "rows-reordered",
1357                               G_CALLBACK (rows_reordered_cb), scaffold);
1358         }
1359
1360       rebuild_and_reset_internals (scaffold);
1361     }
1362 }
1363
1364 GtkTreeModel *
1365 cell_area_scaffold_get_model (CellAreaScaffold *scaffold)
1366 {
1367   CellAreaScaffoldPrivate *priv;
1368   
1369   g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), NULL);
1370
1371   priv = scaffold->priv;
1372
1373   return priv->model;
1374 }
1375
1376
1377 void
1378 cell_area_scaffold_set_row_spacing (CellAreaScaffold *scaffold,
1379                                     gint              spacing)
1380 {
1381   CellAreaScaffoldPrivate *priv;
1382   
1383   g_return_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold));
1384
1385   priv = scaffold->priv;
1386
1387   if (priv->row_spacing != spacing)
1388     {
1389       priv->row_spacing = spacing;
1390       gtk_widget_queue_resize (GTK_WIDGET (scaffold));
1391     }
1392 }
1393
1394 gint
1395 cell_area_scaffold_get_row_spacing (CellAreaScaffold *scaffold)
1396 {
1397   CellAreaScaffoldPrivate *priv;
1398   
1399   g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), 0);
1400
1401   priv = scaffold->priv;
1402
1403   return priv->row_spacing;
1404 }
1405
1406 void
1407 cell_area_scaffold_set_indentation (CellAreaScaffold *scaffold,
1408                                     gint              indent)
1409 {
1410   CellAreaScaffoldPrivate *priv;
1411   
1412   g_return_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold));
1413
1414   priv = scaffold->priv;
1415
1416   if (priv->indent != indent)
1417     {
1418       priv->indent = indent;
1419       gtk_widget_queue_resize (GTK_WIDGET (scaffold));
1420     }
1421 }
1422
1423 gint
1424 cell_area_scaffold_get_indentation (CellAreaScaffold *scaffold)
1425 {
1426   CellAreaScaffoldPrivate *priv;
1427   
1428   g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), 0);
1429
1430   priv = scaffold->priv;
1431
1432   return priv->indent;
1433 }
1434
1435