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