535d4924972ee194f87368ba4f4704c60b614c76
[pspp] / src / ui / gui / pspp-sheet-view.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2011, 2012, 2013 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 /* gtktreeview.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 <gtk/gtk.h>
41 #include <gdk/gdk.h>
42 #include <gdk/gdkkeysyms.h>
43 #include <gdk/gdkkeysyms-compat.h>
44 #include <string.h>
45
46 #include "ui/gui/psppire-marshal.h"
47 #include "ui/gui/pspp-sheet-selection.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 /* Many keyboard shortcuts for Mac are the same as for X
54  * except they use Command key instead of Control (e.g. Cut,
55  * Copy, Paste). This symbol is for those simple cases. */
56 #ifndef GDK_WINDOWING_QUARTZ
57 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_CONTROL_MASK
58 #else
59 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_META_MASK
60 #endif
61
62 #define PSPP_SHEET_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
63 #define PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC (PSPP_SHEET_VIEW_PRIORITY_VALIDATE + 2)
64 #define PSPP_SHEET_VIEW_TIME_MS_PER_IDLE 30
65 #define SCROLL_EDGE_SIZE 15
66 #define EXPANDER_EXTRA_PADDING 4
67 #define PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT 5000
68
69 /* The "background" areas of all rows/cells add up to cover the entire tree.
70  * The background includes all inter-row and inter-cell spacing.
71  * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
72  * i.e. just the cells, no spacing.
73  */
74
75 #define BACKGROUND_HEIGHT(tree_view) (tree_view->priv->fixed_height)
76 #define CELL_HEIGHT(tree_view, separator) ((BACKGROUND_HEIGHT (tree_view)) - (separator))
77
78 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
79  * vice versa.
80  */
81 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy)
82 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy)
83
84 /* This is in bin_window coordinates */
85 #define BACKGROUND_FIRST_PIXEL(tree_view,node) (RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, pspp_sheet_view_node_find_offset (tree_view, (node))))
86 #define CELL_FIRST_PIXEL(tree_view,node,separator) (BACKGROUND_FIRST_PIXEL (tree_view,node) + separator/2)
87
88 #define ROW_HEIGHT(tree_view) \
89   ((tree_view->priv->fixed_height > 0) ? (tree_view->priv->fixed_height) : (tree_view)->priv->expander_size)
90
91
92 typedef struct _PsppSheetViewChild PsppSheetViewChild;
93 struct _PsppSheetViewChild
94 {
95   GtkWidget *widget;
96   gint x;
97   gint y;
98   gint width;
99   gint height;
100 };
101
102
103 typedef struct _TreeViewDragInfo TreeViewDragInfo;
104 struct _TreeViewDragInfo
105 {
106   GdkModifierType start_button_mask;
107   GtkTargetList *_unused_source_target_list;
108   GdkDragAction source_actions;
109
110   GtkTargetList *_unused_dest_target_list;
111
112   guint source_set : 1;
113   guint dest_set : 1;
114 };
115
116
117 /* Signals */
118 enum
119 {
120   ROW_ACTIVATED,
121   COLUMNS_CHANGED,
122   CURSOR_CHANGED,
123   MOVE_CURSOR,
124   SELECT_ALL,
125   UNSELECT_ALL,
126   SELECT_CURSOR_ROW,
127   TOGGLE_CURSOR_ROW,
128   START_INTERACTIVE_SEARCH,
129   LAST_SIGNAL
130 };
131
132 /* Properties */
133 enum {
134   PROP_0,
135   PROP_MODEL,
136   PROP_HADJUSTMENT,
137   PROP_VADJUSTMENT,
138   PROP_HSCROLL_POLICY,
139   PROP_VSCROLL_POLICY,
140   PROP_HEADERS_VISIBLE,
141   PROP_HEADERS_CLICKABLE,
142   PROP_REORDERABLE,
143   PROP_RULES_HINT,
144   PROP_ENABLE_SEARCH,
145   PROP_SEARCH_COLUMN,
146   PROP_HOVER_SELECTION,
147   PROP_RUBBER_BANDING,
148   PROP_ENABLE_GRID_LINES,
149   PROP_TOOLTIP_COLUMN,
150   PROP_SPECIAL_CELLS,
151   PROP_FIXED_HEIGHT,
152   PROP_FIXED_HEIGHT_SET
153 };
154
155 /* object signals */
156 static void     pspp_sheet_view_finalize             (GObject          *object);
157 static void     pspp_sheet_view_set_property         (GObject         *object,
158                                                     guint            prop_id,
159                                                     const GValue    *value,
160                                                     GParamSpec      *pspec);
161 static void     pspp_sheet_view_get_property         (GObject         *object,
162                                                     guint            prop_id,
163                                                     GValue          *value,
164                                                     GParamSpec      *pspec);
165
166 static void     pspp_sheet_view_dispose              (GObject        *object);
167
168 /* gtkwidget signals */
169 static void     pspp_sheet_view_realize              (GtkWidget        *widget);
170 static void     pspp_sheet_view_unrealize            (GtkWidget        *widget);
171 static void     pspp_sheet_view_map                  (GtkWidget        *widget);
172 static void     pspp_sheet_view_size_request         (GtkWidget        *widget,
173                                                     GtkRequisition   *requisition);
174 static void     pspp_sheet_view_size_allocate        (GtkWidget        *widget,
175                                                     GtkAllocation    *allocation);
176 static gboolean pspp_sheet_view_draw               (GtkWidget        *widget,
177                                                     cairo_t *cr);
178 static gboolean pspp_sheet_view_key_press            (GtkWidget        *widget,
179                                                     GdkEventKey      *event);
180 static gboolean pspp_sheet_view_key_release          (GtkWidget        *widget,
181                                                     GdkEventKey      *event);
182 static gboolean pspp_sheet_view_motion               (GtkWidget        *widget,
183                                                     GdkEventMotion   *event);
184 static gboolean pspp_sheet_view_enter_notify         (GtkWidget        *widget,
185                                                     GdkEventCrossing *event);
186 static gboolean pspp_sheet_view_leave_notify         (GtkWidget        *widget,
187                                                     GdkEventCrossing *event);
188 static gboolean pspp_sheet_view_button_press         (GtkWidget        *widget,
189                                                     GdkEventButton   *event);
190 static gboolean pspp_sheet_view_button_release       (GtkWidget        *widget,
191                                                     GdkEventButton   *event);
192 static gboolean pspp_sheet_view_grab_broken          (GtkWidget          *widget,
193                                                     GdkEventGrabBroken *event);
194
195 static void     pspp_sheet_view_set_focus_child      (GtkContainer     *container,
196                                                     GtkWidget        *child);
197 static gint     pspp_sheet_view_focus_out            (GtkWidget        *widget,
198                                                     GdkEventFocus    *event);
199 static gint     pspp_sheet_view_focus                (GtkWidget        *widget,
200                                                     GtkDirectionType  direction);
201 static void     pspp_sheet_view_grab_focus           (GtkWidget        *widget);
202 static void     pspp_sheet_view_style_updated        (GtkWidget        *widget);
203 static void     pspp_sheet_view_grab_notify          (GtkWidget        *widget,
204                                                     gboolean          was_grabbed);
205 static void     pspp_sheet_view_state_changed        (GtkWidget        *widget,
206                                                     GtkStateType      previous_state);
207
208 /* container signals */
209 static void     pspp_sheet_view_remove               (GtkContainer     *container,
210                                                     GtkWidget        *widget);
211 static void     pspp_sheet_view_forall               (GtkContainer     *container,
212                                                     gboolean          include_internals,
213                                                     GtkCallback       callback,
214                                                     gpointer          callback_data);
215
216 /* Source side drag signals */
217 static void pspp_sheet_view_drag_begin       (GtkWidget        *widget,
218                                             GdkDragContext   *context);
219 static void pspp_sheet_view_drag_end         (GtkWidget        *widget,
220                                             GdkDragContext   *context);
221 static void pspp_sheet_view_drag_data_get    (GtkWidget        *widget,
222                                             GdkDragContext   *context,
223                                             GtkSelectionData *selection_data,
224                                             guint             info,
225                                             guint             time);
226 static void pspp_sheet_view_drag_data_delete (GtkWidget        *widget,
227                                             GdkDragContext   *context);
228
229 /* Target side drag signals */
230 static void     pspp_sheet_view_drag_leave         (GtkWidget        *widget,
231                                                   GdkDragContext   *context,
232                                                   guint             time);
233 static gboolean pspp_sheet_view_drag_motion        (GtkWidget        *widget,
234                                                   GdkDragContext   *context,
235                                                   gint              x,
236                                                   gint              y,
237                                                   guint             time);
238 static gboolean pspp_sheet_view_drag_drop          (GtkWidget        *widget,
239                                                   GdkDragContext   *context,
240                                                   gint              x,
241                                                   gint              y,
242                                                   guint             time);
243 static void     pspp_sheet_view_drag_data_received (GtkWidget        *widget,
244                                                   GdkDragContext   *context,
245                                                   gint              x,
246                                                   gint              y,
247                                                   GtkSelectionData *selection_data,
248                                                   guint             info,
249                                                   guint             time);
250
251 /* tree_model signals */
252 static void pspp_sheet_view_set_adjustments                 (PsppSheetView     *tree_view,
253                                                            GtkAdjustment   *hadj,
254                                                            GtkAdjustment   *vadj);
255 static gboolean pspp_sheet_view_real_move_cursor            (PsppSheetView     *tree_view,
256                                                            GtkMovementStep  step,
257                                                            gint             count);
258 static gboolean pspp_sheet_view_real_select_all             (PsppSheetView     *tree_view);
259 static gboolean pspp_sheet_view_real_unselect_all           (PsppSheetView     *tree_view);
260 static gboolean pspp_sheet_view_real_select_cursor_row      (PsppSheetView     *tree_view,
261                                                              gboolean         start_editing,
262                                                              PsppSheetSelectMode mode);
263 static gboolean pspp_sheet_view_real_toggle_cursor_row      (PsppSheetView     *tree_view);
264 static void pspp_sheet_view_row_changed                     (GtkTreeModel    *model,
265                                                            GtkTreePath     *path,
266                                                            GtkTreeIter     *iter,
267                                                            gpointer         data);
268 static void pspp_sheet_view_row_inserted                    (GtkTreeModel    *model,
269                                                            GtkTreePath     *path,
270                                                            GtkTreeIter     *iter,
271                                                            gpointer         data);
272 static void pspp_sheet_view_row_deleted                     (GtkTreeModel    *model,
273                                                            GtkTreePath     *path,
274                                                            gpointer         data);
275 static void pspp_sheet_view_rows_reordered                  (GtkTreeModel    *model,
276                                                            GtkTreePath     *parent,
277                                                            GtkTreeIter     *iter,
278                                                            gint            *new_order,
279                                                            gpointer         data);
280
281 /* Incremental reflow */
282 static gint validate_row             (PsppSheetView *tree_view,
283                                           int node,
284                                           GtkTreeIter *iter,
285                                           GtkTreePath *path);
286 static void     validate_visible_area    (PsppSheetView *tree_view);
287 static gboolean validate_rows_handler    (PsppSheetView *tree_view);
288 static gboolean presize_handler_callback (gpointer     data);
289 static void     install_presize_handler  (PsppSheetView *tree_view);
290 static void     install_scroll_sync_handler (PsppSheetView *tree_view);
291 static void     pspp_sheet_view_set_top_row   (PsppSheetView *tree_view,
292                                              GtkTreePath *path,
293                                              gint         offset);
294 static void     pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view);
295 static void     pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view);
296 static void     invalidate_empty_focus      (PsppSheetView *tree_view);
297
298 /* Internal functions */
299 static GtkAdjustment *pspp_sheet_view_do_get_hadjustment (PsppSheetView *);
300 static GtkAdjustment *pspp_sheet_view_do_get_vadjustment (PsppSheetView *);
301 static void pspp_sheet_view_do_set_hadjustment (PsppSheetView   *tree_view,
302                                               GtkAdjustment *adjustment);
303 static void pspp_sheet_view_do_set_vadjustment (PsppSheetView   *tree_view,
304                                               GtkAdjustment *adjustment);
305 static void     pspp_sheet_view_add_move_binding               (GtkBindingSet      *binding_set,
306                                                               guint               keyval,
307                                                               guint               modmask,
308                                                               gboolean            add_shifted_binding,
309                                                               GtkMovementStep     step,
310                                                               gint                count);
311 static void     pspp_sheet_view_queue_draw_path                (PsppSheetView        *tree_view,
312                                                               GtkTreePath        *path,
313                                                               const GdkRectangle *clip_rect);
314 static gint     pspp_sheet_view_new_column_width               (PsppSheetView        *tree_view,
315                                                               gint                i,
316                                                               gint               *x);
317 static void     pspp_sheet_view_adjustment_changed             (GtkAdjustment      *adjustment,
318                                                               PsppSheetView        *tree_view);
319 static void     pspp_sheet_view_clamp_node_visible             (PsppSheetView        *tree_view,
320                                                               int node);
321 static void     pspp_sheet_view_clamp_column_visible           (PsppSheetView        *tree_view,
322                                                               PsppSheetViewColumn  *column,
323                                                               gboolean            focus_to_cell);
324 static gboolean pspp_sheet_view_maybe_begin_dragging_row       (PsppSheetView        *tree_view,
325                                                               GdkEventMotion     *event);
326 static void     pspp_sheet_view_focus_to_cursor                (PsppSheetView        *tree_view);
327 static gboolean pspp_sheet_view_move_cursor_up_down            (PsppSheetView        *tree_view,
328                                                               gint                count,
329                                                                 PsppSheetSelectMode mode);
330 static void     pspp_sheet_view_move_cursor_page_up_down       (PsppSheetView        *tree_view,
331                                                               gint                count,
332                                                                 PsppSheetSelectMode mode);
333 static void     pspp_sheet_view_move_cursor_left_right         (PsppSheetView        *tree_view,
334                                                                 gint                count,
335                                                                 PsppSheetSelectMode mode);
336 static void     pspp_sheet_view_move_cursor_line_start_end     (PsppSheetView        *tree_view,
337                                                                 gint                count,
338                                                                 PsppSheetSelectMode mode);
339 static void     pspp_sheet_view_move_cursor_tab                (PsppSheetView        *tree_view,
340                                                               gint                count);
341 static void     pspp_sheet_view_move_cursor_start_end          (PsppSheetView        *tree_view,
342                                                               gint                count,
343                                                                 PsppSheetSelectMode mode);
344 static void     pspp_sheet_view_real_set_cursor                (PsppSheetView        *tree_view,
345                                                               GtkTreePath        *path,
346                                                               gboolean            clear_and_select,
347                                                               gboolean            clamp_node,
348                                                               PsppSheetSelectMode mode);
349 static gboolean pspp_sheet_view_has_special_cell               (PsppSheetView        *tree_view);
350 static void     pspp_sheet_view_stop_rubber_band               (PsppSheetView        *tree_view);
351 static void     update_prelight                              (PsppSheetView        *tree_view,
352                                                               int                 x,
353                                                               int                 y);
354 static void initialize_fixed_height_mode (PsppSheetView *tree_view);
355
356 /* interactive search */
357 static void     pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view);
358 static void     pspp_sheet_view_search_dialog_hide     (GtkWidget        *search_dialog,
359                                                          PsppSheetView      *tree_view);
360 static void     pspp_sheet_view_search_position_func      (PsppSheetView      *tree_view,
361                                                          GtkWidget        *search_dialog,
362                                                          gpointer          user_data);
363 static void     pspp_sheet_view_search_disable_popdown    (GtkEntry         *entry,
364                                                          GtkMenu          *menu,
365                                                          gpointer          data);
366 #if GTK3_TRANSITION
367 static void     pspp_sheet_view_search_preedit_changed    (GtkIMContext     *im_context,
368                                                          PsppSheetView      *tree_view);
369 #endif
370 static void     pspp_sheet_view_search_activate           (GtkEntry         *entry,
371                                                          PsppSheetView      *tree_view);
372 static gboolean pspp_sheet_view_real_search_enable_popdown(gpointer          data);
373 static void     pspp_sheet_view_search_enable_popdown     (GtkWidget        *widget,
374                                                          gpointer          data);
375 static gboolean pspp_sheet_view_search_delete_event       (GtkWidget        *widget,
376                                                          GdkEventAny      *event,
377                                                          PsppSheetView      *tree_view);
378 static gboolean pspp_sheet_view_search_button_press_event (GtkWidget        *widget,
379                                                          GdkEventButton   *event,
380                                                          PsppSheetView      *tree_view);
381 static gboolean pspp_sheet_view_search_scroll_event       (GtkWidget        *entry,
382                                                          GdkEventScroll   *event,
383                                                          PsppSheetView      *tree_view);
384 static gboolean pspp_sheet_view_search_key_press_event    (GtkWidget        *entry,
385                                                          GdkEventKey      *event,
386                                                          PsppSheetView      *tree_view);
387 static gboolean pspp_sheet_view_search_move               (GtkWidget        *window,
388                                                          PsppSheetView      *tree_view,
389                                                          gboolean          up);
390 static gboolean pspp_sheet_view_search_equal_func         (GtkTreeModel     *model,
391                                                          gint              column,
392                                                          const gchar      *key,
393                                                          GtkTreeIter      *iter,
394                                                          gpointer          search_data);
395 static gboolean pspp_sheet_view_search_iter               (GtkTreeModel     *model,
396                                                          PsppSheetSelection *selection,
397                                                          GtkTreeIter      *iter,
398                                                          const gchar      *text,
399                                                          gint             *count,
400                                                          gint              n);
401 static void     pspp_sheet_view_search_init               (GtkWidget        *entry,
402                                                          PsppSheetView      *tree_view);
403 static void     pspp_sheet_view_put                       (PsppSheetView      *tree_view,
404                                                          GtkWidget        *child_widget,
405                                                          gint              x,
406                                                          gint              y,
407                                                          gint              width,
408                                                          gint              height);
409 static gboolean pspp_sheet_view_start_editing             (PsppSheetView      *tree_view,
410                                                          GtkTreePath      *cursor_path);
411 static gboolean pspp_sheet_view_editable_button_press_event (GtkWidget *,
412                                                              GdkEventButton *,
413                                                              PsppSheetView *);
414 static void pspp_sheet_view_editable_clicked (GtkButton *, PsppSheetView *);
415 static void pspp_sheet_view_real_start_editing (PsppSheetView       *tree_view,
416                                               PsppSheetViewColumn *column,
417                                               GtkTreePath       *path,
418                                               GtkCellEditable   *cell_editable,
419                                               GdkRectangle      *cell_area,
420                                               GdkEvent          *event,
421                                               guint              flags);
422 static gboolean pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
423                                                              gboolean     keybinding);
424 static gboolean pspp_sheet_view_start_interactive_search      (PsppSheetView *tree_view);
425 static PsppSheetViewColumn *pspp_sheet_view_get_drop_column (PsppSheetView       *tree_view,
426                                                          PsppSheetViewColumn *column,
427                                                          gint               drop_position);
428 static void
429 pspp_sheet_view_adjust_cell_area (PsppSheetView        *tree_view,
430                                   PsppSheetViewColumn  *column,
431                                   const GdkRectangle   *background_area,
432                                   gboolean              subtract_focus_rect,
433                                   GdkRectangle         *cell_area);
434 static gint pspp_sheet_view_find_offset (PsppSheetView *tree_view,
435                                          gint height,
436                                          int *new_node);
437
438 /* GtkBuildable */
439 static void pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
440                                                GtkBuilder  *builder,
441                                                GObject     *child,
442                                                const gchar *type);
443 static void pspp_sheet_view_buildable_init      (GtkBuildableIface *iface);
444
445
446 static gboolean scroll_row_timeout                   (gpointer     data);
447 static void     add_scroll_timeout                   (PsppSheetView *tree_view);
448 static void     remove_scroll_timeout                (PsppSheetView *tree_view);
449
450 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
451
452 static GtkBindingSet *edit_bindings;
453
454 \f
455
456 /* GType Methods
457  */
458
459 G_DEFINE_TYPE_WITH_CODE (PsppSheetView, pspp_sheet_view, GTK_TYPE_CONTAINER,
460                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
461                                                 pspp_sheet_view_buildable_init)
462                          G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
463
464 static void
465 pspp_sheet_view_get_preferred_width (GtkWidget *widget,
466                                gint      *minimal_width,
467                                gint      *natural_width)
468 {
469   GtkRequisition requisition;
470
471   pspp_sheet_view_size_request (widget, &requisition);
472
473   *minimal_width = *natural_width = requisition.width;
474 }
475
476 static void
477 pspp_sheet_view_get_preferred_height (GtkWidget *widget,
478                                 gint      *minimal_height,
479                                 gint      *natural_height)
480 {
481   GtkRequisition requisition;
482
483   pspp_sheet_view_size_request (widget, &requisition);
484
485   *minimal_height = *natural_height = requisition.height;
486 }
487
488 static void
489 pspp_sheet_view_class_init (PsppSheetViewClass *class)
490 {
491   GObjectClass *o_class;
492   GtkWidgetClass *widget_class;
493   GtkContainerClass *container_class;
494   GtkBindingSet *binding_set[2];
495   int i;
496
497   binding_set[0] = gtk_binding_set_by_class (class);
498
499   binding_set[1] = gtk_binding_set_new ("PsppSheetViewEditing");
500   edit_bindings = binding_set[1];
501
502   o_class = (GObjectClass *) class;
503   widget_class = (GtkWidgetClass *) class;
504   container_class = (GtkContainerClass *) class;
505
506   /* GObject signals */
507   o_class->set_property = pspp_sheet_view_set_property;
508   o_class->get_property = pspp_sheet_view_get_property;
509   o_class->finalize = pspp_sheet_view_finalize;
510   o_class->dispose = pspp_sheet_view_dispose;
511
512   /* GtkWidget signals */
513   widget_class->map = pspp_sheet_view_map;
514   widget_class->realize = pspp_sheet_view_realize;
515   widget_class->unrealize = pspp_sheet_view_unrealize;
516   widget_class->get_preferred_width = pspp_sheet_view_get_preferred_width;
517   widget_class->get_preferred_height = pspp_sheet_view_get_preferred_height;
518   widget_class->size_allocate = pspp_sheet_view_size_allocate;
519   widget_class->button_press_event = pspp_sheet_view_button_press;
520   widget_class->button_release_event = pspp_sheet_view_button_release;
521   widget_class->grab_broken_event = pspp_sheet_view_grab_broken;
522   /*widget_class->configure_event = pspp_sheet_view_configure;*/
523   widget_class->motion_notify_event = pspp_sheet_view_motion;
524   widget_class->draw = pspp_sheet_view_draw;
525   widget_class->key_press_event = pspp_sheet_view_key_press;
526   widget_class->key_release_event = pspp_sheet_view_key_release;
527   widget_class->enter_notify_event = pspp_sheet_view_enter_notify;
528   widget_class->leave_notify_event = pspp_sheet_view_leave_notify;
529   widget_class->focus_out_event = pspp_sheet_view_focus_out;
530   widget_class->drag_begin = pspp_sheet_view_drag_begin;
531   widget_class->drag_end = pspp_sheet_view_drag_end;
532   widget_class->drag_data_get = pspp_sheet_view_drag_data_get;
533   widget_class->drag_data_delete = pspp_sheet_view_drag_data_delete;
534   widget_class->drag_leave = pspp_sheet_view_drag_leave;
535   widget_class->drag_motion = pspp_sheet_view_drag_motion;
536   widget_class->drag_drop = pspp_sheet_view_drag_drop;
537   widget_class->drag_data_received = pspp_sheet_view_drag_data_received;
538   widget_class->focus = pspp_sheet_view_focus;
539   widget_class->grab_focus = pspp_sheet_view_grab_focus;
540   widget_class->style_updated = pspp_sheet_view_style_updated;
541   widget_class->grab_notify = pspp_sheet_view_grab_notify;
542   widget_class->state_changed = pspp_sheet_view_state_changed;
543
544   /* GtkContainer signals */
545   container_class->remove = pspp_sheet_view_remove;
546   container_class->forall = pspp_sheet_view_forall;
547   container_class->set_focus_child = pspp_sheet_view_set_focus_child;
548
549   class->set_scroll_adjustments = pspp_sheet_view_set_adjustments;
550   class->move_cursor = pspp_sheet_view_real_move_cursor;
551   class->select_all = pspp_sheet_view_real_select_all;
552   class->unselect_all = pspp_sheet_view_real_unselect_all;
553   class->select_cursor_row = pspp_sheet_view_real_select_cursor_row;
554   class->toggle_cursor_row = pspp_sheet_view_real_toggle_cursor_row;
555   class->start_interactive_search = pspp_sheet_view_start_interactive_search;
556
557   /* Properties */
558
559   g_object_class_install_property (o_class,
560                                    PROP_MODEL,
561                                    g_param_spec_object ("model",
562                                                         P_("TreeView Model"),
563                                                         P_("The model for the tree view"),
564                                                         GTK_TYPE_TREE_MODEL,
565                                                         GTK_PARAM_READWRITE));
566
567   g_object_class_override_property (o_class, PROP_HADJUSTMENT,    "hadjustment");
568   g_object_class_override_property (o_class, PROP_VADJUSTMENT,    "vadjustment");
569   g_object_class_override_property (o_class, PROP_HSCROLL_POLICY, "hscroll-policy");
570   g_object_class_override_property (o_class, PROP_VSCROLL_POLICY, "vscroll-policy");
571
572   g_object_class_install_property (o_class,
573                                    PROP_HEADERS_VISIBLE,
574                                    g_param_spec_boolean ("headers-visible",
575                                                          P_("Headers Visible"),
576                                                          P_("Show the column header buttons"),
577                                                          TRUE,
578                                                          GTK_PARAM_READWRITE));
579
580   g_object_class_install_property (o_class,
581                                    PROP_HEADERS_CLICKABLE,
582                                    g_param_spec_boolean ("headers-clickable",
583                                                          P_("Headers Clickable"),
584                                                          P_("Column headers respond to click events"),
585                                                          TRUE,
586                                                          GTK_PARAM_READWRITE));
587
588   g_object_class_install_property (o_class,
589                                    PROP_REORDERABLE,
590                                    g_param_spec_boolean ("reorderable",
591                                                          P_("Reorderable"),
592                                                          P_("View is reorderable"),
593                                                          FALSE,
594                                                          GTK_PARAM_READWRITE));
595
596   g_object_class_install_property (o_class,
597                                    PROP_RULES_HINT,
598                                    g_param_spec_boolean ("rules-hint",
599                                                          P_("Rules Hint"),
600                                                          P_("Set a hint to the theme engine to draw rows in alternating colors"),
601                                                          FALSE,
602                                                          GTK_PARAM_READWRITE));
603
604     g_object_class_install_property (o_class,
605                                      PROP_ENABLE_SEARCH,
606                                      g_param_spec_boolean ("enable-search",
607                                                            P_("Enable Search"),
608                                                            P_("View allows user to search through columns interactively"),
609                                                            TRUE,
610                                                            GTK_PARAM_READWRITE));
611
612     g_object_class_install_property (o_class,
613                                      PROP_SEARCH_COLUMN,
614                                      g_param_spec_int ("search-column",
615                                                        P_("Search Column"),
616                                                        P_("Model column to search through during interactive search"),
617                                                        -1,
618                                                        G_MAXINT,
619                                                        -1,
620                                                        GTK_PARAM_READWRITE));
621
622     /**
623      * PsppSheetView:hover-selection:
624      * 
625      * Enables of disables the hover selection mode of @tree_view.
626      * Hover selection makes the selected row follow the pointer.
627      * Currently, this works only for the selection modes 
628      * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
629      *
630      * This mode is primarily intended for treeviews in popups, e.g.
631      * in #GtkComboBox or #GtkEntryCompletion.
632      *
633      * Since: 2.6
634      */
635     g_object_class_install_property (o_class,
636                                      PROP_HOVER_SELECTION,
637                                      g_param_spec_boolean ("hover-selection",
638                                                            P_("Hover Selection"),
639                                                            P_("Whether the selection should follow the pointer"),
640                                                            FALSE,
641                                                            GTK_PARAM_READWRITE));
642
643     g_object_class_install_property (o_class,
644                                      PROP_RUBBER_BANDING,
645                                      g_param_spec_boolean ("rubber-banding",
646                                                            P_("Rubber Banding"),
647                                                            P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
648                                                            FALSE,
649                                                            GTK_PARAM_READWRITE));
650
651     g_object_class_install_property (o_class,
652                                      PROP_ENABLE_GRID_LINES,
653                                      g_param_spec_enum ("enable-grid-lines",
654                                                         P_("Enable Grid Lines"),
655                                                         P_("Whether grid lines should be drawn in the tree view"),
656                                                         PSPP_TYPE_SHEET_VIEW_GRID_LINES,
657                                                         PSPP_SHEET_VIEW_GRID_LINES_NONE,
658                                                         GTK_PARAM_READWRITE));
659
660     g_object_class_install_property (o_class,
661                                      PROP_TOOLTIP_COLUMN,
662                                      g_param_spec_int ("tooltip-column",
663                                                        P_("Tooltip Column"),
664                                                        P_("The column in the model containing the tooltip texts for the rows"),
665                                                        -1,
666                                                        G_MAXINT,
667                                                        -1,
668                                                        GTK_PARAM_READWRITE));
669
670     g_object_class_install_property (o_class,
671                                      PROP_SPECIAL_CELLS,
672                                      g_param_spec_enum ("special-cells",
673                                                         P_("Special Cells"),
674                                                         P_("Whether rows have special cells."),
675                                                         PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS,
676                                                         PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT,
677                                                         GTK_PARAM_READWRITE));
678
679     g_object_class_install_property (o_class,
680                                      PROP_FIXED_HEIGHT,
681                                      g_param_spec_int ("fixed-height",
682                                                        P_("Fixed Height"),
683                                                        P_("Height of a single row.  Normally the height of a row is determined automatically.  Writing this property sets fixed-height-set to true, preventing this property's value from changing."),
684                                                        -1,
685                                                        G_MAXINT,
686                                                        -1,
687                                                        GTK_PARAM_READWRITE));
688
689     g_object_class_install_property (o_class,
690                                      PROP_FIXED_HEIGHT_SET,
691                                      g_param_spec_boolean ("fixed-height-set",
692                                                            P_("Fixed Height Set"),
693                                                            P_("Whether fixed-height was set externally."),
694                                                            FALSE,
695                                                            GTK_PARAM_READWRITE));
696
697   /* Style properties */
698 #define _TREE_VIEW_EXPANDER_SIZE 12
699 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
700 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
701
702   gtk_widget_class_install_style_property (widget_class,
703                                            g_param_spec_int ("expander-size",
704                                                              P_("Expander Size"),
705                                                              P_("Size of the expander arrow"),
706                                                              0,
707                                                              G_MAXINT,
708                                                              _TREE_VIEW_EXPANDER_SIZE,
709                                                              GTK_PARAM_READABLE));
710
711   gtk_widget_class_install_style_property (widget_class,
712                                            g_param_spec_int ("vertical-separator",
713                                                              P_("Vertical Separator Width"),
714                                                              P_("Vertical space between cells.  Must be an even number"),
715                                                              0,
716                                                              G_MAXINT,
717                                                              _TREE_VIEW_VERTICAL_SEPARATOR,
718                                                              GTK_PARAM_READABLE));
719
720   gtk_widget_class_install_style_property (widget_class,
721                                            g_param_spec_int ("horizontal-separator",
722                                                              P_("Horizontal Separator Width"),
723                                                              P_("Horizontal space between cells.  Must be an even number"),
724                                                              0,
725                                                              G_MAXINT,
726                                                              _TREE_VIEW_HORIZONTAL_SEPARATOR,
727                                                              GTK_PARAM_READABLE));
728
729   gtk_widget_class_install_style_property (widget_class,
730                                            g_param_spec_boolean ("allow-rules",
731                                                                  P_("Allow Rules"),
732                                                                  P_("Allow drawing of alternating color rows"),
733                                                                  TRUE,
734                                                                  GTK_PARAM_READABLE));
735
736   gtk_widget_class_install_style_property (widget_class,
737                                            g_param_spec_boxed ("even-row-color",
738                                                                P_("Even Row Color"),
739                                                                P_("Color to use for even rows"),
740                                                                GDK_TYPE_COLOR,
741                                                                GTK_PARAM_READABLE));
742
743   gtk_widget_class_install_style_property (widget_class,
744                                            g_param_spec_boxed ("odd-row-color",
745                                                                P_("Odd Row Color"),
746                                                                P_("Color to use for odd rows"),
747                                                                GDK_TYPE_COLOR,
748                                                                GTK_PARAM_READABLE));
749
750   gtk_widget_class_install_style_property (widget_class,
751                                            g_param_spec_boolean ("row-ending-details",
752                                                                  P_("Row Ending details"),
753                                                                  P_("Enable extended row background theming"),
754                                                                  FALSE,
755                                                                  GTK_PARAM_READABLE));
756
757   gtk_widget_class_install_style_property (widget_class,
758                                            g_param_spec_int ("grid-line-width",
759                                                              P_("Grid line width"),
760                                                              P_("Width, in pixels, of the tree view grid lines"),
761                                                              0, G_MAXINT, 1,
762                                                              GTK_PARAM_READABLE));
763
764   gtk_widget_class_install_style_property (widget_class,
765                                            g_param_spec_int ("tree-line-width",
766                                                              P_("Tree line width"),
767                                                              P_("Width, in pixels, of the tree view lines"),
768                                                              0, G_MAXINT, 1,
769                                                              GTK_PARAM_READABLE));
770
771   gtk_widget_class_install_style_property (widget_class,
772                                            g_param_spec_string ("tree-line-pattern",
773                                                                 P_("Tree line pattern"),
774                                                                 P_("Dash pattern used to draw the tree view lines"),
775                                                                 "\1\1",
776                                                                 GTK_PARAM_READABLE));
777
778   /* Signals */
779 #if GTK3_TRANSITION
780   /**
781    * PsppSheetView::set-scroll-adjustments
782    * @horizontal: the horizontal #GtkAdjustment
783    * @vertical: the vertical #GtkAdjustment
784    *
785    * Set the scroll adjustments for the tree view. Usually scrolled containers
786    * like #GtkScrolledWindow will emit this signal to connect two instances
787    * of #GtkScrollbar to the scroll directions of the #PsppSheetView.
788    */
789   widget_class->set_scroll_adjustments_signal =
790     g_signal_new ("set-scroll-adjustments",
791                   G_TYPE_FROM_CLASS (o_class),
792                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
793                   G_STRUCT_OFFSET (PsppSheetViewClass, set_scroll_adjustments),
794                   NULL, NULL,
795                   psppire_marshal_VOID__OBJECT_OBJECT,
796                   G_TYPE_NONE, 2,
797                   GTK_TYPE_ADJUSTMENT,
798                   GTK_TYPE_ADJUSTMENT);
799 #endif
800
801   /**
802    * PsppSheetView::row-activated:
803    * @tree_view: the object on which the signal is emitted
804    * @path: the #GtkTreePath for the activated row
805    * @column: the #PsppSheetViewColumn in which the activation occurred
806    *
807    * The "row-activated" signal is emitted when the method
808    * pspp_sheet_view_row_activated() is called or the user double clicks 
809    * a treeview row. It is also emitted when a non-editable row is 
810    * selected and one of the keys: Space, Shift+Space, Return or 
811    * Enter is pressed.
812    * 
813    * For selection handling refer to the <link linkend="TreeWidget">tree 
814    * widget conceptual overview</link> as well as #PsppSheetSelection.
815    */
816   tree_view_signals[ROW_ACTIVATED] =
817     g_signal_new ("row-activated",
818                   G_TYPE_FROM_CLASS (o_class),
819                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
820                   G_STRUCT_OFFSET (PsppSheetViewClass, row_activated),
821                   NULL, NULL,
822                   psppire_marshal_VOID__BOXED_OBJECT,
823                   G_TYPE_NONE, 2,
824                   GTK_TYPE_TREE_PATH,
825                   PSPP_TYPE_SHEET_VIEW_COLUMN);
826
827   /**
828    * PsppSheetView::columns-changed:
829    * @tree_view: the object on which the signal is emitted 
830    * 
831    * The number of columns of the treeview has changed.
832    */
833   tree_view_signals[COLUMNS_CHANGED] =
834     g_signal_new ("columns-changed",
835                   G_TYPE_FROM_CLASS (o_class),
836                   G_SIGNAL_RUN_LAST,
837                   G_STRUCT_OFFSET (PsppSheetViewClass, columns_changed),
838                   NULL, NULL,
839                   g_cclosure_marshal_VOID__VOID,
840                   G_TYPE_NONE, 0);
841
842   /**
843    * PsppSheetView::cursor-changed:
844    * @tree_view: the object on which the signal is emitted
845    * 
846    * The position of the cursor (focused cell) has changed.
847    */
848   tree_view_signals[CURSOR_CHANGED] =
849     g_signal_new ("cursor-changed",
850                   G_TYPE_FROM_CLASS (o_class),
851                   G_SIGNAL_RUN_LAST,
852                   G_STRUCT_OFFSET (PsppSheetViewClass, cursor_changed),
853                   NULL, NULL,
854                   g_cclosure_marshal_VOID__VOID,
855                   G_TYPE_NONE, 0);
856
857   tree_view_signals[MOVE_CURSOR] =
858     g_signal_new ("move-cursor",
859                   G_TYPE_FROM_CLASS (o_class),
860                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
861                   G_STRUCT_OFFSET (PsppSheetViewClass, move_cursor),
862                   NULL, NULL,
863                   psppire_marshal_BOOLEAN__ENUM_INT,
864                   G_TYPE_BOOLEAN, 2,
865                   GTK_TYPE_MOVEMENT_STEP,
866                   G_TYPE_INT);
867
868   tree_view_signals[SELECT_ALL] =
869     g_signal_new ("select-all",
870                   G_TYPE_FROM_CLASS (o_class),
871                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
872                   G_STRUCT_OFFSET (PsppSheetViewClass, select_all),
873                   NULL, NULL,
874                   psppire_marshal_BOOLEAN__VOID,
875                   G_TYPE_BOOLEAN, 0);
876
877   tree_view_signals[UNSELECT_ALL] =
878     g_signal_new ("unselect-all",
879                   G_TYPE_FROM_CLASS (o_class),
880                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
881                   G_STRUCT_OFFSET (PsppSheetViewClass, unselect_all),
882                   NULL, NULL,
883                   psppire_marshal_BOOLEAN__VOID,
884                   G_TYPE_BOOLEAN, 0);
885
886   tree_view_signals[SELECT_CURSOR_ROW] =
887     g_signal_new ("select-cursor-row",
888                   G_TYPE_FROM_CLASS (o_class),
889                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
890                   G_STRUCT_OFFSET (PsppSheetViewClass, select_cursor_row),
891                   NULL, NULL,
892                   psppire_marshal_BOOLEAN__BOOLEAN,
893                   G_TYPE_BOOLEAN, 2,
894                   G_TYPE_BOOLEAN, G_TYPE_INT);
895
896   tree_view_signals[TOGGLE_CURSOR_ROW] =
897     g_signal_new ("toggle-cursor-row",
898                   G_TYPE_FROM_CLASS (o_class),
899                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
900                   G_STRUCT_OFFSET (PsppSheetViewClass, toggle_cursor_row),
901                   NULL, NULL,
902                   psppire_marshal_BOOLEAN__VOID,
903                   G_TYPE_BOOLEAN, 0);
904
905   tree_view_signals[START_INTERACTIVE_SEARCH] =
906     g_signal_new ("start-interactive-search",
907                   G_TYPE_FROM_CLASS (o_class),
908                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
909                   G_STRUCT_OFFSET (PsppSheetViewClass, start_interactive_search),
910                   NULL, NULL,
911                   psppire_marshal_BOOLEAN__VOID,
912                   G_TYPE_BOOLEAN, 0);
913
914   /* Key bindings */
915   for (i = 0; i < 2; i++)
916     {
917       pspp_sheet_view_add_move_binding (binding_set[i], GDK_Up, 0, TRUE,
918                                         GTK_MOVEMENT_DISPLAY_LINES, -1);
919       pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Up, 0, TRUE,
920                                         GTK_MOVEMENT_DISPLAY_LINES, -1);
921
922       pspp_sheet_view_add_move_binding (binding_set[i], GDK_Down, 0, TRUE,
923                                         GTK_MOVEMENT_DISPLAY_LINES, 1);
924       pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Down, 0, TRUE,
925                                         GTK_MOVEMENT_DISPLAY_LINES, 1);
926
927       pspp_sheet_view_add_move_binding (binding_set[i], GDK_p, GDK_CONTROL_MASK, FALSE,
928                                         GTK_MOVEMENT_DISPLAY_LINES, -1);
929
930       pspp_sheet_view_add_move_binding (binding_set[i], GDK_n, GDK_CONTROL_MASK, FALSE,
931                                         GTK_MOVEMENT_DISPLAY_LINES, 1);
932
933       pspp_sheet_view_add_move_binding (binding_set[i], GDK_Home, 0, TRUE,
934                                         GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
935       pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Home, 0, TRUE,
936                                         GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
937
938       pspp_sheet_view_add_move_binding (binding_set[i], GDK_End, 0, TRUE,
939                                         GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
940       pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_End, 0, TRUE,
941                                         GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
942
943       pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Up, 0, TRUE,
944                                         GTK_MOVEMENT_PAGES, -1);
945       pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Up, 0, TRUE,
946                                         GTK_MOVEMENT_PAGES, -1);
947
948       pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Down, 0, TRUE,
949                                         GTK_MOVEMENT_PAGES, 1);
950       pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Down, 0, TRUE,
951                                         GTK_MOVEMENT_PAGES, 1);
952
953
954       gtk_binding_entry_add_signal (binding_set[i], GDK_Up, GDK_CONTROL_MASK, "move-cursor", 2,
955                                     G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
956                                     G_TYPE_INT, -1);
957
958       gtk_binding_entry_add_signal (binding_set[i], GDK_Down, GDK_CONTROL_MASK, "move-cursor", 2,
959                                     G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
960                                     G_TYPE_INT, 1);
961
962       gtk_binding_entry_add_signal (binding_set[i], GDK_Right, 0, "move-cursor", 2,
963                                     G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
964                                     G_TYPE_INT, 1);
965
966       gtk_binding_entry_add_signal (binding_set[i], GDK_Left, 0, "move-cursor", 2,
967                                     G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
968                                     G_TYPE_INT, -1);
969
970       gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2,
971                                     G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
972                                     G_TYPE_INT, 1);
973
974       gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2,
975                                     G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
976                                     G_TYPE_INT, -1);
977
978       gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2,
979                                     G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
980                                     G_TYPE_INT, 1);
981
982       gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, 0, "move-cursor", 2,
983                                     G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
984                                     G_TYPE_INT, -1);
985
986       gtk_binding_entry_add_signal (binding_set[i], GDK_Right, GDK_CONTROL_MASK,
987                                     "move-cursor", 2,
988                                     G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
989                                     G_TYPE_INT, 1);
990
991       gtk_binding_entry_add_signal (binding_set[i], GDK_Left, GDK_CONTROL_MASK,
992                                     "move-cursor", 2,
993                                     G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
994                                     G_TYPE_INT, -1);
995
996       gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, GDK_CONTROL_MASK,
997                                     "move-cursor", 2,
998                                     G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
999                                     G_TYPE_INT, 1);
1000
1001       gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, GDK_CONTROL_MASK,
1002                                     "move-cursor", 2,
1003                                     G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1004                                     G_TYPE_INT, -1);
1005
1006       gtk_binding_entry_add_signal (binding_set[i], GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
1007
1008       gtk_binding_entry_add_signal (binding_set[i], GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
1009     }
1010
1011   gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1012   gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1013
1014   gtk_binding_entry_add_signal (binding_set[0], GDK_a, GDK_CONTROL_MASK, "select-all", 0);
1015   gtk_binding_entry_add_signal (binding_set[0], GDK_slash, GDK_CONTROL_MASK, "select-all", 0);
1016
1017   gtk_binding_entry_add_signal (binding_set[0], GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
1018   gtk_binding_entry_add_signal (binding_set[0], GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
1019
1020   gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1021                                 G_TYPE_BOOLEAN, TRUE,
1022                                 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1023   gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1024                                 G_TYPE_BOOLEAN, TRUE,
1025                                 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1026
1027   gtk_binding_entry_add_signal (binding_set[0], GDK_space, 0, "select-cursor-row", 1,
1028                                 G_TYPE_BOOLEAN, TRUE,
1029                                 G_TYPE_INT, 0);
1030   gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, 0, "select-cursor-row", 1,
1031                                 G_TYPE_BOOLEAN, TRUE,
1032                                 G_TYPE_INT, 0);
1033   gtk_binding_entry_add_signal (binding_set[0], GDK_Return, 0, "select-cursor-row", 1,
1034                                 G_TYPE_BOOLEAN, TRUE,
1035                                 G_TYPE_INT, 0);
1036   gtk_binding_entry_add_signal (binding_set[0], GDK_ISO_Enter, 0, "select-cursor-row", 1,
1037                                 G_TYPE_BOOLEAN, TRUE,
1038                                 G_TYPE_INT, 0);
1039   gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Enter, 0, "select-cursor-row", 1,
1040                                 G_TYPE_BOOLEAN, TRUE,
1041                                 G_TYPE_INT, 0);
1042
1043   gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, 0, "select-cursor-parent", 0);
1044   gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
1045
1046   g_type_class_add_private (o_class, sizeof (PsppSheetViewPrivate));
1047 }
1048
1049 static void
1050 pspp_sheet_view_buildable_init (GtkBuildableIface *iface)
1051 {
1052   iface->add_child = pspp_sheet_view_buildable_add_child;
1053 }
1054
1055 static void
1056 pspp_sheet_view_init (PsppSheetView *tree_view)
1057 {
1058   tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, PSPP_TYPE_SHEET_VIEW, PsppSheetViewPrivate);
1059
1060   gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1061   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
1062
1063   tree_view->priv->flags =  PSPP_SHEET_VIEW_DRAW_KEYFOCUS
1064                             | PSPP_SHEET_VIEW_HEADERS_VISIBLE;
1065
1066   /* We need some padding */
1067   tree_view->priv->selected = range_tower_create ();
1068   tree_view->priv->dy = 0;
1069   tree_view->priv->cursor_offset = 0;
1070   tree_view->priv->n_columns = 0;
1071   tree_view->priv->header_height = 1;
1072   tree_view->priv->x_drag = 0;
1073   tree_view->priv->drag_pos = -1;
1074   tree_view->priv->header_has_focus = FALSE;
1075   tree_view->priv->pressed_button = -1;
1076   tree_view->priv->press_start_x = -1;
1077   tree_view->priv->press_start_y = -1;
1078   tree_view->priv->reorderable = FALSE;
1079   tree_view->priv->presize_handler_timer = 0;
1080   tree_view->priv->scroll_sync_timer = 0;
1081   tree_view->priv->fixed_height = -1;
1082   tree_view->priv->fixed_height_set = FALSE;
1083   pspp_sheet_view_set_adjustments (tree_view, NULL, NULL);
1084   tree_view->priv->selection = _pspp_sheet_selection_new_with_tree_view (tree_view);
1085   tree_view->priv->enable_search = TRUE;
1086   tree_view->priv->search_column = -1;
1087   tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
1088   tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
1089   tree_view->priv->search_custom_entry_set = FALSE;
1090   tree_view->priv->typeselect_flush_timeout = 0;
1091   tree_view->priv->init_hadjust_value = TRUE;    
1092   tree_view->priv->width = 0;
1093           
1094   tree_view->priv->hover_selection = FALSE;
1095
1096   tree_view->priv->rubber_banding_enable = FALSE;
1097
1098   tree_view->priv->grid_lines = PSPP_SHEET_VIEW_GRID_LINES_NONE;
1099
1100   tree_view->priv->tooltip_column = -1;
1101
1102   tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT;
1103
1104   tree_view->priv->post_validation_flag = FALSE;
1105
1106   tree_view->priv->last_button_x = -1;
1107   tree_view->priv->last_button_y = -1;
1108
1109   tree_view->priv->event_last_x = -10000;
1110   tree_view->priv->event_last_y = -10000;
1111
1112   tree_view->priv->prelight_node = -1;
1113   tree_view->priv->rubber_band_start_node = -1;
1114   tree_view->priv->rubber_band_end_node = -1;
1115
1116   tree_view->priv->anchor_column = NULL;
1117
1118   tree_view->priv->button_style = NULL;
1119
1120   tree_view->dispose_has_run = FALSE;
1121
1122   pspp_sheet_view_do_set_vadjustment (tree_view, NULL);
1123   pspp_sheet_view_do_set_hadjustment (tree_view, NULL);
1124   gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (tree_view)),
1125                                GTK_STYLE_CLASS_VIEW);
1126 }
1127
1128 \f
1129
1130 /* GObject Methods
1131  */
1132
1133 static void
1134 pspp_sheet_view_set_property (GObject         *object,
1135                             guint            prop_id,
1136                             const GValue    *value,
1137                             GParamSpec      *pspec)
1138 {
1139   PsppSheetView *tree_view;
1140
1141   tree_view = PSPP_SHEET_VIEW (object);
1142
1143   switch (prop_id)
1144     {
1145     case PROP_MODEL:
1146       pspp_sheet_view_set_model (tree_view, g_value_get_object (value));
1147       break;
1148     case PROP_HADJUSTMENT:
1149       pspp_sheet_view_do_set_hadjustment (tree_view, g_value_get_object (value));
1150       break;
1151     case PROP_VADJUSTMENT:
1152       pspp_sheet_view_do_set_vadjustment (tree_view, g_value_get_object (value));
1153       break;
1154     case PROP_HSCROLL_POLICY:
1155       tree_view->priv->hscroll_policy = g_value_get_enum (value);
1156       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1157       break;
1158     case PROP_VSCROLL_POLICY:
1159       tree_view->priv->vscroll_policy = g_value_get_enum (value);
1160       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1161       break;
1162      case PROP_HEADERS_VISIBLE:
1163       pspp_sheet_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1164       break;
1165     case PROP_HEADERS_CLICKABLE:
1166       pspp_sheet_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1167       break;
1168     case PROP_REORDERABLE:
1169       pspp_sheet_view_set_reorderable (tree_view, g_value_get_boolean (value));
1170       break;
1171     case PROP_RULES_HINT:
1172       pspp_sheet_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1173       break;
1174     case PROP_ENABLE_SEARCH:
1175       pspp_sheet_view_set_enable_search (tree_view, g_value_get_boolean (value));
1176       break;
1177     case PROP_SEARCH_COLUMN:
1178       pspp_sheet_view_set_search_column (tree_view, g_value_get_int (value));
1179       break;
1180     case PROP_HOVER_SELECTION:
1181       tree_view->priv->hover_selection = g_value_get_boolean (value);
1182       break;
1183     case PROP_RUBBER_BANDING:
1184       tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
1185       break;
1186     case PROP_ENABLE_GRID_LINES:
1187       pspp_sheet_view_set_grid_lines (tree_view, g_value_get_enum (value));
1188       break;
1189     case PROP_TOOLTIP_COLUMN:
1190       pspp_sheet_view_set_tooltip_column (tree_view, g_value_get_int (value));
1191       break;
1192     case PROP_SPECIAL_CELLS:
1193       pspp_sheet_view_set_special_cells (tree_view, g_value_get_enum (value));
1194       break;
1195     case PROP_FIXED_HEIGHT:
1196       pspp_sheet_view_set_fixed_height (tree_view, g_value_get_int (value));
1197       break;
1198     case PROP_FIXED_HEIGHT_SET:
1199       if (g_value_get_boolean (value))
1200         {
1201           if (!tree_view->priv->fixed_height_set
1202               && tree_view->priv->fixed_height >= 0)
1203             {
1204               tree_view->priv->fixed_height_set = true;
1205               g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1206             }
1207         }
1208       else
1209         {
1210           if (tree_view->priv->fixed_height_set)
1211             {
1212               tree_view->priv->fixed_height_set = false;
1213               g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1214               install_presize_handler (tree_view);
1215             }
1216         }
1217       break;
1218     default:
1219       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1220       break;
1221     }
1222 }
1223
1224 static void
1225 pspp_sheet_view_get_property (GObject    *object,
1226                             guint       prop_id,
1227                             GValue     *value,
1228                             GParamSpec *pspec)
1229 {
1230   PsppSheetView *tree_view;
1231
1232   tree_view = PSPP_SHEET_VIEW (object);
1233
1234   switch (prop_id)
1235     {
1236     case PROP_MODEL:
1237       g_value_set_object (value, tree_view->priv->model);
1238       break;
1239     case PROP_HADJUSTMENT:
1240       g_value_set_object (value, tree_view->priv->hadjustment);
1241       break;
1242     case PROP_VADJUSTMENT:
1243       g_value_set_object (value, tree_view->priv->vadjustment);
1244       break;
1245     case PROP_HSCROLL_POLICY:
1246       g_value_set_enum (value, tree_view->priv->hscroll_policy);
1247       break;
1248     case PROP_VSCROLL_POLICY:
1249       g_value_set_enum (value, tree_view->priv->vscroll_policy);
1250       break;
1251     case PROP_HEADERS_VISIBLE:
1252       g_value_set_boolean (value, pspp_sheet_view_get_headers_visible (tree_view));
1253       break;
1254     case PROP_HEADERS_CLICKABLE:
1255       g_value_set_boolean (value, pspp_sheet_view_get_headers_clickable (tree_view));
1256       break;
1257     case PROP_REORDERABLE:
1258       g_value_set_boolean (value, tree_view->priv->reorderable);
1259       break;
1260     case PROP_RULES_HINT:
1261       g_value_set_boolean (value, tree_view->priv->has_rules);
1262       break;
1263     case PROP_ENABLE_SEARCH:
1264       g_value_set_boolean (value, tree_view->priv->enable_search);
1265       break;
1266     case PROP_SEARCH_COLUMN:
1267       g_value_set_int (value, tree_view->priv->search_column);
1268       break;
1269     case PROP_HOVER_SELECTION:
1270       g_value_set_boolean (value, tree_view->priv->hover_selection);
1271       break;
1272     case PROP_RUBBER_BANDING:
1273       g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
1274       break;
1275     case PROP_ENABLE_GRID_LINES:
1276       g_value_set_enum (value, tree_view->priv->grid_lines);
1277       break;
1278     case PROP_TOOLTIP_COLUMN:
1279       g_value_set_int (value, tree_view->priv->tooltip_column);
1280       break;
1281     case PROP_SPECIAL_CELLS:
1282       g_value_set_enum (value, tree_view->priv->special_cells);
1283       break;
1284     case PROP_FIXED_HEIGHT:
1285       g_value_set_int (value, pspp_sheet_view_get_fixed_height (tree_view));
1286       break;
1287     case PROP_FIXED_HEIGHT_SET:
1288       g_value_set_boolean (value, tree_view->priv->fixed_height_set);
1289       break;
1290     default:
1291       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1292       break;
1293     }
1294 }
1295
1296 static void
1297 pspp_sheet_view_dispose (GObject *object)
1298 {
1299   PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1300
1301   if (tree_view->dispose_has_run)
1302     return;
1303
1304   tree_view->dispose_has_run = TRUE;
1305
1306   if (tree_view->priv->selection != NULL)
1307     {
1308       _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL);
1309       g_object_unref (tree_view->priv->selection);
1310       tree_view->priv->selection = NULL;
1311     }
1312
1313   if (tree_view->priv->hadjustment)
1314     {
1315       g_object_unref (tree_view->priv->hadjustment);
1316       tree_view->priv->hadjustment = NULL;
1317     }
1318   if (tree_view->priv->vadjustment)
1319     {
1320       g_object_unref (tree_view->priv->vadjustment);
1321       tree_view->priv->vadjustment = NULL;
1322     }
1323
1324   if (tree_view->priv->button_style)
1325     {
1326       g_object_unref (tree_view->priv->button_style);
1327       tree_view->priv->button_style = NULL;
1328     }
1329
1330
1331   G_OBJECT_CLASS (pspp_sheet_view_parent_class)->dispose (object);
1332 }
1333
1334 \f
1335
1336 static void
1337 pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
1338                                    GtkBuilder  *builder,
1339                                    GObject     *child,
1340                                    const gchar *type)
1341 {
1342   pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_COLUMN (child));
1343 }
1344
1345 static void
1346 pspp_sheet_view_finalize (GObject *object)
1347 {
1348   PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1349
1350   pspp_sheet_view_stop_editing (tree_view, TRUE);
1351
1352   if (tree_view->priv->selected != NULL)
1353     {
1354       range_tower_destroy (tree_view->priv->selected);
1355       tree_view->priv->selected = NULL;
1356     }
1357
1358
1359   tree_view->priv->prelight_node = -1;
1360
1361
1362   if (tree_view->priv->scroll_to_path != NULL)
1363     {
1364       gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
1365       tree_view->priv->scroll_to_path = NULL;
1366     }
1367
1368   if (tree_view->priv->drag_dest_row != NULL)
1369     {
1370       gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
1371       tree_view->priv->drag_dest_row = NULL;
1372     }
1373
1374   if (tree_view->priv->top_row != NULL)
1375     {
1376       gtk_tree_row_reference_free (tree_view->priv->top_row);
1377       tree_view->priv->top_row = NULL;
1378     }
1379
1380   if (tree_view->priv->column_drop_func_data &&
1381       tree_view->priv->column_drop_func_data_destroy)
1382     {
1383       tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
1384       tree_view->priv->column_drop_func_data = NULL;
1385     }
1386
1387   if (tree_view->priv->destroy_count_destroy &&
1388       tree_view->priv->destroy_count_data)
1389     {
1390       tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
1391       tree_view->priv->destroy_count_data = NULL;
1392     }
1393
1394   gtk_tree_row_reference_free (tree_view->priv->cursor);
1395   tree_view->priv->cursor = NULL;
1396
1397   gtk_tree_row_reference_free (tree_view->priv->anchor);
1398   tree_view->priv->anchor = NULL;
1399
1400   /* destroy interactive search dialog */
1401   if (tree_view->priv->search_window)
1402     {
1403       gtk_widget_destroy (tree_view->priv->search_window);
1404       tree_view->priv->search_window = NULL;
1405       tree_view->priv->search_entry = NULL;
1406       if (tree_view->priv->typeselect_flush_timeout)
1407         {
1408           g_source_remove (tree_view->priv->typeselect_flush_timeout);
1409           tree_view->priv->typeselect_flush_timeout = 0;
1410         }
1411     }
1412
1413   if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
1414     {
1415       tree_view->priv->search_destroy (tree_view->priv->search_user_data);
1416       tree_view->priv->search_user_data = NULL;
1417     }
1418
1419   if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
1420     {
1421       tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
1422       tree_view->priv->search_position_user_data = NULL;
1423     }
1424
1425   pspp_sheet_view_set_model (tree_view, NULL);
1426
1427
1428   G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object);
1429 }
1430
1431 \f
1432
1433 /* GtkWidget Methods
1434  */
1435
1436 /* GtkWidget::map helper */
1437 static void
1438 pspp_sheet_view_map_buttons (PsppSheetView *tree_view)
1439 {
1440   GList *list;
1441
1442   g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
1443
1444   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
1445     {
1446       PsppSheetViewColumn *column;
1447
1448       for (list = tree_view->priv->columns; list; list = list->next)
1449         {
1450           column = list->data;
1451           if (column->button != NULL &&
1452               gtk_widget_get_visible (column->button) &&
1453               !gtk_widget_get_mapped (column->button))
1454             gtk_widget_map (column->button);
1455         }
1456       for (list = tree_view->priv->columns; list; list = list->next)
1457         {
1458           column = list->data;
1459           if (column->visible == FALSE || column->window == NULL)
1460             continue;
1461           if (column->resizable)
1462             {
1463               gdk_window_raise (column->window);
1464               gdk_window_show (column->window);
1465             }
1466           else
1467             gdk_window_hide (column->window);
1468         }
1469       gdk_window_show (tree_view->priv->header_window);
1470     }
1471 }
1472
1473 static void
1474 pspp_sheet_view_map (GtkWidget *widget)
1475 {
1476   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1477   GList *tmp_list;
1478
1479   gtk_widget_set_mapped (widget, TRUE);
1480
1481   tmp_list = tree_view->priv->children;
1482   while (tmp_list)
1483     {
1484       PsppSheetViewChild *child = tmp_list->data;
1485       tmp_list = tmp_list->next;
1486
1487       if (gtk_widget_get_visible (child->widget))
1488         {
1489           if (!gtk_widget_get_mapped (child->widget))
1490             gtk_widget_map (child->widget);
1491         }
1492     }
1493   gdk_window_show (tree_view->priv->bin_window);
1494
1495   pspp_sheet_view_map_buttons (tree_view);
1496
1497   gdk_window_show (gtk_widget_get_window (widget));
1498 }
1499
1500 static void
1501 pspp_sheet_view_realize (GtkWidget *widget)
1502 {
1503   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1504   GList *tmp_list;
1505   GdkWindow *window;
1506   GdkWindowAttr attributes;
1507   gint attributes_mask;
1508   GtkAllocation allocation;
1509
1510   gtk_widget_set_realized (widget, TRUE);
1511
1512   gtk_widget_get_allocation (widget, &allocation);
1513
1514   /* Make the main, clipping window */
1515   attributes.window_type = GDK_WINDOW_CHILD;
1516   attributes.x =      allocation.x;
1517   attributes.y =      allocation.y;
1518   attributes.width =  allocation.width;
1519   attributes.height = allocation.height;
1520   attributes.wclass = GDK_INPUT_OUTPUT;
1521   attributes.visual = gtk_widget_get_visual (widget);
1522   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1523
1524   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1525
1526   window = gdk_window_new (gtk_widget_get_parent_window (widget),
1527                            &attributes, attributes_mask);
1528   gtk_widget_set_window (widget, window);
1529
1530 #if GTK_CHECK_VERSION(3,8,0)
1531   gtk_widget_register_window (widget, window);
1532 #else
1533   gdk_window_set_user_data (window, widget);
1534 #endif
1535   gtk_widget_get_allocation (widget, &allocation);
1536
1537   /* Make the window for the tree */
1538   attributes.x = 0;
1539   attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1540   attributes.width = MAX (tree_view->priv->width, allocation.width);
1541   attributes.height = allocation.height;
1542   attributes.event_mask = (GDK_EXPOSURE_MASK |
1543                            GDK_SCROLL_MASK |
1544                            GDK_POINTER_MOTION_MASK |
1545                            GDK_ENTER_NOTIFY_MASK |
1546                            GDK_LEAVE_NOTIFY_MASK |
1547                            GDK_BUTTON_PRESS_MASK |
1548                            GDK_BUTTON_RELEASE_MASK |
1549                            gtk_widget_get_events (widget));
1550
1551   tree_view->priv->bin_window = gdk_window_new (window,
1552                                                 &attributes, attributes_mask);
1553 #if GTK_CHECK_VERSION(3,8,0)
1554   gtk_widget_register_window (widget, tree_view->priv->bin_window);
1555 #else
1556   gdk_window_set_user_data (tree_view->priv->bin_window, widget);
1557 #endif
1558   gtk_widget_get_allocation (widget, &allocation);
1559
1560   /* Make the column header window */
1561   attributes.x = 0;
1562   attributes.y = 0;
1563   attributes.width = MAX (tree_view->priv->width, allocation.width);
1564   attributes.height = tree_view->priv->header_height;
1565   attributes.event_mask = (GDK_EXPOSURE_MASK |
1566                            GDK_SCROLL_MASK |
1567                            GDK_BUTTON_PRESS_MASK |
1568                            GDK_BUTTON_RELEASE_MASK |
1569                            GDK_KEY_PRESS_MASK |
1570                            GDK_KEY_RELEASE_MASK |
1571                            gtk_widget_get_events (widget));
1572
1573   tree_view->priv->header_window = gdk_window_new (window,
1574                                                    &attributes, attributes_mask);
1575 #if GTK_CHECK_VERSION(3,8,0)
1576   gtk_widget_register_window (widget, tree_view->priv->header_window);
1577 #else
1578   gdk_window_set_user_data (tree_view->priv->header_window, widget);
1579 #endif
1580
1581   { /* Ensure Background */
1582     GtkStyleContext *context;
1583
1584     context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
1585
1586     gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
1587     gtk_style_context_set_background (context, tree_view->priv->header_window);
1588   }
1589
1590   tmp_list = tree_view->priv->children;
1591   while (tmp_list)
1592     {
1593       PsppSheetViewChild *child = tmp_list->data;
1594       tmp_list = tmp_list->next;
1595
1596       gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1597     }
1598
1599   for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1600     _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data));
1601
1602   /* Need to call those here, since they create GCs */
1603   pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1604
1605   install_presize_handler (tree_view); 
1606 }
1607
1608 static void
1609 pspp_sheet_view_unrealize (GtkWidget *widget)
1610 {
1611   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1612   PsppSheetViewPrivate *priv = tree_view->priv;
1613   GList *list;
1614
1615   GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget);
1616
1617   if (priv->scroll_timeout != 0)
1618     {
1619       g_source_remove (priv->scroll_timeout);
1620       priv->scroll_timeout = 0;
1621     }
1622
1623   if (priv->open_dest_timeout != 0)
1624     {
1625       g_source_remove (priv->open_dest_timeout);
1626       priv->open_dest_timeout = 0;
1627     }
1628
1629   if (priv->presize_handler_timer != 0)
1630     {
1631       g_source_remove (priv->presize_handler_timer);
1632       priv->presize_handler_timer = 0;
1633     }
1634
1635   if (priv->validate_rows_timer != 0)
1636     {
1637       g_source_remove (priv->validate_rows_timer);
1638       priv->validate_rows_timer = 0;
1639     }
1640
1641   if (priv->scroll_sync_timer != 0)
1642     {
1643       g_source_remove (priv->scroll_sync_timer);
1644       priv->scroll_sync_timer = 0;
1645     }
1646
1647   if (priv->typeselect_flush_timeout)
1648     {
1649       g_source_remove (priv->typeselect_flush_timeout);
1650       priv->typeselect_flush_timeout = 0;
1651     }
1652   
1653   for (list = priv->columns; list; list = list->next)
1654     _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data));
1655
1656   gdk_window_set_user_data (priv->bin_window, NULL);
1657   gdk_window_destroy (priv->bin_window);
1658   priv->bin_window = NULL;
1659
1660   gdk_window_set_user_data (priv->header_window, NULL);
1661   gdk_window_destroy (priv->header_window);
1662   priv->header_window = NULL;
1663
1664   if (priv->drag_window)
1665     {
1666       gdk_window_set_user_data (priv->drag_window, NULL);
1667       gdk_window_destroy (priv->drag_window);
1668       priv->drag_window = NULL;
1669     }
1670
1671   if (priv->drag_highlight_window)
1672     {
1673       gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1674       gdk_window_destroy (priv->drag_highlight_window);
1675       priv->drag_highlight_window = NULL;
1676     }
1677
1678   if (tree_view->priv->columns != NULL)
1679     {
1680       list = tree_view->priv->columns;
1681       while (list)
1682         {
1683           PsppSheetViewColumn *column;
1684           column = PSPP_SHEET_VIEW_COLUMN (list->data);
1685           list = list->next;
1686           pspp_sheet_view_remove_column (tree_view, column);
1687         }
1688       tree_view->priv->columns = NULL;
1689     }
1690 }
1691
1692 /* GtkWidget::size_request helper */
1693 static void
1694 pspp_sheet_view_size_request_columns (PsppSheetView *tree_view)
1695 {
1696   GList *list;
1697
1698   tree_view->priv->header_height = 0;
1699
1700   if (tree_view->priv->model)
1701     {
1702       for (list = tree_view->priv->columns; list; list = list->next)
1703         {
1704           GtkRequisition requisition;
1705           PsppSheetViewColumn *column = list->data;
1706
1707           pspp_sheet_view_column_size_request (column, &requisition);
1708           column->button_request = requisition.width;
1709           tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1710         }
1711     }
1712 }
1713
1714
1715 /* Called only by ::size_request */
1716 static void
1717 pspp_sheet_view_update_size (PsppSheetView *tree_view)
1718 {
1719   GList *list;
1720   PsppSheetViewColumn *column;
1721   gint i;
1722
1723   if (tree_view->priv->model == NULL)
1724     {
1725       tree_view->priv->width = 0;
1726       tree_view->priv->prev_width = 0;                   
1727       tree_view->priv->height = 0;
1728       return;
1729     }
1730
1731   tree_view->priv->prev_width = tree_view->priv->width;  
1732   tree_view->priv->width = 0;
1733
1734   /* keep this in sync with size_allocate below */
1735   for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
1736     {
1737       gint real_requested_width = 0;
1738       column = list->data;
1739       if (!column->visible)
1740         continue;
1741
1742       if (column->use_resized_width)
1743         {
1744           real_requested_width = column->resized_width;
1745         }
1746       else
1747         {
1748           real_requested_width = column->fixed_width;
1749         }
1750
1751       if (column->min_width != -1)
1752         real_requested_width = MAX (real_requested_width, column->min_width);
1753       if (column->max_width != -1)
1754         real_requested_width = MIN (real_requested_width, column->max_width);
1755
1756       tree_view->priv->width += real_requested_width;
1757     }
1758
1759   tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count;
1760 }
1761
1762 static void
1763 pspp_sheet_view_size_request (GtkWidget      *widget,
1764                             GtkRequisition *requisition)
1765 {
1766   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1767   GList *tmp_list;
1768
1769   /* we validate some rows initially just to make sure we have some size. 
1770    * In practice, with a lot of static lists, this should get a good width.
1771    */
1772   initialize_fixed_height_mode (tree_view);
1773   pspp_sheet_view_size_request_columns (tree_view);
1774   pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget));
1775
1776   requisition->width = tree_view->priv->width;
1777   requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
1778
1779   tmp_list = tree_view->priv->children;
1780
1781   while (tmp_list)
1782     {
1783       PsppSheetViewChild *child = tmp_list->data;
1784       GtkRequisition child_requisition;
1785
1786       tmp_list = tmp_list->next;
1787
1788       if (gtk_widget_get_visible (child->widget))
1789         gtk_widget_size_request (child->widget, &child_requisition);
1790     }
1791 }
1792
1793 static void
1794 invalidate_column (PsppSheetView       *tree_view,
1795                    PsppSheetViewColumn *column)
1796 {
1797   gint column_offset = 0;
1798   GList *list;
1799   GtkWidget *widget = GTK_WIDGET (tree_view);
1800   gboolean rtl;
1801
1802   if (!gtk_widget_get_realized (widget))
1803     return;
1804
1805   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1806   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1807        list;
1808        list = (rtl ? list->prev : list->next))
1809     {
1810       PsppSheetViewColumn *tmpcolumn = list->data;
1811       if (tmpcolumn == column)
1812         {
1813           GdkRectangle invalid_rect;
1814           GtkAllocation allocation;
1815
1816           gtk_widget_get_allocation (widget, &allocation);
1817           invalid_rect.x = column_offset;
1818           invalid_rect.y = 0;
1819           invalid_rect.width = column->width;
1820           invalid_rect.height = allocation.height;
1821           
1822           gdk_window_invalidate_rect (gtk_widget_get_window (widget), &invalid_rect, TRUE);
1823           break;
1824         }
1825       
1826       column_offset += tmpcolumn->width;
1827     }
1828 }
1829
1830 static void
1831 invalidate_last_column (PsppSheetView *tree_view)
1832 {
1833   GList *last_column;
1834   gboolean rtl;
1835
1836   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1837
1838   for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1839        last_column;
1840        last_column = (rtl ? last_column->next : last_column->prev))
1841     {
1842       if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1843         {
1844           invalidate_column (tree_view, last_column->data);
1845           return;
1846         }
1847     }
1848 }
1849
1850 static gint
1851 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView       *tree_view,
1852                                                     PsppSheetViewColumn *column)
1853 {
1854   gint real_requested_width;
1855
1856   if (column->use_resized_width)
1857     {
1858       real_requested_width = column->resized_width;
1859     }
1860   else
1861     {
1862       real_requested_width = column->fixed_width;
1863     }
1864
1865   if (column->min_width != -1)
1866     real_requested_width = MAX (real_requested_width, column->min_width);
1867   if (column->max_width != -1)
1868     real_requested_width = MIN (real_requested_width, column->max_width);
1869
1870   return real_requested_width;
1871 }
1872
1873 static gboolean
1874 span_intersects (int a0, int a_width,
1875                  int b0, int b_width)
1876 {
1877   int a1 = a0 + a_width;
1878   int b1 = b0 + b_width;
1879   return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1880 }
1881
1882 /* GtkWidget::size_allocate helper */
1883 static void
1884 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1885                                      gboolean  *width_changed)
1886 {
1887   PsppSheetView *tree_view;
1888   GList *list, *first_column, *last_column;
1889   PsppSheetViewColumn *column;
1890   GtkAllocation col_allocation;
1891   GtkAllocation allocation;
1892   gint width = 0;
1893   gint extra, extra_per_column;
1894   gint full_requested_width = 0;
1895   gint number_of_expand_columns = 0;
1896   gboolean column_changed = FALSE;
1897   gboolean rtl;
1898
1899   tree_view = PSPP_SHEET_VIEW (widget);
1900
1901   for (last_column = g_list_last (tree_view->priv->columns);
1902        last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1903        last_column = last_column->prev)
1904     ;
1905
1906   if (last_column == NULL)
1907     return;
1908
1909   for (first_column = g_list_first (tree_view->priv->columns);
1910        first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1911        first_column = first_column->next)
1912     ;
1913
1914   col_allocation.y = 0;
1915   col_allocation.height = tree_view->priv->header_height;
1916
1917   rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1918
1919   /* find out how many extra space and expandable columns we have */
1920   for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1921     {
1922       column = (PsppSheetViewColumn *)list->data;
1923
1924       if (!column->visible)
1925         continue;
1926
1927       full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1928
1929       if (column->expand)
1930         number_of_expand_columns++;
1931     }
1932
1933   gtk_widget_get_allocation (widget, &allocation);
1934   extra = MAX (allocation.width - full_requested_width, 0);
1935   if (number_of_expand_columns > 0)
1936     extra_per_column = extra/number_of_expand_columns;
1937   else
1938     extra_per_column = 0;
1939
1940   for (list = (rtl ? last_column : first_column); 
1941        list != (rtl ? first_column->prev : last_column->next);
1942        list = (rtl ? list->prev : list->next)) 
1943     {
1944       gint real_requested_width = 0;
1945       gint old_width;
1946
1947       column = list->data;
1948       old_width = column->width;
1949
1950       if (!column->visible)
1951         continue;
1952
1953       /* We need to handle the dragged button specially.
1954        */
1955       if (column == tree_view->priv->drag_column)
1956         {
1957           GtkAllocation drag_allocation;
1958           drag_allocation.width =  gdk_window_get_width (tree_view->priv->drag_window);
1959           drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
1960           drag_allocation.x = 0;
1961           drag_allocation.y = 0;
1962           pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1963                                                 &drag_allocation);
1964           width += drag_allocation.width;
1965           continue;
1966         }
1967
1968       real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1969
1970       col_allocation.x = width;
1971       column->width = real_requested_width;
1972
1973       if (column->expand)
1974         {
1975           if (number_of_expand_columns == 1)
1976             {
1977               /* We add the remander to the last column as
1978                * */
1979               column->width += extra;
1980             }
1981           else
1982             {
1983               column->width += extra_per_column;
1984               extra -= extra_per_column;
1985               number_of_expand_columns --;
1986             }
1987         }
1988
1989       if (column->width != old_width)
1990         g_object_notify (G_OBJECT (column), "width");
1991
1992       col_allocation.width = column->width;
1993       width += column->width;
1994
1995       if (column->width > old_width)
1996         column_changed = TRUE;
1997
1998       pspp_sheet_view_column_size_allocate (column, &col_allocation);
1999
2000       if (column->window)
2001         gdk_window_move_resize (column->window,
2002                                 col_allocation.x + (rtl ? 0 : col_allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
2003                                 col_allocation.y,
2004                                 TREE_VIEW_DRAG_WIDTH, col_allocation.height);
2005     }
2006
2007   /* We change the width here.  The user might have been resizing columns,
2008    * so the total width of the tree view changes.
2009    */
2010   tree_view->priv->width = width;
2011   if (width_changed)
2012     *width_changed = TRUE;
2013
2014   if (column_changed)
2015     gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2016 }
2017
2018 static void
2019 pspp_sheet_view_size_allocate (GtkWidget     *widget,
2020                              GtkAllocation *allocation)
2021 {
2022   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2023   GList *tmp_list;
2024   gboolean width_changed = FALSE;
2025   GtkAllocation old_allocation;
2026   gtk_widget_get_allocation (widget, &old_allocation);
2027
2028   if (allocation->width != old_allocation.width)
2029     width_changed = TRUE;
2030
2031
2032   gtk_widget_set_allocation (widget, allocation);
2033
2034   tmp_list = tree_view->priv->children;
2035
2036   while (tmp_list)
2037     {
2038       GtkAllocation allocation;
2039
2040       PsppSheetViewChild *child = tmp_list->data;
2041       tmp_list = tmp_list->next;
2042
2043       /* totally ignore our child's requisition */
2044       allocation.x = child->x;
2045       allocation.y = child->y;
2046       allocation.width = child->width;
2047       allocation.height = child->height;
2048       gtk_widget_size_allocate (child->widget, &allocation);
2049     }
2050
2051   /* We size-allocate the columns first because the width of the
2052    * tree view (used in updating the adjustments below) might change.
2053    */
2054   pspp_sheet_view_size_allocate_columns (widget, &width_changed);
2055
2056   gtk_adjustment_set_page_size (tree_view->priv->hadjustment, allocation->width);
2057   gtk_adjustment_set_page_increment (tree_view->priv->hadjustment, allocation->width * 0.9);
2058   gtk_adjustment_set_step_increment (tree_view->priv->hadjustment, allocation->width * 0.1);
2059   gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2060   gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment), tree_view->priv->width));
2061
2062   if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)   
2063     {
2064       if (allocation->width < tree_view->priv->width)
2065         {
2066           if (tree_view->priv->init_hadjust_value)
2067             {
2068               gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2069               tree_view->priv->init_hadjust_value = FALSE;
2070             }
2071           else if (allocation->width != old_allocation.width)
2072             {
2073               gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) - allocation->width + old_allocation.width, 0, tree_view->priv->width - allocation->width));
2074             }
2075           else
2076             gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (tree_view->priv->width - (tree_view->priv->prev_width - gtk_adjustment_get_value (tree_view->priv->hadjustment)), 0, tree_view->priv->width - allocation->width));
2077         }
2078       else
2079         {
2080           gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2081           tree_view->priv->init_hadjust_value = TRUE;
2082         }
2083     }
2084   else
2085     if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width)
2086       gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2087
2088   gtk_adjustment_changed (tree_view->priv->hadjustment);
2089
2090   gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2091   gtk_adjustment_set_step_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.1);
2092   gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.9);
2093   gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
2094   gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->vadjustment), tree_view->priv->height));
2095
2096   gtk_adjustment_changed (tree_view->priv->vadjustment);
2097
2098   /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2099   if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
2100     gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2101   else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
2102     gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2103                               tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
2104   else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2105     pspp_sheet_view_top_row_to_dy (tree_view);
2106   else
2107     pspp_sheet_view_dy_to_top_row (tree_view);
2108   
2109   if (gtk_widget_get_realized (widget))
2110     {
2111       gdk_window_move_resize (gtk_widget_get_window (widget),
2112                               allocation->x, allocation->y,
2113                               allocation->width, allocation->height);
2114       gdk_window_move_resize (tree_view->priv->header_window,
2115                               - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2116                               0,
2117                               MAX (tree_view->priv->width, allocation->width),
2118                               tree_view->priv->header_height);
2119       gdk_window_move_resize (tree_view->priv->bin_window,
2120                               - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2121                               TREE_VIEW_HEADER_HEIGHT (tree_view),
2122                               MAX (tree_view->priv->width, allocation->width),
2123                               allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2124     }
2125
2126   if (tree_view->priv->row_count == 0)
2127     invalidate_empty_focus (tree_view);
2128
2129   if (gtk_widget_get_realized (widget))
2130     {
2131       gboolean has_expand_column = FALSE;
2132       for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2133         {
2134           if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2135             {
2136               has_expand_column = TRUE;
2137               break;
2138             }
2139         }
2140
2141       /* This little hack only works if we have an LTR locale, and no column has the  */
2142       if (width_changed)
2143         {
2144           if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2145               ! has_expand_column)
2146             invalidate_last_column (tree_view);
2147           else
2148             gtk_widget_queue_draw (widget);
2149         }
2150     }
2151 }
2152
2153 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2154 static void
2155 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2156 {
2157   GtkWidget *widget = GTK_WIDGET (tree_view);
2158
2159   if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2160     gtk_widget_grab_focus (widget);
2161   PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2162 }
2163
2164 gboolean
2165 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2166                                   int node)
2167 {
2168   return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2169 }
2170
2171 void
2172 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2173                              int node)
2174 {
2175   range_tower_set1 (tree_view->priv->selected, node, 1);
2176 }
2177
2178 void
2179 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2180                                int node)
2181 {
2182   range_tower_set0 (tree_view->priv->selected, node, 1);
2183 }
2184
2185 gint
2186 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2187                            gint node)
2188 {
2189   return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2190 }
2191
2192 gint
2193 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2194                            gint node)
2195 {
2196   return node > 0 ? node - 1 : -1;
2197 }
2198
2199 static gboolean
2200 all_columns_selected (PsppSheetView *tree_view)
2201 {
2202   GList *list;
2203
2204   for (list = tree_view->priv->columns; list; list = list->next)
2205     {
2206       PsppSheetViewColumn *column = list->data;
2207       if (column->selectable && !column->selected)
2208         return FALSE;
2209     }
2210
2211   return TRUE;
2212 }
2213
2214 static gboolean
2215 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2216                                   gint node,
2217                                   PsppSheetViewColumn *column,
2218                                   GdkEventButton *event)
2219 {
2220   PsppSheetSelection *selection;
2221   PsppSheetSelectionMode mode;
2222   GtkTreePath *path;
2223   gboolean update_anchor;
2224   gboolean handled;
2225   guint modifiers;
2226
2227   g_return_val_if_fail (tree_view != NULL, FALSE);
2228   g_return_val_if_fail (column != NULL, FALSE);
2229
2230   selection = tree_view->priv->selection;
2231   mode = pspp_sheet_selection_get_mode (selection);
2232   if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2233     return FALSE;
2234
2235   if (!column->row_head)
2236     return FALSE;
2237
2238   if (event)
2239     {
2240       modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2241       if (event->type != GDK_BUTTON_PRESS
2242           || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2243         return FALSE;
2244     }
2245   else
2246     modifiers = 0;
2247
2248   path = gtk_tree_path_new_from_indices (node, -1);
2249   if (event == NULL)
2250     {
2251       pspp_sheet_selection_unselect_all (selection);
2252       pspp_sheet_selection_select_path (selection, path);
2253       pspp_sheet_selection_select_all_columns (selection);
2254       update_anchor = TRUE;
2255       handled = TRUE;
2256     }
2257   else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2258     {
2259       if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2260           || !all_columns_selected (tree_view))
2261         {
2262           pspp_sheet_selection_unselect_all (selection);
2263           pspp_sheet_selection_select_path (selection, path);
2264           pspp_sheet_selection_select_all_columns (selection);
2265           update_anchor = TRUE;
2266           handled = FALSE;
2267         }
2268       else
2269         update_anchor = handled = FALSE;
2270     }
2271   else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2272            && modifiers == GDK_CONTROL_MASK)
2273     {
2274       if (!all_columns_selected (tree_view))
2275         {
2276           pspp_sheet_selection_unselect_all (selection);
2277           pspp_sheet_selection_select_all_columns (selection);
2278         }
2279
2280       if (pspp_sheet_selection_path_is_selected (selection, path))
2281         pspp_sheet_selection_unselect_path (selection, path);
2282       else
2283         pspp_sheet_selection_select_path (selection, path);
2284       update_anchor = TRUE;
2285       handled = TRUE;
2286     }
2287   else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2288            && modifiers == GDK_SHIFT_MASK)
2289     {
2290       GtkTreeRowReference *anchor = tree_view->priv->anchor;
2291       GtkTreePath *anchor_path;
2292
2293       if (all_columns_selected (tree_view)
2294           && gtk_tree_row_reference_valid (anchor))
2295         {
2296           update_anchor = FALSE;
2297           anchor_path = gtk_tree_row_reference_get_path (anchor);
2298         }
2299       else
2300         {
2301           update_anchor = TRUE;
2302           anchor_path = gtk_tree_path_copy (path);
2303         }
2304
2305       pspp_sheet_selection_unselect_all (selection);
2306       pspp_sheet_selection_select_range (selection, anchor_path, path);
2307       pspp_sheet_selection_select_all_columns (selection);
2308
2309       gtk_tree_path_free (anchor_path);
2310
2311       handled = TRUE;
2312     }
2313   else
2314     update_anchor = handled = FALSE;
2315
2316   if (update_anchor)
2317     {
2318       if (tree_view->priv->anchor)
2319         gtk_tree_row_reference_free (tree_view->priv->anchor);
2320       tree_view->priv->anchor =
2321         gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2322                                           tree_view->priv->model,
2323                                           path);
2324     }
2325
2326   gtk_tree_path_free (path);
2327   return handled;
2328 }
2329
2330 static gboolean
2331 find_click (PsppSheetView *tree_view,
2332             gint x, gint y,
2333             gint *node,
2334             PsppSheetViewColumn **column,
2335             GdkRectangle *background_area,
2336             GdkRectangle *cell_area)
2337 {
2338   gint y_offset;
2339   gboolean rtl;
2340   GList *list;
2341   gint new_y;
2342
2343   /* find the node that was clicked */
2344   new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2345   if (new_y < 0)
2346     new_y = 0;
2347   y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2348
2349   if (*node < 0)
2350     return FALSE;
2351
2352   background_area->y = y_offset + y;
2353   background_area->height = ROW_HEIGHT (tree_view);
2354   background_area->x = 0;
2355
2356   /* Let the column have a chance at selecting it. */
2357   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2358   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2359        list; list = (rtl ? list->prev : list->next))
2360     {
2361       PsppSheetViewColumn *candidate = list->data;
2362
2363       if (!candidate->visible)
2364         continue;
2365
2366       background_area->width = candidate->width;
2367       if ((background_area->x > x) ||
2368           (background_area->x + background_area->width <= x))
2369         {
2370           background_area->x += background_area->width;
2371           continue;
2372         }
2373
2374       /* we found the focus column */
2375
2376       pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2377                                         TRUE, cell_area);
2378       *column = candidate;
2379       return TRUE;
2380     }
2381
2382   return FALSE;
2383 }
2384
2385 static gboolean
2386 pspp_sheet_view_button_press (GtkWidget      *widget,
2387                             GdkEventButton *event)
2388 {
2389   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2390   GList *list;
2391   PsppSheetViewColumn *column = NULL;
2392   gint i;
2393   GdkRectangle background_area;
2394   GdkRectangle cell_area;
2395   gboolean rtl;
2396
2397   rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2398   pspp_sheet_view_stop_editing (tree_view, FALSE);
2399
2400
2401   /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2402    * we're done handling the button press.
2403    */
2404
2405   if (event->window == tree_view->priv->bin_window)
2406     {
2407       int node;
2408       GtkTreePath *path;
2409       gint dval;
2410       gint pre_val, aft_val;
2411       PsppSheetViewColumn *column = NULL;
2412       GtkCellRenderer *focus_cell = NULL;
2413       gboolean row_double_click = FALSE;
2414
2415       /* Empty tree? */
2416       if (tree_view->priv->row_count == 0)
2417         {
2418           grab_focus_and_unset_draw_keyfocus (tree_view);
2419           return TRUE;
2420         }
2421
2422       if (!find_click (tree_view, event->x, event->y, &node, &column,
2423                        &background_area, &cell_area))
2424         {
2425           grab_focus_and_unset_draw_keyfocus (tree_view);
2426           return FALSE;
2427         }
2428
2429       tree_view->priv->focus_column = column;
2430
2431       if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2432         return TRUE;
2433
2434       /* select */
2435       pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2436
2437       path = _pspp_sheet_view_find_path (tree_view, node);
2438
2439       /* we only handle selection modifications on the first button press
2440        */
2441       if (event->type == GDK_BUTTON_PRESS)
2442         {
2443           PsppSheetSelectionMode mode = 0;
2444
2445           if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2446             mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
2447           if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2448             mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
2449
2450           focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2451           if (focus_cell)
2452             pspp_sheet_view_column_focus_cell (column, focus_cell);
2453
2454           if (event->state & GDK_CONTROL_MASK)
2455             {
2456               pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode);
2457               pspp_sheet_view_real_toggle_cursor_row (tree_view);
2458             }
2459           else if (event->state & GDK_SHIFT_MASK)
2460             {
2461               pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
2462               pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode);
2463             }
2464           else
2465             {
2466               pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
2467             }
2468
2469           if (tree_view->priv->anchor_column == NULL ||
2470               !(event->state & GDK_SHIFT_MASK))
2471             tree_view->priv->anchor_column = column;
2472           pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2473           pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2474                                                     tree_view->priv->anchor_column,
2475                                                     column);
2476         }
2477
2478       /* the treeview may have been scrolled because of _set_cursor,
2479        * correct here
2480        */
2481
2482       aft_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2483       dval = pre_val - aft_val;
2484
2485       cell_area.y += dval;
2486       background_area.y += dval;
2487
2488       /* Save press to possibly begin a drag
2489        */
2490       if (!tree_view->priv->in_grab &&
2491           tree_view->priv->pressed_button < 0)
2492         {
2493           tree_view->priv->pressed_button = event->button;
2494           tree_view->priv->press_start_x = event->x;
2495           tree_view->priv->press_start_y = event->y;
2496           tree_view->priv->press_start_node = node;
2497
2498           if (tree_view->priv->rubber_banding_enable
2499               && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2500                   tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2501             {
2502               tree_view->priv->press_start_y += tree_view->priv->dy;
2503               tree_view->priv->rubber_band_x = event->x;
2504               tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2505               tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2506
2507               if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2508                 tree_view->priv->rubber_band_ctrl = TRUE;
2509               if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2510                 tree_view->priv->rubber_band_shift = TRUE;
2511
2512             }
2513         }
2514
2515       /* Test if a double click happened on the same row. */
2516       if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2517         {
2518           int double_click_time, double_click_distance;
2519
2520           g_object_get (gtk_settings_get_for_screen (
2521                           gtk_widget_get_screen (widget)),
2522                         "gtk-double-click-time", &double_click_time,
2523                         "gtk-double-click-distance", &double_click_distance,
2524                         NULL);
2525
2526           /* Same conditions as _gdk_event_button_generate */
2527           if (tree_view->priv->last_button_x != -1 &&
2528               (event->time < tree_view->priv->last_button_time + double_click_time) &&
2529               (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2530               (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2531             {
2532               /* We do no longer compare paths of this row and the
2533                * row clicked previously.  We use the double click
2534                * distance to decide whether this is a valid click,
2535                * allowing the mouse to slightly move over another row.
2536                */
2537               row_double_click = TRUE;
2538
2539               tree_view->priv->last_button_time = 0;
2540               tree_view->priv->last_button_x = -1;
2541               tree_view->priv->last_button_y = -1;
2542             }
2543           else
2544             {
2545               tree_view->priv->last_button_time = event->time;
2546               tree_view->priv->last_button_x = event->x;
2547               tree_view->priv->last_button_y = event->y;
2548             }
2549         }
2550
2551       if (row_double_click)
2552         {
2553           gtk_grab_remove (widget);
2554           pspp_sheet_view_row_activated (tree_view, path, column);
2555
2556           if (tree_view->priv->pressed_button == event->button)
2557             tree_view->priv->pressed_button = -1;
2558         }
2559
2560       gtk_tree_path_free (path);
2561
2562       /* If we activated the row through a double click we don't want to grab
2563        * focus back, as moving focus to another widget is pretty common.
2564        */
2565       if (!row_double_click)
2566         grab_focus_and_unset_draw_keyfocus (tree_view);
2567
2568       return TRUE;
2569     }
2570
2571   /* We didn't click in the window.  Let's check to see if we clicked on a column resize window.
2572    */
2573   for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2574     {
2575       column = list->data;
2576       if (event->window == column->window &&
2577           column->resizable &&
2578           column->window)
2579         {
2580           gpointer drag_data;
2581
2582           if (gdk_pointer_grab (column->window, FALSE,
2583                                 GDK_POINTER_MOTION_HINT_MASK |
2584                                 GDK_BUTTON1_MOTION_MASK |
2585                                 GDK_BUTTON_RELEASE_MASK,
2586                                 NULL, NULL, event->time))
2587             return FALSE;
2588
2589           gtk_grab_add (widget);
2590           PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2591           column->resized_width = column->width;
2592
2593           /* block attached dnd signal handler */
2594           drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2595           if (drag_data)
2596             g_signal_handlers_block_matched (widget,
2597                                              G_SIGNAL_MATCH_DATA,
2598                                              0, 0, NULL, NULL,
2599                                              drag_data);
2600
2601           tree_view->priv->drag_pos = i;
2602           tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2603
2604           if (!gtk_widget_has_focus (widget))
2605             gtk_widget_grab_focus (widget);
2606
2607           return TRUE;
2608         }
2609     }
2610   return FALSE;
2611 }
2612
2613 /* GtkWidget::button_release_event helper */
2614 static gboolean
2615 pspp_sheet_view_button_release_drag_column (GtkWidget      *widget,
2616                                           GdkEventButton *event)
2617 {
2618   PsppSheetView *tree_view;
2619   GList *l;
2620   gboolean rtl;
2621
2622   tree_view = PSPP_SHEET_VIEW (widget);
2623
2624   rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2625   gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2626   gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2627
2628   /* Move the button back */
2629   g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2630
2631   g_object_ref (tree_view->priv->drag_column->button);
2632   gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2633   gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2634   gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2635   g_object_unref (tree_view->priv->drag_column->button);
2636   gtk_widget_queue_resize (widget);
2637   if (tree_view->priv->drag_column->resizable)
2638     {
2639       gdk_window_raise (tree_view->priv->drag_column->window);
2640       gdk_window_show (tree_view->priv->drag_column->window);
2641     }
2642   else
2643     gdk_window_hide (tree_view->priv->drag_column->window);
2644
2645   gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2646
2647   if (rtl)
2648     {
2649       if (tree_view->priv->cur_reorder &&
2650           tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2651         pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2652                                          tree_view->priv->cur_reorder->right_column);
2653     }
2654   else
2655     {
2656       if (tree_view->priv->cur_reorder &&
2657           tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2658         pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2659                                          tree_view->priv->cur_reorder->left_column);
2660     }
2661   tree_view->priv->drag_column = NULL;
2662   gdk_window_hide (tree_view->priv->drag_window);
2663
2664   for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2665     g_slice_free (PsppSheetViewColumnReorder, l->data);
2666   g_list_free (tree_view->priv->column_drag_info);
2667   tree_view->priv->column_drag_info = NULL;
2668   tree_view->priv->cur_reorder = NULL;
2669
2670   if (tree_view->priv->drag_highlight_window)
2671     gdk_window_hide (tree_view->priv->drag_highlight_window);
2672
2673   /* Reset our flags */
2674   tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2675   PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2676
2677   return TRUE;
2678 }
2679
2680 /* GtkWidget::button_release_event helper */
2681 static gboolean
2682 pspp_sheet_view_button_release_column_resize (GtkWidget      *widget,
2683                                             GdkEventButton *event)
2684 {
2685   PsppSheetView *tree_view;
2686   gpointer drag_data;
2687
2688   tree_view = PSPP_SHEET_VIEW (widget);
2689
2690   tree_view->priv->drag_pos = -1;
2691
2692   /* unblock attached dnd signal handler */
2693   drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2694   if (drag_data)
2695     g_signal_handlers_unblock_matched (widget,
2696                                        G_SIGNAL_MATCH_DATA,
2697                                        0, 0, NULL, NULL,
2698                                        drag_data);
2699
2700   PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2701   gtk_grab_remove (widget);
2702   gdk_display_pointer_ungrab (gdk_window_get_display (event->window),
2703                               event->time);
2704   return TRUE;
2705 }
2706
2707 static gboolean
2708 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2709                                      GdkEventButton *event)
2710 {
2711   GtkCellEditable *cell_editable;
2712   gchar *path_string;
2713   GtkTreePath *path;
2714   gint left, right;
2715   GtkTreeIter iter;
2716   PsppSheetViewColumn *column;
2717   GdkRectangle background_area;
2718   GdkRectangle cell_area;
2719   GdkRectangle area;
2720   guint modifiers;
2721   guint flags;
2722   int node;
2723
2724   if (event->window != tree_view->priv->bin_window)
2725     return FALSE;
2726
2727   /* Ignore a released button, if that button wasn't depressed */
2728   if (tree_view->priv->pressed_button != event->button)
2729     return FALSE;
2730
2731   if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2732                    &cell_area))
2733     return FALSE;
2734
2735   /* decide if we edit */
2736   path = _pspp_sheet_view_find_path (tree_view, node);
2737   modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2738   if (event->button != 1 || modifiers)
2739     return FALSE;
2740
2741   gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2742   pspp_sheet_view_column_cell_set_cell_data (column,
2743                                              tree_view->priv->model,
2744                                              &iter);
2745
2746   if (!pspp_sheet_view_column_get_quick_edit (column)
2747       && _pspp_sheet_view_column_has_editable_cell (column))
2748     return FALSE;
2749
2750   flags = 0;                    /* FIXME: get the right flags */
2751   path_string = gtk_tree_path_to_string (path);
2752
2753   if (!_pspp_sheet_view_column_cell_event (column,
2754                                            &cell_editable,
2755                                            (GdkEvent *)event,
2756                                            path_string,
2757                                            &background_area,
2758                                            &cell_area, flags))
2759     return FALSE;
2760
2761   if (cell_editable == NULL)
2762     return FALSE;
2763
2764   pspp_sheet_view_real_set_cursor (tree_view, path,
2765                                    TRUE, TRUE, 0); /* XXX mode? */
2766   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2767
2768   area = cell_area;
2769   _pspp_sheet_view_column_get_neighbor_sizes (
2770     column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2771
2772   area.x += left;
2773   area.width -= right + left;
2774
2775   pspp_sheet_view_real_start_editing (tree_view,
2776                                       column,
2777                                       path,
2778                                       cell_editable,
2779                                       &area,
2780                                       (GdkEvent *)event,
2781                                       flags);
2782   g_free (path_string);
2783   gtk_tree_path_free (path);
2784   return TRUE;
2785 }
2786
2787 static gboolean
2788 pspp_sheet_view_button_release (GtkWidget      *widget,
2789                               GdkEventButton *event)
2790 {
2791   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2792
2793   pspp_sheet_view_stop_editing (tree_view, FALSE);
2794   if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2795       && pspp_sheet_view_button_release_edit (tree_view, event))
2796     {
2797       if (tree_view->priv->pressed_button == event->button)
2798         tree_view->priv->pressed_button = -1;
2799
2800       tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2801       return TRUE;
2802     }
2803
2804   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2805     return pspp_sheet_view_button_release_drag_column (widget, event);
2806
2807   if (tree_view->priv->rubber_band_status)
2808     pspp_sheet_view_stop_rubber_band (tree_view);
2809
2810   if (tree_view->priv->pressed_button == event->button)
2811     tree_view->priv->pressed_button = -1;
2812
2813   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2814     return pspp_sheet_view_button_release_column_resize (widget, event);
2815
2816   return FALSE;
2817 }
2818
2819 static gboolean
2820 pspp_sheet_view_grab_broken (GtkWidget          *widget,
2821                            GdkEventGrabBroken *event)
2822 {
2823   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2824
2825   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2826     pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2827
2828   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2829     pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2830
2831   return TRUE;
2832 }
2833
2834 /* GtkWidget::motion_event function set.
2835  */
2836
2837 static void
2838 do_prelight (PsppSheetView *tree_view,
2839              int node,
2840              /* these are in bin_window coords */
2841              gint         x,
2842              gint         y)
2843 {
2844   int prev_node = tree_view->priv->prelight_node;
2845
2846   if (prev_node != node)
2847     {
2848       tree_view->priv->prelight_node = node;
2849
2850       if (prev_node >= 0)
2851         _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2852
2853       if (node >= 0)
2854         _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2855     }
2856 }
2857
2858
2859 static void
2860 prelight_or_select (PsppSheetView *tree_view,
2861                     int node,
2862                     /* these are in bin_window coords */
2863                     gint         x,
2864                     gint         y)
2865 {
2866   PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2867   
2868   if (tree_view->priv->hover_selection &&
2869       (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2870       !(tree_view->priv->edited_column &&
2871         tree_view->priv->edited_column->editable_widget))
2872     {
2873       if (node >= 0)
2874         {
2875           if (!pspp_sheet_view_node_is_selected (tree_view, node))
2876             {
2877               GtkTreePath *path;
2878               
2879               path = _pspp_sheet_view_find_path (tree_view, node);
2880               pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2881               if (pspp_sheet_view_node_is_selected (tree_view, node))
2882                 {
2883                   PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2884                   pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */
2885                 }
2886               gtk_tree_path_free (path);
2887             }
2888         }
2889
2890       else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2891         pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2892     }
2893
2894     do_prelight (tree_view, node, x, y);
2895 }
2896
2897 static void
2898 ensure_unprelighted (PsppSheetView *tree_view)
2899 {
2900   do_prelight (tree_view,
2901                -1,
2902                -1000, -1000); /* coords not possibly over an arrow */
2903
2904   g_assert (tree_view->priv->prelight_node < 0);
2905 }
2906
2907 static void
2908 update_prelight (PsppSheetView *tree_view,
2909                  gint         x,
2910                  gint         y)
2911 {
2912   int new_y;
2913   int node;
2914
2915   if (tree_view->priv->row_count == 0)
2916     return;
2917
2918   if (x == -10000)
2919     {
2920       ensure_unprelighted (tree_view);
2921       return;
2922     }
2923
2924   new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2925   if (new_y < 0)
2926     new_y = 0;
2927
2928   pspp_sheet_view_find_offset (tree_view, new_y, &node);
2929
2930   if (node >= 0)
2931     prelight_or_select (tree_view, node, x, y);
2932 }
2933
2934
2935
2936
2937 /* Our motion arrow is either a box (in the case of the original spot)
2938  * or an arrow.  It is expander_size wide.
2939  */
2940 /*
2941  * 11111111111111
2942  * 01111111111110
2943  * 00111111111100
2944  * 00011111111000
2945  * 00001111110000
2946  * 00000111100000
2947  * 00000111100000
2948  * 00000111100000
2949  * ~ ~ ~ ~ ~ ~ ~
2950  * 00000111100000
2951  * 00000111100000
2952  * 00000111100000
2953  * 00001111110000
2954  * 00011111111000
2955  * 00111111111100
2956  * 01111111111110
2957  * 11111111111111
2958  */
2959
2960 static void
2961 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2962 {
2963 #if GTK3_TRANSITION
2964   PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2965   GtkWidget *widget = GTK_WIDGET (tree_view);
2966   GdkBitmap *mask = NULL;
2967   gint x;
2968   gint y;
2969   gint width;
2970   gint height;
2971   gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2972   GdkWindowAttr attributes;
2973   guint attributes_mask;
2974
2975   if (!reorder ||
2976       reorder->left_column == tree_view->priv->drag_column ||
2977       reorder->right_column == tree_view->priv->drag_column)
2978     arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2979   else if (reorder->left_column || reorder->right_column)
2980     {
2981       GdkRectangle visible_rect;
2982       pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2983       if (reorder->left_column)
2984         x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2985       else
2986         x = reorder->right_column->allocation.x;
2987
2988       if (x < visible_rect.x)
2989         arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2990       else if (x > visible_rect.x + visible_rect.width)
2991         arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2992       else
2993         arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2994     }
2995
2996   /* We want to draw the rectangle over the initial location. */
2997   if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2998     {
2999       GdkGC *gc;
3000       GdkColor col;
3001
3002       if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
3003         {
3004           if (tree_view->priv->drag_highlight_window)
3005             {
3006               gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3007                                         NULL);
3008               gdk_window_destroy (tree_view->priv->drag_highlight_window);
3009             }
3010
3011           attributes.window_type = GDK_WINDOW_CHILD;
3012           attributes.wclass = GDK_INPUT_OUTPUT;
3013           attributes.x = tree_view->priv->drag_column_x;
3014           attributes.y = 0;
3015           width = attributes.width = tree_view->priv->drag_column->allocation.width;
3016           height = attributes.height = tree_view->priv->drag_column->allocation.height;
3017           attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3018           attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3019           attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3020           attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3021           tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3022           gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3023
3024           mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3025           gc = gdk_gc_new (mask);
3026           col.pixel = 1;
3027           gdk_gc_set_foreground (gc, &col);
3028           gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3029           col.pixel = 0;
3030           gdk_gc_set_foreground(gc, &col);
3031           gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
3032           g_object_unref (gc);
3033
3034           gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3035                                          mask, 0, 0);
3036           if (mask) g_object_unref (mask);
3037           tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3038         }
3039     }
3040   else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3041     {
3042       gint i, j = 1;
3043       GdkGC *gc;
3044       GdkColor col;
3045
3046       width = tree_view->priv->expander_size;
3047
3048       /* Get x, y, width, height of arrow */
3049       gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3050       if (reorder->left_column)
3051         {
3052           x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
3053           height = reorder->left_column->allocation.height;
3054         }
3055       else
3056         {
3057           x += reorder->right_column->allocation.x - width/2;
3058           height = reorder->right_column->allocation.height;
3059         }
3060       y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3061       height += tree_view->priv->expander_size;
3062
3063       /* Create the new window */
3064       if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3065         {
3066           if (tree_view->priv->drag_highlight_window)
3067             {
3068               gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3069                                         NULL);
3070               gdk_window_destroy (tree_view->priv->drag_highlight_window);
3071             }
3072
3073           attributes.window_type = GDK_WINDOW_TEMP;
3074           attributes.wclass = GDK_INPUT_OUTPUT;
3075           attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3076           attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3077           attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3078           attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3079           attributes.x = x;
3080           attributes.y = y;
3081           attributes.width = width;
3082           attributes.height = height;
3083           tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3084                                                                    &attributes, attributes_mask);
3085           gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3086
3087           mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3088           gc = gdk_gc_new (mask);
3089           col.pixel = 1;
3090           gdk_gc_set_foreground (gc, &col);
3091           gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3092
3093           /* Draw the 2 arrows as per above */
3094           col.pixel = 0;
3095           gdk_gc_set_foreground (gc, &col);
3096           for (i = 0; i < width; i ++)
3097             {
3098               if (i == (width/2 - 1))
3099                 continue;
3100               gdk_draw_line (mask, gc, i, j, i, height - j);
3101               if (i < (width/2 - 1))
3102                 j++;
3103               else
3104                 j--;
3105             }
3106           g_object_unref (gc);
3107           gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3108                                          mask, 0, 0);
3109           if (mask) g_object_unref (mask);
3110         }
3111
3112       tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3113       gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3114     }
3115   else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3116            arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3117     {
3118       gint i, j = 1;
3119       GdkGC *gc;
3120       GdkColor col;
3121
3122       width = tree_view->priv->expander_size;
3123
3124       /* Get x, y, width, height of arrow */
3125       width = width/2; /* remember, the arrow only takes half the available width */
3126       gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
3127       if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3128         x += widget->allocation.width - width;
3129
3130       if (reorder->left_column)
3131         height = reorder->left_column->allocation.height;
3132       else
3133         height = reorder->right_column->allocation.height;
3134
3135       y -= tree_view->priv->expander_size;
3136       height += 2*tree_view->priv->expander_size;
3137
3138       /* Create the new window */
3139       if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3140           tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3141         {
3142           if (tree_view->priv->drag_highlight_window)
3143             {
3144               gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3145                                         NULL);
3146               gdk_window_destroy (tree_view->priv->drag_highlight_window);
3147             }
3148
3149           attributes.window_type = GDK_WINDOW_TEMP;
3150           attributes.wclass = GDK_INPUT_OUTPUT;
3151           attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3152           attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3153           attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3154           attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3155           attributes.x = x;
3156           attributes.y = y;
3157           attributes.width = width;
3158           attributes.height = height;
3159           tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3160           gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3161
3162           mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3163           gc = gdk_gc_new (mask);
3164           col.pixel = 1;
3165           gdk_gc_set_foreground (gc, &col);
3166           gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3167
3168           /* Draw the 2 arrows as per above */
3169           col.pixel = 0;
3170           gdk_gc_set_foreground (gc, &col);
3171           j = tree_view->priv->expander_size;
3172           for (i = 0; i < width; i ++)
3173             {
3174               gint k;
3175               if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3176                 k = width - i - 1;
3177               else
3178                 k = i;
3179               gdk_draw_line (mask, gc, k, j, k, height - j);
3180               gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3181               gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3182               j--;
3183             }
3184           g_object_unref (gc);
3185           gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3186                                          mask, 0, 0);
3187           if (mask) g_object_unref (mask);
3188         }
3189
3190       tree_view->priv->drag_column_window_state = arrow_type;
3191       gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3192    }
3193   else
3194     {
3195       g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3196       gdk_window_hide (tree_view->priv->drag_highlight_window);
3197       return;
3198     }
3199
3200   gdk_window_show (tree_view->priv->drag_highlight_window);
3201   gdk_window_raise (tree_view->priv->drag_highlight_window);
3202 #endif
3203 }
3204
3205 static gboolean
3206 pspp_sheet_view_motion_resize_column (GtkWidget      *widget,
3207                                     GdkEventMotion *event)
3208 {
3209   gint x;
3210   gint new_width;
3211   PsppSheetViewColumn *column;
3212   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3213
3214   column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3215
3216   if (event->is_hint || event->window != gtk_widget_get_window (widget))
3217     gtk_widget_get_pointer (widget, &x, NULL);
3218   else
3219     x = event->x;
3220
3221   if (tree_view->priv->hadjustment)
3222     x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
3223
3224   new_width = pspp_sheet_view_new_column_width (tree_view,
3225                                               tree_view->priv->drag_pos, &x);
3226   if (x != tree_view->priv->x_drag &&
3227       (new_width != column->fixed_width))
3228     {
3229       column->use_resized_width = TRUE;
3230       column->resized_width = new_width;
3231 #if 0
3232       if (column->expand)
3233         column->resized_width -= tree_view->priv->last_extra_space_per_column;
3234 #endif
3235       gtk_widget_queue_resize (widget);
3236     }
3237
3238   return FALSE;
3239 }
3240
3241
3242 static void
3243 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3244 {
3245   PsppSheetViewColumnReorder *reorder = NULL;
3246   GList *list;
3247   gint mouse_x;
3248
3249   gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3250   for (list = tree_view->priv->column_drag_info; list; list = list->next)
3251     {
3252       reorder = (PsppSheetViewColumnReorder *) list->data;
3253       if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3254         break;
3255       reorder = NULL;
3256     }
3257
3258   /*  if (reorder && reorder == tree_view->priv->cur_reorder)
3259       return;*/
3260
3261   tree_view->priv->cur_reorder = reorder;
3262   pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3263 }
3264
3265 static void
3266 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3267 {
3268   GdkRectangle visible_rect;
3269   gint y;
3270   gint offset;
3271   gfloat value;
3272
3273   gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3274   y += tree_view->priv->dy;
3275
3276   pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3277
3278   /* see if we are near the edge. */
3279   offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3280   if (offset > 0)
3281     {
3282       offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3283       if (offset < 0)
3284         return;
3285     }
3286
3287   value = CLAMP (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0,
3288                  gtk_adjustment_get_upper (tree_view->priv->vadjustment) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
3289   gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3290 }
3291
3292 static gboolean
3293 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3294 {
3295   GdkRectangle visible_rect;
3296   gint x;
3297   gint offset;
3298   gfloat value;
3299
3300   gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3301
3302   pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3303
3304   /* See if we are near the edge. */
3305   offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3306   if (offset > 0)
3307     {
3308       offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3309       if (offset < 0)
3310         return TRUE;
3311     }
3312   offset = offset/3;
3313
3314   value = CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset,
3315                  0.0, gtk_adjustment_get_upper (tree_view->priv->hadjustment) - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
3316   gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3317
3318   return TRUE;
3319
3320 }
3321
3322 static gboolean
3323 pspp_sheet_view_motion_drag_column (GtkWidget      *widget,
3324                                   GdkEventMotion *event)
3325 {
3326   PsppSheetView *tree_view = (PsppSheetView *) widget;
3327   PsppSheetViewColumn *column = tree_view->priv->drag_column;
3328   gint x, y;
3329   GtkAllocation allocation;
3330
3331   /* Sanity Check */
3332   if ((column == NULL) ||
3333       (event->window != tree_view->priv->drag_window))
3334     return FALSE;
3335
3336   /* Handle moving the header */
3337   gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3338   gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
3339   x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3340              MAX (tree_view->priv->width, allocation.width) - column->allocation.width);
3341   gdk_window_move (tree_view->priv->drag_window, x, y);
3342   
3343   /* autoscroll, if needed */
3344   pspp_sheet_view_horizontal_autoscroll (tree_view);
3345   /* Update the current reorder position and arrow; */
3346   pspp_sheet_view_update_current_reorder (tree_view);
3347
3348   return TRUE;
3349 }
3350
3351 static void
3352 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3353 {
3354   remove_scroll_timeout (tree_view);
3355   gtk_grab_remove (GTK_WIDGET (tree_view));
3356
3357   if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3358     {
3359       GtkTreePath *tmp_path;
3360
3361       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3362
3363       /* The anchor path should be set to the start path */
3364       tmp_path = _pspp_sheet_view_find_path (tree_view,
3365                                            tree_view->priv->rubber_band_start_node);
3366
3367       if (tree_view->priv->anchor)
3368         gtk_tree_row_reference_free (tree_view->priv->anchor);
3369
3370       tree_view->priv->anchor =
3371         gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3372                                           tree_view->priv->model,
3373                                           tmp_path);
3374
3375       gtk_tree_path_free (tmp_path);
3376
3377       /* ... and the cursor to the end path */
3378       tmp_path = _pspp_sheet_view_find_path (tree_view,
3379                                            tree_view->priv->rubber_band_end_node);
3380       pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */
3381       gtk_tree_path_free (tmp_path);
3382
3383       _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3384     }
3385
3386   /* Clear status variables */
3387   tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3388   tree_view->priv->rubber_band_shift = 0;
3389   tree_view->priv->rubber_band_ctrl = 0;
3390
3391   tree_view->priv->rubber_band_start_node = -1;
3392   tree_view->priv->rubber_band_end_node = -1;
3393 }
3394
3395 static void
3396 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3397                                                  int start_node,
3398                                                  int end_node,
3399                                                  gboolean     select,
3400                                                  gboolean     skip_start,
3401                                                  gboolean     skip_end)
3402 {
3403   if (start_node == end_node)
3404     return;
3405
3406   /* We skip the first node and jump inside the loop */
3407   if (skip_start)
3408     goto skip_first;
3409
3410   do
3411     {
3412       /* Small optimization by assuming insensitive nodes are never
3413        * selected.
3414        */
3415       if (select)
3416         {
3417           if (tree_view->priv->rubber_band_shift)
3418             pspp_sheet_view_node_select (tree_view, start_node);
3419           else if (tree_view->priv->rubber_band_ctrl)
3420             {
3421               /* Toggle the selection state */
3422               if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3423                 pspp_sheet_view_node_unselect (tree_view, start_node);
3424               else
3425                 pspp_sheet_view_node_select (tree_view, start_node);
3426             }
3427           else
3428             pspp_sheet_view_node_select (tree_view, start_node);
3429         }
3430       else
3431         {
3432           /* Mirror the above */
3433           if (tree_view->priv->rubber_band_shift)
3434                 pspp_sheet_view_node_unselect (tree_view, start_node);
3435           else if (tree_view->priv->rubber_band_ctrl)
3436             {
3437               /* Toggle the selection state */
3438               if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3439                 pspp_sheet_view_node_unselect (tree_view, start_node);
3440               else
3441                 pspp_sheet_view_node_select (tree_view, start_node);
3442             }
3443           else
3444             pspp_sheet_view_node_unselect (tree_view, start_node);
3445         }
3446
3447       _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3448
3449       if (start_node == end_node)
3450         break;
3451
3452 skip_first:
3453
3454       start_node = pspp_sheet_view_node_next (tree_view, start_node);
3455
3456       if (start_node < 0)
3457         /* Ran out of tree */
3458         break;
3459
3460       if (skip_end && start_node == end_node)
3461         break;
3462     }
3463   while (TRUE);
3464 }
3465
3466 static gint
3467 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3468                                   int node)
3469 {
3470   return node * tree_view->priv->fixed_height;
3471 }
3472
3473 static gint
3474 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3475                              gint height,
3476                              int *new_node)
3477 {
3478   int fixed_height = tree_view->priv->fixed_height;
3479   if (fixed_height <= 0
3480       || height < 0
3481       || height >= tree_view->priv->row_count * fixed_height)
3482     {
3483       *new_node = -1;
3484       return 0;
3485     }
3486   else
3487     {
3488       *new_node = height / fixed_height;
3489       return height % fixed_height;
3490     }
3491 }
3492
3493 static void
3494 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3495 {
3496   int start_node;
3497   int end_node;
3498
3499   pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3500   pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3501
3502   /* Handle the start area first */
3503   if (tree_view->priv->rubber_band_start_node < 0)
3504     {
3505       pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3506                                                        start_node,
3507                                                        end_node,
3508                                                        TRUE,
3509                                                        FALSE,
3510                                                        FALSE);
3511     }
3512   else if (start_node < tree_view->priv->rubber_band_start_node)
3513     {
3514       /* New node is above the old one; selection became bigger */
3515       pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3516                                                        start_node,
3517                                                        tree_view->priv->rubber_band_start_node,
3518                                                        TRUE,
3519                                                        FALSE,
3520                                                        TRUE);
3521     }
3522   else if (start_node > tree_view->priv->rubber_band_start_node)
3523     {
3524       /* New node is below the old one; selection became smaller */
3525       pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3526                                                        tree_view->priv->rubber_band_start_node,
3527                                                        start_node,
3528                                                        FALSE,
3529                                                        FALSE,
3530                                                        TRUE);
3531     }
3532
3533   tree_view->priv->rubber_band_start_node = start_node;
3534
3535   /* Next, handle the end area */
3536   if (tree_view->priv->rubber_band_end_node < 0)
3537     {
3538       /* In the event this happens, start_node was also -1; this case is
3539        * handled above.
3540        */
3541     }
3542   else if (end_node < 0)
3543     {
3544       /* Find the last node in the tree */
3545       pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3546                                &end_node);
3547
3548       /* Selection reached end of the tree */
3549       pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3550                                                        tree_view->priv->rubber_band_end_node,
3551                                                        end_node,
3552                                                        TRUE,
3553                                                        TRUE,
3554                                                        FALSE);
3555     }
3556   else if (end_node > tree_view->priv->rubber_band_end_node)
3557     {
3558       /* New node is below the old one; selection became bigger */
3559       pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3560                                                        tree_view->priv->rubber_band_end_node,
3561                                                        end_node,
3562                                                        TRUE,
3563                                                        TRUE,
3564                                                        FALSE);
3565     }
3566   else if (end_node < tree_view->priv->rubber_band_end_node)
3567     {
3568       /* New node is above the old one; selection became smaller */
3569       pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3570                                                        end_node,
3571                                                        tree_view->priv->rubber_band_end_node,
3572                                                        FALSE,
3573                                                        TRUE,
3574                                                        FALSE);
3575     }
3576
3577   tree_view->priv->rubber_band_end_node = end_node;
3578 }
3579
3580 #define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X))
3581
3582 static void
3583 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3584 {
3585   gint x, y;
3586   cairo_rectangle_int_t old_area;
3587   cairo_rectangle_int_t new_area;
3588   cairo_rectangle_int_t common;
3589   cairo_region_t *invalid_region;
3590   PsppSheetViewColumn *column;
3591
3592   old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3593   old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3594   old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3595   old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3596
3597   gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3598
3599   x = MAX (x, 0);
3600   y = MAX (y, 0) + tree_view->priv->dy;
3601
3602   new_area.x = MIN (tree_view->priv->press_start_x, x);
3603   new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3604   new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3605   new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3606
3607   invalid_region = cairo_region_create_rectangle (&old_area);
3608   cairo_region_union_rectangle (invalid_region, &new_area);
3609
3610   gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area), 
3611                            GDK_RECTANGLE_PTR (&new_area), GDK_RECTANGLE_PTR (&common));
3612   if (common.width > 2 && common.height > 2)
3613     {
3614       cairo_region_t *common_region;
3615
3616       /* make sure the border is invalidated */
3617       common.x += 1;
3618       common.y += 1;
3619       common.width -= 2;
3620       common.height -= 2;
3621
3622       common_region = cairo_region_create_rectangle (&common);
3623
3624       cairo_region_subtract (invalid_region, common_region);
3625       cairo_region_destroy (common_region);
3626     }
3627
3628 #if GTK_MAJOR_VERSION == 3
3629   gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);  
3630 #else
3631   {
3632     cairo_rectangle_int_t extents;
3633     GdkRegion *ereg;
3634     cairo_region_get_extents (invalid_region, &extents);
3635     ereg = gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents));
3636     gdk_window_invalidate_region (tree_view->priv->bin_window, ereg, TRUE);
3637     gdk_region_destroy (ereg);
3638   }
3639 #endif
3640
3641   cairo_region_destroy (invalid_region);
3642
3643   tree_view->priv->rubber_band_x = x;
3644   tree_view->priv->rubber_band_y = y;
3645   pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3646
3647   pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3648   pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3649                                             tree_view->priv->anchor_column,
3650                                             column);
3651
3652   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3653
3654   pspp_sheet_view_update_rubber_band_selection (tree_view);
3655 }
3656
3657 #if GTK3_TRANSITION
3658 static void
3659 pspp_sheet_view_paint_rubber_band (PsppSheetView  *tree_view,
3660                                 GdkRectangle *area)
3661 {
3662   cairo_t *cr;
3663   GdkRectangle rect;
3664   GdkRectangle rubber_rect;
3665   GtkStyle *style;
3666
3667   return;
3668   rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3669   rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3670   rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3671   rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3672
3673   if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3674     return;
3675
3676   cr = gdk_cairo_create (tree_view->priv->bin_window);
3677   cairo_set_line_width (cr, 1.0);
3678
3679   style = gtk_widget_get_style (GTK_WIDGET (tree_view));
3680   cairo_set_source_rgba (cr,
3681                          style->fg[GTK_STATE_NORMAL].red / 65535.,
3682                          style->fg[GTK_STATE_NORMAL].green / 65535.,
3683                          style->fg[GTK_STATE_NORMAL].blue / 65535.,
3684                          .25);
3685
3686   gdk_cairo_rectangle (cr, &rect);
3687   cairo_clip (cr);
3688   cairo_paint (cr);
3689
3690   cairo_set_source_rgb (cr,
3691                         style->fg[GTK_STATE_NORMAL].red / 65535.,
3692                         style->fg[GTK_STATE_NORMAL].green / 65535.,
3693                         style->fg[GTK_STATE_NORMAL].blue / 65535.);
3694
3695   cairo_rectangle (cr,
3696                    rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3697                    rubber_rect.width - 1, rubber_rect.height - 1);
3698   cairo_stroke (cr);
3699
3700   cairo_destroy (cr);
3701 }
3702 #endif
3703
3704
3705 static gboolean
3706 pspp_sheet_view_motion_bin_window (GtkWidget      *widget,
3707                                  GdkEventMotion *event)
3708 {
3709   PsppSheetView *tree_view;
3710   int node;
3711   gint new_y;
3712
3713   tree_view = (PsppSheetView *) widget;
3714
3715   if (tree_view->priv->row_count == 0)
3716     return FALSE;
3717
3718   if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3719     {
3720       GdkRectangle background_area, cell_area;
3721       PsppSheetViewColumn *column;
3722
3723       if (find_click (tree_view, event->x, event->y, &node, &column,
3724                       &background_area, &cell_area)
3725           && tree_view->priv->focus_column == column
3726           && tree_view->priv->press_start_node == node)
3727         return FALSE;
3728
3729       gtk_grab_add (GTK_WIDGET (tree_view));
3730       pspp_sheet_view_update_rubber_band (tree_view);
3731
3732       tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3733     }
3734   else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3735     {
3736       pspp_sheet_view_update_rubber_band (tree_view);
3737
3738       add_scroll_timeout (tree_view);
3739     }
3740
3741   /* only check for an initiated drag when a button is pressed */
3742   if (tree_view->priv->pressed_button >= 0
3743       && !tree_view->priv->rubber_band_status)
3744     pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3745
3746   new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3747   if (new_y < 0)
3748     new_y = 0;
3749
3750   pspp_sheet_view_find_offset (tree_view, new_y, &node);
3751
3752   tree_view->priv->event_last_x = event->x;
3753   tree_view->priv->event_last_y = event->y;
3754
3755   prelight_or_select (tree_view, node, event->x, event->y);
3756
3757   return TRUE;
3758 }
3759
3760 static gboolean
3761 pspp_sheet_view_motion (GtkWidget      *widget,
3762                       GdkEventMotion *event)
3763 {
3764   PsppSheetView *tree_view;
3765
3766   tree_view = (PsppSheetView *) widget;
3767
3768   /* Resizing a column */
3769   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3770     return pspp_sheet_view_motion_resize_column (widget, event);
3771
3772   /* Drag column */
3773   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3774     return pspp_sheet_view_motion_drag_column (widget, event);
3775
3776   /* Sanity check it */
3777   if (event->window == tree_view->priv->bin_window)
3778     return pspp_sheet_view_motion_bin_window (widget, event);
3779
3780   return FALSE;
3781 }
3782
3783 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3784  * the tree is empty.
3785  */
3786 static void
3787 invalidate_empty_focus (PsppSheetView *tree_view)
3788 {
3789   GdkRectangle area;
3790
3791   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3792     return;
3793
3794   area.x = 0;
3795   area.y = 0;
3796   area.width = gdk_window_get_width (tree_view->priv->bin_window);
3797   area.height = gdk_window_get_height (tree_view->priv->bin_window);
3798   gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3799 }
3800
3801 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3802  * is empty.
3803  */
3804 static void
3805 draw_empty_focus (PsppSheetView *tree_view)
3806 {
3807   GtkWidget *widget = GTK_WIDGET (tree_view);
3808   gint w, h;
3809   cairo_t *cr = gdk_cairo_create (tree_view->priv->bin_window);
3810
3811   if (!gtk_widget_has_focus (widget))
3812     return;
3813
3814   w = gdk_window_get_width (tree_view->priv->bin_window);
3815   h = gdk_window_get_height (tree_view->priv->bin_window);
3816
3817   w -= 2;
3818   h -= 2;
3819
3820   if (w > 0 && h > 0)
3821     gtk_paint_focus (gtk_widget_get_style (widget),
3822                      cr,
3823                      gtk_widget_get_state (widget),
3824                      widget,
3825                      NULL,
3826                      1, 1, w, h);
3827   cairo_destroy (cr);
3828 }
3829
3830 static void
3831 pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView    *tree_view,
3832                                           cairo_t *cr,
3833                                           gint n_visible_columns,
3834                                           gint min_y,
3835                                           gint max_y)
3836 {
3837   GList *list = tree_view->priv->columns;
3838   gint i = 0;
3839   gint current_x = 0;
3840
3841   if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3842       && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3843     return;
3844
3845   /* Only draw the lines for visible rows and columns */
3846   for (list = tree_view->priv->columns; list; list = list->next, i++)
3847     {
3848       PsppSheetViewColumn *column = list->data;
3849       gint x;
3850
3851       if (! column->visible)
3852         continue;
3853
3854       current_x += column->width;
3855
3856       /* Generally the grid lines should fit within the column, but for the
3857          last visible column we put it just past the end of the column.
3858          (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3859       x = current_x;
3860       if (i != n_visible_columns - 1)
3861         x--;
3862
3863       cairo_set_line_width (cr, 1.0);
3864       cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3865       cairo_move_to (cr, x + 0.5, min_y);
3866       cairo_line_to (cr, x + 0.5, max_y - min_y);
3867       cairo_stroke (cr);
3868     }
3869 }
3870
3871 /* Warning: Very scary function.
3872  * Modify at your own risk
3873  *
3874  * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3875  * FIXME: It's not...
3876  */
3877 static void
3878 pspp_sheet_view_draw_bin (GtkWidget      *widget,
3879                           cairo_t *cr)
3880 {
3881   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3882   GtkTreePath *path;
3883   GList *list;
3884   int node;
3885   int cursor = -1;
3886   int drag_highlight = -1;
3887   GtkTreeIter iter;
3888   gint new_y;
3889   gint y_offset, cell_offset;
3890   gint max_height;
3891   GdkRectangle background_area;
3892   GdkRectangle cell_area;
3893   guint flags;
3894   gint bin_window_width;
3895   gint bin_window_height;
3896   GtkTreePath *cursor_path;
3897   GtkTreePath *drag_dest_path;
3898   GList *first_column, *last_column;
3899   gint vertical_separator;
3900   gint horizontal_separator;
3901   gint focus_line_width;
3902   gboolean allow_rules;
3903   gboolean has_special_cell;
3904   gboolean rtl;
3905   gint n_visible_columns;
3906   gint grid_line_width;
3907   gboolean row_ending_details;
3908   gboolean draw_vgrid_lines, draw_hgrid_lines;
3909   gint min_y, max_y;
3910   GtkStyleContext *context;
3911   context = gtk_widget_get_style_context (widget);
3912
3913   GdkRectangle Zarea;
3914   GtkAllocation allocation;
3915   gtk_widget_get_allocation (widget, &allocation);
3916
3917   GdkRectangle exposed_rect;
3918   gdk_cairo_get_clip_rectangle (cr, &exposed_rect);
3919   
3920   Zarea.x =      0;
3921   Zarea.y =      0;
3922   Zarea.height = allocation.height;
3923
3924   rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3925
3926   gtk_widget_style_get (widget,
3927                         "horizontal-separator", &horizontal_separator,
3928                         "vertical-separator", &vertical_separator,
3929                         "allow-rules", &allow_rules,
3930                         "focus-line-width", &focus_line_width,
3931                         "row-ending-details", &row_ending_details,
3932                         NULL);
3933
3934   if (tree_view->priv->row_count == 0)
3935     {
3936       draw_empty_focus (tree_view);
3937       return;
3938     }
3939
3940 #if GTK3_TRANSITION
3941   /* clip event->area to the visible area */
3942   if (Zarea.height < 0.5)
3943     return;
3944 #endif
3945
3946   validate_visible_area (tree_view);
3947
3948   new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, Zarea.y);
3949
3950   if (new_y < 0)
3951     new_y = 0;
3952   y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3953   bin_window_width = 
3954     gdk_window_get_width (tree_view->priv->bin_window);
3955
3956   bin_window_height = 
3957     gdk_window_get_height (tree_view->priv->bin_window);
3958
3959
3960   if (tree_view->priv->height < bin_window_height)
3961     {
3962       gtk_paint_flat_box (gtk_widget_get_style (widget),
3963                           cr,
3964                           gtk_widget_get_state (widget),
3965                           GTK_SHADOW_NONE,
3966                           widget,
3967                           "cell_even",
3968                           0, tree_view->priv->height,
3969                           bin_window_width,
3970                           bin_window_height - tree_view->priv->height);
3971     }
3972
3973   if (node < 0)
3974     return;
3975
3976   /* find the path for the node */
3977   path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3978   gtk_tree_model_get_iter (tree_view->priv->model,
3979                            &iter,
3980                            path);
3981   gtk_tree_path_free (path);
3982   
3983   cursor_path = NULL;
3984   drag_dest_path = NULL;
3985
3986   if (tree_view->priv->cursor)
3987     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3988
3989   if (cursor_path)
3990     _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3991
3992   if (tree_view->priv->drag_dest_row)
3993     drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3994
3995   if (drag_dest_path)
3996     _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3997                                 &drag_highlight);
3998
3999   draw_vgrid_lines =
4000     tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
4001     || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
4002   draw_hgrid_lines =
4003     tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
4004     || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
4005
4006   if (draw_vgrid_lines || draw_hgrid_lines)
4007     gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
4008   
4009   n_visible_columns = 0;
4010   for (list = tree_view->priv->columns; list; list = list->next)
4011     {
4012       if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4013         continue;
4014       n_visible_columns ++;
4015     }
4016
4017   /* Find the last column */
4018   for (last_column = g_list_last (tree_view->priv->columns);
4019        last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
4020        last_column = last_column->prev)
4021     ;
4022
4023   /* and the first */
4024   for (first_column = g_list_first (tree_view->priv->columns);
4025        first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
4026        first_column = first_column->next)
4027     ;
4028
4029   /* Actually process the expose event.  To do this, we want to
4030    * start at the first node of the event, and walk the tree in
4031    * order, drawing each successive node.
4032    */
4033
4034   min_y = y_offset;
4035   do
4036     {
4037       gboolean parity;
4038       gboolean is_first = FALSE;
4039       gboolean is_last = FALSE;
4040       gboolean done = FALSE;
4041       gboolean selected;
4042
4043       max_height = ROW_HEIGHT (tree_view);
4044
4045       cell_offset = 0;
4046
4047       background_area.y = y_offset + Zarea.y;
4048       background_area.height = max_height;
4049       max_y = background_area.y + max_height;
4050
4051       flags = 0;
4052
4053       if (node == tree_view->priv->prelight_node)
4054         flags |= GTK_CELL_RENDERER_PRELIT;
4055
4056       selected = pspp_sheet_view_node_is_selected (tree_view, node);
4057
4058       parity = node % 2;
4059
4060       if (tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
4061         {
4062           /* we *need* to set cell data on all cells before the call
4063            * to _has_special_cell, else _has_special_cell() does not
4064            * return a correct value.
4065            */
4066           for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4067                list;
4068                list = (rtl ? list->prev : list->next))
4069             {
4070               PsppSheetViewColumn *column = list->data;
4071               pspp_sheet_view_column_cell_set_cell_data (column,
4072                                                          tree_view->priv->model,
4073                                                          &iter);
4074             }
4075
4076           has_special_cell = pspp_sheet_view_has_special_cell (tree_view);
4077         }
4078       else
4079         has_special_cell = tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
4080
4081       for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4082            list;
4083            list = (rtl ? list->prev : list->next))
4084         {
4085           PsppSheetViewColumn *column = list->data;
4086           const gchar *detail = NULL;
4087           gboolean selected_column;
4088           GtkStateType state;
4089
4090           if (!column->visible)
4091             continue;
4092
4093           if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
4094             selected_column = column->selected && column->selectable;
4095           else
4096             selected_column = TRUE;
4097
4098 #if GTK3_TRANSITION
4099           if (cell_offset > Zarea.x + Zarea.width ||
4100               cell_offset + column->width < Zarea.x)
4101             {
4102               cell_offset += column->width;
4103               continue;
4104             }
4105 #endif
4106
4107           if (selected && selected_column)
4108             flags |= GTK_CELL_RENDERER_SELECTED;
4109           else
4110             flags &= ~GTK_CELL_RENDERER_SELECTED;
4111
4112           if (column->show_sort_indicator)
4113             flags |= GTK_CELL_RENDERER_SORTED;
4114           else
4115             flags &= ~GTK_CELL_RENDERER_SORTED;
4116
4117           if (cursor == node)
4118             flags |= GTK_CELL_RENDERER_FOCUSED;
4119           else
4120             flags &= ~GTK_CELL_RENDERER_FOCUSED;
4121
4122           background_area.x = cell_offset;
4123           background_area.width = column->width;
4124
4125           cell_area = background_area;
4126           cell_area.y += vertical_separator / 2;
4127           cell_area.x += horizontal_separator / 2;
4128           cell_area.height -= vertical_separator;
4129           cell_area.width -= horizontal_separator;
4130
4131           if (draw_vgrid_lines)
4132             {
4133               if (list == first_column)
4134                 {
4135                   cell_area.width -= grid_line_width / 2;
4136                 }
4137               else if (list == last_column)
4138                 {
4139                   cell_area.x += grid_line_width / 2;
4140                   cell_area.width -= grid_line_width / 2;
4141                 }
4142               else
4143                 {
4144                   cell_area.x += grid_line_width / 2;
4145                   cell_area.width -= grid_line_width;
4146                 }
4147             }
4148
4149           if (draw_hgrid_lines)
4150             {
4151               cell_area.y += grid_line_width / 2;
4152               cell_area.height -= grid_line_width;
4153             }
4154
4155 #if GTK3_TRANSITION
4156           if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4157 #else
4158           if (!gdk_rectangle_intersect (&background_area, &exposed_rect, NULL))
4159 #endif
4160             {
4161               cell_offset += column->width;
4162               continue;
4163             }
4164
4165
4166           pspp_sheet_view_column_cell_set_cell_data (column,
4167                                                      tree_view->priv->model,
4168                                                      &iter);
4169
4170           /* Select the detail for drawing the cell.  relevant
4171            * factors are parity, sortedness, and whether to
4172            * display rules.
4173            */
4174           if (allow_rules && tree_view->priv->has_rules)
4175             {
4176               if ((flags & GTK_CELL_RENDERER_SORTED) &&
4177                   n_visible_columns >= 3)
4178                 {
4179                   if (parity)
4180                     detail = "cell_odd_ruled_sorted";
4181                   else
4182                     detail = "cell_even_ruled_sorted";
4183                 }
4184               else
4185                 {
4186                   if (parity)
4187                     detail = "cell_odd_ruled";
4188                   else
4189                     detail = "cell_even_ruled";
4190                 }
4191             }
4192           else
4193             {
4194               if ((flags & GTK_CELL_RENDERER_SORTED) &&
4195                   n_visible_columns >= 3)
4196                 {
4197                   if (parity)
4198                     detail = "cell_odd_sorted";
4199                   else
4200                     detail = "cell_even_sorted";
4201                 }
4202               else
4203                 {
4204                   if (parity)
4205                     detail = "cell_odd";
4206                   else
4207                     detail = "cell_even";
4208                 }
4209             }
4210
4211           g_assert (detail);
4212
4213           gtk_style_context_save (context);
4214           state = gtk_cell_renderer_get_state (NULL, widget, flags);
4215           gtk_style_context_set_state (context, state);
4216           gtk_style_context_add_class (context, GTK_STYLE_CLASS_CELL);
4217
4218           /* Draw background */
4219           gtk_render_background (context, cr,
4220                                  background_area.x,
4221                                  background_area.y,
4222                                  background_area.width,
4223                                  background_area.height);
4224
4225           /* Draw frame */
4226           gtk_render_frame (context, cr,
4227                             background_area.x,
4228                             background_area.y,
4229                             background_area.width,
4230                             background_area.height);
4231
4232           if (draw_hgrid_lines)
4233             {
4234               cairo_set_line_width (cr, 1.0);
4235               cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
4236
4237               if (background_area.y >= 0)
4238                 {
4239 #if GTK3_TRANSITION
4240                   gdk_draw_line (event->window,
4241                                  tree_view->priv->grid_line_gc[widget->state],
4242                                  background_area.x, background_area.y,
4243                                  background_area.x + background_area.width,
4244                                  background_area.y);
4245 #else
4246                   cairo_move_to (cr, background_area.x, background_area.y - 0.5);
4247                   cairo_line_to (cr, background_area.x + background_area.width,
4248                                  background_area.y - 0.5);
4249 #endif
4250                 }
4251
4252               if (y_offset + max_height <= Zarea.height - 0.5)
4253                 {
4254 #if GTK3_TRANSITION
4255                   gdk_draw_line (event->window,
4256                                  tree_view->priv->grid_line_gc[widget->state],
4257                                  background_area.x, background_area.y + max_height,
4258                                  background_area.x + background_area.width,
4259                                  background_area.y + max_height);
4260 #else
4261
4262                   cairo_move_to (cr, background_area.x, background_area.y + max_height - 0.5);
4263                   cairo_line_to (cr, background_area.x + background_area.width,
4264                                  background_area.y + max_height - 0.5);
4265 #endif
4266                 }
4267               cairo_stroke (cr);
4268             }
4269
4270           _pspp_sheet_view_column_cell_render (column,
4271                                                cr,
4272                                                &background_area,
4273                                                &cell_area,
4274                                                flags);
4275
4276           if (node == cursor && has_special_cell &&
4277               ((column == tree_view->priv->focus_column &&
4278                 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4279                 gtk_widget_has_focus (widget)) ||
4280                (column == tree_view->priv->edited_column)))
4281             {
4282               _pspp_sheet_view_column_cell_draw_focus (column,
4283                                                        cr,
4284                                                      &background_area,
4285                                                      &cell_area,
4286                                                      flags);
4287             }
4288
4289           cell_offset += column->width;
4290           gtk_style_context_restore (context);
4291         }
4292
4293       if (cell_offset < Zarea.x)
4294         {
4295           gtk_paint_flat_box (gtk_widget_get_style (widget),
4296                               cr,
4297                               GTK_STATE_NORMAL,
4298                               GTK_SHADOW_NONE,
4299                               widget,
4300                               "base",
4301                               cell_offset,
4302                               background_area.y,
4303                               Zarea.x - cell_offset,
4304                               background_area.height);
4305         }
4306
4307       if (node == drag_highlight)
4308         {
4309           /* Draw indicator for the drop
4310            */
4311           gint highlight_y = -1;
4312           int node = -1;
4313           gint width;
4314
4315           switch (tree_view->priv->drag_dest_pos)
4316             {
4317             case PSPP_SHEET_VIEW_DROP_BEFORE:
4318               highlight_y = background_area.y - 1;
4319               if (highlight_y < 0)
4320                       highlight_y = 0;
4321               break;
4322
4323             case PSPP_SHEET_VIEW_DROP_AFTER:
4324               highlight_y = background_area.y + background_area.height - 1;
4325               break;
4326
4327             case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4328             case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4329               _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4330
4331               if (node < 0)
4332                 break;
4333               width = gdk_window_get_width (tree_view->priv->bin_window);
4334
4335               if (row_ending_details)
4336                 gtk_paint_focus (gtk_widget_get_style (widget),
4337                                  cr,
4338                                  gtk_widget_get_state (widget),
4339                                  widget,
4340                                  (is_first
4341                                   ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4342                                   : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4343                                  0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4344                                  - focus_line_width / 2,
4345                                  width, ROW_HEIGHT (tree_view)
4346                                - focus_line_width + 1);
4347               else
4348                 gtk_paint_focus (gtk_widget_get_style (widget),
4349                                  cr,
4350                                  gtk_widget_get_state (widget),
4351                                  widget,
4352                                  "treeview-drop-indicator",
4353                                  0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4354                                  - focus_line_width / 2,
4355                                  width, ROW_HEIGHT (tree_view)
4356                                  - focus_line_width + 1);
4357               break;
4358             }
4359
4360 #if GTK3_TRANSITION
4361           if (highlight_y >= 0)
4362             {
4363               gdk_draw_line (event->window,
4364                              widget->style->fg_gc[gtk_widget_get_state (widget)],
4365                              0,
4366                              highlight_y,
4367                              rtl ? 0 : bin_window_width,
4368                              highlight_y);
4369             }
4370 #endif
4371         }
4372
4373       /* draw the big row-spanning focus rectangle, if needed */
4374       if (!has_special_cell && node == cursor &&
4375           PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4376           gtk_widget_has_focus (widget))
4377         {
4378           gint tmp_y, tmp_height;
4379           gint width;
4380           GtkStateType focus_rect_state;
4381
4382           focus_rect_state =
4383             flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
4384             (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
4385              (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE :
4386               GTK_STATE_NORMAL));
4387
4388           width = gdk_window_get_width (tree_view->priv->bin_window);
4389           
4390           if (draw_hgrid_lines)
4391             {
4392               tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node) + grid_line_width / 2;
4393               tmp_height = ROW_HEIGHT (tree_view) - grid_line_width;
4394             }
4395           else
4396             {
4397               tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node);
4398               tmp_height = ROW_HEIGHT (tree_view);
4399             }
4400
4401           if (row_ending_details)
4402             gtk_paint_focus (gtk_widget_get_style (widget),
4403                              cr,
4404                              focus_rect_state,
4405                              widget,
4406                              (is_first
4407                               ? (is_last ? "treeview" : "treeview-left" )
4408                               : (is_last ? "treeview-right" : "treeview-middle" )),
4409                              0, tmp_y,
4410                              width, tmp_height);
4411           else
4412             gtk_paint_focus (gtk_widget_get_style (widget),
4413                              cr,
4414                              focus_rect_state,
4415                              widget,
4416                              "treeview",
4417                              0, tmp_y,
4418                              width, tmp_height);
4419         }
4420
4421       y_offset += max_height;
4422
4423       do
4424         {
4425           node = pspp_sheet_view_node_next (tree_view, node);
4426           if (node >= 0)
4427             {
4428               gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
4429               done = TRUE;
4430
4431               /* Sanity Check! */
4432               TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
4433             }
4434           else
4435             goto done;
4436         }
4437       while (!done);
4438     }
4439   while (y_offset < Zarea.height);
4440
4441 done:
4442   pspp_sheet_view_draw_vertical_grid_lines (tree_view, cr, n_visible_columns,
4443                                    min_y, max_y);
4444
4445 #if GTK3_TRANSITION
4446  if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4447    {
4448      GdkRectangle *rectangles;
4449      gint n_rectangles;
4450
4451      gdk_region_get_rectangles (event->region,
4452                                 &rectangles,
4453                                 &n_rectangles);
4454
4455      while (n_rectangles--)
4456        pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4457
4458      g_free (rectangles);
4459    }
4460 #endif
4461
4462   if (cursor_path)
4463     gtk_tree_path_free (cursor_path);
4464
4465   if (drag_dest_path)
4466     gtk_tree_path_free (drag_dest_path);
4467
4468   return;
4469 }
4470
4471
4472 static gboolean
4473 pspp_sheet_view_draw (GtkWidget      *widget,
4474                       cairo_t *cr)
4475 {
4476   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4477   GtkStyleContext *context;
4478
4479   context = gtk_widget_get_style_context (widget);
4480
4481   if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
4482     {
4483       GList *tmp_list;
4484
4485       cairo_save (cr);
4486       gtk_cairo_transform_to_window(cr,widget,tree_view->priv->bin_window);
4487       pspp_sheet_view_draw_bin (widget, cr);
4488       cairo_restore (cr);
4489
4490       /* We can't just chain up to Container::expose as it will try to send the
4491        * event to the headers, so we handle propagating it to our children
4492        * (eg. widgets being edited) ourselves.
4493        */
4494       tmp_list = tree_view->priv->children;
4495       while (tmp_list)
4496         {
4497           PsppSheetViewChild *child = tmp_list->data;
4498           tmp_list = tmp_list->next;
4499
4500           gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
4501         }
4502     }
4503   else
4504     {
4505       gtk_render_background (context, cr,
4506                              0, 0,
4507                              gtk_widget_get_allocated_width (widget),
4508                              gtk_widget_get_allocated_height (widget));
4509     }
4510
4511   gtk_style_context_save (context);
4512   gtk_style_context_remove_class (context, GTK_STYLE_CLASS_VIEW);
4513
4514   if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window))
4515     {
4516       gint n_visible_columns;
4517       GList *list;
4518
4519       for (list = tree_view->priv->columns; list != NULL; list = list->next)
4520         {
4521           PsppSheetViewColumn *column = list->data;
4522
4523           if (column == tree_view->priv->drag_column || !column->visible)
4524             continue;
4525
4526           if (span_intersects (column->allocation.x, column->allocation.width,
4527                                (int) gtk_adjustment_get_value (tree_view->priv->hadjustment),
4528                                (int) gtk_widget_get_allocated_width (widget))
4529               && column->button != NULL)
4530             gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4531                                           column->button, cr);
4532         }
4533
4534       n_visible_columns = 0;
4535       for (list = tree_view->priv->columns; list; list = list->next)
4536         {
4537           if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4538             continue;
4539           n_visible_columns ++;
4540         }
4541       cairo_save (cr);
4542       gtk_cairo_transform_to_window(cr,widget,tree_view->priv->header_window);
4543       pspp_sheet_view_draw_vertical_grid_lines (tree_view,
4544                                                 cr,
4545                                                 n_visible_columns,
4546                                                 0,
4547                                                 TREE_VIEW_HEADER_HEIGHT (tree_view));
4548       cairo_restore (cr);
4549     }
4550   if (tree_view->priv->drag_window &&
4551       gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
4552     {
4553       gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4554                                     tree_view->priv->drag_column->button,
4555                                     cr);
4556     }
4557
4558   gtk_style_context_restore (context);
4559   return FALSE;
4560 }
4561
4562 enum
4563 {
4564   DROP_HOME,
4565   DROP_RIGHT,
4566   DROP_LEFT,
4567   DROP_END
4568 };
4569
4570 /* returns 0x1 when no column has been found -- yes it's hackish */
4571 static PsppSheetViewColumn *
4572 pspp_sheet_view_get_drop_column (PsppSheetView       *tree_view,
4573                                PsppSheetViewColumn *column,
4574                                gint               drop_position)
4575 {
4576   PsppSheetViewColumn *left_column = NULL;
4577   PsppSheetViewColumn *cur_column = NULL;
4578   GList *tmp_list;
4579
4580   if (!column->reorderable)
4581     return (PsppSheetViewColumn *)0x1;
4582
4583   switch (drop_position)
4584     {
4585       case DROP_HOME:
4586         /* find first column where we can drop */
4587         tmp_list = tree_view->priv->columns;
4588         if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4589           return (PsppSheetViewColumn *)0x1;
4590
4591         while (tmp_list)
4592           {
4593             g_assert (tmp_list);
4594
4595             cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4596             tmp_list = tmp_list->next;
4597
4598             if (left_column && left_column->visible == FALSE)
4599               continue;
4600
4601             if (!tree_view->priv->column_drop_func)
4602               return left_column;
4603
4604             if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4605               {
4606                 left_column = cur_column;
4607                 continue;
4608               }
4609
4610             return left_column;
4611           }
4612
4613         if (!tree_view->priv->column_drop_func)
4614           return left_column;
4615
4616         if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4617           return left_column;
4618         else
4619           return (PsppSheetViewColumn *)0x1;
4620         break;
4621
4622       case DROP_RIGHT:
4623         /* find first column after column where we can drop */
4624         tmp_list = tree_view->priv->columns;
4625
4626         for (; tmp_list; tmp_list = tmp_list->next)
4627           if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4628             break;
4629
4630         if (!tmp_list || !tmp_list->next)
4631           return (PsppSheetViewColumn *)0x1;
4632
4633         tmp_list = tmp_list->next;
4634         left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4635         tmp_list = tmp_list->next;
4636
4637         while (tmp_list)
4638           {
4639             g_assert (tmp_list);
4640
4641             cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4642             tmp_list = tmp_list->next;
4643
4644             if (left_column && left_column->visible == FALSE)
4645               {
4646                 left_column = cur_column;
4647                 if (tmp_list)
4648                   tmp_list = tmp_list->next;
4649                 continue;
4650               }
4651
4652             if (!tree_view->priv->column_drop_func)
4653               return left_column;
4654
4655             if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4656               {
4657                 left_column = cur_column;
4658                 continue;
4659               }
4660
4661             return left_column;
4662           }
4663
4664         if (!tree_view->priv->column_drop_func)
4665           return left_column;
4666
4667         if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4668           return left_column;
4669         else
4670           return (PsppSheetViewColumn *)0x1;
4671         break;
4672
4673       case DROP_LEFT:
4674         /* find first column before column where we can drop */
4675         tmp_list = tree_view->priv->columns;
4676
4677         for (; tmp_list; tmp_list = tmp_list->next)
4678           if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4679             break;
4680
4681         if (!tmp_list || !tmp_list->prev)
4682           return (PsppSheetViewColumn *)0x1;
4683
4684         tmp_list = tmp_list->prev;
4685         cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4686         tmp_list = tmp_list->prev;
4687
4688         while (tmp_list)
4689           {
4690             g_assert (tmp_list);
4691
4692             left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4693
4694             if (left_column && !left_column->visible)
4695               {
4696                 /*if (!tmp_list->prev)
4697                   return (PsppSheetViewColumn *)0x1;
4698                   */
4699 /*
4700                 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4701                 tmp_list = tmp_list->prev->prev;
4702                 continue;*/
4703
4704                 cur_column = left_column;
4705                 if (tmp_list)
4706                   tmp_list = tmp_list->prev;
4707                 continue;
4708               }
4709
4710             if (!tree_view->priv->column_drop_func)
4711               return left_column;
4712
4713             if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4714               return left_column;
4715
4716             cur_column = left_column;
4717             tmp_list = tmp_list->prev;
4718           }
4719
4720         if (!tree_view->priv->column_drop_func)
4721           return NULL;
4722
4723         if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4724           return NULL;
4725         else
4726           return (PsppSheetViewColumn *)0x1;
4727         break;
4728
4729       case DROP_END:
4730         /* same as DROP_HOME case, but doing it backwards */
4731         tmp_list = g_list_last (tree_view->priv->columns);
4732         cur_column = NULL;
4733
4734         if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4735           return (PsppSheetViewColumn *)0x1;
4736
4737         while (tmp_list)
4738           {
4739             g_assert (tmp_list);
4740
4741             left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4742
4743             if (left_column && !left_column->visible)
4744               {
4745                 cur_column = left_column;
4746                 tmp_list = tmp_list->prev;
4747               }
4748
4749             if (!tree_view->priv->column_drop_func)
4750               return left_column;
4751
4752             if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4753               return left_column;
4754
4755             cur_column = left_column;
4756             tmp_list = tmp_list->prev;
4757           }
4758
4759         if (!tree_view->priv->column_drop_func)
4760           return NULL;
4761
4762         if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4763           return NULL;
4764         else
4765           return (PsppSheetViewColumn *)0x1;
4766         break;
4767     }
4768
4769   return (PsppSheetViewColumn *)0x1;
4770 }
4771
4772 static gboolean
4773 pspp_sheet_view_key_press (GtkWidget   *widget,
4774                          GdkEventKey *event)
4775 {
4776   PsppSheetView *tree_view = (PsppSheetView *) widget;
4777
4778   if (tree_view->priv->rubber_band_status)
4779     {
4780       if (event->keyval == GDK_Escape)
4781         pspp_sheet_view_stop_rubber_band (tree_view);
4782
4783       return TRUE;
4784     }
4785
4786   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4787     {
4788       if (event->keyval == GDK_Escape)
4789         {
4790           tree_view->priv->cur_reorder = NULL;
4791           pspp_sheet_view_button_release_drag_column (widget, NULL);
4792         }
4793       return TRUE;
4794     }
4795
4796   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4797     {
4798       GList *focus_column;
4799       gboolean rtl;
4800
4801       rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4802
4803       for (focus_column = tree_view->priv->columns;
4804            focus_column;
4805            focus_column = focus_column->next)
4806         {
4807           PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4808
4809           if (column->button && gtk_widget_has_focus (column->button))
4810             break;
4811         }
4812
4813       if (focus_column &&
4814           (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4815           (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4816            || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4817         {
4818           PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4819
4820           if (!column->resizable)
4821             {
4822               gtk_widget_error_bell (widget);
4823               return TRUE;
4824             }
4825
4826           if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4827               || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4828             {
4829               gint old_width = column->resized_width;
4830
4831               column->resized_width = MAX (column->resized_width,
4832                                            column->width);
4833               column->resized_width -= 2;
4834               if (column->resized_width < 0)
4835                 column->resized_width = 0;
4836
4837               if (column->min_width == -1)
4838                 column->resized_width = MAX (column->button_request,
4839                                              column->resized_width);
4840               else
4841                 column->resized_width = MAX (column->min_width,
4842                                              column->resized_width);
4843
4844               if (column->max_width != -1)
4845                 column->resized_width = MIN (column->resized_width,
4846                                              column->max_width);
4847
4848               column->use_resized_width = TRUE;
4849
4850               if (column->resized_width != old_width)
4851                 gtk_widget_queue_resize (widget);
4852               else
4853                 gtk_widget_error_bell (widget);
4854             }
4855           else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4856                    || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4857             {
4858               gint old_width = column->resized_width;
4859
4860               column->resized_width = MAX (column->resized_width,
4861                                            column->width);
4862               column->resized_width += 2;
4863
4864               if (column->max_width != -1)
4865                 column->resized_width = MIN (column->resized_width,
4866                                              column->max_width);
4867
4868               column->use_resized_width = TRUE;
4869
4870               if (column->resized_width != old_width)
4871                 gtk_widget_queue_resize (widget);
4872               else
4873                 gtk_widget_error_bell (widget);
4874             }
4875
4876           return TRUE;
4877         }
4878
4879       if (focus_column &&
4880           (event->state & GDK_MOD1_MASK) &&
4881           (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4882            || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4883            || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4884            || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4885         {
4886           PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4887
4888           if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4889               || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4890             {
4891               PsppSheetViewColumn *col;
4892               col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4893               if (col != (PsppSheetViewColumn *)0x1)
4894                 pspp_sheet_view_move_column_after (tree_view, column, col);
4895               else
4896                 gtk_widget_error_bell (widget);
4897             }
4898           else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4899                    || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4900             {
4901               PsppSheetViewColumn *col;
4902               col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4903               if (col != (PsppSheetViewColumn *)0x1)
4904                 pspp_sheet_view_move_column_after (tree_view, column, col);
4905               else
4906                 gtk_widget_error_bell (widget);
4907             }
4908           else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4909             {
4910               PsppSheetViewColumn *col;
4911               col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4912               if (col != (PsppSheetViewColumn *)0x1)
4913                 pspp_sheet_view_move_column_after (tree_view, column, col);
4914               else
4915                 gtk_widget_error_bell (widget);
4916             }
4917           else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4918             {
4919               PsppSheetViewColumn *col;
4920               col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4921               if (col != (PsppSheetViewColumn *)0x1)
4922                 pspp_sheet_view_move_column_after (tree_view, column, col);
4923               else
4924                 gtk_widget_error_bell (widget);
4925             }
4926
4927           return TRUE;
4928         }
4929     }
4930
4931   /* Chain up to the parent class.  It handles the keybindings. */
4932   if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4933     return TRUE;
4934
4935   if (tree_view->priv->search_entry_avoid_unhandled_binding)
4936     {
4937       tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4938       return FALSE;
4939     }
4940
4941   /* We pass the event to the search_entry.  If its text changes, then we start
4942    * the typeahead find capabilities. */
4943   if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4944       && tree_view->priv->enable_search
4945       && !tree_view->priv->search_custom_entry_set)
4946     {
4947       GdkEvent *new_event;
4948       char *old_text;
4949       const char *new_text;
4950       gboolean retval;
4951       GdkScreen *screen;
4952       gboolean text_modified;
4953       gulong popup_menu_id;
4954
4955       pspp_sheet_view_ensure_interactive_directory (tree_view);
4956
4957       /* Make a copy of the current text */
4958       old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4959       new_event = gdk_event_copy ((GdkEvent *) event);
4960       g_object_unref (((GdkEventKey *) new_event)->window);
4961       ((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window));
4962       gtk_widget_realize (tree_view->priv->search_window);
4963
4964       popup_menu_id = g_signal_connect (tree_view->priv->search_entry, 
4965                                         "popup-menu", G_CALLBACK (gtk_true),
4966                                         NULL);
4967
4968       /* Move the entry off screen */
4969       screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4970       gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4971                        gdk_screen_get_width (screen) + 1,
4972                        gdk_screen_get_height (screen) + 1);
4973       gtk_widget_show (tree_view->priv->search_window);
4974
4975       /* Send the event to the window.  If the preedit_changed signal is emitted
4976        * during this event, we will set priv->imcontext_changed  */
4977       tree_view->priv->imcontext_changed = FALSE;
4978       retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4979       gdk_event_free (new_event);
4980       gtk_widget_hide (tree_view->priv->search_window);
4981
4982       g_signal_handler_disconnect (tree_view->priv->search_entry, 
4983                                    popup_menu_id);
4984
4985       /* We check to make sure that the entry tried to handle the text, and that
4986        * the text has changed.
4987        */
4988       new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4989       text_modified = strcmp (old_text, new_text) != 0;
4990       g_free (old_text);
4991       if (tree_view->priv->imcontext_changed ||    /* we're in a preedit */
4992           (retval && text_modified))               /* ...or the text was modified */
4993         {
4994           if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4995             {
4996               gtk_widget_grab_focus (GTK_WIDGET (tree_view));
4997               return TRUE;
4998             }
4999           else
5000             {
5001               gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
5002               return FALSE;
5003             }
5004         }
5005     }
5006
5007   return FALSE;
5008 }
5009
5010 static gboolean
5011 pspp_sheet_view_key_release (GtkWidget   *widget,
5012                            GdkEventKey *event)
5013 {
5014   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
5015
5016   if (tree_view->priv->rubber_band_status)
5017     return TRUE;
5018
5019   return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
5020 }
5021
5022 /* FIXME Is this function necessary? Can I get an enter_notify event
5023  * w/o either an expose event or a mouse motion event?
5024  */
5025 static gboolean
5026 pspp_sheet_view_enter_notify (GtkWidget        *widget,
5027                             GdkEventCrossing *event)
5028 {
5029   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
5030   int node;
5031   gint new_y;
5032
5033   /* Sanity check it */
5034   if (event->window != tree_view->priv->bin_window)
5035     return FALSE;
5036
5037   if (tree_view->priv->row_count == 0)
5038     return FALSE;
5039
5040   if (event->mode == GDK_CROSSING_GRAB ||
5041       event->mode == GDK_CROSSING_GTK_GRAB ||
5042       event->mode == GDK_CROSSING_GTK_UNGRAB ||
5043       event->mode == GDK_CROSSING_STATE_CHANGED)
5044     return TRUE;
5045
5046   /* find the node internally */
5047   new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
5048   if (new_y < 0)
5049     new_y = 0;
5050   pspp_sheet_view_find_offset (tree_view, new_y, &node);
5051
5052   tree_view->priv->event_last_x = event->x;
5053   tree_view->priv->event_last_y = event->y;
5054
5055   prelight_or_select (tree_view, node, event->x, event->y);
5056
5057   return TRUE;
5058 }
5059
5060 static gboolean
5061 pspp_sheet_view_leave_notify (GtkWidget        *widget,
5062                             GdkEventCrossing *event)
5063 {
5064   PsppSheetView *tree_view;
5065
5066   if (event->mode == GDK_CROSSING_GRAB)
5067     return TRUE;
5068
5069   tree_view = PSPP_SHEET_VIEW (widget);
5070
5071   if (tree_view->priv->prelight_node >= 0)
5072     _pspp_sheet_view_queue_draw_node (tree_view,
5073                                    tree_view->priv->prelight_node,
5074                                    NULL);
5075
5076   tree_view->priv->event_last_x = -10000;
5077   tree_view->priv->event_last_y = -10000;
5078
5079   prelight_or_select (tree_view,
5080                       -1,
5081                       -1000, -1000); /* coords not possibly over an arrow */
5082
5083   return TRUE;
5084 }
5085
5086
5087 static gint
5088 pspp_sheet_view_focus_out (GtkWidget     *widget,
5089                          GdkEventFocus *event)
5090 {
5091   PsppSheetView *tree_view;
5092
5093   tree_view = PSPP_SHEET_VIEW (widget);
5094
5095   gtk_widget_queue_draw (widget);
5096
5097   /* destroy interactive search dialog */
5098   if (tree_view->priv->search_window)
5099     pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
5100
5101   return FALSE;
5102 }
5103
5104
5105 /* Incremental Reflow
5106  */
5107
5108 static void
5109 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
5110                                  int node)
5111 {
5112   GtkAllocation allocation;
5113   gint y = pspp_sheet_view_node_find_offset (tree_view, node)
5114     - gtk_adjustment_get_value (tree_view->priv->vadjustment)
5115     + TREE_VIEW_HEADER_HEIGHT (tree_view);
5116
5117   gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5118
5119   gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5120                               0, y,
5121                               allocation.width,
5122                               tree_view->priv->fixed_height);
5123 }
5124
5125 static gboolean
5126 node_is_visible (PsppSheetView *tree_view,
5127                  int node)
5128 {
5129   int y;
5130   int height;
5131
5132   y = pspp_sheet_view_node_find_offset (tree_view, node);
5133   height = ROW_HEIGHT (tree_view);
5134
5135   if (y >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5136       y + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5137                      + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5138     return TRUE;
5139
5140   return FALSE;
5141 }
5142
5143 /* Returns the row height. */
5144 static gint
5145 validate_row (PsppSheetView *tree_view,
5146               int node,
5147               GtkTreeIter *iter,
5148               GtkTreePath *path)
5149 {
5150   PsppSheetViewColumn *column;
5151   GList *list, *first_column, *last_column;
5152   gint height = 0;
5153   gint horizontal_separator;
5154   gint vertical_separator;
5155   gint focus_line_width;
5156   gboolean draw_vgrid_lines, draw_hgrid_lines;
5157   gint focus_pad;
5158   gint grid_line_width;
5159   gboolean wide_separators;
5160   gint separator_height;
5161
5162   gtk_widget_style_get (GTK_WIDGET (tree_view),
5163                         "focus-padding", &focus_pad,
5164                         "focus-line-width", &focus_line_width,
5165                         "horizontal-separator", &horizontal_separator,
5166                         "vertical-separator", &vertical_separator,
5167                         "grid-line-width", &grid_line_width,
5168                         "wide-separators",  &wide_separators,
5169                         "separator-height", &separator_height,
5170                         NULL);
5171   
5172   draw_vgrid_lines =
5173     tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5174     || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5175   draw_hgrid_lines =
5176     tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5177     || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5178
5179   for (last_column = g_list_last (tree_view->priv->columns);
5180        last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5181        last_column = last_column->prev)
5182     ;
5183
5184   for (first_column = g_list_first (tree_view->priv->columns);
5185        first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5186        first_column = first_column->next)
5187     ;
5188
5189   for (list = tree_view->priv->columns; list; list = list->next)
5190     {
5191       gint tmp_width;
5192       gint tmp_height;
5193
5194       column = list->data;
5195
5196       if (! column->visible)
5197         continue;
5198
5199       pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5200       pspp_sheet_view_column_cell_get_size (column,
5201                                           NULL, NULL, NULL,
5202                                           &tmp_width, &tmp_height);
5203
5204       tmp_height += vertical_separator;
5205       height = MAX (height, tmp_height);
5206
5207       tmp_width = tmp_width + horizontal_separator;
5208
5209       if (draw_vgrid_lines)
5210         {
5211           if (list->data == first_column || list->data == last_column)
5212             tmp_width += grid_line_width / 2.0;
5213           else
5214             tmp_width += grid_line_width;
5215         }
5216
5217       if (tmp_width > column->requested_width)
5218         column->requested_width = tmp_width;
5219     }
5220
5221   if (draw_hgrid_lines)
5222     height += grid_line_width;
5223
5224   tree_view->priv->post_validation_flag = TRUE;
5225   return height;
5226 }
5227
5228
5229 static void
5230 validate_visible_area (PsppSheetView *tree_view)
5231 {
5232   GtkTreePath *path = NULL;
5233   GtkTreePath *above_path = NULL;
5234   GtkTreeIter iter;
5235   int node = -1;
5236   gboolean size_changed = FALSE;
5237   gint total_height;
5238   gint area_above = 0;
5239   gint area_below = 0;
5240   GtkAllocation allocation;
5241
5242   if (tree_view->priv->row_count == 0)
5243     return;
5244
5245   if (tree_view->priv->scroll_to_path == NULL)
5246     return;
5247
5248   gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5249
5250   total_height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5251
5252   if (total_height == 0)
5253     return;
5254
5255   path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5256   if (path)
5257     {
5258       /* we are going to scroll, and will update dy */
5259       _pspp_sheet_view_find_node (tree_view, path, &node);
5260       gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5261
5262       if (tree_view->priv->scroll_to_use_align)
5263         {
5264           gint height = ROW_HEIGHT (tree_view);
5265           area_above = (total_height - height) *
5266             tree_view->priv->scroll_to_row_align;
5267           area_below = total_height - area_above - height;
5268           area_above = MAX (area_above, 0);
5269           area_below = MAX (area_below, 0);
5270         }
5271       else
5272         {
5273           /* two cases:
5274            * 1) row not visible
5275            * 2) row visible
5276            */
5277           gint dy;
5278           gint height = ROW_HEIGHT (tree_view);
5279
5280           dy = pspp_sheet_view_node_find_offset (tree_view, node);
5281
5282           if (dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5283               dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5284                               + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5285             {
5286               /* row visible: keep the row at the same position */
5287               area_above = dy - gtk_adjustment_get_value (tree_view->priv->vadjustment);
5288               area_below = (gtk_adjustment_get_value (tree_view->priv->vadjustment) +
5289                             gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5290                 - dy - height;
5291             }
5292           else
5293             {
5294               /* row not visible */
5295               if (dy >= 0
5296                   && dy + height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5297                 {
5298                   /* row at the beginning -- fixed */
5299                   area_above = dy;
5300                   area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment)
5301                     - area_above - height;
5302                 }
5303               else if (dy >= (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5304                               gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5305                 {
5306                   /* row at the end -- fixed */
5307                   area_above = dy - (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5308                                      gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
5309                   area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) -
5310                     area_above - height;
5311
5312                   if (area_below < 0)
5313                     {
5314                       area_above = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - height;
5315                       area_below = 0;
5316                     }
5317                 }
5318               else
5319                 {
5320                   /* row somewhere in the middle, bring it to the top
5321                    * of the view
5322                    */
5323                   area_above = 0;
5324                   area_below = total_height - height;
5325                 }
5326             }
5327         }
5328     }
5329   else
5330     /* the scroll to isn't valid; ignore it.
5331      */
5332     {
5333       gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5334       tree_view->priv->scroll_to_path = NULL;
5335       return;
5336     }
5337
5338   above_path = gtk_tree_path_copy (path);
5339
5340   /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5341    * backwards is much slower then forward, as there is no iter_prev function.
5342    * We go forwards first in case we run out of tree.  Then we go backwards to
5343    * fill out the top.
5344    */
5345   while (node >= 0 && area_below > 0)
5346     {
5347       gboolean done = FALSE;
5348       do
5349         {
5350           node = pspp_sheet_view_node_next (tree_view, node);
5351           if (node >= 0)
5352             {
5353               gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5354               done = TRUE;
5355               gtk_tree_path_next (path);
5356
5357               /* Sanity Check! */
5358               TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5359             }
5360           else
5361             break;
5362         }
5363       while (!done);
5364
5365       if (node < 0)
5366         break;
5367
5368       area_below -= ROW_HEIGHT (tree_view);
5369     }
5370   gtk_tree_path_free (path);
5371
5372   /* If we ran out of tree, and have extra area_below left, we need to add it
5373    * to area_above */
5374   if (area_below > 0)
5375     area_above += area_below;
5376
5377   _pspp_sheet_view_find_node (tree_view, above_path, &node);
5378
5379   /* We walk backwards */
5380   while (area_above > 0)
5381     {
5382       node = pspp_sheet_view_node_prev (tree_view, node);
5383
5384       /* Always find the new path in the tree.  We cannot just assume
5385        * a gtk_tree_path_prev() is enough here, as there might be children
5386        * in between this node and the previous sibling node.  If this
5387        * appears to be a performance hotspot in profiles, we can look into
5388        * intrigate logic for keeping path, node and iter in sync like
5389        * we do for forward walks.  (Which will be hard because of the lacking
5390        * iter_prev).
5391        */
5392
5393       if (node < 0)
5394         break;
5395
5396       gtk_tree_path_free (above_path);
5397       above_path = _pspp_sheet_view_find_path (tree_view, node);
5398
5399       gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5400
5401       area_above -= ROW_HEIGHT (tree_view);
5402     }
5403
5404   /* set the dy here to scroll to the path,
5405    * and sync the top row accordingly
5406    */
5407   pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5408   pspp_sheet_view_top_row_to_dy (tree_view);
5409
5410   /* update width/height and queue a resize */
5411   if (size_changed)
5412     {
5413       GtkRequisition requisition;
5414
5415       /* We temporarily guess a size, under the assumption that it will be the
5416        * same when we get our next size_allocate.  If we don't do this, we'll be
5417        * in an inconsistent state if we call top_row_to_dy. */
5418
5419       gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5420       gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5421       gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5422       gtk_adjustment_changed (tree_view->priv->hadjustment);
5423       gtk_adjustment_changed (tree_view->priv->vadjustment);
5424       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5425     }
5426
5427   gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5428   tree_view->priv->scroll_to_path = NULL;
5429
5430   if (above_path)
5431     gtk_tree_path_free (above_path);
5432
5433   if (tree_view->priv->scroll_to_column)
5434     {
5435       tree_view->priv->scroll_to_column = NULL;
5436     }
5437   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5438 }
5439
5440 static void
5441 initialize_fixed_height_mode (PsppSheetView *tree_view)
5442 {
5443   if (!tree_view->priv->row_count)
5444     return;
5445
5446   if (tree_view->priv->fixed_height_set)
5447     return;
5448
5449   if (tree_view->priv->fixed_height < 0)
5450     {
5451       GtkTreeIter iter;
5452       GtkTreePath *path;
5453
5454       int node = 0;
5455
5456       path = _pspp_sheet_view_find_path (tree_view, node);
5457       gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5458
5459       tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5460
5461       gtk_tree_path_free (path);
5462
5463       g_object_notify (G_OBJECT (tree_view), "fixed-height");
5464     }
5465 }
5466
5467 /* Our strategy for finding nodes to validate is a little convoluted.  We find
5468  * the left-most uninvalidated node.  We then try walking right, validating
5469  * nodes.  Once we find a valid node, we repeat the previous process of finding
5470  * the first invalid node.
5471  */
5472
5473 static gboolean
5474 validate_rows_handler (PsppSheetView *tree_view)
5475 {
5476   initialize_fixed_height_mode (tree_view);
5477   if (tree_view->priv->validate_rows_timer)
5478     {
5479       g_source_remove (tree_view->priv->validate_rows_timer);
5480       tree_view->priv->validate_rows_timer = 0;
5481     }
5482
5483   return FALSE;
5484 }
5485
5486 static gboolean
5487 do_presize_handler (PsppSheetView *tree_view)
5488 {
5489   GtkRequisition requisition;
5490
5491   validate_visible_area (tree_view);
5492   tree_view->priv->presize_handler_timer = 0;
5493
5494   if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5495     return FALSE;
5496
5497   gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5498
5499   gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5500   gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5501   gtk_adjustment_changed (tree_view->priv->hadjustment);
5502   gtk_adjustment_changed (tree_view->priv->vadjustment);
5503   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5504                    
5505   return FALSE;
5506 }
5507
5508 static gboolean
5509 presize_handler_callback (gpointer data)
5510 {
5511   do_presize_handler (PSPP_SHEET_VIEW (data));
5512                    
5513   return FALSE;
5514 }
5515
5516 static void
5517 install_presize_handler (PsppSheetView *tree_view)
5518 {
5519   if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5520     return;
5521
5522   if (! tree_view->priv->presize_handler_timer)
5523     {
5524       tree_view->priv->presize_handler_timer =
5525         gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5526     }
5527   if (! tree_view->priv->validate_rows_timer)
5528     {
5529       tree_view->priv->validate_rows_timer =
5530         gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5531     }
5532 }
5533
5534 static gboolean
5535 scroll_sync_handler (PsppSheetView *tree_view)
5536 {
5537   if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5538     gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5539   else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5540     pspp_sheet_view_top_row_to_dy (tree_view);
5541   else
5542     pspp_sheet_view_dy_to_top_row (tree_view);
5543
5544   tree_view->priv->scroll_sync_timer = 0;
5545
5546   return FALSE;
5547 }
5548
5549 static void
5550 install_scroll_sync_handler (PsppSheetView *tree_view)
5551 {
5552   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5553     return;
5554
5555   if (!tree_view->priv->scroll_sync_timer)
5556     {
5557       tree_view->priv->scroll_sync_timer =
5558         gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5559     }
5560 }
5561
5562 static void
5563 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5564                            GtkTreePath *path,
5565                            gint         offset)
5566 {
5567   gtk_tree_row_reference_free (tree_view->priv->top_row);
5568
5569   if (!path)
5570     {
5571       tree_view->priv->top_row = NULL;
5572       tree_view->priv->top_row_dy = 0;
5573     }
5574   else
5575     {
5576       tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5577       tree_view->priv->top_row_dy = offset;
5578     }
5579 }
5580
5581 /* Always call this iff dy is in the visible range.  If the tree is empty, then
5582  * it's set to be NULL, and top_row_dy is 0;
5583  */
5584 static void
5585 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5586 {
5587   gint offset;
5588   GtkTreePath *path;
5589   int node;
5590
5591   if (tree_view->priv->row_count == 0)
5592     {
5593       pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5594     }
5595   else
5596     {
5597       offset = pspp_sheet_view_find_offset (tree_view,
5598                                             tree_view->priv->dy,
5599                                             &node);
5600
5601       if (node < 0)
5602         {
5603           pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5604         }
5605       else
5606         {
5607           path = _pspp_sheet_view_find_path (tree_view, node);
5608           pspp_sheet_view_set_top_row (tree_view, path, offset);
5609           gtk_tree_path_free (path);
5610         }
5611     }
5612 }
5613
5614 static void
5615 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5616 {
5617   GtkTreePath *path;
5618   int node;
5619   int new_dy;
5620
5621   /* Avoid recursive calls */
5622   if (tree_view->priv->in_top_row_to_dy)
5623     return;
5624
5625   if (tree_view->priv->top_row)
5626     path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5627   else
5628     path = NULL;
5629
5630   if (!path)
5631     node = -1;
5632   else
5633     _pspp_sheet_view_find_node (tree_view, path, &node);
5634
5635   if (path)
5636     gtk_tree_path_free (path);
5637
5638   if (node < 0)
5639     {
5640       /* keep dy and set new toprow */
5641       gtk_tree_row_reference_free (tree_view->priv->top_row);
5642       tree_view->priv->top_row = NULL;
5643       tree_view->priv->top_row_dy = 0;
5644       /* DO NOT install the idle handler */
5645       pspp_sheet_view_dy_to_top_row (tree_view);
5646       return;
5647     }
5648
5649   if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5650     {
5651       /* new top row -- do NOT install the idle handler */
5652       pspp_sheet_view_dy_to_top_row (tree_view);
5653       return;
5654     }
5655
5656   new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5657   new_dy += tree_view->priv->top_row_dy;
5658
5659   if (new_dy + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
5660     new_dy = tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
5661
5662   new_dy = MAX (0, new_dy);
5663
5664   tree_view->priv->in_top_row_to_dy = TRUE;
5665   gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5666   tree_view->priv->in_top_row_to_dy = FALSE;
5667 }
5668
5669
5670 void
5671 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5672 {
5673   install_presize_handler (tree_view);
5674 }
5675
5676 /* Drag-and-drop */
5677
5678 static void
5679 set_source_row (GdkDragContext *context,
5680                 GtkTreeModel   *model,
5681                 GtkTreePath    *source_row)
5682 {
5683   g_object_set_data_full (G_OBJECT (context),
5684                           "gtk-tree-view-source-row",
5685                           source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5686                           (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5687 }
5688
5689 static GtkTreePath*
5690 get_source_row (GdkDragContext *context)
5691 {
5692   GtkTreeRowReference *ref =
5693     g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5694
5695   if (ref)
5696     return gtk_tree_row_reference_get_path (ref);
5697   else
5698     return NULL;
5699 }
5700
5701 typedef struct
5702 {
5703   GtkTreeRowReference *dest_row;
5704   guint                path_down_mode   : 1;
5705   guint                empty_view_drop  : 1;
5706   guint                drop_append_mode : 1;
5707 }
5708 DestRow;
5709
5710 static void
5711 dest_row_free (gpointer data)
5712 {
5713   DestRow *dr = (DestRow *)data;
5714
5715   gtk_tree_row_reference_free (dr->dest_row);
5716   g_slice_free (DestRow, dr);
5717 }
5718
5719 static void
5720 set_dest_row (GdkDragContext *context,
5721               GtkTreeModel   *model,
5722               GtkTreePath    *dest_row,
5723               gboolean        path_down_mode,
5724               gboolean        empty_view_drop,
5725               gboolean        drop_append_mode)
5726 {
5727   DestRow *dr;
5728
5729   if (!dest_row)
5730     {
5731       g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5732                               NULL, NULL);
5733       return;
5734     }
5735
5736   dr = g_slice_new (DestRow);
5737
5738   dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5739   dr->path_down_mode = path_down_mode != FALSE;
5740   dr->empty_view_drop = empty_view_drop != FALSE;
5741   dr->drop_append_mode = drop_append_mode != FALSE;
5742
5743   g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5744                           dr, (GDestroyNotify) dest_row_free);
5745 }
5746
5747 static GtkTreePath*
5748 get_dest_row (GdkDragContext *context,
5749               gboolean       *path_down_mode)
5750 {
5751   DestRow *dr =
5752     g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5753
5754   if (dr)
5755     {
5756       GtkTreePath *path = NULL;
5757
5758       if (path_down_mode)
5759         *path_down_mode = dr->path_down_mode;
5760
5761       if (dr->dest_row)
5762         path = gtk_tree_row_reference_get_path (dr->dest_row);
5763       else if (dr->empty_view_drop)
5764         path = gtk_tree_path_new_from_indices (0, -1);
5765       else
5766         path = NULL;
5767
5768       if (path && dr->drop_append_mode)
5769         gtk_tree_path_next (path);
5770
5771       return path;
5772     }
5773   else
5774     return NULL;
5775 }
5776
5777 /* Get/set whether drag_motion requested the drag data and
5778  * drag_data_received should thus not actually insert the data,
5779  * since the data doesn't result from a drop.
5780  */
5781 static void
5782 set_status_pending (GdkDragContext *context,
5783                     GdkDragAction   suggested_action)
5784 {
5785   g_object_set_data (G_OBJECT (context),
5786                      "gtk-tree-view-status-pending",
5787                      GINT_TO_POINTER (suggested_action));
5788 }
5789
5790 static GdkDragAction
5791 get_status_pending (GdkDragContext *context)
5792 {
5793   return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5794                                              "gtk-tree-view-status-pending"));
5795 }
5796
5797 static TreeViewDragInfo*
5798 get_info (PsppSheetView *tree_view)
5799 {
5800   return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5801 }
5802
5803 static void
5804 destroy_info (TreeViewDragInfo *di)
5805 {
5806   g_slice_free (TreeViewDragInfo, di);
5807 }
5808
5809 static TreeViewDragInfo*
5810 ensure_info (PsppSheetView *tree_view)
5811 {
5812   TreeViewDragInfo *di;
5813
5814   di = get_info (tree_view);
5815
5816   if (di == NULL)
5817     {
5818       di = g_slice_new0 (TreeViewDragInfo);
5819
5820       g_object_set_data_full (G_OBJECT (tree_view),
5821                               "gtk-tree-view-drag-info",
5822                               di,
5823                               (GDestroyNotify) destroy_info);
5824     }
5825
5826   return di;
5827 }
5828
5829 static void
5830 remove_info (PsppSheetView *tree_view)
5831 {
5832   g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5833 }
5834
5835 #if 0
5836 static gint
5837 drag_scan_timeout (gpointer data)
5838 {
5839   PsppSheetView *tree_view;
5840   gint x, y;
5841   GdkModifierType state;
5842   GtkTreePath *path = NULL;
5843   PsppSheetViewColumn *column = NULL;
5844   GdkRectangle visible_rect;
5845
5846   GDK_THREADS_ENTER ();
5847
5848   tree_view = PSPP_SHEET_VIEW (data);
5849
5850   gdk_window_get_pointer (tree_view->priv->bin_window,
5851                           &x, &y, &state);
5852
5853   pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5854
5855   /* See if we are near the edge. */
5856   if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5857       (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5858       (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5859       (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5860     {
5861       pspp_sheet_view_get_path_at_pos (tree_view,
5862                                      tree_view->priv->bin_window,
5863                                      x, y,
5864                                      &path,
5865                                      &column,
5866                                      NULL,
5867                                      NULL);
5868
5869       if (path != NULL)
5870         {
5871           pspp_sheet_view_scroll_to_cell (tree_view,
5872                                         path,
5873                                         column,
5874                                         TRUE,
5875                                         0.5, 0.5);
5876
5877           gtk_tree_path_free (path);
5878         }
5879     }
5880
5881   GDK_THREADS_LEAVE ();
5882
5883   return TRUE;
5884 }
5885 #endif /* 0 */
5886
5887 static void
5888 add_scroll_timeout (PsppSheetView *tree_view)
5889 {
5890   if (tree_view->priv->scroll_timeout == 0)
5891     {
5892       tree_view->priv->scroll_timeout =
5893         gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5894     }
5895 }
5896
5897 static void
5898 remove_scroll_timeout (PsppSheetView *tree_view)
5899 {
5900   if (tree_view->priv->scroll_timeout != 0)
5901     {
5902       g_source_remove (tree_view->priv->scroll_timeout);
5903       tree_view->priv->scroll_timeout = 0;
5904     }
5905 }
5906
5907 static gboolean
5908 check_model_dnd (GtkTreeModel *model,
5909                  GType         required_iface,
5910                  const gchar  *signal)
5911 {
5912   if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5913     {
5914       g_warning ("You must override the default '%s' handler "
5915                  "on PsppSheetView when using models that don't support "
5916                  "the %s interface and enabling drag-and-drop. The simplest way to do this "
5917                  "is to connect to '%s' and call "
5918                  "g_signal_stop_emission_by_name() in your signal handler to prevent "
5919                  "the default handler from running. Look at the source code "
5920                  "for the default handler in gtktreeview.c to get an idea what "
5921                  "your handler should do. (gtktreeview.c is in the GTK source "
5922                  "code.) If you're using GTK from a language other than C, "
5923                  "there may be a more natural way to override default handlers, e.g. via derivation.",
5924                  signal, g_type_name (required_iface), signal);
5925       return FALSE;
5926     }
5927   else
5928     return TRUE;
5929 }
5930
5931 static gboolean
5932 scroll_row_timeout (gpointer data)
5933 {
5934   PsppSheetView *tree_view = data;
5935
5936   pspp_sheet_view_horizontal_autoscroll (tree_view);
5937   pspp_sheet_view_vertical_autoscroll (tree_view);
5938
5939   if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5940     pspp_sheet_view_update_rubber_band (tree_view);
5941
5942   return TRUE;
5943 }
5944
5945 /* Returns TRUE if event should not be propagated to parent widgets */
5946 static gboolean
5947 set_destination_row (PsppSheetView    *tree_view,
5948                      GdkDragContext *context,
5949                      /* coordinates relative to the widget */
5950                      gint            x,
5951                      gint            y,
5952                      GdkDragAction  *suggested_action,
5953                      GdkAtom        *target)
5954 {
5955   GtkTreePath *path = NULL;
5956   PsppSheetViewDropPosition pos;
5957   PsppSheetViewDropPosition old_pos;
5958   TreeViewDragInfo *di;
5959   GtkWidget *widget;
5960   GtkTreePath *old_dest_path = NULL;
5961   gboolean can_drop = FALSE;
5962
5963   *suggested_action = 0;
5964   *target = GDK_NONE;
5965
5966   widget = GTK_WIDGET (tree_view);
5967
5968   di = get_info (tree_view);
5969
5970   if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5971     {
5972       /* someone unset us as a drag dest, note that if
5973        * we return FALSE drag_leave isn't called
5974        */
5975
5976       pspp_sheet_view_set_drag_dest_row (tree_view,
5977                                        NULL,
5978                                        PSPP_SHEET_VIEW_DROP_BEFORE);
5979
5980       remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5981
5982       return FALSE; /* no longer a drop site */
5983     }
5984
5985   *target = gtk_drag_dest_find_target (widget, context,
5986                                        gtk_drag_dest_get_target_list (widget));
5987   if (*target == GDK_NONE)
5988     {
5989       return FALSE;
5990     }
5991
5992   if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
5993                                           x, y,
5994                                           &path,
5995                                           &pos))
5996     {
5997       gint n_children;
5998       GtkTreeModel *model;
5999
6000       /* the row got dropped on empty space, let's setup a special case
6001        */
6002
6003       if (path)
6004         gtk_tree_path_free (path);
6005
6006       model = pspp_sheet_view_get_model (tree_view);
6007
6008       n_children = gtk_tree_model_iter_n_children (model, NULL);
6009       if (n_children)
6010         {
6011           pos = PSPP_SHEET_VIEW_DROP_AFTER;
6012           path = gtk_tree_path_new_from_indices (n_children - 1, -1);
6013         }
6014       else
6015         {
6016           pos = PSPP_SHEET_VIEW_DROP_BEFORE;
6017           path = gtk_tree_path_new_from_indices (0, -1);
6018         }
6019
6020       can_drop = TRUE;
6021
6022       goto out;
6023     }
6024
6025   g_assert (path);
6026
6027   /* If we left the current row's "open" zone, unset the timeout for
6028    * opening the row
6029    */
6030   pspp_sheet_view_get_drag_dest_row (tree_view,
6031                                    &old_dest_path,
6032                                    &old_pos);
6033
6034   if (old_dest_path)
6035     gtk_tree_path_free (old_dest_path);
6036
6037   if (TRUE /* FIXME if the location droppable predicate */)
6038     {
6039       can_drop = TRUE;
6040     }
6041
6042 out:
6043   if (can_drop)
6044     {
6045       GtkWidget *source_widget;
6046
6047       *suggested_action = gdk_drag_context_get_suggested_action (context);
6048       source_widget = gtk_drag_get_source_widget (context);
6049
6050       if (source_widget == widget)
6051         {
6052           /* Default to MOVE, unless the user has
6053            * pressed ctrl or shift to affect available actions
6054            */
6055           if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
6056             *suggested_action = GDK_ACTION_MOVE;
6057         }
6058
6059       pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6060                                        path, pos);
6061     }
6062   else
6063     {
6064       /* can't drop here */
6065       pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6066                                        NULL,
6067                                        PSPP_SHEET_VIEW_DROP_BEFORE);
6068     }
6069
6070   if (path)
6071     gtk_tree_path_free (path);
6072
6073   return TRUE;
6074 }
6075
6076 static GtkTreePath*
6077 get_logical_dest_row (PsppSheetView *tree_view,
6078                       gboolean    *path_down_mode,
6079                       gboolean    *drop_append_mode)
6080 {
6081   /* adjust path to point to the row the drop goes in front of */
6082   GtkTreePath *path = NULL;
6083   PsppSheetViewDropPosition pos;
6084
6085   g_return_val_if_fail (path_down_mode != NULL, NULL);
6086   g_return_val_if_fail (drop_append_mode != NULL, NULL);
6087
6088   *path_down_mode = FALSE;
6089   *drop_append_mode = 0;
6090
6091   pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6092
6093   if (path == NULL)
6094     return NULL;
6095
6096   if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
6097     ; /* do nothing */
6098   else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
6099            pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
6100     *path_down_mode = TRUE;
6101   else
6102     {
6103       GtkTreeIter iter;
6104       GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
6105
6106       g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
6107
6108       if (!gtk_tree_model_get_iter (model, &iter, path) ||
6109           !gtk_tree_model_iter_next (model, &iter))
6110         *drop_append_mode = 1;
6111       else
6112         {
6113           *drop_append_mode = 0;
6114           gtk_tree_path_next (path);
6115         }
6116     }
6117
6118   return path;
6119 }
6120
6121 static gboolean
6122 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView      *tree_view,
6123                                         GdkEventMotion   *event)
6124 {
6125   GtkWidget *widget = GTK_WIDGET (tree_view);
6126   GdkDragContext *context;
6127   TreeViewDragInfo *di;
6128   GtkTreePath *path = NULL;
6129   gint button;
6130   gint cell_x, cell_y;
6131   GtkTreeModel *model;
6132   gboolean retval = FALSE;
6133
6134   di = get_info (tree_view);
6135
6136   if (di == NULL || !di->source_set)
6137     goto out;
6138
6139   if (tree_view->priv->pressed_button < 0)
6140     goto out;
6141
6142   if (!gtk_drag_check_threshold (widget,
6143                                  tree_view->priv->press_start_x,
6144                                  tree_view->priv->press_start_y,
6145                                  event->x, event->y))
6146     goto out;
6147
6148   model = pspp_sheet_view_get_model (tree_view);
6149
6150   if (model == NULL)
6151     goto out;
6152
6153   button = tree_view->priv->pressed_button;
6154   tree_view->priv->pressed_button = -1;
6155
6156   pspp_sheet_view_get_path_at_pos (tree_view,
6157                                  tree_view->priv->press_start_x,
6158                                  tree_view->priv->press_start_y,
6159                                  &path,
6160                                  NULL,
6161                                  &cell_x,
6162                                  &cell_y);
6163
6164   if (path == NULL)
6165     goto out;
6166
6167   if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6168       !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6169                                            path))
6170     goto out;
6171
6172   if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6173     goto out;
6174
6175   /* Now we can begin the drag */
6176
6177   retval = TRUE;
6178
6179   context = gtk_drag_begin (widget,
6180                             gtk_drag_source_get_target_list (widget),
6181                             di->source_actions,
6182                             button,
6183                             (GdkEvent*)event);
6184
6185   set_source_row (context, model, path);
6186
6187  out:
6188   if (path)
6189     gtk_tree_path_free (path);
6190
6191   return retval;
6192 }
6193
6194
6195
6196 static void
6197 pspp_sheet_view_drag_begin (GtkWidget      *widget,
6198                           GdkDragContext *context)
6199 {
6200 #if GTK3_TRANSITION
6201   PsppSheetView *tree_view;
6202   GtkTreePath *path = NULL;
6203   gint cell_x, cell_y;
6204   GdkPixmap *row_pix;
6205   TreeViewDragInfo *di;
6206
6207   tree_view = PSPP_SHEET_VIEW (widget);
6208
6209   /* if the user uses a custom DND source impl, we don't set the icon here */
6210   di = get_info (tree_view);
6211
6212   if (di == NULL || !di->source_set)
6213     return;
6214
6215   pspp_sheet_view_get_path_at_pos (tree_view,
6216                                  tree_view->priv->press_start_x,
6217                                  tree_view->priv->press_start_y,
6218                                  &path,
6219                                  NULL,
6220                                  &cell_x,
6221                                  &cell_y);
6222
6223   g_return_if_fail (path != NULL);
6224
6225   row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6226                                                 path);
6227
6228   gtk_drag_set_icon_pixmap (context,
6229                             gdk_drawable_get_colormap (row_pix),
6230                             row_pix,
6231                             NULL,
6232                             /* the + 1 is for the black border in the icon */
6233                             tree_view->priv->press_start_x + 1,
6234                             cell_y + 1);
6235
6236   g_object_unref (row_pix);
6237   gtk_tree_path_free (path);
6238 #endif
6239 }
6240
6241
6242 static void
6243 pspp_sheet_view_drag_end (GtkWidget      *widget,
6244                         GdkDragContext *context)
6245 {
6246   /* do nothing */
6247 }
6248
6249 /* Default signal implementations for the drag signals */
6250 static void
6251 pspp_sheet_view_drag_data_get (GtkWidget        *widget,
6252                              GdkDragContext   *context,
6253                              GtkSelectionData *selection_data,
6254                              guint             info,
6255                              guint             time)
6256 {
6257   PsppSheetView *tree_view;
6258   GtkTreeModel *model;
6259   TreeViewDragInfo *di;
6260   GtkTreePath *source_row;
6261
6262   tree_view = PSPP_SHEET_VIEW (widget);
6263
6264   model = pspp_sheet_view_get_model (tree_view);
6265
6266   if (model == NULL)
6267     return;
6268
6269   di = get_info (PSPP_SHEET_VIEW (widget));
6270
6271   if (di == NULL)
6272     return;
6273
6274   source_row = get_source_row (context);
6275
6276   if (source_row == NULL)
6277     return;
6278
6279   /* We can implement the GTK_TREE_MODEL_ROW target generically for
6280    * any model; for DragSource models there are some other targets
6281    * we also support.
6282    */
6283
6284   if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6285       gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6286                                           source_row,
6287                                           selection_data))
6288     goto done;
6289
6290   /* If drag_data_get does nothing, try providing row data. */
6291   if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6292     {
6293       gtk_tree_set_row_drag_data (selection_data,
6294                                   model,
6295                                   source_row);
6296     }
6297
6298  done:
6299   gtk_tree_path_free (source_row);
6300 }
6301
6302
6303 static void
6304 pspp_sheet_view_drag_data_delete (GtkWidget      *widget,
6305                                 GdkDragContext *context)
6306 {
6307   TreeViewDragInfo *di;
6308   GtkTreeModel *model;
6309   PsppSheetView *tree_view;
6310   GtkTreePath *source_row;
6311
6312   tree_view = PSPP_SHEET_VIEW (widget);
6313   model = pspp_sheet_view_get_model (tree_view);
6314
6315   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6316     return;
6317
6318   di = get_info (tree_view);
6319
6320   if (di == NULL)
6321     return;
6322
6323   source_row = get_source_row (context);
6324
6325   if (source_row == NULL)
6326     return;
6327
6328   gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6329                                          source_row);
6330
6331   gtk_tree_path_free (source_row);
6332
6333   set_source_row (context, NULL, NULL);
6334 }
6335
6336 static void
6337 pspp_sheet_view_drag_leave (GtkWidget      *widget,
6338                           GdkDragContext *context,
6339                           guint             time)
6340 {
6341   /* unset any highlight row */
6342   pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6343                                    NULL,
6344                                    PSPP_SHEET_VIEW_DROP_BEFORE);
6345
6346   remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6347 }
6348
6349
6350 static gboolean
6351 pspp_sheet_view_drag_motion (GtkWidget        *widget,
6352                            GdkDragContext   *context,
6353                            /* coordinates relative to the widget */
6354                            gint              x,
6355                            gint              y,
6356                            guint             time)
6357 {
6358   gboolean empty;
6359   GtkTreePath *path = NULL;
6360   PsppSheetViewDropPosition pos;
6361   PsppSheetView *tree_view;
6362   GdkDragAction suggested_action = 0;
6363   GdkAtom target;
6364
6365   tree_view = PSPP_SHEET_VIEW (widget);
6366
6367   if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6368     return FALSE;
6369
6370   pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6371
6372   /* we only know this *after* set_desination_row */
6373   empty = tree_view->priv->empty_view_drop;
6374
6375   if (path == NULL && !empty)
6376     {
6377       /* Can't drop here. */
6378       gdk_drag_status (context, 0, time);
6379     }
6380   else
6381     {
6382       if (tree_view->priv->open_dest_timeout == 0 &&
6383           (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6384            pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6385         {
6386           /* Nothing. */
6387         }
6388       else
6389         {
6390           add_scroll_timeout (tree_view);
6391         }
6392
6393       if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6394         {
6395           /* Request data so we can use the source row when
6396            * determining whether to accept the drop
6397            */
6398           set_status_pending (context, suggested_action);
6399           gtk_drag_get_data (widget, context, target, time);
6400         }
6401       else
6402         {
6403           set_status_pending (context, 0);
6404           gdk_drag_status (context, suggested_action, time);
6405         }
6406     }
6407
6408   if (path)
6409     gtk_tree_path_free (path);
6410
6411   return TRUE;
6412 }
6413
6414
6415 static gboolean
6416 pspp_sheet_view_drag_drop (GtkWidget        *widget,
6417                          GdkDragContext   *context,
6418                          /* coordinates relative to the widget */
6419                          gint              x,
6420                          gint              y,
6421                          guint             time)
6422 {
6423   PsppSheetView *tree_view;
6424   GtkTreePath *path;
6425   GdkDragAction suggested_action = 0;
6426   GdkAtom target = GDK_NONE;
6427   TreeViewDragInfo *di;
6428   GtkTreeModel *model;
6429   gboolean path_down_mode;
6430   gboolean drop_append_mode;
6431
6432   tree_view = PSPP_SHEET_VIEW (widget);
6433
6434   model = pspp_sheet_view_get_model (tree_view);
6435
6436   remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6437
6438   di = get_info (tree_view);
6439
6440   if (di == NULL)
6441     return FALSE;
6442
6443   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6444     return FALSE;
6445
6446   if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6447     return FALSE;
6448
6449   path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6450
6451   if (target != GDK_NONE && path != NULL)
6452     {
6453       /* in case a motion had requested drag data, change things so we
6454        * treat drag data receives as a drop.
6455        */
6456       set_status_pending (context, 0);
6457       set_dest_row (context, model, path,
6458                     path_down_mode, tree_view->priv->empty_view_drop,
6459                     drop_append_mode);
6460     }
6461
6462   if (path)
6463     gtk_tree_path_free (path);
6464
6465   /* Unset this thing */
6466   pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6467                                    NULL,
6468                                    PSPP_SHEET_VIEW_DROP_BEFORE);
6469
6470   if (target != GDK_NONE)
6471     {
6472       gtk_drag_get_data (widget, context, target, time);
6473       return TRUE;
6474     }
6475   else
6476     return FALSE;
6477 }
6478
6479 static void
6480 pspp_sheet_view_drag_data_received (GtkWidget        *widget,
6481                                   GdkDragContext   *context,
6482                                   /* coordinates relative to the widget */
6483                                   gint              x,
6484                                   gint              y,
6485                                   GtkSelectionData *selection_data,
6486                                   guint             info,
6487                                   guint             time)
6488 {
6489   GtkTreePath *path;
6490   TreeViewDragInfo *di;
6491   gboolean accepted = FALSE;
6492   GtkTreeModel *model;
6493   PsppSheetView *tree_view;
6494   GtkTreePath *dest_row;
6495   GdkDragAction suggested_action;
6496   gboolean path_down_mode;
6497   gboolean drop_append_mode;
6498
6499   tree_view = PSPP_SHEET_VIEW (widget);
6500
6501   model = pspp_sheet_view_get_model (tree_view);
6502
6503   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6504     return;
6505
6506   di = get_info (tree_view);
6507
6508   if (di == NULL)
6509     return;
6510
6511   suggested_action = get_status_pending (context);
6512
6513   if (suggested_action)
6514     {
6515       /* We are getting this data due to a request in drag_motion,
6516        * rather than due to a request in drag_drop, so we are just
6517        * supposed to call drag_status, not actually paste in the
6518        * data.
6519        */
6520       path = get_logical_dest_row (tree_view, &path_down_mode,
6521                                    &drop_append_mode);
6522
6523       if (path == NULL)
6524         suggested_action = 0;
6525       else if (path_down_mode)
6526         gtk_tree_path_down (path);
6527
6528       if (suggested_action)
6529         {
6530           if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6531                                                      path,
6532                                                      selection_data))
6533             {
6534               if (path_down_mode)
6535                 {
6536                   path_down_mode = FALSE;
6537                   gtk_tree_path_up (path);
6538
6539                   if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6540                                                              path,
6541                                                              selection_data))
6542                     suggested_action = 0;
6543                 }
6544               else
6545                 suggested_action = 0;
6546             }
6547         }
6548
6549       gdk_drag_status (context, suggested_action, time);
6550
6551       if (path)
6552         gtk_tree_path_free (path);
6553
6554       /* If you can't drop, remove user drop indicator until the next motion */
6555       if (suggested_action == 0)
6556         pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6557                                          NULL,
6558                                          PSPP_SHEET_VIEW_DROP_BEFORE);
6559
6560       return;
6561     }
6562
6563   dest_row = get_dest_row (context, &path_down_mode);
6564
6565   if (dest_row == NULL)
6566     return;
6567
6568   if (gtk_selection_data_get_length (selection_data) >= 0)
6569     {
6570       if (path_down_mode)
6571         {
6572           gtk_tree_path_down (dest_row);
6573           if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6574                                                      dest_row, selection_data))
6575             gtk_tree_path_up (dest_row);
6576         }
6577     }
6578
6579   if (gtk_selection_data_get_length (selection_data) >= 0)
6580     {
6581       if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6582                                                  dest_row,
6583                                                  selection_data))
6584         accepted = TRUE;
6585     }
6586
6587   gtk_drag_finish (context,
6588                    accepted,
6589                    (gdk_drag_context_get_actions (context) == GDK_ACTION_MOVE),
6590                    time);
6591
6592   if (gtk_tree_path_get_depth (dest_row) == 1
6593       && gtk_tree_path_get_indices (dest_row)[0] == 0)
6594     {
6595       /* special special case drag to "0", scroll to first item */
6596       if (!tree_view->priv->scroll_to_path)
6597         pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6598     }
6599
6600   gtk_tree_path_free (dest_row);
6601
6602   /* drop dest_row */
6603   set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6604 }
6605
6606
6607
6608 /* GtkContainer Methods
6609  */
6610
6611
6612 static void
6613 pspp_sheet_view_remove (GtkContainer *container,
6614                       GtkWidget    *widget)
6615 {
6616   PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6617   PsppSheetViewChild *child = NULL;
6618   GList *tmp_list;
6619
6620   tmp_list = tree_view->priv->children;
6621   while (tmp_list)
6622     {
6623       child = tmp_list->data;
6624       if (child->widget == widget)
6625         {
6626           gtk_widget_unparent (widget);
6627
6628           tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6629           g_list_free_1 (tmp_list);
6630           g_slice_free (PsppSheetViewChild, child);
6631           return;
6632         }
6633
6634       tmp_list = tmp_list->next;
6635     }
6636
6637   tmp_list = tree_view->priv->columns;
6638
6639   while (tmp_list)
6640     {
6641       PsppSheetViewColumn *column;
6642       column = tmp_list->data;
6643       if (column->button == widget)
6644         {
6645           gtk_widget_unparent (widget);
6646           return;
6647         }
6648       tmp_list = tmp_list->next;
6649     }
6650 }
6651
6652 static void
6653 pspp_sheet_view_forall (GtkContainer *container,
6654                       gboolean      include_internals,
6655                       GtkCallback   callback,
6656                       gpointer      callback_data)
6657 {
6658   PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6659   PsppSheetViewChild *child = NULL;
6660   PsppSheetViewColumn *column;
6661   GList *tmp_list;
6662
6663   tmp_list = tree_view->priv->children;
6664   while (tmp_list)
6665     {
6666       child = tmp_list->data;
6667       tmp_list = tmp_list->next;
6668
6669       (* callback) (child->widget, callback_data);
6670     }
6671   if (include_internals == FALSE)
6672     return;
6673
6674   for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6675     {
6676       column = tmp_list->data;
6677
6678       if (column->button)
6679         (* callback) (column->button, callback_data);
6680     }
6681 }
6682
6683 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6684  * cells. If so we draw one big row-spanning focus rectangle.
6685  */
6686 static gboolean
6687 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6688 {
6689   GList *list;
6690
6691   if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6692     return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6693
6694   for (list = tree_view->priv->columns; list; list = list->next)
6695     {
6696       if (!((PsppSheetViewColumn *)list->data)->visible)
6697         continue;
6698       if (_pspp_sheet_view_column_count_special_cells (list->data))
6699         return TRUE;
6700     }
6701
6702   return FALSE;
6703 }
6704
6705 static void
6706 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6707                               PsppSheetViewColumn *focus_column,
6708                               gboolean clamp_column_visible)
6709 {
6710   g_return_if_fail (focus_column != NULL);
6711
6712   tree_view->priv->focus_column = focus_column;
6713
6714   if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button)
6715     gtk_widget_grab_focus (focus_column->button);
6716
6717   if (clamp_column_visible)
6718     pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6719 }
6720
6721 /* Returns TRUE if the focus is within the headers, after the focus operation is
6722  * done
6723  */
6724 static gboolean
6725 pspp_sheet_view_header_focus (PsppSheetView      *tree_view,
6726                             GtkDirectionType  dir,
6727                             gboolean          clamp_column_visible)
6728 {
6729   GtkWidget *focus_child;
6730   PsppSheetViewColumn *focus_column;
6731   GList *last_column, *first_column;
6732   GList *tmp_list;
6733   gboolean rtl;
6734
6735   if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6736     return FALSE;
6737
6738   focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
6739
6740   first_column = tree_view->priv->columns;
6741   while (first_column)
6742     {
6743       PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6744
6745       if (pspp_sheet_view_column_can_focus (c) && c->visible)
6746         break;
6747       first_column = first_column->next;
6748     }
6749
6750   /* No headers are visible, or are focusable.  We can't focus in or out.
6751    */
6752   if (first_column == NULL)
6753     return FALSE;
6754
6755   last_column = g_list_last (tree_view->priv->columns);
6756   while (last_column)
6757     {
6758       PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6759
6760       if (pspp_sheet_view_column_can_focus (c) && c->visible)
6761         break;
6762       last_column = last_column->prev;
6763     }
6764
6765
6766   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6767
6768   switch (dir)
6769     {
6770     case GTK_DIR_TAB_BACKWARD:
6771     case GTK_DIR_TAB_FORWARD:
6772     case GTK_DIR_UP:
6773     case GTK_DIR_DOWN:
6774       if (focus_child == NULL)
6775         {
6776           if (tree_view->priv->focus_column != NULL &&
6777               pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6778             focus_column = tree_view->priv->focus_column;
6779           else
6780             focus_column = first_column->data;
6781           pspp_sheet_view_focus_column (tree_view, focus_column,
6782                                         clamp_column_visible);
6783           return TRUE;
6784         }
6785       return FALSE;
6786
6787     case GTK_DIR_LEFT:
6788     case GTK_DIR_RIGHT:
6789       if (focus_child == NULL)
6790         {
6791           if (tree_view->priv->focus_column != NULL)
6792             focus_column = tree_view->priv->focus_column;
6793           else if (dir == GTK_DIR_LEFT)
6794             focus_column = last_column->data;
6795           else
6796             focus_column = first_column->data;
6797           pspp_sheet_view_focus_column (tree_view, focus_column,
6798                                         clamp_column_visible);
6799           return TRUE;
6800         }
6801
6802       if (gtk_widget_child_focus (focus_child, dir))
6803         {
6804           /* The focus moves inside the button. */
6805           /* This is probably a great example of bad UI */
6806           if (clamp_column_visible)
6807             pspp_sheet_view_clamp_column_visible (tree_view,
6808                                                   tree_view->priv->focus_column,
6809                                                   FALSE);
6810           return TRUE;
6811         }
6812
6813       /* We need to move the focus among the row of buttons. */
6814       for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6815         if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6816           break;
6817
6818       if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6819           || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6820         {
6821           gtk_widget_error_bell (GTK_WIDGET (tree_view));
6822           return TRUE;
6823         }
6824
6825       while (tmp_list)
6826         {
6827           PsppSheetViewColumn *column;
6828
6829           if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6830             tmp_list = tmp_list->next;
6831           else
6832             tmp_list = tmp_list->prev;
6833
6834           if (tmp_list == NULL)
6835             {
6836               g_warning ("Internal button not found");
6837               break;
6838             }
6839           column = tmp_list->data;
6840           if (column->visible &&
6841               pspp_sheet_view_column_can_focus (column))
6842             {
6843               if (column->button)
6844                 {
6845                   pspp_sheet_view_focus_column (tree_view, column,
6846                                                 clamp_column_visible);
6847                   return TRUE;
6848                 }
6849             }
6850         }
6851       return FALSE;
6852
6853     default:
6854       g_assert_not_reached ();
6855       break;
6856     }
6857
6858   return FALSE;
6859 }
6860
6861 /* This function returns in 'path' the first focusable path, if the given path
6862  * is already focusable, it's the returned one.
6863  *
6864  */
6865 static gboolean
6866 search_first_focusable_path (PsppSheetView  *tree_view,
6867                              GtkTreePath **path,
6868                              gboolean      search_forward,
6869                              int *new_node)
6870 {
6871   /* XXX this function is trivial given that the sheetview doesn't support
6872      separator rows */
6873   int node = -1;
6874
6875   if (!path || !*path)
6876     return FALSE;
6877
6878   _pspp_sheet_view_find_node (tree_view, *path, &node);
6879
6880   if (node < 0)
6881     return FALSE;
6882
6883   if (new_node)
6884     *new_node = node;
6885
6886   return (*path != NULL);
6887 }
6888
6889 static gint
6890 pspp_sheet_view_focus (GtkWidget        *widget,
6891                      GtkDirectionType  direction)
6892 {
6893   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6894   GtkContainer *container = GTK_CONTAINER (widget);
6895   GtkWidget *focus_child;
6896
6897   if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6898     return FALSE;
6899
6900   focus_child = gtk_container_get_focus_child (container);
6901
6902   pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6903   /* Case 1.  Headers currently have focus. */
6904   if (focus_child)
6905     {
6906       switch (direction)
6907         {
6908         case GTK_DIR_LEFT:
6909         case GTK_DIR_RIGHT:
6910           pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6911           return TRUE;
6912         case GTK_DIR_TAB_BACKWARD:
6913         case GTK_DIR_UP:
6914           return FALSE;
6915         case GTK_DIR_TAB_FORWARD:
6916         case GTK_DIR_DOWN:
6917           gtk_widget_grab_focus (widget);
6918           return TRUE;
6919         default:
6920           g_assert_not_reached ();
6921           return FALSE;
6922         }
6923     }
6924
6925   /* Case 2. We don't have focus at all. */
6926   if (!gtk_widget_has_focus (widget))
6927     {
6928       if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6929         gtk_widget_grab_focus (widget);
6930       return TRUE;
6931     }
6932
6933   /* Case 3. We have focus already. */
6934   if (direction == GTK_DIR_TAB_BACKWARD)
6935     return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6936   else if (direction == GTK_DIR_TAB_FORWARD)
6937     return FALSE;
6938
6939   /* Other directions caught by the keybindings */
6940   gtk_widget_grab_focus (widget);
6941   return TRUE;
6942 }
6943
6944 static void
6945 pspp_sheet_view_grab_focus (GtkWidget *widget)
6946 {
6947   GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6948
6949   pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6950 }
6951
6952 static void
6953 pspp_sheet_view_style_updated (GtkWidget *widget)
6954 {
6955   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6956   GList *list;
6957   PsppSheetViewColumn *column;
6958   GtkStyleContext *context;
6959
6960   GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->style_updated (widget);
6961
6962   if (gtk_widget_get_realized (widget))
6963     {
6964       context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
6965       gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
6966       gtk_style_context_set_background (context, tree_view->priv->header_window);
6967       pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6968     }
6969
6970   gtk_widget_style_get (widget,
6971                         "expander-size", &tree_view->priv->expander_size,
6972                         NULL);
6973   tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6974
6975   for (list = tree_view->priv->columns; list; list = list->next)
6976     {
6977       column = list->data;
6978       _pspp_sheet_view_column_cell_set_dirty (column);
6979     }
6980
6981   tree_view->priv->fixed_height = -1;
6982
6983   /* Invalidate cached button style. */
6984   if (tree_view->priv->button_style)
6985     {
6986       g_object_unref (tree_view->priv->button_style);
6987       tree_view->priv->button_style = NULL;
6988     }
6989
6990   gtk_widget_queue_resize (widget);
6991 }
6992
6993
6994 static void
6995 pspp_sheet_view_set_focus_child (GtkContainer *container,
6996                                GtkWidget    *child)
6997 {
6998   PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6999   GList *list;
7000
7001   for (list = tree_view->priv->columns; list; list = list->next)
7002     {
7003       if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
7004         {
7005           tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7006           break;
7007         }
7008     }
7009
7010   GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
7011 }
7012
7013 static void
7014 pspp_sheet_view_set_adjustments (PsppSheetView   *tree_view,
7015                                GtkAdjustment *hadj,
7016                                GtkAdjustment *vadj)
7017 {
7018   gboolean need_adjust = FALSE;
7019
7020   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7021
7022   if (hadj)
7023     g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
7024   else
7025     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7026   if (vadj)
7027     g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
7028   else
7029     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7030
7031   if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
7032     {
7033       g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
7034                                             pspp_sheet_view_adjustment_changed,
7035                                             tree_view);
7036       g_object_unref (tree_view->priv->hadjustment);
7037     }
7038
7039   if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
7040     {
7041       g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
7042                                             pspp_sheet_view_adjustment_changed,
7043                                             tree_view);
7044       g_object_unref (tree_view->priv->vadjustment);
7045     }
7046
7047   if (tree_view->priv->hadjustment != hadj)
7048     {
7049       tree_view->priv->hadjustment = hadj;
7050       g_object_ref_sink (tree_view->priv->hadjustment);
7051
7052       g_signal_connect (tree_view->priv->hadjustment, "value-changed",
7053                         G_CALLBACK (pspp_sheet_view_adjustment_changed),
7054                         tree_view);
7055       need_adjust = TRUE;
7056     }
7057
7058   if (tree_view->priv->vadjustment != vadj)
7059     {
7060       tree_view->priv->vadjustment = vadj;
7061       g_object_ref_sink (tree_view->priv->vadjustment);
7062
7063       g_signal_connect (tree_view->priv->vadjustment, "value-changed",
7064                         G_CALLBACK (pspp_sheet_view_adjustment_changed),
7065                         tree_view);
7066       need_adjust = TRUE;
7067     }
7068
7069   if (need_adjust)
7070     pspp_sheet_view_adjustment_changed (NULL, tree_view);
7071 }
7072
7073
7074 static gboolean
7075 pspp_sheet_view_real_move_cursor (PsppSheetView       *tree_view,
7076                                 GtkMovementStep    step,
7077                                 gint               count)
7078 {
7079   PsppSheetSelectMode mode;
7080   GdkModifierType state;
7081
7082   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
7083   g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
7084                         step == GTK_MOVEMENT_VISUAL_POSITIONS ||
7085                         step == GTK_MOVEMENT_DISPLAY_LINES ||
7086                         step == GTK_MOVEMENT_PAGES ||
7087                         step == GTK_MOVEMENT_BUFFER_ENDS ||
7088                         step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
7089
7090   if (tree_view->priv->row_count == 0)
7091     return FALSE;
7092   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7093     return FALSE;
7094
7095   pspp_sheet_view_stop_editing (tree_view, FALSE);
7096   PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7097   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7098
7099   mode = 0;
7100   if (gtk_get_current_event_state (&state))
7101     {
7102       if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7103         mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
7104       if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
7105         mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
7106     }
7107   /* else we assume not pressed */
7108
7109   switch (step)
7110     {
7111     case GTK_MOVEMENT_LOGICAL_POSITIONS:
7112       pspp_sheet_view_move_cursor_tab (tree_view, count);
7113       break;
7114     case GTK_MOVEMENT_VISUAL_POSITIONS:
7115       pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
7116       break;
7117     case GTK_MOVEMENT_DISPLAY_LINES:
7118       pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
7119       break;
7120     case GTK_MOVEMENT_PAGES:
7121       pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
7122       break;
7123     case GTK_MOVEMENT_BUFFER_ENDS:
7124       pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7125       break;
7126     case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7127       pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7128       break;
7129     default:
7130       g_assert_not_reached ();
7131     }
7132
7133   return TRUE;
7134 }
7135
7136 static void
7137 pspp_sheet_view_put (PsppSheetView *tree_view,
7138                    GtkWidget   *child_widget,
7139                    /* in bin_window coordinates */
7140                    gint         x,
7141                    gint         y,
7142                    gint         width,
7143                    gint         height)
7144 {
7145   PsppSheetViewChild *child;
7146   
7147   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7148   g_return_if_fail (GTK_IS_WIDGET (child_widget));
7149
7150   child = g_slice_new (PsppSheetViewChild);
7151
7152   child->widget = child_widget;
7153   child->x = x;
7154   child->y = y;
7155   child->width = width;
7156   child->height = height;
7157
7158   tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7159
7160   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7161     gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7162   
7163   gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7164 }
7165
7166 void
7167 _pspp_sheet_view_child_move_resize (PsppSheetView *tree_view,
7168                                   GtkWidget   *widget,
7169                                   /* in tree coordinates */
7170                                   gint         x,
7171                                   gint         y,
7172                                   gint         width,
7173                                   gint         height)
7174 {
7175   PsppSheetViewChild *child = NULL;
7176   GList *list;
7177   GdkRectangle allocation;
7178
7179   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7180   g_return_if_fail (GTK_IS_WIDGET (widget));
7181
7182   for (list = tree_view->priv->children; list; list = list->next)
7183     {
7184       if (((PsppSheetViewChild *)list->data)->widget == widget)
7185         {
7186           child = list->data;
7187           break;
7188         }
7189     }
7190   if (child == NULL)
7191     return;
7192
7193   allocation.x = child->x = x;
7194   allocation.y = child->y = y;
7195   allocation.width = child->width = width;
7196   allocation.height = child->height = height;
7197
7198   if (gtk_widget_get_realized (widget))
7199     gtk_widget_size_allocate (widget, &allocation);
7200 }
7201
7202
7203 /* TreeModel Callbacks
7204  */
7205
7206 static void
7207 pspp_sheet_view_row_changed (GtkTreeModel *model,
7208                            GtkTreePath  *path,
7209                            GtkTreeIter  *iter,
7210                            gpointer      data)
7211 {
7212   PsppSheetView *tree_view = (PsppSheetView *)data;
7213   int node;
7214   gboolean free_path = FALSE;
7215   GtkTreePath *cursor_path;
7216
7217   g_return_if_fail (path != NULL || iter != NULL);
7218
7219   if (tree_view->priv->cursor != NULL)
7220     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7221   else
7222     cursor_path = NULL;
7223
7224   if (tree_view->priv->edited_column &&
7225       (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7226     pspp_sheet_view_stop_editing (tree_view, TRUE);
7227
7228   if (cursor_path != NULL)
7229     gtk_tree_path_free (cursor_path);
7230
7231   if (path == NULL)
7232     {
7233       path = gtk_tree_model_get_path (model, iter);
7234       free_path = TRUE;
7235     }
7236   else if (iter == NULL)
7237     gtk_tree_model_get_iter (model, iter, path);
7238
7239   _pspp_sheet_view_find_node (tree_view,
7240                               path,
7241                               &node);
7242
7243   if (node >= 0)
7244     {
7245       if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7246         pspp_sheet_view_node_queue_redraw (tree_view, node);
7247     }
7248   
7249   if (free_path)
7250     gtk_tree_path_free (path);
7251 }
7252
7253 static void
7254 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7255                             GtkTreePath  *path,
7256                             GtkTreeIter  *iter,
7257                             gpointer      data)
7258 {
7259   PsppSheetView *tree_view = (PsppSheetView *) data;
7260   gint *indices;
7261   int tmpnode = -1;
7262   gint height = tree_view->priv->fixed_height;
7263   gboolean free_path = FALSE;
7264   gboolean node_visible = TRUE;
7265
7266   g_return_if_fail (path != NULL || iter != NULL);
7267
7268   if (path == NULL)
7269     {
7270       path = gtk_tree_model_get_path (model, iter);
7271       free_path = TRUE;
7272     }
7273   else if (iter == NULL)
7274     gtk_tree_model_get_iter (model, iter, path);
7275
7276   tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7277
7278   /* Update all row-references */
7279   gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7280   indices = gtk_tree_path_get_indices (path);
7281   tmpnode = indices[0];
7282
7283   range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7284
7285   if (height > 0)
7286     {
7287       if (node_visible && node_is_visible (tree_view, tmpnode))
7288         gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7289       else
7290         gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7291     }
7292   else
7293     install_presize_handler (tree_view);
7294   if (free_path)
7295     gtk_tree_path_free (path);
7296 }
7297
7298 static void
7299 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7300                            GtkTreePath  *path,
7301                            gpointer      data)
7302 {
7303   PsppSheetView *tree_view = (PsppSheetView *)data;
7304   int node;
7305
7306   g_return_if_fail (path != NULL);
7307
7308   gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7309
7310   _pspp_sheet_view_find_node (tree_view, path, &node);
7311
7312   if (node < 0)
7313     return;
7314
7315   range_tower_delete (tree_view->priv->selected, node, 1);
7316
7317   /* Ensure we don't have a dangling pointer to a dead node */
7318   ensure_unprelighted (tree_view);
7319
7320   /* Cancel editting if we've started */
7321   pspp_sheet_view_stop_editing (tree_view, TRUE);
7322
7323   if (tree_view->priv->destroy_count_func)
7324     {
7325       gint child_count = 0;
7326       tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7327     }
7328
7329   tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7330
7331   if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7332     {
7333       gtk_tree_row_reference_free (tree_view->priv->top_row);
7334       tree_view->priv->top_row = NULL;
7335     }
7336
7337   install_scroll_sync_handler (tree_view);
7338
7339   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7340
7341 #if 0
7342   if (helper_data.changed)
7343     g_signal_emit_by_name (tree_view->priv->selection, "changed");
7344 #endif
7345 }
7346
7347 static void
7348 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7349                               GtkTreePath  *parent,
7350                               GtkTreeIter  *iter,
7351                               gint         *new_order,
7352                               gpointer      data)
7353 {
7354   PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7355   gint len;
7356
7357   /* XXX need to adjust selection */
7358   len = gtk_tree_model_iter_n_children (model, iter);
7359
7360   if (len < 2)
7361     return;
7362
7363   gtk_tree_row_reference_reordered (G_OBJECT (data),
7364                                     parent,
7365                                     iter,
7366                                     new_order);
7367
7368   if (gtk_tree_path_get_depth (parent) != 0)
7369     return;
7370
7371   if (tree_view->priv->edited_column)
7372     pspp_sheet_view_stop_editing (tree_view, TRUE);
7373
7374   /* we need to be unprelighted */
7375   ensure_unprelighted (tree_view);
7376
7377   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7378
7379   pspp_sheet_view_dy_to_top_row (tree_view);
7380 }
7381
7382
7383 /* Internal tree functions
7384  */
7385
7386
7387 static void
7388 pspp_sheet_view_get_background_xrange (PsppSheetView       *tree_view,
7389                                      PsppSheetViewColumn *column,
7390                                      gint              *x1,
7391                                      gint              *x2)
7392 {
7393   PsppSheetViewColumn *tmp_column = NULL;
7394   gint total_width;
7395   GList *list;
7396   gboolean rtl;
7397
7398   if (x1)
7399     *x1 = 0;
7400
7401   if (x2)
7402     *x2 = 0;
7403
7404   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7405
7406   total_width = 0;
7407   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7408        list;
7409        list = (rtl ? list->prev : list->next))
7410     {
7411       tmp_column = list->data;
7412
7413       if (tmp_column == column)
7414         break;
7415
7416       if (tmp_column->visible)
7417         total_width += tmp_column->width;
7418     }
7419
7420   if (tmp_column != column)
7421     {
7422       g_warning (G_STRLOC": passed-in column isn't in the tree");
7423       return;
7424     }
7425
7426   if (x1)
7427     *x1 = total_width;
7428
7429   if (x2)
7430     {
7431       if (column->visible)
7432         *x2 = total_width + column->width;
7433       else
7434         *x2 = total_width; /* width of 0 */
7435     }
7436 }
7437
7438 /* Make sure the node is visible vertically */
7439 static void
7440 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7441                                     int node)
7442 {
7443   gint node_dy, height;
7444   GtkTreePath *path = NULL;
7445
7446   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7447     return;
7448
7449   /* just return if the node is visible, avoiding a costly expose */
7450   node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7451   height = ROW_HEIGHT (tree_view);
7452   if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
7453       && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
7454                               + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
7455     return;
7456
7457   path = _pspp_sheet_view_find_path (tree_view, node);
7458   if (path)
7459     {
7460       /* We process updates because we want to clear old selected items when we scroll.
7461        * if this is removed, we get a "selection streak" at the bottom. */
7462       gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7463       pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7464       gtk_tree_path_free (path);
7465     }
7466 }
7467
7468 static void
7469 pspp_sheet_view_clamp_column_visible (PsppSheetView       *tree_view,
7470                                     PsppSheetViewColumn *column,
7471                                     gboolean           focus_to_cell)
7472 {
7473   gint x, width;
7474
7475   if (column == NULL)
7476     return;
7477
7478   x = column->allocation.x;
7479   width = column->allocation.width;
7480
7481   if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7482     {
7483       /* The column is larger than the horizontal page size.  If the
7484        * column has cells which can be focussed individually, then we make
7485        * sure the cell which gets focus is fully visible (if even the
7486        * focus cell is bigger than the page size, we make sure the
7487        * left-hand side of the cell is visible).
7488        *
7489        * If the column does not have those so-called special cells, we
7490        * make sure the left-hand side of the column is visible.
7491        */
7492
7493       if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7494         {
7495           GtkTreePath *cursor_path;
7496           GdkRectangle background_area, cell_area, focus_area;
7497
7498           cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7499
7500           pspp_sheet_view_get_cell_area (tree_view,
7501                                        cursor_path, column, &cell_area);
7502           pspp_sheet_view_get_background_area (tree_view,
7503                                              cursor_path, column,
7504                                              &background_area);
7505
7506           gtk_tree_path_free (cursor_path);
7507
7508           _pspp_sheet_view_column_get_focus_area (column,
7509                                                 &background_area,
7510                                                 &cell_area,
7511                                                 &focus_area);
7512
7513           x = focus_area.x;
7514           width = focus_area.width;
7515
7516           if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7517             {
7518               if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7519                 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7520                                           x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7521               else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7522                 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7523             }
7524         }
7525
7526       gtk_adjustment_set_value (tree_view->priv->hadjustment,
7527                                 CLAMP (x,
7528                                        gtk_adjustment_get_lower (tree_view->priv->hadjustment),
7529                                        gtk_adjustment_get_upper (tree_view->priv->hadjustment)
7530                                        - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)));
7531     }
7532   else
7533     {
7534       if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7535           gtk_adjustment_set_value (tree_view->priv->hadjustment,
7536                                     x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7537       else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7538         gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7539   }
7540 }
7541
7542 GtkTreePath *
7543 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7544                             int node)
7545 {
7546   GtkTreePath *path;
7547
7548   path = gtk_tree_path_new ();
7549   if (node >= 0)
7550     gtk_tree_path_append_index (path, node);
7551   return path;
7552 }
7553
7554 void
7555 _pspp_sheet_view_find_node (PsppSheetView  *tree_view,
7556                           GtkTreePath  *path,
7557                           int *node)
7558 {
7559   gint *indices = gtk_tree_path_get_indices (path);
7560   gint depth = gtk_tree_path_get_depth (path);
7561
7562   *node = -1;
7563   if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7564     return;
7565   *node = indices[0];
7566 }
7567
7568 static void
7569 pspp_sheet_view_add_move_binding (GtkBindingSet  *binding_set,
7570                                 guint           keyval,
7571                                 guint           modmask,
7572                                 gboolean        add_shifted_binding,
7573                                 GtkMovementStep step,
7574                                 gint            count)
7575 {
7576   
7577   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7578                                 "move-cursor", 2,
7579                                 G_TYPE_ENUM, step,
7580                                 G_TYPE_INT, count);
7581
7582   if (add_shifted_binding)
7583     gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7584                                   "move-cursor", 2,
7585                                   G_TYPE_ENUM, step,
7586                                   G_TYPE_INT, count);
7587
7588   if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7589    return;
7590
7591   gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7592                                 "move-cursor", 2,
7593                                 G_TYPE_ENUM, step,
7594                                 G_TYPE_INT, count);
7595
7596   gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7597                                 "move-cursor", 2,
7598                                 G_TYPE_ENUM, step,
7599                                 G_TYPE_INT, count);
7600 }
7601
7602 static void
7603 pspp_sheet_view_set_column_drag_info (PsppSheetView       *tree_view,
7604                                     PsppSheetViewColumn *column)
7605 {
7606   PsppSheetViewColumn *left_column;
7607   PsppSheetViewColumn *cur_column = NULL;
7608   PsppSheetViewColumnReorder *reorder;
7609   gboolean rtl;
7610   GList *tmp_list;
7611   gint left;
7612
7613   /* We want to precalculate the motion list such that we know what column slots
7614    * are available.
7615    */
7616   left_column = NULL;
7617   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7618
7619   /* First, identify all possible drop spots */
7620   if (rtl)
7621     tmp_list = g_list_last (tree_view->priv->columns);
7622   else
7623     tmp_list = g_list_first (tree_view->priv->columns);
7624
7625   while (tmp_list)
7626     {
7627       cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7628       tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7629
7630       if (cur_column->visible == FALSE)
7631         continue;
7632
7633       /* If it's not the column moving and func tells us to skip over the column, we continue. */
7634       if (left_column != column && cur_column != column &&
7635           tree_view->priv->column_drop_func &&
7636           ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7637         {
7638           left_column = cur_column;
7639           continue;
7640         }
7641       reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7642       reorder->left_column = left_column;
7643       left_column = reorder->right_column = cur_column;
7644
7645       tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7646     }
7647
7648   /* Add the last one */
7649   if (tree_view->priv->column_drop_func == NULL ||
7650       ((left_column != column) &&
7651        tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7652     {
7653       reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7654       reorder->left_column = left_column;
7655       reorder->right_column = NULL;
7656       tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7657     }
7658
7659   /* We quickly check to see if it even makes sense to reorder columns. */
7660   /* If there is nothing that can be moved, then we return */
7661
7662   if (tree_view->priv->column_drag_info == NULL)
7663     return;
7664
7665   /* We know there are always 2 slots possbile, as you can always return column. */
7666   /* If that's all there is, return */
7667   if (tree_view->priv->column_drag_info->next == NULL || 
7668       (tree_view->priv->column_drag_info->next->next == NULL &&
7669        ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7670        ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7671     {
7672       for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7673         g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7674       g_list_free (tree_view->priv->column_drag_info);
7675       tree_view->priv->column_drag_info = NULL;
7676       return;
7677     }
7678   /* We fill in the ranges for the columns, now that we've isolated them */
7679   left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7680
7681   for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7682     {
7683       reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7684
7685       reorder->left_align = left;
7686       if (tmp_list->next != NULL)
7687         {
7688           g_assert (tmp_list->next->data);
7689           left = reorder->right_align = (reorder->right_column->allocation.x +
7690                                          reorder->right_column->allocation.width +
7691                                          ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7692         }
7693       else
7694         {
7695           gint width = gdk_window_get_width (tree_view->priv->header_window);
7696           reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7697         }
7698     }
7699 }
7700
7701 void
7702 _pspp_sheet_view_column_start_drag (PsppSheetView       *tree_view,
7703                                   PsppSheetViewColumn *column)
7704 {
7705   GdkEvent *send_event;
7706   GtkAllocation allocation;
7707   gint x, y;
7708   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7709   GdkDisplay *display = gdk_screen_get_display (screen);
7710
7711   g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7712   g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7713   g_return_if_fail (column->button);
7714
7715   pspp_sheet_view_set_column_drag_info (tree_view, column);
7716
7717   if (tree_view->priv->column_drag_info == NULL)
7718     return;
7719
7720   if (tree_view->priv->drag_window == NULL)
7721     {
7722       GdkWindowAttr attributes;
7723       guint attributes_mask;
7724
7725       attributes.window_type = GDK_WINDOW_CHILD;
7726       attributes.wclass = GDK_INPUT_OUTPUT;
7727       attributes.x = column->allocation.x;
7728       attributes.y = 0;
7729       attributes.width = column->allocation.width;
7730       attributes.height = column->allocation.height;
7731       attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7732       attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7733       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ;
7734
7735       tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7736                                                      &attributes,
7737                                                      attributes_mask);
7738       gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7739     }
7740
7741   gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7742   gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7743
7744   gtk_grab_remove (column->button);
7745
7746   send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7747   send_event->crossing.send_event = TRUE;
7748   send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
7749   send_event->crossing.subwindow = NULL;
7750   send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7751   send_event->crossing.time = GDK_CURRENT_TIME;
7752
7753   gtk_propagate_event (column->button, send_event);
7754   gdk_event_free (send_event);
7755
7756   send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7757   send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7758   send_event->button.send_event = TRUE;
7759   send_event->button.time = GDK_CURRENT_TIME;
7760   send_event->button.x = -1;
7761   send_event->button.y = -1;
7762   send_event->button.axes = NULL;
7763   send_event->button.state = 0;
7764   send_event->button.button = 1;
7765   send_event->button.device = 
7766     gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
7767
7768   send_event->button.x_root = 0;
7769   send_event->button.y_root = 0;
7770
7771   gtk_propagate_event (column->button, send_event);
7772   gdk_event_free (send_event);
7773
7774   /* Kids, don't try this at home */
7775   g_object_ref (column->button);
7776   gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7777   gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7778   gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7779   g_object_unref (column->button);
7780
7781   tree_view->priv->drag_column_x = column->allocation.x;
7782   allocation = column->allocation;
7783   allocation.x = 0;
7784   gtk_widget_size_allocate (column->button, &allocation);
7785   gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7786
7787   tree_view->priv->drag_column = column;
7788   gdk_window_show (tree_view->priv->drag_window);
7789
7790   gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7791
7792   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7793   while (gtk_events_pending ())
7794     gtk_main_iteration ();
7795
7796   PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7797   gdk_pointer_grab (tree_view->priv->drag_window,
7798                     FALSE,
7799                     GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7800                     NULL, NULL, GDK_CURRENT_TIME);
7801   gdk_keyboard_grab (tree_view->priv->drag_window,
7802                      FALSE,
7803                      GDK_CURRENT_TIME);
7804 }
7805
7806 void
7807 _pspp_sheet_view_queue_draw_node (PsppSheetView        *tree_view,
7808                                 int node,
7809                                 const GdkRectangle *clip_rect)
7810 {
7811   GdkRectangle rect;
7812   GtkAllocation allocation;
7813
7814   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7815     return;
7816
7817   gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
7818   rect.x = 0;
7819   rect.width = MAX (tree_view->priv->width, allocation.width);
7820
7821   rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7822   rect.height = ROW_HEIGHT (tree_view);
7823
7824   if (clip_rect)
7825     {
7826       GdkRectangle new_rect;
7827
7828       gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7829
7830       gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7831     }
7832   else
7833     {
7834       gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7835     }
7836 }
7837
7838 static void
7839 pspp_sheet_view_queue_draw_path (PsppSheetView        *tree_view,
7840                                GtkTreePath        *path,
7841                                const GdkRectangle *clip_rect)
7842 {
7843   int node = -1;
7844
7845   _pspp_sheet_view_find_node (tree_view, path, &node);
7846
7847   if (node)
7848     _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7849 }
7850
7851 static void
7852 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7853
7854 {
7855   GtkTreePath *cursor_path;
7856
7857   if ((tree_view->priv->row_count == 0) ||
7858       (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7859     return;
7860
7861   cursor_path = NULL;
7862   if (tree_view->priv->cursor)
7863     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7864
7865   if (cursor_path == NULL)
7866     {
7867       /* There's no cursor.  Move the cursor to the first selected row, if any
7868        * are selected, otherwise to the first row in the sheetview.
7869        */
7870       GList *selected_rows;
7871       GtkTreeModel *model;
7872       PsppSheetSelection *selection;
7873
7874       selection = pspp_sheet_view_get_selection (tree_view);
7875       selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7876
7877       if (selected_rows)
7878         {
7879           /* XXX we could avoid doing O(n) work to get this result */
7880           cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7881           g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7882           g_list_free (selected_rows);
7883         }
7884       else
7885         {
7886           cursor_path = gtk_tree_path_new_first ();
7887           search_first_focusable_path (tree_view, &cursor_path,
7888                                        TRUE, NULL);
7889         }
7890
7891       gtk_tree_row_reference_free (tree_view->priv->cursor);
7892       tree_view->priv->cursor = NULL;
7893
7894       if (cursor_path)
7895         {
7896           if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7897               tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7898             pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7899           else
7900             pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7901         }
7902     }
7903
7904   if (cursor_path)
7905     {
7906       /* Now find a column for the cursor. */
7907       PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7908
7909       pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7910       gtk_tree_path_free (cursor_path);
7911
7912       if (tree_view->priv->focus_column == NULL)
7913         {
7914           GList *list;
7915           for (list = tree_view->priv->columns; list; list = list->next)
7916             {
7917               if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7918                 {
7919                   tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7920                   pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7921                   pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7922                   break;
7923                 }
7924             }
7925
7926         }
7927     }
7928 }
7929
7930 static gboolean
7931 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7932                                    gint         count,
7933                                    PsppSheetSelectMode mode)
7934 {
7935   gint selection_count;
7936   int cursor_node = -1;
7937   int new_cursor_node = -1;
7938   GtkTreePath *cursor_path = NULL;
7939   gboolean grab_focus = TRUE;
7940
7941   if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7942     return FALSE;
7943
7944   cursor_path = NULL;
7945   if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7946     /* FIXME: we lost the cursor; should we get the first? */
7947     return FALSE;
7948
7949   cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7950   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7951
7952   if (cursor_node < 0)
7953     /* FIXME: we lost the cursor; should we get the first? */
7954     return FALSE;
7955
7956   selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7957
7958   if (selection_count == 0
7959       && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7960       && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7961     {
7962       /* Don't move the cursor, but just select the current node */
7963       new_cursor_node = cursor_node;
7964     }
7965   else
7966     {
7967       if (count == -1)
7968         new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7969       else
7970         new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7971     }
7972
7973   gtk_tree_path_free (cursor_path);
7974
7975   if (new_cursor_node)
7976     {
7977       cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7978
7979       search_first_focusable_path (tree_view, &cursor_path,
7980                                    (count != -1),
7981                                    &new_cursor_node);
7982
7983       if (cursor_path)
7984         gtk_tree_path_free (cursor_path);
7985     }
7986
7987   /*
7988    * If the list has only one item and multi-selection is set then select
7989    * the row (if not yet selected).
7990    */
7991   if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7992        tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7993       new_cursor_node < 0)
7994     {
7995       if (count == -1)
7996         new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7997       else
7998         new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7999
8000       if (new_cursor_node < 0
8001           && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
8002         {
8003           new_cursor_node = cursor_node;
8004         }
8005       else
8006         {
8007           new_cursor_node = -1;
8008         }
8009     }
8010
8011   if (new_cursor_node >= 0)
8012     {
8013       cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
8014       pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
8015       gtk_tree_path_free (cursor_path);
8016     }
8017   else
8018     {
8019       pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8020
8021       if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8022         {
8023           if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
8024                                           count < 0 ?
8025                                           GTK_DIR_UP : GTK_DIR_DOWN))
8026             {
8027               GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8028
8029               if (toplevel)
8030                 gtk_widget_child_focus (toplevel,
8031                                         count < 0 ?
8032                                         GTK_DIR_TAB_BACKWARD :
8033                                         GTK_DIR_TAB_FORWARD);
8034
8035               grab_focus = FALSE;
8036             }
8037         }
8038       else
8039         {
8040           gtk_widget_error_bell (GTK_WIDGET (tree_view));
8041         }
8042     }
8043
8044   if (grab_focus)
8045     gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8046
8047   return new_cursor_node >= 0;
8048 }
8049
8050 static void
8051 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
8052                                           gint         count,
8053                                           PsppSheetSelectMode mode)
8054 {
8055   int cursor_node = -1;
8056   GtkTreePath *old_cursor_path = NULL;
8057   GtkTreePath *cursor_path = NULL;
8058   int start_cursor_node = -1;
8059   gint y;
8060   gint window_y;
8061   gint vertical_separator;
8062
8063   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8064     return;
8065
8066   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8067     old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8068   else
8069     /* This is sorta weird.  Focus in should give us a cursor */
8070     return;
8071
8072   gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
8073   _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
8074
8075   if (cursor_node < 0)
8076     {
8077       /* FIXME: we lost the cursor.  Should we try to get one? */
8078       gtk_tree_path_free (old_cursor_path);
8079       return;
8080     }
8081
8082   y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8083   window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
8084   y += tree_view->priv->cursor_offset;
8085   y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
8086   y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment),  (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
8087
8088   if (y >= tree_view->priv->height)
8089     y = tree_view->priv->height - 1;
8090
8091   tree_view->priv->cursor_offset =
8092     pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
8093
8094   if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
8095     {
8096       cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
8097       tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
8098     }
8099
8100   y -= tree_view->priv->cursor_offset;
8101   cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8102
8103   start_cursor_node = cursor_node;
8104
8105   if (! search_first_focusable_path (tree_view, &cursor_path,
8106                                      (count != -1),
8107                                      &cursor_node))
8108     {
8109       /* It looks like we reached the end of the view without finding
8110        * a focusable row.  We will step backwards to find the last
8111        * focusable row.
8112        */
8113       cursor_node = start_cursor_node;
8114       cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8115
8116       search_first_focusable_path (tree_view, &cursor_path,
8117                                    (count == -1),
8118                                    &cursor_node);
8119     }
8120
8121   if (!cursor_path)
8122     goto cleanup;
8123
8124   /* update y */
8125   y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8126
8127   pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
8128
8129   y -= window_y;
8130   pspp_sheet_view_scroll_to_point (tree_view, -1, y);
8131   pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8132   _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8133
8134   if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
8135     gtk_widget_error_bell (GTK_WIDGET (tree_view));
8136
8137   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8138
8139 cleanup:
8140   gtk_tree_path_free (old_cursor_path);
8141   gtk_tree_path_free (cursor_path);
8142 }
8143
8144 static void
8145 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8146                                         gint         count,
8147                                         PsppSheetSelectMode mode)
8148 {
8149   int cursor_node = -1;
8150   GtkTreePath *cursor_path = NULL;
8151   PsppSheetViewColumn *column;
8152   GtkTreeIter iter;
8153   GList *list;
8154   gboolean found_column = FALSE;
8155   gboolean rtl;
8156
8157   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8158
8159   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8160     return;
8161
8162   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8163     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8164   else
8165     return;
8166
8167   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8168   if (cursor_node < 0)
8169     return;
8170   if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8171     {
8172       gtk_tree_path_free (cursor_path);
8173       return;
8174     }
8175   gtk_tree_path_free (cursor_path);
8176
8177   list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8178   if (tree_view->priv->focus_column)
8179     {
8180       for (; list; list = (rtl ? list->prev : list->next))
8181         {
8182           if (list->data == tree_view->priv->focus_column)
8183             break;
8184         }
8185     }
8186
8187   while (list)
8188     {
8189       gboolean left, right;
8190
8191       column = list->data;
8192       if (column->visible == FALSE || column->row_head)
8193         goto loop_end;
8194
8195       pspp_sheet_view_column_cell_set_cell_data (column,
8196                                                tree_view->priv->model,
8197                                                &iter);
8198
8199       if (rtl)
8200         {
8201           right = list->prev ? TRUE : FALSE;
8202           left = list->next ? TRUE : FALSE;
8203         }
8204       else
8205         {
8206           left = list->prev ? TRUE : FALSE;
8207           right = list->next ? TRUE : FALSE;
8208         }
8209
8210       if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8211         {
8212           tree_view->priv->focus_column = column;
8213           found_column = TRUE;
8214           break;
8215         }
8216     loop_end:
8217       if (count == 1)
8218         list = rtl ? list->prev : list->next;
8219       else
8220         list = rtl ? list->next : list->prev;
8221     }
8222
8223   if (found_column)
8224     {
8225       _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8226       g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8227       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8228     }
8229   else
8230     {
8231       gtk_widget_error_bell (GTK_WIDGET (tree_view));
8232     }
8233
8234   pspp_sheet_view_clamp_column_visible (tree_view,
8235                                       tree_view->priv->focus_column, TRUE);
8236 }
8237
8238 static void
8239 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8240                                             gint         count,
8241                                             PsppSheetSelectMode mode)
8242 {
8243   int cursor_node = -1;
8244   GtkTreePath *cursor_path = NULL;
8245   PsppSheetViewColumn *column;
8246   PsppSheetViewColumn *found_column;
8247   GtkTreeIter iter;
8248   GList *list;
8249   gboolean rtl;
8250
8251   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8252
8253   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8254     return;
8255
8256   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8257     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8258   else
8259     return;
8260
8261   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8262   if (cursor_node < 0)
8263     return;
8264   if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8265     {
8266       gtk_tree_path_free (cursor_path);
8267       return;
8268     }
8269   gtk_tree_path_free (cursor_path);
8270
8271   list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8272   if (tree_view->priv->focus_column)
8273     {
8274       for (; list; list = (rtl ? list->prev : list->next))
8275         {
8276           if (list->data == tree_view->priv->focus_column)
8277             break;
8278         }
8279     }
8280
8281   found_column = NULL;
8282   while (list)
8283     {
8284       gboolean left, right;
8285
8286       column = list->data;
8287       if (column->visible == FALSE || column->row_head)
8288         goto loop_end;
8289
8290       pspp_sheet_view_column_cell_set_cell_data (column,
8291                                                tree_view->priv->model,
8292                                                &iter);
8293
8294       if (rtl)
8295         {
8296           right = list->prev ? TRUE : FALSE;
8297           left = list->next ? TRUE : FALSE;
8298         }
8299       else
8300         {
8301           left = list->prev ? TRUE : FALSE;
8302           right = list->next ? TRUE : FALSE;
8303         }
8304
8305       if (column->tabbable
8306           && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8307         found_column = column;
8308
8309     loop_end:
8310       if (count == 1)
8311         list = rtl ? list->prev : list->next;
8312       else
8313         list = rtl ? list->next : list->prev;
8314     }
8315
8316   if (found_column)
8317     {
8318       tree_view->priv->focus_column = found_column;
8319       _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8320       g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8321       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8322     }
8323
8324   pspp_sheet_view_clamp_column_visible (tree_view,
8325                                       tree_view->priv->focus_column, TRUE);
8326 }
8327
8328 static gboolean
8329 try_move_cursor_tab (PsppSheetView *tree_view,
8330                      gboolean start_at_focus_column,
8331                      gint count)
8332 {
8333   PsppSheetViewColumn *column;
8334   GtkTreeIter iter;
8335   int cursor_node = -1;
8336   GtkTreePath *cursor_path = NULL;
8337   gboolean rtl;
8338   GList *list;
8339
8340   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8341     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8342   else
8343     return TRUE;
8344
8345   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8346   if (cursor_node < 0)
8347     return TRUE;
8348   if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8349     {
8350       gtk_tree_path_free (cursor_path);
8351       return TRUE;
8352     }
8353   gtk_tree_path_free (cursor_path);
8354
8355   rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8356   if (start_at_focus_column)
8357     {
8358       list = (rtl
8359               ? g_list_last (tree_view->priv->columns)
8360               : g_list_first (tree_view->priv->columns));
8361       if (tree_view->priv->focus_column)
8362         {
8363           for (; list; list = (rtl ? list->prev : list->next))
8364             {
8365               if (list->data == tree_view->priv->focus_column)
8366                 break;
8367             }
8368         }
8369     }
8370   else
8371     {
8372       list = (rtl ^ (count == 1)
8373               ? g_list_first (tree_view->priv->columns)
8374               : g_list_last (tree_view->priv->columns));
8375     }
8376
8377   while (list)
8378     {
8379       gboolean left, right;
8380
8381       column = list->data;
8382       if (column->visible == FALSE || !column->tabbable)
8383         goto loop_end;
8384
8385       pspp_sheet_view_column_cell_set_cell_data (column,
8386                                                  tree_view->priv->model,
8387                                                  &iter);
8388
8389       if (rtl)
8390         {
8391           right = list->prev ? TRUE : FALSE;
8392           left = list->next ? TRUE : FALSE;
8393         }
8394       else
8395         {
8396           left = list->prev ? TRUE : FALSE;
8397           right = list->next ? TRUE : FALSE;
8398         }
8399
8400       if (column->tabbable
8401           && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8402         {
8403           tree_view->priv->focus_column = column;
8404           _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8405           g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8406           gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8407           return TRUE;
8408         }
8409     loop_end:
8410       if (count == 1)
8411         list = rtl ? list->prev : list->next;
8412       else
8413         list = rtl ? list->next : list->prev;
8414     }
8415
8416   return FALSE;
8417 }
8418
8419 static void
8420 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8421                                  gint         count)
8422 {
8423   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8424     return;
8425
8426   if (!try_move_cursor_tab (tree_view, TRUE, count))
8427     {
8428       if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8429           && !try_move_cursor_tab (tree_view, FALSE, count))
8430         gtk_widget_error_bell (GTK_WIDGET (tree_view));
8431     }
8432
8433   pspp_sheet_view_clamp_column_visible (tree_view,
8434                                         tree_view->priv->focus_column, TRUE);
8435 }
8436
8437 static void
8438 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8439                                        gint         count,
8440                                        PsppSheetSelectMode mode)
8441 {
8442   int cursor_node;
8443   GtkTreePath *path;
8444   GtkTreePath *old_path;
8445
8446   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8447     return;
8448
8449   g_return_if_fail (tree_view->priv->row_count > 0);
8450
8451   pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8452
8453   if (count == -1)
8454     {
8455       /* Now go forward to find the first focusable row. */
8456       path = _pspp_sheet_view_find_path (tree_view, 0);
8457       search_first_focusable_path (tree_view, &path,
8458                                    TRUE, &cursor_node);
8459     }
8460   else
8461     {
8462       /* Now go backwards to find last focusable row. */
8463       path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8464       search_first_focusable_path (tree_view, &path,
8465                                    FALSE, &cursor_node);
8466     }
8467
8468   if (!path)
8469     goto cleanup;
8470
8471   if (gtk_tree_path_compare (old_path, path))
8472     {
8473       pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8474       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8475     }
8476   else
8477     {
8478       gtk_widget_error_bell (GTK_WIDGET (tree_view));
8479     }
8480
8481 cleanup:
8482   gtk_tree_path_free (old_path);
8483   gtk_tree_path_free (path);
8484 }
8485
8486 static gboolean
8487 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8488 {
8489   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8490     return FALSE;
8491
8492   if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8493       tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8494     return FALSE;
8495
8496   pspp_sheet_selection_select_all (tree_view->priv->selection);
8497
8498   return TRUE;
8499 }
8500
8501 static gboolean
8502 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8503 {
8504   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8505     return FALSE;
8506
8507   if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8508       tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8509     return FALSE;
8510
8511   pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8512
8513   return TRUE;
8514 }
8515
8516 static gboolean
8517 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8518                                         gboolean     start_editing,
8519                                         PsppSheetSelectMode mode)
8520 {
8521   int new_node = -1;
8522   int cursor_node = -1;
8523   GtkTreePath *cursor_path = NULL;
8524
8525   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8526     return FALSE;
8527
8528   if (tree_view->priv->cursor)
8529     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8530
8531   if (cursor_path == NULL)
8532     return FALSE;
8533
8534   _pspp_sheet_view_find_node (tree_view, cursor_path,
8535                               &cursor_node);
8536
8537   if (cursor_node < 0)
8538     {
8539       gtk_tree_path_free (cursor_path);
8540       return FALSE;
8541     }
8542
8543   if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8544       tree_view->priv->focus_column)
8545     {
8546       if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8547         {
8548           gtk_tree_path_free (cursor_path);
8549           return TRUE;
8550         }
8551     }
8552
8553   _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8554                                             cursor_node,
8555                                             cursor_path,
8556                                             mode,
8557                                             FALSE);
8558
8559   /* We bail out if the original (tree, node) don't exist anymore after
8560    * handling the selection-changed callback.  We do return TRUE because
8561    * the key press has been handled at this point.
8562    */
8563   _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8564
8565   if (cursor_node != new_node)
8566     return FALSE;
8567
8568   pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8569
8570   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8571   _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8572
8573   if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8574     pspp_sheet_view_row_activated (tree_view, cursor_path,
8575                                  tree_view->priv->focus_column);
8576     
8577   gtk_tree_path_free (cursor_path);
8578
8579   return TRUE;
8580 }
8581
8582 static gboolean
8583 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8584 {
8585   int new_node = -1;
8586   int cursor_node = -1;
8587   GtkTreePath *cursor_path = NULL;
8588
8589   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8590     return FALSE;
8591
8592   cursor_path = NULL;
8593   if (tree_view->priv->cursor)
8594     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8595
8596   if (cursor_path == NULL)
8597     return FALSE;
8598
8599   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8600   if (cursor_node < 0)
8601     {
8602       gtk_tree_path_free (cursor_path);
8603       return FALSE;
8604     }
8605
8606   _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8607                                             cursor_node,
8608                                             cursor_path,
8609                                             PSPP_SHEET_SELECT_MODE_TOGGLE,
8610                                             FALSE);
8611
8612   /* We bail out if the original (tree, node) don't exist anymore after
8613    * handling the selection-changed callback.  We do return TRUE because
8614    * the key press has been handled at this point.
8615    */
8616   _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8617
8618   if (cursor_node != new_node)
8619     return FALSE;
8620
8621   pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8622
8623   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8624   pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8625   gtk_tree_path_free (cursor_path);
8626
8627   return TRUE;
8628 }
8629
8630 static gboolean
8631 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8632 {
8633   pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8634   tree_view->priv->typeselect_flush_timeout = 0;
8635
8636   return FALSE;
8637 }
8638
8639 /* Cut and paste from gtkwindow.c */
8640 static void
8641 send_focus_change (GtkWidget *widget,
8642                    gboolean   in)
8643 {
8644   GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8645
8646   fevent->focus_change.type = GDK_FOCUS_CHANGE;
8647   fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
8648   fevent->focus_change.in = in;
8649   
8650   gtk_widget_send_focus_change (widget, fevent);
8651   gdk_event_free (fevent);
8652 }
8653
8654 static void
8655 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8656 {
8657   GtkWidget *frame, *vbox, *toplevel;
8658   GdkScreen *screen;
8659
8660   if (tree_view->priv->search_custom_entry_set)
8661     return;
8662
8663   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8664   screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8665
8666    if (tree_view->priv->search_window != NULL)
8667      {
8668        if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8669          gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8670                                       GTK_WINDOW (tree_view->priv->search_window));
8671        else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)))
8672          gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
8673                                          GTK_WINDOW (tree_view->priv->search_window));
8674        gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8675        return;
8676      }
8677    
8678   tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8679   gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8680
8681   if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8682     gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8683                                  GTK_WINDOW (tree_view->priv->search_window));
8684
8685   gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8686                             GDK_WINDOW_TYPE_HINT_UTILITY);
8687   gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8688   g_signal_connect (tree_view->priv->search_window, "delete-event",
8689                     G_CALLBACK (pspp_sheet_view_search_delete_event),
8690                     tree_view);
8691   g_signal_connect (tree_view->priv->search_window, "key-press-event",
8692                     G_CALLBACK (pspp_sheet_view_search_key_press_event),
8693                     tree_view);
8694   g_signal_connect (tree_view->priv->search_window, "button-press-event",
8695                     G_CALLBACK (pspp_sheet_view_search_button_press_event),
8696                     tree_view);
8697   g_signal_connect (tree_view->priv->search_window, "scroll-event",
8698                     G_CALLBACK (pspp_sheet_view_search_scroll_event),
8699                     tree_view);
8700
8701   frame = gtk_frame_new (NULL);
8702   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8703   gtk_widget_show (frame);
8704   gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8705
8706   vbox = gtk_vbox_new (FALSE, 0);
8707   gtk_widget_show (vbox);
8708   gtk_container_add (GTK_CONTAINER (frame), vbox);
8709   gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8710
8711   /* add entry */
8712   tree_view->priv->search_entry = gtk_entry_new ();
8713   gtk_widget_show (tree_view->priv->search_entry);
8714   g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8715                     G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8716                     tree_view);
8717   g_signal_connect (tree_view->priv->search_entry,
8718                     "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8719                     tree_view);
8720
8721 #if GTK3_TRANSITION
8722   g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8723                     "preedit-changed",
8724                     G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8725                     tree_view);
8726 #endif
8727
8728   gtk_container_add (GTK_CONTAINER (vbox),
8729                      tree_view->priv->search_entry);
8730
8731   gtk_widget_realize (tree_view->priv->search_entry);
8732 }
8733
8734 /* Pops up the interactive search entry.  If keybinding is TRUE then the user
8735  * started this by typing the start_interactive_search keybinding.  Otherwise, it came from 
8736  */
8737 static gboolean
8738 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8739                                              gboolean     keybinding)
8740 {
8741   /* We only start interactive search if we have focus or the columns
8742    * have focus.  If one of our children have focus, we don't want to
8743    * start the search.
8744    */
8745   GList *list;
8746   gboolean found_focus = FALSE;
8747   GtkWidgetClass *entry_parent_class;
8748   
8749   if (!tree_view->priv->enable_search && !keybinding)
8750     return FALSE;
8751
8752   if (tree_view->priv->search_custom_entry_set)
8753     return FALSE;
8754
8755   if (tree_view->priv->search_window != NULL &&
8756       gtk_widget_get_visible (tree_view->priv->search_window))
8757     return TRUE;
8758
8759   for (list = tree_view->priv->columns; list; list = list->next)
8760     {
8761       PsppSheetViewColumn *column;
8762
8763       column = list->data;
8764       if (! column->visible)
8765         continue;
8766
8767       if (column->button && gtk_widget_has_focus (column->button))
8768         {
8769           found_focus = TRUE;
8770           break;
8771         }
8772     }
8773   
8774   if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8775     found_focus = TRUE;
8776
8777   if (!found_focus)
8778     return FALSE;
8779
8780   if (tree_view->priv->search_column < 0)
8781     return FALSE;
8782
8783   pspp_sheet_view_ensure_interactive_directory (tree_view);
8784
8785   if (keybinding)
8786     gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8787
8788   /* done, show it */
8789   tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8790   gtk_widget_show (tree_view->priv->search_window);
8791   if (tree_view->priv->search_entry_changed_id == 0)
8792     {
8793       tree_view->priv->search_entry_changed_id =
8794         g_signal_connect (tree_view->priv->search_entry, "changed",
8795                           G_CALLBACK (pspp_sheet_view_search_init),
8796                           tree_view);
8797     }
8798
8799   tree_view->priv->typeselect_flush_timeout =
8800     gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8801                    (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8802                    tree_view);
8803
8804   /* Grab focus will select all the text.  We don't want that to happen, so we
8805    * call the parent instance and bypass the selection change.  This is probably
8806    * really non-kosher. */
8807   entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8808   (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8809
8810   /* send focus-in event */
8811   send_focus_change (tree_view->priv->search_entry, TRUE);
8812
8813   /* search first matching iter */
8814   pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8815
8816   return TRUE;
8817 }
8818
8819 static gboolean
8820 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8821 {
8822   return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8823 }
8824
8825 /* this function returns the new width of the column being resized given
8826  * the column and x position of the cursor; the x cursor position is passed
8827  * in as a pointer and automagicly corrected if it's beyond min/max limits
8828  */
8829 static gint
8830 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8831                                 gint       i,
8832                                 gint      *x)
8833 {
8834   PsppSheetViewColumn *column;
8835   gint width;
8836   gboolean rtl;
8837
8838   /* first translate the x position from gtk_widget_get_window (widget)
8839    * to clist->clist_window
8840    */
8841   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8842   column = g_list_nth (tree_view->priv->columns, i)->data;
8843   width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8844  
8845   /* Clamp down the value */
8846   if (column->min_width == -1)
8847     width = MAX (column->button_request, width);
8848   else
8849     width = MAX (column->min_width, width);
8850   if (column->max_width != -1)
8851     width = MIN (width, column->max_width);
8852
8853   *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8854  
8855   return width;
8856 }
8857
8858
8859 /* FIXME this adjust_allocation is a big cut-and-paste from
8860  * GtkCList, needs to be some "official" way to do this
8861  * factored out.
8862  */
8863 typedef struct
8864 {
8865   GdkWindow *window;
8866   int dx;
8867   int dy;
8868 } ScrollData;
8869
8870 /* The window to which gtk_widget_get_window (widget) is relative */
8871 #define ALLOCATION_WINDOW(widget)               \
8872    (!gtk_widget_get_has_window (widget) ?               \
8873     gtk_widget_get_window (widget) :                          \
8874     gdk_window_get_parent (gtk_widget_get_window (widget)))
8875
8876 static void
8877 adjust_allocation_recurse (GtkWidget *widget,
8878                            gpointer   data)
8879 {
8880   ScrollData *scroll_data = data;
8881   GtkAllocation allocation;
8882   gtk_widget_get_allocation (widget, &allocation);
8883   /* Need to really size allocate instead of just poking
8884    * into widget->allocation if the widget is not realized.
8885    * FIXME someone figure out why this was.
8886    */
8887   if (!gtk_widget_get_realized (widget))
8888     {
8889       if (gtk_widget_get_visible (widget))
8890         {
8891           GdkRectangle tmp_rectangle = allocation;
8892           tmp_rectangle.x += scroll_data->dx;
8893           tmp_rectangle.y += scroll_data->dy;
8894           
8895           gtk_widget_size_allocate (widget, &tmp_rectangle);
8896         }
8897     }
8898   else
8899     {
8900       if (ALLOCATION_WINDOW (widget) == scroll_data->window)
8901         {
8902           allocation.x += scroll_data->dx;
8903           allocation.y += scroll_data->dy;
8904           
8905           if (GTK_IS_CONTAINER (widget))
8906             gtk_container_forall (GTK_CONTAINER (widget),
8907                                   adjust_allocation_recurse,
8908                                   data);
8909         }
8910     }
8911 }
8912
8913 static void
8914 adjust_allocation (GtkWidget *widget,
8915                    int        dx,
8916                    int        dy)
8917 {
8918   ScrollData scroll_data;
8919
8920   if (gtk_widget_get_realized (widget))
8921     scroll_data.window = ALLOCATION_WINDOW (widget);
8922   else
8923     scroll_data.window = NULL;
8924     
8925   scroll_data.dx = dx;
8926   scroll_data.dy = dy;
8927   
8928   adjust_allocation_recurse (widget, &scroll_data);
8929 }
8930
8931 void 
8932 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8933
8934 /* Callbacks */
8935 static void
8936 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8937                                   PsppSheetView   *tree_view)
8938 {
8939   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8940     {
8941       GList *list;
8942       gint dy;
8943         
8944       gdk_window_move (tree_view->priv->bin_window,
8945                        - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8946                        TREE_VIEW_HEADER_HEIGHT (tree_view));
8947       gdk_window_move (tree_view->priv->header_window,
8948                        - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8949                        0);
8950       dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8951       if (dy)
8952         {
8953           update_prelight (tree_view,
8954                            tree_view->priv->event_last_x,
8955                            tree_view->priv->event_last_y - dy);
8956
8957           if (tree_view->priv->edited_column &&
8958               GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget))
8959             {
8960               GList *list;
8961               GtkWidget *widget;
8962               PsppSheetViewChild *child = NULL;
8963
8964               widget = GTK_WIDGET (tree_view->priv->edited_column->editable_widget);
8965               adjust_allocation (widget, 0, dy); 
8966               
8967               for (list = tree_view->priv->children; list; list = list->next)
8968                 {
8969                   child = (PsppSheetViewChild *)list->data;
8970                   if (child->widget == widget)
8971                     {
8972                       child->y += dy;
8973                       break;
8974                     }
8975                 }
8976             }
8977         }
8978       gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8979
8980       if (tree_view->priv->dy != (int) gtk_adjustment_get_value (tree_view->priv->vadjustment))
8981         {
8982           /* update our dy and top_row */
8983           tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8984
8985           if (!tree_view->priv->in_top_row_to_dy)
8986             pspp_sheet_view_dy_to_top_row (tree_view);
8987         }
8988     }
8989 }
8990
8991 \f
8992
8993 /* Public methods
8994  */
8995
8996 /**
8997  * pspp_sheet_view_new:
8998  *
8999  * Creates a new #PsppSheetView widget.
9000  *
9001  * Return value: A newly created #PsppSheetView widget.
9002  **/
9003 GtkWidget *
9004 pspp_sheet_view_new (void)
9005 {
9006   return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
9007 }
9008
9009 /**
9010  * pspp_sheet_view_new_with_model:
9011  * @model: the model.
9012  *
9013  * Creates a new #PsppSheetView widget with the model initialized to @model.
9014  *
9015  * Return value: A newly created #PsppSheetView widget.
9016  **/
9017 GtkWidget *
9018 pspp_sheet_view_new_with_model (GtkTreeModel *model)
9019 {
9020   return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
9021 }
9022
9023 /* Public Accessors
9024  */
9025
9026 /**
9027  * pspp_sheet_view_get_model:
9028  * @tree_view: a #PsppSheetView
9029  *
9030  * Returns the model the #PsppSheetView is based on.  Returns %NULL if the
9031  * model is unset.
9032  *
9033  * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
9034  **/
9035 GtkTreeModel *
9036 pspp_sheet_view_get_model (PsppSheetView *tree_view)
9037 {
9038   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9039
9040   return tree_view->priv->model;
9041 }
9042
9043 /**
9044  * pspp_sheet_view_set_model:
9045  * @tree_view: A #GtkTreeNode.
9046  * @model: (allow-none): The model.
9047  *
9048  * Sets the model for a #PsppSheetView.  If the @tree_view already has a model
9049  * set, it will remove it before setting the new model.  If @model is %NULL,
9050  * then it will unset the old model.
9051  **/
9052 void
9053 pspp_sheet_view_set_model (PsppSheetView  *tree_view,
9054                          GtkTreeModel *model)
9055 {
9056   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9057   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
9058
9059   if (model == tree_view->priv->model)
9060     return;
9061
9062   if (tree_view->priv->scroll_to_path)
9063     {
9064       gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9065       tree_view->priv->scroll_to_path = NULL;
9066     }
9067
9068   if (tree_view->priv->model)
9069     {
9070       GList *tmplist = tree_view->priv->columns;
9071
9072       if (tree_view->priv->selected)
9073         range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
9074       pspp_sheet_view_stop_editing (tree_view, TRUE);
9075
9076       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9077                                             pspp_sheet_view_row_changed,
9078                                             tree_view);
9079       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9080                                             pspp_sheet_view_row_inserted,
9081                                             tree_view);
9082       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9083                                             pspp_sheet_view_row_deleted,
9084                                             tree_view);
9085       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9086                                             pspp_sheet_view_rows_reordered,
9087                                             tree_view);
9088
9089       for (; tmplist; tmplist = tmplist->next)
9090         _pspp_sheet_view_column_unset_model (tmplist->data,
9091                                            tree_view->priv->model);
9092
9093       tree_view->priv->prelight_node = -1;
9094
9095       gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
9096       tree_view->priv->drag_dest_row = NULL;
9097       gtk_tree_row_reference_free (tree_view->priv->cursor);
9098       tree_view->priv->cursor = NULL;
9099       gtk_tree_row_reference_free (tree_view->priv->anchor);
9100       tree_view->priv->anchor = NULL;
9101       gtk_tree_row_reference_free (tree_view->priv->top_row);
9102       tree_view->priv->top_row = NULL;
9103       gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9104       tree_view->priv->scroll_to_path = NULL;
9105
9106       tree_view->priv->scroll_to_column = NULL;
9107
9108       g_object_unref (tree_view->priv->model);
9109
9110       tree_view->priv->search_column = -1;
9111       tree_view->priv->fixed_height = -1;
9112       tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
9113       tree_view->priv->last_button_x = -1;
9114       tree_view->priv->last_button_y = -1;
9115     }
9116
9117   tree_view->priv->model = model;
9118
9119   if (tree_view->priv->model)
9120     {
9121       gint i;
9122
9123       if (tree_view->priv->search_column == -1)
9124         {
9125           for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
9126             {
9127               GType type = gtk_tree_model_get_column_type (model, i);
9128
9129               if (g_value_type_transformable (type, G_TYPE_STRING))
9130                 {
9131                   tree_view->priv->search_column = i;
9132                   break;
9133                 }
9134             }
9135         }
9136
9137       g_object_ref (tree_view->priv->model);
9138       g_signal_connect (tree_view->priv->model,
9139                         "row-changed",
9140                         G_CALLBACK (pspp_sheet_view_row_changed),
9141                         tree_view);
9142       g_signal_connect (tree_view->priv->model,
9143                         "row-inserted",
9144                         G_CALLBACK (pspp_sheet_view_row_inserted),
9145                         tree_view);
9146       g_signal_connect (tree_view->priv->model,
9147                         "row-deleted",
9148                         G_CALLBACK (pspp_sheet_view_row_deleted),
9149                         tree_view);
9150       g_signal_connect (tree_view->priv->model,
9151                         "rows-reordered",
9152                         G_CALLBACK (pspp_sheet_view_rows_reordered),
9153                         tree_view);
9154
9155       tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
9156
9157       /*  FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
9158       install_presize_handler (tree_view);
9159     }
9160
9161   g_object_notify (G_OBJECT (tree_view), "model");
9162
9163   if (tree_view->priv->selection)
9164     _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
9165
9166   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9167     gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9168 }
9169
9170 /**
9171  * pspp_sheet_view_get_selection:
9172  * @tree_view: A #PsppSheetView.
9173  *
9174  * Gets the #PsppSheetSelection associated with @tree_view.
9175  *
9176  * Return value: A #PsppSheetSelection object.
9177  **/
9178 PsppSheetSelection *
9179 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
9180 {
9181   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9182
9183   return tree_view->priv->selection;
9184 }
9185
9186 /**
9187  * pspp_sheet_view_get_hadjustment:
9188  * @tree_view: A #PsppSheetView
9189  *
9190  * Gets the #GtkAdjustment currently being used for the horizontal aspect.
9191  *
9192  * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9193  * used.
9194  **/
9195 GtkAdjustment *
9196 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
9197 {
9198   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9199
9200   return pspp_sheet_view_do_get_hadjustment (tree_view);
9201 }
9202
9203 static GtkAdjustment *
9204 pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view)
9205 {
9206   return tree_view->priv->hadjustment;
9207 }
9208
9209 /**
9210  * pspp_sheet_view_set_hadjustment:
9211  * @tree_view: A #PsppSheetView
9212  * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9213  *
9214  * Sets the #GtkAdjustment for the current horizontal aspect.
9215  **/
9216 void
9217 pspp_sheet_view_set_hadjustment (PsppSheetView   *tree_view,
9218                                GtkAdjustment *adjustment)
9219 {
9220   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9221
9222   pspp_sheet_view_set_adjustments (tree_view,
9223                                  adjustment,
9224                                  tree_view->priv->vadjustment);
9225
9226   g_object_notify (G_OBJECT (tree_view), "hadjustment");
9227 }
9228
9229 static void
9230 pspp_sheet_view_do_set_hadjustment (PsppSheetView   *tree_view,
9231                                   GtkAdjustment *adjustment)
9232 {
9233   PsppSheetViewPrivate *priv = tree_view->priv;
9234
9235   if (adjustment && priv->hadjustment == adjustment)
9236     return;
9237
9238   if (priv->hadjustment != NULL)
9239     {
9240       g_signal_handlers_disconnect_by_func (priv->hadjustment,
9241                                             pspp_sheet_view_adjustment_changed,
9242                                             tree_view);
9243       g_object_unref (priv->hadjustment);
9244     }
9245
9246   if (adjustment == NULL)
9247     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9248                                      0.0, 0.0, 0.0);
9249
9250   g_signal_connect (adjustment, "value-changed",
9251                     G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9252   priv->hadjustment = g_object_ref_sink (adjustment);
9253   /* FIXME: Adjustment should probably be populated here with fresh values, but
9254    * internal details are too complicated for me to decipher right now.
9255    */
9256   pspp_sheet_view_adjustment_changed (NULL, tree_view);
9257
9258   g_object_notify (G_OBJECT (tree_view), "hadjustment");
9259 }
9260
9261 /**
9262  * pspp_sheet_view_get_vadjustment:
9263  * @tree_view: A #PsppSheetView
9264  *
9265  * Gets the #GtkAdjustment currently being used for the vertical aspect.
9266  *
9267  * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9268  *     if none is currently being used.
9269  *
9270  * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9271  **/
9272 GtkAdjustment *
9273 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9274 {
9275   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9276
9277   return pspp_sheet_view_do_get_vadjustment (tree_view);
9278 }
9279
9280 static GtkAdjustment *
9281 pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view)
9282 {
9283   return tree_view->priv->vadjustment;
9284 }
9285
9286 /**
9287  * pspp_sheet_view_set_vadjustment:
9288  * @tree_view: A #PsppSheetView
9289  * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9290  *
9291  * Sets the #GtkAdjustment for the current vertical aspect.
9292  *
9293  * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9294  **/
9295 void
9296 pspp_sheet_view_set_vadjustment (PsppSheetView   *tree_view,
9297                                GtkAdjustment *adjustment)
9298 {
9299   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9300   g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
9301
9302   pspp_sheet_view_do_set_vadjustment (tree_view, adjustment);
9303 }
9304
9305 static void
9306 pspp_sheet_view_do_set_vadjustment (PsppSheetView   *tree_view,
9307                                   GtkAdjustment *adjustment)
9308 {
9309   PsppSheetViewPrivate *priv = tree_view->priv;
9310
9311   if (adjustment && priv->vadjustment == adjustment)
9312     return;
9313
9314   if (priv->vadjustment != NULL)
9315     {
9316       g_signal_handlers_disconnect_by_func (priv->vadjustment,
9317                                             pspp_sheet_view_adjustment_changed,
9318                                             tree_view);
9319       g_object_unref (priv->vadjustment);
9320     }
9321
9322   if (adjustment == NULL)
9323     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9324                                      0.0, 0.0, 0.0);
9325
9326   g_signal_connect (adjustment, "value-changed",
9327                     G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9328   priv->vadjustment = g_object_ref_sink (adjustment);
9329   /* FIXME: Adjustment should probably be populated here with fresh values, but
9330    * internal details are too complicated for me to decipher right now.
9331    */
9332   pspp_sheet_view_adjustment_changed (NULL, tree_view);
9333   g_object_notify (G_OBJECT (tree_view), "vadjustment");
9334 }
9335
9336 /* Column and header operations */
9337
9338 /**
9339  * pspp_sheet_view_get_headers_visible:
9340  * @tree_view: A #PsppSheetView.
9341  *
9342  * Returns %TRUE if the headers on the @tree_view are visible.
9343  *
9344  * Return value: Whether the headers are visible or not.
9345  **/
9346 gboolean
9347 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9348 {
9349   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9350
9351   return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9352 }
9353
9354 /**
9355  * pspp_sheet_view_set_headers_visible:
9356  * @tree_view: A #PsppSheetView.
9357  * @headers_visible: %TRUE if the headers are visible
9358  *
9359  * Sets the visibility state of the headers.
9360  **/
9361 void
9362 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9363                                    gboolean     headers_visible)
9364 {
9365   gint x, y;
9366   GList *list;
9367   PsppSheetViewColumn *column;
9368   GtkAllocation allocation;
9369
9370   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9371
9372   gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9373
9374   headers_visible = !! headers_visible;
9375
9376   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9377     return;
9378
9379   if (headers_visible)
9380     PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9381   else
9382     PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9383
9384   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9385     {
9386       gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9387       if (headers_visible)
9388         {
9389           gdk_window_move_resize (tree_view->priv->bin_window, x, y  + TREE_VIEW_HEADER_HEIGHT (tree_view), 
9390                                   tree_view->priv->width, allocation.height -  + TREE_VIEW_HEADER_HEIGHT (tree_view));
9391
9392           if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9393             pspp_sheet_view_map_buttons (tree_view);
9394         }
9395       else
9396         {
9397           gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9398
9399           for (list = tree_view->priv->columns; list; list = list->next)
9400             {
9401               column = list->data;
9402               if (column->button)
9403                 gtk_widget_unmap (column->button);
9404             }
9405           gdk_window_hide (tree_view->priv->header_window);
9406         }
9407     }
9408
9409   gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view));
9410   gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2);
9411   gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
9412   gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height);
9413   gtk_adjustment_changed (tree_view->priv->vadjustment);
9414
9415   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9416
9417   g_object_notify (G_OBJECT (tree_view), "headers-visible");
9418 }
9419
9420 /**
9421  * pspp_sheet_view_columns_autosize:
9422  * @tree_view: A #PsppSheetView.
9423  *
9424  * Resizes all columns to their optimal width. Only works after the
9425  * treeview has been realized.
9426  **/
9427 void
9428 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9429 {
9430   gboolean dirty = FALSE;
9431   GList *list;
9432   PsppSheetViewColumn *column;
9433
9434   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9435
9436   for (list = tree_view->priv->columns; list; list = list->next)
9437     {
9438       column = list->data;
9439       _pspp_sheet_view_column_cell_set_dirty (column);
9440       dirty = TRUE;
9441     }
9442
9443   if (dirty)
9444     gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9445 }
9446
9447 /**
9448  * pspp_sheet_view_set_headers_clickable:
9449  * @tree_view: A #PsppSheetView.
9450  * @setting: %TRUE if the columns are clickable.
9451  *
9452  * Allow the column title buttons to be clicked.
9453  **/
9454 void
9455 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9456                                      gboolean   setting)
9457 {
9458   GList *list;
9459
9460   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9461
9462   for (list = tree_view->priv->columns; list; list = list->next)
9463     pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9464
9465   g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9466 }
9467
9468
9469 /**
9470  * pspp_sheet_view_get_headers_clickable:
9471  * @tree_view: A #PsppSheetView.
9472  *
9473  * Returns whether all header columns are clickable.
9474  *
9475  * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9476  *
9477  * Since: 2.10
9478  **/
9479 gboolean 
9480 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9481 {
9482   GList *list;
9483   
9484   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9485
9486   for (list = tree_view->priv->columns; list; list = list->next)
9487     if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9488       return FALSE;
9489
9490   return TRUE;
9491 }
9492
9493 /**
9494  * pspp_sheet_view_set_rules_hint
9495  * @tree_view: a #PsppSheetView
9496  * @setting: %TRUE if the tree requires reading across rows
9497  *
9498  * This function tells GTK+ that the user interface for your
9499  * application requires users to read across tree rows and associate
9500  * cells with one another. By default, GTK+ will then render the tree
9501  * with alternating row colors. Do <emphasis>not</emphasis> use it
9502  * just because you prefer the appearance of the ruled tree; that's a
9503  * question for the theme. Some themes will draw tree rows in
9504  * alternating colors even when rules are turned off, and users who
9505  * prefer that appearance all the time can choose those themes. You
9506  * should call this function only as a <emphasis>semantic</emphasis>
9507  * hint to the theme engine that your tree makes alternating colors
9508  * useful from a functional standpoint (since it has lots of columns,
9509  * generally).
9510  *
9511  **/
9512 void
9513 pspp_sheet_view_set_rules_hint (PsppSheetView  *tree_view,
9514                               gboolean      setting)
9515 {
9516   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9517
9518   setting = setting != FALSE;
9519
9520   if (tree_view->priv->has_rules != setting)
9521     {
9522       tree_view->priv->has_rules = setting;
9523       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9524     }
9525
9526   g_object_notify (G_OBJECT (tree_view), "rules-hint");
9527 }
9528
9529 /**
9530  * pspp_sheet_view_get_rules_hint
9531  * @tree_view: a #PsppSheetView
9532  *
9533  * Gets the setting set by pspp_sheet_view_set_rules_hint().
9534  *
9535  * Return value: %TRUE if rules are useful for the user of this tree
9536  **/
9537 gboolean
9538 pspp_sheet_view_get_rules_hint (PsppSheetView  *tree_view)
9539 {
9540   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9541
9542   return tree_view->priv->has_rules;
9543 }
9544
9545 /* Public Column functions
9546  */
9547
9548 /**
9549  * pspp_sheet_view_append_column:
9550  * @tree_view: A #PsppSheetView.
9551  * @column: The #PsppSheetViewColumn to add.
9552  *
9553  * Appends @column to the list of columns.
9554  *
9555  * Return value: The number of columns in @tree_view after appending.
9556  **/
9557 gint
9558 pspp_sheet_view_append_column (PsppSheetView       *tree_view,
9559                              PsppSheetViewColumn *column)
9560 {
9561   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9562   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9563   g_return_val_if_fail (column->tree_view == NULL, -1);
9564
9565   return pspp_sheet_view_insert_column (tree_view, column, -1);
9566 }
9567
9568
9569 /**
9570  * pspp_sheet_view_remove_column:
9571  * @tree_view: A #PsppSheetView.
9572  * @column: The #PsppSheetViewColumn to remove.
9573  *
9574  * Removes @column from @tree_view.
9575  *
9576  * Return value: The number of columns in @tree_view after removing.
9577  **/
9578 gint
9579 pspp_sheet_view_remove_column (PsppSheetView       *tree_view,
9580                              PsppSheetViewColumn *column)
9581 {
9582   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9583   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9584   g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9585
9586   if (tree_view->priv->focus_column == column)
9587     tree_view->priv->focus_column = NULL;
9588
9589   if (tree_view->priv->edited_column == column)
9590     {
9591       pspp_sheet_view_stop_editing (tree_view, TRUE);
9592
9593       /* no need to, but just to be sure ... */
9594       tree_view->priv->edited_column = NULL;
9595     }
9596
9597   _pspp_sheet_view_column_unset_tree_view (column);
9598
9599   tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9600   tree_view->priv->n_columns--;
9601
9602   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9603     {
9604       GList *list;
9605
9606       _pspp_sheet_view_column_unrealize_button (column);
9607       for (list = tree_view->priv->columns; list; list = list->next)
9608         {
9609           PsppSheetViewColumn *tmp_column;
9610
9611           tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9612           if (tmp_column->visible)
9613             _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9614         }
9615
9616       if (tree_view->priv->n_columns == 0 &&
9617           pspp_sheet_view_get_headers_visible (tree_view) && 
9618           tree_view->priv->header_window)
9619         gdk_window_hide (tree_view->priv->header_window);
9620
9621       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9622     }
9623
9624   g_object_unref (column);
9625   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9626
9627   return tree_view->priv->n_columns;
9628 }
9629
9630 /**
9631  * pspp_sheet_view_insert_column:
9632  * @tree_view: A #PsppSheetView.
9633  * @column: The #PsppSheetViewColumn to be inserted.
9634  * @position: The position to insert @column in.
9635  *
9636  * This inserts the @column into the @tree_view at @position.  If @position is
9637  * -1, then the column is inserted at the end.
9638  *
9639  * Return value: The number of columns in @tree_view after insertion.
9640  **/
9641 gint
9642 pspp_sheet_view_insert_column (PsppSheetView       *tree_view,
9643                              PsppSheetViewColumn *column,
9644                              gint               position)
9645 {
9646   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9647   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9648   g_return_val_if_fail (column->tree_view == NULL, -1);
9649
9650   g_object_ref_sink (column);
9651
9652   if (tree_view->priv->n_columns == 0 &&
9653       gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9654       pspp_sheet_view_get_headers_visible (tree_view))
9655     {
9656       gdk_window_show (tree_view->priv->header_window);
9657     }
9658
9659   tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9660                                             column, position);
9661   tree_view->priv->n_columns++;
9662
9663   _pspp_sheet_view_column_set_tree_view (column, tree_view);
9664
9665   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9666     {
9667       GList *list;
9668
9669       _pspp_sheet_view_column_realize_button (column);
9670
9671       for (list = tree_view->priv->columns; list; list = list->next)
9672         {
9673           column = PSPP_SHEET_VIEW_COLUMN (list->data);
9674           if (column->visible)
9675             _pspp_sheet_view_column_cell_set_dirty (column);
9676         }
9677       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9678     }
9679
9680   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9681
9682   return tree_view->priv->n_columns;
9683 }
9684
9685 /**
9686  * pspp_sheet_view_insert_column_with_attributes:
9687  * @tree_view: A #PsppSheetView
9688  * @position: The position to insert the new column in.
9689  * @title: The title to set the header to.
9690  * @cell: The #GtkCellRenderer.
9691  * @Varargs: A %NULL-terminated list of attributes.
9692  *
9693  * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9694  * @position.  If @position is -1, then the newly created column is inserted at
9695  * the end.  The column is initialized with the attributes given.
9696  *
9697  * Return value: The number of columns in @tree_view after insertion.
9698  **/
9699 gint
9700 pspp_sheet_view_insert_column_with_attributes (PsppSheetView     *tree_view,
9701                                              gint             position,
9702                                              const gchar     *title,
9703                                              GtkCellRenderer *cell,
9704                                              ...)
9705 {
9706   PsppSheetViewColumn *column;
9707   gchar *attribute;
9708   va_list args;
9709   gint column_id;
9710
9711   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9712
9713   column = pspp_sheet_view_column_new ();
9714   pspp_sheet_view_column_set_title (column, title);
9715   pspp_sheet_view_column_pack_start (column, cell, TRUE);
9716
9717   va_start (args, cell);
9718
9719   attribute = va_arg (args, gchar *);
9720
9721   while (attribute != NULL)
9722     {
9723       column_id = va_arg (args, gint);
9724       pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9725       attribute = va_arg (args, gchar *);
9726     }
9727
9728   va_end (args);
9729
9730   pspp_sheet_view_insert_column (tree_view, column, position);
9731
9732   return tree_view->priv->n_columns;
9733 }
9734
9735 /**
9736  * pspp_sheet_view_insert_column_with_data_func:
9737  * @tree_view: a #PsppSheetView
9738  * @position: Position to insert, -1 for append
9739  * @title: column title
9740  * @cell: cell renderer for column
9741  * @func: function to set attributes of cell renderer
9742  * @data: data for @func
9743  * @dnotify: destroy notifier for @data
9744  *
9745  * Convenience function that inserts a new column into the #PsppSheetView
9746  * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9747  * attributes (normally using data from the model). See also
9748  * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9749  *
9750  * Return value: number of columns in the tree view post-insert
9751  **/
9752 gint
9753 pspp_sheet_view_insert_column_with_data_func  (PsppSheetView               *tree_view,
9754                                              gint                       position,
9755                                              const gchar               *title,
9756                                              GtkCellRenderer           *cell,
9757                                              PsppSheetCellDataFunc        func,
9758                                              gpointer                   data,
9759                                              GDestroyNotify             dnotify)
9760 {
9761   PsppSheetViewColumn *column;
9762
9763   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9764
9765   column = pspp_sheet_view_column_new ();
9766   pspp_sheet_view_column_set_title (column, title);
9767   pspp_sheet_view_column_pack_start (column, cell, TRUE);
9768   pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9769
9770   pspp_sheet_view_insert_column (tree_view, column, position);
9771
9772   return tree_view->priv->n_columns;
9773 }
9774
9775 /**
9776  * pspp_sheet_view_get_column:
9777  * @tree_view: A #PsppSheetView.
9778  * @n: The position of the column, counting from 0.
9779  *
9780  * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9781  *
9782  * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9783  * range of columns.
9784  **/
9785 PsppSheetViewColumn *
9786 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9787                           gint         n)
9788 {
9789   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9790
9791   if (n < 0 || n >= tree_view->priv->n_columns)
9792     return NULL;
9793
9794   if (tree_view->priv->columns == NULL)
9795     return NULL;
9796
9797   return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9798 }
9799
9800 /**
9801  * pspp_sheet_view_get_columns:
9802  * @tree_view: A #PsppSheetView
9803  *
9804  * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9805  * The returned list must be freed with g_list_free ().
9806  *
9807  * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9808  **/
9809 GList *
9810 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9811 {
9812   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9813
9814   return g_list_copy (tree_view->priv->columns);
9815 }
9816
9817 /**
9818  * pspp_sheet_view_move_column_after:
9819  * @tree_view: A #PsppSheetView
9820  * @column: The #PsppSheetViewColumn to be moved.
9821  * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9822  *
9823  * Moves @column to be after to @base_column.  If @base_column is %NULL, then
9824  * @column is placed in the first position.
9825  **/
9826 void
9827 pspp_sheet_view_move_column_after (PsppSheetView       *tree_view,
9828                                  PsppSheetViewColumn *column,
9829                                  PsppSheetViewColumn *base_column)
9830 {
9831   GList *column_list_el, *base_el = NULL;
9832
9833   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9834
9835   column_list_el = g_list_find (tree_view->priv->columns, column);
9836   g_return_if_fail (column_list_el != NULL);
9837
9838   if (base_column)
9839     {
9840       base_el = g_list_find (tree_view->priv->columns, base_column);
9841       g_return_if_fail (base_el != NULL);
9842     }
9843
9844   if (column_list_el->prev == base_el)
9845     return;
9846
9847   tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9848   if (base_el == NULL)
9849     {
9850       column_list_el->prev = NULL;
9851       column_list_el->next = tree_view->priv->columns;
9852       if (column_list_el->next)
9853         column_list_el->next->prev = column_list_el;
9854       tree_view->priv->columns = column_list_el;
9855     }
9856   else
9857     {
9858       column_list_el->prev = base_el;
9859       column_list_el->next = base_el->next;
9860       if (column_list_el->next)
9861         column_list_el->next->prev = column_list_el;
9862       base_el->next = column_list_el;
9863     }
9864
9865   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9866     {
9867       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9868       pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9869     }
9870
9871   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9872 }
9873
9874 /**
9875  * pspp_sheet_view_set_column_drag_function:
9876  * @tree_view: A #PsppSheetView.
9877  * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9878  * @user_data: (allow-none): User data to be passed to @func, or %NULL
9879  * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9880  *
9881  * Sets a user function for determining where a column may be dropped when
9882  * dragged.  This function is called on every column pair in turn at the
9883  * beginning of a column drag to determine where a drop can take place.  The
9884  * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9885  * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9886  * @user_data.  If either of the #PsppSheetViewColumn arguments for the drop spot
9887  * are %NULL, then they indicate an edge.  If @func is set to be %NULL, then
9888  * @tree_view reverts to the default behavior of allowing all columns to be
9889  * dropped everywhere.
9890  **/
9891 void
9892 pspp_sheet_view_set_column_drag_function (PsppSheetView               *tree_view,
9893                                         PsppSheetViewColumnDropFunc  func,
9894                                         gpointer                   user_data,
9895                                         GDestroyNotify             destroy)
9896 {
9897   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9898
9899   if (tree_view->priv->column_drop_func_data_destroy)
9900     tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9901
9902   tree_view->priv->column_drop_func = func;
9903   tree_view->priv->column_drop_func_data = user_data;
9904   tree_view->priv->column_drop_func_data_destroy = destroy;
9905 }
9906
9907 /**
9908  * pspp_sheet_view_scroll_to_point:
9909  * @tree_view: a #PsppSheetView
9910  * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9911  * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9912  *
9913  * Scrolls the tree view such that the top-left corner of the visible
9914  * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9915  * in tree coordinates.  The @tree_view must be realized before
9916  * this function is called.  If it isn't, you probably want to be
9917  * using pspp_sheet_view_scroll_to_cell().
9918  *
9919  * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9920  **/
9921 void
9922 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9923                                gint         tree_x,
9924                                gint         tree_y)
9925 {
9926   GtkAdjustment *hadj;
9927   GtkAdjustment *vadj;
9928
9929   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9930   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9931
9932   hadj = tree_view->priv->hadjustment;
9933   vadj = tree_view->priv->vadjustment;
9934
9935   if (tree_x != -1)
9936     gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj)));
9937   if (tree_y != -1)
9938     gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj)));
9939 }
9940
9941 /**
9942  * pspp_sheet_view_scroll_to_cell:
9943  * @tree_view: A #PsppSheetView.
9944  * @path: (allow-none): The path of the row to move to, or %NULL.
9945  * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9946  * @use_align: whether to use alignment arguments, or %FALSE.
9947  * @row_align: The vertical alignment of the row specified by @path.
9948  * @col_align: The horizontal alignment of the column specified by @column.
9949  *
9950  * Moves the alignments of @tree_view to the position specified by @column and
9951  * @path.  If @column is %NULL, then no horizontal scrolling occurs.  Likewise,
9952  * if @path is %NULL no vertical scrolling occurs.  At a minimum, one of @column
9953  * or @path need to be non-%NULL.  @row_align determines where the row is
9954  * placed, and @col_align determines where @column is placed.  Both are expected
9955  * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9956  * right/bottom alignment, 0.5 means center.
9957  *
9958  * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9959  * tree does the minimum amount of work to scroll the cell onto the screen.
9960  * This means that the cell will be scrolled to the edge closest to its current
9961  * position.  If the cell is currently visible on the screen, nothing is done.
9962  *
9963  * This function only works if the model is set, and @path is a valid row on the
9964  * model.  If the model changes before the @tree_view is realized, the centered
9965  * path will be modified to reflect this change.
9966  **/
9967 void
9968 pspp_sheet_view_scroll_to_cell (PsppSheetView       *tree_view,
9969                               GtkTreePath       *path,
9970                               PsppSheetViewColumn *column,
9971                               gboolean           use_align,
9972                               gfloat             row_align,
9973                               gfloat             col_align)
9974 {
9975   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9976   g_return_if_fail (tree_view->priv->model != NULL);
9977   g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9978   g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9979   g_return_if_fail (path != NULL || column != NULL);
9980
9981 #if 0
9982   g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9983            gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9984 #endif
9985   row_align = CLAMP (row_align, 0.0, 1.0);
9986   col_align = CLAMP (col_align, 0.0, 1.0);
9987
9988
9989   /* Note: Despite the benefits that come from having one code path for the
9990    * scrolling code, we short-circuit validate_visible_area's immplementation as
9991    * it is much slower than just going to the point.
9992    */
9993   if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9994       !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9995       /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9996     {
9997       if (tree_view->priv->scroll_to_path)
9998         gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9999
10000       tree_view->priv->scroll_to_path = NULL;
10001       tree_view->priv->scroll_to_column = NULL;
10002
10003       if (path)
10004         tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
10005       if (column)
10006         tree_view->priv->scroll_to_column = column;
10007       tree_view->priv->scroll_to_use_align = use_align;
10008       tree_view->priv->scroll_to_row_align = row_align;
10009       tree_view->priv->scroll_to_col_align = col_align;
10010
10011       install_presize_handler (tree_view);
10012     }
10013   else
10014     {
10015       GdkRectangle cell_rect;
10016       GdkRectangle vis_rect;
10017       gint dest_x, dest_y;
10018
10019       pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
10020       pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
10021
10022       cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
10023
10024       dest_x = vis_rect.x;
10025       dest_y = vis_rect.y;
10026
10027       if (column)
10028         {
10029           if (use_align)
10030             {
10031               dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
10032             }
10033           else
10034             {
10035               if (cell_rect.x < vis_rect.x)
10036                 dest_x = cell_rect.x;
10037               if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
10038                 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
10039             }
10040         }
10041
10042       if (path)
10043         {
10044           if (use_align)
10045             {
10046               dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
10047               dest_y = MAX (dest_y, 0);
10048             }
10049           else
10050             {
10051               if (cell_rect.y < vis_rect.y)
10052                 dest_y = cell_rect.y;
10053               if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
10054                 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
10055             }
10056         }
10057
10058       pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
10059     }
10060 }
10061
10062 /**
10063  * pspp_sheet_view_row_activated:
10064  * @tree_view: A #PsppSheetView
10065  * @path: The #GtkTreePath to be activated.
10066  * @column: The #PsppSheetViewColumn to be activated.
10067  *
10068  * Activates the cell determined by @path and @column.
10069  **/
10070 void
10071 pspp_sheet_view_row_activated (PsppSheetView       *tree_view,
10072                              GtkTreePath       *path,
10073                              PsppSheetViewColumn *column)
10074 {
10075   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10076
10077   g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
10078 }
10079
10080
10081 /**
10082  * pspp_sheet_view_get_reorderable:
10083  * @tree_view: a #PsppSheetView
10084  *
10085  * Retrieves whether the user can reorder the tree via drag-and-drop. See
10086  * pspp_sheet_view_set_reorderable().
10087  *
10088  * Return value: %TRUE if the tree can be reordered.
10089  **/
10090 gboolean
10091 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
10092 {
10093   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10094
10095   return tree_view->priv->reorderable;
10096 }
10097
10098 /**
10099  * pspp_sheet_view_set_reorderable:
10100  * @tree_view: A #PsppSheetView.
10101  * @reorderable: %TRUE, if the tree can be reordered.
10102  *
10103  * This function is a convenience function to allow you to reorder
10104  * models that support the #GtkDragSourceIface and the
10105  * #GtkDragDestIface.  Both #GtkTreeStore and #GtkListStore support
10106  * these.  If @reorderable is %TRUE, then the user can reorder the
10107  * model by dragging and dropping rows. The developer can listen to
10108  * these changes by connecting to the model's row_inserted and
10109  * row_deleted signals. The reordering is implemented by setting up
10110  * the tree view as a drag source and destination. Therefore, drag and
10111  * drop can not be used in a reorderable view for any other purpose.
10112  *
10113  * This function does not give you any degree of control over the order -- any
10114  * reordering is allowed.  If more control is needed, you should probably
10115  * handle drag and drop manually.
10116  **/
10117 void
10118 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
10119                                gboolean     reorderable)
10120 {
10121   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10122
10123   reorderable = reorderable != FALSE;
10124
10125   if (tree_view->priv->reorderable == reorderable)
10126     return;
10127
10128   if (reorderable)
10129     {
10130       const GtkTargetEntry row_targets[] = {
10131         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
10132       };
10133
10134       pspp_sheet_view_enable_model_drag_source (tree_view,
10135                                               GDK_BUTTON1_MASK,
10136                                               row_targets,
10137                                               G_N_ELEMENTS (row_targets),
10138                                               GDK_ACTION_MOVE);
10139       pspp_sheet_view_enable_model_drag_dest (tree_view,
10140                                             row_targets,
10141                                             G_N_ELEMENTS (row_targets),
10142                                             GDK_ACTION_MOVE);
10143     }
10144   else
10145     {
10146       pspp_sheet_view_unset_rows_drag_source (tree_view);
10147       pspp_sheet_view_unset_rows_drag_dest (tree_view);
10148     }
10149
10150   tree_view->priv->reorderable = reorderable;
10151
10152   g_object_notify (G_OBJECT (tree_view), "reorderable");
10153 }
10154
10155 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
10156    is pressed, other rows will be unselected.
10157
10158    If CLAMP_NODE is true, then the sheetview will scroll to make the row
10159    visible. */
10160 static void
10161 pspp_sheet_view_real_set_cursor (PsppSheetView     *tree_view,
10162                                GtkTreePath     *path,
10163                                gboolean         clear_and_select,
10164                                gboolean         clamp_node,
10165                                PsppSheetSelectMode mode)
10166 {
10167   int node = -1;
10168
10169   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10170     {
10171       GtkTreePath *cursor_path;
10172       cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10173       pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
10174       gtk_tree_path_free (cursor_path);
10175     }
10176
10177   gtk_tree_row_reference_free (tree_view->priv->cursor);
10178   tree_view->priv->cursor = NULL;
10179
10180   _pspp_sheet_view_find_node (tree_view, path, &node);
10181   tree_view->priv->cursor =
10182     gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
10183                                       tree_view->priv->model,
10184                                       path);
10185
10186   if (tree_view->priv->row_count > 0)
10187     {
10188       int new_node = -1;
10189
10190       if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
10191         _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
10192                                                     node, path, mode,
10193                                                     FALSE);
10194
10195       /* We have to re-find tree and node here again, somebody might have
10196        * cleared the node or the whole tree in the PsppSheetSelection::changed
10197        * callback. If the nodes differ we bail out here.
10198        */
10199       _pspp_sheet_view_find_node (tree_view, path, &new_node);
10200
10201       if (node != new_node)
10202         return;
10203
10204       if (clamp_node)
10205         {
10206           pspp_sheet_view_clamp_node_visible (tree_view, node);
10207           _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
10208         }
10209     }
10210
10211   g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
10212 }
10213
10214 /**
10215  * pspp_sheet_view_get_cursor:
10216  * @tree_view: A #PsppSheetView
10217  * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
10218  * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
10219  *
10220  * Fills in @path and @focus_column with the current path and focus column.  If
10221  * the cursor isn't currently set, then *@path will be %NULL.  If no column
10222  * currently has focus, then *@focus_column will be %NULL.
10223  *
10224  * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
10225  * you are done with it.
10226  **/
10227 void
10228 pspp_sheet_view_get_cursor (PsppSheetView        *tree_view,
10229                           GtkTreePath       **path,
10230                           PsppSheetViewColumn **focus_column)
10231 {
10232   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10233
10234   if (path)
10235     {
10236       if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10237         *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10238       else
10239         *path = NULL;
10240     }
10241
10242   if (focus_column)
10243     {
10244       *focus_column = tree_view->priv->focus_column;
10245     }
10246 }
10247
10248 /**
10249  * pspp_sheet_view_set_cursor:
10250  * @tree_view: A #PsppSheetView
10251  * @path: A #GtkTreePath
10252  * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10253  * @start_editing: %TRUE if the specified cell should start being edited.
10254  *
10255  * Sets the current keyboard focus to be at @path, and selects it.  This is
10256  * useful when you want to focus the user's attention on a particular row.  If
10257  * @focus_column is not %NULL, then focus is given to the column specified by 
10258  * it. Additionally, if @focus_column is specified, and @start_editing is 
10259  * %TRUE, then editing should be started in the specified cell.  
10260  * This function is often followed by @gtk_widget_grab_focus (@tree_view) 
10261  * in order to give keyboard focus to the widget.  Please note that editing 
10262  * can only happen when the widget is realized.
10263  *
10264  * If @path is invalid for @model, the current cursor (if any) will be unset
10265  * and the function will return without failing.
10266  **/
10267 void
10268 pspp_sheet_view_set_cursor (PsppSheetView       *tree_view,
10269                           GtkTreePath       *path,
10270                           PsppSheetViewColumn *focus_column,
10271                           gboolean           start_editing)
10272 {
10273   pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10274                                     NULL, start_editing);
10275 }
10276
10277 /**
10278  * pspp_sheet_view_set_cursor_on_cell:
10279  * @tree_view: A #PsppSheetView
10280  * @path: A #GtkTreePath
10281  * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10282  * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10283  * @start_editing: %TRUE if the specified cell should start being edited.
10284  *
10285  * Sets the current keyboard focus to be at @path, and selects it.  This is
10286  * useful when you want to focus the user's attention on a particular row.  If
10287  * @focus_column is not %NULL, then focus is given to the column specified by
10288  * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10289  * contains 2 or more editable or activatable cells, then focus is given to
10290  * the cell specified by @focus_cell. Additionally, if @focus_column is
10291  * specified, and @start_editing is %TRUE, then editing should be started in
10292  * the specified cell.  This function is often followed by
10293  * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10294  * widget.  Please note that editing can only happen when the widget is
10295  * realized.
10296  *
10297  * If @path is invalid for @model, the current cursor (if any) will be unset
10298  * and the function will return without failing.
10299  *
10300  * Since: 2.2
10301  **/
10302 void
10303 pspp_sheet_view_set_cursor_on_cell (PsppSheetView       *tree_view,
10304                                   GtkTreePath       *path,
10305                                   PsppSheetViewColumn *focus_column,
10306                                   GtkCellRenderer   *focus_cell,
10307                                   gboolean           start_editing)
10308 {
10309   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10310   g_return_if_fail (path != NULL);
10311   g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10312
10313   if (!tree_view->priv->model)
10314     return;
10315
10316   if (focus_cell)
10317     {
10318       g_return_if_fail (focus_column);
10319       g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10320     }
10321
10322   /* cancel the current editing, if it exists */
10323   if (tree_view->priv->edited_column &&
10324       tree_view->priv->edited_column->editable_widget)
10325     pspp_sheet_view_stop_editing (tree_view, TRUE);
10326
10327   pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10328
10329   if (focus_column && focus_column->visible)
10330     {
10331       GList *list;
10332       gboolean column_in_tree = FALSE;
10333
10334       for (list = tree_view->priv->columns; list; list = list->next)
10335         if (list->data == focus_column)
10336           {
10337             column_in_tree = TRUE;
10338             break;
10339           }
10340       g_return_if_fail (column_in_tree);
10341       tree_view->priv->focus_column = focus_column;
10342       if (focus_cell)
10343         pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10344       if (start_editing)
10345         pspp_sheet_view_start_editing (tree_view, path);
10346
10347       pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10348       pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10349
10350     }
10351 }
10352
10353 /**
10354  * pspp_sheet_view_get_bin_window:
10355  * @tree_view: A #PsppSheetView
10356  * 
10357  * Returns the window that @tree_view renders to.  This is used primarily to
10358  * compare to <literal>event->window</literal> to confirm that the event on
10359  * @tree_view is on the right window.
10360  * 
10361  * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10362  **/
10363 GdkWindow *
10364 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10365 {
10366   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10367
10368   return tree_view->priv->bin_window;
10369 }
10370
10371 /**
10372  * pspp_sheet_view_get_path_at_pos:
10373  * @tree_view: A #PsppSheetView.
10374  * @x: The x position to be identified (relative to bin_window).
10375  * @y: The y position to be identified (relative to bin_window).
10376  * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10377  * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10378  * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10379  * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10380  *
10381  * Finds the path at the point (@x, @y), relative to bin_window coordinates
10382  * (please see pspp_sheet_view_get_bin_window()).
10383  * That is, @x and @y are relative to an events coordinates. @x and @y must
10384  * come from an event on the @tree_view only where <literal>event->window ==
10385  * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10386  * things like popup menus. If @path is non-%NULL, then it will be filled
10387  * with the #GtkTreePath at that point.  This path should be freed with
10388  * gtk_tree_path_free().  If @column is non-%NULL, then it will be filled
10389  * with the column at that point.  @cell_x and @cell_y return the coordinates
10390  * relative to the cell background (i.e. the @background_area passed to
10391  * gtk_cell_renderer_render()).  This function is only meaningful if
10392  * @tree_view is realized.  Therefore this function will always return %FALSE
10393  * if @tree_view is not realized or does not have a model.
10394  *
10395  * For converting widget coordinates (eg. the ones you get from
10396  * GtkWidget::query-tooltip), please see
10397  * pspp_sheet_view_convert_widget_to_bin_window_coords().
10398  *
10399  * Return value: %TRUE if a row exists at that coordinate.
10400  **/
10401 gboolean
10402 pspp_sheet_view_get_path_at_pos (PsppSheetView        *tree_view,
10403                                gint                x,
10404                                gint                y,
10405                                GtkTreePath       **path,
10406                                PsppSheetViewColumn **column,
10407                                gint               *cell_x,
10408                                gint               *cell_y)
10409 {
10410   int node;
10411   gint y_offset;
10412
10413   g_return_val_if_fail (tree_view != NULL, FALSE);
10414
10415   if (path)
10416     *path = NULL;
10417   if (column)
10418     *column = NULL;
10419
10420   if (tree_view->priv->bin_window == NULL)
10421     return FALSE;
10422
10423   if (tree_view->priv->row_count == 0)
10424     return FALSE;
10425
10426   if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
10427     return FALSE;
10428
10429   if (x < 0 || y < 0)
10430     return FALSE;
10431
10432   if (column || cell_x)
10433     {
10434       PsppSheetViewColumn *tmp_column;
10435       PsppSheetViewColumn *last_column = NULL;
10436       GList *list;
10437       gint remaining_x = x;
10438       gboolean found = FALSE;
10439       gboolean rtl;
10440
10441       rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10442       for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10443            list;
10444            list = (rtl ? list->prev : list->next))
10445         {
10446           tmp_column = list->data;
10447
10448           if (tmp_column->visible == FALSE)
10449             continue;
10450
10451           last_column = tmp_column;
10452           if (remaining_x <= tmp_column->width)
10453             {
10454               found = TRUE;
10455
10456               if (column)
10457                 *column = tmp_column;
10458
10459               if (cell_x)
10460                 *cell_x = remaining_x;
10461
10462               break;
10463             }
10464           remaining_x -= tmp_column->width;
10465         }
10466
10467       /* If found is FALSE and there is a last_column, then it the remainder
10468        * space is in that area
10469        */
10470       if (!found)
10471         {
10472           if (last_column)
10473             {
10474               if (column)
10475                 *column = last_column;
10476               
10477               if (cell_x)
10478                 *cell_x = last_column->width + remaining_x;
10479             }
10480           else
10481             {
10482               return FALSE;
10483             }
10484         }
10485     }
10486
10487   y_offset = pspp_sheet_view_find_offset (tree_view,
10488                                           TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10489                                           &node);
10490
10491   if (node < 0)
10492     return FALSE;
10493
10494   if (cell_y)
10495     *cell_y = y_offset;
10496
10497   if (path)
10498     *path = _pspp_sheet_view_find_path (tree_view, node);
10499
10500   return TRUE;
10501 }
10502
10503 /* Computes 'cell_area' from 'background_area', which must be the background
10504    area for a cell.  Set 'subtract_focus_rect' to TRUE to compute the cell area
10505    as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10506    the cell area as passed to _pspp_sheet_view_column_cell_render().
10507
10508    'column' is required to properly adjust 'cell_area->x' and
10509    'cell_area->width'.  It may be set to NULL if these values are not of
10510    interest.  In this case 'cell_area->x' and 'cell_area->width' will be
10511    returned as 0. */
10512 static void
10513 pspp_sheet_view_adjust_cell_area (PsppSheetView        *tree_view,
10514                                   PsppSheetViewColumn  *column,
10515                                   const GdkRectangle   *background_area,
10516                                   gboolean              subtract_focus_rect,
10517                                   GdkRectangle         *cell_area)
10518 {
10519   gint vertical_separator;
10520   gint horizontal_separator;
10521
10522   *cell_area = *background_area;
10523
10524   gtk_widget_style_get (GTK_WIDGET (tree_view),
10525                         "vertical-separator", &vertical_separator,
10526                         "horizontal-separator", &horizontal_separator,
10527                         NULL);
10528   cell_area->x += horizontal_separator / 2;
10529   cell_area->y += vertical_separator / 2;
10530   cell_area->width -= horizontal_separator;
10531   cell_area->height -= vertical_separator;
10532
10533   if (subtract_focus_rect)
10534     {
10535       int focus_line_width;
10536
10537       gtk_widget_style_get (GTK_WIDGET (tree_view),
10538                             "focus-line-width", &focus_line_width,
10539                             NULL);
10540       cell_area->x += focus_line_width;
10541       cell_area->y += focus_line_width;
10542       cell_area->width -= 2 * focus_line_width;
10543       cell_area->height -= 2 * focus_line_width;
10544     }
10545
10546   if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10547     {
10548       gint grid_line_width;
10549       gtk_widget_style_get (GTK_WIDGET (tree_view),
10550                             "grid-line-width", &grid_line_width,
10551                             NULL);
10552
10553       if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10554            || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10555           && column != NULL)
10556         {
10557           PsppSheetViewColumn *first_column, *last_column;
10558           GList *list;
10559
10560           /* Find the last visible column. */
10561           last_column = NULL;
10562           for (list = g_list_last (tree_view->priv->columns);
10563                list;
10564                list = list->prev)
10565             {
10566               PsppSheetViewColumn *c = list->data;
10567               if (c->visible)
10568                 {
10569                   last_column = c;
10570                   break;
10571                 }
10572             }
10573
10574           /* Find the first visible column. */
10575           first_column = NULL;
10576           for (list = g_list_first (tree_view->priv->columns);
10577                list;
10578                list = list->next)
10579             {
10580               PsppSheetViewColumn *c = list->data;
10581               if (c->visible)
10582                 {
10583                   first_column = c;
10584                   break;
10585                 }
10586             }
10587
10588           if (column == first_column)
10589             {
10590               cell_area->width -= grid_line_width / 2;
10591             }
10592           else if (column == last_column)
10593             {
10594               cell_area->x += grid_line_width / 2;
10595               cell_area->width -= grid_line_width / 2;
10596             }
10597           else
10598             {
10599               cell_area->x += grid_line_width / 2;
10600               cell_area->width -= grid_line_width;
10601             }
10602         }
10603
10604       if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10605           || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10606         {
10607           cell_area->y += grid_line_width / 2;
10608           cell_area->height -= grid_line_width;
10609         }
10610     }
10611
10612   if (column == NULL)
10613     {
10614       cell_area->x = 0;
10615       cell_area->width = 0;
10616     }
10617 }
10618
10619 /**
10620  * pspp_sheet_view_get_cell_area:
10621  * @tree_view: a #PsppSheetView
10622  * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10623  * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10624  * @rect: rectangle to fill with cell rect
10625  *
10626  * Fills the bounding rectangle in bin_window coordinates for the cell at the
10627  * row specified by @path and the column specified by @column.  If @path is
10628  * %NULL, or points to a path not currently displayed, the @y and @height fields
10629  * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10630  * fields will be filled with 0.  The sum of all cell rects does not cover the
10631  * entire tree; there are extra pixels in between rows, for example. The
10632  * returned rectangle is equivalent to the @cell_area passed to
10633  * gtk_cell_renderer_render().  This function is only valid if @tree_view is
10634  * realized.
10635  **/
10636 void
10637 pspp_sheet_view_get_cell_area (PsppSheetView        *tree_view,
10638                              GtkTreePath        *path,
10639                              PsppSheetViewColumn  *column,
10640                              GdkRectangle       *rect)
10641 {
10642   GdkRectangle background_area;
10643
10644   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10645   g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10646   g_return_if_fail (rect != NULL);
10647   g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10648   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10649
10650   pspp_sheet_view_get_background_area (tree_view, path, column,
10651                                        &background_area);
10652   pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10653                                     FALSE, rect);
10654 }
10655
10656 /**
10657  * pspp_sheet_view_get_background_area:
10658  * @tree_view: a #PsppSheetView
10659  * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10660  * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10661  * @rect: rectangle to fill with cell background rect
10662  *
10663  * Fills the bounding rectangle in bin_window coordinates for the cell at the
10664  * row specified by @path and the column specified by @column.  If @path is
10665  * %NULL, or points to a node not found in the tree, the @y and @height fields of
10666  * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10667  * fields will be filled with 0.  The returned rectangle is equivalent to the
10668  * @background_area passed to gtk_cell_renderer_render().  These background
10669  * areas tile to cover the entire bin window.  Contrast with the @cell_area,
10670  * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10671  * itself, excluding surrounding borders.
10672  *
10673  **/
10674 void
10675 pspp_sheet_view_get_background_area (PsppSheetView        *tree_view,
10676                                    GtkTreePath        *path,
10677                                    PsppSheetViewColumn  *column,
10678                                    GdkRectangle       *rect)
10679 {
10680   int node = -1;
10681
10682   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10683   g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10684   g_return_if_fail (rect != NULL);
10685
10686   rect->x = 0;
10687   rect->y = 0;
10688   rect->width = 0;
10689   rect->height = 0;
10690
10691   if (path)
10692     {
10693       /* Get vertical coords */
10694
10695       _pspp_sheet_view_find_node (tree_view, path, &node);
10696       if (node < 0)
10697         return;
10698
10699       rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10700
10701       rect->height = ROW_HEIGHT (tree_view);
10702     }
10703
10704   if (column)
10705     {
10706       gint x2 = 0;
10707
10708       pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10709       rect->width = x2 - rect->x;
10710     }
10711 }
10712
10713 /**
10714  * pspp_sheet_view_get_visible_rect:
10715  * @tree_view: a #PsppSheetView
10716  * @visible_rect: rectangle to fill
10717  *
10718  * Fills @visible_rect with the currently-visible region of the
10719  * buffer, in tree coordinates. Convert to bin_window coordinates with
10720  * pspp_sheet_view_convert_tree_to_bin_window_coords().
10721  * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10722  * scrollable area of the tree.
10723  **/
10724 void
10725 pspp_sheet_view_get_visible_rect (PsppSheetView  *tree_view,
10726                                 GdkRectangle *visible_rect)
10727 {
10728   GtkWidget *widget;
10729
10730   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10731
10732   widget = GTK_WIDGET (tree_view);
10733
10734   if (visible_rect)
10735     {
10736       GtkAllocation allocation;
10737       gtk_widget_get_allocation (widget, &allocation);
10738       visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
10739       visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
10740       visible_rect->width  = allocation.width;
10741       visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10742     }
10743 }
10744
10745 /**
10746  * pspp_sheet_view_widget_to_tree_coords:
10747  * @tree_view: a #PsppSheetView
10748  * @wx: X coordinate relative to bin_window
10749  * @wy: Y coordinate relative to bin_window
10750  * @tx: return location for tree X coordinate
10751  * @ty: return location for tree Y coordinate
10752  *
10753  * Converts bin_window coordinates to coordinates for the
10754  * tree (the full scrollable area of the tree).
10755  *
10756  * Deprecated: 2.12: Due to historial reasons the name of this function is
10757  * incorrect.  For converting coordinates relative to the widget to
10758  * bin_window coordinates, please see
10759  * pspp_sheet_view_convert_widget_to_bin_window_coords().
10760  *
10761  **/
10762 void
10763 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10764                                       gint         wx,
10765                                       gint         wy,
10766                                       gint        *tx,
10767                                       gint        *ty)
10768 {
10769   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10770
10771   if (tx)
10772     *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10773   if (ty)
10774     *ty = wy + tree_view->priv->dy;
10775 }
10776
10777 /**
10778  * pspp_sheet_view_tree_to_widget_coords:
10779  * @tree_view: a #PsppSheetView
10780  * @tx: tree X coordinate
10781  * @ty: tree Y coordinate
10782  * @wx: return location for X coordinate relative to bin_window
10783  * @wy: return location for Y coordinate relative to bin_window
10784  *
10785  * Converts tree coordinates (coordinates in full scrollable area of the tree)
10786  * to bin_window coordinates.
10787  *
10788  * Deprecated: 2.12: Due to historial reasons the name of this function is
10789  * incorrect.  For converting bin_window coordinates to coordinates relative
10790  * to bin_window, please see
10791  * pspp_sheet_view_convert_bin_window_to_widget_coords().
10792  *
10793  **/
10794 void
10795 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10796                                      gint         tx,
10797                                      gint         ty,
10798                                      gint        *wx,
10799                                      gint        *wy)
10800 {
10801   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10802
10803   if (wx)
10804     *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10805   if (wy)
10806     *wy = ty - tree_view->priv->dy;
10807 }
10808
10809
10810 /**
10811  * pspp_sheet_view_convert_widget_to_tree_coords:
10812  * @tree_view: a #PsppSheetView
10813  * @wx: X coordinate relative to the widget
10814  * @wy: Y coordinate relative to the widget
10815  * @tx: return location for tree X coordinate
10816  * @ty: return location for tree Y coordinate
10817  *
10818  * Converts widget coordinates to coordinates for the
10819  * tree (the full scrollable area of the tree).
10820  *
10821  * Since: 2.12
10822  **/
10823 void
10824 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10825                                              gint         wx,
10826                                              gint         wy,
10827                                              gint        *tx,
10828                                              gint        *ty)
10829 {
10830   gint x, y;
10831
10832   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10833
10834   pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10835                                                      wx, wy,
10836                                                      &x, &y);
10837   pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10838                                                    x, y,
10839                                                    tx, ty);
10840 }
10841
10842 /**
10843  * pspp_sheet_view_convert_tree_to_widget_coords:
10844  * @tree_view: a #PsppSheetView
10845  * @tx: X coordinate relative to the tree
10846  * @ty: Y coordinate relative to the tree
10847  * @wx: return location for widget X coordinate
10848  * @wy: return location for widget Y coordinate
10849  *
10850  * Converts tree coordinates (coordinates in full scrollable area of the tree)
10851  * to widget coordinates.
10852  *
10853  * Since: 2.12
10854  **/
10855 void
10856 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10857                                              gint         tx,
10858                                              gint         ty,
10859                                              gint        *wx,
10860                                              gint        *wy)
10861 {
10862   gint x, y;
10863
10864   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10865
10866   pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10867                                                    tx, ty,
10868                                                    &x, &y);
10869   pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10870                                                      x, y,
10871                                                      wx, wy);
10872 }
10873
10874 /**
10875  * pspp_sheet_view_convert_widget_to_bin_window_coords:
10876  * @tree_view: a #PsppSheetView
10877  * @wx: X coordinate relative to the widget
10878  * @wy: Y coordinate relative to the widget
10879  * @bx: return location for bin_window X coordinate
10880  * @by: return location for bin_window Y coordinate
10881  *
10882  * Converts widget coordinates to coordinates for the bin_window
10883  * (see pspp_sheet_view_get_bin_window()).
10884  *
10885  * Since: 2.12
10886  **/
10887 void
10888 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10889                                                    gint         wx,
10890                                                    gint         wy,
10891                                                    gint        *bx,
10892                                                    gint        *by)
10893 {
10894   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10895
10896   if (bx)
10897     *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10898   if (by)
10899     *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10900 }
10901
10902 /**
10903  * pspp_sheet_view_convert_bin_window_to_widget_coords:
10904  * @tree_view: a #PsppSheetView
10905  * @bx: bin_window X coordinate
10906  * @by: bin_window Y coordinate
10907  * @wx: return location for widget X coordinate
10908  * @wy: return location for widget Y coordinate
10909  *
10910  * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10911  * to widget relative coordinates.
10912  *
10913  * Since: 2.12
10914  **/
10915 void
10916 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10917                                                    gint         bx,
10918                                                    gint         by,
10919                                                    gint        *wx,
10920                                                    gint        *wy)
10921 {
10922   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10923
10924   if (wx)
10925     *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10926   if (wy)
10927     *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10928 }
10929
10930 /**
10931  * pspp_sheet_view_convert_tree_to_bin_window_coords:
10932  * @tree_view: a #PsppSheetView
10933  * @tx: tree X coordinate
10934  * @ty: tree Y coordinate
10935  * @bx: return location for X coordinate relative to bin_window
10936  * @by: return location for Y coordinate relative to bin_window
10937  *
10938  * Converts tree coordinates (coordinates in full scrollable area of the tree)
10939  * to bin_window coordinates.
10940  *
10941  * Since: 2.12
10942  **/
10943 void
10944 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10945                                                  gint         tx,
10946                                                  gint         ty,
10947                                                  gint        *bx,
10948                                                  gint        *by)
10949 {
10950   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10951
10952   if (bx)
10953     *bx = tx;
10954   if (by)
10955     *by = ty - tree_view->priv->dy;
10956 }
10957
10958 /**
10959  * pspp_sheet_view_convert_bin_window_to_tree_coords:
10960  * @tree_view: a #PsppSheetView
10961  * @bx: X coordinate relative to bin_window
10962  * @by: Y coordinate relative to bin_window
10963  * @tx: return location for tree X coordinate
10964  * @ty: return location for tree Y coordinate
10965  *
10966  * Converts bin_window coordinates to coordinates for the
10967  * tree (the full scrollable area of the tree).
10968  *
10969  * Since: 2.12
10970  **/
10971 void
10972 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10973                                                  gint         bx,
10974                                                  gint         by,
10975                                                  gint        *tx,
10976                                                  gint        *ty)
10977 {
10978   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10979
10980   if (tx)
10981     *tx = bx;
10982   if (ty)
10983     *ty = by + tree_view->priv->dy;
10984 }
10985
10986
10987
10988 /**
10989  * pspp_sheet_view_get_visible_range:
10990  * @tree_view: A #PsppSheetView
10991  * @start_path: (allow-none): Return location for start of region, or %NULL.
10992  * @end_path: (allow-none): Return location for end of region, or %NULL.
10993  *
10994  * Sets @start_path and @end_path to be the first and last visible path.
10995  * Note that there may be invisible paths in between.
10996  *
10997  * The paths should be freed with gtk_tree_path_free() after use.
10998  *
10999  * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
11000  *
11001  * Since: 2.8
11002  **/
11003 gboolean
11004 pspp_sheet_view_get_visible_range (PsppSheetView  *tree_view,
11005                                  GtkTreePath **start_path,
11006                                  GtkTreePath **end_path)
11007 {
11008   int node;
11009   gboolean retval;
11010   
11011   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11012
11013   if (!tree_view->priv->row_count)
11014     return FALSE;
11015
11016   retval = TRUE;
11017
11018   if (start_path)
11019     {
11020       pspp_sheet_view_find_offset (tree_view,
11021                                    TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
11022                                    &node);
11023       if (node >= 0)
11024         *start_path = _pspp_sheet_view_find_path (tree_view, node);
11025       else
11026         retval = FALSE;
11027     }
11028
11029   if (end_path)
11030     {
11031       gint y;
11032
11033       if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
11034         y = tree_view->priv->height - 1;
11035       else
11036         y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
11037
11038       pspp_sheet_view_find_offset (tree_view, y, &node);
11039       if (node >= 0)
11040         *end_path = _pspp_sheet_view_find_path (tree_view, node);
11041       else
11042         retval = FALSE;
11043     }
11044
11045   return retval;
11046 }
11047
11048 static void
11049 unset_reorderable (PsppSheetView *tree_view)
11050 {
11051   if (tree_view->priv->reorderable)
11052     {
11053       tree_view->priv->reorderable = FALSE;
11054       g_object_notify (G_OBJECT (tree_view), "reorderable");
11055     }
11056 }
11057
11058 /**
11059  * pspp_sheet_view_enable_model_drag_source:
11060  * @tree_view: a #PsppSheetView
11061  * @start_button_mask: Mask of allowed buttons to start drag
11062  * @targets: the table of targets that the drag will support
11063  * @n_targets: the number of items in @targets
11064  * @actions: the bitmask of possible actions for a drag from this
11065  *    widget
11066  *
11067  * Turns @tree_view into a drag source for automatic DND. Calling this
11068  * method sets #PsppSheetView:reorderable to %FALSE.
11069  **/
11070 void
11071 pspp_sheet_view_enable_model_drag_source (PsppSheetView              *tree_view,
11072                                         GdkModifierType           start_button_mask,
11073                                         const GtkTargetEntry     *targets,
11074                                         gint                      n_targets,
11075                                         GdkDragAction             actions)
11076 {
11077   TreeViewDragInfo *di;
11078
11079   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11080
11081   gtk_drag_source_set (GTK_WIDGET (tree_view),
11082                        0,
11083                        targets,
11084                        n_targets,
11085                        actions);
11086
11087   di = ensure_info (tree_view);
11088
11089   di->start_button_mask = start_button_mask;
11090   di->source_actions = actions;
11091   di->source_set = TRUE;
11092
11093   unset_reorderable (tree_view);
11094 }
11095
11096 /**
11097  * pspp_sheet_view_enable_model_drag_dest:
11098  * @tree_view: a #PsppSheetView
11099  * @targets: the table of targets that the drag will support
11100  * @n_targets: the number of items in @targets
11101  * @actions: the bitmask of possible actions for a drag from this
11102  *    widget
11103  * 
11104  * Turns @tree_view into a drop destination for automatic DND. Calling
11105  * this method sets #PsppSheetView:reorderable to %FALSE.
11106  **/
11107 void
11108 pspp_sheet_view_enable_model_drag_dest (PsppSheetView              *tree_view,
11109                                       const GtkTargetEntry     *targets,
11110                                       gint                      n_targets,
11111                                       GdkDragAction             actions)
11112 {
11113   TreeViewDragInfo *di;
11114
11115   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11116
11117   gtk_drag_dest_set (GTK_WIDGET (tree_view),
11118                      0,
11119                      targets,
11120                      n_targets,
11121                      actions);
11122
11123   di = ensure_info (tree_view);
11124   di->dest_set = TRUE;
11125
11126   unset_reorderable (tree_view);
11127 }
11128
11129 /**
11130  * pspp_sheet_view_unset_rows_drag_source:
11131  * @tree_view: a #PsppSheetView
11132  *
11133  * Undoes the effect of
11134  * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
11135  * #PsppSheetView:reorderable to %FALSE.
11136  **/
11137 void
11138 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
11139 {
11140   TreeViewDragInfo *di;
11141
11142   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11143
11144   di = get_info (tree_view);
11145
11146   if (di)
11147     {
11148       if (di->source_set)
11149         {
11150           gtk_drag_source_unset (GTK_WIDGET (tree_view));
11151           di->source_set = FALSE;
11152         }
11153
11154       if (!di->dest_set && !di->source_set)
11155         remove_info (tree_view);
11156     }
11157   
11158   unset_reorderable (tree_view);
11159 }
11160
11161 /**
11162  * pspp_sheet_view_unset_rows_drag_dest:
11163  * @tree_view: a #PsppSheetView
11164  *
11165  * Undoes the effect of
11166  * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
11167  * #PsppSheetView:reorderable to %FALSE.
11168  **/
11169 void
11170 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
11171 {
11172   TreeViewDragInfo *di;
11173
11174   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11175
11176   di = get_info (tree_view);
11177
11178   if (di)
11179     {
11180       if (di->dest_set)
11181         {
11182           gtk_drag_dest_unset (GTK_WIDGET (tree_view));
11183           di->dest_set = FALSE;
11184         }
11185
11186       if (!di->dest_set && !di->source_set)
11187         remove_info (tree_view);
11188     }
11189
11190   unset_reorderable (tree_view);
11191 }
11192
11193 /**
11194  * pspp_sheet_view_set_drag_dest_row:
11195  * @tree_view: a #PsppSheetView
11196  * @path: (allow-none): The path of the row to highlight, or %NULL.
11197  * @pos: Specifies whether to drop before, after or into the row
11198  * 
11199  * Sets the row that is highlighted for feedback.
11200  **/
11201 void
11202 pspp_sheet_view_set_drag_dest_row (PsppSheetView            *tree_view,
11203                                  GtkTreePath            *path,
11204                                  PsppSheetViewDropPosition pos)
11205 {
11206   GtkTreePath *current_dest;
11207
11208   /* Note; this function is exported to allow a custom DND
11209    * implementation, so it can't touch TreeViewDragInfo
11210    */
11211
11212   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11213
11214   current_dest = NULL;
11215
11216   if (tree_view->priv->drag_dest_row)
11217     {
11218       current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11219       gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
11220     }
11221
11222   /* special case a drop on an empty model */
11223   tree_view->priv->empty_view_drop = 0;
11224
11225   if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
11226       && gtk_tree_path_get_depth (path) == 1
11227       && gtk_tree_path_get_indices (path)[0] == 0)
11228     {
11229       gint n_children;
11230
11231       n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
11232                                                    NULL);
11233
11234       if (!n_children)
11235         tree_view->priv->empty_view_drop = 1;
11236     }
11237
11238   tree_view->priv->drag_dest_pos = pos;
11239
11240   if (path)
11241     {
11242       tree_view->priv->drag_dest_row =
11243         gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
11244       pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
11245     }
11246   else
11247     tree_view->priv->drag_dest_row = NULL;
11248
11249   if (current_dest)
11250     {
11251       int node, new_node;
11252
11253       _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11254       _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11255
11256       if (node >= 0)
11257         {
11258           new_node = pspp_sheet_view_node_next (tree_view, node);
11259           if (new_node >= 0)
11260             _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11261
11262           new_node = pspp_sheet_view_node_prev (tree_view, node);
11263           if (new_node >= 0)
11264             _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11265         }
11266       gtk_tree_path_free (current_dest);
11267     }
11268 }
11269
11270 /**
11271  * pspp_sheet_view_get_drag_dest_row:
11272  * @tree_view: a #PsppSheetView
11273  * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11274  * @pos: (allow-none): Return location for the drop position, or %NULL
11275  * 
11276  * Gets information about the row that is highlighted for feedback.
11277  **/
11278 void
11279 pspp_sheet_view_get_drag_dest_row (PsppSheetView              *tree_view,
11280                                  GtkTreePath             **path,
11281                                  PsppSheetViewDropPosition  *pos)
11282 {
11283   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11284
11285   if (path)
11286     {
11287       if (tree_view->priv->drag_dest_row)
11288         *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11289       else
11290         {
11291           if (tree_view->priv->empty_view_drop)
11292             *path = gtk_tree_path_new_from_indices (0, -1);
11293           else
11294             *path = NULL;
11295         }
11296     }
11297
11298   if (pos)
11299     *pos = tree_view->priv->drag_dest_pos;
11300 }
11301
11302 /**
11303  * pspp_sheet_view_get_dest_row_at_pos:
11304  * @tree_view: a #PsppSheetView
11305  * @drag_x: the position to determine the destination row for
11306  * @drag_y: the position to determine the destination row for
11307  * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11308  * @pos: (allow-none): Return location for the drop position, or %NULL
11309  * 
11310  * Determines the destination row for a given position.  @drag_x and
11311  * @drag_y are expected to be in widget coordinates.  This function is only
11312  * meaningful if @tree_view is realized.  Therefore this function will always
11313  * return %FALSE if @tree_view is not realized or does not have a model.
11314  * 
11315  * Return value: whether there is a row at the given position, %TRUE if this
11316  * is indeed the case.
11317  **/
11318 gboolean
11319 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView             *tree_view,
11320                                    gint                     drag_x,
11321                                    gint                     drag_y,
11322                                    GtkTreePath            **path,
11323                                    PsppSheetViewDropPosition *pos)
11324 {
11325   gint cell_y;
11326   gint bin_x, bin_y;
11327   gdouble offset_into_row;
11328   gdouble third;
11329   GdkRectangle cell;
11330   PsppSheetViewColumn *column = NULL;
11331   GtkTreePath *tmp_path = NULL;
11332
11333   /* Note; this function is exported to allow a custom DND
11334    * implementation, so it can't touch TreeViewDragInfo
11335    */
11336
11337   g_return_val_if_fail (tree_view != NULL, FALSE);
11338   g_return_val_if_fail (drag_x >= 0, FALSE);
11339   g_return_val_if_fail (drag_y >= 0, FALSE);
11340
11341   if (path)
11342     *path = NULL;
11343
11344   if (tree_view->priv->bin_window == NULL)
11345     return FALSE;
11346
11347   if (tree_view->priv->row_count == 0)
11348     return FALSE;
11349
11350   /* If in the top third of a row, we drop before that row; if
11351    * in the bottom third, drop after that row; if in the middle,
11352    * and the row has children, drop into the row.
11353    */
11354   pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11355                                                      &bin_x, &bin_y);
11356
11357   if (!pspp_sheet_view_get_path_at_pos (tree_view,
11358                                       bin_x,
11359                                       bin_y,
11360                                       &tmp_path,
11361                                       &column,
11362                                       NULL,
11363                                       &cell_y))
11364     return FALSE;
11365
11366   pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11367                                      &cell);
11368
11369   offset_into_row = cell_y;
11370
11371   if (path)
11372     *path = tmp_path;
11373   else
11374     gtk_tree_path_free (tmp_path);
11375
11376   tmp_path = NULL;
11377
11378   third = cell.height / 3.0;
11379
11380   if (pos)
11381     {
11382       if (offset_into_row < third)
11383         {
11384           *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11385         }
11386       else if (offset_into_row < (cell.height / 2.0))
11387         {
11388           *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11389         }
11390       else if (offset_into_row < third * 2.0)
11391         {
11392           *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11393         }
11394       else
11395         {
11396           *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11397         }
11398     }
11399
11400   return TRUE;
11401 }
11402
11403
11404 #if GTK3_TRANSITION
11405 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11406 /**
11407  * pspp_sheet_view_create_row_drag_icon:
11408  * @tree_view: a #PsppSheetView
11409  * @path: a #GtkTreePath in @tree_view
11410  *
11411  * Creates a #GdkPixmap representation of the row at @path.  
11412  * This image is used for a drag icon.
11413  *
11414  * Return value: a newly-allocated pixmap of the drag icon.
11415  **/
11416 GdkPixmap *
11417 pspp_sheet_view_create_row_drag_icon (PsppSheetView  *tree_view,
11418                                     GtkTreePath  *path)
11419 {
11420   GtkTreeIter   iter;
11421   int node;
11422   gint cell_offset;
11423   GList *list;
11424   GdkRectangle background_area;
11425   GdkRectangle expose_area;
11426   GtkWidget *widget;
11427   /* start drawing inside the black outline */
11428   gint x = 1, y = 1;
11429   GdkDrawable *drawable;
11430   gint bin_window_width;
11431   gboolean rtl;
11432
11433   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11434   g_return_val_if_fail (path != NULL, NULL);
11435
11436   widget = GTK_WIDGET (tree_view);
11437
11438   if (!gtk_widget_get_realized (widget))
11439     return NULL;
11440
11441   _pspp_sheet_view_find_node (tree_view,
11442                             path,
11443                             &node);
11444
11445   if (node < 0)
11446     return NULL;
11447
11448   if (!gtk_tree_model_get_iter (tree_view->priv->model,
11449                                 &iter,
11450                                 path))
11451     return NULL;
11452   
11453   cell_offset = x;
11454
11455   background_area.y = y;
11456   background_area.height = ROW_HEIGHT (tree_view);
11457
11458   bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
11459
11460   drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11461                              bin_window_width + 2,
11462                              background_area.height + 2,
11463                              -1);
11464
11465   expose_area.x = 0;
11466   expose_area.y = 0;
11467   expose_area.width = bin_window_width + 2;
11468   expose_area.height = background_area.height + 2;
11469
11470 #if GTK3_TRANSITION
11471   gdk_draw_rectangle (drawable,
11472                       widget->style->base_gc [gtk_widget_get_state (widget)],
11473                       TRUE,
11474                       0, 0,
11475                       bin_window_width + 2,
11476                       background_area.height + 2);
11477 #endif
11478
11479   rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11480
11481   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11482       list;
11483       list = (rtl ? list->prev : list->next))
11484     {
11485       PsppSheetViewColumn *column = list->data;
11486       GdkRectangle cell_area;
11487       gint vertical_separator;
11488
11489       if (!column->visible)
11490         continue;
11491
11492       pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11493
11494       background_area.x = cell_offset;
11495       background_area.width = column->width;
11496
11497       gtk_widget_style_get (widget,
11498                             "vertical-separator", &vertical_separator,
11499                             NULL);
11500
11501       cell_area = background_area;
11502
11503       cell_area.y += vertical_separator / 2;
11504       cell_area.height -= vertical_separator;
11505
11506       if (pspp_sheet_view_column_cell_is_visible (column))
11507         _pspp_sheet_view_column_cell_render (column,
11508                                              drawable,
11509                                              &background_area,
11510                                              &cell_area,
11511                                              &expose_area,
11512                                              0);
11513       cell_offset += column->width;
11514     }
11515
11516 #if GTK3_TRANSITION
11517   gdk_draw_rectangle (drawable,
11518                       widget->style->black_gc,
11519                       FALSE,
11520                       0, 0,
11521                       bin_window_width + 1,
11522                       background_area.height + 1);
11523 #endif
11524
11525   return drawable;
11526 }
11527 #endif
11528
11529 /**
11530  * pspp_sheet_view_set_destroy_count_func:
11531  * @tree_view: A #PsppSheetView
11532  * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11533  * @data: (allow-none): User data to be passed to @func, or %NULL
11534  * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11535  *
11536  * This function should almost never be used.  It is meant for private use by
11537  * ATK for determining the number of visible children that are removed when a row is deleted.
11538  **/
11539 void
11540 pspp_sheet_view_set_destroy_count_func (PsppSheetView             *tree_view,
11541                                       PsppSheetDestroyCountFunc  func,
11542                                       gpointer                 data,
11543                                       GDestroyNotify           destroy)
11544 {
11545   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11546
11547   if (tree_view->priv->destroy_count_destroy)
11548     tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11549
11550   tree_view->priv->destroy_count_func = func;
11551   tree_view->priv->destroy_count_data = data;
11552   tree_view->priv->destroy_count_destroy = destroy;
11553 }
11554
11555
11556 /*
11557  * Interactive search
11558  */
11559
11560 /**
11561  * pspp_sheet_view_set_enable_search:
11562  * @tree_view: A #PsppSheetView
11563  * @enable_search: %TRUE, if the user can search interactively
11564  *
11565  * If @enable_search is set, then the user can type in text to search through
11566  * the tree interactively (this is sometimes called "typeahead find").
11567  * 
11568  * Note that even if this is %FALSE, the user can still initiate a search 
11569  * using the "start-interactive-search" key binding.
11570  */
11571 void
11572 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11573                                  gboolean     enable_search)
11574 {
11575   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11576
11577   enable_search = !!enable_search;
11578   
11579   if (tree_view->priv->enable_search != enable_search)
11580     {
11581        tree_view->priv->enable_search = enable_search;
11582        g_object_notify (G_OBJECT (tree_view), "enable-search");
11583     }
11584 }
11585
11586 /**
11587  * pspp_sheet_view_get_enable_search:
11588  * @tree_view: A #PsppSheetView
11589  *
11590  * Returns whether or not the tree allows to start interactive searching 
11591  * by typing in text.
11592  *
11593  * Return value: whether or not to let the user search interactively
11594  */
11595 gboolean
11596 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11597 {
11598   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11599
11600   return tree_view->priv->enable_search;
11601 }
11602
11603
11604 /**
11605  * pspp_sheet_view_get_search_column:
11606  * @tree_view: A #PsppSheetView
11607  *
11608  * Gets the column searched on by the interactive search code.
11609  *
11610  * Return value: the column the interactive search code searches in.
11611  */
11612 gint
11613 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11614 {
11615   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11616
11617   return (tree_view->priv->search_column);
11618 }
11619
11620 /**
11621  * pspp_sheet_view_set_search_column:
11622  * @tree_view: A #PsppSheetView
11623  * @column: the column of the model to search in, or -1 to disable searching
11624  *
11625  * Sets @column as the column where the interactive search code should
11626  * search in for the current model. 
11627  * 
11628  * If the search column is set, users can use the "start-interactive-search"
11629  * key binding to bring up search popup. The enable-search property controls
11630  * whether simply typing text will also start an interactive search.
11631  *
11632  * Note that @column refers to a column of the current model. The search 
11633  * column is reset to -1 when the model is changed.
11634  */
11635 void
11636 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11637                                  gint         column)
11638 {
11639   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11640   g_return_if_fail (column >= -1);
11641
11642   if (tree_view->priv->search_column == column)
11643     return;
11644
11645   tree_view->priv->search_column = column;
11646   g_object_notify (G_OBJECT (tree_view), "search-column");
11647 }
11648
11649 /**
11650  * pspp_sheet_view_get_search_equal_func:
11651  * @tree_view: A #PsppSheetView
11652  *
11653  * Returns the compare function currently in use.
11654  *
11655  * Return value: the currently used compare function for the search code.
11656  */
11657
11658 PsppSheetViewSearchEqualFunc
11659 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11660 {
11661   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11662
11663   return tree_view->priv->search_equal_func;
11664 }
11665
11666 /**
11667  * pspp_sheet_view_set_search_equal_func:
11668  * @tree_view: A #PsppSheetView
11669  * @search_equal_func: the compare function to use during the search
11670  * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11671  * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11672  *
11673  * Sets the compare function for the interactive search capabilities; note
11674  * that somewhat like strcmp() returning 0 for equality
11675  * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11676  **/
11677 void
11678 pspp_sheet_view_set_search_equal_func (PsppSheetView                *tree_view,
11679                                      PsppSheetViewSearchEqualFunc  search_equal_func,
11680                                      gpointer                    search_user_data,
11681                                      GDestroyNotify              search_destroy)
11682 {
11683   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11684   g_return_if_fail (search_equal_func != NULL);
11685
11686   if (tree_view->priv->search_destroy)
11687     tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11688
11689   tree_view->priv->search_equal_func = search_equal_func;
11690   tree_view->priv->search_user_data = search_user_data;
11691   tree_view->priv->search_destroy = search_destroy;
11692   if (tree_view->priv->search_equal_func == NULL)
11693     tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11694 }
11695
11696 /**
11697  * pspp_sheet_view_get_search_entry:
11698  * @tree_view: A #PsppSheetView
11699  *
11700  * Returns the #GtkEntry which is currently in use as interactive search
11701  * entry for @tree_view.  In case the built-in entry is being used, %NULL
11702  * will be returned.
11703  *
11704  * Return value: the entry currently in use as search entry.
11705  *
11706  * Since: 2.10
11707  */
11708 GtkEntry *
11709 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11710 {
11711   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11712
11713   if (tree_view->priv->search_custom_entry_set)
11714     return GTK_ENTRY (tree_view->priv->search_entry);
11715
11716   return NULL;
11717 }
11718
11719 /**
11720  * pspp_sheet_view_set_search_entry:
11721  * @tree_view: A #PsppSheetView
11722  * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11723  *
11724  * Sets the entry which the interactive search code will use for this
11725  * @tree_view.  This is useful when you want to provide a search entry
11726  * in our interface at all time at a fixed position.  Passing %NULL for
11727  * @entry will make the interactive search code use the built-in popup
11728  * entry again.
11729  *
11730  * Since: 2.10
11731  */
11732 void
11733 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11734                                 GtkEntry    *entry)
11735 {
11736   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11737   g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11738
11739   if (tree_view->priv->search_custom_entry_set)
11740     {
11741       if (tree_view->priv->search_entry_changed_id)
11742         {
11743           g_signal_handler_disconnect (tree_view->priv->search_entry,
11744                                        tree_view->priv->search_entry_changed_id);
11745           tree_view->priv->search_entry_changed_id = 0;
11746         }
11747       g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11748                                             G_CALLBACK (pspp_sheet_view_search_key_press_event),
11749                                             tree_view);
11750
11751       g_object_unref (tree_view->priv->search_entry);
11752     }
11753   else if (tree_view->priv->search_window)
11754     {
11755       gtk_widget_destroy (tree_view->priv->search_window);
11756
11757       tree_view->priv->search_window = NULL;
11758     }
11759
11760   if (entry)
11761     {
11762       tree_view->priv->search_entry = g_object_ref (entry);
11763       tree_view->priv->search_custom_entry_set = TRUE;
11764
11765       if (tree_view->priv->search_entry_changed_id == 0)
11766         {
11767           tree_view->priv->search_entry_changed_id =
11768             g_signal_connect (tree_view->priv->search_entry, "changed",
11769                               G_CALLBACK (pspp_sheet_view_search_init),
11770                               tree_view);
11771         }
11772       
11773         g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11774                           G_CALLBACK (pspp_sheet_view_search_key_press_event),
11775                           tree_view);
11776
11777         pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11778     }
11779   else
11780     {
11781       tree_view->priv->search_entry = NULL;
11782       tree_view->priv->search_custom_entry_set = FALSE;
11783     }
11784 }
11785
11786 /**
11787  * pspp_sheet_view_set_search_position_func:
11788  * @tree_view: A #PsppSheetView
11789  * @func: (allow-none): the function to use to position the search dialog, or %NULL
11790  *    to use the default search position function
11791  * @data: (allow-none): user data to pass to @func, or %NULL
11792  * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11793  *
11794  * Sets the function to use when positioning the search dialog.
11795  *
11796  * Since: 2.10
11797  **/
11798 void
11799 pspp_sheet_view_set_search_position_func (PsppSheetView                   *tree_view,
11800                                         PsppSheetViewSearchPositionFunc  func,
11801                                         gpointer                       user_data,
11802                                         GDestroyNotify                 destroy)
11803 {
11804   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11805
11806   if (tree_view->priv->search_position_destroy)
11807     tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11808
11809   tree_view->priv->search_position_func = func;
11810   tree_view->priv->search_position_user_data = user_data;
11811   tree_view->priv->search_position_destroy = destroy;
11812   if (tree_view->priv->search_position_func == NULL)
11813     tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11814 }
11815
11816 /**
11817  * pspp_sheet_view_get_search_position_func:
11818  * @tree_view: A #PsppSheetView
11819  *
11820  * Returns the positioning function currently in use.
11821  *
11822  * Return value: the currently used function for positioning the search dialog.
11823  *
11824  * Since: 2.10
11825  */
11826 PsppSheetViewSearchPositionFunc
11827 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11828 {
11829   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11830
11831   return tree_view->priv->search_position_func;
11832 }
11833
11834
11835 static void
11836 pspp_sheet_view_search_dialog_hide (GtkWidget   *search_dialog,
11837                                   PsppSheetView *tree_view)
11838 {
11839   if (tree_view->priv->disable_popdown)
11840     return;
11841
11842   if (tree_view->priv->search_entry_changed_id)
11843     {
11844       g_signal_handler_disconnect (tree_view->priv->search_entry,
11845                                    tree_view->priv->search_entry_changed_id);
11846       tree_view->priv->search_entry_changed_id = 0;
11847     }
11848   if (tree_view->priv->typeselect_flush_timeout)
11849     {
11850       g_source_remove (tree_view->priv->typeselect_flush_timeout);
11851       tree_view->priv->typeselect_flush_timeout = 0;
11852     }
11853         
11854   if (gtk_widget_get_visible (search_dialog))
11855     {
11856       /* send focus-in event */
11857       send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11858       gtk_widget_hide (search_dialog);
11859       gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11860       send_focus_change (GTK_WIDGET (tree_view), TRUE);
11861     }
11862 }
11863
11864 static void
11865 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11866                                     GtkWidget   *search_dialog,
11867                                     gpointer     user_data)
11868 {
11869   gint x, y;
11870   gint tree_x, tree_y;
11871   gint tree_width, tree_height;
11872   GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
11873   GdkScreen *screen = gdk_window_get_screen (tree_window);
11874   GtkRequisition requisition;
11875   gint monitor_num;
11876   GdkRectangle monitor;
11877
11878   monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11879   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11880
11881   gtk_widget_realize (search_dialog);
11882
11883   gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11884   tree_width = gdk_window_get_width (tree_window);
11885   tree_height = gdk_window_get_height (tree_window);
11886
11887   gtk_widget_size_request (search_dialog, &requisition);
11888
11889   if (tree_x + tree_width > gdk_screen_get_width (screen))
11890     x = gdk_screen_get_width (screen) - requisition.width;
11891   else if (tree_x + tree_width - requisition.width < 0)
11892     x = 0;
11893   else
11894     x = tree_x + tree_width - requisition.width;
11895
11896   if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11897     y = gdk_screen_get_height (screen) - requisition.height;
11898   else if (tree_y + tree_height < 0) /* isn't really possible ... */
11899     y = 0;
11900   else
11901     y = tree_y + tree_height;
11902
11903   gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11904 }
11905
11906 static void
11907 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11908                                       GtkMenu  *menu,
11909                                       gpointer  data)
11910 {
11911   PsppSheetView *tree_view = (PsppSheetView *)data;
11912
11913   tree_view->priv->disable_popdown = 1;
11914   g_signal_connect (menu, "hide",
11915                     G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11916 }
11917
11918 #if GTK3_TRANSITION
11919 /* Because we're visible but offscreen, we just set a flag in the preedit
11920  * callback.
11921  */
11922 static void
11923 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11924                                       PsppSheetView  *tree_view)
11925 {
11926   tree_view->priv->imcontext_changed = 1;
11927   if (tree_view->priv->typeselect_flush_timeout)
11928     {
11929       g_source_remove (tree_view->priv->typeselect_flush_timeout);
11930       tree_view->priv->typeselect_flush_timeout =
11931         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11932                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11933                        tree_view);
11934     }
11935
11936 }
11937 #endif
11938
11939 static void
11940 pspp_sheet_view_search_activate (GtkEntry    *entry,
11941                                PsppSheetView *tree_view)
11942 {
11943   GtkTreePath *path;
11944   int node;
11945
11946   pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11947                                     tree_view);
11948
11949   /* If we have a row selected and it's the cursor row, we activate
11950    * the row XXX */
11951   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11952     {
11953       path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11954       
11955       _pspp_sheet_view_find_node (tree_view, path, &node);
11956       
11957       if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11958         pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11959       
11960       gtk_tree_path_free (path);
11961     }
11962 }
11963
11964 static gboolean
11965 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11966 {
11967   PsppSheetView *tree_view = (PsppSheetView *)data;
11968
11969   tree_view->priv->disable_popdown = 0;
11970
11971   return FALSE;
11972 }
11973
11974 static void
11975 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11976                                      gpointer   data)
11977 {
11978   gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11979 }
11980
11981 static gboolean
11982 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11983                                    GdkEventAny *event,
11984                                    PsppSheetView *tree_view)
11985 {
11986   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11987
11988   pspp_sheet_view_search_dialog_hide (widget, tree_view);
11989
11990   return TRUE;
11991 }
11992
11993 static gboolean
11994 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11995                                          GdkEventButton *event,
11996                                          PsppSheetView *tree_view)
11997 {
11998   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11999
12000   pspp_sheet_view_search_dialog_hide (widget, tree_view);
12001
12002   if (event->window == tree_view->priv->bin_window)
12003     pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
12004
12005   return TRUE;
12006 }
12007
12008 static gboolean
12009 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
12010                                    GdkEventScroll *event,
12011                                    PsppSheetView *tree_view)
12012 {
12013   gboolean retval = FALSE;
12014
12015   if (event->direction == GDK_SCROLL_UP)
12016     {
12017       pspp_sheet_view_search_move (widget, tree_view, TRUE);
12018       retval = TRUE;
12019     }
12020   else if (event->direction == GDK_SCROLL_DOWN)
12021     {
12022       pspp_sheet_view_search_move (widget, tree_view, FALSE);
12023       retval = TRUE;
12024     }
12025
12026   /* renew the flush timeout */
12027   if (retval && tree_view->priv->typeselect_flush_timeout
12028       && !tree_view->priv->search_custom_entry_set)
12029     {
12030       g_source_remove (tree_view->priv->typeselect_flush_timeout);
12031       tree_view->priv->typeselect_flush_timeout =
12032         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12033                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12034                        tree_view);
12035     }
12036
12037   return retval;
12038 }
12039
12040 static gboolean
12041 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
12042                                       GdkEventKey *event,
12043                                       PsppSheetView *tree_view)
12044 {
12045   gboolean retval = FALSE;
12046
12047   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
12048   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12049
12050   /* close window and cancel the search */
12051   if (!tree_view->priv->search_custom_entry_set
12052       && (event->keyval == GDK_Escape ||
12053           event->keyval == GDK_Tab ||
12054             event->keyval == GDK_KP_Tab ||
12055             event->keyval == GDK_ISO_Left_Tab))
12056     {
12057       pspp_sheet_view_search_dialog_hide (widget, tree_view);
12058       return TRUE;
12059     }
12060
12061   /* select previous matching iter */
12062   if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
12063     {
12064       if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12065         gtk_widget_error_bell (widget);
12066
12067       retval = TRUE;
12068     }
12069
12070   if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
12071       && (event->keyval == GDK_g || event->keyval == GDK_G))
12072     {
12073       if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12074         gtk_widget_error_bell (widget);
12075
12076       retval = TRUE;
12077     }
12078
12079   /* select next matching iter */
12080   if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
12081     {
12082       if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12083         gtk_widget_error_bell (widget);
12084
12085       retval = TRUE;
12086     }
12087
12088   if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
12089       && (event->keyval == GDK_g || event->keyval == GDK_G))
12090     {
12091       if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12092         gtk_widget_error_bell (widget);
12093
12094       retval = TRUE;
12095     }
12096
12097   /* renew the flush timeout */
12098   if (retval && tree_view->priv->typeselect_flush_timeout
12099       && !tree_view->priv->search_custom_entry_set)
12100     {
12101       g_source_remove (tree_view->priv->typeselect_flush_timeout);
12102       tree_view->priv->typeselect_flush_timeout =
12103         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12104                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12105                        tree_view);
12106     }
12107
12108   return retval;
12109 }
12110
12111 /*  this function returns FALSE if there is a search string but
12112  *  nothing was found, and TRUE otherwise.
12113  */
12114 static gboolean
12115 pspp_sheet_view_search_move (GtkWidget   *window,
12116                            PsppSheetView *tree_view,
12117                            gboolean     up)
12118 {
12119   gboolean ret;
12120   gint len;
12121   gint count = 0;
12122   const gchar *text;
12123   GtkTreeIter iter;
12124   GtkTreeModel *model;
12125   PsppSheetSelection *selection;
12126
12127   text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
12128
12129   g_return_val_if_fail (text != NULL, FALSE);
12130
12131   len = strlen (text);
12132
12133   if (up && tree_view->priv->selected_iter == 1)
12134     return strlen (text) < 1;
12135
12136   len = strlen (text);
12137
12138   if (len < 1)
12139     return TRUE;
12140
12141   model = pspp_sheet_view_get_model (tree_view);
12142   selection = pspp_sheet_view_get_selection (tree_view);
12143
12144   /* search */
12145   pspp_sheet_selection_unselect_all (selection);
12146   if (!gtk_tree_model_get_iter_first (model, &iter))
12147     return TRUE;
12148
12149   ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
12150                                    &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
12151
12152   if (ret)
12153     {
12154       /* found */
12155       tree_view->priv->selected_iter += up?(-1):(1);
12156       return TRUE;
12157     }
12158   else
12159     {
12160       /* return to old iter */
12161       count = 0;
12162       gtk_tree_model_get_iter_first (model, &iter);
12163       pspp_sheet_view_search_iter (model, selection,
12164                                  &iter, text,
12165                                  &count, tree_view->priv->selected_iter);
12166       return FALSE;
12167     }
12168 }
12169
12170 static gboolean
12171 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
12172                                  gint          column,
12173                                  const gchar  *key,
12174                                  GtkTreeIter  *iter,
12175                                  gpointer      search_data)
12176 {
12177   gboolean retval = TRUE;
12178   const gchar *str;
12179   gchar *normalized_string;
12180   gchar *normalized_key;
12181   gchar *case_normalized_string = NULL;
12182   gchar *case_normalized_key = NULL;
12183   GValue value = {0,};
12184   GValue transformed = {0,};
12185
12186   gtk_tree_model_get_value (model, iter, column, &value);
12187
12188   g_value_init (&transformed, G_TYPE_STRING);
12189
12190   if (!g_value_transform (&value, &transformed))
12191     {
12192       g_value_unset (&value);
12193       return TRUE;
12194     }
12195
12196   g_value_unset (&value);
12197
12198   str = g_value_get_string (&transformed);
12199   if (!str)
12200     {
12201       g_value_unset (&transformed);
12202       return TRUE;
12203     }
12204
12205   normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
12206   normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
12207
12208   if (normalized_string && normalized_key)
12209     {
12210       case_normalized_string = g_utf8_casefold (normalized_string, -1);
12211       case_normalized_key = g_utf8_casefold (normalized_key, -1);
12212
12213       if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
12214         retval = FALSE;
12215     }
12216
12217   g_value_unset (&transformed);
12218   g_free (normalized_key);
12219   g_free (normalized_string);
12220   g_free (case_normalized_key);
12221   g_free (case_normalized_string);
12222
12223   return retval;
12224 }
12225
12226 static gboolean
12227 pspp_sheet_view_search_iter (GtkTreeModel     *model,
12228                              PsppSheetSelection *selection,
12229                              GtkTreeIter      *iter,
12230                              const gchar      *text,
12231                              gint             *count,
12232                              gint              n)
12233 {
12234   int node = -1;
12235   GtkTreePath *path;
12236
12237   PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
12238
12239   path = gtk_tree_model_get_path (model, iter);
12240   _pspp_sheet_view_find_node (tree_view, path, &node);
12241
12242   do
12243     {
12244       gboolean done = FALSE;
12245
12246       if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12247         {
12248           (*count)++;
12249           if (*count == n)
12250             {
12251               pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12252                                               TRUE, 0.5, 0.0);
12253               pspp_sheet_selection_select_iter (selection, iter);
12254               pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12255
12256               if (path)
12257                 gtk_tree_path_free (path);
12258
12259               return TRUE;
12260             }
12261         }
12262
12263
12264       do
12265         {
12266           node = pspp_sheet_view_node_next (tree_view, node);
12267
12268           if (node >= 0)
12269             {
12270               gboolean has_next;
12271
12272               has_next = gtk_tree_model_iter_next (model, iter);
12273
12274               done = TRUE;
12275               gtk_tree_path_next (path);
12276
12277               /* sanity check */
12278               TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12279             }
12280           else
12281             {
12282               if (path)
12283                 gtk_tree_path_free (path);
12284
12285               /* we've run out of tree, done with this func */
12286               return FALSE;
12287             }
12288         }
12289       while (!done);
12290     }
12291   while (1);
12292
12293   return FALSE;
12294 }
12295
12296 static void
12297 pspp_sheet_view_search_init (GtkWidget   *entry,
12298                            PsppSheetView *tree_view)
12299 {
12300   gint ret;
12301   gint count = 0;
12302   const gchar *text;
12303   GtkTreeIter iter;
12304   GtkTreeModel *model;
12305   PsppSheetSelection *selection;
12306
12307   g_return_if_fail (GTK_IS_ENTRY (entry));
12308   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12309
12310   text = gtk_entry_get_text (GTK_ENTRY (entry));
12311
12312   model = pspp_sheet_view_get_model (tree_view);
12313   selection = pspp_sheet_view_get_selection (tree_view);
12314
12315   /* search */
12316   pspp_sheet_selection_unselect_all (selection);
12317   if (tree_view->priv->typeselect_flush_timeout
12318       && !tree_view->priv->search_custom_entry_set)
12319     {
12320       g_source_remove (tree_view->priv->typeselect_flush_timeout);
12321       tree_view->priv->typeselect_flush_timeout =
12322         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12323                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12324                        tree_view);
12325     }
12326
12327   if (*text == '\0')
12328     return;
12329
12330   if (!gtk_tree_model_get_iter_first (model, &iter))
12331     return;
12332
12333   ret = pspp_sheet_view_search_iter (model, selection,
12334                                    &iter, text,
12335                                    &count, 1);
12336
12337   if (ret)
12338     tree_view->priv->selected_iter = 1;
12339 }
12340
12341 static void
12342 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12343                              PsppSheetView     *tree_view)
12344 {
12345   if (tree_view->priv->edited_column == NULL)
12346     return;
12347
12348   _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12349   tree_view->priv->edited_column = NULL;
12350
12351   if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12352     gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12353
12354   g_signal_handlers_disconnect_by_func (cell_editable,
12355                                         pspp_sheet_view_remove_widget,
12356                                         tree_view);
12357   g_signal_handlers_disconnect_by_func (cell_editable,
12358                                         pspp_sheet_view_editable_button_press_event,
12359                                         tree_view);
12360   g_signal_handlers_disconnect_by_func (cell_editable,
12361                                         pspp_sheet_view_editable_clicked,
12362                                         tree_view);
12363
12364   gtk_container_remove (GTK_CONTAINER (tree_view),
12365                         GTK_WIDGET (cell_editable));  
12366
12367   /* FIXME should only redraw a single node */
12368   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12369 }
12370
12371 static gboolean
12372 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12373                              GtkTreePath *cursor_path)
12374 {
12375   GtkTreeIter iter;
12376   GdkRectangle background_area;
12377   GdkRectangle cell_area;
12378   GtkCellEditable *editable_widget = NULL;
12379   gchar *path_string;
12380   guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12381   gint retval = FALSE;
12382   int cursor_node;
12383
12384   g_assert (tree_view->priv->focus_column);
12385
12386   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12387     return FALSE;
12388
12389   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12390   if (cursor_node < 0)
12391     return FALSE;
12392
12393   path_string = gtk_tree_path_to_string (cursor_path);
12394   gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12395
12396   pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12397                                            tree_view->priv->model,
12398                                            &iter);
12399   pspp_sheet_view_get_background_area (tree_view,
12400                                      cursor_path,
12401                                      tree_view->priv->focus_column,
12402                                      &background_area);
12403   pspp_sheet_view_get_cell_area (tree_view,
12404                                cursor_path,
12405                                tree_view->priv->focus_column,
12406                                &cell_area);
12407
12408   if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12409                                         &editable_widget,
12410                                         NULL,
12411                                         path_string,
12412                                         &background_area,
12413                                         &cell_area,
12414                                         flags))
12415     {
12416       retval = TRUE;
12417       if (editable_widget != NULL)
12418         {
12419           gint left, right;
12420           GdkRectangle area;
12421           GtkCellRenderer *cell;
12422
12423           area = cell_area;
12424           cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12425
12426           _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12427
12428           area.x += left;
12429           area.width -= right + left;
12430
12431           pspp_sheet_view_real_start_editing (tree_view,
12432                                             tree_view->priv->focus_column,
12433                                             cursor_path,
12434                                             editable_widget,
12435                                             &area,
12436                                             NULL,
12437                                             flags);
12438         }
12439
12440     }
12441   g_free (path_string);
12442   return retval;
12443 }
12444
12445 static gboolean
12446 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12447                                              GdkEventButton *event,
12448                                              PsppSheetView *sheet_view)
12449 {
12450   gint node;
12451
12452   node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12453                                              "pspp-sheet-view-node"));
12454   return pspp_sheet_view_row_head_clicked (sheet_view,
12455                                            node,
12456                                            sheet_view->priv->edited_column,
12457                                            event);
12458 }
12459
12460 static void
12461 pspp_sheet_view_editable_clicked (GtkButton *button,
12462                                   PsppSheetView *sheet_view)
12463 {
12464   pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12465                                                sheet_view);
12466 }
12467
12468 static gboolean
12469 is_all_selected (GtkWidget *widget)
12470 {
12471   GtkEntryBuffer *buffer;
12472   gint start_pos, end_pos;
12473
12474   if (!GTK_IS_ENTRY (widget))
12475     return FALSE;
12476
12477   buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12478   return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12479                                              &start_pos, &end_pos)
12480           && start_pos == 0
12481           && end_pos == gtk_entry_buffer_get_length (buffer));
12482 }
12483
12484 static gboolean
12485 is_at_left (GtkWidget *widget)
12486 {
12487   return (GTK_IS_ENTRY (widget)
12488           && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12489 }
12490
12491 static gboolean
12492 is_at_right (GtkWidget *widget)
12493 {
12494   GtkEntryBuffer *buffer;
12495   gint length;
12496
12497   if (!GTK_IS_ENTRY (widget))
12498     return FALSE;
12499
12500   buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12501   length = gtk_entry_buffer_get_length (buffer);
12502   return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12503 }
12504
12505 static gboolean
12506 pspp_sheet_view_event (GtkWidget *widget,
12507                        GdkEventKey *event,
12508                        PsppSheetView *tree_view)
12509 {
12510   PsppSheetViewColumn *column;
12511   GtkTreePath *path;
12512   gboolean handled;
12513   gboolean cancel;
12514   guint keyval;
12515   gint row;
12516
12517   /* Intercept only key press events.
12518      It would make sense to use "key-press-event" instead of "event", but
12519      GtkEntry attaches its own signal handler to "key-press-event" that runs
12520      before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12521   */
12522   if (event->type != GDK_KEY_PRESS)
12523     return FALSE;
12524
12525   keyval = event->keyval;
12526   cancel = FALSE;
12527   switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12528     {
12529     case 0:
12530       switch (event->keyval)
12531         {
12532         case GDK_Left:      case GDK_KP_Left:
12533         case GDK_Home:      case GDK_KP_Home:
12534           if (!is_all_selected (widget) && !is_at_left (widget))
12535             return FALSE;
12536           break;
12537
12538         case GDK_Right:     case GDK_KP_Right:
12539         case GDK_End:       case GDK_KP_End:
12540           if (!is_all_selected (widget) && !is_at_right (widget))
12541             return FALSE;
12542           break;
12543
12544         case GDK_Up:        case GDK_KP_Up:
12545         case GDK_Down:      case GDK_KP_Down:
12546           break;
12547
12548         case GDK_Page_Up:   case GDK_KP_Page_Up:
12549         case GDK_Page_Down: case GDK_KP_Page_Down:
12550           break;
12551
12552         case GDK_Escape:
12553           cancel = TRUE;
12554           break;
12555
12556         case GDK_Return:
12557           keyval = GDK_Down;
12558           break;
12559
12560         case GDK_Tab:       case GDK_KP_Tab:
12561         case GDK_ISO_Left_Tab:
12562           keyval = GDK_Tab;
12563           break;
12564
12565         default:
12566           return FALSE;
12567         }
12568       break;
12569
12570     case GDK_SHIFT_MASK:
12571       switch (event->keyval)
12572         {
12573         case GDK_Tab:
12574         case GDK_ISO_Left_Tab:
12575           keyval = GDK_Tab;
12576           break;
12577
12578         default:
12579           return FALSE;
12580         }
12581       break;
12582
12583     case GDK_CONTROL_MASK:
12584       switch (event->keyval)
12585         {
12586         case GDK_Left:      case GDK_KP_Left:
12587           if (!is_all_selected (widget) && !is_at_left (widget))
12588             return FALSE;
12589           break;
12590
12591         case GDK_Right:     case GDK_KP_Right:
12592           if (!is_all_selected (widget) && !is_at_right (widget))
12593             return FALSE;
12594           break;
12595
12596         case GDK_Up:        case GDK_KP_Up:
12597         case GDK_Down:      case GDK_KP_Down:
12598           break;
12599
12600         default:
12601           return FALSE;
12602         }
12603       break;
12604
12605     default:
12606       return FALSE;
12607     }
12608
12609   row = tree_view->priv->edited_row;
12610   column = tree_view->priv->edited_column;
12611   path = gtk_tree_path_new_from_indices (row, -1);
12612
12613   pspp_sheet_view_stop_editing (tree_view, cancel);
12614   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12615
12616   pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12617   gtk_tree_path_free (path);
12618
12619   handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12620                                       G_OBJECT (tree_view));
12621   if (handled)
12622     g_signal_stop_emission_by_name (widget, "event");
12623
12624   pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12625   pspp_sheet_view_start_editing (tree_view, path);
12626   gtk_tree_path_free (path);
12627
12628   return handled;
12629 }
12630
12631 static void
12632 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12633                                           gpointer data)
12634 {
12635   PsppSheetView *sheet_view = data;
12636
12637   g_signal_connect (widget, "event",
12638                     G_CALLBACK (pspp_sheet_view_event),
12639                     sheet_view);
12640
12641   if (GTK_IS_CONTAINER (widget))
12642     gtk_container_foreach (GTK_CONTAINER (widget),
12643                            pspp_sheet_view_override_cell_keypresses,
12644                            data);
12645 }
12646
12647 static void
12648 pspp_sheet_view_real_start_editing (PsppSheetView       *tree_view,
12649                                   PsppSheetViewColumn *column,
12650                                   GtkTreePath       *path,
12651                                   GtkCellEditable   *cell_editable,
12652                                   GdkRectangle      *cell_area,
12653                                   GdkEvent          *event,
12654                                   guint              flags)
12655 {
12656   PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12657   gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
12658   GtkRequisition requisition;
12659   gint row;
12660
12661   g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12662
12663   tree_view->priv->edited_column = column;
12664   _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12665
12666   row = gtk_tree_path_get_indices (path)[0];
12667   tree_view->priv->edited_row = row;
12668   pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12669   cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment);
12670
12671   pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12672   pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12673   tree_view->priv->anchor_column = column;
12674
12675   gtk_widget_size_request (GTK_WIDGET (cell_editable), &requisition);
12676
12677   PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12678
12679   if (requisition.height < cell_area->height)
12680     {
12681       gint diff = cell_area->height - requisition.height;
12682       pspp_sheet_view_put (tree_view,
12683                          GTK_WIDGET (cell_editable),
12684                          cell_area->x, cell_area->y + diff/2,
12685                          cell_area->width, requisition.height);
12686     }
12687   else
12688     {
12689       pspp_sheet_view_put (tree_view,
12690                          GTK_WIDGET (cell_editable),
12691                          cell_area->x, cell_area->y,
12692                          cell_area->width, cell_area->height);
12693     }
12694
12695   gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12696                                    (GdkEvent *)event);
12697
12698   gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12699   g_signal_connect (cell_editable, "remove-widget",
12700                     G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12701   if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12702       GTK_IS_BUTTON (cell_editable))
12703     {
12704       g_signal_connect (cell_editable, "button-press-event",
12705                         G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12706                         tree_view);
12707       g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12708                          GINT_TO_POINTER (row));
12709       g_signal_connect (cell_editable, "clicked",
12710                         G_CALLBACK (pspp_sheet_view_editable_clicked),
12711                         tree_view);
12712     }
12713
12714   pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12715                                             tree_view);
12716 }
12717
12718 void
12719 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12720                               gboolean     cancel_editing)
12721 {
12722   PsppSheetViewColumn *column;
12723   GtkCellRenderer *cell;
12724
12725   if (tree_view->priv->edited_column == NULL)
12726     return;
12727
12728   /*
12729    * This is very evil. We need to do this, because
12730    * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12731    * later on. If pspp_sheet_view_row_changed notices
12732    * tree_view->priv->edited_column != NULL, it'll call
12733    * pspp_sheet_view_stop_editing again. Bad things will happen then.
12734    *
12735    * Please read that again if you intend to modify anything here.
12736    */
12737
12738   column = tree_view->priv->edited_column;
12739   tree_view->priv->edited_column = NULL;
12740
12741   cell = _pspp_sheet_view_column_get_edited_cell (column);
12742   gtk_cell_renderer_stop_editing (cell, cancel_editing);
12743
12744   if (!cancel_editing)
12745     gtk_cell_editable_editing_done (column->editable_widget);
12746
12747   tree_view->priv->edited_column = column;
12748
12749   gtk_cell_editable_remove_widget (column->editable_widget);
12750 }
12751
12752
12753 /**
12754  * pspp_sheet_view_set_hover_selection:
12755  * @tree_view: a #PsppSheetView
12756  * @hover: %TRUE to enable hover selection mode
12757  *
12758  * Enables of disables the hover selection mode of @tree_view.
12759  * Hover selection makes the selected row follow the pointer.
12760  * Currently, this works only for the selection modes 
12761  * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12762  * 
12763  * Since: 2.6
12764  **/
12765 void     
12766 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12767                                    gboolean     hover)
12768 {
12769   hover = hover != FALSE;
12770
12771   if (hover != tree_view->priv->hover_selection)
12772     {
12773       tree_view->priv->hover_selection = hover;
12774
12775       g_object_notify (G_OBJECT (tree_view), "hover-selection");
12776     }
12777 }
12778
12779 /**
12780  * pspp_sheet_view_get_hover_selection:
12781  * @tree_view: a #PsppSheetView
12782  * 
12783  * Returns whether hover selection mode is turned on for @tree_view.
12784  * 
12785  * Return value: %TRUE if @tree_view is in hover selection mode
12786  *
12787  * Since: 2.6 
12788  **/
12789 gboolean 
12790 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12791 {
12792   return tree_view->priv->hover_selection;
12793 }
12794
12795 /**
12796  * pspp_sheet_view_set_rubber_banding:
12797  * @tree_view: a #PsppSheetView
12798  * @enable: %TRUE to enable rubber banding
12799  *
12800  * Enables or disables rubber banding in @tree_view.  If the selection mode is
12801  * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12802  * banding will allow the user to select multiple rows by dragging the mouse.
12803  * 
12804  * Since: 2.10
12805  **/
12806 void
12807 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12808                                   gboolean     enable)
12809 {
12810   enable = enable != FALSE;
12811
12812   if (enable != tree_view->priv->rubber_banding_enable)
12813     {
12814       tree_view->priv->rubber_banding_enable = enable;
12815
12816       g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12817     }
12818 }
12819
12820 /**
12821  * pspp_sheet_view_get_rubber_banding:
12822  * @tree_view: a #PsppSheetView
12823  * 
12824  * Returns whether rubber banding is turned on for @tree_view.  If the
12825  * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12826  * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12827  * select multiple rows by dragging the mouse.
12828  * 
12829  * Return value: %TRUE if rubber banding in @tree_view is enabled.
12830  *
12831  * Since: 2.10
12832  **/
12833 gboolean
12834 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12835 {
12836   return tree_view->priv->rubber_banding_enable;
12837 }
12838
12839 /**
12840  * pspp_sheet_view_is_rubber_banding_active:
12841  * @tree_view: a #PsppSheetView
12842  * 
12843  * Returns whether a rubber banding operation is currently being done
12844  * in @tree_view.
12845  *
12846  * Return value: %TRUE if a rubber banding operation is currently being
12847  * done in @tree_view.
12848  *
12849  * Since: 2.12
12850  **/
12851 gboolean
12852 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12853 {
12854   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12855
12856   if (tree_view->priv->rubber_banding_enable
12857       && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12858     return TRUE;
12859
12860   return FALSE;
12861 }
12862
12863 static void
12864 pspp_sheet_view_grab_notify (GtkWidget *widget,
12865                            gboolean   was_grabbed)
12866 {
12867   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12868
12869   tree_view->priv->in_grab = !was_grabbed;
12870
12871   if (!was_grabbed)
12872     {
12873       tree_view->priv->pressed_button = -1;
12874
12875       if (tree_view->priv->rubber_band_status)
12876         pspp_sheet_view_stop_rubber_band (tree_view);
12877     }
12878 }
12879
12880 static void
12881 pspp_sheet_view_state_changed (GtkWidget      *widget,
12882                              GtkStateType    previous_state)
12883 {
12884   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12885
12886   if (gtk_widget_get_realized (widget))
12887     {
12888       GtkStyle *style = gtk_widget_get_style (widget);
12889       gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]);
12890     }
12891
12892   gtk_widget_queue_draw (widget);
12893 }
12894
12895 /**
12896  * pspp_sheet_view_get_grid_lines:
12897  * @tree_view: a #PsppSheetView
12898  *
12899  * Returns which grid lines are enabled in @tree_view.
12900  *
12901  * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12902  * are enabled.
12903  *
12904  * Since: 2.10
12905  */
12906 PsppSheetViewGridLines
12907 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12908 {
12909   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12910
12911   return tree_view->priv->grid_lines;
12912 }
12913
12914 /**
12915  * pspp_sheet_view_set_grid_lines:
12916  * @tree_view: a #PsppSheetView
12917  * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12918  * enable.
12919  *
12920  * Sets which grid lines to draw in @tree_view.
12921  *
12922  * Since: 2.10
12923  */
12924 void
12925 pspp_sheet_view_set_grid_lines (PsppSheetView           *tree_view,
12926                               PsppSheetViewGridLines   grid_lines)
12927 {
12928   PsppSheetViewPrivate *priv;
12929   PsppSheetViewGridLines old_grid_lines;
12930
12931   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12932
12933   priv = tree_view->priv;
12934
12935   old_grid_lines = priv->grid_lines;
12936   priv->grid_lines = grid_lines;
12937   
12938   if (old_grid_lines != grid_lines)
12939     {
12940       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12941       
12942       g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12943     }
12944 }
12945
12946 /**
12947  * pspp_sheet_view_get_special_cells:
12948  * @tree_view: a #PsppSheetView
12949  *
12950  * Returns which grid lines are enabled in @tree_view.
12951  *
12952  * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12953  * the sheet view contain special cells.
12954  */
12955 PsppSheetViewSpecialCells
12956 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12957 {
12958   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12959
12960   return tree_view->priv->special_cells;
12961 }
12962
12963 /**
12964  * pspp_sheet_view_set_special_cells:
12965  * @tree_view: a #PsppSheetView
12966  * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12967  * the sheet view contain special cells.
12968  *
12969  * Sets whether rows in the sheet view contain special cells, controlling the
12970  * rendering of row selections.
12971  */
12972 void
12973 pspp_sheet_view_set_special_cells (PsppSheetView           *tree_view,
12974                               PsppSheetViewSpecialCells   special_cells)
12975 {
12976   PsppSheetViewPrivate *priv;
12977
12978   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12979
12980   priv = tree_view->priv;
12981
12982   if (priv->special_cells != special_cells)
12983     {
12984       priv->special_cells = special_cells;
12985       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12986       g_object_notify (G_OBJECT (tree_view), "special-cells");
12987     }
12988 }
12989
12990 int
12991 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12992 {
12993   /* XXX (re)calculate fixed_height if necessary */
12994   return tree_view->priv->fixed_height;
12995 }
12996
12997 void
12998 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12999                                   int fixed_height)
13000 {
13001   g_return_if_fail (fixed_height > 0);
13002
13003   if (tree_view->priv->fixed_height != fixed_height)
13004     {
13005       tree_view->priv->fixed_height = fixed_height;
13006       g_object_notify (G_OBJECT (tree_view), "fixed-height");
13007     }
13008   if (!tree_view->priv->fixed_height_set)
13009     {
13010       tree_view->priv->fixed_height_set = TRUE;
13011       g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
13012     }
13013 }
13014
13015 /**
13016  * pspp_sheet_view_set_tooltip_row:
13017  * @tree_view: a #PsppSheetView
13018  * @tooltip: a #GtkTooltip
13019  * @path: a #GtkTreePath
13020  *
13021  * Sets the tip area of @tooltip to be the area covered by the row at @path.
13022  * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13023  * See also gtk_tooltip_set_tip_area().
13024  *
13025  * Since: 2.12
13026  */
13027 void
13028 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
13029                                GtkTooltip  *tooltip,
13030                                GtkTreePath *path)
13031 {
13032   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13033   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13034
13035   pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
13036 }
13037
13038 /**
13039  * pspp_sheet_view_set_tooltip_cell:
13040  * @tree_view: a #PsppSheetView
13041  * @tooltip: a #GtkTooltip
13042  * @path: (allow-none): a #GtkTreePath or %NULL
13043  * @column: (allow-none): a #PsppSheetViewColumn or %NULL
13044  * @cell: (allow-none): a #GtkCellRenderer or %NULL
13045  *
13046  * Sets the tip area of @tooltip to the area @path, @column and @cell have
13047  * in common.  For example if @path is %NULL and @column is set, the tip
13048  * area will be set to the full area covered by @column.  See also
13049  * gtk_tooltip_set_tip_area().
13050  *
13051  * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13052  *
13053  * Since: 2.12
13054  */
13055 void
13056 pspp_sheet_view_set_tooltip_cell (PsppSheetView       *tree_view,
13057                                 GtkTooltip        *tooltip,
13058                                 GtkTreePath       *path,
13059                                 PsppSheetViewColumn *column,
13060                                 GtkCellRenderer   *cell)
13061 {
13062   GdkRectangle rect;
13063
13064   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13065   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13066   g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
13067   g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
13068
13069   /* Determine x values. */
13070   if (column && cell)
13071     {
13072       GdkRectangle tmp;
13073       gint start, width;
13074
13075       pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
13076       pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
13077
13078       pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13079                                                          tmp.x + start, 0,
13080                                                          &rect.x, NULL);
13081       rect.width = width;
13082     }
13083   else if (column)
13084     {
13085       GdkRectangle tmp;
13086
13087       pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
13088       pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13089                                                          tmp.x, 0,
13090                                                          &rect.x, NULL);
13091       rect.width = tmp.width;
13092     }
13093   else
13094     {
13095       GtkAllocation allocation;
13096       gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
13097       rect.x = 0;
13098       rect.width = allocation.width;
13099     }
13100
13101   /* Determine y values. */
13102   if (path)
13103     {
13104       GdkRectangle tmp;
13105
13106       pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
13107       pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13108                                                          0, tmp.y,
13109                                                          NULL, &rect.y);
13110       rect.height = tmp.height;
13111     }
13112   else
13113     {
13114       rect.y = 0;
13115       rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
13116     }
13117
13118   gtk_tooltip_set_tip_area (tooltip, &rect);
13119 }
13120
13121 /**
13122  * pspp_sheet_view_get_tooltip_context:
13123  * @tree_view: a #PsppSheetView
13124  * @x: the x coordinate (relative to widget coordinates)
13125  * @y: the y coordinate (relative to widget coordinates)
13126  * @keyboard_tip: whether this is a keyboard tooltip or not
13127  * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
13128  * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
13129  * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
13130  *
13131  * This function is supposed to be used in a #GtkWidget::query-tooltip
13132  * signal handler for #PsppSheetView.  The @x, @y and @keyboard_tip values
13133  * which are received in the signal handler, should be passed to this
13134  * function without modification.
13135  *
13136  * The return value indicates whether there is a tree view row at the given
13137  * coordinates (%TRUE) or not (%FALSE) for mouse tooltips.  For keyboard
13138  * tooltips the row returned will be the cursor row.  When %TRUE, then any of
13139  * @model, @path and @iter which have been provided will be set to point to
13140  * that row and the corresponding model.  @x and @y will always be converted
13141  * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
13142  *
13143  * Return value: whether or not the given tooltip context points to a row.
13144  *
13145  * Since: 2.12
13146  */
13147 gboolean
13148 pspp_sheet_view_get_tooltip_context (PsppSheetView   *tree_view,
13149                                    gint          *x,
13150                                    gint          *y,
13151                                    gboolean       keyboard_tip,
13152                                    GtkTreeModel **model,
13153                                    GtkTreePath  **path,
13154                                    GtkTreeIter   *iter)
13155 {
13156   GtkTreePath *tmppath = NULL;
13157
13158   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
13159   g_return_val_if_fail (x != NULL, FALSE);
13160   g_return_val_if_fail (y != NULL, FALSE);
13161
13162   if (keyboard_tip)
13163     {
13164       pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
13165
13166       if (!tmppath)
13167         return FALSE;
13168     }
13169   else
13170     {
13171       pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
13172                                                          x, y);
13173
13174       if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
13175                                           &tmppath, NULL, NULL, NULL))
13176         return FALSE;
13177     }
13178
13179   if (model)
13180     *model = pspp_sheet_view_get_model (tree_view);
13181
13182   if (iter)
13183     gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
13184                              iter, tmppath);
13185
13186   if (path)
13187     *path = tmppath;
13188   else
13189     gtk_tree_path_free (tmppath);
13190
13191   return TRUE;
13192 }
13193
13194 static gboolean
13195 pspp_sheet_view_set_tooltip_query_cb (GtkWidget  *widget,
13196                                     gint        x,
13197                                     gint        y,
13198                                     gboolean    keyboard_tip,
13199                                     GtkTooltip *tooltip,
13200                                     gpointer    data)
13201 {
13202   GValue value = { 0, };
13203   GValue transformed = { 0, };
13204   GtkTreeIter iter;
13205   GtkTreePath *path;
13206   GtkTreeModel *model;
13207   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
13208
13209   if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
13210                                           &x, &y,
13211                                           keyboard_tip,
13212                                           &model, &path, &iter))
13213     return FALSE;
13214
13215   gtk_tree_model_get_value (model, &iter,
13216                             tree_view->priv->tooltip_column, &value);
13217
13218   g_value_init (&transformed, G_TYPE_STRING);
13219
13220   if (!g_value_transform (&value, &transformed))
13221     {
13222       g_value_unset (&value);
13223       gtk_tree_path_free (path);
13224
13225       return FALSE;
13226     }
13227
13228   g_value_unset (&value);
13229
13230   if (!g_value_get_string (&transformed))
13231     {
13232       g_value_unset (&transformed);
13233       gtk_tree_path_free (path);
13234
13235       return FALSE;
13236     }
13237
13238   gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
13239   pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
13240
13241   gtk_tree_path_free (path);
13242   g_value_unset (&transformed);
13243
13244   return TRUE;
13245 }
13246
13247 /**
13248  * pspp_sheet_view_set_tooltip_column:
13249  * @tree_view: a #PsppSheetView
13250  * @column: an integer, which is a valid column number for @tree_view's model
13251  *
13252  * If you only plan to have simple (text-only) tooltips on full rows, you
13253  * can use this function to have #PsppSheetView handle these automatically
13254  * for you. @column should be set to the column in @tree_view's model
13255  * containing the tooltip texts, or -1 to disable this feature.
13256  *
13257  * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
13258  * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13259  *
13260  * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13261  * so &amp;, &lt;, etc have to be escaped in the text.
13262  *
13263  * Since: 2.12
13264  */
13265 void
13266 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13267                                   gint         column)
13268 {
13269   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13270
13271   if (column == tree_view->priv->tooltip_column)
13272     return;
13273
13274   if (column == -1)
13275     {
13276       g_signal_handlers_disconnect_by_func (tree_view,
13277                                             pspp_sheet_view_set_tooltip_query_cb,
13278                                             NULL);
13279       gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13280     }
13281   else
13282     {
13283       if (tree_view->priv->tooltip_column == -1)
13284         {
13285           g_signal_connect (tree_view, "query-tooltip",
13286                             G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13287           gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13288         }
13289     }
13290
13291   tree_view->priv->tooltip_column = column;
13292   g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13293 }
13294
13295 /**
13296  * pspp_sheet_view_get_tooltip_column:
13297  * @tree_view: a #PsppSheetView
13298  *
13299  * Returns the column of @tree_view's model which is being used for
13300  * displaying tooltips on @tree_view's rows.
13301  *
13302  * Return value: the index of the tooltip column that is currently being
13303  * used, or -1 if this is disabled.
13304  *
13305  * Since: 2.12
13306  */
13307 gint
13308 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13309 {
13310   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13311
13312   return tree_view->priv->tooltip_column;
13313 }
13314
13315 gboolean
13316 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13317                                   GValue                *return_accu,
13318                                   const GValue          *handler_return,
13319                                   gpointer               dummy)
13320 {
13321   gboolean continue_emission;
13322   gboolean signal_handled;
13323   
13324   signal_handled = g_value_get_boolean (handler_return);
13325   g_value_set_boolean (return_accu, signal_handled);
13326   continue_emission = !signal_handled;
13327   
13328   return continue_emission;
13329 }
13330
13331
13332 GType
13333 pspp_sheet_view_grid_lines_get_type (void)
13334 {
13335     static GType etype = 0;
13336     if (G_UNLIKELY(etype == 0)) {
13337         static const GEnumValue values[] = {
13338             { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13339             { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13340             { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13341             { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13342             { 0, NULL, NULL }
13343         };
13344         etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13345     }
13346     return etype;
13347 }
13348
13349 GType
13350 pspp_sheet_view_special_cells_get_type (void)
13351 {
13352     static GType etype = 0;
13353     if (G_UNLIKELY(etype == 0)) {
13354         static const GEnumValue values[] = {
13355             { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13356             { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13357             { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13358             { 0, NULL, NULL }
13359         };
13360         etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);
13361     }
13362     return etype;
13363 }