]> Pileus Git - ~andy/gtk/blob - tests/cellareascaffold.c
Change FSF Address
[~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, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <string.h>
23 #include "cellareascaffold.h"
24
25 /* GObjectClass */
26 static void      cell_area_scaffold_finalize                       (GObject              *object);
27 static void      cell_area_scaffold_dispose                        (GObject              *object);
28
29 /* GtkWidgetClass */
30 static void      cell_area_scaffold_realize                        (GtkWidget       *widget);
31 static void      cell_area_scaffold_unrealize                      (GtkWidget       *widget);
32 static gboolean  cell_area_scaffold_draw                           (GtkWidget       *widget,
33                                                                     cairo_t         *cr);
34 static void      cell_area_scaffold_size_allocate                  (GtkWidget       *widget,
35                                                                     GtkAllocation   *allocation);
36 static void      cell_area_scaffold_get_preferred_width            (GtkWidget       *widget,
37                                                                     gint            *minimum_size,
38                                                                     gint            *natural_size);
39 static void      cell_area_scaffold_get_preferred_height_for_width (GtkWidget       *widget,
40                                                                     gint             for_size,
41                                                                     gint            *minimum_size,
42                                                                     gint            *natural_size);
43 static void      cell_area_scaffold_get_preferred_height           (GtkWidget       *widget,
44                                                                     gint            *minimum_size,
45                                                                     gint            *natural_size);
46 static void      cell_area_scaffold_get_preferred_width_for_height (GtkWidget       *widget,
47                                                                     gint             for_size,
48                                                                     gint            *minimum_size,
49                                                                     gint            *natural_size);
50 static void      cell_area_scaffold_map                            (GtkWidget       *widget);
51 static void      cell_area_scaffold_unmap                          (GtkWidget       *widget);
52 static gint      cell_area_scaffold_focus                          (GtkWidget       *widget,
53                                                                     GtkDirectionType direction);
54 static gboolean  cell_area_scaffold_button_press                   (GtkWidget       *widget,
55                                                                     GdkEventButton  *event);
56
57 /* GtkContainerClass */
58 static void      cell_area_scaffold_forall                         (GtkContainer    *container,
59                                                                     gboolean         include_internals,
60                                                                     GtkCallback      callback,
61                                                                     gpointer         callback_data);
62 static void      cell_area_scaffold_remove                         (GtkContainer    *container,
63                                                                     GtkWidget       *child);
64 static void      cell_area_scaffold_put_edit_widget                (CellAreaScaffold *scaffold,
65                                                                     GtkWidget        *edit_widget,
66                                                                     gint              x,
67                                                                     gint              y,
68                                                                     gint              width,
69                                                                     gint              height);
70
71 /* CellAreaScaffoldClass */
72 static void      cell_area_scaffold_activate                       (CellAreaScaffold *scaffold);
73
74 /* CellArea/GtkTreeModel callbacks */
75 static void      size_changed_cb                                   (GtkCellAreaContext *context,
76                                                                     GParamSpec       *pspec,
77                                                                     CellAreaScaffold *scaffold);
78 static void      focus_changed_cb                                  (GtkCellArea      *area,
79                                                                     GtkCellRenderer  *renderer,
80                                                                     const gchar      *path,
81                                                                     CellAreaScaffold *scaffold);
82 static void      add_editable_cb                                   (GtkCellArea      *area,
83                                                                     GtkCellRenderer  *renderer,
84                                                                     GtkCellEditable  *edit_widget,
85                                                                     GdkRectangle     *cell_area,
86                                                                     const gchar      *path,
87                                                                     CellAreaScaffold *scaffold);
88 static void      remove_editable_cb                                (GtkCellArea      *area,
89                                                                     GtkCellRenderer  *renderer,
90                                                                     GtkCellEditable  *edit_widget,
91                                                                     CellAreaScaffold *scaffold);
92 static void      row_changed_cb                                    (GtkTreeModel     *model,
93                                                                     GtkTreePath      *path,
94                                                                     GtkTreeIter      *iter,
95                                                                     CellAreaScaffold *scaffold);
96 static void      row_inserted_cb                                   (GtkTreeModel     *model,
97                                                                     GtkTreePath      *path,
98                                                                     GtkTreeIter      *iter,
99                                                                     CellAreaScaffold *scaffold);
100 static void      row_deleted_cb                                    (GtkTreeModel     *model,
101                                                                     GtkTreePath      *path,
102                                                                     CellAreaScaffold *scaffold);
103 static void      rows_reordered_cb                                 (GtkTreeModel     *model,
104                                                                     GtkTreePath      *parent,
105                                                                     GtkTreeIter      *iter,
106                                                                     gint             *new_order,
107                                                                     CellAreaScaffold *scaffold);
108
109 typedef struct {
110   gint    size; /* The height of rows in the scaffold's */
111 } RowData;
112
113 struct _CellAreaScaffoldPrivate {
114
115   /* Window for catching events and dispatching them to the cell area */
116   GdkWindow       *event_window;
117
118   /* The model we're showing data for */
119   GtkTreeModel    *model;
120   gulong           row_changed_id;
121   gulong           row_inserted_id;
122   gulong           row_deleted_id;
123   gulong           rows_reordered_id;
124
125   /* The area rendering the data and a global context */
126   GtkCellArea        *area;
127   GtkCellAreaContext *context;
128
129   /* Cache some info about rows (hieghts etc) */
130   GArray          *row_data;
131
132   /* Focus handling */
133   gint             focus_row;
134   gulong           focus_changed_id;
135
136   /* Check when the underlying area changes the size and
137    * we need to queue a redraw */
138   gulong           size_changed_id;
139
140   /* Currently edited widget */
141   GtkWidget       *edit_widget;
142   GdkRectangle     edit_rect;
143   gulong           add_editable_id;
144   gulong           remove_editable_id;
145
146
147   gint             row_spacing;
148   gint             indent;
149 };
150
151 enum {
152   ACTIVATE,
153   N_SIGNALS
154 };
155
156 static guint scaffold_signals[N_SIGNALS] = { 0 };
157
158 #define DIRECTION_STR(dir)                              \
159   ((dir) == GTK_DIR_TAB_FORWARD  ? "tab forward" :      \
160    (dir) == GTK_DIR_TAB_BACKWARD ? "tab backward" :     \
161    (dir) == GTK_DIR_UP           ? "up" :               \
162    (dir) == GTK_DIR_DOWN         ? "down" :             \
163    (dir) == GTK_DIR_LEFT         ? "left" :             \
164    (dir) == GTK_DIR_RIGHT        ? "right" : "invalid")
165
166 G_DEFINE_TYPE_WITH_CODE (CellAreaScaffold, cell_area_scaffold, GTK_TYPE_CONTAINER,
167                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL));
168
169
170 static void
171 cell_area_scaffold_init (CellAreaScaffold *scaffold)
172 {
173   CellAreaScaffoldPrivate *priv;
174
175   scaffold->priv = G_TYPE_INSTANCE_GET_PRIVATE (scaffold,
176                                                 TYPE_CELL_AREA_SCAFFOLD,
177                                                 CellAreaScaffoldPrivate);
178   priv = scaffold->priv;
179
180   priv->area    = gtk_cell_area_box_new ();
181   priv->context = gtk_cell_area_create_context (priv->area);
182
183   priv->row_data = g_array_new (FALSE, FALSE, sizeof (RowData));
184
185   gtk_widget_set_has_window (GTK_WIDGET (scaffold), FALSE);
186   gtk_widget_set_can_focus (GTK_WIDGET (scaffold), TRUE);
187
188   priv->size_changed_id = 
189     g_signal_connect (priv->context, "notify",
190                       G_CALLBACK (size_changed_cb), scaffold);
191
192   priv->focus_changed_id =
193     g_signal_connect (priv->area, "focus-changed",
194                       G_CALLBACK (focus_changed_cb), scaffold);
195
196   priv->add_editable_id =
197     g_signal_connect (priv->area, "add-editable",
198                       G_CALLBACK (add_editable_cb), scaffold);
199
200   priv->remove_editable_id =
201     g_signal_connect (priv->area, "remove-editable",
202                       G_CALLBACK (remove_editable_cb), scaffold);
203 }
204
205 static void
206 cell_area_scaffold_class_init (CellAreaScaffoldClass *class)
207 {
208   GObjectClass      *gobject_class;
209   GtkWidgetClass    *widget_class;
210   GtkContainerClass *container_class;
211
212   gobject_class = G_OBJECT_CLASS (class);
213   gobject_class->dispose = cell_area_scaffold_dispose;
214   gobject_class->finalize = cell_area_scaffold_finalize;
215
216   widget_class = GTK_WIDGET_CLASS (class);
217   widget_class->realize = cell_area_scaffold_realize;
218   widget_class->unrealize = cell_area_scaffold_unrealize;
219   widget_class->draw = cell_area_scaffold_draw;
220   widget_class->size_allocate = cell_area_scaffold_size_allocate;
221   widget_class->get_preferred_width = cell_area_scaffold_get_preferred_width;
222   widget_class->get_preferred_height_for_width = cell_area_scaffold_get_preferred_height_for_width;
223   widget_class->get_preferred_height = cell_area_scaffold_get_preferred_height;
224   widget_class->get_preferred_width_for_height = cell_area_scaffold_get_preferred_width_for_height;
225   widget_class->map = cell_area_scaffold_map;
226   widget_class->unmap = cell_area_scaffold_unmap;
227   widget_class->focus = cell_area_scaffold_focus;
228   widget_class->button_press_event = cell_area_scaffold_button_press;
229
230   container_class = GTK_CONTAINER_CLASS (class);
231   container_class->forall = cell_area_scaffold_forall;
232   container_class->remove = cell_area_scaffold_remove;
233
234   class->activate = cell_area_scaffold_activate;
235
236   scaffold_signals[ACTIVATE] =
237     g_signal_new ("activate",
238                   G_OBJECT_CLASS_TYPE (gobject_class),
239                   G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
240                   G_STRUCT_OFFSET (CellAreaScaffoldClass, activate),
241                   NULL, NULL,
242                   g_cclosure_marshal_VOID__VOID,
243                   G_TYPE_NONE, 0);
244   widget_class->activate_signal = scaffold_signals[ACTIVATE];
245
246
247   g_type_class_add_private (gobject_class, sizeof (CellAreaScaffoldPrivate));
248 }
249
250 /*********************************************************
251  *                    GObjectClass                       *
252  *********************************************************/
253 static void
254 cell_area_scaffold_finalize (GObject *object)
255 {
256   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (object);
257   CellAreaScaffoldPrivate *priv;
258
259   priv = scaffold->priv;
260
261   g_array_free (priv->row_data, TRUE);
262
263   G_OBJECT_CLASS (cell_area_scaffold_parent_class)->finalize (object);
264 }
265
266 static void
267 cell_area_scaffold_dispose (GObject *object)
268 {
269   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (object);
270   CellAreaScaffoldPrivate *priv;
271
272   priv = scaffold->priv;
273
274   cell_area_scaffold_set_model (scaffold, NULL);
275
276   if (priv->context)
277     {
278       /* Disconnect signals */
279       g_signal_handler_disconnect (priv->context, priv->size_changed_id);
280
281       g_object_unref (priv->context);
282       priv->context = NULL;
283       priv->size_changed_id = 0;
284     }
285
286   if (priv->area)
287     {
288       /* Disconnect signals */
289       g_signal_handler_disconnect (priv->area, priv->focus_changed_id);
290       g_signal_handler_disconnect (priv->area, priv->add_editable_id);
291       g_signal_handler_disconnect (priv->area, priv->remove_editable_id);
292
293       g_object_unref (priv->area);
294       priv->area = NULL;
295       priv->focus_changed_id = 0;
296     }
297
298   G_OBJECT_CLASS (cell_area_scaffold_parent_class)->dispose (object);  
299 }
300
301 /*********************************************************
302  *                    GtkWidgetClass                     *
303  *********************************************************/
304 static void
305 cell_area_scaffold_realize (GtkWidget *widget)
306 {
307   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
308   CellAreaScaffoldPrivate *priv     = scaffold->priv;
309   GtkAllocation            allocation;
310   GdkWindow               *window;
311   GdkWindowAttr            attributes;
312   gint                     attributes_mask;
313
314   gtk_widget_get_allocation (widget, &allocation);
315
316   gtk_widget_set_realized (widget, TRUE);
317
318   attributes.window_type = GDK_WINDOW_CHILD;
319   attributes.x = allocation.x;
320   attributes.y = allocation.y;
321   attributes.width = allocation.width;
322   attributes.height = allocation.height;
323   attributes.wclass = GDK_INPUT_ONLY;
324   attributes.event_mask = gtk_widget_get_events (widget);
325   attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
326                             GDK_BUTTON_RELEASE_MASK |
327                             GDK_KEY_PRESS_MASK |
328                             GDK_KEY_RELEASE_MASK);
329
330   attributes_mask = GDK_WA_X | GDK_WA_Y;
331
332   window = gtk_widget_get_parent_window (widget);
333   gtk_widget_set_window (widget, window);
334   g_object_ref (window);
335
336   priv->event_window = gdk_window_new (window, &attributes, attributes_mask);
337   gdk_window_set_user_data (priv->event_window, widget);
338 }
339
340 static void
341 cell_area_scaffold_unrealize (GtkWidget *widget)
342 {
343   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
344   CellAreaScaffoldPrivate *priv     = scaffold->priv;
345
346   if (priv->event_window)
347     {
348       gdk_window_set_user_data (priv->event_window, NULL);
349       gdk_window_destroy (priv->event_window);
350       priv->event_window = NULL;
351     }
352   
353   GTK_WIDGET_CLASS (cell_area_scaffold_parent_class)->unrealize (widget);
354 }
355
356 static gboolean
357 cell_area_scaffold_draw (GtkWidget       *widget,
358                          cairo_t         *cr)
359 {
360   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
361   CellAreaScaffoldPrivate *priv     = scaffold->priv;
362   GtkTreeIter              iter;
363   gboolean                 valid;
364   GdkRectangle             background_area;
365   GdkRectangle             render_area;
366   GtkAllocation            allocation;
367   gint                     i = 0;
368   gboolean                 have_focus;
369   GtkCellRendererState     flags;
370
371   if (!priv->model)
372     return FALSE;
373
374   have_focus  = gtk_widget_has_focus (widget);
375
376   gtk_widget_get_allocation (widget, &allocation);
377
378   render_area.x      = 0;
379   render_area.y      = 0;
380   render_area.width  = allocation.width;
381   render_area.height = allocation.height;
382
383   background_area = render_area;
384
385   render_area.x      = priv->indent;
386   render_area.width -= priv->indent;
387
388   valid = gtk_tree_model_get_iter_first (priv->model, &iter);
389   while (valid)
390     {
391       RowData *data = &g_array_index (priv->row_data, RowData, i);
392
393       if (have_focus && i == priv->focus_row)
394         flags = GTK_CELL_RENDERER_FOCUSED;
395       else
396         flags = 0;
397
398       render_area.height     = data->size;
399
400       background_area.height = render_area.height;
401       background_area.y      = render_area.y;
402
403       if (i == 0)
404         {
405           background_area.height += priv->row_spacing / 2;
406           background_area.height += priv->row_spacing % 2;
407         }
408       else if (i == priv->row_data->len - 1)
409         {
410           background_area.y      -= priv->row_spacing / 2;
411           background_area.height += priv->row_spacing / 2;
412         }
413       else
414         {
415           background_area.y      -= priv->row_spacing / 2;
416           background_area.height += priv->row_spacing;
417         }
418
419       gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
420       gtk_cell_area_render (priv->area, priv->context, widget, cr, 
421                             &background_area, &render_area, flags,
422                             (have_focus && i == priv->focus_row));
423
424       render_area.y += data->size;
425       render_area.y += priv->row_spacing;
426
427       i++;
428       valid = gtk_tree_model_iter_next (priv->model, &iter);
429     }
430
431   /* Draw the edit widget after drawing everything else */
432   GTK_WIDGET_CLASS (cell_area_scaffold_parent_class)->draw (widget, cr);
433
434   return FALSE;
435 }
436
437 static void 
438 request_all_base (CellAreaScaffold *scaffold)
439 {
440   CellAreaScaffoldPrivate *priv = scaffold->priv;
441   GtkWidget               *widget = GTK_WIDGET (scaffold);
442   GtkTreeIter              iter;
443   gboolean                 valid;
444
445   if (!priv->model)
446     return;
447
448   g_signal_handler_block (priv->context, priv->size_changed_id);
449
450   valid = gtk_tree_model_get_iter_first (priv->model, &iter);
451   while (valid)
452     {
453       gint min, nat;
454
455       gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
456       gtk_cell_area_get_preferred_width (priv->area, priv->context, widget, &min, &nat);
457
458       valid = gtk_tree_model_iter_next (priv->model, &iter);
459     }
460
461   g_signal_handler_unblock (priv->context, priv->size_changed_id);
462 }
463
464 static void 
465 get_row_sizes (CellAreaScaffold *scaffold, 
466                GArray           *array,
467                gint              for_size)
468 {
469   CellAreaScaffoldPrivate *priv = scaffold->priv;
470   GtkWidget               *widget = GTK_WIDGET (scaffold);
471   GtkTreeIter              iter;
472   gboolean                 valid;
473   gint                     i = 0;
474
475   if (!priv->model)
476     return;
477
478   valid = gtk_tree_model_get_iter_first (priv->model, &iter);
479   while (valid)
480     {
481       RowData *data = &g_array_index (array, RowData, i);
482
483       gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
484       gtk_cell_area_get_preferred_height_for_width (priv->area, priv->context, widget, 
485                                                     for_size, &data->size, NULL);
486
487       i++;
488       valid = gtk_tree_model_iter_next (priv->model, &iter);
489     }
490 }
491
492 static void
493 cell_area_scaffold_size_allocate (GtkWidget           *widget,
494                                   GtkAllocation       *allocation)
495 {
496   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
497   CellAreaScaffoldPrivate *priv     = scaffold->priv;
498
499   gtk_widget_set_allocation (widget, allocation);
500
501   if (gtk_widget_get_realized (widget))
502     gdk_window_move_resize (priv->event_window,
503                             allocation->x,
504                             allocation->y,
505                             allocation->width,
506                             allocation->height);
507
508   /* Allocate the child GtkCellEditable widget if one is currently editing a row */
509   if (priv->edit_widget)
510     gtk_widget_size_allocate (priv->edit_widget, &priv->edit_rect);
511
512   if (!priv->model)
513     return;
514
515   /* Cache the per-row sizes and allocate the context */
516   gtk_cell_area_context_allocate (priv->context, allocation->width - priv->indent, -1);
517   get_row_sizes (scaffold, priv->row_data, allocation->width - priv->indent);
518 }
519
520
521 static void
522 cell_area_scaffold_get_preferred_width (GtkWidget       *widget,
523                                         gint            *minimum_size,
524                                         gint            *natural_size)
525 {
526   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
527   CellAreaScaffoldPrivate *priv     = scaffold->priv;
528
529   if (!priv->model)
530     return;
531
532   request_all_base (scaffold);
533   gtk_cell_area_context_get_preferred_width (priv->context, minimum_size, natural_size);
534   
535   *minimum_size += priv->indent;
536   *natural_size += priv->indent;
537 }
538
539 static void
540 cell_area_scaffold_get_preferred_height_for_width (GtkWidget       *widget,
541                                                    gint             for_size,
542                                                    gint            *minimum_size,
543                                                    gint            *natural_size)
544 {
545   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
546   CellAreaScaffoldPrivate *priv     = scaffold->priv;
547   GArray                  *request_array;
548   gint                     n_rows, i, full_size = 0;
549
550   if (!priv->model)
551     return;
552
553   n_rows = gtk_tree_model_iter_n_children (priv->model, NULL);
554
555   /* Get an array for the contextual request */
556   request_array = g_array_new (FALSE, FALSE, sizeof (RowData));
557   g_array_set_size (request_array, n_rows);
558   memset (request_array->data, 0x0, n_rows * sizeof (RowData));
559
560   /* Gather each contextual size into the request array */
561   get_row_sizes (scaffold, request_array, for_size - priv->indent);
562
563   /* Sum up the size and add some row spacing */
564   for (i = 0; i < n_rows; i++)
565     {
566       RowData *data = &g_array_index (request_array, RowData, i);
567       
568       full_size += data->size;
569     }
570   
571   full_size += MAX (0, n_rows -1) * priv->row_spacing;
572   
573   g_array_free (request_array, TRUE);
574   
575   *minimum_size = full_size;
576   *natural_size = full_size;
577 }
578
579 static void
580 cell_area_scaffold_get_preferred_height (GtkWidget       *widget,
581                                          gint            *minimum_size,
582                                          gint            *natural_size)
583 {
584   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
585   CellAreaScaffoldPrivate *priv     = scaffold->priv;
586   gint                     min_size, nat_size;
587
588   if (!priv->model)
589     return;
590
591   GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_size, &nat_size);
592   GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_size, 
593                                                                  minimum_size, natural_size);
594 }
595
596 static void
597 cell_area_scaffold_get_preferred_width_for_height (GtkWidget       *widget,
598                                                    gint             for_size,
599                                                    gint            *minimum_size,
600                                                    gint            *natural_size)
601 {
602   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
603   CellAreaScaffoldPrivate *priv     = scaffold->priv;
604
605   if (!priv->model)
606     return;
607
608   GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_size, natural_size);
609 }
610
611 static void
612 cell_area_scaffold_map (GtkWidget       *widget)
613 {
614   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
615   CellAreaScaffoldPrivate *priv     = scaffold->priv;
616   
617   GTK_WIDGET_CLASS (cell_area_scaffold_parent_class)->map (widget);
618
619   if (priv->event_window)
620     gdk_window_show (priv->event_window);
621 }
622
623 static void
624 cell_area_scaffold_unmap (GtkWidget       *widget)
625 {
626   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
627   CellAreaScaffoldPrivate *priv     = scaffold->priv;
628   
629   GTK_WIDGET_CLASS (cell_area_scaffold_parent_class)->unmap (widget);
630
631   if (priv->event_window)
632     gdk_window_hide (priv->event_window);
633 }
634
635
636 static gint
637 cell_area_scaffold_focus (GtkWidget       *widget,
638                           GtkDirectionType direction)
639 {
640   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
641   CellAreaScaffoldPrivate *priv     = scaffold->priv;
642   GtkTreeIter              iter;
643   gboolean                 valid;
644   gint                     focus_row;
645   gboolean                 changed = FALSE;
646
647   /* Grab focus on ourself if we dont already have focus */
648   if (!gtk_widget_has_focus (widget))
649     gtk_widget_grab_focus (widget);
650
651   /* Move focus from cell to cell and row to row */
652   focus_row = priv->focus_row;
653
654   g_signal_handler_block (priv->area, priv->focus_changed_id);
655   
656   valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, priv->focus_row);
657   while (valid)
658     {
659       gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
660       
661       /* If focus stays in the area we dont need to do any more */
662       if (gtk_cell_area_focus (priv->area, direction))
663         {
664           priv->focus_row = focus_row;
665
666           /* XXX A smarter implementation would only invalidate the rectangles where
667            * focus was removed from and new focus was placed */
668           gtk_widget_queue_draw (widget);
669           changed = TRUE;
670           break;
671         }
672       else
673         {
674           if (direction == GTK_DIR_RIGHT ||
675               direction == GTK_DIR_LEFT)
676             break;
677           else if (direction == GTK_DIR_UP ||
678                    direction == GTK_DIR_TAB_BACKWARD)
679             {
680               if (focus_row == 0)
681                 break;
682               else
683                 {
684                   /* XXX A real implementation should check if the
685                    * previous row can focus with its attributes setup */
686                   focus_row--;
687                   valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, focus_row);
688                 }
689             }
690           else /* direction == GTK_DIR_DOWN || GTK_DIR_TAB_FORWARD */
691             {
692               if (focus_row == priv->row_data->len - 1)
693                 break;
694               else
695                 {
696                   /* XXX A real implementation should check if the
697                    * previous row can focus with its attributes setup */
698                   focus_row++;
699                   valid = gtk_tree_model_iter_next (priv->model, &iter);
700                 }
701             }
702         }
703     }
704
705   g_signal_handler_unblock (priv->area, priv->focus_changed_id);
706
707   /* XXX A smarter implementation would only invalidate the rectangles where
708    * focus was removed from and new focus was placed */
709   gtk_widget_queue_draw (widget);
710
711   return changed;
712 }
713
714 static gboolean
715 cell_area_scaffold_button_press (GtkWidget       *widget,
716                                  GdkEventButton  *event)
717 {
718   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (widget);
719   CellAreaScaffoldPrivate *priv     = scaffold->priv;
720   GtkTreeIter              iter;
721   gboolean                 valid;
722   gint                     i = 0;
723   GdkRectangle             event_area;
724   GtkAllocation            allocation;
725   gboolean                 handled = FALSE;
726
727   gtk_widget_get_allocation (widget, &allocation);
728
729   event_area.x      = 0;
730   event_area.y      = 0;
731   event_area.width  = allocation.width;
732   event_area.height = allocation.height;
733
734   event_area.x      = priv->indent;
735   event_area.width -= priv->indent;
736
737   valid = gtk_tree_model_get_iter_first (priv->model, &iter);
738   while (valid)
739     {
740       RowData *data = &g_array_index (priv->row_data, RowData, i);
741
742       event_area.height = data->size;
743
744       if (event->y >= event_area.y && 
745           event->y <= event_area.y + event_area.height)
746         {
747           /* XXX A real implementation would assemble GtkCellRendererState flags here */
748           gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
749           handled = gtk_cell_area_event (priv->area, priv->context, GTK_WIDGET (scaffold),
750                                          (GdkEvent *)event, &event_area, 0);
751           break;
752         }
753       
754       event_area.y += data->size;
755       event_area.y += priv->row_spacing;
756       
757       i++;
758       valid = gtk_tree_model_iter_next (priv->model, &iter);
759     }
760
761   return handled;
762 }
763
764
765 /*********************************************************
766  *                   GtkContainerClass                   *
767  *********************************************************/
768 static void
769 cell_area_scaffold_put_edit_widget (CellAreaScaffold *scaffold,
770                                     GtkWidget        *edit_widget,
771                                     gint              x,
772                                     gint              y,
773                                     gint              width,
774                                     gint              height)
775 {
776   CellAreaScaffoldPrivate *priv = scaffold->priv;
777
778   priv->edit_rect.x      = x;
779   priv->edit_rect.y      = y;
780   priv->edit_rect.width  = width;
781   priv->edit_rect.height = height;
782   priv->edit_widget      = edit_widget;
783
784   gtk_widget_set_parent (edit_widget, GTK_WIDGET (scaffold));
785 }
786
787 static void
788 cell_area_scaffold_forall (GtkContainer    *container,
789                            gboolean         include_internals,
790                            GtkCallback      callback,
791                            gpointer         callback_data)
792 {
793   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (container);
794   CellAreaScaffoldPrivate *priv     = scaffold->priv;
795
796   if (priv->edit_widget)
797     (* callback) (priv->edit_widget, callback_data);
798 }
799
800 static void
801 cell_area_scaffold_remove (GtkContainer    *container,
802                            GtkWidget       *child)
803 {
804   CellAreaScaffold        *scaffold = CELL_AREA_SCAFFOLD (container);
805   CellAreaScaffoldPrivate *priv     = scaffold->priv;
806
807   g_return_if_fail (child == priv->edit_widget);
808
809   gtk_widget_unparent (priv->edit_widget);
810   priv->edit_widget = NULL;
811 }
812
813 /*********************************************************
814  *                CellAreaScaffoldClass                  *
815  *********************************************************/
816 static void
817 cell_area_scaffold_activate (CellAreaScaffold *scaffold)
818 {
819   CellAreaScaffoldPrivate *priv     = scaffold->priv;
820   GtkWidget               *widget   = GTK_WIDGET (scaffold);
821   GtkAllocation            allocation;
822   GdkRectangle             cell_area;
823   GtkTreeIter              iter;
824   gboolean                 valid;
825   gint                     i = 0;
826
827   gtk_widget_get_allocation (widget, &allocation);
828
829   cell_area.x = 0;
830   cell_area.y = 0;
831   cell_area.width  = allocation.width;
832   cell_area.height = allocation.height;
833
834   cell_area.x      = priv->indent;
835   cell_area.width -= priv->indent;
836
837   valid = gtk_tree_model_get_iter_first (priv->model, &iter);
838   while (valid)
839     {
840       RowData *data = &g_array_index (priv->row_data, RowData, i);
841
842       if (i == priv->focus_row)
843         {
844           cell_area.height = data->size;
845
846           gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
847           gtk_cell_area_activate (priv->area, priv->context, widget, &cell_area, 
848                                   GTK_CELL_RENDERER_FOCUSED, FALSE);
849
850           break;
851         }
852
853       cell_area.y += data->size + priv->row_spacing;
854
855       i++;
856       valid = gtk_tree_model_iter_next (priv->model, &iter);
857     }
858 }
859
860 /*********************************************************
861  *           CellArea/GtkTreeModel callbacks             *
862  *********************************************************/
863 static void
864 size_changed_cb (GtkCellAreaContext  *context,
865                  GParamSpec          *pspec,
866                  CellAreaScaffold    *scaffold)
867 {
868   if (!strcmp (pspec->name, "minimum-width") ||
869       !strcmp (pspec->name, "natural-width") ||
870       !strcmp (pspec->name, "minimum-height") ||
871       !strcmp (pspec->name, "natural-height"))
872     gtk_widget_queue_resize (GTK_WIDGET (scaffold));
873 }
874
875 static void
876 focus_changed_cb (GtkCellArea      *area,
877                   GtkCellRenderer  *renderer,
878                   const gchar      *path,
879                   CellAreaScaffold *scaffold)
880 {
881   CellAreaScaffoldPrivate *priv = scaffold->priv;
882   GtkWidget               *widget = GTK_WIDGET (scaffold);
883   GtkTreePath             *treepath;
884   gint                    *indices;
885
886   if (!priv->model)
887     return;
888
889   /* We can be signaled that a renderer lost focus, here
890    * we dont care */
891   if (!renderer)
892     return;
893   
894   treepath = gtk_tree_path_new_from_string (path);
895   indices = gtk_tree_path_get_indices (treepath);
896
897   priv->focus_row = indices[0];
898
899   gtk_tree_path_free (treepath);
900
901   /* Make sure we have focus now */
902   if (!gtk_widget_has_focus (widget))
903     gtk_widget_grab_focus (widget);
904
905   gtk_widget_queue_draw (widget);
906 }
907
908 static void
909 add_editable_cb (GtkCellArea      *area,
910                  GtkCellRenderer  *renderer,
911                  GtkCellEditable  *edit_widget,
912                  GdkRectangle     *cell_area,
913                  const gchar      *path,
914                  CellAreaScaffold *scaffold)
915 {
916   GtkAllocation allocation;
917
918   gtk_widget_get_allocation (GTK_WIDGET (scaffold), &allocation);
919
920   cell_area_scaffold_put_edit_widget (scaffold, GTK_WIDGET (edit_widget),
921                                       allocation.x + cell_area->x, 
922                                       allocation.y + cell_area->y, 
923                                       cell_area->width, cell_area->height);
924 }
925
926 static void
927 remove_editable_cb (GtkCellArea      *area,
928                     GtkCellRenderer  *renderer,
929                     GtkCellEditable  *edit_widget,
930                     CellAreaScaffold *scaffold)
931 {
932   gtk_container_remove (GTK_CONTAINER (scaffold), GTK_WIDGET (edit_widget));
933
934   gtk_widget_grab_focus (GTK_WIDGET (scaffold));
935 }
936
937 static void 
938 rebuild_and_reset_internals (CellAreaScaffold *scaffold)
939 {
940   CellAreaScaffoldPrivate *priv = scaffold->priv;
941   gint n_rows;
942
943   if (priv->model)
944     {
945       n_rows = gtk_tree_model_iter_n_children (priv->model, NULL);
946
947       /* Clear/reset the array */
948       g_array_set_size (priv->row_data, n_rows);
949       memset (priv->row_data->data, 0x0, n_rows * sizeof (RowData));
950     }
951   else
952     g_array_set_size (priv->row_data, 0);
953
954   /* Data changed, lets reset the context and consequently queue resize and
955    * start everything over again (note this is definitly far from optimized) */
956   gtk_cell_area_context_reset (priv->context);
957 }
958
959 static void
960 row_changed_cb (GtkTreeModel     *model,
961                 GtkTreePath      *path,
962                 GtkTreeIter      *iter,
963                 CellAreaScaffold *scaffold)
964 {
965   rebuild_and_reset_internals (scaffold);
966 }
967
968 static void
969 row_inserted_cb (GtkTreeModel     *model,
970                  GtkTreePath      *path,
971                  GtkTreeIter      *iter,
972                  CellAreaScaffold *scaffold)
973 {
974   rebuild_and_reset_internals (scaffold);
975 }
976
977 static void
978 row_deleted_cb (GtkTreeModel     *model,
979                 GtkTreePath      *path,
980                 CellAreaScaffold *scaffold)
981 {
982   rebuild_and_reset_internals (scaffold);
983 }
984
985 static void
986 rows_reordered_cb (GtkTreeModel     *model,
987                    GtkTreePath      *parent,
988                    GtkTreeIter      *iter,
989                    gint             *new_order,
990                    CellAreaScaffold *scaffold)
991 {
992   rebuild_and_reset_internals (scaffold);
993 }
994
995 /*********************************************************
996  *                         API                           *
997  *********************************************************/
998 GtkWidget *
999 cell_area_scaffold_new (void)
1000 {
1001   return (GtkWidget *)g_object_new (TYPE_CELL_AREA_SCAFFOLD, NULL);
1002 }
1003
1004 GtkCellArea  *
1005 cell_area_scaffold_get_area (CellAreaScaffold *scaffold)
1006 {
1007   CellAreaScaffoldPrivate *priv;
1008   
1009   g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), NULL);
1010
1011   priv = scaffold->priv;
1012
1013   return priv->area;
1014 }
1015
1016 void
1017 cell_area_scaffold_set_model (CellAreaScaffold *scaffold,
1018                               GtkTreeModel     *model)
1019 {
1020   CellAreaScaffoldPrivate *priv;
1021   
1022   g_return_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold));
1023
1024   priv = scaffold->priv;
1025
1026   if (priv->model != model)
1027     {
1028       if (priv->model)
1029         {
1030           g_signal_handler_disconnect (priv->model, priv->row_changed_id);
1031           g_signal_handler_disconnect (priv->model, priv->row_inserted_id);
1032           g_signal_handler_disconnect (priv->model, priv->row_deleted_id);
1033           g_signal_handler_disconnect (priv->model, priv->rows_reordered_id);
1034
1035           g_object_unref (priv->model);
1036         }
1037
1038       priv->model = model;
1039
1040       if (priv->model)
1041         {
1042           g_object_ref (priv->model);
1043
1044           priv->row_changed_id = 
1045             g_signal_connect (priv->model, "row-changed",
1046                               G_CALLBACK (row_changed_cb), scaffold);
1047
1048           priv->row_inserted_id = 
1049             g_signal_connect (priv->model, "row-inserted",
1050                               G_CALLBACK (row_inserted_cb), scaffold);
1051
1052           priv->row_deleted_id = 
1053             g_signal_connect (priv->model, "row-deleted",
1054                               G_CALLBACK (row_deleted_cb), scaffold);
1055
1056           priv->rows_reordered_id = 
1057             g_signal_connect (priv->model, "rows-reordered",
1058                               G_CALLBACK (rows_reordered_cb), scaffold);
1059         }
1060
1061       rebuild_and_reset_internals (scaffold);
1062     }
1063 }
1064
1065 GtkTreeModel *
1066 cell_area_scaffold_get_model (CellAreaScaffold *scaffold)
1067 {
1068   CellAreaScaffoldPrivate *priv;
1069   
1070   g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), NULL);
1071
1072   priv = scaffold->priv;
1073
1074   return priv->model;
1075 }
1076
1077
1078 void
1079 cell_area_scaffold_set_row_spacing (CellAreaScaffold *scaffold,
1080                                     gint              spacing)
1081 {
1082   CellAreaScaffoldPrivate *priv;
1083   
1084   g_return_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold));
1085
1086   priv = scaffold->priv;
1087
1088   if (priv->row_spacing != spacing)
1089     {
1090       priv->row_spacing = spacing;
1091       gtk_widget_queue_resize (GTK_WIDGET (scaffold));
1092     }
1093 }
1094
1095 gint
1096 cell_area_scaffold_get_row_spacing (CellAreaScaffold *scaffold)
1097 {
1098   CellAreaScaffoldPrivate *priv;
1099   
1100   g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), 0);
1101
1102   priv = scaffold->priv;
1103
1104   return priv->row_spacing;
1105 }
1106
1107 void
1108 cell_area_scaffold_set_indentation (CellAreaScaffold *scaffold,
1109                                     gint              indent)
1110 {
1111   CellAreaScaffoldPrivate *priv;
1112   
1113   g_return_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold));
1114
1115   priv = scaffold->priv;
1116
1117   if (priv->indent != indent)
1118     {
1119       priv->indent = indent;
1120       gtk_widget_queue_resize (GTK_WIDGET (scaffold));
1121     }
1122 }
1123
1124 gint
1125 cell_area_scaffold_get_indentation (CellAreaScaffold *scaffold)
1126 {
1127   CellAreaScaffoldPrivate *priv;
1128   
1129   g_return_val_if_fail (IS_CELL_AREA_SCAFFOLD (scaffold), 0);
1130
1131   priv = scaffold->priv;
1132
1133   return priv->indent;
1134 }
1135
1136