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