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