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