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