Import GtkTreeView from GTK+ 2.0-2.20.1 renamed as PsppSheetView.
[pspp] / src / ui / gui / pspp-sheet-view-column.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2011, 2012 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 /* gtktreeviewcolumn.c
18  * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
19  *
20  * This library is free software; you can redistribute it and/or
21  * modify it under the terms of the GNU Library General Public
22  * License as published by the Free Software Foundation; either
23  * version 2 of the License, or (at your option) any later version.
24  *
25  * This library is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
28  * Library General Public License for more details.
29  *
30  * You should have received a copy of the GNU Library General Public
31  * License along with this library; if not, write to the
32  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
33  * Boston, MA 02111-1307, USA.
34  */
35
36 #include <config.h>
37
38 #include "ui/gui/pspp-sheet-private.h"
39
40 #include <errno.h>
41 #include <gtk/gtk.h>
42 #include <stdlib.h>
43 #include <string.h>
44
45 #include "ui/gui/psppire-marshal.h"
46
47 #define P_(STRING) STRING
48 #define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
49 #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
50
51 enum
52 {
53   PROP_0,
54   PROP_VISIBLE,
55   PROP_RESIZABLE,
56   PROP_WIDTH,
57   PROP_SPACING,
58   PROP_SIZING,
59   PROP_FIXED_WIDTH,
60   PROP_MIN_WIDTH,
61   PROP_MAX_WIDTH,
62   PROP_TITLE,
63   PROP_EXPAND,
64   PROP_CLICKABLE,
65   PROP_WIDGET,
66   PROP_ALIGNMENT,
67   PROP_REORDERABLE,
68   PROP_SORT_INDICATOR,
69   PROP_SORT_ORDER,
70   PROP_SORT_COLUMN_ID
71 };
72
73 enum
74 {
75   CLICKED,
76   LAST_SIGNAL
77 };
78
79 typedef struct _PsppSheetViewColumnCellInfo PsppSheetViewColumnCellInfo;
80 struct _PsppSheetViewColumnCellInfo
81 {
82   GtkCellRenderer *cell;
83   GSList *attributes;
84   PsppSheetCellDataFunc func;
85   gpointer func_data;
86   GDestroyNotify destroy;
87   gint requested_width;
88   gint real_width;
89   guint expand : 1;
90   guint pack : 1;
91   guint has_focus : 1;
92   guint in_editing_mode : 1;
93 };
94
95 /* Type methods */
96 static void pspp_sheet_view_column_cell_layout_init              (GtkCellLayoutIface      *iface);
97
98 /* GObject methods */
99 static void pspp_sheet_view_column_set_property                  (GObject                 *object,
100                                                                 guint                    prop_id,
101                                                                 const GValue            *value,
102                                                                 GParamSpec              *pspec);
103 static void pspp_sheet_view_column_get_property                  (GObject                 *object,
104                                                                 guint                    prop_id,
105                                                                 GValue                  *value,
106                                                                 GParamSpec              *pspec);
107 static void pspp_sheet_view_column_finalize                      (GObject                 *object);
108
109 /* GtkCellLayout implementation */
110 static void pspp_sheet_view_column_cell_layout_pack_start         (GtkCellLayout         *cell_layout,
111                                                                  GtkCellRenderer       *cell,
112                                                                  gboolean               expand);
113 static void pspp_sheet_view_column_cell_layout_pack_end           (GtkCellLayout         *cell_layout,
114                                                                  GtkCellRenderer       *cell,
115                                                                  gboolean               expand);
116 static void pspp_sheet_view_column_cell_layout_clear              (GtkCellLayout         *cell_layout);
117 static void pspp_sheet_view_column_cell_layout_add_attribute      (GtkCellLayout         *cell_layout,
118                                                                  GtkCellRenderer       *cell,
119                                                                  const gchar           *attribute,
120                                                                  gint                   column);
121 static void pspp_sheet_view_column_cell_layout_set_cell_data_func (GtkCellLayout         *cell_layout,
122                                                                  GtkCellRenderer       *cell,
123                                                                  GtkCellLayoutDataFunc  func,
124                                                                  gpointer               func_data,
125                                                                  GDestroyNotify         destroy);
126 static void pspp_sheet_view_column_cell_layout_clear_attributes   (GtkCellLayout         *cell_layout,
127                                                                  GtkCellRenderer       *cell);
128 static void pspp_sheet_view_column_cell_layout_reorder            (GtkCellLayout         *cell_layout,
129                                                                  GtkCellRenderer       *cell,
130                                                                  gint                   position);
131 static GList *pspp_sheet_view_column_cell_layout_get_cells        (GtkCellLayout         *cell_layout);
132
133 /* Button handling code */
134 static void pspp_sheet_view_column_create_button                 (PsppSheetViewColumn       *tree_column);
135 static void pspp_sheet_view_column_update_button                 (PsppSheetViewColumn       *tree_column);
136
137 /* Button signal handlers */
138 static gint pspp_sheet_view_column_button_event                  (GtkWidget               *widget,
139                                                                 GdkEvent                *event,
140                                                                 gpointer                 data);
141 static void pspp_sheet_view_column_button_clicked                (GtkWidget               *widget,
142                                                                 gpointer                 data);
143 static gboolean pspp_sheet_view_column_mnemonic_activate         (GtkWidget *widget,
144                                                                 gboolean   group_cycling,
145                                                                 gpointer   data);
146
147 /* Property handlers */
148 static void pspp_sheet_view_model_sort_column_changed            (GtkTreeSortable         *sortable,
149                                                                 PsppSheetViewColumn       *tree_column);
150
151 /* Internal functions */
152 static void pspp_sheet_view_column_sort                          (PsppSheetViewColumn       *tree_column,
153                                                                 gpointer                 data);
154 static void pspp_sheet_view_column_setup_sort_column_id_callback (PsppSheetViewColumn       *tree_column);
155 static void pspp_sheet_view_column_set_attributesv               (PsppSheetViewColumn       *tree_column,
156                                                                 GtkCellRenderer         *cell_renderer,
157                                                                 va_list                  args);
158 static PsppSheetViewColumnCellInfo *pspp_sheet_view_column_get_cell_info (PsppSheetViewColumn *tree_column,
159                                                                       GtkCellRenderer   *cell_renderer);
160
161 /* cell list manipulation */
162 static GList *pspp_sheet_view_column_cell_first                  (PsppSheetViewColumn      *tree_column);
163 static GList *pspp_sheet_view_column_cell_last                   (PsppSheetViewColumn      *tree_column);
164 static GList *pspp_sheet_view_column_cell_next                   (PsppSheetViewColumn      *tree_column,
165                                                                 GList                  *current);
166 static GList *pspp_sheet_view_column_cell_prev                   (PsppSheetViewColumn      *tree_column,
167                                                                 GList                  *current);
168 static void pspp_sheet_view_column_clear_attributes_by_info      (PsppSheetViewColumn      *tree_column,
169                                                                 PsppSheetViewColumnCellInfo *info);
170 /* GtkBuildable implementation */
171 static void pspp_sheet_view_column_buildable_init                 (GtkBuildableIface     *iface);
172
173 static guint tree_column_signals[LAST_SIGNAL] = { 0 };
174
175 G_DEFINE_TYPE_WITH_CODE (PsppSheetViewColumn, pspp_sheet_view_column, GTK_TYPE_OBJECT,
176                          G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
177                                                 pspp_sheet_view_column_cell_layout_init)
178                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
179                                                 pspp_sheet_view_column_buildable_init))
180
181
182 static void
183 pspp_sheet_view_column_class_init (PsppSheetViewColumnClass *class)
184 {
185   GObjectClass *object_class;
186
187   object_class = (GObjectClass*) class;
188
189   class->clicked = NULL;
190
191   object_class->finalize = pspp_sheet_view_column_finalize;
192   object_class->set_property = pspp_sheet_view_column_set_property;
193   object_class->get_property = pspp_sheet_view_column_get_property;
194   
195   tree_column_signals[CLICKED] =
196     g_signal_new ("clicked",
197                   G_OBJECT_CLASS_TYPE (object_class),
198                   G_SIGNAL_RUN_LAST,
199                   G_STRUCT_OFFSET (PsppSheetViewColumnClass, clicked),
200                   NULL, NULL,
201                   g_cclosure_marshal_VOID__VOID,
202                   G_TYPE_NONE, 0);
203
204   g_object_class_install_property (object_class,
205                                    PROP_VISIBLE,
206                                    g_param_spec_boolean ("visible",
207                                                         P_("Visible"),
208                                                         P_("Whether to display the column"),
209                                                          TRUE,
210                                                          GTK_PARAM_READWRITE));
211   
212   g_object_class_install_property (object_class,
213                                    PROP_RESIZABLE,
214                                    g_param_spec_boolean ("resizable",
215                                                          P_("Resizable"),
216                                                          P_("Column is user-resizable"),
217                                                          FALSE,
218                                                          GTK_PARAM_READWRITE));
219   
220   g_object_class_install_property (object_class,
221                                    PROP_WIDTH,
222                                    g_param_spec_int ("width",
223                                                      P_("Width"),
224                                                      P_("Current width of the column"),
225                                                      0,
226                                                      G_MAXINT,
227                                                      0,
228                                                      GTK_PARAM_READABLE));
229   g_object_class_install_property (object_class,
230                                    PROP_SPACING,
231                                    g_param_spec_int ("spacing",
232                                                      P_("Spacing"),
233                                                      P_("Space which is inserted between cells"),
234                                                      0,
235                                                      G_MAXINT,
236                                                      0,
237                                                      GTK_PARAM_READWRITE));
238   g_object_class_install_property (object_class,
239                                    PROP_SIZING,
240                                    g_param_spec_enum ("sizing",
241                                                       P_("Sizing"),
242                                                       P_("Resize mode of the column"),
243                                                       PSPP_TYPE_SHEET_VIEW_COLUMN_SIZING,
244                                                       PSPP_SHEET_VIEW_COLUMN_GROW_ONLY,
245                                                       GTK_PARAM_READWRITE));
246   
247   g_object_class_install_property (object_class,
248                                    PROP_FIXED_WIDTH,
249                                    g_param_spec_int ("fixed-width",
250                                                      P_("Fixed Width"),
251                                                      P_("Current fixed width of the column"),
252                                                      1,
253                                                      G_MAXINT,
254                                                      1, /* not useful */
255                                                      GTK_PARAM_READWRITE));
256
257   g_object_class_install_property (object_class,
258                                    PROP_MIN_WIDTH,
259                                    g_param_spec_int ("min-width",
260                                                      P_("Minimum Width"),
261                                                      P_("Minimum allowed width of the column"),
262                                                      -1,
263                                                      G_MAXINT,
264                                                      -1,
265                                                      GTK_PARAM_READWRITE));
266
267   g_object_class_install_property (object_class,
268                                    PROP_MAX_WIDTH,
269                                    g_param_spec_int ("max-width",
270                                                      P_("Maximum Width"),
271                                                      P_("Maximum allowed width of the column"),
272                                                      -1,
273                                                      G_MAXINT,
274                                                      -1,
275                                                      GTK_PARAM_READWRITE));
276
277   g_object_class_install_property (object_class,
278                                    PROP_TITLE,
279                                    g_param_spec_string ("title",
280                                                         P_("Title"),
281                                                         P_("Title to appear in column header"),
282                                                         "",
283                                                         GTK_PARAM_READWRITE));
284   
285   g_object_class_install_property (object_class,
286                                    PROP_EXPAND,
287                                    g_param_spec_boolean ("expand",
288                                                          P_("Expand"),
289                                                          P_("Column gets share of extra width allocated to the widget"),
290                                                          FALSE,
291                                                          GTK_PARAM_READWRITE));
292   
293   g_object_class_install_property (object_class,
294                                    PROP_CLICKABLE,
295                                    g_param_spec_boolean ("clickable",
296                                                         P_("Clickable"),
297                                                         P_("Whether the header can be clicked"),
298                                                          FALSE,
299                                                          GTK_PARAM_READWRITE));
300   
301
302   g_object_class_install_property (object_class,
303                                    PROP_WIDGET,
304                                    g_param_spec_object ("widget",
305                                                         P_("Widget"),
306                                                         P_("Widget to put in column header button instead of column title"),
307                                                         GTK_TYPE_WIDGET,
308                                                         GTK_PARAM_READWRITE));
309
310   g_object_class_install_property (object_class,
311                                    PROP_ALIGNMENT,
312                                    g_param_spec_float ("alignment",
313                                                        P_("Alignment"),
314                                                        P_("X Alignment of the column header text or widget"),
315                                                        0.0,
316                                                        1.0,
317                                                        0.0,
318                                                        GTK_PARAM_READWRITE));
319
320   g_object_class_install_property (object_class,
321                                    PROP_REORDERABLE,
322                                    g_param_spec_boolean ("reorderable",
323                                                          P_("Reorderable"),
324                                                          P_("Whether the column can be reordered around the headers"),
325                                                          FALSE,
326                                                          GTK_PARAM_READWRITE));
327
328   g_object_class_install_property (object_class,
329                                    PROP_SORT_INDICATOR,
330                                    g_param_spec_boolean ("sort-indicator",
331                                                         P_("Sort indicator"),
332                                                         P_("Whether to show a sort indicator"),
333                                                          FALSE,
334                                                          GTK_PARAM_READWRITE));
335
336   g_object_class_install_property (object_class,
337                                    PROP_SORT_ORDER,
338                                    g_param_spec_enum ("sort-order",
339                                                       P_("Sort order"),
340                                                       P_("Sort direction the sort indicator should indicate"),
341                                                       GTK_TYPE_SORT_TYPE,
342                                                       GTK_SORT_ASCENDING,
343                                                       GTK_PARAM_READWRITE));
344
345   /**
346    * PsppSheetViewColumn:sort-column-id:
347    *
348    * Logical sort column ID this column sorts on when selected for sorting. Setting the sort column ID makes the column header
349    * clickable. Set to %-1 to make the column unsortable.
350    *
351    * Since: 2.18
352    **/
353   g_object_class_install_property (object_class,
354                                    PROP_SORT_COLUMN_ID,
355                                    g_param_spec_int ("sort-column-id",
356                                                      P_("Sort column ID"),
357                                                      P_("Logical sort column ID this column sorts on when selected for sorting"),
358                                                      -1,
359                                                      G_MAXINT,
360                                                      -1,
361                                                      GTK_PARAM_READWRITE));
362 }
363
364 static void
365 pspp_sheet_view_column_buildable_init (GtkBuildableIface *iface)
366 {
367   iface->add_child = _gtk_cell_layout_buildable_add_child;
368   iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start;
369   iface->custom_tag_end = _gtk_cell_layout_buildable_custom_tag_end;
370 }
371
372 static void
373 pspp_sheet_view_column_cell_layout_init (GtkCellLayoutIface *iface)
374 {
375   iface->pack_start = pspp_sheet_view_column_cell_layout_pack_start;
376   iface->pack_end = pspp_sheet_view_column_cell_layout_pack_end;
377   iface->clear = pspp_sheet_view_column_cell_layout_clear;
378   iface->add_attribute = pspp_sheet_view_column_cell_layout_add_attribute;
379   iface->set_cell_data_func = pspp_sheet_view_column_cell_layout_set_cell_data_func;
380   iface->clear_attributes = pspp_sheet_view_column_cell_layout_clear_attributes;
381   iface->reorder = pspp_sheet_view_column_cell_layout_reorder;
382   iface->get_cells = pspp_sheet_view_column_cell_layout_get_cells;
383 }
384
385 static void
386 pspp_sheet_view_column_init (PsppSheetViewColumn *tree_column)
387 {
388   tree_column->button = NULL;
389   tree_column->xalign = 0.0;
390   tree_column->width = 0;
391   tree_column->spacing = 0;
392   tree_column->requested_width = -1;
393   tree_column->min_width = -1;
394   tree_column->max_width = -1;
395   tree_column->resized_width = 0;
396   tree_column->column_type = PSPP_SHEET_VIEW_COLUMN_GROW_ONLY;
397   tree_column->visible = TRUE;
398   tree_column->resizable = FALSE;
399   tree_column->expand = FALSE;
400   tree_column->clickable = FALSE;
401   tree_column->dirty = TRUE;
402   tree_column->sort_order = GTK_SORT_ASCENDING;
403   tree_column->show_sort_indicator = FALSE;
404   tree_column->property_changed_signal = 0;
405   tree_column->sort_clicked_signal = 0;
406   tree_column->sort_column_changed_signal = 0;
407   tree_column->sort_column_id = -1;
408   tree_column->reorderable = FALSE;
409   tree_column->maybe_reordered = FALSE;
410   tree_column->fixed_width = 1;
411   tree_column->use_resized_width = FALSE;
412   tree_column->title = g_strdup ("");
413 }
414
415 static void
416 pspp_sheet_view_column_finalize (GObject *object)
417 {
418   PsppSheetViewColumn *tree_column = (PsppSheetViewColumn *) object;
419   GList *list;
420
421   for (list = tree_column->cell_list; list; list = list->next)
422     {
423       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
424
425       if (info->destroy)
426         {
427           GDestroyNotify d = info->destroy;
428
429           info->destroy = NULL;
430           d (info->func_data);
431         }
432       pspp_sheet_view_column_clear_attributes_by_info (tree_column, info);
433       g_object_unref (info->cell);
434       g_free (info);
435     }
436
437   g_free (tree_column->title);
438   g_list_free (tree_column->cell_list);
439
440   if (tree_column->child)
441     g_object_unref (tree_column->child);
442
443   G_OBJECT_CLASS (pspp_sheet_view_column_parent_class)->finalize (object);
444 }
445
446 static void
447 pspp_sheet_view_column_set_property (GObject         *object,
448                                    guint            prop_id,
449                                    const GValue    *value,
450                                    GParamSpec      *pspec)
451 {
452   PsppSheetViewColumn *tree_column;
453
454   tree_column = PSPP_SHEET_VIEW_COLUMN (object);
455
456   switch (prop_id)
457     {
458     case PROP_VISIBLE:
459       pspp_sheet_view_column_set_visible (tree_column,
460                                         g_value_get_boolean (value));
461       break;
462
463     case PROP_RESIZABLE:
464       pspp_sheet_view_column_set_resizable (tree_column,
465                                           g_value_get_boolean (value));
466       break;
467
468     case PROP_SIZING:
469       pspp_sheet_view_column_set_sizing (tree_column,
470                                        g_value_get_enum (value));
471       break;
472
473     case PROP_FIXED_WIDTH:
474       pspp_sheet_view_column_set_fixed_width (tree_column,
475                                             g_value_get_int (value));
476       break;
477
478     case PROP_MIN_WIDTH:
479       pspp_sheet_view_column_set_min_width (tree_column,
480                                           g_value_get_int (value));
481       break;
482
483     case PROP_MAX_WIDTH:
484       pspp_sheet_view_column_set_max_width (tree_column,
485                                           g_value_get_int (value));
486       break;
487
488     case PROP_SPACING:
489       pspp_sheet_view_column_set_spacing (tree_column,
490                                         g_value_get_int (value));
491       break;
492
493     case PROP_TITLE:
494       pspp_sheet_view_column_set_title (tree_column,
495                                       g_value_get_string (value));
496       break;
497
498     case PROP_EXPAND:
499       pspp_sheet_view_column_set_expand (tree_column,
500                                        g_value_get_boolean (value));
501       break;
502
503     case PROP_CLICKABLE:
504       pspp_sheet_view_column_set_clickable (tree_column,
505                                           g_value_get_boolean (value));
506       break;
507
508     case PROP_WIDGET:
509       pspp_sheet_view_column_set_widget (tree_column,
510                                        (GtkWidget*) g_value_get_object (value));
511       break;
512
513     case PROP_ALIGNMENT:
514       pspp_sheet_view_column_set_alignment (tree_column,
515                                           g_value_get_float (value));
516       break;
517
518     case PROP_REORDERABLE:
519       pspp_sheet_view_column_set_reorderable (tree_column,
520                                             g_value_get_boolean (value));
521       break;
522
523     case PROP_SORT_INDICATOR:
524       pspp_sheet_view_column_set_sort_indicator (tree_column,
525                                                g_value_get_boolean (value));
526       break;
527
528     case PROP_SORT_ORDER:
529       pspp_sheet_view_column_set_sort_order (tree_column,
530                                            g_value_get_enum (value));
531       break;
532       
533     case PROP_SORT_COLUMN_ID:
534       pspp_sheet_view_column_set_sort_column_id (tree_column,
535                                                g_value_get_int (value));
536       break;
537       
538     default:
539       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
540       break;
541     }
542 }
543
544 static void
545 pspp_sheet_view_column_get_property (GObject         *object,
546                                    guint            prop_id,
547                                    GValue          *value,
548                                    GParamSpec      *pspec)
549 {
550   PsppSheetViewColumn *tree_column;
551
552   tree_column = PSPP_SHEET_VIEW_COLUMN (object);
553
554   switch (prop_id)
555     {
556     case PROP_VISIBLE:
557       g_value_set_boolean (value,
558                            pspp_sheet_view_column_get_visible (tree_column));
559       break;
560
561     case PROP_RESIZABLE:
562       g_value_set_boolean (value,
563                            pspp_sheet_view_column_get_resizable (tree_column));
564       break;
565
566     case PROP_WIDTH:
567       g_value_set_int (value,
568                        pspp_sheet_view_column_get_width (tree_column));
569       break;
570
571     case PROP_SPACING:
572       g_value_set_int (value,
573                        pspp_sheet_view_column_get_spacing (tree_column));
574       break;
575
576     case PROP_SIZING:
577       g_value_set_enum (value,
578                         pspp_sheet_view_column_get_sizing (tree_column));
579       break;
580
581     case PROP_FIXED_WIDTH:
582       g_value_set_int (value,
583                        pspp_sheet_view_column_get_fixed_width (tree_column));
584       break;
585
586     case PROP_MIN_WIDTH:
587       g_value_set_int (value,
588                        pspp_sheet_view_column_get_min_width (tree_column));
589       break;
590
591     case PROP_MAX_WIDTH:
592       g_value_set_int (value,
593                        pspp_sheet_view_column_get_max_width (tree_column));
594       break;
595
596     case PROP_TITLE:
597       g_value_set_string (value,
598                           pspp_sheet_view_column_get_title (tree_column));
599       break;
600
601     case PROP_EXPAND:
602       g_value_set_boolean (value,
603                           pspp_sheet_view_column_get_expand (tree_column));
604       break;
605
606     case PROP_CLICKABLE:
607       g_value_set_boolean (value,
608                            pspp_sheet_view_column_get_clickable (tree_column));
609       break;
610
611     case PROP_WIDGET:
612       g_value_set_object (value,
613                           (GObject*) pspp_sheet_view_column_get_widget (tree_column));
614       break;
615
616     case PROP_ALIGNMENT:
617       g_value_set_float (value,
618                          pspp_sheet_view_column_get_alignment (tree_column));
619       break;
620
621     case PROP_REORDERABLE:
622       g_value_set_boolean (value,
623                            pspp_sheet_view_column_get_reorderable (tree_column));
624       break;
625
626     case PROP_SORT_INDICATOR:
627       g_value_set_boolean (value,
628                            pspp_sheet_view_column_get_sort_indicator (tree_column));
629       break;
630
631     case PROP_SORT_ORDER:
632       g_value_set_enum (value,
633                         pspp_sheet_view_column_get_sort_order (tree_column));
634       break;
635       
636     case PROP_SORT_COLUMN_ID:
637       g_value_set_int (value,
638                        pspp_sheet_view_column_get_sort_column_id (tree_column));
639       break;
640       
641     default:
642       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
643       break;
644     }
645 }
646
647 /* Implementation of GtkCellLayout interface
648  */
649
650 static void
651 pspp_sheet_view_column_cell_layout_pack_start (GtkCellLayout   *cell_layout,
652                                              GtkCellRenderer *cell,
653                                              gboolean         expand)
654 {
655   PsppSheetViewColumn *column;
656   PsppSheetViewColumnCellInfo *cell_info;
657
658   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout));
659   column = PSPP_SHEET_VIEW_COLUMN (cell_layout);
660   g_return_if_fail (! pspp_sheet_view_column_get_cell_info (column, cell));
661
662   g_object_ref_sink (cell);
663
664   cell_info = g_new0 (PsppSheetViewColumnCellInfo, 1);
665   cell_info->cell = cell;
666   cell_info->expand = expand ? TRUE : FALSE;
667   cell_info->pack = GTK_PACK_START;
668   cell_info->has_focus = 0;
669   cell_info->attributes = NULL;
670
671   column->cell_list = g_list_append (column->cell_list, cell_info);
672 }
673
674 static void
675 pspp_sheet_view_column_cell_layout_pack_end (GtkCellLayout   *cell_layout,
676                                            GtkCellRenderer *cell,
677                                            gboolean         expand)
678 {
679   PsppSheetViewColumn *column;
680   PsppSheetViewColumnCellInfo *cell_info;
681
682   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout));
683   column = PSPP_SHEET_VIEW_COLUMN (cell_layout);
684   g_return_if_fail (! pspp_sheet_view_column_get_cell_info (column, cell));
685
686   g_object_ref_sink (cell);
687
688   cell_info = g_new0 (PsppSheetViewColumnCellInfo, 1);
689   cell_info->cell = cell;
690   cell_info->expand = expand ? TRUE : FALSE;
691   cell_info->pack = GTK_PACK_END;
692   cell_info->has_focus = 0;
693   cell_info->attributes = NULL;
694
695   column->cell_list = g_list_append (column->cell_list, cell_info);
696 }
697
698 static void
699 pspp_sheet_view_column_cell_layout_clear (GtkCellLayout *cell_layout)
700 {
701   PsppSheetViewColumn *column;
702
703   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout));
704   column = PSPP_SHEET_VIEW_COLUMN (cell_layout);
705
706   while (column->cell_list)
707     {
708       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *)column->cell_list->data;
709
710       pspp_sheet_view_column_cell_layout_clear_attributes (cell_layout, info->cell);
711       g_object_unref (info->cell);
712       g_free (info);
713       column->cell_list = g_list_delete_link (column->cell_list, 
714                                               column->cell_list);
715     }
716 }
717
718 static void
719 pspp_sheet_view_column_cell_layout_add_attribute (GtkCellLayout   *cell_layout,
720                                                 GtkCellRenderer *cell,
721                                                 const gchar     *attribute,
722                                                 gint             column)
723 {
724   PsppSheetViewColumn *tree_column;
725   PsppSheetViewColumnCellInfo *info;
726
727   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout));
728   tree_column = PSPP_SHEET_VIEW_COLUMN (cell_layout);
729
730   info = pspp_sheet_view_column_get_cell_info (tree_column, cell);
731   g_return_if_fail (info != NULL);
732
733   info->attributes = g_slist_prepend (info->attributes, GINT_TO_POINTER (column));
734   info->attributes = g_slist_prepend (info->attributes, g_strdup (attribute));
735
736   if (tree_column->tree_view)
737     _pspp_sheet_view_column_cell_set_dirty (tree_column, TRUE);
738 }
739
740 static void
741 pspp_sheet_view_column_cell_layout_set_cell_data_func (GtkCellLayout         *cell_layout,
742                                                      GtkCellRenderer       *cell,
743                                                      GtkCellLayoutDataFunc  func,
744                                                      gpointer               func_data,
745                                                      GDestroyNotify         destroy)
746 {
747   PsppSheetViewColumn *column;
748   PsppSheetViewColumnCellInfo *info;
749
750   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout));
751   column = PSPP_SHEET_VIEW_COLUMN (cell_layout);
752
753   info = pspp_sheet_view_column_get_cell_info (column, cell);
754   g_return_if_fail (info != NULL);
755
756   if (info->destroy)
757     {
758       GDestroyNotify d = info->destroy;
759
760       info->destroy = NULL;
761       d (info->func_data);
762     }
763
764   info->func = (PsppSheetCellDataFunc)func;
765   info->func_data = func_data;
766   info->destroy = destroy;
767
768   if (column->tree_view)
769     _pspp_sheet_view_column_cell_set_dirty (column, TRUE);
770 }
771
772 static void
773 pspp_sheet_view_column_cell_layout_clear_attributes (GtkCellLayout    *cell_layout,
774                                                    GtkCellRenderer  *cell_renderer)
775 {
776   PsppSheetViewColumn *column;
777   PsppSheetViewColumnCellInfo *info;
778
779   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout));
780   column = PSPP_SHEET_VIEW_COLUMN (cell_layout);
781
782   info = pspp_sheet_view_column_get_cell_info (column, cell_renderer);
783   if (info)
784     pspp_sheet_view_column_clear_attributes_by_info (column, info);
785 }
786
787 static void
788 pspp_sheet_view_column_cell_layout_reorder (GtkCellLayout   *cell_layout,
789                                           GtkCellRenderer *cell,
790                                           gint             position)
791 {
792   GList *link;
793   PsppSheetViewColumn *column;
794   PsppSheetViewColumnCellInfo *info;
795
796   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout));
797   column = PSPP_SHEET_VIEW_COLUMN (cell_layout);
798
799   info = pspp_sheet_view_column_get_cell_info (column, cell);
800
801   g_return_if_fail (info != NULL);
802   g_return_if_fail (position >= 0);
803
804   link = g_list_find (column->cell_list, info);
805
806   g_return_if_fail (link != NULL);
807
808   column->cell_list = g_list_delete_link (column->cell_list, link);
809   column->cell_list = g_list_insert (column->cell_list, info, position);
810
811   if (column->tree_view)
812     gtk_widget_queue_draw (column->tree_view);
813 }
814
815 static void
816 pspp_sheet_view_column_clear_attributes_by_info (PsppSheetViewColumn *tree_column,
817                                                PsppSheetViewColumnCellInfo *info)
818 {
819   GSList *list;
820
821   list = info->attributes;
822
823   while (list && list->next)
824     {
825       g_free (list->data);
826       list = list->next->next;
827     }
828   g_slist_free (info->attributes);
829   info->attributes = NULL;
830
831   if (tree_column->tree_view)
832     _pspp_sheet_view_column_cell_set_dirty (tree_column, TRUE);
833 }
834
835 /* Helper functions
836  */
837
838 /* Button handling code
839  */
840 static void
841 pspp_sheet_view_column_create_button (PsppSheetViewColumn *tree_column)
842 {
843   PsppSheetView *tree_view;
844   GtkWidget *child;
845   GtkWidget *hbox;
846
847   tree_view = (PsppSheetView *) tree_column->tree_view;
848
849   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
850   g_return_if_fail (tree_column->button == NULL);
851
852   gtk_widget_push_composite_child ();
853   tree_column->button = gtk_button_new ();
854   gtk_widget_add_events (tree_column->button, GDK_POINTER_MOTION_MASK);
855   gtk_widget_pop_composite_child ();
856
857   /* make sure we own a reference to it as well. */
858   if (tree_view->priv->header_window)
859     gtk_widget_set_parent_window (tree_column->button, tree_view->priv->header_window);
860   gtk_widget_set_parent (tree_column->button, GTK_WIDGET (tree_view));
861
862   g_signal_connect (tree_column->button, "event",
863                     G_CALLBACK (pspp_sheet_view_column_button_event),
864                     tree_column);
865   g_signal_connect (tree_column->button, "clicked",
866                     G_CALLBACK (pspp_sheet_view_column_button_clicked),
867                     tree_column);
868
869   tree_column->alignment = gtk_alignment_new (tree_column->xalign, 0.5, 0.0, 0.0);
870
871   hbox = gtk_hbox_new (FALSE, 2);
872   tree_column->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_IN);
873
874   if (tree_column->child)
875     child = tree_column->child;
876   else
877     {
878       child = gtk_label_new (tree_column->title);
879       gtk_widget_show (child);
880     }
881
882   g_signal_connect (child, "mnemonic-activate",
883                     G_CALLBACK (pspp_sheet_view_column_mnemonic_activate),
884                     tree_column);
885
886   if (tree_column->xalign <= 0.5)
887     gtk_box_pack_end (GTK_BOX (hbox), tree_column->arrow, FALSE, FALSE, 0);
888   else
889     gtk_box_pack_start (GTK_BOX (hbox), tree_column->arrow, FALSE, FALSE, 0);
890
891   gtk_box_pack_start (GTK_BOX (hbox), tree_column->alignment, TRUE, TRUE, 0);
892         
893   gtk_container_add (GTK_CONTAINER (tree_column->alignment), child);
894   gtk_container_add (GTK_CONTAINER (tree_column->button), hbox);
895
896   gtk_widget_show (hbox);
897   gtk_widget_show (tree_column->alignment);
898   pspp_sheet_view_column_update_button (tree_column);
899 }
900
901 static void 
902 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column)
903 {
904   gint sort_column_id = -1;
905   GtkWidget *hbox;
906   GtkWidget *alignment;
907   GtkWidget *arrow;
908   GtkWidget *current_child;
909   GtkArrowType arrow_type = GTK_ARROW_NONE;
910   GtkTreeModel *model;
911
912   if (tree_column->tree_view)
913     model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (tree_column->tree_view));
914   else
915     model = NULL;
916
917   /* Create a button if necessary */
918   if (tree_column->visible &&
919       tree_column->button == NULL &&
920       tree_column->tree_view &&
921       gtk_widget_get_realized (tree_column->tree_view))
922     pspp_sheet_view_column_create_button (tree_column);
923   
924   if (! tree_column->button)
925     return;
926
927   hbox = GTK_BIN (tree_column->button)->child;
928   alignment = tree_column->alignment;
929   arrow = tree_column->arrow;
930   current_child = GTK_BIN (alignment)->child;
931
932   /* Set up the actual button */
933   gtk_alignment_set (GTK_ALIGNMENT (alignment), tree_column->xalign,
934                      0.5, 0.0, 0.0);
935       
936   if (tree_column->child)
937     {
938       if (current_child != tree_column->child)
939         {
940           gtk_container_remove (GTK_CONTAINER (alignment),
941                                 current_child);
942           gtk_container_add (GTK_CONTAINER (alignment),
943                              tree_column->child);
944         }
945     }
946   else 
947     {
948       if (current_child == NULL)
949         {
950           current_child = gtk_label_new (NULL);
951           gtk_widget_show (current_child);
952           gtk_container_add (GTK_CONTAINER (alignment),
953                              current_child);
954         }
955
956       g_return_if_fail (GTK_IS_LABEL (current_child));
957
958       if (tree_column->title)
959         gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child),
960                                           tree_column->title);
961       else
962         gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child),
963                                           "");
964     }
965
966   if (GTK_IS_TREE_SORTABLE (model))
967     gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model),
968                                           &sort_column_id,
969                                           NULL);
970
971   if (tree_column->show_sort_indicator)
972     {
973       gboolean alternative;
974
975       g_object_get (gtk_widget_get_settings (tree_column->tree_view),
976                     "gtk-alternative-sort-arrows", &alternative,
977                     NULL);
978
979       switch (tree_column->sort_order)
980         {
981           case GTK_SORT_ASCENDING:
982             arrow_type = alternative ? GTK_ARROW_UP : GTK_ARROW_DOWN;
983             break;
984
985           case GTK_SORT_DESCENDING:
986             arrow_type = alternative ? GTK_ARROW_DOWN : GTK_ARROW_UP;
987             break;
988
989           default:
990             g_warning (G_STRLOC": bad sort order");
991             break;
992         }
993     }
994
995   gtk_arrow_set (GTK_ARROW (arrow),
996                  arrow_type,
997                  GTK_SHADOW_IN);
998
999   /* Put arrow on the right if the text is left-or-center justified, and on the
1000    * left otherwise; do this by packing boxes, so flipping text direction will
1001    * reverse things
1002    */
1003   g_object_ref (arrow);
1004   gtk_container_remove (GTK_CONTAINER (hbox), arrow);
1005
1006   if (tree_column->xalign <= 0.5)
1007     {
1008       gtk_box_pack_end (GTK_BOX (hbox), arrow, FALSE, FALSE, 0);
1009     }
1010   else
1011     {
1012       gtk_box_pack_start (GTK_BOX (hbox), arrow, FALSE, FALSE, 0);
1013       /* move it to the front */
1014       gtk_box_reorder_child (GTK_BOX (hbox), arrow, 0);
1015     }
1016   g_object_unref (arrow);
1017
1018   if (tree_column->show_sort_indicator
1019       || (GTK_IS_TREE_SORTABLE (model) && tree_column->sort_column_id >= 0))
1020     gtk_widget_show (arrow);
1021   else
1022     gtk_widget_hide (arrow);
1023
1024   /* It's always safe to hide the button.  It isn't always safe to show it, as
1025    * if you show it before it's realized, it'll get the wrong window. */
1026   if (tree_column->button &&
1027       tree_column->tree_view != NULL &&
1028       gtk_widget_get_realized (tree_column->tree_view))
1029     {
1030       if (tree_column->visible)
1031         {
1032           gtk_widget_show_now (tree_column->button);
1033           if (tree_column->window)
1034             {
1035               if (tree_column->resizable)
1036                 {
1037                   gdk_window_show (tree_column->window);
1038                   gdk_window_raise (tree_column->window);
1039                 }
1040               else
1041                 {
1042                   gdk_window_hide (tree_column->window);
1043                 }
1044             }
1045         }
1046       else
1047         {
1048           gtk_widget_hide (tree_column->button);
1049           if (tree_column->window)
1050             gdk_window_hide (tree_column->window);
1051         }
1052     }
1053   
1054   if (tree_column->reorderable || tree_column->clickable)
1055     {
1056       gtk_widget_set_can_focus (tree_column->button, TRUE);
1057     }
1058   else
1059     {
1060       gtk_widget_set_can_focus (tree_column->button, FALSE);
1061       if (gtk_widget_has_focus (tree_column->button))
1062         {
1063           GtkWidget *toplevel = gtk_widget_get_toplevel (tree_column->tree_view);
1064           if (gtk_widget_is_toplevel (toplevel))
1065             {
1066               gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
1067             }
1068         }
1069     }
1070   /* Queue a resize on the assumption that we always want to catch all changes
1071    * and columns don't change all that often.
1072    */
1073   if (gtk_widget_get_realized (tree_column->tree_view))
1074      gtk_widget_queue_resize (tree_column->tree_view);
1075
1076 }
1077
1078 /* Button signal handlers
1079  */
1080
1081 static gint
1082 pspp_sheet_view_column_button_event (GtkWidget *widget,
1083                                    GdkEvent  *event,
1084                                    gpointer   data)
1085 {
1086   PsppSheetViewColumn *column = (PsppSheetViewColumn *) data;
1087
1088   g_return_val_if_fail (event != NULL, FALSE);
1089
1090   if (event->type == GDK_BUTTON_PRESS &&
1091       column->reorderable &&
1092       ((GdkEventButton *)event)->button == 1)
1093     {
1094       column->maybe_reordered = TRUE;
1095       gdk_window_get_pointer (GTK_BUTTON (widget)->event_window,
1096                               &column->drag_x,
1097                               &column->drag_y,
1098                               NULL);
1099       gtk_widget_grab_focus (widget);
1100     }
1101
1102   if (event->type == GDK_BUTTON_RELEASE ||
1103       event->type == GDK_LEAVE_NOTIFY)
1104     column->maybe_reordered = FALSE;
1105   
1106   if (event->type == GDK_MOTION_NOTIFY &&
1107       column->maybe_reordered &&
1108       (gtk_drag_check_threshold (widget,
1109                                  column->drag_x,
1110                                  column->drag_y,
1111                                  (gint) ((GdkEventMotion *)event)->x,
1112                                  (gint) ((GdkEventMotion *)event)->y)))
1113     {
1114       column->maybe_reordered = FALSE;
1115       _pspp_sheet_view_column_start_drag (PSPP_SHEET_VIEW (column->tree_view), column);
1116       return TRUE;
1117     }
1118   if (column->clickable == FALSE)
1119     {
1120       switch (event->type)
1121         {
1122         case GDK_BUTTON_PRESS:
1123         case GDK_2BUTTON_PRESS:
1124         case GDK_3BUTTON_PRESS:
1125         case GDK_MOTION_NOTIFY:
1126         case GDK_BUTTON_RELEASE:
1127         case GDK_ENTER_NOTIFY:
1128         case GDK_LEAVE_NOTIFY:
1129           return TRUE;
1130         default:
1131           return FALSE;
1132         }
1133     }
1134   return FALSE;
1135 }
1136
1137
1138 static void
1139 pspp_sheet_view_column_button_clicked (GtkWidget *widget, gpointer data)
1140 {
1141   g_signal_emit_by_name (data, "clicked");
1142 }
1143
1144 static gboolean
1145 pspp_sheet_view_column_mnemonic_activate (GtkWidget *widget,
1146                                         gboolean   group_cycling,
1147                                         gpointer   data)
1148 {
1149   PsppSheetViewColumn *column = (PsppSheetViewColumn *)data;
1150
1151   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), FALSE);
1152
1153   PSPP_SHEET_VIEW (column->tree_view)->priv->focus_column = column;
1154   if (column->clickable)
1155     gtk_button_clicked (GTK_BUTTON (column->button));
1156   else if (gtk_widget_get_can_focus (column->button))
1157     gtk_widget_grab_focus (column->button);
1158   else
1159     gtk_widget_grab_focus (column->tree_view);
1160
1161   return TRUE;
1162 }
1163
1164 static void
1165 pspp_sheet_view_model_sort_column_changed (GtkTreeSortable   *sortable,
1166                                          PsppSheetViewColumn *column)
1167 {
1168   gint sort_column_id;
1169   GtkSortType order;
1170
1171   if (gtk_tree_sortable_get_sort_column_id (sortable,
1172                                             &sort_column_id,
1173                                             &order))
1174     {
1175       if (sort_column_id == column->sort_column_id)
1176         {
1177           pspp_sheet_view_column_set_sort_indicator (column, TRUE);
1178           pspp_sheet_view_column_set_sort_order (column, order);
1179         }
1180       else
1181         {
1182           pspp_sheet_view_column_set_sort_indicator (column, FALSE);
1183         }
1184     }
1185   else
1186     {
1187       pspp_sheet_view_column_set_sort_indicator (column, FALSE);
1188     }
1189 }
1190
1191 static void
1192 pspp_sheet_view_column_sort (PsppSheetViewColumn *tree_column,
1193                            gpointer           data)
1194 {
1195   gint sort_column_id;
1196   GtkSortType order;
1197   gboolean has_sort_column;
1198   gboolean has_default_sort_func;
1199
1200   g_return_if_fail (tree_column->tree_view != NULL);
1201
1202   has_sort_column =
1203     gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model),
1204                                           &sort_column_id,
1205                                           &order);
1206   has_default_sort_func =
1207     gtk_tree_sortable_has_default_sort_func (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model));
1208
1209   if (has_sort_column &&
1210       sort_column_id == tree_column->sort_column_id)
1211     {
1212       if (order == GTK_SORT_ASCENDING)
1213         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model),
1214                                               tree_column->sort_column_id,
1215                                               GTK_SORT_DESCENDING);
1216       else if (order == GTK_SORT_DESCENDING && has_default_sort_func)
1217         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model),
1218                                               GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
1219                                               GTK_SORT_ASCENDING);
1220       else
1221         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model),
1222                                               tree_column->sort_column_id,
1223                                               GTK_SORT_ASCENDING);
1224     }
1225   else
1226     {
1227       gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model),
1228                                             tree_column->sort_column_id,
1229                                             GTK_SORT_ASCENDING);
1230     }
1231 }
1232
1233
1234 static void
1235 pspp_sheet_view_column_setup_sort_column_id_callback (PsppSheetViewColumn *tree_column)
1236 {
1237   GtkTreeModel *model;
1238
1239   if (tree_column->tree_view == NULL)
1240     return;
1241
1242   model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (tree_column->tree_view));
1243
1244   if (model == NULL)
1245     return;
1246
1247   if (GTK_IS_TREE_SORTABLE (model) &&
1248       tree_column->sort_column_id != -1)
1249     {
1250       gint real_sort_column_id;
1251       GtkSortType real_order;
1252
1253       if (tree_column->sort_column_changed_signal == 0)
1254         tree_column->sort_column_changed_signal =
1255           g_signal_connect (model, "sort-column-changed",
1256                             G_CALLBACK (pspp_sheet_view_model_sort_column_changed),
1257                             tree_column);
1258       
1259       if (gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model),
1260                                                 &real_sort_column_id,
1261                                                 &real_order) &&
1262           (real_sort_column_id == tree_column->sort_column_id))
1263         {
1264           pspp_sheet_view_column_set_sort_indicator (tree_column, TRUE);
1265           pspp_sheet_view_column_set_sort_order (tree_column, real_order);
1266         }
1267       else 
1268         {
1269           pspp_sheet_view_column_set_sort_indicator (tree_column, FALSE);
1270         }
1271    }
1272 }
1273
1274
1275 /* Exported Private Functions.
1276  * These should only be called by gtktreeview.c or gtktreeviewcolumn.c
1277  */
1278
1279 void
1280 _pspp_sheet_view_column_realize_button (PsppSheetViewColumn *column)
1281 {
1282   PsppSheetView *tree_view;
1283   GdkWindowAttr attr;
1284   guint attributes_mask;
1285   gboolean rtl;
1286
1287   tree_view = (PsppSheetView *)column->tree_view;
1288   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1289
1290   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
1291   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
1292   g_return_if_fail (tree_view->priv->header_window != NULL);
1293   g_return_if_fail (column->button != NULL);
1294
1295   gtk_widget_set_parent_window (column->button, tree_view->priv->header_window);
1296
1297   if (column->visible)
1298     gtk_widget_show (column->button);
1299
1300   attr.window_type = GDK_WINDOW_CHILD;
1301   attr.wclass = GDK_INPUT_ONLY;
1302   attr.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
1303   attr.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
1304   attr.event_mask = gtk_widget_get_events (GTK_WIDGET (tree_view)) |
1305                     (GDK_BUTTON_PRESS_MASK |
1306                      GDK_BUTTON_RELEASE_MASK |
1307                      GDK_POINTER_MOTION_MASK |
1308                      GDK_POINTER_MOTION_HINT_MASK |
1309                      GDK_KEY_PRESS_MASK);
1310   attributes_mask = GDK_WA_CURSOR | GDK_WA_X | GDK_WA_Y;
1311   attr.cursor = gdk_cursor_new_for_display (gdk_drawable_get_display (tree_view->priv->header_window),
1312                                             GDK_SB_H_DOUBLE_ARROW);
1313   attr.y = 0;
1314   attr.width = TREE_VIEW_DRAG_WIDTH;
1315   attr.height = tree_view->priv->header_height;
1316
1317   attr.x = (column->button->allocation.x + (rtl ? 0 : column->button->allocation.width)) - TREE_VIEW_DRAG_WIDTH / 2;
1318   column->window = gdk_window_new (tree_view->priv->header_window,
1319                                    &attr, attributes_mask);
1320   gdk_window_set_user_data (column->window, tree_view);
1321
1322   pspp_sheet_view_column_update_button (column);
1323
1324   gdk_cursor_unref (attr.cursor);
1325 }
1326
1327 void
1328 _pspp_sheet_view_column_unrealize_button (PsppSheetViewColumn *column)
1329 {
1330   g_return_if_fail (column != NULL);
1331   g_return_if_fail (column->window != NULL);
1332
1333   gdk_window_set_user_data (column->window, NULL);
1334   gdk_window_destroy (column->window);
1335   column->window = NULL;
1336 }
1337
1338 void
1339 _pspp_sheet_view_column_unset_model (PsppSheetViewColumn *column,
1340                                    GtkTreeModel      *old_model)
1341 {
1342   if (column->sort_column_changed_signal)
1343     {
1344       g_signal_handler_disconnect (old_model,
1345                                    column->sort_column_changed_signal);
1346       column->sort_column_changed_signal = 0;
1347     }
1348   pspp_sheet_view_column_set_sort_indicator (column, FALSE);
1349 }
1350
1351 void
1352 _pspp_sheet_view_column_set_tree_view (PsppSheetViewColumn *column,
1353                                      PsppSheetView       *tree_view)
1354 {
1355   g_assert (column->tree_view == NULL);
1356
1357   column->tree_view = GTK_WIDGET (tree_view);
1358   pspp_sheet_view_column_create_button (column);
1359
1360   column->property_changed_signal =
1361           g_signal_connect_swapped (tree_view,
1362                                     "notify::model",
1363                                     G_CALLBACK (pspp_sheet_view_column_setup_sort_column_id_callback),
1364                                     column);
1365
1366   pspp_sheet_view_column_setup_sort_column_id_callback (column);
1367 }
1368
1369 void
1370 _pspp_sheet_view_column_unset_tree_view (PsppSheetViewColumn *column)
1371 {
1372   if (column->tree_view && column->button)
1373     {
1374       gtk_container_remove (GTK_CONTAINER (column->tree_view), column->button);
1375     }
1376   if (column->property_changed_signal)
1377     {
1378       g_signal_handler_disconnect (column->tree_view, column->property_changed_signal);
1379       column->property_changed_signal = 0;
1380     }
1381
1382   if (column->sort_column_changed_signal)
1383     {
1384       g_signal_handler_disconnect (pspp_sheet_view_get_model (PSPP_SHEET_VIEW (column->tree_view)),
1385                                    column->sort_column_changed_signal);
1386       column->sort_column_changed_signal = 0;
1387     }
1388
1389   column->tree_view = NULL;
1390   column->button = NULL;
1391 }
1392
1393 gboolean
1394 _pspp_sheet_view_column_has_editable_cell (PsppSheetViewColumn *column)
1395 {
1396   GList *list;
1397
1398   for (list = column->cell_list; list; list = list->next)
1399     if (((PsppSheetViewColumnCellInfo *)list->data)->cell->mode ==
1400         GTK_CELL_RENDERER_MODE_EDITABLE)
1401       return TRUE;
1402
1403   return FALSE;
1404 }
1405
1406 /* gets cell being edited */
1407 GtkCellRenderer *
1408 _pspp_sheet_view_column_get_edited_cell (PsppSheetViewColumn *column)
1409 {
1410   GList *list;
1411
1412   for (list = column->cell_list; list; list = list->next)
1413     if (((PsppSheetViewColumnCellInfo *)list->data)->in_editing_mode)
1414       return ((PsppSheetViewColumnCellInfo *)list->data)->cell;
1415
1416   return NULL;
1417 }
1418
1419 gint
1420 _pspp_sheet_view_column_count_special_cells (PsppSheetViewColumn *column)
1421 {
1422   gint i = 0;
1423   GList *list;
1424
1425   for (list = column->cell_list; list; list = list->next)
1426     {
1427       PsppSheetViewColumnCellInfo *cellinfo = list->data;
1428
1429       if ((cellinfo->cell->mode == GTK_CELL_RENDERER_MODE_EDITABLE ||
1430           cellinfo->cell->mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE) &&
1431           cellinfo->cell->visible)
1432         i++;
1433     }
1434
1435   return i;
1436 }
1437
1438 GtkCellRenderer *
1439 _pspp_sheet_view_column_get_cell_at_pos (PsppSheetViewColumn *column,
1440                                        gint               x)
1441 {
1442   GList *list;
1443   gint current_x = 0;
1444
1445   list = pspp_sheet_view_column_cell_first (column);
1446   for (; list; list = pspp_sheet_view_column_cell_next (column, list))
1447    {
1448      PsppSheetViewColumnCellInfo *cellinfo = list->data;
1449      if (current_x <= x && x <= current_x + cellinfo->real_width)
1450        return cellinfo->cell;
1451      current_x += cellinfo->real_width;
1452    }
1453
1454   return NULL;
1455 }
1456
1457 /* Public Functions */
1458
1459
1460 /**
1461  * pspp_sheet_view_column_new:
1462  * 
1463  * Creates a new #PsppSheetViewColumn.
1464  * 
1465  * Return value: A newly created #PsppSheetViewColumn.
1466  **/
1467 PsppSheetViewColumn *
1468 pspp_sheet_view_column_new (void)
1469 {
1470   PsppSheetViewColumn *tree_column;
1471
1472   tree_column = g_object_new (PSPP_TYPE_SHEET_VIEW_COLUMN, NULL);
1473
1474   return tree_column;
1475 }
1476
1477 /**
1478  * pspp_sheet_view_column_new_with_attributes:
1479  * @title: The title to set the header to.
1480  * @cell: The #GtkCellRenderer.
1481  * @Varargs: A %NULL-terminated list of attributes.
1482  * 
1483  * Creates a new #PsppSheetViewColumn with a number of default values.  This is
1484  * equivalent to calling pspp_sheet_view_column_set_title(),
1485  * pspp_sheet_view_column_pack_start(), and
1486  * pspp_sheet_view_column_set_attributes() on the newly created #PsppSheetViewColumn.
1487  *
1488  * Here's a simple example:
1489  * |[
1490  *  enum { TEXT_COLUMN, COLOR_COLUMN, N_COLUMNS };
1491  *  ...
1492  *  {
1493  *    PsppSheetViewColumn *column;
1494  *    GtkCellRenderer   *renderer = gtk_cell_renderer_text_new ();
1495  *  
1496  *    column = pspp_sheet_view_column_new_with_attributes ("Title",
1497  *                                                       renderer,
1498  *                                                       "text", TEXT_COLUMN,
1499  *                                                       "foreground", COLOR_COLUMN,
1500  *                                                       NULL);
1501  *  }
1502  * ]|
1503  * 
1504  * Return value: A newly created #PsppSheetViewColumn.
1505  **/
1506 PsppSheetViewColumn *
1507 pspp_sheet_view_column_new_with_attributes (const gchar     *title,
1508                                           GtkCellRenderer *cell,
1509                                           ...)
1510 {
1511   PsppSheetViewColumn *retval;
1512   va_list args;
1513
1514   retval = pspp_sheet_view_column_new ();
1515
1516   pspp_sheet_view_column_set_title (retval, title);
1517   pspp_sheet_view_column_pack_start (retval, cell, TRUE);
1518
1519   va_start (args, cell);
1520   pspp_sheet_view_column_set_attributesv (retval, cell, args);
1521   va_end (args);
1522
1523   return retval;
1524 }
1525
1526 static PsppSheetViewColumnCellInfo *
1527 pspp_sheet_view_column_get_cell_info (PsppSheetViewColumn *tree_column,
1528                                     GtkCellRenderer   *cell_renderer)
1529 {
1530   GList *list;
1531   for (list = tree_column->cell_list; list; list = list->next)
1532     if (((PsppSheetViewColumnCellInfo *)list->data)->cell == cell_renderer)
1533       return (PsppSheetViewColumnCellInfo *) list->data;
1534   return NULL;
1535 }
1536
1537
1538 /**
1539  * pspp_sheet_view_column_pack_start:
1540  * @tree_column: A #PsppSheetViewColumn.
1541  * @cell: The #GtkCellRenderer. 
1542  * @expand: %TRUE if @cell is to be given extra space allocated to @tree_column.
1543  *
1544  * Packs the @cell into the beginning of the column. If @expand is %FALSE, then
1545  * the @cell is allocated no more space than it needs. Any unused space is divided
1546  * evenly between cells for which @expand is %TRUE.
1547  **/
1548 void
1549 pspp_sheet_view_column_pack_start (PsppSheetViewColumn *tree_column,
1550                                  GtkCellRenderer   *cell,
1551                                  gboolean           expand)
1552 {
1553   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (tree_column), cell, expand);
1554 }
1555
1556 /**
1557  * pspp_sheet_view_column_pack_end:
1558  * @tree_column: A #PsppSheetViewColumn.
1559  * @cell: The #GtkCellRenderer. 
1560  * @expand: %TRUE if @cell is to be given extra space allocated to @tree_column.
1561  *
1562  * Adds the @cell to end of the column. If @expand is %FALSE, then the @cell
1563  * is allocated no more space than it needs. Any unused space is divided
1564  * evenly between cells for which @expand is %TRUE.
1565  **/
1566 void
1567 pspp_sheet_view_column_pack_end (PsppSheetViewColumn  *tree_column,
1568                                GtkCellRenderer    *cell,
1569                                gboolean            expand)
1570 {
1571   gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (tree_column), cell, expand);
1572 }
1573
1574 /**
1575  * pspp_sheet_view_column_clear:
1576  * @tree_column: A #PsppSheetViewColumn
1577  * 
1578  * Unsets all the mappings on all renderers on the @tree_column.
1579  **/
1580 void
1581 pspp_sheet_view_column_clear (PsppSheetViewColumn *tree_column)
1582 {
1583   gtk_cell_layout_clear (GTK_CELL_LAYOUT (tree_column));
1584 }
1585
1586 static GList *
1587 pspp_sheet_view_column_cell_layout_get_cells (GtkCellLayout *layout)
1588 {
1589   PsppSheetViewColumn *tree_column = PSPP_SHEET_VIEW_COLUMN (layout);
1590   GList *retval = NULL, *list;
1591
1592   g_return_val_if_fail (tree_column != NULL, NULL);
1593
1594   for (list = tree_column->cell_list; list; list = list->next)
1595     {
1596       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *)list->data;
1597
1598       retval = g_list_append (retval, info->cell);
1599     }
1600
1601   return retval;
1602 }
1603
1604 /**
1605  * pspp_sheet_view_column_get_cell_renderers:
1606  * @tree_column: A #PsppSheetViewColumn
1607  *
1608  * Returns a newly-allocated #GList of all the cell renderers in the column,
1609  * in no particular order.  The list must be freed with g_list_free().
1610  *
1611  * Return value: A list of #GtkCellRenderers
1612  *
1613  * Deprecated: 2.18: use gtk_cell_layout_get_cells() instead.
1614  **/
1615 GList *
1616 pspp_sheet_view_column_get_cell_renderers (PsppSheetViewColumn *tree_column)
1617 {
1618   return pspp_sheet_view_column_cell_layout_get_cells (GTK_CELL_LAYOUT (tree_column));
1619 }
1620
1621 /**
1622  * pspp_sheet_view_column_add_attribute:
1623  * @tree_column: A #PsppSheetViewColumn.
1624  * @cell_renderer: the #GtkCellRenderer to set attributes on
1625  * @attribute: An attribute on the renderer
1626  * @column: The column position on the model to get the attribute from.
1627  * 
1628  * Adds an attribute mapping to the list in @tree_column.  The @column is the
1629  * column of the model to get a value from, and the @attribute is the
1630  * parameter on @cell_renderer to be set from the value. So for example
1631  * if column 2 of the model contains strings, you could have the
1632  * "text" attribute of a #GtkCellRendererText get its values from
1633  * column 2.
1634  **/
1635 void
1636 pspp_sheet_view_column_add_attribute (PsppSheetViewColumn *tree_column,
1637                                     GtkCellRenderer   *cell_renderer,
1638                                     const gchar       *attribute,
1639                                     gint               column)
1640 {
1641   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (tree_column),
1642                                  cell_renderer, attribute, column);
1643 }
1644
1645 static void
1646 pspp_sheet_view_column_set_attributesv (PsppSheetViewColumn *tree_column,
1647                                       GtkCellRenderer   *cell_renderer,
1648                                       va_list            args)
1649 {
1650   gchar *attribute;
1651   gint column;
1652
1653   attribute = va_arg (args, gchar *);
1654
1655   pspp_sheet_view_column_clear_attributes (tree_column, cell_renderer);
1656   
1657   while (attribute != NULL)
1658     {
1659       column = va_arg (args, gint);
1660       pspp_sheet_view_column_add_attribute (tree_column, cell_renderer, attribute, column);
1661       attribute = va_arg (args, gchar *);
1662     }
1663 }
1664
1665 /**
1666  * pspp_sheet_view_column_set_attributes:
1667  * @tree_column: A #PsppSheetViewColumn.
1668  * @cell_renderer: the #GtkCellRenderer we're setting the attributes of
1669  * @Varargs: A %NULL-terminated list of attributes.
1670  * 
1671  * Sets the attributes in the list as the attributes of @tree_column.
1672  * The attributes should be in attribute/column order, as in
1673  * pspp_sheet_view_column_add_attribute(). All existing attributes
1674  * are removed, and replaced with the new attributes.
1675  **/
1676 void
1677 pspp_sheet_view_column_set_attributes (PsppSheetViewColumn *tree_column,
1678                                      GtkCellRenderer   *cell_renderer,
1679                                      ...)
1680 {
1681   va_list args;
1682
1683   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
1684   g_return_if_fail (GTK_IS_CELL_RENDERER (cell_renderer));
1685   g_return_if_fail (pspp_sheet_view_column_get_cell_info (tree_column, cell_renderer));
1686
1687   va_start (args, cell_renderer);
1688   pspp_sheet_view_column_set_attributesv (tree_column, cell_renderer, args);
1689   va_end (args);
1690 }
1691
1692
1693 /**
1694  * pspp_sheet_view_column_set_cell_data_func:
1695  * @tree_column: A #PsppSheetViewColumn
1696  * @cell_renderer: A #GtkCellRenderer
1697  * @func: The #PsppSheetViewColumnFunc to use. 
1698  * @func_data: The user data for @func.
1699  * @destroy: The destroy notification for @func_data
1700  * 
1701  * Sets the #PsppSheetViewColumnFunc to use for the column.  This
1702  * function is used instead of the standard attributes mapping for
1703  * setting the column value, and should set the value of @tree_column's
1704  * cell renderer as appropriate.  @func may be %NULL to remove an
1705  * older one.
1706  **/
1707 void
1708 pspp_sheet_view_column_set_cell_data_func (PsppSheetViewColumn   *tree_column,
1709                                          GtkCellRenderer     *cell_renderer,
1710                                          PsppSheetCellDataFunc  func,
1711                                          gpointer             func_data,
1712                                          GDestroyNotify       destroy)
1713 {
1714   gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (tree_column),
1715                                       cell_renderer,
1716                                       (GtkCellLayoutDataFunc)func,
1717                                       func_data, destroy);
1718 }
1719
1720
1721 /**
1722  * pspp_sheet_view_column_clear_attributes:
1723  * @tree_column: a #PsppSheetViewColumn
1724  * @cell_renderer: a #GtkCellRenderer to clear the attribute mapping on.
1725  * 
1726  * Clears all existing attributes previously set with
1727  * pspp_sheet_view_column_set_attributes().
1728  **/
1729 void
1730 pspp_sheet_view_column_clear_attributes (PsppSheetViewColumn *tree_column,
1731                                        GtkCellRenderer   *cell_renderer)
1732 {
1733   gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (tree_column),
1734                                     cell_renderer);
1735 }
1736
1737 /**
1738  * pspp_sheet_view_column_set_spacing:
1739  * @tree_column: A #PsppSheetViewColumn.
1740  * @spacing: distance between cell renderers in pixels.
1741  * 
1742  * Sets the spacing field of @tree_column, which is the number of pixels to
1743  * place between cell renderers packed into it.
1744  **/
1745 void
1746 pspp_sheet_view_column_set_spacing (PsppSheetViewColumn *tree_column,
1747                                   gint               spacing)
1748 {
1749   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
1750   g_return_if_fail (spacing >= 0);
1751
1752   if (tree_column->spacing == spacing)
1753     return;
1754
1755   tree_column->spacing = spacing;
1756   if (tree_column->tree_view)
1757     _pspp_sheet_view_column_cell_set_dirty (tree_column, TRUE);
1758 }
1759
1760 /**
1761  * pspp_sheet_view_column_get_spacing:
1762  * @tree_column: A #PsppSheetViewColumn.
1763  * 
1764  * Returns the spacing of @tree_column.
1765  * 
1766  * Return value: the spacing of @tree_column.
1767  **/
1768 gint
1769 pspp_sheet_view_column_get_spacing (PsppSheetViewColumn *tree_column)
1770 {
1771   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
1772
1773   return tree_column->spacing;
1774 }
1775
1776 /* Options for manipulating the columns */
1777
1778 /**
1779  * pspp_sheet_view_column_set_visible:
1780  * @tree_column: A #PsppSheetViewColumn.
1781  * @visible: %TRUE if the @tree_column is visible.
1782  * 
1783  * Sets the visibility of @tree_column.
1784  **/
1785 void
1786 pspp_sheet_view_column_set_visible (PsppSheetViewColumn *tree_column,
1787                                   gboolean           visible)
1788 {
1789   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
1790
1791   visible = !! visible;
1792   
1793   if (tree_column->visible == visible)
1794     return;
1795
1796   tree_column->visible = visible;
1797
1798   if (tree_column->visible)
1799     _pspp_sheet_view_column_cell_set_dirty (tree_column, TRUE);
1800
1801   pspp_sheet_view_column_update_button (tree_column);
1802   g_object_notify (G_OBJECT (tree_column), "visible");
1803 }
1804
1805 /**
1806  * pspp_sheet_view_column_get_visible:
1807  * @tree_column: A #PsppSheetViewColumn.
1808  * 
1809  * Returns %TRUE if @tree_column is visible.
1810  * 
1811  * Return value: whether the column is visible or not.  If it is visible, then
1812  * the tree will show the column.
1813  **/
1814 gboolean
1815 pspp_sheet_view_column_get_visible (PsppSheetViewColumn *tree_column)
1816 {
1817   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
1818
1819   return tree_column->visible;
1820 }
1821
1822 /**
1823  * pspp_sheet_view_column_set_resizable:
1824  * @tree_column: A #PsppSheetViewColumn
1825  * @resizable: %TRUE, if the column can be resized
1826  * 
1827  * If @resizable is %TRUE, then the user can explicitly resize the column by
1828  * grabbing the outer edge of the column button.  If resizable is %TRUE and
1829  * sizing mode of the column is #PSPP_SHEET_VIEW_COLUMN_AUTOSIZE, then the sizing
1830  * mode is changed to #PSPP_SHEET_VIEW_COLUMN_GROW_ONLY.
1831  **/
1832 void
1833 pspp_sheet_view_column_set_resizable (PsppSheetViewColumn *tree_column,
1834                                     gboolean           resizable)
1835 {
1836   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
1837
1838   resizable = !! resizable;
1839
1840   if (tree_column->resizable == resizable)
1841     return;
1842
1843   tree_column->resizable = resizable;
1844
1845   if (resizable && tree_column->column_type == PSPP_SHEET_VIEW_COLUMN_AUTOSIZE)
1846     pspp_sheet_view_column_set_sizing (tree_column, PSPP_SHEET_VIEW_COLUMN_GROW_ONLY);
1847
1848   pspp_sheet_view_column_update_button (tree_column);
1849
1850   g_object_notify (G_OBJECT (tree_column), "resizable");
1851 }
1852
1853 /**
1854  * pspp_sheet_view_column_get_resizable:
1855  * @tree_column: A #PsppSheetViewColumn
1856  * 
1857  * Returns %TRUE if the @tree_column can be resized by the end user.
1858  * 
1859  * Return value: %TRUE, if the @tree_column can be resized.
1860  **/
1861 gboolean
1862 pspp_sheet_view_column_get_resizable (PsppSheetViewColumn *tree_column)
1863 {
1864   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
1865
1866   return tree_column->resizable;
1867 }
1868
1869
1870 /**
1871  * pspp_sheet_view_column_set_sizing:
1872  * @tree_column: A #PsppSheetViewColumn.
1873  * @type: The #PsppSheetViewColumnSizing.
1874  * 
1875  * Sets the growth behavior of @tree_column to @type.
1876  **/
1877 void
1878 pspp_sheet_view_column_set_sizing (PsppSheetViewColumn       *tree_column,
1879                                  PsppSheetViewColumnSizing  type)
1880 {
1881   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
1882
1883   if (type == tree_column->column_type)
1884     return;
1885
1886   if (type == PSPP_SHEET_VIEW_COLUMN_AUTOSIZE)
1887     pspp_sheet_view_column_set_resizable (tree_column, FALSE);
1888
1889 #if 0
1890   /* I was clearly on crack when I wrote this.  I'm not sure what's supposed to
1891    * be below so I'll leave it until I figure it out.
1892    */
1893   if (tree_column->column_type == PSPP_SHEET_VIEW_COLUMN_AUTOSIZE &&
1894       tree_column->requested_width != -1)
1895     {
1896       pspp_sheet_view_column_set_sizing (tree_column, tree_column->requested_width);
1897     }
1898 #endif
1899   tree_column->column_type = type;
1900
1901   pspp_sheet_view_column_update_button (tree_column);
1902
1903   g_object_notify (G_OBJECT (tree_column), "sizing");
1904 }
1905
1906 /**
1907  * pspp_sheet_view_column_get_sizing:
1908  * @tree_column: A #PsppSheetViewColumn.
1909  * 
1910  * Returns the current type of @tree_column.
1911  * 
1912  * Return value: The type of @tree_column.
1913  **/
1914 PsppSheetViewColumnSizing
1915 pspp_sheet_view_column_get_sizing (PsppSheetViewColumn *tree_column)
1916 {
1917   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
1918
1919   return tree_column->column_type;
1920 }
1921
1922 /**
1923  * pspp_sheet_view_column_get_width:
1924  * @tree_column: A #PsppSheetViewColumn.
1925  * 
1926  * Returns the current size of @tree_column in pixels.
1927  * 
1928  * Return value: The current width of @tree_column.
1929  **/
1930 gint
1931 pspp_sheet_view_column_get_width (PsppSheetViewColumn *tree_column)
1932 {
1933   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
1934
1935   return tree_column->width;
1936 }
1937
1938 /**
1939  * pspp_sheet_view_column_set_fixed_width:
1940  * @tree_column: A #PsppSheetViewColumn.
1941  * @fixed_width: The size to set @tree_column to. Must be greater than 0.
1942  * 
1943  * Sets the size of the column in pixels.  This is meaningful only if the sizing
1944  * type is #PSPP_SHEET_VIEW_COLUMN_FIXED.  The size of the column is clamped to
1945  * the min/max width for the column.  Please note that the min/max width of the
1946  * column doesn't actually affect the "fixed_width" property of the widget, just
1947  * the actual size when displayed.
1948  **/
1949 void
1950 pspp_sheet_view_column_set_fixed_width (PsppSheetViewColumn *tree_column,
1951                                       gint               fixed_width)
1952 {
1953   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
1954   g_return_if_fail (fixed_width > 0);
1955
1956   tree_column->fixed_width = fixed_width;
1957   tree_column->use_resized_width = FALSE;
1958
1959   if (tree_column->tree_view &&
1960       gtk_widget_get_realized (tree_column->tree_view) &&
1961       tree_column->column_type == PSPP_SHEET_VIEW_COLUMN_FIXED)
1962     {
1963       gtk_widget_queue_resize (tree_column->tree_view);
1964     }
1965
1966   g_object_notify (G_OBJECT (tree_column), "fixed-width");
1967 }
1968
1969 /**
1970  * pspp_sheet_view_column_get_fixed_width:
1971  * @tree_column: a #PsppSheetViewColumn
1972  * 
1973  * Gets the fixed width of the column.  This value is only meaning may not be
1974  * the actual width of the column on the screen, just what is requested.
1975  * 
1976  * Return value: the fixed width of the column
1977  **/
1978 gint
1979 pspp_sheet_view_column_get_fixed_width (PsppSheetViewColumn *tree_column)
1980 {
1981   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
1982
1983   return tree_column->fixed_width;
1984 }
1985
1986 /**
1987  * pspp_sheet_view_column_set_min_width:
1988  * @tree_column: A #PsppSheetViewColumn.
1989  * @min_width: The minimum width of the column in pixels, or -1.
1990  * 
1991  * Sets the minimum width of the @tree_column.  If @min_width is -1, then the
1992  * minimum width is unset.
1993  **/
1994 void
1995 pspp_sheet_view_column_set_min_width (PsppSheetViewColumn *tree_column,
1996                                     gint               min_width)
1997 {
1998   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
1999   g_return_if_fail (min_width >= -1);
2000
2001   if (min_width == tree_column->min_width)
2002     return;
2003
2004   if (tree_column->visible &&
2005       tree_column->tree_view != NULL &&
2006       gtk_widget_get_realized (tree_column->tree_view))
2007     {
2008       if (min_width > tree_column->width)
2009         gtk_widget_queue_resize (tree_column->tree_view);
2010     }
2011
2012   tree_column->min_width = min_width;
2013   g_object_freeze_notify (G_OBJECT (tree_column));
2014   if (tree_column->max_width != -1 && tree_column->max_width < min_width)
2015     {
2016       tree_column->max_width = min_width;
2017       g_object_notify (G_OBJECT (tree_column), "max-width");
2018     }
2019   g_object_notify (G_OBJECT (tree_column), "min-width");
2020   g_object_thaw_notify (G_OBJECT (tree_column));
2021
2022   if (tree_column->column_type == PSPP_SHEET_VIEW_COLUMN_AUTOSIZE)
2023     _pspp_sheet_view_column_autosize (PSPP_SHEET_VIEW (tree_column->tree_view),
2024                                     tree_column);
2025 }
2026
2027 /**
2028  * pspp_sheet_view_column_get_min_width:
2029  * @tree_column: A #PsppSheetViewColumn.
2030  * 
2031  * Returns the minimum width in pixels of the @tree_column, or -1 if no minimum
2032  * width is set.
2033  * 
2034  * Return value: The minimum width of the @tree_column.
2035  **/
2036 gint
2037 pspp_sheet_view_column_get_min_width (PsppSheetViewColumn *tree_column)
2038 {
2039   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), -1);
2040
2041   return tree_column->min_width;
2042 }
2043
2044 /**
2045  * pspp_sheet_view_column_set_max_width:
2046  * @tree_column: A #PsppSheetViewColumn.
2047  * @max_width: The maximum width of the column in pixels, or -1.
2048  * 
2049  * Sets the maximum width of the @tree_column.  If @max_width is -1, then the
2050  * maximum width is unset.  Note, the column can actually be wider than max
2051  * width if it's the last column in a view.  In this case, the column expands to
2052  * fill any extra space.
2053  **/
2054 void
2055 pspp_sheet_view_column_set_max_width (PsppSheetViewColumn *tree_column,
2056                                     gint               max_width)
2057 {
2058   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2059   g_return_if_fail (max_width >= -1);
2060
2061   if (max_width == tree_column->max_width)
2062     return;
2063
2064   if (tree_column->visible &&
2065       tree_column->tree_view != NULL &&
2066       gtk_widget_get_realized (tree_column->tree_view))
2067     {
2068       if (max_width != -1 && max_width < tree_column->width)
2069         gtk_widget_queue_resize (tree_column->tree_view);
2070     }
2071
2072   tree_column->max_width = max_width;
2073   g_object_freeze_notify (G_OBJECT (tree_column));
2074   if (max_width != -1 && max_width < tree_column->min_width)
2075     {
2076       tree_column->min_width = max_width;
2077       g_object_notify (G_OBJECT (tree_column), "min-width");
2078     }
2079   g_object_notify (G_OBJECT (tree_column), "max-width");
2080   g_object_thaw_notify (G_OBJECT (tree_column));
2081
2082   if (tree_column->column_type == PSPP_SHEET_VIEW_COLUMN_AUTOSIZE)
2083     _pspp_sheet_view_column_autosize (PSPP_SHEET_VIEW (tree_column->tree_view),
2084                                     tree_column);
2085 }
2086
2087 /**
2088  * pspp_sheet_view_column_get_max_width:
2089  * @tree_column: A #PsppSheetViewColumn.
2090  * 
2091  * Returns the maximum width in pixels of the @tree_column, or -1 if no maximum
2092  * width is set.
2093  * 
2094  * Return value: The maximum width of the @tree_column.
2095  **/
2096 gint
2097 pspp_sheet_view_column_get_max_width (PsppSheetViewColumn *tree_column)
2098 {
2099   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), -1);
2100
2101   return tree_column->max_width;
2102 }
2103
2104 /**
2105  * pspp_sheet_view_column_clicked:
2106  * @tree_column: a #PsppSheetViewColumn
2107  * 
2108  * Emits the "clicked" signal on the column.  This function will only work if
2109  * @tree_column is clickable.
2110  **/
2111 void
2112 pspp_sheet_view_column_clicked (PsppSheetViewColumn *tree_column)
2113 {
2114   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2115
2116   if (tree_column->visible &&
2117       tree_column->button &&
2118       tree_column->clickable)
2119     gtk_button_clicked (GTK_BUTTON (tree_column->button));
2120 }
2121
2122 /**
2123  * pspp_sheet_view_column_set_title:
2124  * @tree_column: A #PsppSheetViewColumn.
2125  * @title: The title of the @tree_column.
2126  * 
2127  * Sets the title of the @tree_column.  If a custom widget has been set, then
2128  * this value is ignored.
2129  **/
2130 void
2131 pspp_sheet_view_column_set_title (PsppSheetViewColumn *tree_column,
2132                                 const gchar       *title)
2133 {
2134   gchar *new_title;
2135   
2136   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2137
2138   new_title = g_strdup (title);
2139   g_free (tree_column->title);
2140   tree_column->title = new_title;
2141
2142   pspp_sheet_view_column_update_button (tree_column);
2143   g_object_notify (G_OBJECT (tree_column), "title");
2144 }
2145
2146 /**
2147  * pspp_sheet_view_column_get_title:
2148  * @tree_column: A #PsppSheetViewColumn.
2149  * 
2150  * Returns the title of the widget.
2151  * 
2152  * Return value: the title of the column. This string should not be
2153  * modified or freed.
2154  **/
2155 G_CONST_RETURN gchar *
2156 pspp_sheet_view_column_get_title (PsppSheetViewColumn *tree_column)
2157 {
2158   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), NULL);
2159
2160   return tree_column->title;
2161 }
2162
2163 /**
2164  * pspp_sheet_view_column_set_expand:
2165  * @tree_column: A #PsppSheetViewColumn
2166  * @expand: %TRUE if the column should take available extra space, %FALSE if not
2167  * 
2168  * Sets the column to take available extra space.  This space is shared equally
2169  * amongst all columns that have the expand set to %TRUE.  If no column has this
2170  * option set, then the last column gets all extra space.  By default, every
2171  * column is created with this %FALSE.
2172  *
2173  * Since: 2.4
2174  **/
2175 void
2176 pspp_sheet_view_column_set_expand (PsppSheetViewColumn *tree_column,
2177                                  gboolean           expand)
2178 {
2179   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2180
2181   expand = expand?TRUE:FALSE;
2182   if (tree_column->expand == expand)
2183     return;
2184   tree_column->expand = expand;
2185
2186   if (tree_column->visible &&
2187       tree_column->tree_view != NULL &&
2188       gtk_widget_get_realized (tree_column->tree_view))
2189     {
2190       /* We want to continue using the original width of the
2191        * column that includes additional space added by the user
2192        * resizing the columns and possibly extra (expanded) space, which
2193        * are not included in the resized width.
2194        */
2195       tree_column->use_resized_width = FALSE;
2196
2197       gtk_widget_queue_resize (tree_column->tree_view);
2198     }
2199
2200   g_object_notify (G_OBJECT (tree_column), "expand");
2201 }
2202
2203 /**
2204  * pspp_sheet_view_column_get_expand:
2205  * @tree_column: a #PsppSheetViewColumn
2206  * 
2207  * Return %TRUE if the column expands to take any available space.
2208  * 
2209  * Return value: %TRUE, if the column expands
2210  *
2211  * Since: 2.4
2212  **/
2213 gboolean
2214 pspp_sheet_view_column_get_expand (PsppSheetViewColumn *tree_column)
2215 {
2216   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2217
2218   return tree_column->expand;
2219 }
2220
2221 /**
2222  * pspp_sheet_view_column_set_clickable:
2223  * @tree_column: A #PsppSheetViewColumn.
2224  * @clickable: %TRUE if the header is active.
2225  * 
2226  * Sets the header to be active if @active is %TRUE.  When the header is active,
2227  * then it can take keyboard focus, and can be clicked.
2228  **/
2229 void
2230 pspp_sheet_view_column_set_clickable (PsppSheetViewColumn *tree_column,
2231                                     gboolean           clickable)
2232 {
2233   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2234
2235   clickable = !! clickable;
2236   if (tree_column->clickable == clickable)
2237     return;
2238
2239   tree_column->clickable = clickable;
2240   pspp_sheet_view_column_update_button (tree_column);
2241   g_object_notify (G_OBJECT (tree_column), "clickable");
2242 }
2243
2244 /**
2245  * pspp_sheet_view_column_get_clickable:
2246  * @tree_column: a #PsppSheetViewColumn
2247  * 
2248  * Returns %TRUE if the user can click on the header for the column.
2249  * 
2250  * Return value: %TRUE if user can click the column header.
2251  **/
2252 gboolean
2253 pspp_sheet_view_column_get_clickable (PsppSheetViewColumn *tree_column)
2254 {
2255   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2256
2257   return tree_column->clickable;
2258 }
2259
2260 /**
2261  * pspp_sheet_view_column_set_widget:
2262  * @tree_column: A #PsppSheetViewColumn.
2263  * @widget: (allow-none): A child #GtkWidget, or %NULL.
2264  *
2265  * Sets the widget in the header to be @widget.  If widget is %NULL, then the
2266  * header button is set with a #GtkLabel set to the title of @tree_column.
2267  **/
2268 void
2269 pspp_sheet_view_column_set_widget (PsppSheetViewColumn *tree_column,
2270                                  GtkWidget         *widget)
2271 {
2272   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2273   g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
2274
2275   if (widget)
2276     g_object_ref_sink (widget);
2277
2278   if (tree_column->child)      
2279     g_object_unref (tree_column->child);
2280
2281   tree_column->child = widget;
2282   pspp_sheet_view_column_update_button (tree_column);
2283   g_object_notify (G_OBJECT (tree_column), "widget");
2284 }
2285
2286 /**
2287  * pspp_sheet_view_column_get_widget:
2288  * @tree_column: A #PsppSheetViewColumn.
2289  * 
2290  * Returns the #GtkWidget in the button on the column header.  If a custom
2291  * widget has not been set then %NULL is returned.
2292  * 
2293  * Return value: The #GtkWidget in the column header, or %NULL
2294  **/
2295 GtkWidget *
2296 pspp_sheet_view_column_get_widget (PsppSheetViewColumn *tree_column)
2297 {
2298   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), NULL);
2299
2300   return tree_column->child;
2301 }
2302
2303 /**
2304  * pspp_sheet_view_column_set_alignment:
2305  * @tree_column: A #PsppSheetViewColumn.
2306  * @xalign: The alignment, which is between [0.0 and 1.0] inclusive.
2307  * 
2308  * Sets the alignment of the title or custom widget inside the column header.
2309  * The alignment determines its location inside the button -- 0.0 for left, 0.5
2310  * for center, 1.0 for right.
2311  **/
2312 void
2313 pspp_sheet_view_column_set_alignment (PsppSheetViewColumn *tree_column,
2314                                     gfloat             xalign)
2315 {
2316   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2317
2318   xalign = CLAMP (xalign, 0.0, 1.0);
2319
2320   if (tree_column->xalign == xalign)
2321     return;
2322
2323   tree_column->xalign = xalign;
2324   pspp_sheet_view_column_update_button (tree_column);
2325   g_object_notify (G_OBJECT (tree_column), "alignment");
2326 }
2327
2328 /**
2329  * pspp_sheet_view_column_get_alignment:
2330  * @tree_column: A #PsppSheetViewColumn.
2331  * 
2332  * Returns the current x alignment of @tree_column.  This value can range
2333  * between 0.0 and 1.0.
2334  * 
2335  * Return value: The current alignent of @tree_column.
2336  **/
2337 gfloat
2338 pspp_sheet_view_column_get_alignment (PsppSheetViewColumn *tree_column)
2339 {
2340   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0.5);
2341
2342   return tree_column->xalign;
2343 }
2344
2345 /**
2346  * pspp_sheet_view_column_set_reorderable:
2347  * @tree_column: A #PsppSheetViewColumn
2348  * @reorderable: %TRUE, if the column can be reordered.
2349  * 
2350  * If @reorderable is %TRUE, then the column can be reordered by the end user
2351  * dragging the header.
2352  **/
2353 void
2354 pspp_sheet_view_column_set_reorderable (PsppSheetViewColumn *tree_column,
2355                                       gboolean           reorderable)
2356 {
2357   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2358
2359   /*  if (reorderable)
2360       pspp_sheet_view_column_set_clickable (tree_column, TRUE);*/
2361
2362   if (tree_column->reorderable == (reorderable?TRUE:FALSE))
2363     return;
2364
2365   tree_column->reorderable = (reorderable?TRUE:FALSE);
2366   pspp_sheet_view_column_update_button (tree_column);
2367   g_object_notify (G_OBJECT (tree_column), "reorderable");
2368 }
2369
2370 /**
2371  * pspp_sheet_view_column_get_reorderable:
2372  * @tree_column: A #PsppSheetViewColumn
2373  * 
2374  * Returns %TRUE if the @tree_column can be reordered by the user.
2375  * 
2376  * Return value: %TRUE if the @tree_column can be reordered by the user.
2377  **/
2378 gboolean
2379 pspp_sheet_view_column_get_reorderable (PsppSheetViewColumn *tree_column)
2380 {
2381   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2382
2383   return tree_column->reorderable;
2384 }
2385
2386
2387 /**
2388  * pspp_sheet_view_column_set_sort_column_id:
2389  * @tree_column: a #PsppSheetViewColumn
2390  * @sort_column_id: The @sort_column_id of the model to sort on.
2391  *
2392  * Sets the logical @sort_column_id that this column sorts on when this column 
2393  * is selected for sorting.  Doing so makes the column header clickable.
2394  **/
2395 void
2396 pspp_sheet_view_column_set_sort_column_id (PsppSheetViewColumn *tree_column,
2397                                          gint               sort_column_id)
2398 {
2399   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2400   g_return_if_fail (sort_column_id >= -1);
2401
2402   if (tree_column->sort_column_id == sort_column_id)
2403     return;
2404
2405   tree_column->sort_column_id = sort_column_id;
2406
2407   /* Handle unsetting the id */
2408   if (sort_column_id == -1)
2409     {
2410       GtkTreeModel *model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (tree_column->tree_view));
2411
2412       if (tree_column->sort_clicked_signal)
2413         {
2414           g_signal_handler_disconnect (tree_column, tree_column->sort_clicked_signal);
2415           tree_column->sort_clicked_signal = 0;
2416         }
2417
2418       if (tree_column->sort_column_changed_signal)
2419         {
2420           g_signal_handler_disconnect (model, tree_column->sort_column_changed_signal);
2421           tree_column->sort_column_changed_signal = 0;
2422         }
2423
2424       pspp_sheet_view_column_set_sort_order (tree_column, GTK_SORT_ASCENDING);
2425       pspp_sheet_view_column_set_sort_indicator (tree_column, FALSE);
2426       pspp_sheet_view_column_set_clickable (tree_column, FALSE);
2427       g_object_notify (G_OBJECT (tree_column), "sort-column-id");
2428       return;
2429     }
2430
2431   pspp_sheet_view_column_set_clickable (tree_column, TRUE);
2432
2433   if (! tree_column->sort_clicked_signal)
2434     tree_column->sort_clicked_signal = g_signal_connect (tree_column,
2435                                                          "clicked",
2436                                                          G_CALLBACK (pspp_sheet_view_column_sort),
2437                                                          NULL);
2438
2439   pspp_sheet_view_column_setup_sort_column_id_callback (tree_column);
2440   g_object_notify (G_OBJECT (tree_column), "sort-column-id");
2441 }
2442
2443 /**
2444  * pspp_sheet_view_column_get_sort_column_id:
2445  * @tree_column: a #PsppSheetViewColumn
2446  *
2447  * Gets the logical @sort_column_id that the model sorts on when this
2448  * column is selected for sorting.
2449  * See pspp_sheet_view_column_set_sort_column_id().
2450  *
2451  * Return value: the current @sort_column_id for this column, or -1 if
2452  *               this column can't be used for sorting.
2453  **/
2454 gint
2455 pspp_sheet_view_column_get_sort_column_id (PsppSheetViewColumn *tree_column)
2456 {
2457   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
2458
2459   return tree_column->sort_column_id;
2460 }
2461
2462 /**
2463  * pspp_sheet_view_column_set_sort_indicator:
2464  * @tree_column: a #PsppSheetViewColumn
2465  * @setting: %TRUE to display an indicator that the column is sorted
2466  *
2467  * Call this function with a @setting of %TRUE to display an arrow in
2468  * the header button indicating the column is sorted. Call
2469  * pspp_sheet_view_column_set_sort_order() to change the direction of
2470  * the arrow.
2471  * 
2472  **/
2473 void
2474 pspp_sheet_view_column_set_sort_indicator (PsppSheetViewColumn     *tree_column,
2475                                          gboolean               setting)
2476 {
2477   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2478
2479   setting = setting != FALSE;
2480
2481   if (setting == tree_column->show_sort_indicator)
2482     return;
2483
2484   tree_column->show_sort_indicator = setting;
2485   pspp_sheet_view_column_update_button (tree_column);
2486   g_object_notify (G_OBJECT (tree_column), "sort-indicator");
2487 }
2488
2489 /**
2490  * pspp_sheet_view_column_get_sort_indicator:
2491  * @tree_column: a #PsppSheetViewColumn
2492  * 
2493  * Gets the value set by pspp_sheet_view_column_set_sort_indicator().
2494  * 
2495  * Return value: whether the sort indicator arrow is displayed
2496  **/
2497 gboolean
2498 pspp_sheet_view_column_get_sort_indicator  (PsppSheetViewColumn     *tree_column)
2499 {
2500   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2501
2502   return tree_column->show_sort_indicator;
2503 }
2504
2505 /**
2506  * pspp_sheet_view_column_set_sort_order:
2507  * @tree_column: a #PsppSheetViewColumn
2508  * @order: sort order that the sort indicator should indicate
2509  *
2510  * Changes the appearance of the sort indicator. 
2511  * 
2512  * This <emphasis>does not</emphasis> actually sort the model.  Use
2513  * pspp_sheet_view_column_set_sort_column_id() if you want automatic sorting
2514  * support.  This function is primarily for custom sorting behavior, and should
2515  * be used in conjunction with gtk_tree_sortable_set_sort_column() to do
2516  * that. For custom models, the mechanism will vary. 
2517  * 
2518  * The sort indicator changes direction to indicate normal sort or reverse sort.
2519  * Note that you must have the sort indicator enabled to see anything when 
2520  * calling this function; see pspp_sheet_view_column_set_sort_indicator().
2521  **/
2522 void
2523 pspp_sheet_view_column_set_sort_order      (PsppSheetViewColumn     *tree_column,
2524                                           GtkSortType            order)
2525 {
2526   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2527
2528   if (order == tree_column->sort_order)
2529     return;
2530
2531   tree_column->sort_order = order;
2532   pspp_sheet_view_column_update_button (tree_column);
2533   g_object_notify (G_OBJECT (tree_column), "sort-order");
2534 }
2535
2536 /**
2537  * pspp_sheet_view_column_get_sort_order:
2538  * @tree_column: a #PsppSheetViewColumn
2539  * 
2540  * Gets the value set by pspp_sheet_view_column_set_sort_order().
2541  * 
2542  * Return value: the sort order the sort indicator is indicating
2543  **/
2544 GtkSortType
2545 pspp_sheet_view_column_get_sort_order      (PsppSheetViewColumn     *tree_column)
2546 {
2547   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
2548
2549   return tree_column->sort_order;
2550 }
2551
2552 /**
2553  * pspp_sheet_view_column_cell_set_cell_data:
2554  * @tree_column: A #PsppSheetViewColumn.
2555  * @tree_model: The #GtkTreeModel to to get the cell renderers attributes from.
2556  * @iter: The #GtkTreeIter to to get the cell renderer's attributes from.
2557  * @is_expander: %TRUE, if the row has children
2558  * @is_expanded: %TRUE, if the row has visible children
2559  * 
2560  * Sets the cell renderer based on the @tree_model and @iter.  That is, for
2561  * every attribute mapping in @tree_column, it will get a value from the set
2562  * column on the @iter, and use that value to set the attribute on the cell
2563  * renderer.  This is used primarily by the #PsppSheetView.
2564  **/
2565 void
2566 pspp_sheet_view_column_cell_set_cell_data (PsppSheetViewColumn *tree_column,
2567                                          GtkTreeModel      *tree_model,
2568                                          GtkTreeIter       *iter,
2569                                          gboolean           is_expander,
2570                                          gboolean           is_expanded)
2571 {
2572   GSList *list;
2573   GValue value = { 0, };
2574   GList *cell_list;
2575
2576   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2577
2578   if (tree_model == NULL)
2579     return;
2580
2581   for (cell_list = tree_column->cell_list; cell_list; cell_list = cell_list->next)
2582     {
2583       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) cell_list->data;
2584       GObject *cell = (GObject *) info->cell;
2585
2586       list = info->attributes;
2587
2588       g_object_freeze_notify (cell);
2589
2590       if (info->cell->is_expander != is_expander)
2591         g_object_set (cell, "is-expander", is_expander, NULL);
2592
2593       if (info->cell->is_expanded != is_expanded)
2594         g_object_set (cell, "is-expanded", is_expanded, NULL);
2595
2596       while (list && list->next)
2597         {
2598           gtk_tree_model_get_value (tree_model, iter,
2599                                     GPOINTER_TO_INT (list->next->data),
2600                                     &value);
2601           g_object_set_property (cell, (gchar *) list->data, &value);
2602           g_value_unset (&value);
2603           list = list->next->next;
2604         }
2605
2606       if (info->func)
2607         (* info->func) (tree_column, info->cell, tree_model, iter, info->func_data);
2608       g_object_thaw_notify (G_OBJECT (info->cell));
2609     }
2610
2611 }
2612
2613 /**
2614  * pspp_sheet_view_column_cell_get_size:
2615  * @tree_column: A #PsppSheetViewColumn.
2616  * @cell_area: (allow-none): The area a cell in the column will be allocated, or %NULL
2617  * @x_offset: (allow-none): location to return x offset of a cell relative to @cell_area, or %NULL
2618  * @y_offset: (allow-none): location to return y offset of a cell relative to @cell_area, or %NULL
2619  * @width: (allow-none): location to return width needed to render a cell, or %NULL
2620  * @height: (allow-none): location to return height needed to render a cell, or %NULL
2621  * 
2622  * Obtains the width and height needed to render the column.  This is used
2623  * primarily by the #PsppSheetView.
2624  **/
2625 void
2626 pspp_sheet_view_column_cell_get_size (PsppSheetViewColumn  *tree_column,
2627                                     const GdkRectangle *cell_area,
2628                                     gint               *x_offset,
2629                                     gint               *y_offset,
2630                                     gint               *width,
2631                                     gint               *height)
2632 {
2633   GList *list;
2634   gboolean first_cell = TRUE;
2635   gint focus_line_width;
2636
2637   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2638
2639   if (height)
2640     * height = 0;
2641   if (width)
2642     * width = 0;
2643
2644   gtk_widget_style_get (tree_column->tree_view, "focus-line-width", &focus_line_width, NULL);
2645   
2646   for (list = tree_column->cell_list; list; list = list->next)
2647     {
2648       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
2649       gboolean visible;
2650       gint new_height = 0;
2651       gint new_width = 0;
2652       g_object_get (info->cell, "visible", &visible, NULL);
2653
2654       if (visible == FALSE)
2655         continue;
2656
2657       if (first_cell == FALSE && width)
2658         *width += tree_column->spacing;
2659
2660       gtk_cell_renderer_get_size (info->cell,
2661                                   tree_column->tree_view,
2662                                   cell_area,
2663                                   x_offset,
2664                                   y_offset,
2665                                   &new_width,
2666                                   &new_height);
2667
2668       if (height)
2669         * height = MAX (*height, new_height + focus_line_width * 2);
2670       info->requested_width = MAX (info->requested_width, new_width + focus_line_width * 2);
2671       if (width)
2672         * width += info->requested_width;
2673       first_cell = FALSE;
2674     }
2675 }
2676
2677 /* rendering, event handling and rendering focus are somewhat complicated, and
2678  * quite a bit of code.  Rather than duplicate them, we put them together to
2679  * keep the code in one place.
2680  *
2681  * To better understand what's going on, check out
2682  * docs/tree-column-sizing.png
2683  */
2684 enum {
2685   CELL_ACTION_RENDER,
2686   CELL_ACTION_FOCUS,
2687   CELL_ACTION_EVENT
2688 };
2689
2690 static gboolean
2691 pspp_sheet_view_column_cell_process_action (PsppSheetViewColumn  *tree_column,
2692                                           GdkWindow          *window,
2693                                           const GdkRectangle *background_area,
2694                                           const GdkRectangle *cell_area,
2695                                           guint               flags,
2696                                           gint                action,
2697                                           const GdkRectangle *expose_area,     /* RENDER */
2698                                           GdkRectangle       *focus_rectangle, /* FOCUS  */
2699                                           GtkCellEditable   **editable_widget, /* EVENT  */
2700                                           GdkEvent           *event,           /* EVENT  */
2701                                           gchar              *path_string)     /* EVENT  */
2702 {
2703   GList *list;
2704   GdkRectangle real_cell_area;
2705   GdkRectangle real_background_area;
2706   GdkRectangle real_expose_area = *cell_area;
2707   gint depth = 0;
2708   gint expand_cell_count = 0;
2709   gint full_requested_width = 0;
2710   gint extra_space;
2711   gint min_x, min_y, max_x, max_y;
2712   gint focus_line_width;
2713   gint special_cells;
2714   gint horizontal_separator;
2715   gboolean cursor_row = FALSE;
2716   gboolean first_cell = TRUE;
2717   gboolean rtl;
2718   /* If we have rtl text, we need to transform our areas */
2719   GdkRectangle rtl_cell_area;
2720   GdkRectangle rtl_background_area;
2721
2722   min_x = G_MAXINT;
2723   min_y = G_MAXINT;
2724   max_x = 0;
2725   max_y = 0;
2726
2727   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_column->tree_view)) == GTK_TEXT_DIR_RTL);
2728   special_cells = _pspp_sheet_view_column_count_special_cells (tree_column);
2729
2730   if (special_cells > 1 && action == CELL_ACTION_FOCUS)
2731     {
2732       PsppSheetViewColumnCellInfo *info = NULL;
2733       gboolean found_has_focus = FALSE;
2734
2735       /* one should have focus */
2736       for (list = tree_column->cell_list; list; list = list->next)
2737         {
2738           info = list->data;
2739           if (info && info->has_focus)
2740             {
2741               found_has_focus = TRUE;
2742               break;
2743             }
2744         }
2745
2746       if (!found_has_focus)
2747         {
2748           /* give the first one focus */
2749           info = pspp_sheet_view_column_cell_first (tree_column)->data;
2750           info->has_focus = TRUE;
2751         }
2752     }
2753
2754   cursor_row = flags & GTK_CELL_RENDERER_FOCUSED;
2755
2756   gtk_widget_style_get (GTK_WIDGET (tree_column->tree_view),
2757                         "focus-line-width", &focus_line_width,
2758                         "horizontal-separator", &horizontal_separator,
2759                         NULL);
2760
2761   real_cell_area = *cell_area;
2762   real_background_area = *background_area;
2763
2764
2765   real_cell_area.x += focus_line_width;
2766   real_cell_area.y += focus_line_width;
2767   real_cell_area.height -= 2 * focus_line_width;
2768
2769   if (rtl)
2770     depth = real_background_area.width - real_cell_area.width;
2771   else
2772     depth = real_cell_area.x - real_background_area.x;
2773
2774   /* Find out how much extra space we have to allocate */
2775   for (list = tree_column->cell_list; list; list = list->next)
2776     {
2777       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *)list->data;
2778
2779       if (! info->cell->visible)
2780         continue;
2781
2782       if (info->expand == TRUE)
2783         expand_cell_count ++;
2784       full_requested_width += info->requested_width;
2785
2786       if (!first_cell)
2787         full_requested_width += tree_column->spacing;
2788
2789       first_cell = FALSE;
2790     }
2791
2792   extra_space = cell_area->width - full_requested_width;
2793   if (extra_space < 0)
2794     extra_space = 0;
2795   else if (extra_space > 0 && expand_cell_count > 0)
2796     extra_space /= expand_cell_count;
2797
2798   /* iterate list for GTK_PACK_START cells */
2799   for (list = tree_column->cell_list; list; list = list->next)
2800     {
2801       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
2802
2803       if (info->pack == GTK_PACK_END)
2804         continue;
2805
2806       if (! info->cell->visible)
2807         continue;
2808
2809       if ((info->has_focus || special_cells == 1) && cursor_row)
2810         flags |= GTK_CELL_RENDERER_FOCUSED;
2811       else
2812         flags &= ~GTK_CELL_RENDERER_FOCUSED;
2813
2814       info->real_width = info->requested_width + (info->expand?extra_space:0);
2815
2816       /* We constrain ourselves to only the width available */
2817       if (real_cell_area.x - focus_line_width + info->real_width > cell_area->x + cell_area->width)
2818         {
2819           info->real_width = cell_area->x + cell_area->width - real_cell_area.x;
2820         }   
2821
2822       if (real_cell_area.x > cell_area->x + cell_area->width)
2823         break;
2824
2825       real_cell_area.width = info->real_width;
2826       real_cell_area.width -= 2 * focus_line_width;
2827
2828       if (list->next)
2829         {
2830           real_background_area.width = info->real_width + depth;
2831         }
2832       else
2833         {
2834           /* fill the rest of background for the last cell */
2835           real_background_area.width = background_area->x + background_area->width - real_background_area.x;
2836         }
2837
2838       rtl_cell_area = real_cell_area;
2839       rtl_background_area = real_background_area;
2840       
2841       if (rtl)
2842         {
2843           rtl_cell_area.x = cell_area->x + cell_area->width - (real_cell_area.x - cell_area->x) - real_cell_area.width;
2844           rtl_background_area.x = background_area->x + background_area->width - (real_background_area.x - background_area->x) - real_background_area.width;
2845         }
2846
2847       /* RENDER */
2848       if (action == CELL_ACTION_RENDER)
2849         {
2850           gtk_cell_renderer_render (info->cell,
2851                                     window,
2852                                     tree_column->tree_view,
2853                                     &rtl_background_area,
2854                                     &rtl_cell_area,
2855                                     &real_expose_area, 
2856                                     flags);
2857         }
2858       /* FOCUS */
2859       else if (action == CELL_ACTION_FOCUS)
2860         {
2861           gint x_offset, y_offset, width, height;
2862
2863           gtk_cell_renderer_get_size (info->cell,
2864                                       tree_column->tree_view,
2865                                       &rtl_cell_area,
2866                                       &x_offset, &y_offset,
2867                                       &width, &height);
2868
2869           if (special_cells > 1)
2870             {
2871               if (info->has_focus)
2872                 {
2873                   min_x = rtl_cell_area.x + x_offset;
2874                   max_x = min_x + width;
2875                   min_y = rtl_cell_area.y + y_offset;
2876                   max_y = min_y + height;
2877                 }
2878             }
2879           else
2880             {
2881               if (min_x > (rtl_cell_area.x + x_offset))
2882                 min_x = rtl_cell_area.x + x_offset;
2883               if (max_x < rtl_cell_area.x + x_offset + width)
2884                 max_x = rtl_cell_area.x + x_offset + width;
2885               if (min_y > (rtl_cell_area.y + y_offset))
2886                 min_y = rtl_cell_area.y + y_offset;
2887               if (max_y < rtl_cell_area.y + y_offset + height)
2888                 max_y = rtl_cell_area.y + y_offset + height;
2889             }
2890         }
2891       /* EVENT */
2892       else if (action == CELL_ACTION_EVENT)
2893         {
2894           gboolean try_event = FALSE;
2895
2896           if (event)
2897             {
2898               if (special_cells == 1)
2899                 {
2900                   /* only 1 activatable cell -> whole column can activate */
2901                   if (cell_area->x <= ((GdkEventButton *)event)->x &&
2902                       cell_area->x + cell_area->width > ((GdkEventButton *)event)->x)
2903                     try_event = TRUE;
2904                 }
2905               else if (rtl_cell_area.x <= ((GdkEventButton *)event)->x &&
2906                   rtl_cell_area.x + rtl_cell_area.width > ((GdkEventButton *)event)->x)
2907                   /* only activate cell if the user clicked on an individual
2908                    * cell
2909                    */
2910                 try_event = TRUE;
2911             }
2912           else if (special_cells > 1 && info->has_focus)
2913             try_event = TRUE;
2914           else if (special_cells == 1)
2915             try_event = TRUE;
2916
2917           if (try_event)
2918             {
2919               gboolean visible, mode;
2920
2921               g_object_get (info->cell,
2922                             "visible", &visible,
2923                             "mode", &mode,
2924                             NULL);
2925               if (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
2926                 {
2927                   if (gtk_cell_renderer_activate (info->cell,
2928                                                   event,
2929                                                   tree_column->tree_view,
2930                                                   path_string,
2931                                                   &rtl_background_area,
2932                                                   &rtl_cell_area,
2933                                                   flags))
2934                     {
2935                       flags &= ~GTK_CELL_RENDERER_FOCUSED;
2936                       return TRUE;
2937                     }
2938                 }
2939               else if (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE)
2940                 {
2941                   *editable_widget =
2942                     gtk_cell_renderer_start_editing (info->cell,
2943                                                      event,
2944                                                      tree_column->tree_view,
2945                                                      path_string,
2946                                                      &rtl_background_area,
2947                                                      &rtl_cell_area,
2948                                                      flags);
2949
2950                   if (*editable_widget != NULL)
2951                     {
2952                       g_return_val_if_fail (GTK_IS_CELL_EDITABLE (*editable_widget), FALSE);
2953                       info->in_editing_mode = TRUE;
2954                       pspp_sheet_view_column_focus_cell (tree_column, info->cell);
2955                       
2956                       flags &= ~GTK_CELL_RENDERER_FOCUSED;
2957
2958                       return TRUE;
2959                     }
2960                 }
2961             }
2962         }
2963
2964       flags &= ~GTK_CELL_RENDERER_FOCUSED;
2965
2966       real_cell_area.x += (real_cell_area.width + 2 * focus_line_width + tree_column->spacing);
2967       real_background_area.x += real_background_area.width + tree_column->spacing;
2968
2969       /* Only needed for first cell */
2970       depth = 0;
2971     }
2972
2973   /* iterate list for PACK_END cells */
2974   for (list = g_list_last (tree_column->cell_list); list; list = list->prev)
2975     {
2976       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
2977
2978       if (info->pack == GTK_PACK_START)
2979         continue;
2980
2981       if (! info->cell->visible)
2982         continue;
2983
2984       if ((info->has_focus || special_cells == 1) && cursor_row)
2985         flags |= GTK_CELL_RENDERER_FOCUSED;
2986       else
2987         flags &= ~GTK_CELL_RENDERER_FOCUSED;
2988
2989       info->real_width = info->requested_width + (info->expand?extra_space:0);
2990
2991       /* We constrain ourselves to only the width available */
2992       if (real_cell_area.x - focus_line_width + info->real_width > cell_area->x + cell_area->width)
2993         {
2994           info->real_width = cell_area->x + cell_area->width - real_cell_area.x;
2995         }   
2996
2997       if (real_cell_area.x > cell_area->x + cell_area->width)
2998         break;
2999
3000       real_cell_area.width = info->real_width;
3001       real_cell_area.width -= 2 * focus_line_width;
3002       real_background_area.width = info->real_width + depth;
3003
3004       rtl_cell_area = real_cell_area;
3005       rtl_background_area = real_background_area;
3006       if (rtl)
3007         {
3008           rtl_cell_area.x = cell_area->x + cell_area->width - (real_cell_area.x - cell_area->x) - real_cell_area.width;
3009           rtl_background_area.x = background_area->x + background_area->width - (real_background_area.x - background_area->x) - real_background_area.width;
3010         }
3011
3012       /* RENDER */
3013       if (action == CELL_ACTION_RENDER)
3014         {
3015           gtk_cell_renderer_render (info->cell,
3016                                     window,
3017                                     tree_column->tree_view,
3018                                     &rtl_background_area,
3019                                     &rtl_cell_area,
3020                                     &real_expose_area,
3021                                     flags);
3022         }
3023       /* FOCUS */
3024       else if (action == CELL_ACTION_FOCUS)
3025         {
3026           gint x_offset, y_offset, width, height;
3027
3028           gtk_cell_renderer_get_size (info->cell,
3029                                       tree_column->tree_view,
3030                                       &rtl_cell_area,
3031                                       &x_offset, &y_offset,
3032                                       &width, &height);
3033
3034           if (special_cells > 1)
3035             {
3036               if (info->has_focus)
3037                 {
3038                   min_x = rtl_cell_area.x + x_offset;
3039                   max_x = min_x + width;
3040                   min_y = rtl_cell_area.y + y_offset;
3041                   max_y = min_y + height;
3042                 }
3043             }
3044           else
3045             {
3046               if (min_x > (rtl_cell_area.x + x_offset))
3047                 min_x = rtl_cell_area.x + x_offset;
3048               if (max_x < rtl_cell_area.x + x_offset + width)
3049                 max_x = rtl_cell_area.x + x_offset + width;
3050               if (min_y > (rtl_cell_area.y + y_offset))
3051                 min_y = rtl_cell_area.y + y_offset;
3052               if (max_y < rtl_cell_area.y + y_offset + height)
3053                 max_y = rtl_cell_area.y + y_offset + height;
3054             }
3055         }
3056       /* EVENT */
3057       else if (action == CELL_ACTION_EVENT)
3058         {
3059           gboolean try_event = FALSE;
3060
3061           if (event)
3062             {
3063               if (special_cells == 1)
3064                 {
3065                   /* only 1 activatable cell -> whole column can activate */
3066                   if (cell_area->x <= ((GdkEventButton *)event)->x &&
3067                       cell_area->x + cell_area->width > ((GdkEventButton *)event)->x)
3068                     try_event = TRUE;
3069                 }
3070               else if (rtl_cell_area.x <= ((GdkEventButton *)event)->x &&
3071                   rtl_cell_area.x + rtl_cell_area.width > ((GdkEventButton *)event)->x)
3072                 /* only activate cell if the user clicked on an individual
3073                  * cell
3074                  */
3075                 try_event = TRUE;
3076             }
3077           else if (special_cells > 1 && info->has_focus)
3078             try_event = TRUE;
3079           else if (special_cells == 1)
3080             try_event = TRUE;
3081
3082           if (try_event)
3083             {
3084               gboolean visible, mode;
3085
3086               g_object_get (info->cell,
3087                             "visible", &visible,
3088                             "mode", &mode,
3089                             NULL);
3090               if (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
3091                 {
3092                   if (gtk_cell_renderer_activate (info->cell,
3093                                                   event,
3094                                                   tree_column->tree_view,
3095                                                   path_string,
3096                                                   &rtl_background_area,
3097                                                   &rtl_cell_area,
3098                                                   flags))
3099                     {
3100                       flags &= ~GTK_CELL_RENDERER_FOCUSED;
3101                       return TRUE;
3102                     }
3103                 }
3104               else if (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE)
3105                 {
3106                   *editable_widget =
3107                     gtk_cell_renderer_start_editing (info->cell,
3108                                                      event,
3109                                                      tree_column->tree_view,
3110                                                      path_string,
3111                                                      &rtl_background_area,
3112                                                      &rtl_cell_area,
3113                                                      flags);
3114
3115                   if (*editable_widget != NULL)
3116                     {
3117                       g_return_val_if_fail (GTK_IS_CELL_EDITABLE (*editable_widget), FALSE);
3118                       info->in_editing_mode = TRUE;
3119                       pspp_sheet_view_column_focus_cell (tree_column, info->cell);
3120
3121                       flags &= ~GTK_CELL_RENDERER_FOCUSED;
3122                       return TRUE;
3123                     }
3124                 }
3125             }
3126         }
3127
3128       flags &= ~GTK_CELL_RENDERER_FOCUSED;
3129
3130       real_cell_area.x += (real_cell_area.width + 2 * focus_line_width + tree_column->spacing);
3131       real_background_area.x += (real_background_area.width + tree_column->spacing);
3132
3133       /* Only needed for first cell */
3134       depth = 0;
3135     }
3136
3137   /* fill focus_rectangle when required */
3138   if (action == CELL_ACTION_FOCUS)
3139     {
3140       if (min_x >= max_x || min_y >= max_y)
3141         {
3142           *focus_rectangle = *cell_area;
3143           /* don't change the focus_rectangle, just draw it nicely inside
3144            * the cell area */
3145         }
3146       else
3147         {
3148           focus_rectangle->x = min_x - focus_line_width;
3149           focus_rectangle->y = min_y - focus_line_width;
3150           focus_rectangle->width = (max_x - min_x) + 2 * focus_line_width;
3151           focus_rectangle->height = (max_y - min_y) + 2 * focus_line_width;
3152         }
3153     }
3154
3155   return FALSE;
3156 }
3157
3158 /**
3159  * pspp_sheet_view_column_cell_render:
3160  * @tree_column: A #PsppSheetViewColumn.
3161  * @window: a #GdkDrawable to draw to
3162  * @background_area: entire cell area (including tree expanders and maybe padding on the sides)
3163  * @cell_area: area normally rendered by a cell renderer
3164  * @expose_area: area that actually needs updating
3165  * @flags: flags that affect rendering
3166  * 
3167  * Renders the cell contained by #tree_column. This is used primarily by the
3168  * #PsppSheetView.
3169  **/
3170 void
3171 _pspp_sheet_view_column_cell_render (PsppSheetViewColumn  *tree_column,
3172                                    GdkWindow          *window,
3173                                    const GdkRectangle *background_area,
3174                                    const GdkRectangle *cell_area,
3175                                    const GdkRectangle *expose_area,
3176                                    guint               flags)
3177 {
3178   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
3179   g_return_if_fail (background_area != NULL);
3180   g_return_if_fail (cell_area != NULL);
3181   g_return_if_fail (expose_area != NULL);
3182
3183   pspp_sheet_view_column_cell_process_action (tree_column,
3184                                             window,
3185                                             background_area,
3186                                             cell_area,
3187                                             flags,
3188                                             CELL_ACTION_RENDER,
3189                                             expose_area,
3190                                             NULL, NULL, NULL, NULL);
3191 }
3192
3193 gboolean
3194 _pspp_sheet_view_column_cell_event (PsppSheetViewColumn  *tree_column,
3195                                   GtkCellEditable   **editable_widget,
3196                                   GdkEvent           *event,
3197                                   gchar              *path_string,
3198                                   const GdkRectangle *background_area,
3199                                   const GdkRectangle *cell_area,
3200                                   guint               flags)
3201 {
3202   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
3203
3204   return pspp_sheet_view_column_cell_process_action (tree_column,
3205                                                    NULL,
3206                                                    background_area,
3207                                                    cell_area,
3208                                                    flags,
3209                                                    CELL_ACTION_EVENT,
3210                                                    NULL, NULL,
3211                                                    editable_widget,
3212                                                    event,
3213                                                    path_string);
3214 }
3215
3216 void
3217 _pspp_sheet_view_column_get_focus_area (PsppSheetViewColumn  *tree_column,
3218                                       const GdkRectangle *background_area,
3219                                       const GdkRectangle *cell_area,
3220                                       GdkRectangle       *focus_area)
3221 {
3222   pspp_sheet_view_column_cell_process_action (tree_column,
3223                                             NULL,
3224                                             background_area,
3225                                             cell_area,
3226                                             0,
3227                                             CELL_ACTION_FOCUS,
3228                                             NULL,
3229                                             focus_area,
3230                                             NULL, NULL, NULL);
3231 }
3232
3233
3234 /* cell list manipulation */
3235 static GList *
3236 pspp_sheet_view_column_cell_first (PsppSheetViewColumn *tree_column)
3237 {
3238   GList *list = tree_column->cell_list;
3239
3240   /* first GTK_PACK_START cell we find */
3241   for ( ; list; list = list->next)
3242     {
3243       PsppSheetViewColumnCellInfo *info = list->data;
3244       if (info->pack == GTK_PACK_START)
3245         return list;
3246     }
3247
3248   /* hmm, else the *last* GTK_PACK_END cell */
3249   list = g_list_last (tree_column->cell_list);
3250
3251   for ( ; list; list = list->prev)
3252     {
3253       PsppSheetViewColumnCellInfo *info = list->data;
3254       if (info->pack == GTK_PACK_END)
3255         return list;
3256     }
3257
3258   return NULL;
3259 }
3260
3261 static GList *
3262 pspp_sheet_view_column_cell_last (PsppSheetViewColumn *tree_column)
3263 {
3264   GList *list = tree_column->cell_list;
3265
3266   /* *first* GTK_PACK_END cell we find */
3267   for ( ; list ; list = list->next)
3268     {
3269       PsppSheetViewColumnCellInfo *info = list->data;
3270       if (info->pack == GTK_PACK_END)
3271         return list;
3272     }
3273
3274   /* hmm, else the last GTK_PACK_START cell */
3275   list = g_list_last (tree_column->cell_list);
3276
3277   for ( ; list; list = list->prev)
3278     {
3279       PsppSheetViewColumnCellInfo *info = list->data;
3280       if (info->pack == GTK_PACK_START)
3281         return list;
3282     }
3283
3284   return NULL;
3285 }
3286
3287 static GList *
3288 pspp_sheet_view_column_cell_next (PsppSheetViewColumn *tree_column,
3289                                 GList             *current)
3290 {
3291   GList *list;
3292   PsppSheetViewColumnCellInfo *info = current->data;
3293
3294   if (info->pack == GTK_PACK_START)
3295     {
3296       for (list = current->next; list; list = list->next)
3297         {
3298           PsppSheetViewColumnCellInfo *inf = list->data;
3299           if (inf->pack == GTK_PACK_START)
3300             return list;
3301         }
3302
3303       /* out of GTK_PACK_START cells, get *last* GTK_PACK_END one */
3304       list = g_list_last (tree_column->cell_list);
3305       for (; list; list = list->prev)
3306         {
3307           PsppSheetViewColumnCellInfo *inf = list->data;
3308           if (inf->pack == GTK_PACK_END)
3309             return list;
3310         }
3311     }
3312
3313   for (list = current->prev; list; list = list->prev)
3314     {
3315       PsppSheetViewColumnCellInfo *inf = list->data;
3316       if (inf->pack == GTK_PACK_END)
3317         return list;
3318     }
3319
3320   return NULL;
3321 }
3322
3323 static GList *
3324 pspp_sheet_view_column_cell_prev (PsppSheetViewColumn *tree_column,
3325                                 GList             *current)
3326 {
3327   GList *list;
3328   PsppSheetViewColumnCellInfo *info = current->data;
3329
3330   if (info->pack == GTK_PACK_END)
3331     {
3332       for (list = current->next; list; list = list->next)
3333         {
3334           PsppSheetViewColumnCellInfo *inf = list->data;
3335           if (inf->pack == GTK_PACK_END)
3336             return list;
3337         }
3338
3339       /* out of GTK_PACK_END, get last GTK_PACK_START one */
3340       list = g_list_last (tree_column->cell_list);
3341       for ( ; list; list = list->prev)
3342         {
3343           PsppSheetViewColumnCellInfo *inf = list->data;
3344           if (inf->pack == GTK_PACK_START)
3345             return list;
3346         }
3347     }
3348
3349   for (list = current->prev; list; list = list->prev)
3350     {
3351       PsppSheetViewColumnCellInfo *inf = list->data;
3352       if (inf->pack == GTK_PACK_START)
3353         return list;
3354     }
3355
3356   return NULL;
3357 }
3358
3359 gboolean
3360 _pspp_sheet_view_column_cell_focus (PsppSheetViewColumn *tree_column,
3361                                   gint               direction,
3362                                   gboolean           left,
3363                                   gboolean           right)
3364 {
3365   gint count;
3366   gboolean rtl;
3367
3368   count = _pspp_sheet_view_column_count_special_cells (tree_column);
3369   rtl = gtk_widget_get_direction (GTK_WIDGET (tree_column->tree_view)) == GTK_TEXT_DIR_RTL;
3370
3371   /* if we are the current focus column and have multiple editable cells,
3372    * try to select the next one, else move the focus to the next column
3373    */
3374   if (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->focus_column == tree_column)
3375     {
3376       if (count > 1)
3377         {
3378           GList *next, *prev;
3379           GList *list = tree_column->cell_list;
3380           PsppSheetViewColumnCellInfo *info = NULL;
3381
3382           /* find current focussed cell */
3383           for ( ; list; list = list->next)
3384             {
3385               info = list->data;
3386               if (info->has_focus)
3387                 break;
3388             }
3389
3390           /* not a focussed cell in the focus column? */
3391           if (!list || !info || !info->has_focus)
3392             return FALSE;
3393
3394           if (rtl)
3395             {
3396               prev = pspp_sheet_view_column_cell_next (tree_column, list);
3397               next = pspp_sheet_view_column_cell_prev (tree_column, list);
3398             }
3399           else
3400             {
3401               next = pspp_sheet_view_column_cell_next (tree_column, list);
3402               prev = pspp_sheet_view_column_cell_prev (tree_column, list);
3403             }
3404
3405           info->has_focus = FALSE;
3406           if (direction > 0 && next)
3407             {
3408               info = next->data;
3409               info->has_focus = TRUE;
3410               return TRUE;
3411             }
3412           else if (direction > 0 && !next && !right)
3413             {
3414               /* keep focus on last cell */
3415               if (rtl)
3416                 info = pspp_sheet_view_column_cell_first (tree_column)->data;
3417               else
3418                 info = pspp_sheet_view_column_cell_last (tree_column)->data;
3419
3420               info->has_focus = TRUE;
3421               return TRUE;
3422             }
3423           else if (direction < 0 && prev)
3424             {
3425               info = prev->data;
3426               info->has_focus = TRUE;
3427               return TRUE;
3428             }
3429           else if (direction < 0 && !prev && !left)
3430             {
3431               /* keep focus on first cell */
3432               if (rtl)
3433                 info = pspp_sheet_view_column_cell_last (tree_column)->data;
3434               else
3435                 info = pspp_sheet_view_column_cell_first (tree_column)->data;
3436
3437               info->has_focus = TRUE;
3438               return TRUE;
3439             }
3440         }
3441       return FALSE;
3442     }
3443
3444   /* we get focus, if we have multiple editable cells, give the correct one
3445    * focus
3446    */
3447   if (count > 1)
3448     {
3449       GList *list = tree_column->cell_list;
3450
3451       /* clear focus first */
3452       for ( ; list ; list = list->next)
3453         {
3454           PsppSheetViewColumnCellInfo *info = list->data;
3455           if (info->has_focus)
3456             info->has_focus = FALSE;
3457         }
3458
3459       list = NULL;
3460       if (rtl)
3461         {
3462           if (direction > 0)
3463             list = pspp_sheet_view_column_cell_last (tree_column);
3464           else if (direction < 0)
3465             list = pspp_sheet_view_column_cell_first (tree_column);
3466         }
3467       else
3468         {
3469           if (direction > 0)
3470             list = pspp_sheet_view_column_cell_first (tree_column);
3471           else if (direction < 0)
3472             list = pspp_sheet_view_column_cell_last (tree_column);
3473         }
3474
3475       if (list)
3476         ((PsppSheetViewColumnCellInfo *) list->data)->has_focus = TRUE;
3477     }
3478
3479   return TRUE;
3480 }
3481
3482 void
3483 _pspp_sheet_view_column_cell_draw_focus (PsppSheetViewColumn  *tree_column,
3484                                        GdkWindow          *window,
3485                                        const GdkRectangle *background_area,
3486                                        const GdkRectangle *cell_area,
3487                                        const GdkRectangle *expose_area,
3488                                        guint               flags)
3489 {
3490   gint focus_line_width;
3491   GtkStateType cell_state;
3492   
3493   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
3494   gtk_widget_style_get (GTK_WIDGET (tree_column->tree_view),
3495                         "focus-line-width", &focus_line_width, NULL);
3496   if (tree_column->editable_widget)
3497     {
3498       /* This function is only called on the editable row when editing.
3499        */
3500 #if 0
3501       gtk_paint_focus (tree_column->tree_view->style,
3502                        window,
3503                        gtk_widget_get_state (tree_column->tree_view),
3504                        NULL,
3505                        tree_column->tree_view,
3506                        "treeview",
3507                        cell_area->x - focus_line_width,
3508                        cell_area->y - focus_line_width,
3509                        cell_area->width + 2 * focus_line_width,
3510                        cell_area->height + 2 * focus_line_width);
3511 #endif      
3512     }
3513   else
3514     {
3515       GdkRectangle focus_rectangle;
3516       pspp_sheet_view_column_cell_process_action (tree_column,
3517                                                 window,
3518                                                 background_area,
3519                                                 cell_area,
3520                                                 flags,
3521                                                 CELL_ACTION_FOCUS,
3522                                                 expose_area,
3523                                                 &focus_rectangle,
3524                                                 NULL, NULL, NULL);
3525
3526       cell_state = flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
3527               (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
3528               (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL));
3529       gtk_paint_focus (tree_column->tree_view->style,
3530                        window,
3531                        cell_state,
3532                        cell_area,
3533                        tree_column->tree_view,
3534                        "treeview",
3535                        focus_rectangle.x,
3536                        focus_rectangle.y,
3537                        focus_rectangle.width,
3538                        focus_rectangle.height);
3539     }
3540 }
3541
3542 /**
3543  * pspp_sheet_view_column_cell_is_visible:
3544  * @tree_column: A #PsppSheetViewColumn
3545  * 
3546  * Returns %TRUE if any of the cells packed into the @tree_column are visible.
3547  * For this to be meaningful, you must first initialize the cells with
3548  * pspp_sheet_view_column_cell_set_cell_data()
3549  * 
3550  * Return value: %TRUE, if any of the cells packed into the @tree_column are currently visible
3551  **/
3552 gboolean
3553 pspp_sheet_view_column_cell_is_visible (PsppSheetViewColumn *tree_column)
3554 {
3555   GList *list;
3556
3557   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
3558
3559   for (list = tree_column->cell_list; list; list = list->next)
3560     {
3561       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
3562
3563       if (info->cell->visible)
3564         return TRUE;
3565     }
3566
3567   return FALSE;
3568 }
3569
3570 /**
3571  * pspp_sheet_view_column_focus_cell:
3572  * @tree_column: A #PsppSheetViewColumn
3573  * @cell: A #GtkCellRenderer
3574  *
3575  * Sets the current keyboard focus to be at @cell, if the column contains
3576  * 2 or more editable and activatable cells.
3577  *
3578  * Since: 2.2
3579  **/
3580 void
3581 pspp_sheet_view_column_focus_cell (PsppSheetViewColumn *tree_column,
3582                                  GtkCellRenderer   *cell)
3583 {
3584   GList *list;
3585   gboolean found_cell = FALSE;
3586
3587   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
3588   g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
3589
3590   if (_pspp_sheet_view_column_count_special_cells (tree_column) < 2)
3591     return;
3592
3593   for (list = tree_column->cell_list; list; list = list->next)
3594     {
3595       PsppSheetViewColumnCellInfo *info = list->data;
3596
3597       if (info->cell == cell)
3598         {
3599           info->has_focus = TRUE;
3600           found_cell = TRUE;
3601           break;
3602         }
3603     }
3604
3605   if (found_cell)
3606     {
3607       for (list = tree_column->cell_list; list; list = list->next)
3608         {
3609           PsppSheetViewColumnCellInfo *info = list->data;
3610
3611           if (info->cell != cell)
3612             info->has_focus = FALSE;
3613         }
3614
3615       /* FIXME: redraw? */
3616     }
3617 }
3618
3619 void
3620 _pspp_sheet_view_column_cell_set_dirty (PsppSheetViewColumn *tree_column,
3621                                       gboolean           install_handler)
3622 {
3623   GList *list;
3624
3625   for (list = tree_column->cell_list; list; list = list->next)
3626     {
3627       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
3628
3629       info->requested_width = 0;
3630     }
3631   tree_column->dirty = TRUE;
3632   tree_column->requested_width = -1;
3633   tree_column->width = 0;
3634
3635   if (tree_column->tree_view &&
3636       gtk_widget_get_realized (tree_column->tree_view))
3637     {
3638       if (install_handler)
3639         _pspp_sheet_view_install_mark_rows_col_dirty (PSPP_SHEET_VIEW (tree_column->tree_view));
3640       else
3641         PSPP_SHEET_VIEW (tree_column->tree_view)->priv->mark_rows_col_dirty = TRUE;
3642       gtk_widget_queue_resize (tree_column->tree_view);
3643     }
3644 }
3645
3646 void
3647 _pspp_sheet_view_column_start_editing (PsppSheetViewColumn *tree_column,
3648                                      GtkCellEditable   *cell_editable)
3649 {
3650   g_return_if_fail (tree_column->editable_widget == NULL);
3651
3652   tree_column->editable_widget = cell_editable;
3653 }
3654
3655 void
3656 _pspp_sheet_view_column_stop_editing (PsppSheetViewColumn *tree_column)
3657 {
3658   GList *list;
3659
3660   g_return_if_fail (tree_column->editable_widget != NULL);
3661
3662   tree_column->editable_widget = NULL;
3663   for (list = tree_column->cell_list; list; list = list->next)
3664     ((PsppSheetViewColumnCellInfo *)list->data)->in_editing_mode = FALSE;
3665 }
3666
3667 void
3668 _pspp_sheet_view_column_get_neighbor_sizes (PsppSheetViewColumn *column,
3669                                           GtkCellRenderer   *cell,
3670                                           gint              *left,
3671                                           gint              *right)
3672 {
3673   GList *list;
3674   PsppSheetViewColumnCellInfo *info;
3675   gint l, r;
3676   gboolean rtl;
3677
3678   l = r = 0;
3679
3680   list = pspp_sheet_view_column_cell_first (column);  
3681
3682   while (list)
3683     {
3684       info = (PsppSheetViewColumnCellInfo *)list->data;
3685       
3686       list = pspp_sheet_view_column_cell_next (column, list);
3687
3688       if (info->cell == cell)
3689         break;
3690       
3691       if (info->cell->visible)
3692         l += info->real_width + column->spacing;
3693     }
3694
3695   while (list)
3696     {
3697       info = (PsppSheetViewColumnCellInfo *)list->data;
3698       
3699       list = pspp_sheet_view_column_cell_next (column, list);
3700
3701       if (info->cell->visible)
3702         r += info->real_width + column->spacing;
3703     }
3704
3705   rtl = (gtk_widget_get_direction (GTK_WIDGET (column->tree_view)) == GTK_TEXT_DIR_RTL);
3706   if (left)
3707     *left = rtl ? r : l;
3708
3709   if (right)
3710     *right = rtl ? l : r;
3711 }
3712
3713 /**
3714  * pspp_sheet_view_column_cell_get_position:
3715  * @tree_column: a #PsppSheetViewColumn
3716  * @cell_renderer: a #GtkCellRenderer
3717  * @start_pos: return location for the horizontal position of @cell within
3718  *            @tree_column, may be %NULL
3719  * @width: return location for the width of @cell, may be %NULL
3720  *
3721  * Obtains the horizontal position and size of a cell in a column. If the
3722  * cell is not found in the column, @start_pos and @width are not changed and
3723  * %FALSE is returned.
3724  * 
3725  * Return value: %TRUE if @cell belongs to @tree_column.
3726  */
3727 gboolean
3728 pspp_sheet_view_column_cell_get_position (PsppSheetViewColumn *tree_column,
3729                                         GtkCellRenderer   *cell_renderer,
3730                                         gint              *start_pos,
3731                                         gint              *width)
3732 {
3733   GList *list;
3734   gint current_x = 0;
3735   gboolean found_cell = FALSE;
3736   PsppSheetViewColumnCellInfo *cellinfo = NULL;
3737
3738   list = pspp_sheet_view_column_cell_first (tree_column);
3739   for (; list; list = pspp_sheet_view_column_cell_next (tree_column, list))
3740     {
3741       cellinfo = list->data;
3742       if (cellinfo->cell == cell_renderer)
3743         {
3744           found_cell = TRUE;
3745           break;
3746         }
3747
3748       if (cellinfo->cell->visible)
3749         current_x += cellinfo->real_width;
3750     }
3751
3752   if (found_cell)
3753     {
3754       if (start_pos)
3755         *start_pos = current_x;
3756       if (width)
3757         *width = cellinfo->real_width;
3758     }
3759
3760   return found_cell;
3761 }
3762
3763 /**
3764  * pspp_sheet_view_column_queue_resize:
3765  * @tree_column: A #PsppSheetViewColumn
3766  *
3767  * Flags the column, and the cell renderers added to this column, to have
3768  * their sizes renegotiated.
3769  *
3770  * Since: 2.8
3771  **/
3772 void
3773 pspp_sheet_view_column_queue_resize (PsppSheetViewColumn *tree_column)
3774 {
3775   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
3776
3777   if (tree_column->tree_view)
3778     _pspp_sheet_view_column_cell_set_dirty (tree_column, TRUE);
3779 }
3780
3781 /**
3782  * pspp_sheet_view_column_get_tree_view:
3783  * @tree_column: A #PsppSheetViewColumn
3784  *
3785  * Returns the #PsppSheetView wherein @tree_column has been inserted.  If
3786  * @column is currently not inserted in any tree view, %NULL is
3787  * returned.
3788  *
3789  * Return value: The tree view wherein @column has been inserted if any,
3790  *               %NULL otherwise.
3791  *
3792  * Since: 2.12
3793  */
3794 GtkWidget *
3795 pspp_sheet_view_column_get_tree_view (PsppSheetViewColumn *tree_column)
3796 {
3797   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), NULL);
3798
3799   return tree_column->tree_view;
3800 }
3801
3802 typedef struct {
3803   GtkCellLayout   *cell_layout;
3804   GtkCellRenderer *renderer;
3805   gchar           *attr_name;
3806 } AttributesSubParserData;
3807
3808 static void
3809 attributes_start_element (GMarkupParseContext *context,
3810                           const gchar         *element_name,
3811                           const gchar        **names,
3812                           const gchar        **values,
3813                           gpointer             user_data,
3814                           GError             **error)
3815 {
3816   AttributesSubParserData *parser_data = (AttributesSubParserData*)user_data;
3817   guint i;
3818
3819   if (strcmp (element_name, "attribute") == 0)
3820     {
3821       for (i = 0; names[i]; i++)
3822         if (strcmp (names[i], "name") == 0)
3823           parser_data->attr_name = g_strdup (values[i]);
3824     }
3825   else if (strcmp (element_name, "attributes") == 0)
3826     return;
3827   else
3828     g_warning ("Unsupported tag for GtkCellLayout: %s\n", element_name);
3829 }
3830
3831 static void
3832 attributes_text_element (GMarkupParseContext *context,
3833                          const gchar         *text,
3834                          gsize                text_len,
3835                          gpointer             user_data,
3836                          GError             **error)
3837 {
3838   AttributesSubParserData *parser_data = (AttributesSubParserData*)user_data;
3839   glong l;
3840   gchar *endptr;
3841   gchar *string;
3842   
3843   if (!parser_data->attr_name)
3844     return;
3845
3846   errno = 0;
3847   string = g_strndup (text, text_len);
3848   l = strtol (string, &endptr, 0);
3849   if (errno || endptr == string)
3850     {
3851       g_set_error (error, 
3852                    GTK_BUILDER_ERROR,
3853                    GTK_BUILDER_ERROR_INVALID_VALUE,
3854                    "Could not parse integer `%s'",
3855                    string);
3856       g_free (string);
3857       return;
3858     }
3859   g_free (string);
3860
3861   gtk_cell_layout_add_attribute (parser_data->cell_layout,
3862                                  parser_data->renderer,
3863                                  parser_data->attr_name, l);
3864   g_free (parser_data->attr_name);
3865   parser_data->attr_name = NULL;
3866 }
3867
3868 static const GMarkupParser attributes_parser =
3869   {
3870     attributes_start_element,
3871     NULL,
3872     attributes_text_element,
3873   };
3874
3875 gboolean
3876 _gtk_cell_layout_buildable_custom_tag_start (GtkBuildable  *buildable,
3877                                              GtkBuilder    *builder,
3878                                              GObject       *child,
3879                                              const gchar   *tagname,
3880                                              GMarkupParser *parser,
3881                                              gpointer      *data)
3882 {
3883   AttributesSubParserData *parser_data;
3884
3885   if (!child)
3886     return FALSE;
3887
3888   if (strcmp (tagname, "attributes") == 0)
3889     {
3890       parser_data = g_slice_new0 (AttributesSubParserData);
3891       parser_data->cell_layout = GTK_CELL_LAYOUT (buildable);
3892       parser_data->renderer = GTK_CELL_RENDERER (child);
3893       parser_data->attr_name = NULL;
3894
3895       *parser = attributes_parser;
3896       *data = parser_data;
3897       return TRUE;
3898     }
3899
3900   return FALSE;
3901 }
3902
3903 void
3904 _gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable,
3905                                            GtkBuilder   *builder,
3906                                            GObject      *child,
3907                                            const gchar  *tagname,
3908                                            gpointer     *data)
3909 {
3910   AttributesSubParserData *parser_data;
3911
3912   parser_data = (AttributesSubParserData*)data;
3913   g_assert (!parser_data->attr_name);
3914   g_slice_free (AttributesSubParserData, parser_data);
3915 }
3916
3917 void
3918 _gtk_cell_layout_buildable_add_child (GtkBuildable      *buildable,
3919                                       GtkBuilder        *builder,
3920                                       GObject           *child,
3921                                       const gchar       *type)
3922 {
3923   GtkCellLayoutIface *iface;
3924   
3925   g_return_if_fail (GTK_IS_CELL_LAYOUT (buildable));
3926   g_return_if_fail (GTK_IS_CELL_RENDERER (child));
3927
3928   iface = GTK_CELL_LAYOUT_GET_IFACE (buildable);
3929   g_return_if_fail (iface->pack_start != NULL);
3930   iface->pack_start (GTK_CELL_LAYOUT (buildable), GTK_CELL_RENDERER (child), FALSE);
3931 }
3932
3933 GType
3934 pspp_sheet_view_column_sizing_get_type (void)
3935 {
3936     static GType etype = 0;
3937     if (G_UNLIKELY(etype == 0)) {
3938         static const GEnumValue values[] = {
3939             { PSPP_SHEET_VIEW_COLUMN_GROW_ONLY, "PSPP_SHEET_VIEW_COLUMN_GROW_ONLY", "grow-only" },
3940             { PSPP_SHEET_VIEW_COLUMN_AUTOSIZE, "PSPP_SHEET_VIEW_COLUMN_AUTOSIZE", "autosize" },
3941             { PSPP_SHEET_VIEW_COLUMN_FIXED, "PSPP_SHEET_VIEW_COLUMN_FIXED", "fixed" },
3942             { 0, NULL, NULL }
3943         };
3944         etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewColumnSizing"), values);
3945     }
3946     return etype;
3947 }