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