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