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