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