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