pspp-sheet-view: Optimize by making column header widgets lazy.
[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?TRUE:FALSE;
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   if (tree_column->reorderable == (reorderable?TRUE:FALSE))
2549     return;
2550
2551   tree_column->reorderable = (reorderable?TRUE:FALSE);
2552   pspp_sheet_view_column_update_button (tree_column);
2553   g_object_notify (G_OBJECT (tree_column), "reorderable");
2554 }
2555
2556 /**
2557  * pspp_sheet_view_column_get_reorderable:
2558  * @tree_column: A #PsppSheetViewColumn
2559  * 
2560  * Returns %TRUE if the @tree_column can be reordered by the user.
2561  * 
2562  * Return value: %TRUE if the @tree_column can be reordered by the user.
2563  **/
2564 gboolean
2565 pspp_sheet_view_column_get_reorderable (PsppSheetViewColumn *tree_column)
2566 {
2567   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2568
2569   return tree_column->reorderable;
2570 }
2571
2572 /**
2573  * pspp_sheet_view_column_set_quick_edit:
2574  * @tree_column: A #PsppSheetViewColumn
2575  * @quick_edit: If true, editing starts upon the first click in the column.  If
2576  * false, the first click selects the column and a second click is needed to
2577  * begin editing.  This has no effect on cells that are not editable.
2578  **/
2579 void
2580 pspp_sheet_view_column_set_quick_edit (PsppSheetViewColumn *tree_column,
2581                                       gboolean           quick_edit)
2582 {
2583   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2584
2585   quick_edit = !!quick_edit;
2586   if (tree_column->quick_edit != quick_edit)
2587     {
2588       tree_column->quick_edit = (quick_edit?TRUE:FALSE);
2589       g_object_notify (G_OBJECT (tree_column), "quick-edit");
2590     }
2591 }
2592
2593 /**
2594  * pspp_sheet_view_column_get_quick_edit:
2595  * @tree_column: A #PsppSheetViewColumn
2596  *
2597  * Returns %TRUE if editing starts upon the first click in the column.  Returns
2598  * %FALSE, the first click selects the column and a second click is needed to
2599  * begin editing.  This is not meaningful for cells that are not editable.
2600  *
2601  * Return value: %TRUE if editing starts upon the first click.
2602  **/
2603 gboolean
2604 pspp_sheet_view_column_get_quick_edit (PsppSheetViewColumn *tree_column)
2605 {
2606   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2607
2608   return tree_column->quick_edit;
2609 }
2610
2611
2612 /**
2613  * pspp_sheet_view_column_set_selected:
2614  * @tree_column: A #PsppSheetViewColumn
2615  * @selected: If true, the column is selected as part of a rectangular
2616  * selection.
2617  **/
2618 void
2619 pspp_sheet_view_column_set_selected (PsppSheetViewColumn *tree_column,
2620                                       gboolean           selected)
2621 {
2622   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2623
2624   selected = !!selected;
2625   if (tree_column->selected != selected)
2626     {
2627       PsppSheetSelection *selection;
2628       PsppSheetView *sheet_view;
2629
2630       if (tree_column->tree_view != NULL)
2631         gtk_widget_queue_draw (GTK_WIDGET (tree_column->tree_view));
2632       tree_column->selected = (selected?TRUE:FALSE);
2633       g_object_notify (G_OBJECT (tree_column), "selected");
2634
2635       sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view (
2636                                       tree_column));
2637       selection = pspp_sheet_view_get_selection (sheet_view);
2638       _pspp_sheet_selection_emit_changed (selection);
2639     }
2640 }
2641
2642 /**
2643  * pspp_sheet_view_column_get_selected:
2644  * @tree_column: A #PsppSheetViewColumn
2645  *
2646  * Returns %TRUE if the column is selected as part of a rectangular
2647  * selection.
2648  *
2649  * Return value: %TRUE if the column is selected as part of a rectangular
2650  * selection.
2651  **/
2652 gboolean
2653 pspp_sheet_view_column_get_selected (PsppSheetViewColumn *tree_column)
2654 {
2655   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2656
2657   return tree_column->selected;
2658 }
2659
2660 /**
2661  * pspp_sheet_view_column_set_selectable:
2662  * @tree_column: A #PsppSheetViewColumn
2663  * @selectable: If true, the column may be selected as part of a rectangular
2664  * selection.
2665  **/
2666 void
2667 pspp_sheet_view_column_set_selectable (PsppSheetViewColumn *tree_column,
2668                                       gboolean           selectable)
2669 {
2670   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2671
2672   selectable = !!selectable;
2673   if (tree_column->selectable != selectable)
2674     {
2675       if (tree_column->tree_view != NULL)
2676         gtk_widget_queue_draw (GTK_WIDGET (tree_column->tree_view));
2677       tree_column->selectable = (selectable?TRUE:FALSE);
2678       g_object_notify (G_OBJECT (tree_column), "selectable");
2679     }
2680 }
2681
2682 /**
2683  * pspp_sheet_view_column_get_selectable:
2684  * @tree_column: A #PsppSheetViewColumn
2685  *
2686  * Returns %TRUE if the column may be selected as part of a rectangular
2687  * selection.
2688  *
2689  * Return value: %TRUE if the column may be selected as part of a rectangular
2690  * selection.
2691  **/
2692 gboolean
2693 pspp_sheet_view_column_get_selectable (PsppSheetViewColumn *tree_column)
2694 {
2695   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2696
2697   return tree_column->selectable;
2698 }
2699
2700
2701 /**
2702  * pspp_sheet_view_column_set_row_head:
2703  * @tree_column: A #PsppSheetViewColumn
2704  * @row_head: If true, the column is a "row head", analogous to a column head.
2705  * See the description of the row-head property for more information.
2706  **/
2707 void
2708 pspp_sheet_view_column_set_row_head (PsppSheetViewColumn *tree_column,
2709                                       gboolean           row_head)
2710 {
2711   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2712
2713   row_head = !!row_head;
2714   if (tree_column->row_head != row_head)
2715     {
2716       tree_column->row_head = (row_head?TRUE:FALSE);
2717       g_object_notify (G_OBJECT (tree_column), "row_head");
2718     }
2719 }
2720
2721 /**
2722  * pspp_sheet_view_column_get_row_head:
2723  * @tree_column: A #PsppSheetViewColumn
2724  *
2725  * Returns %TRUE if the column is a row head.
2726  *
2727  * Return value: %TRUE if the column is a row head.
2728  **/
2729 gboolean
2730 pspp_sheet_view_column_get_row_head (PsppSheetViewColumn *tree_column)
2731 {
2732   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2733
2734   return tree_column->row_head;
2735 }
2736
2737
2738 /**
2739  * pspp_sheet_view_column_set_sort_column_id:
2740  * @tree_column: a #PsppSheetViewColumn
2741  * @sort_column_id: The @sort_column_id of the model to sort on.
2742  *
2743  * Sets the logical @sort_column_id that this column sorts on when this column 
2744  * is selected for sorting.  Doing so makes the column header clickable.
2745  **/
2746 void
2747 pspp_sheet_view_column_set_sort_column_id (PsppSheetViewColumn *tree_column,
2748                                          gint               sort_column_id)
2749 {
2750   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2751   g_return_if_fail (sort_column_id >= -1);
2752
2753   if (tree_column->sort_column_id == sort_column_id)
2754     return;
2755
2756   tree_column->sort_column_id = sort_column_id;
2757
2758   /* Handle unsetting the id */
2759   if (sort_column_id == -1)
2760     {
2761       GtkTreeModel *model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (tree_column->tree_view));
2762
2763       if (tree_column->sort_clicked_signal)
2764         {
2765           g_signal_handler_disconnect (tree_column, tree_column->sort_clicked_signal);
2766           tree_column->sort_clicked_signal = 0;
2767         }
2768
2769       if (tree_column->sort_column_changed_signal)
2770         {
2771           g_signal_handler_disconnect (model, tree_column->sort_column_changed_signal);
2772           tree_column->sort_column_changed_signal = 0;
2773         }
2774
2775       pspp_sheet_view_column_set_sort_order (tree_column, GTK_SORT_ASCENDING);
2776       pspp_sheet_view_column_set_sort_indicator (tree_column, FALSE);
2777       pspp_sheet_view_column_set_clickable (tree_column, FALSE);
2778       g_object_notify (G_OBJECT (tree_column), "sort-column-id");
2779       return;
2780     }
2781
2782   pspp_sheet_view_column_set_clickable (tree_column, TRUE);
2783
2784   if (! tree_column->sort_clicked_signal)
2785     tree_column->sort_clicked_signal = g_signal_connect (tree_column,
2786                                                          "clicked",
2787                                                          G_CALLBACK (pspp_sheet_view_column_sort),
2788                                                          NULL);
2789
2790   pspp_sheet_view_column_setup_sort_column_id_callback (tree_column);
2791   g_object_notify (G_OBJECT (tree_column), "sort-column-id");
2792 }
2793
2794 /**
2795  * pspp_sheet_view_column_get_sort_column_id:
2796  * @tree_column: a #PsppSheetViewColumn
2797  *
2798  * Gets the logical @sort_column_id that the model sorts on when this
2799  * column is selected for sorting.
2800  * See pspp_sheet_view_column_set_sort_column_id().
2801  *
2802  * Return value: the current @sort_column_id for this column, or -1 if
2803  *               this column can't be used for sorting.
2804  **/
2805 gint
2806 pspp_sheet_view_column_get_sort_column_id (PsppSheetViewColumn *tree_column)
2807 {
2808   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
2809
2810   return tree_column->sort_column_id;
2811 }
2812
2813 /**
2814  * pspp_sheet_view_column_set_sort_indicator:
2815  * @tree_column: a #PsppSheetViewColumn
2816  * @setting: %TRUE to display an indicator that the column is sorted
2817  *
2818  * Call this function with a @setting of %TRUE to display an arrow in
2819  * the header button indicating the column is sorted. Call
2820  * pspp_sheet_view_column_set_sort_order() to change the direction of
2821  * the arrow.
2822  * 
2823  **/
2824 void
2825 pspp_sheet_view_column_set_sort_indicator (PsppSheetViewColumn     *tree_column,
2826                                          gboolean               setting)
2827 {
2828   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2829
2830   setting = setting != FALSE;
2831
2832   if (setting == tree_column->show_sort_indicator)
2833     return;
2834
2835   tree_column->show_sort_indicator = setting;
2836   pspp_sheet_view_column_update_button (tree_column);
2837   g_object_notify (G_OBJECT (tree_column), "sort-indicator");
2838 }
2839
2840 /**
2841  * pspp_sheet_view_column_get_sort_indicator:
2842  * @tree_column: a #PsppSheetViewColumn
2843  * 
2844  * Gets the value set by pspp_sheet_view_column_set_sort_indicator().
2845  * 
2846  * Return value: whether the sort indicator arrow is displayed
2847  **/
2848 gboolean
2849 pspp_sheet_view_column_get_sort_indicator  (PsppSheetViewColumn     *tree_column)
2850 {
2851   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2852
2853   return tree_column->show_sort_indicator;
2854 }
2855
2856 /**
2857  * pspp_sheet_view_column_set_sort_order:
2858  * @tree_column: a #PsppSheetViewColumn
2859  * @order: sort order that the sort indicator should indicate
2860  *
2861  * Changes the appearance of the sort indicator. 
2862  * 
2863  * This <emphasis>does not</emphasis> actually sort the model.  Use
2864  * pspp_sheet_view_column_set_sort_column_id() if you want automatic sorting
2865  * support.  This function is primarily for custom sorting behavior, and should
2866  * be used in conjunction with gtk_tree_sortable_set_sort_column() to do
2867  * that. For custom models, the mechanism will vary. 
2868  * 
2869  * The sort indicator changes direction to indicate normal sort or reverse sort.
2870  * Note that you must have the sort indicator enabled to see anything when 
2871  * calling this function; see pspp_sheet_view_column_set_sort_indicator().
2872  **/
2873 void
2874 pspp_sheet_view_column_set_sort_order      (PsppSheetViewColumn     *tree_column,
2875                                           GtkSortType            order)
2876 {
2877   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2878
2879   if (order == tree_column->sort_order)
2880     return;
2881
2882   tree_column->sort_order = order;
2883   pspp_sheet_view_column_update_button (tree_column);
2884   g_object_notify (G_OBJECT (tree_column), "sort-order");
2885 }
2886
2887 /**
2888  * pspp_sheet_view_column_get_sort_order:
2889  * @tree_column: a #PsppSheetViewColumn
2890  * 
2891  * Gets the value set by pspp_sheet_view_column_set_sort_order().
2892  * 
2893  * Return value: the sort order the sort indicator is indicating
2894  **/
2895 GtkSortType
2896 pspp_sheet_view_column_get_sort_order      (PsppSheetViewColumn     *tree_column)
2897 {
2898   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
2899
2900   return tree_column->sort_order;
2901 }
2902
2903 /**
2904  * pspp_sheet_view_column_cell_set_cell_data:
2905  * @tree_column: A #PsppSheetViewColumn.
2906  * @tree_model: The #GtkTreeModel to to get the cell renderers attributes from.
2907  * @iter: The #GtkTreeIter to to get the cell renderer's attributes from.
2908  * 
2909  * Sets the cell renderer based on the @tree_model and @iter.  That is, for
2910  * every attribute mapping in @tree_column, it will get a value from the set
2911  * column on the @iter, and use that value to set the attribute on the cell
2912  * renderer.  This is used primarily by the #PsppSheetView.
2913  **/
2914 void
2915 pspp_sheet_view_column_cell_set_cell_data (PsppSheetViewColumn *tree_column,
2916                                          GtkTreeModel      *tree_model,
2917                                          GtkTreeIter       *iter)
2918 {
2919   GSList *list;
2920   GValue value = { 0, };
2921   GList *cell_list;
2922
2923   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2924
2925   if (tree_model == NULL)
2926     return;
2927
2928   for (cell_list = tree_column->cell_list; cell_list; cell_list = cell_list->next)
2929     {
2930       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) cell_list->data;
2931       GObject *cell = (GObject *) info->cell;
2932
2933       list = info->attributes;
2934
2935       g_object_freeze_notify (cell);
2936
2937       while (list && list->next)
2938         {
2939           gtk_tree_model_get_value (tree_model, iter,
2940                                     GPOINTER_TO_INT (list->next->data),
2941                                     &value);
2942           g_object_set_property (cell, (gchar *) list->data, &value);
2943           g_value_unset (&value);
2944           list = list->next->next;
2945         }
2946
2947       if (info->func)
2948         (* info->func) (tree_column, info->cell, tree_model, iter, info->func_data);
2949       g_object_thaw_notify (G_OBJECT (info->cell));
2950     }
2951
2952 }
2953
2954 /**
2955  * pspp_sheet_view_column_cell_get_size:
2956  * @tree_column: A #PsppSheetViewColumn.
2957  * @cell_area: (allow-none): The area a cell in the column will be allocated, or %NULL
2958  * @x_offset: (allow-none): location to return x offset of a cell relative to @cell_area, or %NULL
2959  * @y_offset: (allow-none): location to return y offset of a cell relative to @cell_area, or %NULL
2960  * @width: (allow-none): location to return width needed to render a cell, or %NULL
2961  * @height: (allow-none): location to return height needed to render a cell, or %NULL
2962  * 
2963  * Obtains the width and height needed to render the column.  This is used
2964  * primarily by the #PsppSheetView.
2965  **/
2966 void
2967 pspp_sheet_view_column_cell_get_size (PsppSheetViewColumn  *tree_column,
2968                                     const GdkRectangle *cell_area,
2969                                     gint               *x_offset,
2970                                     gint               *y_offset,
2971                                     gint               *width,
2972                                     gint               *height)
2973 {
2974   GList *list;
2975   gboolean first_cell = TRUE;
2976   gint focus_line_width;
2977
2978   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2979
2980   if (height)
2981     * height = 0;
2982   if (width)
2983     * width = 0;
2984
2985   gtk_widget_style_get (tree_column->tree_view, "focus-line-width", &focus_line_width, NULL);
2986   
2987   for (list = tree_column->cell_list; list; list = list->next)
2988     {
2989       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
2990       gboolean visible;
2991       gint new_height = 0;
2992       gint new_width = 0;
2993       g_object_get (info->cell, "visible", &visible, NULL);
2994
2995       if (visible == FALSE)
2996         continue;
2997
2998       if (first_cell == FALSE && width)
2999         *width += tree_column->spacing;
3000
3001       gtk_cell_renderer_get_size (info->cell,
3002                                   tree_column->tree_view,
3003                                   cell_area,
3004                                   x_offset,
3005                                   y_offset,
3006                                   &new_width,
3007                                   &new_height);
3008
3009       if (height)
3010         * height = MAX (*height, new_height + focus_line_width * 2);
3011       info->requested_width = MAX (info->requested_width, new_width + focus_line_width * 2);
3012       if (width)
3013         * width += info->requested_width;
3014       first_cell = FALSE;
3015     }
3016 }
3017
3018 /* rendering, event handling and rendering focus are somewhat complicated, and
3019  * quite a bit of code.  Rather than duplicate them, we put them together to
3020  * keep the code in one place.
3021  *
3022  * To better understand what's going on, check out
3023  * docs/tree-column-sizing.png
3024  */
3025 enum {
3026   CELL_ACTION_RENDER,
3027   CELL_ACTION_FOCUS,
3028   CELL_ACTION_EVENT
3029 };
3030
3031 static gboolean
3032 pspp_sheet_view_column_cell_process_action (PsppSheetViewColumn  *tree_column,
3033                                           GdkWindow          *window,
3034                                           const GdkRectangle *background_area,
3035                                           const GdkRectangle *cell_area,
3036                                           guint               flags,
3037                                           gint                action,
3038                                           const GdkRectangle *expose_area,     /* RENDER */
3039                                           GdkRectangle       *focus_rectangle, /* FOCUS  */
3040                                           GtkCellEditable   **editable_widget, /* EVENT  */
3041                                           GdkEvent           *event,           /* EVENT  */
3042                                           gchar              *path_string)     /* EVENT  */
3043 {
3044   GList *list;
3045   GdkRectangle real_cell_area;
3046   GdkRectangle real_background_area;
3047   GdkRectangle real_expose_area = *cell_area;
3048   gint depth = 0;
3049   gint expand_cell_count = 0;
3050   gint full_requested_width = 0;
3051   gint extra_space;
3052   gint min_x, min_y, max_x, max_y;
3053   gint focus_line_width;
3054   gint special_cells;
3055   gint horizontal_separator;
3056   gboolean cursor_row = FALSE;
3057   gboolean first_cell = TRUE;
3058   gboolean rtl;
3059   /* If we have rtl text, we need to transform our areas */
3060   GdkRectangle rtl_cell_area;
3061   GdkRectangle rtl_background_area;
3062
3063   min_x = G_MAXINT;
3064   min_y = G_MAXINT;
3065   max_x = 0;
3066   max_y = 0;
3067
3068   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_column->tree_view)) == GTK_TEXT_DIR_RTL);
3069   special_cells = _pspp_sheet_view_column_count_special_cells (tree_column);
3070
3071   if (special_cells > 1 && action == CELL_ACTION_FOCUS)
3072     {
3073       PsppSheetViewColumnCellInfo *info = NULL;
3074       gboolean found_has_focus = FALSE;
3075
3076       /* one should have focus */
3077       for (list = tree_column->cell_list; list; list = list->next)
3078         {
3079           info = list->data;
3080           if (info && info->has_focus)
3081             {
3082               found_has_focus = TRUE;
3083               break;
3084             }
3085         }
3086
3087       if (!found_has_focus)
3088         {
3089           /* give the first one focus */
3090           info = pspp_sheet_view_column_cell_first (tree_column)->data;
3091           info->has_focus = TRUE;
3092         }
3093     }
3094
3095   cursor_row = flags & GTK_CELL_RENDERER_FOCUSED;
3096
3097   gtk_widget_style_get (GTK_WIDGET (tree_column->tree_view),
3098                         "focus-line-width", &focus_line_width,
3099                         "horizontal-separator", &horizontal_separator,
3100                         NULL);
3101
3102   real_cell_area = *cell_area;
3103   real_background_area = *background_area;
3104
3105
3106   real_cell_area.x += focus_line_width;
3107   real_cell_area.y += focus_line_width;
3108   real_cell_area.height -= 2 * focus_line_width;
3109
3110   if (rtl)
3111     depth = real_background_area.width - real_cell_area.width;
3112   else
3113     depth = real_cell_area.x - real_background_area.x;
3114
3115   /* Find out how much extra space we have to allocate */
3116   for (list = tree_column->cell_list; list; list = list->next)
3117     {
3118       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *)list->data;
3119
3120       if (! info->cell->visible)
3121         continue;
3122
3123       if (info->expand == TRUE)
3124         expand_cell_count ++;
3125       full_requested_width += info->requested_width;
3126
3127       if (!first_cell)
3128         full_requested_width += tree_column->spacing;
3129
3130       first_cell = FALSE;
3131     }
3132
3133   extra_space = cell_area->width - full_requested_width;
3134   if (extra_space < 0)
3135     extra_space = 0;
3136   else if (extra_space > 0 && expand_cell_count > 0)
3137     extra_space /= expand_cell_count;
3138
3139   /* iterate list for GTK_PACK_START cells */
3140   for (list = tree_column->cell_list; list; list = list->next)
3141     {
3142       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
3143
3144       if (info->pack == GTK_PACK_END)
3145         continue;
3146
3147       if (! info->cell->visible)
3148         continue;
3149
3150       if ((info->has_focus || special_cells == 1) && cursor_row)
3151         flags |= GTK_CELL_RENDERER_FOCUSED;
3152       else
3153         flags &= ~GTK_CELL_RENDERER_FOCUSED;
3154
3155       info->real_width = info->requested_width + (info->expand?extra_space:0);
3156
3157       /* We constrain ourselves to only the width available */
3158       if (real_cell_area.x - focus_line_width + info->real_width > cell_area->x + cell_area->width)
3159         {
3160           info->real_width = cell_area->x + cell_area->width - real_cell_area.x;
3161         }   
3162
3163       if (real_cell_area.x > cell_area->x + cell_area->width)
3164         break;
3165
3166       real_cell_area.width = info->real_width;
3167       real_cell_area.width -= 2 * focus_line_width;
3168
3169       if (list->next)
3170         {
3171           real_background_area.width = info->real_width + depth;
3172         }
3173       else
3174         {
3175           /* fill the rest of background for the last cell */
3176           real_background_area.width = background_area->x + background_area->width - real_background_area.x;
3177         }
3178
3179       rtl_cell_area = real_cell_area;
3180       rtl_background_area = real_background_area;
3181       
3182       if (rtl)
3183         {
3184           rtl_cell_area.x = cell_area->x + cell_area->width - (real_cell_area.x - cell_area->x) - real_cell_area.width;
3185           rtl_background_area.x = background_area->x + background_area->width - (real_background_area.x - background_area->x) - real_background_area.width;
3186         }
3187
3188       /* RENDER */
3189       if (action == CELL_ACTION_RENDER)
3190         {
3191           gtk_cell_renderer_render (info->cell,
3192                                     window,
3193                                     tree_column->tree_view,
3194                                     &rtl_background_area,
3195                                     &rtl_cell_area,
3196                                     &real_expose_area, 
3197                                     flags);
3198         }
3199       /* FOCUS */
3200       else if (action == CELL_ACTION_FOCUS)
3201         {
3202           gint x_offset, y_offset, width, height;
3203
3204           gtk_cell_renderer_get_size (info->cell,
3205                                       tree_column->tree_view,
3206                                       &rtl_cell_area,
3207                                       &x_offset, &y_offset,
3208                                       &width, &height);
3209
3210           if (special_cells > 1)
3211             {
3212               if (info->has_focus)
3213                 {
3214                   min_x = rtl_cell_area.x + x_offset;
3215                   max_x = min_x + width;
3216                   min_y = rtl_cell_area.y + y_offset;
3217                   max_y = min_y + height;
3218                 }
3219             }
3220           else
3221             {
3222               if (min_x > (rtl_cell_area.x + x_offset))
3223                 min_x = rtl_cell_area.x + x_offset;
3224               if (max_x < rtl_cell_area.x + x_offset + width)
3225                 max_x = rtl_cell_area.x + x_offset + width;
3226               if (min_y > (rtl_cell_area.y + y_offset))
3227                 min_y = rtl_cell_area.y + y_offset;
3228               if (max_y < rtl_cell_area.y + y_offset + height)
3229                 max_y = rtl_cell_area.y + y_offset + height;
3230             }
3231         }
3232       /* EVENT */
3233       else if (action == CELL_ACTION_EVENT)
3234         {
3235           gboolean try_event = FALSE;
3236
3237           if (event)
3238             {
3239               if (special_cells == 1)
3240                 {
3241                   /* only 1 activatable cell -> whole column can activate */
3242                   if (cell_area->x <= ((GdkEventButton *)event)->x &&
3243                       cell_area->x + cell_area->width > ((GdkEventButton *)event)->x)
3244                     try_event = TRUE;
3245                 }
3246               else if (rtl_cell_area.x <= ((GdkEventButton *)event)->x &&
3247                   rtl_cell_area.x + rtl_cell_area.width > ((GdkEventButton *)event)->x)
3248                   /* only activate cell if the user clicked on an individual
3249                    * cell
3250                    */
3251                 try_event = TRUE;
3252             }
3253           else if (special_cells > 1 && info->has_focus)
3254             try_event = TRUE;
3255           else if (special_cells == 1)
3256             try_event = TRUE;
3257
3258           if (try_event)
3259             {
3260               gboolean visible, mode;
3261
3262               g_object_get (info->cell,
3263                             "visible", &visible,
3264                             "mode", &mode,
3265                             NULL);
3266               if (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
3267                 {
3268                   if (gtk_cell_renderer_activate (info->cell,
3269                                                   event,
3270                                                   tree_column->tree_view,
3271                                                   path_string,
3272                                                   &rtl_background_area,
3273                                                   &rtl_cell_area,
3274                                                   flags))
3275                     {
3276                       flags &= ~GTK_CELL_RENDERER_FOCUSED;
3277                       return TRUE;
3278                     }
3279                 }
3280               else if (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE)
3281                 {
3282                   *editable_widget =
3283                     gtk_cell_renderer_start_editing (info->cell,
3284                                                      event,
3285                                                      tree_column->tree_view,
3286                                                      path_string,
3287                                                      &rtl_background_area,
3288                                                      &rtl_cell_area,
3289                                                      flags);
3290
3291                   if (*editable_widget != NULL)
3292                     {
3293                       g_return_val_if_fail (GTK_IS_CELL_EDITABLE (*editable_widget), FALSE);
3294                       info->in_editing_mode = TRUE;
3295                       pspp_sheet_view_column_focus_cell (tree_column, info->cell);
3296                       
3297                       flags &= ~GTK_CELL_RENDERER_FOCUSED;
3298
3299                       return TRUE;
3300                     }
3301                 }
3302             }
3303         }
3304
3305       flags &= ~GTK_CELL_RENDERER_FOCUSED;
3306
3307       real_cell_area.x += (real_cell_area.width + 2 * focus_line_width + tree_column->spacing);
3308       real_background_area.x += real_background_area.width + tree_column->spacing;
3309
3310       /* Only needed for first cell */
3311       depth = 0;
3312     }
3313
3314   /* iterate list for PACK_END cells */
3315   for (list = g_list_last (tree_column->cell_list); list; list = list->prev)
3316     {
3317       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
3318
3319       if (info->pack == GTK_PACK_START)
3320         continue;
3321
3322       if (! info->cell->visible)
3323         continue;
3324
3325       if ((info->has_focus || special_cells == 1) && cursor_row)
3326         flags |= GTK_CELL_RENDERER_FOCUSED;
3327       else
3328         flags &= ~GTK_CELL_RENDERER_FOCUSED;
3329
3330       info->real_width = info->requested_width + (info->expand?extra_space:0);
3331
3332       /* We constrain ourselves to only the width available */
3333       if (real_cell_area.x - focus_line_width + info->real_width > cell_area->x + cell_area->width)
3334         {
3335           info->real_width = cell_area->x + cell_area->width - real_cell_area.x;
3336         }   
3337
3338       if (real_cell_area.x > cell_area->x + cell_area->width)
3339         break;
3340
3341       real_cell_area.width = info->real_width;
3342       real_cell_area.width -= 2 * focus_line_width;
3343       real_background_area.width = info->real_width + depth;
3344
3345       rtl_cell_area = real_cell_area;
3346       rtl_background_area = real_background_area;
3347       if (rtl)
3348         {
3349           rtl_cell_area.x = cell_area->x + cell_area->width - (real_cell_area.x - cell_area->x) - real_cell_area.width;
3350           rtl_background_area.x = background_area->x + background_area->width - (real_background_area.x - background_area->x) - real_background_area.width;
3351         }
3352
3353       /* RENDER */
3354       if (action == CELL_ACTION_RENDER)
3355         {
3356           gtk_cell_renderer_render (info->cell,
3357                                     window,
3358                                     tree_column->tree_view,
3359                                     &rtl_background_area,
3360                                     &rtl_cell_area,
3361                                     &real_expose_area,
3362                                     flags);
3363         }
3364       /* FOCUS */
3365       else if (action == CELL_ACTION_FOCUS)
3366         {
3367           gint x_offset, y_offset, width, height;
3368
3369           gtk_cell_renderer_get_size (info->cell,
3370                                       tree_column->tree_view,
3371                                       &rtl_cell_area,
3372                                       &x_offset, &y_offset,
3373                                       &width, &height);
3374
3375           if (special_cells > 1)
3376             {
3377               if (info->has_focus)
3378                 {
3379                   min_x = rtl_cell_area.x + x_offset;
3380                   max_x = min_x + width;
3381                   min_y = rtl_cell_area.y + y_offset;
3382                   max_y = min_y + height;
3383                 }
3384             }
3385           else
3386             {
3387               if (min_x > (rtl_cell_area.x + x_offset))
3388                 min_x = rtl_cell_area.x + x_offset;
3389               if (max_x < rtl_cell_area.x + x_offset + width)
3390                 max_x = rtl_cell_area.x + x_offset + width;
3391               if (min_y > (rtl_cell_area.y + y_offset))
3392                 min_y = rtl_cell_area.y + y_offset;
3393               if (max_y < rtl_cell_area.y + y_offset + height)
3394                 max_y = rtl_cell_area.y + y_offset + height;
3395             }
3396         }
3397       /* EVENT */
3398       else if (action == CELL_ACTION_EVENT)
3399         {
3400           gboolean try_event = FALSE;
3401
3402           if (event)
3403             {
3404               if (special_cells == 1)
3405                 {
3406                   /* only 1 activatable cell -> whole column can activate */
3407                   if (cell_area->x <= ((GdkEventButton *)event)->x &&
3408                       cell_area->x + cell_area->width > ((GdkEventButton *)event)->x)
3409                     try_event = TRUE;
3410                 }
3411               else if (rtl_cell_area.x <= ((GdkEventButton *)event)->x &&
3412                   rtl_cell_area.x + rtl_cell_area.width > ((GdkEventButton *)event)->x)
3413                 /* only activate cell if the user clicked on an individual
3414                  * cell
3415                  */
3416                 try_event = TRUE;
3417             }
3418           else if (special_cells > 1 && info->has_focus)
3419             try_event = TRUE;
3420           else if (special_cells == 1)
3421             try_event = TRUE;
3422
3423           if (try_event)
3424             {
3425               gboolean visible, mode;
3426
3427               g_object_get (info->cell,
3428                             "visible", &visible,
3429                             "mode", &mode,
3430                             NULL);
3431               if (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
3432                 {
3433                   if (gtk_cell_renderer_activate (info->cell,
3434                                                   event,
3435                                                   tree_column->tree_view,
3436                                                   path_string,
3437                                                   &rtl_background_area,
3438                                                   &rtl_cell_area,
3439                                                   flags))
3440                     {
3441                       flags &= ~GTK_CELL_RENDERER_FOCUSED;
3442                       return TRUE;
3443                     }
3444                 }
3445               else if (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE)
3446                 {
3447                   *editable_widget =
3448                     gtk_cell_renderer_start_editing (info->cell,
3449                                                      event,
3450                                                      tree_column->tree_view,
3451                                                      path_string,
3452                                                      &rtl_background_area,
3453                                                      &rtl_cell_area,
3454                                                      flags);
3455
3456                   if (*editable_widget != NULL)
3457                     {
3458                       g_return_val_if_fail (GTK_IS_CELL_EDITABLE (*editable_widget), FALSE);
3459                       info->in_editing_mode = TRUE;
3460                       pspp_sheet_view_column_focus_cell (tree_column, info->cell);
3461
3462                       flags &= ~GTK_CELL_RENDERER_FOCUSED;
3463                       return TRUE;
3464                     }
3465                 }
3466             }
3467         }
3468
3469       flags &= ~GTK_CELL_RENDERER_FOCUSED;
3470
3471       real_cell_area.x += (real_cell_area.width + 2 * focus_line_width + tree_column->spacing);
3472       real_background_area.x += (real_background_area.width + tree_column->spacing);
3473
3474       /* Only needed for first cell */
3475       depth = 0;
3476     }
3477
3478   /* fill focus_rectangle when required */
3479   if (action == CELL_ACTION_FOCUS)
3480     {
3481       if (min_x >= max_x || min_y >= max_y)
3482         {
3483           *focus_rectangle = *cell_area;
3484           /* don't change the focus_rectangle, just draw it nicely inside
3485            * the cell area */
3486         }
3487       else
3488         {
3489           focus_rectangle->x = min_x - focus_line_width;
3490           focus_rectangle->y = min_y - focus_line_width;
3491           focus_rectangle->width = (max_x - min_x) + 2 * focus_line_width;
3492           focus_rectangle->height = (max_y - min_y) + 2 * focus_line_width;
3493         }
3494     }
3495
3496   return FALSE;
3497 }
3498
3499 /**
3500  * pspp_sheet_view_column_cell_render:
3501  * @tree_column: A #PsppSheetViewColumn.
3502  * @window: a #GdkDrawable to draw to
3503  * @background_area: entire cell area (including tree expanders and maybe padding on the sides)
3504  * @cell_area: area normally rendered by a cell renderer
3505  * @expose_area: area that actually needs updating
3506  * @flags: flags that affect rendering
3507  * 
3508  * Renders the cell contained by #tree_column. This is used primarily by the
3509  * #PsppSheetView.
3510  **/
3511 void
3512 _pspp_sheet_view_column_cell_render (PsppSheetViewColumn  *tree_column,
3513                                    GdkWindow          *window,
3514                                    const GdkRectangle *background_area,
3515                                    const GdkRectangle *cell_area,
3516                                    const GdkRectangle *expose_area,
3517                                    guint               flags)
3518 {
3519   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
3520   g_return_if_fail (background_area != NULL);
3521   g_return_if_fail (cell_area != NULL);
3522   g_return_if_fail (expose_area != NULL);
3523
3524   pspp_sheet_view_column_cell_process_action (tree_column,
3525                                             window,
3526                                             background_area,
3527                                             cell_area,
3528                                             flags,
3529                                             CELL_ACTION_RENDER,
3530                                             expose_area,
3531                                             NULL, NULL, NULL, NULL);
3532 }
3533
3534 gboolean
3535 _pspp_sheet_view_column_cell_event (PsppSheetViewColumn  *tree_column,
3536                                   GtkCellEditable   **editable_widget,
3537                                   GdkEvent           *event,
3538                                   gchar              *path_string,
3539                                   const GdkRectangle *background_area,
3540                                   const GdkRectangle *cell_area,
3541                                   guint               flags)
3542 {
3543   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
3544
3545   return pspp_sheet_view_column_cell_process_action (tree_column,
3546                                                    NULL,
3547                                                    background_area,
3548                                                    cell_area,
3549                                                    flags,
3550                                                    CELL_ACTION_EVENT,
3551                                                    NULL, NULL,
3552                                                    editable_widget,
3553                                                    event,
3554                                                    path_string);
3555 }
3556
3557 void
3558 _pspp_sheet_view_column_get_focus_area (PsppSheetViewColumn  *tree_column,
3559                                       const GdkRectangle *background_area,
3560                                       const GdkRectangle *cell_area,
3561                                       GdkRectangle       *focus_area)
3562 {
3563   pspp_sheet_view_column_cell_process_action (tree_column,
3564                                             NULL,
3565                                             background_area,
3566                                             cell_area,
3567                                             0,
3568                                             CELL_ACTION_FOCUS,
3569                                             NULL,
3570                                             focus_area,
3571                                             NULL, NULL, NULL);
3572 }
3573
3574
3575 /* cell list manipulation */
3576 static GList *
3577 pspp_sheet_view_column_cell_first (PsppSheetViewColumn *tree_column)
3578 {
3579   GList *list = tree_column->cell_list;
3580
3581   /* first GTK_PACK_START cell we find */
3582   for ( ; list; list = list->next)
3583     {
3584       PsppSheetViewColumnCellInfo *info = list->data;
3585       if (info->pack == GTK_PACK_START)
3586         return list;
3587     }
3588
3589   /* hmm, else the *last* GTK_PACK_END cell */
3590   list = g_list_last (tree_column->cell_list);
3591
3592   for ( ; list; list = list->prev)
3593     {
3594       PsppSheetViewColumnCellInfo *info = list->data;
3595       if (info->pack == GTK_PACK_END)
3596         return list;
3597     }
3598
3599   return NULL;
3600 }
3601
3602 static GList *
3603 pspp_sheet_view_column_cell_last (PsppSheetViewColumn *tree_column)
3604 {
3605   GList *list = tree_column->cell_list;
3606
3607   /* *first* GTK_PACK_END cell we find */
3608   for ( ; list ; list = list->next)
3609     {
3610       PsppSheetViewColumnCellInfo *info = list->data;
3611       if (info->pack == GTK_PACK_END)
3612         return list;
3613     }
3614
3615   /* hmm, else the last GTK_PACK_START cell */
3616   list = g_list_last (tree_column->cell_list);
3617
3618   for ( ; list; list = list->prev)
3619     {
3620       PsppSheetViewColumnCellInfo *info = list->data;
3621       if (info->pack == GTK_PACK_START)
3622         return list;
3623     }
3624
3625   return NULL;
3626 }
3627
3628 static GList *
3629 pspp_sheet_view_column_cell_next (PsppSheetViewColumn *tree_column,
3630                                 GList             *current)
3631 {
3632   GList *list;
3633   PsppSheetViewColumnCellInfo *info = current->data;
3634
3635   if (info->pack == GTK_PACK_START)
3636     {
3637       for (list = current->next; list; list = list->next)
3638         {
3639           PsppSheetViewColumnCellInfo *inf = list->data;
3640           if (inf->pack == GTK_PACK_START)
3641             return list;
3642         }
3643
3644       /* out of GTK_PACK_START cells, get *last* GTK_PACK_END one */
3645       list = g_list_last (tree_column->cell_list);
3646       for (; list; list = list->prev)
3647         {
3648           PsppSheetViewColumnCellInfo *inf = list->data;
3649           if (inf->pack == GTK_PACK_END)
3650             return list;
3651         }
3652     }
3653
3654   for (list = current->prev; list; list = list->prev)
3655     {
3656       PsppSheetViewColumnCellInfo *inf = list->data;
3657       if (inf->pack == GTK_PACK_END)
3658         return list;
3659     }
3660
3661   return NULL;
3662 }
3663
3664 static GList *
3665 pspp_sheet_view_column_cell_prev (PsppSheetViewColumn *tree_column,
3666                                 GList             *current)
3667 {
3668   GList *list;
3669   PsppSheetViewColumnCellInfo *info = current->data;
3670
3671   if (info->pack == GTK_PACK_END)
3672     {
3673       for (list = current->next; list; list = list->next)
3674         {
3675           PsppSheetViewColumnCellInfo *inf = list->data;
3676           if (inf->pack == GTK_PACK_END)
3677             return list;
3678         }
3679
3680       /* out of GTK_PACK_END, get last GTK_PACK_START one */
3681       list = g_list_last (tree_column->cell_list);
3682       for ( ; list; list = list->prev)
3683         {
3684           PsppSheetViewColumnCellInfo *inf = list->data;
3685           if (inf->pack == GTK_PACK_START)
3686             return list;
3687         }
3688     }
3689
3690   for (list = current->prev; list; list = list->prev)
3691     {
3692       PsppSheetViewColumnCellInfo *inf = list->data;
3693       if (inf->pack == GTK_PACK_START)
3694         return list;
3695     }
3696
3697   return NULL;
3698 }
3699
3700 gboolean
3701 _pspp_sheet_view_column_cell_focus (PsppSheetViewColumn *tree_column,
3702                                   gint               direction,
3703                                   gboolean           left,
3704                                   gboolean           right)
3705 {
3706   gint count;
3707   gboolean rtl;
3708
3709   count = _pspp_sheet_view_column_count_special_cells (tree_column);
3710   rtl = gtk_widget_get_direction (GTK_WIDGET (tree_column->tree_view)) == GTK_TEXT_DIR_RTL;
3711
3712   /* if we are the current focus column and have multiple editable cells,
3713    * try to select the next one, else move the focus to the next column
3714    */
3715   if (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->focus_column == tree_column)
3716     {
3717       if (count > 1)
3718         {
3719           GList *next, *prev;
3720           GList *list = tree_column->cell_list;
3721           PsppSheetViewColumnCellInfo *info = NULL;
3722
3723           /* find current focussed cell */
3724           for ( ; list; list = list->next)
3725             {
3726               info = list->data;
3727               if (info->has_focus)
3728                 break;
3729             }
3730
3731           /* not a focussed cell in the focus column? */
3732           if (!list || !info || !info->has_focus)
3733             return FALSE;
3734
3735           if (rtl)
3736             {
3737               prev = pspp_sheet_view_column_cell_next (tree_column, list);
3738               next = pspp_sheet_view_column_cell_prev (tree_column, list);
3739             }
3740           else
3741             {
3742               next = pspp_sheet_view_column_cell_next (tree_column, list);
3743               prev = pspp_sheet_view_column_cell_prev (tree_column, list);
3744             }
3745
3746           info->has_focus = FALSE;
3747           if (direction > 0 && next)
3748             {
3749               info = next->data;
3750               info->has_focus = TRUE;
3751               return TRUE;
3752             }
3753           else if (direction > 0 && !next && !right)
3754             {
3755               /* keep focus on last cell */
3756               if (rtl)
3757                 info = pspp_sheet_view_column_cell_first (tree_column)->data;
3758               else
3759                 info = pspp_sheet_view_column_cell_last (tree_column)->data;
3760
3761               info->has_focus = TRUE;
3762               return TRUE;
3763             }
3764           else if (direction < 0 && prev)
3765             {
3766               info = prev->data;
3767               info->has_focus = TRUE;
3768               return TRUE;
3769             }
3770           else if (direction < 0 && !prev && !left)
3771             {
3772               /* keep focus on first cell */
3773               if (rtl)
3774                 info = pspp_sheet_view_column_cell_last (tree_column)->data;
3775               else
3776                 info = pspp_sheet_view_column_cell_first (tree_column)->data;
3777
3778               info->has_focus = TRUE;
3779               return TRUE;
3780             }
3781         }
3782       return FALSE;
3783     }
3784
3785   /* we get focus, if we have multiple editable cells, give the correct one
3786    * focus
3787    */
3788   if (count > 1)
3789     {
3790       GList *list = tree_column->cell_list;
3791
3792       /* clear focus first */
3793       for ( ; list ; list = list->next)
3794         {
3795           PsppSheetViewColumnCellInfo *info = list->data;
3796           if (info->has_focus)
3797             info->has_focus = FALSE;
3798         }
3799
3800       list = NULL;
3801       if (rtl)
3802         {
3803           if (direction > 0)
3804             list = pspp_sheet_view_column_cell_last (tree_column);
3805           else if (direction < 0)
3806             list = pspp_sheet_view_column_cell_first (tree_column);
3807         }
3808       else
3809         {
3810           if (direction > 0)
3811             list = pspp_sheet_view_column_cell_first (tree_column);
3812           else if (direction < 0)
3813             list = pspp_sheet_view_column_cell_last (tree_column);
3814         }
3815
3816       if (list)
3817         ((PsppSheetViewColumnCellInfo *) list->data)->has_focus = TRUE;
3818     }
3819
3820   return TRUE;
3821 }
3822
3823 void
3824 _pspp_sheet_view_column_cell_draw_focus (PsppSheetViewColumn  *tree_column,
3825                                        GdkWindow          *window,
3826                                        const GdkRectangle *background_area,
3827                                        const GdkRectangle *cell_area,
3828                                        const GdkRectangle *expose_area,
3829                                        guint               flags)
3830 {
3831   gint focus_line_width;
3832   GtkStateType cell_state;
3833   
3834   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
3835   gtk_widget_style_get (GTK_WIDGET (tree_column->tree_view),
3836                         "focus-line-width", &focus_line_width, NULL);
3837   if (tree_column->editable_widget)
3838     {
3839       /* This function is only called on the editable row when editing.
3840        */
3841 #if 0
3842       gtk_paint_focus (tree_column->tree_view->style,
3843                        window,
3844                        gtk_widget_get_state (tree_column->tree_view),
3845                        NULL,
3846                        tree_column->tree_view,
3847                        "treeview",
3848                        cell_area->x - focus_line_width,
3849                        cell_area->y - focus_line_width,
3850                        cell_area->width + 2 * focus_line_width,
3851                        cell_area->height + 2 * focus_line_width);
3852 #endif      
3853     }
3854   else
3855     {
3856       GdkRectangle focus_rectangle;
3857       pspp_sheet_view_column_cell_process_action (tree_column,
3858                                                 window,
3859                                                 background_area,
3860                                                 cell_area,
3861                                                 flags,
3862                                                 CELL_ACTION_FOCUS,
3863                                                 expose_area,
3864                                                 &focus_rectangle,
3865                                                 NULL, NULL, NULL);
3866
3867       cell_state = flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
3868               (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
3869               (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL));
3870       gtk_paint_focus (tree_column->tree_view->style,
3871                        window,
3872                        cell_state,
3873                        cell_area,
3874                        tree_column->tree_view,
3875                        "treeview",
3876                        focus_rectangle.x,
3877                        focus_rectangle.y,
3878                        focus_rectangle.width,
3879                        focus_rectangle.height);
3880     }
3881 }
3882
3883 /**
3884  * pspp_sheet_view_column_cell_is_visible:
3885  * @tree_column: A #PsppSheetViewColumn
3886  * 
3887  * Returns %TRUE if any of the cells packed into the @tree_column are visible.
3888  * For this to be meaningful, you must first initialize the cells with
3889  * pspp_sheet_view_column_cell_set_cell_data()
3890  * 
3891  * Return value: %TRUE, if any of the cells packed into the @tree_column are currently visible
3892  **/
3893 gboolean
3894 pspp_sheet_view_column_cell_is_visible (PsppSheetViewColumn *tree_column)
3895 {
3896   GList *list;
3897
3898   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
3899
3900   for (list = tree_column->cell_list; list; list = list->next)
3901     {
3902       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
3903
3904       if (info->cell->visible)
3905         return TRUE;
3906     }
3907
3908   return FALSE;
3909 }
3910
3911 /**
3912  * pspp_sheet_view_column_focus_cell:
3913  * @tree_column: A #PsppSheetViewColumn
3914  * @cell: A #GtkCellRenderer
3915  *
3916  * Sets the current keyboard focus to be at @cell, if the column contains
3917  * 2 or more editable and activatable cells.
3918  *
3919  * Since: 2.2
3920  **/
3921 void
3922 pspp_sheet_view_column_focus_cell (PsppSheetViewColumn *tree_column,
3923                                  GtkCellRenderer   *cell)
3924 {
3925   GList *list;
3926   gboolean found_cell = FALSE;
3927
3928   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
3929   g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
3930
3931   if (_pspp_sheet_view_column_count_special_cells (tree_column) < 2)
3932     return;
3933
3934   for (list = tree_column->cell_list; list; list = list->next)
3935     {
3936       PsppSheetViewColumnCellInfo *info = list->data;
3937
3938       if (info->cell == cell)
3939         {
3940           info->has_focus = TRUE;
3941           found_cell = TRUE;
3942           break;
3943         }
3944     }
3945
3946   if (found_cell)
3947     {
3948       for (list = tree_column->cell_list; list; list = list->next)
3949         {
3950           PsppSheetViewColumnCellInfo *info = list->data;
3951
3952           if (info->cell != cell)
3953             info->has_focus = FALSE;
3954         }
3955
3956       /* FIXME: redraw? */
3957     }
3958 }
3959
3960 void
3961 _pspp_sheet_view_column_cell_set_dirty (PsppSheetViewColumn *tree_column)
3962 {
3963   GList *list;
3964
3965   for (list = tree_column->cell_list; list; list = list->next)
3966     {
3967       PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
3968
3969       info->requested_width = 0;
3970     }
3971   tree_column->dirty = TRUE;
3972   tree_column->requested_width = -1;
3973   tree_column->width = 0;
3974
3975   if (tree_column->tree_view &&
3976       gtk_widget_get_realized (tree_column->tree_view))
3977     {
3978       _pspp_sheet_view_install_mark_rows_col_dirty (PSPP_SHEET_VIEW (tree_column->tree_view));
3979       gtk_widget_queue_resize (tree_column->tree_view);
3980     }
3981 }
3982
3983 void
3984 _pspp_sheet_view_column_start_editing (PsppSheetViewColumn *tree_column,
3985                                      GtkCellEditable   *cell_editable)
3986 {
3987   g_return_if_fail (tree_column->editable_widget == NULL);
3988
3989   tree_column->editable_widget = cell_editable;
3990 }
3991
3992 void
3993 _pspp_sheet_view_column_stop_editing (PsppSheetViewColumn *tree_column)
3994 {
3995   GList *list;
3996
3997   g_return_if_fail (tree_column->editable_widget != NULL);
3998
3999   tree_column->editable_widget = NULL;
4000   for (list = tree_column->cell_list; list; list = list->next)
4001     ((PsppSheetViewColumnCellInfo *)list->data)->in_editing_mode = FALSE;
4002 }
4003
4004 void
4005 _pspp_sheet_view_column_get_neighbor_sizes (PsppSheetViewColumn *column,
4006                                           GtkCellRenderer   *cell,
4007                                           gint              *left,
4008                                           gint              *right)
4009 {
4010   GList *list;
4011   PsppSheetViewColumnCellInfo *info;
4012   gint l, r;
4013   gboolean rtl;
4014
4015   l = r = 0;
4016
4017   list = pspp_sheet_view_column_cell_first (column);  
4018
4019   while (list)
4020     {
4021       info = (PsppSheetViewColumnCellInfo *)list->data;
4022       
4023       list = pspp_sheet_view_column_cell_next (column, list);
4024
4025       if (info->cell == cell)
4026         break;
4027       
4028       if (info->cell->visible)
4029         l += info->real_width + column->spacing;
4030     }
4031
4032   while (list)
4033     {
4034       info = (PsppSheetViewColumnCellInfo *)list->data;
4035       
4036       list = pspp_sheet_view_column_cell_next (column, list);
4037
4038       if (info->cell->visible)
4039         r += info->real_width + column->spacing;
4040     }
4041
4042   rtl = (gtk_widget_get_direction (GTK_WIDGET (column->tree_view)) == GTK_TEXT_DIR_RTL);
4043   if (left)
4044     *left = rtl ? r : l;
4045
4046   if (right)
4047     *right = rtl ? l : r;
4048 }
4049
4050 /**
4051  * pspp_sheet_view_column_cell_get_position:
4052  * @tree_column: a #PsppSheetViewColumn
4053  * @cell_renderer: a #GtkCellRenderer
4054  * @start_pos: return location for the horizontal position of @cell within
4055  *            @tree_column, may be %NULL
4056  * @width: return location for the width of @cell, may be %NULL
4057  *
4058  * Obtains the horizontal position and size of a cell in a column. If the
4059  * cell is not found in the column, @start_pos and @width are not changed and
4060  * %FALSE is returned.
4061  * 
4062  * Return value: %TRUE if @cell belongs to @tree_column.
4063  */
4064 gboolean
4065 pspp_sheet_view_column_cell_get_position (PsppSheetViewColumn *tree_column,
4066                                         GtkCellRenderer   *cell_renderer,
4067                                         gint              *start_pos,
4068                                         gint              *width)
4069 {
4070   GList *list;
4071   gint current_x = 0;
4072   gboolean found_cell = FALSE;
4073   PsppSheetViewColumnCellInfo *cellinfo = NULL;
4074
4075   list = pspp_sheet_view_column_cell_first (tree_column);
4076   for (; list; list = pspp_sheet_view_column_cell_next (tree_column, list))
4077     {
4078       cellinfo = list->data;
4079       if (cellinfo->cell == cell_renderer)
4080         {
4081           found_cell = TRUE;
4082           break;
4083         }
4084
4085       if (cellinfo->cell->visible)
4086         current_x += cellinfo->real_width;
4087     }
4088
4089   if (found_cell)
4090     {
4091       if (start_pos)
4092         *start_pos = current_x;
4093       if (width)
4094         *width = cellinfo->real_width;
4095     }
4096
4097   return found_cell;
4098 }
4099
4100 /**
4101  * pspp_sheet_view_column_queue_resize:
4102  * @tree_column: A #PsppSheetViewColumn
4103  *
4104  * Flags the column, and the cell renderers added to this column, to have
4105  * their sizes renegotiated.
4106  *
4107  * Since: 2.8
4108  **/
4109 void
4110 pspp_sheet_view_column_queue_resize (PsppSheetViewColumn *tree_column)
4111 {
4112   g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
4113
4114   if (tree_column->tree_view)
4115     _pspp_sheet_view_column_cell_set_dirty (tree_column);
4116 }
4117
4118 /**
4119  * pspp_sheet_view_column_get_tree_view:
4120  * @tree_column: A #PsppSheetViewColumn
4121  *
4122  * Returns the #PsppSheetView wherein @tree_column has been inserted.  If
4123  * @column is currently not inserted in any tree view, %NULL is
4124  * returned.
4125  *
4126  * Return value: The tree view wherein @column has been inserted if any,
4127  *               %NULL otherwise.
4128  *
4129  * Since: 2.12
4130  */
4131 GtkWidget *
4132 pspp_sheet_view_column_get_tree_view (PsppSheetViewColumn *tree_column)
4133 {
4134   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), NULL);
4135
4136   return tree_column->tree_view;
4137 }
4138
4139 typedef struct {
4140   GtkCellLayout   *cell_layout;
4141   GtkCellRenderer *renderer;
4142   gchar           *attr_name;
4143 } AttributesSubParserData;
4144
4145 static void
4146 attributes_start_element (GMarkupParseContext *context,
4147                           const gchar         *element_name,
4148                           const gchar        **names,
4149                           const gchar        **values,
4150                           gpointer             user_data,
4151                           GError             **error)
4152 {
4153   AttributesSubParserData *parser_data = (AttributesSubParserData*)user_data;
4154   guint i;
4155
4156   if (strcmp (element_name, "attribute") == 0)
4157     {
4158       for (i = 0; names[i]; i++)
4159         if (strcmp (names[i], "name") == 0)
4160           parser_data->attr_name = g_strdup (values[i]);
4161     }
4162   else if (strcmp (element_name, "attributes") == 0)
4163     return;
4164   else
4165     g_warning ("Unsupported tag for GtkCellLayout: %s\n", element_name);
4166 }
4167
4168 static void
4169 attributes_text_element (GMarkupParseContext *context,
4170                          const gchar         *text,
4171                          gsize                text_len,
4172                          gpointer             user_data,
4173                          GError             **error)
4174 {
4175   AttributesSubParserData *parser_data = (AttributesSubParserData*)user_data;
4176   glong l;
4177   gchar *endptr;
4178   gchar *string;
4179   
4180   if (!parser_data->attr_name)
4181     return;
4182
4183   errno = 0;
4184   string = g_strndup (text, text_len);
4185   l = strtol (string, &endptr, 0);
4186   if (errno || endptr == string)
4187     {
4188       g_set_error (error, 
4189                    GTK_BUILDER_ERROR,
4190                    GTK_BUILDER_ERROR_INVALID_VALUE,
4191                    "Could not parse integer `%s'",
4192                    string);
4193       g_free (string);
4194       return;
4195     }
4196   g_free (string);
4197
4198   gtk_cell_layout_add_attribute (parser_data->cell_layout,
4199                                  parser_data->renderer,
4200                                  parser_data->attr_name, l);
4201   g_free (parser_data->attr_name);
4202   parser_data->attr_name = NULL;
4203 }
4204
4205 static const GMarkupParser attributes_parser =
4206   {
4207     attributes_start_element,
4208     NULL,
4209     attributes_text_element,
4210   };
4211
4212 gboolean
4213 _gtk_cell_layout_buildable_custom_tag_start (GtkBuildable  *buildable,
4214                                              GtkBuilder    *builder,
4215                                              GObject       *child,
4216                                              const gchar   *tagname,
4217                                              GMarkupParser *parser,
4218                                              gpointer      *data)
4219 {
4220   AttributesSubParserData *parser_data;
4221
4222   if (!child)
4223     return FALSE;
4224
4225   if (strcmp (tagname, "attributes") == 0)
4226     {
4227       parser_data = g_slice_new0 (AttributesSubParserData);
4228       parser_data->cell_layout = GTK_CELL_LAYOUT (buildable);
4229       parser_data->renderer = GTK_CELL_RENDERER (child);
4230       parser_data->attr_name = NULL;
4231
4232       *parser = attributes_parser;
4233       *data = parser_data;
4234       return TRUE;
4235     }
4236
4237   return FALSE;
4238 }
4239
4240 void
4241 _gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable,
4242                                            GtkBuilder   *builder,
4243                                            GObject      *child,
4244                                            const gchar  *tagname,
4245                                            gpointer     *data)
4246 {
4247   AttributesSubParserData *parser_data;
4248
4249   parser_data = (AttributesSubParserData*)data;
4250   g_assert (!parser_data->attr_name);
4251   g_slice_free (AttributesSubParserData, parser_data);
4252 }
4253
4254 void
4255 _gtk_cell_layout_buildable_add_child (GtkBuildable      *buildable,
4256                                       GtkBuilder        *builder,
4257                                       GObject           *child,
4258                                       const gchar       *type)
4259 {
4260   GtkCellLayoutIface *iface;
4261   
4262   g_return_if_fail (GTK_IS_CELL_LAYOUT (buildable));
4263   g_return_if_fail (GTK_IS_CELL_RENDERER (child));
4264
4265   iface = GTK_CELL_LAYOUT_GET_IFACE (buildable);
4266   g_return_if_fail (iface->pack_start != NULL);
4267   iface->pack_start (GTK_CELL_LAYOUT (buildable), GTK_CELL_RENDERER (child), FALSE);
4268 }
4269
4270 void
4271 pspp_sheet_view_column_size_request (PsppSheetViewColumn       *tree_column,
4272                                      GtkRequisition            *request)
4273 {
4274   GtkWidget *base = GTK_WIDGET (tree_column->tree_view);
4275   GtkRequisition label_req;
4276   GtkRequisition align_req;
4277   GtkRequisition arrow_req;
4278   GtkRequisition hbox_req;
4279   GtkStyle **button_style;
4280
4281   if (tree_column->button)
4282     {
4283       gtk_widget_size_request (tree_column->button, request);
4284       return;
4285     }
4286
4287   facade_label_get_size_request (0, 0, base, tree_column->title, &label_req);
4288   facade_alignment_get_size_request (0, 0, 0, 0, 0, &label_req, &align_req);
4289   facade_arrow_get_size_request (0, 0, &arrow_req);
4290
4291   facade_hbox_get_base_size_request (0, 2, 2, &hbox_req);
4292   facade_hbox_add_child_size_request (0, &arrow_req, 0, &hbox_req);
4293   facade_hbox_add_child_size_request (0, &align_req, 0, &hbox_req);
4294
4295   button_style = &PSPP_SHEET_VIEW (tree_column->tree_view)->priv->button_style;
4296   if (*button_style == NULL)
4297     {
4298       *button_style = facade_get_style (base, GTK_TYPE_BUTTON, 0);
4299       g_object_ref (*button_style);
4300     }
4301   facade_button_get_size_request (0, base, *button_style, &hbox_req, request);
4302 }
4303
4304 void
4305 pspp_sheet_view_column_size_allocate (PsppSheetViewColumn       *tree_column,
4306                                       GtkAllocation             *allocation)
4307 {
4308   tree_column->allocation = *allocation;
4309   if (tree_column->button)
4310     gtk_widget_size_allocate (tree_column->button, allocation);
4311 }
4312
4313 gboolean
4314 pspp_sheet_view_column_can_focus (PsppSheetViewColumn       *tree_column)
4315 {
4316   return tree_column->reorderable || tree_column->clickable;
4317 }
4318
4319 void
4320 pspp_sheet_view_column_set_need_button (PsppSheetViewColumn       *tree_column,
4321                                         gboolean                   need_button)
4322 {
4323   if (tree_column->need_button != need_button)
4324     {
4325       tree_column->need_button = need_button;
4326       pspp_sheet_view_column_update_button (tree_column);
4327       _pspp_sheet_view_column_realize_button (tree_column);
4328     }
4329 }