Yet more sealage
[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_get_child (GTK_BIN (tree_column->button));
1089   alignment = tree_column->alignment;
1090   arrow = tree_column->arrow;
1091   current_child = gtk_bin_get_child (GTK_BIN (alignment));
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   GtkAllocation allocation;
1556   PsppSheetView *tree_view;
1557   GdkWindowAttr attr;
1558   guint attributes_mask;
1559   gboolean rtl;
1560
1561   tree_view = (PsppSheetView *)column->tree_view;
1562   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1563
1564   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
1565   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
1566   g_return_if_fail (tree_view->priv->header_window != NULL);
1567   if (!column->need_button || !column->button)
1568     return;
1569
1570   g_return_if_fail (column->button != NULL);
1571
1572   gtk_widget_set_parent_window (column->button, tree_view->priv->header_window);
1573
1574   if (column->visible)
1575     gtk_widget_show (column->button);
1576
1577   attr.window_type = GDK_WINDOW_CHILD;
1578   attr.wclass = GDK_INPUT_ONLY;
1579   attr.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
1580   attr.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
1581   attr.event_mask = gtk_widget_get_events (GTK_WIDGET (tree_view)) |
1582                     (GDK_BUTTON_PRESS_MASK |
1583                      GDK_BUTTON_RELEASE_MASK |
1584                      GDK_POINTER_MOTION_MASK |
1585                      GDK_POINTER_MOTION_HINT_MASK |
1586                      GDK_KEY_PRESS_MASK);
1587   attributes_mask = GDK_WA_CURSOR | GDK_WA_X | GDK_WA_Y;
1588   attr.cursor = gdk_cursor_new_for_display (gdk_window_get_display (tree_view->priv->header_window),
1589                                             GDK_SB_H_DOUBLE_ARROW);
1590   attr.y = 0;
1591   attr.width = TREE_VIEW_DRAG_WIDTH;
1592   attr.height = tree_view->priv->header_height;
1593   gtk_widget_get_allocation (column->button, &allocation);
1594   attr.x = (allocation.x + (rtl ? 0 : allocation.width)) - TREE_VIEW_DRAG_WIDTH / 2;
1595   column->window = gdk_window_new (tree_view->priv->header_window,
1596                                    &attr, attributes_mask);
1597   gdk_window_set_user_data (column->window, tree_view);
1598
1599   pspp_sheet_view_column_update_button (column);
1600
1601   gdk_cursor_unref (attr.cursor);
1602 }
1603
1604 void
1605 _pspp_sheet_view_column_unrealize_button (PsppSheetViewColumn *column)
1606 {
1607   g_return_if_fail (column != NULL);
1608   if (column->window != NULL)
1609     {
1610       gdk_window_set_user_data (column->window, NULL);
1611       gdk_window_destroy (column->window);
1612       column->window = NULL;
1613     }
1614 }
1615
1616 void
1617 _pspp_sheet_view_column_unset_model (PsppSheetViewColumn *column,
1618                                    GtkTreeModel      *old_model)
1619 {
1620   if (column->sort_column_changed_signal)
1621     {
1622       g_signal_handler_disconnect (old_model,
1623                                    column->sort_column_changed_signal);
1624       column->sort_column_changed_signal = 0;
1625     }
1626   pspp_sheet_view_column_set_sort_indicator (column, FALSE);
1627 }
1628
1629 void
1630 _pspp_sheet_view_column_set_tree_view (PsppSheetViewColumn *column,
1631                                      PsppSheetView       *tree_view)
1632 {
1633   g_assert (column->tree_view == NULL);
1634
1635   column->tree_view = GTK_WIDGET (tree_view);
1636   if (column->need_button)
1637     pspp_sheet_view_column_create_button (column);
1638
1639   column->property_changed_signal =
1640           g_signal_connect_swapped (tree_view,
1641                                     "notify::model",
1642                                     G_CALLBACK (pspp_sheet_view_column_setup_sort_column_id_callback),
1643                                     column);
1644
1645   pspp_sheet_view_column_setup_sort_column_id_callback (column);
1646 }
1647
1648 void
1649 _pspp_sheet_view_column_unset_tree_view (PsppSheetViewColumn *column)
1650 {
1651   if (column->tree_view && column->button)
1652     {
1653       gtk_container_remove (GTK_CONTAINER (column->tree_view), column->button);
1654     }
1655   if (column->property_changed_signal)
1656     {
1657       g_signal_handler_disconnect (column->tree_view, column->property_changed_signal);
1658       column->property_changed_signal = 0;
1659     }
1660
1661   if (column->sort_column_changed_signal)
1662     {
1663       g_signal_handler_disconnect (pspp_sheet_view_get_model (PSPP_SHEET_VIEW (column->tree_view)),
1664                                    column->sort_column_changed_signal);
1665       column->sort_column_changed_signal = 0;
1666     }
1667
1668   column->tree_view = NULL;
1669   column->button = NULL;
1670 }
1671
1672 gboolean
1673 _pspp_sheet_view_column_has_editable_cell (PsppSheetViewColumn *column)
1674 {
1675   GList *list;
1676
1677   for (list = column->cell_list; list; list = list->next)
1678     if (((PsppSheetViewColumnCellInfo *)list->data)->cell->mode ==
1679         GTK_CELL_RENDERER_MODE_EDITABLE)
1680       return TRUE;
1681
1682   return FALSE;
1683 }
1684
1685 /* gets cell being edited */
1686 GtkCellRenderer *
1687 _pspp_sheet_view_column_get_edited_cell (PsppSheetViewColumn *column)
1688 {
1689   GList *list;
1690
1691   for (list = column->cell_list; list; list = list->next)
1692     if (((PsppSheetViewColumnCellInfo *)list->data)->in_editing_mode)
1693       return ((PsppSheetViewColumnCellInfo *)list->data)->cell;
1694
1695   return NULL;
1696 }
1697
1698 gint
1699 _pspp_sheet_view_column_count_special_cells (PsppSheetViewColumn *column)
1700 {
1701   gint i = 0;
1702   GList *list;
1703
1704   for (list = column->cell_list; list; list = list->next)
1705     {
1706       PsppSheetViewColumnCellInfo *cellinfo = list->data;
1707
1708       if ((cellinfo->cell->mode == GTK_CELL_RENDERER_MODE_EDITABLE ||
1709           cellinfo->cell->mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE) &&
1710           gtk_cell_renderer_get_visible (cellinfo->cell))
1711         i++;
1712     }
1713
1714   return i;
1715 }
1716
1717 GtkCellRenderer *
1718 _pspp_sheet_view_column_get_cell_at_pos (PsppSheetViewColumn *column,
1719                                        gint               x)
1720 {
1721   GList *list;
1722   gint current_x = 0;
1723
1724   list = pspp_sheet_view_column_cell_first (column);
1725   for (; list; list = pspp_sheet_view_column_cell_next (column, list))
1726    {
1727      PsppSheetViewColumnCellInfo *cellinfo = list->data;
1728      if (current_x <= x && x <= current_x + cellinfo->real_width)
1729        return cellinfo->cell;
1730      current_x += cellinfo->real_width;
1731    }
1732
1733   return NULL;
1734 }
1735
1736 /* Public Functions */
1737
1738
1739 /**
1740  * pspp_sheet_view_column_new:
1741  * 
1742  * Creates a new #PsppSheetViewColumn.
1743  * 
1744  * Return value: A newly created #PsppSheetViewColumn.
1745  **/
1746 PsppSheetViewColumn *
1747 pspp_sheet_view_column_new (void)
1748 {
1749   PsppSheetViewColumn *tree_column;
1750
1751   tree_column = g_object_new (PSPP_TYPE_SHEET_VIEW_COLUMN, NULL);
1752
1753   return tree_column;
1754 }
1755
1756 /**
1757  * pspp_sheet_view_column_new_with_attributes:
1758  * @title: The title to set the header to.
1759  * @cell: The #GtkCellRenderer.
1760  * @Varargs: A %NULL-terminated list of attributes.
1761  * 
1762  * Creates a new #PsppSheetViewColumn with a number of default values.  This is
1763  * equivalent to calling pspp_sheet_view_column_set_title(),
1764  * pspp_sheet_view_column_pack_start(), and
1765  * pspp_sheet_view_column_set_attributes() on the newly created #PsppSheetViewColumn.
1766  *
1767  * Here's a simple example:
1768  * |[
1769  *  enum { TEXT_COLUMN, COLOR_COLUMN, N_COLUMNS };
1770  *  ...
1771  *  {
1772  *    PsppSheetViewColumn *column;
1773  *    GtkCellRenderer   *renderer = gtk_cell_renderer_text_new ();
1774  *  
1775  *    column = pspp_sheet_view_column_new_with_attributes ("Title",
1776  *                                                       renderer,
1777  *                                                       "text", TEXT_COLUMN,
1778  *                                                       "foreground", COLOR_COLUMN,
1779  *                                                       NULL);
1780  *  }
1781  * ]|
1782  * 
1783  * Return value: A newly created #PsppSheetViewColumn.
1784  **/
1785 PsppSheetViewColumn *
1786 pspp_sheet_view_column_new_with_attributes (const gchar     *title,
1787                                           GtkCellRenderer *cell,
1788                                           ...)
1789 {
1790   PsppSheetViewColumn *retval;
1791   va_list args;
1792
1793   retval = pspp_sheet_view_column_new ();
1794
1795   pspp_sheet_view_column_set_title (retval, title);
1796   pspp_sheet_view_column_pack_start (retval, cell, TRUE);
1797
1798   va_start (args, cell);
1799   pspp_sheet_view_column_set_attributesv (retval, cell, args);
1800   va_end (args);
1801
1802   return retval;
1803 }
1804
1805 static PsppSheetViewColumnCellInfo *
1806 pspp_sheet_view_column_get_cell_info (PsppSheetViewColumn *tree_column,
1807                                     GtkCellRenderer   *cell_renderer)
1808 {
1809   GList *list;
1810   for (list = tree_column->cell_list; list; list = list->next)
1811     if (((PsppSheetViewColumnCellInfo *)list->data)->cell == cell_renderer)
1812       return (PsppSheetViewColumnCellInfo *) list->data;
1813   return NULL;
1814 }
1815
1816
1817 /**
1818  * pspp_sheet_view_column_pack_start:
1819  * @tree_column: A #PsppSheetViewColumn.
1820  * @cell: The #GtkCellRenderer. 
1821  * @expand: %TRUE if @cell is to be given extra space allocated to @tree_column.
1822  *
1823  * Packs the @cell into the beginning of the column. If @expand is %FALSE, then
1824  * the @cell is allocated no more space than it needs. Any unused space is divided
1825  * evenly between cells for which @expand is %TRUE.
1826  **/
1827 void
1828 pspp_sheet_view_column_pack_start (PsppSheetViewColumn *tree_column,
1829                                  GtkCellRenderer   *cell,
1830                                  gboolean           expand)
1831 {
1832   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (tree_column), cell, expand);
1833 }
1834
1835 /**
1836  * pspp_sheet_view_column_pack_end:
1837  * @tree_column: A #PsppSheetViewColumn.
1838  * @cell: The #GtkCellRenderer. 
1839  * @expand: %TRUE if @cell is to be given extra space allocated to @tree_column.
1840  *
1841  * Adds the @cell to end of the column. If @expand is %FALSE, then the @cell
1842  * is allocated no more space than it needs. Any unused space is divided
1843  * evenly between cells for which @expand is %TRUE.
1844  **/
1845 void
1846 pspp_sheet_view_column_pack_end (PsppSheetViewColumn  *tree_column,
1847                                GtkCellRenderer    *cell,
1848                                gboolean            expand)
1849 {
1850   gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (tree_column), cell, expand);
1851 }
1852
1853 /**
1854  * pspp_sheet_view_column_clear:
1855  * @tree_column: A #PsppSheetViewColumn
1856  * 
1857  * Unsets all the mappings on all renderers on the @tree_column.
1858  **/
1859 void
1860 pspp_sheet_view_column_clear (PsppSheetViewColumn *tree_column)
1861 {
1862   gtk_cell_layout_clear (GTK_CELL_LAYOUT (tree_column));
1863 }
1864
1865 static GList *
1866 pspp_sheet_view_column_cell_layout_get_cells (GtkCellLayout *layout)
1867 {
1868   PsppSheetViewColumn *tree_column = PSPP_SHEET_VIEW_COLUMN (layout);
1869   GList *retval = NULL, *list;
1870
1871   g_return_val_if_fail (tree_column != NULL, NULL);
1872
1873   for (list = tree_column->cell_list; list; list = list->next)
1874     {
1875       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *)list->data;
1876
1877       retval = g_list_append (retval, info->cell);
1878     }
1879
1880   return retval;
1881 }
1882
1883 /**
1884  * pspp_sheet_view_column_get_cell_renderers:
1885  * @tree_column: A #PsppSheetViewColumn
1886  *
1887  * Returns a newly-allocated #GList of all the cell renderers in the column,
1888  * in no particular order.  The list must be freed with g_list_free().
1889  *
1890  * Return value: A list of #GtkCellRenderers
1891  *
1892  * Deprecated: 2.18: use gtk_cell_layout_get_cells() instead.
1893  **/
1894 GList *
1895 pspp_sheet_view_column_get_cell_renderers (PsppSheetViewColumn *tree_column)
1896 {
1897   return pspp_sheet_view_column_cell_layout_get_cells (GTK_CELL_LAYOUT (tree_column));
1898 }
1899
1900 /**
1901  * pspp_sheet_view_column_add_attribute:
1902  * @tree_column: A #PsppSheetViewColumn.
1903  * @cell_renderer: the #GtkCellRenderer to set attributes on
1904  * @attribute: An attribute on the renderer
1905  * @column: The column position on the model to get the attribute from.
1906  * 
1907  * Adds an attribute mapping to the list in @tree_column.  The @column is the
1908  * column of the model to get a value from, and the @attribute is the
1909  * parameter on @cell_renderer to be set from the value. So for example
1910  * if column 2 of the model contains strings, you could have the
1911  * "text" attribute of a #GtkCellRendererText get its values from
1912  * column 2.
1913  **/
1914 void
1915 pspp_sheet_view_column_add_attribute (PsppSheetViewColumn *tree_column,
1916                                     GtkCellRenderer   *cell_renderer,
1917                                     const gchar       *attribute,
1918                                     gint               column)
1919 {
1920   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (tree_column),
1921                                  cell_renderer, attribute, column);
1922 }
1923
1924 static void
1925 pspp_sheet_view_column_set_attributesv (PsppSheetViewColumn *tree_column,
1926                                       GtkCellRenderer   *cell_renderer,
1927                                       va_list            args)
1928 {
1929   gchar *attribute;
1930   gint column;
1931
1932   attribute = va_arg (args, gchar *);
1933
1934   pspp_sheet_view_column_clear_attributes (tree_column, cell_renderer);
1935   
1936   while (attribute != NULL)
1937     {
1938       column = va_arg (args, gint);
1939       pspp_sheet_view_column_add_attribute (tree_column, cell_renderer, attribute, column);
1940       attribute = va_arg (args, gchar *);
1941     }
1942 }
1943
1944 /**
1945  * pspp_sheet_view_column_set_attributes:
1946  * @tree_column: A #PsppSheetViewColumn.
1947  * @cell_renderer: the #GtkCellRenderer we're setting the attributes of
1948  * @Varargs: A %NULL-terminated list of attributes.
1949  * 
1950  * Sets the attributes in the list as the attributes of @tree_column.
1951  * The attributes should be in attribute/column order, as in
1952  * pspp_sheet_view_column_add_attribute(). All existing attributes
1953  * are removed, and replaced with the new attributes.
1954  **/
1955 void
1956 pspp_sheet_view_column_set_attributes (PsppSheetViewColumn *tree_column,
1957                                      GtkCellRenderer   *cell_renderer,
1958                                      ...)
1959 {
1960   va_list args;
1961
1962   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
1963   g_return_if_fail (GTK_IS_CELL_RENDERER (cell_renderer));
1964   g_return_if_fail (pspp_sheet_view_column_get_cell_info (tree_column, cell_renderer));
1965
1966   va_start (args, cell_renderer);
1967   pspp_sheet_view_column_set_attributesv (tree_column, cell_renderer, args);
1968   va_end (args);
1969 }
1970
1971
1972 /**
1973  * pspp_sheet_view_column_set_cell_data_func:
1974  * @tree_column: A #PsppSheetViewColumn
1975  * @cell_renderer: A #GtkCellRenderer
1976  * @func: The #PsppSheetViewColumnFunc to use. 
1977  * @func_data: The user data for @func.
1978  * @destroy: The destroy notification for @func_data
1979  * 
1980  * Sets the #PsppSheetViewColumnFunc to use for the column.  This
1981  * function is used instead of the standard attributes mapping for
1982  * setting the column value, and should set the value of @tree_column's
1983  * cell renderer as appropriate.  @func may be %NULL to remove an
1984  * older one.
1985  **/
1986 void
1987 pspp_sheet_view_column_set_cell_data_func (PsppSheetViewColumn   *tree_column,
1988                                          GtkCellRenderer     *cell_renderer,
1989                                          PsppSheetCellDataFunc  func,
1990                                          gpointer             func_data,
1991                                          GDestroyNotify       destroy)
1992 {
1993   gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (tree_column),
1994                                       cell_renderer,
1995                                       (GtkCellLayoutDataFunc)func,
1996                                       func_data, destroy);
1997 }
1998
1999
2000 /**
2001  * pspp_sheet_view_column_clear_attributes:
2002  * @tree_column: a #PsppSheetViewColumn
2003  * @cell_renderer: a #GtkCellRenderer to clear the attribute mapping on.
2004  * 
2005  * Clears all existing attributes previously set with
2006  * pspp_sheet_view_column_set_attributes().
2007  **/
2008 void
2009 pspp_sheet_view_column_clear_attributes (PsppSheetViewColumn *tree_column,
2010                                        GtkCellRenderer   *cell_renderer)
2011 {
2012   gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (tree_column),
2013                                     cell_renderer);
2014 }
2015
2016 /**
2017  * pspp_sheet_view_column_set_spacing:
2018  * @tree_column: A #PsppSheetViewColumn.
2019  * @spacing: distance between cell renderers in pixels.
2020  * 
2021  * Sets the spacing field of @tree_column, which is the number of pixels to
2022  * place between cell renderers packed into it.
2023  **/
2024 void
2025 pspp_sheet_view_column_set_spacing (PsppSheetViewColumn *tree_column,
2026                                   gint               spacing)
2027 {
2028   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2029   g_return_if_fail (spacing >= 0);
2030
2031   if (tree_column->spacing == spacing)
2032     return;
2033
2034   tree_column->spacing = spacing;
2035   if (tree_column->tree_view)
2036     _pspp_sheet_view_column_cell_set_dirty (tree_column);
2037 }
2038
2039 /**
2040  * pspp_sheet_view_column_get_spacing:
2041  * @tree_column: A #PsppSheetViewColumn.
2042  * 
2043  * Returns the spacing of @tree_column.
2044  * 
2045  * Return value: the spacing of @tree_column.
2046  **/
2047 gint
2048 pspp_sheet_view_column_get_spacing (PsppSheetViewColumn *tree_column)
2049 {
2050   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
2051
2052   return tree_column->spacing;
2053 }
2054
2055 /* Options for manipulating the columns */
2056
2057 /**
2058  * pspp_sheet_view_column_set_visible:
2059  * @tree_column: A #PsppSheetViewColumn.
2060  * @visible: %TRUE if the @tree_column is visible.
2061  * 
2062  * Sets the visibility of @tree_column.
2063  **/
2064 void
2065 pspp_sheet_view_column_set_visible (PsppSheetViewColumn *tree_column,
2066                                   gboolean           visible)
2067 {
2068   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2069
2070   visible = !! visible;
2071   
2072   if (tree_column->visible == visible)
2073     return;
2074
2075   tree_column->visible = visible;
2076
2077   if (tree_column->visible)
2078     _pspp_sheet_view_column_cell_set_dirty (tree_column);
2079
2080   pspp_sheet_view_column_update_button (tree_column);
2081   g_object_notify (G_OBJECT (tree_column), "visible");
2082 }
2083
2084 /**
2085  * pspp_sheet_view_column_get_visible:
2086  * @tree_column: A #PsppSheetViewColumn.
2087  * 
2088  * Returns %TRUE if @tree_column is visible.
2089  * 
2090  * Return value: whether the column is visible or not.  If it is visible, then
2091  * the tree will show the column.
2092  **/
2093 gboolean
2094 pspp_sheet_view_column_get_visible (PsppSheetViewColumn *tree_column)
2095 {
2096   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2097
2098   return tree_column->visible;
2099 }
2100
2101 /**
2102  * pspp_sheet_view_column_set_resizable:
2103  * @tree_column: A #PsppSheetViewColumn
2104  * @resizable: %TRUE, if the column can be resized
2105  * 
2106  * If @resizable is %TRUE, then the user can explicitly resize the column by
2107  * grabbing the outer edge of the column button.
2108  **/
2109 void
2110 pspp_sheet_view_column_set_resizable (PsppSheetViewColumn *tree_column,
2111                                     gboolean           resizable)
2112 {
2113   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2114
2115   resizable = !! resizable;
2116
2117   if (tree_column->resizable == resizable)
2118     return;
2119
2120   tree_column->resizable = resizable;
2121
2122   pspp_sheet_view_column_update_button (tree_column);
2123
2124   g_object_notify (G_OBJECT (tree_column), "resizable");
2125 }
2126
2127 /**
2128  * pspp_sheet_view_column_get_resizable:
2129  * @tree_column: A #PsppSheetViewColumn
2130  * 
2131  * Returns %TRUE if the @tree_column can be resized by the end user.
2132  * 
2133  * Return value: %TRUE, if the @tree_column can be resized.
2134  **/
2135 gboolean
2136 pspp_sheet_view_column_get_resizable (PsppSheetViewColumn *tree_column)
2137 {
2138   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2139
2140   return tree_column->resizable;
2141 }
2142
2143
2144 /**
2145  * pspp_sheet_view_column_get_width:
2146  * @tree_column: A #PsppSheetViewColumn.
2147  * 
2148  * Returns the current size of @tree_column in pixels.
2149  * 
2150  * Return value: The current width of @tree_column.
2151  **/
2152 gint
2153 pspp_sheet_view_column_get_width (PsppSheetViewColumn *tree_column)
2154 {
2155   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
2156
2157   return tree_column->width;
2158 }
2159
2160 /**
2161  * pspp_sheet_view_column_set_fixed_width:
2162  * @tree_column: A #PsppSheetViewColumn.
2163  * @fixed_width: The size to set @tree_column to. Must be greater than 0.
2164  * 
2165  * Sets the size of the column in pixels.  The size of the column is clamped to
2166  * the min/max width for the column.  Please note that the min/max width of the
2167  * column doesn't actually affect the "fixed_width" property of the widget, just
2168  * the actual size when displayed.
2169  **/
2170 void
2171 pspp_sheet_view_column_set_fixed_width (PsppSheetViewColumn *tree_column,
2172                                       gint               fixed_width)
2173 {
2174   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2175   g_return_if_fail (fixed_width > 0);
2176
2177   tree_column->fixed_width = fixed_width;
2178   tree_column->use_resized_width = FALSE;
2179
2180   if (tree_column->tree_view &&
2181       gtk_widget_get_realized (tree_column->tree_view))
2182     {
2183       gtk_widget_queue_resize (tree_column->tree_view);
2184     }
2185
2186   g_object_notify (G_OBJECT (tree_column), "fixed-width");
2187 }
2188
2189 /**
2190  * pspp_sheet_view_column_get_fixed_width:
2191  * @tree_column: a #PsppSheetViewColumn
2192  * 
2193  * Gets the fixed width of the column.  This value is only meaning may not be
2194  * the actual width of the column on the screen, just what is requested.
2195  * 
2196  * Return value: the fixed width of the column
2197  **/
2198 gint
2199 pspp_sheet_view_column_get_fixed_width (PsppSheetViewColumn *tree_column)
2200 {
2201   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
2202
2203   return tree_column->fixed_width;
2204 }
2205
2206 /**
2207  * pspp_sheet_view_column_set_min_width:
2208  * @tree_column: A #PsppSheetViewColumn.
2209  * @min_width: The minimum width of the column in pixels, or -1.
2210  * 
2211  * Sets the minimum width of the @tree_column.  If @min_width is -1, then the
2212  * minimum width is unset.
2213  **/
2214 void
2215 pspp_sheet_view_column_set_min_width (PsppSheetViewColumn *tree_column,
2216                                     gint               min_width)
2217 {
2218   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2219   g_return_if_fail (min_width >= -1);
2220
2221   if (min_width == tree_column->min_width)
2222     return;
2223
2224   if (tree_column->visible &&
2225       tree_column->tree_view != NULL &&
2226       gtk_widget_get_realized (tree_column->tree_view))
2227     {
2228       if (min_width > tree_column->width)
2229         gtk_widget_queue_resize (tree_column->tree_view);
2230     }
2231
2232   tree_column->min_width = min_width;
2233   g_object_freeze_notify (G_OBJECT (tree_column));
2234   if (tree_column->max_width != -1 && tree_column->max_width < min_width)
2235     {
2236       tree_column->max_width = min_width;
2237       g_object_notify (G_OBJECT (tree_column), "max-width");
2238     }
2239   g_object_notify (G_OBJECT (tree_column), "min-width");
2240   g_object_thaw_notify (G_OBJECT (tree_column));
2241 }
2242
2243 /**
2244  * pspp_sheet_view_column_get_min_width:
2245  * @tree_column: A #PsppSheetViewColumn.
2246  * 
2247  * Returns the minimum width in pixels of the @tree_column, or -1 if no minimum
2248  * width is set.
2249  * 
2250  * Return value: The minimum width of the @tree_column.
2251  **/
2252 gint
2253 pspp_sheet_view_column_get_min_width (PsppSheetViewColumn *tree_column)
2254 {
2255   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), -1);
2256
2257   return tree_column->min_width;
2258 }
2259
2260 /**
2261  * pspp_sheet_view_column_set_max_width:
2262  * @tree_column: A #PsppSheetViewColumn.
2263  * @max_width: The maximum width of the column in pixels, or -1.
2264  * 
2265  * Sets the maximum width of the @tree_column.  If @max_width is -1, then the
2266  * maximum width is unset.  Note, the column can actually be wider than max
2267  * width if it's the last column in a view.  In this case, the column expands to
2268  * fill any extra space.
2269  **/
2270 void
2271 pspp_sheet_view_column_set_max_width (PsppSheetViewColumn *tree_column,
2272                                     gint               max_width)
2273 {
2274   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2275   g_return_if_fail (max_width >= -1);
2276
2277   if (max_width == tree_column->max_width)
2278     return;
2279
2280   if (tree_column->visible &&
2281       tree_column->tree_view != NULL &&
2282       gtk_widget_get_realized (tree_column->tree_view))
2283     {
2284       if (max_width != -1 && max_width < tree_column->width)
2285         gtk_widget_queue_resize (tree_column->tree_view);
2286     }
2287
2288   tree_column->max_width = max_width;
2289   g_object_freeze_notify (G_OBJECT (tree_column));
2290   if (max_width != -1 && max_width < tree_column->min_width)
2291     {
2292       tree_column->min_width = max_width;
2293       g_object_notify (G_OBJECT (tree_column), "min-width");
2294     }
2295   g_object_notify (G_OBJECT (tree_column), "max-width");
2296   g_object_thaw_notify (G_OBJECT (tree_column));
2297 }
2298
2299 /**
2300  * pspp_sheet_view_column_get_max_width:
2301  * @tree_column: A #PsppSheetViewColumn.
2302  * 
2303  * Returns the maximum width in pixels of the @tree_column, or -1 if no maximum
2304  * width is set.
2305  * 
2306  * Return value: The maximum width of the @tree_column.
2307  **/
2308 gint
2309 pspp_sheet_view_column_get_max_width (PsppSheetViewColumn *tree_column)
2310 {
2311   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), -1);
2312
2313   return tree_column->max_width;
2314 }
2315
2316 /**
2317  * pspp_sheet_view_column_clicked:
2318  * @tree_column: a #PsppSheetViewColumn
2319  * 
2320  * Emits the "clicked" signal on the column.  This function will only work if
2321  * @tree_column is clickable.
2322  **/
2323 void
2324 pspp_sheet_view_column_clicked (PsppSheetViewColumn *tree_column)
2325 {
2326   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2327
2328   if (tree_column->visible &&
2329       tree_column->button &&
2330       tree_column->clickable)
2331     gtk_button_clicked (GTK_BUTTON (tree_column->button));
2332 }
2333
2334 /**
2335  * pspp_sheet_view_column_set_title:
2336  * @tree_column: A #PsppSheetViewColumn.
2337  * @title: The title of the @tree_column.
2338  * 
2339  * Sets the title of the @tree_column.  If a custom widget has been set, then
2340  * this value is ignored.
2341  **/
2342 void
2343 pspp_sheet_view_column_set_title (PsppSheetViewColumn *tree_column,
2344                                 const gchar       *title)
2345 {
2346   gchar *new_title;
2347   
2348   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2349
2350   new_title = g_strdup (title);
2351   g_free (tree_column->title);
2352   tree_column->title = new_title;
2353
2354   pspp_sheet_view_column_update_button (tree_column);
2355   g_object_notify (G_OBJECT (tree_column), "title");
2356 }
2357
2358 /**
2359  * pspp_sheet_view_column_get_title:
2360  * @tree_column: A #PsppSheetViewColumn.
2361  * 
2362  * Returns the title of the widget.
2363  * 
2364  * Return value: the title of the column. This string should not be
2365  * modified or freed.
2366  **/
2367 const gchar *
2368 pspp_sheet_view_column_get_title (PsppSheetViewColumn *tree_column)
2369 {
2370   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), NULL);
2371
2372   return tree_column->title;
2373 }
2374
2375 /**
2376  * pspp_sheet_view_column_set_expand:
2377  * @tree_column: A #PsppSheetViewColumn
2378  * @expand: %TRUE if the column should take available extra space, %FALSE if not
2379  * 
2380  * Sets the column to take available extra space.  This space is shared equally
2381  * amongst all columns that have the expand set to %TRUE.  If no column has this
2382  * option set, then the last column gets all extra space.  By default, every
2383  * column is created with this %FALSE.
2384  *
2385  * Since: 2.4
2386  **/
2387 void
2388 pspp_sheet_view_column_set_expand (PsppSheetViewColumn *tree_column,
2389                                  gboolean           expand)
2390 {
2391   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2392
2393   expand = !!expand;
2394   if (tree_column->expand == expand)
2395     return;
2396   tree_column->expand = expand;
2397
2398   if (tree_column->visible &&
2399       tree_column->tree_view != NULL &&
2400       gtk_widget_get_realized (tree_column->tree_view))
2401     {
2402       /* We want to continue using the original width of the
2403        * column that includes additional space added by the user
2404        * resizing the columns and possibly extra (expanded) space, which
2405        * are not included in the resized width.
2406        */
2407       tree_column->use_resized_width = FALSE;
2408
2409       gtk_widget_queue_resize (tree_column->tree_view);
2410     }
2411
2412   g_object_notify (G_OBJECT (tree_column), "expand");
2413 }
2414
2415 /**
2416  * pspp_sheet_view_column_get_expand:
2417  * @tree_column: a #PsppSheetViewColumn
2418  * 
2419  * Return %TRUE if the column expands to take any available space.
2420  * 
2421  * Return value: %TRUE, if the column expands
2422  *
2423  * Since: 2.4
2424  **/
2425 gboolean
2426 pspp_sheet_view_column_get_expand (PsppSheetViewColumn *tree_column)
2427 {
2428   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2429
2430   return tree_column->expand;
2431 }
2432
2433 /**
2434  * pspp_sheet_view_column_set_clickable:
2435  * @tree_column: A #PsppSheetViewColumn.
2436  * @clickable: %TRUE if the header is active.
2437  * 
2438  * Sets the header to be active if @active is %TRUE.  When the header is active,
2439  * then it can take keyboard focus, and can be clicked.
2440  **/
2441 void
2442 pspp_sheet_view_column_set_clickable (PsppSheetViewColumn *tree_column,
2443                                     gboolean           clickable)
2444 {
2445   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2446
2447   clickable = !! clickable;
2448   if (tree_column->clickable == clickable)
2449     return;
2450
2451   tree_column->clickable = clickable;
2452   pspp_sheet_view_column_update_button (tree_column);
2453   g_object_notify (G_OBJECT (tree_column), "clickable");
2454 }
2455
2456 /**
2457  * pspp_sheet_view_column_get_clickable:
2458  * @tree_column: a #PsppSheetViewColumn
2459  * 
2460  * Returns %TRUE if the user can click on the header for the column.
2461  * 
2462  * Return value: %TRUE if user can click the column header.
2463  **/
2464 gboolean
2465 pspp_sheet_view_column_get_clickable (PsppSheetViewColumn *tree_column)
2466 {
2467   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2468
2469   return tree_column->clickable;
2470 }
2471
2472 /**
2473  * pspp_sheet_view_column_set_widget:
2474  * @tree_column: A #PsppSheetViewColumn.
2475  * @widget: (allow-none): A child #GtkWidget, or %NULL.
2476  *
2477  * Sets the widget in the header to be @widget.  If widget is %NULL, then the
2478  * header button is set with a #GtkLabel set to the title of @tree_column.
2479  **/
2480 void
2481 pspp_sheet_view_column_set_widget (PsppSheetViewColumn *tree_column,
2482                                  GtkWidget         *widget)
2483 {
2484   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2485   g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
2486
2487   if (widget)
2488     g_object_ref_sink (widget);
2489
2490   if (tree_column->child)      
2491     g_object_unref (tree_column->child);
2492
2493   tree_column->child = widget;
2494   pspp_sheet_view_column_update_button (tree_column);
2495   g_object_notify (G_OBJECT (tree_column), "widget");
2496 }
2497
2498 /**
2499  * pspp_sheet_view_column_get_widget:
2500  * @tree_column: A #PsppSheetViewColumn.
2501  * 
2502  * Returns the #GtkWidget in the button on the column header.  If a custom
2503  * widget has not been set then %NULL is returned.
2504  * 
2505  * Return value: The #GtkWidget in the column header, or %NULL
2506  **/
2507 GtkWidget *
2508 pspp_sheet_view_column_get_widget (PsppSheetViewColumn *tree_column)
2509 {
2510   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), NULL);
2511
2512   return tree_column->child;
2513 }
2514
2515 /**
2516  * pspp_sheet_view_column_set_alignment:
2517  * @tree_column: A #PsppSheetViewColumn.
2518  * @xalign: The alignment, which is between [0.0 and 1.0] inclusive.
2519  * 
2520  * Sets the alignment of the title or custom widget inside the column header.
2521  * The alignment determines its location inside the button -- 0.0 for left, 0.5
2522  * for center, 1.0 for right.
2523  **/
2524 void
2525 pspp_sheet_view_column_set_alignment (PsppSheetViewColumn *tree_column,
2526                                     gfloat             xalign)
2527 {
2528   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2529
2530   xalign = CLAMP (xalign, 0.0, 1.0);
2531
2532   if (tree_column->xalign == xalign)
2533     return;
2534
2535   tree_column->xalign = xalign;
2536   pspp_sheet_view_column_update_button (tree_column);
2537   g_object_notify (G_OBJECT (tree_column), "alignment");
2538 }
2539
2540 /**
2541  * pspp_sheet_view_column_get_alignment:
2542  * @tree_column: A #PsppSheetViewColumn.
2543  * 
2544  * Returns the current x alignment of @tree_column.  This value can range
2545  * between 0.0 and 1.0.
2546  * 
2547  * Return value: The current alignent of @tree_column.
2548  **/
2549 gfloat
2550 pspp_sheet_view_column_get_alignment (PsppSheetViewColumn *tree_column)
2551 {
2552   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0.5);
2553
2554   return tree_column->xalign;
2555 }
2556
2557 /**
2558  * pspp_sheet_view_column_set_reorderable:
2559  * @tree_column: A #PsppSheetViewColumn
2560  * @reorderable: %TRUE, if the column can be reordered.
2561  * 
2562  * If @reorderable is %TRUE, then the column can be reordered by the end user
2563  * dragging the header.
2564  **/
2565 void
2566 pspp_sheet_view_column_set_reorderable (PsppSheetViewColumn *tree_column,
2567                                       gboolean           reorderable)
2568 {
2569   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2570
2571   /*  if (reorderable)
2572       pspp_sheet_view_column_set_clickable (tree_column, TRUE);*/
2573
2574   reorderable = !!reorderable;
2575   if (tree_column->reorderable == reorderable)
2576     return;
2577
2578   tree_column->reorderable = reorderable;
2579   pspp_sheet_view_column_update_button (tree_column);
2580   g_object_notify (G_OBJECT (tree_column), "reorderable");
2581 }
2582
2583 /**
2584  * pspp_sheet_view_column_get_reorderable:
2585  * @tree_column: A #PsppSheetViewColumn
2586  * 
2587  * Returns %TRUE if the @tree_column can be reordered by the user.
2588  * 
2589  * Return value: %TRUE if the @tree_column can be reordered by the user.
2590  **/
2591 gboolean
2592 pspp_sheet_view_column_get_reorderable (PsppSheetViewColumn *tree_column)
2593 {
2594   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2595
2596   return tree_column->reorderable;
2597 }
2598
2599 /**
2600  * pspp_sheet_view_column_set_quick_edit:
2601  * @tree_column: A #PsppSheetViewColumn
2602  * @quick_edit: If true, editing starts upon the first click in the column.  If
2603  * false, the first click selects the column and a second click is needed to
2604  * begin editing.  This has no effect on cells that are not editable.
2605  **/
2606 void
2607 pspp_sheet_view_column_set_quick_edit (PsppSheetViewColumn *tree_column,
2608                                       gboolean           quick_edit)
2609 {
2610   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2611
2612   quick_edit = !!quick_edit;
2613   if (tree_column->quick_edit != quick_edit)
2614     {
2615       tree_column->quick_edit = quick_edit;
2616       g_object_notify (G_OBJECT (tree_column), "quick-edit");
2617     }
2618 }
2619
2620 /**
2621  * pspp_sheet_view_column_get_quick_edit:
2622  * @tree_column: A #PsppSheetViewColumn
2623  *
2624  * Returns %TRUE if editing starts upon the first click in the column.  Returns
2625  * %FALSE, the first click selects the column and a second click is needed to
2626  * begin editing.  This is not meaningful for cells that are not editable.
2627  *
2628  * Return value: %TRUE if editing starts upon the first click.
2629  **/
2630 gboolean
2631 pspp_sheet_view_column_get_quick_edit (PsppSheetViewColumn *tree_column)
2632 {
2633   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2634
2635   return tree_column->quick_edit;
2636 }
2637
2638
2639 /**
2640  * pspp_sheet_view_column_set_selected:
2641  * @tree_column: A #PsppSheetViewColumn
2642  * @selected: If true, the column is selected as part of a rectangular
2643  * selection.
2644  **/
2645 void
2646 pspp_sheet_view_column_set_selected (PsppSheetViewColumn *tree_column,
2647                                       gboolean           selected)
2648 {
2649   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2650
2651   selected = !!selected;
2652   if (tree_column->selected != selected)
2653     {
2654       PsppSheetSelection *selection;
2655       PsppSheetView *sheet_view;
2656
2657       if (tree_column->tree_view != NULL)
2658         gtk_widget_queue_draw (GTK_WIDGET (tree_column->tree_view));
2659       tree_column->selected = selected;
2660       g_object_notify (G_OBJECT (tree_column), "selected");
2661
2662       sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view (
2663                                       tree_column));
2664       selection = pspp_sheet_view_get_selection (sheet_view);
2665       _pspp_sheet_selection_emit_changed (selection);
2666     }
2667 }
2668
2669 /**
2670  * pspp_sheet_view_column_get_selected:
2671  * @tree_column: A #PsppSheetViewColumn
2672  *
2673  * Returns %TRUE if the column is selected as part of a rectangular
2674  * selection.
2675  *
2676  * Return value: %TRUE if the column is selected as part of a rectangular
2677  * selection.
2678  **/
2679 gboolean
2680 pspp_sheet_view_column_get_selected (PsppSheetViewColumn *tree_column)
2681 {
2682   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2683
2684   return tree_column->selected;
2685 }
2686
2687 /**
2688  * pspp_sheet_view_column_set_selectable:
2689  * @tree_column: A #PsppSheetViewColumn
2690  * @selectable: If true, the column may be selected as part of a rectangular
2691  * selection.
2692  **/
2693 void
2694 pspp_sheet_view_column_set_selectable (PsppSheetViewColumn *tree_column,
2695                                       gboolean           selectable)
2696 {
2697   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2698
2699   selectable = !!selectable;
2700   if (tree_column->selectable != selectable)
2701     {
2702       if (tree_column->tree_view != NULL)
2703         gtk_widget_queue_draw (GTK_WIDGET (tree_column->tree_view));
2704       tree_column->selectable = selectable;
2705       g_object_notify (G_OBJECT (tree_column), "selectable");
2706     }
2707 }
2708
2709 /**
2710  * pspp_sheet_view_column_get_selectable:
2711  * @tree_column: A #PsppSheetViewColumn
2712  *
2713  * Returns %TRUE if the column may be selected as part of a rectangular
2714  * selection.
2715  *
2716  * Return value: %TRUE if the column may be selected as part of a rectangular
2717  * selection.
2718  **/
2719 gboolean
2720 pspp_sheet_view_column_get_selectable (PsppSheetViewColumn *tree_column)
2721 {
2722   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2723
2724   return tree_column->selectable;
2725 }
2726
2727
2728 /**
2729  * pspp_sheet_view_column_set_row_head:
2730  * @tree_column: A #PsppSheetViewColumn
2731  * @row_head: If true, the column is a "row head", analogous to a column head.
2732  * See the description of the row-head property for more information.
2733  **/
2734 void
2735 pspp_sheet_view_column_set_row_head (PsppSheetViewColumn *tree_column,
2736                                       gboolean           row_head)
2737 {
2738   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2739
2740   row_head = !!row_head;
2741   if (tree_column->row_head != row_head)
2742     {
2743       tree_column->row_head = row_head;
2744       g_object_notify (G_OBJECT (tree_column), "row_head");
2745     }
2746 }
2747
2748 /**
2749  * pspp_sheet_view_column_get_row_head:
2750  * @tree_column: A #PsppSheetViewColumn
2751  *
2752  * Returns %TRUE if the column is a row head.
2753  *
2754  * Return value: %TRUE if the column is a row head.
2755  **/
2756 gboolean
2757 pspp_sheet_view_column_get_row_head (PsppSheetViewColumn *tree_column)
2758 {
2759   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2760
2761   return tree_column->row_head;
2762 }
2763
2764
2765 /**
2766  * pspp_sheet_view_column_set_tabbable:
2767  * @tree_column: A #PsppSheetViewColumn
2768  * @tabbable: If true, the column is "tabbable", meaning that Tab and Shift+Tab
2769  * in the sheet visit this column.  If false, Tab and Shift+Tab skip this
2770  * column.
2771  **/
2772 void
2773 pspp_sheet_view_column_set_tabbable (PsppSheetViewColumn *tree_column,
2774                                      gboolean           tabbable)
2775 {
2776   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2777
2778   tabbable = !!tabbable;
2779   if (tree_column->tabbable != tabbable)
2780     {
2781       tree_column->tabbable = tabbable;
2782       g_object_notify (G_OBJECT (tree_column), "tabbable");
2783     }
2784 }
2785
2786 /**
2787  * pspp_sheet_view_column_get_tabbable:
2788  * @tree_column: A #PsppSheetViewColumn
2789  *
2790  * Returns %TRUE if the column is tabbable.
2791  *
2792  * Return value: %TRUE if the column is tabbable.
2793  **/
2794 gboolean
2795 pspp_sheet_view_column_get_tabbable (PsppSheetViewColumn *tree_column)
2796 {
2797   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2798
2799   return tree_column->tabbable;
2800 }
2801
2802
2803 /**
2804  * pspp_sheet_view_column_set_sort_column_id:
2805  * @tree_column: a #PsppSheetViewColumn
2806  * @sort_column_id: The @sort_column_id of the model to sort on.
2807  *
2808  * Sets the logical @sort_column_id that this column sorts on when this column 
2809  * is selected for sorting.  Doing so makes the column header clickable.
2810  **/
2811 void
2812 pspp_sheet_view_column_set_sort_column_id (PsppSheetViewColumn *tree_column,
2813                                          gint               sort_column_id)
2814 {
2815   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2816   g_return_if_fail (sort_column_id >= -1);
2817
2818   if (tree_column->sort_column_id == sort_column_id)
2819     return;
2820
2821   tree_column->sort_column_id = sort_column_id;
2822
2823   /* Handle unsetting the id */
2824   if (sort_column_id == -1)
2825     {
2826       GtkTreeModel *model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (tree_column->tree_view));
2827
2828       if (tree_column->sort_clicked_signal)
2829         {
2830           g_signal_handler_disconnect (tree_column, tree_column->sort_clicked_signal);
2831           tree_column->sort_clicked_signal = 0;
2832         }
2833
2834       if (tree_column->sort_column_changed_signal)
2835         {
2836           g_signal_handler_disconnect (model, tree_column->sort_column_changed_signal);
2837           tree_column->sort_column_changed_signal = 0;
2838         }
2839
2840       pspp_sheet_view_column_set_sort_order (tree_column, GTK_SORT_ASCENDING);
2841       pspp_sheet_view_column_set_sort_indicator (tree_column, FALSE);
2842       pspp_sheet_view_column_set_clickable (tree_column, FALSE);
2843       g_object_notify (G_OBJECT (tree_column), "sort-column-id");
2844       return;
2845     }
2846
2847   pspp_sheet_view_column_set_clickable (tree_column, TRUE);
2848
2849   if (! tree_column->sort_clicked_signal)
2850     tree_column->sort_clicked_signal = g_signal_connect (tree_column,
2851                                                          "clicked",
2852                                                          G_CALLBACK (pspp_sheet_view_column_sort),
2853                                                          NULL);
2854
2855   pspp_sheet_view_column_setup_sort_column_id_callback (tree_column);
2856   g_object_notify (G_OBJECT (tree_column), "sort-column-id");
2857 }
2858
2859 /**
2860  * pspp_sheet_view_column_get_sort_column_id:
2861  * @tree_column: a #PsppSheetViewColumn
2862  *
2863  * Gets the logical @sort_column_id that the model sorts on when this
2864  * column is selected for sorting.
2865  * See pspp_sheet_view_column_set_sort_column_id().
2866  *
2867  * Return value: the current @sort_column_id for this column, or -1 if
2868  *               this column can't be used for sorting.
2869  **/
2870 gint
2871 pspp_sheet_view_column_get_sort_column_id (PsppSheetViewColumn *tree_column)
2872 {
2873   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
2874
2875   return tree_column->sort_column_id;
2876 }
2877
2878 /**
2879  * pspp_sheet_view_column_set_sort_indicator:
2880  * @tree_column: a #PsppSheetViewColumn
2881  * @setting: %TRUE to display an indicator that the column is sorted
2882  *
2883  * Call this function with a @setting of %TRUE to display an arrow in
2884  * the header button indicating the column is sorted. Call
2885  * pspp_sheet_view_column_set_sort_order() to change the direction of
2886  * the arrow.
2887  * 
2888  **/
2889 void
2890 pspp_sheet_view_column_set_sort_indicator (PsppSheetViewColumn     *tree_column,
2891                                          gboolean               setting)
2892 {
2893   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2894
2895   setting = setting != FALSE;
2896
2897   if (setting == tree_column->show_sort_indicator)
2898     return;
2899
2900   tree_column->show_sort_indicator = setting;
2901   pspp_sheet_view_column_update_button (tree_column);
2902   g_object_notify (G_OBJECT (tree_column), "sort-indicator");
2903 }
2904
2905 /**
2906  * pspp_sheet_view_column_get_sort_indicator:
2907  * @tree_column: a #PsppSheetViewColumn
2908  * 
2909  * Gets the value set by pspp_sheet_view_column_set_sort_indicator().
2910  * 
2911  * Return value: whether the sort indicator arrow is displayed
2912  **/
2913 gboolean
2914 pspp_sheet_view_column_get_sort_indicator  (PsppSheetViewColumn     *tree_column)
2915 {
2916   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2917
2918   return tree_column->show_sort_indicator;
2919 }
2920
2921 /**
2922  * pspp_sheet_view_column_set_sort_order:
2923  * @tree_column: a #PsppSheetViewColumn
2924  * @order: sort order that the sort indicator should indicate
2925  *
2926  * Changes the appearance of the sort indicator. 
2927  * 
2928  * This <emphasis>does not</emphasis> actually sort the model.  Use
2929  * pspp_sheet_view_column_set_sort_column_id() if you want automatic sorting
2930  * support.  This function is primarily for custom sorting behavior, and should
2931  * be used in conjunction with gtk_tree_sortable_set_sort_column() to do
2932  * that. For custom models, the mechanism will vary. 
2933  * 
2934  * The sort indicator changes direction to indicate normal sort or reverse sort.
2935  * Note that you must have the sort indicator enabled to see anything when 
2936  * calling this function; see pspp_sheet_view_column_set_sort_indicator().
2937  **/
2938 void
2939 pspp_sheet_view_column_set_sort_order      (PsppSheetViewColumn     *tree_column,
2940                                           GtkSortType            order)
2941 {
2942   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2943
2944   if (order == tree_column->sort_order)
2945     return;
2946
2947   tree_column->sort_order = order;
2948   pspp_sheet_view_column_update_button (tree_column);
2949   g_object_notify (G_OBJECT (tree_column), "sort-order");
2950 }
2951
2952 /**
2953  * pspp_sheet_view_column_get_sort_order:
2954  * @tree_column: a #PsppSheetViewColumn
2955  * 
2956  * Gets the value set by pspp_sheet_view_column_set_sort_order().
2957  * 
2958  * Return value: the sort order the sort indicator is indicating
2959  **/
2960 GtkSortType
2961 pspp_sheet_view_column_get_sort_order      (PsppSheetViewColumn     *tree_column)
2962 {
2963   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
2964
2965   return tree_column->sort_order;
2966 }
2967
2968 /**
2969  * pspp_sheet_view_column_cell_set_cell_data:
2970  * @tree_column: A #PsppSheetViewColumn.
2971  * @tree_model: The #GtkTreeModel to to get the cell renderers attributes from.
2972  * @iter: The #GtkTreeIter to to get the cell renderer's attributes from.
2973  * 
2974  * Sets the cell renderer based on the @tree_model and @iter.  That is, for
2975  * every attribute mapping in @tree_column, it will get a value from the set
2976  * column on the @iter, and use that value to set the attribute on the cell
2977  * renderer.  This is used primarily by the #PsppSheetView.
2978  **/
2979 void
2980 pspp_sheet_view_column_cell_set_cell_data (PsppSheetViewColumn *tree_column,
2981                                          GtkTreeModel      *tree_model,
2982                                          GtkTreeIter       *iter)
2983 {
2984   GSList *list;
2985   GValue value = { 0, };
2986   GList *cell_list;
2987
2988   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2989
2990   if (tree_model == NULL)
2991     return;
2992
2993   for (cell_list = tree_column->cell_list; cell_list; cell_list = cell_list->next)
2994     {
2995       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) cell_list->data;
2996       GObject *cell = (GObject *) info->cell;
2997
2998       list = info->attributes;
2999
3000       g_object_freeze_notify (cell);
3001
3002       while (list && list->next)
3003         {
3004           gtk_tree_model_get_value (tree_model, iter,
3005                                     GPOINTER_TO_INT (list->next->data),
3006                                     &value);
3007           g_object_set_property (cell, (gchar *) list->data, &value);
3008           g_value_unset (&value);
3009           list = list->next->next;
3010         }
3011
3012       if (info->func)
3013         (* info->func) (tree_column, info->cell, tree_model, iter, info->func_data);
3014       g_object_thaw_notify (G_OBJECT (info->cell));
3015     }
3016
3017 }
3018
3019 /**
3020  * pspp_sheet_view_column_cell_get_size:
3021  * @tree_column: A #PsppSheetViewColumn.
3022  * @cell_area: (allow-none): The area a cell in the column will be allocated, or %NULL
3023  * @x_offset: (allow-none): location to return x offset of a cell relative to @cell_area, or %NULL
3024  * @y_offset: (allow-none): location to return y offset of a cell relative to @cell_area, or %NULL
3025  * @width: (allow-none): location to return width needed to render a cell, or %NULL
3026  * @height: (allow-none): location to return height needed to render a cell, or %NULL
3027  * 
3028  * Obtains the width and height needed to render the column.  This is used
3029  * primarily by the #PsppSheetView.
3030  **/
3031 void
3032 pspp_sheet_view_column_cell_get_size (PsppSheetViewColumn  *tree_column,
3033                                     const GdkRectangle *cell_area,
3034                                     gint               *x_offset,
3035                                     gint               *y_offset,
3036                                     gint               *width,
3037                                     gint               *height)
3038 {
3039   GList *list;
3040   gboolean first_cell = TRUE;
3041   gint focus_line_width;
3042
3043   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
3044
3045   if (height)
3046     * height = 0;
3047   if (width)
3048     * width = 0;
3049
3050   gtk_widget_style_get (tree_column->tree_view, "focus-line-width", &focus_line_width, NULL);
3051   
3052   for (list = tree_column->cell_list; list; list = list->next)
3053     {
3054       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
3055       gboolean visible;
3056       gint new_height = 0;
3057       gint new_width = 0;
3058       g_object_get (info->cell, "visible", &visible, NULL);
3059
3060       if (visible == FALSE)
3061         continue;
3062
3063       if (first_cell == FALSE && width)
3064         *width += tree_column->spacing;
3065
3066       gtk_cell_renderer_get_size (info->cell,
3067                                   tree_column->tree_view,
3068                                   cell_area,
3069                                   x_offset,
3070                                   y_offset,
3071                                   &new_width,
3072                                   &new_height);
3073
3074       if (height)
3075         * height = MAX (*height, new_height + focus_line_width * 2);
3076       info->requested_width = MAX (info->requested_width, new_width + focus_line_width * 2);
3077       if (width)
3078         * width += info->requested_width;
3079       first_cell = FALSE;
3080     }
3081 }
3082
3083 /* rendering, event handling and rendering focus are somewhat complicated, and
3084  * quite a bit of code.  Rather than duplicate them, we put them together to
3085  * keep the code in one place.
3086  *
3087  * To better understand what's going on, check out
3088  * docs/tree-column-sizing.png
3089  */
3090 enum {
3091   CELL_ACTION_RENDER,
3092   CELL_ACTION_FOCUS,
3093   CELL_ACTION_EVENT
3094 };
3095
3096 static gboolean
3097 pspp_sheet_view_column_cell_process_action (PsppSheetViewColumn  *tree_column,
3098                                           GdkWindow          *window,
3099                                           const GdkRectangle *background_area,
3100                                           const GdkRectangle *cell_area,
3101                                           guint               flags,
3102                                           gint                action,
3103                                           const GdkRectangle *expose_area,     /* RENDER */
3104                                           GdkRectangle       *focus_rectangle, /* FOCUS  */
3105                                           GtkCellEditable   **editable_widget, /* EVENT  */
3106                                           GdkEvent           *event,           /* EVENT  */
3107                                           gchar              *path_string)     /* EVENT  */
3108 {
3109   GList *list;
3110   GdkRectangle real_cell_area;
3111   GdkRectangle real_background_area;
3112   GdkRectangle real_expose_area = *cell_area;
3113   gint depth = 0;
3114   gint expand_cell_count = 0;
3115   gint full_requested_width = 0;
3116   gint extra_space;
3117   gint min_x, min_y, max_x, max_y;
3118   gint focus_line_width;
3119   gint special_cells;
3120   gint horizontal_separator;
3121   gboolean cursor_row = FALSE;
3122   gboolean first_cell = TRUE;
3123   gboolean rtl;
3124   /* If we have rtl text, we need to transform our areas */
3125   GdkRectangle rtl_cell_area;
3126   GdkRectangle rtl_background_area;
3127
3128   min_x = G_MAXINT;
3129   min_y = G_MAXINT;
3130   max_x = 0;
3131   max_y = 0;
3132
3133   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_column->tree_view)) == GTK_TEXT_DIR_RTL);
3134   special_cells = _pspp_sheet_view_column_count_special_cells (tree_column);
3135
3136   if (special_cells > 1 && action == CELL_ACTION_FOCUS)
3137     {
3138       PsppSheetViewColumnCellInfo *info = NULL;
3139       gboolean found_has_focus = FALSE;
3140
3141       /* one should have focus */
3142       for (list = tree_column->cell_list; list; list = list->next)
3143         {
3144           info = list->data;
3145           if (info && info->has_focus)
3146             {
3147               found_has_focus = TRUE;
3148               break;
3149             }
3150         }
3151
3152       if (!found_has_focus)
3153         {
3154           /* give the first one focus */
3155           info = pspp_sheet_view_column_cell_first (tree_column)->data;
3156           info->has_focus = TRUE;
3157         }
3158     }
3159
3160   cursor_row = flags & GTK_CELL_RENDERER_FOCUSED;
3161
3162   gtk_widget_style_get (GTK_WIDGET (tree_column->tree_view),
3163                         "focus-line-width", &focus_line_width,
3164                         "horizontal-separator", &horizontal_separator,
3165                         NULL);
3166
3167   real_cell_area = *cell_area;
3168   real_background_area = *background_area;
3169
3170
3171   real_cell_area.x += focus_line_width;
3172   real_cell_area.y += focus_line_width;
3173   real_cell_area.height -= 2 * focus_line_width;
3174
3175   if (rtl)
3176     depth = real_background_area.width - real_cell_area.width;
3177   else
3178     depth = real_cell_area.x - real_background_area.x;
3179
3180   /* Find out how much extra space we have to allocate */
3181   for (list = tree_column->cell_list; list; list = list->next)
3182     {
3183       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *)list->data;
3184
3185       if (! gtk_cell_renderer_get_visible (info->cell))
3186         continue;
3187
3188       if (info->expand == TRUE)
3189         expand_cell_count ++;
3190       full_requested_width += info->requested_width;
3191
3192       if (!first_cell)
3193         full_requested_width += tree_column->spacing;
3194
3195       first_cell = FALSE;
3196     }
3197
3198   extra_space = cell_area->width - full_requested_width;
3199   if (extra_space < 0)
3200     extra_space = 0;
3201   else if (extra_space > 0 && expand_cell_count > 0)
3202     extra_space /= expand_cell_count;
3203
3204   /* iterate list for GTK_PACK_START cells */
3205   for (list = tree_column->cell_list; list; list = list->next)
3206     {
3207       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
3208
3209       if (info->pack == GTK_PACK_END)
3210         continue;
3211
3212       if (! gtk_cell_renderer_get_visible (info->cell))
3213         continue;
3214
3215       if ((info->has_focus || special_cells == 1) && cursor_row)
3216         flags |= GTK_CELL_RENDERER_FOCUSED;
3217       else
3218         flags &= ~GTK_CELL_RENDERER_FOCUSED;
3219
3220       info->real_width = info->requested_width + (info->expand?extra_space:0);
3221
3222       /* We constrain ourselves to only the width available */
3223       if (real_cell_area.x - focus_line_width + info->real_width > cell_area->x + cell_area->width)
3224         {
3225           info->real_width = cell_area->x + cell_area->width - real_cell_area.x;
3226         }   
3227
3228       if (real_cell_area.x > cell_area->x + cell_area->width)
3229         break;
3230
3231       real_cell_area.width = info->real_width;
3232       real_cell_area.width -= 2 * focus_line_width;
3233
3234       if (list->next)
3235         {
3236           real_background_area.width = info->real_width + depth;
3237         }
3238       else
3239         {
3240           /* fill the rest of background for the last cell */
3241           real_background_area.width = background_area->x + background_area->width - real_background_area.x;
3242         }
3243
3244       rtl_cell_area = real_cell_area;
3245       rtl_background_area = real_background_area;
3246       
3247       if (rtl)
3248         {
3249           rtl_cell_area.x = cell_area->x + cell_area->width - (real_cell_area.x - cell_area->x) - real_cell_area.width;
3250           rtl_background_area.x = background_area->x + background_area->width - (real_background_area.x - background_area->x) - real_background_area.width;
3251         }
3252
3253       /* RENDER */
3254       if (action == CELL_ACTION_RENDER)
3255         {
3256           gtk_cell_renderer_render (info->cell,
3257                                     window,
3258                                     tree_column->tree_view,
3259                                     &rtl_background_area,
3260                                     &rtl_cell_area,
3261                                     &real_expose_area, 
3262                                     flags);
3263         }
3264       /* FOCUS */
3265       else if (action == CELL_ACTION_FOCUS)
3266         {
3267           gint x_offset, y_offset, width, height;
3268
3269           gtk_cell_renderer_get_size (info->cell,
3270                                       tree_column->tree_view,
3271                                       &rtl_cell_area,
3272                                       &x_offset, &y_offset,
3273                                       &width, &height);
3274
3275           if (special_cells > 1)
3276             {
3277               if (info->has_focus)
3278                 {
3279                   min_x = rtl_cell_area.x + x_offset;
3280                   max_x = min_x + width;
3281                   min_y = rtl_cell_area.y + y_offset;
3282                   max_y = min_y + height;
3283                 }
3284             }
3285           else
3286             {
3287               if (min_x > (rtl_cell_area.x + x_offset))
3288                 min_x = rtl_cell_area.x + x_offset;
3289               if (max_x < rtl_cell_area.x + x_offset + width)
3290                 max_x = rtl_cell_area.x + x_offset + width;
3291               if (min_y > (rtl_cell_area.y + y_offset))
3292                 min_y = rtl_cell_area.y + y_offset;
3293               if (max_y < rtl_cell_area.y + y_offset + height)
3294                 max_y = rtl_cell_area.y + y_offset + height;
3295             }
3296         }
3297       /* EVENT */
3298       else if (action == CELL_ACTION_EVENT)
3299         {
3300           gboolean try_event = FALSE;
3301
3302           if (event)
3303             {
3304               if (special_cells == 1)
3305                 {
3306                   /* only 1 activatable cell -> whole column can activate */
3307                   if (cell_area->x <= ((GdkEventButton *)event)->x &&
3308                       cell_area->x + cell_area->width > ((GdkEventButton *)event)->x)
3309                     try_event = TRUE;
3310                 }
3311               else if (rtl_cell_area.x <= ((GdkEventButton *)event)->x &&
3312                   rtl_cell_area.x + rtl_cell_area.width > ((GdkEventButton *)event)->x)
3313                   /* only activate cell if the user clicked on an individual
3314                    * cell
3315                    */
3316                 try_event = TRUE;
3317             }
3318           else if (special_cells > 1 && info->has_focus)
3319             try_event = TRUE;
3320           else if (special_cells == 1)
3321             try_event = TRUE;
3322
3323           if (try_event)
3324             {
3325               gboolean visible, mode;
3326
3327               g_object_get (info->cell,
3328                             "visible", &visible,
3329                             "mode", &mode,
3330                             NULL);
3331               if (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
3332                 {
3333                   if (gtk_cell_renderer_activate (info->cell,
3334                                                   event,
3335                                                   tree_column->tree_view,
3336                                                   path_string,
3337                                                   &rtl_background_area,
3338                                                   &rtl_cell_area,
3339                                                   flags))
3340                     {
3341                       flags &= ~GTK_CELL_RENDERER_FOCUSED;
3342                       return TRUE;
3343                     }
3344                 }
3345               else if (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE)
3346                 {
3347                   *editable_widget =
3348                     gtk_cell_renderer_start_editing (info->cell,
3349                                                      event,
3350                                                      tree_column->tree_view,
3351                                                      path_string,
3352                                                      &rtl_background_area,
3353                                                      &rtl_cell_area,
3354                                                      flags);
3355
3356                   if (*editable_widget != NULL)
3357                     {
3358                       g_return_val_if_fail (GTK_IS_CELL_EDITABLE (*editable_widget), FALSE);
3359                       info->in_editing_mode = TRUE;
3360                       pspp_sheet_view_column_focus_cell (tree_column, info->cell);
3361                       
3362                       flags &= ~GTK_CELL_RENDERER_FOCUSED;
3363
3364                       return TRUE;
3365                     }
3366                 }
3367             }
3368         }
3369
3370       flags &= ~GTK_CELL_RENDERER_FOCUSED;
3371
3372       real_cell_area.x += (real_cell_area.width + 2 * focus_line_width + tree_column->spacing);
3373       real_background_area.x += real_background_area.width + tree_column->spacing;
3374
3375       /* Only needed for first cell */
3376       depth = 0;
3377     }
3378
3379   /* iterate list for PACK_END cells */
3380   for (list = g_list_last (tree_column->cell_list); list; list = list->prev)
3381     {
3382       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
3383
3384       if (info->pack == GTK_PACK_START)
3385         continue;
3386
3387       if (! gtk_cell_renderer_get_visible (info->cell))
3388         continue;
3389
3390       if ((info->has_focus || special_cells == 1) && cursor_row)
3391         flags |= GTK_CELL_RENDERER_FOCUSED;
3392       else
3393         flags &= ~GTK_CELL_RENDERER_FOCUSED;
3394
3395       info->real_width = info->requested_width + (info->expand?extra_space:0);
3396
3397       /* We constrain ourselves to only the width available */
3398       if (real_cell_area.x - focus_line_width + info->real_width > cell_area->x + cell_area->width)
3399         {
3400           info->real_width = cell_area->x + cell_area->width - real_cell_area.x;
3401         }   
3402
3403       if (real_cell_area.x > cell_area->x + cell_area->width)
3404         break;
3405
3406       real_cell_area.width = info->real_width;
3407       real_cell_area.width -= 2 * focus_line_width;
3408       real_background_area.width = info->real_width + depth;
3409
3410       rtl_cell_area = real_cell_area;
3411       rtl_background_area = real_background_area;
3412       if (rtl)
3413         {
3414           rtl_cell_area.x = cell_area->x + cell_area->width - (real_cell_area.x - cell_area->x) - real_cell_area.width;
3415           rtl_background_area.x = background_area->x + background_area->width - (real_background_area.x - background_area->x) - real_background_area.width;
3416         }
3417
3418       /* RENDER */
3419       if (action == CELL_ACTION_RENDER)
3420         {
3421           gtk_cell_renderer_render (info->cell,
3422                                     window,
3423                                     tree_column->tree_view,
3424                                     &rtl_background_area,
3425                                     &rtl_cell_area,
3426                                     &real_expose_area,
3427                                     flags);
3428         }
3429       /* FOCUS */
3430       else if (action == CELL_ACTION_FOCUS)
3431         {
3432           gint x_offset, y_offset, width, height;
3433
3434           gtk_cell_renderer_get_size (info->cell,
3435                                       tree_column->tree_view,
3436                                       &rtl_cell_area,
3437                                       &x_offset, &y_offset,
3438                                       &width, &height);
3439
3440           if (special_cells > 1)
3441             {
3442               if (info->has_focus)
3443                 {
3444                   min_x = rtl_cell_area.x + x_offset;
3445                   max_x = min_x + width;
3446                   min_y = rtl_cell_area.y + y_offset;
3447                   max_y = min_y + height;
3448                 }
3449             }
3450           else
3451             {
3452               if (min_x > (rtl_cell_area.x + x_offset))
3453                 min_x = rtl_cell_area.x + x_offset;
3454               if (max_x < rtl_cell_area.x + x_offset + width)
3455                 max_x = rtl_cell_area.x + x_offset + width;
3456               if (min_y > (rtl_cell_area.y + y_offset))
3457                 min_y = rtl_cell_area.y + y_offset;
3458               if (max_y < rtl_cell_area.y + y_offset + height)
3459                 max_y = rtl_cell_area.y + y_offset + height;
3460             }
3461         }
3462       /* EVENT */
3463       else if (action == CELL_ACTION_EVENT)
3464         {
3465           gboolean try_event = FALSE;
3466
3467           if (event)
3468             {
3469               if (special_cells == 1)
3470                 {
3471                   /* only 1 activatable cell -> whole column can activate */
3472                   if (cell_area->x <= ((GdkEventButton *)event)->x &&
3473                       cell_area->x + cell_area->width > ((GdkEventButton *)event)->x)
3474                     try_event = TRUE;
3475                 }
3476               else if (rtl_cell_area.x <= ((GdkEventButton *)event)->x &&
3477                   rtl_cell_area.x + rtl_cell_area.width > ((GdkEventButton *)event)->x)
3478                 /* only activate cell if the user clicked on an individual
3479                  * cell
3480                  */
3481                 try_event = TRUE;
3482             }
3483           else if (special_cells > 1 && info->has_focus)
3484             try_event = TRUE;
3485           else if (special_cells == 1)
3486             try_event = TRUE;
3487
3488           if (try_event)
3489             {
3490               gboolean visible, mode;
3491
3492               g_object_get (info->cell,
3493                             "visible", &visible,
3494                             "mode", &mode,
3495                             NULL);
3496               if (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
3497                 {
3498                   if (gtk_cell_renderer_activate (info->cell,
3499                                                   event,
3500                                                   tree_column->tree_view,
3501                                                   path_string,
3502                                                   &rtl_background_area,
3503                                                   &rtl_cell_area,
3504                                                   flags))
3505                     {
3506                       flags &= ~GTK_CELL_RENDERER_FOCUSED;
3507                       return TRUE;
3508                     }
3509                 }
3510               else if (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE)
3511                 {
3512                   *editable_widget =
3513                     gtk_cell_renderer_start_editing (info->cell,
3514                                                      event,
3515                                                      tree_column->tree_view,
3516                                                      path_string,
3517                                                      &rtl_background_area,
3518                                                      &rtl_cell_area,
3519                                                      flags);
3520
3521                   if (*editable_widget != NULL)
3522                     {
3523                       g_return_val_if_fail (GTK_IS_CELL_EDITABLE (*editable_widget), FALSE);
3524                       info->in_editing_mode = TRUE;
3525                       pspp_sheet_view_column_focus_cell (tree_column, info->cell);
3526
3527                       flags &= ~GTK_CELL_RENDERER_FOCUSED;
3528                       return TRUE;
3529                     }
3530                 }
3531             }
3532         }
3533
3534       flags &= ~GTK_CELL_RENDERER_FOCUSED;
3535
3536       real_cell_area.x += (real_cell_area.width + 2 * focus_line_width + tree_column->spacing);
3537       real_background_area.x += (real_background_area.width + tree_column->spacing);
3538
3539       /* Only needed for first cell */
3540       depth = 0;
3541     }
3542
3543   /* fill focus_rectangle when required */
3544   if (action == CELL_ACTION_FOCUS)
3545     {
3546       if (min_x >= max_x || min_y >= max_y)
3547         {
3548           *focus_rectangle = *cell_area;
3549           /* don't change the focus_rectangle, just draw it nicely inside
3550            * the cell area */
3551         }
3552       else
3553         {
3554           focus_rectangle->x = min_x - focus_line_width;
3555           focus_rectangle->y = min_y - focus_line_width;
3556           focus_rectangle->width = (max_x - min_x) + 2 * focus_line_width;
3557           focus_rectangle->height = (max_y - min_y) + 2 * focus_line_width;
3558         }
3559     }
3560
3561   return FALSE;
3562 }
3563
3564 /**
3565  * pspp_sheet_view_column_cell_render:
3566  * @tree_column: A #PsppSheetViewColumn.
3567  * @window: a #GdkDrawable to draw to
3568  * @background_area: entire cell area (including tree expanders and maybe padding on the sides)
3569  * @cell_area: area normally rendered by a cell renderer
3570  * @expose_area: area that actually needs updating
3571  * @flags: flags that affect rendering
3572  * 
3573  * Renders the cell contained by #tree_column. This is used primarily by the
3574  * #PsppSheetView.
3575  **/
3576 void
3577 _pspp_sheet_view_column_cell_render (PsppSheetViewColumn  *tree_column,
3578                                    GdkWindow          *window,
3579                                    const GdkRectangle *background_area,
3580                                    const GdkRectangle *cell_area,
3581                                    const GdkRectangle *expose_area,
3582                                    guint               flags)
3583 {
3584   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
3585   g_return_if_fail (background_area != NULL);
3586   g_return_if_fail (cell_area != NULL);
3587   g_return_if_fail (expose_area != NULL);
3588
3589   pspp_sheet_view_column_cell_process_action (tree_column,
3590                                             window,
3591                                             background_area,
3592                                             cell_area,
3593                                             flags,
3594                                             CELL_ACTION_RENDER,
3595                                             expose_area,
3596                                             NULL, NULL, NULL, NULL);
3597 }
3598
3599 gboolean
3600 _pspp_sheet_view_column_cell_event (PsppSheetViewColumn  *tree_column,
3601                                   GtkCellEditable   **editable_widget,
3602                                   GdkEvent           *event,
3603                                   gchar              *path_string,
3604                                   const GdkRectangle *background_area,
3605                                   const GdkRectangle *cell_area,
3606                                   guint               flags)
3607 {
3608   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
3609
3610   return pspp_sheet_view_column_cell_process_action (tree_column,
3611                                                    NULL,
3612                                                    background_area,
3613                                                    cell_area,
3614                                                    flags,
3615                                                    CELL_ACTION_EVENT,
3616                                                    NULL, NULL,
3617                                                    editable_widget,
3618                                                    event,
3619                                                    path_string);
3620 }
3621
3622 void
3623 _pspp_sheet_view_column_get_focus_area (PsppSheetViewColumn  *tree_column,
3624                                       const GdkRectangle *background_area,
3625                                       const GdkRectangle *cell_area,
3626                                       GdkRectangle       *focus_area)
3627 {
3628   pspp_sheet_view_column_cell_process_action (tree_column,
3629                                             NULL,
3630                                             background_area,
3631                                             cell_area,
3632                                             0,
3633                                             CELL_ACTION_FOCUS,
3634                                             NULL,
3635                                             focus_area,
3636                                             NULL, NULL, NULL);
3637 }
3638
3639
3640 /* cell list manipulation */
3641 static GList *
3642 pspp_sheet_view_column_cell_first (PsppSheetViewColumn *tree_column)
3643 {
3644   GList *list = tree_column->cell_list;
3645
3646   /* first GTK_PACK_START cell we find */
3647   for ( ; list; list = list->next)
3648     {
3649       PsppSheetViewColumnCellInfo *info = list->data;
3650       if (info->pack == GTK_PACK_START)
3651         return list;
3652     }
3653
3654   /* hmm, else the *last* GTK_PACK_END cell */
3655   list = g_list_last (tree_column->cell_list);
3656
3657   for ( ; list; list = list->prev)
3658     {
3659       PsppSheetViewColumnCellInfo *info = list->data;
3660       if (info->pack == GTK_PACK_END)
3661         return list;
3662     }
3663
3664   return NULL;
3665 }
3666
3667 static GList *
3668 pspp_sheet_view_column_cell_last (PsppSheetViewColumn *tree_column)
3669 {
3670   GList *list = tree_column->cell_list;
3671
3672   /* *first* GTK_PACK_END cell we find */
3673   for ( ; list ; list = list->next)
3674     {
3675       PsppSheetViewColumnCellInfo *info = list->data;
3676       if (info->pack == GTK_PACK_END)
3677         return list;
3678     }
3679
3680   /* hmm, else the last GTK_PACK_START cell */
3681   list = g_list_last (tree_column->cell_list);
3682
3683   for ( ; list; list = list->prev)
3684     {
3685       PsppSheetViewColumnCellInfo *info = list->data;
3686       if (info->pack == GTK_PACK_START)
3687         return list;
3688     }
3689
3690   return NULL;
3691 }
3692
3693 static GList *
3694 pspp_sheet_view_column_cell_next (PsppSheetViewColumn *tree_column,
3695                                 GList             *current)
3696 {
3697   GList *list;
3698   PsppSheetViewColumnCellInfo *info = current->data;
3699
3700   if (info->pack == GTK_PACK_START)
3701     {
3702       for (list = current->next; list; list = list->next)
3703         {
3704           PsppSheetViewColumnCellInfo *inf = list->data;
3705           if (inf->pack == GTK_PACK_START)
3706             return list;
3707         }
3708
3709       /* out of GTK_PACK_START cells, get *last* GTK_PACK_END one */
3710       list = g_list_last (tree_column->cell_list);
3711       for (; list; list = list->prev)
3712         {
3713           PsppSheetViewColumnCellInfo *inf = list->data;
3714           if (inf->pack == GTK_PACK_END)
3715             return list;
3716         }
3717     }
3718
3719   for (list = current->prev; list; list = list->prev)
3720     {
3721       PsppSheetViewColumnCellInfo *inf = list->data;
3722       if (inf->pack == GTK_PACK_END)
3723         return list;
3724     }
3725
3726   return NULL;
3727 }
3728
3729 static GList *
3730 pspp_sheet_view_column_cell_prev (PsppSheetViewColumn *tree_column,
3731                                 GList             *current)
3732 {
3733   GList *list;
3734   PsppSheetViewColumnCellInfo *info = current->data;
3735
3736   if (info->pack == GTK_PACK_END)
3737     {
3738       for (list = current->next; list; list = list->next)
3739         {
3740           PsppSheetViewColumnCellInfo *inf = list->data;
3741           if (inf->pack == GTK_PACK_END)
3742             return list;
3743         }
3744
3745       /* out of GTK_PACK_END, get last GTK_PACK_START one */
3746       list = g_list_last (tree_column->cell_list);
3747       for ( ; list; list = list->prev)
3748         {
3749           PsppSheetViewColumnCellInfo *inf = list->data;
3750           if (inf->pack == GTK_PACK_START)
3751             return list;
3752         }
3753     }
3754
3755   for (list = current->prev; list; list = list->prev)
3756     {
3757       PsppSheetViewColumnCellInfo *inf = list->data;
3758       if (inf->pack == GTK_PACK_START)
3759         return list;
3760     }
3761
3762   return NULL;
3763 }
3764
3765 gboolean
3766 _pspp_sheet_view_column_cell_focus (PsppSheetViewColumn *tree_column,
3767                                   gint               direction,
3768                                   gboolean           left,
3769                                   gboolean           right)
3770 {
3771   gint count;
3772   gboolean rtl;
3773
3774   count = _pspp_sheet_view_column_count_special_cells (tree_column);
3775   rtl = gtk_widget_get_direction (GTK_WIDGET (tree_column->tree_view)) == GTK_TEXT_DIR_RTL;
3776
3777   /* if we are the current focus column and have multiple editable cells,
3778    * try to select the next one, else move the focus to the next column
3779    */
3780   if (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->focus_column == tree_column)
3781     {
3782       if (count > 1)
3783         {
3784           GList *next, *prev;
3785           GList *list = tree_column->cell_list;
3786           PsppSheetViewColumnCellInfo *info = NULL;
3787
3788           /* find current focussed cell */
3789           for ( ; list; list = list->next)
3790             {
3791               info = list->data;
3792               if (info->has_focus)
3793                 break;
3794             }
3795
3796           /* not a focussed cell in the focus column? */
3797           if (!list || !info || !info->has_focus)
3798             return FALSE;
3799
3800           if (rtl)
3801             {
3802               prev = pspp_sheet_view_column_cell_next (tree_column, list);
3803               next = pspp_sheet_view_column_cell_prev (tree_column, list);
3804             }
3805           else
3806             {
3807               next = pspp_sheet_view_column_cell_next (tree_column, list);
3808               prev = pspp_sheet_view_column_cell_prev (tree_column, list);
3809             }
3810
3811           info->has_focus = FALSE;
3812           if (direction > 0 && next)
3813             {
3814               info = next->data;
3815               info->has_focus = TRUE;
3816               return TRUE;
3817             }
3818           else if (direction > 0 && !next && !right)
3819             {
3820               /* keep focus on last cell */
3821               if (rtl)
3822                 info = pspp_sheet_view_column_cell_first (tree_column)->data;
3823               else
3824                 info = pspp_sheet_view_column_cell_last (tree_column)->data;
3825
3826               info->has_focus = TRUE;
3827               return TRUE;
3828             }
3829           else if (direction < 0 && prev)
3830             {
3831               info = prev->data;
3832               info->has_focus = TRUE;
3833               return TRUE;
3834             }
3835           else if (direction < 0 && !prev && !left)
3836             {
3837               /* keep focus on first cell */
3838               if (rtl)
3839                 info = pspp_sheet_view_column_cell_last (tree_column)->data;
3840               else
3841                 info = pspp_sheet_view_column_cell_first (tree_column)->data;
3842
3843               info->has_focus = TRUE;
3844               return TRUE;
3845             }
3846         }
3847       return FALSE;
3848     }
3849
3850   /* we get focus, if we have multiple editable cells, give the correct one
3851    * focus
3852    */
3853   if (count > 1)
3854     {
3855       GList *list = tree_column->cell_list;
3856
3857       /* clear focus first */
3858       for ( ; list ; list = list->next)
3859         {
3860           PsppSheetViewColumnCellInfo *info = list->data;
3861           if (info->has_focus)
3862             info->has_focus = FALSE;
3863         }
3864
3865       list = NULL;
3866       if (rtl)
3867         {
3868           if (direction > 0)
3869             list = pspp_sheet_view_column_cell_last (tree_column);
3870           else if (direction < 0)
3871             list = pspp_sheet_view_column_cell_first (tree_column);
3872         }
3873       else
3874         {
3875           if (direction > 0)
3876             list = pspp_sheet_view_column_cell_first (tree_column);
3877           else if (direction < 0)
3878             list = pspp_sheet_view_column_cell_last (tree_column);
3879         }
3880
3881       if (list)
3882         ((PsppSheetViewColumnCellInfo *) list->data)->has_focus = TRUE;
3883     }
3884
3885   return TRUE;
3886 }
3887
3888 void
3889 _pspp_sheet_view_column_cell_draw_focus (PsppSheetViewColumn  *tree_column,
3890                                        GdkWindow          *window,
3891                                        const GdkRectangle *background_area,
3892                                        const GdkRectangle *cell_area,
3893                                        const GdkRectangle *expose_area,
3894                                        guint               flags)
3895 {
3896   gint focus_line_width;
3897   GtkStateType cell_state;
3898   
3899   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
3900   gtk_widget_style_get (GTK_WIDGET (tree_column->tree_view),
3901                         "focus-line-width", &focus_line_width, NULL);
3902   if (tree_column->editable_widget)
3903     {
3904       /* This function is only called on the editable row when editing.
3905        */
3906 #if 0
3907       gtk_paint_focus (tree_column->tree_view->style,
3908                        window,
3909                        gtk_widget_get_state (tree_column->tree_view),
3910                        NULL,
3911                        tree_column->tree_view,
3912                        "treeview",
3913                        cell_area->x - focus_line_width,
3914                        cell_area->y - focus_line_width,
3915                        cell_area->width + 2 * focus_line_width,
3916                        cell_area->height + 2 * focus_line_width);
3917 #endif      
3918     }
3919   else
3920     {
3921       GdkRectangle focus_rectangle;
3922       pspp_sheet_view_column_cell_process_action (tree_column,
3923                                                 window,
3924                                                 background_area,
3925                                                 cell_area,
3926                                                 flags,
3927                                                 CELL_ACTION_FOCUS,
3928                                                 expose_area,
3929                                                 &focus_rectangle,
3930                                                 NULL, NULL, NULL);
3931
3932       cell_state = flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
3933               (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
3934               (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL));
3935       gtk_paint_focus (gtk_widget_get_style (GTK_WIDGET (tree_column->tree_view)),
3936                        window,
3937                        cell_state,
3938                        cell_area,
3939                        tree_column->tree_view,
3940                        "treeview",
3941                        focus_rectangle.x,
3942                        focus_rectangle.y,
3943                        focus_rectangle.width,
3944                        focus_rectangle.height);
3945     }
3946 }
3947
3948 /**
3949  * pspp_sheet_view_column_cell_is_visible:
3950  * @tree_column: A #PsppSheetViewColumn
3951  * 
3952  * Returns %TRUE if any of the cells packed into the @tree_column are visible.
3953  * For this to be meaningful, you must first initialize the cells with
3954  * pspp_sheet_view_column_cell_set_cell_data()
3955  * 
3956  * Return value: %TRUE, if any of the cells packed into the @tree_column are currently visible
3957  **/
3958 gboolean
3959 pspp_sheet_view_column_cell_is_visible (PsppSheetViewColumn *tree_column)
3960 {
3961   GList *list;
3962
3963   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
3964
3965   for (list = tree_column->cell_list; list; list = list->next)
3966     {
3967       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
3968
3969       if (gtk_cell_renderer_get_visible (info->cell))
3970         return TRUE;
3971     }
3972
3973   return FALSE;
3974 }
3975
3976 /**
3977  * pspp_sheet_view_column_focus_cell:
3978  * @tree_column: A #PsppSheetViewColumn
3979  * @cell: A #GtkCellRenderer
3980  *
3981  * Sets the current keyboard focus to be at @cell, if the column contains
3982  * 2 or more editable and activatable cells.
3983  *
3984  * Since: 2.2
3985  **/
3986 void
3987 pspp_sheet_view_column_focus_cell (PsppSheetViewColumn *tree_column,
3988                                  GtkCellRenderer   *cell)
3989 {
3990   GList *list;
3991   gboolean found_cell = FALSE;
3992
3993   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
3994   g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
3995
3996   if (_pspp_sheet_view_column_count_special_cells (tree_column) < 2)
3997     return;
3998
3999   for (list = tree_column->cell_list; list; list = list->next)
4000     {
4001       PsppSheetViewColumnCellInfo *info = list->data;
4002
4003       if (info->cell == cell)
4004         {
4005           info->has_focus = TRUE;
4006           found_cell = TRUE;
4007           break;
4008         }
4009     }
4010
4011   if (found_cell)
4012     {
4013       for (list = tree_column->cell_list; list; list = list->next)
4014         {
4015           PsppSheetViewColumnCellInfo *info = list->data;
4016
4017           if (info->cell != cell)
4018             info->has_focus = FALSE;
4019         }
4020
4021       /* FIXME: redraw? */
4022     }
4023 }
4024
4025 void
4026 _pspp_sheet_view_column_cell_set_dirty (PsppSheetViewColumn *tree_column)
4027 {
4028   GList *list;
4029
4030   for (list = tree_column->cell_list; list; list = list->next)
4031     {
4032       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
4033
4034       info->requested_width = 0;
4035     }
4036   tree_column->dirty = TRUE;
4037   tree_column->requested_width = -1;
4038   tree_column->width = 0;
4039
4040   if (tree_column->tree_view &&
4041       gtk_widget_get_realized (tree_column->tree_view))
4042     {
4043       _pspp_sheet_view_install_mark_rows_col_dirty (PSPP_SHEET_VIEW (tree_column->tree_view));
4044       gtk_widget_queue_resize (tree_column->tree_view);
4045     }
4046 }
4047
4048 void
4049 _pspp_sheet_view_column_start_editing (PsppSheetViewColumn *tree_column,
4050                                      GtkCellEditable   *cell_editable)
4051 {
4052   g_return_if_fail (tree_column->editable_widget == NULL);
4053
4054   tree_column->editable_widget = cell_editable;
4055 }
4056
4057 void
4058 _pspp_sheet_view_column_stop_editing (PsppSheetViewColumn *tree_column)
4059 {
4060   GList *list;
4061
4062   g_return_if_fail (tree_column->editable_widget != NULL);
4063
4064   tree_column->editable_widget = NULL;
4065   for (list = tree_column->cell_list; list; list = list->next)
4066     ((PsppSheetViewColumnCellInfo *)list->data)->in_editing_mode = FALSE;
4067 }
4068
4069 void
4070 _pspp_sheet_view_column_get_neighbor_sizes (PsppSheetViewColumn *column,
4071                                           GtkCellRenderer   *cell,
4072                                           gint              *left,
4073                                           gint              *right)
4074 {
4075   GList *list;
4076   PsppSheetViewColumnCellInfo *info;
4077   gint l, r;
4078   gboolean rtl;
4079
4080   l = r = 0;
4081
4082   list = pspp_sheet_view_column_cell_first (column);  
4083
4084   while (list)
4085     {
4086       info = (PsppSheetViewColumnCellInfo *)list->data;
4087       
4088       list = pspp_sheet_view_column_cell_next (column, list);
4089
4090       if (info->cell == cell)
4091         break;
4092       
4093       if (gtk_cell_renderer_get_visible (info->cell))
4094         l += info->real_width + column->spacing;
4095     }
4096
4097   while (list)
4098     {
4099       info = (PsppSheetViewColumnCellInfo *)list->data;
4100       
4101       list = pspp_sheet_view_column_cell_next (column, list);
4102
4103       if (gtk_cell_renderer_get_visible (info->cell))
4104         r += info->real_width + column->spacing;
4105     }
4106
4107   rtl = (gtk_widget_get_direction (GTK_WIDGET (column->tree_view)) == GTK_TEXT_DIR_RTL);
4108   if (left)
4109     *left = rtl ? r : l;
4110
4111   if (right)
4112     *right = rtl ? l : r;
4113 }
4114
4115 /**
4116  * pspp_sheet_view_column_cell_get_position:
4117  * @tree_column: a #PsppSheetViewColumn
4118  * @cell_renderer: a #GtkCellRenderer
4119  * @start_pos: return location for the horizontal position of @cell within
4120  *            @tree_column, may be %NULL
4121  * @width: return location for the width of @cell, may be %NULL
4122  *
4123  * Obtains the horizontal position and size of a cell in a column. If the
4124  * cell is not found in the column, @start_pos and @width are not changed and
4125  * %FALSE is returned.
4126  * 
4127  * Return value: %TRUE if @cell belongs to @tree_column.
4128  */
4129 gboolean
4130 pspp_sheet_view_column_cell_get_position (PsppSheetViewColumn *tree_column,
4131                                         GtkCellRenderer   *cell_renderer,
4132                                         gint              *start_pos,
4133                                         gint              *width)
4134 {
4135   GList *list;
4136   gint current_x = 0;
4137   gboolean found_cell = FALSE;
4138   PsppSheetViewColumnCellInfo *cellinfo = NULL;
4139
4140   list = pspp_sheet_view_column_cell_first (tree_column);
4141   for (; list; list = pspp_sheet_view_column_cell_next (tree_column, list))
4142     {
4143       cellinfo = list->data;
4144       if (cellinfo->cell == cell_renderer)
4145         {
4146           found_cell = TRUE;
4147           break;
4148         }
4149
4150       if (gtk_cell_renderer_get_visible (cellinfo->cell))
4151         current_x += cellinfo->real_width;
4152     }
4153
4154   if (found_cell)
4155     {
4156       if (start_pos)
4157         *start_pos = current_x;
4158       if (width)
4159         *width = cellinfo->real_width;
4160     }
4161
4162   return found_cell;
4163 }
4164
4165 /**
4166  * pspp_sheet_view_column_queue_resize:
4167  * @tree_column: A #PsppSheetViewColumn
4168  *
4169  * Flags the column, and the cell renderers added to this column, to have
4170  * their sizes renegotiated.
4171  *
4172  * Since: 2.8
4173  **/
4174 void
4175 pspp_sheet_view_column_queue_resize (PsppSheetViewColumn *tree_column)
4176 {
4177   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
4178
4179   if (tree_column->tree_view)
4180     _pspp_sheet_view_column_cell_set_dirty (tree_column);
4181 }
4182
4183 /**
4184  * pspp_sheet_view_column_get_tree_view:
4185  * @tree_column: A #PsppSheetViewColumn
4186  *
4187  * Returns the #PsppSheetView wherein @tree_column has been inserted.  If
4188  * @column is currently not inserted in any tree view, %NULL is
4189  * returned.
4190  *
4191  * Return value: The tree view wherein @column has been inserted if any,
4192  *               %NULL otherwise.
4193  *
4194  * Since: 2.12
4195  */
4196 GtkWidget *
4197 pspp_sheet_view_column_get_tree_view (PsppSheetViewColumn *tree_column)
4198 {
4199   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), NULL);
4200
4201   return tree_column->tree_view;
4202 }
4203
4204 typedef struct {
4205   GtkCellLayout   *cell_layout;
4206   GtkCellRenderer *renderer;
4207   gchar           *attr_name;
4208 } AttributesSubParserData;
4209
4210 static void
4211 attributes_start_element (GMarkupParseContext *context,
4212                           const gchar         *element_name,
4213                           const gchar        **names,
4214                           const gchar        **values,
4215                           gpointer             user_data,
4216                           GError             **error)
4217 {
4218   AttributesSubParserData *parser_data = (AttributesSubParserData*)user_data;
4219   guint i;
4220
4221   if (strcmp (element_name, "attribute") == 0)
4222     {
4223       for (i = 0; names[i]; i++)
4224         if (strcmp (names[i], "name") == 0)
4225           parser_data->attr_name = g_strdup (values[i]);
4226     }
4227   else if (strcmp (element_name, "attributes") == 0)
4228     return;
4229   else
4230     g_warning ("Unsupported tag for GtkCellLayout: %s\n", element_name);
4231 }
4232
4233 static void
4234 attributes_text_element (GMarkupParseContext *context,
4235                          const gchar         *text,
4236                          gsize                text_len,
4237                          gpointer             user_data,
4238                          GError             **error)
4239 {
4240   AttributesSubParserData *parser_data = (AttributesSubParserData*)user_data;
4241   glong l;
4242   gchar *endptr;
4243   gchar *string;
4244   
4245   if (!parser_data->attr_name)
4246     return;
4247
4248   errno = 0;
4249   string = g_strndup (text, text_len);
4250   l = strtol (string, &endptr, 0);
4251   if (errno || endptr == string)
4252     {
4253       g_set_error (error, 
4254                    GTK_BUILDER_ERROR,
4255                    GTK_BUILDER_ERROR_INVALID_VALUE,
4256                    "Could not parse integer `%s'",
4257                    string);
4258       g_free (string);
4259       return;
4260     }
4261   g_free (string);
4262
4263   gtk_cell_layout_add_attribute (parser_data->cell_layout,
4264                                  parser_data->renderer,
4265                                  parser_data->attr_name, l);
4266   g_free (parser_data->attr_name);
4267   parser_data->attr_name = NULL;
4268 }
4269
4270 static const GMarkupParser attributes_parser =
4271   {
4272     attributes_start_element,
4273     NULL,
4274     attributes_text_element,
4275   };
4276
4277 gboolean
4278 _gtk_cell_layout_buildable_custom_tag_start (GtkBuildable  *buildable,
4279                                              GtkBuilder    *builder,
4280                                              GObject       *child,
4281                                              const gchar   *tagname,
4282                                              GMarkupParser *parser,
4283                                              gpointer      *data)
4284 {
4285   AttributesSubParserData *parser_data;
4286
4287   if (!child)
4288     return FALSE;
4289
4290   if (strcmp (tagname, "attributes") == 0)
4291     {
4292       parser_data = g_slice_new0 (AttributesSubParserData);
4293       parser_data->cell_layout = GTK_CELL_LAYOUT (buildable);
4294       parser_data->renderer = GTK_CELL_RENDERER (child);
4295       parser_data->attr_name = NULL;
4296
4297       *parser = attributes_parser;
4298       *data = parser_data;
4299       return TRUE;
4300     }
4301
4302   return FALSE;
4303 }
4304
4305 void
4306 _gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable,
4307                                            GtkBuilder   *builder,
4308                                            GObject      *child,
4309                                            const gchar  *tagname,
4310                                            gpointer     *data)
4311 {
4312   AttributesSubParserData *parser_data;
4313
4314   parser_data = (AttributesSubParserData*)data;
4315   g_assert (!parser_data->attr_name);
4316   g_slice_free (AttributesSubParserData, parser_data);
4317 }
4318
4319 void
4320 _gtk_cell_layout_buildable_add_child (GtkBuildable      *buildable,
4321                                       GtkBuilder        *builder,
4322                                       GObject           *child,
4323                                       const gchar       *type)
4324 {
4325   GtkCellLayoutIface *iface;
4326   
4327   g_return_if_fail (GTK_IS_CELL_LAYOUT (buildable));
4328   g_return_if_fail (GTK_IS_CELL_RENDERER (child));
4329
4330   iface = GTK_CELL_LAYOUT_GET_IFACE (buildable);
4331   g_return_if_fail (iface->pack_start != NULL);
4332   iface->pack_start (GTK_CELL_LAYOUT (buildable), GTK_CELL_RENDERER (child), FALSE);
4333 }
4334
4335 void
4336 pspp_sheet_view_column_size_request (PsppSheetViewColumn       *tree_column,
4337                                      GtkRequisition            *request)
4338 {
4339   GtkWidget *base = GTK_WIDGET (tree_column->tree_view);
4340   GtkRequisition label_req;
4341   GtkRequisition align_req;
4342   GtkRequisition arrow_req;
4343   GtkRequisition hbox_req;
4344   GtkStyle **button_style;
4345
4346   if (tree_column->button)
4347     {
4348       gtk_widget_size_request (tree_column->button, request);
4349       return;
4350     }
4351
4352   facade_label_get_size_request (0, 0, base, tree_column->title, &label_req);
4353   facade_alignment_get_size_request (0, 0, 0, 0, 0, &label_req, &align_req);
4354   facade_arrow_get_size_request (0, 0, &arrow_req);
4355
4356   facade_hbox_get_base_size_request (0, 2, 2, &hbox_req);
4357   facade_hbox_add_child_size_request (0, &arrow_req, 0, &hbox_req);
4358   facade_hbox_add_child_size_request (0, &align_req, 0, &hbox_req);
4359
4360   button_style = &PSPP_SHEET_VIEW (tree_column->tree_view)->priv->button_style;
4361   if (*button_style == NULL)
4362     {
4363       *button_style = facade_get_style (base, GTK_TYPE_BUTTON, 0);
4364       g_object_ref (*button_style);
4365     }
4366   facade_button_get_size_request (0, base, *button_style, &hbox_req, request);
4367 }
4368
4369 void
4370 pspp_sheet_view_column_size_allocate (PsppSheetViewColumn       *tree_column,
4371                                       GtkAllocation             *allocation)
4372 {
4373   tree_column->allocation = *allocation;
4374   if (tree_column->button)
4375     gtk_widget_size_allocate (tree_column->button, allocation);
4376 }
4377
4378 gboolean
4379 pspp_sheet_view_column_can_focus (PsppSheetViewColumn       *tree_column)
4380 {
4381   return tree_column->reorderable || tree_column->clickable;
4382 }
4383
4384 void
4385 pspp_sheet_view_column_set_need_button (PsppSheetViewColumn       *tree_column,
4386                                         gboolean                   need_button)
4387 {
4388   if (tree_column->need_button != need_button)
4389     {
4390       tree_column->need_button = need_button;
4391       pspp_sheet_view_column_update_button (tree_column);
4392       _pspp_sheet_view_column_realize_button (tree_column);
4393     }
4394 }