cbc14efd926e1cfd8c11e0499988acb19645368b
[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 (column->window)
1983         gdk_window_move_resize (column->window,
1984                                 col_allocation.x + (rtl ? 0 : col_allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
1985                                 col_allocation.y,
1986                                 TREE_VIEW_DRAG_WIDTH, col_allocation.height);
1987     }
1988
1989   /* We change the width here.  The user might have been resizing columns,
1990    * so the total width of the tree view changes.
1991    */
1992   tree_view->priv->width = width;
1993   if (width_changed)
1994     *width_changed = TRUE;
1995
1996   if (column_changed)
1997     gtk_widget_queue_draw (GTK_WIDGET (tree_view));
1998 }
1999
2000 static void
2001 pspp_sheet_view_size_allocate (GtkWidget     *widget,
2002                              GtkAllocation *allocation)
2003 {
2004   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2005   GList *tmp_list;
2006   gboolean width_changed = FALSE;
2007   GtkAllocation old_allocation;
2008   gtk_widget_get_allocation (widget, &old_allocation);
2009
2010   if (allocation->width != old_allocation.width)
2011     width_changed = TRUE;
2012
2013
2014   gtk_widget_set_allocation (widget, allocation);
2015
2016   tmp_list = tree_view->priv->children;
2017
2018   while (tmp_list)
2019     {
2020       GtkAllocation allocation;
2021
2022       PsppSheetViewChild *child = tmp_list->data;
2023       tmp_list = tmp_list->next;
2024
2025       /* totally ignore our child's requisition */
2026       allocation.x = child->x;
2027       allocation.y = child->y;
2028       allocation.width = child->width;
2029       allocation.height = child->height;
2030       gtk_widget_size_allocate (child->widget, &allocation);
2031     }
2032
2033   /* We size-allocate the columns first because the width of the
2034    * tree view (used in updating the adjustments below) might change.
2035    */
2036   pspp_sheet_view_size_allocate_columns (widget, &width_changed);
2037
2038   gtk_adjustment_set_page_size (tree_view->priv->hadjustment, allocation->width);
2039   gtk_adjustment_set_page_increment (tree_view->priv->hadjustment, allocation->width * 0.9);
2040   gtk_adjustment_set_step_increment (tree_view->priv->hadjustment, allocation->width * 0.1);
2041   gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2042   gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment), tree_view->priv->width));
2043
2044   if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)   
2045     {
2046       if (allocation->width < tree_view->priv->width)
2047         {
2048           if (tree_view->priv->init_hadjust_value)
2049             {
2050               gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2051               tree_view->priv->init_hadjust_value = FALSE;
2052             }
2053           else if (allocation->width != old_allocation.width)
2054             {
2055               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));
2056             }
2057           else
2058             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));
2059         }
2060       else
2061         {
2062           gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2063           tree_view->priv->init_hadjust_value = TRUE;
2064         }
2065     }
2066   else
2067     if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width)
2068       gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2069
2070   gtk_adjustment_changed (tree_view->priv->hadjustment);
2071
2072   gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2073   gtk_adjustment_set_step_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.1);
2074   gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.9);
2075   gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
2076   gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->vadjustment), tree_view->priv->height));
2077
2078   gtk_adjustment_changed (tree_view->priv->vadjustment);
2079
2080   /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2081   if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
2082     gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2083   else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
2084     gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2085                               tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
2086   else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2087     pspp_sheet_view_top_row_to_dy (tree_view);
2088   else
2089     pspp_sheet_view_dy_to_top_row (tree_view);
2090   
2091   if (gtk_widget_get_realized (widget))
2092     {
2093       gdk_window_move_resize (gtk_widget_get_window (widget),
2094                               allocation->x, allocation->y,
2095                               allocation->width, allocation->height);
2096       gdk_window_move_resize (tree_view->priv->header_window,
2097                               - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2098                               0,
2099                               MAX (tree_view->priv->width, allocation->width),
2100                               tree_view->priv->header_height);
2101       gdk_window_move_resize (tree_view->priv->bin_window,
2102                               - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2103                               TREE_VIEW_HEADER_HEIGHT (tree_view),
2104                               MAX (tree_view->priv->width, allocation->width),
2105                               allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2106     }
2107
2108   if (tree_view->priv->row_count == 0)
2109     invalidate_empty_focus (tree_view);
2110
2111   if (gtk_widget_get_realized (widget))
2112     {
2113       gboolean has_expand_column = FALSE;
2114       for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2115         {
2116           if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2117             {
2118               has_expand_column = TRUE;
2119               break;
2120             }
2121         }
2122
2123       /* This little hack only works if we have an LTR locale, and no column has the  */
2124       if (width_changed)
2125         {
2126           if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2127               ! has_expand_column)
2128             invalidate_last_column (tree_view);
2129           else
2130             gtk_widget_queue_draw (widget);
2131         }
2132     }
2133 }
2134
2135 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2136 static void
2137 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2138 {
2139   GtkWidget *widget = GTK_WIDGET (tree_view);
2140
2141   if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2142     gtk_widget_grab_focus (widget);
2143   PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2144 }
2145
2146 gboolean
2147 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2148                                   int node)
2149 {
2150   return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2151 }
2152
2153 void
2154 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2155                              int node)
2156 {
2157   range_tower_set1 (tree_view->priv->selected, node, 1);
2158 }
2159
2160 void
2161 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2162                                int node)
2163 {
2164   range_tower_set0 (tree_view->priv->selected, node, 1);
2165 }
2166
2167 gint
2168 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2169                            gint node)
2170 {
2171   return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2172 }
2173
2174 gint
2175 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2176                            gint node)
2177 {
2178   return node > 0 ? node - 1 : -1;
2179 }
2180
2181 static gboolean
2182 all_columns_selected (PsppSheetView *tree_view)
2183 {
2184   GList *list;
2185
2186   for (list = tree_view->priv->columns; list; list = list->next)
2187     {
2188       PsppSheetViewColumn *column = list->data;
2189       if (column->selectable && !column->selected)
2190         return FALSE;
2191     }
2192
2193   return TRUE;
2194 }
2195
2196 static gboolean
2197 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2198                                   gint node,
2199                                   PsppSheetViewColumn *column,
2200                                   GdkEventButton *event)
2201 {
2202   PsppSheetSelection *selection;
2203   PsppSheetSelectionMode mode;
2204   GtkTreePath *path;
2205   gboolean update_anchor;
2206   gboolean handled;
2207   guint modifiers;
2208
2209   g_return_val_if_fail (tree_view != NULL, FALSE);
2210   g_return_val_if_fail (column != NULL, FALSE);
2211
2212   selection = tree_view->priv->selection;
2213   mode = pspp_sheet_selection_get_mode (selection);
2214   if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2215     return FALSE;
2216
2217   if (!column->row_head)
2218     return FALSE;
2219
2220   if (event)
2221     {
2222       modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2223       if (event->type != GDK_BUTTON_PRESS
2224           || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2225         return FALSE;
2226     }
2227   else
2228     modifiers = 0;
2229
2230   path = gtk_tree_path_new_from_indices (node, -1);
2231   if (event == NULL)
2232     {
2233       pspp_sheet_selection_unselect_all (selection);
2234       pspp_sheet_selection_select_path (selection, path);
2235       pspp_sheet_selection_select_all_columns (selection);
2236       update_anchor = TRUE;
2237       handled = TRUE;
2238     }
2239   else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2240     {
2241       if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2242           || !all_columns_selected (tree_view))
2243         {
2244           pspp_sheet_selection_unselect_all (selection);
2245           pspp_sheet_selection_select_path (selection, path);
2246           pspp_sheet_selection_select_all_columns (selection);
2247           update_anchor = TRUE;
2248           handled = FALSE;
2249         }
2250       else
2251         update_anchor = handled = FALSE;
2252     }
2253   else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2254            && modifiers == GDK_CONTROL_MASK)
2255     {
2256       if (!all_columns_selected (tree_view))
2257         {
2258           pspp_sheet_selection_unselect_all (selection);
2259           pspp_sheet_selection_select_all_columns (selection);
2260         }
2261
2262       if (pspp_sheet_selection_path_is_selected (selection, path))
2263         pspp_sheet_selection_unselect_path (selection, path);
2264       else
2265         pspp_sheet_selection_select_path (selection, path);
2266       update_anchor = TRUE;
2267       handled = TRUE;
2268     }
2269   else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2270            && modifiers == GDK_SHIFT_MASK)
2271     {
2272       GtkTreeRowReference *anchor = tree_view->priv->anchor;
2273       GtkTreePath *anchor_path;
2274
2275       if (all_columns_selected (tree_view)
2276           && gtk_tree_row_reference_valid (anchor))
2277         {
2278           update_anchor = FALSE;
2279           anchor_path = gtk_tree_row_reference_get_path (anchor);
2280         }
2281       else
2282         {
2283           update_anchor = TRUE;
2284           anchor_path = gtk_tree_path_copy (path);
2285         }
2286
2287       pspp_sheet_selection_unselect_all (selection);
2288       pspp_sheet_selection_select_range (selection, anchor_path, path);
2289       pspp_sheet_selection_select_all_columns (selection);
2290
2291       gtk_tree_path_free (anchor_path);
2292
2293       handled = TRUE;
2294     }
2295   else
2296     update_anchor = handled = FALSE;
2297
2298   if (update_anchor)
2299     {
2300       if (tree_view->priv->anchor)
2301         gtk_tree_row_reference_free (tree_view->priv->anchor);
2302       tree_view->priv->anchor =
2303         gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2304                                           tree_view->priv->model,
2305                                           path);
2306     }
2307
2308   gtk_tree_path_free (path);
2309   return handled;
2310 }
2311
2312 static gboolean
2313 find_click (PsppSheetView *tree_view,
2314             gint x, gint y,
2315             gint *node,
2316             PsppSheetViewColumn **column,
2317             GdkRectangle *background_area,
2318             GdkRectangle *cell_area)
2319 {
2320   gint y_offset;
2321   gboolean rtl;
2322   GList *list;
2323   gint new_y;
2324
2325   /* find the node that was clicked */
2326   new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2327   if (new_y < 0)
2328     new_y = 0;
2329   y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2330
2331   if (*node < 0)
2332     return FALSE;
2333
2334   background_area->y = y_offset + y;
2335   background_area->height = ROW_HEIGHT (tree_view);
2336   background_area->x = 0;
2337
2338   /* Let the column have a chance at selecting it. */
2339   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2340   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2341        list; list = (rtl ? list->prev : list->next))
2342     {
2343       PsppSheetViewColumn *candidate = list->data;
2344
2345       if (!candidate->visible)
2346         continue;
2347
2348       background_area->width = candidate->width;
2349       if ((background_area->x > x) ||
2350           (background_area->x + background_area->width <= x))
2351         {
2352           background_area->x += background_area->width;
2353           continue;
2354         }
2355
2356       /* we found the focus column */
2357
2358       pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2359                                         TRUE, cell_area);
2360       *column = candidate;
2361       return TRUE;
2362     }
2363
2364   return FALSE;
2365 }
2366
2367 static gboolean
2368 pspp_sheet_view_button_press (GtkWidget      *widget,
2369                             GdkEventButton *event)
2370 {
2371   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2372   GList *list;
2373   PsppSheetViewColumn *column = NULL;
2374   gint i;
2375   GdkRectangle background_area;
2376   GdkRectangle cell_area;
2377   gboolean rtl;
2378
2379   rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2380   pspp_sheet_view_stop_editing (tree_view, FALSE);
2381
2382
2383   /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2384    * we're done handling the button press.
2385    */
2386
2387   if (event->window == tree_view->priv->bin_window)
2388     {
2389       int node;
2390       GtkTreePath *path;
2391       gint dval;
2392       gint pre_val, aft_val;
2393       PsppSheetViewColumn *column = NULL;
2394       GtkCellRenderer *focus_cell = NULL;
2395       gboolean row_double_click = FALSE;
2396
2397       /* Empty tree? */
2398       if (tree_view->priv->row_count == 0)
2399         {
2400           grab_focus_and_unset_draw_keyfocus (tree_view);
2401           return TRUE;
2402         }
2403
2404       if (!find_click (tree_view, event->x, event->y, &node, &column,
2405                        &background_area, &cell_area))
2406         {
2407           grab_focus_and_unset_draw_keyfocus (tree_view);
2408           return FALSE;
2409         }
2410
2411       tree_view->priv->focus_column = column;
2412
2413       if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2414         return TRUE;
2415
2416       /* select */
2417       pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2418
2419       path = _pspp_sheet_view_find_path (tree_view, node);
2420
2421       /* we only handle selection modifications on the first button press
2422        */
2423       if (event->type == GDK_BUTTON_PRESS)
2424         {
2425           PsppSheetSelectionMode mode = 0;
2426
2427           if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2428             mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
2429           if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2430             mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
2431
2432           focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2433           if (focus_cell)
2434             pspp_sheet_view_column_focus_cell (column, focus_cell);
2435
2436           if (event->state & GDK_CONTROL_MASK)
2437             {
2438               pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode);
2439               pspp_sheet_view_real_toggle_cursor_row (tree_view);
2440             }
2441           else if (event->state & GDK_SHIFT_MASK)
2442             {
2443               pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
2444               pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode);
2445             }
2446           else
2447             {
2448               pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
2449             }
2450
2451           if (tree_view->priv->anchor_column == NULL ||
2452               !(event->state & GDK_SHIFT_MASK))
2453             tree_view->priv->anchor_column = column;
2454           pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2455           pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2456                                                     tree_view->priv->anchor_column,
2457                                                     column);
2458         }
2459
2460       /* the treeview may have been scrolled because of _set_cursor,
2461        * correct here
2462        */
2463
2464       aft_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2465       dval = pre_val - aft_val;
2466
2467       cell_area.y += dval;
2468       background_area.y += dval;
2469
2470       /* Save press to possibly begin a drag
2471        */
2472       if (!tree_view->priv->in_grab &&
2473           tree_view->priv->pressed_button < 0)
2474         {
2475           tree_view->priv->pressed_button = event->button;
2476           tree_view->priv->press_start_x = event->x;
2477           tree_view->priv->press_start_y = event->y;
2478           tree_view->priv->press_start_node = node;
2479
2480           if (tree_view->priv->rubber_banding_enable
2481               && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2482                   tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2483             {
2484               tree_view->priv->press_start_y += tree_view->priv->dy;
2485               tree_view->priv->rubber_band_x = event->x;
2486               tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2487               tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2488
2489               if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2490                 tree_view->priv->rubber_band_ctrl = TRUE;
2491               if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2492                 tree_view->priv->rubber_band_shift = TRUE;
2493
2494             }
2495         }
2496
2497       /* Test if a double click happened on the same row. */
2498       if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2499         {
2500           int double_click_time, double_click_distance;
2501
2502           g_object_get (gtk_settings_get_for_screen (
2503                           gtk_widget_get_screen (widget)),
2504                         "gtk-double-click-time", &double_click_time,
2505                         "gtk-double-click-distance", &double_click_distance,
2506                         NULL);
2507
2508           /* Same conditions as _gdk_event_button_generate */
2509           if (tree_view->priv->last_button_x != -1 &&
2510               (event->time < tree_view->priv->last_button_time + double_click_time) &&
2511               (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2512               (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2513             {
2514               /* We do no longer compare paths of this row and the
2515                * row clicked previously.  We use the double click
2516                * distance to decide whether this is a valid click,
2517                * allowing the mouse to slightly move over another row.
2518                */
2519               row_double_click = TRUE;
2520
2521               tree_view->priv->last_button_time = 0;
2522               tree_view->priv->last_button_x = -1;
2523               tree_view->priv->last_button_y = -1;
2524             }
2525           else
2526             {
2527               tree_view->priv->last_button_time = event->time;
2528               tree_view->priv->last_button_x = event->x;
2529               tree_view->priv->last_button_y = event->y;
2530             }
2531         }
2532
2533       if (row_double_click)
2534         {
2535           gtk_grab_remove (widget);
2536           pspp_sheet_view_row_activated (tree_view, path, column);
2537
2538           if (tree_view->priv->pressed_button == event->button)
2539             tree_view->priv->pressed_button = -1;
2540         }
2541
2542       gtk_tree_path_free (path);
2543
2544       /* If we activated the row through a double click we don't want to grab
2545        * focus back, as moving focus to another widget is pretty common.
2546        */
2547       if (!row_double_click)
2548         grab_focus_and_unset_draw_keyfocus (tree_view);
2549
2550       return TRUE;
2551     }
2552
2553   /* We didn't click in the window.  Let's check to see if we clicked on a column resize window.
2554    */
2555   for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2556     {
2557       column = list->data;
2558       if (event->window == column->window &&
2559           column->resizable &&
2560           column->window)
2561         {
2562           gpointer drag_data;
2563
2564           if (gdk_pointer_grab (column->window, FALSE,
2565                                 GDK_POINTER_MOTION_HINT_MASK |
2566                                 GDK_BUTTON1_MOTION_MASK |
2567                                 GDK_BUTTON_RELEASE_MASK,
2568                                 NULL, NULL, event->time))
2569             return FALSE;
2570
2571           gtk_grab_add (widget);
2572           PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2573           column->resized_width = column->width;
2574
2575           /* block attached dnd signal handler */
2576           drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2577           if (drag_data)
2578             g_signal_handlers_block_matched (widget,
2579                                              G_SIGNAL_MATCH_DATA,
2580                                              0, 0, NULL, NULL,
2581                                              drag_data);
2582
2583           tree_view->priv->drag_pos = i;
2584           tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2585
2586           if (!gtk_widget_has_focus (widget))
2587             gtk_widget_grab_focus (widget);
2588
2589           return TRUE;
2590         }
2591     }
2592   return FALSE;
2593 }
2594
2595 /* GtkWidget::button_release_event helper */
2596 static gboolean
2597 pspp_sheet_view_button_release_drag_column (GtkWidget      *widget,
2598                                           GdkEventButton *event)
2599 {
2600   PsppSheetView *tree_view;
2601   GList *l;
2602   gboolean rtl;
2603
2604   tree_view = PSPP_SHEET_VIEW (widget);
2605
2606   rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2607   gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2608   gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2609
2610   /* Move the button back */
2611   g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2612
2613   g_object_ref (tree_view->priv->drag_column->button);
2614   gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2615   gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2616   gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2617   g_object_unref (tree_view->priv->drag_column->button);
2618   gtk_widget_queue_resize (widget);
2619   if (tree_view->priv->drag_column->resizable)
2620     {
2621       gdk_window_raise (tree_view->priv->drag_column->window);
2622       gdk_window_show (tree_view->priv->drag_column->window);
2623     }
2624   else
2625     gdk_window_hide (tree_view->priv->drag_column->window);
2626
2627   gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2628
2629   if (rtl)
2630     {
2631       if (tree_view->priv->cur_reorder &&
2632           tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2633         pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2634                                          tree_view->priv->cur_reorder->right_column);
2635     }
2636   else
2637     {
2638       if (tree_view->priv->cur_reorder &&
2639           tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2640         pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2641                                          tree_view->priv->cur_reorder->left_column);
2642     }
2643   tree_view->priv->drag_column = NULL;
2644   gdk_window_hide (tree_view->priv->drag_window);
2645
2646   for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2647     g_slice_free (PsppSheetViewColumnReorder, l->data);
2648   g_list_free (tree_view->priv->column_drag_info);
2649   tree_view->priv->column_drag_info = NULL;
2650   tree_view->priv->cur_reorder = NULL;
2651
2652   if (tree_view->priv->drag_highlight_window)
2653     gdk_window_hide (tree_view->priv->drag_highlight_window);
2654
2655   /* Reset our flags */
2656   tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2657   PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2658
2659   return TRUE;
2660 }
2661
2662 /* GtkWidget::button_release_event helper */
2663 static gboolean
2664 pspp_sheet_view_button_release_column_resize (GtkWidget      *widget,
2665                                             GdkEventButton *event)
2666 {
2667   PsppSheetView *tree_view;
2668   gpointer drag_data;
2669
2670   tree_view = PSPP_SHEET_VIEW (widget);
2671
2672   tree_view->priv->drag_pos = -1;
2673
2674   /* unblock attached dnd signal handler */
2675   drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2676   if (drag_data)
2677     g_signal_handlers_unblock_matched (widget,
2678                                        G_SIGNAL_MATCH_DATA,
2679                                        0, 0, NULL, NULL,
2680                                        drag_data);
2681
2682   PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2683   gtk_grab_remove (widget);
2684   gdk_display_pointer_ungrab (gdk_window_get_display (event->window),
2685                               event->time);
2686   return TRUE;
2687 }
2688
2689 static gboolean
2690 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2691                                      GdkEventButton *event)
2692 {
2693   GtkCellEditable *cell_editable;
2694   gchar *path_string;
2695   GtkTreePath *path;
2696   gint left, right;
2697   GtkTreeIter iter;
2698   PsppSheetViewColumn *column;
2699   GdkRectangle background_area;
2700   GdkRectangle cell_area;
2701   GdkRectangle area;
2702   guint modifiers;
2703   guint flags;
2704   int node;
2705
2706   if (event->window != tree_view->priv->bin_window)
2707     return FALSE;
2708
2709   /* Ignore a released button, if that button wasn't depressed */
2710   if (tree_view->priv->pressed_button != event->button)
2711     return FALSE;
2712
2713   if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2714                    &cell_area))
2715     return FALSE;
2716
2717   /* decide if we edit */
2718   path = _pspp_sheet_view_find_path (tree_view, node);
2719   modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2720   if (event->button != 1 || modifiers)
2721     return FALSE;
2722
2723   gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2724   pspp_sheet_view_column_cell_set_cell_data (column,
2725                                              tree_view->priv->model,
2726                                              &iter);
2727
2728   if (!pspp_sheet_view_column_get_quick_edit (column)
2729       && _pspp_sheet_view_column_has_editable_cell (column))
2730     return FALSE;
2731
2732   flags = 0;                    /* FIXME: get the right flags */
2733   path_string = gtk_tree_path_to_string (path);
2734
2735   if (!_pspp_sheet_view_column_cell_event (column,
2736                                            &cell_editable,
2737                                            (GdkEvent *)event,
2738                                            path_string,
2739                                            &background_area,
2740                                            &cell_area, flags))
2741     return FALSE;
2742
2743   if (cell_editable == NULL)
2744     return FALSE;
2745
2746   pspp_sheet_view_real_set_cursor (tree_view, path,
2747                                    TRUE, TRUE, 0); /* XXX mode? */
2748   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2749
2750   area = cell_area;
2751   _pspp_sheet_view_column_get_neighbor_sizes (
2752     column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2753
2754   area.x += left;
2755   area.width -= right + left;
2756
2757   pspp_sheet_view_real_start_editing (tree_view,
2758                                       column,
2759                                       path,
2760                                       cell_editable,
2761                                       &area,
2762                                       (GdkEvent *)event,
2763                                       flags);
2764   g_free (path_string);
2765   gtk_tree_path_free (path);
2766   return TRUE;
2767 }
2768
2769 static gboolean
2770 pspp_sheet_view_button_release (GtkWidget      *widget,
2771                               GdkEventButton *event)
2772 {
2773   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2774
2775   pspp_sheet_view_stop_editing (tree_view, FALSE);
2776   if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2777       && pspp_sheet_view_button_release_edit (tree_view, event))
2778     {
2779       if (tree_view->priv->pressed_button == event->button)
2780         tree_view->priv->pressed_button = -1;
2781
2782       tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2783       return TRUE;
2784     }
2785
2786   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2787     return pspp_sheet_view_button_release_drag_column (widget, event);
2788
2789   if (tree_view->priv->rubber_band_status)
2790     pspp_sheet_view_stop_rubber_band (tree_view);
2791
2792   if (tree_view->priv->pressed_button == event->button)
2793     tree_view->priv->pressed_button = -1;
2794
2795   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2796     return pspp_sheet_view_button_release_column_resize (widget, event);
2797
2798   return FALSE;
2799 }
2800
2801 static gboolean
2802 pspp_sheet_view_grab_broken (GtkWidget          *widget,
2803                            GdkEventGrabBroken *event)
2804 {
2805   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2806
2807   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2808     pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2809
2810   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2811     pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2812
2813   return TRUE;
2814 }
2815
2816 /* GtkWidget::motion_event function set.
2817  */
2818
2819 static void
2820 do_prelight (PsppSheetView *tree_view,
2821              int node,
2822              /* these are in bin_window coords */
2823              gint         x,
2824              gint         y)
2825 {
2826   int prev_node = tree_view->priv->prelight_node;
2827
2828   if (prev_node != node)
2829     {
2830       tree_view->priv->prelight_node = node;
2831
2832       if (prev_node >= 0)
2833         _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2834
2835       if (node >= 0)
2836         _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2837     }
2838 }
2839
2840
2841 static void
2842 prelight_or_select (PsppSheetView *tree_view,
2843                     int node,
2844                     /* these are in bin_window coords */
2845                     gint         x,
2846                     gint         y)
2847 {
2848   PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2849   
2850   if (tree_view->priv->hover_selection &&
2851       (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2852       !(tree_view->priv->edited_column &&
2853         tree_view->priv->edited_column->editable_widget))
2854     {
2855       if (node >= 0)
2856         {
2857           if (!pspp_sheet_view_node_is_selected (tree_view, node))
2858             {
2859               GtkTreePath *path;
2860               
2861               path = _pspp_sheet_view_find_path (tree_view, node);
2862               pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2863               if (pspp_sheet_view_node_is_selected (tree_view, node))
2864                 {
2865                   PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2866                   pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */
2867                 }
2868               gtk_tree_path_free (path);
2869             }
2870         }
2871
2872       else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2873         pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2874     }
2875
2876     do_prelight (tree_view, node, x, y);
2877 }
2878
2879 static void
2880 ensure_unprelighted (PsppSheetView *tree_view)
2881 {
2882   do_prelight (tree_view,
2883                -1,
2884                -1000, -1000); /* coords not possibly over an arrow */
2885
2886   g_assert (tree_view->priv->prelight_node < 0);
2887 }
2888
2889 static void
2890 update_prelight (PsppSheetView *tree_view,
2891                  gint         x,
2892                  gint         y)
2893 {
2894   int new_y;
2895   int node;
2896
2897   if (tree_view->priv->row_count == 0)
2898     return;
2899
2900   if (x == -10000)
2901     {
2902       ensure_unprelighted (tree_view);
2903       return;
2904     }
2905
2906   new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2907   if (new_y < 0)
2908     new_y = 0;
2909
2910   pspp_sheet_view_find_offset (tree_view, new_y, &node);
2911
2912   if (node >= 0)
2913     prelight_or_select (tree_view, node, x, y);
2914 }
2915
2916
2917
2918
2919 /* Our motion arrow is either a box (in the case of the original spot)
2920  * or an arrow.  It is expander_size wide.
2921  */
2922 /*
2923  * 11111111111111
2924  * 01111111111110
2925  * 00111111111100
2926  * 00011111111000
2927  * 00001111110000
2928  * 00000111100000
2929  * 00000111100000
2930  * 00000111100000
2931  * ~ ~ ~ ~ ~ ~ ~
2932  * 00000111100000
2933  * 00000111100000
2934  * 00000111100000
2935  * 00001111110000
2936  * 00011111111000
2937  * 00111111111100
2938  * 01111111111110
2939  * 11111111111111
2940  */
2941
2942 static void
2943 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2944 {
2945 #if GTK3_TRANSITION
2946   PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2947   GtkWidget *widget = GTK_WIDGET (tree_view);
2948   GdkBitmap *mask = NULL;
2949   gint x;
2950   gint y;
2951   gint width;
2952   gint height;
2953   gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2954   GdkWindowAttr attributes;
2955   guint attributes_mask;
2956
2957   if (!reorder ||
2958       reorder->left_column == tree_view->priv->drag_column ||
2959       reorder->right_column == tree_view->priv->drag_column)
2960     arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2961   else if (reorder->left_column || reorder->right_column)
2962     {
2963       GdkRectangle visible_rect;
2964       pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2965       if (reorder->left_column)
2966         x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2967       else
2968         x = reorder->right_column->allocation.x;
2969
2970       if (x < visible_rect.x)
2971         arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2972       else if (x > visible_rect.x + visible_rect.width)
2973         arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2974       else
2975         arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2976     }
2977
2978   /* We want to draw the rectangle over the initial location. */
2979   if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2980     {
2981       GdkGC *gc;
2982       GdkColor col;
2983
2984       if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2985         {
2986           if (tree_view->priv->drag_highlight_window)
2987             {
2988               gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2989                                         NULL);
2990               gdk_window_destroy (tree_view->priv->drag_highlight_window);
2991             }
2992
2993           attributes.window_type = GDK_WINDOW_CHILD;
2994           attributes.wclass = GDK_INPUT_OUTPUT;
2995           attributes.x = tree_view->priv->drag_column_x;
2996           attributes.y = 0;
2997           width = attributes.width = tree_view->priv->drag_column->allocation.width;
2998           height = attributes.height = tree_view->priv->drag_column->allocation.height;
2999           attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3000           attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3001           attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3002           attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3003           tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3004           gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3005
3006           mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3007           gc = gdk_gc_new (mask);
3008           col.pixel = 1;
3009           gdk_gc_set_foreground (gc, &col);
3010           gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3011           col.pixel = 0;
3012           gdk_gc_set_foreground(gc, &col);
3013           gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
3014           g_object_unref (gc);
3015
3016           gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3017                                          mask, 0, 0);
3018           if (mask) g_object_unref (mask);
3019           tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3020         }
3021     }
3022   else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3023     {
3024       gint i, j = 1;
3025       GdkGC *gc;
3026       GdkColor col;
3027
3028       width = tree_view->priv->expander_size;
3029
3030       /* Get x, y, width, height of arrow */
3031       gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3032       if (reorder->left_column)
3033         {
3034           x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
3035           height = reorder->left_column->allocation.height;
3036         }
3037       else
3038         {
3039           x += reorder->right_column->allocation.x - width/2;
3040           height = reorder->right_column->allocation.height;
3041         }
3042       y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3043       height += tree_view->priv->expander_size;
3044
3045       /* Create the new window */
3046       if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3047         {
3048           if (tree_view->priv->drag_highlight_window)
3049             {
3050               gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3051                                         NULL);
3052               gdk_window_destroy (tree_view->priv->drag_highlight_window);
3053             }
3054
3055           attributes.window_type = GDK_WINDOW_TEMP;
3056           attributes.wclass = GDK_INPUT_OUTPUT;
3057           attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3058           attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3059           attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3060           attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3061           attributes.x = x;
3062           attributes.y = y;
3063           attributes.width = width;
3064           attributes.height = height;
3065           tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3066                                                                    &attributes, attributes_mask);
3067           gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3068
3069           mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3070           gc = gdk_gc_new (mask);
3071           col.pixel = 1;
3072           gdk_gc_set_foreground (gc, &col);
3073           gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3074
3075           /* Draw the 2 arrows as per above */
3076           col.pixel = 0;
3077           gdk_gc_set_foreground (gc, &col);
3078           for (i = 0; i < width; i ++)
3079             {
3080               if (i == (width/2 - 1))
3081                 continue;
3082               gdk_draw_line (mask, gc, i, j, i, height - j);
3083               if (i < (width/2 - 1))
3084                 j++;
3085               else
3086                 j--;
3087             }
3088           g_object_unref (gc);
3089           gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3090                                          mask, 0, 0);
3091           if (mask) g_object_unref (mask);
3092         }
3093
3094       tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3095       gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3096     }
3097   else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3098            arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3099     {
3100       gint i, j = 1;
3101       GdkGC *gc;
3102       GdkColor col;
3103
3104       width = tree_view->priv->expander_size;
3105
3106       /* Get x, y, width, height of arrow */
3107       width = width/2; /* remember, the arrow only takes half the available width */
3108       gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
3109       if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3110         x += widget->allocation.width - width;
3111
3112       if (reorder->left_column)
3113         height = reorder->left_column->allocation.height;
3114       else
3115         height = reorder->right_column->allocation.height;
3116
3117       y -= tree_view->priv->expander_size;
3118       height += 2*tree_view->priv->expander_size;
3119
3120       /* Create the new window */
3121       if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3122           tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3123         {
3124           if (tree_view->priv->drag_highlight_window)
3125             {
3126               gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3127                                         NULL);
3128               gdk_window_destroy (tree_view->priv->drag_highlight_window);
3129             }
3130
3131           attributes.window_type = GDK_WINDOW_TEMP;
3132           attributes.wclass = GDK_INPUT_OUTPUT;
3133           attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3134           attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3135           attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3136           attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3137           attributes.x = x;
3138           attributes.y = y;
3139           attributes.width = width;
3140           attributes.height = height;
3141           tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3142           gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3143
3144           mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3145           gc = gdk_gc_new (mask);
3146           col.pixel = 1;
3147           gdk_gc_set_foreground (gc, &col);
3148           gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3149
3150           /* Draw the 2 arrows as per above */
3151           col.pixel = 0;
3152           gdk_gc_set_foreground (gc, &col);
3153           j = tree_view->priv->expander_size;
3154           for (i = 0; i < width; i ++)
3155             {
3156               gint k;
3157               if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3158                 k = width - i - 1;
3159               else
3160                 k = i;
3161               gdk_draw_line (mask, gc, k, j, k, height - j);
3162               gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3163               gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3164               j--;
3165             }
3166           g_object_unref (gc);
3167           gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3168                                          mask, 0, 0);
3169           if (mask) g_object_unref (mask);
3170         }
3171
3172       tree_view->priv->drag_column_window_state = arrow_type;
3173       gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3174    }
3175   else
3176     {
3177       g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3178       gdk_window_hide (tree_view->priv->drag_highlight_window);
3179       return;
3180     }
3181
3182   gdk_window_show (tree_view->priv->drag_highlight_window);
3183   gdk_window_raise (tree_view->priv->drag_highlight_window);
3184 #endif
3185 }
3186
3187 static gboolean
3188 pspp_sheet_view_motion_resize_column (GtkWidget      *widget,
3189                                     GdkEventMotion *event)
3190 {
3191   gint x;
3192   gint new_width;
3193   PsppSheetViewColumn *column;
3194   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3195
3196   column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3197
3198   if (event->is_hint || event->window != gtk_widget_get_window (widget))
3199     gtk_widget_get_pointer (widget, &x, NULL);
3200   else
3201     x = event->x;
3202
3203   if (tree_view->priv->hadjustment)
3204     x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
3205
3206   new_width = pspp_sheet_view_new_column_width (tree_view,
3207                                               tree_view->priv->drag_pos, &x);
3208   if (x != tree_view->priv->x_drag &&
3209       (new_width != column->fixed_width))
3210     {
3211       column->use_resized_width = TRUE;
3212       column->resized_width = new_width;
3213 #if 0
3214       if (column->expand)
3215         column->resized_width -= tree_view->priv->last_extra_space_per_column;
3216 #endif
3217       gtk_widget_queue_resize (widget);
3218     }
3219
3220   return FALSE;
3221 }
3222
3223
3224 static void
3225 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3226 {
3227   PsppSheetViewColumnReorder *reorder = NULL;
3228   GList *list;
3229   gint mouse_x;
3230
3231   gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3232   for (list = tree_view->priv->column_drag_info; list; list = list->next)
3233     {
3234       reorder = (PsppSheetViewColumnReorder *) list->data;
3235       if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3236         break;
3237       reorder = NULL;
3238     }
3239
3240   /*  if (reorder && reorder == tree_view->priv->cur_reorder)
3241       return;*/
3242
3243   tree_view->priv->cur_reorder = reorder;
3244   pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3245 }
3246
3247 static void
3248 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3249 {
3250   GdkRectangle visible_rect;
3251   gint y;
3252   gint offset;
3253   gfloat value;
3254
3255   gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3256   y += tree_view->priv->dy;
3257
3258   pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3259
3260   /* see if we are near the edge. */
3261   offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3262   if (offset > 0)
3263     {
3264       offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3265       if (offset < 0)
3266         return;
3267     }
3268
3269   value = CLAMP (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0,
3270                  gtk_adjustment_get_upper (tree_view->priv->vadjustment) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
3271   gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3272 }
3273
3274 static gboolean
3275 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3276 {
3277   GdkRectangle visible_rect;
3278   gint x;
3279   gint offset;
3280   gfloat value;
3281
3282   gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3283
3284   pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3285
3286   /* See if we are near the edge. */
3287   offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3288   if (offset > 0)
3289     {
3290       offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3291       if (offset < 0)
3292         return TRUE;
3293     }
3294   offset = offset/3;
3295
3296   value = CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset,
3297                  0.0, gtk_adjustment_get_upper (tree_view->priv->hadjustment) - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
3298   gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3299
3300   return TRUE;
3301
3302 }
3303
3304 static gboolean
3305 pspp_sheet_view_motion_drag_column (GtkWidget      *widget,
3306                                   GdkEventMotion *event)
3307 {
3308   PsppSheetView *tree_view = (PsppSheetView *) widget;
3309   PsppSheetViewColumn *column = tree_view->priv->drag_column;
3310   gint x, y;
3311   GtkAllocation allocation;
3312
3313   /* Sanity Check */
3314   if ((column == NULL) ||
3315       (event->window != tree_view->priv->drag_window))
3316     return FALSE;
3317
3318   /* Handle moving the header */
3319   gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3320   gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
3321   x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3322              MAX (tree_view->priv->width, allocation.width) - column->allocation.width);
3323   gdk_window_move (tree_view->priv->drag_window, x, y);
3324   
3325   /* autoscroll, if needed */
3326   pspp_sheet_view_horizontal_autoscroll (tree_view);
3327   /* Update the current reorder position and arrow; */
3328   pspp_sheet_view_update_current_reorder (tree_view);
3329
3330   return TRUE;
3331 }
3332
3333 static void
3334 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3335 {
3336   remove_scroll_timeout (tree_view);
3337   gtk_grab_remove (GTK_WIDGET (tree_view));
3338
3339   if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3340     {
3341       GtkTreePath *tmp_path;
3342
3343       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3344
3345       /* The anchor path should be set to the start path */
3346       tmp_path = _pspp_sheet_view_find_path (tree_view,
3347                                            tree_view->priv->rubber_band_start_node);
3348
3349       if (tree_view->priv->anchor)
3350         gtk_tree_row_reference_free (tree_view->priv->anchor);
3351
3352       tree_view->priv->anchor =
3353         gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3354                                           tree_view->priv->model,
3355                                           tmp_path);
3356
3357       gtk_tree_path_free (tmp_path);
3358
3359       /* ... and the cursor to the end path */
3360       tmp_path = _pspp_sheet_view_find_path (tree_view,
3361                                            tree_view->priv->rubber_band_end_node);
3362       pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */
3363       gtk_tree_path_free (tmp_path);
3364
3365       _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3366     }
3367
3368   /* Clear status variables */
3369   tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3370   tree_view->priv->rubber_band_shift = 0;
3371   tree_view->priv->rubber_band_ctrl = 0;
3372
3373   tree_view->priv->rubber_band_start_node = -1;
3374   tree_view->priv->rubber_band_end_node = -1;
3375 }
3376
3377 static void
3378 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3379                                                  int start_node,
3380                                                  int end_node,
3381                                                  gboolean     select,
3382                                                  gboolean     skip_start,
3383                                                  gboolean     skip_end)
3384 {
3385   if (start_node == end_node)
3386     return;
3387
3388   /* We skip the first node and jump inside the loop */
3389   if (skip_start)
3390     goto skip_first;
3391
3392   do
3393     {
3394       /* Small optimization by assuming insensitive nodes are never
3395        * selected.
3396        */
3397       if (select)
3398         {
3399           if (tree_view->priv->rubber_band_shift)
3400             pspp_sheet_view_node_select (tree_view, start_node);
3401           else if (tree_view->priv->rubber_band_ctrl)
3402             {
3403               /* Toggle the selection state */
3404               if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3405                 pspp_sheet_view_node_unselect (tree_view, start_node);
3406               else
3407                 pspp_sheet_view_node_select (tree_view, start_node);
3408             }
3409           else
3410             pspp_sheet_view_node_select (tree_view, start_node);
3411         }
3412       else
3413         {
3414           /* Mirror the above */
3415           if (tree_view->priv->rubber_band_shift)
3416                 pspp_sheet_view_node_unselect (tree_view, start_node);
3417           else if (tree_view->priv->rubber_band_ctrl)
3418             {
3419               /* Toggle the selection state */
3420               if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3421                 pspp_sheet_view_node_unselect (tree_view, start_node);
3422               else
3423                 pspp_sheet_view_node_select (tree_view, start_node);
3424             }
3425           else
3426             pspp_sheet_view_node_unselect (tree_view, start_node);
3427         }
3428
3429       _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3430
3431       if (start_node == end_node)
3432         break;
3433
3434 skip_first:
3435
3436       start_node = pspp_sheet_view_node_next (tree_view, start_node);
3437
3438       if (start_node < 0)
3439         /* Ran out of tree */
3440         break;
3441
3442       if (skip_end && start_node == end_node)
3443         break;
3444     }
3445   while (TRUE);
3446 }
3447
3448 static gint
3449 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3450                                   int node)
3451 {
3452   return node * tree_view->priv->fixed_height;
3453 }
3454
3455 static gint
3456 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3457                              gint height,
3458                              int *new_node)
3459 {
3460   int fixed_height = tree_view->priv->fixed_height;
3461   if (fixed_height <= 0
3462       || height < 0
3463       || height >= tree_view->priv->row_count * fixed_height)
3464     {
3465       *new_node = -1;
3466       return 0;
3467     }
3468   else
3469     {
3470       *new_node = height / fixed_height;
3471       return height % fixed_height;
3472     }
3473 }
3474
3475 static void
3476 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3477 {
3478   int start_node;
3479   int end_node;
3480
3481   pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3482   pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3483
3484   /* Handle the start area first */
3485   if (tree_view->priv->rubber_band_start_node < 0)
3486     {
3487       pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3488                                                        start_node,
3489                                                        end_node,
3490                                                        TRUE,
3491                                                        FALSE,
3492                                                        FALSE);
3493     }
3494   else if (start_node < tree_view->priv->rubber_band_start_node)
3495     {
3496       /* New node is above the old one; selection became bigger */
3497       pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3498                                                        start_node,
3499                                                        tree_view->priv->rubber_band_start_node,
3500                                                        TRUE,
3501                                                        FALSE,
3502                                                        TRUE);
3503     }
3504   else if (start_node > tree_view->priv->rubber_band_start_node)
3505     {
3506       /* New node is below the old one; selection became smaller */
3507       pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3508                                                        tree_view->priv->rubber_band_start_node,
3509                                                        start_node,
3510                                                        FALSE,
3511                                                        FALSE,
3512                                                        TRUE);
3513     }
3514
3515   tree_view->priv->rubber_band_start_node = start_node;
3516
3517   /* Next, handle the end area */
3518   if (tree_view->priv->rubber_band_end_node < 0)
3519     {
3520       /* In the event this happens, start_node was also -1; this case is
3521        * handled above.
3522        */
3523     }
3524   else if (end_node < 0)
3525     {
3526       /* Find the last node in the tree */
3527       pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3528                                &end_node);
3529
3530       /* Selection reached end of the tree */
3531       pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3532                                                        tree_view->priv->rubber_band_end_node,
3533                                                        end_node,
3534                                                        TRUE,
3535                                                        TRUE,
3536                                                        FALSE);
3537     }
3538   else if (end_node > tree_view->priv->rubber_band_end_node)
3539     {
3540       /* New node is below the old one; selection became bigger */
3541       pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3542                                                        tree_view->priv->rubber_band_end_node,
3543                                                        end_node,
3544                                                        TRUE,
3545                                                        TRUE,
3546                                                        FALSE);
3547     }
3548   else if (end_node < tree_view->priv->rubber_band_end_node)
3549     {
3550       /* New node is above the old one; selection became smaller */
3551       pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3552                                                        end_node,
3553                                                        tree_view->priv->rubber_band_end_node,
3554                                                        FALSE,
3555                                                        TRUE,
3556                                                        FALSE);
3557     }
3558
3559   tree_view->priv->rubber_band_end_node = end_node;
3560 }
3561
3562 #define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X))
3563
3564 static void
3565 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3566 {
3567   gint x, y;
3568   cairo_rectangle_int_t old_area;
3569   cairo_rectangle_int_t new_area;
3570   cairo_rectangle_int_t common;
3571   cairo_region_t *invalid_region;
3572   PsppSheetViewColumn *column;
3573
3574   old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3575   old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3576   old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3577   old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3578
3579   gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3580
3581   x = MAX (x, 0);
3582   y = MAX (y, 0) + tree_view->priv->dy;
3583
3584   new_area.x = MIN (tree_view->priv->press_start_x, x);
3585   new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3586   new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3587   new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3588
3589   invalid_region = cairo_region_create_rectangle (&old_area);
3590   cairo_region_union_rectangle (invalid_region, &new_area);
3591
3592   gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area), 
3593                            GDK_RECTANGLE_PTR (&new_area), GDK_RECTANGLE_PTR (&common));
3594   if (common.width > 2 && common.height > 2)
3595     {
3596       cairo_region_t *common_region;
3597
3598       /* make sure the border is invalidated */
3599       common.x += 1;
3600       common.y += 1;
3601       common.width -= 2;
3602       common.height -= 2;
3603
3604       common_region = cairo_region_create_rectangle (&common);
3605
3606       cairo_region_subtract (invalid_region, common_region);
3607       cairo_region_destroy (common_region);
3608     }
3609
3610 #if GTK_MAJOR_VERSION == 3
3611   gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);  
3612 #else
3613   {
3614     cairo_rectangle_int_t extents;
3615     GdkRegion *ereg;
3616     cairo_region_get_extents (invalid_region, &extents);
3617     ereg = gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents));
3618     gdk_window_invalidate_region (tree_view->priv->bin_window, ereg, TRUE);
3619     gdk_region_destroy (ereg);
3620   }
3621 #endif
3622
3623   cairo_region_destroy (invalid_region);
3624
3625   tree_view->priv->rubber_band_x = x;
3626   tree_view->priv->rubber_band_y = y;
3627   pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3628
3629   pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3630   pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3631                                             tree_view->priv->anchor_column,
3632                                             column);
3633
3634   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3635
3636   pspp_sheet_view_update_rubber_band_selection (tree_view);
3637 }
3638
3639 #if GTK3_TRANSITION
3640 static void
3641 pspp_sheet_view_paint_rubber_band (PsppSheetView  *tree_view,
3642                                 GdkRectangle *area)
3643 {
3644   cairo_t *cr;
3645   GdkRectangle rect;
3646   GdkRectangle rubber_rect;
3647   GtkStyle *style;
3648
3649   return;
3650   rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3651   rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3652   rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3653   rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3654
3655   if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3656     return;
3657
3658   cr = gdk_cairo_create (tree_view->priv->bin_window);
3659   cairo_set_line_width (cr, 1.0);
3660
3661   style = gtk_widget_get_style (GTK_WIDGET (tree_view));
3662   cairo_set_source_rgba (cr,
3663                          style->fg[GTK_STATE_NORMAL].red / 65535.,
3664                          style->fg[GTK_STATE_NORMAL].green / 65535.,
3665                          style->fg[GTK_STATE_NORMAL].blue / 65535.,
3666                          .25);
3667
3668   gdk_cairo_rectangle (cr, &rect);
3669   cairo_clip (cr);
3670   cairo_paint (cr);
3671
3672   cairo_set_source_rgb (cr,
3673                         style->fg[GTK_STATE_NORMAL].red / 65535.,
3674                         style->fg[GTK_STATE_NORMAL].green / 65535.,
3675                         style->fg[GTK_STATE_NORMAL].blue / 65535.);
3676
3677   cairo_rectangle (cr,
3678                    rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3679                    rubber_rect.width - 1, rubber_rect.height - 1);
3680   cairo_stroke (cr);
3681
3682   cairo_destroy (cr);
3683 }
3684 #endif
3685
3686
3687 static gboolean
3688 pspp_sheet_view_motion_bin_window (GtkWidget      *widget,
3689                                  GdkEventMotion *event)
3690 {
3691   PsppSheetView *tree_view;
3692   int node;
3693   gint new_y;
3694
3695   tree_view = (PsppSheetView *) widget;
3696
3697   if (tree_view->priv->row_count == 0)
3698     return FALSE;
3699
3700   if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3701     {
3702       GdkRectangle background_area, cell_area;
3703       PsppSheetViewColumn *column;
3704
3705       if (find_click (tree_view, event->x, event->y, &node, &column,
3706                       &background_area, &cell_area)
3707           && tree_view->priv->focus_column == column
3708           && tree_view->priv->press_start_node == node)
3709         return FALSE;
3710
3711       gtk_grab_add (GTK_WIDGET (tree_view));
3712       pspp_sheet_view_update_rubber_band (tree_view);
3713
3714       tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3715     }
3716   else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3717     {
3718       pspp_sheet_view_update_rubber_band (tree_view);
3719
3720       add_scroll_timeout (tree_view);
3721     }
3722
3723   /* only check for an initiated drag when a button is pressed */
3724   if (tree_view->priv->pressed_button >= 0
3725       && !tree_view->priv->rubber_band_status)
3726     pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3727
3728   new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3729   if (new_y < 0)
3730     new_y = 0;
3731
3732   pspp_sheet_view_find_offset (tree_view, new_y, &node);
3733
3734   tree_view->priv->event_last_x = event->x;
3735   tree_view->priv->event_last_y = event->y;
3736
3737   prelight_or_select (tree_view, node, event->x, event->y);
3738
3739   return TRUE;
3740 }
3741
3742 static gboolean
3743 pspp_sheet_view_motion (GtkWidget      *widget,
3744                       GdkEventMotion *event)
3745 {
3746   PsppSheetView *tree_view;
3747
3748   tree_view = (PsppSheetView *) widget;
3749
3750   /* Resizing a column */
3751   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3752     return pspp_sheet_view_motion_resize_column (widget, event);
3753
3754   /* Drag column */
3755   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3756     return pspp_sheet_view_motion_drag_column (widget, event);
3757
3758   /* Sanity check it */
3759   if (event->window == tree_view->priv->bin_window)
3760     return pspp_sheet_view_motion_bin_window (widget, event);
3761
3762   return FALSE;
3763 }
3764
3765 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3766  * the tree is empty.
3767  */
3768 static void
3769 invalidate_empty_focus (PsppSheetView *tree_view)
3770 {
3771   GdkRectangle area;
3772
3773   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3774     return;
3775
3776   area.x = 0;
3777   area.y = 0;
3778   area.width = gdk_window_get_width (tree_view->priv->bin_window);
3779   area.height = gdk_window_get_height (tree_view->priv->bin_window);
3780   gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3781 }
3782
3783 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3784  * is empty.
3785  */
3786 static void
3787 draw_empty_focus (PsppSheetView *tree_view)
3788 {
3789   GtkWidget *widget = GTK_WIDGET (tree_view);
3790   gint w, h;
3791   cairo_t *cr = gdk_cairo_create (tree_view->priv->bin_window);
3792
3793   if (!gtk_widget_has_focus (widget))
3794     return;
3795
3796   w = gdk_window_get_width (tree_view->priv->bin_window);
3797   h = gdk_window_get_height (tree_view->priv->bin_window);
3798
3799   w -= 2;
3800   h -= 2;
3801
3802   if (w > 0 && h > 0)
3803     gtk_paint_focus (gtk_widget_get_style (widget),
3804                      cr,
3805                      gtk_widget_get_state (widget),
3806                      widget,
3807                      NULL,
3808                      1, 1, w, h);
3809   cairo_destroy (cr);
3810 }
3811
3812 static void
3813 pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView    *tree_view,
3814                                           cairo_t *cr,
3815                                           gint n_visible_columns,
3816                                           gint min_y,
3817                                           gint max_y)
3818 {
3819   GList *list = tree_view->priv->columns;
3820   gint i = 0;
3821   gint current_x = 0;
3822
3823   if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3824       && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3825     return;
3826
3827   /* Only draw the lines for visible rows and columns */
3828   for (list = tree_view->priv->columns; list; list = list->next, i++)
3829     {
3830       PsppSheetViewColumn *column = list->data;
3831       gint x;
3832
3833       if (! column->visible)
3834         continue;
3835
3836       current_x += column->width;
3837
3838       /* Generally the grid lines should fit within the column, but for the
3839          last visible column we put it just past the end of the column.
3840          (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3841       x = current_x;
3842       if (i != n_visible_columns - 1)
3843         x--;
3844
3845       cairo_set_line_width (cr, 1.0);
3846       cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3847       cairo_move_to (cr, x + 0.5, min_y);
3848       cairo_line_to (cr, x + 0.5, max_y - min_y);
3849       cairo_stroke (cr);
3850     }
3851 }
3852
3853 /* Warning: Very scary function.
3854  * Modify at your own risk
3855  *
3856  * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3857  * FIXME: It's not...
3858  */
3859 static gboolean
3860 pspp_sheet_view_bin_expose (GtkWidget      *widget,
3861                             cairo_t *cr)
3862 {
3863   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3864   GtkTreePath *path;
3865   GList *list;
3866   int node;
3867   int cursor = -1;
3868   int drag_highlight = -1;
3869   GtkTreeIter iter;
3870   gint new_y;
3871   gint y_offset, cell_offset;
3872   gint max_height;
3873   GdkRectangle background_area;
3874   GdkRectangle cell_area;
3875   guint flags;
3876   gint bin_window_width;
3877   gint bin_window_height;
3878   GtkTreePath *cursor_path;
3879   GtkTreePath *drag_dest_path;
3880   GList *first_column, *last_column;
3881   gint vertical_separator;
3882   gint horizontal_separator;
3883   gint focus_line_width;
3884   gboolean allow_rules;
3885   gboolean has_special_cell;
3886   gboolean rtl;
3887   gint n_visible_columns;
3888   gint grid_line_width;
3889   gboolean row_ending_details;
3890   gboolean draw_vgrid_lines, draw_hgrid_lines;
3891   gint min_y, max_y;
3892
3893   GdkRectangle Zarea;
3894   GtkAllocation allocation;
3895   gtk_widget_get_allocation (widget, &allocation);
3896
3897   GdkRectangle exposed_rect;
3898   gdk_cairo_get_clip_rectangle (cr, &exposed_rect);
3899   
3900   Zarea.x =      0;
3901   Zarea.y =      0;
3902   Zarea.height = allocation.height;
3903
3904   rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3905
3906   gtk_widget_style_get (widget,
3907                         "horizontal-separator", &horizontal_separator,
3908                         "vertical-separator", &vertical_separator,
3909                         "allow-rules", &allow_rules,
3910                         "focus-line-width", &focus_line_width,
3911                         "row-ending-details", &row_ending_details,
3912                         NULL);
3913
3914   if (tree_view->priv->row_count == 0)
3915     {
3916       draw_empty_focus (tree_view);
3917       return TRUE;
3918     }
3919
3920 #if GTK3_TRANSITION
3921   /* clip event->area to the visible area */
3922   if (Zarea.height < 0.5)
3923     return TRUE;
3924 #endif
3925
3926   validate_visible_area (tree_view);
3927
3928   new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, Zarea.y);
3929
3930   if (new_y < 0)
3931     new_y = 0;
3932   y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3933   bin_window_width = 
3934     gdk_window_get_width (tree_view->priv->bin_window);
3935
3936   bin_window_height = 
3937     gdk_window_get_height (tree_view->priv->bin_window);
3938
3939
3940   if (tree_view->priv->height < bin_window_height)
3941     {
3942       gtk_paint_flat_box (gtk_widget_get_style (widget),
3943                           cr,
3944                           gtk_widget_get_state (widget),
3945                           GTK_SHADOW_NONE,
3946                           widget,
3947                           "cell_even",
3948                           0, tree_view->priv->height,
3949                           bin_window_width,
3950                           bin_window_height - tree_view->priv->height);
3951     }
3952
3953   if (node < 0)
3954     return TRUE;
3955
3956   /* find the path for the node */
3957   path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3958   gtk_tree_model_get_iter (tree_view->priv->model,
3959                            &iter,
3960                            path);
3961   gtk_tree_path_free (path);
3962   
3963   cursor_path = NULL;
3964   drag_dest_path = NULL;
3965
3966   if (tree_view->priv->cursor)
3967     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3968
3969   if (cursor_path)
3970     _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3971
3972   if (tree_view->priv->drag_dest_row)
3973     drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3974
3975   if (drag_dest_path)
3976     _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3977                                 &drag_highlight);
3978
3979   draw_vgrid_lines =
3980     tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3981     || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3982   draw_hgrid_lines =
3983     tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
3984     || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3985
3986   if (draw_vgrid_lines || draw_hgrid_lines)
3987     gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
3988   
3989   n_visible_columns = 0;
3990   for (list = tree_view->priv->columns; list; list = list->next)
3991     {
3992       if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
3993         continue;
3994       n_visible_columns ++;
3995     }
3996
3997   /* Find the last column */
3998   for (last_column = g_list_last (tree_view->priv->columns);
3999        last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
4000        last_column = last_column->prev)
4001     ;
4002
4003   /* and the first */
4004   for (first_column = g_list_first (tree_view->priv->columns);
4005        first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
4006        first_column = first_column->next)
4007     ;
4008
4009   /* Actually process the expose event.  To do this, we want to
4010    * start at the first node of the event, and walk the tree in
4011    * order, drawing each successive node.
4012    */
4013
4014   min_y = y_offset;
4015   do
4016     {
4017       gboolean parity;
4018       gboolean is_first = FALSE;
4019       gboolean is_last = FALSE;
4020       gboolean done = FALSE;
4021       gboolean selected;
4022
4023       max_height = ROW_HEIGHT (tree_view);
4024
4025       cell_offset = 0;
4026
4027       background_area.y = y_offset + Zarea.y;
4028       background_area.height = max_height;
4029       max_y = background_area.y + max_height;
4030
4031       flags = 0;
4032
4033       if (node == tree_view->priv->prelight_node)
4034         flags |= GTK_CELL_RENDERER_PRELIT;
4035
4036       selected = pspp_sheet_view_node_is_selected (tree_view, node);
4037
4038       parity = node % 2;
4039
4040       if (tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
4041         {
4042           /* we *need* to set cell data on all cells before the call
4043            * to _has_special_cell, else _has_special_cell() does not
4044            * return a correct value.
4045            */
4046           for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4047                list;
4048                list = (rtl ? list->prev : list->next))
4049             {
4050               PsppSheetViewColumn *column = list->data;
4051               pspp_sheet_view_column_cell_set_cell_data (column,
4052                                                          tree_view->priv->model,
4053                                                          &iter);
4054             }
4055
4056           has_special_cell = pspp_sheet_view_has_special_cell (tree_view);
4057         }
4058       else
4059         has_special_cell = tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
4060
4061       for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4062            list;
4063            list = (rtl ? list->prev : list->next))
4064         {
4065           PsppSheetViewColumn *column = list->data;
4066           const gchar *detail = NULL;
4067           gboolean selected_column;
4068           GtkStateType state;
4069
4070           if (!column->visible)
4071             continue;
4072
4073           if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
4074             selected_column = column->selected && column->selectable;
4075           else
4076             selected_column = TRUE;
4077
4078 #if GTK3_TRANSITION
4079           if (cell_offset > Zarea.x + Zarea.width ||
4080               cell_offset + column->width < Zarea.x)
4081             {
4082               cell_offset += column->width;
4083               continue;
4084             }
4085 #endif
4086
4087           if (selected && selected_column)
4088             flags |= GTK_CELL_RENDERER_SELECTED;
4089           else
4090             flags &= ~GTK_CELL_RENDERER_SELECTED;
4091
4092           if (column->show_sort_indicator)
4093             flags |= GTK_CELL_RENDERER_SORTED;
4094           else
4095             flags &= ~GTK_CELL_RENDERER_SORTED;
4096
4097           if (cursor == node)
4098             flags |= GTK_CELL_RENDERER_FOCUSED;
4099           else
4100             flags &= ~GTK_CELL_RENDERER_FOCUSED;
4101
4102           background_area.x = cell_offset;
4103           background_area.width = column->width;
4104
4105           cell_area = background_area;
4106           cell_area.y += vertical_separator / 2;
4107           cell_area.x += horizontal_separator / 2;
4108           cell_area.height -= vertical_separator;
4109           cell_area.width -= horizontal_separator;
4110
4111           if (draw_vgrid_lines)
4112             {
4113               if (list == first_column)
4114                 {
4115                   cell_area.width -= grid_line_width / 2;
4116                 }
4117               else if (list == last_column)
4118                 {
4119                   cell_area.x += grid_line_width / 2;
4120                   cell_area.width -= grid_line_width / 2;
4121                 }
4122               else
4123                 {
4124                   cell_area.x += grid_line_width / 2;
4125                   cell_area.width -= grid_line_width;
4126                 }
4127             }
4128
4129           if (draw_hgrid_lines)
4130             {
4131               cell_area.y += grid_line_width / 2;
4132               cell_area.height -= grid_line_width;
4133             }
4134
4135 #if GTK3_TRANSITION
4136           if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4137 #else
4138           if (!gdk_rectangle_intersect (&background_area, &exposed_rect, NULL))
4139 #endif
4140             {
4141               cell_offset += column->width;
4142               continue;
4143             }
4144
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
6717   if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button)
6718     gtk_widget_grab_focus (focus_column->button);
6719
6720   if (clamp_column_visible)
6721     pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6722 }
6723
6724 /* Returns TRUE if the focus is within the headers, after the focus operation is
6725  * done
6726  */
6727 static gboolean
6728 pspp_sheet_view_header_focus (PsppSheetView      *tree_view,
6729                             GtkDirectionType  dir,
6730                             gboolean          clamp_column_visible)
6731 {
6732   GtkWidget *focus_child;
6733   PsppSheetViewColumn *focus_column;
6734   GList *last_column, *first_column;
6735   GList *tmp_list;
6736   gboolean rtl;
6737
6738   if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6739     return FALSE;
6740
6741   focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
6742
6743   first_column = tree_view->priv->columns;
6744   while (first_column)
6745     {
6746       PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6747
6748       if (pspp_sheet_view_column_can_focus (c) && c->visible)
6749         break;
6750       first_column = first_column->next;
6751     }
6752
6753   /* No headers are visible, or are focusable.  We can't focus in or out.
6754    */
6755   if (first_column == NULL)
6756     return FALSE;
6757
6758   last_column = g_list_last (tree_view->priv->columns);
6759   while (last_column)
6760     {
6761       PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6762
6763       if (pspp_sheet_view_column_can_focus (c) && c->visible)
6764         break;
6765       last_column = last_column->prev;
6766     }
6767
6768
6769   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6770
6771   switch (dir)
6772     {
6773     case GTK_DIR_TAB_BACKWARD:
6774     case GTK_DIR_TAB_FORWARD:
6775     case GTK_DIR_UP:
6776     case GTK_DIR_DOWN:
6777       if (focus_child == NULL)
6778         {
6779           if (tree_view->priv->focus_column != NULL &&
6780               pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6781             focus_column = tree_view->priv->focus_column;
6782           else
6783             focus_column = first_column->data;
6784           pspp_sheet_view_focus_column (tree_view, focus_column,
6785                                         clamp_column_visible);
6786           return TRUE;
6787         }
6788       return FALSE;
6789
6790     case GTK_DIR_LEFT:
6791     case GTK_DIR_RIGHT:
6792       if (focus_child == NULL)
6793         {
6794           if (tree_view->priv->focus_column != NULL)
6795             focus_column = tree_view->priv->focus_column;
6796           else if (dir == GTK_DIR_LEFT)
6797             focus_column = last_column->data;
6798           else
6799             focus_column = first_column->data;
6800           pspp_sheet_view_focus_column (tree_view, focus_column,
6801                                         clamp_column_visible);
6802           return TRUE;
6803         }
6804
6805       if (gtk_widget_child_focus (focus_child, dir))
6806         {
6807           /* The focus moves inside the button. */
6808           /* This is probably a great example of bad UI */
6809           if (clamp_column_visible)
6810             pspp_sheet_view_clamp_column_visible (tree_view,
6811                                                   tree_view->priv->focus_column,
6812                                                   FALSE);
6813           return TRUE;
6814         }
6815
6816       /* We need to move the focus among the row of buttons. */
6817       for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6818         if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6819           break;
6820
6821       if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6822           || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6823         {
6824           gtk_widget_error_bell (GTK_WIDGET (tree_view));
6825           return TRUE;
6826         }
6827
6828       while (tmp_list)
6829         {
6830           PsppSheetViewColumn *column;
6831
6832           if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6833             tmp_list = tmp_list->next;
6834           else
6835             tmp_list = tmp_list->prev;
6836
6837           if (tmp_list == NULL)
6838             {
6839               g_warning ("Internal button not found");
6840               break;
6841             }
6842           column = tmp_list->data;
6843           if (column->visible &&
6844               pspp_sheet_view_column_can_focus (column))
6845             {
6846               if (column->button)
6847                 {
6848                   pspp_sheet_view_focus_column (tree_view, column,
6849                                                 clamp_column_visible);
6850                   return TRUE;
6851                 }
6852             }
6853         }
6854       return FALSE;
6855
6856     default:
6857       g_assert_not_reached ();
6858       break;
6859     }
6860
6861   return FALSE;
6862 }
6863
6864 /* This function returns in 'path' the first focusable path, if the given path
6865  * is already focusable, it's the returned one.
6866  *
6867  */
6868 static gboolean
6869 search_first_focusable_path (PsppSheetView  *tree_view,
6870                              GtkTreePath **path,
6871                              gboolean      search_forward,
6872                              int *new_node)
6873 {
6874   /* XXX this function is trivial given that the sheetview doesn't support
6875      separator rows */
6876   int node = -1;
6877
6878   if (!path || !*path)
6879     return FALSE;
6880
6881   _pspp_sheet_view_find_node (tree_view, *path, &node);
6882
6883   if (node < 0)
6884     return FALSE;
6885
6886   if (new_node)
6887     *new_node = node;
6888
6889   return (*path != NULL);
6890 }
6891
6892 static gint
6893 pspp_sheet_view_focus (GtkWidget        *widget,
6894                      GtkDirectionType  direction)
6895 {
6896   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6897   GtkContainer *container = GTK_CONTAINER (widget);
6898   GtkWidget *focus_child;
6899
6900   if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6901     return FALSE;
6902
6903   focus_child = gtk_container_get_focus_child (container);
6904
6905   pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6906   /* Case 1.  Headers currently have focus. */
6907   if (focus_child)
6908     {
6909       switch (direction)
6910         {
6911         case GTK_DIR_LEFT:
6912         case GTK_DIR_RIGHT:
6913           pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6914           return TRUE;
6915         case GTK_DIR_TAB_BACKWARD:
6916         case GTK_DIR_UP:
6917           return FALSE;
6918         case GTK_DIR_TAB_FORWARD:
6919         case GTK_DIR_DOWN:
6920           gtk_widget_grab_focus (widget);
6921           return TRUE;
6922         default:
6923           g_assert_not_reached ();
6924           return FALSE;
6925         }
6926     }
6927
6928   /* Case 2. We don't have focus at all. */
6929   if (!gtk_widget_has_focus (widget))
6930     {
6931       if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6932         gtk_widget_grab_focus (widget);
6933       return TRUE;
6934     }
6935
6936   /* Case 3. We have focus already. */
6937   if (direction == GTK_DIR_TAB_BACKWARD)
6938     return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6939   else if (direction == GTK_DIR_TAB_FORWARD)
6940     return FALSE;
6941
6942   /* Other directions caught by the keybindings */
6943   gtk_widget_grab_focus (widget);
6944   return TRUE;
6945 }
6946
6947 static void
6948 pspp_sheet_view_grab_focus (GtkWidget *widget)
6949 {
6950   GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6951
6952   pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6953 }
6954
6955 static void
6956 pspp_sheet_view_style_set (GtkWidget *widget,
6957                          GtkStyle *previous_style)
6958 {
6959   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6960   GList *list;
6961   PsppSheetViewColumn *column;
6962
6963   if (gtk_widget_get_realized (widget))
6964     {
6965       gdk_window_set_background (tree_view->priv->bin_window, &gtk_widget_get_style (widget)->base[gtk_widget_get_state (widget)]);
6966       gtk_style_set_background (gtk_widget_get_style (widget), tree_view->priv->header_window, GTK_STATE_NORMAL);
6967       pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6968     }
6969
6970   gtk_widget_style_get (widget,
6971                         "expander-size", &tree_view->priv->expander_size,
6972                         NULL);
6973   tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6974
6975   for (list = tree_view->priv->columns; list; list = list->next)
6976     {
6977       column = list->data;
6978       _pspp_sheet_view_column_cell_set_dirty (column);
6979     }
6980
6981   tree_view->priv->fixed_height = -1;
6982
6983   /* Invalidate cached button style. */
6984   if (tree_view->priv->button_style)
6985     {
6986       g_object_unref (tree_view->priv->button_style);
6987       tree_view->priv->button_style = NULL;
6988     }
6989
6990   gtk_widget_queue_resize (widget);
6991 }
6992
6993
6994 static void
6995 pspp_sheet_view_set_focus_child (GtkContainer *container,
6996                                GtkWidget    *child)
6997 {
6998   PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6999   GList *list;
7000
7001   for (list = tree_view->priv->columns; list; list = list->next)
7002     {
7003       if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
7004         {
7005           tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7006           break;
7007         }
7008     }
7009
7010   GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
7011 }
7012
7013 static void
7014 pspp_sheet_view_set_adjustments (PsppSheetView   *tree_view,
7015                                GtkAdjustment *hadj,
7016                                GtkAdjustment *vadj)
7017 {
7018   gboolean need_adjust = FALSE;
7019
7020   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7021
7022   if (hadj)
7023     g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
7024   else
7025     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7026   if (vadj)
7027     g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
7028   else
7029     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7030
7031   if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
7032     {
7033       g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
7034                                             pspp_sheet_view_adjustment_changed,
7035                                             tree_view);
7036       g_object_unref (tree_view->priv->hadjustment);
7037     }
7038
7039   if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
7040     {
7041       g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
7042                                             pspp_sheet_view_adjustment_changed,
7043                                             tree_view);
7044       g_object_unref (tree_view->priv->vadjustment);
7045     }
7046
7047   if (tree_view->priv->hadjustment != hadj)
7048     {
7049       tree_view->priv->hadjustment = hadj;
7050       g_object_ref_sink (tree_view->priv->hadjustment);
7051
7052       g_signal_connect (tree_view->priv->hadjustment, "value-changed",
7053                         G_CALLBACK (pspp_sheet_view_adjustment_changed),
7054                         tree_view);
7055       need_adjust = TRUE;
7056     }
7057
7058   if (tree_view->priv->vadjustment != vadj)
7059     {
7060       tree_view->priv->vadjustment = vadj;
7061       g_object_ref_sink (tree_view->priv->vadjustment);
7062
7063       g_signal_connect (tree_view->priv->vadjustment, "value-changed",
7064                         G_CALLBACK (pspp_sheet_view_adjustment_changed),
7065                         tree_view);
7066       need_adjust = TRUE;
7067     }
7068
7069   if (need_adjust)
7070     pspp_sheet_view_adjustment_changed (NULL, tree_view);
7071 }
7072
7073
7074 static gboolean
7075 pspp_sheet_view_real_move_cursor (PsppSheetView       *tree_view,
7076                                 GtkMovementStep    step,
7077                                 gint               count)
7078 {
7079   PsppSheetSelectMode mode;
7080   GdkModifierType state;
7081
7082   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
7083   g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
7084                         step == GTK_MOVEMENT_VISUAL_POSITIONS ||
7085                         step == GTK_MOVEMENT_DISPLAY_LINES ||
7086                         step == GTK_MOVEMENT_PAGES ||
7087                         step == GTK_MOVEMENT_BUFFER_ENDS ||
7088                         step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
7089
7090   if (tree_view->priv->row_count == 0)
7091     return FALSE;
7092   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7093     return FALSE;
7094
7095   pspp_sheet_view_stop_editing (tree_view, FALSE);
7096   PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7097   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7098
7099   mode = 0;
7100   if (gtk_get_current_event_state (&state))
7101     {
7102       if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7103         mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
7104       if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
7105         mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
7106     }
7107   /* else we assume not pressed */
7108
7109   switch (step)
7110     {
7111     case GTK_MOVEMENT_LOGICAL_POSITIONS:
7112       pspp_sheet_view_move_cursor_tab (tree_view, count);
7113       break;
7114     case GTK_MOVEMENT_VISUAL_POSITIONS:
7115       pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
7116       break;
7117     case GTK_MOVEMENT_DISPLAY_LINES:
7118       pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
7119       break;
7120     case GTK_MOVEMENT_PAGES:
7121       pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
7122       break;
7123     case GTK_MOVEMENT_BUFFER_ENDS:
7124       pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7125       break;
7126     case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7127       pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7128       break;
7129     default:
7130       g_assert_not_reached ();
7131     }
7132
7133   return TRUE;
7134 }
7135
7136 static void
7137 pspp_sheet_view_put (PsppSheetView *tree_view,
7138                    GtkWidget   *child_widget,
7139                    /* in bin_window coordinates */
7140                    gint         x,
7141                    gint         y,
7142                    gint         width,
7143                    gint         height)
7144 {
7145   PsppSheetViewChild *child;
7146   
7147   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7148   g_return_if_fail (GTK_IS_WIDGET (child_widget));
7149
7150   child = g_slice_new (PsppSheetViewChild);
7151
7152   child->widget = child_widget;
7153   child->x = x;
7154   child->y = y;
7155   child->width = width;
7156   child->height = height;
7157
7158   tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7159
7160   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7161     gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7162   
7163   gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7164 }
7165
7166 void
7167 _pspp_sheet_view_child_move_resize (PsppSheetView *tree_view,
7168                                   GtkWidget   *widget,
7169                                   /* in tree coordinates */
7170                                   gint         x,
7171                                   gint         y,
7172                                   gint         width,
7173                                   gint         height)
7174 {
7175   PsppSheetViewChild *child = NULL;
7176   GList *list;
7177   GdkRectangle allocation;
7178
7179   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7180   g_return_if_fail (GTK_IS_WIDGET (widget));
7181
7182   for (list = tree_view->priv->children; list; list = list->next)
7183     {
7184       if (((PsppSheetViewChild *)list->data)->widget == widget)
7185         {
7186           child = list->data;
7187           break;
7188         }
7189     }
7190   if (child == NULL)
7191     return;
7192
7193   allocation.x = child->x = x;
7194   allocation.y = child->y = y;
7195   allocation.width = child->width = width;
7196   allocation.height = child->height = height;
7197
7198   if (gtk_widget_get_realized (widget))
7199     gtk_widget_size_allocate (widget, &allocation);
7200 }
7201
7202
7203 /* TreeModel Callbacks
7204  */
7205
7206 static void
7207 pspp_sheet_view_row_changed (GtkTreeModel *model,
7208                            GtkTreePath  *path,
7209                            GtkTreeIter  *iter,
7210                            gpointer      data)
7211 {
7212   PsppSheetView *tree_view = (PsppSheetView *)data;
7213   int node;
7214   gboolean free_path = FALSE;
7215   GtkTreePath *cursor_path;
7216
7217   g_return_if_fail (path != NULL || iter != NULL);
7218
7219   if (tree_view->priv->cursor != NULL)
7220     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7221   else
7222     cursor_path = NULL;
7223
7224   if (tree_view->priv->edited_column &&
7225       (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7226     pspp_sheet_view_stop_editing (tree_view, TRUE);
7227
7228   if (cursor_path != NULL)
7229     gtk_tree_path_free (cursor_path);
7230
7231   if (path == NULL)
7232     {
7233       path = gtk_tree_model_get_path (model, iter);
7234       free_path = TRUE;
7235     }
7236   else if (iter == NULL)
7237     gtk_tree_model_get_iter (model, iter, path);
7238
7239   _pspp_sheet_view_find_node (tree_view,
7240                               path,
7241                               &node);
7242
7243   if (node >= 0)
7244     {
7245       if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7246         pspp_sheet_view_node_queue_redraw (tree_view, node);
7247     }
7248   
7249   if (free_path)
7250     gtk_tree_path_free (path);
7251 }
7252
7253 static void
7254 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7255                             GtkTreePath  *path,
7256                             GtkTreeIter  *iter,
7257                             gpointer      data)
7258 {
7259   PsppSheetView *tree_view = (PsppSheetView *) data;
7260   gint *indices;
7261   int tmpnode = -1;
7262   gint height = tree_view->priv->fixed_height;
7263   gboolean free_path = FALSE;
7264   gboolean node_visible = TRUE;
7265
7266   g_return_if_fail (path != NULL || iter != NULL);
7267
7268   if (path == NULL)
7269     {
7270       path = gtk_tree_model_get_path (model, iter);
7271       free_path = TRUE;
7272     }
7273   else if (iter == NULL)
7274     gtk_tree_model_get_iter (model, iter, path);
7275
7276   tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7277
7278   /* Update all row-references */
7279   gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7280   indices = gtk_tree_path_get_indices (path);
7281   tmpnode = indices[0];
7282
7283   range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7284
7285   if (height > 0)
7286     {
7287       if (node_visible && node_is_visible (tree_view, tmpnode))
7288         gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7289       else
7290         gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7291     }
7292   else
7293     install_presize_handler (tree_view);
7294   if (free_path)
7295     gtk_tree_path_free (path);
7296 }
7297
7298 static void
7299 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7300                            GtkTreePath  *path,
7301                            gpointer      data)
7302 {
7303   PsppSheetView *tree_view = (PsppSheetView *)data;
7304   int node;
7305
7306   g_return_if_fail (path != NULL);
7307
7308   gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7309
7310   _pspp_sheet_view_find_node (tree_view, path, &node);
7311
7312   if (node < 0)
7313     return;
7314
7315   range_tower_delete (tree_view->priv->selected, node, 1);
7316
7317   /* Ensure we don't have a dangling pointer to a dead node */
7318   ensure_unprelighted (tree_view);
7319
7320   /* Cancel editting if we've started */
7321   pspp_sheet_view_stop_editing (tree_view, TRUE);
7322
7323   if (tree_view->priv->destroy_count_func)
7324     {
7325       gint child_count = 0;
7326       tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7327     }
7328
7329   tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7330
7331   if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7332     {
7333       gtk_tree_row_reference_free (tree_view->priv->top_row);
7334       tree_view->priv->top_row = NULL;
7335     }
7336
7337   install_scroll_sync_handler (tree_view);
7338
7339   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7340
7341 #if 0
7342   if (helper_data.changed)
7343     g_signal_emit_by_name (tree_view->priv->selection, "changed");
7344 #endif
7345 }
7346
7347 static void
7348 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7349                               GtkTreePath  *parent,
7350                               GtkTreeIter  *iter,
7351                               gint         *new_order,
7352                               gpointer      data)
7353 {
7354   PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7355   gint len;
7356
7357   /* XXX need to adjust selection */
7358   len = gtk_tree_model_iter_n_children (model, iter);
7359
7360   if (len < 2)
7361     return;
7362
7363   gtk_tree_row_reference_reordered (G_OBJECT (data),
7364                                     parent,
7365                                     iter,
7366                                     new_order);
7367
7368   if (gtk_tree_path_get_depth (parent) != 0)
7369     return;
7370
7371   if (tree_view->priv->edited_column)
7372     pspp_sheet_view_stop_editing (tree_view, TRUE);
7373
7374   /* we need to be unprelighted */
7375   ensure_unprelighted (tree_view);
7376
7377   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7378
7379   pspp_sheet_view_dy_to_top_row (tree_view);
7380 }
7381
7382
7383 /* Internal tree functions
7384  */
7385
7386
7387 static void
7388 pspp_sheet_view_get_background_xrange (PsppSheetView       *tree_view,
7389                                      PsppSheetViewColumn *column,
7390                                      gint              *x1,
7391                                      gint              *x2)
7392 {
7393   PsppSheetViewColumn *tmp_column = NULL;
7394   gint total_width;
7395   GList *list;
7396   gboolean rtl;
7397
7398   if (x1)
7399     *x1 = 0;
7400
7401   if (x2)
7402     *x2 = 0;
7403
7404   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7405
7406   total_width = 0;
7407   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7408        list;
7409        list = (rtl ? list->prev : list->next))
7410     {
7411       tmp_column = list->data;
7412
7413       if (tmp_column == column)
7414         break;
7415
7416       if (tmp_column->visible)
7417         total_width += tmp_column->width;
7418     }
7419
7420   if (tmp_column != column)
7421     {
7422       g_warning (G_STRLOC": passed-in column isn't in the tree");
7423       return;
7424     }
7425
7426   if (x1)
7427     *x1 = total_width;
7428
7429   if (x2)
7430     {
7431       if (column->visible)
7432         *x2 = total_width + column->width;
7433       else
7434         *x2 = total_width; /* width of 0 */
7435     }
7436 }
7437
7438 /* Make sure the node is visible vertically */
7439 static void
7440 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7441                                     int node)
7442 {
7443   gint node_dy, height;
7444   GtkTreePath *path = NULL;
7445
7446   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7447     return;
7448
7449   /* just return if the node is visible, avoiding a costly expose */
7450   node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7451   height = ROW_HEIGHT (tree_view);
7452   if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
7453       && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
7454                               + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
7455     return;
7456
7457   path = _pspp_sheet_view_find_path (tree_view, node);
7458   if (path)
7459     {
7460       /* We process updates because we want to clear old selected items when we scroll.
7461        * if this is removed, we get a "selection streak" at the bottom. */
7462       gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7463       pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7464       gtk_tree_path_free (path);
7465     }
7466 }
7467
7468 static void
7469 pspp_sheet_view_clamp_column_visible (PsppSheetView       *tree_view,
7470                                     PsppSheetViewColumn *column,
7471                                     gboolean           focus_to_cell)
7472 {
7473   gint x, width;
7474
7475   if (column == NULL)
7476     return;
7477
7478   x = column->allocation.x;
7479   width = column->allocation.width;
7480
7481   if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7482     {
7483       /* The column is larger than the horizontal page size.  If the
7484        * column has cells which can be focussed individually, then we make
7485        * sure the cell which gets focus is fully visible (if even the
7486        * focus cell is bigger than the page size, we make sure the
7487        * left-hand side of the cell is visible).
7488        *
7489        * If the column does not have those so-called special cells, we
7490        * make sure the left-hand side of the column is visible.
7491        */
7492
7493       if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7494         {
7495           GtkTreePath *cursor_path;
7496           GdkRectangle background_area, cell_area, focus_area;
7497
7498           cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7499
7500           pspp_sheet_view_get_cell_area (tree_view,
7501                                        cursor_path, column, &cell_area);
7502           pspp_sheet_view_get_background_area (tree_view,
7503                                              cursor_path, column,
7504                                              &background_area);
7505
7506           gtk_tree_path_free (cursor_path);
7507
7508           _pspp_sheet_view_column_get_focus_area (column,
7509                                                 &background_area,
7510                                                 &cell_area,
7511                                                 &focus_area);
7512
7513           x = focus_area.x;
7514           width = focus_area.width;
7515
7516           if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7517             {
7518               if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7519                 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7520                                           x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7521               else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7522                 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7523             }
7524         }
7525
7526       gtk_adjustment_set_value (tree_view->priv->hadjustment,
7527                                 CLAMP (x,
7528                                        gtk_adjustment_get_lower (tree_view->priv->hadjustment),
7529                                        gtk_adjustment_get_upper (tree_view->priv->hadjustment)
7530                                        - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)));
7531     }
7532   else
7533     {
7534       if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7535           gtk_adjustment_set_value (tree_view->priv->hadjustment,
7536                                     x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7537       else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7538         gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7539   }
7540 }
7541
7542 GtkTreePath *
7543 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7544                             int node)
7545 {
7546   GtkTreePath *path;
7547
7548   path = gtk_tree_path_new ();
7549   if (node >= 0)
7550     gtk_tree_path_append_index (path, node);
7551   return path;
7552 }
7553
7554 void
7555 _pspp_sheet_view_find_node (PsppSheetView  *tree_view,
7556                           GtkTreePath  *path,
7557                           int *node)
7558 {
7559   gint *indices = gtk_tree_path_get_indices (path);
7560   gint depth = gtk_tree_path_get_depth (path);
7561
7562   *node = -1;
7563   if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7564     return;
7565   *node = indices[0];
7566 }
7567
7568 static void
7569 pspp_sheet_view_add_move_binding (GtkBindingSet  *binding_set,
7570                                 guint           keyval,
7571                                 guint           modmask,
7572                                 gboolean        add_shifted_binding,
7573                                 GtkMovementStep step,
7574                                 gint            count)
7575 {
7576   
7577   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7578                                 "move-cursor", 2,
7579                                 G_TYPE_ENUM, step,
7580                                 G_TYPE_INT, count);
7581
7582   if (add_shifted_binding)
7583     gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7584                                   "move-cursor", 2,
7585                                   G_TYPE_ENUM, step,
7586                                   G_TYPE_INT, count);
7587
7588   if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7589    return;
7590
7591   gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7592                                 "move-cursor", 2,
7593                                 G_TYPE_ENUM, step,
7594                                 G_TYPE_INT, count);
7595
7596   gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7597                                 "move-cursor", 2,
7598                                 G_TYPE_ENUM, step,
7599                                 G_TYPE_INT, count);
7600 }
7601
7602 static void
7603 pspp_sheet_view_set_column_drag_info (PsppSheetView       *tree_view,
7604                                     PsppSheetViewColumn *column)
7605 {
7606   PsppSheetViewColumn *left_column;
7607   PsppSheetViewColumn *cur_column = NULL;
7608   PsppSheetViewColumnReorder *reorder;
7609   gboolean rtl;
7610   GList *tmp_list;
7611   gint left;
7612
7613   /* We want to precalculate the motion list such that we know what column slots
7614    * are available.
7615    */
7616   left_column = NULL;
7617   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7618
7619   /* First, identify all possible drop spots */
7620   if (rtl)
7621     tmp_list = g_list_last (tree_view->priv->columns);
7622   else
7623     tmp_list = g_list_first (tree_view->priv->columns);
7624
7625   while (tmp_list)
7626     {
7627       cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7628       tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7629
7630       if (cur_column->visible == FALSE)
7631         continue;
7632
7633       /* If it's not the column moving and func tells us to skip over the column, we continue. */
7634       if (left_column != column && cur_column != column &&
7635           tree_view->priv->column_drop_func &&
7636           ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7637         {
7638           left_column = cur_column;
7639           continue;
7640         }
7641       reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7642       reorder->left_column = left_column;
7643       left_column = reorder->right_column = cur_column;
7644
7645       tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7646     }
7647
7648   /* Add the last one */
7649   if (tree_view->priv->column_drop_func == NULL ||
7650       ((left_column != column) &&
7651        tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7652     {
7653       reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7654       reorder->left_column = left_column;
7655       reorder->right_column = NULL;
7656       tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7657     }
7658
7659   /* We quickly check to see if it even makes sense to reorder columns. */
7660   /* If there is nothing that can be moved, then we return */
7661
7662   if (tree_view->priv->column_drag_info == NULL)
7663     return;
7664
7665   /* We know there are always 2 slots possbile, as you can always return column. */
7666   /* If that's all there is, return */
7667   if (tree_view->priv->column_drag_info->next == NULL || 
7668       (tree_view->priv->column_drag_info->next->next == NULL &&
7669        ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7670        ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7671     {
7672       for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7673         g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7674       g_list_free (tree_view->priv->column_drag_info);
7675       tree_view->priv->column_drag_info = NULL;
7676       return;
7677     }
7678   /* We fill in the ranges for the columns, now that we've isolated them */
7679   left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7680
7681   for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7682     {
7683       reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7684
7685       reorder->left_align = left;
7686       if (tmp_list->next != NULL)
7687         {
7688           g_assert (tmp_list->next->data);
7689           left = reorder->right_align = (reorder->right_column->allocation.x +
7690                                          reorder->right_column->allocation.width +
7691                                          ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7692         }
7693       else
7694         {
7695           gint width = gdk_window_get_width (tree_view->priv->header_window);
7696           reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7697         }
7698     }
7699 }
7700
7701 void
7702 _pspp_sheet_view_column_start_drag (PsppSheetView       *tree_view,
7703                                   PsppSheetViewColumn *column)
7704 {
7705   GdkEvent *send_event;
7706   GtkAllocation allocation;
7707   gint x, y;
7708   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7709   GdkDisplay *display = gdk_screen_get_display (screen);
7710
7711   g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7712   g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7713   g_return_if_fail (column->button);
7714
7715   pspp_sheet_view_set_column_drag_info (tree_view, column);
7716
7717   if (tree_view->priv->column_drag_info == NULL)
7718     return;
7719
7720   if (tree_view->priv->drag_window == NULL)
7721     {
7722       GdkWindowAttr attributes;
7723       guint attributes_mask;
7724
7725       attributes.window_type = GDK_WINDOW_CHILD;
7726       attributes.wclass = GDK_INPUT_OUTPUT;
7727       attributes.x = column->allocation.x;
7728       attributes.y = 0;
7729       attributes.width = column->allocation.width;
7730       attributes.height = column->allocation.height;
7731       attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7732       attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7733       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ;
7734
7735       tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7736                                                      &attributes,
7737                                                      attributes_mask);
7738       gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7739     }
7740
7741   gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7742   gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7743
7744   gtk_grab_remove (column->button);
7745
7746   send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7747   send_event->crossing.send_event = TRUE;
7748   send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
7749   send_event->crossing.subwindow = NULL;
7750   send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7751   send_event->crossing.time = GDK_CURRENT_TIME;
7752
7753   gtk_propagate_event (column->button, send_event);
7754   gdk_event_free (send_event);
7755
7756   send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7757   send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7758   send_event->button.send_event = TRUE;
7759   send_event->button.time = GDK_CURRENT_TIME;
7760   send_event->button.x = -1;
7761   send_event->button.y = -1;
7762   send_event->button.axes = NULL;
7763   send_event->button.state = 0;
7764   send_event->button.button = 1;
7765   send_event->button.device = 
7766     gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
7767
7768   send_event->button.x_root = 0;
7769   send_event->button.y_root = 0;
7770
7771   gtk_propagate_event (column->button, send_event);
7772   gdk_event_free (send_event);
7773
7774   /* Kids, don't try this at home */
7775   g_object_ref (column->button);
7776   gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7777   gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7778   gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7779   g_object_unref (column->button);
7780
7781   tree_view->priv->drag_column_x = column->allocation.x;
7782   allocation = column->allocation;
7783   allocation.x = 0;
7784   gtk_widget_size_allocate (column->button, &allocation);
7785   gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7786
7787   tree_view->priv->drag_column = column;
7788   gdk_window_show (tree_view->priv->drag_window);
7789
7790   gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7791
7792   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7793   while (gtk_events_pending ())
7794     gtk_main_iteration ();
7795
7796   PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7797   gdk_pointer_grab (tree_view->priv->drag_window,
7798                     FALSE,
7799                     GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7800                     NULL, NULL, GDK_CURRENT_TIME);
7801   gdk_keyboard_grab (tree_view->priv->drag_window,
7802                      FALSE,
7803                      GDK_CURRENT_TIME);
7804 }
7805
7806 void
7807 _pspp_sheet_view_queue_draw_node (PsppSheetView        *tree_view,
7808                                 int node,
7809                                 const GdkRectangle *clip_rect)
7810 {
7811   GdkRectangle rect;
7812   GtkAllocation allocation;
7813
7814   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7815     return;
7816
7817   gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
7818   rect.x = 0;
7819   rect.width = MAX (tree_view->priv->width, allocation.width);
7820
7821   rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7822   rect.height = ROW_HEIGHT (tree_view);
7823
7824   if (clip_rect)
7825     {
7826       GdkRectangle new_rect;
7827
7828       gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7829
7830       gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7831     }
7832   else
7833     {
7834       gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7835     }
7836 }
7837
7838 static void
7839 pspp_sheet_view_queue_draw_path (PsppSheetView        *tree_view,
7840                                GtkTreePath        *path,
7841                                const GdkRectangle *clip_rect)
7842 {
7843   int node = -1;
7844
7845   _pspp_sheet_view_find_node (tree_view, path, &node);
7846
7847   if (node)
7848     _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7849 }
7850
7851 static void
7852 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7853
7854 {
7855   GtkTreePath *cursor_path;
7856
7857   if ((tree_view->priv->row_count == 0) ||
7858       (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7859     return;
7860
7861   cursor_path = NULL;
7862   if (tree_view->priv->cursor)
7863     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7864
7865   if (cursor_path == NULL)
7866     {
7867       /* There's no cursor.  Move the cursor to the first selected row, if any
7868        * are selected, otherwise to the first row in the sheetview.
7869        */
7870       GList *selected_rows;
7871       GtkTreeModel *model;
7872       PsppSheetSelection *selection;
7873
7874       selection = pspp_sheet_view_get_selection (tree_view);
7875       selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7876
7877       if (selected_rows)
7878         {
7879           /* XXX we could avoid doing O(n) work to get this result */
7880           cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7881           g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7882           g_list_free (selected_rows);
7883         }
7884       else
7885         {
7886           cursor_path = gtk_tree_path_new_first ();
7887           search_first_focusable_path (tree_view, &cursor_path,
7888                                        TRUE, NULL);
7889         }
7890
7891       gtk_tree_row_reference_free (tree_view->priv->cursor);
7892       tree_view->priv->cursor = NULL;
7893
7894       if (cursor_path)
7895         {
7896           if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7897               tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7898             pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7899           else
7900             pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7901         }
7902     }
7903
7904   if (cursor_path)
7905     {
7906       /* Now find a column for the cursor. */
7907       PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7908
7909       pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7910       gtk_tree_path_free (cursor_path);
7911
7912       if (tree_view->priv->focus_column == NULL)
7913         {
7914           GList *list;
7915           for (list = tree_view->priv->columns; list; list = list->next)
7916             {
7917               if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7918                 {
7919                   tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7920                   pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7921                   pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7922                   break;
7923                 }
7924             }
7925
7926         }
7927     }
7928 }
7929
7930 static gboolean
7931 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7932                                    gint         count,
7933                                    PsppSheetSelectMode mode)
7934 {
7935   gint selection_count;
7936   int cursor_node = -1;
7937   int new_cursor_node = -1;
7938   GtkTreePath *cursor_path = NULL;
7939   gboolean grab_focus = TRUE;
7940
7941   if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7942     return FALSE;
7943
7944   cursor_path = NULL;
7945   if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7946     /* FIXME: we lost the cursor; should we get the first? */
7947     return FALSE;
7948
7949   cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7950   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7951
7952   if (cursor_node < 0)
7953     /* FIXME: we lost the cursor; should we get the first? */
7954     return FALSE;
7955
7956   selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7957
7958   if (selection_count == 0
7959       && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7960       && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7961     {
7962       /* Don't move the cursor, but just select the current node */
7963       new_cursor_node = cursor_node;
7964     }
7965   else
7966     {
7967       if (count == -1)
7968         new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7969       else
7970         new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7971     }
7972
7973   gtk_tree_path_free (cursor_path);
7974
7975   if (new_cursor_node)
7976     {
7977       cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7978
7979       search_first_focusable_path (tree_view, &cursor_path,
7980                                    (count != -1),
7981                                    &new_cursor_node);
7982
7983       if (cursor_path)
7984         gtk_tree_path_free (cursor_path);
7985     }
7986
7987   /*
7988    * If the list has only one item and multi-selection is set then select
7989    * the row (if not yet selected).
7990    */
7991   if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7992        tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7993       new_cursor_node < 0)
7994     {
7995       if (count == -1)
7996         new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7997       else
7998         new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7999
8000       if (new_cursor_node < 0
8001           && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
8002         {
8003           new_cursor_node = cursor_node;
8004         }
8005       else
8006         {
8007           new_cursor_node = -1;
8008         }
8009     }
8010
8011   if (new_cursor_node >= 0)
8012     {
8013       cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
8014       pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
8015       gtk_tree_path_free (cursor_path);
8016     }
8017   else
8018     {
8019       pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8020
8021       if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8022         {
8023           if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
8024                                           count < 0 ?
8025                                           GTK_DIR_UP : GTK_DIR_DOWN))
8026             {
8027               GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8028
8029               if (toplevel)
8030                 gtk_widget_child_focus (toplevel,
8031                                         count < 0 ?
8032                                         GTK_DIR_TAB_BACKWARD :
8033                                         GTK_DIR_TAB_FORWARD);
8034
8035               grab_focus = FALSE;
8036             }
8037         }
8038       else
8039         {
8040           gtk_widget_error_bell (GTK_WIDGET (tree_view));
8041         }
8042     }
8043
8044   if (grab_focus)
8045     gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8046
8047   return new_cursor_node >= 0;
8048 }
8049
8050 static void
8051 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
8052                                           gint         count,
8053                                           PsppSheetSelectMode mode)
8054 {
8055   int cursor_node = -1;
8056   GtkTreePath *old_cursor_path = NULL;
8057   GtkTreePath *cursor_path = NULL;
8058   int start_cursor_node = -1;
8059   gint y;
8060   gint window_y;
8061   gint vertical_separator;
8062
8063   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8064     return;
8065
8066   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8067     old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8068   else
8069     /* This is sorta weird.  Focus in should give us a cursor */
8070     return;
8071
8072   gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
8073   _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
8074
8075   if (cursor_node < 0)
8076     {
8077       /* FIXME: we lost the cursor.  Should we try to get one? */
8078       gtk_tree_path_free (old_cursor_path);
8079       return;
8080     }
8081
8082   y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8083   window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
8084   y += tree_view->priv->cursor_offset;
8085   y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
8086   y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment),  (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
8087
8088   if (y >= tree_view->priv->height)
8089     y = tree_view->priv->height - 1;
8090
8091   tree_view->priv->cursor_offset =
8092     pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
8093
8094   if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
8095     {
8096       cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
8097       tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
8098     }
8099
8100   y -= tree_view->priv->cursor_offset;
8101   cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8102
8103   start_cursor_node = cursor_node;
8104
8105   if (! search_first_focusable_path (tree_view, &cursor_path,
8106                                      (count != -1),
8107                                      &cursor_node))
8108     {
8109       /* It looks like we reached the end of the view without finding
8110        * a focusable row.  We will step backwards to find the last
8111        * focusable row.
8112        */
8113       cursor_node = start_cursor_node;
8114       cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8115
8116       search_first_focusable_path (tree_view, &cursor_path,
8117                                    (count == -1),
8118                                    &cursor_node);
8119     }
8120
8121   if (!cursor_path)
8122     goto cleanup;
8123
8124   /* update y */
8125   y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8126
8127   pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
8128
8129   y -= window_y;
8130   pspp_sheet_view_scroll_to_point (tree_view, -1, y);
8131   pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8132   _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8133
8134   if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
8135     gtk_widget_error_bell (GTK_WIDGET (tree_view));
8136
8137   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8138
8139 cleanup:
8140   gtk_tree_path_free (old_cursor_path);
8141   gtk_tree_path_free (cursor_path);
8142 }
8143
8144 static void
8145 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8146                                         gint         count,
8147                                         PsppSheetSelectMode mode)
8148 {
8149   int cursor_node = -1;
8150   GtkTreePath *cursor_path = NULL;
8151   PsppSheetViewColumn *column;
8152   GtkTreeIter iter;
8153   GList *list;
8154   gboolean found_column = FALSE;
8155   gboolean rtl;
8156
8157   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8158
8159   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8160     return;
8161
8162   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8163     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8164   else
8165     return;
8166
8167   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8168   if (cursor_node < 0)
8169     return;
8170   if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8171     {
8172       gtk_tree_path_free (cursor_path);
8173       return;
8174     }
8175   gtk_tree_path_free (cursor_path);
8176
8177   list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8178   if (tree_view->priv->focus_column)
8179     {
8180       for (; list; list = (rtl ? list->prev : list->next))
8181         {
8182           if (list->data == tree_view->priv->focus_column)
8183             break;
8184         }
8185     }
8186
8187   while (list)
8188     {
8189       gboolean left, right;
8190
8191       column = list->data;
8192       if (column->visible == FALSE || column->row_head)
8193         goto loop_end;
8194
8195       pspp_sheet_view_column_cell_set_cell_data (column,
8196                                                tree_view->priv->model,
8197                                                &iter);
8198
8199       if (rtl)
8200         {
8201           right = list->prev ? TRUE : FALSE;
8202           left = list->next ? TRUE : FALSE;
8203         }
8204       else
8205         {
8206           left = list->prev ? TRUE : FALSE;
8207           right = list->next ? TRUE : FALSE;
8208         }
8209
8210       if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8211         {
8212           tree_view->priv->focus_column = column;
8213           found_column = TRUE;
8214           break;
8215         }
8216     loop_end:
8217       if (count == 1)
8218         list = rtl ? list->prev : list->next;
8219       else
8220         list = rtl ? list->next : list->prev;
8221     }
8222
8223   if (found_column)
8224     {
8225       _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8226       g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8227       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8228     }
8229   else
8230     {
8231       gtk_widget_error_bell (GTK_WIDGET (tree_view));
8232     }
8233
8234   pspp_sheet_view_clamp_column_visible (tree_view,
8235                                       tree_view->priv->focus_column, TRUE);
8236 }
8237
8238 static void
8239 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8240                                             gint         count,
8241                                             PsppSheetSelectMode mode)
8242 {
8243   int cursor_node = -1;
8244   GtkTreePath *cursor_path = NULL;
8245   PsppSheetViewColumn *column;
8246   PsppSheetViewColumn *found_column;
8247   GtkTreeIter iter;
8248   GList *list;
8249   gboolean rtl;
8250
8251   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8252
8253   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8254     return;
8255
8256   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8257     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8258   else
8259     return;
8260
8261   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8262   if (cursor_node < 0)
8263     return;
8264   if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8265     {
8266       gtk_tree_path_free (cursor_path);
8267       return;
8268     }
8269   gtk_tree_path_free (cursor_path);
8270
8271   list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8272   if (tree_view->priv->focus_column)
8273     {
8274       for (; list; list = (rtl ? list->prev : list->next))
8275         {
8276           if (list->data == tree_view->priv->focus_column)
8277             break;
8278         }
8279     }
8280
8281   found_column = NULL;
8282   while (list)
8283     {
8284       gboolean left, right;
8285
8286       column = list->data;
8287       if (column->visible == FALSE || column->row_head)
8288         goto loop_end;
8289
8290       pspp_sheet_view_column_cell_set_cell_data (column,
8291                                                tree_view->priv->model,
8292                                                &iter);
8293
8294       if (rtl)
8295         {
8296           right = list->prev ? TRUE : FALSE;
8297           left = list->next ? TRUE : FALSE;
8298         }
8299       else
8300         {
8301           left = list->prev ? TRUE : FALSE;
8302           right = list->next ? TRUE : FALSE;
8303         }
8304
8305       if (column->tabbable
8306           && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8307         found_column = column;
8308
8309     loop_end:
8310       if (count == 1)
8311         list = rtl ? list->prev : list->next;
8312       else
8313         list = rtl ? list->next : list->prev;
8314     }
8315
8316   if (found_column)
8317     {
8318       tree_view->priv->focus_column = found_column;
8319       _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8320       g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8321       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8322     }
8323
8324   pspp_sheet_view_clamp_column_visible (tree_view,
8325                                       tree_view->priv->focus_column, TRUE);
8326 }
8327
8328 static gboolean
8329 try_move_cursor_tab (PsppSheetView *tree_view,
8330                      gboolean start_at_focus_column,
8331                      gint count)
8332 {
8333   PsppSheetViewColumn *column;
8334   GtkTreeIter iter;
8335   int cursor_node = -1;
8336   GtkTreePath *cursor_path = NULL;
8337   gboolean rtl;
8338   GList *list;
8339
8340   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8341     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8342   else
8343     return TRUE;
8344
8345   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8346   if (cursor_node < 0)
8347     return TRUE;
8348   if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8349     {
8350       gtk_tree_path_free (cursor_path);
8351       return TRUE;
8352     }
8353   gtk_tree_path_free (cursor_path);
8354
8355   rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8356   if (start_at_focus_column)
8357     {
8358       list = (rtl
8359               ? g_list_last (tree_view->priv->columns)
8360               : g_list_first (tree_view->priv->columns));
8361       if (tree_view->priv->focus_column)
8362         {
8363           for (; list; list = (rtl ? list->prev : list->next))
8364             {
8365               if (list->data == tree_view->priv->focus_column)
8366                 break;
8367             }
8368         }
8369     }
8370   else
8371     {
8372       list = (rtl ^ (count == 1)
8373               ? g_list_first (tree_view->priv->columns)
8374               : g_list_last (tree_view->priv->columns));
8375     }
8376
8377   while (list)
8378     {
8379       gboolean left, right;
8380
8381       column = list->data;
8382       if (column->visible == FALSE || !column->tabbable)
8383         goto loop_end;
8384
8385       pspp_sheet_view_column_cell_set_cell_data (column,
8386                                                  tree_view->priv->model,
8387                                                  &iter);
8388
8389       if (rtl)
8390         {
8391           right = list->prev ? TRUE : FALSE;
8392           left = list->next ? TRUE : FALSE;
8393         }
8394       else
8395         {
8396           left = list->prev ? TRUE : FALSE;
8397           right = list->next ? TRUE : FALSE;
8398         }
8399
8400       if (column->tabbable
8401           && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8402         {
8403           tree_view->priv->focus_column = column;
8404           _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8405           g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8406           gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8407           return TRUE;
8408         }
8409     loop_end:
8410       if (count == 1)
8411         list = rtl ? list->prev : list->next;
8412       else
8413         list = rtl ? list->next : list->prev;
8414     }
8415
8416   return FALSE;
8417 }
8418
8419 static void
8420 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8421                                  gint         count)
8422 {
8423   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8424     return;
8425
8426   if (!try_move_cursor_tab (tree_view, TRUE, count))
8427     {
8428       if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8429           && !try_move_cursor_tab (tree_view, FALSE, count))
8430         gtk_widget_error_bell (GTK_WIDGET (tree_view));
8431     }
8432
8433   pspp_sheet_view_clamp_column_visible (tree_view,
8434                                         tree_view->priv->focus_column, TRUE);
8435 }
8436
8437 static void
8438 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8439                                        gint         count,
8440                                        PsppSheetSelectMode mode)
8441 {
8442   int cursor_node;
8443   GtkTreePath *path;
8444   GtkTreePath *old_path;
8445
8446   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8447     return;
8448
8449   g_return_if_fail (tree_view->priv->row_count > 0);
8450
8451   pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8452
8453   if (count == -1)
8454     {
8455       /* Now go forward to find the first focusable row. */
8456       path = _pspp_sheet_view_find_path (tree_view, 0);
8457       search_first_focusable_path (tree_view, &path,
8458                                    TRUE, &cursor_node);
8459     }
8460   else
8461     {
8462       /* Now go backwards to find last focusable row. */
8463       path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8464       search_first_focusable_path (tree_view, &path,
8465                                    FALSE, &cursor_node);
8466     }
8467
8468   if (!path)
8469     goto cleanup;
8470
8471   if (gtk_tree_path_compare (old_path, path))
8472     {
8473       pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8474       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8475     }
8476   else
8477     {
8478       gtk_widget_error_bell (GTK_WIDGET (tree_view));
8479     }
8480
8481 cleanup:
8482   gtk_tree_path_free (old_path);
8483   gtk_tree_path_free (path);
8484 }
8485
8486 static gboolean
8487 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8488 {
8489   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8490     return FALSE;
8491
8492   if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8493       tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8494     return FALSE;
8495
8496   pspp_sheet_selection_select_all (tree_view->priv->selection);
8497
8498   return TRUE;
8499 }
8500
8501 static gboolean
8502 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8503 {
8504   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8505     return FALSE;
8506
8507   if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8508       tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8509     return FALSE;
8510
8511   pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8512
8513   return TRUE;
8514 }
8515
8516 static gboolean
8517 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8518                                         gboolean     start_editing,
8519                                         PsppSheetSelectMode mode)
8520 {
8521   int new_node = -1;
8522   int cursor_node = -1;
8523   GtkTreePath *cursor_path = NULL;
8524
8525   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8526     return FALSE;
8527
8528   if (tree_view->priv->cursor)
8529     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8530
8531   if (cursor_path == NULL)
8532     return FALSE;
8533
8534   _pspp_sheet_view_find_node (tree_view, cursor_path,
8535                               &cursor_node);
8536
8537   if (cursor_node < 0)
8538     {
8539       gtk_tree_path_free (cursor_path);
8540       return FALSE;
8541     }
8542
8543   if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8544       tree_view->priv->focus_column)
8545     {
8546       if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8547         {
8548           gtk_tree_path_free (cursor_path);
8549           return TRUE;
8550         }
8551     }
8552
8553   _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8554                                             cursor_node,
8555                                             cursor_path,
8556                                             mode,
8557                                             FALSE);
8558
8559   /* We bail out if the original (tree, node) don't exist anymore after
8560    * handling the selection-changed callback.  We do return TRUE because
8561    * the key press has been handled at this point.
8562    */
8563   _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8564
8565   if (cursor_node != new_node)
8566     return FALSE;
8567
8568   pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8569
8570   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8571   _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8572
8573   if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8574     pspp_sheet_view_row_activated (tree_view, cursor_path,
8575                                  tree_view->priv->focus_column);
8576     
8577   gtk_tree_path_free (cursor_path);
8578
8579   return TRUE;
8580 }
8581
8582 static gboolean
8583 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8584 {
8585   int new_node = -1;
8586   int cursor_node = -1;
8587   GtkTreePath *cursor_path = NULL;
8588
8589   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8590     return FALSE;
8591
8592   cursor_path = NULL;
8593   if (tree_view->priv->cursor)
8594     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8595
8596   if (cursor_path == NULL)
8597     return FALSE;
8598
8599   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8600   if (cursor_node < 0)
8601     {
8602       gtk_tree_path_free (cursor_path);
8603       return FALSE;
8604     }
8605
8606   _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8607                                             cursor_node,
8608                                             cursor_path,
8609                                             PSPP_SHEET_SELECT_MODE_TOGGLE,
8610                                             FALSE);
8611
8612   /* We bail out if the original (tree, node) don't exist anymore after
8613    * handling the selection-changed callback.  We do return TRUE because
8614    * the key press has been handled at this point.
8615    */
8616   _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8617
8618   if (cursor_node != new_node)
8619     return FALSE;
8620
8621   pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8622
8623   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8624   pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8625   gtk_tree_path_free (cursor_path);
8626
8627   return TRUE;
8628 }
8629
8630 static gboolean
8631 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8632 {
8633   pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8634   tree_view->priv->typeselect_flush_timeout = 0;
8635
8636   return FALSE;
8637 }
8638
8639 /* Cut and paste from gtkwindow.c */
8640 static void
8641 send_focus_change (GtkWidget *widget,
8642                    gboolean   in)
8643 {
8644   GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8645
8646   fevent->focus_change.type = GDK_FOCUS_CHANGE;
8647   fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
8648   fevent->focus_change.in = in;
8649   
8650   gtk_widget_send_focus_change (widget, fevent);
8651   gdk_event_free (fevent);
8652 }
8653
8654 static void
8655 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8656 {
8657   GtkWidget *frame, *vbox, *toplevel;
8658   GdkScreen *screen;
8659
8660   if (tree_view->priv->search_custom_entry_set)
8661     return;
8662
8663   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8664   screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8665
8666    if (tree_view->priv->search_window != NULL)
8667      {
8668        if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8669          gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8670                                       GTK_WINDOW (tree_view->priv->search_window));
8671        else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)))
8672          gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
8673                                          GTK_WINDOW (tree_view->priv->search_window));
8674        gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8675        return;
8676      }
8677    
8678   tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8679   gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8680
8681   if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8682     gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8683                                  GTK_WINDOW (tree_view->priv->search_window));
8684
8685   gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8686                             GDK_WINDOW_TYPE_HINT_UTILITY);
8687   gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8688   g_signal_connect (tree_view->priv->search_window, "delete-event",
8689                     G_CALLBACK (pspp_sheet_view_search_delete_event),
8690                     tree_view);
8691   g_signal_connect (tree_view->priv->search_window, "key-press-event",
8692                     G_CALLBACK (pspp_sheet_view_search_key_press_event),
8693                     tree_view);
8694   g_signal_connect (tree_view->priv->search_window, "button-press-event",
8695                     G_CALLBACK (pspp_sheet_view_search_button_press_event),
8696                     tree_view);
8697   g_signal_connect (tree_view->priv->search_window, "scroll-event",
8698                     G_CALLBACK (pspp_sheet_view_search_scroll_event),
8699                     tree_view);
8700
8701   frame = gtk_frame_new (NULL);
8702   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8703   gtk_widget_show (frame);
8704   gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8705
8706   vbox = gtk_vbox_new (FALSE, 0);
8707   gtk_widget_show (vbox);
8708   gtk_container_add (GTK_CONTAINER (frame), vbox);
8709   gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8710
8711   /* add entry */
8712   tree_view->priv->search_entry = gtk_entry_new ();
8713   gtk_widget_show (tree_view->priv->search_entry);
8714   g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8715                     G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8716                     tree_view);
8717   g_signal_connect (tree_view->priv->search_entry,
8718                     "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8719                     tree_view);
8720
8721 #if GTK3_TRANSITION
8722   g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8723                     "preedit-changed",
8724                     G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8725                     tree_view);
8726 #endif
8727
8728   gtk_container_add (GTK_CONTAINER (vbox),
8729                      tree_view->priv->search_entry);
8730
8731   gtk_widget_realize (tree_view->priv->search_entry);
8732 }
8733
8734 /* Pops up the interactive search entry.  If keybinding is TRUE then the user
8735  * started this by typing the start_interactive_search keybinding.  Otherwise, it came from 
8736  */
8737 static gboolean
8738 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8739                                              gboolean     keybinding)
8740 {
8741   /* We only start interactive search if we have focus or the columns
8742    * have focus.  If one of our children have focus, we don't want to
8743    * start the search.
8744    */
8745   GList *list;
8746   gboolean found_focus = FALSE;
8747   GtkWidgetClass *entry_parent_class;
8748   
8749   if (!tree_view->priv->enable_search && !keybinding)
8750     return FALSE;
8751
8752   if (tree_view->priv->search_custom_entry_set)
8753     return FALSE;
8754
8755   if (tree_view->priv->search_window != NULL &&
8756       gtk_widget_get_visible (tree_view->priv->search_window))
8757     return TRUE;
8758
8759   for (list = tree_view->priv->columns; list; list = list->next)
8760     {
8761       PsppSheetViewColumn *column;
8762
8763       column = list->data;
8764       if (! column->visible)
8765         continue;
8766
8767       if (column->button && gtk_widget_has_focus (column->button))
8768         {
8769           found_focus = TRUE;
8770           break;
8771         }
8772     }
8773   
8774   if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8775     found_focus = TRUE;
8776
8777   if (!found_focus)
8778     return FALSE;
8779
8780   if (tree_view->priv->search_column < 0)
8781     return FALSE;
8782
8783   pspp_sheet_view_ensure_interactive_directory (tree_view);
8784
8785   if (keybinding)
8786     gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8787
8788   /* done, show it */
8789   tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8790   gtk_widget_show (tree_view->priv->search_window);
8791   if (tree_view->priv->search_entry_changed_id == 0)
8792     {
8793       tree_view->priv->search_entry_changed_id =
8794         g_signal_connect (tree_view->priv->search_entry, "changed",
8795                           G_CALLBACK (pspp_sheet_view_search_init),
8796                           tree_view);
8797     }
8798
8799   tree_view->priv->typeselect_flush_timeout =
8800     gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8801                    (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8802                    tree_view);
8803
8804   /* Grab focus will select all the text.  We don't want that to happen, so we
8805    * call the parent instance and bypass the selection change.  This is probably
8806    * really non-kosher. */
8807   entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8808   (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8809
8810   /* send focus-in event */
8811   send_focus_change (tree_view->priv->search_entry, TRUE);
8812
8813   /* search first matching iter */
8814   pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8815
8816   return TRUE;
8817 }
8818
8819 static gboolean
8820 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8821 {
8822   return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8823 }
8824
8825 /* this function returns the new width of the column being resized given
8826  * the column and x position of the cursor; the x cursor position is passed
8827  * in as a pointer and automagicly corrected if it's beyond min/max limits
8828  */
8829 static gint
8830 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8831                                 gint       i,
8832                                 gint      *x)
8833 {
8834   PsppSheetViewColumn *column;
8835   gint width;
8836   gboolean rtl;
8837
8838   /* first translate the x position from gtk_widget_get_window (widget)
8839    * to clist->clist_window
8840    */
8841   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8842   column = g_list_nth (tree_view->priv->columns, i)->data;
8843   width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8844  
8845   /* Clamp down the value */
8846   if (column->min_width == -1)
8847     width = MAX (column->button_request, width);
8848   else
8849     width = MAX (column->min_width, width);
8850   if (column->max_width != -1)
8851     width = MIN (width, column->max_width);
8852
8853   *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8854  
8855   return width;
8856 }
8857
8858
8859 /* FIXME this adjust_allocation is a big cut-and-paste from
8860  * GtkCList, needs to be some "official" way to do this
8861  * factored out.
8862  */
8863 typedef struct
8864 {
8865   GdkWindow *window;
8866   int dx;
8867   int dy;
8868 } ScrollData;
8869
8870 /* The window to which gtk_widget_get_window (widget) is relative */
8871 #define ALLOCATION_WINDOW(widget)               \
8872    (!gtk_widget_get_has_window (widget) ?               \
8873     gtk_widget_get_window (widget) :                          \
8874     gdk_window_get_parent (gtk_widget_get_window (widget)))
8875
8876 static void
8877 adjust_allocation_recurse (GtkWidget *widget,
8878                            gpointer   data)
8879 {
8880   ScrollData *scroll_data = data;
8881   GtkAllocation allocation;
8882   gtk_widget_get_allocation (widget, &allocation);
8883   /* Need to really size allocate instead of just poking
8884    * into widget->allocation if the widget is not realized.
8885    * FIXME someone figure out why this was.
8886    */
8887   if (!gtk_widget_get_realized (widget))
8888     {
8889       if (gtk_widget_get_visible (widget))
8890         {
8891           GdkRectangle tmp_rectangle = allocation;
8892           tmp_rectangle.x += scroll_data->dx;
8893           tmp_rectangle.y += scroll_data->dy;
8894           
8895           gtk_widget_size_allocate (widget, &tmp_rectangle);
8896         }
8897     }
8898   else
8899     {
8900       if (ALLOCATION_WINDOW (widget) == scroll_data->window)
8901         {
8902           allocation.x += scroll_data->dx;
8903           allocation.y += scroll_data->dy;
8904           
8905           if (GTK_IS_CONTAINER (widget))
8906             gtk_container_forall (GTK_CONTAINER (widget),
8907                                   adjust_allocation_recurse,
8908                                   data);
8909         }
8910     }
8911 }
8912
8913 static void
8914 adjust_allocation (GtkWidget *widget,
8915                    int        dx,
8916                    int        dy)
8917 {
8918   ScrollData scroll_data;
8919
8920   if (gtk_widget_get_realized (widget))
8921     scroll_data.window = ALLOCATION_WINDOW (widget);
8922   else
8923     scroll_data.window = NULL;
8924     
8925   scroll_data.dx = dx;
8926   scroll_data.dy = dy;
8927   
8928   adjust_allocation_recurse (widget, &scroll_data);
8929 }
8930
8931 void 
8932 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8933
8934 /* Callbacks */
8935 static void
8936 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8937                                   PsppSheetView   *tree_view)
8938 {
8939   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8940     {
8941       GList *list;
8942       gint dy;
8943         
8944       gdk_window_move (tree_view->priv->bin_window,
8945                        - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8946                        TREE_VIEW_HEADER_HEIGHT (tree_view));
8947       gdk_window_move (tree_view->priv->header_window,
8948                        - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8949                        0);
8950       dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8951       if (dy)
8952         {
8953           update_prelight (tree_view,
8954                            tree_view->priv->event_last_x,
8955                            tree_view->priv->event_last_y - dy);
8956
8957           if (tree_view->priv->edited_column &&
8958               GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget))
8959             {
8960               GList *list;
8961               GtkWidget *widget;
8962               PsppSheetViewChild *child = NULL;
8963
8964               widget = GTK_WIDGET (tree_view->priv->edited_column->editable_widget);
8965               adjust_allocation (widget, 0, dy); 
8966               
8967               for (list = tree_view->priv->children; list; list = list->next)
8968                 {
8969                   child = (PsppSheetViewChild *)list->data;
8970                   if (child->widget == widget)
8971                     {
8972                       child->y += dy;
8973                       break;
8974                     }
8975                 }
8976             }
8977         }
8978       gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8979
8980       if (tree_view->priv->dy != (int) gtk_adjustment_get_value (tree_view->priv->vadjustment))
8981         {
8982           /* update our dy and top_row */
8983           tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8984
8985           if (!tree_view->priv->in_top_row_to_dy)
8986             pspp_sheet_view_dy_to_top_row (tree_view);
8987         }
8988     }
8989 }
8990
8991 \f
8992
8993 /* Public methods
8994  */
8995
8996 /**
8997  * pspp_sheet_view_new:
8998  *
8999  * Creates a new #PsppSheetView widget.
9000  *
9001  * Return value: A newly created #PsppSheetView widget.
9002  **/
9003 GtkWidget *
9004 pspp_sheet_view_new (void)
9005 {
9006   return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
9007 }
9008
9009 /**
9010  * pspp_sheet_view_new_with_model:
9011  * @model: the model.
9012  *
9013  * Creates a new #PsppSheetView widget with the model initialized to @model.
9014  *
9015  * Return value: A newly created #PsppSheetView widget.
9016  **/
9017 GtkWidget *
9018 pspp_sheet_view_new_with_model (GtkTreeModel *model)
9019 {
9020   return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
9021 }
9022
9023 /* Public Accessors
9024  */
9025
9026 /**
9027  * pspp_sheet_view_get_model:
9028  * @tree_view: a #PsppSheetView
9029  *
9030  * Returns the model the #PsppSheetView is based on.  Returns %NULL if the
9031  * model is unset.
9032  *
9033  * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
9034  **/
9035 GtkTreeModel *
9036 pspp_sheet_view_get_model (PsppSheetView *tree_view)
9037 {
9038   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9039
9040   return tree_view->priv->model;
9041 }
9042
9043 /**
9044  * pspp_sheet_view_set_model:
9045  * @tree_view: A #GtkTreeNode.
9046  * @model: (allow-none): The model.
9047  *
9048  * Sets the model for a #PsppSheetView.  If the @tree_view already has a model
9049  * set, it will remove it before setting the new model.  If @model is %NULL,
9050  * then it will unset the old model.
9051  **/
9052 void
9053 pspp_sheet_view_set_model (PsppSheetView  *tree_view,
9054                          GtkTreeModel *model)
9055 {
9056   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9057   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
9058
9059   if (model == tree_view->priv->model)
9060     return;
9061
9062   if (tree_view->priv->scroll_to_path)
9063     {
9064       gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9065       tree_view->priv->scroll_to_path = NULL;
9066     }
9067
9068   if (tree_view->priv->model)
9069     {
9070       GList *tmplist = tree_view->priv->columns;
9071
9072       if (tree_view->priv->selected)
9073         range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
9074       pspp_sheet_view_stop_editing (tree_view, TRUE);
9075
9076       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9077                                             pspp_sheet_view_row_changed,
9078                                             tree_view);
9079       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9080                                             pspp_sheet_view_row_inserted,
9081                                             tree_view);
9082       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9083                                             pspp_sheet_view_row_deleted,
9084                                             tree_view);
9085       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9086                                             pspp_sheet_view_rows_reordered,
9087                                             tree_view);
9088
9089       for (; tmplist; tmplist = tmplist->next)
9090         _pspp_sheet_view_column_unset_model (tmplist->data,
9091                                            tree_view->priv->model);
9092
9093       tree_view->priv->prelight_node = -1;
9094
9095       gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
9096       tree_view->priv->drag_dest_row = NULL;
9097       gtk_tree_row_reference_free (tree_view->priv->cursor);
9098       tree_view->priv->cursor = NULL;
9099       gtk_tree_row_reference_free (tree_view->priv->anchor);
9100       tree_view->priv->anchor = NULL;
9101       gtk_tree_row_reference_free (tree_view->priv->top_row);
9102       tree_view->priv->top_row = NULL;
9103       gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9104       tree_view->priv->scroll_to_path = NULL;
9105
9106       tree_view->priv->scroll_to_column = NULL;
9107
9108       g_object_unref (tree_view->priv->model);
9109
9110       tree_view->priv->search_column = -1;
9111       tree_view->priv->fixed_height = -1;
9112       tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
9113       tree_view->priv->last_button_x = -1;
9114       tree_view->priv->last_button_y = -1;
9115     }
9116
9117   tree_view->priv->model = model;
9118
9119   if (tree_view->priv->model)
9120     {
9121       gint i;
9122
9123       if (tree_view->priv->search_column == -1)
9124         {
9125           for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
9126             {
9127               GType type = gtk_tree_model_get_column_type (model, i);
9128
9129               if (g_value_type_transformable (type, G_TYPE_STRING))
9130                 {
9131                   tree_view->priv->search_column = i;
9132                   break;
9133                 }
9134             }
9135         }
9136
9137       g_object_ref (tree_view->priv->model);
9138       g_signal_connect (tree_view->priv->model,
9139                         "row-changed",
9140                         G_CALLBACK (pspp_sheet_view_row_changed),
9141                         tree_view);
9142       g_signal_connect (tree_view->priv->model,
9143                         "row-inserted",
9144                         G_CALLBACK (pspp_sheet_view_row_inserted),
9145                         tree_view);
9146       g_signal_connect (tree_view->priv->model,
9147                         "row-deleted",
9148                         G_CALLBACK (pspp_sheet_view_row_deleted),
9149                         tree_view);
9150       g_signal_connect (tree_view->priv->model,
9151                         "rows-reordered",
9152                         G_CALLBACK (pspp_sheet_view_rows_reordered),
9153                         tree_view);
9154
9155       tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
9156
9157       /*  FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
9158       install_presize_handler (tree_view);
9159     }
9160
9161   g_object_notify (G_OBJECT (tree_view), "model");
9162
9163   if (tree_view->priv->selection)
9164     _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
9165
9166   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9167     gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9168 }
9169
9170 /**
9171  * pspp_sheet_view_get_selection:
9172  * @tree_view: A #PsppSheetView.
9173  *
9174  * Gets the #PsppSheetSelection associated with @tree_view.
9175  *
9176  * Return value: A #PsppSheetSelection object.
9177  **/
9178 PsppSheetSelection *
9179 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
9180 {
9181   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9182
9183   return tree_view->priv->selection;
9184 }
9185
9186 /**
9187  * pspp_sheet_view_get_hadjustment:
9188  * @tree_view: A #PsppSheetView
9189  *
9190  * Gets the #GtkAdjustment currently being used for the horizontal aspect.
9191  *
9192  * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9193  * used.
9194  **/
9195 GtkAdjustment *
9196 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
9197 {
9198   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9199
9200   return pspp_sheet_view_do_get_hadjustment (tree_view);
9201 }
9202
9203 static GtkAdjustment *
9204 pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view)
9205 {
9206   return tree_view->priv->hadjustment;
9207 }
9208
9209 /**
9210  * pspp_sheet_view_set_hadjustment:
9211  * @tree_view: A #PsppSheetView
9212  * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9213  *
9214  * Sets the #GtkAdjustment for the current horizontal aspect.
9215  **/
9216 void
9217 pspp_sheet_view_set_hadjustment (PsppSheetView   *tree_view,
9218                                GtkAdjustment *adjustment)
9219 {
9220   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9221
9222   pspp_sheet_view_set_adjustments (tree_view,
9223                                  adjustment,
9224                                  tree_view->priv->vadjustment);
9225
9226   g_object_notify (G_OBJECT (tree_view), "hadjustment");
9227 }
9228
9229 static void
9230 pspp_sheet_view_do_set_hadjustment (PsppSheetView   *tree_view,
9231                                   GtkAdjustment *adjustment)
9232 {
9233   PsppSheetViewPrivate *priv = tree_view->priv;
9234
9235   if (adjustment && priv->hadjustment == adjustment)
9236     return;
9237
9238   if (priv->hadjustment != NULL)
9239     {
9240       g_signal_handlers_disconnect_by_func (priv->hadjustment,
9241                                             pspp_sheet_view_adjustment_changed,
9242                                             tree_view);
9243       g_object_unref (priv->hadjustment);
9244     }
9245
9246   if (adjustment == NULL)
9247     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9248                                      0.0, 0.0, 0.0);
9249
9250   g_signal_connect (adjustment, "value-changed",
9251                     G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9252   priv->hadjustment = g_object_ref_sink (adjustment);
9253   /* FIXME: Adjustment should probably be populated here with fresh values, but
9254    * internal details are too complicated for me to decipher right now.
9255    */
9256   pspp_sheet_view_adjustment_changed (NULL, tree_view);
9257
9258   g_object_notify (G_OBJECT (tree_view), "hadjustment");
9259 }
9260
9261 /**
9262  * pspp_sheet_view_get_vadjustment:
9263  * @tree_view: A #PsppSheetView
9264  *
9265  * Gets the #GtkAdjustment currently being used for the vertical aspect.
9266  *
9267  * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9268  *     if none is currently being used.
9269  *
9270  * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9271  **/
9272 GtkAdjustment *
9273 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9274 {
9275   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9276
9277   return pspp_sheet_view_do_get_vadjustment (tree_view);
9278 }
9279
9280 static GtkAdjustment *
9281 pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view)
9282 {
9283   return tree_view->priv->vadjustment;
9284 }
9285
9286 /**
9287  * pspp_sheet_view_set_vadjustment:
9288  * @tree_view: A #PsppSheetView
9289  * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9290  *
9291  * Sets the #GtkAdjustment for the current vertical aspect.
9292  *
9293  * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9294  **/
9295 void
9296 pspp_sheet_view_set_vadjustment (PsppSheetView   *tree_view,
9297                                GtkAdjustment *adjustment)
9298 {
9299   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9300   g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
9301
9302   pspp_sheet_view_do_set_vadjustment (tree_view, adjustment);
9303 }
9304
9305 static void
9306 pspp_sheet_view_do_set_vadjustment (PsppSheetView   *tree_view,
9307                                   GtkAdjustment *adjustment)
9308 {
9309   PsppSheetViewPrivate *priv = tree_view->priv;
9310
9311   if (adjustment && priv->vadjustment == adjustment)
9312     return;
9313
9314   if (priv->vadjustment != NULL)
9315     {
9316       g_signal_handlers_disconnect_by_func (priv->vadjustment,
9317                                             pspp_sheet_view_adjustment_changed,
9318                                             tree_view);
9319       g_object_unref (priv->vadjustment);
9320     }
9321
9322   if (adjustment == NULL)
9323     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9324                                      0.0, 0.0, 0.0);
9325
9326   g_signal_connect (adjustment, "value-changed",
9327                     G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9328   priv->vadjustment = g_object_ref_sink (adjustment);
9329   /* FIXME: Adjustment should probably be populated here with fresh values, but
9330    * internal details are too complicated for me to decipher right now.
9331    */
9332   pspp_sheet_view_adjustment_changed (NULL, tree_view);
9333   g_object_notify (G_OBJECT (tree_view), "vadjustment");
9334 }
9335
9336 /* Column and header operations */
9337
9338 /**
9339  * pspp_sheet_view_get_headers_visible:
9340  * @tree_view: A #PsppSheetView.
9341  *
9342  * Returns %TRUE if the headers on the @tree_view are visible.
9343  *
9344  * Return value: Whether the headers are visible or not.
9345  **/
9346 gboolean
9347 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9348 {
9349   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9350
9351   return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9352 }
9353
9354 /**
9355  * pspp_sheet_view_set_headers_visible:
9356  * @tree_view: A #PsppSheetView.
9357  * @headers_visible: %TRUE if the headers are visible
9358  *
9359  * Sets the visibility state of the headers.
9360  **/
9361 void
9362 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9363                                    gboolean     headers_visible)
9364 {
9365   gint x, y;
9366   GList *list;
9367   PsppSheetViewColumn *column;
9368   GtkAllocation allocation;
9369
9370   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9371
9372   gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9373
9374   headers_visible = !! headers_visible;
9375
9376   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9377     return;
9378
9379   if (headers_visible)
9380     PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9381   else
9382     PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9383
9384   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9385     {
9386       gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9387       if (headers_visible)
9388         {
9389           gdk_window_move_resize (tree_view->priv->bin_window, x, y  + TREE_VIEW_HEADER_HEIGHT (tree_view), 
9390                                   tree_view->priv->width, allocation.height -  + TREE_VIEW_HEADER_HEIGHT (tree_view));
9391
9392           if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9393             pspp_sheet_view_map_buttons (tree_view);
9394         }
9395       else
9396         {
9397           gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9398
9399           for (list = tree_view->priv->columns; list; list = list->next)
9400             {
9401               column = list->data;
9402               if (column->button)
9403                 gtk_widget_unmap (column->button);
9404             }
9405           gdk_window_hide (tree_view->priv->header_window);
9406         }
9407     }
9408
9409   gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view));
9410   gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2);
9411   gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
9412   gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height);
9413   gtk_adjustment_changed (tree_view->priv->vadjustment);
9414
9415   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9416
9417   g_object_notify (G_OBJECT (tree_view), "headers-visible");
9418 }
9419
9420 /**
9421  * pspp_sheet_view_columns_autosize:
9422  * @tree_view: A #PsppSheetView.
9423  *
9424  * Resizes all columns to their optimal width. Only works after the
9425  * treeview has been realized.
9426  **/
9427 void
9428 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9429 {
9430   gboolean dirty = FALSE;
9431   GList *list;
9432   PsppSheetViewColumn *column;
9433
9434   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9435
9436   for (list = tree_view->priv->columns; list; list = list->next)
9437     {
9438       column = list->data;
9439       _pspp_sheet_view_column_cell_set_dirty (column);
9440       dirty = TRUE;
9441     }
9442
9443   if (dirty)
9444     gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9445 }
9446
9447 /**
9448  * pspp_sheet_view_set_headers_clickable:
9449  * @tree_view: A #PsppSheetView.
9450  * @setting: %TRUE if the columns are clickable.
9451  *
9452  * Allow the column title buttons to be clicked.
9453  **/
9454 void
9455 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9456                                      gboolean   setting)
9457 {
9458   GList *list;
9459
9460   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9461
9462   for (list = tree_view->priv->columns; list; list = list->next)
9463     pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9464
9465   g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9466 }
9467
9468
9469 /**
9470  * pspp_sheet_view_get_headers_clickable:
9471  * @tree_view: A #PsppSheetView.
9472  *
9473  * Returns whether all header columns are clickable.
9474  *
9475  * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9476  *
9477  * Since: 2.10
9478  **/
9479 gboolean 
9480 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9481 {
9482   GList *list;
9483   
9484   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9485
9486   for (list = tree_view->priv->columns; list; list = list->next)
9487     if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9488       return FALSE;
9489
9490   return TRUE;
9491 }
9492
9493 /**
9494  * pspp_sheet_view_set_rules_hint
9495  * @tree_view: a #PsppSheetView
9496  * @setting: %TRUE if the tree requires reading across rows
9497  *
9498  * This function tells GTK+ that the user interface for your
9499  * application requires users to read across tree rows and associate
9500  * cells with one another. By default, GTK+ will then render the tree
9501  * with alternating row colors. Do <emphasis>not</emphasis> use it
9502  * just because you prefer the appearance of the ruled tree; that's a
9503  * question for the theme. Some themes will draw tree rows in
9504  * alternating colors even when rules are turned off, and users who
9505  * prefer that appearance all the time can choose those themes. You
9506  * should call this function only as a <emphasis>semantic</emphasis>
9507  * hint to the theme engine that your tree makes alternating colors
9508  * useful from a functional standpoint (since it has lots of columns,
9509  * generally).
9510  *
9511  **/
9512 void
9513 pspp_sheet_view_set_rules_hint (PsppSheetView  *tree_view,
9514                               gboolean      setting)
9515 {
9516   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9517
9518   setting = setting != FALSE;
9519
9520   if (tree_view->priv->has_rules != setting)
9521     {
9522       tree_view->priv->has_rules = setting;
9523       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9524     }
9525
9526   g_object_notify (G_OBJECT (tree_view), "rules-hint");
9527 }
9528
9529 /**
9530  * pspp_sheet_view_get_rules_hint
9531  * @tree_view: a #PsppSheetView
9532  *
9533  * Gets the setting set by pspp_sheet_view_set_rules_hint().
9534  *
9535  * Return value: %TRUE if rules are useful for the user of this tree
9536  **/
9537 gboolean
9538 pspp_sheet_view_get_rules_hint (PsppSheetView  *tree_view)
9539 {
9540   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9541
9542   return tree_view->priv->has_rules;
9543 }
9544
9545 /* Public Column functions
9546  */
9547
9548 /**
9549  * pspp_sheet_view_append_column:
9550  * @tree_view: A #PsppSheetView.
9551  * @column: The #PsppSheetViewColumn to add.
9552  *
9553  * Appends @column to the list of columns.
9554  *
9555  * Return value: The number of columns in @tree_view after appending.
9556  **/
9557 gint
9558 pspp_sheet_view_append_column (PsppSheetView       *tree_view,
9559                              PsppSheetViewColumn *column)
9560 {
9561   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9562   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9563   g_return_val_if_fail (column->tree_view == NULL, -1);
9564
9565   return pspp_sheet_view_insert_column (tree_view, column, -1);
9566 }
9567
9568
9569 /**
9570  * pspp_sheet_view_remove_column:
9571  * @tree_view: A #PsppSheetView.
9572  * @column: The #PsppSheetViewColumn to remove.
9573  *
9574  * Removes @column from @tree_view.
9575  *
9576  * Return value: The number of columns in @tree_view after removing.
9577  **/
9578 gint
9579 pspp_sheet_view_remove_column (PsppSheetView       *tree_view,
9580                              PsppSheetViewColumn *column)
9581 {
9582   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9583   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9584   g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9585
9586   if (tree_view->priv->focus_column == column)
9587     tree_view->priv->focus_column = NULL;
9588
9589   if (tree_view->priv->edited_column == column)
9590     {
9591       pspp_sheet_view_stop_editing (tree_view, TRUE);
9592
9593       /* no need to, but just to be sure ... */
9594       tree_view->priv->edited_column = NULL;
9595     }
9596
9597   _pspp_sheet_view_column_unset_tree_view (column);
9598
9599   tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9600   tree_view->priv->n_columns--;
9601
9602   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9603     {
9604       GList *list;
9605
9606       _pspp_sheet_view_column_unrealize_button (column);
9607       for (list = tree_view->priv->columns; list; list = list->next)
9608         {
9609           PsppSheetViewColumn *tmp_column;
9610
9611           tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9612           if (tmp_column->visible)
9613             _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9614         }
9615
9616       if (tree_view->priv->n_columns == 0 &&
9617           pspp_sheet_view_get_headers_visible (tree_view) && 
9618           tree_view->priv->header_window)
9619         gdk_window_hide (tree_view->priv->header_window);
9620
9621       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9622     }
9623
9624   g_object_unref (column);
9625   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9626
9627   return tree_view->priv->n_columns;
9628 }
9629
9630 /**
9631  * pspp_sheet_view_insert_column:
9632  * @tree_view: A #PsppSheetView.
9633  * @column: The #PsppSheetViewColumn to be inserted.
9634  * @position: The position to insert @column in.
9635  *
9636  * This inserts the @column into the @tree_view at @position.  If @position is
9637  * -1, then the column is inserted at the end.
9638  *
9639  * Return value: The number of columns in @tree_view after insertion.
9640  **/
9641 gint
9642 pspp_sheet_view_insert_column (PsppSheetView       *tree_view,
9643                              PsppSheetViewColumn *column,
9644                              gint               position)
9645 {
9646   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9647   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9648   g_return_val_if_fail (column->tree_view == NULL, -1);
9649
9650   g_object_ref_sink (column);
9651
9652   if (tree_view->priv->n_columns == 0 &&
9653       gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9654       pspp_sheet_view_get_headers_visible (tree_view))
9655     {
9656       gdk_window_show (tree_view->priv->header_window);
9657     }
9658
9659   tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9660                                             column, position);
9661   tree_view->priv->n_columns++;
9662
9663   _pspp_sheet_view_column_set_tree_view (column, tree_view);
9664
9665   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9666     {
9667       GList *list;
9668
9669       _pspp_sheet_view_column_realize_button (column);
9670
9671       for (list = tree_view->priv->columns; list; list = list->next)
9672         {
9673           column = PSPP_SHEET_VIEW_COLUMN (list->data);
9674           if (column->visible)
9675             _pspp_sheet_view_column_cell_set_dirty (column);
9676         }
9677       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9678     }
9679
9680   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9681
9682   return tree_view->priv->n_columns;
9683 }
9684
9685 /**
9686  * pspp_sheet_view_insert_column_with_attributes:
9687  * @tree_view: A #PsppSheetView
9688  * @position: The position to insert the new column in.
9689  * @title: The title to set the header to.
9690  * @cell: The #GtkCellRenderer.
9691  * @Varargs: A %NULL-terminated list of attributes.
9692  *
9693  * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9694  * @position.  If @position is -1, then the newly created column is inserted at
9695  * the end.  The column is initialized with the attributes given.
9696  *
9697  * Return value: The number of columns in @tree_view after insertion.
9698  **/
9699 gint
9700 pspp_sheet_view_insert_column_with_attributes (PsppSheetView     *tree_view,
9701                                              gint             position,
9702                                              const gchar     *title,
9703                                              GtkCellRenderer *cell,
9704                                              ...)
9705 {
9706   PsppSheetViewColumn *column;
9707   gchar *attribute;
9708   va_list args;
9709   gint column_id;
9710
9711   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9712
9713   column = pspp_sheet_view_column_new ();
9714   pspp_sheet_view_column_set_title (column, title);
9715   pspp_sheet_view_column_pack_start (column, cell, TRUE);
9716
9717   va_start (args, cell);
9718
9719   attribute = va_arg (args, gchar *);
9720
9721   while (attribute != NULL)
9722     {
9723       column_id = va_arg (args, gint);
9724       pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9725       attribute = va_arg (args, gchar *);
9726     }
9727
9728   va_end (args);
9729
9730   pspp_sheet_view_insert_column (tree_view, column, position);
9731
9732   return tree_view->priv->n_columns;
9733 }
9734
9735 /**
9736  * pspp_sheet_view_insert_column_with_data_func:
9737  * @tree_view: a #PsppSheetView
9738  * @position: Position to insert, -1 for append
9739  * @title: column title
9740  * @cell: cell renderer for column
9741  * @func: function to set attributes of cell renderer
9742  * @data: data for @func
9743  * @dnotify: destroy notifier for @data
9744  *
9745  * Convenience function that inserts a new column into the #PsppSheetView
9746  * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9747  * attributes (normally using data from the model). See also
9748  * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9749  *
9750  * Return value: number of columns in the tree view post-insert
9751  **/
9752 gint
9753 pspp_sheet_view_insert_column_with_data_func  (PsppSheetView               *tree_view,
9754                                              gint                       position,
9755                                              const gchar               *title,
9756                                              GtkCellRenderer           *cell,
9757                                              PsppSheetCellDataFunc        func,
9758                                              gpointer                   data,
9759                                              GDestroyNotify             dnotify)
9760 {
9761   PsppSheetViewColumn *column;
9762
9763   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9764
9765   column = pspp_sheet_view_column_new ();
9766   pspp_sheet_view_column_set_title (column, title);
9767   pspp_sheet_view_column_pack_start (column, cell, TRUE);
9768   pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9769
9770   pspp_sheet_view_insert_column (tree_view, column, position);
9771
9772   return tree_view->priv->n_columns;
9773 }
9774
9775 /**
9776  * pspp_sheet_view_get_column:
9777  * @tree_view: A #PsppSheetView.
9778  * @n: The position of the column, counting from 0.
9779  *
9780  * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9781  *
9782  * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9783  * range of columns.
9784  **/
9785 PsppSheetViewColumn *
9786 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9787                           gint         n)
9788 {
9789   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9790
9791   if (n < 0 || n >= tree_view->priv->n_columns)
9792     return NULL;
9793
9794   if (tree_view->priv->columns == NULL)
9795     return NULL;
9796
9797   return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9798 }
9799
9800 /**
9801  * pspp_sheet_view_get_columns:
9802  * @tree_view: A #PsppSheetView
9803  *
9804  * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9805  * The returned list must be freed with g_list_free ().
9806  *
9807  * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9808  **/
9809 GList *
9810 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9811 {
9812   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9813
9814   return g_list_copy (tree_view->priv->columns);
9815 }
9816
9817 /**
9818  * pspp_sheet_view_move_column_after:
9819  * @tree_view: A #PsppSheetView
9820  * @column: The #PsppSheetViewColumn to be moved.
9821  * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9822  *
9823  * Moves @column to be after to @base_column.  If @base_column is %NULL, then
9824  * @column is placed in the first position.
9825  **/
9826 void
9827 pspp_sheet_view_move_column_after (PsppSheetView       *tree_view,
9828                                  PsppSheetViewColumn *column,
9829                                  PsppSheetViewColumn *base_column)
9830 {
9831   GList *column_list_el, *base_el = NULL;
9832
9833   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9834
9835   column_list_el = g_list_find (tree_view->priv->columns, column);
9836   g_return_if_fail (column_list_el != NULL);
9837
9838   if (base_column)
9839     {
9840       base_el = g_list_find (tree_view->priv->columns, base_column);
9841       g_return_if_fail (base_el != NULL);
9842     }
9843
9844   if (column_list_el->prev == base_el)
9845     return;
9846
9847   tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9848   if (base_el == NULL)
9849     {
9850       column_list_el->prev = NULL;
9851       column_list_el->next = tree_view->priv->columns;
9852       if (column_list_el->next)
9853         column_list_el->next->prev = column_list_el;
9854       tree_view->priv->columns = column_list_el;
9855     }
9856   else
9857     {
9858       column_list_el->prev = base_el;
9859       column_list_el->next = base_el->next;
9860       if (column_list_el->next)
9861         column_list_el->next->prev = column_list_el;
9862       base_el->next = column_list_el;
9863     }
9864
9865   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9866     {
9867       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9868       pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9869     }
9870
9871   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9872 }
9873
9874 /**
9875  * pspp_sheet_view_set_column_drag_function:
9876  * @tree_view: A #PsppSheetView.
9877  * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9878  * @user_data: (allow-none): User data to be passed to @func, or %NULL
9879  * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9880  *
9881  * Sets a user function for determining where a column may be dropped when
9882  * dragged.  This function is called on every column pair in turn at the
9883  * beginning of a column drag to determine where a drop can take place.  The
9884  * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9885  * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9886  * @user_data.  If either of the #PsppSheetViewColumn arguments for the drop spot
9887  * are %NULL, then they indicate an edge.  If @func is set to be %NULL, then
9888  * @tree_view reverts to the default behavior of allowing all columns to be
9889  * dropped everywhere.
9890  **/
9891 void
9892 pspp_sheet_view_set_column_drag_function (PsppSheetView               *tree_view,
9893                                         PsppSheetViewColumnDropFunc  func,
9894                                         gpointer                   user_data,
9895                                         GDestroyNotify             destroy)
9896 {
9897   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9898
9899   if (tree_view->priv->column_drop_func_data_destroy)
9900     tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9901
9902   tree_view->priv->column_drop_func = func;
9903   tree_view->priv->column_drop_func_data = user_data;
9904   tree_view->priv->column_drop_func_data_destroy = destroy;
9905 }
9906
9907 /**
9908  * pspp_sheet_view_scroll_to_point:
9909  * @tree_view: a #PsppSheetView
9910  * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9911  * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9912  *
9913  * Scrolls the tree view such that the top-left corner of the visible
9914  * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9915  * in tree coordinates.  The @tree_view must be realized before
9916  * this function is called.  If it isn't, you probably want to be
9917  * using pspp_sheet_view_scroll_to_cell().
9918  *
9919  * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9920  **/
9921 void
9922 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9923                                gint         tree_x,
9924                                gint         tree_y)
9925 {
9926   GtkAdjustment *hadj;
9927   GtkAdjustment *vadj;
9928
9929   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9930   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9931
9932   hadj = tree_view->priv->hadjustment;
9933   vadj = tree_view->priv->vadjustment;
9934
9935   if (tree_x != -1)
9936     gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj)));
9937   if (tree_y != -1)
9938     gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj)));
9939 }
9940
9941 /**
9942  * pspp_sheet_view_scroll_to_cell:
9943  * @tree_view: A #PsppSheetView.
9944  * @path: (allow-none): The path of the row to move to, or %NULL.
9945  * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9946  * @use_align: whether to use alignment arguments, or %FALSE.
9947  * @row_align: The vertical alignment of the row specified by @path.
9948  * @col_align: The horizontal alignment of the column specified by @column.
9949  *
9950  * Moves the alignments of @tree_view to the position specified by @column and
9951  * @path.  If @column is %NULL, then no horizontal scrolling occurs.  Likewise,
9952  * if @path is %NULL no vertical scrolling occurs.  At a minimum, one of @column
9953  * or @path need to be non-%NULL.  @row_align determines where the row is
9954  * placed, and @col_align determines where @column is placed.  Both are expected
9955  * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9956  * right/bottom alignment, 0.5 means center.
9957  *
9958  * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9959  * tree does the minimum amount of work to scroll the cell onto the screen.
9960  * This means that the cell will be scrolled to the edge closest to its current
9961  * position.  If the cell is currently visible on the screen, nothing is done.
9962  *
9963  * This function only works if the model is set, and @path is a valid row on the
9964  * model.  If the model changes before the @tree_view is realized, the centered
9965  * path will be modified to reflect this change.
9966  **/
9967 void
9968 pspp_sheet_view_scroll_to_cell (PsppSheetView       *tree_view,
9969                               GtkTreePath       *path,
9970                               PsppSheetViewColumn *column,
9971                               gboolean           use_align,
9972                               gfloat             row_align,
9973                               gfloat             col_align)
9974 {
9975   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9976   g_return_if_fail (tree_view->priv->model != NULL);
9977   g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9978   g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9979   g_return_if_fail (path != NULL || column != NULL);
9980
9981 #if 0
9982   g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9983            gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9984 #endif
9985   row_align = CLAMP (row_align, 0.0, 1.0);
9986   col_align = CLAMP (col_align, 0.0, 1.0);
9987
9988
9989   /* Note: Despite the benefits that come from having one code path for the
9990    * scrolling code, we short-circuit validate_visible_area's immplementation as
9991    * it is much slower than just going to the point.
9992    */
9993   if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9994       !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9995       /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9996     {
9997       if (tree_view->priv->scroll_to_path)
9998         gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9999
10000       tree_view->priv->scroll_to_path = NULL;
10001       tree_view->priv->scroll_to_column = NULL;
10002
10003       if (path)
10004         tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
10005       if (column)
10006         tree_view->priv->scroll_to_column = column;
10007       tree_view->priv->scroll_to_use_align = use_align;
10008       tree_view->priv->scroll_to_row_align = row_align;
10009       tree_view->priv->scroll_to_col_align = col_align;
10010
10011       install_presize_handler (tree_view);
10012     }
10013   else
10014     {
10015       GdkRectangle cell_rect;
10016       GdkRectangle vis_rect;
10017       gint dest_x, dest_y;
10018
10019       pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
10020       pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
10021
10022       cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
10023
10024       dest_x = vis_rect.x;
10025       dest_y = vis_rect.y;
10026
10027       if (column)
10028         {
10029           if (use_align)
10030             {
10031               dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
10032             }
10033           else
10034             {
10035               if (cell_rect.x < vis_rect.x)
10036                 dest_x = cell_rect.x;
10037               if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
10038                 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
10039             }
10040         }
10041
10042       if (path)
10043         {
10044           if (use_align)
10045             {
10046               dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
10047               dest_y = MAX (dest_y, 0);
10048             }
10049           else
10050             {
10051               if (cell_rect.y < vis_rect.y)
10052                 dest_y = cell_rect.y;
10053               if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
10054                 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
10055             }
10056         }
10057
10058       pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
10059     }
10060 }
10061
10062 /**
10063  * pspp_sheet_view_row_activated:
10064  * @tree_view: A #PsppSheetView
10065  * @path: The #GtkTreePath to be activated.
10066  * @column: The #PsppSheetViewColumn to be activated.
10067  *
10068  * Activates the cell determined by @path and @column.
10069  **/
10070 void
10071 pspp_sheet_view_row_activated (PsppSheetView       *tree_view,
10072                              GtkTreePath       *path,
10073                              PsppSheetViewColumn *column)
10074 {
10075   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10076
10077   g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
10078 }
10079
10080
10081 /**
10082  * pspp_sheet_view_get_reorderable:
10083  * @tree_view: a #PsppSheetView
10084  *
10085  * Retrieves whether the user can reorder the tree via drag-and-drop. See
10086  * pspp_sheet_view_set_reorderable().
10087  *
10088  * Return value: %TRUE if the tree can be reordered.
10089  **/
10090 gboolean
10091 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
10092 {
10093   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10094
10095   return tree_view->priv->reorderable;
10096 }
10097
10098 /**
10099  * pspp_sheet_view_set_reorderable:
10100  * @tree_view: A #PsppSheetView.
10101  * @reorderable: %TRUE, if the tree can be reordered.
10102  *
10103  * This function is a convenience function to allow you to reorder
10104  * models that support the #GtkDragSourceIface and the
10105  * #GtkDragDestIface.  Both #GtkTreeStore and #GtkListStore support
10106  * these.  If @reorderable is %TRUE, then the user can reorder the
10107  * model by dragging and dropping rows. The developer can listen to
10108  * these changes by connecting to the model's row_inserted and
10109  * row_deleted signals. The reordering is implemented by setting up
10110  * the tree view as a drag source and destination. Therefore, drag and
10111  * drop can not be used in a reorderable view for any other purpose.
10112  *
10113  * This function does not give you any degree of control over the order -- any
10114  * reordering is allowed.  If more control is needed, you should probably
10115  * handle drag and drop manually.
10116  **/
10117 void
10118 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
10119                                gboolean     reorderable)
10120 {
10121   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10122
10123   reorderable = reorderable != FALSE;
10124
10125   if (tree_view->priv->reorderable == reorderable)
10126     return;
10127
10128   if (reorderable)
10129     {
10130       const GtkTargetEntry row_targets[] = {
10131         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
10132       };
10133
10134       pspp_sheet_view_enable_model_drag_source (tree_view,
10135                                               GDK_BUTTON1_MASK,
10136                                               row_targets,
10137                                               G_N_ELEMENTS (row_targets),
10138                                               GDK_ACTION_MOVE);
10139       pspp_sheet_view_enable_model_drag_dest (tree_view,
10140                                             row_targets,
10141                                             G_N_ELEMENTS (row_targets),
10142                                             GDK_ACTION_MOVE);
10143     }
10144   else
10145     {
10146       pspp_sheet_view_unset_rows_drag_source (tree_view);
10147       pspp_sheet_view_unset_rows_drag_dest (tree_view);
10148     }
10149
10150   tree_view->priv->reorderable = reorderable;
10151
10152   g_object_notify (G_OBJECT (tree_view), "reorderable");
10153 }
10154
10155 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
10156    is pressed, other rows will be unselected.
10157
10158    If CLAMP_NODE is true, then the sheetview will scroll to make the row
10159    visible. */
10160 static void
10161 pspp_sheet_view_real_set_cursor (PsppSheetView     *tree_view,
10162                                GtkTreePath     *path,
10163                                gboolean         clear_and_select,
10164                                gboolean         clamp_node,
10165                                PsppSheetSelectMode mode)
10166 {
10167   int node = -1;
10168
10169   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10170     {
10171       GtkTreePath *cursor_path;
10172       cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10173       pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
10174       gtk_tree_path_free (cursor_path);
10175     }
10176
10177   gtk_tree_row_reference_free (tree_view->priv->cursor);
10178   tree_view->priv->cursor = NULL;
10179
10180   _pspp_sheet_view_find_node (tree_view, path, &node);
10181   tree_view->priv->cursor =
10182     gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
10183                                       tree_view->priv->model,
10184                                       path);
10185
10186   if (tree_view->priv->row_count > 0)
10187     {
10188       int new_node = -1;
10189
10190       if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
10191         _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
10192                                                     node, path, mode,
10193                                                     FALSE);
10194
10195       /* We have to re-find tree and node here again, somebody might have
10196        * cleared the node or the whole tree in the PsppSheetSelection::changed
10197        * callback. If the nodes differ we bail out here.
10198        */
10199       _pspp_sheet_view_find_node (tree_view, path, &new_node);
10200
10201       if (node != new_node)
10202         return;
10203
10204       if (clamp_node)
10205         {
10206           pspp_sheet_view_clamp_node_visible (tree_view, node);
10207           _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
10208         }
10209     }
10210
10211   g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
10212 }
10213
10214 /**
10215  * pspp_sheet_view_get_cursor:
10216  * @tree_view: A #PsppSheetView
10217  * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
10218  * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
10219  *
10220  * Fills in @path and @focus_column with the current path and focus column.  If
10221  * the cursor isn't currently set, then *@path will be %NULL.  If no column
10222  * currently has focus, then *@focus_column will be %NULL.
10223  *
10224  * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
10225  * you are done with it.
10226  **/
10227 void
10228 pspp_sheet_view_get_cursor (PsppSheetView        *tree_view,
10229                           GtkTreePath       **path,
10230                           PsppSheetViewColumn **focus_column)
10231 {
10232   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10233
10234   if (path)
10235     {
10236       if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10237         *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10238       else
10239         *path = NULL;
10240     }
10241
10242   if (focus_column)
10243     {
10244       *focus_column = tree_view->priv->focus_column;
10245     }
10246 }
10247
10248 /**
10249  * pspp_sheet_view_set_cursor:
10250  * @tree_view: A #PsppSheetView
10251  * @path: A #GtkTreePath
10252  * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10253  * @start_editing: %TRUE if the specified cell should start being edited.
10254  *
10255  * Sets the current keyboard focus to be at @path, and selects it.  This is
10256  * useful when you want to focus the user's attention on a particular row.  If
10257  * @focus_column is not %NULL, then focus is given to the column specified by 
10258  * it. Additionally, if @focus_column is specified, and @start_editing is 
10259  * %TRUE, then editing should be started in the specified cell.  
10260  * This function is often followed by @gtk_widget_grab_focus (@tree_view) 
10261  * in order to give keyboard focus to the widget.  Please note that editing 
10262  * can only happen when the widget is realized.
10263  *
10264  * If @path is invalid for @model, the current cursor (if any) will be unset
10265  * and the function will return without failing.
10266  **/
10267 void
10268 pspp_sheet_view_set_cursor (PsppSheetView       *tree_view,
10269                           GtkTreePath       *path,
10270                           PsppSheetViewColumn *focus_column,
10271                           gboolean           start_editing)
10272 {
10273   pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10274                                     NULL, start_editing);
10275 }
10276
10277 /**
10278  * pspp_sheet_view_set_cursor_on_cell:
10279  * @tree_view: A #PsppSheetView
10280  * @path: A #GtkTreePath
10281  * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10282  * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10283  * @start_editing: %TRUE if the specified cell should start being edited.
10284  *
10285  * Sets the current keyboard focus to be at @path, and selects it.  This is
10286  * useful when you want to focus the user's attention on a particular row.  If
10287  * @focus_column is not %NULL, then focus is given to the column specified by
10288  * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10289  * contains 2 or more editable or activatable cells, then focus is given to
10290  * the cell specified by @focus_cell. Additionally, if @focus_column is
10291  * specified, and @start_editing is %TRUE, then editing should be started in
10292  * the specified cell.  This function is often followed by
10293  * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10294  * widget.  Please note that editing can only happen when the widget is
10295  * realized.
10296  *
10297  * If @path is invalid for @model, the current cursor (if any) will be unset
10298  * and the function will return without failing.
10299  *
10300  * Since: 2.2
10301  **/
10302 void
10303 pspp_sheet_view_set_cursor_on_cell (PsppSheetView       *tree_view,
10304                                   GtkTreePath       *path,
10305                                   PsppSheetViewColumn *focus_column,
10306                                   GtkCellRenderer   *focus_cell,
10307                                   gboolean           start_editing)
10308 {
10309   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10310   g_return_if_fail (path != NULL);
10311   g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10312
10313   if (!tree_view->priv->model)
10314     return;
10315
10316   if (focus_cell)
10317     {
10318       g_return_if_fail (focus_column);
10319       g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10320     }
10321
10322   /* cancel the current editing, if it exists */
10323   if (tree_view->priv->edited_column &&
10324       tree_view->priv->edited_column->editable_widget)
10325     pspp_sheet_view_stop_editing (tree_view, TRUE);
10326
10327   pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10328
10329   if (focus_column && focus_column->visible)
10330     {
10331       GList *list;
10332       gboolean column_in_tree = FALSE;
10333
10334       for (list = tree_view->priv->columns; list; list = list->next)
10335         if (list->data == focus_column)
10336           {
10337             column_in_tree = TRUE;
10338             break;
10339           }
10340       g_return_if_fail (column_in_tree);
10341       tree_view->priv->focus_column = focus_column;
10342       if (focus_cell)
10343         pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10344       if (start_editing)
10345         pspp_sheet_view_start_editing (tree_view, path);
10346
10347       pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10348       pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10349
10350     }
10351 }
10352
10353 /**
10354  * pspp_sheet_view_get_bin_window:
10355  * @tree_view: A #PsppSheetView
10356  * 
10357  * Returns the window that @tree_view renders to.  This is used primarily to
10358  * compare to <literal>event->window</literal> to confirm that the event on
10359  * @tree_view is on the right window.
10360  * 
10361  * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10362  **/
10363 GdkWindow *
10364 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10365 {
10366   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10367
10368   return tree_view->priv->bin_window;
10369 }
10370
10371 /**
10372  * pspp_sheet_view_get_path_at_pos:
10373  * @tree_view: A #PsppSheetView.
10374  * @x: The x position to be identified (relative to bin_window).
10375  * @y: The y position to be identified (relative to bin_window).
10376  * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10377  * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10378  * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10379  * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10380  *
10381  * Finds the path at the point (@x, @y), relative to bin_window coordinates
10382  * (please see pspp_sheet_view_get_bin_window()).
10383  * That is, @x and @y are relative to an events coordinates. @x and @y must
10384  * come from an event on the @tree_view only where <literal>event->window ==
10385  * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10386  * things like popup menus. If @path is non-%NULL, then it will be filled
10387  * with the #GtkTreePath at that point.  This path should be freed with
10388  * gtk_tree_path_free().  If @column is non-%NULL, then it will be filled
10389  * with the column at that point.  @cell_x and @cell_y return the coordinates
10390  * relative to the cell background (i.e. the @background_area passed to
10391  * gtk_cell_renderer_render()).  This function is only meaningful if
10392  * @tree_view is realized.  Therefore this function will always return %FALSE
10393  * if @tree_view is not realized or does not have a model.
10394  *
10395  * For converting widget coordinates (eg. the ones you get from
10396  * GtkWidget::query-tooltip), please see
10397  * pspp_sheet_view_convert_widget_to_bin_window_coords().
10398  *
10399  * Return value: %TRUE if a row exists at that coordinate.
10400  **/
10401 gboolean
10402 pspp_sheet_view_get_path_at_pos (PsppSheetView        *tree_view,
10403                                gint                x,
10404                                gint                y,
10405                                GtkTreePath       **path,
10406                                PsppSheetViewColumn **column,
10407                                gint               *cell_x,
10408                                gint               *cell_y)
10409 {
10410   int node;
10411   gint y_offset;
10412
10413   g_return_val_if_fail (tree_view != NULL, FALSE);
10414
10415   if (path)
10416     *path = NULL;
10417   if (column)
10418     *column = NULL;
10419
10420   if (tree_view->priv->bin_window == NULL)
10421     return FALSE;
10422
10423   if (tree_view->priv->row_count == 0)
10424     return FALSE;
10425
10426   if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
10427     return FALSE;
10428
10429   if (x < 0 || y < 0)
10430     return FALSE;
10431
10432   if (column || cell_x)
10433     {
10434       PsppSheetViewColumn *tmp_column;
10435       PsppSheetViewColumn *last_column = NULL;
10436       GList *list;
10437       gint remaining_x = x;
10438       gboolean found = FALSE;
10439       gboolean rtl;
10440
10441       rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10442       for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10443            list;
10444            list = (rtl ? list->prev : list->next))
10445         {
10446           tmp_column = list->data;
10447
10448           if (tmp_column->visible == FALSE)
10449             continue;
10450
10451           last_column = tmp_column;
10452           if (remaining_x <= tmp_column->width)
10453             {
10454               found = TRUE;
10455
10456               if (column)
10457                 *column = tmp_column;
10458
10459               if (cell_x)
10460                 *cell_x = remaining_x;
10461
10462               break;
10463             }
10464           remaining_x -= tmp_column->width;
10465         }
10466
10467       /* If found is FALSE and there is a last_column, then it the remainder
10468        * space is in that area
10469        */
10470       if (!found)
10471         {
10472           if (last_column)
10473             {
10474               if (column)
10475                 *column = last_column;
10476               
10477               if (cell_x)
10478                 *cell_x = last_column->width + remaining_x;
10479             }
10480           else
10481             {
10482               return FALSE;
10483             }
10484         }
10485     }
10486
10487   y_offset = pspp_sheet_view_find_offset (tree_view,
10488                                           TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10489                                           &node);
10490
10491   if (node < 0)
10492     return FALSE;
10493
10494   if (cell_y)
10495     *cell_y = y_offset;
10496
10497   if (path)
10498     *path = _pspp_sheet_view_find_path (tree_view, node);
10499
10500   return TRUE;
10501 }
10502
10503 /* Computes 'cell_area' from 'background_area', which must be the background
10504    area for a cell.  Set 'subtract_focus_rect' to TRUE to compute the cell area
10505    as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10506    the cell area as passed to _pspp_sheet_view_column_cell_render().
10507
10508    'column' is required to properly adjust 'cell_area->x' and
10509    'cell_area->width'.  It may be set to NULL if these values are not of
10510    interest.  In this case 'cell_area->x' and 'cell_area->width' will be
10511    returned as 0. */
10512 static void
10513 pspp_sheet_view_adjust_cell_area (PsppSheetView        *tree_view,
10514                                   PsppSheetViewColumn  *column,
10515                                   const GdkRectangle   *background_area,
10516                                   gboolean              subtract_focus_rect,
10517                                   GdkRectangle         *cell_area)
10518 {
10519   gint vertical_separator;
10520   gint horizontal_separator;
10521
10522   *cell_area = *background_area;
10523
10524   gtk_widget_style_get (GTK_WIDGET (tree_view),
10525                         "vertical-separator", &vertical_separator,
10526                         "horizontal-separator", &horizontal_separator,
10527                         NULL);
10528   cell_area->x += horizontal_separator / 2;
10529   cell_area->y += vertical_separator / 2;
10530   cell_area->width -= horizontal_separator;
10531   cell_area->height -= vertical_separator;
10532
10533   if (subtract_focus_rect)
10534     {
10535       int focus_line_width;
10536
10537       gtk_widget_style_get (GTK_WIDGET (tree_view),
10538                             "focus-line-width", &focus_line_width,
10539                             NULL);
10540       cell_area->x += focus_line_width;
10541       cell_area->y += focus_line_width;
10542       cell_area->width -= 2 * focus_line_width;
10543       cell_area->height -= 2 * focus_line_width;
10544     }
10545
10546   if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10547     {
10548       gint grid_line_width;
10549       gtk_widget_style_get (GTK_WIDGET (tree_view),
10550                             "grid-line-width", &grid_line_width,
10551                             NULL);
10552
10553       if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10554            || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10555           && column != NULL)
10556         {
10557           PsppSheetViewColumn *first_column, *last_column;
10558           GList *list;
10559
10560           /* Find the last visible column. */
10561           last_column = NULL;
10562           for (list = g_list_last (tree_view->priv->columns);
10563                list;
10564                list = list->prev)
10565             {
10566               PsppSheetViewColumn *c = list->data;
10567               if (c->visible)
10568                 {
10569                   last_column = c;
10570                   break;
10571                 }
10572             }
10573
10574           /* Find the first visible column. */
10575           first_column = NULL;
10576           for (list = g_list_first (tree_view->priv->columns);
10577                list;
10578                list = list->next)
10579             {
10580               PsppSheetViewColumn *c = list->data;
10581               if (c->visible)
10582                 {
10583                   first_column = c;
10584                   break;
10585                 }
10586             }
10587
10588           if (column == first_column)
10589             {
10590               cell_area->width -= grid_line_width / 2;
10591             }
10592           else if (column == last_column)
10593             {
10594               cell_area->x += grid_line_width / 2;
10595               cell_area->width -= grid_line_width / 2;
10596             }
10597           else
10598             {
10599               cell_area->x += grid_line_width / 2;
10600               cell_area->width -= grid_line_width;
10601             }
10602         }
10603
10604       if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10605           || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10606         {
10607           cell_area->y += grid_line_width / 2;
10608           cell_area->height -= grid_line_width;
10609         }
10610     }
10611
10612   if (column == NULL)
10613     {
10614       cell_area->x = 0;
10615       cell_area->width = 0;
10616     }
10617 }
10618
10619 /**
10620  * pspp_sheet_view_get_cell_area:
10621  * @tree_view: a #PsppSheetView
10622  * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10623  * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10624  * @rect: rectangle to fill with cell rect
10625  *
10626  * Fills the bounding rectangle in bin_window coordinates for the cell at the
10627  * row specified by @path and the column specified by @column.  If @path is
10628  * %NULL, or points to a path not currently displayed, the @y and @height fields
10629  * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10630  * fields will be filled with 0.  The sum of all cell rects does not cover the
10631  * entire tree; there are extra pixels in between rows, for example. The
10632  * returned rectangle is equivalent to the @cell_area passed to
10633  * gtk_cell_renderer_render().  This function is only valid if @tree_view is
10634  * realized.
10635  **/
10636 void
10637 pspp_sheet_view_get_cell_area (PsppSheetView        *tree_view,
10638                              GtkTreePath        *path,
10639                              PsppSheetViewColumn  *column,
10640                              GdkRectangle       *rect)
10641 {
10642   GdkRectangle background_area;
10643
10644   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10645   g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10646   g_return_if_fail (rect != NULL);
10647   g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10648   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10649
10650   pspp_sheet_view_get_background_area (tree_view, path, column,
10651                                        &background_area);
10652   pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10653                                     FALSE, rect);
10654 }
10655
10656 /**
10657  * pspp_sheet_view_get_background_area:
10658  * @tree_view: a #PsppSheetView
10659  * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10660  * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10661  * @rect: rectangle to fill with cell background rect
10662  *
10663  * Fills the bounding rectangle in bin_window coordinates for the cell at the
10664  * row specified by @path and the column specified by @column.  If @path is
10665  * %NULL, or points to a node not found in the tree, the @y and @height fields of
10666  * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10667  * fields will be filled with 0.  The returned rectangle is equivalent to the
10668  * @background_area passed to gtk_cell_renderer_render().  These background
10669  * areas tile to cover the entire bin window.  Contrast with the @cell_area,
10670  * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10671  * itself, excluding surrounding borders.
10672  *
10673  **/
10674 void
10675 pspp_sheet_view_get_background_area (PsppSheetView        *tree_view,
10676                                    GtkTreePath        *path,
10677                                    PsppSheetViewColumn  *column,
10678                                    GdkRectangle       *rect)
10679 {
10680   int node = -1;
10681
10682   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10683   g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10684   g_return_if_fail (rect != NULL);
10685
10686   rect->x = 0;
10687   rect->y = 0;
10688   rect->width = 0;
10689   rect->height = 0;
10690
10691   if (path)
10692     {
10693       /* Get vertical coords */
10694
10695       _pspp_sheet_view_find_node (tree_view, path, &node);
10696       if (node < 0)
10697         return;
10698
10699       rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10700
10701       rect->height = ROW_HEIGHT (tree_view);
10702     }
10703
10704   if (column)
10705     {
10706       gint x2 = 0;
10707
10708       pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10709       rect->width = x2 - rect->x;
10710     }
10711 }
10712
10713 /**
10714  * pspp_sheet_view_get_visible_rect:
10715  * @tree_view: a #PsppSheetView
10716  * @visible_rect: rectangle to fill
10717  *
10718  * Fills @visible_rect with the currently-visible region of the
10719  * buffer, in tree coordinates. Convert to bin_window coordinates with
10720  * pspp_sheet_view_convert_tree_to_bin_window_coords().
10721  * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10722  * scrollable area of the tree.
10723  **/
10724 void
10725 pspp_sheet_view_get_visible_rect (PsppSheetView  *tree_view,
10726                                 GdkRectangle *visible_rect)
10727 {
10728   GtkWidget *widget;
10729
10730   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10731
10732   widget = GTK_WIDGET (tree_view);
10733
10734   if (visible_rect)
10735     {
10736       GtkAllocation allocation;
10737       gtk_widget_get_allocation (widget, &allocation);
10738       visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
10739       visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
10740       visible_rect->width  = allocation.width;
10741       visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10742     }
10743 }
10744
10745 /**
10746  * pspp_sheet_view_widget_to_tree_coords:
10747  * @tree_view: a #PsppSheetView
10748  * @wx: X coordinate relative to bin_window
10749  * @wy: Y coordinate relative to bin_window
10750  * @tx: return location for tree X coordinate
10751  * @ty: return location for tree Y coordinate
10752  *
10753  * Converts bin_window coordinates to coordinates for the
10754  * tree (the full scrollable area of the tree).
10755  *
10756  * Deprecated: 2.12: Due to historial reasons the name of this function is
10757  * incorrect.  For converting coordinates relative to the widget to
10758  * bin_window coordinates, please see
10759  * pspp_sheet_view_convert_widget_to_bin_window_coords().
10760  *
10761  **/
10762 void
10763 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10764                                       gint         wx,
10765                                       gint         wy,
10766                                       gint        *tx,
10767                                       gint        *ty)
10768 {
10769   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10770
10771   if (tx)
10772     *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10773   if (ty)
10774     *ty = wy + tree_view->priv->dy;
10775 }
10776
10777 /**
10778  * pspp_sheet_view_tree_to_widget_coords:
10779  * @tree_view: a #PsppSheetView
10780  * @tx: tree X coordinate
10781  * @ty: tree Y coordinate
10782  * @wx: return location for X coordinate relative to bin_window
10783  * @wy: return location for Y coordinate relative to bin_window
10784  *
10785  * Converts tree coordinates (coordinates in full scrollable area of the tree)
10786  * to bin_window coordinates.
10787  *
10788  * Deprecated: 2.12: Due to historial reasons the name of this function is
10789  * incorrect.  For converting bin_window coordinates to coordinates relative
10790  * to bin_window, please see
10791  * pspp_sheet_view_convert_bin_window_to_widget_coords().
10792  *
10793  **/
10794 void
10795 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10796                                      gint         tx,
10797                                      gint         ty,
10798                                      gint        *wx,
10799                                      gint        *wy)
10800 {
10801   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10802
10803   if (wx)
10804     *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10805   if (wy)
10806     *wy = ty - tree_view->priv->dy;
10807 }
10808
10809
10810 /**
10811  * pspp_sheet_view_convert_widget_to_tree_coords:
10812  * @tree_view: a #PsppSheetView
10813  * @wx: X coordinate relative to the widget
10814  * @wy: Y coordinate relative to the widget
10815  * @tx: return location for tree X coordinate
10816  * @ty: return location for tree Y coordinate
10817  *
10818  * Converts widget coordinates to coordinates for the
10819  * tree (the full scrollable area of the tree).
10820  *
10821  * Since: 2.12
10822  **/
10823 void
10824 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10825                                              gint         wx,
10826                                              gint         wy,
10827                                              gint        *tx,
10828                                              gint        *ty)
10829 {
10830   gint x, y;
10831
10832   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10833
10834   pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10835                                                      wx, wy,
10836                                                      &x, &y);
10837   pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10838                                                    x, y,
10839                                                    tx, ty);
10840 }
10841
10842 /**
10843  * pspp_sheet_view_convert_tree_to_widget_coords:
10844  * @tree_view: a #PsppSheetView
10845  * @tx: X coordinate relative to the tree
10846  * @ty: Y coordinate relative to the tree
10847  * @wx: return location for widget X coordinate
10848  * @wy: return location for widget Y coordinate
10849  *
10850  * Converts tree coordinates (coordinates in full scrollable area of the tree)
10851  * to widget coordinates.
10852  *
10853  * Since: 2.12
10854  **/
10855 void
10856 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10857                                              gint         tx,
10858                                              gint         ty,
10859                                              gint        *wx,
10860                                              gint        *wy)
10861 {
10862   gint x, y;
10863
10864   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10865
10866   pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10867                                                    tx, ty,
10868                                                    &x, &y);
10869   pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10870                                                      x, y,
10871                                                      wx, wy);
10872 }
10873
10874 /**
10875  * pspp_sheet_view_convert_widget_to_bin_window_coords:
10876  * @tree_view: a #PsppSheetView
10877  * @wx: X coordinate relative to the widget
10878  * @wy: Y coordinate relative to the widget
10879  * @bx: return location for bin_window X coordinate
10880  * @by: return location for bin_window Y coordinate
10881  *
10882  * Converts widget coordinates to coordinates for the bin_window
10883  * (see pspp_sheet_view_get_bin_window()).
10884  *
10885  * Since: 2.12
10886  **/
10887 void
10888 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10889                                                    gint         wx,
10890                                                    gint         wy,
10891                                                    gint        *bx,
10892                                                    gint        *by)
10893 {
10894   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10895
10896   if (bx)
10897     *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10898   if (by)
10899     *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10900 }
10901
10902 /**
10903  * pspp_sheet_view_convert_bin_window_to_widget_coords:
10904  * @tree_view: a #PsppSheetView
10905  * @bx: bin_window X coordinate
10906  * @by: bin_window Y coordinate
10907  * @wx: return location for widget X coordinate
10908  * @wy: return location for widget Y coordinate
10909  *
10910  * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10911  * to widget relative coordinates.
10912  *
10913  * Since: 2.12
10914  **/
10915 void
10916 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10917                                                    gint         bx,
10918                                                    gint         by,
10919                                                    gint        *wx,
10920                                                    gint        *wy)
10921 {
10922   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10923
10924   if (wx)
10925     *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10926   if (wy)
10927     *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10928 }
10929
10930 /**
10931  * pspp_sheet_view_convert_tree_to_bin_window_coords:
10932  * @tree_view: a #PsppSheetView
10933  * @tx: tree X coordinate
10934  * @ty: tree Y coordinate
10935  * @bx: return location for X coordinate relative to bin_window
10936  * @by: return location for Y coordinate relative to bin_window
10937  *
10938  * Converts tree coordinates (coordinates in full scrollable area of the tree)
10939  * to bin_window coordinates.
10940  *
10941  * Since: 2.12
10942  **/
10943 void
10944 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10945                                                  gint         tx,
10946                                                  gint         ty,
10947                                                  gint        *bx,
10948                                                  gint        *by)
10949 {
10950   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10951
10952   if (bx)
10953     *bx = tx;
10954   if (by)
10955     *by = ty - tree_view->priv->dy;
10956 }
10957
10958 /**
10959  * pspp_sheet_view_convert_bin_window_to_tree_coords:
10960  * @tree_view: a #PsppSheetView
10961  * @bx: X coordinate relative to bin_window
10962  * @by: Y coordinate relative to bin_window
10963  * @tx: return location for tree X coordinate
10964  * @ty: return location for tree Y coordinate
10965  *
10966  * Converts bin_window coordinates to coordinates for the
10967  * tree (the full scrollable area of the tree).
10968  *
10969  * Since: 2.12
10970  **/
10971 void
10972 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10973                                                  gint         bx,
10974                                                  gint         by,
10975                                                  gint        *tx,
10976                                                  gint        *ty)
10977 {
10978   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10979
10980   if (tx)
10981     *tx = bx;
10982   if (ty)
10983     *ty = by + tree_view->priv->dy;
10984 }
10985
10986
10987
10988 /**
10989  * pspp_sheet_view_get_visible_range:
10990  * @tree_view: A #PsppSheetView
10991  * @start_path: (allow-none): Return location for start of region, or %NULL.
10992  * @end_path: (allow-none): Return location for end of region, or %NULL.
10993  *
10994  * Sets @start_path and @end_path to be the first and last visible path.
10995  * Note that there may be invisible paths in between.
10996  *
10997  * The paths should be freed with gtk_tree_path_free() after use.
10998  *
10999  * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
11000  *
11001  * Since: 2.8
11002  **/
11003 gboolean
11004 pspp_sheet_view_get_visible_range (PsppSheetView  *tree_view,
11005                                  GtkTreePath **start_path,
11006                                  GtkTreePath **end_path)
11007 {
11008   int node;
11009   gboolean retval;
11010   
11011   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11012
11013   if (!tree_view->priv->row_count)
11014     return FALSE;
11015
11016   retval = TRUE;
11017
11018   if (start_path)
11019     {
11020       pspp_sheet_view_find_offset (tree_view,
11021                                    TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
11022                                    &node);
11023       if (node >= 0)
11024         *start_path = _pspp_sheet_view_find_path (tree_view, node);
11025       else
11026         retval = FALSE;
11027     }
11028
11029   if (end_path)
11030     {
11031       gint y;
11032
11033       if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
11034         y = tree_view->priv->height - 1;
11035       else
11036         y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
11037
11038       pspp_sheet_view_find_offset (tree_view, y, &node);
11039       if (node >= 0)
11040         *end_path = _pspp_sheet_view_find_path (tree_view, node);
11041       else
11042         retval = FALSE;
11043     }
11044
11045   return retval;
11046 }
11047
11048 static void
11049 unset_reorderable (PsppSheetView *tree_view)
11050 {
11051   if (tree_view->priv->reorderable)
11052     {
11053       tree_view->priv->reorderable = FALSE;
11054       g_object_notify (G_OBJECT (tree_view), "reorderable");
11055     }
11056 }
11057
11058 /**
11059  * pspp_sheet_view_enable_model_drag_source:
11060  * @tree_view: a #PsppSheetView
11061  * @start_button_mask: Mask of allowed buttons to start drag
11062  * @targets: the table of targets that the drag will support
11063  * @n_targets: the number of items in @targets
11064  * @actions: the bitmask of possible actions for a drag from this
11065  *    widget
11066  *
11067  * Turns @tree_view into a drag source for automatic DND. Calling this
11068  * method sets #PsppSheetView:reorderable to %FALSE.
11069  **/
11070 void
11071 pspp_sheet_view_enable_model_drag_source (PsppSheetView              *tree_view,
11072                                         GdkModifierType           start_button_mask,
11073                                         const GtkTargetEntry     *targets,
11074                                         gint                      n_targets,
11075                                         GdkDragAction             actions)
11076 {
11077   TreeViewDragInfo *di;
11078
11079   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11080
11081   gtk_drag_source_set (GTK_WIDGET (tree_view),
11082                        0,
11083                        targets,
11084                        n_targets,
11085                        actions);
11086
11087   di = ensure_info (tree_view);
11088
11089   di->start_button_mask = start_button_mask;
11090   di->source_actions = actions;
11091   di->source_set = TRUE;
11092
11093   unset_reorderable (tree_view);
11094 }
11095
11096 /**
11097  * pspp_sheet_view_enable_model_drag_dest:
11098  * @tree_view: a #PsppSheetView
11099  * @targets: the table of targets that the drag will support
11100  * @n_targets: the number of items in @targets
11101  * @actions: the bitmask of possible actions for a drag from this
11102  *    widget
11103  * 
11104  * Turns @tree_view into a drop destination for automatic DND. Calling
11105  * this method sets #PsppSheetView:reorderable to %FALSE.
11106  **/
11107 void
11108 pspp_sheet_view_enable_model_drag_dest (PsppSheetView              *tree_view,
11109                                       const GtkTargetEntry     *targets,
11110                                       gint                      n_targets,
11111                                       GdkDragAction             actions)
11112 {
11113   TreeViewDragInfo *di;
11114
11115   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11116
11117   gtk_drag_dest_set (GTK_WIDGET (tree_view),
11118                      0,
11119                      targets,
11120                      n_targets,
11121                      actions);
11122
11123   di = ensure_info (tree_view);
11124   di->dest_set = TRUE;
11125
11126   unset_reorderable (tree_view);
11127 }
11128
11129 /**
11130  * pspp_sheet_view_unset_rows_drag_source:
11131  * @tree_view: a #PsppSheetView
11132  *
11133  * Undoes the effect of
11134  * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
11135  * #PsppSheetView:reorderable to %FALSE.
11136  **/
11137 void
11138 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
11139 {
11140   TreeViewDragInfo *di;
11141
11142   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11143
11144   di = get_info (tree_view);
11145
11146   if (di)
11147     {
11148       if (di->source_set)
11149         {
11150           gtk_drag_source_unset (GTK_WIDGET (tree_view));
11151           di->source_set = FALSE;
11152         }
11153
11154       if (!di->dest_set && !di->source_set)
11155         remove_info (tree_view);
11156     }
11157   
11158   unset_reorderable (tree_view);
11159 }
11160
11161 /**
11162  * pspp_sheet_view_unset_rows_drag_dest:
11163  * @tree_view: a #PsppSheetView
11164  *
11165  * Undoes the effect of
11166  * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
11167  * #PsppSheetView:reorderable to %FALSE.
11168  **/
11169 void
11170 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
11171 {
11172   TreeViewDragInfo *di;
11173
11174   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11175
11176   di = get_info (tree_view);
11177
11178   if (di)
11179     {
11180       if (di->dest_set)
11181         {
11182           gtk_drag_dest_unset (GTK_WIDGET (tree_view));
11183           di->dest_set = FALSE;
11184         }
11185
11186       if (!di->dest_set && !di->source_set)
11187         remove_info (tree_view);
11188     }
11189
11190   unset_reorderable (tree_view);
11191 }
11192
11193 /**
11194  * pspp_sheet_view_set_drag_dest_row:
11195  * @tree_view: a #PsppSheetView
11196  * @path: (allow-none): The path of the row to highlight, or %NULL.
11197  * @pos: Specifies whether to drop before, after or into the row
11198  * 
11199  * Sets the row that is highlighted for feedback.
11200  **/
11201 void
11202 pspp_sheet_view_set_drag_dest_row (PsppSheetView            *tree_view,
11203                                  GtkTreePath            *path,
11204                                  PsppSheetViewDropPosition pos)
11205 {
11206   GtkTreePath *current_dest;
11207
11208   /* Note; this function is exported to allow a custom DND
11209    * implementation, so it can't touch TreeViewDragInfo
11210    */
11211
11212   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11213
11214   current_dest = NULL;
11215
11216   if (tree_view->priv->drag_dest_row)
11217     {
11218       current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11219       gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
11220     }
11221
11222   /* special case a drop on an empty model */
11223   tree_view->priv->empty_view_drop = 0;
11224
11225   if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
11226       && gtk_tree_path_get_depth (path) == 1
11227       && gtk_tree_path_get_indices (path)[0] == 0)
11228     {
11229       gint n_children;
11230
11231       n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
11232                                                    NULL);
11233
11234       if (!n_children)
11235         tree_view->priv->empty_view_drop = 1;
11236     }
11237
11238   tree_view->priv->drag_dest_pos = pos;
11239
11240   if (path)
11241     {
11242       tree_view->priv->drag_dest_row =
11243         gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
11244       pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
11245     }
11246   else
11247     tree_view->priv->drag_dest_row = NULL;
11248
11249   if (current_dest)
11250     {
11251       int node, new_node;
11252
11253       _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11254       _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11255
11256       if (node >= 0)
11257         {
11258           new_node = pspp_sheet_view_node_next (tree_view, node);
11259           if (new_node >= 0)
11260             _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11261
11262           new_node = pspp_sheet_view_node_prev (tree_view, node);
11263           if (new_node >= 0)
11264             _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11265         }
11266       gtk_tree_path_free (current_dest);
11267     }
11268 }
11269
11270 /**
11271  * pspp_sheet_view_get_drag_dest_row:
11272  * @tree_view: a #PsppSheetView
11273  * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11274  * @pos: (allow-none): Return location for the drop position, or %NULL
11275  * 
11276  * Gets information about the row that is highlighted for feedback.
11277  **/
11278 void
11279 pspp_sheet_view_get_drag_dest_row (PsppSheetView              *tree_view,
11280                                  GtkTreePath             **path,
11281                                  PsppSheetViewDropPosition  *pos)
11282 {
11283   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11284
11285   if (path)
11286     {
11287       if (tree_view->priv->drag_dest_row)
11288         *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11289       else
11290         {
11291           if (tree_view->priv->empty_view_drop)
11292             *path = gtk_tree_path_new_from_indices (0, -1);
11293           else
11294             *path = NULL;
11295         }
11296     }
11297
11298   if (pos)
11299     *pos = tree_view->priv->drag_dest_pos;
11300 }
11301
11302 /**
11303  * pspp_sheet_view_get_dest_row_at_pos:
11304  * @tree_view: a #PsppSheetView
11305  * @drag_x: the position to determine the destination row for
11306  * @drag_y: the position to determine the destination row for
11307  * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11308  * @pos: (allow-none): Return location for the drop position, or %NULL
11309  * 
11310  * Determines the destination row for a given position.  @drag_x and
11311  * @drag_y are expected to be in widget coordinates.  This function is only
11312  * meaningful if @tree_view is realized.  Therefore this function will always
11313  * return %FALSE if @tree_view is not realized or does not have a model.
11314  * 
11315  * Return value: whether there is a row at the given position, %TRUE if this
11316  * is indeed the case.
11317  **/
11318 gboolean
11319 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView             *tree_view,
11320                                    gint                     drag_x,
11321                                    gint                     drag_y,
11322                                    GtkTreePath            **path,
11323                                    PsppSheetViewDropPosition *pos)
11324 {
11325   gint cell_y;
11326   gint bin_x, bin_y;
11327   gdouble offset_into_row;
11328   gdouble third;
11329   GdkRectangle cell;
11330   PsppSheetViewColumn *column = NULL;
11331   GtkTreePath *tmp_path = NULL;
11332
11333   /* Note; this function is exported to allow a custom DND
11334    * implementation, so it can't touch TreeViewDragInfo
11335    */
11336
11337   g_return_val_if_fail (tree_view != NULL, FALSE);
11338   g_return_val_if_fail (drag_x >= 0, FALSE);
11339   g_return_val_if_fail (drag_y >= 0, FALSE);
11340
11341   if (path)
11342     *path = NULL;
11343
11344   if (tree_view->priv->bin_window == NULL)
11345     return FALSE;
11346
11347   if (tree_view->priv->row_count == 0)
11348     return FALSE;
11349
11350   /* If in the top third of a row, we drop before that row; if
11351    * in the bottom third, drop after that row; if in the middle,
11352    * and the row has children, drop into the row.
11353    */
11354   pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11355                                                      &bin_x, &bin_y);
11356
11357   if (!pspp_sheet_view_get_path_at_pos (tree_view,
11358                                       bin_x,
11359                                       bin_y,
11360                                       &tmp_path,
11361                                       &column,
11362                                       NULL,
11363                                       &cell_y))
11364     return FALSE;
11365
11366   pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11367                                      &cell);
11368
11369   offset_into_row = cell_y;
11370
11371   if (path)
11372     *path = tmp_path;
11373   else
11374     gtk_tree_path_free (tmp_path);
11375
11376   tmp_path = NULL;
11377
11378   third = cell.height / 3.0;
11379
11380   if (pos)
11381     {
11382       if (offset_into_row < third)
11383         {
11384           *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11385         }
11386       else if (offset_into_row < (cell.height / 2.0))
11387         {
11388           *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11389         }
11390       else if (offset_into_row < third * 2.0)
11391         {
11392           *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11393         }
11394       else
11395         {
11396           *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11397         }
11398     }
11399
11400   return TRUE;
11401 }
11402
11403
11404 #if GTK3_TRANSITION
11405 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11406 /**
11407  * pspp_sheet_view_create_row_drag_icon:
11408  * @tree_view: a #PsppSheetView
11409  * @path: a #GtkTreePath in @tree_view
11410  *
11411  * Creates a #GdkPixmap representation of the row at @path.  
11412  * This image is used for a drag icon.
11413  *
11414  * Return value: a newly-allocated pixmap of the drag icon.
11415  **/
11416 GdkPixmap *
11417 pspp_sheet_view_create_row_drag_icon (PsppSheetView  *tree_view,
11418                                     GtkTreePath  *path)
11419 {
11420   GtkTreeIter   iter;
11421   int node;
11422   gint cell_offset;
11423   GList *list;
11424   GdkRectangle background_area;
11425   GdkRectangle expose_area;
11426   GtkWidget *widget;
11427   /* start drawing inside the black outline */
11428   gint x = 1, y = 1;
11429   GdkDrawable *drawable;
11430   gint bin_window_width;
11431   gboolean rtl;
11432
11433   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11434   g_return_val_if_fail (path != NULL, NULL);
11435
11436   widget = GTK_WIDGET (tree_view);
11437
11438   if (!gtk_widget_get_realized (widget))
11439     return NULL;
11440
11441   _pspp_sheet_view_find_node (tree_view,
11442                             path,
11443                             &node);
11444
11445   if (node < 0)
11446     return NULL;
11447
11448   if (!gtk_tree_model_get_iter (tree_view->priv->model,
11449                                 &iter,
11450                                 path))
11451     return NULL;
11452   
11453   cell_offset = x;
11454
11455   background_area.y = y;
11456   background_area.height = ROW_HEIGHT (tree_view);
11457
11458   bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
11459
11460   drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11461                              bin_window_width + 2,
11462                              background_area.height + 2,
11463                              -1);
11464
11465   expose_area.x = 0;
11466   expose_area.y = 0;
11467   expose_area.width = bin_window_width + 2;
11468   expose_area.height = background_area.height + 2;
11469
11470 #if GTK3_TRANSITION
11471   gdk_draw_rectangle (drawable,
11472                       widget->style->base_gc [gtk_widget_get_state (widget)],
11473                       TRUE,
11474                       0, 0,
11475                       bin_window_width + 2,
11476                       background_area.height + 2);
11477 #endif
11478
11479   rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11480
11481   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11482       list;
11483       list = (rtl ? list->prev : list->next))
11484     {
11485       PsppSheetViewColumn *column = list->data;
11486       GdkRectangle cell_area;
11487       gint vertical_separator;
11488
11489       if (!column->visible)
11490         continue;
11491
11492       pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11493
11494       background_area.x = cell_offset;
11495       background_area.width = column->width;
11496
11497       gtk_widget_style_get (widget,
11498                             "vertical-separator", &vertical_separator,
11499                             NULL);
11500
11501       cell_area = background_area;
11502
11503       cell_area.y += vertical_separator / 2;
11504       cell_area.height -= vertical_separator;
11505
11506       if (pspp_sheet_view_column_cell_is_visible (column))
11507         _pspp_sheet_view_column_cell_render (column,
11508                                              drawable,
11509                                              &background_area,
11510                                              &cell_area,
11511                                              &expose_area,
11512                                              0);
11513       cell_offset += column->width;
11514     }
11515
11516 #if GTK3_TRANSITION
11517   gdk_draw_rectangle (drawable,
11518                       widget->style->black_gc,
11519                       FALSE,
11520                       0, 0,
11521                       bin_window_width + 1,
11522                       background_area.height + 1);
11523 #endif
11524
11525   return drawable;
11526 }
11527 #endif
11528
11529 /**
11530  * pspp_sheet_view_set_destroy_count_func:
11531  * @tree_view: A #PsppSheetView
11532  * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11533  * @data: (allow-none): User data to be passed to @func, or %NULL
11534  * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11535  *
11536  * This function should almost never be used.  It is meant for private use by
11537  * ATK for determining the number of visible children that are removed when a row is deleted.
11538  **/
11539 void
11540 pspp_sheet_view_set_destroy_count_func (PsppSheetView             *tree_view,
11541                                       PsppSheetDestroyCountFunc  func,
11542                                       gpointer                 data,
11543                                       GDestroyNotify           destroy)
11544 {
11545   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11546
11547   if (tree_view->priv->destroy_count_destroy)
11548     tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11549
11550   tree_view->priv->destroy_count_func = func;
11551   tree_view->priv->destroy_count_data = data;
11552   tree_view->priv->destroy_count_destroy = destroy;
11553 }
11554
11555
11556 /*
11557  * Interactive search
11558  */
11559
11560 /**
11561  * pspp_sheet_view_set_enable_search:
11562  * @tree_view: A #PsppSheetView
11563  * @enable_search: %TRUE, if the user can search interactively
11564  *
11565  * If @enable_search is set, then the user can type in text to search through
11566  * the tree interactively (this is sometimes called "typeahead find").
11567  * 
11568  * Note that even if this is %FALSE, the user can still initiate a search 
11569  * using the "start-interactive-search" key binding.
11570  */
11571 void
11572 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11573                                  gboolean     enable_search)
11574 {
11575   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11576
11577   enable_search = !!enable_search;
11578   
11579   if (tree_view->priv->enable_search != enable_search)
11580     {
11581        tree_view->priv->enable_search = enable_search;
11582        g_object_notify (G_OBJECT (tree_view), "enable-search");
11583     }
11584 }
11585
11586 /**
11587  * pspp_sheet_view_get_enable_search:
11588  * @tree_view: A #PsppSheetView
11589  *
11590  * Returns whether or not the tree allows to start interactive searching 
11591  * by typing in text.
11592  *
11593  * Return value: whether or not to let the user search interactively
11594  */
11595 gboolean
11596 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11597 {
11598   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11599
11600   return tree_view->priv->enable_search;
11601 }
11602
11603
11604 /**
11605  * pspp_sheet_view_get_search_column:
11606  * @tree_view: A #PsppSheetView
11607  *
11608  * Gets the column searched on by the interactive search code.
11609  *
11610  * Return value: the column the interactive search code searches in.
11611  */
11612 gint
11613 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11614 {
11615   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11616
11617   return (tree_view->priv->search_column);
11618 }
11619
11620 /**
11621  * pspp_sheet_view_set_search_column:
11622  * @tree_view: A #PsppSheetView
11623  * @column: the column of the model to search in, or -1 to disable searching
11624  *
11625  * Sets @column as the column where the interactive search code should
11626  * search in for the current model. 
11627  * 
11628  * If the search column is set, users can use the "start-interactive-search"
11629  * key binding to bring up search popup. The enable-search property controls
11630  * whether simply typing text will also start an interactive search.
11631  *
11632  * Note that @column refers to a column of the current model. The search 
11633  * column is reset to -1 when the model is changed.
11634  */
11635 void
11636 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11637                                  gint         column)
11638 {
11639   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11640   g_return_if_fail (column >= -1);
11641
11642   if (tree_view->priv->search_column == column)
11643     return;
11644
11645   tree_view->priv->search_column = column;
11646   g_object_notify (G_OBJECT (tree_view), "search-column");
11647 }
11648
11649 /**
11650  * pspp_sheet_view_get_search_equal_func:
11651  * @tree_view: A #PsppSheetView
11652  *
11653  * Returns the compare function currently in use.
11654  *
11655  * Return value: the currently used compare function for the search code.
11656  */
11657
11658 PsppSheetViewSearchEqualFunc
11659 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11660 {
11661   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11662
11663   return tree_view->priv->search_equal_func;
11664 }
11665
11666 /**
11667  * pspp_sheet_view_set_search_equal_func:
11668  * @tree_view: A #PsppSheetView
11669  * @search_equal_func: the compare function to use during the search
11670  * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11671  * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11672  *
11673  * Sets the compare function for the interactive search capabilities; note
11674  * that somewhat like strcmp() returning 0 for equality
11675  * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11676  **/
11677 void
11678 pspp_sheet_view_set_search_equal_func (PsppSheetView                *tree_view,
11679                                      PsppSheetViewSearchEqualFunc  search_equal_func,
11680                                      gpointer                    search_user_data,
11681                                      GDestroyNotify              search_destroy)
11682 {
11683   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11684   g_return_if_fail (search_equal_func != NULL);
11685
11686   if (tree_view->priv->search_destroy)
11687     tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11688
11689   tree_view->priv->search_equal_func = search_equal_func;
11690   tree_view->priv->search_user_data = search_user_data;
11691   tree_view->priv->search_destroy = search_destroy;
11692   if (tree_view->priv->search_equal_func == NULL)
11693     tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11694 }
11695
11696 /**
11697  * pspp_sheet_view_get_search_entry:
11698  * @tree_view: A #PsppSheetView
11699  *
11700  * Returns the #GtkEntry which is currently in use as interactive search
11701  * entry for @tree_view.  In case the built-in entry is being used, %NULL
11702  * will be returned.
11703  *
11704  * Return value: the entry currently in use as search entry.
11705  *
11706  * Since: 2.10
11707  */
11708 GtkEntry *
11709 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11710 {
11711   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11712
11713   if (tree_view->priv->search_custom_entry_set)
11714     return GTK_ENTRY (tree_view->priv->search_entry);
11715
11716   return NULL;
11717 }
11718
11719 /**
11720  * pspp_sheet_view_set_search_entry:
11721  * @tree_view: A #PsppSheetView
11722  * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11723  *
11724  * Sets the entry which the interactive search code will use for this
11725  * @tree_view.  This is useful when you want to provide a search entry
11726  * in our interface at all time at a fixed position.  Passing %NULL for
11727  * @entry will make the interactive search code use the built-in popup
11728  * entry again.
11729  *
11730  * Since: 2.10
11731  */
11732 void
11733 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11734                                 GtkEntry    *entry)
11735 {
11736   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11737   g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11738
11739   if (tree_view->priv->search_custom_entry_set)
11740     {
11741       if (tree_view->priv->search_entry_changed_id)
11742         {
11743           g_signal_handler_disconnect (tree_view->priv->search_entry,
11744                                        tree_view->priv->search_entry_changed_id);
11745           tree_view->priv->search_entry_changed_id = 0;
11746         }
11747       g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11748                                             G_CALLBACK (pspp_sheet_view_search_key_press_event),
11749                                             tree_view);
11750
11751       g_object_unref (tree_view->priv->search_entry);
11752     }
11753   else if (tree_view->priv->search_window)
11754     {
11755       gtk_widget_destroy (tree_view->priv->search_window);
11756
11757       tree_view->priv->search_window = NULL;
11758     }
11759
11760   if (entry)
11761     {
11762       tree_view->priv->search_entry = g_object_ref (entry);
11763       tree_view->priv->search_custom_entry_set = TRUE;
11764
11765       if (tree_view->priv->search_entry_changed_id == 0)
11766         {
11767           tree_view->priv->search_entry_changed_id =
11768             g_signal_connect (tree_view->priv->search_entry, "changed",
11769                               G_CALLBACK (pspp_sheet_view_search_init),
11770                               tree_view);
11771         }
11772       
11773         g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11774                           G_CALLBACK (pspp_sheet_view_search_key_press_event),
11775                           tree_view);
11776
11777         pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11778     }
11779   else
11780     {
11781       tree_view->priv->search_entry = NULL;
11782       tree_view->priv->search_custom_entry_set = FALSE;
11783     }
11784 }
11785
11786 /**
11787  * pspp_sheet_view_set_search_position_func:
11788  * @tree_view: A #PsppSheetView
11789  * @func: (allow-none): the function to use to position the search dialog, or %NULL
11790  *    to use the default search position function
11791  * @data: (allow-none): user data to pass to @func, or %NULL
11792  * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11793  *
11794  * Sets the function to use when positioning the search dialog.
11795  *
11796  * Since: 2.10
11797  **/
11798 void
11799 pspp_sheet_view_set_search_position_func (PsppSheetView                   *tree_view,
11800                                         PsppSheetViewSearchPositionFunc  func,
11801                                         gpointer                       user_data,
11802                                         GDestroyNotify                 destroy)
11803 {
11804   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11805
11806   if (tree_view->priv->search_position_destroy)
11807     tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11808
11809   tree_view->priv->search_position_func = func;
11810   tree_view->priv->search_position_user_data = user_data;
11811   tree_view->priv->search_position_destroy = destroy;
11812   if (tree_view->priv->search_position_func == NULL)
11813     tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11814 }
11815
11816 /**
11817  * pspp_sheet_view_get_search_position_func:
11818  * @tree_view: A #PsppSheetView
11819  *
11820  * Returns the positioning function currently in use.
11821  *
11822  * Return value: the currently used function for positioning the search dialog.
11823  *
11824  * Since: 2.10
11825  */
11826 PsppSheetViewSearchPositionFunc
11827 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11828 {
11829   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11830
11831   return tree_view->priv->search_position_func;
11832 }
11833
11834
11835 static void
11836 pspp_sheet_view_search_dialog_hide (GtkWidget   *search_dialog,
11837                                   PsppSheetView *tree_view)
11838 {
11839   if (tree_view->priv->disable_popdown)
11840     return;
11841
11842   if (tree_view->priv->search_entry_changed_id)
11843     {
11844       g_signal_handler_disconnect (tree_view->priv->search_entry,
11845                                    tree_view->priv->search_entry_changed_id);
11846       tree_view->priv->search_entry_changed_id = 0;
11847     }
11848   if (tree_view->priv->typeselect_flush_timeout)
11849     {
11850       g_source_remove (tree_view->priv->typeselect_flush_timeout);
11851       tree_view->priv->typeselect_flush_timeout = 0;
11852     }
11853         
11854   if (gtk_widget_get_visible (search_dialog))
11855     {
11856       /* send focus-in event */
11857       send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11858       gtk_widget_hide (search_dialog);
11859       gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11860       send_focus_change (GTK_WIDGET (tree_view), TRUE);
11861     }
11862 }
11863
11864 static void
11865 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11866                                     GtkWidget   *search_dialog,
11867                                     gpointer     user_data)
11868 {
11869   gint x, y;
11870   gint tree_x, tree_y;
11871   gint tree_width, tree_height;
11872   GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
11873   GdkScreen *screen = gdk_window_get_screen (tree_window);
11874   GtkRequisition requisition;
11875   gint monitor_num;
11876   GdkRectangle monitor;
11877
11878   monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11879   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11880
11881   gtk_widget_realize (search_dialog);
11882
11883   gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11884   tree_width = gdk_window_get_width (tree_window);
11885   tree_height = gdk_window_get_height (tree_window);
11886
11887   gtk_widget_size_request (search_dialog, &requisition);
11888
11889   if (tree_x + tree_width > gdk_screen_get_width (screen))
11890     x = gdk_screen_get_width (screen) - requisition.width;
11891   else if (tree_x + tree_width - requisition.width < 0)
11892     x = 0;
11893   else
11894     x = tree_x + tree_width - requisition.width;
11895
11896   if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11897     y = gdk_screen_get_height (screen) - requisition.height;
11898   else if (tree_y + tree_height < 0) /* isn't really possible ... */
11899     y = 0;
11900   else
11901     y = tree_y + tree_height;
11902
11903   gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11904 }
11905
11906 static void
11907 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11908                                       GtkMenu  *menu,
11909                                       gpointer  data)
11910 {
11911   PsppSheetView *tree_view = (PsppSheetView *)data;
11912
11913   tree_view->priv->disable_popdown = 1;
11914   g_signal_connect (menu, "hide",
11915                     G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11916 }
11917
11918 #if GTK3_TRANSITION
11919 /* Because we're visible but offscreen, we just set a flag in the preedit
11920  * callback.
11921  */
11922 static void
11923 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11924                                       PsppSheetView  *tree_view)
11925 {
11926   tree_view->priv->imcontext_changed = 1;
11927   if (tree_view->priv->typeselect_flush_timeout)
11928     {
11929       g_source_remove (tree_view->priv->typeselect_flush_timeout);
11930       tree_view->priv->typeselect_flush_timeout =
11931         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11932                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11933                        tree_view);
11934     }
11935
11936 }
11937 #endif
11938
11939 static void
11940 pspp_sheet_view_search_activate (GtkEntry    *entry,
11941                                PsppSheetView *tree_view)
11942 {
11943   GtkTreePath *path;
11944   int node;
11945
11946   pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11947                                     tree_view);
11948
11949   /* If we have a row selected and it's the cursor row, we activate
11950    * the row XXX */
11951   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11952     {
11953       path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11954       
11955       _pspp_sheet_view_find_node (tree_view, path, &node);
11956       
11957       if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11958         pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11959       
11960       gtk_tree_path_free (path);
11961     }
11962 }
11963
11964 static gboolean
11965 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11966 {
11967   PsppSheetView *tree_view = (PsppSheetView *)data;
11968
11969   tree_view->priv->disable_popdown = 0;
11970
11971   return FALSE;
11972 }
11973
11974 static void
11975 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11976                                      gpointer   data)
11977 {
11978   gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11979 }
11980
11981 static gboolean
11982 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11983                                    GdkEventAny *event,
11984                                    PsppSheetView *tree_view)
11985 {
11986   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11987
11988   pspp_sheet_view_search_dialog_hide (widget, tree_view);
11989
11990   return TRUE;
11991 }
11992
11993 static gboolean
11994 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11995                                          GdkEventButton *event,
11996                                          PsppSheetView *tree_view)
11997 {
11998   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11999
12000   pspp_sheet_view_search_dialog_hide (widget, tree_view);
12001
12002   if (event->window == tree_view->priv->bin_window)
12003     pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
12004
12005   return TRUE;
12006 }
12007
12008 static gboolean
12009 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
12010                                    GdkEventScroll *event,
12011                                    PsppSheetView *tree_view)
12012 {
12013   gboolean retval = FALSE;
12014
12015   if (event->direction == GDK_SCROLL_UP)
12016     {
12017       pspp_sheet_view_search_move (widget, tree_view, TRUE);
12018       retval = TRUE;
12019     }
12020   else if (event->direction == GDK_SCROLL_DOWN)
12021     {
12022       pspp_sheet_view_search_move (widget, tree_view, FALSE);
12023       retval = TRUE;
12024     }
12025
12026   /* renew the flush timeout */
12027   if (retval && tree_view->priv->typeselect_flush_timeout
12028       && !tree_view->priv->search_custom_entry_set)
12029     {
12030       g_source_remove (tree_view->priv->typeselect_flush_timeout);
12031       tree_view->priv->typeselect_flush_timeout =
12032         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12033                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12034                        tree_view);
12035     }
12036
12037   return retval;
12038 }
12039
12040 static gboolean
12041 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
12042                                       GdkEventKey *event,
12043                                       PsppSheetView *tree_view)
12044 {
12045   gboolean retval = FALSE;
12046
12047   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
12048   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12049
12050   /* close window and cancel the search */
12051   if (!tree_view->priv->search_custom_entry_set
12052       && (event->keyval == GDK_Escape ||
12053           event->keyval == GDK_Tab ||
12054             event->keyval == GDK_KP_Tab ||
12055             event->keyval == GDK_ISO_Left_Tab))
12056     {
12057       pspp_sheet_view_search_dialog_hide (widget, tree_view);
12058       return TRUE;
12059     }
12060
12061   /* select previous matching iter */
12062   if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
12063     {
12064       if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12065         gtk_widget_error_bell (widget);
12066
12067       retval = TRUE;
12068     }
12069
12070   if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
12071       && (event->keyval == GDK_g || event->keyval == GDK_G))
12072     {
12073       if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12074         gtk_widget_error_bell (widget);
12075
12076       retval = TRUE;
12077     }
12078
12079   /* select next matching iter */
12080   if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
12081     {
12082       if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12083         gtk_widget_error_bell (widget);
12084
12085       retval = TRUE;
12086     }
12087
12088   if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
12089       && (event->keyval == GDK_g || event->keyval == GDK_G))
12090     {
12091       if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12092         gtk_widget_error_bell (widget);
12093
12094       retval = TRUE;
12095     }
12096
12097   /* renew the flush timeout */
12098   if (retval && tree_view->priv->typeselect_flush_timeout
12099       && !tree_view->priv->search_custom_entry_set)
12100     {
12101       g_source_remove (tree_view->priv->typeselect_flush_timeout);
12102       tree_view->priv->typeselect_flush_timeout =
12103         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12104                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12105                        tree_view);
12106     }
12107
12108   return retval;
12109 }
12110
12111 /*  this function returns FALSE if there is a search string but
12112  *  nothing was found, and TRUE otherwise.
12113  */
12114 static gboolean
12115 pspp_sheet_view_search_move (GtkWidget   *window,
12116                            PsppSheetView *tree_view,
12117                            gboolean     up)
12118 {
12119   gboolean ret;
12120   gint len;
12121   gint count = 0;
12122   const gchar *text;
12123   GtkTreeIter iter;
12124   GtkTreeModel *model;
12125   PsppSheetSelection *selection;
12126
12127   text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
12128
12129   g_return_val_if_fail (text != NULL, FALSE);
12130
12131   len = strlen (text);
12132
12133   if (up && tree_view->priv->selected_iter == 1)
12134     return strlen (text) < 1;
12135
12136   len = strlen (text);
12137
12138   if (len < 1)
12139     return TRUE;
12140
12141   model = pspp_sheet_view_get_model (tree_view);
12142   selection = pspp_sheet_view_get_selection (tree_view);
12143
12144   /* search */
12145   pspp_sheet_selection_unselect_all (selection);
12146   if (!gtk_tree_model_get_iter_first (model, &iter))
12147     return TRUE;
12148
12149   ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
12150                                    &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
12151
12152   if (ret)
12153     {
12154       /* found */
12155       tree_view->priv->selected_iter += up?(-1):(1);
12156       return TRUE;
12157     }
12158   else
12159     {
12160       /* return to old iter */
12161       count = 0;
12162       gtk_tree_model_get_iter_first (model, &iter);
12163       pspp_sheet_view_search_iter (model, selection,
12164                                  &iter, text,
12165                                  &count, tree_view->priv->selected_iter);
12166       return FALSE;
12167     }
12168 }
12169
12170 static gboolean
12171 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
12172                                  gint          column,
12173                                  const gchar  *key,
12174                                  GtkTreeIter  *iter,
12175                                  gpointer      search_data)
12176 {
12177   gboolean retval = TRUE;
12178   const gchar *str;
12179   gchar *normalized_string;
12180   gchar *normalized_key;
12181   gchar *case_normalized_string = NULL;
12182   gchar *case_normalized_key = NULL;
12183   GValue value = {0,};
12184   GValue transformed = {0,};
12185
12186   gtk_tree_model_get_value (model, iter, column, &value);
12187
12188   g_value_init (&transformed, G_TYPE_STRING);
12189
12190   if (!g_value_transform (&value, &transformed))
12191     {
12192       g_value_unset (&value);
12193       return TRUE;
12194     }
12195
12196   g_value_unset (&value);
12197
12198   str = g_value_get_string (&transformed);
12199   if (!str)
12200     {
12201       g_value_unset (&transformed);
12202       return TRUE;
12203     }
12204
12205   normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
12206   normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
12207
12208   if (normalized_string && normalized_key)
12209     {
12210       case_normalized_string = g_utf8_casefold (normalized_string, -1);
12211       case_normalized_key = g_utf8_casefold (normalized_key, -1);
12212
12213       if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
12214         retval = FALSE;
12215     }
12216
12217   g_value_unset (&transformed);
12218   g_free (normalized_key);
12219   g_free (normalized_string);
12220   g_free (case_normalized_key);
12221   g_free (case_normalized_string);
12222
12223   return retval;
12224 }
12225
12226 static gboolean
12227 pspp_sheet_view_search_iter (GtkTreeModel     *model,
12228                              PsppSheetSelection *selection,
12229                              GtkTreeIter      *iter,
12230                              const gchar      *text,
12231                              gint             *count,
12232                              gint              n)
12233 {
12234   int node = -1;
12235   GtkTreePath *path;
12236
12237   PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
12238
12239   path = gtk_tree_model_get_path (model, iter);
12240   _pspp_sheet_view_find_node (tree_view, path, &node);
12241
12242   do
12243     {
12244       gboolean done = FALSE;
12245
12246       if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12247         {
12248           (*count)++;
12249           if (*count == n)
12250             {
12251               pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12252                                               TRUE, 0.5, 0.0);
12253               pspp_sheet_selection_select_iter (selection, iter);
12254               pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12255
12256               if (path)
12257                 gtk_tree_path_free (path);
12258
12259               return TRUE;
12260             }
12261         }
12262
12263
12264       do
12265         {
12266           node = pspp_sheet_view_node_next (tree_view, node);
12267
12268           if (node >= 0)
12269             {
12270               gboolean has_next;
12271
12272               has_next = gtk_tree_model_iter_next (model, iter);
12273
12274               done = TRUE;
12275               gtk_tree_path_next (path);
12276
12277               /* sanity check */
12278               TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12279             }
12280           else
12281             {
12282               if (path)
12283                 gtk_tree_path_free (path);
12284
12285               /* we've run out of tree, done with this func */
12286               return FALSE;
12287             }
12288         }
12289       while (!done);
12290     }
12291   while (1);
12292
12293   return FALSE;
12294 }
12295
12296 static void
12297 pspp_sheet_view_search_init (GtkWidget   *entry,
12298                            PsppSheetView *tree_view)
12299 {
12300   gint ret;
12301   gint count = 0;
12302   const gchar *text;
12303   GtkTreeIter iter;
12304   GtkTreeModel *model;
12305   PsppSheetSelection *selection;
12306
12307   g_return_if_fail (GTK_IS_ENTRY (entry));
12308   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12309
12310   text = gtk_entry_get_text (GTK_ENTRY (entry));
12311
12312   model = pspp_sheet_view_get_model (tree_view);
12313   selection = pspp_sheet_view_get_selection (tree_view);
12314
12315   /* search */
12316   pspp_sheet_selection_unselect_all (selection);
12317   if (tree_view->priv->typeselect_flush_timeout
12318       && !tree_view->priv->search_custom_entry_set)
12319     {
12320       g_source_remove (tree_view->priv->typeselect_flush_timeout);
12321       tree_view->priv->typeselect_flush_timeout =
12322         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12323                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12324                        tree_view);
12325     }
12326
12327   if (*text == '\0')
12328     return;
12329
12330   if (!gtk_tree_model_get_iter_first (model, &iter))
12331     return;
12332
12333   ret = pspp_sheet_view_search_iter (model, selection,
12334                                    &iter, text,
12335                                    &count, 1);
12336
12337   if (ret)
12338     tree_view->priv->selected_iter = 1;
12339 }
12340
12341 static void
12342 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12343                              PsppSheetView     *tree_view)
12344 {
12345   if (tree_view->priv->edited_column == NULL)
12346     return;
12347
12348   _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12349   tree_view->priv->edited_column = NULL;
12350
12351   if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12352     gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12353
12354   g_signal_handlers_disconnect_by_func (cell_editable,
12355                                         pspp_sheet_view_remove_widget,
12356                                         tree_view);
12357   g_signal_handlers_disconnect_by_func (cell_editable,
12358                                         pspp_sheet_view_editable_button_press_event,
12359                                         tree_view);
12360   g_signal_handlers_disconnect_by_func (cell_editable,
12361                                         pspp_sheet_view_editable_clicked,
12362                                         tree_view);
12363
12364   gtk_container_remove (GTK_CONTAINER (tree_view),
12365                         GTK_WIDGET (cell_editable));  
12366
12367   /* FIXME should only redraw a single node */
12368   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12369 }
12370
12371 static gboolean
12372 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12373                              GtkTreePath *cursor_path)
12374 {
12375   GtkTreeIter iter;
12376   GdkRectangle background_area;
12377   GdkRectangle cell_area;
12378   GtkCellEditable *editable_widget = NULL;
12379   gchar *path_string;
12380   guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12381   gint retval = FALSE;
12382   int cursor_node;
12383
12384   g_assert (tree_view->priv->focus_column);
12385
12386   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12387     return FALSE;
12388
12389   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12390   if (cursor_node < 0)
12391     return FALSE;
12392
12393   path_string = gtk_tree_path_to_string (cursor_path);
12394   gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12395
12396   pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12397                                            tree_view->priv->model,
12398                                            &iter);
12399   pspp_sheet_view_get_background_area (tree_view,
12400                                      cursor_path,
12401                                      tree_view->priv->focus_column,
12402                                      &background_area);
12403   pspp_sheet_view_get_cell_area (tree_view,
12404                                cursor_path,
12405                                tree_view->priv->focus_column,
12406                                &cell_area);
12407
12408   if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12409                                         &editable_widget,
12410                                         NULL,
12411                                         path_string,
12412                                         &background_area,
12413                                         &cell_area,
12414                                         flags))
12415     {
12416       retval = TRUE;
12417       if (editable_widget != NULL)
12418         {
12419           gint left, right;
12420           GdkRectangle area;
12421           GtkCellRenderer *cell;
12422
12423           area = cell_area;
12424           cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12425
12426           _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12427
12428           area.x += left;
12429           area.width -= right + left;
12430
12431           pspp_sheet_view_real_start_editing (tree_view,
12432                                             tree_view->priv->focus_column,
12433                                             cursor_path,
12434                                             editable_widget,
12435                                             &area,
12436                                             NULL,
12437                                             flags);
12438         }
12439
12440     }
12441   g_free (path_string);
12442   return retval;
12443 }
12444
12445 static gboolean
12446 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12447                                              GdkEventButton *event,
12448                                              PsppSheetView *sheet_view)
12449 {
12450   gint node;
12451
12452   node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12453                                              "pspp-sheet-view-node"));
12454   return pspp_sheet_view_row_head_clicked (sheet_view,
12455                                            node,
12456                                            sheet_view->priv->edited_column,
12457                                            event);
12458 }
12459
12460 static void
12461 pspp_sheet_view_editable_clicked (GtkButton *button,
12462                                   PsppSheetView *sheet_view)
12463 {
12464   pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12465                                                sheet_view);
12466 }
12467
12468 static gboolean
12469 is_all_selected (GtkWidget *widget)
12470 {
12471   GtkEntryBuffer *buffer;
12472   gint start_pos, end_pos;
12473
12474   if (!GTK_IS_ENTRY (widget))
12475     return FALSE;
12476
12477   buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12478   return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12479                                              &start_pos, &end_pos)
12480           && start_pos == 0
12481           && end_pos == gtk_entry_buffer_get_length (buffer));
12482 }
12483
12484 static gboolean
12485 is_at_left (GtkWidget *widget)
12486 {
12487   return (GTK_IS_ENTRY (widget)
12488           && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12489 }
12490
12491 static gboolean
12492 is_at_right (GtkWidget *widget)
12493 {
12494   GtkEntryBuffer *buffer;
12495   gint length;
12496
12497   if (!GTK_IS_ENTRY (widget))
12498     return FALSE;
12499
12500   buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12501   length = gtk_entry_buffer_get_length (buffer);
12502   return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12503 }
12504
12505 static gboolean
12506 pspp_sheet_view_event (GtkWidget *widget,
12507                        GdkEventKey *event,
12508                        PsppSheetView *tree_view)
12509 {
12510   PsppSheetViewColumn *column;
12511   GtkTreePath *path;
12512   gboolean handled;
12513   gboolean cancel;
12514   guint keyval;
12515   gint row;
12516
12517   /* Intercept only key press events.
12518      It would make sense to use "key-press-event" instead of "event", but
12519      GtkEntry attaches its own signal handler to "key-press-event" that runs
12520      before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12521   */
12522   if (event->type != GDK_KEY_PRESS)
12523     return FALSE;
12524
12525   keyval = event->keyval;
12526   cancel = FALSE;
12527   switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12528     {
12529     case 0:
12530       switch (event->keyval)
12531         {
12532         case GDK_Left:      case GDK_KP_Left:
12533         case GDK_Home:      case GDK_KP_Home:
12534           if (!is_all_selected (widget) && !is_at_left (widget))
12535             return FALSE;
12536           break;
12537
12538         case GDK_Right:     case GDK_KP_Right:
12539         case GDK_End:       case GDK_KP_End:
12540           if (!is_all_selected (widget) && !is_at_right (widget))
12541             return FALSE;
12542           break;
12543
12544         case GDK_Up:        case GDK_KP_Up:
12545         case GDK_Down:      case GDK_KP_Down:
12546           break;
12547
12548         case GDK_Page_Up:   case GDK_KP_Page_Up:
12549         case GDK_Page_Down: case GDK_KP_Page_Down:
12550           break;
12551
12552         case GDK_Escape:
12553           cancel = TRUE;
12554           break;
12555
12556         case GDK_Return:
12557           keyval = GDK_Down;
12558           break;
12559
12560         case GDK_Tab:       case GDK_KP_Tab:
12561         case GDK_ISO_Left_Tab:
12562           keyval = GDK_Tab;
12563           break;
12564
12565         default:
12566           return FALSE;
12567         }
12568       break;
12569
12570     case GDK_SHIFT_MASK:
12571       switch (event->keyval)
12572         {
12573         case GDK_Tab:
12574         case GDK_ISO_Left_Tab:
12575           keyval = GDK_Tab;
12576           break;
12577
12578         default:
12579           return FALSE;
12580         }
12581       break;
12582
12583     case GDK_CONTROL_MASK:
12584       switch (event->keyval)
12585         {
12586         case GDK_Left:      case GDK_KP_Left:
12587           if (!is_all_selected (widget) && !is_at_left (widget))
12588             return FALSE;
12589           break;
12590
12591         case GDK_Right:     case GDK_KP_Right:
12592           if (!is_all_selected (widget) && !is_at_right (widget))
12593             return FALSE;
12594           break;
12595
12596         case GDK_Up:        case GDK_KP_Up:
12597         case GDK_Down:      case GDK_KP_Down:
12598           break;
12599
12600         default:
12601           return FALSE;
12602         }
12603       break;
12604
12605     default:
12606       return FALSE;
12607     }
12608
12609   row = tree_view->priv->edited_row;
12610   column = tree_view->priv->edited_column;
12611   path = gtk_tree_path_new_from_indices (row, -1);
12612
12613   pspp_sheet_view_stop_editing (tree_view, cancel);
12614   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12615
12616   pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12617   gtk_tree_path_free (path);
12618
12619   handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12620                                       G_OBJECT (tree_view));
12621   if (handled)
12622     g_signal_stop_emission_by_name (widget, "event");
12623
12624   pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12625   pspp_sheet_view_start_editing (tree_view, path);
12626   gtk_tree_path_free (path);
12627
12628   return handled;
12629 }
12630
12631 static void
12632 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12633                                           gpointer data)
12634 {
12635   PsppSheetView *sheet_view = data;
12636
12637   g_signal_connect (widget, "event",
12638                     G_CALLBACK (pspp_sheet_view_event),
12639                     sheet_view);
12640
12641   if (GTK_IS_CONTAINER (widget))
12642     gtk_container_foreach (GTK_CONTAINER (widget),
12643                            pspp_sheet_view_override_cell_keypresses,
12644                            data);
12645 }
12646
12647 static void
12648 pspp_sheet_view_real_start_editing (PsppSheetView       *tree_view,
12649                                   PsppSheetViewColumn *column,
12650                                   GtkTreePath       *path,
12651                                   GtkCellEditable   *cell_editable,
12652                                   GdkRectangle      *cell_area,
12653                                   GdkEvent          *event,
12654                                   guint              flags)
12655 {
12656   PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12657   gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
12658   GtkRequisition requisition;
12659   gint row;
12660
12661   g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12662
12663   tree_view->priv->edited_column = column;
12664   _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12665
12666   row = gtk_tree_path_get_indices (path)[0];
12667   tree_view->priv->edited_row = row;
12668   pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12669   cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment);
12670
12671   pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12672   pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12673   tree_view->priv->anchor_column = column;
12674
12675   gtk_widget_size_request (GTK_WIDGET (cell_editable), &requisition);
12676
12677   PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12678
12679   if (requisition.height < cell_area->height)
12680     {
12681       gint diff = cell_area->height - requisition.height;
12682       pspp_sheet_view_put (tree_view,
12683                          GTK_WIDGET (cell_editable),
12684                          cell_area->x, cell_area->y + diff/2,
12685                          cell_area->width, requisition.height);
12686     }
12687   else
12688     {
12689       pspp_sheet_view_put (tree_view,
12690                          GTK_WIDGET (cell_editable),
12691                          cell_area->x, cell_area->y,
12692                          cell_area->width, cell_area->height);
12693     }
12694
12695   gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12696                                    (GdkEvent *)event);
12697
12698   gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12699   g_signal_connect (cell_editable, "remove-widget",
12700                     G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12701   if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12702       GTK_IS_BUTTON (cell_editable))
12703     {
12704       g_signal_connect (cell_editable, "button-press-event",
12705                         G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12706                         tree_view);
12707       g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12708                          GINT_TO_POINTER (row));
12709       g_signal_connect (cell_editable, "clicked",
12710                         G_CALLBACK (pspp_sheet_view_editable_clicked),
12711                         tree_view);
12712     }
12713
12714   pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12715                                             tree_view);
12716 }
12717
12718 void
12719 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12720                               gboolean     cancel_editing)
12721 {
12722   PsppSheetViewColumn *column;
12723   GtkCellRenderer *cell;
12724
12725   if (tree_view->priv->edited_column == NULL)
12726     return;
12727
12728   /*
12729    * This is very evil. We need to do this, because
12730    * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12731    * later on. If pspp_sheet_view_row_changed notices
12732    * tree_view->priv->edited_column != NULL, it'll call
12733    * pspp_sheet_view_stop_editing again. Bad things will happen then.
12734    *
12735    * Please read that again if you intend to modify anything here.
12736    */
12737
12738   column = tree_view->priv->edited_column;
12739   tree_view->priv->edited_column = NULL;
12740
12741   cell = _pspp_sheet_view_column_get_edited_cell (column);
12742   gtk_cell_renderer_stop_editing (cell, cancel_editing);
12743
12744   if (!cancel_editing)
12745     gtk_cell_editable_editing_done (column->editable_widget);
12746
12747   tree_view->priv->edited_column = column;
12748
12749   gtk_cell_editable_remove_widget (column->editable_widget);
12750 }
12751
12752
12753 /**
12754  * pspp_sheet_view_set_hover_selection:
12755  * @tree_view: a #PsppSheetView
12756  * @hover: %TRUE to enable hover selection mode
12757  *
12758  * Enables of disables the hover selection mode of @tree_view.
12759  * Hover selection makes the selected row follow the pointer.
12760  * Currently, this works only for the selection modes 
12761  * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12762  * 
12763  * Since: 2.6
12764  **/
12765 void     
12766 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12767                                    gboolean     hover)
12768 {
12769   hover = hover != FALSE;
12770
12771   if (hover != tree_view->priv->hover_selection)
12772     {
12773       tree_view->priv->hover_selection = hover;
12774
12775       g_object_notify (G_OBJECT (tree_view), "hover-selection");
12776     }
12777 }
12778
12779 /**
12780  * pspp_sheet_view_get_hover_selection:
12781  * @tree_view: a #PsppSheetView
12782  * 
12783  * Returns whether hover selection mode is turned on for @tree_view.
12784  * 
12785  * Return value: %TRUE if @tree_view is in hover selection mode
12786  *
12787  * Since: 2.6 
12788  **/
12789 gboolean 
12790 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12791 {
12792   return tree_view->priv->hover_selection;
12793 }
12794
12795 /**
12796  * pspp_sheet_view_set_rubber_banding:
12797  * @tree_view: a #PsppSheetView
12798  * @enable: %TRUE to enable rubber banding
12799  *
12800  * Enables or disables rubber banding in @tree_view.  If the selection mode is
12801  * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12802  * banding will allow the user to select multiple rows by dragging the mouse.
12803  * 
12804  * Since: 2.10
12805  **/
12806 void
12807 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12808                                   gboolean     enable)
12809 {
12810   enable = enable != FALSE;
12811
12812   if (enable != tree_view->priv->rubber_banding_enable)
12813     {
12814       tree_view->priv->rubber_banding_enable = enable;
12815
12816       g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12817     }
12818 }
12819
12820 /**
12821  * pspp_sheet_view_get_rubber_banding:
12822  * @tree_view: a #PsppSheetView
12823  * 
12824  * Returns whether rubber banding is turned on for @tree_view.  If the
12825  * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12826  * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12827  * select multiple rows by dragging the mouse.
12828  * 
12829  * Return value: %TRUE if rubber banding in @tree_view is enabled.
12830  *
12831  * Since: 2.10
12832  **/
12833 gboolean
12834 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12835 {
12836   return tree_view->priv->rubber_banding_enable;
12837 }
12838
12839 /**
12840  * pspp_sheet_view_is_rubber_banding_active:
12841  * @tree_view: a #PsppSheetView
12842  * 
12843  * Returns whether a rubber banding operation is currently being done
12844  * in @tree_view.
12845  *
12846  * Return value: %TRUE if a rubber banding operation is currently being
12847  * done in @tree_view.
12848  *
12849  * Since: 2.12
12850  **/
12851 gboolean
12852 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12853 {
12854   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12855
12856   if (tree_view->priv->rubber_banding_enable
12857       && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12858     return TRUE;
12859
12860   return FALSE;
12861 }
12862
12863 static void
12864 pspp_sheet_view_grab_notify (GtkWidget *widget,
12865                            gboolean   was_grabbed)
12866 {
12867   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12868
12869   tree_view->priv->in_grab = !was_grabbed;
12870
12871   if (!was_grabbed)
12872     {
12873       tree_view->priv->pressed_button = -1;
12874
12875       if (tree_view->priv->rubber_band_status)
12876         pspp_sheet_view_stop_rubber_band (tree_view);
12877     }
12878 }
12879
12880 static void
12881 pspp_sheet_view_state_changed (GtkWidget      *widget,
12882                              GtkStateType    previous_state)
12883 {
12884   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12885
12886   if (gtk_widget_get_realized (widget))
12887     {
12888       GtkStyle *style = gtk_widget_get_style (widget);
12889       gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]);
12890     }
12891
12892   gtk_widget_queue_draw (widget);
12893 }
12894
12895 /**
12896  * pspp_sheet_view_get_grid_lines:
12897  * @tree_view: a #PsppSheetView
12898  *
12899  * Returns which grid lines are enabled in @tree_view.
12900  *
12901  * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12902  * are enabled.
12903  *
12904  * Since: 2.10
12905  */
12906 PsppSheetViewGridLines
12907 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12908 {
12909   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12910
12911   return tree_view->priv->grid_lines;
12912 }
12913
12914 /**
12915  * pspp_sheet_view_set_grid_lines:
12916  * @tree_view: a #PsppSheetView
12917  * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12918  * enable.
12919  *
12920  * Sets which grid lines to draw in @tree_view.
12921  *
12922  * Since: 2.10
12923  */
12924 void
12925 pspp_sheet_view_set_grid_lines (PsppSheetView           *tree_view,
12926                               PsppSheetViewGridLines   grid_lines)
12927 {
12928   PsppSheetViewPrivate *priv;
12929   PsppSheetViewGridLines old_grid_lines;
12930
12931   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12932
12933   priv = tree_view->priv;
12934
12935   old_grid_lines = priv->grid_lines;
12936   priv->grid_lines = grid_lines;
12937   
12938   if (old_grid_lines != grid_lines)
12939     {
12940       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12941       
12942       g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12943     }
12944 }
12945
12946 /**
12947  * pspp_sheet_view_get_special_cells:
12948  * @tree_view: a #PsppSheetView
12949  *
12950  * Returns which grid lines are enabled in @tree_view.
12951  *
12952  * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12953  * the sheet view contain special cells.
12954  */
12955 PsppSheetViewSpecialCells
12956 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12957 {
12958   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12959
12960   return tree_view->priv->special_cells;
12961 }
12962
12963 /**
12964  * pspp_sheet_view_set_special_cells:
12965  * @tree_view: a #PsppSheetView
12966  * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12967  * the sheet view contain special cells.
12968  *
12969  * Sets whether rows in the sheet view contain special cells, controlling the
12970  * rendering of row selections.
12971  */
12972 void
12973 pspp_sheet_view_set_special_cells (PsppSheetView           *tree_view,
12974                               PsppSheetViewSpecialCells   special_cells)
12975 {
12976   PsppSheetViewPrivate *priv;
12977
12978   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12979
12980   priv = tree_view->priv;
12981
12982   if (priv->special_cells != special_cells)
12983     {
12984       priv->special_cells = special_cells;
12985       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12986       g_object_notify (G_OBJECT (tree_view), "special-cells");
12987     }
12988 }
12989
12990 int
12991 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12992 {
12993   /* XXX (re)calculate fixed_height if necessary */
12994   return tree_view->priv->fixed_height;
12995 }
12996
12997 void
12998 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12999                                   int fixed_height)
13000 {
13001   g_return_if_fail (fixed_height > 0);
13002
13003   if (tree_view->priv->fixed_height != fixed_height)
13004     {
13005       tree_view->priv->fixed_height = fixed_height;
13006       g_object_notify (G_OBJECT (tree_view), "fixed-height");
13007     }
13008   if (!tree_view->priv->fixed_height_set)
13009     {
13010       tree_view->priv->fixed_height_set = TRUE;
13011       g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
13012     }
13013 }
13014
13015 /**
13016  * pspp_sheet_view_set_tooltip_row:
13017  * @tree_view: a #PsppSheetView
13018  * @tooltip: a #GtkTooltip
13019  * @path: a #GtkTreePath
13020  *
13021  * Sets the tip area of @tooltip to be the area covered by the row at @path.
13022  * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13023  * See also gtk_tooltip_set_tip_area().
13024  *
13025  * Since: 2.12
13026  */
13027 void
13028 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
13029                                GtkTooltip  *tooltip,
13030                                GtkTreePath *path)
13031 {
13032   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13033   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13034
13035   pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
13036 }
13037
13038 /**
13039  * pspp_sheet_view_set_tooltip_cell:
13040  * @tree_view: a #PsppSheetView
13041  * @tooltip: a #GtkTooltip
13042  * @path: (allow-none): a #GtkTreePath or %NULL
13043  * @column: (allow-none): a #PsppSheetViewColumn or %NULL
13044  * @cell: (allow-none): a #GtkCellRenderer or %NULL
13045  *
13046  * Sets the tip area of @tooltip to the area @path, @column and @cell have
13047  * in common.  For example if @path is %NULL and @column is set, the tip
13048  * area will be set to the full area covered by @column.  See also
13049  * gtk_tooltip_set_tip_area().
13050  *
13051  * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13052  *
13053  * Since: 2.12
13054  */
13055 void
13056 pspp_sheet_view_set_tooltip_cell (PsppSheetView       *tree_view,
13057                                 GtkTooltip        *tooltip,
13058                                 GtkTreePath       *path,
13059                                 PsppSheetViewColumn *column,
13060                                 GtkCellRenderer   *cell)
13061 {
13062   GdkRectangle rect;
13063
13064   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13065   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13066   g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
13067   g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
13068
13069   /* Determine x values. */
13070   if (column && cell)
13071     {
13072       GdkRectangle tmp;
13073       gint start, width;
13074
13075       pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
13076       pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
13077
13078       pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13079                                                          tmp.x + start, 0,
13080                                                          &rect.x, NULL);
13081       rect.width = width;
13082     }
13083   else if (column)
13084     {
13085       GdkRectangle tmp;
13086
13087       pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
13088       pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13089                                                          tmp.x, 0,
13090                                                          &rect.x, NULL);
13091       rect.width = tmp.width;
13092     }
13093   else
13094     {
13095       GtkAllocation allocation;
13096       gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
13097       rect.x = 0;
13098       rect.width = allocation.width;
13099     }
13100
13101   /* Determine y values. */
13102   if (path)
13103     {
13104       GdkRectangle tmp;
13105
13106       pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
13107       pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13108                                                          0, tmp.y,
13109                                                          NULL, &rect.y);
13110       rect.height = tmp.height;
13111     }
13112   else
13113     {
13114       rect.y = 0;
13115       rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
13116     }
13117
13118   gtk_tooltip_set_tip_area (tooltip, &rect);
13119 }
13120
13121 /**
13122  * pspp_sheet_view_get_tooltip_context:
13123  * @tree_view: a #PsppSheetView
13124  * @x: the x coordinate (relative to widget coordinates)
13125  * @y: the y coordinate (relative to widget coordinates)
13126  * @keyboard_tip: whether this is a keyboard tooltip or not
13127  * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
13128  * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
13129  * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
13130  *
13131  * This function is supposed to be used in a #GtkWidget::query-tooltip
13132  * signal handler for #PsppSheetView.  The @x, @y and @keyboard_tip values
13133  * which are received in the signal handler, should be passed to this
13134  * function without modification.
13135  *
13136  * The return value indicates whether there is a tree view row at the given
13137  * coordinates (%TRUE) or not (%FALSE) for mouse tooltips.  For keyboard
13138  * tooltips the row returned will be the cursor row.  When %TRUE, then any of
13139  * @model, @path and @iter which have been provided will be set to point to
13140  * that row and the corresponding model.  @x and @y will always be converted
13141  * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
13142  *
13143  * Return value: whether or not the given tooltip context points to a row.
13144  *
13145  * Since: 2.12
13146  */
13147 gboolean
13148 pspp_sheet_view_get_tooltip_context (PsppSheetView   *tree_view,
13149                                    gint          *x,
13150                                    gint          *y,
13151                                    gboolean       keyboard_tip,
13152                                    GtkTreeModel **model,
13153                                    GtkTreePath  **path,
13154                                    GtkTreeIter   *iter)
13155 {
13156   GtkTreePath *tmppath = NULL;
13157
13158   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
13159   g_return_val_if_fail (x != NULL, FALSE);
13160   g_return_val_if_fail (y != NULL, FALSE);
13161
13162   if (keyboard_tip)
13163     {
13164       pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
13165
13166       if (!tmppath)
13167         return FALSE;
13168     }
13169   else
13170     {
13171       pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
13172                                                          x, y);
13173
13174       if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
13175                                           &tmppath, NULL, NULL, NULL))
13176         return FALSE;
13177     }
13178
13179   if (model)
13180     *model = pspp_sheet_view_get_model (tree_view);
13181
13182   if (iter)
13183     gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
13184                              iter, tmppath);
13185
13186   if (path)
13187     *path = tmppath;
13188   else
13189     gtk_tree_path_free (tmppath);
13190
13191   return TRUE;
13192 }
13193
13194 static gboolean
13195 pspp_sheet_view_set_tooltip_query_cb (GtkWidget  *widget,
13196                                     gint        x,
13197                                     gint        y,
13198                                     gboolean    keyboard_tip,
13199                                     GtkTooltip *tooltip,
13200                                     gpointer    data)
13201 {
13202   GValue value = { 0, };
13203   GValue transformed = { 0, };
13204   GtkTreeIter iter;
13205   GtkTreePath *path;
13206   GtkTreeModel *model;
13207   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
13208
13209   if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
13210                                           &x, &y,
13211                                           keyboard_tip,
13212                                           &model, &path, &iter))
13213     return FALSE;
13214
13215   gtk_tree_model_get_value (model, &iter,
13216                             tree_view->priv->tooltip_column, &value);
13217
13218   g_value_init (&transformed, G_TYPE_STRING);
13219
13220   if (!g_value_transform (&value, &transformed))
13221     {
13222       g_value_unset (&value);
13223       gtk_tree_path_free (path);
13224
13225       return FALSE;
13226     }
13227
13228   g_value_unset (&value);
13229
13230   if (!g_value_get_string (&transformed))
13231     {
13232       g_value_unset (&transformed);
13233       gtk_tree_path_free (path);
13234
13235       return FALSE;
13236     }
13237
13238   gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
13239   pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
13240
13241   gtk_tree_path_free (path);
13242   g_value_unset (&transformed);
13243
13244   return TRUE;
13245 }
13246
13247 /**
13248  * pspp_sheet_view_set_tooltip_column:
13249  * @tree_view: a #PsppSheetView
13250  * @column: an integer, which is a valid column number for @tree_view's model
13251  *
13252  * If you only plan to have simple (text-only) tooltips on full rows, you
13253  * can use this function to have #PsppSheetView handle these automatically
13254  * for you. @column should be set to the column in @tree_view's model
13255  * containing the tooltip texts, or -1 to disable this feature.
13256  *
13257  * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
13258  * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13259  *
13260  * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13261  * so &amp;, &lt;, etc have to be escaped in the text.
13262  *
13263  * Since: 2.12
13264  */
13265 void
13266 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13267                                   gint         column)
13268 {
13269   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13270
13271   if (column == tree_view->priv->tooltip_column)
13272     return;
13273
13274   if (column == -1)
13275     {
13276       g_signal_handlers_disconnect_by_func (tree_view,
13277                                             pspp_sheet_view_set_tooltip_query_cb,
13278                                             NULL);
13279       gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13280     }
13281   else
13282     {
13283       if (tree_view->priv->tooltip_column == -1)
13284         {
13285           g_signal_connect (tree_view, "query-tooltip",
13286                             G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13287           gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13288         }
13289     }
13290
13291   tree_view->priv->tooltip_column = column;
13292   g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13293 }
13294
13295 /**
13296  * pspp_sheet_view_get_tooltip_column:
13297  * @tree_view: a #PsppSheetView
13298  *
13299  * Returns the column of @tree_view's model which is being used for
13300  * displaying tooltips on @tree_view's rows.
13301  *
13302  * Return value: the index of the tooltip column that is currently being
13303  * used, or -1 if this is disabled.
13304  *
13305  * Since: 2.12
13306  */
13307 gint
13308 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13309 {
13310   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13311
13312   return tree_view->priv->tooltip_column;
13313 }
13314
13315 gboolean
13316 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13317                                   GValue                *return_accu,
13318                                   const GValue          *handler_return,
13319                                   gpointer               dummy)
13320 {
13321   gboolean continue_emission;
13322   gboolean signal_handled;
13323   
13324   signal_handled = g_value_get_boolean (handler_return);
13325   g_value_set_boolean (return_accu, signal_handled);
13326   continue_emission = !signal_handled;
13327   
13328   return continue_emission;
13329 }
13330
13331
13332 GType
13333 pspp_sheet_view_grid_lines_get_type (void)
13334 {
13335     static GType etype = 0;
13336     if (G_UNLIKELY(etype == 0)) {
13337         static const GEnumValue values[] = {
13338             { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13339             { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13340             { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13341             { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13342             { 0, NULL, NULL }
13343         };
13344         etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13345     }
13346     return etype;
13347 }
13348
13349 GType
13350 pspp_sheet_view_special_cells_get_type (void)
13351 {
13352     static GType etype = 0;
13353     if (G_UNLIKELY(etype == 0)) {
13354         static const GEnumValue values[] = {
13355             { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13356             { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13357             { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13358             { 0, NULL, NULL }
13359         };
13360         etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);
13361     }
13362     return etype;
13363 }